character 0.1.0 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (173) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +4 -1
  3. data/.rspec +1 -0
  4. data/README.md +185 -14
  5. data/Rakefile +8 -1
  6. data/app/assets/images/character/logo.jpg +0 -0
  7. data/app/assets/javascripts/character.coffee +134 -0
  8. data/app/assets/javascripts/character/dashboard/_visitors.coffee +27 -0
  9. data/app/assets/javascripts/character/dashboard/layout.coffee +156 -0
  10. data/app/assets/javascripts/character/dashboard/module.coffee +51 -0
  11. data/app/assets/javascripts/character/generic/details.coffee +233 -0
  12. data/app/assets/javascripts/character/generic/helpers/compact_object.coffee +7 -0
  13. data/app/assets/javascripts/character/generic/helpers/data_inputs.coffee +21 -0
  14. data/app/assets/javascripts/character/generic/helpers/date_select.coffee +45 -0
  15. data/app/assets/javascripts/character/generic/helpers/editor.coffee +11 -0
  16. data/app/assets/javascripts/character/generic/helpers/redactor.coffee +38 -0
  17. data/app/assets/javascripts/character/generic/helpers/reorder.coffee +36 -0
  18. data/app/assets/javascripts/character/generic/layout.coffee +40 -0
  19. data/app/assets/javascripts/character/generic/list.coffee +214 -0
  20. data/app/assets/javascripts/character/generic/model.coffee +135 -0
  21. data/app/assets/javascripts/character/generic/module.coffee +157 -0
  22. data/app/assets/javascripts/character/images/module.coffee +148 -0
  23. data/app/assets/javascripts/character/pages/module.coffee +43 -0
  24. data/app/assets/javascripts/character/posts/module.coffee +113 -0
  25. data/app/assets/javascripts/character/settings/_admins.coffee +61 -0
  26. data/app/assets/javascripts/character/settings/_authors.coffee +56 -0
  27. data/app/assets/javascripts/character/settings/_categories.coffee +61 -0
  28. data/app/assets/javascripts/character/settings/_layout.coffee +7 -0
  29. data/app/assets/javascripts/character/settings/_redirects.coffee +56 -0
  30. data/app/assets/javascripts/character/settings/_website.coffee +7 -0
  31. data/app/assets/javascripts/character/settings/details.coffee +16 -0
  32. data/app/assets/javascripts/character/settings/layout.coffee +46 -0
  33. data/app/assets/javascripts/character/settings/module.coffee +78 -0
  34. data/app/assets/stylesheets/character.scss +37 -0
  35. data/app/assets/stylesheets/character/_admins.scss +30 -0
  36. data/app/assets/stylesheets/character/_authors.scss +30 -0
  37. data/app/assets/stylesheets/character/_categories.scss +32 -0
  38. data/app/assets/stylesheets/character/_dashboard.scss +143 -0
  39. data/app/assets/stylesheets/character/_posts.scss +93 -0
  40. data/app/assets/stylesheets/character/_redirects.scss +35 -0
  41. data/app/assets/stylesheets/character/base.scss +967 -0
  42. data/app/assets/stylesheets/character/typography.scss +29 -0
  43. data/app/controllers/character/api_controller.rb +170 -0
  44. data/app/controllers/character/application_controller.rb +37 -0
  45. data/app/controllers/character/settings_controller.rb +72 -0
  46. data/app/controllers/concerns/character/auth_concern.rb +41 -0
  47. data/app/controllers/concerns/character/instance_concern.rb +31 -0
  48. data/app/controllers/concerns/character/json_object_concern.rb +32 -0
  49. data/app/controllers/concerns/character/model_class_concern.rb +28 -0
  50. data/app/controllers/concerns/character/params_concern.rb +33 -0
  51. data/app/controllers/concerns/character/templates_concern.rb +32 -0
  52. data/app/controllers/concerns/not_found.rb +18 -0
  53. data/app/controllers/concerns/website_settings.rb +18 -0
  54. data/app/controllers/pages_controller.rb +8 -0
  55. data/app/controllers/posts_controller.rb +43 -0
  56. data/app/helpers/character_helper.rb +8 -0
  57. data/app/helpers/page_helper.rb +67 -0
  58. data/app/inputs/foundation_string_input.rb +44 -0
  59. data/app/inputs/foundation_switch_input.rb +35 -0
  60. data/app/models/character/image.rb +12 -0
  61. data/app/models/character/page.rb +21 -0
  62. data/app/models/character/post.rb +32 -12
  63. data/app/models/character/post_author.rb +22 -0
  64. data/app/models/character/post_category.rb +21 -0
  65. data/app/models/character/redirect.rb +15 -0
  66. data/app/models/character/settings/variable.rb +23 -0
  67. data/app/models/character/sitemap/sitemap_generator_helper.rb +15 -0
  68. data/app/models/character/user.rb +29 -0
  69. data/app/models/concerns/created_ago.rb +12 -0
  70. data/app/models/concerns/hideable.rb +27 -0
  71. data/app/models/concerns/orderable.rb +8 -0
  72. data/app/models/concerns/report.rb +11 -0
  73. data/app/models/concerns/report_daily.rb +32 -0
  74. data/app/models/concerns/report_monthly.rb +18 -0
  75. data/app/models/concerns/report_weekly.rb +19 -0
  76. data/app/models/concerns/updated_ago.rb +12 -0
  77. data/app/models/reports/analytics_daily.rb +26 -0
  78. data/app/models/reports/analytics_monthly.rb +16 -0
  79. data/app/models/reports/analytics_weekly.rb +16 -0
  80. data/app/services/google_analytics.rb +43 -0
  81. data/app/uploaders/character/image_uploader.rb +22 -0
  82. data/app/uploaders/character/settings/file_uploader.rb +5 -0
  83. data/app/views/character/character.html.erb +67 -0
  84. data/app/views/character/generic/form.html.erb +8 -0
  85. data/app/views/character/pages/form.html.erb +28 -0
  86. data/app/views/character/posts/form.html.erb +38 -0
  87. data/app/views/character/settings/admins.html.erb +29 -0
  88. data/app/views/character/settings/post_authors.html.erb +28 -0
  89. data/app/views/character/settings/post_categories.html.erb +31 -0
  90. data/app/views/character/settings/redirects.html.erb +30 -0
  91. data/app/views/character/settings/settings_group.html.erb +67 -0
  92. data/app/views/errors/not_found.html.erb +157 -0
  93. data/app/views/pages/_default.html.erb +3 -0
  94. data/app/views/pages/_redactor.html.erb +3 -0
  95. data/app/views/pages/show.html.erb +5 -0
  96. data/app/views/posts/_post.html.erb +17 -0
  97. data/app/views/posts/author.html.erb +18 -0
  98. data/app/views/posts/category.html.erb +18 -0
  99. data/app/views/posts/index.html.erb +18 -0
  100. data/app/views/posts/rss.builder +19 -0
  101. data/app/views/posts/show.html.erb +14 -0
  102. data/app/views/shared/_google_analytics.html.erb +13 -0
  103. data/character.gemspec +48 -5
  104. data/doc/README_old.md +161 -0
  105. data/doc/generic_app.md +19 -0
  106. data/doc/img/demo-1.jpg +0 -0
  107. data/doc/img/demo-2.jpg +0 -0
  108. data/doc/img/demo-3.jpg +0 -0
  109. data/doc/img/demo-4.jpg +0 -0
  110. data/doc/img/demo-5.jpg +0 -0
  111. data/doc/instances.md +39 -0
  112. data/doc/settings.md +1 -0
  113. data/lib/character.rb +29 -1
  114. data/lib/character/engine.rb +33 -1
  115. data/lib/character/generators/bootstrap_generator.rb +51 -0
  116. data/lib/character/instance.rb +59 -0
  117. data/lib/character/routing.rb +42 -5
  118. data/lib/character/settings.rb +101 -0
  119. data/lib/character/templates/admin.coffee +15 -0
  120. data/lib/character/templates/admin.scss +3 -0
  121. data/lib/character/templates/application.html.erb +44 -0
  122. data/lib/character/templates/application.scss +12 -0
  123. data/lib/character/templates/assets.rb +1 -0
  124. data/lib/character/templates/initializer.rb +5 -0
  125. data/lib/character/templates/settings.scss +11 -0
  126. data/lib/character/templates/settings.yml +67 -0
  127. data/lib/character/templates/typography.scss +13 -0
  128. data/lib/character/version.rb +2 -2
  129. data/lib/mongoid/carrierwave_serialization_patch.rb +9 -0
  130. data/lib/tasks/analytics.rake +52 -0
  131. data/test/config/application.rb +65 -0
  132. data/test/config/mongoid.yml +12 -0
  133. data/test/config/secrets.yml +22 -0
  134. data/test/controllers/character/api_controller_test.rb +94 -0
  135. data/test/factories/product_factory.rb +5 -0
  136. data/test/lib/character/engine_test.rb +33 -0
  137. data/test/lib/character/routing_test.rb +31 -0
  138. data/test/test_helper.rb +48 -0
  139. data/vendor/assets/javascripts/backbone.js +944 -794
  140. data/vendor/assets/javascripts/jquery.fileupload.js +1426 -0
  141. data/vendor/assets/javascripts/jquery.form.js +1278 -0
  142. data/vendor/assets/javascripts/jquery.iframe-transport.js +214 -0
  143. data/vendor/assets/javascripts/raphael.js +8117 -0
  144. data/vendor/assets/javascripts/raphael.morris.js +1885 -0
  145. data/vendor/assets/javascripts/underscore.inflection.js +177 -0
  146. data/vendor/assets/javascripts/underscore.string.js +1 -1
  147. data/vendor/assets/stylesheets/csspinner.css +361 -0
  148. data/vendor/assets/stylesheets/normalize.css +423 -0
  149. metadata +499 -49
  150. data/app/controllers/character/posts_controller.rb +0 -27
  151. data/lib/generators/character/install_generator.rb +0 -42
  152. data/lib/generators/character/templates/README +0 -1
  153. data/lib/generators/character/templates/admin/character.rb +0 -3
  154. data/vendor/assets/fonts/general_foundicons.eot +0 -0
  155. data/vendor/assets/fonts/general_foundicons.svg +0 -15
  156. data/vendor/assets/fonts/general_foundicons.ttf +0 -0
  157. data/vendor/assets/fonts/general_foundicons.woff +0 -0
  158. data/vendor/assets/javascripts/character/index.js.coffee +0 -53
  159. data/vendor/assets/javascripts/character/models/post.js.coffee +0 -39
  160. data/vendor/assets/javascripts/character/views/app.js.coffee +0 -81
  161. data/vendor/assets/javascripts/character/views/editor.js.coffee +0 -231
  162. data/vendor/assets/javascripts/character/views/editor_settings.js.coffee +0 -44
  163. data/vendor/assets/javascripts/character/views/index.js.coffee +0 -116
  164. data/vendor/assets/javascripts/character/views/preview.js.coffee +0 -49
  165. data/vendor/assets/javascripts/jquery.smartresize.js +0 -30
  166. data/vendor/assets/javascripts/lodash.js +0 -4258
  167. data/vendor/assets/javascripts/showdown.js +0 -62
  168. data/vendor/assets/stylesheets/character/_base.css.scss +0 -84
  169. data/vendor/assets/stylesheets/character/_icons.css.scss.erb +0 -96
  170. data/vendor/assets/stylesheets/character/_view_editor.css.scss +0 -115
  171. data/vendor/assets/stylesheets/character/_view_index.css.scss +0 -73
  172. data/vendor/assets/stylesheets/character/_view_preview.css.scss +0 -49
  173. data/vendor/assets/stylesheets/character/index.css.scss +0 -32
