jQuery(function($){ /***************************************************************************** ** NGG DEFINITION ***/ /** Setup a namespace for NextGEN-offered Backbone components **/ var Ngg = { Models: {}, Views: {} }; /***************************************************************************** ** NGG MODELS ***/ /** * Ngg.Models.SelectableItems * A collection of items that can be selectable. Commonly used with the * Ngg.Views.SelectTag widget (view) **/ Ngg.Models.SelectableItems = Backbone.Collection.extend({ selected: function(){ return this.filter(function(item){ return item.get('selected') == true; }); }, deselect_all: function(){ this.each(function(item){ item.set('selected', false); }); }, selected_ids: function(){ return _.pluck(this.selected(), 'id'); }, select: function(ids){ if (!_.isArray(ids)) ids = [ids]; this.each(function(item){ if (_.indexOf(ids, item.id) >= 0) { item.set('selected', true); } }); this.trigger('selected'); } }); /***************************************************************************** ** NGG VIEWS ***/ /** * Ngg.Views.SelectTag * Used to render a Select tag (drop-down list) **/ Ngg.Views.SelectTag = Backbone.View.extend({ tagName: 'select', collection: null, multiple: false, value_field: 'id', text_field: 'title', initialize: function(options) { this.options = options || {}; _.each(this.options, function(value, key){ this[key] = value; }, this); this.collection.on('add', this.render_new_option, this); this.collection.on('remove', this.remove_existing_option, this); this.collection.on('reset', this.empty_list, this); }, events: { 'change': 'selection_changed' }, empty_list: function(){ this.$el.empty(); }, render_new_option: function(item){ this.$el.append(new this.Option({ model: item, value_field: this.value_field, text_field: this.text_field }).render().el); }, remove_existing_option: function(item){ this.$el.find("option[value='"+item.id+"']").remove(); }, /** * After a selection has changed, set the 'selected' property for each item in the * collection * @triggers 'selected' **/ selection_changed: function(){ // Get selected options from DOM var selections = _.map(this.$el.find(':selected'), function(element){ return $(element).val(); }); // Set the 'selected' attribute for each item in the collection this.collection.each(function(item){ if (_.indexOf(selections, item.id) >= 0 || _.indexOf(selections, item.id.toString()) >= 0) item.set('selected', true); else item.set('selected', false); }); this.collection.trigger('selected'); }, render: function(){ this.$el.empty(); if (this.options.include_blank) { this.$el.append(""); } this.collection.each(function(item){ var option = new this.Option({ model: item, value_field: this.value_field, text_field: this.text_field }); this.$el.append(option.render().el); }, this); if (this.multiple) this.$el.prop('multiple', true).attr('multiple', 'multiple'); if (this.width) this.$el.width(this.width); return this; }, /** * Represents an option in the Select drop-down **/ Option: Backbone.View.extend({ tagName: 'option', model: null, initialize: function(options) { this.options = options || {}; _.each(this.options, function(value, key){ this[key] = value; }, this); this.model.on('change', this.render, this); }, render: function(){ var self = this; this.$el.html(this.model.get(this.text_field).replace(/\\&/g, '&').replace(/\\'/g, "'")); this.$el.prop({ value: this.value_field == 'id' ? this.model.id : this.model.get(this.value_field), }); if (self.model.get('selected') == true) { this.$el.prop('selected', true).attr('selected', 'selected'); } return this; } }) }); Ngg.Views.Chosen = Backbone.View.extend({ tagName: 'span', initialize: function(options) { this.options = options || {}; this.collection = this.options.collection; this.options.include_blank = true; this.select_tag = new Ngg.Views.SelectTag(this.options); this.collection.on('change', this.selection_changed, this); }, selection_changed: function(e){ if (_.isUndefined(e.changed['selected'])) this.render(); }, render: function(){ this.$el.append(this.select_tag.render().$el); if (this.options.width) this.select_tag.$el.width(this.options.width); // Configure select2 options this.select2_opts = { placeholder: this.options.placeholder }; // Create the select2 drop-down if (this.$el.parent().length == 0) { $('body').append(this.$el); this.select_tag.$el.select2(this.select2_opts); var container = this.select_tag.$el.select2('container').detach(); this.$el.append(container); this.$el.detach(); } else this.select_tag.$el.select2(this.select2_opts); // Ensure that values are pre-populated if (this.options.multiple) { var selected = []; _.each(this.collection.selected_ids(), function(id){ selected.push(id.toString()); }); if (selected.length == 0) selected = ''; this.select_tag.$el.select2('val', selected); } // For IE, ensure that the text field has a width this.$el.find('.select2-input').width(this.options.width-20); return this; } }); /***************************************************************************** ** DISPLAY TAB DEFINITION ***/ /** * Setup a namespace **/ Ngg.DisplayTab = { Models: {}, Views: {}, App: {} }; /***************************************************************************** * MODEL CLASSES **/ /** * A collection that can fetch it's entities from the server **/ Ngg.Models.Remote_Collection = Ngg.Models.SelectableItems.extend({ fetch_limit: 5000, in_progress: false, fetch_url: photocrati_ajax.url, action: '', extra_data: {}, _create_request: function(limit, offset) { var request = ; request = _.extend(request, { action: this.action, limit: limit ? limit : this.fetch_limit, offset: offset ? offset : 0 }); for (var index in this.extra_data) { var value = this.extra_data[index]; if (typeof(request[index]) == 'undefined') { request[index] = {}; } if (typeof(value['toJSON']) != 'undefined') { value = value.toJSON(); } request[index] = _.extend(request[index], value); } return request; }, _add_item: function(item) { this.push(item); }, fetch: function(limit, offset){ // Request the entities from the server var self = this; this.in_progress = true; $.post(this.fetch_url, this._create_request(limit, offset), function(response){ if (typeof(_) == 'undefined') return; if (!_.isObject(response)) response = JSON.parse(response); if (response.items) { _.each(response.items, function(item){ self._add_item(item); }); // Continue fetching ? if (response.total >= response.limit+response.offset) { self.fetch(response.limit, response.offset+response.limit); } else { self.in_progress = false; self.trigger('finished_fetching'); } } }); } }); /** * Ngg.DisplayTab.Models.Displayed_Gallery * Represents the displayed gallery being edited or created by the Display Tab **/ Ngg.DisplayTab.Models.Displayed_Gallery = Backbone.Model.extend({ defaults: { source: null, container_ids: [], entity_ids: [], display_type: null, display_settings: {}, exclusions: [], sortorder: [], slug: null } }); /** * Ngg.DisplayTab.Models.Source * Represents an individual source used to collect displayable entities from **/ Ngg.DisplayTab.Models.Source = Backbone.Model.extend({ idAttribute: 'name', defaults: { title: '', name: '', selected: false } }); /** * Ngg.DisplayTab.Models.Source_Collection * Used as a collection of all the available sources for entities **/ Ngg.DisplayTab.Models.Source_Collection = Ngg.Models.SelectableItems.extend({ model: Ngg.DisplayTab.Models.Source, selected_value: function(){ var retval = null; var selected = this.selected(); if (selected.length > 0) { retval = selected[0].get('name'); } return retval; } }); /** * Ngg.DisplayTab.Models.Gallery * Represents an individual gallery entity **/ Ngg.DisplayTab.Models.Gallery = Backbone.Model.extend({ idAttribute: '', defaults: { title: '', name: '' } }); /** * Ngg.DisplayTab.Models.Gallery_Collection * Collection of gallery objects **/ Ngg.DisplayTab.Models.Gallery_Collection = Ngg.Models.Remote_Collection.extend({ model: Ngg.DisplayTab.Models.Gallery, action: 'get_existing_galleries' }); /** * Ngg.DisplayTab.Models.Album * Represents an individual Album object **/ Ngg.DisplayTab.Models.Album = Backbone.Model.extend({ defaults: { title: '', name: '' } }); /** * Ngg.DisplayTab.Models.Album_Collection * Used as a collection of album objects **/ Ngg.DisplayTab.Models.Album_Collection = Ngg.Models.Remote_Collection.extend({ model: Ngg.DisplayTab.Models.Album, action: 'get_existing_albums' }); /** * Ngg.DisplayTab.Models.Tag * Represents an individual tag object **/ Ngg.DisplayTab.Models.Tag = Backbone.Model.extend({ defaults: { title: '' } }); /** * Ngg.DisplayTab.Models.Tag_Collection * Represents a collection of tag objects **/ Ngg.DisplayTab.Models.Tag_Collection = Ngg.Models.Remote_Collection.extend({ model: Ngg.DisplayTab.Models.Tag, /* selected_ids: function(){ return this.selected().map(function(item){ return item.get('name'); }); }, */ action: 'get_existing_image_tags' }); /** * Ngg.DisplayTab.Models.Display_Type * Represents an individual display type **/ Ngg.DisplayTab.Models.Display_Type = Backbone.Model.extend({ idAttribute: 'name', defaults: { title: '' }, is_compatible_with_source: function(source){ var success = true; for (index in source.get('returns')) { var returned_entity_type = source.get('returns')[index]; if (_.indexOf(this.get('entity_types'), returned_entity_type) < 0) { success = false; break; } } return success; } }); /** * Ngg.DisplayTab.Models.Display_Type_Collection * Represents a collection of display type objects **/ Ngg.DisplayTab.Models.Display_Type_Collection = Ngg.Models.SelectableItems.extend({ model: Ngg.DisplayTab.Models.Display_Type, selected_value: function(){ var retval = null; var selected = this.selected(); if (selected.length > 0) { return selected[0].get('name'); } return retval; } }); /** * Ngg.DisplayTab.Models.Entity * Represents an entity to display on the front-end **/ Ngg.DisplayTab.Models.Entity = Backbone.Model.extend({ entity_id: function(){ return this.get(this.get('id_field')); }, is_excluded: function() { current_value = this.get('exclude'); if (_.isUndefined(current_value)) return false; else if (_.isBoolean(current_value)) return current_value; else return parseInt(current_value) == 0 ? false : true; }, is_included: function(){ return !this.is_excluded(); }, is_gallery: function(){ retval = false; if (this.get('is_gallery') == true) retval = true; return retval; }, is_album: function(){ retval = false; if (this.get('is_album') == true) retval = true; return retval; }, is_image: function(){ return !this.is_album() && !this.is_gallery(); }, alttext: function(){ if (this.is_image()) { return this.get('alttext'); } else if (this.is_gallery()) { return this.get('title'); } else if (this.is_album()) { return this.get('name'); } } }); /** * Ngg.DisplayTab.Models.Entity_Collection * Represents a collection of entities **/ Ngg.DisplayTab.Models.Entity_Collection = Ngg.Models.Remote_Collection.extend({ model: Ngg.DisplayTab.Models.Entity, action: 'get_displayed_gallery_entities', _add_item: function(item){ item.exclude = parseInt(item.exclude) == 1 ? true : false; item.is_gallery = parseInt(item.is_gallery) == 1 ? true : false; item.is_album = parseInt(item.is_album) == 1 ? true : false; this.push(item); }, entity_ids: function(){ return this.map(function(item){ return item.entity_id(); }); }, included_ids: function(){ return _.compact(this.map(function(item){ if (item.is_included()) return item.entity_id(); })); }, excluded_ids: function() { return _.compact(this.map(function(item) { if (!item.is_included()) { return item.entity_id(); } })); } }); Ngg.DisplayTab.Models.SortOrder = Backbone.Model.extend({ }); Ngg.DisplayTab.Models.SortOrder_Options = Ngg.Models.SelectableItems.extend({ model: Ngg.DisplayTab.Models.SortOrder }); Ngg.DisplayTab.Models.SortDirection = Backbone.Model.extend({ }); Ngg.DisplayTab.Models.SortDirection_Options = Backbone.Collection.extend({ model: Ngg.DisplayTab.Models.SortDirection }); Ngg.DisplayTab.Models.Slug = Backbone.Model.extend({}); /***************************************************************************** * VIEW CLASSES **/ /** * Ngg.DisplayTab.Views.Source_Config * Used to populate the source configuration tab **/ Ngg.DisplayTab.Views.Source_Config = Backbone.View.extend({ el: '#source_configuration', selected_view: null, /** * Bind to the "sources" collection to know when a selection has been made * and determine what sub-view to render **/ initialize: function(){ this.sources = Ngg.DisplayTab.instance.sources; this.sources.on('selected', this.render, this); _.bindAll(this, 'render'); this.render(); }, render: function(){ var chosen = new Ngg.Views.Chosen({ id: 'source_select', collection: this.sources, placeholder: 'Select a source', width: 500 }); this.$el.html('