PageSourceSearch

https://paevo.ru/plugins/system/easyslider/assets/js/lib/view.js

js paevo.ru collected 2026-10-08 14:31:09 UTC 21,936 bytes, 738 lines download raw bytes

1/* Backbone Extension */
2void function ( root, factory ) {
3
4	// Set up Backbone appropriately for the environment. Start with AMD.
5	if ( typeof define === 'function' && define.amd ) {
6		define([ 'jquery', 'underscore', 'backbone', 'exports' ], function ( $, _, Backbone, exports ) {
7			// Export global even in AMD case in case this script is loaded with
8			// others that may still expect a global Backbone.
9			root.B = factory(root, exports, $, _, Backbone);
10		});
11
12		// Next for Node.js or CommonJS.
13	} else if ( typeof exports !== 'undefined' ) {
14		var $ = require('jquery');
15		var _ = require('underscore');
16		var Backbone = require('backbone');
17		factory(root, exports, $, _, Backbone);
18
19		// Finally, as a browser global.
20	} else {
21		root.B = factory(root, root.B || {}, root.jQuery, root._, root.Backbone);
22	}
23
24}(this, function ( root, B, $, _, Backbone ) {
25
26	var View, CollectionView, DataBinder;
27
28	_.mixin({
29		isPrototypeOf: function( child, parent ) {
30			if ( !child || !parent )
31				return false;
32			var result = false;
33			var proto = child.prototype;
34			while ( proto ) {
35				if ( proto == parent.prototype ) {
36					result = true;
37					break;
38				}
39				proto = proto.__proto__;
40			}
41			return result;
42		},
43		setPrototypeOf: function( child, prototype ) {
44			if (_.isFunction(Object.setPrototypeOf))
45				Object.setPrototypeOf(child.prototype || child, prototype);
46			else
47				(child.prototype || child).__proto__ = prototype;
48			return child
49		},
50		extendPrototype: function( child, prototype ) {
51			_.extend(child.prototype, prototype)
52			return child
53		},
54	});
55
56	// Cached backbone prototypes and methods
57	var BackboneModel = Backbone.Model;
58	var BackboneCollection = Backbone.Collection;
59	var BackboneEvents = Backbone.Events;
60	var BackboneView = Backbone.View;
61	var BackboneViewEnsureElement = BackboneView.prototype._ensureElement;
62
63	// Use this element to store unused element of deleted views
64	var $reusableElements = $('<div>');
65
66
67	/* --- View --- */
68	View = B.View = function() {
69		function View( options ) {
70
71			if ( !(this instanceof View) )
72				return new View(options);
73
74			var options = (options || {});
75
76			this.__ready = _.once(this.__ready);
77			this.subViews = [];
78
79			_.extend(this, _.pick(options, 'template', 'views', 'bindings', 'events', 'modelEvents', 'superView'));
80
81			if ( this.template )
82				switch ( typeof this.template ) {
83					case 'string':
84						this.template = this.template.trim();
85						if (!this.template.match('<') && !this.template.match(/\s/))
86							this.template = $(this.template).html();
87						break;
88				}
89
90			Object.defineProperties(this, {
91				'rootView': {
92					get: function() {
93						var root = this;
94						var parent = this.superView;
95						while ( parent ) {
96							root = parent;
97							parent = parent.superView;
98						}
99						return root;
100					}
101				}
102			});
103
104			BackboneView.call(this, options);
105			this.el && this.el.parentNode && this.__ready();
106		}
107
108		_.extend(View.prototype, {
109			bindings       : false,
110			initialize     : _.noop,
111			ready          : _.noop,
112			_ensureElement : function() {
113				BackboneViewEnsureElement.call(this);
114				this.render();
115				// This function is called right before initialize
116				// So when initialize is called, element HTML is ready to be bound to views
117			},
118			setModel       : function( model ) {
119				if ( !model )
120					return;
121				if ( this.modelEvents )
122					_(this.modelEvents).each(function( handler, bind ) {
123						bind = _([ 'event', 'selector' ]).object(_(bind.match(/(.*)\s(.*)/) || [ 0, bind ]).rest(1));
124						handler = _.isString(handler) ? this[ handler ] : handler;
125
126						var unbindModel = this.model && bind.selector ? this.model.get(bind.selector) : this.model;
127						var bindModel = (bind.selector && this.model) ? this.model.get(bind.selector) : model;
128
129						unbindModel && this.stopListening(unbindModel, bind.event);
130						this.listenTo(bindModel, bind.event, handler);
131
132					}, this);
133				this.model = model;
134				this.setBindingModel(this.model)
135				this.trigger('set:model', this.model)
136			},
137			setBindingModel: function( model ) {
138				!this.__dataBinding && console.warn("Data-binding hasn't been initialized for this view.");
139				this.__dataBinding && this.__dataBinding.setModel(model);
140				this.trigger('set:binding:models', this.__dataBinding.models)
141				return this;
142			},
143			__ready        : function() {
144				this.initSubViews();
145				this.initDataBinding();
146				this.setModel(this.model);
147				this.ready();
148			},
149			freeze         : function() {
150				this._frozen = true;
151				this.trigger('freeze');
152				return this;
153			},
154			unfreeze       : function() {
155				this._frozen = false;
156				this.trigger('unfreeze');
157				return this;
158			},
159			preventDefault : function( e ) {
160				e.preventDefault();
161			},
162			stopPropagation: function( e ) {
163				e.stopPropagation();
164			},
165			defer          : function( fn, context ) {
166				_.defer(_.bind(fn, context || this));
167				return this;
168			},
169			delay          : function( fn, delay, context ) {
170				_.delay(_.bind(fn, context || this), delay);
171				return this;
172			},
173			render         : function() {
174				return this.__render.apply(this, arguments);
175			},
176			__render       : function() {
177				this.trigger('before:render');
178				this.template && this.$el.html(this.template);
179				this.trigger('render');
180				return this;
181			},
182
183			initDataBinding: function() {
184				if ( this.__dataBinding )
185					return;
186				this.__dataBinding = new DataBinder(this, this.model, { bindings: _.result(this, 'bindings') || [] });
187			},
188			initSubViews   : function() {
189				var regex = (/^(\w+)(?: (collection|model):([$\w.]+))?\s*>\s*(.*)$/);
190				_.each(this.views, function( view, define ) {
191					var match = define.match(regex);
192					if ( !match )
193						throw "View definition syntax error: '" + define + "'";
194					var key = match[ 1 ];
195					var type = match[ 2 ];
196					var attr = match[ 3 ];
197					var selector = match[ 4 ];
198					var options = {};
199					type && attr && (options[ type ] = (attr == '$model') ? this.model : (attr == '$collection') ? this.collection : this.model.get(attr));
200					this[ key ] = this.attachView(view, selector, options);
201				}, this);
202			},
203			hasView        : function( view ) {
204				return this.subViews && this.subViews.indexOf(view) > -1;
205			},
206			attachView     : function( view, selector, options ) {
207				var el = this.$(selector).get(0);
208				if ( !el )
209					throw 'No element found for selector "' + selector + '"';
210				if ( _.isFunction(view) ) {
211					options || (options = {});
212					options.el = el;
213					options.superView = this;
214					view = new view(options);
215				}
216				else {
217					view.setElement(el);
218					view.superView = this;
219					this.hasView(view) || this.subViews.push(view);
220				}
221				view.__ready();
222				return view;
223			},
224			appendView     : function( view ) {
225				this.hasView(view) || this.subViews.push(view);
226				view.superView = this;
227				view.$el.appendTo(this.el);
228				view.__ready();
229				return view;
230			},
231			prependView    : function( view ) {
232				this.hasView(view) || this.subViews.push(view);
233				view.superView = this;
234				view.$el.prependTo(this.el);
235				view.__ready();
236				return view;
237			},
238			remove         : function() {
239				this.trigger('before:remove');
240				BackboneView.prototype.remove.call(this);
241				this.superView &&
242				this.superView.subViews &&
243				_(this.superView.subViews).each(function( subView, index ) {
244					if ( subView && subView.cid == this.cid )
245						this.superView.subViews.splice(index, 1);
246				}, this);
247				this.trigger('remove');
248				return this;
249			}
250		});
251		_.setPrototypeOf(View, BackboneView.prototype);
252		View.extend = BackboneView.extend;
253		return View;
254	}()
255
256	/* --- CollectionView --- */
257	CollectionView = B.CollectionView = function() {
258		function CollectionView( options ) {
259
260			if ( !(this instanceof CollectionView) )
261				return new CollectionView(options);
262
263			this.previousSubViews = {};
264			this.options = options || {};
265			this._debounceReset = _.debounce(this.reset);
266			options.collection || (options.collection = new BackboneCollection);
267			View.call(this, options);
268		}
269
270		_.extend(CollectionView.prototype, {
271			itemView       : View,
272			itemViews      : {},
273			_reverseOrder  : false,
274			_ensureElement : function() {
275				BackboneViewEnsureElement.call(this);
276				this.__render();
277				// This function is called right before initialize
278				// So when initialize is called, element HTML is ready to be bound to views
279				this.getItemTemplate();
280			},
281			__ready        : function() {
282				this.setCollection(this.collection);
283				this.ready();
284			},
285			getItemTemplate: function() {
286				if (!_.isEmpty(this.itemViews)) {
287					_.each(this.el.children, function( child ) {
288						var rel = child.getAttribute('data-relation');
289						if (rel && this.itemViews[rel]) {
290							var template = child.innerHTML.trim();
291							if ( this.itemViews[rel].prototype == View.prototype )
292								this.itemViews[rel] = this.itemViews[rel].extend();
293							_.extend(this.itemViews[rel].prototype, {
294								template  : template,
295								tagName   : child.tagName,
296								className : child.className,
297								attributes: _(child.attributes)
298										.chain()
299										.values()
300										.map(function( attr ) {
301											return attr.name;
302										})
303										.object([])
304										.mapObject(function( value, key ) {
305											return child.getAttribute(key);
306										})
307										.value()
308							});
309						}
310					}, this);
311				}
312				else {
313					var child = this.el.children[ 0 ];
314					if ( child ) {
315						var template = child.innerHTML.trim();
316						if ( this.itemView.prototype == View.prototype )
317							this.itemView = this.itemView.extend();
318						_.extend(this.itemView.prototype, {
319							template  : template,
320							tagName   : child.tagName,
321							className : child.className,
322							attributes: _(child.attributes)
323									.chain()
324									.values()
325									.map(function( attr ) {
326										return attr.name;
327									})
328									.object([])
329									.mapObject(function( value, key ) {
330										return child.getAttribute(key);
331									})
332									.value()
333						});
334					}
335				}
336				this.$el.empty();
337			},
338			setCollection  : function( collection ) {
339				if ( !collection )
340					return;
341				if ( this.collection ) {
342					this.stopListening(this.collection, 'add', this.reset);
343					this.stopListening(this.collection, 'reset', this.reset);
344					this.stopListening(this.collection, 'sort', this.reset);
345					this.trigger('unset:collection', this.collection);
346				}
347				this.collection = collection;
348				this.listenTo(this.collection, 'add', this.reset);
349				this.listenTo(this.collection, 'reset', this.reset);
350				this.listenTo(this.collection, 'sort', this.reset);
351				this.trigger('set:collection', this.collection);
352				this.reset();
353			},
354			add            : function( model ) {
355				if ( this.previousSubViews[ model.cid ] ) {
356					var view = this.previousSubViews[ model.cid ];
357					delete this.previousSubViews[ model.cid ];
358				}
359				else {
360					var viewClass = this.itemView;
361					if (model.get('_rel') && this.itemViews[model.get('_rel')])
362						viewClass = this.itemViews[model.get('_rel')];
363					var view = new viewClass({ model: model });
364				}
365				view.mid = model.cid;
366				this._reverseOrder ?
367						this.prependView(view) :
368						this.appendView(view);
369				return view;
370			},
371			reset          : function( models ) {
372				while ( this.subViews.length ) {
373					var view = this.subViews.pop();
374					this.previousSubViews[ view.mid ] = view;
375				}
376				this.collection.each(this.add, this);
377
378				for ( var key in this.previousSubViews ) {
379					this.previousSubViews[ key ].$el.appendTo($reusableElements);
380					//delete this.previousSubViews[key];
381				}
382				return this;
383			}
384		})
385		_.setPrototypeOf(CollectionView, View.prototype);
386		CollectionView.extend = View.extend;
387		return CollectionView;
388	}()
389
390	/* --- Data Binder --- */
391	DataBinder = B.DataBind = function() {
392
393		// Parse element data binding definition string
394		var bindingRegex = (/(?:(\w+):)?({.*}|[^;]+);?/ig);
395
396		var parseb = function parseBindingString( string ) {
397			var bindRegex = (/\s*(\w+)\s*:\s*([^,{]+(?!\()|\{(.+)\},(?=\s*\w)|\w+\(.+\)),?/g);
398			var match, obj = Object.create(null, {});
399			var string = string
400					.replace(/\s+/, ' ')
401					.replace(/(.*)\}$/, '$1},a');
402			while ( match = bindRegex.exec(string) ) {
403				var key          = match[ 1 ],
404						value        = match[ 2 ],
405						nestedValues = match[ 3 ];
406				if ( nestedValues )
407					value = parseBindingString(nestedValues);
408				obj[ key ] = value;
409			}
410			return obj;
411		};
412
413		function DataBinder( view, models, options ) {
414
415			if ( this instanceof DataBinder === false )
416				return new DataBinder(view, models, options);
417
418			options || (options = {});
419
420			this.options = _(options).defaults({
421				bindings: []
422			});
423
424			this.view = null;
425			this.models = [];
426			this.bindings = [];
427
428			_.bindAll(this,
429					'inputEventHandler',
430					'changeEventHandler'
431			);
432
433			this.setupView(view);
434			this.setModel(models);
435
436			return this;
437		};
438
439		_.setPrototypeOf(DataBinder, BackboneEvents);
440
441		_.extend(DataBinder.prototype, {
442
443			setModel: function( models ) {
444				var self = this;
445				_(self.models).each(function( model ) {
446					self.stopListening(model);
447				}, self);
448				self.models = _.filter(_.isArray(models) ? models : [ models ], function( model ) {
449					return model instanceof BackboneModel;
450				});
451				_(self.models).each(function( model ) {
452					_.each(self.bindingsIndex, function( bindings, attr ) {
453						self.listenTo(model, 'change:' + attr, function( model, value, options ) {
454							self.updateView(attr)
455						});
456					})
457				}, self);
458				self.updateView();
459				self.trigger('set:models', self.models)
460				return this;
461			},
462
463			setupView : function( view ) {
464				if ( view instanceof Backbone.View == false )
465					return false;
466
467				_(this.bindings).each(function( binding ) {
468					binding.$el.off(binding.events || 'change', this.changeEventHandler);
469				}, this);
470
471				var self = this;
472				var binding, $children, matched, type, value;
473
474				this.view = view;
475				this.bindings = [];
476				this.bindingsIndex = {};
477
478				_(this.options.bindings).each(function( opt ) {
479					$children = view.$(opt.selector);
480					($children.length ? $children : view.$el).each(function( index, el ) {
481						_(_.isString(opt.attr) ? [ opt.attr ] : opt.attr).each(function( attribute, key ) {
482							binding = _({
483								$el : $(el),
484								el  : el,
485								type: opt.type,
486								attr: attribute
487							}).defaults(opt);
488							if ( _.isString(key) )
489								binding.key = key;
490
491							self.bindings.push(binding);
492
493							boundAttributes = [ binding.attr ];
494							_(boundAttributes).each(function( attr ) {
495								self.bindingsIndex[ attr ] || (self.bindingsIndex[ attr ] = []);
496								self.bindingsIndex[ attr ].indexOf(binding) == -1 && self.bindingsIndex[ attr ].push(binding);
497							});
498						});
499					});
500				});
501
502				var hasBindingEl;
503				if ( view.$el.attr('data-bind') )
504					parseBinding(view.el);
505				while ( hasBindingEl = view.el.querySelector('[data-bind]') )
506					parseBinding(hasBindingEl);
507
508				function parseBinding( el ) {
509
510					var $el = $(el);
511					var syntax = ($el.attr('data-bind') || '').replace(/\s+/g, '');
512					bindingRegex.lastIndex = 0;
513					$el.removeAttr('data-bind');
514
515					matching : while ( matched = bindingRegex.exec(syntax) ) {
516						type = matched[ 1 ];
517						value = matched[ 2 ];
518
519						if ( !type )
520							if ( $el.is('input[type="checkbox"]') )
521								type = 'checked';
522							else if ( $el.is('input[type="radio"]') )
523								type = 'radio';
524							else if ( $el.is('input,select,textarea') )
525								type = 'value';
526							else if ( $el.is('[contenteditable]') )
527								type = 'html';
528							else
529								type = 'text';
530
531						if ( type == 'model' ) {
532							_.defer(function( view, value, template ) {
533								var childView = view[ value + 'View' ] = new View({
534									el: el,
535									template: template
536								});
537								view.on('set:model', function() {
538									var model = value == 'this' || value == '$model' ? view.model : view.model.get(value);
539									childView.setModel(model);
540								});
541								view.model && view.trigger('set:model', view.model);
542							}, view, value, el.innerHTML);
543
544							el.innerHTML = '';
545							continue matching;
546						}
547						if ( type == 'collection' ) {
548							_.defer(function( view, value, template ) {
549								var childView = view[ value + 'View' ] = new CollectionView({
550									el      : el,
551									template: template
552								});
553								if (value == 'this' || value == '$collection') {
554									view.on('set:collection', function() {
555										childView.setCollection(view.collection);
556									})
557									view.collection && view.trigger('set:collection', view.collection);
558								}
559								else {
560									view.on('set:model', function() {
561										childView.setCollection(view.model.get(value));
562									});
563									view.model && view.trigger('set:model', view.model);
564								}
565							}, view, value, el.innerHTML);
566
567							el.innerHTML = '';
568							continue matching;
569						}
570
571						nested = value.match(/{(.*)}/);
572						value = nested ? nested[ 1 ] : value;
573						_(nested ? nested[ 1 ].split(',') : [ value ]).each(function( string ) {
574							split = string.split(':');
575							binding = {
576								$el : $el,
577								el  : el,
578								type: type,
579								attr: split.length == 1 ? split[ 0 ] : split[ 1 ]
580							}
581							if ( split.length == 2 )
582								binding.key = split[ 0 ];
583							if ( $el.data('bind-events') )
584								binding.events = $el.data('bind-events');
585
586							self.bindings.push(binding);
587
588							boundAttributes = [ binding.attr ];
589							_(boundAttributes).each(function( attr ) {
590								self.bindingsIndex[ attr ] || (self.bindingsIndex[ attr ] = []);
591								self.bindingsIndex[ attr ].indexOf(binding) == -1 && self.bindingsIndex[ attr ].push(binding);
592							});
593						});
594					}
595				}
596
597				_(this.bindings).each(function( binding ) {
598					//binding.$el.on( 'input', { binding: binding }, this.inputEventHandler);
599					binding.$el.on(binding.events || 'change', { binding: binding }, this.changeEventHandler);
600
601					binding.$el
602							.removeAttr('data-bind')
603							.removeAttr('data-bind-events');
604				}, this);
605			},
606			updateView: function( attr ) {
607
608				var self = this;
609				var value, originalValue, setter;
610
611				_(attr ? self.bindingsIndex[ attr ] : self.bindings).each(function( binding ) {
612
613					value = self.getData(binding.attr);
614					if ( value instanceof BackboneModel || value instanceof BackboneCollection ) {
615						originalValue = value;
616						value = value.toJSON();
617					}
618
619					setter = binding.set || self.setters[ binding.type ] || null;
620
621					if ( setter ) {
622						if ( _.isFunction(binding.parse) )
623							value = binding.parse.call(binding, value, binding.key, self.view);
624						setter(binding.$el, value, binding.key);
625					}
626				});
627			},
628
629			setters: {
630				text    : function( $el, value ) {
631					($el.text() === value) || $el.text(_.isUndefined(value) ? ' ' : value);
632				},
633				html    : function( $el, value ) {
634					($el.html() === value) || $el.html(_.isUndefined(value) ? ' ' : value);
635				},
636				value   : function( $el, value ) {
637					($el.val() === value) || $el.val(_.isUndefined(value) ? ' ' : value);
638				},
639				attr    : function( $el, value, key ) {
640					if ( !key ) return;
641					$el.attr(key, value);
642				},
643				prop    : function( $el, value, key ) {
644					if ( !key ) return;
645					$el.prop(key, value);
646				},
647				style   : function( $el, value, key ) {
648					key ? $el.css(key, value) : $el.css(value || {});
649				},
650				class   : function( $el, value, key ) {
651					$el[ value ? 'addClass' : 'removeClass' ](key);
652				},
653				checked : function( $el, value ) {
654					$el.attr('checked', value);
655				},
656				radio : function( $el, value ) {
657					$el.attr('checked', $el.val() == value ? true : false );
658				},
659				enabled : function( $el, value ) {
660					$el.attr('disabled', !value);
661				},
662				disabled: function( $el, value ) {
663					$el.attr('disabled', value);
664				},
665				toggle  : function( $el, value ) {
666					$el.toggle(value);
667				},
668				visible : function( $el, value ) {
669					$el.toggle(!!value);
670				},
671				hidden  : function( $el, value ) {
672					$el.toggle(!value);
673				},
674			},
675			getters: {
676				text   : function( $el ) {
677					return $el.text();
678				},
679				html   : function( $el ) {
680					return $el.html();
681				},
682				value  : function( $el ) {
683					var value = $el.val();
684					if ( $el.is('input[type="number"],input[type="range"]') ) {
685						value = parseFloat(value);
686						if(_.isNaN(value) ) value = '';
687					}
688					return value
689				},
690				checked: function( $el ) {
691					return $el.prop('checked');
692				},
693				radio: function( $el ) {
694					return $el.prop('checked') ? $el.val() : undefined;
695				},
696			},
697
698			getData: function( key ) {
699				var values = [];
700				_.each(this.models, function( model ) {
701					var value = model.get(key);
702					values.indexOf(value) == -1 && values.push(value);
703				});
704				return values.length < 2 ? values[ 0 ] : '-';
705			},
706			setData: function( key, value ) {
707				_.invoke(this.models, 'set', key, value);
708			},
709
710			inputEventHandler : function( e ) {
711				return;
712				if ( e.data && e.data.binding && e.data.binding.$el.is(e.currentTarget) ) {
713					var binding = e.data.binding;
714					if ( this.getters[ binding.type ] ) {
715						var value = this.getters[ binding.type ](binding.$el);
716						console.log(value)
717						_.invoke(this.models, 'trigger', 'input', binding.attr, value);
718						_.invoke(this.models, 'trigger', 'input:' + binding.attr, value);
719					}
720				}
721			},
722			changeEventHandler: function( e ) {
723				if ( e.data && e.data.binding && e.data.binding.$el.is(e.currentTarget) ) {
724					var binding = e.data.binding;
725					if ( this.getters[ binding.type ] ) {
726						var value =  this.getters[ binding.type ](binding.$el, e);
727						value === undefined || this.setData( binding.attr, value );
728					}
729				}
730			}
731
732		});
733
734		return DataBinder;
735	}();
736
737	return B;
738});

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.