slices 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG.md +3 -0
- data/README.md +51 -0
- data/Rakefile +9 -0
- data/app/assets/images/slices/ajax-loader.gif +0 -0
- data/app/assets/images/slices/asset-background.png +0 -0
- data/app/assets/images/slices/asset-spinner.gif +0 -0
- data/app/assets/images/slices/bg_header.gif +0 -0
- data/app/assets/images/slices/black-Linen.png +0 -0
- data/app/assets/images/slices/calendar.svg +68 -0
- data/app/assets/images/slices/chosen-sprite.png +0 -0
- data/app/assets/images/slices/drag-handle.svg +9 -0
- data/app/assets/images/slices/icon_admins.png +0 -0
- data/app/assets/images/slices/icon_app.png +0 -0
- data/app/assets/images/slices/icon_assets.png +0 -0
- data/app/assets/images/slices/icon_collapse.png +0 -0
- data/app/assets/images/slices/icon_drag.png +0 -0
- data/app/assets/images/slices/icon_files.png +0 -0
- data/app/assets/images/slices/icon_generic_file.png +0 -0
- data/app/assets/images/slices/icon_images.png +0 -0
- data/app/assets/images/slices/icon_padlock.png +0 -0
- data/app/assets/images/slices/icon_page.png +0 -0
- data/app/assets/images/slices/icon_search.png +0 -0
- data/app/assets/images/slices/icon_set-link.png +0 -0
- data/app/assets/images/slices/icon_set.png +0 -0
- data/app/assets/images/slices/icon_sitemap.png +0 -0
- data/app/assets/images/slices/icon_snippets.png +0 -0
- data/app/assets/images/slices/icon_template.jpg +0 -0
- data/app/assets/images/slices/icon_upload_happy.png +0 -0
- data/app/assets/images/slices/icon_upload_sad.png +0 -0
- data/app/assets/images/slices/icon_upload_thinking.png +0 -0
- data/app/assets/images/slices/noise.png +0 -0
- data/app/assets/images/slices/sitemap_icon_ghost.png +0 -0
- data/app/assets/images/slices/sitemap_icon_home.png +0 -0
- data/app/assets/images/slices/sitemap_icon_page.png +0 -0
- data/app/assets/images/slices/sitemap_icon_set_page.png +0 -0
- data/app/assets/images/slices/sitemap_icon_virtual_page.png +0 -0
- data/app/assets/images/slices/sitemap_overlay.png +0 -0
- data/app/assets/images/slices/spinner.gif +0 -0
- data/app/assets/images/slices/trash.png +0 -0
- data/app/assets/javascripts/admin.js.erb +18 -0
- data/app/assets/javascripts/slices/app/backbones/admins.js +114 -0
- data/app/assets/javascripts/slices/app/backbones/entries.js +172 -0
- data/app/assets/javascripts/slices/app/backbones/generic.js +101 -0
- data/app/assets/javascripts/slices/app/backbones/snippets.js +113 -0
- data/app/assets/javascripts/slices/app/helpers/assets.js +61 -0
- data/app/assets/javascripts/slices/app/helpers/breadcrumbs.js +30 -0
- data/app/assets/javascripts/slices/app/helpers/composer.js +26 -0
- data/app/assets/javascripts/slices/app/helpers/date_field.js +16 -0
- data/app/assets/javascripts/slices/app/helpers/get_value.js +31 -0
- data/app/assets/javascripts/slices/app/helpers/icon_upload_names.js.erb +5 -0
- data/app/assets/javascripts/slices/app/helpers/layout.js +20 -0
- data/app/assets/javascripts/slices/app/helpers/sitemap.js +150 -0
- data/app/assets/javascripts/slices/app/helpers/slice_preview.js +48 -0
- data/app/assets/javascripts/slices/app/helpers/tagging.js +73 -0
- data/app/assets/javascripts/slices/app/helpers/token_field.js +17 -0
- data/app/assets/javascripts/slices/app/helpers/upload_icons.js.erb +5 -0
- data/app/assets/javascripts/slices/app/helpers/uploader.js +127 -0
- data/app/assets/javascripts/slices/app/models/asset.js +29 -0
- data/app/assets/javascripts/slices/app/models/asset_collection.js +41 -0
- data/app/assets/javascripts/slices/app/models/attachment.js +29 -0
- data/app/assets/javascripts/slices/app/models/attachment_collection.js +7 -0
- data/app/assets/javascripts/slices/app/models/composer_item.js +1 -0
- data/app/assets/javascripts/slices/app/models/composer_item_collection.js +3 -0
- data/app/assets/javascripts/slices/app/models/file.js +103 -0
- data/app/assets/javascripts/slices/app/models/page.js +186 -0
- data/app/assets/javascripts/slices/app/models/s3_file.js +64 -0
- data/app/assets/javascripts/slices/app/slices.js +661 -0
- data/app/assets/javascripts/slices/app/views/asset_editor_view.js.erb +209 -0
- data/app/assets/javascripts/slices/app/views/asset_library_view.js +720 -0
- data/app/assets/javascripts/slices/app/views/asset_thumb_view.js.erb +191 -0
- data/app/assets/javascripts/slices/app/views/attachment_composer_view.js +350 -0
- data/app/assets/javascripts/slices/app/views/attachment_view.js +101 -0
- data/app/assets/javascripts/slices/app/views/calendar_view.js +198 -0
- data/app/assets/javascripts/slices/app/views/composer_item_view.js +54 -0
- data/app/assets/javascripts/slices/app/views/composer_view.js +130 -0
- data/app/assets/javascripts/slices/app/views/date_field_view.js +177 -0
- data/app/assets/javascripts/slices/app/views/file_view.js +142 -0
- data/app/assets/javascripts/slices/app/views/token_field_view.js +253 -0
- data/app/assets/javascripts/slices/lib/freeze.js +14 -0
- data/app/assets/javascripts/slices/lib/human_file_size.js +16 -0
- data/app/assets/javascripts/slices/lib/json_patch.js +9 -0
- data/app/assets/javascripts/slices/lib/moment.js +47 -0
- data/app/assets/javascripts/slices/lib/plugins.js +101 -0
- data/app/assets/javascripts/slices/lib/sortable.js +14 -0
- data/app/assets/javascripts/slices/slices.js +27 -0
- data/app/assets/javascripts/slices/vendor/autoscroll.js +188 -0
- data/app/assets/javascripts/slices/vendor/backbone.js +38 -0
- data/app/assets/javascripts/slices/vendor/handlebars.js +1920 -0
- data/app/assets/javascripts/slices/vendor/jqmodal.js +69 -0
- data/app/assets/javascripts/slices/vendor/jquery-ui.js +274 -0
- data/app/assets/javascripts/slices/vendor/jquery-ui_nested-sortable.js +357 -0
- data/app/assets/javascripts/slices/vendor/jquery.ajaxprogress.js +76 -0
- data/app/assets/javascripts/slices/vendor/jquery.js +2 -0
- data/app/assets/javascripts/slices/vendor/livefield.js +459 -0
- data/app/assets/javascripts/slices/vendor/moment.js +6 -0
- data/app/assets/javascripts/slices/vendor/rails.js +315 -0
- data/app/assets/javascripts/slices/vendor/underscore-string.js +1 -0
- data/app/assets/javascripts/slices/vendor/underscore.js +5 -0
- data/app/assets/stylesheets/admin.css +1 -0
- data/app/assets/stylesheets/slices/admin.css.erb +2237 -0
- data/app/assets/stylesheets/slices/reset_html5.css +106 -0
- data/app/assets/stylesheets/slices/slices.css +7 -0
- data/app/controllers/admin/admin_controller.rb +10 -0
- data/app/controllers/admin/admins_controller.rb +76 -0
- data/app/controllers/admin/assets_controller.rb +53 -0
- data/app/controllers/admin/auth/omniauth_callbacks_controller.rb +15 -0
- data/app/controllers/admin/auth/passwords_controller.rb +4 -0
- data/app/controllers/admin/auth/sessions_controller.rb +4 -0
- data/app/controllers/admin/entries_controller.rb +88 -0
- data/app/controllers/admin/page_search_controller.rb +12 -0
- data/app/controllers/admin/pages_controller.rb +103 -0
- data/app/controllers/admin/site_maps_controller.rb +15 -0
- data/app/controllers/admin/snippets_controller.rb +33 -0
- data/app/controllers/application_controller.rb +4 -0
- data/app/controllers/pages_controller.rb +45 -0
- data/app/controllers/slices_controller.rb +63 -0
- data/app/controllers/static_assets_controller.rb +52 -0
- data/app/helpers/admin/admin_helper.rb +63 -0
- data/app/helpers/admin/assets_helper.rb +36 -0
- data/app/helpers/admin/entries_helper.rb +13 -0
- data/app/helpers/admin/site_maps_helper.rb +104 -0
- data/app/helpers/assets_helper.rb +64 -0
- data/app/helpers/navigation_helper.rb +195 -0
- data/app/helpers/pages_helper.rb +119 -0
- data/app/models/admin.rb +34 -0
- data/app/models/asset.rb +211 -0
- data/app/models/attachment.rb +11 -0
- data/app/models/layout.rb +44 -0
- data/app/models/page.rb +214 -0
- data/app/models/placeholder_slice.rb +8 -0
- data/app/models/set_page.rb +12 -0
- data/app/models/set_slice.rb +57 -0
- data/app/models/site_map.rb +24 -0
- data/app/models/slice.rb +80 -0
- data/app/models/snippet.rb +21 -0
- data/app/observers/asset_observer.rb +6 -0
- data/app/observers/page_observer.rb +37 -0
- data/app/presenters/entry_presenter.rb +17 -0
- data/app/presenters/page_presenter.rb +67 -0
- data/app/presenters/presenter.rb +9 -0
- data/app/presenters/set_page_presenter.rb +2 -0
- data/app/views/admin/admins/index.html.erb +26 -0
- data/app/views/admin/admins/show.html.erb +27 -0
- data/app/views/admin/assets/index.html.erb +1 -0
- data/app/views/admin/auth/passwords/edit.html.erb +20 -0
- data/app/views/admin/auth/passwords/new.html.erb +14 -0
- data/app/views/admin/auth/sessions/_form.html.erb +35 -0
- data/app/views/admin/auth/sessions/new.html.erb +14 -0
- data/app/views/admin/entries/index.html.erb +32 -0
- data/app/views/admin/pages/_breadcrumbs.html.erb +32 -0
- data/app/views/admin/pages/_slices.html.erb +27 -0
- data/app/views/admin/pages/new.html.erb +14 -0
- data/app/views/admin/pages/show.html.erb +50 -0
- data/app/views/admin/shared/_asset_storage.html.erb +17 -0
- data/app/views/admin/shared/_custom_links.html.erb +1 -0
- data/app/views/admin/shared/_custom_navigation.html.erb +1 -0
- data/app/views/admin/shared/_navigation.html.erb +5 -0
- data/app/views/admin/site_maps/_page_li.html.erb +20 -0
- data/app/views/admin/site_maps/_set_page_li.html.erb +23 -0
- data/app/views/admin/site_maps/index.html.erb +29 -0
- data/app/views/admin/snippets/form.html.erb +12 -0
- data/app/views/admin/snippets/index.html.erb +20 -0
- data/app/views/admin/snippets/update.html.erb +0 -0
- data/app/views/layouts/admin.html.erb +72 -0
- data/lib/ext/file_store_cache.rb +18 -0
- data/lib/generators/humans/USAGE +8 -0
- data/lib/generators/humans/humans_generator.rb +10 -0
- data/lib/generators/humans/templates/humans.txt +6 -0
- data/lib/generators/slice/USAGE +28 -0
- data/lib/generators/slice/slice_generator.rb +123 -0
- data/lib/generators/slice/templates/main_fields.hbs +11 -0
- data/lib/generators/slice/templates/meta_fields.hbs +11 -0
- data/lib/generators/slice/templates/page.rb +19 -0
- data/lib/generators/slice/templates/presenter.rb +53 -0
- data/lib/generators/slice/templates/set.html.erb +8 -0
- data/lib/generators/slice/templates/set_slice.rb +14 -0
- data/lib/generators/slice/templates/set_slice_fields.hbs +5 -0
- data/lib/generators/slice/templates/show.html.erb +48 -0
- data/lib/generators/slice/templates/show_slice.rb +20 -0
- data/lib/generators/slice/templates/slice.rb +58 -0
- data/lib/generators/slice/templates/slice_fields.hbs +74 -0
- data/lib/generators/templates/slices.rb +211 -0
- data/lib/mongo_search.rb +84 -0
- data/lib/paperclip_validator.rb +5 -0
- data/lib/rack_utf8_fix.rb +10 -0
- data/lib/sRGB.icc +0 -0
- data/lib/set_link_renderer.rb +31 -0
- data/lib/slices.rb +68 -0
- data/lib/slices/asset/maker.rb +55 -0
- data/lib/slices/asset/rename.rb +67 -0
- data/lib/slices/available_slices.rb +43 -0
- data/lib/slices/cms_form_builder.rb +42 -0
- data/lib/slices/config.rb +93 -0
- data/lib/slices/container_parser.rb +70 -0
- data/lib/slices/generator_macros.rb +36 -0
- data/lib/slices/has_attachments.rb +111 -0
- data/lib/slices/has_slices.rb +88 -0
- data/lib/slices/i18n.rb +6 -0
- data/lib/slices/i18n/backend.rb +32 -0
- data/lib/slices/i18n_backend.rb +24 -0
- data/lib/slices/paperclip.rb +13 -0
- data/lib/slices/position_helper.rb +98 -0
- data/lib/slices/renderer.rb +52 -0
- data/lib/slices/slices_engine.rb +51 -0
- data/lib/slices/split_date_time_field.rb +14 -0
- data/lib/slices/tasks/assets.rake +35 -0
- data/lib/slices/tasks/db.rake +50 -0
- data/lib/slices/tasks/seeds.rake +93 -0
- data/lib/slices/tasks/validate.rake +62 -0
- data/lib/slices/tree.rb +306 -0
- data/lib/slices/version.rb +4 -0
- data/lib/slices/will_paginate.rb +12 -0
- data/lib/slices/will_paginate_mongoid.rb +45 -0
- data/lib/standard_tree.rb +193 -0
- 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,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 @@
|
|
1
|
+
slices.ComposerItem = Backbone.Model.extend();
|
@@ -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
|
+
|