slices 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (215) hide show
  1. data/CHANGELOG.md +3 -0
  2. data/README.md +51 -0
  3. data/Rakefile +9 -0
  4. data/app/assets/images/slices/ajax-loader.gif +0 -0
  5. data/app/assets/images/slices/asset-background.png +0 -0
  6. data/app/assets/images/slices/asset-spinner.gif +0 -0
  7. data/app/assets/images/slices/bg_header.gif +0 -0
  8. data/app/assets/images/slices/black-Linen.png +0 -0
  9. data/app/assets/images/slices/calendar.svg +68 -0
  10. data/app/assets/images/slices/chosen-sprite.png +0 -0
  11. data/app/assets/images/slices/drag-handle.svg +9 -0
  12. data/app/assets/images/slices/icon_admins.png +0 -0
  13. data/app/assets/images/slices/icon_app.png +0 -0
  14. data/app/assets/images/slices/icon_assets.png +0 -0
  15. data/app/assets/images/slices/icon_collapse.png +0 -0
  16. data/app/assets/images/slices/icon_drag.png +0 -0
  17. data/app/assets/images/slices/icon_files.png +0 -0
  18. data/app/assets/images/slices/icon_generic_file.png +0 -0
  19. data/app/assets/images/slices/icon_images.png +0 -0
  20. data/app/assets/images/slices/icon_padlock.png +0 -0
  21. data/app/assets/images/slices/icon_page.png +0 -0
  22. data/app/assets/images/slices/icon_search.png +0 -0
  23. data/app/assets/images/slices/icon_set-link.png +0 -0
  24. data/app/assets/images/slices/icon_set.png +0 -0
  25. data/app/assets/images/slices/icon_sitemap.png +0 -0
  26. data/app/assets/images/slices/icon_snippets.png +0 -0
  27. data/app/assets/images/slices/icon_template.jpg +0 -0
  28. data/app/assets/images/slices/icon_upload_happy.png +0 -0
  29. data/app/assets/images/slices/icon_upload_sad.png +0 -0
  30. data/app/assets/images/slices/icon_upload_thinking.png +0 -0
  31. data/app/assets/images/slices/noise.png +0 -0
  32. data/app/assets/images/slices/sitemap_icon_ghost.png +0 -0
  33. data/app/assets/images/slices/sitemap_icon_home.png +0 -0
  34. data/app/assets/images/slices/sitemap_icon_page.png +0 -0
  35. data/app/assets/images/slices/sitemap_icon_set_page.png +0 -0
  36. data/app/assets/images/slices/sitemap_icon_virtual_page.png +0 -0
  37. data/app/assets/images/slices/sitemap_overlay.png +0 -0
  38. data/app/assets/images/slices/spinner.gif +0 -0
  39. data/app/assets/images/slices/trash.png +0 -0
  40. data/app/assets/javascripts/admin.js.erb +18 -0
  41. data/app/assets/javascripts/slices/app/backbones/admins.js +114 -0
  42. data/app/assets/javascripts/slices/app/backbones/entries.js +172 -0
  43. data/app/assets/javascripts/slices/app/backbones/generic.js +101 -0
  44. data/app/assets/javascripts/slices/app/backbones/snippets.js +113 -0
  45. data/app/assets/javascripts/slices/app/helpers/assets.js +61 -0
  46. data/app/assets/javascripts/slices/app/helpers/breadcrumbs.js +30 -0
  47. data/app/assets/javascripts/slices/app/helpers/composer.js +26 -0
  48. data/app/assets/javascripts/slices/app/helpers/date_field.js +16 -0
  49. data/app/assets/javascripts/slices/app/helpers/get_value.js +31 -0
  50. data/app/assets/javascripts/slices/app/helpers/icon_upload_names.js.erb +5 -0
  51. data/app/assets/javascripts/slices/app/helpers/layout.js +20 -0
  52. data/app/assets/javascripts/slices/app/helpers/sitemap.js +150 -0
  53. data/app/assets/javascripts/slices/app/helpers/slice_preview.js +48 -0
  54. data/app/assets/javascripts/slices/app/helpers/tagging.js +73 -0
  55. data/app/assets/javascripts/slices/app/helpers/token_field.js +17 -0
  56. data/app/assets/javascripts/slices/app/helpers/upload_icons.js.erb +5 -0
  57. data/app/assets/javascripts/slices/app/helpers/uploader.js +127 -0
  58. data/app/assets/javascripts/slices/app/models/asset.js +29 -0
  59. data/app/assets/javascripts/slices/app/models/asset_collection.js +41 -0
  60. data/app/assets/javascripts/slices/app/models/attachment.js +29 -0
  61. data/app/assets/javascripts/slices/app/models/attachment_collection.js +7 -0
  62. data/app/assets/javascripts/slices/app/models/composer_item.js +1 -0
  63. data/app/assets/javascripts/slices/app/models/composer_item_collection.js +3 -0
  64. data/app/assets/javascripts/slices/app/models/file.js +103 -0
  65. data/app/assets/javascripts/slices/app/models/page.js +186 -0
  66. data/app/assets/javascripts/slices/app/models/s3_file.js +64 -0
  67. data/app/assets/javascripts/slices/app/slices.js +661 -0
  68. data/app/assets/javascripts/slices/app/views/asset_editor_view.js.erb +209 -0
  69. data/app/assets/javascripts/slices/app/views/asset_library_view.js +720 -0
  70. data/app/assets/javascripts/slices/app/views/asset_thumb_view.js.erb +191 -0
  71. data/app/assets/javascripts/slices/app/views/attachment_composer_view.js +350 -0
  72. data/app/assets/javascripts/slices/app/views/attachment_view.js +101 -0
  73. data/app/assets/javascripts/slices/app/views/calendar_view.js +198 -0
  74. data/app/assets/javascripts/slices/app/views/composer_item_view.js +54 -0
  75. data/app/assets/javascripts/slices/app/views/composer_view.js +130 -0
  76. data/app/assets/javascripts/slices/app/views/date_field_view.js +177 -0
  77. data/app/assets/javascripts/slices/app/views/file_view.js +142 -0
  78. data/app/assets/javascripts/slices/app/views/token_field_view.js +253 -0
  79. data/app/assets/javascripts/slices/lib/freeze.js +14 -0
  80. data/app/assets/javascripts/slices/lib/human_file_size.js +16 -0
  81. data/app/assets/javascripts/slices/lib/json_patch.js +9 -0
  82. data/app/assets/javascripts/slices/lib/moment.js +47 -0
  83. data/app/assets/javascripts/slices/lib/plugins.js +101 -0
  84. data/app/assets/javascripts/slices/lib/sortable.js +14 -0
  85. data/app/assets/javascripts/slices/slices.js +27 -0
  86. data/app/assets/javascripts/slices/vendor/autoscroll.js +188 -0
  87. data/app/assets/javascripts/slices/vendor/backbone.js +38 -0
  88. data/app/assets/javascripts/slices/vendor/handlebars.js +1920 -0
  89. data/app/assets/javascripts/slices/vendor/jqmodal.js +69 -0
  90. data/app/assets/javascripts/slices/vendor/jquery-ui.js +274 -0
  91. data/app/assets/javascripts/slices/vendor/jquery-ui_nested-sortable.js +357 -0
  92. data/app/assets/javascripts/slices/vendor/jquery.ajaxprogress.js +76 -0
  93. data/app/assets/javascripts/slices/vendor/jquery.js +2 -0
  94. data/app/assets/javascripts/slices/vendor/livefield.js +459 -0
  95. data/app/assets/javascripts/slices/vendor/moment.js +6 -0
  96. data/app/assets/javascripts/slices/vendor/rails.js +315 -0
  97. data/app/assets/javascripts/slices/vendor/underscore-string.js +1 -0
  98. data/app/assets/javascripts/slices/vendor/underscore.js +5 -0
  99. data/app/assets/stylesheets/admin.css +1 -0
  100. data/app/assets/stylesheets/slices/admin.css.erb +2237 -0
  101. data/app/assets/stylesheets/slices/reset_html5.css +106 -0
  102. data/app/assets/stylesheets/slices/slices.css +7 -0
  103. data/app/controllers/admin/admin_controller.rb +10 -0
  104. data/app/controllers/admin/admins_controller.rb +76 -0
  105. data/app/controllers/admin/assets_controller.rb +53 -0
  106. data/app/controllers/admin/auth/omniauth_callbacks_controller.rb +15 -0
  107. data/app/controllers/admin/auth/passwords_controller.rb +4 -0
  108. data/app/controllers/admin/auth/sessions_controller.rb +4 -0
  109. data/app/controllers/admin/entries_controller.rb +88 -0
  110. data/app/controllers/admin/page_search_controller.rb +12 -0
  111. data/app/controllers/admin/pages_controller.rb +103 -0
  112. data/app/controllers/admin/site_maps_controller.rb +15 -0
  113. data/app/controllers/admin/snippets_controller.rb +33 -0
  114. data/app/controllers/application_controller.rb +4 -0
  115. data/app/controllers/pages_controller.rb +45 -0
  116. data/app/controllers/slices_controller.rb +63 -0
  117. data/app/controllers/static_assets_controller.rb +52 -0
  118. data/app/helpers/admin/admin_helper.rb +63 -0
  119. data/app/helpers/admin/assets_helper.rb +36 -0
  120. data/app/helpers/admin/entries_helper.rb +13 -0
  121. data/app/helpers/admin/site_maps_helper.rb +104 -0
  122. data/app/helpers/assets_helper.rb +64 -0
  123. data/app/helpers/navigation_helper.rb +195 -0
  124. data/app/helpers/pages_helper.rb +119 -0
  125. data/app/models/admin.rb +34 -0
  126. data/app/models/asset.rb +211 -0
  127. data/app/models/attachment.rb +11 -0
  128. data/app/models/layout.rb +44 -0
  129. data/app/models/page.rb +214 -0
  130. data/app/models/placeholder_slice.rb +8 -0
  131. data/app/models/set_page.rb +12 -0
  132. data/app/models/set_slice.rb +57 -0
  133. data/app/models/site_map.rb +24 -0
  134. data/app/models/slice.rb +80 -0
  135. data/app/models/snippet.rb +21 -0
  136. data/app/observers/asset_observer.rb +6 -0
  137. data/app/observers/page_observer.rb +37 -0
  138. data/app/presenters/entry_presenter.rb +17 -0
  139. data/app/presenters/page_presenter.rb +67 -0
  140. data/app/presenters/presenter.rb +9 -0
  141. data/app/presenters/set_page_presenter.rb +2 -0
  142. data/app/views/admin/admins/index.html.erb +26 -0
  143. data/app/views/admin/admins/show.html.erb +27 -0
  144. data/app/views/admin/assets/index.html.erb +1 -0
  145. data/app/views/admin/auth/passwords/edit.html.erb +20 -0
  146. data/app/views/admin/auth/passwords/new.html.erb +14 -0
  147. data/app/views/admin/auth/sessions/_form.html.erb +35 -0
  148. data/app/views/admin/auth/sessions/new.html.erb +14 -0
  149. data/app/views/admin/entries/index.html.erb +32 -0
  150. data/app/views/admin/pages/_breadcrumbs.html.erb +32 -0
  151. data/app/views/admin/pages/_slices.html.erb +27 -0
  152. data/app/views/admin/pages/new.html.erb +14 -0
  153. data/app/views/admin/pages/show.html.erb +50 -0
  154. data/app/views/admin/shared/_asset_storage.html.erb +17 -0
  155. data/app/views/admin/shared/_custom_links.html.erb +1 -0
  156. data/app/views/admin/shared/_custom_navigation.html.erb +1 -0
  157. data/app/views/admin/shared/_navigation.html.erb +5 -0
  158. data/app/views/admin/site_maps/_page_li.html.erb +20 -0
  159. data/app/views/admin/site_maps/_set_page_li.html.erb +23 -0
  160. data/app/views/admin/site_maps/index.html.erb +29 -0
  161. data/app/views/admin/snippets/form.html.erb +12 -0
  162. data/app/views/admin/snippets/index.html.erb +20 -0
  163. data/app/views/admin/snippets/update.html.erb +0 -0
  164. data/app/views/layouts/admin.html.erb +72 -0
  165. data/lib/ext/file_store_cache.rb +18 -0
  166. data/lib/generators/humans/USAGE +8 -0
  167. data/lib/generators/humans/humans_generator.rb +10 -0
  168. data/lib/generators/humans/templates/humans.txt +6 -0
  169. data/lib/generators/slice/USAGE +28 -0
  170. data/lib/generators/slice/slice_generator.rb +123 -0
  171. data/lib/generators/slice/templates/main_fields.hbs +11 -0
  172. data/lib/generators/slice/templates/meta_fields.hbs +11 -0
  173. data/lib/generators/slice/templates/page.rb +19 -0
  174. data/lib/generators/slice/templates/presenter.rb +53 -0
  175. data/lib/generators/slice/templates/set.html.erb +8 -0
  176. data/lib/generators/slice/templates/set_slice.rb +14 -0
  177. data/lib/generators/slice/templates/set_slice_fields.hbs +5 -0
  178. data/lib/generators/slice/templates/show.html.erb +48 -0
  179. data/lib/generators/slice/templates/show_slice.rb +20 -0
  180. data/lib/generators/slice/templates/slice.rb +58 -0
  181. data/lib/generators/slice/templates/slice_fields.hbs +74 -0
  182. data/lib/generators/templates/slices.rb +211 -0
  183. data/lib/mongo_search.rb +84 -0
  184. data/lib/paperclip_validator.rb +5 -0
  185. data/lib/rack_utf8_fix.rb +10 -0
  186. data/lib/sRGB.icc +0 -0
  187. data/lib/set_link_renderer.rb +31 -0
  188. data/lib/slices.rb +68 -0
  189. data/lib/slices/asset/maker.rb +55 -0
  190. data/lib/slices/asset/rename.rb +67 -0
  191. data/lib/slices/available_slices.rb +43 -0
  192. data/lib/slices/cms_form_builder.rb +42 -0
  193. data/lib/slices/config.rb +93 -0
  194. data/lib/slices/container_parser.rb +70 -0
  195. data/lib/slices/generator_macros.rb +36 -0
  196. data/lib/slices/has_attachments.rb +111 -0
  197. data/lib/slices/has_slices.rb +88 -0
  198. data/lib/slices/i18n.rb +6 -0
  199. data/lib/slices/i18n/backend.rb +32 -0
  200. data/lib/slices/i18n_backend.rb +24 -0
  201. data/lib/slices/paperclip.rb +13 -0
  202. data/lib/slices/position_helper.rb +98 -0
  203. data/lib/slices/renderer.rb +52 -0
  204. data/lib/slices/slices_engine.rb +51 -0
  205. data/lib/slices/split_date_time_field.rb +14 -0
  206. data/lib/slices/tasks/assets.rake +35 -0
  207. data/lib/slices/tasks/db.rake +50 -0
  208. data/lib/slices/tasks/seeds.rake +93 -0
  209. data/lib/slices/tasks/validate.rake +62 -0
  210. data/lib/slices/tree.rb +306 -0
  211. data/lib/slices/version.rb +4 -0
  212. data/lib/slices/will_paginate.rb +12 -0
  213. data/lib/slices/will_paginate_mongoid.rb +45 -0
  214. data/lib/standard_tree.rb +193 -0
  215. metadata +483 -0