@@ -0,0 +1,40 @@
1
+ #= require ./list
2
+
3
+ #
4
+ # Marionette.js Layout Documentation
5
+ # https://github.com/marionettejs/backbone.marionette/blob/master/docs/marionette.layout.md
6
+ #
7
+ @Character.Generic.Layout = Backbone.Marionette.LayoutView.extend
8
+ className: 'chr-layout'
9
+
10
+ template: -> "<div class='left'>
11
+ <header id=list_header class='chr-list-header'></header>
12
+ <div id=list_content class='chr-list'></div>
13
+ </div>
14
+ <div id=details class='right chr-logo'>
15
+ </div>"
16
+
17
+ regions:
18
+ list_header: '#list_header'
19
+ list_content: '#list_content'
20
+ details: '#details'
21
+
22
+ initialize: ->
23
+ @ListHeaderView = @options.module.ListHeaderView
24
+ @ListView = @options.module.ListView
25
+ @collection = @options.module.collection
26
+
27
+ onRender: ->
28
+ @header = new @ListHeaderView(@options)
29
+ @list = new @ListView({ collection: @collection, module: @options.module })
30
+
31
+ @list_header.show(@header)
32
+ @list_content.show(@list)
33
+
34
+ closeDetails: ->
35
+ @list.unselectCurrentItem()
36
+ @details.reset()
37
+
38
+ updateListScope: (listScope, callback) ->
39
+ @header.update(listScope)
40
+ @collection.setScope(listScope).fetchPage(1, callback)
@@ -0,0 +1,214 @@
1
+
2
+ #
3
+ # Marionette.js Item View Documentation
4
+ # https://github.com/marionettejs/backbone.marionette/blob/master/docs/marionette.itemview.md
5
+ #
6
+ @Character.Generic.ListHeaderView = Backbone.Marionette.ItemView.extend
7
+ template: -> "<aside id=list_search class=chr-list-search>
8
+ <i class='chr-icon icon-search icon-flip'></i>
9
+ <input type=search placeholder='Search...' />
10
+ <a id=list_search_hide href='#'><i class='chr-icon icon-close'></i></a>
11
+ </aside>
12
+ <a id=list_search_show class=search title='Search' href='#'><i class='chr-icon icon-search'></i></a>
13
+ <a id=new class=new title='Create new item' href='#'><i class='chr-icon icon-plus'></i></a>
14
+ <div class=scope><span id=list_title class=title></span></div>"
15
+
16
+ ui:
17
+ title: '#list_title'
18
+ search: '#list_search'
19
+ searchInput: '#list_search input'
20
+ searchShow: '#list_search_show'
21
+ newAction: '#new'
22
+
23
+ events:
24
+ 'click #list_search_hide': '_hideSearch'
25
+ 'click #list_search_show': '_showSearch'
26
+ 'keyup #list_search input': '_onKeyup'
27
+ 'click #list_title': '_toggleScopesMenu'
28
+
29
+ _toggleScopesMenu: ->
30
+ if @ui.scopes
31
+ @ui.scopes.toggleClass 'show'
32
+
33
+ _onKeyup: (e) ->
34
+ if @searchTypeTimeout
35
+ clearTimeout(@searchTypeTimeout)
36
+
37
+ return @toggleSearchBar() if e.keyCode == 27
38
+
39
+ query = @ui.searchInput.val()
40
+
41
+ search = =>
42
+ @collection.setSearchQuery(query).fetchPage(1)
43
+
44
+ if e.keyCode == 13
45
+ search()
46
+ else
47
+ @searchTypeTimeout = setTimeout(search, 800)
48
+
49
+ _showSearch: ->
50
+ @ui.search.addClass('active')
51
+ @ui.searchInput.focus()
52
+ false
53
+
54
+ _hideSearch: ->
55
+ @ui.search.removeClass('active')
56
+ @ui.searchInput.val('')
57
+ @collection.setSearchQuery()
58
+ @collection.fetchPage(1)
59
+ false
60
+
61
+ _addScopesDropdown: ->
62
+ if @scopes and _(@scopes).keys().length > 0
63
+ if not @ui.scopes
64
+ @ui.scopes = $('<ul />')
65
+ @ui.scopes.append """<li><a href='#{ @path }'>#{ @options.listTitle }</a></li>"""
66
+
67
+ _.each @scopes, (scope, key) =>
68
+ @ui.scopes.append """<li><a href='#{ @path }/#{ scope.slug }'>#{ scope.title }</a></li>"""
69
+
70
+ @ui.title.after(@ui.scopes).addClass('dropdown')
71
+
72
+ @_setActiveScopeLink()
73
+ @_hideScopesDropdown()
74
+
75
+ _setActiveScopeLink: ->
76
+ currentScopeTitle = @ui.title.html()
77
+ @ui.scopes.find('a').each (i, el) ->
78
+ if $(el).html() == currentScopeTitle then $(el).addClass('active') else $(el).removeClass('active')
79
+
80
+ _hideScopesDropdown: ->
81
+ @ui.scopes.removeClass('show')
82
+
83
+ onRender: ->
84
+ @beforeOnRender?()
85
+
86
+ @collection = @options.module.collection
87
+ @scopes = @options.listScopes
88
+ @path = "#/#{ @options.moduleName }"
89
+
90
+ if not @options.newItems
91
+ @ui.newAction.hide()
92
+
93
+ if @options.listSearch
94
+ @ui.searchShow.show()
95
+ else
96
+ @ui.searchShow.hide()
97
+
98
+ @afterOnRender?()
99
+
100
+ onDestroy: ->
101
+ @beforeOnClose?()
102
+ @afterOnClose?()
103
+
104
+ update: (scopeSlug) ->
105
+ @beforeUpdate?()
106
+
107
+ title = @options.listTitle
108
+ path = @path
109
+
110
+ if @scopes and scopeSlug
111
+ title = @scopes[scopeSlug].title
112
+ path += '/' + @scopes[scopeSlug].slug
113
+
114
+ @ui.title.html(title).parent().attr('href', path)
115
+
116
+ @ui.newAction.attr('href', path + "/new")
117
+
118
+ @_addScopesDropdown()
119
+
120
+ @afterUpdate?()
121
+
122
+
123
+ #
124
+ # Marionette.js Item View Documentation
125
+ # https://github.com/marionettejs/backbone.marionette/blob/master/docs/marionette.itemview.md
126
+ #
127
+ @Character.Generic.ListEmtpyView = Backbone.Marionette.ItemView.extend
128
+ tagName: 'li'
129
+ className: 'empty'
130
+ template: -> "No items found"
131
+
132
+
133
+ #
134
+ # Marionette.js Item View Documentation
135
+ # https://github.com/marionettejs/backbone.marionette/blob/master/docs/marionette.itemview.md
136
+ #
137
+ @Character.Generic.ListItemView = Backbone.Marionette.ItemView.extend
138
+ tagName: 'li'
139
+ className: 'chr-list-item'
140
+
141
+ template: (item) -> """<a title='#{ item.__title }'>
142
+ <img src='#{ item.__thumb }' class='thumbnail' />
143
+ <div class='container'>
144
+ <div class='title'>#{ item.__title }</div>
145
+ <div class='meta'>#{ item.__meta }</div>
146
+ </div>
147
+ </a>"""
148
+
149
+ modelEvents:
150
+ 'change': 'render'
151
+ 'destroy': 'remove'
152
+
153
+ ui:
154
+ link: 'a'
155
+
156
+ onRender: ->
157
+ path = chr.currentPath
158
+
159
+ if @model.getThumb()
160
+ @$el.addClass('has-thumbnail')
161
+
162
+ @ui.link.attr('href', "#/#{ path }/edit/#{ @model.id }")
163
+
164
+ @$el.attr('data-id', @model.id)
165
+ @$el.attr('data-position', @model.getPosition())
166
+
167
+
168
+ #
169
+ # Marionette.js Collection View Documentation
170
+ # https://github.com/marionettejs/backbone.marionette/blob/master/docs/marionette.collectionview.md
171
+ #
172
+ @Character.Generic.ListView = Backbone.Marionette.CollectionView.extend
173
+ tagName: 'ul'
174
+
175
+ itemViewOptions: (model, index) ->
176
+ module: @options.module
177
+
178
+ initialize: ->
179
+ @childView = @options.module.ListItemView
180
+ @emptyView = @options.module.ListEmtpyView
181
+
182
+ @listenTo(@collection, 'sort', @render, @)
183
+
184
+ loadMoreItems: ->
185
+ # NOTE: This still will request a new page every time if last item is last on last page
186
+ if @collection.size() == @collection.page * @collection.options.constantParams.pp
187
+ if not @loading_more_items_in_progress
188
+ @loading_more_items_in_progress = true
189
+ @collection.fetchNextPage =>
190
+ @loading_more_items_in_progress = false
191
+
192
+ onRender: ->
193
+ @$el.parent().scroll (e) =>
194
+ if e.target.offsetHeight + e.target.scrollTop + 272 > @$el.height()
195
+ @loadMoreItems()
196
+
197
+ if @selected_item_id
198
+ @selectItem(@selected_item_id)
199
+
200
+ if @collection.options.reorder
201
+ Character.Generic.Helpers.startReorder(@$el, @collection)
202
+
203
+ selectItem: (id) ->
204
+ @selected_item_id = id
205
+ @$el.find('li.active').removeClass('active')
206
+ @$el.find("li[data-id=#{ id }]").addClass('active')
207
+
208
+ unselectCurrentItem: ->
209
+ @selected_item_id = null
210
+ @$el.find('li.active').removeClass('active')
211
+
212
+ onDestroy: ->
213
+ if @collection.options.reorder
214
+ Character.Generic.Helpers.stopReorder(@$el)
@@ -0,0 +1,135 @@
1
+
2
+ #
3
+ # Backbone.js Model Documentation
4
+ # http://backbonejs.org/#Model
5
+ #
6
+ @Character.Generic.Model = Backbone.Model.extend
7
+ idAttribute: '_id'
8
+
9
+ urlRoot: ->
10
+ @collection.options.collectionUrl
11
+
12
+ getTitle: ->
13
+ @get(@collection.options.titleField || _(@attributes).keys()[0])
14
+
15
+ getMeta: ->
16
+ @get(@collection.options.metaField) || ''
17
+
18
+ getThumb: ->
19
+ @get(@collection.options.thumbField)
20
+
21
+ getPosition: ->
22
+ @get('_position')
23
+
24
+ toJSON: (options={}) ->
25
+ # add helpers for list item template
26
+ object = _.clone(@attributes)
27
+
28
+ object['__title'] = @getTitle()
29
+ object['__meta'] = @getMeta()
30
+ object['__thumb'] = @getThumb() || '#'
31
+
32
+ return object
33
+
34
+
35
+ #
36
+ # Backbone.js Collection Documentation
37
+ # http://backbonejs.org/#Collection
38
+ #
39
+ @Character.Generic.Collection = Backbone.Collection.extend
40
+ model: Character.Generic.Model
41
+
42
+ # Query parameters:
43
+ #
44
+ # @page
45
+ # @search_query
46
+ # @order_by
47
+
48
+ # Sort options:
49
+ #
50
+ # @sortField
51
+ # @sortDirection
52
+
53
+ url: (params={}) ->
54
+ params.p = @page
55
+ params.q = @searchQuery
56
+ params.o = @orderBy
57
+ if @options.where
58
+ [name, value] = @options.where.split('=')
59
+ params["where__#{name}"] = value
60
+
61
+ _.extend(params, @filter)
62
+ _.extend(params, @options.constantParams)
63
+
64
+ _.compactObject(params)
65
+
66
+ @options.collectionUrl + "?" + $.param(params, true)
67
+
68
+
69
+ setSearchQuery: (@searchQuery) ->
70
+ return @
71
+
72
+
73
+ setScope: (slug) ->
74
+ @sortField = null
75
+ @sortDirection = null
76
+ @orderBy = null
77
+ @filter = {}
78
+
79
+ scope = @options.scopes?[slug]
80
+
81
+ @orderBy = scope?.order_by || @options.orderBy
82
+
83
+ if @orderBy
84
+ [ @sortField, @sortDirection ] = @orderBy.split(':')
85
+
86
+ if scope
87
+ [name, value] = scope.where.split('=')
88
+ @filter["where__#{name}"] = value
89
+
90
+ return @
91
+
92
+
93
+ fetchPage: (@page, callback=false, reset=true, remove=true) ->
94
+ @fetch
95
+ reset: reset
96
+ remove: remove
97
+ success: (response) -> callback?(response)
98
+ error: (collection, response, options) -> chr.execute('error', response)
99
+
100
+
101
+ fetchNextPage: (callback) ->
102
+ after_fetch = (response) => callback?()
103
+ @fetchPage(@page + 1, after_fetch, false, false)
104
+
105
+
106
+ # reverse sorting for backbone collection
107
+ # http://stackoverflow.com/questions/5013819/reverse-sort-order-with-backbone-js
108
+
109
+
110
+ comparator: (m) ->
111
+ if @sortField
112
+ return m.get(@sortField)
113
+
114
+
115
+ sortBy: (iterator, context) ->
116
+ obj = @models
117
+ direction = @sortDirection
118
+
119
+ return _.pluck( _.map(obj, ( (value, index, list) ->
120
+ return {
121
+ value: value
122
+ index: index
123
+ criteria: iterator.call(context, value, index, list)
124
+ }
125
+ ) ).sort( ( (left, right) ->
126
+ # swap a and b for reverse sort
127
+ a = if direction is "asc" then left.criteria else right.criteria
128
+ b = if direction is "asc" then right.criteria else left.criteria
129
+
130
+ if a != b
131
+ if a > b or a is undefined then return 1
132
+ if a < b or b is undefined then return -1
133
+
134
+ return (if left.index < right.index then -1 else 1)
135
+ ) ), 'value' )
@@ -0,0 +1,157 @@
1
+ #= require_self
2
+ #= require ./model
3
+ #= require ./layout
4
+ #= require ./details
5
+ #= require_tree ./helpers
6
+
7
+ @Character.Generic ||= {}
8
+ @Character.Generic.Helpers ||= {}
9
+
10
+ #
11
+ # Marionette.js Router Documentation
12
+ # https://github.com/marionettejs/backbone.marionette/blob/master/docs/marionette.router.md
13
+ #
14
+ @Character.Generic.Router = Backbone.Marionette.AppRouter.extend
15
+ initialize: (options) ->
16
+ @appRoutes ||= {}
17
+ @appRoutes["#{ options.path }(/:listScope)/new"] = "new"
18
+ @appRoutes["#{ options.path }(/:listScope)/edit/:id"] = "edit"
19
+ @appRoutes["#{ options.path }(/:listScope)"] = "index"
20
+
21
+ #
22
+ # Marionette.js Controller Documentation
23
+ # https://github.com/marionettejs/backbone.marionette/blob/master/docs/marionette.controller.md
24
+ #
25
+ @Character.Generic.Controller = Backbone.Marionette.Controller.extend
26
+ initialize: ->
27
+ @module = @options.module
28
+ @collection = @module.collection
29
+
30
+ index: (listScope, callback) ->
31
+ chr.execute('showModule', @module)
32
+ @module.layout.closeDetails()
33
+
34
+ path = @options.moduleName + ( if listScope then "/#{ listScope }" else '' )
35
+
36
+ if chr.currentPath != path
37
+ chr.currentPath = path
38
+ @module.layout.updateListScope(listScope, callback)
39
+ else
40
+ callback?()
41
+
42
+ new: (listScope) ->
43
+ @index(listScope)
44
+ detailsLayout = new @module.DetailsLayout
45
+ model: no
46
+ collection: @collection
47
+ objectName: @options.objectName
48
+ module: @module
49
+ formUrl: "#{ chr.options.url }/#{ @options.modelName }/new"
50
+ fullscreen: @options.fullscreen
51
+ editorOptions: @options.editorOptions
52
+ redactorOptions: @options.redactorOptions
53
+ @module.layout.details.show(detailsLayout)
54
+
55
+ edit: (listScope, id) ->
56
+ @index(listScope, =>
57
+ @module.layout.list.selectItem(id)
58
+ doc = @collection.get(id)
59
+ detailsLayout = new @module.DetailsLayout
60
+ model: doc
61
+ collection: @collection
62
+ formUrl: "#{ chr.options.url }/#{ @options.modelName }/#{ id }/edit"
63
+ deletable: @options.deletable
64
+ module: @module
65
+ fullscreen: @options.fullscreen
66
+ editorOptions: @options.editorOptions
67
+ redactorOptions: @options.redactorOptions
68
+ @module.layout.details.show(detailsLayout)
69
+ )
70
+
71
+ #
72
+ # Character Generic Module
73
+ # Initialize function
74
+ #
75
+ chr.genericModule = (name, options={}) ->
76
+ options.menuTitle ?= name
77
+ options.menuIcon ?= 'bolt'
78
+
79
+ options.listTitle ?= _.pluralize(name)
80
+ options.listSearch ?= false
81
+ options.listReorder ?= false
82
+ options.listItemsPerPage ?= 25
83
+ options.listItem ?= {}
84
+
85
+ options.newItems ?= true
86
+ options.deletable ?= true
87
+ options.fullscreen ?= true
88
+
89
+ options.editorOptions ?= {}
90
+ options.redactorOptions ?= {}
91
+
92
+ options.moduleName ?= _.underscored(_.pluralize(name))
93
+ options.objectName ?= name
94
+ options.modelName ?= name
95
+
96
+ options.implementation ?= {}
97
+
98
+ # list scopes
99
+ if options.listScopes
100
+ if options.listScopes.default
101
+ listDefaultOrderBy = options.listScopes.default.orderBy
102
+ listDefaultWhere = options.listScopes.default.where
103
+ delete options.listScopes['default']
104
+
105
+ _(options.listScopes).each (scope, slug) ->
106
+ scope.title ||= scope.title || _(slug).titleize()
107
+ scope.slug ||= slug
108
+
109
+ # when reorder, consider of using _position field with desc sorting
110
+ if options.listReorder
111
+ listDefaultOrderBy = '_position:desc'
112
+
113
+ listDefaultOrderBy ?= options.listDefaultOrderBy
114
+ listDefaultWhere ?= options.listDefaultWhere
115
+
116
+ # include model fields
117
+ imf = options.includeModelFields || []
118
+
119
+ # list item template
120
+ # - options.listItem.titleField
121
+ # - options.listItem.metaField
122
+ # - options.listItem.thumbField
123
+ imf.push( options.listItem.titleField ) if options.listItem.titleField
124
+ imf.push( options.listItem.metaField ) if options.listItem.metaField
125
+ imf.push( options.listItem.thumbField ) if options.listItem.thumbField
126
+
127
+ options.includeModelFields = _.uniq(_.compact(imf))
128
+
129
+
130
+ chr.module options.moduleName, (module) ->
131
+
132
+ module = _(module).extend(Character.Generic, options.implementation)
133
+
134
+ module.on 'start', ->
135
+
136
+ @collection = new @Collection()
137
+ @collection.options =
138
+ where: listDefaultWhere
139
+ orderBy: listDefaultOrderBy
140
+ collectionUrl: options.collectionUrl || "#{ chr.options.url }/#{ options.modelName }"
141
+ scopes: options.listScopes
142
+ reorder: options.listReorder
143
+ search: options.listSearch
144
+ titleField: options.listItem.titleField
145
+ metaField: options.listItem.metaField
146
+ thumbField: options.listItem.thumbField
147
+ constantParams:
148
+ f: options.includeModelFields.join(',')
149
+ pp: options.listItemsPerPage
150
+
151
+ options.module = module
152
+
153
+ @controller = new @Controller(options)
154
+ @layout = new @Layout(options)
155
+ @router = new @Router({ path: options.moduleName, controller: @controller })
156
+
157
+ chr.execute('addMenuItem', options.moduleName, options.menuIcon, options.menuTitle)