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,48 @@
1
+ // Default slice-preview helper. If attachments are present it will emit
2
+ // 'n item(s)', otherwise it will simply emit the content of any textarea
3
+ // or text input.
4
+ //
5
+ // This method has been kept purposefully simple to encourage custom
6
+ // preview helpers.
7
+ //
8
+ // Example:
9
+ //
10
+ // slices.defaultSlicePreview.call($('.slice-content'))
11
+ //
12
+ slices.defaultSlicePreview = function() {
13
+ if (this.find('.attachment-list').length) {
14
+ var count = this.find('.attachment-list li').length;
15
+ return count + (count === 1 ? ' item' : ' items');
16
+ } else {
17
+ var html = this.find('textarea, input[type="text"]')
18
+ .map(function() { return $(this).val() })
19
+ .get()
20
+ .join(' ');
21
+
22
+ return $('<div />').html(html).text();
23
+ }
24
+ }
25
+
26
+ // Define custom previews for slices using this helper.
27
+ //
28
+ // In true jQuery style, the context of the method `this` is the slice-content
29
+ // element (in this case wrapped in a jQuery object).
30
+ //
31
+ // If the helper is empty, the default slice preview helper will be used.
32
+ //
33
+ // Example:
34
+ //
35
+ // {{#slicePreview}}
36
+ // return this.find('input').val().toUpperCase();
37
+ // {{/slicePreview}}
38
+ //
39
+ Handlebars.registerHelper('slicePreview', function(options) {
40
+ try {
41
+ var proc = options.fn && options.fn();
42
+ if (proc.match(/\S/)) window.customSlicePreview = new Function(proc);
43
+ } catch(error) {
44
+ console.error('This slicePreview caused an error:\n' + proc);
45
+ console.error(error);
46
+ }
47
+ });
48
+
@@ -0,0 +1,73 @@
1
+ var Tagging = {
2
+
3
+ detect: function() {
4
+ var self = this;
5
+ $('.multi-tag-list').each(function(index, tag_list) {
6
+ self.setup_multi(tag_list);
7
+ });
8
+ $('.single-tag-list').each(function(index, tag_list) {
9
+ self.setup_single(tag_list);
10
+ });
11
+ },
12
+
13
+ setup_single: function(tag_list) {
14
+ var self = this;
15
+ var $tag_list = $(tag_list);
16
+ if ($tag_list.length) {
17
+ var $tag_input = $tag_list.parent().find('input');
18
+ $tag_list.children('li').click(function(event) {
19
+ var $el = $(event.target)
20
+ var tagvalue = $.trim($el.text()).replace(/\,\s*$/,'');
21
+ $tag_input.val(tagvalue).trigger('change');
22
+ self.mark_active_tags($tag_list, $tag_input.val());
23
+ });
24
+
25
+ self.mark_active_tags($tag_list, $tag_input.val());
26
+ }
27
+ },
28
+
29
+ setup_multi: function(tag_list) {
30
+ var self = this;
31
+ var $tag_list = $(tag_list)
32
+ if ($tag_list.length) {
33
+ var $tag_input = $tag_list.parent().find('input');
34
+
35
+ $tag_list.children('li').click(function(event) {
36
+ var $el = $(event.target);
37
+ var tagvalue = $.trim($el.text()).replace(/\,\s*$/, '');
38
+ var regex = new RegExp(tagvalue + ',?')
39
+
40
+ if ($el.hasClass('on')) { //remove the tag
41
+ var new_value = $tag_input.val().replace(regex, '');
42
+ new_value = self.strip_whitespace(new_value);
43
+ $tag_input.val(new_value).trigger('change');
44
+ $el.removeClass('on');
45
+ } else { //add the tag
46
+ var new_value = ($tag_input.val() + ', ' + tagvalue);
47
+ new_value = self.strip_whitespace(new_value);
48
+ $tag_input.val(new_value).trigger('change');
49
+ $el.addClass('on');
50
+ }
51
+ });
52
+ self.mark_active_tags($tag_list, $tag_input.val());
53
+ }
54
+ },
55
+
56
+ mark_active_tags: function($tag_list, current_tags) {
57
+ var self = this;
58
+ $tag_list.children('li').each(function(index, tag) {
59
+ var $tag = $(tag);
60
+ var regex = new RegExp(self.strip_whitespace($tag.text()));
61
+ if (current_tags.match(regex)) {
62
+ $tag.addClass('on');
63
+ } else {
64
+ $tag.removeClass('on');
65
+ }
66
+ });
67
+ },
68
+
69
+ strip_whitespace: function(string) {
70
+ string = string.replace(/[\,\s\s+]*$/g,'').replace(/^[\,\s+]*/g,'').replace(/\s\s+/,' ');
71
+ return string;
72
+ }
73
+ }
@@ -0,0 +1,17 @@
1
+ // Create a token field for a given field and source on the model.
2
+ //
3
+ // Example:
4
+ //
5
+ // {{tokenField field="categories" options="available_categories"}}
6
+ //
7
+ Handlebars.registerHelper('tokenField', function(options) {
8
+ var view = new slices.TokenFieldView({
9
+ id : slices.fieldId(this, options.hash.field),
10
+ values : this[options.hash.field],
11
+ options : options.hash.options && this[options.hash.options],
12
+ singular : options.hash.singular === 'true',
13
+ autoAttach : true
14
+ });
15
+
16
+ return new Handlebars.SafeString(view.placeholder());
17
+ });
@@ -0,0 +1,5 @@
1
+ slices.UPLOAD_ICONS = {
2
+ <% %w(thinking happy sad).each do |name| %>
3
+ '<%= name %>' : '<%= asset_path("slices/icon_upload_#{ name }") %>.png',
4
+ <% end %>
5
+ };
@@ -0,0 +1,127 @@
1
+ // Returns a freshly prepared slices.Uploader instance.
2
+ //
3
+ // new slices.Uploader({
4
+ // button : '#my-button',
5
+ // drop : '#my-drop-target',
6
+ // params : {}
7
+ // });
8
+ //
9
+ 'use strict';
10
+
11
+ slices.Uploader = function(options) {
12
+ this.options = $.extend({}, this.options, options);
13
+ this.initialize();
14
+ }
15
+ $.extend(slices.Uploader.prototype, Backbone.Events, {
16
+
17
+ // Default options
18
+ options: {
19
+ url: '/admin/assets.json'
20
+ },
21
+
22
+ initialize: function() {
23
+ this.files = [];
24
+
25
+ this.button = $(this.options.button);
26
+ this.input = this.createInput();
27
+ this.drop = $(this.options.drop);
28
+
29
+ this.configureCSRF();
30
+ this.observeEvents();
31
+ },
32
+
33
+ configureCSRF: function() {
34
+ var csrfParam = $('meta[name="csrf-param"]').attr('content'),
35
+ csrfToken = $('meta[name="csrf-token"]').attr('content');
36
+
37
+ this.options.params = this.options.params || {};
38
+ this.options.params[csrfParam] = csrfToken;
39
+ },
40
+
41
+ createInput: function() {
42
+ return $('<input />', {
43
+ type : 'file',
44
+ multiple : 'multiple',
45
+ style : 'position: absolute; visibility: hidden'
46
+ });
47
+ },
48
+
49
+ observeEvents: function() {
50
+ var self = this;
51
+
52
+ this.input.on('change', function(e) { self.onInputChange(e) });
53
+ this.button.on('click', function(e) { self.onButtonClick(e) });
54
+ this.drop.on('dragenter', false);
55
+ this.drop.on('dragover', false);
56
+ this.drop.on('drop', function(e) { self.onDrop(e) });
57
+ },
58
+
59
+ onInputChange: function(event) {
60
+ this.addFiles(this.input.prop('files'));
61
+ this.input.val('');
62
+ },
63
+
64
+ onButtonClick: function(event) {
65
+ event.preventDefault();
66
+ this.input.click();
67
+ },
68
+
69
+ onDrop: function(event) {
70
+ event.preventDefault();
71
+ this.addFiles(event.originalEvent.dataTransfer.files);
72
+ },
73
+
74
+ addFiles: function(fileList) {
75
+ var self = this;
76
+ _(fileList).each(function(browserFile) {
77
+ var file = new slices.fileClass({
78
+ uploader : this,
79
+ uploadOptions : this.uploadOptions(),
80
+ browserFile : browserFile
81
+ });
82
+ self.files.push(file);
83
+ }, this);
84
+
85
+ this.trigger('filesAdded', { uploader: this, files: this.files });
86
+ },
87
+
88
+ uploadOptions: function() {
89
+ return {
90
+ url: this.options.url,
91
+ params: this.options.params
92
+ };
93
+ },
94
+
95
+ start: function() {
96
+ // Only start uploading if there are files to upload...
97
+ // and there is not a current file
98
+ if (this.files.length == 0 || this.file != undefined) {
99
+ return false;
100
+ }
101
+
102
+ // Take the first file from the list and start the upload
103
+ this.file = this.files.shift();
104
+ this.file.start();
105
+ },
106
+
107
+ onprogress: function(event) {
108
+ this.trigger('fileProgress', event);
109
+ },
110
+
111
+ onload: function(event) {
112
+ this.trigger('fileUploaded', event);
113
+ this.file = undefined;
114
+ this.start();
115
+ },
116
+
117
+ onfail: function(event) {
118
+ this.trigger('fileError', event);
119
+ this.file = undefined;
120
+ this.start();
121
+ },
122
+
123
+ toString: function() {
124
+ return 'slices.Uploader';
125
+ }
126
+
127
+ });
@@ -0,0 +1,29 @@
1
+ // Represents assets in a few different states. Simple assets as they exist
2
+ // in the Asset Library; Assets in use within a Slice; and Assets in the
3
+ // process of being uploaded.
4
+ slices.Asset = Backbone.Model.extend({
5
+
6
+ urlRoot: '/admin/assets',
7
+
8
+ // Filtered set of attrs
9
+ toJSON: function() {
10
+ return { asset: {
11
+ name: this.get('name'),
12
+ tags: this.get('tags')
13
+ }};
14
+ },
15
+
16
+ isImage: function() {
17
+ return (this.get('file_content_type') || '').indexOf('image') === 0;
18
+ },
19
+
20
+ // Okay, so this method involves a bit of reaching into the prototype
21
+ // cookie jar, as Backbone doesn’t really have a way to subclass these
22
+ // fundamental methods of models.
23
+ destroy: function(options) {
24
+ this.trigger('destroying');
25
+ Backbone.Model.prototype.destroy.apply(this);
26
+ }
27
+
28
+ });
29
+
@@ -0,0 +1,41 @@
1
+ // This is our connection to the Asset library.
2
+ slices.AssetCollection = Backbone.Collection.extend({
3
+
4
+ model: slices.Asset,
5
+
6
+ url: '/admin/assets',
7
+
8
+ initialize: function() {
9
+ this.bind('add', this.afterAdd);
10
+ this.bind('remove', this.afterRemove);
11
+ },
12
+
13
+ // This request responds with a page worth of results in `items`.
14
+ parse: function(response) {
15
+ this.currentPage = response.current_page;
16
+ this.perPage = response.per_page;
17
+ this.totalEntries = response.total_entries
18
+ this.totalPages = response.total_pages;
19
+ return response.items;
20
+ },
21
+
22
+ // Returns true if there are more pages in the current criteria.
23
+ hasMorePages: function() {
24
+ return this.currentPage < this.totalPages;
25
+ },
26
+
27
+ // After an asset is added, we need to update the non-standard
28
+ // totalEntries count.
29
+ afterAdd: function() {
30
+ this.totalEntires += 1;
31
+ this.totalPages = Math.ceil(this.totalEntries / this.perPage);
32
+ },
33
+
34
+ // Similarly, we need to update the count on remove.
35
+ afterRemove: function() {
36
+ this.totalEntries -= 1;
37
+ this.totalPages = Math.ceil(this.totalEntries / this.perPage);
38
+ }
39
+
40
+ });
41
+
@@ -0,0 +1,29 @@
1
+ // Represents an attachment object, which will contain an asset and any other
2
+ // fields the attachment is decorated with.
3
+ slices.Attachment = Backbone.Model.extend({
4
+
5
+ // Initialize the attachment. Only thing of interest here is handling
6
+ // the presence of files.
7
+ initialize: function() {
8
+ if (this.get('file')) {
9
+ this.get('file').attachment = this;
10
+ this.updateFileStatus();
11
+ }
12
+ if (this.get('asset') && this.get('asset').constructor !== slices.Asset) {
13
+ this.set({ asset: new slices.Asset(this.get('asset')) });
14
+ }
15
+ },
16
+
17
+ // Trigger normal change event when things happen with the file.
18
+ updateFileStatus: function() {
19
+ this.trigger('change');
20
+ },
21
+
22
+ toJSON: function() {
23
+ var attrs = _.clone(this.attributes);
24
+ delete attrs.asset;
25
+ return attrs;
26
+ }
27
+
28
+ });
29
+
@@ -0,0 +1,7 @@
1
+ // Represents a collection of attachments.
2
+ slices.AttachmentCollection = Backbone.Collection.extend({
3
+
4
+ model: slices.Attachment,
5
+
6
+ });
7
+
@@ -0,0 +1 @@
1
+ slices.ComposerItem = Backbone.Model.extend();
@@ -0,0 +1,3 @@
1
+ slices.ComposerItemCollection = Backbone.Collection.extend({
2
+ model: slices.ComposerItem
3
+ });
@@ -0,0 +1,103 @@
1
+ slices.FILE_QUEUED = 'queued';
2
+ slices.FILE_UPLOADING = 'uploading';
3
+ slices.FILE_FAILED = 'failed';
4
+ slices.FILE_DONE = 'done';
5
+
6
+ slices.File = Backbone.Model.extend({
7
+
8
+ defaults: {
9
+ status: slices.FILE_QUEUED
10
+ },
11
+
12
+ initialize: function() {
13
+ this.uploader = this.get('uploader');
14
+ this.url = this.get('uploadOptions').url;
15
+ this.params = this.get('uploadOptions').params;
16
+ },
17
+
18
+ start: function() {
19
+ this.upload();
20
+ },
21
+
22
+ status: function() {
23
+ switch(this.get('status')) {
24
+ case slices.FILE_QUEUED:
25
+ return 'queued';
26
+ case slices.FILE_UPLOADING:
27
+ if (this.progress() < 1) return 'uploading';
28
+ else return 'processing';
29
+ case slices.FILE_FAILED:
30
+ return 'failed';
31
+ case slices.FILE_DONE:
32
+ return 'done';
33
+ default:
34
+ return 'queued';
35
+ }
36
+ },
37
+
38
+ progress: function() {
39
+ return this.get('loaded') / this.get('total');
40
+ },
41
+
42
+ formDataWithOptions: function(options) {
43
+ var formData = new FormData();
44
+ for (var i in options) {
45
+ formData.append(i, options[i])
46
+ }
47
+ return formData;
48
+ },
49
+
50
+ formData: function() {
51
+ var formData = this.formDataWithOptions(this.params);
52
+ formData.append('file', this.get('browserFile'));
53
+ return formData;
54
+ },
55
+
56
+ upload: function() {
57
+ var self = this,
58
+ url = this.url,
59
+ data = this.formData();
60
+
61
+ this.uploadWithOptions(url, data, {
62
+ progress: function(xhr, progress) { self.onProgress(progress) },
63
+ success: function (response) { self.onSuccess(response) },
64
+ error: function (response) { self.onError(response) }
65
+ });
66
+ },
67
+
68
+ uploadWithOptions: function(url, data, callbacks) {
69
+ var jqXHR = $.ajax({
70
+ url : url,
71
+ data : data,
72
+ cache : false,
73
+ processData : false,
74
+ type : 'POST',
75
+ contentType : false,
76
+ progress : callbacks.progress,
77
+ success : callbacks.success,
78
+ error : callbacks.error
79
+ });
80
+ },
81
+
82
+ onProgress: function(progress) {
83
+ if (!progress.lengthComputable) return;
84
+
85
+ this.set({
86
+ status: slices.FILE_UPLOADING,
87
+ loaded: progress.loaded,
88
+ total: progress.total
89
+ });
90
+ },
91
+
92
+ onSuccess: function(response) {
93
+ this.set('status', slices.FILE_DONE);
94
+ this.uploader.onload({ file: this, response: response });
95
+ },
96
+
97
+ onError: function(response) {
98
+ this.set('status', slices.FILE_FAILED);
99
+ this.uploader.onfail({ file: this, response: response });
100
+ }
101
+
102
+ });
103
+