@@ -0,0 +1,101 @@
1
+ // Responsible for the managing the ui for a single attachment,
2
+ // used within AssetComposerView.
3
+ slices.AttachmentView = Backbone.View.extend({
4
+
5
+ events: {
6
+ 'change' : 'update'
7
+ },
8
+
9
+ tagName: 'li',
10
+
11
+ template: Handlebars.compile(
12
+ '<div class="attachment-thumb"></div>' +
13
+ '<div class="attachment-fields"></div>' +
14
+ '<span data-action="remove" class="remove">Remove</span>'
15
+ ),
16
+
17
+ // Initialize the view.
18
+ initialize: function() {
19
+ _.bindAll(this);
20
+ // Make view and model accessible from el.
21
+ $(this.el).data('view', this);
22
+ $(this.el).data('model', this.model);
23
+ },
24
+
25
+ // Render our template to this.el.
26
+ render: function() {
27
+ $(this.el).html(this.template(this));
28
+ this.renderAssetThumb();
29
+ this.renderFields();
30
+ return this;
31
+ },
32
+
33
+ // If this attachment has an asset, we can render an AssetThumbView.
34
+ // We make sure to switch of the selection functionality.
35
+ renderAssetThumb: function() {
36
+ if (!this.model.get('asset')) return;
37
+
38
+ this.assetThumb = new slices.AssetThumbView({
39
+ model: this.model.get('asset'),
40
+ selectable: false,
41
+ tagName: 'div'
42
+ });
43
+
44
+ this.$('.attachment-thumb').prepend(this.assetThumb.render().el);
45
+ },
46
+
47
+ // We don’t know what extra fields will come on the attachment, so we’ll
48
+ // render this template using the attachment model’s attributes, rather
49
+ // than `this`. Doesn’t provide the usual opportunities for overriding
50
+ // and proxying, but much more flexible.
51
+ renderFields: function() {
52
+ if (!_.isFunction(this.options.fields)) return;
53
+
54
+ this.$('.attachment-fields').
55
+ html(this.options.fields(this.model.attributes)).
56
+ applyDataValues().
57
+ initDataPlugins();
58
+ },
59
+
60
+ // Remove this view and unbind any event handlers.
61
+ remove: function() {
62
+ this.model.unbind('change', this.render);
63
+ $(this.el).remove();
64
+ },
65
+
66
+ // Write contents of fields to our model.
67
+ update: function() {
68
+ this.model.set(this.getValues());
69
+ },
70
+
71
+ // Retrieve values from our fields and return as an object.
72
+ getValues: function() {
73
+ var result = {};
74
+ this.$('[name]').each(function() {
75
+ var field = $(this);
76
+ result[field.attr('name')] = slices.getValue(field);
77
+ });
78
+ return result;
79
+ },
80
+
81
+ // Update the upload progress information, wrapped around the file.
82
+ updateFile: function(attrs) {
83
+ this.assetThumb.updateFile(attrs);
84
+ },
85
+
86
+ // Update the upload progress information so we see happy face, then
87
+ // wait for the thumbnail to load or happyTime, whichever is longer.
88
+ updateFileAndComplete: function(file) {
89
+ this.assetThumb.updateFileAndComplete(file);
90
+ },
91
+
92
+ // Returns the midpoint of the view, relative to the document.
93
+ midPoint: function() {
94
+ return {
95
+ x: $(this.el).offset().left + ($(this.el).width() / 2),
96
+ y: $(this.el).offset().top + ($(this.el).height() / 2)
97
+ }
98
+ }
99
+
100
+ });
101
+
@@ -0,0 +1,198 @@
1
+ // Calendar View
2
+ slices.CalendarView = Backbone.View.extend({
3
+
4
+ // -- Config --
5
+
6
+ events: {
7
+ 'click [data-action="hide"]' : 'hide',
8
+ 'click [data-action="prev-month"]' : 'prevMonth',
9
+ 'click [data-action="next-month"]' : 'nextMonth',
10
+ 'mousedown time' : 'onClickDay'
11
+ },
12
+
13
+ className: 'calendar-modal',
14
+
15
+ template: Handlebars.compile(
16
+ '<span class="background" data-action="hide"></span>' +
17
+ '<div class="calendar">' +
18
+ '<table>' +
19
+ '<thead>' +
20
+ '<tr>' +
21
+ '<td data-action="prev-month">&larr;</td>' +
22
+ '<th class="month" colspan="5">{{monthName}}</th>' +
23
+ '<td data-action="next-month">&rarr;</td>' +
24
+ '</tr>' +
25
+ '<tr>' +
26
+ '<th class="weekday">M</th>' +
27
+ '<th class="weekday">T</th>' +
28
+ '<th class="weekday">W</th>' +
29
+ '<th class="weekday">T</th>' +
30
+ '<th class="weekday">F</th>' +
31
+ '<th class="weekday">S</th>' +
32
+ '<th class="weekday">S</th>' +
33
+ '</tr>' +
34
+ '</thead>' +
35
+ '<tbody>{{{body}}}</tbody>' +
36
+ '</table>' +
37
+ '</div>'
38
+ ),
39
+
40
+ bodyTemplate: Handlebars.compile(
41
+ '{{#each this}}' +
42
+ '<tr>' +
43
+ '{{#each this}}' +
44
+ '<td>' +
45
+ '<time class="{{klass}}" datetime="{{value}}">' +
46
+ '{{date}}' +
47
+ '</time>' +
48
+ '</td>' +
49
+ '{{/each}}' +
50
+ '</tr>' +
51
+ '{{/each}}'
52
+ ),
53
+
54
+ // -- Init --
55
+
56
+ initialize: function() {
57
+ _.bindAll(this);
58
+
59
+ $(document).on('slices:willSubmit', this.remove);
60
+
61
+ this.setValue(this.options.value);
62
+
63
+ this.$el.appendTo('body');
64
+ this.$el.attr('data-state', 'preparing');
65
+ this.$el.hide();
66
+ },
67
+
68
+ // -- Rendering --
69
+
70
+ render: function() {
71
+ this.$el.html(this.template(this));
72
+ this.updatePosition();
73
+ return this;
74
+ },
75
+
76
+ body: function() {
77
+ var data = [],
78
+ today = moment().startOf('day'),
79
+ thisMonth = this.month.clone(),
80
+ nextMonth = thisMonth.clone().add('months', 1),
81
+ firstMonday = thisMonth.clone().day(1),
82
+ finalSunday = nextMonth.clone().day(7),
83
+ date = firstMonday.clone();
84
+
85
+ while (date <= finalSunday) {
86
+ if (date.day() === 1) data.push([]);
87
+
88
+ var obj = {
89
+ date : date.date(),
90
+ value : date.format(),
91
+ klass : []
92
+ };
93
+
94
+ if (date.is(today)) obj.klass.push('today');
95
+ else if (date < thisMonth) obj.klass.push('prev-month');
96
+ else if (date >= nextMonth) obj.klass.push('next-month');
97
+
98
+ if (this.value) {
99
+ if (date.is(this.value.clone().startOf('day'))) obj.klass.push('highlight');
100
+ }
101
+
102
+ obj.klass = obj.klass.join(' ');
103
+
104
+ data[data.length - 1].push(obj);
105
+
106
+ date.add('days', 1);
107
+ }
108
+
109
+ return this.bodyTemplate(data);
110
+ },
111
+
112
+ monthName: function() {
113
+ return this.month.format('MMMM YYYY');
114
+ },
115
+
116
+ // -- Event Handlers --
117
+
118
+ onClickDay: function(event) {
119
+ var day = $(event.target),
120
+ value = moment(day.attr('datetime'));
121
+
122
+ if (this.value) {
123
+ value.hours(this.value.hours()).minutes(this.value.minutes());
124
+ }
125
+
126
+ this.setValue(value);
127
+ this.render();
128
+ this.options.onChange && this.options.onChange();
129
+ },
130
+
131
+ // -- Actions --
132
+
133
+ show: function() {
134
+ this.resetMonth();
135
+ this.$el.show();
136
+ this.render();
137
+ this.updatePosition();
138
+ this.$el.attr('data-state', 'showing');
139
+ },
140
+
141
+ hide: function() {
142
+ this.$el.attr('data-state', 'hiding');
143
+
144
+ var self = this;
145
+ setTimeout(function() { self.$el.hide() }, 250);
146
+ },
147
+
148
+ toggle: function() {
149
+ switch (this.$el.data('state')) {
150
+ case 'preparing':
151
+ case 'hiding':
152
+ this.show(); break;
153
+ case 'showing':
154
+ this.hide(); break;
155
+ }
156
+ },
157
+
158
+ prevMonth: function() {
159
+ this.month.subtract('months', 1);
160
+ this.render();
161
+ },
162
+
163
+ nextMonth: function() {
164
+ this.month.add('months', 1);
165
+ this.render();
166
+ },
167
+
168
+ setValue: function(newValue) {
169
+ this.value = moment(newValue);
170
+ this.resetMonth();
171
+ },
172
+
173
+ resetMonth: function() {
174
+ this.month = (this.value || moment()).clone().startOf('month');
175
+ },
176
+
177
+ remove: function() {
178
+ this.$el.remove();
179
+ },
180
+
181
+ // -- Helpers --
182
+
183
+ updatePosition: function() {
184
+ var calendar = this.$el.find('.calendar'),
185
+ anchor = this.anchor,
186
+ offset = anchor.offset();
187
+
188
+ offset.top += anchor.outerHeight();
189
+ offset.top = offset.top + 'px';
190
+
191
+ offset.left += anchor.outerWidth() / 2;
192
+ offset.left -= calendar.outerWidth() / 2;
193
+ offset.left = offset.left + 'px';
194
+
195
+ calendar.css(offset);
196
+ }
197
+
198
+ });
@@ -0,0 +1,54 @@
1
+ slices.ComposerItemView = Backbone.View.extend({
2
+
3
+ tagName: 'li',
4
+ className: 'composer-item',
5
+
6
+ template: Handlebars.compile(
7
+ '<div class="item-fields"></div>' +
8
+ '<span data-role="drag-handle"></span>' +
9
+ '<span data-action="remove">Remove</span>'
10
+ ),
11
+
12
+ events: {
13
+ 'change': 'update'
14
+ },
15
+
16
+ initialize: function() {
17
+ _.bindAll(this);
18
+ this.$el.data('model', this.model);
19
+ },
20
+
21
+ render: function() {
22
+ $(this.el).html(this.template(this));
23
+ this.renderFields();
24
+ return this;
25
+ },
26
+
27
+ renderFields: function() {
28
+ if (!_.isFunction(this.options.fields)) return;
29
+
30
+ this.$('.item-fields')
31
+ .html(this.options.fields(this.model.attributes))
32
+ .applyDataValues()
33
+ .initDataPlugins();
34
+ },
35
+
36
+ remove: function() {
37
+ this.model.unbind('change', this.render);
38
+ this.$el.remove();
39
+ },
40
+
41
+ update: function() {
42
+ this.model.set(this.getValues());
43
+ },
44
+
45
+ getValues: function() {
46
+ var result = {};
47
+ this.$('[name]').each(function() {
48
+ var field = $(this);
49
+ result[field.attr('name')] = field.val();
50
+ });
51
+ return result;
52
+ }
53
+
54
+ });
@@ -0,0 +1,130 @@
1
+ slices.ComposerView = Backbone.View.extend({
2
+
3
+ className: 'composer',
4
+
5
+ template: Handlebars.compile(
6
+ '<ol class="composer-item-list"></ol>' +
7
+ '<div class="composer-actions">' +
8
+ '<button data-action="add">{{addLabel}}</button>' +
9
+ '</div>'
10
+ ),
11
+
12
+ events: {
13
+ 'click [data-action="add"]' : 'didClickAdd',
14
+ 'click [data-action="remove"]' : 'didClickRemove'
15
+ },
16
+
17
+ addLabel: 'Add An Item',
18
+
19
+ broadcastChanges: true,
20
+ views: {},
21
+
22
+ initialize: function() {
23
+ _.bindAll(this);
24
+
25
+ this.addLabel = this.options.addLabel || this.addLabel;
26
+
27
+ this.collection = new slices.ComposerItemCollection(this.options.value);
28
+ this.collection.bind('add' , this.addItem);
29
+ this.collection.bind('remove' , this.removeItem);
30
+ this.collection.bind('change' , this.update);
31
+ this.collection.bind('reset' , this.update);
32
+
33
+ if (this.options.autoAttach) _.defer(this.attach);
34
+ },
35
+
36
+ // -- Rendering --
37
+
38
+ placeholder: function() {
39
+ return Handlebars.compile('<div id="placeholder-{{id}}"></div>')(this);
40
+ },
41
+
42
+ attach: function() {
43
+ $('#placeholder-' + this.id).replaceWith(this.el);
44
+ this.render();
45
+ },
46
+
47
+ render: function() {
48
+ this.broadcastChanges = false;
49
+ this.$el.html(this.template(this));
50
+ this.collection.each(this.addItem);
51
+ this.makeSortable();
52
+ this.update();
53
+ this.broadcastChanges = true;
54
+ return this;
55
+ },
56
+
57
+ makeSortable: function() {
58
+ var list = this.$('.composer-item-list');
59
+
60
+ list.sortable({
61
+ handle: '[data-role="drag-handle"]',
62
+ scroll: false,
63
+
64
+ beforeStart: _.bind(function(e, ui) {
65
+ list.freezeHeight();
66
+ window.autoscroll.start();
67
+ }, this),
68
+
69
+ stop: _.bind(function(e, ui) {
70
+ list.thawHeight();
71
+ window.autoscroll.stop();
72
+ }, this),
73
+
74
+ update: this.updateOnSort
75
+ });
76
+ },
77
+
78
+ updateOnSort: function() {
79
+ var items = this.$('.composer-item-list').children();
80
+
81
+ var newOrder = items.map(function() {
82
+ return $(this).data('model');
83
+ }).get();
84
+
85
+ this.collection.reset(newOrder);
86
+ },
87
+
88
+ update: function() {
89
+ var value = this.collection.toJSON();
90
+ this.$el.data('computed-value', value);
91
+ this.$el[this.collection.isEmpty() ? 'removeClass' : 'addClass']('not-empty');
92
+ if (this.broadcastChanges) this.$el.trigger('change');
93
+ },
94
+
95
+ addItem: function(item, collection, options) {
96
+ var view = new slices.ComposerItemView({
97
+ fields: this.options.fields,
98
+ model: item
99
+ });
100
+
101
+ this.$('.composer-item-list').append(view.el);
102
+
103
+ view.render();
104
+ this.views[item.cid] = view;
105
+ this.update();
106
+ },
107
+
108
+ removeItem: function(item) {
109
+ var view = this.views[item.cid];
110
+ view.remove();
111
+ delete this.views[item.cid];
112
+ this.update();
113
+ },
114
+
115
+ didClickAdd: function(e) {
116
+ e.preventDefault(); e.stopImmediatePropagation();
117
+ this.collection.add({});
118
+ },
119
+
120
+ didClickRemove: function(e) {
121
+ e.preventDefault(); e.stopImmediatePropagation();
122
+
123
+ var button = $(e.target),
124
+ view = button.closest('li'),
125
+ item = view.data('model');
126
+
127
+ this.collection.remove(item);
128
+ }
129
+
130
+ });