PageRenderTime 21ms CodeModel.GetById 3ms app.highlight 8ms RepoModel.GetById 1ms app.codeStats 0ms

/htdocs/wp-content/plugins/nextgen-gallery/products/photocrati_nextgen/modules/attach_to_post/templates/display_tab_js.php

https://github.com/Fishgate/privatecollectionswp
PHP | 1717 lines | 1332 code | 213 blank | 172 comment | 162 complexity | 7d5bfaf3366e9e6ec968261b54ed2aab MD5 | raw file

Large files files are truncated, but you can click here to view the full file

   1jQuery(function($){
   2
   3    /*****************************************************************************
   4     ** NGG DEFINITION
   5    ***/
   6
   7    /**
   8     Setup a namespace for NextGEN-offered Backbone components
   9    **/
  10    var Ngg = {
  11        Models: {},
  12        Views: {}
  13    };
  14
  15    /*****************************************************************************
  16     ** NGG MODELS
  17    ***/
  18
  19    /**
  20     * Ngg.Models.SelectableItems
  21     * A collection of items that can be selectable. Commonly used with the
  22     * Ngg.Views.SelectTag widget (view)
  23    **/
  24    Ngg.Models.SelectableItems = Backbone.Collection.extend({
  25        selected: function(){
  26            return this.filter(function(item){
  27                return item.get('selected') == true;
  28            });
  29        },
  30
  31		deselect_all: function(){
  32			this.each(function(item){
  33				item.set('selected', false);
  34			});
  35		},
  36
  37        selected_ids: function(){
  38			return _.pluck(this.selected(), 'id');
  39        },
  40
  41		select: function(ids){
  42			if (!_.isArray(ids)) ids = [ids];
  43			this.each(function(item){
  44				if (_.indexOf(ids, item.id) >= 0) {
  45					item.set('selected', true);
  46				}
  47			});
  48			this.trigger('selected');
  49		}
  50    });
  51
  52
  53    /*****************************************************************************
  54     ** NGG VIEWS
  55    ***/
  56
  57    /**
  58     * Ngg.Views.SelectTag
  59     * Used to render a Select tag (drop-down list)
  60    **/
  61    Ngg.Views.SelectTag                    = Backbone.View.extend({
  62        tagName: 'select',
  63
  64        collection: null,
  65
  66		multiple: false,
  67
  68		value_field: 'id',
  69
  70		text_field: 'title',
  71
  72        initialize: function(options) {
  73            this.options = options || {};
  74			_.each(this.options, function(value, key){
  75				this[key] = value;
  76			}, this);
  77			this.collection.on('add', this.render_new_option, this);
  78			this.collection.on('remove', this.remove_existing_option, this);
  79			this.collection.on('reset', this.empty_list, this);
  80        },
  81
  82        events: {
  83            'change': 'selection_changed'
  84        },
  85
  86		empty_list: function(){
  87			this.$el.empty();
  88		},
  89
  90		render_new_option: function(item){
  91			this.$el.append(new this.Option({
  92				model: item,
  93				value_field: this.value_field,
  94				text_field: this.text_field
  95			}).render().el);
  96		},
  97
  98		remove_existing_option: function(item){
  99			this.$el.find("option[value='"+item.id+"']").remove();
 100		},
 101
 102        /**
 103         * After a selection has changed, set the 'selected' property for each item in the
 104         * collection
 105         * @triggers 'selected'
 106        **/
 107        selection_changed: function(){
 108            // Get selected options from DOM
 109            var selections = _.map(this.$el.find(':selected'), function(element){
 110                return $(element).val();
 111            });
 112
 113            // Set the 'selected' attribute for each item in the collection
 114            this.collection.each(function(item){
 115                if (_.indexOf(selections, item.id) >= 0 || _.indexOf(selections, item.id.toString()) >= 0)
 116                    item.set('selected', true);
 117                else
 118                    item.set('selected', false);
 119            });
 120            this.collection.trigger('selected');
 121        },
 122
 123        render: function(){
 124			this.$el.empty();
 125			if (this.options.include_blank) {
 126				this.$el.append("<option></option>");
 127			}
 128            this.collection.each(function(item){
 129                var option = new this.Option({
 130					model: item,
 131					value_field: this.value_field,
 132					text_field: this.text_field
 133				});
 134                this.$el.append(option.render().el);
 135            }, this);
 136			if (this.multiple) this.$el.prop('multiple', true);
 137			if (this.width) this.$el.width(this.width);
 138            return this;
 139        },
 140
 141        /**
 142         * Represents an option in the Select drop-down
 143        **/
 144        Option: Backbone.View.extend({
 145            tagName: 'option',
 146
 147            model: null,
 148
 149            initialize: function(options) {
 150                this.options = options || {};
 151				_.each(this.options, function(value, key){
 152					this[key] = value;
 153				}, this);
 154                this.model.on('change', this.render, this);
 155            },
 156
 157            render: function(){
 158                var self = this;
 159                this.$el.html(this.model.get(this.text_field).replace(/\\&/g, '&').replace(/\\'/g, "'"));
 160                this.$el.prop({
 161                    value:    this.value_field == 'id' ? this.model.id : this.model.get(this.value_field),
 162                });
 163                if (self.model.get('selected') == true) {
 164                    this.$el.attr('selected', 'selected');
 165                }
 166                return this;
 167            }
 168        })
 169    });
 170
 171
 172	Ngg.Views.Chosen								= Backbone.View.extend({
 173		tagName: 'span',
 174
 175		initialize: function(options) {
 176            this.options = options || {};
 177			this.collection = this.options.collection;
 178			if (!this.options.multiple) this.options.include_blank = true;
 179			this.select_tag = new Ngg.Views.SelectTag(this.options);
 180			this.collection.on('change', this.selection_changed, this);
 181		},
 182
 183		selection_changed: function(e){
 184			if (_.isUndefined(e.changed['selected'])) this.render();
 185		},
 186
 187		render: function(){
 188
 189			this.$el.append(this.select_tag.render().$el);
 190			if (this.options.width)
 191				this.select_tag.$el.width(this.options.width);
 192
 193			// Configure select2 options
 194			this.select2_opts = {
 195				placeholder: this.options.placeholder
 196			};
 197
 198			// Create the select2 drop-down
 199			if (this.$el.parent().length == 0) {
 200				$('body').append(this.$el);
 201				this.select_tag.$el.select2(this.select2_opts);
 202				var container = this.select_tag.$el.select2('container').detach();
 203				this.$el.append(container);
 204				this.$el.detach();
 205
 206			}
 207			else this.select_tag.$el.select2(this.select2_opts);
 208
 209			// Hack for multi-select elements
 210			if (this.options.multiple && this.collection.selected().length == 0)
 211				this.select_tag.$el.select2('val', '');
 212
 213			// For IE, ensure that the text field has a width
 214			this.$el.find('.select2-input').width(this.options.width-20);
 215
 216			// For IE8, ensure that the selection is being displayed
 217			if (!this.options.multiple) {
 218				var selected_value = this.$el.find('.select2-choice span:first');
 219				if (selected_value.text().length == 0 && this.collection.selected().length > 0) {
 220					var selected_item = this.collection.selected().pop();
 221					selected_value.text(selected_item.get(this.select_tag.text_field));
 222				}
 223			}
 224			else {
 225				var selected_values = this.$el.find('.select2-search-choice');
 226				if (this.collection.selected().length > 0 && selected_values.length == 0) {
 227					this.select_tag.$el.select2('val', '');
 228					var data = [];
 229					var value_field = this.select_tag.value_field;
 230					_.each(this.collection.selected(), function(item){
 231						var value = value_field == 'id' ? item.id : item.get(value_field);
 232						data.push({
 233							id: 	value,
 234							text: 	item.get(this.select_tag.text_field)
 235						});
 236					}, this);
 237					this.select_tag.$el.select2('data', data);
 238				}
 239			}
 240
 241			return this;
 242		}
 243	});
 244
 245    /*****************************************************************************
 246     ** DISPLAY TAB DEFINITION
 247    ***/
 248
 249    /**
 250     * Setup a namespace
 251    **/
 252    Ngg.DisplayTab = {
 253        Models: {},
 254        Views: {},
 255        App: {}
 256    };
 257
 258    /*****************************************************************************
 259     * MODEL CLASSES
 260    **/
 261
 262	/**
 263	 * A collection that can fetch it's entities from the server
 264	**/
 265	Ngg.Models.Remote_Collection			= Ngg.Models.SelectableItems.extend({
 266		fetch_limit: 5000,
 267		in_progress: false,
 268		fetch_url:   photocrati_ajax.url,
 269		action: 	 '',
 270		extra_data:  {},
 271
 272		_create_request: function(limit, offset) {
 273			var request = <?php echo $sec_token?>;
 274			request = _.extend(request, {
 275				action: this.action,
 276				limit: limit ? limit : this.fetch_limit,
 277				offset: offset ? offset : 0
 278
 279			});
 280			for (var index in this.extra_data) {
 281				var value = this.extra_data[index];
 282				if (typeof(request[index]) == 'undefined') {
 283					request[index] = {};
 284				}
 285				if (typeof(value['toJSON']) != 'undefined') {
 286					value = value.toJSON();
 287				}
 288				request[index] = _.extend(request[index], value);
 289			}
 290			return request;
 291		},
 292
 293		_add_item: function(item) {
 294			this.push(item);
 295		},
 296
 297		fetch: 	function(limit, offset){
 298			// Request the entities from the server
 299			var self = this;
 300			this.in_progress = true;
 301			$.post(this.fetch_url, this._create_request(limit, offset), function(response){
 302                if (typeof(_) == 'undefined') return;
 303				if (!_.isObject(response)) response = JSON.parse(response);
 304
 305				if (response.items) {
 306					_.each(response.items, function(item){
 307						self._add_item(item);
 308					});
 309
 310					// Continue fetching ?
 311					if (response.total >= response.limit+response.offset) {
 312						self.fetch(response.limit, response.offset+response.limit);
 313					}
 314					else {
 315						self.in_progress = false;
 316						self.trigger('finished_fetching');
 317					}
 318				}
 319			});
 320		}
 321	});
 322
 323
 324    /**
 325     * Ngg.DisplayTab.Models.Displayed_Gallery
 326     * Represents the displayed gallery being edited or created by the Display Tab
 327    **/
 328    Ngg.DisplayTab.Models.Displayed_Gallery        = Backbone.Model.extend({
 329        defaults: {
 330            source: null,
 331            container_ids: [],
 332            entity_ids: [],
 333            display_type: null,
 334            display_settings: {},
 335            exclusions: [],
 336            sortorder: [],
 337            slug: null
 338        }
 339    });
 340
 341    /**
 342     * Ngg.DisplayTab.Models.Source
 343     * Represents an individual source used to collect displayable entities from
 344    **/
 345    Ngg.DisplayTab.Models.Source                = Backbone.Model.extend({
 346		idAttribute: 'name',
 347        defaults: {
 348            title: '',
 349			name: '',
 350            selected: false
 351        }
 352    });
 353
 354    /**
 355     * Ngg.DisplayTab.Models.Source_Collection
 356     * Used as a collection of all the available sources for entities
 357    **/
 358    Ngg.DisplayTab.Models.Source_Collection        = Ngg.Models.SelectableItems.extend({
 359        model: Ngg.DisplayTab.Models.Source,
 360
 361		selected_value: function(){
 362			var retval = null;
 363			var selected = this.selected();
 364			if (selected.length > 0) {
 365				retval = selected[0].get('name');
 366			}
 367			return retval;
 368		}
 369    });
 370
 371    /**
 372     * Ngg.DisplayTab.Models.Gallery
 373     * Represents an individual gallery entity
 374    **/
 375    Ngg.DisplayTab.Models.Gallery                = Backbone.Model.extend({
 376		idAttribute: '<?php echo $gallery_primary_key ?>',
 377        defaults: {
 378            title:     '',
 379            name:   ''
 380        }
 381    });
 382
 383    /**
 384     * Ngg.DisplayTab.Models.Gallery_Collection
 385     * Collection of gallery objects
 386    **/
 387    Ngg.DisplayTab.Models.Gallery_Collection    = Ngg.Models.Remote_Collection.extend({
 388        model: Ngg.DisplayTab.Models.Gallery,
 389
 390		action: 'get_existing_galleries'
 391    });
 392
 393    /**
 394     * Ngg.DisplayTab.Models.Album
 395     * Represents an individual Album object
 396    **/
 397    Ngg.DisplayTab.Models.Album                    = Backbone.Model.extend({
 398        defaults: {
 399            title: '',
 400            name:  ''
 401        }
 402    });
 403
 404    /**
 405     * Ngg.DisplayTab.Models.Album_Collection
 406     * Used as a collection of album objects
 407    **/
 408    Ngg.DisplayTab.Models.Album_Collection        = Ngg.Models.Remote_Collection.extend({
 409        model: Ngg.DisplayTab.Models.Album,
 410
 411		action: 'get_existing_albums'
 412    });
 413
 414    /**
 415     * Ngg.DisplayTab.Models.Tag
 416     * Represents an individual tag object
 417    **/
 418    Ngg.DisplayTab.Models.Tag                    = Backbone.Model.extend({
 419        defaults: {
 420            title: ''
 421        }
 422    });
 423
 424    /**
 425     * Ngg.DisplayTab.Models.Tag_Collection
 426     * Represents a collection of tag objects
 427    **/
 428    Ngg.DisplayTab.Models.Tag_Collection        = Ngg.Models.Remote_Collection.extend({
 429        model: Ngg.DisplayTab.Models.Tag,
 430        /*
 431		selected_ids: function(){
 432			return this.selected().map(function(item){
 433				return item.get('name');
 434			});
 435		},
 436        */
 437
 438		action: 'get_existing_image_tags'
 439    });
 440
 441	/**
 442	 * Ngg.DisplayTab.Models.Display_Type
 443	 * Represents an individual display type
 444	**/
 445	Ngg.DisplayTab.Models.Display_Type			= Backbone.Model.extend({
 446		idAttribute: 'name',
 447		defaults: {
 448			title: ''
 449		},
 450
 451		is_compatible_with_source: function(source){
 452			var success = true;
 453			for (index in source.get('returns')) {
 454				var returned_entity_type = source.get('returns')[index];
 455				if (_.indexOf(this.get('entity_types'), returned_entity_type) < 0) {
 456					success = false;
 457					break;
 458				}
 459			}
 460			return success;
 461		}
 462	});
 463
 464	/**
 465	 * Ngg.DisplayTab.Models.Display_Type_Collection
 466	 * Represents a collection of display type objects
 467	**/
 468	Ngg.DisplayTab.Models.Display_Type_Collection = Ngg.Models.SelectableItems.extend({
 469		model: Ngg.DisplayTab.Models.Display_Type,
 470
 471		selected_value: function(){
 472			var retval = null;
 473			var selected = this.selected();
 474			if (selected.length > 0) {
 475				return selected[0].get('name');
 476			}
 477			return retval;
 478		}
 479	});
 480
 481	/**
 482	 * Ngg.DisplayTab.Models.Entity
 483	 * Represents an entity to display on the front-end
 484	**/
 485	Ngg.DisplayTab.Models.Entity				= Backbone.Model.extend({
 486		entity_id: function(){
 487			return this.get(this.get('id_field'));
 488		},
 489
 490		is_excluded: function() {
 491			current_value = this.get('exclude');
 492			if (_.isUndefined(current_value)) return false;
 493			else if (_.isBoolean(current_value)) return current_value;
 494			else return parseInt(current_value) == 0 ? false : true;
 495		},
 496
 497		is_included: function(){
 498			return !this.is_excluded();
 499		},
 500
 501		is_gallery: function(){
 502			retval = false;
 503			if (this.get('is_gallery') == true) retval = true;
 504			return retval;
 505		},
 506
 507		is_album: function(){
 508			retval = false;
 509			if (this.get('is_album') == true) retval = true;
 510			return retval;
 511		},
 512
 513		is_image: function(){
 514			return !this.is_album() && !this.is_gallery();
 515		},
 516
 517		alttext: function(){
 518			if (this.is_image()) {
 519				return this.get('alttext');
 520			}
 521			else if (this.is_gallery()) {
 522				return this.get('title');
 523			}
 524			else if (this.is_album()) {
 525				return this.get('name');
 526			}
 527		}
 528	});
 529
 530	/**
 531	 * Ngg.DisplayTab.Models.Entity_Collection
 532	 * Represents a collection of entities
 533	**/
 534	Ngg.DisplayTab.Models.Entity_Collection		= Ngg.Models.Remote_Collection.extend({
 535		model: Ngg.DisplayTab.Models.Entity,
 536
 537		action: 'get_displayed_gallery_entities',
 538
 539		_add_item: function(item){
 540			item.exclude = parseInt(item.exclude) == 1 ? true : false;
 541			item.is_gallery = parseInt(item.is_gallery) == 1 ? true : false;
 542			item.is_album = parseInt(item.is_album) == 1 ? true : false;
 543			this.push(item);
 544		},
 545
 546		entity_ids: function(){
 547			return this.map(function(item){
 548				return item.entity_id();
 549			});
 550		},
 551
 552		included_ids: function(){
 553			return _.compact(this.map(function(item){
 554				if (item.is_included()) return item.entity_id();
 555			}));
 556		},
 557
 558        excluded_ids: function() {
 559            return _.compact(this.map(function(item) {
 560                if (!item.is_included()) {
 561                    return item.entity_id();
 562                }
 563            }));
 564        }
 565	});
 566
 567
 568	Ngg.DisplayTab.Models.SortOrder				= Backbone.Model.extend({
 569	});
 570
 571	Ngg.DisplayTab.Models.SortOrder_Options		= Ngg.Models.SelectableItems.extend({
 572		model: Ngg.DisplayTab.Models.SortOrder
 573	});
 574	Ngg.DisplayTab.Models.SortDirection			= Backbone.Model.extend({
 575
 576	});
 577	Ngg.DisplayTab.Models.SortDirection_Options = Backbone.Collection.extend({
 578		model: Ngg.DisplayTab.Models.SortDirection
 579	});
 580
 581     Ngg.DisplayTab.Models.Slug = Backbone.Model.extend({});
 582
 583    /*****************************************************************************
 584     * VIEW CLASSES
 585    **/
 586
 587    /**
 588     * Ngg.DisplayTab.Views.Source_Config
 589     * Used to populate the source configuration tab
 590    **/
 591    Ngg.DisplayTab.Views.Source_Config             = Backbone.View.extend({
 592        el: '#source_configuration',
 593
 594        selected_view: null,
 595
 596        /**
 597         * Bind to the "sources" collection to know when a selection has been made
 598         * and determine what sub-view to render
 599        **/
 600        initialize: function(){
 601            this.sources = Ngg.DisplayTab.instance.sources;
 602            this.sources.on('selected', this.render, this);
 603            _.bindAll(this, 'render');
 604            this.render();
 605        },
 606
 607        render: function(){
 608			var chosen = new Ngg.Views.Chosen({
 609				id: 'source_select',
 610				collection: this.sources,
 611				placeholder: 'Select a source',
 612				width: 500
 613			});
 614
 615            this.$el.html('<tr><td><label><?php _e('Sources', 'nggallery'); ?></label></td><td id="source_column"></td></tr>');
 616            this.$el.find('#source_column').append(chosen.render().el);
 617
 618            var selected = this.sources.selected();
 619			if (selected.length) {
 620				var view_name = _.str.capitalize(selected.pop().id)+"Source";
 621				if (typeof(Ngg.DisplayTab.Views[view_name]) != 'undefined') {
 622				   var selected_view = new Ngg.DisplayTab.Views[view_name];
 623				   this.$el.append(selected_view.render().el);
 624				}
 625			}
 626
 627            return this;
 628        }
 629    });
 630
 631    Ngg.DisplayTab.Views.Slug_Config = Backbone.View.extend({
 632        el: '#slug_configuration',
 633
 634        selected_view: null,
 635
 636        initialize: function() {
 637            this.displayed_gallery = Ngg.DisplayTab.instance.displayed_gallery;
 638            this.slug = Ngg.DisplayTab.instance.displayed_gallery.get('slug');
 639            this.render();
 640        },
 641
 642        render: function() {
 643            var self = this;
 644
 645            var input = $('<input>').prop({
 646                type: 'text',
 647                name: 'slug',
 648                value: this.slug,
 649                placeholder: '<?php _e('(optional)', 'nggallery'); ?>',
 650                id: 'field_slug'
 651            });
 652
 653            input.change(function() {
 654                self.displayed_gallery.set('slug', $(this).val());
 655            });
 656
 657            var tooltip = '<?php _e('Sets an SEO-friendly name to this gallery for URLs. Currently only in use by the Pro Lightbox.', 'nggallery'); ?>';
 658            this.$el.append('<tr><td id="slug_label"><label for="field_slug" class="tooltip" title="' + tooltip + '"><?php _e('Slug', 'nggallery'); ?></label></td><td id="slug_column"></td></tr>');
 659            this.$el.find('#slug_column').append(input);
 660
 661            return this;
 662        }
 663    });
 664
 665	Ngg.DisplayTab.Views.Display_Type_Selector = Backbone.View.extend({
 666		el: '#display_type_selector',
 667
 668		initialize: function(){
 669			this.display_types	= Ngg.DisplayTab.instance.display_types;
 670			this.display_type_order_base	= Ngg.DisplayTab.instance.display_type_order_base;
 671			this.display_type_order_step	= Ngg.DisplayTab.instance.display_type_order_step;
 672			this.sources		= Ngg.DisplayTab.instance.sources;
 673			this.render();
 674		},
 675
 676		selection_changed: function(value){
 677			var selected_type = null;
 678			this.display_types.each(function(item){
 679				if (item.get('name') == value) {
 680					selected_type = item;
 681					item.set('selected', true);
 682				}
 683				else {
 684					item.set('selected', false);
 685				}
 686			});
 687
 688			if (selected_type) {
 689				var selected_source = this.sources.selected_value();
 690				var default_source = selected_type.get('default_source');
 691
 692				// If the default source isn't selected, then select it
 693				if (default_source && selected_source != default_source) {
 694
 695					// Get the default source object by name
 696					default_source = this.sources.where({
 697						name: default_source
 698					});
 699
 700					// Does the default source exist ?
 701					if (default_source.length > 0) {
 702						default_source = default_source[0];
 703						this.sources.deselect_all();
 704						this.sources.select(default_source.id);
 705					}
 706				}
 707			}
 708
 709			$('.display_settings_form').each(function(){
 710				$this = $(this);
 711				if ($this.attr('rel') == value) $this.removeClass('hidden');
 712				else $this.addClass('hidden');
 713			});
 714		},
 715
 716		render: function(){
 717			var selected_source = this.sources.selected();
 718			var current_step = 0;
 719			selected_source = selected_source.length > 0 ? selected_source[0] : false;
 720			this.$el.empty();
 721			
 722			var order_base = this.display_type_order_base;
 723			var order_step = this.display_type_order_step;
 724			
 725			this.display_types.each(function(item){
 726				if (selected_source && !item.is_compatible_with_source(selected_source)) {
 727
 728				    // Show all display types if we're viewing the display type
 729					// selector tab
 730					var display_tab =  $('#display_type_tab_content:visible');
 731					if (display_tab.length == 0) return;
 732					else if (display_tab.css('visibility') == 'hidden') return;
 733				}
 734				var display_type = new this.DisplayType;
 735				display_type.model = item;
 736				display_type.on('selected', this.selection_changed, this);
 737				if (!this.display_types.selected_value()) {
 738					item.set('selected', true);
 739					this.selection_changed(item.id);
 740				}
 741				var display_order = item.get('view_order');
 742				if (!display_order)
 743					display_order = order_base;
 744				var display_step = Math.floor(display_order / order_step);
 745				if (current_step > 0 && display_step > current_step) {
 746					this.$el.append('<li class="clear" style="height: 10px" />');
 747				}
 748				current_step = display_step;
 749				this.$el.append(display_type.render().el);
 750			}, this);
 751			return this;
 752		},
 753
 754		DisplayType: Backbone.View.extend({
 755			className: 'display_type_preview',
 756
 757			events: {
 758				click: 'clicked'
 759			},
 760
 761			clicked: function(e){
 762				this.trigger('selected', this.model.get('name'));
 763			},
 764
 765			render: function() {
 766				// Create all elements
 767				var image_container = $('<div/>').addClass('image_container');
 768
 769                // 2.0.66 did not support plugins_url, 2.0.66.3+ does
 770                var installed_at_version = this.model.get('installed_at_version');
 771                var baseurl = photocrati_ajax.wp_plugins_url;
 772                var preview_image_relpath = this.model.get('preview_image_relpath');
 773                if (typeof installed_at_version == 'undefined') {
 774                    baseurl = photocrati_ajax.wp_site_url;
 775                    // those who installed 2.0.66.3 lack the 'installed_at_version' setting but have a
 776                    // plugin-relative path
 777                    if (preview_image_relpath.indexOf('/nextgen-gallery') == 0) {
 778                        baseurl = photocrati_ajax.wp_plugins_url;
 779                    }
 780                }
 781
 782
 783				var img = $('<img/>').attr({
 784					src: baseurl + '/' + preview_image_relpath,
 785					title: this.model.get('title'),
 786					alt: this.model.get('alt')
 787				});
 788				var inner_div = $('<div/>');
 789				var radio_button = $('<input/>').prop({
 790 					type: 'radio',
 791					value: this.model.get('name'),
 792					title: this.model.get('title'),
 793					name: 'display_type',
 794					checked: this.model.get('selected')
 795				});
 796				image_container.append(inner_div);
 797				image_container.append(img);
 798				inner_div.append(radio_button);
 799				inner_div.append(this.model.get('title'));
 800				this.$el.append(image_container);
 801				return this;
 802			}
 803		})
 804	});
 805
 806	Ngg.DisplayTab.Views.Preview_Area = Backbone.View.extend({
 807		el: '#preview_area',
 808
 809		initialize: function(){
 810			this.entities			= Ngg.DisplayTab.instance.entities;
 811			this.sources			= Ngg.DisplayTab.instance.sources;
 812			this.displayed_gallery	= Ngg.DisplayTab.instance.displayed_gallery;
 813
 814			// Create the entity list
 815			this.entity_list		= $('<ul/>').attr('id', 'entity_list').append('<li class="clear"/>');
 816
 817			// When an entity is added/removed to the collection, we'll add/remove it on the DOM
 818			this.entities.on('add', this.render_entity, this);
 819			this.entities.on('remove', this.remove_entity, this);
 820
 821			// When the collection is reset, we add a list item to clear the float. This is important -
 822			// jQuery sortable() will break without the cleared element.
 823			this.entities.on('reset', this.entities_reset, this);
 824
 825			// When jQuery sortable() is finished sorting, we need to adjust the order of models in the collection
 826			this.entities.on('change:sortorder', function(model){
 827				this.entities.remove(model, {silent: true});
 828				this.entities.add(model, {at: model.changed.sortorder, silent: true});
 829				this.displayed_gallery.set('sortorder', this.entities.entity_ids());
 830				if (typeof(console) != 'undefined' && typeof(console.log) != 'undefined') {
 831					console.log(this.entities.entity_ids());
 832				}
 833				this.displayed_gallery.set('order_by', 'sortorder');
 834			}, this);
 835
 836			// Reset when the source changes
 837			this.sources.on('selected', this.render, this);
 838
 839			this.render();
 840		},
 841
 842		events: {
 843			opened: 'entities_reset'
 844		},
 845
 846		entities_reset: function(e){
 847			this.entities.reset(null, {silent: true});
 848			this.entity_list.empty().append('<li class="clear"/>');
 849			if (!this.entities.in_progress) this.entities.fetch();
 850		},
 851
 852		render_entity: function(model){
 853			var entity_element = new this.EntityElement({model: model});
 854			this.entity_list.find('.clear').before(entity_element.render().$el);
 855			entity_element.$el.css('visibility', 'hidden');
 856			setTimeout(function(){
 857				entity_element.$el.css('visibility', 'visible');
 858			}, 0);
 859			if (this.$el.find('.no_entities').length == 1) {
 860				this.render();
 861			}
 862			else if (this.entities.length > 1) {
 863				this.entity_list.sortable('refresh');
 864			}
 865		},
 866
 867		remove_entity: function(model){
 868			var id = this.id = model.get('id_field')+'_'+model.entity_id();
 869			var entity = this.entity_list.find('#'+id).remove();
 870			this.entity_list.sortable('refresh');
 871			if (this.entities.length == 0) {
 872				this.render_no_images_notice();
 873			}
 874		},
 875
 876		render_no_images_notice: function(){
 877			this.$el.empty();
 878			this.$el.append("<p class='no_entities'><?php _e('No entities to display for this source.', 'nggallery'); ?></p>");
 879		},
 880
 881		render: function(){
 882			this.$el.empty();
 883			if (this.entities.length > 0 && this.displayed_gallery.get('container_ids').length > 0) {
 884
 885				// Render header rows
 886				this.$el.append(new this.RefreshButton({
 887					entities: this.entities
 888				}).render().el);
 889				this.$el.append(new this.SortButtons({
 890					entities: this.entities,
 891					displayed_gallery: this.displayed_gallery,
 892					sources: this.sources
 893				}).render().el);
 894				this.$el.append(new this.ExcludeButtons({
 895					entities: this.entities
 896				}).render().el);
 897
 898				this.$el.append(this.entity_list);
 899
 900				// Activate jQuery Sortable for the entity list
 901				this.entity_list.sortable({
 902					placeholder: 'placeholder',
 903					forcePlaceholderSize: true,
 904					containment: 'parent',
 905					opacity: 0.7,
 906					revert: true,
 907					dropOnEmpty: true,
 908					start: function(e, ui){
 909						ui.placeholder.css({
 910							height: ui.item.height()
 911						});
 912						return true;
 913					},
 914					stop: function(e, ui) {
 915						ui.item.trigger('drop', ui.item.index());
 916					}
 917				});
 918				this.entity_list.disableSelection();
 919			}
 920			else {
 921				this.render_no_images_notice();
 922			}
 923			return this;
 924		},
 925
 926		RefreshButton: Backbone.View.extend({
 927			className: 'refresh_button',
 928
 929			tagName: 'input',
 930
 931			label: 'Refresh',
 932
 933			events: {
 934				click: 'clicked'
 935			},
 936
 937			clicked: function(){
 938				this.entities.reset();
 939			},
 940
 941			initialize: function(options) {
 942                this.options = options || {};
 943				_.each(this.options, function(value, key){
 944					this[key] = value;
 945				}, this);
 946			},
 947
 948			render: function(){
 949				this.$el.attr({
 950					value: this.label,
 951					type:  'button'
 952				});
 953				return this;
 954			}
 955		}),
 956
 957		ExcludeButtons: Backbone.View.extend({
 958			className: 'header_row',
 959
 960			initialize: function(options) {
 961                this.options = options || {};
 962				_.each(this.options, function(value, key){
 963					this[key] = value;
 964				}, this);
 965			},
 966
 967			render: function(){
 968				this.$el.empty();
 969				this.$el.append('<strong>Exclude:</strong>');
 970				var all_button = new this.Button({
 971					value: true,
 972					text: 'All',
 973					entities: this.entities
 974				});
 975				this.$el.append(all_button.render().el);
 976				this.$el.append('<span class="separator">|</span>');
 977				var none_button = new this.Button({
 978					value: false,
 979					text: 'None',
 980					entities: this.entities
 981				});
 982				this.$el.append(none_button.render().el);
 983				return this;
 984			},
 985
 986			Button: Backbone.View.extend({
 987				tagName: 'a',
 988
 989				value: 1,
 990
 991				text: '',
 992
 993				events: {
 994					click: 'clicked'
 995				},
 996
 997				initialize: function(options) {
 998                    this.options = options || {};
 999					_.each(this.options, function(value, key){
1000						this[key] = value;
1001					}, this);
1002				},
1003
1004				clicked: function(e){
1005					e.preventDefault();
1006					this.entities.each(function(item){
1007						item.set('exclude', this.value);
1008					}, this);
1009				},
1010
1011				render: function(){
1012					this.$el.text(this.text).attr('href', '#');
1013					return this;
1014				}
1015			})
1016		}),
1017
1018		SortButtons: Backbone.View.extend({
1019			className: 'header_row',
1020
1021			initialize: function(options) {
1022                this.options = options || {};
1023				_.each(this.options, function(value, key){
1024					this[key] = value;
1025				}, this);
1026				this.sortorder_options = new Ngg.DisplayTab.Models.SortOrder_Options();
1027				this.sortorder_options.on('change:selected', this.sortoption_changed, this);
1028
1029				// Create sort directions and listen for selection changes
1030				this.sortdirection_options = new Ngg.DisplayTab.Models.SortDirection_Options([
1031					{
1032						value: 'ASC',
1033						title: 'Ascending',
1034						selected: this.displayed_gallery.get('order_direction') == 'ASC'
1035					},
1036					{
1037						value: 'DESC',
1038						title: 'Descending',
1039						selected: this.displayed_gallery.get('order_direction') == 'DESC'
1040					}
1041				]);
1042				this.sortdirection_options.on('change:selected', this.sortdirection_changed, this);
1043				this.displayed_gallery.on('change:order_by', this.displayed_gallery_order_changed, this);
1044				this.displayed_gallery.on('change.order_direction', this.displayed_gallery_order_dir_changed, this);
1045			},
1046
1047			populate_sorting_fields: function(){
1048				// We display difference sorting buttons depending on what type of entities we're dealing with.
1049				var entity_types = this.sources.selected().pop().get('returns');
1050				if (_.indexOf(entity_types, 'image') !== -1) {
1051					this.fill_image_sortorder_options();
1052				}
1053				else {
1054					this.fill_gallery_sortorder_options();
1055				}
1056			},
1057
1058			create_sortorder_option: function(name, title){
1059				return new Ngg.DisplayTab.Models.SortOrder({
1060					name: name,
1061					title: title,
1062					value: name,
1063					selected: this.displayed_gallery.get('order_by') == name
1064				});
1065			},
1066
1067			fill_image_sortorder_options: function(){
1068				this.sortorder_options.reset();
1069				this.sortorder_options.push(this.create_sortorder_option('', 'None'));
1070				this.sortorder_options.push(this.create_sortorder_option('sortorder', 'Custom'));
1071				this.sortorder_options.push(this.create_sortorder_option(Ngg.DisplayTab.instance.image_key, 'Image ID'));
1072				this.sortorder_options.push(this.create_sortorder_option('filename', 'Filename'));
1073				this.sortorder_options.push(this.create_sortorder_option('alttext', 'Alt/Title Text'));
1074				this.sortorder_options.push(this.create_sortorder_option('imagedate', 'Date/Time'));
1075			},
1076
1077			fill_gallery_sortorder_options: function(){
1078				this.sortorder_options.reset();
1079				this.sortorder_options.push(this.create_sortorder_option('', 'None'));
1080				this.sortorder_options.push(this.create_sortorder_option('sortorder' ,'Custom'));
1081				this.sortorder_options.push(this.create_sortorder_option('name', 'Name'));
1082				this.sortorder_options.push(this.create_sortorder_option('galdesc', 'Description'));
1083			},
1084
1085			displayed_gallery_order_changed: function(e){
1086				this.sortorder_options.findWhere({value: e.get('order_by')}).set('selected', true);
1087			},
1088
1089
1090			displayed_gallery_order_dir_changed: function(e){
1091				this.sortdirection_options.findWhere({value: e.get('order_direction')}).set('selected', true);
1092			},
1093
1094			sortoption_changed: function(model){
1095				this.sortorder_options.each(function(item){
1096					item.set('selected', model.get('value') == item.get('value') ? true : false, {silent: true});
1097				});
1098
1099				this.displayed_gallery.set('sortorder', []);
1100
1101				var sort_by = model.get('value');
1102
1103				// If "None" was selected, then clear the "sortorder" property
1104				if (model.get('value').length == 0) {
1105					sort_by = 'sortorder';
1106				}
1107
1108				// Change the "sort by" parameter
1109				this.displayed_gallery.set('order_by', sort_by);
1110
1111				this.entities.reset();
1112				this.$el.find('a.sortorder').each(function(){
1113					var $item = $(this);
1114					if ($item.attr('value') == model.get('value'))
1115						$item.addClass('selected');
1116					else
1117						$item.removeClass('selected');
1118				});
1119			},
1120
1121			sortdirection_changed: function(model){
1122
1123				this.sortdirection_options.each(function(item){
1124					item.set('selected', model.get('value') == item.get('value') ? true : false, {silent: true});
1125				});
1126				this.displayed_gallery.set('order_direction', model.get('value'));
1127				this.entities.reset();
1128				this.$el.find('a.sortdirection').each(function(){
1129					var $item = $(this);
1130					if ($item.attr('value') == model.get('value'))
1131						$item.addClass('selected');
1132					else
1133						$item.removeClass('selected');
1134				});
1135			},
1136
1137			render: function(){
1138				this.$el.empty();
1139				this.populate_sorting_fields();
1140				this.$el.append('<strong>Sort By:</strong>');
1141				this.sortorder_options.each(function(item, index){
1142					var button = new this.Button({model: item, className: 'sortorder'});
1143					this.$el.append(button.render().el);
1144					if (this.sortorder_options.length-1 > index) {
1145						this.$el.append('<span class="separator">|</span>');
1146					}
1147				}, this);
1148				this.$el.append('<strong style="margin-left: 30px;">Order By:</strong>');
1149				this.sortdirection_options.each(function(item, index){
1150					var button = new this.Button({model: item, className: 'sortdirection'});
1151					this.$el.append(button.render().el);
1152					if (this.sortdirection_options.length-1 > index) {
1153						this.$el.append('<span class="separator">|</span>');
1154					}
1155				}, this);
1156				return this;
1157			},
1158
1159			Button: Backbone.View.extend({
1160				tagName: 'a',
1161
1162				initialize: function(options) {
1163                    this.options = options || {};
1164					_.each(this.options, function(value, key){
1165						this[key] = value;
1166					}, this);
1167				},
1168
1169				events: {
1170					click: 'clicked'
1171				},
1172
1173				clicked: function(e){
1174					e.preventDefault();
1175					this.model.set('selected', true);
1176				},
1177
1178				render: function(){
1179					this.$el.prop({
1180						value: this.model.get('value'),
1181						href: '#'
1182					});
1183					this.$el.text(this.model.get('title'));
1184					if (this.model.get('selected')) this.$el.addClass('selected');
1185					return this;
1186				}
1187			})
1188		}),
1189
1190		// Individual entity in the preview area
1191		EntityElement: Backbone.View.extend({
1192			tagName: 'li',
1193
1194			events: {
1195				drop: 'item_dropped'
1196			},
1197
1198			initialize: function(options) {
1199                this.options = options || {};
1200				_.each(this.options, function(value, key){
1201					this[key] = value;
1202				}, this);
1203				this.model.on('change', this.render, this);
1204				if (this.model.get('sortorder') == 0) {
1205					this.model.set('sortorder', -1, {silent: true});
1206				}
1207				this.id = this.model.get('id_field')+'_'+this.model.entity_id()
1208			},
1209
1210			item_dropped: function(e, index){
1211				Ngg.DisplayTab.instance.displayed_gallery.set('order_by', 'sortorder');
1212				//Ngg.DisplayTab.instance.displayed_gallery.set('order_direction', 'ASC');
1213				this.model.set('sortorder', index);
1214			},
1215
1216			render: function(){
1217				this.$el.empty();
1218				var image_container = $('<div/>').addClass('image_container');
1219				var alt_text = this.model.alttext().replace(/\\&/g, '&').replace(/\\'/g, "'");
1220				var timestamp = new Date().getTime();
1221				image_container.attr({
1222					title: alt_text,
1223					style: "background-image: url('"+this.model.get('thumb_url')+"?timestamp"+timestamp+"')"
1224				}).css({
1225					width:			this.model.get('max_width'),
1226					height:			this.model.get('max_height'),
1227					'max-width':	this.model.get('max_width'),
1228					'max-height':	this.model.get('max_height')
1229				});
1230
1231				this.$el.append(image_container).addClass('ui-state-default');
1232
1233				// Add exclude checkbox
1234				var exclude_container = $('<div/>').addClass('exclude_container');
1235				exclude_container.append('Exclude?');
1236				var exclude_checkbox = new this.ExcludeCheckbox({model: this.model});
1237				exclude_container.append(exclude_checkbox.render().el);
1238				image_container.append(exclude_container);
1239				return this;
1240			},
1241
1242			ExcludeCheckbox: Backbone.View.extend({
1243				tagName: 'input',
1244
1245				events: {
1246					'change': 'entity_excluded'
1247				},
1248
1249				type_set: false,
1250
1251				entity_excluded: function(e){
1252					this.model.set('exclude', e.target.checked);
1253				},
1254
1255				initialize: function(options) {
1256                    this.options = options || {};
1257					_.each(this.options, function(value, key){
1258						this[key] = value;
1259					}, this);
1260					this.model.on('change:exclude', this.render, this);
1261				},
1262
1263				render: function(){
1264					if (!this.type_set) {
1265						this.$el.attr('type', 'checkbox');
1266						this.type_set = true;
1267					}
1268					if (this.model.is_excluded()) this.$el.prop('checked', true);
1269					else this.$el.prop('checked', false);
1270					return this;
1271				}
1272			})
1273		})
1274	});
1275
1276
1277	// Additional source configuration views. These will be rendered dynamically by PHP.
1278	// Adapters will add them.
1279	Ngg.DisplayTab.Views.GalleriesSource = Backbone.View.extend({
1280		tagName: 'tbody',
1281
1282		initialize: function(){
1283			this.galleries = Ngg.DisplayTab.instance.galleries;
1284		},
1285
1286		render: function(){
1287			var select = new Ngg.Views.Chosen({
1288				collection: this.galleries,
1289				placeholder: '<?php _e('Select a gallery', 'nggallery'); ?>',
1290				multiple: true,
1291				width: 500
1292			});
1293			var html = $('<tr><td><label><?php _e('Galleries', 'nggallery'); ?></label></td><td class="galleries_column"></td></tr>');
1294			this.$el.empty();
1295			this.$el.append(html);
1296			this.$el.find('.galleries_column').append(select.render().el);
1297			return this;
1298		}
1299	});
1300
1301	Ngg.DisplayTab.Views.AlbumsSource = Backbone.View.extend({
1302		tagName: 'tbody',
1303
1304		initialize: function(){
1305			this.albums 	= Ngg.DisplayTab.instance.albums;
1306		},
1307
1308		render: function(){
1309			var album_select = new Ngg.Views.Chosen({
1310				collection: this.albums,
1311				multiple: true,
1312				placeholder: 'Select an album',
1313				text_field: 'name',
1314				width: 500
1315			});
1316			this.$el.empty();
1317			this.$el.append('<tr><td><label><?php _e('Albums', 'nggallery'); ?></label></td><td class="albums_column"></td></tr>');
1318			this.$el.find('.albums_column').append(album_select.render().el);
1319			return this;
1320		}
1321	});
1322
1323	Ngg.DisplayTab.Views.TagsSource = Backbone.View.extend({
1324		tagName: 'tbody',
1325
1326		initialize: function(){
1327			this.tags	= Ngg.DisplayTab.instance.tags;
1328		},
1329
1330		render: function(){
1331			var tag_select = new Ngg.Views.Chosen({
1332				collection: this.tags,
1333				multiple: true,
1334				placeholder: 'Select a tag',
1335				text_field: 'name',
1336				width: 500
1337			});
1338			this.$el.empty();
1339			this.$el.append('<tr><td><label>Tags</label></td><td class="tags_column"></td></tr>');
1340			this.$el.find('.tags_column').append(tag_select.render().el);
1341			return this;
1342		}
1343	});
1344
1345	Ngg.DisplayTab.Views.Recent_imagesSource = Backbone.View.extend({
1346		tagName: 'tbody',
1347
1348		initialize: function(){
1349			this.displayed_gallery		= Ngg.DisplayTab.instance.displayed_gallery;
1350			this.maximum_entity_count	= Ngg.DisplayTab.instance.displayed_gallery.get('maximum_entity_count');
1351			this.displayed_gallery.set('container_ids', []);
1352		},
1353
1354		render: function(){
1355			var self = this;
1356			var edit_field = $('<input/>').prop({
1357				type: 'text',
1358				value: this.maximum_entity_count,
1359				name: 'maximum_entity_count'
1360			});
1361
1362			edit_field.change(function () {
1363				self.displayed_gallery.set('maximum_entity_count', $(this).val());
1364			});
1365
1366			this.$el.empty();
1367			this.$el.append('<tr><td><label># of Images To Display</label></td><td class="recent_images_column"></td></tr>');
1368			this.$el.find('.recent_images_column').append(edit_field);
1369			return this;
1370		}
1371	});
1372
1373	Ngg.DisplayTab.Views.Random_imagesSource = Backbone.View.extend({
1374		tagName: 'tbody',
1375
1376		initialize: function(){
1377			this.displayed_gallery		= Ngg.DisplayTab.instance.displayed_gallery;
1378			this.maximum_entity_count	= Ngg.DisplayTab.instance.displayed_gallery.get('maximum_entity_count');
1379			this.displayed_gallery.set('container_ids', []);
1380		},
1381
1382		render: function(){
1383			var self = this;
1384			var edit_field = $('<input/>').prop({
1385				type: 'text',
1386				value: this.maximum_entity_count,
1387				name: 'maximum_entity_count'
1388			});
1389
1390			edit_field.change(function () {
1391				self.displayed_gallery.set('maximum_entity_count', $(this).val());
1392			});
1393
1394			this.$el.empty();
1395			this.$el.append('<tr><td><label># of Images To Display</label></td><td class="random_images_column"></td></tr>');
1396			this.$el.find('.random_images_column').append(edit_field);
1397			return this;
1398		}
1399	});
1400
1401	Ngg.DisplayTab.Views.SaveButton = Backbone.View.extend({
1402		el: '#save_displayed_gallery',
1403
1404		errors_el: '#errors',
1405
1406		displayed_gallery: null,
1407
1408		events: {
1409			click: 'clicked'
1410		},
1411
1412		initialize: function(){
1413			this.displayed_gallery	= Ngg.DisplayTab.instance.displayed_gallery;
1414			this.entities			= Ngg.DisplayTab.instance.entities;
1415			this.render();
1416		},
1417
1418		clicked: function(){
1419			this.set_display_settings();
1420			var request = <?php echo $sec_token?>;
1421			request = _.extend(request, {
1422				action: 'save_displayed_gallery',
1423				displayed_gallery: JSON.stringify(this.displayed_gallery.toJSON())
1424			});
1425
1426			var self = this;
1427			$.post(photocrati_ajax.url, request, function(response){
1428				if (!_.isObject(response)) response = JSON.parse(response);
1429				if (response['validation_errors'] != undefined) {
1430					$(self.errors_el).empty().append(response.validation_errors);
1431				}
1432				else if (response['error'] != undefined) {
1433					alert(response.error);
1434				}
1435				else {
1436					var id_field = response.displayed_gallery.id_field;
1437					var id = response.displayed_gallery[id_field];
1438					self.displayed_gallery.set('id', id);
1439					var editor = parent.tinyMCE.activeEditor;
1440					var preview_url = ngg_displayed_gallery_preview_url + '/id--'+id;
1441					var snippet = "<img class='ngg_displayed_gallery mceItem' src='" + preview_url + "'/>";
1442					if (editor.getContent().indexOf(preview_url) < 0)
1443						editor.execCommand('mceInsertContent', false, snippet);
1444					else {
1445						$(editor.contentDocument).find(".ngg_displayed_gallery[src='"+preview_url+"']").attr('src', preview_url);
1446					}
1447					close_attach_to_post_window();
1448				}
1449			});
1450		},
1451
1452		set_display_settings: function(){
1453			var display_type = this.displayed_gallery.get('display_type');
1454			if (display_type) {
1455				// Collect display settings
1456				var form = $("form[rel='"+display_type+"']");
1457				var display_settings	= (function(item){
1458					var obj = {};
1459                    $.each(item.serializeArray(), function(key, item) {
1460						var parts = item.name.split('[');
1461						var current_obj = obj;
1462						for (var i=0; i<parts.length; i++) {
1463							var part = parts[i].replace(/\]$/, '');
1464							if (!current_obj[part]) {
1465								if (i == parts.length-1)
1466									current_obj[part] = item.value;
1467								else
1468									current_obj[part] = {};
1469							}
1470							current_obj = current_obj[part];
1471						}
1472					});
1473					return obj;
1474				})(form);
1475
1476				// Set display settings for displayed gallery
1477				this.displayed_gallery.set('display_settings', display_settings[display_type]);
1478			}
1479		},
1480
1481		render: function(){
1482			return this;
1483		}
1484	});
1485
1486    /*****************************************************************************
1487     * APPLICATION
1488    **/
1489    Ngg.DisplayTab.App = Backbone.View.extend({
1490        /**
1491         * Initializes the DisplayTab object
1492        **/
1493        initialize: function(){
1494			// TODO: We're currently fetching ALL galleries, albums, and tags
1495			// in one shot. Instead, we should display the displayed_gallery's
1496			// containers, if there are any, otherwise get the first 100 or so.
1497			// We can then use AJAX to fetch the rest of batches.
1498            this.displayed_gallery = new Ngg.DisplayTab.Models.Displayed_Gallery(
1499				<?php echo $displayed_gallery ?>
1500			);
1501
1502			this.original_displayed_gallery = new Ngg.DisplayTab.Models.Displayed_Gallery(
1503				<?php echo $displayed_gallery ?>
1504			);
1505            this.galleries = new Ngg.DisplayTab.Models.Gallery_Collection(
1506				<?php echo $galleries ?>
1507			);
1508            this.albums = new Ngg.DisplayTab.Models.Album_Collection(
1509				<?php echo $albums ?>
1510			);
1511            this.tags = new Ngg.DisplayTab.Models.Tag_Collection(
1512				<?php echo $tags ?>
1513			);
1514            this.sources = new Ngg.DisplayTab.Models.Source_Collection(
1515				<?php echo $sources ?>
1516			)
1517			this.display_types = new Ngg.DisplayTab.Models.Display_Type_Collection(
1518				<?php echo $display_types ?>
1519			);
1520			this.display_type_order_base = <?php echo NGG_DISPLAY_PRIORITY_BASE; ?>;
1521			this.display_type_order_step = <?php echo NGG_DISPLAY_PRIORITY_STEP; ?>;
1522			this.entities = new Ngg.DisplayTab.Models.Entity_Collection();
1523			this.entities.extra_data.displayed_gallery = this.displayed_gallery;
1524			this.image_key = "<?php echo $image_primary_key ?>";
1525
1526			// Pre-select current displayed gallery values
1527			if (this.displayed_gallery.get('source')) {
1528
1529				// Pre-select source
1530				if (this.displayed_gallery.get('source')) {
1531					var source = this.sources.find(function(item){
1532						return item.get('name') == this.displayed_gallery.get('source');
1533					}, this);
1534					if (source) source.set('selected', true);
1535				}
1536
1537				// Pre-select containers
1538				if (this.displayed_gallery.get('container_ids')) {
1539					_.each(this.displayed_gallery.get('container_ids'), function(id){
1540						var container = this[this.displayed_gallery.get('source')].find(function(item){
1541							return item.id == id;
1542						}, this);
1543						if (container) container.set('selected', true);
1544					}, this);
1545				}
1546
1547				// Pre-select display type
1548				if (this.displayed_gallery.get('display_type')) {
1549					var display_type = this.display_types.find(function(item){
1550						return item.get('name') == this.displayed_gallery.get('display_type');
1551					}, this);
1552					if (display_type) display_type.set('selected', true);
1553				}
1554			}
1555
1556            // Bind to the 'selected' event for each of the collections, and update the displayed
1557            // gallery object's 'container_ids' attribute when something has changed
1558            collections = ['galleries', 'albums', 'tags'];
1559            _.each(collections, function(collection){
1560                this[collection].on('selected', function(){this.update_selected_containers(collection);}, this);
1561            }, this);
1562
1563			// Bind to the 'selected' event for the display types collection, updating the displayed gallery
1564			this.display_types.on('change:selected', function(){
1565				this.displayed_gallery.set('display_type', this.display_types.selected_value());
1566			}, this);
1567
1568			// Bind to the 'selected' event for the source, updating the displayed gallery
1569			this.sources.on('selected', function(){
1570				this.displayed_gallery.set('source', this.sources.selected_value());
1571
1572				// If the source changed, and it's not the set to the original value, then
1573				// exclusions get's set to []
1574				if (this.sources.selected_value() != this.original_displayed_gallery.get('source'))
1575					this.displayed_gallery.set('exclusions', this.entities.excluded_ids());
1576
1577				// Otherwise, we revert to the original exclusions
1578				else
1579					this.displayed_gallery.set('exclusions', this.original_displayed_gallery.get('exclusions'));
1580
1581                // special exemption: these should default to a reasonable limit
1582                if (this.sources.selected_value() == 'random_images' || this.sources.selected_value() == 'recent_images') {
1583                    this.displayed_gallery.set('maximum_entity_count', 20);
1584                }
1585
1586				// Reset everything else
1587				this.galleries.deselect_all();
1588				this.albums.deselect_all();
1589				this.tags.deselect_all();
1590
1591				// If the selected source is incompatible with the current display type, then
1592				// display a new list
1593				var selected_display_type = this.display_types.selected();
1594				var selected_source		  = this.sources.selected();
1595				if (selected_display_type.length > 0 && selected_source.length > 0) {
1596					selected_display_type = selected_display_type[0];
1597					selected_source       = selected_source[0];
1598					if (!selected_display_type.is_compatible_with_source(selected_source))
1599						this.display_types.deselect_all();
1600						if (this.display_type_selector) this.display_type_selector.render();
1601				}
1602				if (this.preview_area) this.preview_area.render();
1603			}, this);
1604
1605			// Synchronize changes made to entities with the displayed gallery
1606			this.entities.on('change:exclude finished_fetching', function(){
1607				//this.displayed_gallery.set('sortorder', this.entities.entity_ids());
1608				this.displayed_gallery.set('exclusions', this.entities.excluded_ids());
1609			}, this);
1610
1611			// Monitor events in other tabs and respond as appropriate
1612			if (window.Frame_Event_Publisher) {
1613				var app = this;
1614
1615				// New gallery event
1616				Frame_Event_Publisher.listen_for('attach_to_post:new_gallery', function(){
1617					app.galleries.reset();
1618					app.galleries.fetch();
1619				});
1620
1621				// A change has been made using the "Manage Galleries" page
1622				Frame_Event_Publisher.listen_for('attach_to_post:manage_galleries attach_to_post:manage_images', function(data){
1623
1624					// Refresh the list of galleries
1625					app.galleries.reset();
1626					app.galleries.fetch();
1627
1628					// If we're viewing galleries or images, then we need to refresh the entity list
1629					var selected_source = app.sources.selected().pop();
1630					if (selected_source) {
1631						if (_.indexOf(selected_source.get('returns'), 'image') >= 0 ||
1632							_.indexOf(selected_source.get('returns'), 'gallery')) {
1633							app.entities.reset();
1634						}
1635					}
1636				});
1637
1638				// A change has been made using the "Manage Albums" page
1639				Frame_Event_Publisher.listen_for('attach_to_post:manage_album', function(data){
1640					// Refresh the list of albums
1641					app.albums.reset();
1642					app.albums.fetch();
1643
1644					// If we're viewing albums, then we need to refresh the entity list
1645					var selected_source = app.sources.selected().pop();
1646					if (selected_source) {
1647						if (_.indexOf(selected_source.get('returns'), 'album') >= 0) {
1648							app.entities.reset();
1649						}
1650					}
1651				});
1652
1653				/…

Large files files are truncated, but you can click here to view the full file