PageSourceSearch

https://woodstocksiv.com/pattern-lab/public/js/plugins.js?

js woodstocksiv.com collected 2026-09-30 11:42:58 UTC 230,023 bytes, 6,547 lines download raw bytes

1/*! Backstretch - v2.0.4 - 2013-06-19
2* http://srobbin.com/jquery-plugins/backstretch/
3* Copyright (c) 2013 Scott Robbin; Licensed MIT */
4(function(a,d,p){a.fn.backstretch=function(c,b){(c===p||0===c.length)&&a.error("No images were supplied for Backstretch");0===a(d).scrollTop()&&d.scrollTo(0,0);return this.each(function(){var d=a(this),g=d.data("backstretch");if(g){if("string"==typeof c&&"function"==typeof g[c]){g[c](b);return}b=a.extend(g.options,b);g.destroy(!0)}g=new q(this,c,b);d.data("backstretch",g)})};a.backstretch=function(c,b){return a("body").backstretch(c,b).data("backstretch")};a.expr[":"].backstretch=function(c){return a(c).data("backstretch")!==p};a.fn.backstretch.defaults={centeredX:!0,centeredY:!0,duration:5E3,fade:0};var r={left:0,top:0,overflow:"hidden",margin:0,padding:0,height:"100%",width:"100%",zIndex:-999999},s={position:"absolute",display:"none",margin:0,padding:0,border:"none",width:"auto",height:"auto",maxHeight:"none",maxWidth:"none",zIndex:-999999},q=function(c,b,e){this.options=a.extend({},a.fn.backstretch.defaults,e||{});this.images=a.isArray(b)?b:[b];a.each(this.images,function(){a("<img />")[0].src=this});this.isBody=c===document.body;this.$container=a(c);this.$root=this.isBody?l?a(d):a(document):this.$container;c=this.$container.children(".backstretch").first();this.$wrap=c.length?c:a('<div class="backstretch"></div>').css(r).appendTo(this.$container);this.isBody||(c=this.$container.css("position"),b=this.$container.css("zIndex"),this.$container.css({position:"static"===c?"relative":c,zIndex:"auto"===b?0:b,background:"none"}),this.$wrap.css({zIndex:-999998}));this.$wrap.css({position:this.isBody&&l?"fixed":"absolute"});this.index=0;this.show(this.index);a(d).on("resize.backstretch",a.proxy(this.resize,this)).on("orientationchange.backstretch",a.proxy(function(){this.isBody&&0===d.pageYOffset&&(d.scrollTo(0,1),this.resize())},this))};q.prototype={resize:function(){try{var a={left:0,top:0},b=this.isBody?this.$root.width():this.$root.innerWidth(),e=b,g=this.isBody?d.innerHeight?d.innerHeight:this.$root.height():this.$root.innerHeight(),j=e/this.$img.data("ratio"),f;j>=g?(f=(j-g)/2,this.options.centeredY&&(a.top="-"+f+"px")):(j=g,e=j*this.$img.data("ratio"),f=(e-b)/2,this.options.centeredX&&(a.left="-"+f+"px"));this.$wrap.css({width:b,height:g}).find("img:not(.deleteable)").css({width:e,height:j}).css(a)}catch(h){}return this},show:function(c){if(!(Math.abs(c)>this.images.length-1)){var b=this,e=b.$wrap.find("img").addClass("deleteable"),d={relatedTarget:b.$container[0]};b.$container.trigger(a.Event("backstretch.before",d),[b,c]);this.index=c;clearInterval(b.interval);b.$img=a("<img />").css(s).bind("load",function(f){var h=this.width||a(f.target).width();f=this.height||a(f.target).height();a(this).data("ratio",h/f);a(this).fadeIn(b.options.speed||b.options.fade,function(){e.remove();b.paused||b.cycle();a(["after","show"]).each(function(){b.$container.trigger(a.Event("backstretch."+this,d),[b,c])})});b.resize()}).appendTo(b.$wrap);b.$img.attr("src",b.images[c]);return b}},next:function(){return this.show(this.index<this.images.length-1?this.index+1:0)},prev:function(){return this.show(0===this.index?this.images.length-1:this.index-1)},pause:function(){this.paused=!0;return this},resume:function(){this.paused=!1;this.next();return this},cycle:function(){1<this.images.length&&(clearInterval(this.interval),this.interval=setInterval(a.proxy(function(){this.paused||this.next()},this),this.options.duration));return this},destroy:function(c){a(d).off("resize.backstretch orientationchange.backstretch");clearInterval(this.interval);c||this.$wrap.remove();this.$container.removeData("backstretch")}};var l,f=navigator.userAgent,m=navigator.platform,e=f.match(/AppleWebKit\/([0-9]+)/),e=!!e&&e[1],h=f.match(/Fennec\/([0-9]+)/),h=!!h&&h[1],n=f.match(/Opera Mobi\/([0-9]+)/),t=!!n&&n[1],k=f.match(/MSIE ([0-9]+)/),k=!!k&&k[1];l=!((-1<m.indexOf("iPhone")||-1<m.indexOf("iPad")||-1<m.indexOf("iPod"))&&e&&534>e||d.operamini&&"[object OperaMini]"==={}.toString.call(d.operamini)||n&&7458>t||-1<f.indexOf("Android")&&e&&533>e||h&&6>h||"palmGetResource"in d&&e&&534>e||-1<f.indexOf("MeeGo")&&-1<f.indexOf("NokiaBrowser/8.5.0")||k&&6>=k)})(jQuery,window);
5
6/**
7 * Arctext.js
8 * A jQuery plugin for curved text
9 * http://www.codrops.com
10 *
11 * Copyright 2011, Pedro Botelho / Codrops
12 * Free to use under the MIT license.
13 *
14 * Date: Mon Jan 23 2012
15 */
16
17(function( $, undefined ) {
18	
19	/*!	
20	* FitText.js 1.0
21	*
22	* Copyright 2011, Dave Rupert http://daverupert.com
23	* Released under the WTFPL license 
24	* http://sam.zoy.org/wtfpl/
25	*
26	* Date: Thu May 05 14:23:00 2011 -0600
27	*/
28	$.fn.fitText = function( kompressor, options ) {
29
30	    var settings = {
31			'minFontSize' : Number.NEGATIVE_INFINITY,
32			'maxFontSize' : Number.POSITIVE_INFINITY
33		};
34
35		return this.each(function() {
36			var $this = $(this);              // store the object
37			var compressor = kompressor || 1; // set the compressor
38	
39			if ( options ) { 
40			  $.extend( settings, options );
41			}
42	
43			// Resizer() resizes items based on the object width divided by the compressor * 10
44			var resizer = function () {
45				$this.css('font-size', Math.max(Math.min($this.width() / (compressor*10), parseFloat(settings.maxFontSize)), parseFloat(settings.minFontSize)));
46			};
47
48			// Call once to set.
49			resizer();
50
51			// Call on resize. Opera debounces their resize by default. 
52			$(window).resize(resizer);
53		});
54
55	};
56
57	/*
58	 * Lettering plugin
59	 *
60	 * changed injector function:
61	 *   add &nbsp; for empty chars.
62	 */
63	function injector(t, splitter, klass, after) {
64		var a = t.text().split(splitter), inject = '', emptyclass;
65		if (a.length) {
66			$(a).each(function(i, item) {
67				emptyclass = '';
68				if(item === ' ') {
69					emptyclass = ' empty';
70					item='&nbsp;';
71				}	
72				inject += '<span class="'+klass+(i+1)+emptyclass+'">'+item+'</span>'+after;
73			});	
74			t.empty().append(inject);
75		}
76	}
77	
78	var methods 			= {
79		init : function() {
80
81			return this.each(function() {
82				injector($(this), '', 'char', '');
83			});
84
85		},
86
87		words : function() {
88
89			return this.each(function() {
90				injector($(this), ' ', 'word', ' ');
91			});
92
93		},
94		
95		lines : function() {
96
97			return this.each(function() {
98				var r = "eefec303079ad17405c889e092e105b0";
99				// Because it's hard to split a <br/> tag consistently across browsers,
100				// (*ahem* IE *ahem*), we replaces all <br/> instances with an md5 hash 
101				// (of the word "split").  If you're trying to use this plugin on that 
102				// md5 hash string, it will fail because you're being ridiculous.
103				injector($(this).children("br").replaceWith(r).end(), r, 'line', '');
104			});
105
106		}
107	};
108
109	$.fn.lettering 			= function( method ) {
110		// Method calling logic
111		if ( method && methods[method] ) {
112			return methods[ method ].apply( this, [].slice.call( arguments, 1 ));
113		} else if ( method === 'letters' || ! method ) {
114			return methods.init.apply( this, [].slice.call( arguments, 0 ) ); // always pass an array
115		}
116		$.error( 'Method ' +  method + ' does not exist on jQuery.lettering' );
117		return this;
118	};
119	
120	/*
121	 * Arctext object.
122	 */
123	$.Arctext 				= function( options, element ) {
124	
125		this.$el	= $( element );
126		this._init( options );
127		
128	};
129	
130	$.Arctext.defaults 		= {
131		radius	: 0, 	// the minimum value allowed is half of the word length. if set to -1, the word will be straight.
132		dir		: 1,	// 1: curve is down, -1: curve is up.
133		rotate	: true,	// if true each letter will be rotated.
134		fitText	: false // if you wanna try out the fitText plugin (http://fittextjs.com/) set this to true. Don't forget the wrapper should be fluid.
135    };
136	
137	$.Arctext.prototype 	= {
138		_init 				: function( options ) {
139			
140			this.options 		= $.extend( true, {}, $.Arctext.defaults, options );
141			
142			// apply the lettering plugin.
143			this._applyLettering();
144			
145			this.$el.data( 'arctext', true );
146			
147			// calculate values
148			this._calc();
149			
150			// apply transformation.
151			this._rotateWord();
152			
153			// load the events
154			this._loadEvents();
155			
156		},
157		_applyLettering		: function() {
158		
159			this.$el.lettering();
160			
161			if( this.options.fitText )
162				this.$el.fitText();
163			
164			this.$letters	= this.$el.find('span').css('display', 'inline-block');
165		
166		},
167		_calc				: function() {
168			
169			if( this.options.radius === -1 )
170				return false;
171			
172			// calculate word / arc sizes & distances.
173			this._calcBase();
174			
175			// get final values for each letter.
176			this._calcLetters();
177		
178		},
179		_calcBase			: function() {
180			
181			// total word width (sum of letters widths)
182			this.dtWord		= 0;
183			
184			var _self 		= this;
185			
186			this.$letters.each( function(i) {
187								
188				var $letter 		= $(this),
189					letterWidth		= $letter.outerWidth( true );
190				
191				_self.dtWord += letterWidth;
192				
193				// save the center point of each letter:
194				$letter.data( 'center', _self.dtWord - letterWidth / 2 );
195				
196			});
197			
198			// the middle point of the word.
199			var centerWord = this.dtWord / 2;
200			
201			// check radius : the minimum value allowed is half of the word length.
202			if( this.options.radius < centerWord )
203				this.options.radius = centerWord;
204			
205			// total arc segment length, where the letters will be placed.
206			this.dtArcBase	= this.dtWord;
207			
208			// calculate the arc (length) that goes from the beginning of the first letter (x=0) to the end of the last letter (x=this.dtWord).
209			// first lets calculate the angle for the triangle with base = this.dtArcBase and the other two sides = radius.
210			var angle		= 2 * Math.asin( this.dtArcBase / ( 2 * this.options.radius ) );
211			
212			// given the formula: L(ength) = R(adius) x A(ngle), we calculate our arc length.
213			this.dtArc		= this.options.radius * angle;
214			
215		},
216		_calcLetters		: function() {
217			
218			var _self 		= this,
219				iteratorX 	= 0;
220				
221			this.$letters.each( function(i) {
222					
223				var $letter 		= $(this),
224					// calculate each letter's semi arc given the percentage of each letter on the original word.
225					dtArcLetter		= ( $letter.outerWidth( true ) / _self.dtWord ) * _self.dtArc,
226					// angle for the dtArcLetter given our radius.
227					beta			= dtArcLetter / _self.options.radius,
228					// distance from the middle point of the semi arc's chord to the center of the circle.
229					// this is going to be the place where the letter will be positioned.
230					h				= _self.options.radius * ( Math.cos( beta / 2 ) ),
231					// angle formed by the x-axis and the left most point of the chord.
232					alpha			= Math.acos( ( _self.dtWord / 2 - iteratorX ) / _self.options.radius ),
233					// angle formed by the x-axis and the right most point of the chord.
234					theta 			= alpha + beta / 2,
235					// distances of the sides of the triangle formed by h and the orthogonal to the x-axis.
236					x				= Math.cos( theta ) * h,
237					y				= Math.sin( theta ) * h,
238					// the value for the coordinate x of the middle point of the chord.
239					xpos			= iteratorX + Math.abs( _self.dtWord / 2 - x - iteratorX ),
240					// finally, calculate how much to translate each letter, given its center point.
241					// also calculate the angle to rotate the letter accordingly.
242					xval	= 0| xpos - $letter.data( 'center' ),
243					yval	= 0| _self.options.radius - y,
244					angle 	= ( _self.options.rotate ) ? 0| -Math.asin( x / _self.options.radius ) * ( 180 / Math.PI ) : 0;
245				
246				// the iteratorX will be positioned on the second point of each semi arc
247				iteratorX = 2 * xpos - iteratorX;
248				
249				// save these values
250				$letter.data({
251					x	: xval,
252					y	: ( _self.options.dir === 1 ) ? yval : -yval,
253					a	: ( _self.options.dir === 1 ) ? angle : -angle
254				});
255					
256			});
257		
258		},
259		_rotateWord			: function( animation ) {
260			
261			if( !this.$el.data('arctext') ) return false;
262			
263			var _self = this;
264			
265			this.$letters.each( function(i) {
266				
267				var $letter 		= $(this),
268					transformation	= ( _self.options.radius === -1 ) ? 'none' : 'translateX(' + $letter.data('x') + 'px) translateY(' + $letter.data('y') + 'px) rotate(' + $letter.data('a') + 'deg)',
269					transition		= ( animation ) ? 'all ' + ( animation.speed || 0 ) + 'ms ' + ( animation.easing || 'linear' ) : 'none';
270				
271				$letter.css({
272					'-webkit-transition' : transition,
273					'-moz-transition' : transition,
274					'-o-transition' : transition,
275					'-ms-transition' : transition,
276					'transition' : transition
277				})
278				.css({
279					'-webkit-transform' : transformation,
280					'-moz-transform' : transformation,
281					'-o-transform' : transformation,
282					'-ms-transform' : transformation,
283					'transform' : transformation
284				});
285			
286			});
287			
288		},
289		_loadEvents			: function() {
290			
291			if( this.options.fitText ) {
292			
293				var _self = this;
294				
295				$(window).on( 'resize.arctext', function() {
296					
297					_self._calc();
298					
299					// apply transformation.
300					_self._rotateWord();
301					
302				});
303			
304			}
305		
306		},
307		set					: function( opts ) {
308			
309			if( !opts.radius &&  
310				!opts.dir &&
311				opts.rotate === 'undefined' ) {
312					return false;
313			}
314			
315			this.options.radius = opts.radius || this.options.radius;
316			this.options.dir 	= opts.dir || this.options.dir;
317			
318			if( opts.rotate !== undefined ) {
319				this.options.rotate = opts.rotate;
320			}	
321			
322			this._calc();
323			
324			this._rotateWord( opts.animation );
325			
326		},
327		destroy				: function() {
328			
329			this.options.radius	= -1;
330			this._rotateWord();
331			this.$letters.removeData('x y a center');
332			this.$el.removeData('arctext');
333			$(window).off('.arctext');
334			
335		}
336	};
337	
338	var logError 			= function( message ) {
339		if ( this.console ) {
340			console.error( message );
341		}
342	};
343	
344	$.fn.arctext			= function( options ) {
345	
346		if ( typeof options === 'string' ) {
347			
348			var args = Array.prototype.slice.call( arguments, 1 );
349			
350			this.each(function() {
351			
352				var instance = $.data( this, 'arctext' );
353				
354				if ( !instance ) {
355					logError( "cannot call methods on arctext prior to initialization; " +
356					"attempted to call method '" + options + "'" );
357					return;
358				}
359				
360				if ( !$.isFunction( instance[options] ) || options.charAt(0) === "_" ) {
361					logError( "no such method '" + options + "' for arctext instance" );
362					return;
363				}
364				
365				instance[ options ].apply( instance, args );
366			
367			});
368		
369		} 
370		else {
371		
372			this.each(function() {
373			
374				var instance = $.data( this, 'arctext' );
375				if ( !instance ) {
376					$.data( this, 'arctext', new $.Arctext( options, this ) );
377				}
378			});
379		
380		}
381		
382		return this;
383		
384	};
385	
386})( jQuery );
387
388
389
390/*!
391 * pickadate.js v3.5.3, 2014/07/12
392 * By Amsul, http://amsul.ca
393 * Hosted on http://amsul.github.io/pickadate.js
394 * Licensed under MIT
395 */
396
397(function ( factory ) {
398
399    // AMD.
400    if ( typeof define == 'function' && define.amd )
401        define( 'picker', ['jquery'], factory )
402
403    // Node.js/browserify.
404    else if ( typeof exports == 'object' )
405        module.exports = factory( require('jquery') )
406
407    // Browser globals.
408    else this.Picker = factory( jQuery )
409
410}(function( $ ) {
411
412var $window = $( window )
413var $document = $( document )
414var $html = $( document.documentElement )
415
416
417/**
418 * The picker constructor that creates a blank picker.
419 */
420function PickerConstructor( ELEMENT, NAME, COMPONENT, OPTIONS ) {
421
422    // If there’s no element, return the picker constructor.
423    if ( !ELEMENT ) return PickerConstructor
424
425
426    var
427        IS_DEFAULT_THEME = false,
428
429
430        // The state of the picker.
431        STATE = {
432            id: ELEMENT.id || 'P' + Math.abs( ~~(Math.random() * new Date()) )
433        },
434
435
436        // Merge the defaults and options passed.
437        SETTINGS = COMPONENT ? $.extend( true, {}, COMPONENT.defaults, OPTIONS ) : OPTIONS || {},
438
439
440        // Merge the default classes with the settings classes.
441        CLASSES = $.extend( {}, PickerConstructor.klasses(), SETTINGS.klass ),
442
443
444        // The element node wrapper into a jQuery object.
445        $ELEMENT = $( ELEMENT ),
446
447
448        // Pseudo picker constructor.
449        PickerInstance = function() {
450            return this.start()
451        },
452
453
454        // The picker prototype.
455        P = PickerInstance.prototype = {
456
457            constructor: PickerInstance,
458
459            $node: $ELEMENT,
460
461
462            /**
463             * Initialize everything
464             */
465            start: function() {
466
467                // If it’s already started, do nothing.
468                if ( STATE && STATE.start ) return P
469
470
471                // Update the picker states.
472                STATE.methods = {}
473                STATE.start = true
474                STATE.open = false
475                STATE.type = ELEMENT.type
476
477
478                // Confirm focus state, convert into text input to remove UA stylings,
479                // and set as readonly to prevent keyboard popup.
480                ELEMENT.autofocus = ELEMENT == document.activeElement
481                ELEMENT.readOnly = !SETTINGS.editable
482                ELEMENT.id = ELEMENT.id || STATE.id
483                if ( ELEMENT.type != 'text' ) {
484                    ELEMENT.type = 'text'
485                }
486
487
488                // Create a new picker component with the settings.
489                P.component = new COMPONENT(P, SETTINGS)
490
491
492                // Create the picker root with a holder and then prepare it.
493                P.$root = $( PickerConstructor._.node('div', createWrappedComponent(), CLASSES.picker, 'id="' + ELEMENT.id + '_root"') )
494                prepareElementRoot()
495
496
497                // If there’s a format for the hidden input element, create the element.
498                if ( SETTINGS.formatSubmit ) {
499                    prepareElementHidden()
500                }
501
502
503                // Prepare the input element.
504                prepareElement()
505
506
507                // Insert the root as specified in the settings.
508                if ( SETTINGS.container ) $( SETTINGS.container ).append( P.$root )
509                else $ELEMENT.after( P.$root )
510
511
512                // Bind the default component and settings events.
513                P.on({
514                    start: P.component.onStart,
515                    render: P.component.onRender,
516                    stop: P.component.onStop,
517                    open: P.component.onOpen,
518                    close: P.component.onClose,
519                    set: P.component.onSet
520                }).on({
521                    start: SETTINGS.onStart,
522                    render: SETTINGS.onRender,
523                    stop: SETTINGS.onStop,
524                    open: SETTINGS.onOpen,
525                    close: SETTINGS.onClose,
526                    set: SETTINGS.onSet
527                })
528
529
530                // Once we’re all set, check the theme in use.
531                IS_DEFAULT_THEME = isUsingDefaultTheme( P.$root.children()[ 0 ] )
532
533
534                // If the element has autofocus, open the picker.
535                if ( ELEMENT.autofocus ) {
536                    P.open()
537                }
538
539
540                // Trigger queued the “startâ€? and “renderâ€? events.
541                return P.trigger( 'start' ).trigger( 'render' )
542            }, //start
543
544
545            /**
546             * Render a new picker
547             */
548            render: function( entireComponent ) {
549
550                // Insert a new component holder in the root or box.
551                if ( entireComponent ) P.$root.html( createWrappedComponent() )
552                else P.$root.find( '.' + CLASSES.box ).html( P.component.nodes( STATE.open ) )
553
554                // Trigger the queued “renderâ€? events.
555                return P.trigger( 'render' )
556            }, //render
557
558
559            /**
560             * Destroy everything
561             */
562            stop: function() {
563
564                // If it’s already stopped, do nothing.
565                if ( !STATE.start ) return P
566
567                // Then close the picker.
568                P.close()
569
570                // Remove the hidden field.
571                if ( P._hidden ) {
572                    P._hidden.parentNode.removeChild( P._hidden )
573                }
574
575                // Remove the root.
576                P.$root.remove()
577
578                // Remove the input class, remove the stored data, and unbind
579                // the events (after a tick for IE - see `P.close`).
580                $ELEMENT.removeClass( CLASSES.input ).removeData( NAME )
581                setTimeout( function() {
582                    $ELEMENT.off( '.' + STATE.id )
583                }, 0)
584
585                // Restore the element state
586                ELEMENT.type = STATE.type
587                ELEMENT.readOnly = false
588
589                // Trigger the queued “stopâ€? events.
590                P.trigger( 'stop' )
591
592                // Reset the picker states.
593                STATE.methods = {}
594                STATE.start = false
595
596                return P
597            }, //stop
598
599
600            /**
601             * Open up the picker
602             */
603            open: function( dontGiveFocus ) {
604
605                // If it’s already open, do nothing.
606                if ( STATE.open ) return P
607
608                // Add the “activeâ€? class.
609                $ELEMENT.addClass( CLASSES.active )
610                aria( ELEMENT, 'expanded', true )
611
612                // * A Firefox bug, when `html` has `overflow:hidden`, results in
613                //   killing transitions :(. So add the “openedâ€? state on the next tick.
614                //   Bug: https://bugzilla.mozilla.org/show_bug.cgi?id=625289
615                setTimeout( function() {
616
617                    // Add the “openedâ€? class to the picker root.
618                    P.$root.addClass( CLASSES.opened )
619                    aria( P.$root[0], 'hidden', false )
620
621                }, 0 )
622
623                // If we have to give focus, bind the element and doc events.
624                if ( dontGiveFocus !== false ) {
625
626                    // Set it as open.
627                    STATE.open = true
628
629                    // Prevent the page from scrolling.
630                    if ( IS_DEFAULT_THEME ) {
631                        $html.
632                            css( 'overflow', 'hidden' ).
633                            css( 'padding-right', '+=' + getScrollbarWidth() )
634                    }
635
636                    // Pass focus to the element’s jQuery object.
637                    $ELEMENT.trigger( 'focus' )
638
639                    // Bind the document events.
640                    $document.on( 'click.' + STATE.id + ' focusin.' + STATE.id, function( event ) {
641
642                        var target = event.target
643
644                        // If the target of the event is not the element, close the picker picker.
645                        // * Don’t worry about clicks or focusins on the root because those don’t bubble up.
646                        //   Also, for Firefox, a click on an `option` element bubbles up directly
647                        //   to the doc. So make sure the target wasn't the doc.
648                        // * In Firefox stopPropagation() doesn’t prevent right-click events from bubbling,
649                        //   which causes the picker to unexpectedly close when right-clicking it. So make
650                        //   sure the event wasn’t a right-click.
651                        if ( target != ELEMENT && target != document && event.which != 3 ) {
652
653                            // If the target was the holder that c
653overs the screen,
654                            // keep the element focused to maintain tabindex.
655                            P.close( target === P.$root.children()[0] )
656                        }
657
658                    }).on( 'keydown.' + STATE.id, function( event ) {
659
660                        var
661                            // Get the keycode.
662                            keycode = event.keyCode,
663
664                            // Translate that to a selection change.
665                            keycodeToMove = P.component.key[ keycode ],
666
667                            // Grab the target.
668                            target = event.target
669
670
671                        // On escape, close the picker and give focus.
672                        if ( keycode == 27 ) {
673                            P.close( true )
674                        }
675
676
677                        // Check if there is a key movement or “enterâ€? keypress on the element.
678                        else if ( target == ELEMENT && ( keycodeToMove || keycode == 13 ) ) {
679
680                            // Prevent the default action to stop page movement.
681                            event.preventDefault()
682
683                            // Trigger the key movement action.
684                            if ( keycodeToMove ) {
685                                PickerConstructor._.trigger( P.component.key.go, P, [ PickerConstructor._.trigger( keycodeToMove ) ] )
686                            }
687
688                            // On “enterâ€?, if the highlighted item isn’t disabled, set the value and close.
689                            else if ( !P.$root.find( '.' + CLASSES.highlighted ).hasClass( CLASSES.disabled ) ) {
690                                P.set( 'select', P.component.item.highlight ).close()
691                            }
692                        }
693
694
695                        // If the target is within the root and “enterâ€? is pressed,
696                        // prevent the default action and trigger a click on the target instead.
697                        else if ( $.contains( P.$root[0], target ) && keycode == 13 ) {
698                            event.preventDefault()
699                            target.click()
700                        }
701                    })
702                }
703
704                // Trigger the queued “openâ€? events.
705                return P.trigger( 'open' )
706            }, //open
707
708
709            /**
710             * Close the picker
711             */
712            close: function( giveFocus ) {
713
714                // If we need to give focus, do it before changing states.
715                if ( giveFocus ) {
716                    // ....ah yes! It would’ve been incomplete without a crazy workaround for IE :|
717                    // The focus is triggered *after* the close has completed - causing it
718                    // to open again. So unbind and rebind the event at the next tick.
719                    $ELEMENT.off( 'focus.' + STATE.id ).trigger( 'focus' )
720                    setTimeout( function() {
721                        $ELEMENT.on( 'focus.' + STATE.id, focusToOpen )
722                    }, 0 )
723                }
724
725                // Remove the “activeâ€? class.
726                $ELEMENT.removeClass( CLASSES.active )
727                aria( ELEMENT, 'expanded', false )
728
729                // * A Firefox bug, when `html` has `overflow:hidden`, results in
730                //   killing transitions :(. So remove the “openedâ€? state on the next tick.
731                //   Bug: https://bugzilla.mozilla.org/show_bug.cgi?id=625289
732                setTimeout( function() {
733
734                    // Remove the “openedâ€? and “focusedâ€? class from the picker root.
735                    P.$root.removeClass( CLASSES.opened + ' ' + CLASSES.focused )
736                    aria( P.$root[0], 'hidden', true )
737
738                }, 0 )
739
740                // If it’s already closed, do nothing more.
741                if ( !STATE.open ) return P
742
743                // Set it as closed.
744                STATE.open = false
745
746                // Allow the page to scroll.
747                if ( IS_DEFAULT_THEME ) {
748                    $html.
749                        css( 'overflow', '' ).
750                        css( 'padding-right', '-=' + getScrollbarWidth() )
751                }
752
753                // Unbind the document events.
754                $document.off( '.' + STATE.id )
755
756                // Trigger the queued “closeâ€? events.
757                return P.trigger( 'close' )
758            }, //close
759
760
761            /**
762             * Clear the values
763             */
764            clear: function( options ) {
765                return P.set( 'clear', null, options )
766            }, //clear
767
768
769            /**
770             * Set something
771             */
772            set: function( thing, value, options ) {
773
774                var thingItem, thingValue,
775                    thingIsObject = $.isPlainObject( thing ),
776                    thingObject = thingIsObject ? thing : {}
777
778                // Make sure we have usable options.
779                options = thingIsObject && $.isPlainObject( value ) ? value : options || {}
780
781                if ( thing ) {
782
783                    // If the thing isn’t an object, make it one.
784                    if ( !thingIsObject ) {
785                        thingObject[ thing ] = value
786                    }
787
788                    // Go through the things of items to set.
789                    for ( thingItem in thingObject ) {
790
791                        // Grab the value of the thing.
792                        thingValue = thingObject[ thingItem ]
793
794                        // First, if the item exists and there’s a value, set it.
795                        if ( thingItem in P.component.item ) {
796                            if ( thingValue === undefined ) thingValue = null
797                            P.component.set( thingItem, thingValue, options )
798                        }
799
800                        // Then, check to update the element value and broadcast a change.
801                        if ( thingItem == 'select' || thingItem == 'clear' ) {
802                            $ELEMENT.
803                                val( thingItem == 'clear' ? '' : P.get( thingItem, SETTINGS.format ) ).
804                                trigger( 'change' )
805                        }
806                    }
807
808                    // Render a new picker.
809                    P.render()
810                }
811
812                // When the method isn’t muted, trigger queued “setâ€? events and pass the `thingObject`.
813                return options.muted ? P : P.trigger( 'set', thingObject )
814            }, //set
815
816
817            /**
818             * Get something
819             */
820            get: function( thing, format ) {
821
822                // Make sure there’s something to get.
823                thing = thing || 'value'
824
825                // If a picker state exists, return that.
826                if ( STATE[ thing ] != null ) {
827                    return STATE[ thing ]
828                }
829
830                // Return the value, if that.
831                if ( thing == 'value' ) {
832                    return ELEMENT.value
833                }
834
835                // Check if a component item exists, return that.
836                if ( thing in P.component.item ) {
837                    if ( typeof format == 'string' ) {
838                        var thingValue = P.component.get( thing )
839                        return thingValue ?
840                            PickerConstructor._.trigger(
841                                P.component.formats.toString,
842                                P.component,
843                                [ format, thingValue ]
844                            ) : ''
845                    }
846                    return P.component.get( thing )
847                }
848            }, //get
849
850
851
852            /**
853             * Bind events on the things.
854             */
855            on: function( thing, method, internal ) {
856
857                var thingName, thingMethod,
858                    thingIsObject = $.isPlainObject( thing ),
859                    thingObject = thingIsObject ? thing : {}
860
861                if ( thing ) {
862
863                    // If the thing isn’t an object, make it one.
864                    if ( !thingIsObject ) {
865                        thingObject[ thing ] = method
866                    }
867
868                    // Go through the things to bind to.
869                    for ( thingName in thingObject ) {
870
871                        // Grab the method of the thing.
872                        thingMethod = thingObject[ thingName ]
873
874                        // If it was an internal binding, prefix it.
875                        if ( internal ) {
876                            thingName = '_' + thingName
877                        }
878
879                        // Make sure the thing methods collection exists.
880                        STATE.methods[ thingName ] = STATE.methods[ thingName ] || []
881
882                        // Add the method to the relative method collection.
883                        STATE.methods[ thingName ].push( thingMethod )
884                    }
885                }
886
887                return P
888            }, //on
889
890
891
892            /**
893             * Unbind events on the things.
894             */
895            off: function() {
896                var i, thingName,
897                    names = arguments;
898                for ( i = 0, namesCount = names.length; i < namesCount; i += 1 ) {
899                    thingName = names[i]
900                    if ( thingName in STATE.methods ) {
901                        delete STATE.methods[thingName]
902                    }
903                }
904                return P
905            },
906
907
908            /**
909             * Fire off method events.
910             */
911            trigger: function( name, data ) {
912                var _trigger = function( name ) {
913                    var methodList = STATE.methods[ name ]
914                    if ( methodList ) {
915                        methodList.map( function( method ) {
916                            PickerConstructor._.trigger( method, P, [ data ] )
917                        })
918                    }
919                }
920                _trigger( '_' + name )
921                _trigger( name )
922                return P
923            }
923 //trigger
924        } //PickerInstance.prototype
925
926
927    /**
928     * Wrap the picker holder components together.
929     */
930    function createWrappedComponent() {
931
932        // Create a picker wrapper holder
933        return PickerConstructor._.node( 'div',
934
935            // Create a picker wrapper node
936            PickerConstructor._.node( 'div',
937
938                // Create a picker frame
939                PickerConstructor._.node( 'div',
940
941                    // Create a picker box node
942                    PickerConstructor._.node( 'div',
943
944                        // Create the components nodes.
945                        P.component.nodes( STATE.open ),
946
947                        // The picker box class
948                        CLASSES.box
949                    ),
950
951                    // Picker wrap class
952                    CLASSES.wrap
953                ),
954
955                // Picker frame class
956                CLASSES.frame
957            ),
958
959            // Picker holder class
960            CLASSES.holder
961        ) //endreturn
962    } //createWrappedComponent
963
964
965
966    /**
967     * Prepare the input element with all bindings.
968     */
969    function prepareElement() {
970
971        $ELEMENT.
972
973            // Store the picker data by component name.
974            data(NAME, P).
975
976            // Add the “inputâ€? class name.
977            addClass(CLASSES.input).
978
979            // If there’s a `data-value`, update the value of the element.
980            val( $ELEMENT.data('value') ?
981                P.get('select', SETTINGS.format) :
982                ELEMENT.value
983            ).
984
985            // On focus/click, open the picker and adjust the root “focusedâ€? state.
986            on('focus.' + STATE.id + ' click.' + STATE.id, focusToOpen)
987
988
989        // Only bind keydown events if the element isn’t editable.
990        if ( !SETTINGS.editable ) {
991
992            // Handle keyboard event based on the picker being opened or not.
993            $ELEMENT.on('keydown.' + STATE.id, function(event) {
994
995                var keycode = event.keyCode,
996
997                    // Check if one of the delete keys was pressed.
998                    isKeycodeDelete = /^(8|46)$/.test(keycode)
999
1000                // For some reason IE clears the input value on “escapeâ€?.
1001                if ( keycode == 27 ) {
1002                    P.close()
1003                    return false
1004                }
1005
1006                // Check if `space` or `delete` was pressed or the picker is closed with a key movement.
1007                if ( keycode == 32 || isKeycodeDelete || !STATE.open && P.component.key[keycode] ) {
1008
1009                    // Prevent it from moving the page and bubbling to doc.
1010                    event.preventDefault()
1011                    event.stopPropagation()
1012
1013                    // If `delete` was pressed, clear the values and close the picker.
1014                    // Otherwise open the picker.
1015                    if ( isKeycodeDelete ) { P.clear().close() }
1016                    else { P.open() }
1017                }
1018            })
1019        }
1020
1021
1022        // Update the aria attributes.
1023        aria(ELEMENT, {
1024            haspopup: true,
1025            expanded: false,
1026            readonly: false,
1027            owns: ELEMENT.id + '_root' + (P._hidden ? ' ' + P._hidden.id : '')
1028        })
1029    }
1030
1031
1032    /**
1033     * Prepare the root picker element with all bindings.
1034     */
1035    function prepareElementRoot() {
1036
1037        P.$root.
1038
1039            on({
1040
1041                // When something within the root is focused, stop from bubbling
1042                // to the doc and remove the “focusedâ€? state from the root.
1043                focusin: function( event ) {
1044                    P.$root.removeClass( CLASSES.focused )
1045                    event.stopPropagation()
1046                },
1047
1048                // When something within the root holder is clicked, stop it
1049                // from bubbling to the doc.
1050                'mousedown click': function( event ) {
1051
1052                    var target = event.target
1053
1054                    // Make sure the target isn’t the root holder so it can bubble up.
1055                    if ( target != P.$root.children()[ 0 ] ) {
1056
1057                        event.stopPropagation()
1058
1059                        // * For mousedown events, cancel the default action in order to
1060                        //   prevent cases where focus is shifted onto external elements
1061                        //   when using things like jQuery mobile or MagnificPopup (ref: #249 & #120).
1062                        //   Also, for Firefox, don’t prevent action on the `option` element.
1063                        if ( event.type == 'mousedown' && !$( target ).is( ':input' ) && target.nodeName != 'OPTION' ) {
1064
1065                            event.preventDefault()
1066
1067                            // Re-focus onto the element so that users can click away
1068                            // from elements focused within the picker.
1069                            ELEMENT.focus()
1070                        }
1071                    }
1072                }
1073            }).
1074
1075            // If there’s a click on an actionable element, carry out the actions.
1076            on( 'click', '[data-pick], [data-nav], [data-clear]', function() {
1077
1078                var $target = $( this ),
1079                    targetData = $target.data(),
1080                    targetDisabled = $target.hasClass( CLASSES.navDisabled ) || $target.hasClass( CLASSES.disabled ),
1081
1082                    // * For IE, non-focusable elements can be active elements as well
1083                    //   (http://stackoverflow.com/a/2684561).
1084                    activeElement = document.activeElement
1085                    activeElement = activeElement && ( activeElement.type || activeElement.href ) && activeElement
1086
1087                // If it’s disabled or nothing inside is actively focused, re-focus the element.
1088                if ( targetDisabled || activeElement && !$.contains( P.$root[0], activeElement ) ) {
1089                    ELEMENT.focus()
1090                }
1091
1092                // If something is superficially changed, update the `highlight` based on the `nav`.
1093                if ( targetData.nav && !targetDisabled ) {
1094                    P.set( 'highlight', P.component.item.highlight, { nav: targetData.nav } )
1095                }
1096
1097                // If something is picked, set `select` then close with focus.
1098                else if ( PickerConstructor._.isInteger( targetData.pick ) && !targetDisabled ) {
1099                    P.set( 'select', targetData.pick ).close( true )
1100                }
1101
1102                // If a “clearâ€? button is pressed, empty the values and close with focus.
1103                else if ( targetData.clear ) {
1104                    P.clear().close( true )
1105                }
1106            })
1107            .on('click', '[data-close]', function () {
1108                P.close(true);
1109            }) //P.$root
1110
1111        aria( P.$root[0], 'hidden', true )
1112    }
1113
1114
1115     /**
1116      * Prepare the hidden input element along with all bindings.
1117      */
1118    function prepareElementHidden() {
1119
1120        var name
1121
1122        if ( SETTINGS.hiddenName === true ) {
1123            name = ELEMENT.name
1124            ELEMENT.name = ''
1125        }
1126        else {
1127            name = [
1128                typeof SETTINGS.hiddenPrefix == 'string' ? SETTINGS.hiddenPrefix : '',
1129                typeof SETTINGS.hiddenSuffix == 'string' ? SETTINGS.hiddenSuffix : '_submit'
1130            ]
1131            name = name[0] + ELEMENT.name + name[1]
1132        }
1133
1134        P._hidden = $(
1135            '<input ' +
1136            'type=hidden ' +
1137
1138            // Create the name using the original input’s with a prefix and suffix.
1139            'name="' + name + '"' +
1140
1141            // Create the ID using the original input’s; only if it has one.
1142            (ELEMENT.id ? 'id="' + ELEMENT.id + '_hidden"' : '') +
1143
1144            // If the element has a value, set the hidden value as well.
1145            (
1146                $ELEMENT.data('value') || ELEMENT.value ?
1147                    ' value="' + P.get('select', SETTINGS.formatSubmit) + '"' :
1148                    ''
1149            ) +
1150            '>'
1151        )[0]
1152
1153        $ELEMENT.
1154
1155            // If the value changes, update the hidden input with the correct format.
1156            on('change.' + STATE.id, function() {
1157                P._hidden.value = ELEMENT.value ?
1158                    P.get('select', SETTINGS.formatSubmit) :
1159                    ''
1160            }).
1161
1162            // Insert the hidden input after the element.
1163            after(P._hidden)
1164    }
1165
1166
1167    // Separated for IE
1168    function focusToOpen( event ) {
1169
1170        // Stop the event from propagating to the doc.
1171        event.stopPropagation()
1172
1173        // If it’s a focus event, add the “focusedâ€? class to the root.
1174        if ( event.type == 'focus' ) {
1175            P.$root.addClass( CLASSES.focused )
1176        }
1177
1178        // And then finally open the picker.
1179        P.open()
1180    }
1181
1182
1183    // Return a new picker instance.
1184    return new PickerInstance()
1185} //PickerConstructor
1186
1187
1188
1189/**
1190 * The default classes and prefix to use for the HTML classes.
1191 */
1192PickerConstructor.klasses = function( prefix ) {
1193    prefix = prefix || 'picker'
1194    return {
1195
1196        picker: prefix,
1197        opened: prefix + '--opened',
1198        focused: prefix + '--focused',
1199
1200        input: prefix + '__input',
1201        active: prefix + '__input--active',
1202
1203        holder: prefix + '__holder',
1204
1205        frame: prefix + '__frame',
1206        wrap: prefix + '__wrap',
1207
1208        box: prefix + '__box'
1209    }
1210} //PickerConstructor.klasses
1211
1212
1213
1214/**
1215 * Check if the default theme is being used.
1216 */
1217function isUsingDefaultTheme( element ) {
1218
1219    var theme,
1220        prop = 'position'
1221
1222    // For IE.
1223    if ( element.currentStyle ) {
1224        theme = element.currentStyle[prop]
1225    }
1226
1227    // For normal browsers.
1228    else if ( window.getComputedStyle ) {
1229        theme = getComputedStyle( element )[prop]
1230    }
1231
1232    return theme == 'fixed'
1233}
1234
1235
1236
1237/**
1238 * Get the width of the browser’s scrollbar.
1239 * Taken from: https://github.com/VodkaBears/Remodal/blob/master/src/jquery.remodal.js
1240 */
1241function getScrollbarWidth() {
1242
1243    if ( $html.height() <= $window.height() ) {
1244        return 0
1245    }
1246
1247    var $outer = $( '<div style="visibility:hidden;width:100px" />' ).
1248        appendTo( 'body' )
1249
1250    // Get the width without scrollbars.
1251    var widthWithoutScroll = $outer[0].offsetWidth
1252
1253    // Force adding scrollbars.
1254    $outer.css( 'overflow', 'scroll' )
1255
1256    // Add the inner div.
1257    var $inner = $( '<div style="width:100%" />' ).appendTo( $outer )
1258
1259    // Get the width with scrollbars.
1260    var widthWithScroll = $inner[0].offsetWidth
1261
1262    // Remove the divs.
1263    $outer.remove()
1264
1265    // Return the difference between the widths.
1266    return widthWithoutScroll - widthWithScroll
1267}
1268
1269
1270
1271/**
1272 * PickerConstructor helper methods.
1273 */
1274PickerConstructor._ = {
1275
1276    /**
1277     * Create a group of nodes. Expects:
1278     * `
1279        {
1280            min:    {Integer},
1281            max:    {Integer},
1282            i:      {Integer},
1283            node:   {String},
1284            item:   {Function}
1285        }
1286     * `
1287     */
1288    group: function( groupObject ) {
1289
1290        var
1291            // Scope for the looped object
1292            loopObjectScope,
1293
1294            // Create the nodes list
1295            nodesList = '',
1296
1297            // The counter starts from the `min`
1298            counter = PickerConstructor._.trigger( groupObject.min, groupObject )
1299
1300
1301        // Loop from the `min` to `max`, incrementing by `i`
1302        for ( ; counter <= PickerConstructor._.trigger( groupObject.max, groupObject, [ counter ] ); counter += groupObject.i ) {
1303
1304            // Trigger the `item` function within scope of the object
1305            loopObjectScope = PickerConstructor._.trigger( groupObject.item, groupObject, [ counter ] )
1306
1307            // Splice the subgroup and create nodes out of the sub nodes
1308            nodesList += PickerConstructor._.node(
1309                groupObject.node,
1310                loopObjectScope[ 0 ],   // the node
1311                loopObjectScope[ 1 ],   // the classes
1312                loopObjectScope[ 2 ]    // the attributes
1313            )
1314        }
1315
1316        // Return the list of nodes
1317        return nodesList
1318    }, //group
1319
1320
1321    /**
1322     * Create a dom node string
1323     */
1324    node: function( wrapper, item, klass, attribute ) {
1325
1326        // If the item is false-y, just return an empty string
1327        if ( !item ) return ''
1328
1329        // If the item is an array, do a join
1330        item = $.isArray( item ) ? item.join( '' ) : item
1331
1332        // Check for the class
1333        klass = klass ? ' class="' + klass + '"' : ''
1334
1335        // Check for any attributes
1336        attribute = attribute ? ' ' + attribute : ''
1337
1338        // Return the wrapped item
1339        return '<' + wrapper + klass + attribute + '>' + item + '</' + wrapper + '>'
1340    }, //node
1341
1342
1343    /**
1344     * Lead numbers below 10 with a zero.
1345     */
1346    lead: function( number ) {
1347        return ( number < 10 ? '0': '' ) + number
1348    },
1349
1350
1351    /**
1352     * Trigger a function otherwise return the value.
1353     */
1354    trigger: function( callback, scope, args ) {
1355        return typeof callback == 'function' ? callback.apply( scope, args || [] ) : callback
1356    },
1357
1358
1359    /**
1360     * If the second character is a digit, length is 2 otherwise 1.
1361     */
1362    digits: function( string ) {
1363        return ( /\d/ ).test( string[ 1 ] ) ? 2 : 1
1364    },
1365
1366
1367    /**
1368     * Tell if something is a date object.
1369     */
1370    isDate: function( value ) {
1371        return {}.toString.call( value ).indexOf( 'Date' ) > -1 && this.isInteger( value.getDate() )
1372    },
1373
1374
1375    /**
1376     * Tell if something is an integer.
1377     */
1378    isInteger: function( value ) {
1379        return {}.toString.call( value ).indexOf( 'Number' ) > -1 && value % 1 === 0
1380    },
1381
1382
1383    /**
1384     * Create ARIA attribute strings.
1385     */
1386    ariaAttr: ariaAttr
1387} //PickerConstructor._
1388
1389
1390
1391/**
1392 * Extend the picker with a component and defaults.
1393 */
1394PickerConstructor.extend = function( name, Component ) {
1395
1396    // Extend jQuery.
1397    $.fn[ name ] = function( options, action ) {
1398
1399        // Grab the component data.
1400        var componentData = this.data( name )
1401
1402        // If the picker is requested, return the data object.
1403        if ( options == 'picker' ) {
1404            return componentData
1405        }
1406
1407        // If the component data exists and `options` is a string, carry out the action.
1408        if ( componentData && typeof options == 'string' ) {
1409            return PickerConstructor._.trigger( componentData[ options ], componentData, [ action ] )
1410        }
1411
1412        // Otherwise go through each matched element and if the component
1413        // doesn’t exist, create a new picker using `this` element
1414        // and merging the defaults and options with a deep copy.
1415        return this.each( function() {
1416            var $this = $( this )
1417            if ( !$this.data( name ) ) {
1418                new PickerConstructor( this, name, Component, options )
1419            }
1420        })
1421    }
1422
1423    // Set the defaults.
1424    $.fn[ name ].defaults = Component.defaults
1425} //PickerConstructor.extend
1426
1427
1428
1429function aria(element, attribute, value) {
1430    if ( $.isPlainObject(attribute) ) {
1431        for ( var key in attribute ) {
1432            ariaSet(element, key, attribute[key])
1433        }
1434    }
1435    else {
1436        ariaSet(element, attribute, value)
1437    }
1438}
1439function ariaSet(element, attribute, value) {
1440    element.setAttribute(
1441        (attribute == 'role' ? '' : 'aria-') + attribute,
1442        value
1443    )
1444}
1445function ariaAttr(attribute, data) {
1446    if ( !$.isPlainObject(attribute) ) {
1447        attribute = { attribute: data }
1448    }
1449    data = ''
1450    for ( var key in attribute ) {
1451        var attr = (key == 'role' ? '' : 'aria-') + key,
1452            attrVal = attribute[key]
1453        data += attrVal == null ? '' : attr + '="' + attribute[key] + '"'
1454    }
1455    return data
1456}
1457
1458
1459
1460// Expose the picker constructor.
1461return PickerConstructor
1462
1463
1464}));
1465
1466
1467
1468
1469
1470
1471/*!
1472 * Date picker for pickadate.js v3.5.3
1473 * http://amsul.github.io/pickadate.js/date.htm
1474 */
1475
1476(function ( factory ) {
1477
1478    // AMD.
1479    if ( typeof define == 'function' && define.amd )
1480        define( ['picker','jquery'], factory )
1481
1482    // Node.js/browserify.
1483    else if ( typeof exports == 'object' )
1484        module.exports = factory( require('./picker.js'), require('jquery') )
1485
1486    // Browser globals.
1487    else factory( Picker, jQuery )
1488
1489}(function( Picker, $ ) {
1490
1491
1492/**
1493 * Globals and constants
1494 */
1495var DAYS_IN_WEEK = 7,
1496    WEEKS_IN_CALENDAR = 6,
1497    _ = Picker._
1498
1499
1500
1501/**
1502 * The date picker constructor
1503 */
1504function DatePicker( picker, settings ) {
1505
1506    var calendar = this,
1507        element = picker.$node[ 0 ],
1508        elementValue = element.value,
1509        elementDataValue = picker.$node.data( 'value' ),
1510        valueString = elementDataValue || elementValue,
1511        formatString = elementDataValue ? settings.formatSubmit : settings.format,
1512        isRTL = function() {
1513
1514            return element.currentStyle ?
1515
1516                // For IE.
1517                element.currentStyle.direction == 'rtl' :
1518
1519                // For normal browsers.
1520                getComputedStyle( picker.$root[0] ).direction == 'rtl'
1521        }
1522
1523    calendar.settings = settings
1524    calendar.$node = picker.$node
1525
1526    // The queue of methods that will be used to build item objects.
1527    calendar.queue = {
1528        min: 'measure create',
1529        max: 'measure create',
1530        now: 'now create',
1531        select: 'parse create validate',
1532        highlight: 'parse navigate create validate',
1533        view: 'parse create validate viewset',
1534        disable: 'deactivate',
1535        enable: 'activate'
1536    }
1537
1538    // The component's item object.
1539    calendar.item = {}
1540
1541    calendar.item.clear = null
1542    calendar.item.disable = ( settings.disable || [] ).slice( 0 )
1543    calendar.item.enable = -(function( collectionDisabled ) {
1544        return collectionDisabled[ 0 ] === true ? collectionDisabled.shift() : -1
1545    })( calendar.item.disable )
1546
1547    calendar.
1548        set( 'min', settings.min ).
1549        set( 'max', settings.max ).
1550        set( 'now' )
1551
1552    // When there’s a value, set the `select`, which in turn
1553    // also sets the `highlight` and `view`.
1554    if ( valueString ) {
1555        calendar.set( 'select', valueString, { format: formatString })
1556    }
1557
1558    // If there’s no value, default to highlighting “todayâ€?.
1559    else {
1560        calendar.
1561            set( 'select', null ).
1562            set( 'highlight', calendar.item.now )
1563    }
1564
1565
1566    // The keycode to movement mapping.
1567    calendar.key = {
1568        40: 7, // Down
1569        38: -7, // Up
1570        39: function() { return isRTL() ? -1 : 1 }, // Right
1571        37: function() { return isRTL() ? 1 : -1 }, // Left
1572        go: function( timeChange ) {
1573            var highlightedObject = calendar.item.highlight,
1574                targetDate = new Date( highlightedObject.year, highlightedObject.month, highlightedObject.date + timeChange )
1575            calendar.set(
1576                'highlight',
1577                [ targetDate.getFullYear(), targetDate.getMonth(), targetDate.getDate() ],
1578                { interval: timeChange }
1579            )
1580            this.render()
1581        }
1582    }
1583
1584
1585    // Bind some picker events.
1586    picker.
1587        on( 'render', function() {
1588            picker.$root.find( '.' + settings.klass.selectMonth ).on( 'change', function() {
1589                var value = this.value
1590                if ( value ) {
1591                    picker.set( 'highlight', [ picker.get( 'view' ).year, value, picker.get( 'highlight' ).date ] )
1592                    picker.$root.find( '.' + settings.klass.selectMonth ).trigger( 'focus' )
1593                }
1594            })
1595            picker.$root.find( '.' + settings.klass.selectYear ).on( 'change', function() {
1596                var value = this.value
1597                if ( value ) {
1598                    picker.set( 'highlight', [ value, picker.get( 'view' ).month, picker.get( 'highlight' ).date ] )
1599                    picker.$root.find( '.' + settings.klass.selectYear ).trigger( 'focus' )
1600                }
1601            })
1602        }, 1 ).
1603        on( 'open', function() {
1604            var includeToday = ''
1605            if ( calendar.disabled( calendar.get('now') ) ) {
1606                includeToday = ':not(.' + settings.klass.buttonToday + ')'
1607            }
1608            picker.$root.find( 'button' + includeToday + ', select' ).attr( 'disabled', false )
1609        }, 1 ).
1610        on( 'close', function() {
1611            picker.$root.find( 'button, select' ).attr( 'disabled', true )
1612        }, 1 )
1613
1614} //DatePicker
1615
1616
1617/**
1618 * Set a datepicker item object.
1619 */
1620DatePicker.prototype.set = function( type, value, options ) {
1621
1622    var calendar = this,
1623        calendarItem = calendar.item
1624
1625    // If the value is `null` just set it immediately.
1626    if ( value === null ) {
1627        if ( type == 'clear' ) type = 'select'
1628        calendarItem[ type ] = value
1629        return calendar
1630    }
1631
1632    // Otherwise go through the queue of methods, and invoke the functions.
1633    // Update this as the time unit, and set the final value as this item.
1634    // * In the case of `enable`, keep the queue but set `disable` instead.
1635    //   And in the case of `flip`, keep the queue but set `enable` instead.
1636    calendarItem[ ( type == 'enable' ? 'disable' : type == 'flip' ? 'enable' : type ) ] = calendar.queue[ type ].split( ' ' ).map( function( method ) {
1637        value = calendar[ method ]( type, value, options )
1638        return value
1639    }).pop()
1640
1641    // Check if we need to cascade through more updates.
1642    if ( type == 'select' ) {
1643        calendar.set( 'highlight', calendarItem.select, options )
1644    }
1645    else if ( type == 'highlight' ) {
1646        calendar.set( 'view', calendarItem.highlight, options )
1647    }
1648    else if ( type.match( /^(flip|min|max|disable|enable)$/ ) ) {
1649        if ( calendarItem.select && calendar.disabled( calendarItem.select ) ) {
1650            calendar.set( 'select', calendarItem.select, options )
1651        }
1652        if ( calendarItem.highlight && calendar.disabled( calendarItem.highlight ) ) {
1653            calendar.set( 'highlight', calendarItem.highlight, options )
1654        }
1655    }
1656
1657    return calendar
1658} //DatePicker.prototype.set
1659
1660
1661/**
1662 * Get a datepicker item object.
1663 */
1664DatePicker.prototype.get = function( type ) {
1665    return this.item[ type ]
1666} //DatePicker.prototype.get
1667
1668
1669/**
1670 * Create a picker date object.
1671 */
1672DatePicker.prototype.create = function( type, value, options ) {
1673
1674    var isInfiniteValue,
1675        calendar = this
1676
1677    // If there’s no value, use the type as the value.
1678    value = value === undefined ? type : value
1679
1680
1681    // If it’s infinity, update the value.
1682    if ( value == -Infinity || value == Infinity ) {
1683        isInfiniteValue = value
1684    }
1685
1686    // If it’s an object, use the native date object.
1687    else if ( $.isPlainObject( value ) && _.isInteger( value.pick ) ) {
1688        value = value.obj
1689    }
1690
1691    // If it’s an array, convert it into a date and make sure
1692    // that it’s a valid date – otherwise default to today.
1693    else if ( $.isArray( value ) ) {
1694        value = new Date( value[ 0 ], value[ 1 ], value[ 2 ] )
1695        value = _.isDate( value ) ? value : calendar.create().obj
1696    }
1697
1698    // If it’s a number or date object, make a normalized date.
1699    else if ( _.isInteger( value ) || _.isDate( value ) ) {
1700        value = calendar.normalize( new Date( value ), options )
1701    }
1702
1703    // If it’s a literal true or any other case, set it to now.
1704    else /*if ( value === true )*/ {
1705        value = calendar.now( type, value, options )
1706    }
1707
1708    // Return the compiled object.
1709    return {
1710        year: isInfiniteValue || value.getFullYear(),
1711        month: isInfiniteValue || value.getMonth(),
1712        date: isInfiniteValue || value.getDate(),
1713        day: isInfiniteValue || value.getDay(),
1714        obj: isInfiniteValue || value,
1715        pick: isInfiniteValue || value.getTime()
1716    }
1717} //DatePicker.prototype.create
1718
1719
1720/**
1721 * Create a range limit object using an array, date object,
1722 * literal “trueâ€?, or integer relative to another time.
1723 */
1724DatePicker.prototype.createRange = function( from, to ) {
1725
1726    var calendar = this,
1727        createDate = function( date ) {
1728            if ( date === true || $.isArray( date ) || _.isDate( date ) ) {
1729                return calendar.create( date )
1730            }
1731            return date
1732        }
1733
1734    // Create objects if possible.
1735    if ( !_.isInteger( from ) ) {
1736        from = createDate( from )
1737    }
1738    if ( !_.isInteger( to ) ) {
1739        to = createDate( to )
1740    }
1741
1742    // Create relative dates.
1743    if ( _.isInteger( from ) && $.isPlainObject( to ) ) {
1744        from = [ to.year, to.month, to.date + from ];
1745    }
1746    else if ( _.isInteger( to ) && $.isPlainObject( from ) ) {
1747        to = [ from.year, from.month, from.date + to ];
1748    }
1749
1750    return {
1751        from: createDate( from ),
1752        to: createDate( to )
1753    }
1754} //DatePicker.prototype.createRange
1755
1756
1757/**
1758 * Check if a date unit falls within a date range object.
1759 */
1760DatePicker.prototype.withinRange = function( range, dateUnit ) {
1761    range = this.createRange(range.from, range.to)
1762    return dateUnit.pick >= range.from.pick && dateUnit.pick <= range.to.pick
1763}
1764
1765
1766/**
1767 * Check if two date range objects overlap.
1768 */
1769DatePicker.prototype.overlapRanges = function( one, two ) {
1770
1771    var calendar = this
1772
1773    // Convert the ranges into comparable dates.
1774    one = calendar.createRange( one.from, one.to )
1775    two = calendar.createRange( two.from, two.to )
1776
1777    return calendar.withinRange( one, two.from ) || calendar.withinRange( one, two.to ) ||
1778        calendar.withinRange( two, one.from ) || calendar.withinRange( two, one.to )
1779}
1780
1781
1782/**
1783 * Get the date today.
1784 */
1785DatePicker.prototype.now = function( type, value, options ) {
1786    value = new Date()
1787    if ( options && options.rel ) {
1788        value.setDate( value.getDate() + options.rel )
1789    }
1790    return this.normalize( value, options )
1791}
1792
1793
1794/**
1795 * Navigate to next/prev month.
1796 */
1797DatePicker.prototype.navigate = function( type, value, options ) {
1798
1799    var targetDateObject,
1800        targetYear,
1801        targetMonth,
1802        targetDate,
1803        isTargetArray = $.isArray( value ),
1804        isTargetObject = $.isPlainObject( value ),
1805        viewsetObject = this.item.view/*,
1806        safety = 100*/
1807
1808
1809    if ( isTargetArray || isTargetObject ) {
1810
1811        if ( isTargetObject ) {
1812            targetYear = value.year
1813            targetMonth = value.month
1814            targetDate = value.date
1815        }
1816        else {
1817            targetYear = +value[0]
1818            targetMonth = +value[1]
1819            targetDate = +value[2]
1820        }
1821
1822        // If we’re navigating months but the view is in a different
1823        // month, navigate to the view’s year and month.
1824        if ( options && options.nav && viewsetObject && viewsetObject.month !== targetMonth ) {
1825            targetYear = viewsetObject.year
1826            targetMonth = viewsetObject.month
1827        }
1828
1829        // Figure out the expected target year and month.
1830        targetDateObject = new Date( targetYear, targetMonth + ( options && options.nav ? options.nav : 0 ), 1 )
1831        targetYear = targetDateObject.getFullYear()
1832        targetMonth = targetDateObject.getMonth()
1833
1834        // If the month we’re going to doesn’t have enough days,
1835        // keep decreasing the date until we reach the month’s last date.
1836        while ( /*safety &&*/ new Date( targetYear, targetMonth, targetDate ).getMonth() !== targetMonth ) {
1837            targetDate -= 1
1838            /*safety -= 1
1839            if ( !safety ) {
1840                throw 'Fell into an infinite loop while navigating to ' + new Date( targetYear, targetMonth, targetDate ) + '.'
1841            }*/
1842        }
1843
1844        value = [ targetYear, targetMonth, targetDate ]
1845    }
1846
1847    return value
1848} //DatePicker.prototype.navigate
1849
1850
1851/**
1852 * Normalize a date by setting the hours to midnight.
1853 */
1854DatePicker.prototype.normalize = function( value/*, options*/ ) {
1855    value.setHours( 0, 0, 0, 0 )
1856    return value
1857}
1858
1859
1860/**
1861 * Measure the range of dates.
1862 */
1863DatePicker.prototype.measure = function( type, value/*, options*/ ) {
1864
1865    var calendar = this
1866
1867    // If it’s anything false-y, remove the limits.
1868    if ( !value ) {
1869        value = type == 'min' ? -Infinity : Infinity
1870    }
1871
1872    // If it’s a string, parse it.
1873    else if ( typeof value == 'string' ) {
1874        value = calendar.parse( type, value )
1875    }
1876
1877    // If it's an integer, get a date relative to today.
1878    else if ( _.isInteger( value ) ) {
1879        value = calendar.now( type, value, { rel: value } )
1880    }
1881
1882    return value
1883} ///DatePicker.prototype.measure
1884
1885
1886/**
1887 * Create a viewset object based on navigation.
1888 */
1889DatePicker.prototype.viewset = function( type, dateObject/*, options*/ ) {
1890    return this.create([ dateObject.year, dateObject.month, 1 ])
1891}
1892
1893
1894/**
1895 * Validate a date as enabled and shift if needed.
1896 */
1897DatePicker.prototype.validate = function( type, dateObject, options ) {
1898
1899    var calendar = this,
1900
1901        // Keep a reference to the original date.
1902        originalDateObject = dateObject,
1903
1904        // Make sure we have an interval.
1905        interval = options && options.interval ? options.interval : 1,
1906
1907        // Check if the calendar enabled dates are inverted.
1908        isFlippedBase = calendar.item.enable === -1,
1909
1910        // Check if we have any enabled dates after/before now.
1911        hasEnabledBeforeTarget, hasEnabledAfterTarget,
1912
1913        // The min & max limits.
1914        minLimitObject = calendar.item.min,
1915        maxLimitObject = calendar.item.max,
1916
1917        // Check if we’ve reached the limit during shifting.
1918        reachedMin, reachedMax,
1919
1920        // Check if the calendar is inverted and at least one weekday is enabled.
1921        hasEnabledWeekdays = isFlippedBase && calendar.item.disable.filter( function( value ) {
1922
1923            // If there’s a date, check where it is relative to the target.
1924            if ( $.isArray( value ) ) {
1925                var dateTime = calendar.create( value ).pick
1926                if ( dateTime < dateObject.pick ) hasEnabledBeforeTarget = true
1927                else if ( dateTime > dateObject.pick ) hasEnabledAfterTarget = true
1928            }
1929
1930            // Return only integers for enabled weekdays.
1931            return _.isInteger( value )
1932        }).length/*,
1933
1934        safety = 100*/
1935
1936
1937
1938    // Cases to validate for:
1939    // [1] Not inverted and date disabled.
1940    // [2] Inverted and some dates enabled.
1941    // [3] Not inverted and out of range.
1942    //
1943    // Cases to **not** validate for:
1944    // • Navigating months.
1945    // • Not inverted and date enabled.
1946    // • Inverted and all dates disabled.
1947    // • ..and anything else.
1948    if ( !options || !options.nav ) if (
1949        /* 1 */ ( !isFlippedBase && calendar.disabled( dateObject ) ) ||
1950        /* 2 */ ( isFlippedBase && calendar.disabled( dateObject ) && ( hasEnabledWeekdays || hasEnabledBeforeTarget || hasEnabledAfterTarget ) ) ||
1951        /* 3 */ ( !isFlippedBase && (dateObject.pick <= minLimitObject.pick || dateObject.pick >= maxLimitObject.pick) )
1952    ) {
1953
1954
1955        // When inverted, flip the direction if there aren’t any enabled weekdays
1956        // and there are no enabled dates in the direction of the interval.
1957        if ( isFlippedBase && !hasEnabledWeekdays && ( ( !hasEnabledAfterTarget && interval > 0 ) || ( !hasEnabledBeforeTarget && interval < 0 ) ) ) {
1958            interval *= -1
1959        }
1960
1961
1962        // Keep looping until we reach an enabled date.
1963        while ( /*safety &&*/ calendar.disabled( dateObject ) ) {
1964
1965            /*safety -= 1
1966            if ( !safety ) {
1967                throw 'Fell into an infinite loop while validating ' + dateObject.obj + '.'
1968            }*/
1969
1970
1971            // If we’ve looped into the next/prev month with a large interval, return to the original date and flatten the interval.
1972            if ( Math.abs( interval ) > 1 && ( dateObject.month < originalDateObject.month || dateObject.month > originalDateObject.month ) ) {
1973                dateObject = originalDateObject
1974                interval = interval > 0 ? 1 : -1
1975            }
1976
1977
1978            // If we’ve reached the min/max limit, reverse the direction, flatten the interval and set it to the limit.
1979            if ( dateObject.pick <= minLimitObject.pick ) {
1980                reachedMin = true
1981                interval = 1
1982                dateObject = calendar.create([
1983                    minLimitObject.year,
1984                    minLimitObject.month,
1985                    minLimitObject.date + (dateObject.pick === minLimitObject.pick ? 0 : -1)
1986                ])
1987            }
1988            else if ( dateObject.pick >= maxLimitObject.pick ) {
1989                reachedMax = true
1990                interval = -1
1991                dateObject = calendar.create([
1992                    maxLimitObject.year,
1993                    maxLimitObject.month,
1994                    maxLimitObject.date + (dateObject.pick === maxLimitObject.pick ? 0 : 1)
1995                ])
1996            }
1997
1998
1999            // If we’ve reached both limits, just break out of the loop.
2000            if (
2000 reachedMin && reachedMax ) {
2001                break
2002            }
2003
2004
2005            // Finally, create the shifted date using the interval and keep looping.
2006            dateObject = calendar.create([ dateObject.year, dateObject.month, dateObject.date + interval ])
2007        }
2008
2009    } //endif
2010
2011
2012    // Return the date object settled on.
2013    return dateObject
2014} //DatePicker.prototype.validate
2015
2016
2017/**
2018 * Check if a date is disabled.
2019 */
2020DatePicker.prototype.disabled = function( dateToVerify ) {
2021
2022    var
2023        calendar = this,
2024
2025        // Filter through the disabled dates to check if this is one.
2026        isDisabledMatch = calendar.item.disable.filter( function( dateToDisable ) {
2027
2028            // If the date is a number, match the weekday with 0index and `firstDay` check.
2029            if ( _.isInteger( dateToDisable ) ) {
2030                return dateToVerify.day === ( calendar.settings.firstDay ? dateToDisable : dateToDisable - 1 ) % 7
2031            }
2032
2033            // If it’s an array or a native JS date, create and match the exact date.
2034            if ( $.isArray( dateToDisable ) || _.isDate( dateToDisable ) ) {
2035                return dateToVerify.pick === calendar.create( dateToDisable ).pick
2036            }
2037
2038            // If it’s an object, match a date within the “fromâ€? and “toâ€? range.
2039            if ( $.isPlainObject( dateToDisable ) ) {
2040                return calendar.withinRange( dateToDisable, dateToVerify )
2041            }
2042        })
2043
2044    // If this date matches a disabled date, confirm it’s not inverted.
2045    isDisabledMatch = isDisabledMatch.length && !isDisabledMatch.filter(function( dateToDisable ) {
2046        return $.isArray( dateToDisable ) && dateToDisable[3] == 'inverted' ||
2047            $.isPlainObject( dateToDisable ) && dateToDisable.inverted
2048    }).length
2049
2050    // Check the calendar “enabledâ€? flag and respectively flip the
2051    // disabled state. Then also check if it’s beyond the min/max limits.
2052    return calendar.item.enable === -1 ? !isDisabledMatch : isDisabledMatch ||
2053        dateToVerify.pick < calendar.item.min.pick ||
2054        dateToVerify.pick > calendar.item.max.pick
2055
2056} //DatePicker.prototype.disabled
2057
2058
2059/**
2060 * Parse a string into a usable type.
2061 */
2062DatePicker.prototype.parse = function( type, value, options ) {
2063
2064    var calendar = this,
2065        parsingObject = {}
2066
2067    // If it’s already parsed, we’re good.
2068    if ( !value || typeof value != 'string' ) {
2069        return value
2070    }
2071
2072    // We need a `.format` to parse the value with.
2073    if ( !( options && options.format ) ) {
2074        options = options || {}
2075        options.format = calendar.settings.format
2076    }
2077
2078    // Convert the format into an array and then map through it.
2079    calendar.formats.toArray( options.format ).map( function( label ) {
2080
2081        var
2082            // Grab the formatting label.
2083            formattingLabel = calendar.formats[ label ],
2084
2085            // The format length is from the formatting label function or the
2086            // label length without the escaping exclamation (!) mark.
2087            formatLength = formattingLabel ? _.trigger( formattingLabel, calendar, [ value, parsingObject ] ) : label.replace( /^!/, '' ).length
2088
2089        // If there's a format label, split the value up to the format length.
2090        // Then add it to the parsing object with appropriate label.
2091        if ( formattingLabel ) {
2092            parsingObject[ label ] = value.substr( 0, formatLength )
2093        }
2094
2095        // Update the value as the substring from format length to end.
2096        value = value.substr( formatLength )
2097    })
2098
2099    // Compensate for month 0index.
2100    return [
2101        parsingObject.yyyy || parsingObject.yy,
2102        +( parsingObject.mm || parsingObject.m ) - 1,
2103        parsingObject.dd || parsingObject.d
2104    ]
2105} //DatePicker.prototype.parse
2106
2107
2108/**
2109 * Various formats to display the object in.
2110 */
2111DatePicker.prototype.formats = (function() {
2112
2113    // Return the length of the first word in a collection.
2114    function getWordLengthFromCollection( string, collection, dateObject ) {
2115
2116        // Grab the first word from the string.
2117        var word = string.match( /\w+/ )[ 0 ]
2118
2119        // If there's no month index, add it to the date object
2120        if ( !dateObject.mm && !dateObject.m ) {
2121            dateObject.m = collection.indexOf( word ) + 1
2122        }
2123
2124        // Return the length of the word.
2125        return word.length
2126    }
2127
2128    // Get the length of the first word in a string.
2129    function getFirstWordLength( string ) {
2130        return string.match( /\w+/ )[ 0 ].length
2131    }
2132
2133    return {
2134
2135        d: function( string, dateObject ) {
2136
2137            // If there's string, then get the digits length.
2138            // Otherwise return the selected date.
2139            return string ? _.digits( string ) : dateObject.date
2140        },
2141        dd: function( string, dateObject ) {
2142
2143            // If there's a string, then the length is always 2.
2144            // Otherwise return the selected date with a leading zero.
2145            return string ? 2 : _.lead( dateObject.date )
2146        },
2147        ddd: function( string, dateObject ) {
2148
2149            // If there's a string, then get the length of the first word.
2150            // Otherwise return the short selected weekday.
2151            return string ? getFirstWordLength( string ) : this.settings.weekdaysShort[ dateObject.day ]
2152        },
2153        dddd: function( string, dateObject ) {
2154
2155            // If there's a string, then get the length of the first word.
2156            // Otherwise return the full selected weekday.
2157            return string ? getFirstWordLength( string ) : this.settings.weekdaysFull[ dateObject.day ]
2158        },
2159        m: function( string, dateObject ) {
2160
2161            // If there's a string, then get the length of the digits
2162            // Otherwise return the selected month with 0index compensation.
2163            return string ? _.digits( string ) : dateObject.month + 1
2164        },
2165        mm: function( string, dateObject ) {
2166
2167            // If there's a string, then the length is always 2.
2168            // Otherwise return the selected month with 0index and leading zero.
2169            return string ? 2 : _.lead( dateObject.month + 1 )
2170        },
2171        mmm: function( string, dateObject ) {
2172
2173            var collection = this.settings.monthsShort
2174
2175            // If there's a string, get length of the relevant month from the short
2176            // months collection. Otherwise return the selected month from that c
2176ollection.
2177            return string ? getWordLengthFromCollection( string, collection, dateObject ) : collection[ dateObject.month ]
2178        },
2179        mmmm: function( string, dateObject ) {
2180
2181            var collection = this.settings.monthsFull
2182
2183            // If there's a string, get length of the relevant month from the full
2184            // months collection. Otherwise return the selected month from that collection.
2185            return string ? getWordLengthFromCollection( string, collection, dateObject ) : collection[ dateObject.month ]
2186        },
2187        yy: function( string, dateObject ) {
2188
2189            // If there's a string, then the length is always 2.
2190            // Otherwise return the selected year by slicing out the first 2 digits.
2191            return string ? 2 : ( '' + dateObject.year ).slice( 2 )
2192        },
2193        yyyy: function( string, dateObject ) {
2194
2195            // If there's a string, then the length is always 4.
2196            // Otherwise return the selected year.
2197            return string ? 4 : dateObject.year
2198        },
2199
2200        // Create an array by splitting the formatting string passed.
2201        toArray: function( formatString ) { return formatString.split( /(d{1,4}|m{1,4}|y{4}|yy|!.)/g ) },
2202
2203        // Format an object into a string using the formatting options.
2204        toString: function ( formatString, itemObject ) {
2205            var calendar = this
2206            return calendar.formats.toArray( formatString ).map( function( label ) {
2207                return _.trigger( calendar.formats[ label ], calendar, [ 0, itemObject ] ) || label.replace( /^!/, '' )
2208            }).join( '' )
2209        }
2210    }
2211})() //DatePicker.prototype.formats
2212
2213
2214
2215
2216/**
2217 * Check if two date units are the exact.
2218 */
2219DatePicker.prototype.isDateExact = function( one, two ) {
2220
2221    var calendar = this
2222
2223    // When we’re working with weekdays, do a direct comparison.
2224    if (
2225        ( _.isInteger( one ) && _.isInteger( two ) ) ||
2226        ( typeof one == 'boolean' && typeof two == 'boolean' )
2227     ) {
2228        return one === two
2229    }
2230
2231    // When we’re working with date representations, compare the “pickâ€? value.
2232    if (
2233        ( _.isDate( one ) || $.isArray( one ) ) &&
2234        ( _.isDate( two ) || $.isArray( two ) )
2235    ) {
2236        return calendar.create( one ).pick === calendar.create( two ).pick
2237    }
2238
2239    // When we’re working with range objects, compare the “fromâ€? and “toâ€?.
2240    if ( $.isPlainObject( one ) && $.isPlainObject( two ) ) {
2241        return calendar.isDateExact( one.from, two.from ) && calendar.isDateExact( one.to, two.to )
2242    }
2243
2244    return false
2245}
2246
2247
2248/**
2249 * Check if two date units overlap.
2250 */
2251DatePicker.prototype.isDateOverlap = function( one, two ) {
2252
2253    var calendar = this,
2254        firstDay = calendar.settings.firstDay ? 1 : 0
2255
2256    // When we’re working with a weekday index, compare the days.
2257    if ( _.isInteger( one ) && ( _.isDate( two ) || $.isArray( two ) ) ) {
2258        one = one % 7 + firstDay
2259        return one === calendar.create( two ).day + 1
2260    }
2261    if ( _.isInteger( two ) && ( _.isDate( one ) || $.isArray( one ) ) ) {
2262        two = two % 7 + firstDay
2263        return two === calendar.create( one ).day + 1
2264    }
2265
2266    // When we’re working with range objects, check if the ranges overlap.
2267    if ( $.isPlainObject( one ) && $.isPlainObject( two ) ) {
2268        return calendar.overlapRanges( one, two )
2269    }
2270
2271    return false
2272}
2273
2274
2275/**
2276 * Flip the “enabledâ€? state.
2277 */
2278DatePicker.prototype.flipEnable = function(val) {
2279    var itemObject = this.item
2280    itemObject.enable = val || (itemObject.enable == -1 ? 1 : -1)
2281}
2282
2283
2284/**
2285 * Mark a collection of dates as “disabledâ€?.
2286 */
2287DatePicker.prototype.deactivate = function( type, datesToDisable ) {
2288
2289    var calendar = this,
2290        disabledItems = calendar.item.disable.slice(0)
2291
2292
2293    // If we’re flipping, that’s all we need to do.
2294    if ( datesToDisable == 'flip' ) {
2295        calendar.flipEnable()
2296    }
2297
2298    else if ( datesToDisable === false ) {
2299        calendar.flipEnable(1)
2300        disabledItems = []
2301    }
2302
2303    else if ( datesToDisable === true ) {
2304        calendar.flipEnable(-1)
2305        disabledItems = []
2306    }
2307
2308    // Otherwise go through the dates to disable.
2309    else {
2310
2311        datesToDisable.map(function( unitToDisable ) {
2312
2313            var matchFound
2314
2315            // When we have disabled items, check for matches.
2316            // If something is matched, immediately break out.
2317            for ( var index = 0; index < disabledItems.length; index += 1 ) {
2318                if ( calendar.isDateExact( unitToDisable, disabledItems[index] ) ) {
2319                    matchFound = true
2320                    break
2321                }
2322            }
2323
2324            // If nothing was found, add the validated unit to the collection.
2325            if ( !matchFound ) {
2326                if (
2327                    _.isInteger( unitToDisable ) ||
2328                    _.isDate( unitToDisable ) ||
2329                    $.isArray( unitToDisable ) ||
2330                    ( $.isPlainObject( unitToDisable ) && unitToDisable.from && unitToDisable.to )
2331                ) {
2332                    disabledItems.push( unitToDisable )
2333                }
2334            }
2335        })
2336    }
2337
2338    // Return the updated collection.
2339    return disabledItems
2340} //DatePicker.prototype.deactivate
2341
2342
2343/**
2344 * Mark a collection of dates as “enabledâ€?.
2345 */
2346DatePicker.prototype.activate = function( type, datesToEnable ) {
2347
2348    var calendar = this,
2349        disabledItems = calendar.item.disable,
2350        disabledItemsCount = disabledItems.length
2351
2352    // If we’re flipping, that’s all we need to do.
2353    if ( datesToEnable == 'flip' ) {
2354        calendar.flipEnable()
2355    }
2356
2357    else if ( datesToEnable === true ) {
2358        calendar.flipEnable(1)
2359        disabledItems = []
2360    }
2361
2362    else if ( datesToEnable === false ) {
2363        calendar.flipEnable(-1)
2364        disabledItems = []
2365    }
2366
2367    // Otherwise go through the disabled dates.
2368    else {
2369
2370        datesToEnable.map(function( unitToEnable ) {
2371
2372            var matchFound,
2373                disabledUnit,
2374                index,
2375                isExactRange
2376
2377            // Go through the disabled items and try to find a match.
2378            for ( index = 0; index < disabledItemsCount; index += 1 ) {
2379
2380                disabledUnit = disabledItems[index]
2381
2382                // When an exact match is found, remove it from the collection.
2383                if ( calendar.isDateExact( disabledUnit, unitToEnable ) ) {
2384                    matchFound = disabledItems[index] = null
2385                    isExactRange = true
2386                    break
2387                }
2388
2389                // When an overlapped match is found, add the “invertedâ€? state to it.
2390                else if ( calendar.isDateOverlap( disabledUnit, unitToEnable ) ) {
2391                    if ( $.isPlainObject( unitToEnable ) ) {
2392                        unitToEnable.inverted = true
2393                        matchFound = unitToEnable
2394                    }
2395                    else if ( $.isArray( unitToEnable ) ) {
2396                        matchFound = unitToEnable
2397                        if ( !matchFound[3] ) matchFound.push( '
2397inverted' )
2398                    }
2399                    else if ( _.isDate( unitToEnable ) ) {
2400                        matchFound = [ unitToEnable.getFullYear(), unitToEnable.getMonth(), unitToEnable.getDate(), 'inverted' ]
2401                    }
2402                    break
2403                }
2404            }
2405
2406            // If a match was found, remove a previous duplicate entry.
2407            if ( matchFound ) for ( index = 0; index < disabledItemsCount; index += 1 ) {
2408                if ( calendar.isDateExact( disabledItems[index], unitToEnable ) ) {
2409                    disabledItems[index] = null
2410                    break
2411                }
2412            }
2413
2414            // In the event that we’re dealing with an exact range of dates,
2415            // make sure there are no “invertedâ€? dates because of it.
2416            if ( isExactRange ) for ( index = 0; index < disabledItemsCount; index += 1 ) {
2417                if ( calendar.isDateOverlap( disabledItems[index], unitToEnable ) ) {
2418                    disabledItems[index] = null
2419                    break
2420                }
2421            }
2422
2423            // If something is still matched, add it into the collection.
2424            if ( matchFound ) {
2425                disabledItems.push( matchFound )
2426            }
2427        })
2428    }
2429
2430    // Return the updated collection.
2431    return disabledItems.filter(function( val ) { return val != null })
2432} //DatePicker.prototype.activate
2433
2434
2435/**
2436 * Create a string for the nodes in the picker.
2437 */
2438DatePicker.prototype.nodes = function( isOpen ) {
2439
2440    var
2441        calendar = this,
2442        settings = calendar.settings,
2443        calendarItem = calendar.item,
2444        nowObject = calendarItem.now,
2445        selectedObject = calendarItem.select,
2446        highlightedObject = calendarItem.highlight,
2447        viewsetObject = calendarItem.view,
2448        disabledCollection = calendarItem.disable,
2449        minLimitObject = calendarItem.min,
2450        maxLimitObject = calendarItem.max,
2451
2452
2453        // Create the calendar table head using a copy of weekday labels collection.
2454        // * We do a copy so we don't mutate the original array.
2455        tableHead = (function( collection, fullCollection ) {
2456
2457            // If the first day should be Monday, move Sunday to the end.
2458            if ( settings.firstDay ) {
2459                collection.push( collection.shift() )
2460                fullCollection.push( fullCollection.shift() )
2461            }
2462
2463            // Create and return the table head group.
2464            return _.node(
2465                'thead',
2466                _.node(
2467                    'tr',
2468                    _.group({
2469                        min: 0,
2470                        max: DAYS_IN_WEEK - 1,
2471                        i: 1,
2472                        node: 'th',
2473                        item: function( counter ) {
2474                            return [
2475                                collection[ counter ],
2476                                settings.klass.weekdays,
2477                                'scope=col title="' + fullCollection[ counter ] + '"'
2478                            ]
2479                        }
2480                    })
2481                )
2482            ) //endreturn
2483        })( ( settings.showWeekdaysFull ? settings.weekdaysFull : settings.weekdaysShort ).slice( 0 ), settings.weekdaysFull.slice( 0 ) ), //tableHead
2484
2485
2486        // Create the nav for next/prev month.
2487        createMonthNav = function( next ) {
2488
2489            // Otherwise, return the created month tag.
2490            return _.node(
2491                'div',
2492                ' ',
2493                settings.klass[ 'nav' + ( next ? 'Next' : 'Prev' ) ] + (
2494
2495                    // If the focused month is outside the range, disabled the button.
2496                    ( next && viewsetObject.year >= maxLimitObject.year && viewsetObject.month >= maxLimitObject.month ) ||
2497                    ( !next && viewsetObject.year <= minLimitObject.year && viewsetObject.month <= minLimitObject.month ) ?
2498                    ' ' + settings.klass.navDisabled : ''
2499                ),
2500                'data-nav=' + ( next || -1 ) + ' ' +
2501                _.ariaAttr({
2502                    role: 'button',
2503                    controls: calendar.$node[0].id + '_table'
2504                }) + ' ' +
2505                'title="' + (next ? settings.labelMonthNext : settings.labelMonthPrev ) + '"'
2506            ) //endreturn
2507        }, //createMonthNav
2508
2509
2510        // Create the month label.
2511        createMonthLabel = function() {
2512
2513            var monthsCollection = settings.showMonthsShort ? settings.monthsShort : settings.monthsFull
2514
2515            // If there are months to select, add a dropdown menu.
2516            if ( settings.selectMonths ) {
2517
2518                return _.node( 'select',
2519                    _.group({
2520                        min: 0,
2521                        max: 11,
2522                        i: 1,
2523                        node: 'option',
2524                        item: function( loopedMonth ) {
2525
2526                            return [
2527
2528                                // The looped month and no classes.
2529                                monthsCollection[ loopedMonth ], 0,
2530
2531                                // Set the value and selected index.
2532                                'value=' + loopedMonth +
2533                                ( viewsetObject.month == loopedMonth ? ' selected' : '' ) +
2534                                (
2535                                    (
2536                                        ( viewsetObject.year == minLimitObject.year && loopedMonth < minLimitObject.month ) ||
2537                                        ( viewsetObject.year == maxLimitObject.year && loopedMonth > maxLimitObject.month )
2538                                    ) ?
2539                                    ' disabled' : ''
2540                                )
2541                            ]
2542                        }
2543                    }),
2544                    settings.klass.selectMonth,
2545                    ( isOpen ? '' : 'disabled' ) + ' ' +
2546                    _.ariaAttr({ controls: calendar.$node[0].id + '_table' }) + ' ' +
2547                    'title="' + settings.labelMonthSelect + '"'
2548                )
2549            }
2550
2551            // If there's a need for a month selector
2552            return _.node( 'div', monthsCollection[ viewsetObject.month ], settings.klass.month )
2553        }, //createMonthLabel
2554
2555
2556        // Create the year label.
2557        createYearLabel = function() {
2558
2559            var focusedYear = viewsetObject.year,
2560
2561            // If years selector is set to a literal "true", set it to 5. Otherwise
2562            // divide in half to get half before and half after focused year.
2563            numberYears = settings.selectYears === true ? 5 : ~~( settings.selectYears / 2 )
2564
2565            // If there are years to select, add a dropdown menu.
2566            if ( numberYears ) {
2567
2568                var
2569                    minYear = minLimitObject.year,
2570                    maxYear = maxLimitObject.year,
2571                    lowestYear = focusedYear - numberYears,
2572                    highestYear = focusedYear + numberYears
2573
2574                // If the min year is greater than the lowest year, increase the highest year
2575                // by the difference and set the lowest year to the min year.
2576                if ( minYear > lowestYear ) {
2577                    highestYear += minYear - lowestYear
2578                    lowestYear = minYear
2579                }
2580
2581                // If the max year is less than the highest year, decrease the lowest year
2582                // by the lower of the two: available and needed years. Then set the
2583                // highest year to the max year.
2584                if ( maxYear < highestYear ) {
2585
2586                    var availableYears = lowestYear - minYear,
2587                        neededYears = highestYear - maxYear
2588
2589                    lowestYear -= availableYears > neededYears ? neededYears : availableYears
2590                    highestYear = maxYear
2591                }
2592
2593                return _.node( 'select',
2594                    _.group({
2595                        min: lowestYear,
2596                        max: highestYear,
2597                        i: 1,
2598                        node: 'option',
2599                        item: function( loopedYear ) {
2600                            return [
2601
2602                                // The looped year and no classes.
2603                                loopedYear, 0,
2604
2605                                // Set the value and selected index.
2606                                'value=' + loopedYear + ( focusedYear == loopedYear ? ' selected' : '' )
2607                            ]
2608                        }
2609                    }),
2610                    settings.klass.selectYear,
2611                    ( isOpen ? '' : 'disabled' ) + ' ' + _.ariaAttr({ controls: calendar.$node[0].id + '_table' }) + ' ' +
2612                    'title="' + settings.labelYearSelect + '"'
2613                )
2614            }
2615
2616            // Otherwise just return the year focused
2617            return _.node( 'div', focusedYear, settings.klass.year )
2618        } //createYearLabel
2619
2620
2621    // Create and return the entire calendar.
2622    return _.node(
2623        'div',
2624        ( settings.selectYears ? createYearLabel() + createMonthLabel() : createMonthLabel() + createYearLabel() ) +
2625        createMonthNav() + createMonthNav( 1 ),
2626        settings.klass.header
2627    ) + _.node(
2628        'table',
2629        tableHead +
2630        _.node(
2631            'tbody',
2632            _.group({
2633                min: 0,
2634                max: WEEKS_IN_CALENDAR - 1,
2635                i: 1,
2636                node: 'tr',
2637                item: function( rowCounter ) {
2638
2639                    // If Monday is the first day and the month starts on Sunday, shift the date back a week.
2640                    var shiftDateBy = settings.firstDay && calendar.create([ viewsetObject.year, viewsetObject.month, 1 ]).day === 0 ? -7 : 0
2641
2642                    return [
2643                        _.group({
2644                            min: DAYS_IN_WEEK * rowCounter - viewsetObject.day + shiftDateBy + 1, // Add 1 for weekday 0index
2645                            max: function() {
2646                                return this.min + DAYS_IN_WEEK - 1
2647                            },
2648                            i: 1,
2649                            node: 'td',
2650                            item: function( targetDate ) {
2651
2652                                // Convert the time date from a relative date to a target date.
2653                                targetDate = calendar.create([ viewsetObject.year, viewsetObject.month, targetDate + ( settings.firstDay ? 1 : 0 ) ])
2654
2655                                var isSelected = selectedObject && selectedObject.pick == targetDate.pick,
2656                                    isHighlighted = highlightedObject && highlightedObject.pick == targetDate.pick,
2657                                    isDisabled = disabledCollection && calendar.disabled( targetDate ) || targetDate.pick < minLimitObject.pick || targetDate.pick > maxLimitObject.pick
2658
2659                                return [
2660                                    _.node(
2661                                        'div',
2662                                        targetDate.date,
2663                                        (function( klasses ) {
2664
2665                                            // Add the `infocus` or `outfocus` classes based on month in view.
2666                                            klasses.push( viewsetObject.month == targetDate.month ? settings.klass.infocus : settings.klass.outfocus )
2667
2668                                            // Add the `today` class if needed.
2669                                            if ( nowObject.pick == targetDate.pick ) {
2670                                                klasses.push( settings.klass.now )
2671                                            }
2672
2673                                            // Add the `selected` class if something's selected and the time matches.
2674                                            if ( isSelected ) {
2675                                                klasses.push( settings.klass.selected )
2676                                            }
2677
2678                                            // Add the `highlighted` class if something's highlighted and the time matches.
2679                                            if ( isHighlighted ) {
2680                                                klasses.push( settings.klass.highlighted )
2681                                            }
2682
2683                                            // Add the `disabled` class if something's disabled and the object matches.
2684                                            if ( isDisabled ) {
2685                                                klasses.push( settings.klass.disabled )
2686                                            }
2687
2688                                            return klasses.join( ' ' )
2689                                        })([ settings.klass.day ]),
2690                                        'data-pick=' + targetDate.pick + ' ' + _.ariaAttr({
2691                                            role: 'gridcell',
2692                                            selected: isSelected && calendar.$node.val() === _.trigger(
2693                                                    calendar.formats.toString,
2694                                                    calendar,
2695                                                    [ settings.format, targetDate ]
2696                                                ) ? true : null,
2697                                            activedescendant: isHighlighted ? true : null,
2698                                            disabled: isDisabled ? true : null
2699                                        })
2700                                    ),
2701                                    '',
2702                                    _.ariaAttr({ role: 'presentation' })
2703                                ] //endreturn
2704                            }
2705                        })
2706                    ] //endreturn
2707                }
2708            })
2709        ),
2710        settings.klass.table,
2711        'id="' + calendar.$node[0].id + '_table' + '" ' + _.ariaAttr({
2712            role: 'grid',
2713            controls: calendar.$node[0].id,
2714            readonly: true
2715        })
2716    ) +
2717
2718    // * For Firefox forms to submit, make sure to set the buttons’ `type` attributes as “buttonâ€?.
2719    _.node(
2720        'div',
2721        _.node( 'button', settings.today, settings.klass.buttonToday,
2722            'type=button data-pick=' + nowObject.pick +
2723            ( isOpen && !calendar.disabled(nowObject) ? '' : ' disabled' ) + ' ' +
2724            _.ariaAttr({ controls: calendar.$node[0].id }) ) +
2725        _.node( 'button', settings.clear, settings.klass.buttonClear,
2726            'type=button data-clear=1' +
2727            ( isOpen ? '' : ' disabled' ) + ' ' +
2728            _.ariaAttr({ controls: calendar.$node[0].id }) ) +
2729        _.node('button', settings.close, settings.klass.buttonClose,
2730            'type=button data-close=true ' +
2731            ( isOpen ? '' : ' disabled' ) + ' ' +
2732            _.ariaAttr({ controls: calendar.$node[0].id }) ),
2733        settings.klass.footer
2734    ) //endreturn
2735} //DatePicker.prototype.nodes
2736
2737
2738
2739
2740/**
2741 * The date picker defaults.
2742 */
2743DatePicker.defaults = (function( prefix ) {
2744
2745    return {
2746
2747        // The title label to use for the month nav buttons
2748        labelMonthNext: 'Next month',
2749        labelMonthPrev: 'Previous month',
2750
2751        // The title label to use for the dropdown selectors
2752        labelMonthSelect: 'Select a month',
2753        labelYearSelect: 'Select a year',
2754
2755        // Months and weekdays
2756        monthsFull: [ 'January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December' ],
2757        monthsShort: [ 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec' ],
2758        weekdaysFull: [ 'Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday' ],
2759        weekdaysShort: [ 'Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat' ],
2760
2761        // Today and clear
2762        today: 'Today',
2763        clear: 'Clear',
2764        close: 'Close',
2765
2766        // The format to show on the `input` element
2767        format: 'd mmmm, yyyy',
2768
2769        // Classes
2770        klass: {
2771
2772            table: prefix + 'table',
2773
2774            header: prefix + 'header',
2775
2776            navPrev: prefix + 'nav--prev',
2777            navNext: prefix + 'nav--next',
2778            navDisabled: prefix + 'nav--disabled',
2779
2780            month: prefix + 'month',
2781            year: prefix + 'year',
2782
2783            selectMonth: prefix + 'select--month',
2784            selectYear: prefix + 'select--year',
2785
2786            weekdays: prefix + 'weekday',
2787
2788            day: prefix + 'day',
2789            disabled: prefix + 'day--disabled',
2790            selected: prefix + 'day--selected',
2791            highlighted: prefix + 'day--highlighted',
2792            now: prefix + 'day--today',
2793            infocus: prefix + 'day--infocus',
2794            outfocus: prefix + 'day--outfocus',
2795
2796            footer: prefix + 'footer',
2797
2798            buttonClear: prefix + 'button--clear',
2799            buttonToday: prefix + 'button--today',
2800            buttonClose: prefix + 'button--close'
2801        }
2802    }
2803})( Picker.klasses().picker + '__' )
2804
2805
2806
2807
2808
2809/**
2810 * Extend the picker to add the date picker.
2811 */
2812Picker.extend( 'pickadate', DatePicker )
2813
2814
2815}));
2816
2817
2818
2819
2820/*!
2821 * Time picker for pickadate.js v3.5.3
2822 * http://amsul.github.io/pickadate.js/time.htm
2823 */
2824
2825(function ( factory ) {
2826
2827    // AMD.
2828    if ( typeof define == 'function' && define.amd )
2829        define( ['picker','jquery'], factory )
2830
2831    // Node.js/browserify.
2832    else if ( typeof exports == 'object' )
2833        module.exports = factory( require('./picker.js'), require('jquery') )
2834
2835    // Browser globals.
2836    else factory( Picker, jQuery )
2837
2838}(function( Picker, $ ) {
2839
2840
2841/**
2842 * Globals and constants
2843 */
2844var HOURS_IN_DAY = 24,
2845    MINUTES_IN_HOUR = 60,
2846    HOURS_TO_NOON = 12,
2847    MINUTES_IN_DAY = HOURS_IN_DAY * MINUTES_IN_HOUR,
2848    _ = Picker._
2849
2850
2851
2852/**
2853 * The time picker constructor
2854 */
2855function TimePicker( picker, settings ) {
2856
2857    var clock = this,
2858        elementValue = picker.$node[ 0 ].value,
2859        elementDataValue = picker.$node.data( 'value' ),
2860        valueString = elementDataValue || elementValue,
2861        formatString = elementDataValue ? settings.formatSubmit : settings.format
2862
2863    clock.settings = settings
2864    clock.$node = picker.$node
2865
2866    // The queue of methods that will be used to build item objects.
2867    clock.queue = {
2868        interval: 'i',
2869        min: 'measure create',
2870        max: 'measure create',
2871        now: 'now create',
2872        select: 'parse create validate',
2873        highlight: 'parse create validate',
2874        view: 'parse create validate',
2875        disable: 'deactivate',
2876        enable: 'activate'
2877    }
2878
2879    // The component's item object.
2880    clock.item = {}
2881
2882    clock.item.clear = null
2883    clock.item.interval = settings.interval || 30
2884    clock.item.disable = ( settings.disable || [] ).slice( 0 )
2885    clock.item.enable = -(function( collectionDisabled ) {
2886        return collectionDisabled[ 0 ] === true ? collectionDisabled.shift() : -1
2887    })( clock.item.disable )
2888
2889    clock.
2890        set( 'min', settings.min ).
2891        set( 'max', settings.max ).
2892        set( 'now' )
2893
2894    // When there’s a value, set the `select`, which in turn
2895    // also sets the `highlight` and `view`.
2896    if ( valueString ) {
2897        clock.set( 'select', valueString, {
2898            format: formatString,
2899            fromValue: !!elementValue
2900        })
2901    }
2902
2903    // If there’s no value, default to highlighting “todayâ€?.
2904    else {
2905        clock.
2906            set( 'select', null ).
2907            set( 'highlight', clock.item.now )
2908    }
2909
2910    // The keycode to movement mapping.
2911    clock.key = {
2912        40: 1, // Down
2913        38: -1, // Up
2914        39: 1, // Right
2915        37: -1, // Left
2916        go: function( timeChange ) {
2917            clock.set(
2918                'highlight',
2919                clock.item.highlight.pick + timeChange * clock.item.interval,
2920                { interval: timeChange * clock.item.interval }
2921            )
2922            this.render()
2923        }
2924    }
2925
2926
2927    // Bind some picker events.
2928    picker.
2929        on( 'render', function() {
2930            var $pickerHolder = picker.$root.children(),
2931                $viewset = $pickerHolder.find( '.' + settings.klass.viewset ),
2932                vendors = function( prop ) {
2933                    return ['webkit', 'moz', 'ms', 'o', ''].map(function( vendor ) {
2934                        return ( vendor ? '-' + vendor + '-' : '' ) + prop
2935                    })
2936                },
2937                animations = function( $el, state ) {
2938                    vendors( 'transform' ).map(function( prop ) {
2939                        $el.css( prop, state )
2940                    })
2941                    vendors( 'transition' ).map(function( prop ) {
2942                        $el.css( prop, state )
2943                    })
2944                }
2945            if ( $viewset.length ) {
2946                animations( $pickerHolder, 'none' )
2947                $pickerHolder[ 0 ].scrollTop = ~~$viewset.position().top - ( $viewset[ 0 ].clientHeight * 2 )
2948                animations( $pickerHolder, '' )
2949            }
2950        }, 1 ).
2951        on( 'open', function() {
2952            picker.$root.find( 'button' ).attr( 'disabled', false )
2953        }, 1 ).
2954        on( 'close', function() {
2955            picker.$root.find( 'button' ).attr( 'disabled', true )
2956        }, 1 )
2957
2958} //TimePicker
2959
2960
2961/**
2962 * Set a timepicker item object.
2963 */
2964TimePicker.prototype.set = function( type, value, options ) {
2965
2966    var clock = this,
2967        clockItem = clock.item
2968
2969    // If the value is `null` just set it immediately.
2970    if ( value === null ) {
2971        if ( type == 'clear' ) type = 'select'
2972        clockItem[ type ] = value
2973        return clock
2974    }
2975
2976    // Otherwise go through the queue of methods, and invoke the functions.
2977    // Update this as the time unit, and set the final value as this item.
2978    // * In the case of `enable`, keep the queue but set `disable` instead.
2979    //   And in the case of `flip`, keep the queue but set `enable` instead.
2980    clockItem[ ( type == 'enable' ? 'disable' : type == 'flip' ? 'enable' : type ) ] = clock.queue[ type ].split( ' ' ).map( function( method ) {
2981        value = clock[ method ]( type, value, options )
2982        return value
2983    }).pop()
2984
2985    // Check if we need to cascade through more updates.
2986    if ( type == 'select' ) {
2987        clock.set( 'highlight', clockItem.select, options )
2988    }
2989    else if ( type == 'highlight' ) {
2990        clock.set( 'view', clockItem.highlight, options )
2991    }
2992    else if ( type == 'interval' ) {
2993        clock.
2994            set( 'min', clockItem.min, options ).
2995            set( 'max', clockItem.max, options )
2996    }
2997    else if ( type.match( /^(flip|min|max|disable|enable)$/ ) ) {
2998        if ( type == 'min' ) {
2999            clock.set( 'max', clockItem.max, options )
3000        }
3001        if ( clockItem.select && clock.disabled( clockItem.select ) ) {
3002            clock.set( 'select', clockItem.select, options )
3003        }
3004        if ( clockItem.highlight && clock.disabled( clockItem.highlight ) ) {
3005            clock.set( 'highlight', clockItem.highlight, options )
3006        }
3007    }
3008
3009    return clock
3010} //TimePicker.prototype.set
3011
3012
3013/**
3014 * Get a timepicker item object.
3015 */
3016TimePicker.prototype.get = function( type ) {
3017    return this.item[ type ]
3018} //TimePicker.prototype.get
3019
3020
3021/**
3022 * Create a picker time object.
3023 */
3024TimePicker.prototype.create = function( type, value, options ) {
3025
3026    var clock = this
3027
3028    // If there’s no value, use the type as the value.
3029    value = value === undefined ? type : value
3030
3031    // If it’s a date object, convert it into an array.
3032    if ( _.isDate( value ) ) {
3033        value = [ value.getHours(), value.getMinutes() ]
3034    }
3035
3036    // If it’s an object, use the “pickâ€? value.
3037    if ( $.isPlainObject( value ) && _.isInteger( value.pick ) ) {
3038        value = value.pick
3039    }
3040
3041    // If it’s an array, convert it into minutes.
3042    else if ( $.isArray( value ) ) {
3043        value = +value[ 0 ] * MINUTES_IN_HOUR + (+value[ 1 ])
3044    }
3045
3046    // If no valid value is passed, set it to “nowâ€?.
3047    else if ( !_.isInteger( value ) ) {
3048        value = clock.now( type, value, options )
3049    }
3050
3051    // If we’re setting the max, make sure it’s greater than the min.
3052    if ( type == 'max' && value < clock.item.min.pick ) {
3053        value += MINUTES_IN_DAY
3054    }
3055
3056    // If the value doesn’t fall directly on the interval,
3057    // add one interval to indicate it as “passedâ€?.
3058    if ( type != 'min' && type != 'max' && (value - clock.item.min.pick) % clock.item.interval !== 0 ) {
3059        value += clock.item.interval
3060    }
3061
3062    // Normalize it into a “reachableâ€? interval.
3063    value = clock.normalize( type, value, options )
3064
3065    // Return the compiled object.
3066    return {
3067
3068        // Divide to get hours from minutes.
3069        hour: ~~( HOURS_IN_DAY + value / MINUTES_IN_HOUR ) % HOURS_IN_DAY,
3070
3071        // The remainder is the minutes.
3072        mins: ( MINUTES_IN_HOUR + value % MINUTES_IN_HOUR ) % MINUTES_IN_HOUR,
3073
3074        // The time in total minutes.
3075        time: ( MINUTES_IN_DAY + value ) % MINUTES_IN_DAY,
3076
3077        // Reference to the “relativeâ€? value to pick.
3078        pick: value
3079    }
3080} //TimePicker.prototype.create
3081
3082
3083/**
3084 * Create a range limit object using an array, date object,
3085 * literal “trueâ€?, or integer relative to another time.
3086 */
3087TimePicker.prototype.createRange = function( from, to ) {
3088
3089    var clock = this,
3090        createTime = function( time ) {
3091            if ( time === true || $.isArray( time ) || _.isDate( time ) ) {
3092                return clock.create( time )
3093            }
3094            return time
3095        }
3096
3097    // Create objects if possible.
3098    if ( !_.isInteger( from ) ) {
3099        from = createTime( from )
3100    }
3101    if ( !_.isInteger( to ) ) {
3102        to = createTime( to )
3103    }
3104
3105    // Create relative times.
3106    if ( _.isInteger( from ) && $.isPlainObject( to ) ) {
3107        from = [ to.hour, to.mins + ( from * clock.settings.interval ) ];
3108    }
3109    else if ( _.isInteger( to ) && $.isPlainObject( from ) ) {
3110        to = [ from.hour, from.mins + ( to * clock.settings.interval ) ];
3111    }
3112
3113    return {
3114        from: createTime( from ),
3115        to: createTime( to )
3116    }
3117} //TimePicker.prototype.createRange
3118
3119
3120/**
3121 * Check if a time unit falls within a time range object.
3122 */
3123TimePicker.prototype.withinRange = function( range, timeUnit ) {
3124    range = this.createRange(range.from, range.to)
3125    return timeUnit.pick >= range.from.pick && timeUnit.pick <= range.to.pick
3126}
3127
3128
3129/**
3130 * Check if two time range objects overlap.
3131 */
3132TimePicker.prototype.overlapRanges = function( one, two ) {
3133
3134    var clock = this
3135
3136    // Convert the ranges into comparable times.
3137    one = clock.createRange( one.from, one.to )
3138    two = clock.createRange( two.from, two.to )
3139
3140    return clock.withinRange( one, two.from ) || clock.withinRange( one, two.to ) ||
3141        clock.withinRange( two, one.from ) || clock.withinRange( two, one.to )
3142}
3143
3144
3145/**
3146 * Get the time relative to now.
3147 */
3148TimePicker.prototype.now = function( type, value/*, options*/ ) {
3149
3150    var interval = this.item.interval,
3151        date = new Date(),
3152        nowMinutes = date.getHours() * MINUTES_IN_HOUR + date.getMinutes(),
3153        isValueInteger = _.isInteger( value ),
3154        isBelowInterval
3155
3156    // Make sure “nowâ€? falls within the interval range.
3157    nowMinutes -= nowMinutes % interval
3158
3159    // Check if the difference is less than the interval itself.
3160    isBelowInterval = value < 0 && interval * value + nowMinutes <= -interval
3161
3162    // Add an interval because the time has “passedâ€?.
3163    nowMinutes += type == 'min' && isBelowInterval ? 0 : interval
3164
3165    // If the value is a number, adjust by that many intervals.
3166    if ( isValueInteger ) {
3167        nowMinutes += interval * (
3168            isBelowInterval && type != 'max' ?
3169                value + 1 :
3170                value
3171            )
3172    }
3173
3174    // Return the final calculation.
3175    return nowMinutes
3176} //TimePicker.prototype.now
3177
3178
3179/**
3180 * Normalize minutes to be “reachableâ€? based on the min and interval.
3181 */
3182TimePicker.prototype.normalize = function( type, value/*, options*/ ) {
3183
3184    var interval = this.item.interval,
3185        minTime = this.item.min && this.item.min.pick || 0
3186
3187    // If setting min time, don’t shift anything.
3188    // Otherwise get the value and min difference and then
3189    // normalize the difference with the interval.
3190    value -= type == 'min' ? 0 : ( value - minTime ) % interval
3191
3192    // Return the adjusted value.
3193    return value
3194} //TimePicker.prototype.normalize
3195
3196
3197/**
3198 * Measure the range of minutes.
3199 */
3200TimePicker.prototype.measure = function( type, value, options ) {
3201
3202    var clock = this
3203
3204    // If it’s anything false-y, set it to the default.
3205    if ( !value ) {
3206        value = type == 'min' ? [ 0, 0 ] : [ HOURS_IN_DAY - 1, MINUTES_IN_HOUR - 1 ]
3207    }
3208
3209    // If it’s a string, parse it.
3210    if ( typeof value == 'string' ) {
3211        value = clock.parse( type, value )
3212    }
3213
3214    // If it’s a literal true, or an integer, make it relative to now.
3215    else if ( value === true || _.isInteger( value ) ) {
3216        value = clock.now( type, value, options )
3217    }
3218
3219    // If it’s an object already, just normalize it.
3220    else if ( $.isPlainObject( value ) && _.isInteger( value.pick ) ) {
3221        value = clock.normalize( type, value.pick, options )
3222    }
3223
3224    return value
3225} ///TimePicker.prototype.measure
3226
3227
3228/**
3229 * Validate an object as enabled.
3230 */
3231TimePicker.prototype.validate = function( type, timeObject, options ) {
3232
3233    var clock = this,
3234        interval = options && options.interval ? options.interval : clock.item.interval
3235
3236    // Check if the object is disabled.
3237    if ( clock.disabled( timeObject ) ) {
3238
3239        // Shift with the interval until we reach an enabled time.
3240        timeObject = clock.shift( timeObject, interval )
3241    }
3242
3243    // Scope the object into range.
3244    timeObject = clock.scope( timeObject )
3245
3246    // Do a second check to see if we landed on a disabled min/max.
3247    // In that case, shift using the opposite interval as before.
3248    if ( clock.disabled( timeObject ) ) {
3249        timeObject = clock.shift( timeObject, interval * -1 )
3250    }
3251
3252    // Return the final object.
3253    return timeObject
3254} //TimePicker.prototype.validate
3255
3256
3257/**
3258 * Check if an object is disabled.
3259 */
3260TimePicker.prototype.disabled = function( timeToVerify ) {
3261
3262    var clock = this,
3263
3264        // Filter through the disabled times to check if this is one.
3265        isDisabledMatch = clock.item.disable.filter( function( timeToDisable ) {
3266
3267            // If the time is a number, match the hours.
3268            if ( _.isInteger( timeToDisable ) ) {
3269                return timeToVerify.hour == timeToDisable
3270            }
3271
3272            // If it’s an array, create the object and match the times.
3273            if ( $.isArray( timeToDisable ) || _.isDate( timeToDisable ) ) {
3274                return timeToVerify.pick == clock.create( timeToDisable ).pick
3275            }
3276
3277            // If it’s an object, match a time within the “fromâ€? and “toâ€? range.
3278            if ( $.isPlainObject( timeToDisable ) ) {
3279                return clock.withinRange( timeToDisable, timeToVerify )
3280            }
3281        })
3282
3283    // If this time matches a disabled time, confirm it’s not inverted.
3284    isDisabledMatch = isDisabledMatch.length && !isDisabledMatch.filter(function( timeToDisable ) {
3285        return $.isArray( timeToDisable ) && timeToDisable[2] == 'inverted' ||
3286            $.isPlainObject( timeToDisable ) && timeToDisable.inverted
3287    }).length
3288
3289    // If the clock is "enabled" flag is flipped, flip the condition.
3290    return clock.item.enable === -1 ? !isDisabledMatch : isDisabledMatch ||
3291        timeToVerify.pick < clock.item.min.pick ||
3292        timeToVerify.pick > clock.item.max.pick
3293} //TimePicker.prototype.disabled
3294
3295
3296/**
3297 * Shift an object by an interval until we reach an enabled object.
3298 */
3299TimePicker.prototype.shift = function( timeObject, interval ) {
3300
3301    var clock = this,
3302        minLimit = clock.item.min.pick,
3303        maxLimit = clock.item.max.pick/*,
3304        safety = 1000*/
3305
3306    interval = interval || clock.item.interval
3307
3308    // Keep looping as long as the time is disabled.
3309    while ( /*safety &&*/ clock.disabled( timeObject ) ) {
3310
3311        /*safety -= 1
3312        if ( !safety ) {
3313            throw 'Fell into an infinite loop while shifting to ' + timeObject.hour + ':' + timeObject.mins + '.'
3314        }*/
3315
3316        // Increase/decrease the time by the interval and keep looping.
3317        timeObject = clock.create( timeObject.pick += interval )
3318
3319        // If we've looped beyond the limits, break out of the loop.
3320        if ( timeObject.pick <= minLimit || timeObject.pick >= maxLimit ) {
3321            break
3322        }
3323    }
3324
3325    // Return the final object.
3326    return timeObject
3327} //TimePicker.prototype.shift
3328
3329
3330/**
3331 * Scope an object to be within range of min and max.
3332 */
3333TimePicker.prototype.scope = function( timeObject ) {
3334    var minLimit = this.item.min.pick,
3335        maxLimit = this.item.max.pick
3336    return this.create( timeObject.pick > maxLimit ? maxLimit : timeObject.pick < minLimit ? minLimit : timeObject )
3337} //TimePicker.prototype.scope
3338
3339
3340/**
3341 * Parse a string into a usable type.
3342 */
3343TimePicker.prototype.parse = function( type, value, options ) {
3344
3345    var hour, minutes, isPM, item, parseValue,
3346        clock = this,
3347        parsingObject = {}
3348
3349    // If it’s already parsed, we’re good.
3350    if ( !value || typeof value != 'string' ) {
3351        return value
3352    }
3353
3354    // We need a `.format` to parse the value with.
3355    if ( !( options && options.format ) ) {
3356        options = options || {}
3357        options.format = clock.settings.format
3358    }
3359
3360    // Convert the format into an array and then map through it.
3361    clock.formats.toArray( options.format ).map( function( label ) {
3362
3363        var
3364            substring,
3365
3366            // Grab the formatting label.
3367            formattingLabel = clock.formats[ label ],
3368
3369            // The format length is from the formatting label function or the
3370            // label length without the escaping exclamation (!) mark.
3371            formatLength = formattingLabel ?
3372                _.trigger( formattingLabel, clock, [ value, parsingObject ] ) :
3373                label.replace( /^!/, '' ).length
3374
3375        // If there's a format label, split the value up to the format length.
3376        // Then add it to the parsing object with appropriate label.
3377        if ( formattingLabel ) {
3378            substring = v
3378alue.substr( 0, formatLength )
3379            parsingObject[ label ] = substring.match(/^\d+$/) ? +substring : substring
3380        }
3381
3382        // Update the time value as the substring from format length to end.
3383        value = value.substr( formatLength )
3384    })
3385
3386    // Grab the hour and minutes from the parsing object.
3387    for ( item in parsingObject ) {
3388        parseValue = parsingObject[item]
3389        if ( _.isInteger(parseValue) ) {
3390            if ( item.match(/^(h|hh)$/i) ) {
3391                hour = parseValue
3392                if ( item == 'h' || item == 'hh' ) {
3393                    hour %= 12
3394                }
3395            }
3396            else if ( item == 'i' ) {
3397                minutes = parseValue
3398            }
3399        }
3400        else if ( item.match(/^a$/i) && parseValue.match(/^p/i) && ('h' in parsingObject || 'hh' in parsingObject) ) {
3401            isPM = true
3402        }
3403    }
3404
3405    // Calculate it in minutes and return.
3406    return (isPM ? hour + 12 : hour) * MINUTES_IN_HOUR + minutes
3407} //TimePicker.prototype.parse
3408
3409
3410/**
3411 * Various formats to display the object in.
3412 */
3413TimePicker.prototype.formats = {
3414
3415    h: function( string, timeObject ) {
3416
3417        // If there's string, then get the digits length.
3418        // Otherwise return the selected hour in "standard" format.
3419        return string ? _.digits( string ) : timeObject.hour % HOURS_TO_NOON || HOURS_TO_NOON
3420    },
3421    hh: function( string, timeObject ) {
3422
3423        // If there's a string, then the length is always 2.
3424        // Otherwise return the selected hour in "standard" format with a leading zero.
3425        return string ? 2 : _.lead( timeObject.hour % HOURS_TO_NOON || HOURS_TO_NOON )
3426    },
3427    H: function( string, timeObject ) {
3428
3429        // If there's string, then get the digits length.
3430        // Otherwise return the selected hour in "military" format as a string.
3431        return string ? _.digits( string ) : '' + ( timeObject.hour % 24 )
3432    },
3433    HH: function( string, timeObject ) {
3434
3435        // If there's string, then get the digits length.
3436        // Otherwise return the selected hour in "military" format with a leading zero.
3437        return string ? _.digits( string ) : _.lead( timeObject.hour % 24 )
3438    },
3439    i: function( string, timeObject ) {
3440
3441        // If there's a string, then the length is always 2.
3442        // Otherwise return the selected minutes.
3443        return string ? 2 : _.lead( timeObject.mins )
3444    },
3445    a: function( string, timeObject ) {
3446
3447        // If there's a string, then the length is always 4.
3448        // Otherwise check if it's more than "noon" and return either am/pm.
3449        return string ? 4 : MINUTES_IN_DAY / 2 > timeObject.time % MINUTES_IN_DAY ? 'a.m.' : 'p.m.'
3450    },
3451    A: function( string, timeObject ) {
3452
3453        // If there's a string, then the length is always 2.
3454        // Otherwise check if it's more than "noon" and return either am/pm.
3455        return string ? 2 : MINUTES_IN_DAY / 2 > timeObject.time % MINUTES_IN_DAY ? 'AM' : 'PM'
3456    },
3457
3458    // Create an array by splitting the formatting string passed.
3459    toArray: function( formatString ) { return formatString.split( /(h{1,2}|H{1,2}|i|a|A|!.)/g ) },
3460
3461    // Format an object into a string using the formatting options.
3462    toString: function ( formatString, itemObject ) {
3463        var clock = this
3464        return clock.formats.toArray( formatString ).map( function( label ) {
3465            return _.trigger( clock.formats[ label ], clock, [ 0, itemObject ] ) || label.replace( /^!/, '' )
3466        }).join( '' )
3467    }
3468} //TimePicker.prototype.formats
3469
3470
3471
3472
3473/**
3474 * Check if two time units are the exact.
3475 */
3476TimePicker.prototype.isTimeExact = function( one, two ) {
3477
3478    var clock = this
3479
3480    // When we’re working with minutes, do a direct comparison.
3481    if (
3482        ( _.isInteger( one ) && _.isInteger( two ) ) ||
3483        ( typeof one == 'boolean' && typeof two == 'boolean' )
3484     ) {
3485        return one === two
3486    }
3487
3488    // When we’re working with time representations, compare the “pickâ€? value.
3489    if (
3490        ( _.isDate( one ) || $.isArray( one ) ) &&
3491        ( _.isDate( two ) || $.isArray( two ) )
3492    ) {
3493        return clock.create( one ).pick === clock.create( two ).pick
3494    }
3495
3496    // When we’re working with range objects, compare the “fromâ€? and “toâ€?.
3497    if ( $.isPlainObject( one ) && $.isPlainObject( two ) ) {
3498        return clock.isTimeExact( one.from, two.from ) && clock.isTimeExact( one.to, two.to )
3499    }
3500
3501    return false
3502}
3503
3504
3505/**
3506 * Check if two time units overlap.
3507 */
3508TimePicker.prototype.isTimeOverlap = function( one, two ) {
3509
3510    var clock = this
3511
3512    // When we’re working with an integer, compare the hours.
3513    if ( _.isInteger( one ) && ( _.isDate( two ) || $.isArray( two ) ) ) {
3514        return one === clock.create( two ).hour
3515    }
3516    if ( _.isInteger( two ) && ( _.isDate( one ) || $.isArray( one ) ) ) {
3517        return two === clock.create( one ).hour
3518    }
3519
3520    // When we’re working with range objects, check if the ranges overlap.
3521    if ( $.isPlainObject( one ) && $.isPlainObject( two ) ) {
3522        return clock.overlapRanges( one, two )
3523    }
3524
3525    return false
3526}
3527
3528
3529/**
3530 * Flip the “enabledâ€? state.
3531 */
3532TimePicker.prototype.flipEnable = function(val) {
3533    var itemObject = this.item
3534    itemObject.enable = val || (itemObject.enable == -1 ? 1 : -1)
3535}
3536
3537
3538/**
3539 * Mark a collection of times as “disabledâ€?.
3540 */
3541TimePicker.prototype.deactivate = function( type, timesToDisable ) {
3542
3543    var clock = this,
3544        disabledItems = clock.item.disable.slice(0)
3545
3546
3547    // If we’re flipping, that’s all we need to do.
3548    if ( timesToDisable == 'flip' ) {
3549        clock.flipEnable()
3550    }
3551
3552    else if ( timesToDisable === false ) {
3553        clock.flipEnable(1)
3554        disabledItems = []
3555    }
3556
3557    else if ( timesToDisable === true ) {
3558        clock.flipEnable(-1)
3559        disabledItems = []
3560    }
3561
3562    // Otherwise go through the times to disable.
3563    else {
3564
3565        timesToDisable.map(function( unitToDisable ) {
3566
3567            var matchFound
3568
3569            // When we have disabled items, check for matches.
3570            // If something is matched, immediately break out.
3571            for ( var index = 0; index < disabledItems.length; index += 1 ) {
3572                if ( clock.isTimeExact( unitToDisable, disabledItems[index] ) ) {
3573                    matchFound = true
3574                    break
3575                }
3576            }
3577
3578            // If nothing was found, add the validated unit to the collection.
3579            if ( !matchFound ) {
3580                if (
3581                    _.isInteger( unitToDisable ) ||
3582                    _.isDate( unitToDisable ) ||
3583                    $.isArray( unitToDisable ) ||
3584                    ( $.isPlainObject( unitToDisable ) && unitToDisable.from && unitToDisable.to )
3585                ) {
3586                    disabledItems.push( unitToDisable )
3587                }
3588            }
3589        })
3590    }
3591
3592    // Return the updated collection.
3593    return disabledItems
3594} //TimePicker.prototype.deactivate
3595
3596
3597/**
3598 * Mark a collection of times as “enabledâ€?.
3599 */
3600TimePicker.prototype.activate = function( type, timesToEnable ) {
3601
3602    var clock = this,
3603        disabledItems = clock.item.disable,
3604        disabledItemsCount = disabledItems.length
3605
3606    // If we’re flipping, that’s all we need to do.
3607    if ( timesToEnable == 'flip' ) {
3608        clock.flipEnable()
3609    }
3610
3611    else if ( timesToEnable === true ) {
3612        clock.flipEnable(1)
3613        disabledItems = []
3614    }
3615
3616    else if ( timesToEnable === false ) {
3617        clock.flipEnable(-1)
3618        disabledItems = []
3619    }
3620
3621    // Otherwise go through the disabled times.
3622    else {
3623
3624        timesToEnable.map(function( unitToEnable ) {
3625
3626            var matchFound,
3627                disabledUnit,
3628                index,
3629                isRangeMatched
3630
3631            // Go through the disabled items and try to find a match.
3632            for ( index = 0; index < disabledItemsCount; index += 1 ) {
3633
3634                disabledUnit = disabledItems[index]
3635
3636                // When an exact match is found, remove it from the collection.
3637                if ( clock.isTimeExact( disabledUnit, unitToEnable ) ) {
3638                    matchFound = disabledItems[index] = null
3639                    isRangeMatched = true
3640                    break
3641                }
3642
3643                // When an overlapped match is found, add the “invertedâ€? state to it.
3644                else if ( clock.isTimeOverlap( disabledUnit, unitToEnable ) ) {
3645                    if ( $.isPlainObject( unitToEnable ) ) {
3646                        unitToEnable.inverted = true
3647                        matchFound = unitToEnable
3648                    }
3649                    else if ( $.isArray( unitToEnable ) ) {
3650                        matchFound = unitToEnable
3651                        if ( !matchFound[2] ) matchFound.push( '
3651inverted' )
3652                    }
3653                    else if ( _.isDate( unitToEnable ) ) {
3654                        matchFound = [ unitToEnable.getFullYear(), unitToEnable.getMonth(), unitToEnable.getDate(), 'inverted' ]
3655                    }
3656                    break
3657                }
3658            }
3659
3660            // If a match was found, remove a previous duplicate entry.
3661            if ( matchFound ) for ( index = 0; index < disabledItemsCount; index += 1 ) {
3662                if ( clock.isTimeExact( disabledItems[index], unitToEnable ) ) {
3663                    disabledItems[index] = null
3664                    break
3665                }
3666            }
3667
3668            // In the event that we’re dealing with an overlap of range times,
3669            // make sure there are no “invertedâ€? times because of it.
3670            if ( isRangeMatched ) for ( index = 0; index < disabledItemsCount; index += 1 ) {
3671                if ( clock.isTimeOverlap( disabledItems[index], unitToEnable ) ) {
3672                    disabledItems[index] = null
3673                    break
3674                }
3675            }
3676
3677            // If something is still matched, add it into the collection.
3678            if ( matchFound ) {
3679                disabledItems.push( matchFound )
3680            }
3681        })
3682    }
3683
3684    // Return the updated collection.
3685    return disabledItems.filter(function( val ) { return val != null })
3686} //TimePicker.prototype.activate
3687
3688
3689/**
3690 * The division to use for the range intervals.
3691 */
3692TimePicker.prototype.i = function( type, value/*, options*/ ) {
3693    return _.isInteger( value ) && value > 0 ? value : this.item.interval
3694}
3695
3696
3697/**
3698 * Create a string for the nodes in the picker.
3699 */
3700TimePicker.prototype.nodes = function( isOpen ) {
3701
3702    var
3703        clock = this,
3704        settings = clock.settings,
3705        selectedObject = clock.item.select,
3706        highlightedObject = clock.item.highlight,
3707        viewsetObject = clock.item.view,
3708        disabledCollection = clock.item.disable
3709
3710    return _.node(
3711        'ul',
3712        _.group({
3713            min: clock.item.min.pick,
3714            max: clock.item.max.pick,
3715            i: clock.item.interval,
3716            node: 'li',
3717            item: function( loopedTime ) {
3718                loopedTime = clock.create( loopedTime )
3719                var timeMinutes = loopedTime.pick,
3720                    isSelected = selectedObject && selectedObject.pick == timeMinutes,
3721                    isHighlighted = highlightedObject && highlightedObject.pick == timeMinutes,
3722                    isDisabled = disabledCollection && clock.disabled( loopedTime )
3723                return [
3724                    _.trigger( clock.formats.toString, clock, [ _.trigger( settings.formatLabel, clock, [ loopedTime ] ) || settings.format, loopedTime ] ),
3725                    (function( klasses ) {
3726
3727                        if ( isSelected ) {
3728                            klasses.push( settings.klass.selected )
3729                        }
3730
3731                        if ( isHighlighted ) {
3732                            klasses.push( settings.klass.highlighted )
3733                        }
3734
3735                        if ( viewsetObject && viewsetObject.pick == timeMinutes ) {
3736                            klasses.push( settings.klass.viewset )
3737                        }
3738
3739                        if ( isDisabled ) {
3740                            klasses.push( settings.klass.disabled )
3741                        }
3742
3743                        return klasses.join( ' ' )
3744                    })( [ settings.klass.listItem ] ),
3745                    'data-pick=' + loopedTime.pick + ' ' + _.ariaAttr({
3746                        role: 'option',
3747                        selected: isSelected && clock.$node.val() === _.trigger(
3748                                clock.formats.toString,
3749                                clock,
3750                                [ settings.format, loopedTime ]
3751                            ) ? true : null,
3752                        activedescendant: isHighlighted ? true : null,
3753                        disabled: isDisabled ? true : null
3754                    })
3755                ]
3756            }
3757        }) +
3758
3759        // * For Firefox forms to submit, make sure to set the button’s `type` attribute as “buttonâ€?.
3760        _.node(
3761            'li',
3762            _.node(
3763                'button',
3764                settings.clear,
3765                settings.klass.buttonClear,
3766                'type=button data-clear=1' + ( isOpen ? '' : ' disabled' ) + ' ' +
3767                _.ariaAttr({ controls: clock.$node[0].id })
3768            ),
3769            '', _.ariaAttr({ role: 'presentation' })
3770        ),
3771        settings.klass.list,
3772        _.ariaAttr({ role: 'listbox', controls: clock.$node[0].id })
3773    )
3774} //TimePicker.prototype.nodes
3775
3776
3777
3778
3779
3780
3781
3782/* ==========================================================================
3783   Extend the picker to add the component with the defaults.
3784   ========================================================================== */
3785
3786TimePicker.defaults = (function( prefix ) {
3787
3788    return {
3789
3790        // Clear
3791        clear: 'Clear',
3792
3793        // The format to show on the `input` element
3794        format: 'h:i A',
3795
3796        // The interval between each time
3797        interval: 30,
3798
3799        // Classes
3800        klass: {
3801
3802            picker: prefix + ' ' + prefix + '--time',
3803            holder: prefix + '__holder',
3804
3805            list: prefix + '__list',
3806            listItem: prefix + '__list-item',
3807
3808            disabled: prefix + '__list-item--disabled',
3809            selected: prefix + '__list-item--selected',
3810            highlighted: prefix + '__list-item--highlighted',
3811            viewset: prefix + '__list-item--viewset',
3812            now: prefix + '__list-item--now',
3813
3814            buttonClear: prefix + '__button--clear'
3815        }
3816    }
3817})( Picker.klasses().picker )
3818
3819
3820
3821
3822
3823/**
3824 * Extend the picker to add the time picker.
3825 */
3826Picker.extend( 'pickatime', TimePicker )
3827
3828
3829}));
3830
3831
3832/* Chosen v1.1.0 | (c) 2011-2013 by Harvest | MIT License, https://github.com/harvesthq/chosen/blob/master/LICENSE.md */
vendor: 29,308 bytes, lines 3833-3933
3833!function(){var a,AbstractChosen,Chosen,SelectParser,b,c={}.hasOwnProperty,d=function(a,b){function d(){this.constructor=a}for(var e in b)c.call(b,e)&&(a[e]=b[e]);return d.prototype=b.prototype,a.prototype=new d,a.__super__=b.prototype,a};SelectParser=function(){function SelectParser(){this.options_index=0,this.parsed=[]}return SelectParser.prototype.add_node=function(a){return"OPTGROUP"===a.nodeName.toUpperCase()?this.add_group(a):this.add_option(a)},SelectParser.prototype.add_group=function(a){var b,c,d,e,f,g;for(b=this.parsed.length,this.parsed.push({array_index:b,group:!0,label:this.escapeExpression(a.label),children:0,disabled:a.disabled}),f=a.childNodes,g=[],d=0,e=f.length;e>d;d++)c=f[d],g.push(this.add_option(c,b,a.disabled));return g},SelectParser.prototype.add_option=function(a,b,c){return"OPTION"===a.nodeName.toUpperCase()?(""!==a.text?(null!=b&&(this.parsed[b].children+=1),this.parsed.push({array_index:this.parsed.length,options_index:this.options_index,value:a.value,text:a.text,html:a.innerHTML,selected:a.selected,disabled:c===!0?c:a.disabled,group_array_index:b,classes:a.className,style:a.style.cssText})):this.parsed.push({array_index:this.parsed.length,options_index:this.options_index,empty:!0}),this.options_index+=1):void 0},SelectParser.prototype.escapeExpression=function(a){var b,c;return null==a||a===!1?"":/[\&\<\>\"\'\`]/.test(a)?(b={"<":"&lt;",">":"&gt;",'"':"&quot;","'":"&#x27;","`":"&#x60;"},c=/&(?!\w+;)|[\<\>\"\'\`]/g,a.replace(c,function(a){return b[a]||"&amp;"})):a},SelectParser}(),SelectParser.select_to_array=function(a){var b,c,d,e,f;for(c=new SelectParser,f=a.childNodes,d=0,e=f.length;e>d;d++)b=f[d],c.add_node(b);return c.parsed},AbstractChosen=function(){function AbstractChosen(a,b){this.form_field=a,this.options=null!=b?b:{},AbstractChosen.browser_is_supported()&&(this.is_multiple=this.form_field.multiple,this.set_default_text(),this.set_default_values(),this.setup(),this.set_up_html(),this.register_observers())}return AbstractChosen.prototype.set_default_values=function(){var a=this;return this.click_test_action=function(b){return a.test_active_click(b)},this.activate_action=function(b){return a.activate_field(b)},this.active_field=!1,this.mouse_on_container=!1,this.results_showing=!1,this.result_highlighted=null,this.allow_single_deselect=null!=this.options.allow_single_deselect&&null!=this.form_field.options[0]&&""===this.form_field.options[0].text?this.options.allow_single_deselect:!1,this.disable_search_threshold=this.options.disable_search_threshold||0,this.disable_search=this.options.disable_search||!1,this.enable_split_word_search=null!=this.options.enable_split_word_search?this.options.enable_split_word_search:!0,this.group_search=null!=this.options.group_search?this.options.group_search:!0,this.search_contains=this.options.search_contains||!1,this.single_backstroke_delete=null!=this.options.single_backstroke_delete?this.options.single_backstroke_delete:!0,this.max_selected_options=this.options.max_selected_options||1/0,this.inherit_select_classes=this.options.inherit_select_classes||!1,this.display_selected_options=null!=this.options.display_selected_options?this.options.display_selected_options:!0,this.display_disabled_options=null!=this.options.display_disabled_options?this.options.display_disabled_options:!0},AbstractChosen.prototype.set_default_text=function(){return this.default_text=this.form_field.getAttribute("data-placeholder")?this.form_field.getAttribute("data-placeholder"):this.is_multiple?this.options.placeholder_text_multiple||this.options.placeholder_text||AbstractChosen.default_multiple_text:this.options.placeholder_text_single||this.options.placeholder_text||AbstractChosen.default_single_text,this.results_none_found=this.form_field.getAttribute("data-no_results_text")||this.options.no_results_text||AbstractChosen.default_no_result_text},AbstractChosen.prototype.mouse_enter=function(){return this.mouse_on_container=!0},AbstractChosen.prototype.mouse_leave=function(){return this.mouse_on_container=!1},AbstractChosen.prototype.input_focus=function(){var a=this;if(this.is_multiple){if(!this.active_field)return setTimeout(function(){return a.container_mousedown()},50)}else if(!this.active_field)return this.activate_field()},AbstractChosen.prototype.input_blur=function(){var a=this;return this.mouse_on_container?void 0:(this.active_field=!1,setTimeout(function(){return a.blur_test()},100))},AbstractChosen.prototype.results_option_build=function(a){var b,c,d,e,f;for(b="",f=this.results_data,d=0,e=f.length;e>d;d++)c=f[d],b+=c.group?this.result_add_group(c):this.result_add_option(c),(null!=a?a.first:void 0)&&(c.selected&&this.is_multiple?this.choice_build(c):c.selected&&!this.is_multiple&&this.single_set_selected_text(c.text));return b},AbstractChosen.prototype.result_add_option=function(a){var b,c;return a.search_match?this.include_option_in_results(a)?(b=[],a.disabled||a.selected&&this.is_multiple||b.push("active-result"),!a.disabled||a.selected&&this.is_multiple||b.push("disabled-result"),a.selected&&b.push("result-selected"),null!=a.group_array_index&&b.push("group-option"),""!==a.classes&&b.push(a.classes),c=document.createElement("li"),c.className=b.join(" "),c.style.cssText=a.style,c.setAttribute("data-option-array-index",a.array_index),c.innerHTML=a.search_text,this.outerHTML(c)):"":""},AbstractChosen.prototype.result_add_group=function(a){var b;return a.search_match||a.group_match?a.active_options>0?(b=document.createElement("li"),b.className="group-result",b.innerHTML=a.search_text,this.outerHTML(b)):"":""},AbstractChosen.prototype.results_update_field=function(){return this.set_default_text(),this.is_multiple||this.results_reset_cleanup(),this.result_clear_highlight(),this.results_build(),this.results_showing?this.winnow_results():void 0},AbstractChosen.prototype.reset_single_select_options=function(){var a,b,c,d,e;for(d=this.results_data,e=[],b=0,c=d.length;c>b;b++)a=d[b],a.selected?e.push(a.selected=!1):e.push(void 0);return e},AbstractChosen.prototype.results_toggle=function(){return this.results_showing?this.results_hide():this.results_show()},AbstractChosen.prototype.results_search=function(){return this.results_showing?this.winnow_results():this.results_show()},AbstractChosen.prototype.winnow_results=function(){var a,b,c,d,e,f,g,h,i,j,k,l,m;for(this.no_results_clear(),e=0,g=this.get_search_text(),a=g.replace(/[-[\]{}()*+?.,\\^$|#\s]/g,"\\$&"),d=this.search_contains?"":"^",c=new RegExp(d+a,"i"),j=new RegExp(a,"i"),m=this.results_data,k=0,l=m.length;l>k;k++)b=m[k],b.search_match=!1,f=null,this.include_option_in_results(b)&&(b.group&&(b.group_match=!1,b.active_options=0),null!=b.group_array_index&&this.results_data[b.group_array_index]&&(f=this.results_data[b.group_array_index],0===f.active_options&&f.search_match&&(e+=1),f.active_options+=1),(!b.group||this.group_search)&&(b.search_text=b.group?b.label:b.html,b.search_match=this.search_string_match(b.search_text,c),b.search_match&&!b.group&&(e+=1),b.search_match?(g.length&&(h=b.search_text.search(j),i=b.search_text.substr(0,h+g.length)+"</em>"+b.search_text.substr(h+g.length),b.search_text=i.substr(0,h)+"<em>"+i.substr(h)),null!=f&&(f.group_match=!0)):null!=b.group_array_index&&this.results_data[b.group_array_index].search_match&&(b.search_match=!0)));return this.result_clear_highlight(),1>e&&g.length?(this.update_results_content(""),this.no_results(g)):(this.update_results_content(this.results_option_build()),this.winnow_results_set_highlight())},AbstractChosen.prototype.search_string_match=function(a,b){var c,d,e,f;if(b.test(a))return!0;if(this.enable_split_word_search&&(a.indexOf(" ")>=0||0===a.indexOf("["))&&(d=a.replace(/\[|\]/g,"").split(" "),d.length))for(e=0,f=d.length;f>e;e++)if(c=d[e],b.test(c))return!0},AbstractChosen.prototype.choices_count=function(){var a,b,c,d;if(null!=this.selected_option_count)return this.selected_option_count;for(this.selected_option_count=0,d=this.form_field.options,b=0,c=d.length;c>b;b++)a=d[b],a.selected&&(this.selected_option_count+=1);return this.selected_option_count},AbstractChosen.prototype.choices_click=function(a){return a.preventDefault(),this.results_showing||this.is_disabled?void 0:this.results_show()},AbstractChosen.prototype.keyup_checker=function(a){var b,c;switch(b=null!=(c=a.which)?c:a.keyCode,this.search_field_scale(),b){case 8:if(this.is_multiple&&this.backstroke_length<1&&this.choices_count()>0)return this.keydown_backstroke();if(!this.pending_backstroke)return this.result_clear_highlight(),this.results_search();break;case 13:if(a.preventDefault(),this.results_showing)return this.result_select(a);break;case 27:return this.results_showing&&this.results_hide(),!0;case 9:case 38:case 40:case 16:case 91:case 17:break;default:return this.results_search()}},AbstractChosen.prototype.clipboard_event_checker=function(){var a=this;return setTimeout(function(){return a.results_search()},50)},AbstractChosen.prototype.container_width=function(){return null!=this.options.width?this.options.width:""+this.form_field.offsetWidth+"px"},AbstractChosen.prototype.include_option_in_results=function(a){return this.is_multiple&&!this.display_selected_options&&a.selected?!1:!this.display_disabled_options&&a.disabled?!1:a.empty?!1:!0},AbstractChosen.prototype.search_results_touchstart=function(a){return this.touch_started=!0,this.search_results_mouseover(a)},AbstractChosen.prototype.search_results_touchmove=function(a){return this.touch_started=!1,this.search_results_mouseout(a)},AbstractChosen.prototype.search_results_touchend=function(a){return this.touch_started?this.search_results_mouseup(a):void 0},AbstractChosen.prototype.outerHTML=function(a){var b;return a.outerHTML?a.outerHTML:(b=document.createElement("div"),b.appendChild(a),b.innerHTML)},AbstractChosen.browser_is_supported=function(){return"Microsoft Internet Explorer"===window.navigator.appName?document.documentMode>=8:/iP(od|hone)/i.test(window.navigator.userAgent)?!1:/Android/i.test(window.navigator.userAgent)&&/Mobile/i.test(window.navigator.userAgent)?!1:!0},AbstractChosen.default_multiple_text="Select Some Options",AbstractChosen.default_single_text="Select an Option",AbstractChosen.default_no_result_text="No results match",AbstractChosen}(),a=jQuery,a.fn.extend({chosen:function(b){return AbstractChosen.browser_is_supported()?this.each(function(){var c,d;c=a(this),d=c.data("chosen"),"destroy"===b&&d?d.destroy():d||c.data("chosen",new Chosen(this,b))}):this}}),Chosen=function(c){function Chosen(){return b=Chosen.__super__.constructor.apply(this,arguments)}return d(Chosen,c),Chosen.prototype.setup=function(){return this.form_field_jq=a(this.form_field),this.current_selectedIndex=this.form_field.selectedIndex,this.is_rtl=this.form_field_jq.hasClass("chosen-rtl")},Chosen.prototype.set_up_html=function(){var b,c;return b=["chosen-container"],b.push("chosen-container-"+(this.is_multiple?"multi":"single")),this.inherit_select_classes&&this.form_field.className&&b.push(this.form_field.className),this.is_rtl&&b.push("chosen-rtl"),c={"class":b.join(" "),style:"width: "+this.container_width()+";",title:this.form_field.title},this.form_field.id.length&&(c.id=this.form_field.id.replace(/[^\w]/g,"_")+"_chosen"),this.container=a("<div />",c),this.is_multiple?this.container.html('<ul class="chosen-choices"><li class="search-field"><input type="text" value="'+this.default_text+'" class="default" autocomplete="off" style="width:25px;" /></li></ul><div class="chosen-drop"><ul class="chosen-results"></ul></div>'):this.container.html('<a class="chosen-single chosen-default" tabindex="-1"><span>'+this.default_text+'</span><div><b></b></div></a><div class="chosen-drop"><div class="chosen-search"><input type="text" autocomplete="off" /></div><ul class="chosen-results"></ul></div>'),this.form_field_jq.hide().after(this.container),this.dropdown=this.container.find("div.chosen-drop").first(),this.search_field=this.container.find("input").first(),this.search_results=this.container.find("ul.chosen-results").first(),this.search_field_scale(),this.search_no_results=this.container.find("li.no-results").first(),this.is_multiple?(this.search_choices=this.container.find("ul.chosen-choices").first(),this.search_container=this.container.find("li.search-field").first()):(this.search_container=this.container.find("div.chosen-search").first(),this.selected_item=this.container.find(".chosen-single").first()),this.results_build(),this.set_tab_index(),this.set_label_behavior(),this.form_field_jq.trigger("chosen:ready",{chosen:this})},Chosen.prototype.register_observers=function(){var a=this;return this.container.bind("mousedown.chosen",function(b){a.container_mousedown(b)}),this.container.bind("mouseup.chosen",function(b){a.container_mouseup(b)}),this.container.bind("mouseenter.chosen",function(b){a.mouse_enter(b)}),this.container.bind("mouseleave.chosen",function(b){a.mouse_leave(b)}),this.search_results.bind("mouseup.chosen",function(b){a.search_results_mouseup(b)}),this.search_results.bind("mouseover.chosen",function(b){a.search_results_mouseover(b)}),this.search_results.bind("mouseout.chosen",function(b){a.search_results_mouseout(b)}),this.search_results.bind("mousewheel.chosen DOMMouseScroll.chosen",function(b){a.search_results_mousewheel(b)}),this.search_results.bind("touchstart.chosen",function(b){a.search_results_touchstart(b)}),this.search_results.bind("touchmove.chosen",function(b){a.search_results_touchmove(b)}),this.search_results.bind("touchend.chosen",function(b){a.search_results_touchend(b)}),this.form_field_jq.bind("chosen:updated.chosen",function(b){a.results_update_field(b)}),this.form_field_jq.bind("chosen:activate.chosen",function(b){a.activate_field(b)}),this.form_field_jq.bind("chosen:open.chosen",function(b){a.container_mousedown(b)}),this.form_field_jq.bind("chosen:close.chosen",function(b){a.input_blur(b)}),this.search_field.bind("blur.chosen",function(b){a.input_blur(b)}),this.search_field.bind("keyup.chosen",function(b){a.keyup_checker(b)}),this.search_field.bind("keydown.chosen",function(b){a.keydown_checker(b)}),this.search_field.bind("focus.chosen",function(b){a.input_focus(b)}),this.search_field.bind("cut.chosen",function(b){a.clipboard_event_checker(b)}),this.search_field.bind("paste.chosen",function(b){a.clipboard_event_checker(b)}),this.is_multiple?this.search_choices.bind("click.chosen",function(b){a.choices_click(b)}):this.container.bind("click.chosen",function(a){a.preventDefault()})},Chosen.prototype.destroy=function(){return a(this.container[0].ownerDocument).unbind("click.chosen",this.click_test_action),this.search_field[0].tabIndex&&(this.form_field_jq[0].tabIndex=this.search_field[0].tabIndex),this.container.remove(),this.form_field_jq.removeData("chosen"),this.form_field_jq.show()},Chosen.prototype.search_field_disabled=function(){return this.is_disabled=this.form_field_jq[0].disabled,this.is_disabled?(this.container.addClass("chosen-disabled"),this.search_field[0].disabled=!0,this.is_multiple||this.selected_item.unbind("focus.chosen",this.activate_action),this.close_field()):(this.container.removeClass("chosen-disabled"),this.search_field[0].disabled=!1,this.is_multiple?void 0:this.selected_item.bind("focus.chosen",this.activate_action))},Chosen.prototype.container_mousedown=function(b){return this.is_disabled||(b&&"mousedown"===b.type&&!this.results_showing&&b.preventDefault(),null!=b&&a(b.target).hasClass("search-choice-close"))?void 0:(this.active_field?this.is_multiple||!b||a(b.target)[0]!==this.selected_item[0]&&!a(b.target).parents("a.chosen-single").length||(b.preventDefault(),this.results_toggle()):(this.is_multiple&&this.search_field.val(""),a(this.container[0].ownerDocument).bind("click.chosen",this.click_test_action),this.results_show()),this.activate_field())},Chosen.prototype.container_mouseup=function(a){return"ABBR"!==a.target.nodeName||this.is_disabled?void 0:this.results_reset(a)},Chosen.prototype.search_results_mousewheel=function(a){var b;return a.originalEvent&&(b=-a.originalEvent.wheelDelta||a.originalEvent.detail),null!=b?(a.preventDefault(),"DOMMouseScroll"===a.type&&(b=40*b),this.search_results.scrollTop(b+this.search_results.scrollTop())):void 0},Chosen.prototype.blur_test=function(){return!this.active_field&&this.container.hasClass("chosen-container-active")?this.close_field():void 0},Chosen.prototype.close_field=function(){return a(this.container[0].ownerDocument).unbind("click.chosen",this.click_test_action),this.active_field=!1,this.results_hide(),this.container.removeClass("chosen-container-active"),this.clear_backstroke(),this.show_search_field_default(),this.search_field_scale()},Chosen.prototype.activate_field=function(){return this.container.addClass("chosen-container-active"),this.active_field=!0,this.search_field.val(this.search_field.val()),this.search_field.focus()},Chosen.prototype.test_active_click=function(b){var c;return c=a(b.target).closest(".chosen-container"),c.length&&this.container[0]===c[0]?this.active_field=!0:this.close_field()},Chosen.prototype.results_build=function(){return this.parsing=!0,this.selected_option_count=null,this.results_data=SelectParser.select_to_array(this.form_field),this.is_multiple?this.search_choices.find("li.search-choice").remove():this.is_multiple||(this.single_set_selected_text(),this.disable_search||this.form_field.options.length<=this.disable_search_threshold?(this.search_field[0].readOnly=!0,this.container.addClass("chosen-container-single-nosearch")):(this.search_field[0].readOnly=!1,this.container.removeClass("chosen-container-single-nosearch"))),this.update_results_content(this.results_option_build({first:!0})),this.search_field_disabled(),this.show_search_field_default(),this.search_field_scale(),this.parsing=!1},Chosen.prototype.result_do_highlight=function(a){var b,c,d,e,f;if(a.length){if(this.result_clear_highlight(),this.result_highlight=a,this.result_highlight.addClass("highlighted"),d=parseInt(this.search_results.css("maxHeight"),10),f=this.search_results.scrollTop(),e=d+f,c=this.result_highlight.position().top+this.search_results.scrollTop(),b=c+this.result_highlight.outerHeight(),b>=e)return this.search_results.scrollTop(b-d>0?b-d:0);if(f>c)return this.search_results.scrollTop(c)}},Chosen.prototype.result_clear_highlight=function(){return this.result_highlight&&this.result_highlight.removeClass("highlighted"),this.result_highlight=null},Chosen.prototype.results_show=function(){return this.is_multiple&&this.max_selected_options<=this.choices_count()?(this.form_field_jq.trigger("chosen:maxselected",{chosen:this}),!1):(this.container.addClass("chosen-with-drop"),this.results_showing=!0,this.search_field.focus(),this.search_field.val(this.search_field.val()),this.winnow_results(),this.form_field_jq.trigger("chosen:showing_dropdown",{chosen:this}))},Chosen.prototype.update_results_content=function(a){return this.search_results.html(a)},Chosen.prototype.results_hide=function(){return this.results_showing&&(this.result_clear_highlight(),this.container.removeClass("chosen-with-drop"),this.form_field_jq.trigger("chosen:hiding_dropdown",{chosen:this})),this.results_showing=!1},Chosen.prototype.set_tab_index=function(){var a;return this.form_field.tabIndex?(a=this.form_field.tabIndex,this.form_field.tabIndex=-1,this.search_field[0].tabIndex=a):void 0},Chosen.prototype.set_label_behavior=function(){var b=this;return this.form_field_label=this.form_field_jq.parents("label"),!this.form_field_label.length&&this.form_field.id.length&&(this.form_field_label=a("label[for='"+this.form_field.id+"']")),this.form_field_label.length>0?this.form_field_label.bind("click.chosen",function(a){return b.is_multiple?b.container_mousedown(a):b.activate_field()}):void 0},Chosen.prototype.show_search_field_default=function(){return this.is_multiple&&this.choices_count()<1&&!this.active_field?(this.search_field.val(this.default_text),this.search_field.addClass("default")):(this.search_field.val(""),this.search_field.removeClass("default"))},Chosen.prototype.search_results_mouseup=function(b){var c;return c=a(b.target).hasClass("active-result")?a(b.target):a(b.target).parents(".active-result").first(),c.length?(this.result_highlight=c,this.result_select(b),this.search_field.focus()):void 0},Chosen.prototype.search_results_mouseover=function(b){var c;return c=a(b.target).hasClass("active-result")?a(b.target):a(b.target).parents(".active-result").first(),c?this.result_do_highlight(c):void 0},Chosen.prototype.search_results_mouseout=function(b){return a(b.target).hasClass("active-result")?this.result_clear_highlight():void 0},Chosen.prototype.choice_build=function(b){var c,d,e=this;return c=a("<li />",{"class":"search-choice"}).html("<span>"+b.html+"</span>"),b.disabled?c.addClass("search-choice-disabled"):(d=a("<a />",{"class":"search-choice-close","data-option-array-index":b.array_index}),d.bind("click.chosen",function(a){return e.choice_destroy_link_click(a)}),c.append(d)),this.search_container.before(c)},Chosen.prototype.choice_destroy_link_click=function(b){return b.preventDefault(),b.stopPropagation(),this.is_disabled?void 0:this.choice_destroy(a(b.target))},Chosen.prototype.choice_destroy=function(a){return this.result_deselect(a[0].getAttribute("data-option-array-index"))?(this.show_search_field_default(),this.is_multiple&&this.choices_count()>0&&this.search_field.val().length<1&&this.results_hide(),a.parents("li").first().remove(),this.search_field_scale()):void 0},Chosen.prototype.results_reset=function(){return this.reset_single_select_options(),this.form_field.options[0].selected=!0,this.single_set_selected_text(),this.show_search_field_default(),this.results_reset_cleanup(),this.form_field_jq.trigger("change"),this.active_field?this.results_hide():void 0},Chosen.prototype.results_reset_cleanup=function(){return this.current_selectedIndex=this.form_field.selectedIndex,this.selected_item.find("abbr").remove()},Chosen.prototype.result_select=function(a){var b,c;return this.result_highlight?(b=this.result_highlight,this.result_clear_highlight(),this.is_multiple&&this.max_selected_options<=this.choices_count()?(this.form_field_jq.trigger("chosen:maxselected",{chosen:this}),!1):(this.is_multiple?b.removeClass("active-result"):this.reset_single_select_options(),c=this.results_data[b[0].getAttribute("data-option-array-index")],c.selected=!0,this.form_field.options[c.options_index].selected=!0,this.selected_option_count=null,this.is_multiple?this.choice_build(c):this.single_set_selected_text(c.text),(a.metaKey||a.ctrlKey)&&this.is_multiple||this.results_hide(),this.search_field.val(""),(this.is_multiple||this.form_field.selectedIndex!==this.current_selectedIndex)&&this.form_field_jq.trigger("change",{selected:this.form_field.options[c.options_index].value}),this.current_selectedIndex=this.form_field.selectedIndex,this.search_field_scale())):void 0},Chosen.prototype.single_set_selected_text=function(a){return null==a&&(a=this.default_text),a===this.default_text?this.selected_item.addClass("chosen-default"):(this.single_deselect_control_build(),this.selected_item.removeClass("chosen-default")),this.selected_item.find("span").text(a)},Chosen.prototype.result_deselect=function(a){var b;return b=this.results_data[a],this.form_field.options[b.options_index].disabled?!1:(b.selected=!1,this.form_field.options[b.options_index].selected=!1,this.selected_option_count=null,this.result_clear_highlight(),this.results_showing&&this.winnow_results(),this.form_field_jq.trigger("change",{deselected:this.form_field.options[b.options_index].value}),this.search_field_scale(),!0)},Chosen.prototype.single_deselect_control_build=function(){return this.allow_single_deselect?(this.selected_item.find("abbr").length||this.selected_item.find("span").first().after('<abbr class="search-choice-close"></abbr>'),this.selected_item.addClass("chosen-single-with-deselect")):void 0},Chosen.prototype.get_search_text=function(){return this.search_field.val()===this.default_text?"":a("<div/>").text(a.trim(this.search_field.val())).html()},Chosen.prototype.winnow_results_set_highlight=function(){var a,b;return b=this.is_multiple?[]:this.search_results.find(".result-selected.active-result"),a=b.length?b.first():this.search_results.find(".active-result").first(),null!=a?this.result_do_highlight(a):void 0},Chosen.prototype.no_results=function(b){var c;return c=a('<li class="no-results">'+this.results_none_found+' "<span></span>"</li>'),c.find("span").first().html(b),this.search_results.append(c),this.form_field_jq.trigger("chosen:no_results",{chosen:this})},Chosen.prototype.no_results_clear=function(){return this.search_results.find(".no-results").remove()},Chosen.prototype.keydown_arrow=function(){var a;return this.results_showing&&this.result_highlight?(a=this.result_highlight.nextAll("li.active-result").first())?this.result_do_highlight(a):void 0:this.results_show()},Chosen.prototype.keyup_arrow=function(){var a;return this.results_showing||this.is_multiple?this.result_highlight?(a=this.result_highlight.prevAll("li.active-result"),a.length?this.result_do_highlight(a.first()):(this.choices_count()>0&&this.results_hide(),this.result_clear_highlight())):void 0:this.results_show()},Chosen.prototype.keydown_backstroke=function(){var a;return this.pending_backstroke?(this.choice_destroy(this.pending_backstroke.find("a").first()),this.clear_backstroke()):(a=this.search_container.siblings("li.search-choice").last(),a.length&&!a.hasClass("search-choice-disabled")?(this.pending_backstroke=a,this.single_backstroke_delete?this.keydown_backstroke():this.pending_backstroke.addClass("search-choice-focus")):void 0)},Chosen.prototype.clear_backstroke=function(){return this.pending_backstroke&&this.pending_backstroke.removeClass("search-choice-focus"),this.pending_backstroke=null},Chosen.prototype.keydown_checker=function(a){var b,c;switch(b=null!=(c=a.which)?c:a.keyCode,this.search_field_scale(),8!==b&&this.pending_backstroke&&this.clear_backstroke(),b){case 8:this.backstroke_length=this.search_field.val().length;break;case 9:this.results_showing&&!this.is_multiple&&this.result_select(a),this.mouse_on_container=!1;break;case 13:a.preventDefault();break;case 38:a.preventDefault(),this.keyup_arrow();break;case 40:a.preventDefault(),this.keydown_arrow()}},Chosen.prototype.search_field_scale=function(){var b,c,d,e,f,g,h,i,j;if(this.is_multiple){for(d=0,h=0,f="position:absolute; left: -1000px; top: -1000px; display:none;",g=["font-size","font-style","font-weight","font-family","line-height","text-transform","letter-spacing"],i=0,j=g.length;j>i;i++)e=g[i],f+=e+":"+this.search_field.css(e)+";";return b=a("<div />",{style:f}),b.text(this.search_field.val()),a("body").append(b),h=b.width()+25,b.remove(),c=this.container.outerWidth(),h>c-10&&(h=c-10),this.search_field.css({width:h+"px"})}},Chosen}(AbstractChosen)}.call(this);
3834
3835/*
3836 * jQuery One Page Nav Plugin
3837 * http://github.com/davist11/jQuery-One-Page-Nav
3838 *
3839 * Copyright (c) 2010 Trevor Davis (http://trevordavis.net)
3840 * Dual licensed under the MIT and GPL licenses.
3841 * Uses the same license as jQuery, see:
3842 * http://jquery.org/license
3843 *
3844 * @version 3.0.0
3845 *
3846 * Example usage:
3847 * $('#nav').onePageNav({
3848 *   currentClass: 'current',
3849 *   changeHash: false,
3850 *   scrollSpeed: 750
3851 * });
3852 */
3853
3854;(function($, window, document, undefined){
3855
3856	// our plugin constructor
3857	var OnePageNav = function(elem, options){
3858		this.elem = elem;
3859		this.$elem = $(elem);
3860		this.options = options;
3861		this.metadata = this.$elem.data('plugin-options');
3862		this.$win = $(window);
3863		this.sections = {};
3864		this.didScroll = false;
3865		this.$doc = $(document);
3866		this.docHeight = this.$doc.height();
3867	};
3868
3869	// the plugin prototype
3870	OnePageNav.prototype = {
3871		defaults: {
3872			navItems: 'a',
3873			currentClass: 'current',
3874			changeHash: false,
3875			easing: 'swing',
3876			filter: '',
3877			scrollSpeed: 750,
3878			scrollThreshold: 0.5,
3879			begin: false,
3880			end: false,
3881			scrollChange: false
3882		},
3883
3884		init: function() {
3885			// Introduce defaults that can be extended either
3886			// globally or using an object literal.
3887			this.config = $.extend({}, this.defaults, this.options, this.metadata);
3888
3889			this.$nav = this.$elem.find(this.config.navItems);
3890
3891			//Filter any links out of the nav
3892			if(this.config.filter !== '') {
3893				this.$nav = this.$nav.filter(this.config.filter);
3894			}
3895
3896			//Handle clicks on the nav
3897			this.$nav.on('click.onePageNav', $.proxy(this.handleClick, this));
3898
3899			//Get the section positions
3900			this.getPositions();
3901
3902			//Handle scroll changes
3903			this.bindInterval();
3904
3905			//Update the positions on resize too
3906			this.$win.on('resize.onePageNav', $.proxy(this.getPositions, this));
3907
3908			return this;
3909		},
3910
3911		adjustNav: function(self, $parent) {
3912			self.$elem.find('.' + self.config.currentClass).removeClass(self.config.currentClass);
3913			$parent.addClass(self.config.currentClass);
3914		},
3915
3916		bindInterval: function() {
3917			var self = this;
3918			var docHeight;
3919
3920			self.$win.on('scroll.onePageNav', function() {
3921				self.didScroll = true;
3922			});
3923
3924			self.t = setInterval(function() {
3925				docHeight = self.$doc.height();
3926
3927				//If it was scrolled
3928				if(self.didScroll) {
3929					self.didScroll = false;
3930					self.scrollChange();
3931				}
3932
3933				//If the document height changes
3934				if(docHeight !== self.docHeight) {
3935					self.docHeight = docHeight;
3936					self.getPositions();
3937				}
3938			}, 250);
3939		},
3940
3941		getHash: function($link) {
3942			return $link.attr('href').split('#')[1];
3943		},
3944
3945		getPositions: function() {
3946			var self = this;
3947			var linkHref;
3948			var topPos;
3949			var $target;
3950
3951			self.$nav.each(function() {
3952				linkHref = self.getHash($(this));
3953				$target = $('#' + linkHref);
3954
3955				if($target.length) {
3956					topPos = $target.offset().top;
3957					self.sections[linkHref] = Math.round(topPos);
3958				}
3959			});
3960		},
3961
3962		getSection: function(windowPos) {
3963			var returnValue = null;
3964			var windowHeight = Math.round(this.$win.height() * this.config.scrollThreshold);
3965
3966			for(var section in this.sections) {
3967				if((this.sections[section] - windowHeight) < windowPos) {
3968					returnValue = section;
3969				}
3970			}
3971
3972			return returnValue;
3973		},
3974
3975		handleClick: function(e) {
3976			var self = this;
3977			var $link = $(e.currentTarget);
3978			var $parent = $link.parent();
3979			var newLoc = '#' + self.getHash($link);
3980
3981			if(!$parent.hasClass(self.config.currentClass)) {
3982				//Start callback
3983				if(self.config.begin) {
3984					self.config.begin();
3985				}
3986
3987				//Change the highlighted nav item
3988				self.adjustNav(self, $parent);
3989
3990				//Removing the auto-adjust on scroll
3991				self.unbindInterval();
3992
3993				//Scroll to the correct position
3994				self.scrollTo(newLoc, function() {
3995					//Do we need to change the hash?
3996					if(self.config.changeHash) {
3997						window.location.hash = newLoc;
3998					}
3999
4000					//Add the auto-adjust on scroll back in
4001					self.bindInterval();
4002
4003					//End callback
4004					if(self.config.end) {
4005						self.config.end();
4006					}
4007				});
4008			}
4009
4010			e.preventDefault();
4011		},
4012
4013		scrollChange: function() {
4014			var windowTop = this.$win.scrollTop();
4015			var position = this.getSection(windowTop);
4016			var $parent;
4017
4018			//If the position is set
4019			if(position !== null) {
4020				$parent = this.$elem.find('a[href$="#' + position + '"]').parent();
4021
4022				//If it's not already the current section
4023				if(!$parent.hasClass(this.config.currentClass)) {
4024					//Change the highlighted nav item
4025					this.adjustNav(this, $parent);
4026
4027					//If there is a scrollChange callback
4028					if(this.config.scrollChange) {
4029						this.config.scrollChange($parent);
4030					}
4031				}
4032			}
4033		},
4034
4035		scrollTo: function(target, callback) {
4036			var offset = $(target).offset().top;
4037
4038			$('html, body').animate({
4039				scrollTop: offset
4040			}, this.config.scrollSpeed, this.config.easing, callback);
4041		},
4042
4043		unbindInterval: function() {
4044			clearInterval(this.t);
4045			this.$win.unbind('scroll.onePageNav');
4046		}
4047	};
4048
4049	OnePageNav.defaults = OnePageNav.prototype.defaults;
4050
4051	$.fn.onePageNav = function(options) {
4052		return this.each(function() {
4053			new OnePageNav(this, options).init();
4054		});
4055	};
4056
4057})( jQuery, window , document );
4058
4059
4060/*!
4061 * Bootstrap v3.2.0 (http://getbootstrap.com)
4062 * Copyright 2011-2014 Twitter, Inc.
4063 * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
4064 */
4065
4066/*!
4067 * Generated using the Bootstrap Customizer (http://getbootstrap.com/customize/?id=6dbe887f58ce5549314a)
4068 * Config saved to config.json and https://gist.github.com/6dbe887f58ce5549314a
4069 */
4070if (typeof jQuery === "undefined") { throw new Error("Bootstrap's JavaScript requires jQuery") }
4071
4072/* ========================================================================
4073 * Bootstrap: modal.js v3.2.0
4074 * http://getbootstrap.com/javascript/#modals
4075 * ========================================================================
4076 * Copyright 2011-2014 Twitter, Inc.
4077 * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
4078 * ======================================================================== */
4079
4080
4081+function ($) {
4082  'use strict';
4083
4084  // MODAL CLASS DEFINITION
4085  // ======================
4086
4087  var Modal = function (element, options) {
4088    this.options        = options
4089    this.$body          = $(document.body)
4090    this.$element       = $(element)
4091    this.$backdrop      =
4092    this.isShown        = null
4093    this.scrollbarWidth = 0
4094
4095    if (this.options.remote) {
4096      this.$element
vendor: 7,867 bytes, lines 4097-4364
4097        .find('.modal-content')
4098        .load(this.options.remote, $.proxy(function () {
4099          this.$element.trigger('loaded.bs.modal')
4100        }, this))
4101    }
4102  }
4103
4104  Modal.VERSION  = '3.2.0'
4105
4106  Modal.DEFAULTS = {
4107    backdrop: true,
4108    keyboard: true,
4109    show: true
4110  }
4111
4112  Modal.prototype.toggle = function (_relatedTarget) {
4113    return this.isShown ? this.hide() : this.show(_relatedTarget)
4114  }
4115
4116  Modal.prototype.show = function (_relatedTarget) {
4117    var that = this
4118    var e    = $.Event('show.bs.modal', { relatedTarget: _relatedTarget })
4119
4120    this.$element.trigger(e)
4121
4122    if (this.isShown || e.isDefaultPrevented()) return
4123
4124    this.isShown = true
4125
4126    this.checkScrollbar()
4127    this.$body.addClass('modal-open')
4128
4129    this.setScrollbar()
4130    this.escape()
4131
4132    this.$element.on('click.dismiss.bs.modal', '[data-dismiss="modal"]', $.proxy(this.hide, this))
4133
4134    this.backdrop(function () {
4135      var transition = $.support.transition && that.$element.hasClass('fade')
4136
4137      if (!that.$element.parent().length) {
4138        that.$element.appendTo(that.$body) // don't move modals dom position
4139      }
4140
4141      that.$element
4142        .show()
4143        .scrollTop(0)
4144
4145      if (transition) {
4146        that.$element[0].offsetWidth // force reflow
4147      }
4148
4149      that.$element
4150        .addClass('in')
4151        .attr('aria-hidden', false)
4152
4153      that.enforceFocus()
4154
4155      var e = $.Event('shown.bs.modal', { relatedTarget: _relatedTarget })
4156
4157      transition ?
4158        that.$element.find('.modal-dialog') // wait for modal to slide in
4159          .one('bsTransitionEnd', function () {
4160            that.$element.trigger('focus').trigger(e)
4161          })
4162          .emulateTransitionEnd(300) :
4163        that.$element.trigger('focus').trigger(e)
4164    })
4165  }
4166
4167  Modal.prototype.hide = function (e) {
4168    if (e) e.preventDefault()
4169
4170    e = $.Event('hide.bs.modal')
4171
4172    this.$element.trigger(e)
4173
4174    if (!this.isShown || e.isDefaultPrevented()) return
4175
4176    this.isShown = false
4177
4178    this.$body.removeClass('modal-open')
4179
4180    this.resetScrollbar()
4181    this.escape()
4182
4183    $(document).off('focusin.bs.modal')
4184
4185    this.$element
4186      .removeClass('in')
4187      .attr('aria-hidden', true)
4188      .off('click.dismiss.bs.modal')
4189
4190    $.support.transition && this.$element.hasClass('fade') ?
4191      this.$element
4192        .one('bsTransitionEnd', $.proxy(this.hideModal, this))
4193        .emulateTransitionEnd(300) :
4194      this.hideModal()
4195  }
4196
4197  Modal.prototype.enforceFocus = function () {
4198    $(document)
4199      .off('focusin.bs.modal') // guard against infinite focus loop
4200      .on('focusin.bs.modal', $.proxy(function (e) {
4201        if (this.$element[0] !== e.target && !this.$element.has(e.target).length) {
4202          this.$element.trigger('focus')
4203        }
4204      }, this))
4205  }
4206
4207  Modal.prototype.escape = function () {
4208    if (this.isShown && this.options.keyboard) {
4209      this.$element.on('keyup.dismiss.bs.modal', $.proxy(function (e) {
4210        e.which == 27 && this.hide()
4211      }, this))
4212    } else if (!this.isShown) {
4213      this.$element.off('keyup.dismiss.bs.modal')
4214    }
4215  }
4216
4217  Modal.prototype.hideModal = function () {
4218    var that = this
4219    this.$element.hide()
4220    this.backdrop(function () {
4221      that.$element.trigger('hidden.bs.modal')
4222    })
4223  }
4224
4225  Modal.prototype.removeBackdrop = function () {
4226    this.$backdrop && this.$backdrop.remove()
4227    this.$backdrop = null
4228  }
4229
4230  Modal.prototype.backdrop = function (callback) {
4231    var that = this
4232    var animate = this.$element.hasClass('fade') ? 'fade' : ''
4233
4234    if (this.isShown && this.options.backdrop) {
4235      var doAnimate = $.support.transition && animate
4236
4237      this.$backdrop = $('<div class="modal-backdrop ' + animate + '" />')
4238        .appendTo(this.$body)
4239
4240      this.$element.on('click.dismiss.bs.modal', $.proxy(function (e) {
4241        if (e.target !== e.currentTarget) return
4242        this.options.backdrop == 'static'
4243          ? this.$element[0].focus.call(this.$element[0])
4244          : this.hide.call(this)
4245      }, this))
4246
4247      if (doAnimate) this.$backdrop[0].offsetWidth // force reflow
4248
4249      this.$backdrop.addClass('in')
4250
4251      if (!callback) return
4252
4253      doAnimate ?
4254        this.$backdrop
4255          .one('bsTransitionEnd', callback)
4256          .emulateTransitionEnd(150) :
4257        callback()
4258
4259    } else if (!this.isShown && this.$backdrop) {
4260      this.$backdrop.removeClass('in')
4261
4262      var callbackRemove = function () {
4263        that.removeBackdrop()
4264        callback && callback()
4265      }
4266      $.support.transition && this.$element.hasClass('fade') ?
4267        this.$backdrop
4268          .one('bsTransitionEnd', callbackRemove)
4269          .emulateTransitionEnd(150) :
4270        callbackRemove()
4271
4272    } else if (callback) {
4273      callback()
4274    }
4275  }
4276
4277  Modal.prototype.checkScrollbar = function () {
4278    if (document.body.clientWidth >= window.innerWidth) return
4279    this.scrollbarWidth = this.scrollbarWidth || this.measureScrollbar()
4280  }
4281
4282  Modal.prototype.setScrollbar = function () {
4283    var bodyPad = parseInt((this.$body.css('padding-right') || 0), 10)
4284    if (this.scrollbarWidth) this.$body.css('padding-right', bodyPad + this.scrollbarWidth)
4285  }
4286
4287  Modal.prototype.resetScrollbar = function () {
4288    this.$body.css('padding-right', '')
4289  }
4290
4291  Modal.prototype.measureScrollbar = function () { // thx walsh
4292    var scrollDiv = document.createElement('div')
4293    scrollDiv.className = 'modal-scrollbar-measure'
4294    this.$body.append(scrollDiv)
4295    var scrollbarWidth = scrollDiv.offsetWidth - scrollDiv.clientWidth
4296    this.$body[0].removeChild(scrollDiv)
4297    return scrollbarWidth
4298  }
4299
4300
4301  // MODAL PLUGIN DEFINITION
4302  // =======================
4303
4304  function Plugin(option, _relatedTarget) {
4305    return this.each(function () {
4306      var $this   = $(this)
4307      var data    = $this.data('bs.modal')
4308      var options = $.extend({}, Modal.DEFAULTS, $this.data(), typeof option == 'object' && option)
4309
4310      if (!data) $this.data('bs.modal', (data = new Modal(this, options)))
4311      if (typeof option == 'string') data[option](_relatedTarget)
4312      else if (options.show) data.show(_relatedTarget)
4313    })
4314  }
4315
4316  var old = $.fn.modal
4317
4318  $.fn.modal             = Plugin
4319  $.fn.modal.Constructor = Modal
4320
4321
4322  // MODAL NO CONFLICT
4323  // =================
4324
4325  $.fn.modal.noConflict = function () {
4326    $.fn.modal = old
4327    return this
4328  }
4329
4330
4331  // MODAL DATA-API
4332  // ==============
4333
4334  $(document).on('click.bs.modal.data-api', '[data-toggle="modal"]', function (e) {
4335    var $this   = $(this)
4336    var href    = $this.attr('href')
4337    var $target = $($this.attr('data-target') || (href && href.replace(/.*(?=#[^\s]+$)/, ''))) // strip for ie7
4338    var option  = $target.data('bs.modal') ? 'toggle' : $.extend({ remote: !/#/.test(href) && href }, $target.data(), $this.data())
4339
4340    if ($this.is('a')) e.preventDefault()
4341
4342    $target.one('show.bs.modal', function (showEvent) {
4343      if (showEvent.isDefaultPrevented()) return // only register focus restorer if modal will actually get shown
4344      $target.one('hidden.bs.modal', function () {
4345        $this.is(':visible') && $this.trigger('focus')
4346      })
4347    })
4348    Plugin.call($target, option, this)
4349  })
4350
4351}(jQuery);
4352
4353/* ========================================================================
4354 * Bootstrap: transition.js v3.2.0
4355 * http://getbootstrap.com/javascript/#transitions
4356 * ========================================================================
4357 * Copyright 2011-2014 Twitter, Inc.
4358 * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
4359 * ======================================================================== */
4360
4361
4362+function ($) {
4363  'use strict';
4364
4365  // CSS TRANSITION SUPPORT (Shoutout: http://www.modernizr.com/)
4366  // ============================================================
4367
4368  function transitionEnd() {
4369    var el = document.createElement('bootstrap')
4370
4371    var transEndEventNames = {
4372      WebkitTransition : 'webkitTransitionEnd',
4373      MozTransition    : 'transitionend',
4374      OTransition      : 'oTransitionEnd otransitionend',
4375      transition       : 'transitionend'
4376    }
4377
4378    for (var name in transEndEventNames) {
4379      if (el.style[name] !== undefined) {
4380        return { end: transEndEventNames[name] }
4381      }
4382    }
4383
4384    return false // explicit for ie8 (  ._.)
4385  }
4386
4387  // http://blog.alexmaccaw.com/css-transitions
4388  $.fn.emulateTransitionEnd = function (duration) {
4389    var called = false
4390    var $el = this
4391    $(this).one('bsTransitionEnd', function () { called = true })
4392    var callback = function () { if (!called) $($el).trigger($.support.transition.end) }
4393    setTimeout(callback, duration)
4394    return this
4395  }
4396
4397  $(function () {
4398    $.support.transition = transitionEnd()
4399
4400    if (!$.support.transition) return
4401
4402    $.event.special.bsTransitionEnd = {
4403      bindType: $.support.transition.end,
4404      delegateType: $.support.transition.end,
4405      handle: function (e) {
4406        if ($(e.target).is(this)) return e.handleObj.handler.apply(this, arguments)
4407      }
4408    }
4409  })
4410
4411}(jQuery);
4412
4413/*!
4414 * jQuery Cookie Plugin v1.4.1
4415 * https://github.com/carhartl/jquery-cookie
4416 *
4417 * Copyright 2006, 2014 Klaus Hartl
4418 * Released under the MIT license
4419 */
4420(function (factory) {
4421	if (typeof define === 'function' && define.amd) {
4422		// AMD (Register as an anonymous module)
4423		define(['jquery'], factory);
4424	} else if (typeof exports === 'object') {
4425		// Node/CommonJS
4426		module.exports = factory(require('jquery'));
4427	} else {
4428		// Browser globals
4429		factory(jQuery);
4430	}
4431}(function ($) {
4432
4433	var pluses = /\+/g;
4434
4435	function encode(s) {
4436		return config.raw ? s : encodeURIComponent(s);
4437	}
4438
4439	function decode(s) {
4440		return config.raw ? s : decodeURIComponent(s);
4441	}
4442
4443	function stringifyCookieValue(value) {
4444		return encode(config.json ? JSON.stringify(value) : String(value));
4445	}
4446
4447	function parseCookieValue(s) {
4448		if (s.indexOf('"') === 0) {
4449			// This is a quoted cookie as according to RFC2068, unescape...
4450			s = s.slice(1, -1).replace(/\\"/g, '"').replace(/\\\\/g, '\\');
4451		}
4452
4453		try {
4454			// Replace server-side written pluses with spaces.
4455			// If we can't decode the cookie, ignore it, it's unusable.
4456			// If we can't parse the cookie, ignore it, it's unusable.
4457			s = decodeURIComponent(s.replace(pluses, ' '));
4458			return config.json ? JSON.parse(s) : s;
4459		} catch(e) {}
4460	}
4461
4462	function read(s, converter) {
4463		var value = config.raw ? s : parseCookieValue(s);
4464		return $.isFunction(converter) ? converter(value) : value;
4465	}
4466
4467	var config = $.cookie = function (key, value, options) {
4468
4469		// Write
4470
4471		if (arguments.length > 1 && !$.isFunction(value)) {
4472			options = $.extend({}, config.defaults, options);
4473
4474			if (typeof options.expires === 'number') {
4475				var days = options.expires, t = options.expires = new Date();
4476				t.setMilliseconds(t.getMilliseconds() + days * 864e+5);
4477			}
4478
4479			return (document.cookie = [
4480				encode(key), '=', stringifyCookieValue(value),
4481				options.expires ? '; expires=' + options.expires.toUTCString() : '', // use expires attribute, max-age is not supported by IE
4482				options.path    ? '; path=' + options.path : '',
4483				options.domain  ? '; domain=' + options.domain : '',
4484				options.secure  ? '; secure' : ''
4485			].join(''));
4486		}
4487
4488		// Read
4489
4490		var result = key ? undefined : {},
4491		// To prevent the for loop in the first place assign an empty array
4492		// in case there are no cookies at all. Also prevents odd result when
4493		// calling $.cookie().
4494			cookies = document.cookie ? document.cookie.split('; ') : [],
4495			i = 0,
4496			l = cookies.length;
4497
4498		for (; i < l; i++) {
4499			var parts = cookies[i].split('='),
4500				name = decode(parts.shift()),
4501				cookie = parts.join('=');
4502
4503			if (key === name) {
4504				// If second argument (value) is a function it's a converter...
4505				result = read(cookie, value);
4506				break;
4507			}
4508
4509			// Prevent storing a cookie that we couldn't decode.
4510			if (!key && (cookie = read(cookie)) !== undefined) {
4511				result[name] = cookie;
4512			}
4513		}
4514
4515		return result;
4516	};
4517
4518	config.defaults = {};
4519
4520	$.removeCookie = function (key, options) {
vendor: 7,539 bytes, lines 4521-4807
4521		// Must not alter options, thus extending a fresh object...
4522		$.cookie(key, '', $.extend({}, options, { expires: -1 }));
4523		return !$.cookie(key);
4524	};
4525
4526}));
4527
4528/*!
4529 * fancyBox - jQuery Plugin
4530 * version: 2.1.5 (Fri, 14 Jun 2013)
4531 * @requires jQuery v1.6 or later
4532 *
4533 * Examples at http://fancyapps.com/fancybox/
4534 * License: www.fancyapps.com/fancybox/#license
4535 *
4536 * Copyright 2012 Janis Skarnelis - [email protected]
4537 *
4538 */
4539
4540(function (window, document, $, undefined) {
4541	"use strict";
4542
4543	var H = $("html"),
4544		W = $(window),
4545		D = $(document),
4546		F = $.fancybox = function () {
4547			F.open.apply( this, arguments );
4548		},
4549		IE =  navigator.userAgent.match(/msie/i),
4550		didUpdate	= null,
4551		isTouch		= document.createTouch !== undefined,
4552
4553		isQuery	= function(obj) {
4554			return obj && obj.hasOwnProperty && obj instanceof $;
4555		},
4556		isString = function(str) {
4557			return str && $.type(str) === "string";
4558		},
4559		isPercentage = function(str) {
4560			return isString(str) && str.indexOf('%') > 0;
4561		},
4562		isScrollable = function(el) {
4563			return (el && !(el.style.overflow && el.style.overflow === 'hidden') && ((el.clientWidth && el.scrollWidth > el.clientWidth) || (el.clientHeight && el.scrollHeight > el.clientHeight)));
4564		},
4565		getScalar = function(orig, dim) {
4566			var value = parseInt(orig, 10) || 0;
4567
4568			if (dim && isPercentage(orig)) {
4569				value = F.getViewport()[ dim ] / 100 * value;
4570			}
4571
4572			return Math.ceil(value);
4573		},
4574		getValue = function(value, dim) {
4575			return getScalar(value, dim) + 'px';
4576		};
4577
4578	$.extend(F, {
4579		// The current version of fancyBox
4580		version: '2.1.5',
4581
4582		defaults: {
4583			padding : 15,
4584			margin  : 20,
4585
4586			width     : 800,
4587			height    : 600,
4588			minWidth  : 100,
4589			minHeight : 100,
4590			maxWidth  : 9999,
4591			maxHeight : 9999,
4592			pixelRatio: 1, // Set to 2 for retina display support
4593
4594			autoSize   : true,
4595			autoHeight : false,
4596			autoWidth  : false,
4597
4598			autoResize  : true,
4599			autoCenter  : !isTouch,
4600			fitToView   : true,
4601			aspectRatio : false,
4602			topRatio    : 0.5,
4603			leftRatio   : 0.5,
4604
4605			scrolling : 'auto', // 'auto', 'yes' or 'no'
4606			wrapCSS   : '',
4607
4608			arrows     : true,
4609			closeBtn   : true,
4610			closeClick : false,
4611			nextClick  : false,
4612			mouseWheel : true,
4613			autoPlay   : false,
4614			playSpeed  : 3000,
4615			preload    : 3,
4616			modal      : false,
4617			loop       : true,
4618
4619			ajax  : {
4620				dataType : 'html',
4621				headers  : { 'X-fancyBox': true }
4622			},
4623			iframe : {
4624				scrolling : 'auto',
4625				preload   : true
4626			},
4627			swf : {
4628				wmode: 'transparent',
4629				allowfullscreen   : 'true',
4630				allowscriptaccess : 'always'
4631			},
4632
4633			keys  : {
4634				next : {
4635					13 : 'left', // enter
4636					34 : 'up',   // page down
4637					39 : 'left', // right arrow
4638					40 : 'up'    // down arrow
4639				},
4640				prev : {
4641					8  : 'right',  // backspace
4642					33 : 'down',   // page up
4643					37 : 'right',  // left arrow
4644					38 : 'down'    // up arrow
4645				},
4646				close  : [27], // escape key
4647				play   : [32], // space - start/stop slideshow
4648				toggle : [70]  // letter "f" - toggle fullscreen
4649			},
4650
4651			direction : {
4652				next : 'left',
4653				prev : 'right'
4654			},
4655
4656			scrollOutside  : true,
4657
4658			// Override some properties
4659			index   : 0,
4660			type    : null,
4661			href    : null,
4662			content : null,
4663			title   : null,
4664
4665			// HTML templates
4666			tpl: {
4667				wrap     : '<div class="fancybox-wrap" tabIndex="-1"><div class="fancybox-skin"><div class="fancybox-outer"><div class="fancybox-inner"></div></div></div></div>',
4668				image    : '<img class="fancybox-image" src="{href}" alt="" />',
4669				iframe   : '<iframe id="fancybox-frame{rnd}" name="fancybox-frame{rnd}" class="fancybox-iframe" frameborder="0" vspace="0" hspace="0" webkitAllowFullScreen mozallowfullscreen allowFullScreen' + (IE ? ' allowtransparency="true"' : '') + '></iframe>',
4670				error    : '<p class="fancybox-error">The requested content cannot be loaded.<br/>Please try again later.</p>',
4671				closeBtn : '<a title="Close" class="fancybox-item fancybox-close" href="javascript:;"></a>',
4672				next     : '<a title="Next" class="fancybox-nav fancybox-next" href="javascript:;"><span></span></a>',
4673				prev     : '<a title="Previous" class="fancybox-nav fancybox-prev" href="javascript:;"><span></span></a>'
4674			},
4675
4676			// Properties for each animation type
4677			// Opening fancyBox
4678			openEffect  : 'fade', // 'elastic', 'fade' or 'none'
4679			openSpeed   : 250,
4680			openEasing  : 'swing',
4681			openOpacity : true,
4682			openMethod  : 'zoomIn',
4683
4684			// Closing fancyBox
4685			closeEffect  : 'fade', // 'elastic', 'fade' or 'none'
4686			closeSpeed   : 250,
4687			closeEasing  : 'swing',
4688			closeOpacity : true,
4689			closeMethod  : 'zoomOut',
4690
4691			// Changing next gallery item
4692			nextEffect : 'elastic', // 'elastic', 'fade' or 'none'
4693			nextSpeed  : 250,
4694			nextEasing : 'swing',
4695			nextMethod : 'changeIn',
4696
4697			// Changing previous gallery item
4698			prevEffect : 'elastic', // 'elastic', 'fade' or 'none'
4699			prevSpeed  : 250,
4700			prevEasing : 'swing',
4701			prevMethod : 'changeOut',
4702
4703			// Enable default helpers
4704			helpers : {
4705				overlay : true,
4706				title   : true
4707			},
4708
4709			// Callbacks
4710			onCancel     : $.noop, // If canceling
4711			beforeLoad   : $.noop, // Before loading
4712			afterLoad    : $.noop, // After loading
4713			beforeShow   : $.noop, // Before changing in current item
4714			afterShow    : $.noop, // After opening
4715			beforeChange : $.noop, // Before changing gallery item
4716			beforeClose  : $.noop, // Before closing
4717			afterClose   : $.noop  // After closing
4718		},
4719
4720		//Current state
4721		group    : {}, // Selected group
4722		opts     : {}, // Group options
4723		previous : null,  // Previous element
4724		coming   : null,  // Element being loaded
4725		current  : null,  // Currently loaded element
4726		isActive : false, // Is activated
4727		isOpen   : false, // Is currently open
4728		isOpened : false, // Have been fully opened at least once
4729
4730		wrap  : null,
4731		skin  : null,
4732		outer : null,
4733		inner : null,
4734
4735		player : {
4736			timer    : null,
4737			isActive : false
4738		},
4739
4740		// Loaders
4741		ajaxLoad   : null,
4742		imgPreload : null,
4743
4744		// Some collections
4745		transitions : {},
4746		helpers     : {},
4747
4748		/*
4749		 *	Static methods
4750		 */
4751
4752		open: function (group, opts) {
4753			if (!group) {
4754				return;
4755			}
4756
4757			if (!$.isPlainObject(opts)) {
4758				opts = {};
4759			}
4760
4761			// Close if already active
4762			if (false === F.close(true)) {
4763				return;
4764			}
4765
4766			// Normalize group
4767			if (!$.isArray(group)) {
4768				group = isQuery(group) ? $(group).get() : [group];
4769			}
4770
4771			// Recheck if the type of each element is `object` and set content type (image, ajax, etc)
4772			$.each(group, function(i, element) {
4773				var obj = {},
4774					href,
4775					title,
4776					content,
4777					type,
4778					rez,
4779					hrefParts,
4780					selector;
4781
4782				if ($.type(element) === "object") {
4783					// Check if is DOM element
4784					if (element.nodeType) {
4785						element = $(element);
4786					}
4787
4788					if (isQuery(element)) {
4789						obj = {
4790							href    : element.data('fancybox-href') || element.attr('href'),
4791							title   : element.data('fancybox-title') || element.attr('title'),
4792							isDom   : true,
4793							element : element
4794						};
4795
4796						if ($.metadata) {
4797							$.extend(true, obj, element.metadata());
4798						}
4799
4800					} else {
4801						obj = element;
4802					}
4803				}
4804
4805				href  = opts.href  || obj.href || (isString(element) ? element : null);
4806				title = opts.title !== undefined ? opts.title : obj.title || '';
4807
vendor: 40,030 bytes, lines 4808-6419
4808				content = opts.content || obj.content;
4809				type    = content ? 'html' : (opts.type  || obj.type);
4810
4811				if (!type && obj.isDom) {
4812					type = element.data('fancybox-type');
4813
4814					if (!type) {
4815						rez  = element.prop('class').match(/fancybox\.(\w+)/);
4816						type = rez ? rez[1] : null;
4817					}
4818				}
4819
4820				if (isString(href)) {
4821					// Try to guess the content type
4822					if (!type) {
4823						if (F.isImage(href)) {
4824							type = 'image';
4825
4826						} else if (F.isSWF(href)) {
4827							type = 'swf';
4828
4829						} else if (href.charAt(0) === '#') {
4830							type = 'inline';
4831
4832						} else if (isString(element)) {
4833							type    = 'html';
4834							content = element;
4835						}
4836					}
4837
4838					// Split url into two pieces with source url and content selector, e.g,
4839					// "/mypage.html #my_id" will load "/mypage.html" and display element having id "my_id"
4840					if (type === 'ajax') {
4841						hrefParts = href.split(/\s+/, 2);
4842						href      = hrefParts.shift();
4843						selector  = hrefParts.shift();
4844					}
4845				}
4846
4847				if (!content) {
4848					if (type === 'inline') {
4849						if (href) {
4850							content = $( isString(href) ? href.replace(/.*(?=#[^\s]+$)/, '') : href ); //strip for ie7
4851
4852						} else if (obj.isDom) {
4853							content = element;
4854						}
4855
4856					} else if (type === 'html') {
4857						content = href;
4858
4859					} else if (!type && !href && obj.isDom) {
4860						type    = 'inline';
4861						content = element;
4862					}
4863				}
4864
4865				$.extend(obj, {
4866					href     : href,
4867					type     : type,
4868					content  : content,
4869					title    : title,
4870					selector : selector
4871				});
4872
4873				group[ i ] = obj;
4874			});
4875
4876			// Extend the defaults
4877			F.opts = $.extend(true, {}, F.defaults, opts);
4878
4879			// All options are merged recursive except keys
4880			if (opts.keys !== undefined) {
4881				F.opts.keys = opts.keys ? $.extend({}, F.defaults.keys, opts.keys) : false;
4882			}
4883
4884			F.group = group;
4885
4886			return F._start(F.opts.index);
4887		},
4888
4889		// Cancel image loading or abort ajax request
4890		cancel: function () {
4891			var coming = F.coming;
4892
4893			if (!coming || false === F.trigger('onCancel')) {
4894				return;
4895			}
4896
4897			F.hideLoading();
4898
4899			if (F.ajaxLoad) {
4900				F.ajaxLoad.abort();
4901			}
4902
4903			F.ajaxLoad = null;
4904
4905			if (F.imgPreload) {
4906				F.imgPreload.onload = F.imgPreload.onerror = null;
4907			}
4908
4909			if (coming.wrap) {
4910				coming.wrap.stop(true, true).trigger('onReset').remove();
4911			}
4912
4913			F.coming = null;
4914
4915			// If the first item has been canceled, then clear everything
4916			if (!F.current) {
4917				F._afterZoomOut( coming );
4918			}
4919		},
4920
4921		// Start closing animation if is open; remove immediately if opening/closing
4922		close: function (event) {
4923			F.cancel();
4924
4925			if (false === F.trigger('beforeClose')) {
4926				return;
4927			}
4928
4929			F.unbindEvents();
4930
4931			if (!F.isActive) {
4932				return;
4933			}
4934
4935			if (!F.isOpen || event === true) {
4936				$('.fancybox-wrap').stop(true).trigger('onReset').remove();
4937
4938				F._afterZoomOut();
4939
4940			} else {
4941				F.isOpen = F.isOpened = false;
4942				F.isClosing = true;
4943
4944				$('.fancybox-item, .fancybox-nav').remove();
4945
4946				F.wrap.stop(true, true).removeClass('fancybox-opened');
4947
4948				F.transitions[ F.current.closeMethod ]();
4949			}
4950		},
4951
4952		// Manage slideshow:
4953		//   $.fancybox.play(); - toggle slideshow
4954		//   $.fancybox.play( true ); - start
4955		//   $.fancybox.play( false ); - stop
4956		play: function ( action ) {
4957			var clear = function () {
4958					clearTimeout(F.player.timer);
4959				},
4960				set = function () {
4961					clear();
4962
4963					if (F.current && F.player.isActive) {
4964						F.player.timer = setTimeout(F.next, F.current.playSpeed);
4965					}
4966				},
4967				stop = function () {
4968					clear();
4969
4970					D.unbind('.player');
4971
4972					F.player.isActive = false;
4973
4974					F.trigger('onPlayEnd');
4975				},
4976				start = function () {
4977					if (F.current && (F.current.loop || F.current.index < F.group.length - 1)) {
4978						F.player.isActive = true;
4979
4980						D.bind({
4981							'onCancel.player beforeClose.player' : stop,
4982							'onUpdate.player'   : set,
4983							'beforeLoad.player' : clear
4984						});
4985
4986						set();
4987
4988						F.trigger('onPlayStart');
4989					}
4990				};
4991
4992			if (action === true || (!F.player.isActive && action !== false)) {
4993				start();
4994			} else {
4995				stop();
4996			}
4997		},
4998
4999		// Navigate to next gallery item
5000		next: function ( direction ) {
5001			var current = F.current;
5002
5003			if (current) {
5004				if (!isString(direction)) {
5005					direction = current.direction.next;
5006				}
5007
5008				F.jumpto(current.index + 1, direction, 'next');
5009			}
5010		},
5011
5012		// Navigate to previous gallery item
5013		prev: function ( direction ) {
5014			var current = F.current;
5015
5016			if (current) {
5017				if (!isString(direction)) {
5018					direction = current.direction.prev;
5019				}
5020
5021				F.jumpto(current.index - 1, direction, 'prev');
5022			}
5023		},
5024
5025		// Navigate to gallery item by index
5026		jumpto: function ( index, direction, router ) {
5027			var current = F.current;
5028
5029			if (!current) {
5030				return;
5031			}
5032
5033			index = getScalar(index);
5034
5035			F.direction = direction || current.direction[ (index >= current.index ? 'next' : 'prev') ];
5036			F.router    = router || 'jumpto';
5037
5038			if (current.loop) {
5039				if (index < 0) {
5040					index = current.group.length + (index % current.group.length);
5041				}
5042
5043				index = index % current.group.length;
5044			}
5045
5046			if (current.group[ index ] !== undefined) {
5047				F.cancel();
5048
5049				F._start(index);
5050			}
5051		},
5052
5053		// Center inside viewport and toggle position type to fixed or absolute if needed
5054		reposition: function (e, onlyAbsolute) {
5055			var current = F.current,
5056				wrap    = current ? current.wrap : null,
5057				pos;
5058
5059			if (wrap) {
5060				pos = F._getPosition(onlyAbsolute);
5061
5062				if (e && e.type === 'scroll') {
5063					delete pos.position;
5064
5065					wrap.stop(true, true).animate(pos, 200);
5066
5067				} else {
5068					wrap.css(pos);
5069
5070					current.pos = $.extend({}, current.dim, pos);
5071				}
5072			}
5073		},
5074
5075		update: function (e) {
5076			var type = (e && e.type),
5077				anyway = !type || type === 'orientationchange';
5078
5079			if (anyway) {
5080				clearTimeout(didUpdate);
5081
5082				didUpdate = null;
5083			}
5084
5085			if (!F.isOpen || didUpdate) {
5086				return;
5087			}
5088
5089			didUpdate = setTimeout(function() {
5090				var current = F.current;
5091
5092				if (!current || F.isClosing) {
5093					return;
5094				}
5095
5096				F.wrap.removeClass('fancybox-tmp');
5097
5098				if (anyway || type === 'load' || (type === 'resize' && current.autoResize)) {
5099					F._setDimension();
5100				}
5101
5102				if (!(type === 'scroll' && current.canShrink)) {
5103					F.reposition(e);
5104				}
5105
5106				F.trigger('onUpdate');
5107
5108				didUpdate = null;
5109
5110			}, (anyway && !isTouch ? 0 : 300));
5111		},
5112
5113		// Shrink content to fit inside viewport or restore if resized
5114		toggle: function ( action ) {
5115			if (F.isOpen) {
5116				F.current.fitToView = $.type(action) === "boolean" ? action : !F.current.fitToView;
5117
5118				// Help browser to restore document dimensions
5119				if (isTouch) {
5120					F.wrap.removeAttr('style').addClass('fancybox-tmp');
5121
5122					F.trigger('onUpdate');
5123				}
5124
5125				F.update();
5126			}
5127		},
5128
5129		hideLoading: function () {
5130			D.unbind('.loading');
5131
5132			$('#fancybox-loading').remove();
5133		},
5134
5135		showLoading: function () {
5136			var el, viewport;
5137
5138			F.hideLoading();
5139
5140			el = $('<div id="fancybox-loading"><div></div></div>').click(F.cancel).appendTo('body');
5141
5142			// If user will press the escape-button, the request will be canceled
5143			D.bind('keydown.loading', function(e) {
5144				if ((e.which || e.keyCode) === 27) {
5145					e.preventDefault();
5146
5147					F.cancel();
5148				}
5149			});
5150
5151			if (!F.defaults.fixed) {
5152				viewport = F.getViewport();
5153
5154				el.css({
5155					position : 'absolute',
5156					top  : (viewport.h * 0.5) + viewport.y,
5157					left : (viewport.w * 0.5) + viewport.x
5158				});
5159			}
5160		},
5161
5162		getViewport: function () {
5163			var locked = (F.current && F.current.locked) || false,
5164				rez    = {
5165					x: W.scrollLeft(),
5166					y: W.scrollTop()
5167				};
5168
5169			if (locked) {
5170				rez.w = locked[0].clientWidth;
5171				rez.h = locked[0].clientHeight;
5172
5173			} else {
5174				// See http://bugs.jquery.com/ticket/6724
5175				rez.w = isTouch && window.innerWidth  ? window.innerWidth  : W.width();
5176				rez.h = isTouch && window.innerHeight ? window.innerHeight : W.height();
5177			}
5178
5179			return rez;
5180		},
5181
5182		// Unbind the keyboard / clicking actions
5183		unbindEvents: function () {
5184			if (F.wrap && isQuery(F.wrap)) {
5185				F.wrap.unbind('.fb');
5186			}
5187
5188			D.unbind('.fb');
5189			W.unbind('.fb');
5190		},
5191
5192		bindEvents: function () {
5193			var current = F.current,
5194				keys;
5195
5196			if (!current) {
5197				return;
5198			}
5199
5200			// Changing document height on iOS devices triggers a 'resize' event,
5201			// that can change document height... repeating infinitely
5202			W.bind('orientationchange.fb' + (isTouch ? '' : ' resize.fb') + (current.autoCenter && !current.locked ? ' scroll.fb' : ''), F.update);
5203
5204			keys = current.keys;
5205
5206			if (keys) {
5207				D.bind('keydown.fb', function (e) {
5208					var code   = e.which || e.keyCode,
5209						target = e.target || e.srcElement;
5210
5211					// Skip esc key if loading, because showLoading will cancel preloading
5212					if (code === 27 && F.coming) {
5213						return false;
5214					}
5215
5216					// Ignore key combinations and key events within form elements
5217					if (!e.ctrlKey && !e.altKey && !e.shiftKey && !e.metaKey && !(target && (target.type || $(target).is('[contenteditable]')))) {
5218						$.each(keys, function(i, val) {
5219							if (current.group.length > 1 && val[ code ] !== undefined) {
5220								F[ i ]( val[ code ] );
5221
5222								e.preventDefault();
5223								return false;
5224							}
5225
5226							if ($.inArray(code, val) > -1) {
5227								F[ i ] ();
5228
5229								e.preventDefault();
5230								return false;
5231							}
5232						});
5233					}
5234				});
5235			}
5236
5237			if ($.fn.mousewheel && current.mouseWheel) {
5238				F.wrap.bind('mousewheel.fb', function (e, delta, deltaX, deltaY) {
5239					var target = e.target || null,
5240						parent = $(target),
5241						canScroll = false;
5242
5243					while (parent.length) {
5244						if (canScroll || parent.is('.fancybox-skin') || parent.is('.fancybox-wrap')) {
5245							break;
5246						}
5247
5248						canScroll = isScrollable( parent[0] );
5249						parent    = $(parent).parent();
5250					}
5251
5252					if (delta !== 0 && !canScroll) {
5253						if (F.group.length > 1 && !current.canShrink) {
5254							if (deltaY > 0 || deltaX > 0) {
5255								F.prev( deltaY > 0 ? 'down' : 'left' );
5256
5257							} else if (deltaY < 0 || deltaX < 0) {
5258								F.next( deltaY < 0 ? 'up' : 'right' );
5259							}
5260
5261							e.preventDefault();
5262						}
5263					}
5264				});
5265			}
5266		},
5267
5268		trigger: function (event, o) {
5269			var ret, obj = o || F.coming || F.current;
5270
5271			if (!obj) {
5272				return;
5273			}
5274
5275			if ($.isFunction( obj[event] )) {
5276				ret = obj[event].apply(obj, Array.prototype.slice.call(arguments, 1));
5277			}
5278
5279			if (ret === false) {
5280				return false;
5281			}
5282
5283			if (obj.helpers) {
5284				$.each(obj.helpers, function (helper, opts) {
5285					if (opts && F.helpers[helper] && $.isFunction(F.helpers[helper][event])) {
5286						F.helpers[helper][event]($.extend(true, {}, F.helpers[helper].defaults, opts), obj);
5287					}
5288				});
5289			}
5290
5291			D.trigger(event);
5292		},
5293
5294		isImage: function (str) {
5295			return isString(str) && str.match(/(^data:image\/.*,)|(\.(jp(e|g|eg)|gif|png|bmp|webp|svg)((\?|#).*)?$)/i);
5296		},
5297
5298		isSWF: function (str) {
5299			return isString(str) && str.match(/\.(swf)((\?|#).*)?$/i);
5300		},
5301
5302		_start: function (index) {
5303			var coming = {},
5304				obj,
5305				href,
5306				type,
5307				margin,
5308				padding;
5309
5310			index = getScalar( index );
5311			obj   = F.group[ index ] || null;
5312
5313			if (!obj) {
5314				return false;
5315			}
5316
5317			coming = $.extend(true, {}, F.opts, obj);
5318
5319			// Convert margin and padding properties to array - top, right, bottom, left
5320			margin  = coming.margin;
5321			padding = coming.padding;
5322
5323			if ($.type(margin) === 'number') {
5324				coming.margin = [margin, margin, margin, margin];
5325			}
5326
5327			if ($.type(padding) === 'number') {
5328				coming.padding = [padding, padding, padding, padding];
5329			}
5330
5331			// 'modal' propery is just a shortcut
5332			if (coming.modal) {
5333				$.extend(true, coming, {
5334					closeBtn   : false,
5335					closeClick : false,
5336					nextClick  : false,
5337					arrows     : false,
5338					mouseWheel : false,
5339					keys       : null,
5340					helpers: {
5341						overlay : {
5342							closeClick : false
5343						}
5344					}
5345				});
5346			}
5347
5348			// 'autoSize' property is a shortcut, too
5349			if (coming.autoSize) {
5350				coming.autoWidth = coming.autoHeight = true;
5351			}
5352
5353			if (coming.width === 'auto') {
5354				coming.autoWidth = true;
5355			}
5356
5357			if (coming.height === 'auto') {
5358				coming.autoHeight = true;
5359			}
5360
5361			/*
5362			 * Add reference to the group, so it`s possible to access from callbacks, example:
5363			 * afterLoad : function() {
5364			 *     this.title = 'Image ' + (this.index + 1) + ' of ' + this.group.length + (this.title ? ' - ' + this.title : '');
5365			 * }
5366			 */
5367
5368			coming.group  = F.group;
5369			coming.index  = index;
5370
5371			// Give a chance for callback or helpers to update coming item (type, title, etc)
5372			F.coming = coming;
5373
5374			if (false === F.trigger('beforeLoad')) {
5375				F.coming = null;
5376
5377				return;
5378			}
5379
5380			type = coming.type;
5381			href = coming.href;
5382
5383			if (!type) {
5384				F.coming = null;
5385
5386				//If we can not determine content type then drop silently or display next/prev item if looping through gallery
5387				if (F.current && F.router && F.router !== 'jumpto') {
5388					F.current.index = index;
5389
5390					return F[ F.router ]( F.direction );
5391				}
5392
5393				return false;
5394			}
5395
5396			F.isActive = true;
5397
5398			if (type === 'image' || type === 'swf') {
5399				coming.autoHeight = coming.autoWidth = false;
5400				coming.scrolling  = 'visible';
5401			}
5402
5403			if (type === 'image') {
5404				coming.aspectRatio = true;
5405			}
5406
5407			if (type === 'iframe' && isTouch) {
5408				coming.scrolling = 'scroll';
5409			}
5410
5411			// Build the neccessary markup
5412			coming.wrap = $(coming.tpl.wrap).addClass('fancybox-' + (isTouch ? 'mobile' : 'desktop') + ' fancybox-type-' + type + ' fancybox-tmp ' + coming.wrapCSS).appendTo( coming.parent || 'body' );
5413
5414			$.extend(coming, {
5415				skin  : $('.fancybox-skin',  coming.wrap),
5416				outer : $('.fancybox-outer', coming.wrap),
5417				inner : $('.fancybox-inner', coming.wrap)
5418			});
5419
5420			$.each(["Top", "Right", "Bottom", "Left"], function(i, v) {
5421				coming.skin.css('padding' + v, getValue(coming.padding[ i ]));
5422			});
5423
5424			F.trigger('onReady');
5425
5426			// Check before try to load; 'inline' and 'html' types need content, others - href
5427			if (type === 'inline' || type === 'html') {
5428				if (!coming.content || !coming.content.length) {
5429					return F._error( 'content' );
5430				}
5431
5432			} else if (!href) {
5433				return F._error( 'href' );
5434			}
5435
5436			if (type === 'image') {
5437				F._loadImage();
5438
5439			} else if (type === 'ajax') {
5440				F._loadAjax();
5441
5442			} else if (type === 'iframe') {
5443				F._loadIframe();
5444
5445			} else {
5446				F._afterLoad();
5447			}
5448		},
5449
5450		_error: function ( type ) {
5451			$.extend(F.coming, {
5452				type       : 'html',
5453				autoWidth  : true,
5454				autoHeight : true,
5455				minWidth   : 0,
5456				minHeight  : 0,
5457				scrolling  : 'no',
5458				hasError   : type,
5459				content    : F.coming.tpl.error
5460			});
5461
5462			F._afterLoad();
5463		},
5464
5465		_loadImage: function () {
5466			// Reset preload image so it is later possible to check "complete" property
5467			var img = F.imgPreload = new Image();
5468
5469			img.onload = function () {
5470				this.onload = this.onerror = null;
5471
5472				F.coming.width  = this.width / F.opts.pixelRatio;
5473				F.coming.height = this.height / F.opts.pixelRatio;
5474
5475				F._afterLoad();
5476			};
5477
5478			img.onerror = function () {
5479				this.onload = this.onerror = null;
5480
5481				F._error( 'image' );
5482			};
5483
5484			img.src = F.coming.href;
5485
5486			if (img.complete !== true) {
5487				F.showLoading();
5488			}
5489		},
5490
5491		_loadAjax: function () {
5492			var coming = F.coming;
5493
5494			F.showLoading();
5495
5496			F.ajaxLoad = $.ajax($.extend({}, coming.ajax, {
5497				url: coming.href,
5498				error: function (jqXHR, textStatus) {
5499					if (F.coming && textStatus !== 'abort') {
5500						F._error( 'ajax', jqXHR );
5501
5502					} else {
5503						F.hideLoading();
5504					}
5505				},
5506				success: function (data, textStatus) {
5507					if (textStatus === 'success') {
5508						coming.content = data;
5509
5510						F._afterLoad();
5511					}
5512				}
5513			}));
5514		},
5515
5516		_loadIframe: function() {
5517			var coming = F.coming,
5518				iframe = $(coming.tpl.iframe.replace(/\{rnd\}/g, new Date().getTime()))
5519					.attr('scrolling', isTouch ? 'auto' : coming.iframe.scrolling)
5520					.attr('src', coming.href);
5521
5522			// This helps IE
5523			$(coming.wrap).bind('onReset', function () {
5524				try {
5525					$(this).find('iframe').hide().attr('src', '//about:blank').end().empty();
5526				} catch (e) {}
5527			});
5528
5529			if (coming.iframe.preload) {
5530				F.showLoading();
5531
5532				iframe.one('load', function() {
5533					$(this).data('ready', 1);
5534
5535					// iOS will lose scrolling if we resize
5536					if (!isTouch) {
5537						$(this).bind('load.fb', F.update);
5538					}
5539
5540					// Without this trick:
5541					//   - iframe won't scroll on iOS devices
5542					//   - IE7 sometimes displays empty iframe
5543					$(this).parents('.fancybox-wrap').width('100%').removeClass('fancybox-tmp').show();
5544
5545					F._afterLoad();
5546				});
5547			}
5548
5549			coming.content = iframe.appendTo( coming.inner );
5550
5551			if (!coming.iframe.preload) {
5552				F._afterLoad();
5553			}
5554		},
5555
5556		_preloadImages: function() {
5557			var group   = F.group,
5558				current = F.current,
5559				len     = group.length,
5560				cnt     = current.preload ? Math.min(current.preload, len - 1) : 0,
5561				item,
5562				i;
5563
5564			for (i = 1; i <= cnt; i += 1) {
5565				item = group[ (current.index + i ) % len ];
5566
5567				if (item.type === 'image' && item.href) {
5568					new Image().src = item.href;
5569				}
5570			}
5571		},
5572
5573		_afterLoad: function () {
5574			var coming   = F.coming,
5575				previous = F.current,
5576				placeholder = 'fancybox-placeholder',
5577				current,
5578				content,
5579				type,
5580				scrolling,
5581				href,
5582				embed;
5583
5584			F.hideLoading();
5585
5586			if (!coming || F.isActive === false) {
5587				return;
5588			}
5589
5590			if (false === F.trigger('afterLoad', coming, previous)) {
5591				coming.wrap.stop(true).trigger('onReset').remove();
5592
5593				F.coming = null;
5594
5595				return;
5596			}
5597
5598			if (previous) {
5599				F.trigger('beforeChange', previous);
5600
5601				previous.wrap.stop(true).removeClass('fancybox-opened')
5602					.find('.fancybox-item, .fancybox-nav')
5603					.remove();
5604			}
5605
5606			F.unbindEvents();
5607
5608			current   = coming;
5609			content   = coming.content;
5610			type      = coming.type;
5611			scrolling = coming.scrolling;
5612
5613			$.extend(F, {
5614				wrap  : current.wrap,
5615				skin  : current.skin,
5616				outer : current.outer,
5617				inner : current.inner,
5618				current  : current,
5619				previous : previous
5620			});
5621
5622			href = current.href;
5623
5624			switch (type) {
5625				case 'inline':
5626				case 'ajax':
5627				case 'html':
5628					if (current.selector) {
5629						content = $('<div>').html(content).find(current.selector);
5630
5631					} else if (isQuery(content)) {
5632						if (!content.data(placeholder)) {
5633							content.data(placeholder, $('<div class="' + placeholder + '"></div>').insertAfter( content ).hide() );
5634						}
5635
5636						content = content.show().detach();
5637
5638						current.wrap.bind('onReset', function () {
5639							if ($(this).find(content).length) {
5640								content.hide().replaceAll( content.data(placeholder) ).data(placeholder, false);
5641							}
5642						});
5643					}
5644					break;
5645
5646				case 'image':
5647					content = current.tpl.image.replace('{href}', href);
5648					break;
5649
5650				case 'swf':
5651					content = '<object id="fancybox-swf" classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" width="100%" height="100%"><param name="movie" value="' + href + '"></param>';
5652					embed   = '';
5653
5654					$.each(current.swf, function(name, val) {
5655						content += '<param name="' + name + '" value="' + val + '"></param>';
5656						embed   += ' ' + name + '="' + val + '"';
5657					});
5658
5659					content += '<embed src="' + href + '" type="application/x-shockwave-flash" width="100%" height="100%"' + embed + '></embed></object>';
5660					break;
5661			}
5662
5663			if (!(isQuery(content) && content.parent().is(current.inner))) {
5664				current.inner.append( content );
5665			}
5666
5667			// Give a chance for helpers or callbacks to update elements
5668			F.trigger('beforeShow');
5669
5670			// Set scrolling before calculating dimensions
5671			current.inner.css('overflow', scrolling === 'yes' ? 'scroll' : (scrolling === 'no' ? 'hidden' : scrolling));
5672
5673			// Set initial dimensions and start position
5674			F._setDimension();
5675
5676			F.reposition();
5677
5678			F.isOpen = false;
5679			F.coming = null;
5680
5681			F.bindEvents();
5682
5683			if (!F.isOpened) {
5684				$('.fancybox-wrap').not( current.wrap ).stop(true).trigger('onReset').remove();
5685
5686			} else if (previous.prevMethod) {
5687				F.transitions[ previous.prevMethod ]();
5688			}
5689
5690			F.transitions[ F.isOpened ? current.nextMethod : current.openMethod ]();
5691
5692			F._preloadImages();
5693		},
5694
5695		_setDimension: function () {
5696			var viewport   = F.getViewport(),
5697				steps      = 0,
5698				canShrink  = false,
5699				canExpand  = false,
5700				wrap       = F.wrap,
5701				skin       = F.skin,
5702				inner      = F.inner,
5703				current    = F.current,
5704				width      = current.width,
5705				height     = current.height,
5706				minWidth   = current.minWidth,
5707				minHeight  = current.minHeight,
5708				maxWidth   = current.maxWidth,
5709				maxHeight  = current.maxHeight,
5710				scrolling  = current.scrolling,
5711				scrollOut  = current.scrollOutside ? current.scrollbarWidth : 0,
5712				margin     = current.margin,
5713				wMargin    = getScalar(margin[1] + margin[3]),
5714				hMargin    = getScalar(margin[0] + margin[2]),
5715				wPadding,
5716				hPadding,
5717				wSpace,
5718				hSpace,
5719				origWidth,
5720				origHeight,
5721				origMaxWidth,
5722				origMaxHeight,
5723				ratio,
5724				width_,
5725				height_,
5726				maxWidth_,
5727				maxHeight_,
5728				iframe,
5729				body;
5730
5731			// Reset dimensions so we could re-check actual size
5732			wrap.add(skin).add(inner).width('auto').height('auto').removeClass('fancybox-tmp');
5733
5734			wPadding = getScalar(skin.outerWidth(true)  - skin.width());
5735			hPadding = getScalar(skin.outerHeight(true) - skin.height());
5736
5737			// Any space between content and viewport (margin, padding, border, title)
5738			wSpace = wMargin + wPadding;
5739			hSpace = hMargin + hPadding;
5740
5741			origWidth  = isPercentage(width)  ? (viewport.w - wSpace) * getScalar(width)  / 100 : width;
5742			origHeight = isPercentage(height) ? (viewport.h - hSpace) * getScalar(height) / 100 : height;
5743
5744			if (current.type === 'iframe') {
5745				iframe = current.content;
5746
5747				if (current.autoHeight && iframe.data('ready') === 1) {
5748					try {
5749						if (iframe[0].contentWindow.document.location) {
5750							inner.width( origWidth ).height(9999);
5751
5752							body = iframe.contents().find('body');
5753
5754							if (scrollOut) {
5755								body.css('overflow-x', 'hidden');
5756							}
5757
5758							origHeight = body.outerHeight(true);
5759						}
5760
5761					} catch (e) {}
5762				}
5763
5764			} else if (current.autoWidth || current.autoHeight) {
5765				inner.addClass( 'fancybox-tmp' );
5766
5767				// Set width or height in case we need to calculate only one dimension
5768				if (!current.autoWidth) {
5769					inner.width( origWidth );
5770				}
5771
5772				if (!current.autoHeight) {
5773					inner.height( origHeight );
5774				}
5775
5776				if (current.autoWidth) {
5777					origWidth = inner.width();
5778				}
5779
5780				if (current.autoHeight) {
5781					origHeight = inner.height();
5782				}
5783
5784				inner.removeClass( 'fancybox-tmp' );
5785			}
5786
5787			width  = getScalar( origWidth );
5788			height = getScalar( origHeight );
5789
5790			ratio  = origWidth / origHeight;
5791
5792			// Calculations for the content
5793			minWidth  = getScalar(isPercentage(minWidth) ? getScalar(minWidth, 'w') - wSpace : minWidth);
5794			maxWidth  = getScalar(isPercentage(maxWidth) ? getScalar(maxWidth, 'w') - wSpace : maxWidth);
5795
5796			minHeight = getScalar(isPercentage(minHeight) ? getScalar(minHeight, 'h') - hSpace : minHeight);
5797			maxHeight = getScalar(isPercentage(maxHeight) ? getScalar(maxHeight, 'h') - hSpace : maxHeight);
5798
5799			// These will be used to determine if wrap can fit in the viewport
5800			origMaxWidth  = maxWidth;
5801			origMaxHeight = maxHeight;
5802
5803			if (current.fitToView) {
5804				maxWidth  = Math.min(viewport.w - wSpace, maxWidth);
5805				maxHeight = Math.min(viewport.h - hSpace, maxHeight);
5806			}
5807
5808			maxWidth_  = viewport.w - wMargin;
5809			maxHeight_ = viewport.h - hMargin;
5810
5811			if (current.aspectRatio) {
5812				if (width > maxWidth) {
5813					width  = maxWidth;
5814					height = getScalar(width / ratio);
5815				}
5816
5817				if (height > maxHeight) {
5818					height = maxHeight;
5819					width  = getScalar(height * ratio);
5820				}
5821
5822				if (width < minWidth) {
5823					width  = minWidth;
5824					height = getScalar(width / ratio);
5825				}
5826
5827				if (height < minHeight) {
5828					height = minHeight;
5829					width  = getScalar(height * ratio);
5830				}
5831
5832			} else {
5833				width = Math.max(minWidth, Math.min(width, maxWidth));
5834
5835				if (current.autoHeight && current.type !== 'iframe') {
5836					inner.width( width );
5837
5838					height = inner.height();
5839				}
5840
5841				height = Math.max(minHeight, Math.min(height, maxHeight));
5842			}
5843
5844			// Try to fit inside viewport (including the title)
5845			if (current.fitToView) {
5846				inner.width( width ).height( height );
5847
5848				wrap.width( width + wPadding );
5849
5850				// Real wrap dimensions
5851				width_  = wrap.width();
5852				height_ = wrap.height();
5853
5854				if (current.aspectRatio) {
5855					while ((width_ > maxWidth_ || height_ > maxHeight_) && width > minWidth && height > minHeight) {
5856						if (steps++ > 19) {
5857							break;
5858						}
5859
5860						height = Math.max(minHeight, Math.min(maxHeight, height - 10));
5861						width  = getScalar(height * ratio);
5862
5863						if (width < minWidth) {
5864							width  = minWidth;
5865							height = getScalar(width / ratio);
5866						}
5867
5868						if (width > maxWidth) {
5869							width  = maxWidth;
5870							height = getScalar(width / ratio);
5871						}
5872
5873						inner.width( width ).height( height );
5874
5875						wrap.width( width + wPadding );
5876
5877						width_  = wrap.width();
5878						height_ = wrap.height();
5879					}
5880
5881				} else {
5882					width  = Math.max(minWidth,  Math.min(width,  width  - (width_  - maxWidth_)));
5883					height = Math.max(minHeight, Math.min(height, height - (height_ - maxHeight_)));
5884				}
5885			}
5886
5887			if (scrollOut && scrolling === 'auto' && height < origHeight && (width + wPadding + scrollOut) < maxWidth_) {
5888				width += scrollOut;
5889			}
5890
5891			inner.width( width ).height( height );
5892
5893			wrap.width( width + wPadding );
5894
5895			width_  = wrap.width();
5896			height_ = wrap.height();
5897
5898			canShrink = (width_ > maxWidth_ || height_ > maxHeight_) && width > minWidth && height > minHeight;
5899			canExpand = current.aspectRatio ? (width < origMaxWidth && height < origMaxHeight && width < origWidth && height < origHeight) : ((width < origMaxWidth || height < origMaxHeight) && (width < origWidth || height < origHeight));
5900
5901			$.extend(current, {
5902				dim : {
5903					width	: getValue( width_ ),
5904					height	: getValue( height_ )
5905				},
5906				origWidth  : origWidth,
5907				origHeight : origHeight,
5908				canShrink  : canShrink,
5909				canExpand  : canExpand,
5910				wPadding   : wPadding,
5911				hPadding   : hPadding,
5912				wrapSpace  : height_ - skin.outerHeight(true),
5913				skinSpace  : skin.height() - height
5914			});
5915
5916			if (!iframe && current.autoHeight && height > minHeight && height < maxHeight && !canExpand) {
5917				inner.height('auto');
5918			}
5919		},
5920
5921		_getPosition: function (onlyAbsolute) {
5922			var current  = F.current,
5923				viewport = F.getViewport(),
5924				margin   = current.margin,
5925				width    = F.wrap.width()  + margin[1] + margin[3],
5926				height   = F.wrap.height() + margin[0] + margin[2],
5927				rez      = {
5928					position: 'absolute',
5929					top  : margin[0],
5930					left : margin[3]
5931				};
5932
5933			if (current.autoCenter && current.fixed && !onlyAbsolute && height <= viewport.h && width <= viewport.w) {
5934				rez.position = 'fixed';
5935
5936			} else if (!current.locked) {
5937				rez.top  += viewport.y;
5938				rez.left += viewport.x;
5939			}
5940
5941			rez.top  = getValue(Math.max(rez.top,  rez.top  + ((viewport.h - height) * current.topRatio)));
5942			rez.left = getValue(Math.max(rez.left, rez.left + ((viewport.w - width)  * current.leftRatio)));
5943
5944			return rez;
5945		},
5946
5947		_afterZoomIn: function () {
5948			var current = F.current;
5949
5950			if (!current) {
5951				return;
5952			}
5953
5954			F.isOpen = F.isOpened = true;
5955
5956			F.wrap.css('overflow', 'visible').addClass('fancybox-opened');
5957
5958			F.update();
5959
5960			// Assign a click event
5961			if ( current.closeClick || (current.nextClick && F.group.length > 1) ) {
5962				F.inner.css('cursor', 'pointer').bind('click.fb', function(e) {
5963					if (!$(e.target).is('a') && !$(e.target).parent().is('a')) {
5964						e.preventDefault();
5965
5966						F[ current.closeClick ? 'close' : 'next' ]();
5967					}
5968				});
5969			}
5970
5971			// Create a close button
5972			if (current.closeBtn) {
5973				$(current.tpl.closeBtn).appendTo(F.skin).bind('click.fb', function(e) {
5974					e.preventDefault();
5975
5976					F.close();
5977				});
5978			}
5979
5980			// Create navigation arrows
5981			if (current.arrows && F.group.length > 1) {
5982				if (current.loop || current.index > 0) {
5983					$(current.tpl.prev).appendTo(F.outer).bind('click.fb', F.prev);
5984				}
5985
5986				if (current.loop || current.index < F.group.length - 1) {
5987					$(current.tpl.next).appendTo(F.outer).bind('click.fb', F.next);
5988				}
5989			}
5990
5991			F.trigger('afterShow');
5992
5993			// Stop the slideshow if this is the last item
5994			if (!current.loop && current.index === current.group.length - 1) {
5995				F.play( false );
5996
5997			} else if (F.opts.autoPlay && !F.player.isActive) {
5998				F.opts.autoPlay = false;
5999
6000				F.play();
6001			}
6002		},
6003
6004		_afterZoomOut: function ( obj ) {
6005			obj = obj || F.current;
6006
6007			$('.fancybox-wrap').trigger('onReset').remove();
6008
6009			$.extend(F, {
6010				group  : {},
6011				opts   : {},
6012				router : false,
6013				current   : null,
6014				isActive  : false,
6015				isOpened  : false,
6016				isOpen    : false,
6017				isClosing : false,
6018				wrap   : null,
6019				skin   : null,
6020				outer  : null,
6021				inner  : null
6022			});
6023
6024			F.trigger('afterClose', obj);
6025		}
6026	});
6027
6028	/*
6029	 *	Default transitions
6030	 */
6031
6032	F.transitions = {
6033		getOrigPosition: function () {
6034			var current  = F.current,
6035				element  = current.element,
6036				orig     = current.orig,
6037				pos      = {},
6038				width    = 50,
6039				height   = 50,
6040				hPadding = current.hPadding,
6041				wPadding = current.wPadding,
6042				viewport = F.getViewport();
6043
6044			if (!orig && current.isDom && element.is(':visible')) {
6045				orig = element.find('img:first');
6046
6047				if (!orig.length) {
6048					orig = element;
6049				}
6050			}
6051
6052			if (isQuery(orig)) {
6053				pos = orig.offset();
6054
6055				if (orig.is('img')) {
6056					width  = orig.outerWidth();
6057					height = orig.outerHeight();
6058				}
6059
6060			} else {
6061				pos.top  = viewport.y + (viewport.h - height) * current.topRatio;
6062				pos.left = viewport.x + (viewport.w - width)  * current.leftRatio;
6063			}
6064
6065			if (F.wrap.css('position') === 'fixed' || current.locked) {
6066				pos.top  -= viewport.y;
6067				pos.left -= viewport.x;
6068			}
6069
6070			pos = {
6071				top     : getValue(pos.top  - hPadding * current.topRatio),
6072				left    : getValue(pos.left - wPadding * current.leftRatio),
6073				width   : getValue(width  + wPadding),
6074				height  : getValue(height + hPadding)
6075			};
6076
6077			return pos;
6078		},
6079
6080		step: function (now, fx) {
6081			var ratio,
6082				padding,
6083				value,
6084				prop       = fx.prop,
6085				current    = F.current,
6086				wrapSpace  = current.wrapSpace,
6087				skinSpace  = current.skinSpace;
6088
6089			if (prop === 'width' || prop === 'height') {
6090				ratio = fx.end === fx.start ? 1 : (now - fx.start) / (fx.end - fx.start);
6091
6092				if (F.isClosing) {
6093					ratio = 1 - ratio;
6094				}
6095
6096				padding = prop === 'width' ? current.wPadding : current.hPadding;
6097				value   = now - padding;
6098
6099				F.skin[ prop ](  getScalar( prop === 'width' ?  value : value - (wrapSpace * ratio) ) );
6100				F.inner[ prop ]( getScalar( prop === 'width' ?  value : value - (wrapSpace * ratio) - (skinSpace * ratio) ) );
6101			}
6102		},
6103
6104		zoomIn: function () {
6105			var current  = F.current,
6106				startPos = current.pos,
6107				effect   = current.openEffect,
6108				elastic  = effect === 'elastic',
6109				endPos   = $.extend({opacity : 1}, startPos);
6110
6111			// Remove "position" property that breaks older IE
6112			delete endPos.position;
6113
6114			if (elastic) {
6115				startPos = this.getOrigPosition();
6116
6117				if (current.openOpacity) {
6118					startPos.opacity = 0.1;
6119				}
6120
6121			} else if (effect === 'fade') {
6122				startPos.opacity = 0.1;
6123			}
6124
6125			F.wrap.css(startPos).animate(endPos, {
6126				duration : effect === 'none' ? 0 : current.openSpeed,
6127				easing   : current.openEasing,
6128				step     : elastic ? this.step : null,
6129				complete : F._afterZoomIn
6130			});
6131		},
6132
6133		zoomOut: function () {
6134			var current  = F.current,
6135				effect   = current.closeEffect,
6136				elastic  = effect === 'elastic',
6137				endPos   = {opacity : 0.1};
6138
6139			if (elastic) {
6140				endPos = this.getOrigPosition();
6141
6142				if (current.closeOpacity) {
6143					endPos.opacity = 0.1;
6144				}
6145			}
6146
6147			F.wrap.animate(endPos, {
6148				duration : effect === 'none' ? 0 : current.closeSpeed,
6149				easing   : current.closeEasing,
6150				step     : elastic ? this.step : null,
6151				complete : F._afterZoomOut
6152			});
6153		},
6154
6155		changeIn: function () {
6156			var current   = F.current,
6157				effect    = current.nextEffect,
6158				startPos  = current.pos,
6159				endPos    = { opacity : 1 },
6160				direction = F.direction,
6161				distance  = 200,
6162				field;
6163
6164			startPos.opacity = 0.1;
6165
6166			if (effect === 'elastic') {
6167				field = direction === 'down' || direction === 'up' ? 'top' : 'left';
6168
6169				if (direction === 'down' || direction === 'right') {
6170					startPos[ field ] = getValue(getScalar(startPos[ field ]) - distance);
6171					endPos[ field ]   = '+=' + distance + 'px';
6172
6173				} else {
6174					startPos[ field ] = getValue(getScalar(startPos[ field ]) + distance);
6175					endPos[ field ]   = '-=' + distance + 'px';
6176				}
6177			}
6178
6179			// Workaround for http://bugs.jquery.com/ticket/12273
6180			if (effect === 'none') {
6181				F._afterZoomIn();
6182
6183			} else {
6184				F.wrap.css(startPos).animate(endPos, {
6185					duration : current.nextSpeed,
6186					easing   : current.nextEasing,
6187					complete : F._afterZoomIn
6188				});
6189			}
6190		},
6191
6192		changeOut: function () {
6193			var previous  = F.previous,
6194				effect    = previous.prevEffect,
6195				endPos    = { opacity : 0.1 },
6196				direction = F.direction,
6197				distance  = 200;
6198
6199			if (effect === 'elastic') {
6200				endPos[ direction === 'down' || direction === 'up' ? 'top' : 'left' ] = ( direction === 'up' || direction === 'left' ? '-' : '+' ) + '=' + distance + 'px';
6201			}
6202
6203			previous.wrap.animate(endPos, {
6204				duration : effect === 'none' ? 0 : previous.prevSpeed,
6205				easing   : previous.prevEasing,
6206				complete : function () {
6207					$(this).trigger('onReset').remove();
6208				}
6209			});
6210		}
6211	};
6212
6213	/*
6214	 *	Overlay helper
6215	 */
6216
6217	F.helpers.overlay = {
6218		defaults : {
6219			closeClick : true,      // if true, fancyBox will be closed when user clicks on the overlay
6220			speedOut   : 200,       // duration of fadeOut animation
6221			showEarly  : true,      // indicates if should be opened immediately or wait until the content is ready
6222			css        : {},        // custom CSS properties
6223			locked     : !isTouch,  // if true, the content will be locked into overlay
6224			fixed      : true       // if false, the overlay CSS position property will not be set to "fixed"
6225		},
6226
6227		overlay : null,      // current handle
6228		fixed   : false,     // indicates if the overlay has position "fixed"
6229		el      : $('html'), // element that contains "the lock"
6230
6231		// Public methods
6232		create : function(opts) {
6233			opts = $.extend({}, this.defaults, opts);
6234
6235			if (this.overlay) {
6236				this.close();
6237			}
6238
6239			this.overlay = $('<div class="fancybox-overlay"></div>').appendTo('body');
6240			this.fixed   = false;
6241
6242			if (opts.fixed && F.defaults.fixed) {
6243				this.overlay.addClass('fancybox-overlay-fixed');
6244
6245				this.fixed = true;
6246			}
6247		},
6248
6249		open : function(opts) {
6250			var that = this;
6251
6252			opts = $.extend({}, this.defaults, opts);
6253
6254			if (this.overlay) {
6255				this.overlay.unbind('.overlay').width('auto').height('auto');
6256
6257			} else {
6258				this.create(opts);
6259			}
6260
6261			if (!this.fixed) {
6262				W.bind('resize.overlay', $.proxy( this.update, this) );
6263
6264				this.update();
6265			}
6266
6267			if (opts.closeClick) {
6268				this.overlay.bind('click.overlay', function(e) {
6269					if ($(e.target).hasClass('fancybox-overlay')) {
6270						if (F.isActive) {
6271							F.close();
6272						} else {
6273							that.close();
6274						}
6275
6276						return false;
6277					}
6278				});
6279			}
6280
6281			this.overlay.css( opts.css ).show();
6282		},
6283
6284		close : function() {
6285			var scrollV, scrollH;
6286
6287			W.unbind('resize.overlay');
6288
6289			if (this.el.hasClass('fancybox-lock')) {
6290				$('.fancybox-margin').removeClass('fancybox-margin');
6291
6292				scrollV = W.scrollTop();
6293				scrollH = W.scrollLeft();
6294
6295				this.el.removeClass('fancybox-lock');
6296
6297				W.scrollTop( scrollV ).scrollLeft( scrollH );
6298			}
6299
6300			$('.fancybox-overlay').remove().hide();
6301
6302			$.extend(this, {
6303				overlay : null,
6304				fixed   : false
6305			});
6306		},
6307
6308		// Private, callbacks
6309
6310		update : function () {
6311			var width = '100%', offsetWidth;
6312
6313			// Reset width/height so it will not mess
6314			this.overlay.width(width).height('100%');
6315
6316			// jQuery does not return reliable result for IE
6317			if (IE) {
6318				offsetWidth = Math.max(document.documentElement.offsetWidth, document.body.offsetWidth);
6319
6320				if (D.width() > offsetWidth) {
6321					width = D.width();
6322				}
6323
6324			} else if (D.width() > W.width()) {
6325				width = D.width();
6326			}
6327
6328			this.overlay.width(width).height(D.height());
6329		},
6330
6331		// This is where we can manipulate DOM, because later it would cause iframes to reload
6332		onReady : function (opts, obj) {
6333			var overlay = this.overlay;
6334
6335			$('.fancybox-overlay').stop(true, true);
6336
6337			if (!overlay) {
6338				this.create(opts);
6339			}
6340
6341			if (opts.locked && this.fixed && obj.fixed) {
6342				if (!overlay) {
6343					this.margin = D.height() > W.height() ? $('html').css('margin-right').replace("px", "") : false;
6344				}
6345
6346				obj.locked = this.overlay.append( obj.wrap );
6347				obj.fixed  = false;
6348			}
6349
6350			if (opts.showEarly === true) {
6351				this.beforeShow.apply(this, arguments);
6352			}
6353		},
6354
6355		beforeShow : function(opts, obj) {
6356			var scrollV, scrollH;
6357
6358			if (obj.locked) {
6359				if (this.margin !== false) {
6360					$('*').filter(function(){
6361						return ($(this).css('position') === 'fixed' && !$(this).hasClass("fancybox-overlay") && !$(this).hasClass("fancybox-wrap") );
6362					}).addClass('fancybox-margin');
6363
6364					this.el.addClass('fancybox-margin');
6365				}
6366
6367				scrollV = W.scrollTop();
6368				scrollH = W.scrollLeft();
6369
6370				this.el.addClass('fancybox-lock');
6371
6372				W.scrollTop( scrollV ).scrollLeft( scrollH );
6373			}
6374
6375			this.open(opts);
6376		},
6377
6378		onUpdate : function() {
6379			if (!this.fixed) {
6380				this.update();
6381			}
6382		},
6383
6384		afterClose: function (opts) {
6385			// Remove overlay if exists and fancyBox is not opening
6386			// (e.g., it is not being open using afterClose callback)
6387			//if (this.overlay && !F.isActive) {
6388			if (this.overlay && !F.coming) {
6389				this.overlay.fadeOut(opts.speedOut, $.proxy( this.close, this ));
6390			}
6391		}
6392	};
6393
6394	/*
6395	 *	Title helper
6396	 */
6397
6398	F.helpers.title = {
6399		defaults : {
6400			type     : 'float', // 'float', 'inside', 'outside' or 'over',
6401			position : 'bottom' // 'top' or 'bottom'
6402		},
6403
6404		beforeShow: function (opts) {
6405			var current = F.current,
6406				text    = current.title,
6407				type    = opts.type,
6408				title,
6409				target;
6410
6411			if ($.isFunction(text)) {
6412				text = text.call(current.element, current);
6413			}
6414
6415			if (!isString(text) || $.trim(text) === '') {
6416				return;
6417			}
6418
6419			title = $('<div class="fancybox-title fancybox-title-' + type + '-wrap">
vendor: 3,295 bytes, lines 6419-6547
6419' + text + '</div>');
6420
6421			switch (type) {
6422				case 'inside':
6423					target = F.skin;
6424					break;
6425
6426				case 'outside':
6427					target = F.wrap;
6428					break;
6429
6430				case 'over':
6431					target = F.inner;
6432					break;
6433
6434				default: // 'float'
6435					target = F.skin;
6436
6437					title.appendTo('body');
6438
6439					if (IE) {
6440						title.width( title.width() );
6441					}
6442
6443					title.wrapInner('<span class="child"></span>');
6444
6445					//Increase bottom margin so this title will also fit into viewport
6446					F.current.margin[2] += Math.abs( getScalar(title.css('margin-bottom')) );
6447					break;
6448			}
6449
6450			title[ (opts.position === 'top' ? 'prependTo'  : 'appendTo') ](target);
6451		}
6452	};
6453
6454	// jQuery plugin initialization
6455	$.fn.fancybox = function (options) {
6456		var index,
6457			that     = $(this),
6458			selector = this.selector || '',
6459			run      = function(e) {
6460				var what = $(this).blur(), idx = index, relType, relVal;
6461
6462				if (!(e.ctrlKey || e.altKey || e.shiftKey || e.metaKey) && !what.is('.fancybox-wrap')) {
6463					relType = options.groupAttr || 'data-fancybox-group';
6464					relVal  = what.attr(relType);
6465
6466					if (!relVal) {
6467						relType = 'rel';
6468						relVal  = what.get(0)[ relType ];
6469					}
6470
6471					if (relVal && relVal !== '' && relVal !== 'nofollow') {
6472						what = selector.length ? $(selector) : that;
6473						what = what.filter('[' + relType + '="' + relVal + '"]');
6474						idx  = what.index(this);
6475					}
6476
6477					options.index = idx;
6478
6479					// Stop an event from bubbling if everything is fine
6480					if (F.open(what, options) !== false) {
6481						e.preventDefault();
6482					}
6483				}
6484			};
6485
6486		options = options || {};
6487		index   = options.index || 0;
6488
6489		if (!selector || options.live === false) {
6490			that.unbind('click.fb-start').bind('click.fb-start', run);
6491
6492		} else {
6493			D.undelegate(selector, 'click.fb-start').delegate(selector + ":not('.fancybox-item, .fancybox-nav')", 'click.fb-start', run);
6494		}
6495
6496		this.filter('[data-fancybox-start=1]').trigger('click');
6497
6498		return this;
6499	};
6500
6501	// Tests that need a body at doc ready
6502	D.ready(function() {
6503		var w1, w2;
6504
6505		if ( $.scrollbarWidth === undefined ) {
6506			// http://benalman.com/projects/jquery-misc-plugins/#scrollbarwidth
6507			$.scrollbarWidth = function() {
6508				var parent = $('<div style="width:50px;height:50px;overflow:auto"><div/></div>').appendTo('body'),
6509					child  = parent.children(),
6510					width  = child.innerWidth() - child.height( 99 ).innerWidth();
6511
6512				parent.remove();
6513
6514				return width;
6515			};
6516		}
6517
6518		if ( $.support.fixedPosition === undefined ) {
6519			$.support.fixedPosition = (function() {
6520				var elem  = $('<div style="position:fixed;top:20px;"></div>').appendTo('body'),
6521					fixed = ( elem[0].offsetTop === 20 || elem[0].offsetTop === 15 );
6522
6523				elem.remove();
6524
6525				return fixed;
6526			}());
6527		}
6528
6529		$.extend(F.defaults, {
6530			scrollbarWidth : $.scrollbarWidth(),
6531			fixed  : $.support.fixedPosition,
6532			parent : $('body')
6533		});
6534
6535		//Get real width of page scroll-bar
6536		w1 = $(window).width();
6537
6538		H.addClass('fancybox-lock-test');
6539
6540		w2 = $(window).width();
6541
6542		H.removeClass('fancybox-lock-test');
6543
6544		$("<style type='text/css'>.fancybox-margin{margin-right:" + (w2 - w1) + "px;}</style>").appendTo("head");
6545	});
6546
6547}(window, document, jQuery));

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.