PageSourceSearch

https://whiskyadvocate.com/files/_cache/5616ff85340054b95713ce254312e70c.js

js whiskyadvocate.com collected 2026-10-02 04:58:36 UTC 1,129,438 bytes, 38,319 lines download raw bytes

vendor: 6,409 bytes, lines 1-189
1function parse_str(str, array) {
2  //       discuss at: http://phpjs.org/functions/parse_str/
3  //      original by: Cagri Ekin
4  //      improved by: Michael White (http://getsprink.com)
5  //      improved by: Jack
6  //      improved by: Brett Zamir (http://brett-zamir.me)
7  //      bugfixed by: Onno Marsman
8  //      bugfixed by: Brett Zamir (http://brett-zamir.me)
9  //      bugfixed by: stag019
10  //      bugfixed by: Brett Zamir (http://brett-zamir.me)
11  //      bugfixed by: MIO_KODUKI (http://mio-koduki.blogspot.com/)
12  // reimplemented by: stag019
13  //         input by: Dreamer
14  //         input by: Zaide (http://zaidesthings.com/)
15  //         input by: David Pesta (http://davidpesta.com/)
16  //         input by: jeicquest
17  //             note: When no argument is specified, will put variables in global scope.
18  //             note: When a particular argument has been passed, and the returned value is different parse_str of PHP. For example, a=b=c&d====c
19  //             test: skip
20  //        example 1: var arr = {};
21  //        example 1: parse_str('first=foo&second=bar', arr);
22  //        example 1: $result = arr
23  //        returns 1: { first: 'foo', second: 'bar' }
24  //        example 2: var arr = {};
25  //        example 2: parse_str('str_a=Jack+and+Jill+didn%27t+see+the+well.', arr);
26  //        example 2: $result = arr
27  //        returns 2: { str_a: "Jack and Jill didn't see the well." }
28  //        example 3: var abc = {3:'a'};
29  //        example 3: parse_str('abc[a][b]["c"]=def&abc[q]=t+5');
30  //        returns 3: {"3":"a","a":{"b":{"c":"def"}},"q":"t 5"}
31
32  var strArr = String(str)
33    .replace(/^&/, '')
34    .replace(/&$/, '')
35    .split('&'),
36    sal = strArr.length,
37    i, j, ct, p, lastObj, obj, lastIter, undef, chr, tmp, key, value,
38    postLeftBracketPos, keys, keysLen,
39    fixStr = function(str) {
40      return decodeURIComponent(str.replace(/\+/g, '%20'));
41    };
42
43  if (!array) {
44    array = this.window;
45  }
46
47  for (i = 0; i < sal; i++) {
48    tmp = strArr[i].split('=');
49    key = fixStr(tmp[0]);
50    value = (tmp.length < 2) ? '' : fixStr(tmp[1]);
51
52    while (key.charAt(0) === ' ') {
53      key = key.slice(1);
54    }
55    if (key.indexOf('\x00') > -1) {
56      key = key.slice(0, key.indexOf('\x00'));
57    }
58    if (key && key.charAt(0) !== '[') {
59      keys = [];
60      postLeftBracketPos = 0;
61      for (j = 0; j < key.length; j++) {
62        if (key.charAt(j) === '[' && !postLeftBracketPos) {
63          postLeftBracketPos = j + 1;
64        } else if (key.charAt(j) === ']') {
65          if (postLeftBracketPos) {
66            if (!keys.length) {
67              keys.push(key.slice(0, postLeftBracketPos - 1));
68            }
69            keys.push(key.substr(postLeftBracketPos, j - postLeftBracketPos));
70            postLeftBracketPos = 0;
71            if (key.charAt(j + 1) !== '[') {
72              break;
73            }
74          }
75        }
76      }
77      if (!keys.length) {
78        keys = [key];
79      }
80      for (j = 0; j < keys[0].length; j++) {
81        chr = keys[0].charAt(j);
82        if (chr === ' ' || chr === '.' || chr === '[') {
83          keys[0] = keys[0].substr(0, j) + '_' + keys[0].substr(j + 1);
84        }
85        if (chr === '[') {
86          break;
87        }
88      }
89
90      obj = array;
91      for (j = 0, keysLen = keys.length; j < keysLen; j++) {
92        key = keys[j].replace(/^['"]/, '')
93          .replace(/['"]$/, '');
94        lastIter = j !== keys.length - 1;
95        lastObj = obj;
96        if ((key !== '' && key !== ' ') || j === 0) {
97          if (obj[key] === undef) {
98            obj[key] = {};
99          }
100          obj = obj[key];
101        } else { // To insert new dimension
102          ct = -1;
103          for (p in obj) {
104            if (obj.hasOwnProperty(p)) {
105              if (+p > ct && p.match(/^\d+$/g)) {
106                ct = +p;
107              }
108            }
109          }
110          key = ct + 1;
111        }
112      }
113      lastObj[key] = value;
114    }
115  }
116}
117
118function http_build_query(formdata, numeric_prefix, arg_separator) {
119  //  discuss at: http://phpjs.org/functions/http_build_query/
120  // original by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
121  // improved by: Legaev Andrey
122  // improved by: Michael White (http://getsprink.com)
123  // improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
124  // improved by: Brett Zamir (http://brett-zamir.me)
125  //  revised by: stag019
126  //    input by: Dreamer
127  // bugfixed by: Brett Zamir (http://brett-zamir.me)
128  // bugfixed by: MIO_KODUKI (http://mio-koduki.blogspot.com/)
129  //        note: If the value is null, key and value are skipped in the http_build_query of PHP while in phpjs they are not.
130  //  depends on: urlencode
131  //   example 1: http_build_query({foo: 'bar', php: 'hypertext processor', baz: 'boom', cow: 'milk'}, '', '&amp;');
132  //   returns 1: 'foo=bar&amp;php=hypertext+processor&amp;baz=boom&amp;cow=milk'
133  //   example 2: http_build_query({'php': 'hypertext processor', 0: 'foo', 1: 'bar', 2: 'baz', 3: 'boom', 'cow': 'milk'}, 'myvar_');
134  //   returns 2: 'myvar_0=foo&myvar_1=bar&myvar_2=baz&myvar_3=boom&php=hypertext+processor&cow=milk'
135
136  var value, key, tmp = [],
137    that = this;
138
139  var _http_build_query_helper = function(key, val, arg_separator) {
140    var k, tmp = [];
141    if (val === true) {
142      val = '1';
143    } else if (val === false) {
144      val = '0';
145    }
146    if (val != null) {
147      if (typeof val === 'object') {
148        for (k in val) {
149          if (val[k] != null) {
150            tmp.push(_http_build_query_helper(key + '[' + k + ']', val[k], arg_separator));
151          }
152        }
153        return tmp.join(arg_separator);
154      } else if (typeof val !== 'function') {
155        return that.urlencode(key) + '=' + that.urlencode(val);
156      } else {
157        throw new Error('There was an error processing for http_build_query().');
158      }
159    } else {
160      return '';
161    }
162  };
163
164  if (!arg_separator) {
165    arg_separator = '&';
166  }
167  for (key in formdata) {
168    value = formdata[key];
169    if (numeric_prefix && !isNaN(key)) {
170      key = String(numeric_prefix) + key;
171    }
172    var query = _http_build_query_helper(key, value, arg_separator);
173    if (query !== '') {
174      tmp.push(query);
175    }
176  }
177
178  return tmp.join(arg_separator);
179}
180/*
181    http://www.JSON.org/json2.js
182    2008-07-15
183
184    Public Domain.
185
186    NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK.
187
188    See http://www.JSON.org/js.html
189
190    This file creates a global JSON object containing two methods: stringify
191    and parse.
192
193        JSON.stringify(value, replacer, space)
194            value       any JavaScript value, usually an object or array.
195
196            replacer    an optional parameter that determines how object
197                        values are stringified for objects. It can be a
198                        function or an array.
199
200            space       an optional parameter that specifies the indentation
201                        of nested structures. If it is omitted, the text will
202                        be packed without extra whitespace. If it is a number,
203                        it will specify the number of spaces to indent at each
204                        level. If it is a string (such as '\t' or '&nbsp;'),
205                        it contains the characters used to indent at each level.
206
207            This method produces a JSON text from a JavaScript value.
208
209            When an object value is found, if the object contains a toJSON
210            method, its toJSON method will be called and the result will be
211            stringified. A toJSON method does not serialize: it returns the
212            value represented by the name/value pair that should be serialized,
213            or undefined if nothing should be serialized. The toJSON method
214            will be passed the key associated with the value, and this will be
215            bound to the object holding the key.
216
217            For example, this would serialize Dates as ISO strings.
218
219                Date.prototype.toJSON = function (key) {
220                    function f(n) {
221                        // Format integers to have at least two digits.
222                        return n < 10 ? '0' + n : n;
223                    }
224
225                    return this.getUTCFullYear()   + '-' +
226                         f(this.getUTCMonth() + 1) + '-' +
227                         f(this.getUTCDate())      + 'T' +
228                         f(this.getUTCHours())     + ':' +
229                         f(this.getUTCMinutes())   + ':' +
230                         f(this.getUTCSeconds())   + 'Z';
231                };
232
233            You can provide an optional replacer method. It will be passed the
234            key and value of each member, with this bound to the containing
235            object. The value that is returned from your method will be
236            serialized. If your method returns undefined, then the member will
237            be excluded from the serialization.
238
239            If the replacer parameter is an array, then it will be used to
240            select the members to be serialized. It filters the results such
241            that only members with keys listed in the replacer array are
242            stringified.
243
244            Values that do not have JSON representations, such as undefined or
245            functions, will not be serialized. Such values in objects will be
246            dropped; in arrays they will be replaced with null. You can use
247            a replacer function to replace those with JSON values.
248            JSON.stringify(undefined) returns undefined.
249
250            The optional space parameter produces a stringification of the
251            value that is filled with line breaks and indentation to make it
252            easier to read.
253
254            If the space parameter is a non-empty string, then that string will
255            be used for indentation. If the space parameter is a number, then
256            the indentation will be that many spaces.
257
258            Example:
259
260            text = JSON.stringify(['e', {pluribus: 'unum'}]);
261            // text is '["e",{"pluribus":"unum"}]'
262
263
264            text = JSON.stringify(['e', {pluribus: 'unum'}], null, '\t');
265            // text is '[\n\t"e",\n\t{\n\t\t"pluribus": "unum"\n\t}\n]'
266
267            text = JSON.stringify([new Date()], function (key, value) {
268                return this[key] instanceof Date ?
269                    'Date(' + this[key] + ')' : value;
270            });
271            // text is '["Date(---current time---)"]'
272
273
274        JSON.parse(text, reviver)
275            This method parses a JSON text to produce an object or array.
276            It can throw a SyntaxError exception.
277
278            The optional reviver parameter is a function that can filter and
279            transform the results. It receives each of the keys and values,
280            and its return value is used instead of the original value.
281            If it returns what it received, then the structure is not modified.
282            If it returns undefined then the member is deleted.
283
284            Example:
285
286            // Parse the text. Values that look like ISO date strings will
287            // be converted to Date objects.
288
289            myData = JSON.parse(text, function (key, value) {
290                var a;
291                if (typeof value === 'string') {
292                    a =
293/^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2}(?:\.\d*)?)Z$/.exec(value);
294                    if (a) {
295                        return new Date(Date.UTC(+a[1], +a[2] - 1, +a[3], +a[4],
296                            +a[5], +a[6]));
297                    }
298                }
299                return value;
300            });
301
302            myData = JSON.parse('["Date(09/09/2001)"]', function (key, value) {
303                var d;
304                if (typeof value === 'string' &&
305                        value.slice(0, 5) === 'Date(' &&
306                        value.slice(-1) === ')') {
307                    d = new Date(value.slice(5, -1));
308                    if (d) {
309                        return d;
310                    }
311                }
312                return value;
313            });
314
315
316    This is a reference implementation. You are free to copy, modify, or
317    redistribute.
318
319    This code should be minified before deployment.
320    See http://javascript.crockford.com/jsmin.html
321
322    USE YOUR OWN COPY. IT IS EXTREMELY UNWISE TO LOAD CODE FROM SERVERS YOU DO
323    NOT CONTROL.
324*/
325
326/*jslint evil: true */
327
328/*global JSON */
329
330/*members "", "\b", "\t", "\n", "\f", "\r", "\"", JSON, "\\", call,
331    charCodeAt, getUTCDate, getUTCFullYear, getUTCHours, getUTCMinutes,
332    getUTCMonth, getUTCSeconds, hasOwnProperty, join, lastIndex, length,
333    parse, propertyIsEnumerable, prototype, push, replace, slice, stringify,
334    test, toJSON, toString
335*/
336
337if (!this.JSON) {
338
339// Create a JSON object only if one does not already exist. We create the
340// object in a closure to avoid creating global variables.
341
342    JSON = function () {
343
344        function f(n) {
345            // Format integers to have at least two digits.
346            return n < 10 ? '0' + n : n;
347        }
348
349        Date.prototype.toJSON = function (key) {
350
351            return this.getUTCFullYear()   + '-' +
352                 f(this.getUTCMonth() + 1) + '-' +
353                 f(this.getUTCDate())      + 'T' +
354                 f(this.getUTCHours())     + ':' +
355                 f(this.getUTCMinutes())   + ':' +
356                 f(this.getUTCSeconds())   + 'Z';
357        };
358
359        String.prototype.toJSON =
360        Number.prototype.toJSON =
361        Boolean.prototype.toJSON = function (key) {
362            return this.valueOf();
363        };
364
365        var cx = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
366            escapeable = /[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
367            gap,
368            indent,
369            meta = {    // table of character substitutions
370                '\b': '\\b',
371                '\t': '\\t',
372                '\n': '\\n',
373                '\f': '\\f',
374                '\r': '\\r',
375                '"' : '\\"',
376                '\\': '\\\\'
377            },
378            rep;
379
380
381        function quote(string) {
382
383// If the string contains no control characters, no quote characters, and no
384// backslash characters, then we can safely slap some quotes around it.
385// Otherwise we must also replace the offending characters with safe escape
386// sequences.
387
388            escapeable.lastIndex = 0;
389            return escapeable.test(string) ?
390                '"' + string.replace(escapeable, function (a) {
391                    var c = meta[a];
392                    if (typeof c === 'string') {
393                        return c;
394                    }
395                    return '\\u' + ('0000' +
396                            (+(a.charCodeAt(0))).toString(16)).slice(-4);
397                }) + '"' :
398                '"' + string + '"';
399        }
400
401
402        function str(key, holder) {
403
404// Produce a string from holder[key].
405
406            var i,          // The loop counter.
407                k,          // The member key.
408                v,          // The member value.
409                length,
410                mind = gap,
411                partial,
412                value = holder[key];
413
414// If the value has a toJSON method, call it to obtain a replacement value.
415
416            if (value && typeof value === 'object' &&
417                    typeof value.toJSON === 'function') {
418                value = value.toJSON(key);
419            }
420
421// If we were called with a replacer function, then call the replacer to
422// obtain a replacement value.
423
424            if (typeof rep === 'function') {
425                value = rep.call(holder, key, value);
426            }
427
428// What happens next depends on the value's type.
429
430            switch (typeof value) {
431            case 'string':
432                return quote(value);
433
434            case 'number':
435
436// JSON numbers must be finite. Encode non-finite numbers as null.
437
438                return isFinite(value) ? String(value) : 'null';
439
440            case 'boolean':
441            case 'null':
442
443// If the value is a boolean or null, convert it to a string. Note:
444// typeof null does not produce 'null'. The case is included here in
445// the remote chance that this gets fixed someday.
446
447                return String(value);
448
449// If the type is 'object', we might be dealing with an object or an array or
450// null.
451
452            case 'object':
453
454// Due to a specification blunder in ECMAScript, typeof null is 'object',
455// so watch out for that case.
456
457                if (!value) {
458                    return 'null';
vendor: 5,486 bytes, lines 459-610
459                }
460
461// Make an array to hold the partial results of stringifying this object value.
462
463                gap += indent;
464                partial = [];
465
466// If the object has a dontEnum length property, we'll treat it as an array.
467
468                if (typeof value.length === 'number' &&
469                        !(value.propertyIsEnumerable('length'))) {
470
471// The object is an array. Stringify every element. Use null as a placeholder
472// for non-JSON values.
473
474                    length = value.length;
475                    for (i = 0; i < length; i += 1) {
476                        partial[i] = str(i, value) || 'null';
477                    }
478
479// Join all of the elements together, separated with commas, and wrap them in
480// brackets.
481
482                    v = partial.length === 0 ? '[]' :
483                        gap ? '[\n' + gap +
484                                partial.join(',\n' + gap) + '\n' +
485                                    mind + ']' :
486                              '[' + partial.join(',') + ']';
487                    gap = mind;
488                    return v;
489                }
490
491// If the replacer is an array, use it to select the members to be stringified.
492
493                if (rep && typeof rep === 'object') {
494                    length = rep.length;
495                    for (i = 0; i < length; i += 1) {
496                        k = rep[i];
497                        if (typeof k === 'string') {
498                            v = str(k, value);
499                            if (v) {
500                                partial.push(quote(k) + (gap ? ': ' : ':') + v);
501                            }
502                        }
503                    }
504                } else {
505
506// Otherwise, iterate through all of the keys in the object.
507
508                    for (k in value) {
509                        if (Object.hasOwnProperty.call(value, k)) {
510                            v = str(k, value);
511                            if (v) {
512                                partial.push(quote(k) + (gap ? ': ' : ':') + v);
513                            }
514                        }
515                    }
516                }
517
518// Join all of the member texts together, separated with commas,
519// and wrap them in braces.
520
521                v = partial.length === 0 ? '{}' :
522                    gap ? '{\n' + gap + partial.join(',\n' + gap) + '\n' +
523                            mind + '}' : '{' + partial.join(',') + '}';
524                gap = mind;
525                return v;
526            }
527        }
528
529// Return the JSON object containing the stringify and parse methods.
530
531        return {
532            stringify: function (value, replacer, space) {
533
534// The stringify method takes a value and an optional replacer, and an optional
535// space parameter, and returns a JSON text. The replacer can be a function
536// that can replace values, or an array of strings that will select the keys.
537// A default replacer method can be provided. Use of the space parameter can
538// produce text that is more easily readable.
539
540                var i;
541                gap = '';
542                indent = '';
543
544// If the space parameter is a number, make an indent string containing that
545// many spaces.
546
547                if (typeof space === 'number') {
548                    for (i = 0; i < space; i += 1) {
549                        indent += ' ';
550                    }
551
552// If the space parameter is a string, it will be used as the indent string.
553
554                } else if (typeof space === 'string') {
555                    indent = space;
556                }
557
558// If there is a replacer, it must be a function or an array.
559// Otherwise, throw an error.
560
561                rep = replacer;
562                if (replacer && typeof replacer !== 'function' &&
563                        (typeof replacer !== 'object' ||
564                         typeof replacer.length !== 'number')) {
565                    throw new Error('JSON.stringify');
566                }
567
568// Make a fake root object containing our value under the key of ''.
569// Return the result of stringifying the value.
570
571                return str('', {'': value});
572            },
573
574
575            parse: function (text, reviver) {
576
577// The parse method takes a text and an optional reviver function, and returns
578// a JavaScript value if the text is a valid JSON text.
579
580                var j;
581
582                function walk(holder, key) {
583
584// The walk method is used to recursively walk the resulting structure so
585// that modifications can be made.
586
587                    var k, v, value = holder[key];
588                    if (value && typeof value === 'object') {
589                        for (k in value) {
590                            if (Object.hasOwnProperty.call(value, k)) {
591                                v = walk(value, k);
592                                if (v !== undefined) {
593                                    value[k] = v;
594                                } else {
595                                    delete value[k];
596                                }
597                            }
598                        }
599                    }
600                    return reviver.call(holder, key, value);
601                }
602
603
604// Parsing happens in four stages. In the first stage, we replace certain
605// Unicode characters with escape sequences. JavaScript handles many characters
606// incorrectly, either silently deleting them, or treating them as line endings.
607
608                cx.lastIndex = 0;
609                if (cx.test(text)) {
610                    text = text.replace(cx, function (a) {
611                        return '\\u' + ('0000' +
612                                (+(a.charCodeAt(0))).toString(16)).slice(-4);
613                    });
614                }
615
616// In the second stage, we run the text against regular expressions that look
617// for non-JSON patterns. We are especially concerned with '()' and 'new'
618// because they can cause invocation, and '=' because it can cause mutation.
619// But just to be safe, we want to reject all unexpected forms.
620
621// We split the second stage into 4 regexp operations in order to work around
622// crippling inefficiencies in IE's and Safari's regexp engines. First we
623// replace the JSON backslash pairs with '@' (a non-JSON character). Second, we
624// replace all simple value tokens with ']' characters. Third, we delete all
625// open brackets that follow a colon or comma or that begin the text. Finally,
626// we look to see that the remaining characters are only whitespace or ']' or
627// ',' or ':' or '{' or '}'. If that is so, then the text is safe for eval.
628
629                if (/^[\],:{}\s]*$/.
630test(text.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, '@').
631replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']').
632replace(/(?:^|:|,)(?:\s*\[)+/g, ''))) {
633
634// In the third stage we use the eval function to compile the text into a
635// JavaScript structure. The '{' operator is subject to a syntactic ambiguity
636// in JavaScript: it can begin a block or an object literal. We wrap the text
637// in parens to eliminate the ambiguity.
638
639                    j = eval('(' + text + ')');
640
641// In the optional fourth stage, we recursively walk the new structure, passing
642// each name/value pair to a reviver function for possible transformation.
643
644                    return typeof reviver === 'function' ?
645                        walk({'': j}, '') : j;
646                }
647
648// If the text is not JSON parseable, then a SyntaxError is thrown.
649
650                throw new SyntaxError('JSON.parse');
651            }
652        };
653    }();
654}
655
656/*!
657 * jQuery JavaScript Library v1.7.1
658 * http://jquery.com/
659 *
660 * Copyright 2011, John Resig
661 * Dual licensed under the MIT or GPL Version 2 licenses.
662 * http://jquery.org/license
663 *
664 * Includes Sizzle.js
665 * http://sizzlejs.com/
666 * Copyright 2011, The Dojo Foundation
667 * Released under the MIT, BSD, and GPL Licenses.
668 *
669 * Date: Mon Nov 21 21:11:03 2011 -0500
670 */
671(function( window, undefined ) {
672
673// Use the correct document accordingly with window argument (sandbox)
674var document = window.document,
675	navigator = window.navigator,
676	location = window.location;
677var jQuery = (function() {
678
679// Define a local copy of jQuery
680var jQuery = function( selector, context ) {
681		// The jQuery object is actually just the init constructor 'enhanced'
682		return new jQuery.fn.init( selector, context, rootjQuery );
683	},
684
685	// Map over jQuery in case of overwrite
686	_jQuery = window.jQuery,
687
688	// Map over the $ in case of overwrite
689	_$ = window.$,
690
691	// A central reference to the root jQuery(document)
692	rootjQuery,
693
694	// A simple way to check for HTML strings or ID strings
695	// Prioritize #id over <tag> to avoid XSS via location.hash (#9521)
696	quickExpr = /^(?:[^#<]*(<[\w\W]+>)[^>]*$|#([\w\-]*)$)/,
697
698	// Check if a string has a non-whitespace character in it
699	rnotwhite = /\S/,
700
701	// Used for trimming whitespace
702	trimLeft = /^\s+/,
703	trimRight = /\s+$/,
704
705	// Match a standalone tag
706	rsingleTag = /^<(\w+)\s*\/?>(?:<\/\1>)?$/,
707
708	// JSON RegExp
709	rvalidchars = /^[\],:{}\s]*$/,
710	rvalidescape = /\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,
711	rvalidtokens = /"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,
712	rvalidbraces = /(?:^|:|,)(?:\s*\[)+/g,
713
714	// Useragent RegExp
715	rwebkit = /(webkit)[ \/]([\w.]+)/,
716	ropera = /(opera)(?:.*version)?[ \/]([\w.]+)/,
717	rmsie = /(msie) ([\w.]+)/,
718	rmozilla = /(mozilla)(?:.*? rv:([\w.]+))?/,
719
720	// Matches dashed string for camelizing
721	rdashAlpha = /-([a-z]|[0-9])/ig,
722	rmsPrefix = /^-ms-/,
723
724	// Used by jQuery.camelCase as callback to replace()
725	fcamelCase = function( all, letter ) {
726		return ( letter + "" ).toUpperCase();
727	},
728
729	// Keep a UserAgent string for use with jQuery.browser
730	userAgent = navigator.userAgent,
731
732	// For matching the engine and version of the browser
733	browserMatch,
734
735	// The deferred used on DOM ready
736	readyList,
737
738	// The ready event handler
739	DOMContentLoaded,
740
741	// Save a reference to some core methods
742	toString = Object.prototype.toString,
743	hasOwn = Object.prototype.hasOwnProperty,
744	push = Array.prototype.push,
745	slice = Array.prototype.slice,
746	trim = String.prototype.trim,
747	indexOf = Array.prototype.indexOf,
748
749	// [[Class]] -> type pairs
750	class2type = {};
751
752jQuery.fn = jQuery.prototype = {
753	constructor: jQuery,
754	init: function( selector, context, rootjQuery ) {
755		var match, elem, ret, doc;
756
757		// Handle $(""), $(null), or $(undefined)
758		if ( !selector ) {
759			return this;
760		}
761
762		// Handle $(DOMElement)
763		if ( selector.nodeType ) {
764			this.context = this[0] = selector;
765			this.length = 1;
766			return this;
767		}
768
769		// The body element only exists once, optimize finding it
770		if ( selector === "body" && !context && document.body ) {
771			this.context = document;
772			this[0] = document.body;
773			this.selector = selector;
774			this.length = 1;
775			return this;
776		}
777
778		// Handle HTML strings
779		if ( typeof selector === "string" ) {
780			// Are we dealing with HTML string or an ID?
781			if ( selector.charAt(0) === "<" && selector.charAt( selector.length - 1 ) === ">" && selector.length >= 3 ) {
782				// Assume that strings that start and end with <> are HTML and skip the regex check
783				match = [ null, selector, null ];
784
785			} else {
786				match = quickExpr.exec( selector );
787			}
788
789			// Verify a match, and that no context was specified for #id
790			if ( match && (match[1] || !context) ) {
791
792				// HANDLE: $(html) -> $(array)
793				if ( match[1] ) {
794					context = context instanceof jQuery ? context[0] : context;
795					doc = ( context ? context.ownerDocument || context : document );
796
vendor: 4,597 bytes, lines 797-984
797					// If a single string is passed in and it's a single tag
798					// just do a createElement and skip the rest
799					ret = rsingleTag.exec( selector );
800
801					if ( ret ) {
802						if ( jQuery.isPlainObject( context ) ) {
803							selector = [ document.createElement( ret[1] ) ];
804							jQuery.fn.attr.call( selector, context, true );
805
806						} else {
807							selector = [ doc.createElement( ret[1] ) ];
808						}
809
810					} else {
811						ret = jQuery.buildFragment( [ match[1] ], [ doc ] );
812						selector = ( ret.cacheable ? jQuery.clone(ret.fragment) : ret.fragment ).childNodes;
813					}
814
815					return jQuery.merge( this, selector );
816
817				// HANDLE: $("#id")
818				} else {
819					elem = document.getElementById( match[2] );
820
821					// Check parentNode to catch when Blackberry 4.6 returns
822					// nodes that are no longer in the document #6963
823					if ( elem && elem.parentNode ) {
824						// Handle the case where IE and Opera return items
825						// by name instead of ID
826						if ( elem.id !== match[2] ) {
827							return rootjQuery.find( selector );
828						}
829
830						// Otherwise, we inject the element directly into the jQuery object
831						this.length = 1;
832						this[0] = elem;
833					}
834
835					this.context = document;
836					this.selector = selector;
837					return this;
838				}
839
840			// HANDLE: $(expr, $(...))
841			} else if ( !context || context.jquery ) {
842				return ( context || rootjQuery ).find( selector );
843
844			// HANDLE: $(expr, context)
845			// (which is just equivalent to: $(context).find(expr)
846			} else {
847				return this.constructor( context ).find( selector );
848			}
849
850		// HANDLE: $(function)
851		// Shortcut for document ready
852		} else if ( jQuery.isFunction( selector ) ) {
853			return rootjQuery.ready( selector );
854		}
855
856		if ( selector.selector !== undefined ) {
857			this.selector = selector.selector;
858			this.context = selector.context;
859		}
860
861		return jQuery.makeArray( selector, this );
862	},
863
864	// Start with an empty selector
865	selector: "",
866
867	// The current version of jQuery being used
868	jquery: "1.7.1",
869
870	// The default length of a jQuery object is 0
871	length: 0,
872
873	// The number of elements contained in the matched element set
874	size: function() {
875		return this.length;
876	},
877
878	toArray: function() {
879		return slice.call( this, 0 );
880	},
881
882	// Get the Nth element in the matched element set OR
883	// Get the whole matched element set as a clean array
884	get: function( num ) {
885		return num == null ?
886
887			// Return a 'clean' array
888			this.toArray() :
889
890			// Return just the object
891			( num < 0 ? this[ this.length + num ] : this[ num ] );
892	},
893
894	// Take an array of elements and push it onto the stack
895	// (returning the new matched element set)
896	pushStack: function( elems, name, selector ) {
897		// Build a new jQuery matched element set
898		var ret = this.constructor();
899
900		if ( jQuery.isArray( elems ) ) {
901			push.apply( ret, elems );
902
903		} else {
904			jQuery.merge( ret, elems );
905		}
906
907		// Add the old object onto the stack (as a reference)
908		ret.prevObject = this;
909
910		ret.context = this.context;
911
912		if ( name === "find" ) {
913			ret.selector = this.selector + ( this.selector ? " " : "" ) + selector;
914		} else if ( name ) {
915			ret.selector = this.selector + "." + name + "(" + selector + ")";
916		}
917
918		// Return the newly-formed element set
919		return ret;
920	},
921
922	// Execute a callback for every element in the matched set.
923	// (You can seed the arguments with an array of args, but this is
924	// only used internally.)
925	each: function( callback, args ) {
926		return jQuery.each( this, callback, args );
927	},
928
929	ready: function( fn ) {
930		// Attach the listeners
931		jQuery.bindReady();
932
933		// Add the callback
934		readyList.add( fn );
935
936		return this;
937	},
938
939	eq: function( i ) {
940		i = +i;
941		return i === -1 ?
942			this.slice( i ) :
943			this.slice( i, i + 1 );
944	},
945
946	first: function() {
947		return this.eq( 0 );
948	},
949
950	last: function() {
951		return this.eq( -1 );
952	},
953
954	slice: function() {
955		return this.pushStack( slice.apply( this, arguments ),
956			"slice", slice.call(arguments).join(",") );
957	},
958
959	map: function( callback ) {
960		return this.pushStack( jQuery.map(this, function( elem, i ) {
961			return callback.call( elem, i, elem );
962		}));
963	},
964
965	end: function() {
966		return this.prevObject || this.constructor(null);
967	},
968
969	// For internal use only.
970	// Behaves like an Array's method, not like a jQuery method.
971	push: push,
972	sort: [].sort,
973	splice: [].splice
974};
975
976// Give the init function the jQuery prototype for later instantiation
977jQuery.fn.init.prototype = jQuery.fn;
978
979jQuery.extend = jQuery.fn.extend = function() {
980	var options, name, src, copy, copyIsArray, clone,
981		target = arguments[0] || {},
982		i = 1,
983		length = arguments.length,
984		deep = false;
985
986	// Handle a deep copy situation
987	if ( typeof target === "boolean" ) {
988		deep = target;
989		target = arguments[1] || {};
990		// skip the boolean and the target
991		i = 2;
992	}
993
994	// Handle case when target is a string or something (possible in deep copy)
995	if ( typeof target !== "object" && !jQuery.isFunction(target) ) {
996		target = {};
997	}
998
999	// extend jQuery itself if only one argument is passed
1000	if ( length === i ) {
1001		target = this;
1002		--i;
1003	}
1004
1005	for ( ; i < length; i++ ) {
1006		// Only deal with non-null/undefined values
1007		if ( (options = arguments[ i ]) != null ) {
1008			// Extend the base object
1009			for ( name in options ) {
1010				src = target[ name ];
1011				copy = options[ name ];
1012
1013				// Prevent never-ending loop
1014				if ( target === copy ) {
1015					continue;
1016				}
1017
1018				// Recurse if we're merging plain objects or arrays
1019				if ( deep && copy && ( jQuery.isPlainObject(copy) || (copyIsArray = jQuery.isArray(copy)) ) ) {
1020					if ( copyIsArray ) {
1021						copyIsArray = false;
1022						clone = src && jQuery.isArray(src) ? src : [];
1023
1024					} else {
1025						clone = src && jQuery.isPlainObject(src) ? src : {};
1026					}
1027
1028					// Never move original objects, clone them
1029					target[ name ] = jQuery.extend( deep, clone, copy );
1030
1031				// Don't bring in undefined values
1032				} else if ( copy !== undefined ) {
1033					target[ name ] = copy;
1034				}
1035			}
1036		}
1037	}
1038
1039	// Return the modified object
1040	return target;
1041};
1042
1043jQuery.extend({
1044	noConflict: function( deep ) {
1045		if ( window.$ === jQuery ) {
1046			window.$ = _$;
1047		}
1048
1049		if ( deep && window.jQuery === jQuery ) {
1050			window.jQuery = _jQuery;
1051		}
1052
1053		return jQuery;
1054	},
1055
1056	// Is the DOM ready to be used? Set to true once it occurs.
1057	isReady: false,
1058
1059	// A counter to track how many items to wait for before
1060	// the ready event fires. See #6781
1061	readyWait: 1,
1062
1063	// Hold (or release) the ready event
1064	holdReady: function( hold ) {
1065		if ( hold ) {
1066			jQuery.readyWait++;
1067		} else {
1068			jQuery.ready( true );
1069		}
1070	},
1071
1072	// Handle when the DOM is ready
1073	ready: function( wait ) {
1074		// Either a released hold or an DOMready/load event and not yet ready
1075		if ( (wait === true && !--jQuery.readyWait) || (wait !== true && !jQuery.isReady) ) {
1076			// Make sure body exists, at least, in case IE gets a little overzealous (ticket #5443).
1077			if ( !document.body ) {
1078				return setTimeout( jQuery.ready, 1 );
1079			}
1080
1081			// Remember that the DOM is ready
1082			jQuery.isReady = true;
1083
1084			// If a normal DOM Ready event fired, decrement, and wait if need be
1085			if ( wait !== true && --jQuery.readyWait > 0 ) {
1086				return;
1087			}
1088
1089			// If there are functions bound, to execute
1090			readyList.fireWith( document, [ jQuery ] );
1091
1092			// Trigger any bound ready events
1093			if ( jQuery.fn.trigger ) {
1094				jQuery( document ).trigger( "ready" ).off( "ready" );
1095			}
1096		}
1097	},
1098
1099	bindReady: function() {
1100		if ( readyList ) {
1101			return;
1102		}
1103
1104		readyList = jQuery.Callbacks( "once memory" );
1105
1106		// Catch cases where $(document).ready() is called after the
1107		// browser event has already occurred.
1108		if ( document.readyState === "complete" ) {
1109			// Handle it asynchronously to allow scripts the opportunity to delay ready
1110			return setTimeout( jQuery.ready, 1 );
1111		}
1112
1113		// Mozilla, Opera and webkit nightlies currently support this event
1114		if ( document.addEventListener ) {
1115			// Use the handy event callback
1116			document.addEventListener( "DOMContentLoaded", DOMContentLoaded, false );
1117
1118			// A fallback to window.onload, that will always work
1119			window.addEventListener( "load", jQuery.ready, false );
1120
1121		// If IE event model is used
1122		} else if ( document.attachEvent ) {
1123			// ensure firing before onload,
1124			// maybe late but safe also for iframes
1125			document.attachEvent( "onreadystatechange", DOMContentLoaded );
1126
1127			// A fallback to window.onload, that will always work
1128			window.attachEvent( "onload", jQuery.ready );
1129
1130			// If IE and not a frame
1131			// continually check to see if the document is ready
1132			var toplevel = false;
1133
1134			try {
1135				toplevel = window.frameElement == null;
1136			} catch(e) {}
1137
1138			if ( document.documentElement.doScroll && toplevel ) {
1139				doScrollCheck();
1140			}
1141		}
1142	},
1143
1144	// See test/unit/core.js for details concerning isFunction.
1145	// Since version 1.3, DOM methods and functions like alert
1146	// aren't supported. They return false on IE (#2968).
1147	isFunction: function( obj ) {
1148		return jQuery.type(obj) === "function";
1149	},
1150
1151	isArray: Array.isArray || function( obj ) {
1152		return jQuery.type(obj) === "array";
1153	},
1154
1155	// A crude way of determining if an object is a window
1156	isWindow: function( obj ) {
1157		return obj && typeof obj === "object" && "setInterval" in obj;
1158	},
1159
1160	isNumeric: function( obj ) {
1161		return !isNaN( parseFloat(obj) ) && isFinite( obj );
1162	},
1163
1164	type: function( obj ) {
1165		return obj == null ?
1166			String( obj ) :
1167			class2type[ toString.call(obj) ] || "object";
1168	},
1169
1170	isPlainObject: function( obj ) {
1171		// Must be an Object.
1172		// Because of IE, we also have to check the presence of the constructor property.
1173		// Make sure that DOM nodes and window objects don't pass through, as well
1174		if ( !obj || jQuery.type(obj) !== "object" || obj.nodeType || jQuery.isWindow( obj ) ) {
1175			return false;
vendor: 2,673 bytes, lines 1176-1277
1176		}
1177
1178		try {
1179			// Not own constructor property must be Object
1180			if ( obj.constructor &&
1181				!hasOwn.call(obj, "constructor") &&
1182				!hasOwn.call(obj.constructor.prototype, "isPrototypeOf") ) {
1183				return false;
1184			}
1185		} catch ( e ) {
1186			// IE8,9 Will throw exceptions on certain host objects #9897
1187			return false;
1188		}
1189
1190		// Own properties are enumerated firstly, so to speed up,
1191		// if last one is own, then all properties are own.
1192
1193		var key;
1194		for ( key in obj ) {}
1195
1196		return key === undefined || hasOwn.call( obj, key );
1197	},
1198
1199	isEmptyObject: function( obj ) {
1200		for ( var name in obj ) {
1201			return false;
1202		}
1203		return true;
1204	},
1205
1206	error: function( msg ) {
1207		throw new Error( msg );
1208	},
1209
1210	parseJSON: function( data ) {
1211		if ( typeof data !== "string" || !data ) {
1212			return null;
1213		}
1214
1215		// Make sure leading/trailing whitespace is removed (IE can't handle it)
1216		data = jQuery.trim( data );
1217
1218		// Attempt to parse using the native JSON parser first
1219		if ( window.JSON && window.JSON.parse ) {
1220			return window.JSON.parse( data );
1221		}
1222
1223		// Make sure the incoming data is actual JSON
1224		// Logic borrowed from http://json.org/json2.js
1225		if ( rvalidchars.test( data.replace( rvalidescape, "@" )
1226			.replace( rvalidtokens, "]" )
1227			.replace( rvalidbraces, "")) ) {
1228
1229			return ( new Function( "return " + data ) )();
1230
1231		}
1232		jQuery.error( "Invalid JSON: " + data );
1233	},
1234
1235	// Cross-browser xml parsing
1236	parseXML: function( data ) {
1237		var xml, tmp;
1238		try {
1239			if ( window.DOMParser ) { // Standard
1240				tmp = new DOMParser();
1241				xml = tmp.parseFromString( data , "text/xml" );
1242			} else { // IE
1243				xml = new ActiveXObject( "Microsoft.XMLDOM" );
1244				xml.async = "false";
1245				xml.loadXML( data );
1246			}
1247		} catch( e ) {
1248			xml = undefined;
1249		}
1250		if ( !xml || !xml.documentElement || xml.getElementsByTagName( "parsererror" ).length ) {
1251			jQuery.error( "Invalid XML: " + data );
1252		}
1253		return xml;
1254	},
1255
1256	noop: function() {},
1257
1258	// Evaluates a script in a global context
1259	// Workarounds based on findings by Jim Driscoll
1260	// http://weblogs.java.net/blog/driscoll/archive/2009/09/08/eval-javascript-global-context
1261	globalEval: function( data ) {
1262		if ( data && rnotwhite.test( data ) ) {
1263			// We use execScript on Internet Explorer
1264			// We use an anonymous function so that context is window
1265			// rather than jQuery in Firefox
1266			( window.execScript || function( data ) {
1267				window[ "eval" ].call( window, data );
1268			} )( data );
1269		}
1270	},
1271
1272	// Convert dashed to camelCase; used by the css and data modules
1273	// Microsoft forgot to hump their vendor prefix (#9572)
1274	camelCase: function( string ) {
1275		return string.replace( rmsPrefix, "ms-" ).replace( rdashAlpha, fcamelCase );
1276	},
1277
1278	nodeName: function( elem, name ) {
1279		return elem.nodeName && elem.nodeName.toUpperCase() === name.toUpperCase();
1280	},
1281
1282	// args is for internal usage only
1283	each: function( object, callback, args ) {
1284		var name, i = 0,
1285			length = object.length,
1286			isObj = length === undefined || jQuery.isFunction( object );
1287
1288		if ( args ) {
1289			if ( isObj ) {
1290				for ( name in object ) {
1291					if ( callback.apply( object[ name ], args ) === false ) {
1292						break;
1293					}
1294				}
1295			} else {
1296				for ( ; i < length; ) {
1297					if ( callback.apply( object[ i++ ], args ) === false ) {
1298						break;
1299					}
1300				}
1301			}
1302
1303		// A special, fast, case for the most common use of each
1304		} else {
1305			if ( isObj ) {
1306				for ( name in object ) {
1307					if ( callback.call( object[ name ], name, object[ name ] ) === false ) {
1308						break;
1309					}
1310				}
1311			} else {
1312				for ( ; i < length; ) {
1313					if ( callback.call( object[ i ], i, object[ i++ ] ) === false ) {
1314						break;
1315					}
1316				}
1317			}
1318		}
1319
1320		return object;
1321	},
1322
1323	// Use native String.trim function wherever possible
1324	trim: trim ?
1325		function( text ) {
1326			return text == null ?
1327				"" :
1328				trim.call( text );
1329		} :
1330
1331		// Otherwise use our own trimming functionality
1332		function( text ) {
1333			return text == null ?
1334				"" :
1335				text.toString().replace( trimLeft, "" ).replace( trimRight, "" );
1336		},
1337
1338	// results is for internal usage only
1339	makeArray: function( array, results ) {
1340		var ret = results || [];
1341
1342		if ( array != null ) {
1343			// The window, strings (and functions) also have 'length'
1344			// Tweaked logic slightly to handle Blackberry 4.7 RegExp issues #6930
1345			var type = jQuery.type( array );
1346
1347			if ( array.length == null || type === "string" || type === "function" || type === "regexp" || jQuery.isWindow( array ) ) {
1348				push.call( ret, array );
1349			} else {
1350				jQuery.merge( ret, array );
1351			}
1352		}
1353
1354		return ret;
1355	},
1356
1357	inArray: function( elem, array, i ) {
1358		var len;
1359
1360		if ( array ) {
1361			if ( indexOf ) {
1362				return indexOf.call( array, elem, i );
1363			}
1364
1365			len = array.length;
1366			i = i ? i < 0 ? Math.max( 0, len + i ) : i : 0;
1367
1368			for ( ; i < len; i++ ) {
1369				// Skip accessing in sparse arrays
1370				if ( i in array && array[ i ] === elem ) {
1371					return i;
1372				}
1373			}
1374		}
1375
1376		return -1;
1377	},
1378
1379	merge: function( first, second ) {
1380		var i = first.length,
1381			j = 0;
1382
1383		if ( typeof second.length === "number" ) {
1384			for ( var l = second.length; j < l; j++ ) {
1385				first[ i++ ] = second[ j ];
1386			}
1387
1388		} else {
1389			while ( second[j] !== undefined ) {
1390				first[ i++ ] = second[ j++ ];
1391			}
1392		}
1393
1394		first.length = i;
1395
1396		return first;
1397	},
1398
1399	grep: function( elems, callback, inv ) {
1400		var ret = [], retVal;
1401		inv = !!inv;
1402
1403		// Go through the array, only saving the items
1404		// that pass the validator function
1405		for ( var i = 0, length = elems.length; i < length; i++ ) {
1406			retVal = !!callback( elems[ i ], i );
1407			if ( inv !== retVal ) {
1408				ret.push( elems[ i ] );
1409			}
1410		}
1411
1412		return ret;
1413	},
1414
1415	// arg is for internal usage only
1416	map: function( elems, callback, arg ) {
1417		var value, key, ret = [],
1418			i = 0,
1419			length = elems.length,
1420			// jquery objects are treated as arrays
1421			isArray = elems instanceof jQuery || length !== undefined && typeof length === "number" && ( ( length > 0 && elems[ 0 ] && elems[ length -1 ] ) || length === 0 || jQuery.isArray( elems ) ) ;
1422
1423		// Go through the array, translating each of the items to their
1424		if ( isArray ) {
1425			for ( ; i < length; i++ ) {
1426				value = callback( elems[ i ], i, arg );
1427
1428				if ( value != null ) {
1429					ret[ ret.length ] = value;
1430				}
1431			}
1432
1433		// Go through every key on the object,
1434		} else {
1435			for ( key in elems ) {
1436				value = callback( elems[ key ], key, arg );
1437
1438				if ( value != null ) {
1439					ret[ ret.length ] = value;
1440				}
1441			}
1442		}
1443
1444		// Flatten any nested arrays
1445		return ret.concat.apply( [], ret );
1446	},
1447
1448	// A global GUID counter for objects
1449	guid: 1,
1450
1451	// Bind a function to a context, optionally partially applying any
1452	// arguments.
1453	proxy: function( fn, context ) {
1454		if ( typeof context === "string" ) {
1455			var tmp = fn[ context ];
1456			context = fn;
1457			fn = tmp;
1458		}
1459
1460		// Quick check to determine if target is callable, in the spec
1461		// this throws a TypeError, but we will just return undefined.
1462		if ( !jQuery.isFunction( fn ) ) {
1463			return undefined;
1464		}
1465
1466		// Simulated bind
1467		var args = slice.call( arguments, 2 ),
1468			proxy = function() {
1469				return fn.apply( context, args.concat( slice.call( arguments ) ) );
1470			};
1471
1472		// Set the guid of unique handler to the same of original handler, so it can be removed
1473		proxy.guid = fn.guid = fn.guid || proxy.guid || jQuery.guid++;
1474
1475		return proxy;
1476	},
1477
1478	// Mutifunctional method to get and set values to a collection
1479	// The value/s can optionally be executed if it's a function
1480	access: function( elems, key, value, exec, fn, pass ) {
1481		var length = elems.length;
1482
1483		// Setting many attributes
1484		if ( typeof key === "object" ) {
1485			for ( var k in key ) {
1486				jQuery.access( elems, k, key[k], exec, fn, value );
1487			}
1488			return elems;
1489		}
1490
1491		// Setting one attribute
1492		if ( value !== undefined ) {
1493			// Optionally, function values get executed if exec is true
1494			exec = !pass && exec && jQuery.isFunction(value);
1495
1496			for ( var i = 0; i < length; i++ ) {
1497				fn( elems[i], key, exec ? value.call( elems[i], i, fn( elems[i], key ) ) : value, pass );
1498			}
1499
1500			return elems;
1501		}
1502
1503		// Getting an attribute
1504		return length ? fn( elems[0], key ) : undefined;
1505	},
1506
1507	now: function() {
1508		return ( new Date() ).getTime();
1509	},
1510
1511	// Use of jQuery.browser is frowned upon.
1512	// More details: http://docs.jquery.com/Utilities/jQuery.browser
1513	uaMatch: function( ua ) {
1514		ua = ua.toLowerCase();
1515
1516		var match = rwebkit.exec( ua ) ||
1517			ropera.exec( ua ) ||
1518			rmsie.exec( ua ) ||
1519			ua.indexOf("compatible") < 0 && rmozilla.exec( ua ) ||
1520			[];
1521
1522		return { browser: match[1] || "", version: match[2] || "0" };
1523	},
1524
1525	sub: function() {
1526		function jQuerySub( selector, context ) {
1527			return new jQuerySub.fn.init( selector, context );
1528		}
1529		jQuery.extend( true, jQuerySub, this );
1530		jQuerySub.superclass = this;
1531		jQuerySub.fn = jQuerySub.prototype = this();
1532		jQuerySub.fn.constructor = jQuerySub;
1533		jQuerySub.sub = this.sub;
1534		jQuerySub.fn.init = function init( selector, context ) {
1535			if ( context && context instanceof jQuery && !(context instanceof jQuerySub) ) {
1536				context = jQuerySub( context );
1537			}
1538
1539			return jQuery.fn.init.call( this, selector, context, rootjQuerySub );
1540		};
1541		jQuerySub.fn.init.prototype = jQuerySub.fn;
1542		var rootjQuerySub = jQuerySub(document);
1543		return jQuerySub;
1544	},
1545
1546	browser: {}
1547});
1548
1549// Populate the class2type map
1550jQuery.each("Boolean Number String Function Array Date RegExp Object".split(" "), function(i, name) {
1551	class2type[ "[object " + name + "]" ] = name.toLowerCase();
1552});
1553
1554browserMatch = jQuery.uaMatch( userAgent );
1555if ( browserMatch.browser ) {
1556	jQuery.browser[ browserMatch.browser ] = true;
1557	jQuery.browser.version = browserMatch.version;
1558}
1559
1560// Deprecated, use jQuery.browser.webkit instead
1561if ( jQuery.browser.webkit ) {
1562	jQuery.browser.safari = true;
1563}
1564
1565// IE doesn't match non-breaking spaces with \s
1566if ( rnotwhite.test( "\xA0" ) ) {
1567	trimLeft = /^[\s\xA0]+/;
1568	trimRight = /[\s\xA0]+$/;
1569}
1570
1571// All jQuery objects should point back to these
1572rootjQuery = jQuery(document);
1573
1574// Cleanup functions for the document ready method
1575if ( document.addEventListener ) {
1576	DOMContentLoaded = function() {
1577		document.removeEventListener( "DOMContentLoaded", DOMContentLoaded, false );
1578		jQuery.ready();
1579	};
1580
1581} else if ( document.attachEvent ) {
1582	DOMContentLoaded = function() {
1583		// Make sure body exists, at least, in case IE gets a little overzealous (ticket #5443).
1584		if ( document.readyState === "complete" ) {
1585			document.detachEvent( "onreadystatechange", DOMContentLoaded );
1586			jQuery.ready();
1587		}
1588	};
1589}
1590
1591// The DOM ready check for Internet Explorer
1592function doScrollCheck() {
1593	if ( jQuery.isReady ) {
1594		return;
1595	}
1596
1597	try {
1598		// If IE is used, use the trick by Diego Perini
1599		// http://javascript.nwbox.com/IEContentLoaded/
1600		document.documentElement.doScroll("left");
1601	} catch(e) {
1602		setTimeout( doScrollCheck, 1 );
1603		return;
1604	}
1605
1606	// and execute any waiting functions
1607	jQuery.ready();
1608}
1609
1610return jQuery;
1611
1612})();
1613
1614
1615// String to Object flags format cache
1616var flagsCache = {};
1617
1618// Convert String-formatted flags into Object-formatted ones and store in cache
1619function createFlags( flags ) {
1620	var object = flagsCache[ flags ] = {},
1621		i, length;
1622	flags = flags.split( /\s+/ );
1623	for ( i = 0, length = flags.length; i < length; i++ ) {
1624		object[ flags[i] ] = true;
1625	}
1626	return object;
1627}
1628
1629/*
1630 * Create a callback list using the following parameters:
1631 *
1632 *	flags:	an optional list of space-separated flags that will change how
1633 *			the callback list behaves
1634 *
1635 * By default a callback list will act like an event callback list and can be
1636 * "fired" multiple times.
1637 *
1638 * Possible flags:
1639 *
1640 *	once:			will ensure the callback list can only be fired once (like a Deferred)
1641 *
1642 *	memory:			will keep track of previous values and will call any callback added
1643 *					after the list has been fired right away with the latest "memorized"
1644 *					values (like a Deferred)
1645 *
1646 *	unique:			will ensure a callback can only be added once (no duplicate in the list)
1647 *
1648 *	stopOnFalse:	interrupt callings when a callback returns false
1649 *
1650 */
1651jQuery.Callbacks = function( flags ) {
1652
1653	// Convert flags from String-formatted to Object-formatted
1654	// (we check in cache first)
1655	flags = flags ? ( flagsCache[ flags ] || createFlags( flags ) ) : {};
1656
1657	var // Actual callback list
1658		list = [],
1659		// Stack of fire calls for repeatable lists
1660		stack = [],
1661		// Last fire value (for non-forgettable lists)
1662		memory,
1663		// Flag to know if list is currently firing
1664		firing,
1665		// First callback to fire (used internally by add and fireWith)
1666		firingStart,
1667		// End of the loop when firing
1668		firingLength,
1669		// Index of currently firing callback (modified by remove if needed)
1670		firingIndex,
1671		// Add one or several callbacks to the list
1672		add = function( args ) {
1673			var i,
1674				length,
1675				elem,
1676				type,
1677				actual;
1678			for ( i = 0, length = args.length; i < length; i++ ) {
1679				elem = args[ i ];
1680				type = jQuery.type( elem );
1681				if ( type === "array" ) {
1682					// Inspect recursively
1683					add( elem );
1684				} else if ( type === "function" ) {
1685					// Add if not in unique mode and callback is not in
1686					if ( !flags.unique || !self.has( elem ) ) {
1687						list.push( elem );
1688					}
1689				}
1690			}
1691		},
1692		// Fire callbacks
1693		fire = function( context, args ) {
1694			args = args || [];
1695			memory = !flags.memory || [ context, args ];
1696			firing = true;
1697			firingIndex = firingStart || 0;
1698			firingStart = 0;
1699			firingLength = list.length;
1700			for ( ; list && firingIndex < firingLength; firingIndex++ ) {
1701				if ( list[ firingIndex ].apply( context, args ) === false && flags.stopOnFalse ) {
1702					memory = true; // Mark as halted
1703					break;
1704				}
1705			}
1706			firing = false;
1707			if ( list ) {
1708				if ( !flags.once ) {
1709					if ( stack && stack.length ) {
1710						memory = stack.shift();
1711						self.fireWith( memory[ 0 ], memory[ 1 ] );
1712					}
1713				} else if ( memory === true ) {
1714					self.disable();
1715				} else {
1716					list = [];
1717				}
1718			}
1719		},
1720		// Actual Callbacks object
1721		self = {
1722			// Add a callback or a collection of callbacks to the list
1723			add: function() {
1724				if ( list ) {
1725					var length = list.length;
1726					add( arguments );
1727					// Do we need to add the callbacks to the
1728					// current firing batch?
1729					if ( firing ) {
1730						firingLength = list.length;
1731					// With memory, if we're not firing then
1732					// we should call right away, unless previous
1733					// firing was halted (stopOnFalse)
1734					} else if ( memory && memory !== true ) {
1735						firingStart = length;
1736						fire( memory[ 0 ], memory[ 1 ] );
1737					}
1738				}
1739				return this;
1740			},
1741			// Remove a callback from the list
1742			remove: function() {
1743				if ( list ) {
1744					var args = arguments,
1745						argIndex = 0,
1746						argLength = args.length;
1747					for ( ; argIndex < argLength ; argIndex++ ) {
1748						for ( var i = 0; i < list.length; i++ ) {
1749							if ( args[ argIndex ] === list[ i ] ) {
1750								// Handle firingIndex and firingLength
1751								if ( firing ) {
1752									if ( i <= firingLength ) {
1753										firingLength--;
1754										if ( i <= firingIndex ) {
1755											firingIndex--;
1756										}
1757									}
1758								}
1759								// Remove the element
1760								list.splice( i--, 1 );
1761								// If we have some unicity property then
1762								// we only need to do this once
1763								if ( flags.unique ) {
1764									break;
1765								}
1766							}
1767						}
1768					}
1769				}
1770				return this;
1771			},
1772			// Control if a given callback is in the list
1773			has: function( fn ) {
1774				if ( list ) {
1775					var i = 0,
1776						length = list.length;
1777					for ( ; i < length; i++ ) {
1778						if ( fn === list[ i ] ) {
1779							return true;
1780						}
1781					}
1782				}
1783				return false;
1784			},
1785			// Remove all callbacks from the list
1786			empty: function() {
1787				list = [];
1788				return this;
1789			},
1790			// Have the list do nothing anymore
1791			disable: function() {
1792				list = stack = memory = undefined;
1793				return this;
1794			},
1795			// Is it disabled?
1796			disabled: function() {
1797				return !list;
1798			},
1799			// Lock the list in its current state
1800			lock: function() {
1801				stack = undefined;
1802				if ( !memory || memory === true ) {
1803					self.disable();
1804				}
1805				return this;
1806			},
1807			// Is it locked?
1808			locked: function() {
1809				return !stack;
1810			},
1811			// Call all callbacks with the given context and arguments
1812			fireWith: function( context, args ) {
1813				if ( stack ) {
1814					if ( firing ) {
1815						if ( !flags.once ) {
1816							stack.push( [ context, args ] );
1817						}
1818					} else if ( !( flags.once && memory ) ) {
1819						fire( context, args );
1820					}
1821				}
1822				return this;
1823			},
1824			// Call all the callbacks with the given arguments
1825			fire: function() {
1826				self.fireWith( this, arguments );
1827				return this;
1828			},
1829			// To know if the callbacks have already been called at least once
1830			fired: function() {
1831				return !!memory;
1832			}
1833		};
1834
1835	return self;
1836};
1837
1838
1839
1840
1841var // Static reference to slice
1842	sliceDeferred = [].slice;
1843
1844jQuery.extend({
1845
1846	Deferred: function( func ) {
1847		var doneList = jQuery.Callbacks( "once memory" ),
1848			failList = jQuery.Callbacks( "once memory" ),
1849			progressList = jQuery.Callbacks( "memory" ),
1850			state = "pending",
1851			lists = {
1852				resolve: doneList,
1853				reject: failList,
1854				notify: progressList
1855			},
1856			promise = {
1857				done: doneList.add,
1858				fail: failList.add,
1859				progress: progressList.add,
1860
1861				state: function() {
1862					return state;
1863				},
1864
1865				// Deprecated
1866				isResolved: doneList.fired,
1867				isRejected: failList.fired,
1868
1869				then: function( doneCallbacks, failCallbacks, progressCallbacks ) {
1870					deferred.done( doneCallbacks ).fail( failCallbacks ).progress( progressCallbacks );
1871					return this;
1872				},
1873				always: function() {
1874					deferred.done.apply( deferred, arguments ).fail.apply( deferred, arguments );
1875					return this;
1876				},
1877				pipe: function( fnDone, fnFail, fnProgress ) {
1878					return jQuery.Deferred(function( newDefer ) {
1879						jQuery.each( {
1880							done: [ fnDone, "resolve" ],
1881							fail: [ fnFail, "reject" ],
1882							progress: [ fnProgress, "notify" ]
1883						}, function( handler, data ) {
1884							var fn = data[ 0 ],
1885								action = data[ 1 ],
1886								returned;
1887							if ( jQuery.isFunction( fn ) ) {
1888								deferred[ handler ](function() {
1889									returned = fn.apply( this, arguments );
1890									if ( returned && jQuery.isFunction( returned.promise ) ) {
1891										returned.promise().then( newDefer.resolve, newDefer.reject, newDefer.notify );
1892									} else {
1893										newDefer[ action + "With" ]( this === deferred ? newDefer : this, [ returned ] );
1894									}
1895								});
1896							} else {
1897								deferred[ handler ]( newDefer[ action ] );
1898							}
1899						});
1900					}).promise();
1901				},
1902				// Get a promise for this deferred
1903				// If obj is provided, the promise aspect is added to the object
1904				promise: function( obj ) {
1905					if ( obj == null ) {
1906						obj = promise;
1907					} else {
1908						for ( var key in promise ) {
1909							obj[ key ] = promise[ key ];
1910						}
1911					}
1912					return obj;
1913				}
1914			},
1915			deferred = promise.promise({}),
1916			key;
1917
1918		for ( key in lists ) {
1919			deferred[ key ] = lists[ key ].fire;
1920			deferred[ key + "With" ] = lists[ key ].fireWith;
1921		}
1922
1923		// Handle state
1924		deferred.done( function() {
1925			state = "resolved";
1926		}, failList.disable, progressList.lock ).fail( function() {
1927			state = "rejected";
1928		}, doneList.disable, progressList.lock );
1929
1930		// Call given func if any
1931		if ( func ) {
1932			func.call( deferred, deferred );
1933		}
1934
1935		// All done!
1936		return deferred;
1937	},
1938
1939	// Deferred helper
1940	when: function( firstParam ) {
1941		var args = sliceDeferred.call( arguments, 0 ),
1942			i = 0,
1943			length = args.length,
1944			pValues = new Array( length ),
1945			count = length,
1946			pCount = length,
1947			deferred = length <= 1 && firstParam && jQuery.isFunction( firstParam.promise ) ?
1948				firstParam :
1949				jQuery.Deferred(),
1950			promise = deferred.promise();
1951		function resolveFunc( i ) {
1952			return function( value ) {
1953				args[ i ] = arguments.length > 1 ? sliceDeferred.call( arguments, 0 ) : value;
1954				if ( !( --count ) ) {
1955					deferred.resolveWith( deferred, args );
1956				}
1957			};
1958		}
1959		function progressFunc( i ) {
1960			return function( value ) {
1961				pValues[ i ] = arguments.length > 1 ? sliceDeferred.call( arguments, 0 ) : value;
1962				deferred.notifyWith( promise, pValues );
1963			};
1964		}
1965		if ( length > 1 ) {
1966			for ( ; i < length; i++ ) {
1967				if ( args[ i ] && args[ i ].promise && jQuery.isFunction( args[ i ].promise ) ) {
1968					args[ i ].promise().then( resolveFunc(i), deferred.reject, progressFunc(i) );
1969				} else {
1970					--count;
1971				}
1972			}
1973			if ( !count ) {
1974				deferred.resolveWith( deferred, args );
1975			}
1976		} else if ( deferred !== firstParam ) {
1977			deferred.resolveWith( deferred, length ? [ firstParam ] : [] );
1978		}
1979		return promise;
1980	}
1981});
1982
1983
1984
1985
1986jQuery.support = (function() {
1987
1988	var support,
1989		all,
1990		a,
1991		select,
1992		opt,
1993		input,
1994		marginDiv,
1995		fragment,
1996		tds,
1997		events,
1998		eventName,
1999		i,
2000		isSupported,
2001		div = document.createElement( "div" ),
2002		documentElement = document.documentElement;
2003
2004	// Preliminary tests
2005	div.setAttribute("className", "t");
2006	div.innerHTML = "   <link/><table></table><a href='/a' style='top:1px;float:left;opacity:.55;'>a</a>
2006<input type='checkbox'/>";
2007
2008	all = div.getElementsByTagName( "*" );
2009	a = div.getElementsByTagName( "a" )[ 0 ];
2010
2011	// Can't get basic test support
2012	if ( !all || !all.length || !a ) {
2013		return {};
2014	}
2015
2016	// First batch of supports tests
2017	select = document.createElement( "select" );
2018	opt = select.appendChild( document.createElement("option") );
2019	input = div.getElementsByTagName( "input" )[ 0 ];
2020
2021	support = {
2022		// IE strips leading whitespace when .innerHTML is used
2023		leadingWhitespace: ( div.firstChild.nodeType === 3 ),
2024
2025		// Make sure that tbody elements aren't automatically inserted
2026		// IE will insert them into empty tables
2027		tbody: !div.getElementsByTagName("tbody").length,
2028
2029		// Make sure that link elements get serialized correctly by innerHTML
2030		// This requires a wrapper element in IE
2031		htmlSerialize: !!div.getElementsByTagName("link").length,
2032
2033		// Get the style information from getAttribute
2034		// (IE uses .cssText instead)
2035		style: /top/.test( a.getAttribute("style") ),
2036
2037		// Make sure that URLs aren't manipulated
2038		// (IE normalizes it by default)
2039		hrefNormalized: ( a.getAttribute("href") === "/a" ),
2040
2041		// Make sure that element opacity exists
2042		// (IE uses filter instead)
2043		// Use a regex to work around a WebKit issue. See #5145
2044		opacity: /^0.55/.test( a.style.opacity ),
2045
2046		// Verify style float existence
2047		// (IE uses styleFloat instead of cssFloat)
2048		cssFloat: !!a.style.cssFloat,
2049
2050		// Make sure that if no value is specified for a checkbox
2051		// that it defaults to "on".
2052		// (WebKit defaults to "" instead)
2053		checkOn: ( input.value === "on" ),
2054
2055		// Make sure that a selected-by-default option has a working selected property.
2056		// (WebKit defaults to false instead of true, IE too, if it's in an optgroup)
2057		optSelected: opt.selected,
2058
2059		// Test setAttribute on camelCase class. If it works, we need attrFixes when doing get/setAttribute (ie6/7)
2060		getSetAttribute: div.className !== "t",
2061
2062		// Tests for enctype support on a form(#6743)
2063		enctype: !!document.createElement("form").enctype,
2064
2065		// Makes sure cloning an html5 element does not cause problems
2066		// Where outerHTML is undefined, this still works
2067		html5Clone: document.createElement("nav").cloneNode( true ).outerHTML !== "<:nav></:nav>",
2068
2069		// Will be defined later
2070		submitBubbles: true,
2071		changeBubbles: true,
2072		focusinBubbles: false,
2073		deleteExpando: true,
2074		noCloneEvent: true,
2075		inlineBlockNeedsLayout: false,
2076		shrinkWrapBlocks: false,
2077		reliableMarginRight: true
2078	};
2079
2080	// Make sure checked status is properly cloned
2081	input.checked = true;
2082	support.noCloneChecked = input.cloneNode( true ).checked;
2083
2084	// Make sure that the options inside disabled selects aren't marked as disabled
2085	// (WebKit marks them as disabled)
2086	select.disabled = true;
2087	support.optDisabled = !opt.disabled;
2088
2089	// Test to see if it's possible to delete an expando from an element
2090	// Fails in Internet Explorer
2091	try {
2092		delete div.test;
2093	} catch( e ) {
2094		support.deleteExpando = false;
2095	}
2096
2097	if ( !div.addEventListener && div.attachEvent && div.fireEvent ) {
2098		div.attachEvent( "onclick", function() {
2099			// Cloning a node shouldn't copy over any
2100			// bound event handlers (IE does this)
2101			support.noCloneEvent = false;
2102		});
2103		div.cloneNode( true ).fireEvent( "onclick" );
2104	}
2105
2106	// Check if a radio maintains its value
2107	// after being appended to the DOM
2108	input = document.createElement("input");
2109	input.value = "t";
2110	input.setAttribute("type", "radio");
2111	support.radioValue = input.value === "t";
2112
2113	input.setAttribute("checked", "checked");
2114	div.appendChild( input );
2115	fragment = document.createDocumentFragment();
2116	fragment.appendChild( div.lastChild );
2117
2118	// WebKit doesn't clone checked state correctly in fragments
2119	support.checkClone = fragment.cloneNode( true ).cloneNode( true ).lastChild.checked;
2120
2121	// Check if a disconnected checkbox will retain its checked
2122	// value of true after appended to the DOM (IE6/7)
2123	support.appendChecked = input.checked;
2124
2125	fragment.removeChild( input );
2126	fragment.appendChild( div );
2127
2128	div.innerHTML = "";
2129
2130	// Check if div with explicit width and no margin-right incorrectly
2131	// gets computed margin-right based on width of container. For more
2132	// info see bug #3333
2133	// Fails in WebKit before Feb 2011 nightlies
2134	// WebKit Bug 13343 - getComputedStyle returns wrong value for margin-right
2135	if ( window.getComputedStyle ) {
2136		marginDiv = document.createElement( "div" );
2137		marginDiv.style.width = "0";
2138		marginDiv.style.marginRight = "0";
2139		div.style.width = "2px";
2140		div.appendChild( marginDiv );
2141		support.reliableMarginRight =
2142			( parseInt( ( window.getComputedStyle( marginDiv, null ) || { marginRight: 0 } ).marginRight, 10 ) || 0 ) === 0;
2143	}
2144
2145	// Technique from Juriy Zaytsev
2146	// http://perfectionkills.com/detecting-event-support-without-browser-sniffing/
2147	// We only care about the case where non-standard event systems
2148	// are used, namely in IE. Short-circuiting here helps us to
2149	// avoid an eval call (in setAttribute) which can cause CSP
2150	// to go haywire. See: https://developer.mozilla.org/en/Security/CSP
2151	if ( div.attachEvent ) {
2152		for( i in {
2153			submit: 1,
2154			change: 1,
2155			focusin: 1
2156		}) {
2157			eventName = "on" + i;
2158			isSupported = ( eventName in div );
2159			if ( !isSupported ) {
2160				div.setAttribute( eventName, "return;" );
2161				isSupported = ( typeof div[ eventName ] === "function" );
2162			}
2163			support[ i + "Bubbles" ] = isSupported;
2164		}
2165	}
2166
2167	fragment.removeChild( div );
2168
2169	// Null elements to avoid leaks in IE
2170	fragment = select = opt = marginDiv = div = input = null;
2171
2172	// Run tests that need a body at doc ready
2173	jQuery(function() {
2174		var container, outer, inner, table, td, offsetSupport,
2175			conMarginTop, ptlm, vb, style, html,
2176			body = document.getElementsByTagName("body")[0];
2177
2178		if ( !body ) {
2179			// Return for frameset docs that don't have a body
2180			return;
2181		}
2182
2183		conMarginTop = 1;
2184		ptlm = "position:absolute;top:0;left:0;width:1px;height:1px;margin:0;";
2185		vb = "visibility:hidden;border:0;";
2186		style = "style='" + ptlm + "border:5px solid #000;padding:0;'";
2187		html = "<div " + style + "><div></div></div>" +
2188			"<table " + style + " cellpadding='0' cellspacing='0'>" +
2189			"<tr><td></td></tr></table>";
2190
2191		container = document.createElement("div");
2192		container.style.cssText = vb + "width:0;height:0;position:static;top:0;margin-top:" + conMarginTop + "px";
2193		body.insertBefore( container, body.firstChild );
2194
2195		// Construct the test element
2196		div = document.createElement("div");
2197		container.appendChild( div );
2198
2199		// Check if table cells still have offsetWidth/Height when they are set
2200		// to display:none and there are still other visible table cells in a
2201		// table row; if so, offsetWidth/Height are not reliable for use when
2202		// determining if an element has been hidden directly using
2203		// display:none (it is still safe to use offsets if a parent element is
2204		// hidden; don safety goggles and see bug #4512 for more information).
2205		// (only IE 8 fails this test)
2206		div.innerHTML = "<table><tr><td style='padding:0;border:0;display:none'></td><td>t</td></tr></table>";
2207		tds = div.getElementsByTagName( "td" );
2208		isSupported = ( tds[ 0 ].offsetHeight === 0 );
2209
2210		tds[ 0 ].style.display = "";
2211		tds[ 1 ].style.display = "none";
2212
2213		// Check if empty table cells still have offsetWidth/Height
2214		// (IE <= 8 fail this test)
2215		support.reliableHiddenOffsets = isSupported && ( tds[ 0 ].offsetHeight === 0 );
2216
2217		// Figure out if the W3C box model works as expected
2218		div.innerHTML = "";
2219		div.style.width = div.style.paddingLeft = "1px";
2220		jQuery.boxModel = support.boxModel = div.offsetWidth === 2;
2221
2222		if ( typeof div.style.zoom !== "undefined" ) {
2223			// Check if natively block-level elements act like inline-block
2224			// elements when setting their display to 'inline' and giving
2225			// them layout
2226			// (IE < 8 does this)
2227			div.style.display = "inline";
2228			div.style.zoom = 1;
2229			support.inlineBlockNeedsLayout = ( div.offsetWidth === 2 );
2230
2231			// Check if elements with layout shrink-wrap their children
2232			// (IE 6 does this)
2233			div.style.display = "";
2234			div.innerHTML = "<div style='width:4px;'></div>";
2235			support.shrinkWrapBlocks = ( div.offsetWidth !== 2 );
2236		}
2237
2238		div.style.cssText = ptlm + vb;
2239		div.innerHTML = html;
2240
2241		outer = div.firstChild;
2242		inner = outer.firstChild;
2243		td = outer.nextSibling.firstChild.firstChild;
2244
2245		offsetSupport = {
2246			doesNotAddBorder: ( inner.offsetTop !== 5 ),
2247			doesAddBorderForTableAndCells: ( td.offsetTop === 5 )
2248		};
2249
2250		inner.style.position = "fixed";
2251		inner.style.top = "20px";
2252
2253		// safari subtracts parent border width here which is 5px
2254		offsetSupport.fixedPosition = ( inner.offsetTop === 20 || inner.offsetTop === 15 );
2255		inner.style.position = inner.style.top = "";
2256
2257		outer.style.overflow = "hidden";
2258		outer.style.position = "relative";
2259
2260		offsetSupport.subtractsBorderForOverflowNotVisible = ( inner.offsetTop === -5 );
2261		offsetSupport.doesNotIncludeMarginInBodyOffset = ( body.offsetTop !== conMarginTop );
2262
2263		body.removeChild( container );
2264		div  = container = null;
2265
2266		jQuery.extend( support, offsetSupport );
2267	});
2268
2269	return support;
2270})();
2271
2272
2273
2274
2275var rbrace = /^(?:\{.*\}|\[.*\])$/,
2276	rmultiDash = /([A-Z])/g;
2277
2278jQuery.extend({
2279	cache: {},
2280
2281	// Please use with caution
2282	uuid: 0,
2283
2284	// Unique for each copy of jQuery on the page
2285	// Non-digits removed to match rinlinejQuery
2286	expando: "jQuery" + ( jQuery.fn.jquery + Math.random() ).replace( /\D/g, "" ),
2287
2288	// The following elements throw uncatchable exceptions if you
2289	// attempt to add expando properties to them.
2290	noData: {
2291		"embed": true,
2292		// Ban all objects except for Flash (which handle expandos)
2293		"object": "clsid:D27CDB6E-AE6D-11cf-96B8-444553540000",
2294		"applet": true
2295	},
2296
2297	hasData: function( elem ) {
2298		elem = elem.nodeType ? jQuery.cache[ elem[jQuery.expando] ] : elem[ jQuery.expando ];
2299		return !!elem && !isEmptyDataObject( elem );
2300	},
2301
2302	data: function( elem, name, data, pvt /* Internal Use Only */ ) {
2303		if ( !jQuery.acceptData( elem ) ) {
2304			return;
2305		}
2306
2307		var privateCache, thisCache, ret,
2308			internalKey = jQuery.expando,
2309			getByName = typeof name === "string",
2310
2311			// We have to handle DOM nodes and JS objects differently because IE6-7
2312			// can't GC object references properly across the DOM-JS boundary
2313			isNode = elem.nodeType,
2314
2315			// Only DOM nodes need the global jQuery cache; JS object data is
2316			// attached directly to the object so GC can occur automatically
2317			cache = isNode ? jQuery.cache : elem,
2318
2319			// Only defining an ID for JS objects if its cache already exists allows
2320			// the code to shortcut on the same path as a DOM node with no cache
2321			id = isNode ? elem[ internalKey ] : elem[ internalKey ] && internalKey,
2322			isEvents = name === "events";
2323
2324		// Avoid doing any more work than we need to when trying to get data on an
2325		// object that has no data at all
2326		if ( (!id || !cache[id] || (!isEvents && !pvt && !cache[id].data)) && getByName && data === undefined ) {
2327			return;
2328		}
2329
2330		if ( !id ) {
2331			// Only DOM nodes need a new unique ID for each element since their data
2332			// ends up in the global cache
2333			if ( isNode ) {
2334				elem[ internalKey ] = id = ++jQuery.uuid;
2335			} else {
2336				id = internalKey;
2337			}
2338		}
2339
2340		if ( !cache[ id ] ) {
2341			cache[ id ] = {};
2342
2343			// Avoids exposing jQuery metadata on plain JS objects when the object
2344			// is serialized using JSON.stringify
2345			if ( !isNode ) {
2346				cache[ id ].toJSON = jQuery.noop;
2347			}
2348		}
2349
2350		// An object can be passed to jQuery.data instead of a key/value pair; this gets
2351		// shallow copied over onto the existing cache
2352		if ( typeof name === "object" || typeof name === "function" ) {
2353			if ( pvt ) {
2354				cache[ id ] = jQuery.extend( cache[ id ], name );
2355			} else {
2356				cache[ id ].data = jQuery.extend( cache[ id ].data, name );
2357			}
2358		}
2359
2360		privateCache = thisCache = cache[ id ];
2361
2362		// jQuery data() is stored in a separate object inside the object's internal data
2363		// cache in order to avoid key collisions between internal data and user-defined
2364		// data.
2365		if ( !pvt ) {
2366			if ( !thisCache.data ) {
2367				thisCache.data = {};
2368			}
2369
2370			thisCache = thisCache.data;
2371		}
2372
2373		if ( data !== undefined ) {
2374			thisCache[ jQuery.camelCase( name ) ] = data;
2375		}
2376
2377		// Users should not attempt to inspect the internal events object using jQuery.data,
2378		// it is undocumented and subject to change. But does anyone listen? No.
2379		if ( isEvents && !thisCache[ name ] ) {
2380			return privateCache.events;
2381		}
2382
2383		// Check for both converted-to-camel and non-converted data property names
2384		// If a data property was specified
2385		if ( getByName ) {
2386
2387			// First Try to find as-is property data
2388			ret = thisCache[ name ];
2389
2390			// Test for null|undefined property data
2391			if ( ret == null ) {
2392
2393				// Try to find the camelCased property
2394				ret = thisCache[ jQuery.camelCase( name ) ];
2395			}
2396		} else {
2397			ret = thisCache;
2398		}
2399
2400		return ret;
2401	},
2402
2403	removeData: function( elem, name, pvt /* Internal Use Only */ ) {
2404		if ( !jQuery.acceptData( elem ) ) {
2405			return;
2406		}
2407
2408		var thisCache, i, l,
2409
2410			// Reference to internal data cache key
2411			internalKey = jQuery.expando,
2412
2413			isNode = elem.nodeType,
2414
2415			// See jQuery.data for more information
2416			cache = isNode ? jQuery.cache : elem,
2417
2418			// See jQuery.data for more information
2419			id = isNode ? elem[ internalKey ] : internalKey;
2420
2421		// If there is already no cache entry for this object, there is no
2422		// purpose in continuing
2423		if ( !cache[ id ] ) {
2424			return;
2425		}
2426
2427		if ( name ) {
2428
2429			thisCache = pvt ? cache[ id ] : cache[ id ].data;
2430
2431			if ( thisCache ) {
2432
2433				// Support array or space separated string names for data keys
2434				if ( !jQuery.isArray( name ) ) {
2435
2436					// try the string as a key before any manipulation
2437					if ( name in thisCache ) {
2438						name = [ name ];
2439					} else {
2440
2441						// split the camel cased version by spaces unless a key with the spaces exists
2442						name = jQuery.camelCase( name );
2443						if ( name in thisCache ) {
2444							name = [ name ];
2445						} else {
2446							name = name.split( " " );
2447						}
2448					}
2449				}
2450
2451				for ( i = 0, l = name.length; i < l; i++ ) {
2452					delete thisCache[ name[i] ];
2453				}
2454
2455				// If there is no data left in the cache, we want to continue
2456				// and let the cache object itself get destroyed
2457				if ( !( pvt ? isEmptyDataObject : jQuery.isEmptyObject )( thisCache ) ) {
2458					return;
2459				}
2460			}
2461		}
2462
2463		// See jQuery.data for more information
2464		if ( !pvt ) {
2465			delete cache[ id ].data;
2466
2467			// Don't destroy the parent cache unless the internal data object
2468			// had been the only thing left in it
2469			if ( !isEmptyDataObject(cache[ id ]) ) {
2470				return;
2471			}
2472		}
2473
2474		// Browsers that fail expando deletion also refuse to delete expandos on
2475		// the window, but it will allow it on all other JS objects; other browsers
2476		// don't care
2477		// Ensure that `cache` is not a window object #10080
2478		if ( jQuery.support.deleteExpando || !cache.setInterval ) {
2479			delete cache[ id ];
2480		} else {
2481			cache[ id ] = null;
2482		}
2483
2484		// We destroyed the cache and need to eliminate the expando on the node to avoid
2485		// false lookups in the cache for entries that no longer exist
2486		if ( isNode ) {
2487			// IE does not allow us to delete expando properties from nodes,
2488			// nor does it have a removeAttribute function on Document nodes;
2489			// we must handle all of these cases
2490			if ( jQuery.support.deleteExpando ) {
2491				delete elem[ internalKey ];
2492			} else if ( elem.removeAttribute ) {
2493				elem.removeAttribute( internalKey );
2494			} else {
2495				elem[ internalKey ] = null;
2496			}
2497		}
2498	},
2499
2500	// For internal use only.
2501	_data: function( elem, name, data ) {
2502		return jQuery.data( elem, name, data, true );
2503	},
2504
2505	// A method for determining if a DOM node can handle the data expando
2506	acceptData: function( elem ) {
2507		if ( elem.nodeName ) {
2508			var match = jQuery.noData[ elem.nodeName.toLowerCase() ];
2509
2510			if ( match ) {
2511				return !(match === true || elem.getAttribute("classid") !== match);
2512			}
2513		}
2514
2515		return true;
2516	}
2517});
2518
2519jQuery.fn.extend({
2520	data: function( key, value ) {
2521		var parts, attr, name,
2522			data = null;
2523
2524		if ( typeof key === "undefined" ) {
2525			if ( this.length ) {
2526				data = jQuery.data( this[0] );
2527
2528				if ( this[0].nodeType === 1 && !jQuery._data( this[0], "parsedAttrs" ) ) {
2529					attr = this[0].attributes;
2530					for ( var i = 0, l = attr.length; i < l; i++ ) {
2531						name = attr[i].name;
2532
2533						if ( name.indexOf( "data-" ) === 0 ) {
2534							name = jQuery.camelCase( name.substring(5) );
2535
2536							dataAttr( this[0], name, data[ name ] );
2537						}
2538					}
2539					jQuery._data( this[0], "parsedAttrs", true );
2540				}
2541			}
2542
2543			return data;
2544
2545		} else if ( typeof key === "object" ) {
2546			return this.each(function() {
2547				jQuery.data( this, key );
2548			});
2549		}
2550
2551		parts = key.split(".");
2552		parts[1] = parts[1] ? "." + parts[1] : "";
2553
2554		if ( value === undefined ) {
2555			data = this.triggerHandler("getData" + parts[1] + "!", [parts[0]]);
2556
2557			// Try to fetch any internally stored data first
2558			if ( data === undefined && this.length ) {
2559				data = jQuery.data( this[0], key );
2560				data = dataAttr( this[0], key, data );
2561			}
2562
2563			return data === undefined && parts[1] ?
2564				this.data( parts[0] ) :
2565				data;
2566
2567		} else {
2568			return this.each(function() {
2569				var self = jQuery( this ),
2570					args = [ parts[0], value ];
2571
2572				self.triggerHandler( "setData" + parts[1] + "!", args );
2573				jQuery.data( this, key, value );
2574				self.triggerHandler( "changeData" + parts[1] + "!", args );
2575			});
2576		}
2577	},
2578
2579	removeData: function( key ) {
2580		return this.each(function() {
2581			jQuery.removeData( this, key );
2582		});
2583	}
2584});
2585
2586function dataAttr( elem, key, data ) {
2587	// If nothing was found internally, try to fetch any
2588	// data from the HTML5 data-* attribute
2589	if ( data === undefined && elem.nodeType === 1 ) {
2590
2591		var name = "data-" + key.replace( rmultiDash, "-$1" ).toLowerCase();
2592
2593		data = elem.getAttribute( name );
2594
2595		if ( typeof data === "string" ) {
2596			try {
2597				data = data === "true" ? true :
2598				data === "false" ? false :
2599				data === "null" ? null :
2600				jQuery.isNumeric( data ) ? parseFloat( data ) :
2601					rbrace.test( data ) ? jQuery.parseJSON( data ) :
2602					data;
2603			} catch( e ) {}
2604
2605			// Make sure we set the data so it isn't changed later
2606			jQuery.data( elem, key, data );
2607
2608		} else {
2609			data = undefined;
2610		}
2611	}
2612
2613	return data;
2614}
2615
2616// checks a cache object for emptiness
2617function isEmptyDataObject( obj ) {
2618	for ( var name in obj ) {
2619
2620		// if the public data object is empty, the private is still empty
2621		if ( name === "data" && jQuery.isEmptyObject( obj[name] ) ) {
2622			continue;
2623		}
2624		if ( name !== "toJSON" ) {
2625			return false;
2626		}
2627	}
2628
2629	return true;
2630}
2631
2632
2633
2634
2635function handleQueueMarkDefer( elem, type, src ) {
2636	var deferDataKey = type + "defer",
2637		queueDataKey = type + "queue",
2638		markDataKey = type + "mark",
2639		defer = jQuery._data( elem, deferDataKey );
2640	if ( defer &&
2641		( src === "queue" || !jQuery._data(elem, queueDataKey) ) &&
2642		( src === "mark" || !jQuery._data(elem, markDataKey) ) ) {
2643		// Give room for hard-coded callbacks to fire first
2644		// and eventually mark/queue something else on the element
2645		setTimeout( function() {
2646			if ( !jQuery._data( elem, queueDataKey ) &&
2647				!jQuery._data( elem, markDataKey ) ) {
2648				jQuery.removeData( elem, deferDataKey, true );
2649				defer.fire();
2650			}
2651		}, 0 );
2652	}
2653}
2654
2655jQuery.extend({
2656
2657	_mark: function( elem, type ) {
2658		if ( elem ) {
2659			type = ( type || "fx" ) + "mark";
2660			jQuery._data( elem, type, (jQuery._data( elem, type ) || 0) + 1 );
2661		}
2662	},
2663
2664	_unmark: function( force, elem, type ) {
2665		if ( force !== true ) {
2666			type = elem;
2667			elem = force;
2668			force = false;
2669		}
2670		if ( elem ) {
2671			type = type || "fx";
2672			var key = type + "mark",
2673				count = force ? 0 : ( (jQuery._data( elem, key ) || 1) - 1 );
2674			if ( count ) {
2675				jQuery._data( elem, key, count );
2676			} else {
2677				jQuery.removeData( elem, key, true );
2678				handleQueueMarkDefer( elem, type, "mark" );
2679			}
2680		}
2681	},
2682
2683	queue: function( elem, type, data ) {
2684		var q;
2685		if ( elem ) {
2686			type = ( type || "fx" ) + "queue";
2687			q = jQuery._data( elem, type );
2688
2689			// Speed up dequeue by getting out quickly if this is just a lookup
2690			if ( data ) {
2691				if ( !q || jQuery.isArray(data) ) {
2692					q = jQuery._data( elem, type, jQuery.makeArray(data) );
2693				} else {
2694					q.push( data );
2695				}
2696			}
2697			return q || [];
2698		}
2699	},
2700
2701	dequeue: function( elem, type ) {
2702		type = type || "fx";
2703
2704		var queue = jQuery.queue( elem, type ),
2705			fn = queue.shift(),
2706			hooks = {};
2707
2708		// If the fx queue is dequeued, always remove the progress sentinel
2709		if ( fn === "inprogress" ) {
2710			fn = queue.shift();
2711		}
2712
2713		if ( fn ) {
2714			// Add a progress sentinel to prevent the fx queue from being
2715			// automatically dequeued
2716			if ( type === "fx" ) {
2717				queue.unshift( "inprogress" );
2718			}
2719
2720			jQuery._data( elem, type + ".run", hooks );
2721			fn.call( elem, function() {
2722				jQuery.dequeue( elem, type );
2723			}, hooks );
2724		}
2725
2726		if ( !queue.length ) {
2727			jQuery.removeData( elem, type + "queue " + type + ".run", true );
2728			handleQueueMarkDefer( elem, type, "queue" );
2729		}
2730	}
2731});
2732
2733jQuery.fn.extend({
2734	queue: function( type, data ) {
2735		if ( typeof type !== "string" ) {
2736			data = type;
2737			type = "fx";
2738		}
2739
2740		if ( data === undefined ) {
2741			return jQuery.queue( this[0], type );
2742		}
2743		return this.each(function() {
2744			var queue = jQuery.queue( this, type, data );
2745
2746			if ( type === "fx" && queue[0] !== "inprogress" ) {
2747				jQuery.dequeue( this, type );
2748			}
2749		});
2750	},
2751	dequeue: function( type ) {
2752		return this.each(function() {
2753			jQuery.dequeue( this, type );
2754		});
2755	},
2756	// Based off of the plugin by Clint Helfers, with permission.
2757	// http://blindsignals.com/index.php/2009/07/jquery-delay/
2758	delay: function( time, type ) {
2759		time = jQuery.fx ? jQuery.fx.speeds[ time ] || time : time;
2760		type = type || "fx";
2761
2762		return this.queue( type, function( next, hooks ) {
2763			var timeout = setTimeout( next, time );
2764			hooks.stop = function() {
2765				clearTimeout( timeout );
2766			};
2767		});
2768	},
2769	clearQueue: function( type ) {
2770		return this.queue( type || "fx", [] );
2771	},
2772	// Get a promise resolved when queues of a certain type
2773	// are emptied (fx is the type by default)
2774	promise: function( type, object ) {
2775		if ( typeof type !== "string" ) {
2776			object = type;
2777			type = undefined;
2778		}
2779		type = type || "fx";
2780		var defer = jQuery.Deferred(),
2781			elements = this,
2782			i = elements.length,
2783			count = 1,
2784			deferDataKey = type + "defer",
2785			queueDataKey = type + "queue",
2786			markDataKey = type + "mark",
2787			tmp;
2788		function resolve() {
2789			if ( !( --count ) ) {
2790				defer.resolveWith( elements, [ elements ] );
2791			}
2792		}
2793		while( i-- ) {
2794			if (( tmp = jQuery.data( elements[ i ], deferDataKey, undefined, true ) ||
2795					( jQuery.data( elements[ i ], queueDataKey, undefined, true ) ||
2796						jQuery.data( elements[ i ], markDataKey, undefined, true ) ) &&
2797					jQuery.data( elements[ i ], deferDataKey, jQuery.Callbacks( "once memory" ), true ) )) {
2798				count++;
2799				tmp.add( resolve );
2800			}
2801		}
2802		resolve();
2803		return defer.promise();
2804	}
2805});
2806
2807
2808
2809
2810var rclass = /[\n\t\r]/g,
2811	rspace = /\s+/,
2812	rreturn = /\r/g,
2813	rtype = /^(?:button|input)$/i,
2814	rfocusable = /^(?:button|input|object|select|textarea)$/i,
2815	rclickable = /^a(?:rea)?$/i,
2816	rboolean = /^(?:autofocus|autoplay|async|checked|controls|defer|disabled|hidden|loop|multiple|open|readonly|required|scoped|selected)$/i,
2817	getSetAttribute = jQuery.support.getSetAttribute,
2818	nodeHook, boolHook, fixSpecified;
2819
2820jQuery.fn.extend({
2821	attr: function( name, value ) {
2822		return jQuery.access( this, name, value, true, jQuery.attr );
2823	},
2824
2825	removeAttr: function( name ) {
2826		return this.each(function() {
2827			jQuery.removeAttr( this, name );
2828		});
2829	},
2830
2831	prop: function( name, value ) {
2832		return jQuery.access( this, name, value, true, jQuery.prop );
2833	},
2834
2835	removeProp: function( name ) {
2836		name = jQuery.propFix[ name ] || name;
2837		return this.each(function() {
2838			// try/catch handles cases where IE balks (such as removing a property on window)
2839			try {
2840				this[ name ] = undefined;
2841				delete this[ name ];
2842			} catch( e ) {}
2843		});
2844	},
2845
2846	addClass: function( value ) {
2847		var classNames, i, l, elem,
2848			setClass, c, cl;
2849
2850		if ( jQuery.isFunction( value ) ) {
2851			return this.each(function( j ) {
2852				jQuery( this ).addClass( value.call(this, j, this.className) );
2853			});
2854		}
2855
2856		if ( value && typeof value === "string" ) {
2857			classNames = value.split( rspace );
2858
2859			for ( i = 0, l = this.length; i < l; i++ ) {
2860				elem = this[ i ];
2861
2862				if ( elem.nodeType === 1 ) {
2863					if ( !elem.className && classNames.length === 1 ) {
2864						elem.className = value;
2865
2866					} else {
2867						setClass = " " + elem.className + " ";
2868
2869						for ( c = 0, cl = classNames.length; c < cl; c++ ) {
2870							if ( !~setClass.indexOf( " " + classNames[ c ] + " " ) ) {
2871								setClass += classNames[ c ] + " ";
2872							}
2873						}
2874						elem.className = jQuery.trim( setClass );
2875					}
2876				}
2877			}
2878		}
2879
2880		return this;
2881	},
2882
2883	removeClass: function( value ) {
2884		var classNames, i, l, elem, className, c, cl;
2885
2886		if ( jQuery.isFunction( value ) ) {
2887			return this.each(function( j ) {
2888				jQuery( this ).removeClass( value.call(this, j, this.className) );
2889			});
2890		}
2891
2892		if ( (value && typeof value === "string") || value === undefined ) {
2893			classNames = ( value || "" ).split( rspace );
2894
2895			for ( i = 0, l = this.length; i < l; i++ ) {
2896				elem = this[ i ];
2897
2898				if ( elem.nodeType === 1 && elem.className ) {
2899					if ( value ) {
2900						className = (" " + elem.className + " ").replace( rclass, " " );
2901						for ( c = 0, cl = classNames.length; c < cl; c++ ) {
2902							className = className.replace(" " + classNames[ c ] + " ", " ");
2903						}
2904						elem.className = jQuery.trim( className );
2905
2906					} else {
2907						elem.className = "";
2908					}
2909				}
2910			}
2911		}
2912
2913		return this;
2914	},
2915
2916	toggleClass: function( value, stateVal ) {
2917		var type = typeof value,
2918			isBool = typeof stateVal === "boolean";
2919
2920		if ( jQuery.isFunction( value ) ) {
2921			return this.each(function( i ) {
2922				jQuery( this ).toggleClass( value.call(this, i, this.className, stateVal), stateVal );
2923			});
2924		}
2925
2926		return this.each(function() {
2927			if ( type === "string" ) {
2928				// toggle individual class names
2929				var className,
2930					i = 0,
2931					self = jQuery( this ),
2932					state = stateVal,
2933					classNames = value.split( rspace );
2934
2935				while ( (className = classNames[ i++ ]) ) {
2936					// check each className given, space seperated list
2937					state = isBool ? state : !self.hasClass( className );
2938					self[ state ? "addClass" : "removeClass" ]( className );
2939				}
2940
2941			} else if ( type === "undefined" || type === "boolean" ) {
2942				if ( this.className ) {
2943					// store className if set
2944					jQuery._data( this, "__className__", this.className );
2945				}
2946
2947				// toggle whole className
2948				this.className = this.className || value === false ? "" : jQuery._data( this, "__className__" ) || "";
2949			}
2950		});
2951	},
2952
2953	hasClass: function( selector ) {
2954		var className = " " + selector + " ",
2955			i = 0,
2956			l = this.length;
2957		for ( ; i < l; i++ ) {
2958			if ( this[i].nodeType === 1 && (" " + this[i].className + " ").replace(rclass, " ").indexOf( className ) > -1 ) {
2959				return true;
2960			}
2961		}
2962
2963		return false;
2964	},
2965
2966	val: function( value ) {
2967		var hooks, ret, isFunction,
2968			elem = this[0];
2969
2970		if ( !arguments.length ) {
2971			if ( elem ) {
2972				hooks = jQuery.valHooks[ elem.nodeName.toLowerCase() ] || jQuery.valHooks[ elem.type ];
2973
2974				if ( hooks && "get" in hooks && (ret = hooks.get( elem, "value" )) !== undefined ) {
2975					return ret;
2976				}
2977
2978				ret = elem.value;
2979
2980				return typeof ret === "string" ?
2981					// handle most common string cases
2982					ret.replace(rreturn, "") :
2983					// handle cases where value is null/undef or number
2984					ret == null ? "" : ret;
2985			}
2986
2987			return;
2988		}
2989
2990		isFunction = jQuery.isFunction( value );
2991
2992		return this.each(function( i ) {
2993			var self = jQuery(this), val;
2994
2995			if ( this.nodeType !== 1 ) {
2996				return;
2997			}
2998
2999			if ( isFunction ) {
3000				val = value.call( this, i, self.val() );
3001			} else {
3002				val = value;
3003			}
3004
3005			// Treat null/undefined as ""; convert numbers to string
3006			if ( val == null ) {
3007				val = "";
3008			} else if ( typeof val === "number" ) {
3009				val += "";
3010			} else if ( jQuery.isArray( val ) ) {
3011				val = jQuery.map(val, function ( value ) {
3012					return value == null ? "" : value + "";
3013				});
3014			}
3015
3016			hooks = jQuery.valHooks[ this.nodeName.toLowerCase() ] || jQuery.valHooks[ this.type ];
3017
3018			// If set returns undefined, fall back to normal setting
3019			if ( !hooks || !("set" in hooks) || hooks.set( this, val, "value" ) === undefined ) {
3020				this.value = val;
3021			}
3022		});
3023	}
3024});
3025
3026jQuery.extend({
3027	valHooks: {
3028		option: {
3029			get: function( elem ) {
3030				// attributes.value is undefined in Blackberry 4.7 but
3031				// uses .value. See #6932
3032				var val = elem.attributes.value;
3033				return !val || val.specified ? elem.value : elem.text;
3034			}
3035		},
3036		select: {
3037			get: function( elem ) {
3038				var value, i, max, option,
3039					index = elem.selectedIndex,
3040					values = [],
3041					options = elem.options,
3042					one = elem.type === "select-one";
3043
3044				// Nothing was selected
3045				if ( index < 0 ) {
3046					return null;
3047				}
3048
3049				// Loop through all the selected options
3050				i = one ? index : 0;
3051				max = one ? index + 1 : options.length;
3052				for ( ; i < max; i++ ) {
3053					option = options[ i ];
3054
3055					// Don't return options that are disabled or in a disabled optgroup
3056					if ( option.selected && (jQuery.support.optDisabled ? !option.disabled : option.getAttribute("disabled") === null) &&
3057							(!option.parentNode.disabled || !jQuery.nodeName( option.parentNode, "optgroup" )) ) {
3058
3059						// Get the specific value for the option
3060						value = jQuery( option ).val();
3061
3062						// We don't need an array for one selects
3063						if ( one ) {
3064							return value;
3065						}
3066
3067						// Multi-Selects return an array
3068						values.push( value );
3069					}
3070				}
3071
3072				// Fixes Bug #2551 -- select.val() broken in IE after form.reset()
3073				if ( one && !values.length && options.length ) {
3074					return jQuery( options[ index ] ).val();
3075				}
3076
3077				return values;
3078			},
3079
3080			set: function( elem, value ) {
3081				var values = jQuery.makeArray( value );
3082
3083				jQuery(elem).find("option").each(function() {
3084					this.selected = jQuery.inArray( jQuery(this).val(), values ) >= 0;
3085				});
3086
3087				if ( !values.length ) {
3088					elem.selectedIndex = -1;
3089				}
3090				return values;
3091			}
3092		}
3093	},
3094
3095	attrFn: {
3096		val: true,
3097		css: true,
3098		html: true,
3099		text: true,
3100		data: true,
3101		width: true,
3102		height: true,
3103		offset: true
3104	},
3105
3106	attr: function( elem, name, value, pass ) {
3107		var ret, hooks, notxml,
3108			nType = elem.nodeType;
3109
3110		// don't get/set attributes on text, comment and attribute nodes
3111		if ( !elem || nType === 3 || nType === 8 || nType === 2 ) {
3112			return;
3113		}
3114
3115		if ( pass && name in jQuery.attrFn ) {
3116			return jQuery( elem )[ name ]( value );
3117		}
3118
3119		// Fallback to prop when attributes are not supported
3120		if ( typeof elem.getAttribute === "undefined" ) {
3121			return jQuery.prop( elem, name, value );
3122		}
3123
3124		notxml = nType !== 1 || !jQuery.isXMLDoc( elem );
3125
3126		// All attributes are lowercase
3127		// Grab necessary hook if one is defined
3128		if ( notxml ) {
3129			name = name.toLowerCase();
3130			hooks = jQuery.attrHooks[ name ] || ( rboolean.test( name ) ? boolHook : nodeHook );
3131		}
3132
3133		if ( value !== undefined ) {
3134
3135			if ( value === null ) {
3136				jQuery.removeAttr( elem, name );
3137				return;
3138
3139			} else if ( hooks && "set" in hooks && notxml && (ret = hooks.set( elem, value, name )) !== undefined ) {
3140				return ret;
3141
3142			} else {
3143				elem.setAttribute( name, "" + value );
3144				return value;
3145			}
3146
3147		} else if ( hooks && "get" in hooks && notxml && (ret = hooks.get( elem, name )) !== null ) {
3148			return ret;
3149
3150		} else {
3151
3152			ret = elem.getAttribute( name );
3153
3154			// Non-existent attributes return null, we normalize to undefined
3155			return ret === null ?
3156				undefined :
3157				ret;
3158		}
3159	},
3160
3161	removeAttr: function( elem, value ) {
3162		var propName, attrNames, name, l,
3163			i = 0;
3164
3165		if ( value && elem.nodeType === 1 ) {
3166			attrNames = value.toLowerCase().split( rspace );
3167			l = attrNames.length;
3168
3169			for ( ; i < l; i++ ) {
3170				name = attrNames[ i ];
3171
vendor: 5,612 bytes, lines 3172-3364
3172				if ( name ) {
3173					propName = jQuery.propFix[ name ] || name;
3174
3175					// See #9699 for explanation of this approach (setting first, then removal)
3176					jQuery.attr( elem, name, "" );
3177					elem.removeAttribute( getSetAttribute ? name : propName );
3178
3179					// Set corresponding property to false for boolean attributes
3180					if ( rboolean.test( name ) && propName in elem ) {
3181						elem[ propName ] = false;
3182					}
3183				}
3184			}
3185		}
3186	},
3187
3188	attrHooks: {
3189		type: {
3190			set: function( elem, value ) {
3191				// We can't allow the type property to be changed (since it causes problems in IE)
3192				if ( rtype.test( elem.nodeName ) && elem.parentNode ) {
3193					jQuery.error( "type property can't be changed" );
3194				} else if ( !jQuery.support.radioValue && value === "radio" && jQuery.nodeName(elem, "input") ) {
3195					// Setting the type on a radio button after the value resets the value in IE6-9
3196					// Reset value to it's default in case type is set after value
3197					// This is for element creation
3198					var val = elem.value;
3199					elem.setAttribute( "type", value );
3200					if ( val ) {
3201						elem.value = val;
3202					}
3203					return value;
3204				}
3205			}
3206		},
3207		// Use the value property for back compat
3208		// Use the nodeHook for button elements in IE6/7 (#1954)
3209		value: {
3210			get: function( elem, name ) {
3211				if ( nodeHook && jQuery.nodeName( elem, "button" ) ) {
3212					return nodeHook.get( elem, name );
3213				}
3214				return name in elem ?
3215					elem.value :
3216					null;
3217			},
3218			set: function( elem, value, name ) {
3219				if ( nodeHook && jQuery.nodeName( elem, "button" ) ) {
3220					return nodeHook.set( elem, value, name );
3221				}
3222				// Does not return so that setAttribute is also used
3223				elem.value = value;
3224			}
3225		}
3226	},
3227
3228	propFix: {
3229		tabindex: "tabIndex",
3230		readonly: "readOnly",
3231		"for": "htmlFor",
3232		"class": "className",
3233		maxlength: "maxLength",
3234		cellspacing: "cellSpacing",
3235		cellpadding: "cellPadding",
3236		rowspan: "rowSpan",
3237		colspan: "colSpan",
3238		usemap: "useMap",
3239		frameborder: "frameBorder",
3240		contenteditable: "contentEditable"
3241	},
3242
3243	prop: function( elem, name, value ) {
3244		var ret, hooks, notxml,
3245			nType = elem.nodeType;
3246
3247		// don't get/set properties on text, comment and attribute nodes
3248		if ( !elem || nType === 3 || nType === 8 || nType === 2 ) {
3249			return;
3250		}
3251
3252		notxml = nType !== 1 || !jQuery.isXMLDoc( elem );
3253
3254		if ( notxml ) {
3255			// Fix name and attach hooks
3256			name = jQuery.propFix[ name ] || name;
3257			hooks = jQuery.propHooks[ name ];
3258		}
3259
3260		if ( value !== undefined ) {
3261			if ( hooks && "set" in hooks && (ret = hooks.set( elem, value, name )) !== undefined ) {
3262				return ret;
3263
3264			} else {
3265				return ( elem[ name ] = value );
3266			}
3267
3268		} else {
3269			if ( hooks && "get" in hooks && (ret = hooks.get( elem, name )) !== null ) {
3270				return ret;
3271
3272			} else {
3273				return elem[ name ];
3274			}
3275		}
3276	},
3277
3278	propHooks: {
3279		tabIndex: {
3280			get: function( elem ) {
3281				// elem.tabIndex doesn't always return the correct value when it hasn't been explicitly set
3282				// http://fluidproject.org/blog/2008/01/09/getting-setting-and-removing-tabindex-values-with-javascript/
3283				var attributeNode = elem.getAttributeNode("tabindex");
3284
3285				return attributeNode && attributeNode.specified ?
3286					parseInt( attributeNode.value, 10 ) :
3287					rfocusable.test( elem.nodeName ) || rclickable.test( elem.nodeName ) && elem.href ?
3288						0 :
3289						undefined;
3290			}
3291		}
3292	}
3293});
3294
3295// Add the tabIndex propHook to attrHooks for back-compat (different case is intentional)
3296jQuery.attrHooks.tabindex = jQuery.propHooks.tabIndex;
3297
3298// Hook for boolean attributes
3299boolHook = {
3300	get: function( elem, name ) {
3301		// Align boolean attributes with corresponding properties
3302		// Fall back to attribute presence where some booleans are not supported
3303		var attrNode,
3304			property = jQuery.prop( elem, name );
3305		return property === true || typeof property !== "boolean" && ( attrNode = elem.getAttributeNode(name) ) && attrNode.nodeValue !== false ?
3306			name.toLowerCase() :
3307			undefined;
3308	},
3309	set: function( elem, value, name ) {
3310		var propName;
3311		if ( value === false ) {
3312			// Remove boolean attributes when set to false
3313			jQuery.removeAttr( elem, name );
3314		} else {
3315			// value is true since we know at this point it's type boolean and not false
3316			// Set boolean attributes to the same name and set the DOM property
3317			propName = jQuery.propFix[ name ] || name;
3318			if ( propName in elem ) {
3319				// Only set the IDL specifically if it already exists on the element
3320				elem[ propName ] = true;
3321			}
3322
3323			elem.setAttribute( name, name.toLowerCase() );
3324		}
3325		return name;
3326	}
3327};
3328
3329// IE6/7 do not support getting/setting some attributes with get/setAttribute
3330if ( !getSetAttribute ) {
3331
3332	fixSpecified = {
3333		name: true,
3334		id: true
3335	};
3336
3337	// Use this for any attribute in IE6/7
3338	// This fixes almost every IE6/7 issue
3339	nodeHook = jQuery.valHooks.button = {
3340		get: function( elem, name ) {
3341			var ret;
3342			ret = elem.getAttributeNode( name );
3343			return ret && ( fixSpecified[ name ] ? ret.nodeValue !== "" : ret.specified ) ?
3344				ret.nodeValue :
3345				undefined;
3346		},
3347		set: function( elem, value, name ) {
3348			// Set the existing or create a new attribute node
3349			var ret = elem.getAttributeNode( name );
3350			if ( !ret ) {
3351				ret = document.createAttribute( name );
3352				elem.setAttributeNode( ret );
3353			}
3354			return ( ret.nodeValue = value + "" );
3355		}
3356	};
3357
3358	// Apply the nodeHook to tabindex
3359	jQuery.attrHooks.tabindex.set = nodeHook.set;
3360
3361	// Set width and height to auto instead of 0 on empty string( Bug #8150 )
3362	// This is for removals
3363	jQuery.each([ "width", "height" ], function( i, name ) {
3364		jQuery.attrHooks[ name ] = jQuery.extend( jQuery.attrHooks[ name ], {
3365			set: function( elem, value ) {
3366				if ( value === "" ) {
3367					elem.setAttribute( name, "auto" );
3368					return value;
3369				}
3370			}
3371		});
3372	});
3373
3374	// Set contenteditable to false on removals(#10429)
3375	// Setting to empty string throws an error as an invalid value
3376	jQuery.attrHooks.contenteditable = {
3377		get: nodeHook.get,
3378		set: function( elem, value, name ) {
3379			if ( value === "" ) {
3380				value = "false";
3381			}
3382			nodeHook.set( elem, value, name );
3383		}
3384	};
3385}
3386
3387
3388// Some attributes require a special call on IE
3389if ( !jQuery.support.hrefNormalized ) {
3390	jQuery.each([ "href", "src", "width", "height" ], function( i, name ) {
3391		jQuery.attrHooks[ name ] = jQuery.extend( jQuery.attrHooks[ name ], {
3392			get: function( elem ) {
3393				var ret = elem.getAttribute( name, 2 );
3394				return ret === null ? undefined : ret;
3395			}
3396		});
3397	});
3398}
3399
3400if ( !jQuery.support.style ) {
3401	jQuery.attrHooks.style = {
3402		get: function( elem ) {
3403			// Return undefined in the case of empty string
3404			// Normalize to lowercase since IE uppercases css property names
3405			return elem.style.cssText.toLowerCase() || undefined;
3406		},
3407		set: function( elem, value ) {
3408			return ( elem.style.cssText = "" + value );
3409		}
3410	};
3411}
3412
3413// Safari mis-reports the default selected property of an option
3414// Accessing the parent's selectedIndex property fixes it
3415if ( !jQuery.support.optSelected ) {
3416	jQuery.propHooks.selected = jQuery.extend( jQuery.propHooks.selected, {
3417		get: function( elem ) {
3418			var parent = elem.parentNode;
3419
3420			if ( parent ) {
3421				parent.selectedIndex;
3422
3423				// Make sure that it also works with optgroups, see #5701
3424				if ( parent.parentNode ) {
3425					parent.parentNode.selectedIndex;
3426				}
3427			}
3428			return null;
3429		}
3430	});
3431}
3432
3433// IE6/7 call enctype encoding
3434if ( !jQuery.support.enctype ) {
3435	jQuery.propFix.enctype = "encoding";
3436}
3437
3438// Radios and checkboxes getter/setter
3439if ( !jQuery.support.checkOn ) {
3440	jQuery.each([ "radio", "checkbox" ], function() {
3441		jQuery.valHooks[ this ] = {
3442			get: function( elem ) {
3443				// Handle the case where in Webkit "" is returned instead of "on" if a value isn't specified
3444				return elem.getAttribute("value") === null ? "on" : elem.value;
3445			}
3446		};
3447	});
3448}
3449jQuery.each([ "radio", "checkbox" ], function() {
3450	jQuery.valHooks[ this ] = jQuery.extend( jQuery.valHooks[ this ], {
3451		set: function( elem, value ) {
3452			if ( jQuery.isArray( value ) ) {
3453				return ( elem.checked = jQuery.inArray( jQuery(elem).val(), value ) >= 0 );
3454			}
3455		}
3456	});
3457});
3458
3459
3460
3461
3462var rformElems = /^(?:textarea|input|select)$/i,
3463	rtypenamespace = /^([^\.]*)?(?:\.(.+))?$/,
3464	rhoverHack = /\bhover(\.\S+)?\b/,
3465	rkeyEvent = /^key/,
3466	rmouseEvent = /^(?:mouse|contextmenu)|click/,
3467	rfocusMorph = /^(?:focusinfocus|focusoutblur)$/,
3468	rquickIs = /^(\w*)(?:#([\w\-]+))?(?:\.([\w\-]+))?$/,
3469	quickParse = function( selector ) {
3470		var quick = rquickIs.exec( selector );
3471		if ( quick ) {
3472			//   0  1    2   3
3473			// [ _, tag, id, class ]
3474			quick[1] = ( quick[1] || "" ).toLowerCase();
3475			quick[3] = quick[3] && new RegExp( "(?:^|\\s)" + quick[3] + "(?:\\s|$)" );
3476		}
3477		return quick;
3478	},
3479	quickIs = function( elem, m ) {
3480		var attrs = elem.attributes || {};
3481		return (
3482			(!m[1] || elem.nodeName.toLowerCase() === m[1]) &&
3483			(!m[2] || (attrs.id || {}).value === m[2]) &&
3484			(!m[3] || m[3].test( (attrs[ "class" ] || {}).value ))
3485		);
3486	},
3487	hoverHack = function( events ) {
3488		return jQuery.event.special.hover ? events : events.replace( rhoverHack, "mouseenter$1 mouseleave$1" );
3489	};
3490
3491/*
3492 * Helper functions for managing events -- not part of the public interface.
3493 * Props to Dean Edwards' addEvent library for many of the ideas.
3494 */
3495jQuery.event = {
3496
3497	add: function( elem, types, handler, data, selector ) {
3498
3499		var elemData, eventHandle, events,
3500			t, tns, type, namespaces, handleObj,
3501			handleObjIn, quick, handlers, special;
3502
3503		// Don't attach events to noData or text/comment nodes (allow plain objects tho)
3504		if ( elem.nodeType === 3 || elem.nodeType === 8 || !types || !handler || !(elemData = jQuery._data( elem )) ) {
3505			return;
3506		}
3507
3508		// Caller can pass in an object of custom data in lieu of the handler
3509		if ( handler.handler ) {
3510			handleObjIn = handler;
3511			handler = handleObjIn.handler;
3512		}
3513
3514		// Make sure that the handler has a unique ID, used to find/remove it later
3515		if ( !handler.guid ) {
3516			handler.guid = jQuery.guid++;
3517		}
3518
3519		// Init the element's event structure and main handler, if this is the first
3520		events = elemData.events;
3521		if ( !events ) {
3522			elemData.events = events = {};
3523		}
3524		eventHandle = elemData.handle;
3525		if ( !eventHandle ) {
3526			elemData.handle = eventHandle = function( e ) {
3527				// Discard the second event of a jQuery.event.trigger() and
3528				// when an event is called after a page has unloaded
3529				return typeof jQuery !== "undefined" && (!e || jQuery.event.triggered !== e.type) ?
3530					jQuery.event.dispatch.apply( eventHandle.elem, arguments ) :
3531					undefined;
3532			};
3533			// Add elem as a property of the handle fn to prevent a memory leak with IE non-native events
3534			eventHandle.elem = elem;
3535		}
3536
3537		// Handle multiple events separated by a space
3538		// jQuery(...).bind("mouseover mouseout", fn);
3539		types = jQuery.trim( hoverHack(types) ).split( " " );
3540		for ( t = 0; t < types.length; t++ ) {
3541
3542			tns = rtypenamespace.exec( types[t] ) || [];
3543			type = tns[1];
vendor: 4,277 bytes, lines 3544-3678
3544			namespaces = ( tns[2] || "" ).split( "." ).sort();
3545
3546			// If event changes its type, use the special event handlers for the changed type
3547			special = jQuery.event.special[ type ] || {};
3548
3549			// If selector defined, determine special event api type, otherwise given type
3550			type = ( selector ? special.delegateType : special.bindType ) || type;
3551
3552			// Update special based on newly reset type
3553			special = jQuery.event.special[ type ] || {};
3554
3555			// handleObj is passed to all event handlers
3556			handleObj = jQuery.extend({
3557				type: type,
3558				origType: tns[1],
3559				data: data,
3560				handler: handler,
3561				guid: handler.guid,
3562				selector: selector,
3563				quick: quickParse( selector ),
3564				namespace: namespaces.join(".")
3565			}, handleObjIn );
3566
3567			// Init the event handler queue if we're the first
3568			handlers = events[ type ];
3569			if ( !handlers ) {
3570				handlers = events[ type ] = [];
3571				handlers.delegateCount = 0;
3572
3573				// Only use addEventListener/attachEvent if the special events handler returns false
3574				if ( !special.setup || special.setup.call( elem, data, namespaces, eventHandle ) === false ) {
3575					// Bind the global event handler to the element
3576					if ( elem.addEventListener ) {
3577						elem.addEventListener( type, eventHandle, false );
3578
3579					} else if ( elem.attachEvent ) {
3580						elem.attachEvent( "on" + type, eventHandle );
3581					}
3582				}
3583			}
3584
3585			if ( special.add ) {
3586				special.add.call( elem, handleObj );
3587
3588				if ( !handleObj.handler.guid ) {
3589					handleObj.handler.guid = handler.guid;
3590				}
3591			}
3592
3593			// Add to the element's handler list, delegates in front
3594			if ( selector ) {
3595				handlers.splice( handlers.delegateCount++, 0, handleObj );
3596			} else {
3597				handlers.push( handleObj );
3598			}
3599
3600			// Keep track of which events have ever been used, for event optimization
3601			jQuery.event.global[ type ] = true;
3602		}
3603
3604		// Nullify elem to prevent memory leaks in IE
3605		elem = null;
3606	},
3607
3608	global: {},
3609
3610	// Detach an event or set of events from an element
3611	remove: function( elem, types, handler, selector, mappedTypes ) {
3612
3613		var elemData = jQuery.hasData( elem ) && jQuery._data( elem ),
3614			t, tns, type, origType, namespaces, origCount,
3615			j, events, special, handle, eventType, handleObj;
3616
3617		if ( !elemData || !(events = elemData.events) ) {
3618			return;
3619		}
3620
3621		// Once for each type.namespace in types; type may be omitted
3622		types = jQuery.trim( hoverHack( types || "" ) ).split(" ");
3623		for ( t = 0; t < types.length; t++ ) {
3624			tns = rtypenamespace.exec( types[t] ) || [];
3625			type = origType = tns[1];
3626			namespaces = tns[2];
3627
3628			// Unbind all events (on this namespace, if provided) for the element
3629			if ( !type ) {
3630				for ( type in events ) {
3631					jQuery.event.remove( elem, type + types[ t ], handler, selector, true );
3632				}
3633				continue;
3634			}
3635
3636			special = jQuery.event.special[ type ] || {};
3637			type = ( selector? special.delegateType : special.bindType ) || type;
3638			eventType = events[ type ] || [];
3639			origCount = eventType.length;
3640			namespaces = namespaces ? new RegExp("(^|\\.)" + namespaces.split(".").sort().join("\\.(?:.*\\.)?") + "(\\.|$)") : null;
3641
3642			// Remove matching events
3643			for ( j = 0; j < eventType.length; j++ ) {
3644				handleObj = eventType[ j ];
3645
3646				if ( ( mappedTypes || origType === handleObj.origType ) &&
3647					 ( !handler || handler.guid === handleObj.guid ) &&
3648					 ( !namespaces || namespaces.test( handleObj.namespace ) ) &&
3649					 ( !selector || selector === handleObj.selector || selector === "**" && handleObj.selector ) ) {
3650					eventType.splice( j--, 1 );
3651
3652					if ( handleObj.selector ) {
3653						eventType.delegateCount--;
3654					}
3655					if ( special.remove ) {
3656						special.remove.call( elem, handleObj );
3657					}
3658				}
3659			}
3660
3661			// Remove generic event handler if we removed something and no more handlers exist
3662			// (avoids potential for endless recursion during removal of special event handlers)
3663			if ( eventType.length === 0 && origCount !== eventType.length ) {
3664				if ( !special.teardown || special.teardown.call( elem, namespaces ) === false ) {
3665					jQuery.removeEvent( elem, type, elemData.handle );
3666				}
3667
3668				delete events[ type ];
3669			}
3670		}
3671
3672		// Remove the expando if it's no longer used
3673		if ( jQuery.isEmptyObject( events ) ) {
3674			handle = elemData.handle;
3675			if ( handle ) {
3676				handle.elem = null;
3677			}
3678
3679			// removeData also checks for emptiness and clears the expando if empty
3680			// so use it instead of delete
3681			jQuery.removeData( elem, [ "events", "handle" ], true );
3682		}
3683	},
3684
3685	// Events that are safe to short-circuit if no handlers are attached.
3686	// Native DOM events should not be added, they may have inline handlers.
3687	customEvent: {
3688		"getData": true,
3689		"setData": true,
3690		"changeData": true
3691	},
3692
3693	trigger: function( event, data, elem, onlyHandlers ) {
3694		// Don't do events on text and comment nodes
3695		if ( elem && (elem.nodeType === 3 || elem.nodeType === 8) ) {
3696			return;
3697		}
3698
3699		// Event object or event type
3700		var type = event.type || event,
3701			namespaces = [],
3702			cache, exclusive, i, cur, old, ontype, special, handle, eventPath, bubbleType;
3703
3704		// focus/blur morphs to focusin/out; ensure we're not firing them right now
3705		if ( rfocusMorph.test( type + jQuery.event.triggered ) ) {
3706			return;
3707		}
3708
3709		if ( type.indexOf( "!" ) >= 0 ) {
3710			// Exclusive events trigger only for the exact event (no namespaces)
3711			type = type.slice(0, -1);
3712			exclusive = true;
3713		}
3714
3715		if ( type.indexOf( "." ) >= 0 ) {
3716			// Namespaced trigger; create a regexp to match event type in handle()
3717			namespaces = type.split(".");
3718			type = namespaces.shift();
3719			namespaces.sort();
3720		}
3721
3722		if ( (!elem || jQuery.event.customEvent[ type ]) && !jQuery.event.global[ type ] ) {
3723			// No jQuery handlers for this event type, and it can't have inline handlers
3724			return;
3725		}
3726
3727		// Caller can pass in an Event, Object, or just an event type string
3728		event = typeof event === "object" ?
3729			// jQuery.Event object
3730			event[ jQuery.expando ] ? event :
3731			// Object literal
3732			new jQuery.Event( type, event ) :
3733			// Just the event type (string)
3734			new jQuery.Event( type );
3735
3736		event.type = type;
3737		event.isTrigger = true;
3738		event.exclusive = exclusive;
3739		event.namespace = namespaces.join( "." );
3740		event.namespace_re = event.namespace? new RegExp("(^|\\.)" + namespaces.join("\\.(?:.*\\.)?") + "(\\.|$)") : null;
3741		ontype = type.indexOf( ":" ) < 0 ? "on" + type : "";
3742
3743		// Handle a global trigger
3744		if ( !elem ) {
3745
3746			// TODO: Stop taunting the data cache; remove global events and always attach to document
3747			cache = jQuery.cache;
3748			for ( i in cache ) {
3749				if ( cache[ i ].events && cache[ i ].events[ type ] ) {
3750					jQuery.event.trigger( event, data, cache[ i ].handle.elem, true );
3751				}
3752			}
3753			return;
3754		}
3755
3756		// Clean up the event in case it is being reused
3757		event.result = undefined;
3758		if ( !event.target ) {
3759			event.target = elem;
3760		}
3761
3762		// Clone any incoming data and prepend the event, creating the handler arg list
3763		data = data != null ? jQuery.makeArray( data ) : [];
3764		data.unshift( event );
3765
3766		// Allow special events to draw outside the lines
3767		special = jQuery.event.special[ type ] || {};
3768		if ( special.trigger && special.trigger.apply( elem, data ) === false ) {
3769			return;
3770		}
3771
3772		// Determine event propagation path in advance, per W3C events spec (#9951)
3773		// Bubble up to document, then to window; watch for a global ownerDocument var (#9724)
3774		eventPath = [[ elem, special.bindType || type ]];
3775		if ( !onlyHandlers && !special.noBubble && !jQuery.isWindow( elem ) ) {
3776
3777			bubbleType = special.delegateType || type;
3778			cur = rfocusMorph.test( bubbleType + type ) ? elem : elem.parentNode;
3779			old = null;
3780			for ( ; cur; cur = cur.parentNode ) {
3781				eventPath.push([ cur, bubbleType ]);
3782				old = cur;
3783			}
3784
3785			// Only add window if we got to document (e.g., not plain obj or detached DOM)
3786			if ( old && old === elem.ownerDocument ) {
3787				eventPath.push([ old.defaultView || old.parentWindow || window, bubbleType ]);
3788			}
3789		}
3790
3791		// Fire handlers on the event path
3792		for ( i = 0; i < eventPath.length && !event.isPropagationStopped(); i++ ) {
3793
3794			cur = eventPath[i][0];
3795			event.type = eventPath[i][1];
3796
3797			handle = ( jQuery._data( cur, "events" ) || {} )[ event.type ] && jQuery._data( cur, "handle" );
3798			if ( handle ) {
3799				handle.apply( cur, data );
3800			}
3801			// Note that this is a bare JS function and not a jQuery handler
3802			handle = ontype && cur[ ontype ];
3803			if ( handle && jQuery.acceptData( cur ) && handle.apply( cur, data ) === false ) {
3804				event.preventDefault();
3805			}
3806		}
3807		event.type = type;
3808
3809		// If nobody prevented the default action, do it now
3810		if ( !onlyHandlers && !event.isDefaultPrevented() ) {
3811
3812			if ( (!special._default || special._default.apply( elem.ownerDocument, data ) === false) &&
3813				!(type === "click" && jQuery.nodeName( elem, "a" )) && jQuery.acceptData( elem ) ) {
3814
3815				// Call a native DOM method on the target with the same name name as the event.
3816				// Can't use an .isFunction() check here because IE6/7 fails that test.
3817				// Don't do default actions on window, that's where global variables be (#6170)
3818				// IE<9 dies on focus/blur to hidden element (#1486)
3819				if ( ontype && elem[ type ] && ((type !== "focus" && type !== "blur") || event.target.offsetWidth !== 0) && !jQuery.isWindow( elem ) ) {
3820
3821					// Don't re-trigger an onFOO event when we call its FOO() method
3822					old = elem[ ontype ];
3823
3824					if ( old ) {
3825						elem[ ontype ] = null;
3826					}
3827
3828					// Prevent re-triggering of the same event, since we already bubbled it above
3829					jQuery.event.triggered = type;
3830					elem[ type ]();
3831					jQuery.event.triggered = undefined;
3832
3833					if ( old ) {
3834						elem[ ontype ] = old;
3835					}
3836				}
3837			}
3838		}
3839
3840		return event.result;
3841	},
3842
3843	dispatch: function( event ) {
3844
3845		// Make a writable jQuery.Event from the native event object
3846		event = jQuery.event.fix( event || window.event );
3847
3848		var handlers = ( (jQuery._data( this, "events" ) || {} )[ event.type ] || []),
3849			delegateCount = handlers.delegateCount,
3850			args = [].slice.call( arguments, 0 ),
3851			run_all = !event.exclusive && !event.namespace,
3852			handlerQueue = [],
3853			i, j, cur, jqcur, ret, selMatch, matched, matches, handleObj, sel, related;
3854
3855		// Use the fix-ed jQuery.Event rather than the (read-only) native event
3856		args[0] = event;
3857		event.delegateTarget = this;
3858
3859		// Determine handlers that should run if there are delegated events
3860		// Avoid disabled elements in IE (#6911) and non-left-click bubbling in Firefox (#3861)
3861		if ( delegateCount && !event.target.disabled && !(event.button && event.type === "click") ) {
3862
3863			// Pregenerate a single jQuery object for reuse with .is()
3864			jqcur = jQuery(this);
3865			jqcur.context = this.ownerDocument || this;
3866
3867			for ( cur = event.target; cur != this; cur = cur.parentNode || this ) {
3868				selMatch = {};
3869				matches = [];
3870				jqcur[0] = cur;
3871				for ( i = 0; i < delegateCount; i++ ) {
3872					handleObj = handlers[ i ];
3873					sel = handleObj.selector;
3874
3875					if ( selMatch[ sel ] === undefined ) {
3876						selMatch[ sel ] = (
3877							handleObj.quick ? quickIs( cur, handleObj.quick ) : jqcur.is( sel )
3878						);
3879					}
3880					if ( selMatch[ sel ] ) {
3881						matches.push( handleObj );
3882					}
3883				}
3884				if ( matches.length ) {
3885					handlerQueue.push({ elem: cur, matches: matches });
3886				}
3887			}
3888		}
3889
3890		// Add the remaining (directly-bound) handlers
3891		if ( handlers.length > delegateCount ) {
3892			handlerQueue.push({ elem: this, matches: handlers.slice( delegateCount ) });
3893		}
3894
3895		// Run delegates first; they may want to stop propagation beneath us
3896		for ( i = 0; i < handlerQueue.length && !event.isPropagationStopped(); i++ ) {
3897			matched = handlerQueue[ i ];
3898			event.currentTarget = matched.elem;
3899
3900			for ( j = 0; j < matched.matches.length && !event.isImmediatePropagationStopped(); j++ ) {
3901				handleObj = matched.matches[ j ];
3902
3903				// Triggered event must either 1) be non-exclusive and have no namespace, or
3904				// 2) have namespace(s) a subset or equal to those in the bound event (both can have no namespace).
3905				if ( run_all || (!event.namespace && !handleObj.namespace) || event.namespace_re && event.namespace_re.test( handleObj.namespace ) ) {
3906
3907					event.data = handleObj.data;
3908					event.handleObj = handleObj;
3909
3910					ret = ( (jQuery.event.special[ handleObj.origType ] || {}).handle || handleObj.handler )
3911							.apply( matched.elem, args );
3912
3913					if ( ret !== undefined ) {
3914						event.result = ret;
3915						if ( ret === false ) {
3916							event.preventDefault();
3917							event.stopPropagation();
3918						}
3919					}
3920				}
3921			}
3922		}
3923
3924		return event.result;
3925	},
3926
3927	// Includes some event props shared by KeyEvent and MouseEvent
3928	// *** attrChange attrName relatedNode srcElement  are not normalized, non-W3C, deprecated, will be removed in 1.8 ***
3929	props: "attrChange attrName relatedNode srcElement altKey bubbles cancelable ctrlKey currentTarget eventPhase metaKey relatedTarget shiftKey target timeStamp view which".split(" "),
3930
3931	fixHooks: {},
3932
3933	keyHooks: {
vendor: 15,758 bytes, lines 3934-4495
3934		props: "char charCode key keyCode".split(" "),
3935		filter: function( event, original ) {
3936
3937			// Add which for key events
3938			if ( event.which == null ) {
3939				event.which = original.charCode != null ? original.charCode : original.keyCode;
3940			}
3941
3942			return event;
3943		}
3944	},
3945
3946	mouseHooks: {
3947		props: "button buttons clientX clientY fromElement offsetX offsetY pageX pageY screenX screenY toElement".split(" "),
3948		filter: function( event, original ) {
3949			var eventDoc, doc, body,
3950				button = original.button,
3951				fromElement = original.fromElement;
3952
3953			// Calculate pageX/Y if missing and clientX/Y available
3954			if ( event.pageX == null && original.clientX != null ) {
3955				eventDoc = event.target.ownerDocument || document;
3956				doc = eventDoc.documentElement;
3957				body = eventDoc.body;
3958
3959				event.pageX = original.clientX + ( doc && doc.scrollLeft || body && body.scrollLeft || 0 ) - ( doc && doc.clientLeft || body && body.clientLeft || 0 );
3960				event.pageY = original.clientY + ( doc && doc.scrollTop  || body && body.scrollTop  || 0 ) - ( doc && doc.clientTop  || body && body.clientTop  || 0 );
3961			}
3962
3963			// Add relatedTarget, if necessary
3964			if ( !event.relatedTarget && fromElement ) {
3965				event.relatedTarget = fromElement === event.target ? original.toElement : fromElement;
3966			}
3967
3968			// Add which for click: 1 === left; 2 === middle; 3 === right
3969			// Note: button is not normalized, so don't use it
3970			if ( !event.which && button !== undefined ) {
3971				event.which = ( button & 1 ? 1 : ( button & 2 ? 3 : ( button & 4 ? 2 : 0 ) ) );
3972			}
3973
3974			return event;
3975		}
3976	},
3977
3978	fix: function( event ) {
3979		if ( event[ jQuery.expando ] ) {
3980			return event;
3981		}
3982
3983		// Create a writable copy of the event object and normalize some properties
3984		var i, prop,
3985			originalEvent = event,
3986			fixHook = jQuery.event.fixHooks[ event.type ] || {},
3987			copy = fixHook.props ? this.props.concat( fixHook.props ) : this.props;
3988
3989		event = jQuery.Event( originalEvent );
3990
3991		for ( i = copy.length; i; ) {
3992			prop = copy[ --i ];
3993			event[ prop ] = originalEvent[ prop ];
3994		}
3995
3996		// Fix target property, if necessary (#1925, IE 6/7/8 & Safari2)
3997		if ( !event.target ) {
3998			event.target = originalEvent.srcElement || document;
3999		}
4000
4001		// Target should not be a text node (#504, Safari)
4002		if ( event.target.nodeType === 3 ) {
4003			event.target = event.target.parentNode;
4004		}
4005
4006		// For mouse/key events; add metaKey if it's not there (#3368, IE6/7/8)
4007		if ( event.metaKey === undefined ) {
4008			event.metaKey = event.ctrlKey;
4009		}
4010
4011		return fixHook.filter? fixHook.filter( event, originalEvent ) : event;
4012	},
4013
4014	special: {
4015		ready: {
4016			// Make sure the ready event is setup
4017			setup: jQuery.bindReady
4018		},
4019
4020		load: {
4021			// Prevent triggered image.load events from bubbling to window.load
4022			noBubble: true
4023		},
4024
4025		focus: {
4026			delegateType: "focusin"
4027		},
4028		blur: {
4029			delegateType: "focusout"
4030		},
4031
4032		beforeunload: {
4033			setup: function( data, namespaces, eventHandle ) {
4034				// We only want to do this special case on windows
4035				if ( jQuery.isWindow( this ) ) {
4036					this.onbeforeunload = eventHandle;
4037				}
4038			},
4039
4040			teardown: function( namespaces, eventHandle ) {
4041				if ( this.onbeforeunload === eventHandle ) {
4042					this.onbeforeunload = null;
4043				}
4044			}
4045		}
4046	},
4047
4048	simulate: function( type, elem, event, bubble ) {
4049		// Piggyback on a donor event to simulate a different one.
4050		// Fake originalEvent to avoid donor's stopPropagation, but if the
4051		// simulated event prevents default then we do the same on the donor.
4052		var e = jQuery.extend(
4053			new jQuery.Event(),
4054			event,
4055			{ type: type,
4056				isSimulated: true,
4057				originalEvent: {}
4058			}
4059		);
4060		if ( bubble ) {
4061			jQuery.event.trigger( e, null, elem );
4062		} else {
4063			jQuery.event.dispatch.call( elem, e );
4064		}
4065		if ( e.isDefaultPrevented() ) {
4066			event.preventDefault();
4067		}
4068	}
4069};
4070
4071// Some plugins are using, but it's undocumented/deprecated and will be removed.
4072// The 1.7 special event interface should provide all the hooks needed now.
4073jQuery.event.handle = jQuery.event.dispatch;
4074
4075jQuery.removeEvent = document.removeEventListener ?
4076	function( elem, type, handle ) {
4077		if ( elem.removeEventListener ) {
4078			elem.removeEventListener( type, handle, false );
4079		}
4080	} :
4081	function( elem, type, handle ) {
4082		if ( elem.detachEvent ) {
4083			elem.detachEvent( "on" + type, handle );
4084		}
4085	};
4086
4087jQuery.Event = function( src, props ) {
4088	// Allow instantiation without the 'new' keyword
4089	if ( !(this instanceof jQuery.Event) ) {
4090		return new jQuery.Event( src, props );
4091	}
4092
4093	// Event object
4094	if ( src && src.type ) {
4095		this.originalEvent = src;
4096		this.type = src.type;
4097
4098		// Events bubbling up the document may have been marked as prevented
4099		// by a handler lower down the tree; reflect the correct value.
4100		this.isDefaultPrevented = ( src.defaultPrevented || src.returnValue === false ||
4101			src.getPreventDefault && src.getPreventDefault() ) ? returnTrue : returnFalse;
4102
4103	// Event type
4104	} else {
4105		this.type = src;
4106	}
4107
4108	// Put explicitly provided properties onto the event object
4109	if ( props ) {
4110		jQuery.extend( this, props );
4111	}
4112
4113	// Create a timestamp if incoming event doesn't have one
4114	this.timeStamp = src && src.timeStamp || jQuery.now();
4115
4116	// Mark it as fixed
4117	this[ jQuery.expando ] = true;
4118};
4119
4120function returnFalse() {
4121	return false;
4122}
4123function returnTrue() {
4124	return true;
4125}
4126
4127// jQuery.Event is based on DOM3 Events as specified by the ECMAScript Language Binding
4128// http://www.w3.org/TR/2003/WD-DOM-Level-3-Events-20030331/ecma-script-binding.html
4129jQuery.Event.prototype = {
4130	preventDefault: function() {
4131		this.isDefaultPrevented = returnTrue;
4132
4133		var e = this.originalEvent;
4134		if ( !e ) {
4135			return;
4136		}
4137
4138		// if preventDefault exists run it on the original event
4139		if ( e.preventDefault ) {
4140			e.preventDefault();
4141
4142		// otherwise set the returnValue property of the original event to false (IE)
4143		} else {
4144			e.returnValue = false;
4145		}
4146	},
4147	stopPropagation: function() {
4148		this.isPropagationStopped = returnTrue;
4149
4150		var e = this.originalEvent;
4151		if ( !e ) {
4152			return;
4153		}
4154		// if stopPropagation exists run it on the original event
4155		if ( e.stopPropagation ) {
4156			e.stopPropagation();
4157		}
4158		// otherwise set the cancelBubble property of the original event to true (IE)
4159		e.cancelBubble = true;
4160	},
4161	stopImmediatePropagation: function() {
4162		this.isImmediatePropagationStopped = returnTrue;
4163		this.stopPropagation();
4164	},
4165	isDefaultPrevented: returnFalse,
4166	isPropagationStopped: returnFalse,
4167	isImmediatePropagationStopped: returnFalse
4168};
4169
4170// Create mouseenter/leave events using mouseover/out and event-time checks
4171jQuery.each({
4172	mouseenter: "mouseover",
4173	mouseleave: "mouseout"
4174}, function( orig, fix ) {
4175	jQuery.event.special[ orig ] = {
4176		delegateType: fix,
4177		bindType: fix,
4178
4179		handle: function( event ) {
4180			var target = this,
4181				related = event.relatedTarget,
4182				handleObj = event.handleObj,
4183				selector = handleObj.selector,
4184				ret;
4185
4186			// For mousenter/leave call the handler if related is outside the target.
4187			// NB: No relatedTarget if the mouse left/entered the browser window
4188			if ( !related || (related !== target && !jQuery.contains( target, related )) ) {
4189				event.type = handleObj.origType;
4190				ret = handleObj.handler.apply( this, arguments );
4191				event.type = fix;
4192			}
4193			return ret;
4194		}
4195	};
4196});
4197
4198// IE submit delegation
4199if ( !jQuery.support.submitBubbles ) {
4200
4201	jQuery.event.special.submit = {
4202		setup: function() {
4203			// Only need this for delegated form submit events
4204			if ( jQuery.nodeName( this, "form" ) ) {
4205				return false;
4206			}
4207
4208			// Lazy-add a submit handler when a descendant form may potentially be submitted
4209			jQuery.event.add( this, "click._submit keypress._submit", function( e ) {
4210				// Node name check avoids a VML-related crash in IE (#9807)
4211				var elem = e.target,
4212					form = jQuery.nodeName( elem, "input" ) || jQuery.nodeName( elem, "button" ) ? elem.form : undefined;
4213				if ( form && !form._submit_attached ) {
4214					jQuery.event.add( form, "submit._submit", function( event ) {
4215						// If form was submitted by the user, bubble the event up the tree
4216						if ( this.parentNode && !event.isTrigger ) {
4217							jQuery.event.simulate( "submit", this.parentNode, event, true );
4218						}
4219					});
4220					form._submit_attached = true;
4221				}
4222			});
4223			// return undefined since we don't need an event listener
4224		},
4225
4226		teardown: function() {
4227			// Only need this for delegated form submit events
4228			if ( jQuery.nodeName( this, "form" ) ) {
4229				return false;
4230			}
4231
4232			// Remove delegated handlers; cleanData eventually reaps submit handlers attached above
4233			jQuery.event.remove( this, "._submit" );
4234		}
4235	};
4236}
4237
4238// IE change delegation and checkbox/radio fix
4239if ( !jQuery.support.changeBubbles ) {
4240
4241	jQuery.event.special.change = {
4242
4243		setup: function() {
4244
4245			if ( rformElems.test( this.nodeName ) ) {
4246				// IE doesn't fire change on a check/radio until blur; trigger it on click
4247				// after a propertychange. Eat the blur-change in special.change.handle.
4248				// This still fires onchange a second time for check/radio after blur.
4249				if ( this.type === "checkbox" || this.type === "radio" ) {
4250					jQuery.event.add( this, "propertychange._change", function( event ) {
4251						if ( event.originalEvent.propertyName === "checked" ) {
4252							this._just_changed = true;
4253						}
4254					});
4255					jQuery.event.add( this, "click._change", function( event ) {
4256						if ( this._just_changed && !event.isTrigger ) {
4257							this._just_changed = false;
4258							jQuery.event.simulate( "change", this, event, true );
4259						}
4260					});
4261				}
4262				return false;
4263			}
4264			// Delegated event; lazy-add a change handler on descendant inputs
4265			jQuery.event.add( this, "beforeactivate._change", function( e ) {
4266				var elem = e.target;
4267
4268				if ( rformElems.test( elem.nodeName ) && !elem._change_attached ) {
4269					jQuery.event.add( elem, "change._change", function( event ) {
4270						if ( this.parentNode && !event.isSimulated && !event.isTrigger ) {
4271							jQuery.event.simulate( "change", this.parentNode, event, true );
4272						}
4273					});
4274					elem._change_attached = true;
4275				}
4276			});
4277		},
4278
4279		handle: function( event ) {
4280			var elem = event.target;
4281
4282			// Swallow native change events from checkbox/radio, we already triggered them above
4283			if ( this !== elem || event.isSimulated || event.isTrigger || (elem.type !== "radio" && elem.type !== "checkbox") ) {
4284				return event.handleObj.handler.apply( this, arguments );
4285			}
4286		},
4287
4288		teardown: function() {
4289			jQuery.event.remove( this, "._change" );
4290
4291			return rformElems.test( this.nodeName );
4292		}
4293	};
4294}
4295
4296// Create "bubbling" focus and blur events
4297if ( !jQuery.support.focusinBubbles ) {
4298	jQuery.each({ focus: "focusin", blur: "focusout" }, function( orig, fix ) {
4299
4300		// Attach a single capturing handler while someone wants focusin/focusout
4301		var attaches = 0,
4302			handler = function( event ) {
4303				jQuery.event.simulate( fix, event.target, jQuery.event.fix( event ), true );
4304			};
4305
4306		jQuery.event.special[ fix ] = {
4307			setup: function() {
4308				if ( attaches++ === 0 ) {
4309					document.addEventListener( orig, handler, true );
4310				}
4311			},
4312			teardown: function() {
4313				if ( --attaches === 0 ) {
4314					document.removeEventListener( orig, handler, true );
4315				}
4316			}
4317		};
4318	});
4319}
4320
4321jQuery.fn.extend({
4322
4323	on: function( types, selector, data, fn, /*INTERNAL*/ one ) {
4324		var origFn, type;
4325
4326		// Types can be a map of types/handlers
4327		if ( typeof types === "object" ) {
4328			// ( types-Object, selector, data )
4329			if ( typeof selector !== "string" ) {
4330				// ( types-Object, data )
4331				data = selector;
4332				selector = undefined;
4333			}
4334			for ( type in types ) {
4335				this.on( type, selector, data, types[ type ], one );
4336			}
4337			return this;
4338		}
4339
4340		if ( data == null && fn == null ) {
4341			// ( types, fn )
4342			fn = selector;
4343			data = selector = undefined;
4344		} else if ( fn == null ) {
4345			if ( typeof selector === "string" ) {
4346				// ( types, selector, fn )
4347				fn = data;
4348				data = undefined;
4349			} else {
4350				// ( types, data, fn )
4351				fn = data;
4352				data = selector;
4353				selector = undefined;
4354			}
4355		}
4356		if ( fn === false ) {
4357			fn = returnFalse;
4358		} else if ( !fn ) {
4359			return this;
4360		}
4361
4362		if ( one === 1 ) {
4363			origFn = fn;
4364			fn = function( event ) {
4365				// Can use an empty set, since event contains the info
4366				jQuery().off( event );
4367				return origFn.apply( this, arguments );
4368			};
4369			// Use same guid so caller can remove using origFn
4370			fn.guid = origFn.guid || ( origFn.guid = jQuery.guid++ );
4371		}
4372		return this.each( function() {
4373			jQuery.event.add( this, types, fn, data, selector );
4374		});
4375	},
4376	one: function( types, selector, data, fn ) {
4377		return this.on.call( this, types, selector, data, fn, 1 );
4378	},
4379	off: function( types, selector, fn ) {
4380		if ( types && types.preventDefault && types.handleObj ) {
4381			// ( event )  dispatched jQuery.Event
4382			var handleObj = types.handleObj;
4383			jQuery( types.delegateTarget ).off(
4384				handleObj.namespace? handleObj.type + "." + handleObj.namespace : handleObj.type,
4385				handleObj.selector,
4386				handleObj.handler
4387			);
4388			return this;
4389		}
4390		if ( typeof types === "object" ) {
4391			// ( types-object [, selector] )
4392			for ( var type in types ) {
4393				this.off( type, selector, types[ type ] );
4394			}
4395			return this;
4396		}
4397		if ( selector === false || typeof selector === "function" ) {
4398			// ( types [, fn] )
4399			fn = selector;
4400			selector = undefined;
4401		}
4402		if ( fn === false ) {
4403			fn = returnFalse;
4404		}
4405		return this.each(function() {
4406			jQuery.event.remove( this, types, fn, selector );
4407		});
4408	},
4409
4410	bind: function( types, data, fn ) {
4411		return this.on( types, null, data, fn );
4412	},
4413	unbind: function( types, fn ) {
4414		return this.off( types, null, fn );
4415	},
4416
4417	live: function( types, data, fn ) {
4418		jQuery( this.context ).on( types, this.selector, data, fn );
4419		return this;
4420	},
4421	die: function( types, fn ) {
4422		jQuery( this.context ).off( types, this.selector || "**", fn );
4423		return this;
4424	},
4425
4426	delegate: function( selector, types, data, fn ) {
4427		return this.on( types, selector, data, fn );
4428	},
4429	undelegate: function( selector, types, fn ) {
4430		// ( namespace ) or ( selector, types [, fn] )
4431		return arguments.length == 1? this.off( selector, "**" ) : this.off( types, selector, fn );
4432	},
4433
4434	trigger: function( type, data ) {
4435		return this.each(function() {
4436			jQuery.event.trigger( type, data, this );
4437		});
4438	},
4439	triggerHandler: function( type, data ) {
4440		if ( this[0] ) {
4441			return jQuery.event.trigger( type, data, this[0], true );
4442		}
4443	},
4444
4445	toggle: function( fn ) {
4446		// Save reference to arguments for access in closure
4447		var args = arguments,
4448			guid = fn.guid || jQuery.guid++,
4449			i = 0,
4450			toggler = function( event ) {
4451				// Figure out which function to execute
4452				var lastToggle = ( jQuery._data( this, "lastToggle" + fn.guid ) || 0 ) % i;
4453				jQuery._data( this, "lastToggle" + fn.guid, lastToggle + 1 );
4454
4455				// Make sure that clicks stop
4456				event.preventDefault();
4457
4458				// and execute the function
4459				return args[ lastToggle ].apply( this, arguments ) || false;
4460			};
4461
4462		// link all the functions, so any of them can unbind this click handler
4463		toggler.guid = guid;
4464		while ( i < args.length ) {
4465			args[ i++ ].guid = guid;
4466		}
4467
4468		return this.click( toggler );
4469	},
4470
4471	hover: function( fnOver, fnOut ) {
4472		return this.mouseenter( fnOver ).mouseleave( fnOut || fnOver );
4473	}
4474});
4475
4476jQuery.each( ("blur focus focusin focusout load resize scroll unload click dblclick " +
4477	"mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave " +
4478	"change select submit keydown keypress keyup error contextmenu").split(" "), function( i, name ) {
4479
4480	// Handle event binding
4481	jQuery.fn[ name ] = function( data, fn ) {
4482		if ( fn == null ) {
4483			fn = data;
4484			data = null;
4485		}
4486
4487		return arguments.length > 0 ?
4488			this.on( name, null, data, fn ) :
4489			this.trigger( name );
4490	};
4491
4492	if ( jQuery.attrFn ) {
4493		jQuery.attrFn[ name ] = true;
4494	}
4495
4496	if ( rkeyEvent.test( name ) ) {
4497		jQuery.event.fixHooks[ name ] = jQuery.event.keyHooks;
4498	}
4499
4500	if ( rmouseEvent.test( name ) ) {
4501		jQuery.event.fixHooks[ name ] = jQuery.event.mouseHooks;
4502	}
4503});
4504
4505
4506
4507/*!
4508 * Sizzle CSS Selector Engine
4509 *  Copyright 2011, The Dojo Foundation
4510 *  Released under the MIT, BSD, and GPL Licenses.
4511 *  More information: http://sizzlejs.com/
4512 */
4513(function(){
4514
4515var chunker = /((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^\[\]]*\]|['"][^'"]*['"]|[^\[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g,
4516	expando = "sizcache" + (Math.random() + '').replace('.', ''),
4517	done = 0,
4518	toString = Object.prototype.toString,
4519	hasDuplicate = false,
4520	baseHasDuplicate = true,
4521	rBackslash = /\\/g,
4522	rReturn = /\r\n/g,
4523	rNonWord = /\W/;
4524
4525// Here we check if the JavaScript engine is using some sort of
4526// optimization where it does not always call our comparision
4527// function. If that is the case, discard the hasDuplicate value.
4528//   Thus far that includes Google Chrome.
4529[0, 0].sort(function() {
4530	baseHasDuplicate = false;
4531	return 0;
4532});
4533
4534var Sizzle = function( selector, context, results, seed ) {
4535	results = results || [];
4536	context = context || document;
4537
4538	var origContext = context;
4539
4540	if ( context.nodeType !== 1 && context.nodeType !== 9 ) {
4541		return [];
4542	}
4543	
4544	if ( !selector || typeof selector !== "string" ) {
4545		return results;
4546	}
4547
4548	var m, set, checkSet, extra, ret, cur, pop, i,
4549		prune = true,
4550		contextXML = Sizzle.isXML( context ),
4551		parts = [],
4552		soFar = selector;
4553	
4554	// Reset the position of the chunker regexp (start from head)
4555	do {
4556		chunker.exec( "" );
4557		m = chunker.exec( soFar );
4558
4559		if ( m ) {
4560			soFar = m[3];
4561		
4562			parts.push( m[1] );
4563		
4564			if ( m[2] ) {
4565				extra = m[3];
4566				break;
4567			}
4568		}
4569	} while ( m );
4570
4571	if ( parts.length > 1 && origPOS.exec( selector ) ) {
4572
4573		if ( parts.length === 2 && Expr.relative[ parts[0] ] ) {
4574			set = posProcess( parts[0] + parts[1], context, seed );
4575
4576		} else {
4577			set = Expr.relative[ parts[0] ] ?
4578				[ context ] :
4579				Sizzle( parts.shift(), context );
4580
4581			while ( parts.length ) {
4582				selector = parts.shift();
4583
4584				if ( Expr.relative[ selector ] ) {
4585					selector += parts.shift();
4586				}
4587				
4588				set = posProcess( selector, set, seed );
4589			}
4590		}
4591
4592	} else {
4593		// Take a shortcut and set the context if the root selector is an ID
4594		// (but not if it'll be faster if the inner selector is an ID)
4595		if ( !seed && parts.length > 1 && context.nodeType === 9 && !contextXML &&
4596				Expr.match.ID.test(parts[0]) && !Expr.match.ID.test(parts[parts.length - 1]) ) {
4597
4598			ret = Sizzle.find( parts.shift(), context, contextXML );
4599			context = ret.expr ?
4600				Sizzle.filter( ret.expr, ret.set )[0] :
4601				ret.set[0];
4602		}
4603
4604		if ( context ) {
4605			ret = seed ?
4606				{ expr: parts.pop(), set: makeArray(seed) } :
4607				Sizzle.find( parts.pop(), parts.length === 1 && (parts[0] === "~" || parts[0] === "+") && context.parentNode ? context.parentNode : context, contextXML );
4608
4609			set = ret.expr ?
4610				Sizzle.filter( ret.expr, ret.set ) :
4611				ret.set;
4612
4613			if ( parts.length > 0 ) {
4614				checkSet = makeArray( set );
4615
4616			} else {
4617				prune = false;
4618			}
4619
4620			while ( parts.length ) {
4621				cur = parts.pop();
4622				pop = cur;
4623
4624				if ( !Expr.relative[ cur ] ) {
4625					cur = "";
4626				} else {
4627					pop = parts.pop();
4628				}
4629
4630				if ( pop == null ) {
4631					pop = context;
4632				}
4633
4634				Expr.relative[ cur ]( checkSet, pop, contextXML );
4635			}
4636
4637		} else {
4638			checkSet = parts = [];
4639		}
4640	}
4641
4642	if ( !checkSet ) {
4643		checkSet = set;
4644	}
4645
4646	if ( !checkSet ) {
4647		Sizzle.error( cur || selector );
4648	}
4649
4650	if ( toString.call(checkSet) === "[object Array]" ) {
4651		if ( !prune ) {
4652			results.push.apply( results, checkSet );
4653
4654		} else if ( context && context.nodeType === 1 ) {
4655			for ( i = 0; checkSet[i] != null; i++ ) {
4656				if ( checkSet[i] && (checkSet[i] === true || checkSet[i].nodeType === 1 && Sizzle.contains(context, checkSet[i])) ) {
4657					results.push( set[i] );
4658				}
4659			}
4660
4661		} else {
4662			for ( i = 0; checkSet[i] != null; i++ ) {
4663				if ( checkSet[i] && checkSet[i].nodeType === 1 ) {
4664					results.push( set[i] );
4665				}
4666			}
4667		}
4668
4669	} else {
4670		makeArray( checkSet, results );
4671	}
4672
4673	if ( extra ) {
4674		Sizzle( extra, origContext, results, seed );
4675		Sizzle.uniqueSort( results );
4676	}
4677
4678	return results;
4679};
4680
4681Sizzle.uniqueSort = function( results ) {
4682	if ( sortOrder ) {
4683		hasDuplicate = baseHasDuplicate;
4684		results.sort( sortOrder );
4685
4686		if ( hasDuplicate ) {
4687			for ( var i = 1; i < results.length; i++ ) {
4688				if ( results[i] === results[ i - 1 ] ) {
4689					results.splice( i--, 1 );
4690				}
4691			}
4692		}
4693	}
4694
4695	return results;
4696};
4697
4698Sizzle.matches = function( expr, set ) {
4699	return Sizzle( expr, null, null, set );
4700};
4701
4702Sizzle.matchesSelector = function( node, expr ) {
4703	return Sizzle( expr, null, null, [node] ).length > 0;
4704};
4705
4706Sizzle.find = function( expr, context, isXML ) {
4707	var set, i, len, match, type, left;
4708
4709	if ( !expr ) {
4710		return [];
4711	}
4712
4713	for ( i = 0, len = Expr.order.length; i < len; i++ ) {
4714		type = Expr.order[i];
4715		
4716		if ( (match = Expr.leftMatch[ type ].exec( expr )) ) {
4717			left = match[1];
4718			match.splice( 1, 1 );
4719
4720			if ( left.substr( left.length - 1 ) !== "\\" ) {
4721				match[1] = (match[1] || "").replace( rBackslash, "" );
4722				set = Expr.find[ type ]( match, context, isXML );
4723
4724				if ( set != null ) {
4725					expr = expr.replace( Expr.match[ type ], "" );
4726					break;
4727				}
4728			}
4729		}
4730	}
4731
4732	if ( !set ) {
4733		set = typeof context.getElementsByTagName !== "undefined" ?
4734			context.getElementsByTagName( "*" ) :
4735			[];
4736	}
4737
4738	return { set: set, expr: expr };
4739};
4740
4741Sizzle.filter = function( expr, set, inplace, not ) {
4742	var match, anyFound,
4743		type, found, item, filter, left,
4744		i, pass,
4745		old = expr,
4746		result = [],
4747		curLoop = set,
4748		isXMLFilter = set && set[0] && Sizzle.isXML( set[0] );
4749
4750	while ( expr && set.length ) {
4751		for ( type in Expr.filter ) {
4752			if ( (match = Expr.leftMatch[ type ].exec( expr )) != null && match[2] ) {
4753				filter = Expr.filter[ type ];
4754				left = match[1];
4755
4756				anyFound = false;
4757
4758				match.splice(1,1);
4759
4760				if ( left.substr( left.length - 1 ) === "\\" ) {
4761					continue;
4762				}
4763
4764				if ( curLoop === result ) {
4765					result = [];
4766				}
4767
4768				if ( Expr.preFilter[ type ] ) {
4769					match = Expr.preFilter[ type ]( match, curLoop, inplace, result, not, isXMLFilter );
4770
4771					if ( !match ) {
4772						anyFound = found = true;
4773
4774					} else if ( match === true ) {
4775						continue;
4776					}
4777				}
4778
4779				if ( match ) {
4780					for ( i = 0; (item = curLoop[i]) != null; i++ ) {
4781						if ( item ) {
4782							found = filter( item, match, i, curLoop );
4783							pass = not ^ found;
4784
4785							if ( inplace && found != null ) {
4786								if ( pass ) {
4787									anyFound = true;
4788
4789								} else {
4790									curLoop[i] = false;
4791								}
4792
4793							} else if ( pass ) {
4794								result.push( item );
4795								anyFound = true;
4796							}
4797						}
4798					}
4799				}
4800
4801				if ( found !== undefined ) {
4802					if ( !inplace ) {
4803						curLoop = result;
4804					}
4805
4806					expr = expr.replace( Expr.match[ type ], "" );
4807
4808					if ( !anyFound ) {
4809						return [];
4810					}
4811
4812					break;
4813				}
4814			}
4815		}
4816
4817		// Improper expression
4818		if ( expr === old ) {
4819			if ( anyFound == null ) {
4820				Sizzle.error( expr );
4821
4822			} else {
4823				break;
4824			}
4825		}
4826
4827		old = expr;
4828	}
4829
4830	return curLoop;
4831};
4832
4833Sizzle.error = function( msg ) {
4834	throw new Error( "Syntax error, unrecognized expression: " + msg );
4835};
4836
4837/**
4838 * Utility function for retreiving the text value of an array of DOM nodes
4839 * @param {Array|Element} elem
4840 */
4841var getText = Sizzle.getText = function( elem ) {
4842    var i, node,
4843		nodeType = elem.nodeType,
4844		ret = "";
4845
4846	if ( nodeType ) {
4847		if ( nodeType === 1 || nodeType === 9 ) {
4848			// Use textContent || innerText for elements
4849			if ( typeof elem.textContent === 'string' ) {
4850				return elem.textContent;
4851			} else if ( typeof elem.innerText === 'string' ) {
4852				// Replace IE's carriage returns
4853				return elem.innerText.replace( rReturn, '' );
4854			} else {
4855				// Traverse it's children
4856				for ( elem = elem.firstChild; elem; elem = elem.nextSibling) {
4857					ret += getText( elem );
4858				}
4859			}
4860		} else if ( nodeType === 3 || nodeType === 4 ) {
4861			return elem.nodeValue;
4862		}
4863	} else {
4864
4865		// If no nodeType, this is expected to be an array
4866		for ( i = 0; (node = elem[i]); i++ ) {
4867			// Do not traverse comment nodes
4868			if ( node.nodeType !== 8 ) {
4869				ret += getText( node );
4870			}
4871		}
4872	}
4873	return ret;
4874};
4875
4876var Expr = Sizzle.selectors = {
4877	order: [ "ID", "NAME", "TAG" ],
4878
4879	match: {
4880		ID: /#((?:[\w\u00c0-\uFFFF\-]|\\.)+)/,
4881		CLASS: /\.((?:[\w\u00c0-\uFFFF\-]|\\.)+)/,
4882		NAME: /\[name=['"]*((?:[\w\u00c0-\uFFFF\-]|\\.)+)['"]*\]/,
4883		ATTR: /\[\s*((?:[\w\u00c0-\uFFFF\-]|\\.)+)\s*(?:(\S?=)\s*(?:(['"])(.*?)\3|(#?(?:[\w\u00c0-\uFFFF\-]|\\.)*)|)|)\s*\]/,
4884		TAG: /^((?:[\w\u00c0-\uFFFF\*\-]|\\.)+)/,
4885		CHILD: /:(only|nth|last|first)-child(?:\(\s*(even|odd|(?:[+\-]?\d+|(?:[+\-]?\d*)?n\s*(?:[+\-]\s*\d+)?))\s*\))?/,
4886		POS: /:(nth|eq|gt|lt|first|last|even|odd)(?:\((\d*)\))?(?=[^\-]|$)/,
4887		PSEUDO: /:((?:[\w\u00c0-\uFFFF\-]|\\.)+)(?:\((['"]?)((?:\([^\)]+\)|[^\(\)]*)+)\2\))?/
4888	},
4889
4890	leftMatch: {},
4891
4892	attrMap: {
4893		"class": "className",
4894		"for": "htmlFor"
4895	},
4896
4897	attrHandle: {
4898		href: function( elem ) {
4899			return elem.getAttribute( "href" );
4900		},
4901		type: function( elem ) {
4902			return elem.getAttribute( "type" );
4903		}
4904	},
4905
4906	relative: {
4907		"+": function(checkSet, part){
4908			var isPartStr = typeof part === "string",
4909				isTag = isPartStr && !rNonWord.test( part ),
4910				isPartStrNotTag = isPartStr && !isTag;
4911
4912			if ( isTag ) {
4913				part = part.toLowerCase();
4914			}
4915
4916			for ( var i = 0, l = checkSet.length, elem; i < l; i++ ) {
4917				if ( (elem = checkSet[i]) ) {
4918					while ( (elem = elem.previousSibling) && elem.nodeType !== 1 ) {}
4919
4920					checkSet[i] = isPartStrNotTag || elem && elem.nodeName.toLowerCase() === part ?
4921						elem || false :
4922						elem === part;
4923				}
4924			}
4925
4926			if ( isPartStrNotTag ) {
4927				Sizzle.filter( part, checkSet, true );
4928			}
4929		},
4930
4931		">": function( checkSet, part ) {
4932			var elem,
4933				isPartStr = typeof part === "string",
4934				i = 0,
4935				l = checkSet.length;
4936
4937			if ( isPartStr && !rNonWord.test( part ) ) {
4938				part = part.toLowerCase();
4939
4940				for ( ; i < l; i++ ) {
4941					elem = checkSet[i];
4942
4943					if ( elem ) {
4944						var parent = elem.parentNode;
4945						checkSet[i] = parent.nodeName.toLowerCase() === part ? parent : false;
4946					}
4947				}
4948
4949			} else {
4950				for ( ; i < l; i++ ) {
4951					elem = checkSet[i];
4952
4953					if ( elem ) {
4954						checkSet[i] = isPartStr ?
4955							elem.parentNode :
4956							elem.parentNode === part;
4957					}
4958				}
4959
4960				if ( isPartStr ) {
4961					Sizzle.filter( part, checkSet, true );
4962				}
4963			}
4964		},
4965
4966		"": function(checkSet, part, isXML){
4967			var nodeCheck,
4968				doneName = done++,
4969				checkFn = dirCheck;
4970
4971			if ( typeof part === "string" && !rNonWord.test( part ) ) {
4972				part = part.toLowerCase();
4973				nodeCheck = part;
4974				checkFn = dirNodeCheck;
4975			}
4976
4977			checkFn( "parentNode", part, doneName, checkSet, nodeCheck, isXML );
4978		},
4979
4980		"~": function( checkSet, part, isXML ) {
4981			var nodeCheck,
4982				doneName = done++,
4983				checkFn = dirCheck;
4984
4985			if ( typeof part === "string" && !rNonWord.test( part ) ) {
4986				part = part.toLowerCase();
4987				nodeCheck = part;
4988				checkFn = dirNodeCheck;
4989			}
4990
4991			checkFn( "previousSibling", part, doneName, checkSet, nodeCheck, isXML );
4992		}
4993	},
4994
4995	find: {
4996		ID: function( match, context, isXML ) {
4997			if ( typeof context.getElementById !== "undefined" && !isXML ) {
4998				var m = context.getElementById(match[1]);
4999				// Check parentNode to catch when Blackberry 4.6 returns
5000				// nodes that are no longer in the document #6963
5001				return m && m.parentNode ? [m] : [];
5002			}
5003		},
5004
5005		NAME: function( match, context ) {
5006			if ( typeof context.getElementsByName !== "undefined" ) {
5007				var ret = [],
5008					results = context.getElementsByName( match[1] );
5009
5010				for ( var i = 0, l = results.length; i < l; i++ ) {
5011					if ( results[i].getAttribute("name") === match[1] ) {
5012						ret.push( results[i] );
5013					}
5014				}
5015
5016				return ret.length === 0 ? null : ret;
5017			}
5018		},
5019
5020		TAG: function( match, context ) {
5021			if ( typeof context.getElementsByTagName !== "undefined" ) {
5022				return context.getElementsByTagName( match[1] );
5023			}
5024		}
5025	},
5026	preFilter: {
5027		CLASS: function( match, curLoop, inplace, result, not, isXML ) {
5028			match = " " + match[1].replace( rBackslash, "" ) + " ";
5029
5030			if ( isXML ) {
5031				return match;
5032			}
5033
5034			for ( var i = 0, elem; (elem = curLoop[i]) != null; i++ ) {
5035				if ( elem ) {
5036					if ( not ^ (elem.className && (" " + elem.className + " ").replace(/[\t\n\r]/g, " ").indexOf(match) >= 0) ) {
5037						if ( !inplace ) {
5038							result.push( elem );
5039						}
5040
5041					} else if ( inplace ) {
5042						curLoop[i] = false;
5043					}
5044				}
5045			}
5046
5047			return false;
5048		},
5049
5050		ID: function( match ) {
5051			return match[1].replace( rBackslash, "" );
5052		},
5053
5054		TAG: function( match, curLoop ) {
5055			return match[1].replace( rBackslash, "" ).toLowerCase();
5056		},
5057
5058		CHILD: function( match ) {
5059			if ( match[1] === "nth" ) {
5060				if ( !match[2] ) {
5061					Sizzle.error( match[0] );
5062				}
5063
5064				match[2] = match[2].replace(/^\+|\s*/g, '');
5065
5066				// parse equations like 'even', 'odd', '5', '2n', '3n+2', '4n-1', '-n+6'
5067				var test = /(-?)(\d*)(?:n([+\-]?\d*))?/.exec(
5068					match[2] === "even" && "2n" || match[2] === "odd" && "2n+1" ||
5069					!/\D/.test( match[2] ) && "0n+" + match[2] || match[2]);
5070
5071				// calculate the numbers (first)n+(last) including if they are negative
5072				match[2] = (test[1] + (test[2] || 1)) - 0;
5073				match[3] = test[3] - 0;
5074			}
5075			else if ( match[2] ) {
5076				Sizzle.error( match[0] );
5077			}
5078
5079			// TODO: Move to normal caching system
5080			match[0] = done++;
5081
5082			return match;
5083		},
5084
5085		ATTR: function( match, curLoop, inplace, result, not, isXML ) {
5086			var name = match[1] = match[1].replace( rBackslash, "" );
5087			
5088			if ( !isXML && Expr.attrMap[name] ) {
5089				match[1] = Expr.attrMap[name];
5090			}
5091
5092			// Handle if an un-quoted value was used
5093			match[4] = ( match[4] || match[5] || "" ).replace( rBackslash, "" );
5094
5095			if ( match[2] === "~=" ) {
5096				match[4] = " " + match[4] + " ";
5097			}
5098
5099			return match;
5100		},
5101
5102		PSEUDO: function( match, curLoop, inplace, result, not ) {
5103			if ( match[1] === "not" ) {
5104				// If we're dealing with a complex expression, or a simple one
5105				if ( ( chunker.exec(match[3]) || "" ).length > 1 || /^\w/.test(match[3]) ) {
5106					match[3] = Sizzle(match[3], null, null, curLoop);
5107
5108				} else {
5109					var ret = Sizzle.filter(match[3], curLoop, inplace, true ^ not);
5110
5111					if ( !inplace ) {
5112						result.push.apply( result, ret );
5113					}
5114
5115					return false;
5116				}
5117
5118			} else if ( Expr.match.POS.test( match[0] ) || Expr.match.CHILD.test( match[0] ) ) {
5119				return true;
5120			}
5121			
5122			return match;
5123		},
5124
5125		POS: function( match ) {
5126			match.unshift( true );
5127
5128			return match;
5129		}
5130	},
5131	
5132	filters: {
5133		enabled: function( elem ) {
5134			return elem.disabled === false && elem.type !== "hidden";
5135		},
5136
5137		disabled: function( elem ) {
5138			return elem.disabled === true;
5139		},
5140
5141		checked: function( elem ) {
5142			return elem.checked === true;
5143		},
5144		
5145		selected: function( elem ) {
5146			// Accessing this property makes selected-by-default
5147			// options in Safari work properly
5148			if ( elem.parentNode ) {
5149				elem.parentNode.selectedIndex;
5150			}
5151			
5152			return elem.selected === true;
5153		},
5154
5155		parent: function( elem ) {
5156			return !!elem.firstChild;
5157		},
5158
5159		empty: function( elem ) {
5160			return !elem.firstChild;
5161		},
5162
5163		has: function( elem, i, match ) {
5164			return !!Sizzle( match[3], elem ).length;
5165		},
5166
5167		header: function( elem ) {
5168			return (/h\d/i).test( elem.nodeName );
5169		},
5170
5171		text: function( elem ) {
5172			var attr = elem.getAttribute( "type" ), type = elem.type;
5173			// IE6 and 7 will map elem.type to 'text' for new HTML5 types (search, etc) 
5174			// use getAttribute instead to test this case
5175			return elem.nodeName.toLowerCase() === "input" && "text" === type && ( attr === type || attr === null );
5176		},
5177
5178		radio: function( elem ) {
5179			return elem.nodeName.toLowerCase() === "input" && "radio" === elem.type;
5180		},
5181
5182		checkbox: function( elem ) {
5183			return elem.nodeName.toLowerCase() === "input" && "checkbox" === elem.type;
5184		},
5185
5186		file: function( elem ) {
5187			return elem.nodeName.toLowerCase() === "input" && "file" === elem.type;
5188		},
5189
5190		password: function( elem ) {
5191			return elem.nodeName.toLowerCase() === "input" && "password" === elem.type;
5192		},
5193
5194		submit: function( elem ) {
5195			var name = elem.nodeName.toLowerCase();
5196			return (name === "input" || name === "button") && "submit" === elem.type;
5197		},
5198
5199		image: function( elem ) {
5200			return elem.nodeName.toLowerCase() === "input" && "image" === elem.type;
5201		},
5202
5203		reset: function( elem ) {
5204			var name = elem.nodeName.toLowerCase();
5205			return (name === "input" || name === "button") && "reset" === elem.type;
5206		},
5207
5208		button: function( elem ) {
5209			var name = elem.nodeName.toLowerCase();
5210			return name === "input" && "button" === elem.type || name === "button";
5211		},
5212
5213		input: function( elem ) {
5214			return (/input|select|textarea|button/i).test( elem.nodeName );
5215		},
5216
5217		focus: function( elem ) {
5218			return elem === elem.ownerDocument.activeElement;
5219		}
5220	},
5221	setFilters: {
5222		first: function( elem, i ) {
5223			return i === 0;
5224		},
5225
5226		last: function( elem, i, match, array ) {
5227			return i === array.length - 1;
5228		},
5229
5230		even: function( elem, i ) {
5231			return i % 2 === 0;
5232		},
5233
5234		odd: function( elem, i ) {
5235			return i % 2 === 1;
5236		},
5237
5238		lt: function( elem, i, match ) {
5239			return i < match[3] - 0;
5240		},
5241
5242		gt: function( elem, i, match ) {
5243			return i > match[3] - 0;
5244		},
5245
5246		nth: function( elem, i, match ) {
5247			return match[3] - 0 === i;
5248		},
5249
5250		eq: function( elem, i, match ) {
5251			return match[3] - 0 === i;
5252		}
5253	},
5254	filter: {
5255		PSEUDO: function( elem, match, i, array ) {
5256			var name = match[1],
5257				filter = Expr.filters[ name ];
5258
5259			if ( filter ) {
5260				return filter( elem, i, match, array );
5261
5262			} else if ( name === "contains" ) {
5263				return (elem.textContent || elem.innerText || getText([ elem ]) || "").indexOf(match[3]) >= 0;
5264
5265			} else if ( name === "not" ) {
5266				var not = match[3];
5267
5268				for ( var j = 0, l = not.length; j < l; j++ ) {
5269					if ( not[j] === elem ) {
5270						return false;
5271					}
5272				}
5273
5274				return true;
5275
5276			} else {
5277				Sizzle.error( name );
5278			}
5279		},
5280
5281		CHILD: function( elem, match ) {
5282			var first, last,
5283				doneName, parent, cache,
5284				count, diff,
5285				type = match[1],
5286				node = elem;
5287
5288			switch ( type ) {
5289				case "only":
5290				case "first":
5291					while ( (node = node.previousSibling) )	 {
5292						if ( node.nodeType === 1 ) { 
5293							return false; 
5294						}
5295					}
5296
5297					if ( type === "first" ) { 
5298						return true; 
5299					}
5300
5301					node = elem;
5302
5303				case "last":
5304					while ( (node = node.nextSibling) )	 {
5305						if ( node.nodeType === 1 ) { 
5306							return false; 
5307						}
5308					}
5309
5310					return true;
5311
5312				case "nth":
5313					first = match[2];
5314					last = match[3];
5315
5316					if ( first === 1 && last === 0 ) {
5317						return true;
5318					}
5319					
5320					doneName = match[0];
5321					parent = elem.parentNode;
5322	
5323					if ( parent && (parent[ expando ] !== doneName || !elem.nodeIndex) ) {
5324						count = 0;
5325						
5326						for ( node = parent.firstChild; node; node = node.nextSibling ) {
5327							if ( node.nodeType === 1 ) {
5328								node.nodeIndex = ++count;
5329							}
5330						} 
5331
5332						parent[ expando ] = doneName;
5333					}
5334					
5335					diff = elem.nodeIndex - last;
5336
5337					if ( first === 0 ) {
5338						return diff === 0;
5339
5340					} else {
5341						return ( diff % first === 0 && diff / first >= 0 );
5342					}
5343			}
5344		},
5345
5346		ID: function( elem, match ) {
5347			return elem.nodeType === 1 && elem.getAttribute("id") === match;
5348		},
5349
5350		TAG: function( elem, match ) {
5351			return (match === "*" && elem.nodeType === 1) || !!elem.nodeName && elem.nodeName.toLowerCase() === match;
5352		},
5353		
5354		CLASS: function( elem, match ) {
5355			return (" " + (elem.className || elem.getAttribute("class")) + " ")
5356				.indexOf( match ) > -1;
5357		},
5358
5359		ATTR: function( elem, match ) {
5360			var name = match[1],
5361				result = Sizzle.attr ?
5362					Sizzle.attr( elem, name ) :
5363					Expr.attrHandle[ name ] ?
5364					Expr.attrHandle[ name ]( elem ) :
5365					elem[ name ] != null ?
5366						elem[ name ] :
5367						elem.getAttribute( name ),
5368				value = result + "",
5369				type = match[2],
5370				check = match[4];
5371
5372			return result == null ?
5373				type === "!=" :
5374				!type && Sizzle.attr ?
5375				result != null :
5376				type === "=" ?
5377				value === check :
5378				type === "*=" ?
5379				value.indexOf(check) >= 0 :
5380				type === "~=" ?
5381				(" " + value + " ").indexOf(check) >= 0 :
5382				!check ?
5383				value && result !== false :
5384				type === "!=" ?
5385				value !== check :
5386				type === "^=" ?
5387				value.indexOf(check) === 0 :
5388				type === "$=" ?
5389				value.substr(value.length - check.length) === check :
5390				type === "|=" ?
5391				value === check || value.substr(0, check.length + 1) === check + "-" :
5392				false;
5393		},
5394
5395		POS: function( elem, match, i, array ) {
5396			var name = match[2],
5397				filter = Expr.setFilters[ name ];
5398
5399			if ( filter ) {
5400				return filter( elem, i, match, array );
5401			}
5402		}
5403	}
5404};
5405
5406var origPOS = Expr.match.POS,
5407	fescape = function(all, num){
5408		return "\\" + (num - 0 + 1);
5409	};
5410
5411for ( var type in Expr.match ) {
5412	Expr.match[ type ] = new RegExp( Expr.match[ type ].source + (/(?![^\[]*\])(?![^\(]*\))/.source) );
5413	Expr.leftMatch[ type ] = new RegExp( /(^(?:.|\r|\n)*?)/.source + Expr.match[ type ].source.replace(/\\(\d+)/g, fescape) );
5414}
5415
5416var makeArray = function( array, results ) {
5417	array = Array.prototype.slice.call( array, 0 );
5418
5419	if ( results ) {
5420		results.push.apply( results, array );
5421		return results;
5422	}
5423	
5424	return array;
5425};
5426
5427// Perform a simple check to determine if the browser is capable of
5428// converting a NodeList to an array using builtin methods.
5429// Also verifies that the returned array holds DOM nodes
5430// (which is not the case in the Blackberry browser)
5431try {
5432	Array.prototype.slice.call( document.documentElement.childNodes, 0 )[0].nodeType;
5433
5434// Provide a fallback method if it does not work
5435} catch( e ) {
5436	makeArray = function( array, results ) {
5437		var i = 0,
5438			ret = results || [];
5439
5440		if ( toString.call(array) === "[object Array]" ) {
5441			Array.prototype.push.apply( ret, array );
5442
5443		} else {
5444			if ( typeof array.length === "number" ) {
5445				for ( var l = array.length; i < l; i++ ) {
5446					ret.push( array[i] );
5447				}
5448
5449			} else {
5450				for ( ; array[i]; i++ ) {
5451					ret.push( array[i] );
5452				}
5453			}
5454		}
5455
5456		return ret;
5457	};
5458}
5459
5460var sortOrder, siblingCheck;
5461
5462if ( document.documentElement.compareDocumentPosition ) {
5463	sortOrder = function( a, b ) {
5464		if ( a === b ) {
5465			hasDuplicate = true;
5466			return 0;
5467		}
5468
5469		if ( !a.compareDocumentPosition || !b.compareDocumentPosition ) {
5470			return a.compareDocumentPosition ? -1 : 1;
5471		}
5472
5473		return a.compareDocumentPosition(b) & 4 ? -1 : 1;
5474	};
5475
5476} else {
5477	sortOrder = function( a, b ) {
5478		// The nodes are identical, we can exit early
5479		if ( a === b ) {
5480			hasDuplicate = true;
5481			return 0;
5482
5483		// Fallback to using sourceIndex (in IE) if it's available on both nodes
5484		} else if ( a.sourceIndex && b.sourceIndex ) {
5485			return a.sourceIndex - b.sourceIndex;
5486		}
5487
5488		var al, bl,
5489			ap = [],
5490			bp = [],
5491			aup = a.parentNode,
5492			bup = b.parentNode,
5493			cur = aup;
5494
5495		// If the nodes are siblings (or identical) we can do a quick check
5496		if ( aup === bup ) {
5497			return siblingCheck( a, b );
5498
5499		// If no parents were found then the nodes are disconnected
5500		} else if ( !aup ) {
5501			return -1;
5502
5503		} else if ( !bup ) {
5504			return 1;
5505		}
5506
5507		// Otherwise they're somewhere else in the tree so we need
5508		// to build up a full list of the parentNodes for comparison
5509		while ( cur ) {
5510			ap.unshift( cur );
5511			cur = cur.parentNode;
5512		}
5513
5514		cur = bup;
5515
5516		while ( cur ) {
5517			bp.unshift( cur );
5518			cur = cur.parentNode;
5519		}
5520
5521		al = ap.length;
5522		bl = bp.length;
5523
5524		// Start walking down the tree looking for a discrepancy
5525		for ( var i = 0; i < al && i < bl; i++ ) {
5526			if ( ap[i] !== bp[i] ) {
5527				return siblingCheck( ap[i], bp[i] );
5528			}
5529		}
5530
5531		// We ended someplace up the tree so do a sibling check
5532		return i === al ?
5533			siblingCheck( a, bp[i], -1 ) :
5534			siblingCheck( ap[i], b, 1 );
5535	};
5536
5537	siblingCheck = function( a, b, ret ) {
5538		if ( a === b ) {
5539			return ret;
5540		}
5541
5542		var cur = a.nextSibling;
5543
5544		while ( cur ) {
5545			if ( cur === b ) {
5546				return -1;
5547			}
5548
5549			cur = cur.nextSibling;
5550		}
5551
5552		return 1;
5553	};
5554}
5555
5556// Check to see if the browser returns elements by name when
5557// querying by getElementById (and provide a workaround)
5558(function(){
5559	// We're going to inject a fake input element with a specified name
5560	var form = document.createElement("div"),
5561		id = "script" + (new Date()).getTime(),
5562		root = document.documentElement;
5563
5564	form.innerHTML = "<a name='" + id + "'/>";
5565
5566	// Inject it into the root element, check its status, and remove it quickly
5567	root.insertBefore( form, root.firstChild );
5568
5569	// The workaround has to do additional checks after a getElementById
5570	// Which slows things down for other browsers (hence the branching)
5571	if ( document.getElementById( id ) ) {
5572		Expr.find.ID = function( match, context, isXML ) {
5573			if ( typeof context.getElementById !== "undefined" && !isXML ) {
5574				var m = context.getElementById(match[1]);
5575
5576				return m ?
5577					m.id === match[1] || typeof m.getAttributeNode !== "undefined" && m.getAttributeNode("id").nodeValue === match[1] ?
5578						[m] :
5579						undefined :
5580					[];
5581			}
5582		};
5583
5584		Expr.filter.ID = function( elem, match ) {
5585			var node = typeof elem.getAttributeNode !== "undefined" && elem.getAttributeNode("id");
5586
5587			return elem.nodeType === 1 && node && node.nodeValue === match;
5588		};
5589	}
5590
5591	root.removeChild( form );
5592
5593	// release memory in IE
5594	root = form = null;
5595})();
5596
5597(function(){
5598	// Check to see if the browser returns only elements
5599	// when doing getElementsByTagName("*")
5600
5601	// Create a fake element
5602	var div = document.createElement("div");
5603	div.appendChild( document.createComment("") );
5604
5605	// Make sure no comments are found
5606	if ( div.getElementsByTagName("*").length > 0 ) {
5607		Expr.find.TAG = function( match, context ) {
5608			var results = context.getElementsByTagName( match[1] );
5609
5610			// Filter out possible comments
5611			if ( match[1] === "*" ) {
5612				var tmp = [];
5613
5614				for ( var i = 0; results[i]; i++ ) {
5615					if ( results[i].nodeType === 1 ) {
5616						tmp.push( results[i] );
5617					}
5618				}
5619
5620				results = tmp;
5621			}
5622
5623			return results;
5624		};
5625	}
5626
5627	// Check to see if an attribute returns normalized href attributes
5628	div.innerHTML = "<a href='#'></a>";
5629
5630	if ( div.firstChild && typeof div.firstChild.getAttribute !== "undefined" &&
5631			div.firstChild.getAttribute("href") !== "#" ) {
5632
5633		Expr.attrHandle.href = function( elem ) {
5634			return elem.getAttribute( "href", 2 );
5635		};
5636	}
5637
5638	// release memory in IE
5639	div = null;
5640})();
5641
5642if ( document.querySelectorAll ) {
5643	(function(){
5644		var oldSizzle = Sizzle,
5645			div = document.createElement("div"),
5646			id = "__sizzle__";
5647
5648		div.innerHTML = "<p class='TEST'></p>";
5649
5650		// Safari can't handle uppercase or unicode characters when
5651		// in quirks mode.
5652		if ( div.querySelectorAll && div.querySelectorAll(".TEST").length === 0 ) {
5653			return;
5654		}
5655	
5656		Sizzle = function( query, context, extra, seed ) {
5657			context = context || document;
5658
5659			// Only use querySelectorAll on non-XML documents
5660			// (ID selectors don't work in non-HTML documents)
5661			if ( !seed && !Sizzle.isXML(context) ) {
5662				// See if we find a selector to speed up
5663				var match = /^(\w+$)|^\.([\w\-]+$)|^#([\w\-]+$)/.exec( query );
5664				
5665				if ( match && (context.nodeType === 1 || context.nodeType === 9) ) {
5666					// Speed-up: Sizzle("TAG")
5667					if ( match[1] ) {
5668						return makeArray( context.getElementsByTagName( query ), extra );
5669					
5670					// Speed-up: Sizzle(".CLASS")
5671					} else if ( match[2] && Expr.find.CLASS && context.getElementsByClassName ) {
5672						return makeArray( context.getElementsByClassName( match[2] ), extra );
5673					}
5674				}
5675				
5676				if ( context.nodeType === 9 ) {
5677					// Speed-up: Sizzle("body")
5678					// The body element only exists once, optimize finding it
5679					if ( query === "body" && context.body ) {
5680						return makeArray( [ context.body ], extra );
5681						
5682					// Speed-up: Sizzle("#ID")
5683					} else if ( match && match[3] ) {
5684						var elem = context.getElementById( match[3] );
5685
5686						// Check parentNode to catch when Blackberry 4.6 returns
5687						// nodes that are no longer in the document #6963
5688						if ( elem && elem.parentNode ) {
5689							// Handle the case where IE and Opera return items
5690							// by name instead of ID
5691							if ( elem.id === match[3] ) {
5692								return makeArray( [ elem ], extra );
5693							}
5694							
5695						} else {
5696							return makeArray( [], extra );
5697						}
5698					}
5699					
5700					try {
5701						return makeArray( context.querySelectorAll(query), extra );
5702					} catch(qsaError) {}
5703
5704				// qSA works strangely on Element-rooted queries
5705				// We can work around this by specifying an extra ID on the root
5706				// and working up from there (Thanks to Andrew Dupont for the technique)
5707				// IE 8 doesn't work on object elements
5708				} else if ( context.nodeType === 1 && context.nodeName.toLowerCase() !== "object" ) {
5709					var oldContext = context,
5710						old = context.getAttribute( "id" ),
5711						nid = old || id,
5712						hasParent = context.parentNode,
5713						relativeHierarchySelector = /^\s*[+~]/.test( query );
5714
5715					if ( !old ) {
5716						context.setAttribute( "id", nid );
5717					} else {
5718						nid = nid.replace( /'/g, "\\$&" );
5719					}
5720					if ( relativeHierarchySelector && hasParent ) {
5721						context = context.parentNode;
5722					}
5723
5724					try {
5725						if ( !relativeHierarchySelector || hasParent ) {
5726							return makeArray( context.querySelectorAll( "[id='" + nid + "'] " + query ), extra );
5727						}
5728
5729					} catch(pseudoError) {
5730					} finally {
5731						if ( !old ) {
5732							oldContext.removeAttribute( "id" );
5733						}
5734					}
5735				}
5736			}
5737		
5738			return oldSizzle(query, context, extra, seed);
5739		};
5740
5741		for ( var prop in oldSizzle ) {
5742			Sizzle[ prop ] = oldSizzle[ prop ];
5743		}
5744
5745		// release memory in IE
5746		div = null;
5747	})();
5748}
5749
5750(function(){
5751	var html = document.documentElement,
5752		matches = html.matchesSelector || html.mozMatchesSelector || html.webkitMatchesSelector || html.msMatchesSelector;
5753
5754	if ( matches ) {
5755		// Check to see if it's possible to do matchesSelector
5756		// on a disconnected node (IE 9 fails this)
5757		var disconnectedMatch = !matches.call( document.createElement( "div" ), "div" ),
5758			pseudoWorks = false;
5759
5760		try {
5761			// This should fail with an exception
5762			// Gecko does not error, returns false instead
5763			matches.call( document.documentElement, "[test!='']:sizzle" );
5764	
5765		} catch( pseudoError ) {
5766			pseudoWorks = true;
5767		}
5768
5769		Sizzle.matchesSelector = function( node, expr ) {
5770			// Make sure that attribute selectors are quoted
5771			expr = expr.replace(/\=\s*([^'"\]]*)\s*\]/g, "='$1']");
5772
5773			if ( !Sizzle.isXML( node ) ) {
5774				try { 
5775					if ( pseudoWorks || !Expr.match.PSEUDO.test( expr ) && !/!=/.test( expr ) ) {
5776						var ret = matches.call( node, expr );
5777
5778						// IE 9's matchesSelector returns false on disconnected nodes
5779						if ( ret || !disconnectedMatch ||
5780								// As well, disconnected nodes are said to be in a document
5781								// fragment in IE 9, so check for that
5782								node.document && node.document.nodeType !== 11 ) {
5783							return ret;
5784						}
5785					}
5786				} catch(e) {}
5787			}
5788
5789			return Sizzle(expr, null, null, [node]).length > 0;
5790		};
5791	}
5792})();
5793
5794(function(){
5795	var div = document.createElement("div");
5796
5797	div.innerHTML = "<div class='test e'></div><div class='test'></div>";
5798
5799	// Opera can't find a second classname (in 9.6)
5800	// Also, make sure that getElementsByClassName actually exists
5801	if ( !div.getElementsByClassName || div.getElementsByClassName("e").length === 0 ) {
5802		return;
5803	}
5804
5805	// Safari caches class attributes, doesn't catch changes (in 3.2)
5806	div.lastChild.className = "e";
5807
5808	if ( div.getElementsByClassName("e").length === 1 ) {
5809		return;
5810	}
5811	
5812	Expr.order.splice(1, 0, "CLASS");
5813	Expr.find.CLASS = function( match, context, isXML ) {
5814		if ( typeof context.getElementsByClassName !== "undefined" && !isXML ) {
5815			return context.getElementsByClassName(match[1]);
5816		}
5817	};
5818
5819	// release memory in IE
5820	div = null;
5821})();
5822
5823function dirNodeCheck( dir, cur, doneName, checkSet, nodeCheck, isXML ) {
5824	for ( var i = 0, l = checkSet.length; i < l; i++ ) {
5825		var elem = checkSet[i];
5826
5827		if ( elem ) {
5828			var match = false;
5829
5830			elem = elem[dir];
5831
5832			while ( elem ) {
5833				if ( elem[ expando ] === doneName ) {
5834					match = checkSet[elem.sizset];
5835					break;
5836				}
5837
5838				if ( elem.nodeType === 1 && !isXML ){
5839					elem[ expando ] = doneName;
5840					elem.sizset = i;
5841				}
5842
5843				if ( elem.nodeName.toLowerCase() === cur ) {
5844					match = elem;
5845					break;
5846				}
5847
5848				elem = elem[dir];
5849			}
5850
5851			checkSet[i] = match;
5852		}
5853	}
5854}
5855
5856function dirCheck( dir, cur, doneName, checkSet, nodeCheck, isXML ) {
5857	for ( var i = 0, l = checkSet.length; i < l; i++ ) {
5858		var elem = checkSet[i];
5859
5860		if ( elem ) {
5861			var match = false;
5862			
5863			elem = elem[dir];
5864
5865			while ( elem ) {
5866				if ( elem[ expando ] === doneName ) {
5867					match = checkSet[elem.sizset];
5868					break;
5869				}
5870
5871				if ( elem.nodeType === 1 ) {
5872					if ( !isXML ) {
5873						elem[ expando ] = doneName;
5874						elem.sizset = i;
5875					}
5876
5877					if ( typeof cur !== "string" ) {
5878						if ( elem === cur ) {
5879							match = true;
5880							break;
5881						}
5882
5883					} else if ( Sizzle.filter( cur, [elem] ).length > 0 ) {
5884						match = elem;
5885						break;
5886					}
5887				}
5888
5889				elem = elem[dir];
5890			}
5891
5892			checkSet[i] = match;
5893		}
5894	}
5895}
5896
5897if ( document.documentElement.contains ) {
5898	Sizzle.contains = function( a, b ) {
5899		return a !== b && (a.contains ? a.contains(b) : true);
5900	};
5901
5902} else if ( document.documentElement.compareDocumentPosition ) {
5903	Sizzle.contains = function( a, b ) {
5904		return !!(a.compareDocumentPosition(b) & 16);
5905	};
5906
5907} else {
5908	Sizzle.contains = function() {
5909		return false;
5910	};
5911}
5912
5913Sizzle.isXML = function( elem ) {
5914	// documentElement is verified for cases where it doesn't yet exist
5915	// (such as loading iframes in IE - #4833) 
5916	var documentElement = (elem ? elem.ownerDocument || elem : 0).documentElement;
5917
5918	return documentElement ? documentElement.nodeName !== "HTML" : false;
5919};
5920
5921var posProcess = function( selector, context, seed ) {
5922	var match,
5923		tmpSet = [],
5924		later = "",
5925		root = context.nodeType ? [context] : context;
5926
5927	// Position selectors must be done after the filter
5928	// And so must :not(positional) so we move all PSEUDOs to the end
5929	while ( (match = Expr.match.PSEUDO.exec( selector )) ) {
5930		later += match[0];
5931		selector = selector.replace( Expr.match.PSEUDO, "" );
5932	}
5933
5934	selector = Expr.relative[selector] ? selector + "*" : selector;
5935
5936	for ( var i = 0, l = root.length; i < l; i++ ) {
5937		Sizzle( selector, root[i], tmpSet, seed );
5938	}
5939
5940	return Sizzle.filter( later, tmpSet );
5941};
5942
5943// EXPOSE
5944// Override sizzle attribute retrieval
5945Sizzle.attr = jQuery.attr;
5946Sizzle.selectors.attrMap = {};
5947jQuery.find = Sizzle;
5948jQuery.expr = Sizzle.selectors;
5949jQuery.expr[":"] = jQuery.expr.filters;
5950jQuery.unique = Sizzle.uniqueSort;
5951jQuery.text = Sizzle.getText;
5952jQuery.isXMLDoc = Sizzle.isXML;
5953jQuery.contains = Sizzle.contains;
5954
5955
5956})();
5957
5958
5959var runtil = /Until$/,
5960	rparentsprev = /^(?:parents|prevUntil|prevAll)/,
5961	// Note: This RegExp should be improved, or likely pulled from Sizzle
5962	rmultiselector = /,/,
5963	isSimple = /^.[^:#\[\.,]*$/,
5964	slice = Array.prototype.slice,
5965	POS = jQuery.expr.match.POS,
5966	// methods guaranteed to produce a unique set when starting from a unique set
5967	guaranteedUnique = {
5968		children: true,
5969		contents: true,
5970		next: true,
5971		prev: true
5972	};
5973
5974jQuery.fn.extend({
5975	find: function( selector ) {
5976		var self = this,
5977			i, l;
5978
5979		if ( typeof selector !== "string" ) {
5980			return jQuery( selector ).filter(function() {
5981				for ( i = 0, l = self.length; i < l; i++ ) {
5982					if ( jQuery.contains( self[ i ], this ) ) {
5983						return true;
5984					}
5985				}
5986			});
5987		}
5988
5989		var ret = this.pushStack( "", "find", selector ),
5990			length, n, r;
5991
5992		for ( i = 0, l = this.length; i < l; i++ ) {
5993			length = ret.length;
5994			jQuery.find( selector, this[i], ret );
5995
5996			if ( i > 0 ) {
5997				// Make sure that the results are unique
5998				for ( n = length; n < ret.length; n++ ) {
5999					for ( r = 0; r < length; r++ ) {
6000						if ( ret[r] === ret[n] ) {
6001							ret.splice(n--, 1);
6002							break;
6003						}
6004					}
6005				}
6006			}
6007		}
6008
6009		return ret;
6010	},
6011
6012	has: function( target ) {
6013		var targets = jQuery( target );
6014		return this.filter(function() {
6015			for ( var i = 0, l = targets.length; i < l; i++ ) {
6016				if ( jQuery.contains( this, targets[i] ) ) {
6017					return true;
6018				}
6019			}
6020		});
6021	},
6022
6023	not: function( selector ) {
6024		return this.pushStack( winnow(this, selector, false), "not", selector);
6025	},
6026
6027	filter: function( selector ) {
6028		return this.pushStack( winnow(this, selector, true), "filter", selector );
6029	},
6030
6031	is: function( selector ) {
6032		return !!selector && ( 
6033			typeof selector === "string" ?
6034				// If this is a positional selector, check membership in the returned set
6035				// so $("p:first").is("p:last") won't return true for a doc with two "p".
6036				POS.test( selector ) ? 
6037					jQuery( selector, this.context ).index( this[0] ) >= 0 :
6038					jQuery.filter( selector, this ).length > 0 :
6039				this.filter( selector ).length > 0 );
6040	},
6041
6042	closest: function( selectors, context ) {
6043		var ret = [], i, l, cur = this[0];
6044		
6045		// Array (deprecated as of jQuery 1.7)
6046		if ( jQuery.isArray( selectors ) ) {
6047			var level = 1;
6048
6049			while ( cur && cur.ownerDocument && cur !== context ) {
6050				for ( i = 0; i < selectors.length; i++ ) {
6051
6052					if ( jQuery( cur ).is( selectors[ i ] ) ) {
6053						ret.push({ selector: selectors[ i ], elem: cur, level: level });
6054					}
6055				}
6056
6057				cur = cur.parentNode;
6058				level++;
6059			}
6060
6061			return ret;
6062		}
6063
6064		// String
6065		var pos = POS.test( selectors ) || typeof selectors !== "string" ?
6066				jQuery( selectors, context || this.context ) :
6067				0;
6068
6069		for ( i = 0, l = this.length; i < l; i++ ) {
6070			cur = this[i];
6071
6072			while ( cur ) {
6073				if ( pos ? pos.index(cur) > -1 : jQuery.find.matchesSelector(cur, selectors) ) {
6074					ret.push( cur );
6075					break;
6076
6077				} else {
6078					cur = cur.parentNode;
6079					if ( !cur || !cur.ownerDocument || cur === context || cur.nodeType === 11 ) {
6080						break;
6081					}
6082				}
6083			}
6084		}
6085
6086		ret = ret.length > 1 ? jQuery.unique( ret ) : ret;
6087
6088		return this.pushStack( ret, "closest", selectors );
6089	},
6090
6091	// Determine the position of an element within
6092	// the matched set of elements
6093	index: function( elem ) {
6094
6095		// No argument, return index in parent
6096		if ( !elem ) {
6097			return ( this[0] && this[0].parentNode ) ? this.prevAll().length : -1;
6098		}
6099
6100		// index in selector
6101		if ( typeof elem === "string" ) {
6102			return jQuery.inArray( this[0], jQuery( elem ) );
6103		}
6104
6105		// Locate the position of the desired element
6106		return jQuery.inArray(
6107			// If it receives a jQuery object, the first element is used
6108			elem.jquery ? elem[0] : elem, this );
6109	},
6110
6111	add: function( selector, context ) {
6112		var set = typeof selector === "string" ?
6113				jQuery( selector, context ) :
6114				jQuery.makeArray( selector && selector.nodeType ? [ selector ] : selector ),
6115			all = jQuery.merge( this.get(), set );
6116
6117		return this.pushStack( isDisconnected( set[0] ) || isDisconnected( all[0] ) ?
6118			all :
6119			jQuery.unique( all ) );
6120	},
6121
6122	andSelf: function() {
6123		return this.add( this.prevObject );
6124	}
6125});
6126
6127// A painfully simple check to see if an element is disconnected
6128// from a document (should be improved, where feasible).
6129function isDisconnected( node ) {
6130	return !node || !node.parentNode || node.parentNode.nodeType === 11;
6131}
6132
6133jQuery.each({
6134	parent: function( elem ) {
6135		var parent = elem.parentNode;
6136		return parent && parent.nodeType !== 11 ? parent : null;
6137	},
6138	parents: function( elem ) {
6139		return jQuery.dir( elem, "parentNode" );
6140	},
6141	parentsUntil: function( elem, i, until ) {
6142		return jQuery.dir( elem, "parentNode", until );
6143	},
6144	next: function( elem ) {
6145		return jQuery.nth( elem, 2, "nextSibling" );
6146	},
6147	prev: function( elem ) {
6148		return jQuery.nth( elem, 2, "previousSibling" );
6149	},
6150	nextAll: function( elem ) {
6151		return jQuery.dir( elem, "nextSibling" );
6152	},
6153	prevAll: function( elem ) {
6154		return jQuery.dir( elem, "previousSibling" );
6155	},
6156	nextUntil: function( elem, i, until ) {
6157		return jQuery.dir( elem, "nextSibling", until );
6158	},
6159	prevUntil: function( elem, i, until ) {
6160		return jQuery.dir( elem, "previousSibling", until );
6161	},
6162	siblings: function( elem ) {
6163		return jQuery.sibling( elem.parentNode.firstChild, elem );
6164	},
6165	children: function( elem ) {
6166		return jQuery.sibling( elem.firstChild );
6167	},
6168	contents: function( elem ) {
6169		return jQuery.nodeName( elem, "iframe" ) ?
6170			elem.contentDocument || elem.contentWindow.document :
6171			jQuery.makeArray( elem.childNodes );
6172	}
6173}, function( name, fn ) {
6174	jQuery.fn[ name ] = function( until, selector ) {
6175		var ret = jQuery.map( this, fn, until );
6176
6177		if ( !runtil.test( name ) ) {
6178			selector = until;
6179		}
6180
6181		if ( selector && typeof selector === "string" ) {
6182			ret = jQuery.filter( selector, ret );
6183		}
6184
6185		ret = this.length > 1 && !guaranteedUnique[ name ] ? jQuery.unique( ret ) : ret;
6186
6187		if ( (this.length > 1 || rmultiselector.test( selector )) && rparentsprev.test( name ) ) {
6188			ret = ret.reverse();
6189		}
6190
6191		return this.pushStack( ret, name, slice.call( arguments ).join(",") );
6192	};
6193});
6194
6195jQuery.extend({
6196	filter: function( expr, elems, not ) {
6197		if ( not ) {
6198			expr = ":not(" + expr + ")";
6199		}
6200
6201		return elems.length === 1 ?
6202			jQuery.find.matchesSelector(elems[0], expr) ? [ elems[0] ] : [] :
6203			jQuery.find.matches(expr, elems);
6204	},
6205
6206	dir: function( elem, dir, until ) {
6207		var matched = [],
6208			cur = elem[ dir ];
6209
6210		while ( cur && cur.nodeType !== 9 && (until === undefined || cur.nodeType !== 1 || !jQuery( cur ).is( until )) ) {
6211			if ( cur.nodeType === 1 ) {
6212				matched.push( cur );
6213			}
6214			cur = cur[dir];
6215		}
6216		return matched;
6217	},
6218
6219	nth: function( cur, result, dir, elem ) {
6220		result = result || 1;
6221		var num = 0;
6222
6223		for ( ; cur; cur = cur[dir] ) {
6224			if ( cur.nodeType === 1 && ++num === result ) {
6225				break;
6226			}
6227		}
6228
6229		return cur;
6230	},
6231
6232	sibling: function( n, elem ) {
6233		var r = [];
6234
6235		for ( ; n; n = n.nextSibling ) {
6236			if ( n.nodeType === 1 && n !== elem ) {
6237				r.push( n );
6238			}
6239		}
6240
6241		return r;
6242	}
6243});
6244
6245// Implement the identical functionality for filter and not
6246function winnow( elements, qualifier, keep ) {
6247
6248	// Can't pass null or undefined to indexOf in Firefox 4
6249	// Set to 0 to skip string check
6250	qualifier = qualifier || 0;
6251
6252	if ( jQuery.isFunction( qualifier ) ) {
6253		return jQuery.grep(elements, function( elem, i ) {
6254			var retVal = !!qualifier.call( elem, i, elem );
6255			return retVal === keep;
6256		});
6257
6258	} else if ( qualifier.nodeType ) {
6259		return jQuery.grep(elements, function( elem, i ) {
6260			return ( elem === qualifier ) === keep;
6261		});
6262
6263	} else if ( typeof qualifier === "string" ) {
6264		var filtered = jQuery.grep(elements, function( elem ) {
6265			return elem.nodeType === 1;
6266		});
6267
6268		if ( isSimple.test( qualifier ) ) {
6269			return jQuery.filter(qualifier, filtered, !keep);
6270		} else {
6271			qualifier = jQuery.filter( qualifier, filtered );
6272		}
6273	}
6274
6275	return jQuery.grep(elements, function( elem, i ) {
6276		return ( jQuery.inArray( elem, qualifier ) >= 0 ) === keep;
6277	});
6278}
6279
6280
6281
6282
6283function createSafeFragment( document ) {
6284	var list = nodeNames.split( "|" ),
vendor: 3,540 bytes, lines 6285-6422
6285	safeFrag = document.createDocumentFragment();
6286
6287	if ( safeFrag.createElement ) {
6288		while ( list.length ) {
6289			safeFrag.createElement(
6290				list.pop()
6291			);
6292		}
6293	}
6294	return safeFrag;
6295}
6296
6297var nodeNames = "abbr|article|aside|audio|canvas|datalist|details|figcaption|figure|footer|" +
6298		"header|hgroup|mark|meter|nav|output|progress|section|summary|time|video",
6299	rinlinejQuery = / jQuery\d+="(?:\d+|null)"/g,
6300	rleadingWhitespace = /^\s+/,
6301	rxhtmlTag = /<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/ig,
6302	rtagName = /<([\w:]+)/,
6303	rtbody = /<tbody/i,
6304	rhtml = /<|&#?\w+;/,
6305	rnoInnerhtml = /<(?:script|style)/i,
6306	rnocache = /<(?:script|object|embed|option|style)/i,
6307	rnoshimcache = new RegExp("<(?:" + nodeNames + ")", "i"),
6308	// checked="checked" or checked
6309	rchecked = /checked\s*(?:[^=]|=\s*.checked.)/i,
6310	rscriptType = /\/(java|ecma)script/i,
6311	rcleanScript = /^\s*<!(?:\[CDATA\[|\-\-)/,
6312	wrapMap = {
6313		option: [ 1, "<select multiple='multiple'>", "</select>" ],
6314		legend: [ 1, "<fieldset>", "</fieldset>" ],
6315		thead: [ 1, "<table>", "</table>" ],
6316		tr: [ 2, "<table><tbody>", "</tbody></table>" ],
6317		td: [ 3, "<table><tbody><tr>", "</tr></tbody></table>" ],
6318		col: [ 2, "<table><tbody></tbody><colgroup>", "</colgroup></table>" ],
6319		area: [ 1, "<map>", "</map>" ],
6320		_default: [ 0, "", "" ]
6321	},
6322	safeFragment = createSafeFragment( document );
6323
6324wrapMap.optgroup = wrapMap.option;
6325wrapMap.tbody = wrapMap.tfoot = wrapMap.colgroup = wrapMap.caption = wrapMap.thead;
6326wrapMap.th = wrapMap.td;
6327
6328// IE can't serialize <link> and <script> tags normally
6329if ( !jQuery.support.htmlSerialize ) {
6330	wrapMap._default = [ 1, "div<div>", "</div>" ];
6331}
6332
6333jQuery.fn.extend({
6334	text: function( text ) {
6335		if ( jQuery.isFunction(text) ) {
6336			return this.each(function(i) {
6337				var self = jQuery( this );
6338
6339				self.text( text.call(this, i, self.text()) );
6340			});
6341		}
6342
6343		if ( typeof text !== "object" && text !== undefined ) {
6344			return this.empty().append( (this[0] && this[0].ownerDocument || document).createTextNode( text ) );
6345		}
6346
6347		return jQuery.text( this );
6348	},
6349
6350	wrapAll: function( html ) {
6351		if ( jQuery.isFunction( html ) ) {
6352			return this.each(function(i) {
6353				jQuery(this).wrapAll( html.call(this, i) );
6354			});
6355		}
6356
6357		if ( this[0] ) {
6358			// The elements to wrap the target around
6359			var wrap = jQuery( html, this[0].ownerDocument ).eq(0).clone(true);
6360
6361			if ( this[0].parentNode ) {
6362				wrap.insertBefore( this[0] );
6363			}
6364
6365			wrap.map(function() {
6366				var elem = this;
6367
6368				while ( elem.firstChild && elem.firstChild.nodeType === 1 ) {
6369					elem = elem.firstChild;
6370				}
6371
6372				return elem;
6373			}).append( this );
6374		}
6375
6376		return this;
6377	},
6378
6379	wrapInner: function( html ) {
6380		if ( jQuery.isFunction( html ) ) {
6381			return this.each(function(i) {
6382				jQuery(this).wrapInner( html.call(this, i) );
6383			});
6384		}
6385
6386		return this.each(function() {
6387			var self = jQuery( this ),
6388				contents = self.contents();
6389
6390			if ( contents.length ) {
6391				contents.wrapAll( html );
6392
6393			} else {
6394				self.append( html );
6395			}
6396		});
6397	},
6398
6399	wrap: function( html ) {
6400		var isFunction = jQuery.isFunction( html );
6401
6402		return this.each(function(i) {
6403			jQuery( this ).wrapAll( isFunction ? html.call(this, i) : html );
6404		});
6405	},
6406
6407	unwrap: function() {
6408		return this.parent().each(function() {
6409			if ( !jQuery.nodeName( this, "body" ) ) {
6410				jQuery( this ).replaceWith( this.childNodes );
6411			}
6412		}).end();
6413	},
6414
6415	append: function() {
6416		return this.domManip(arguments, true, function( elem ) {
6417			if ( this.nodeType === 1 ) {
6418				this.appendChild( elem );
6419			}
6420		});
6421	},
6422
6423	prepend: function() {
6424		return this.domManip(arguments, true, function( elem ) {
6425			if ( this.nodeType === 1 ) {
6426				this.insertBefore( elem, this.firstChild );
6427			}
6428		});
6429	},
6430
6431	before: function() {
6432		if ( this[0] && this[0].parentNode ) {
6433			return this.domManip(arguments, false, function( elem ) {
6434				this.parentNode.insertBefore( elem, this );
6435			});
6436		} else if ( arguments.length ) {
6437			var set = jQuery.clean( arguments );
6438			set.push.apply( set, this.toArray() );
6439			return this.pushStack( set, "before", arguments );
6440		}
6441	},
6442
6443	after: function() {
6444		if ( this[0] && this[0].parentNode ) {
6445			return this.domManip(arguments, false, function( elem ) {
6446				this.parentNode.insertBefore( elem, this.nextSibling );
6447			});
6448		} else if ( arguments.length ) {
6449			var set = this.pushStack( this, "after", arguments );
6450			set.push.apply( set, jQuery.clean(arguments) );
6451			return set;
6452		}
6453	},
6454
6455	// keepData is for internal use only--do not document
6456	remove: function( selector, keepData ) {
6457		for ( var i = 0, elem; (elem = this[i]) != null; i++ ) {
6458			if ( !selector || jQuery.filter( selector, [ elem ] ).length ) {
6459				if ( !keepData && elem.nodeType === 1 ) {
6460					jQuery.cleanData( elem.getElementsByTagName("*") );
6461					jQuery.cleanData( [ elem ] );
6462				}
6463
6464				if ( elem.parentNode ) {
6465					elem.parentNode.removeChild( elem );
6466				}
6467			}
6468		}
6469
6470		return this;
6471	},
6472
6473	empty: function() {
6474		for ( var i = 0, elem; (elem = this[i]) != null; i++ ) {
6475			// Remove element nodes and prevent memory leaks
6476			if ( elem.nodeType === 1 ) {
6477				jQuery.cleanData( elem.getElementsByTagName("*") );
6478			}
6479
6480			// Remove any remaining nodes
6481			while ( elem.firstChild ) {
6482				elem.removeChild( elem.firstChild );
6483			}
6484		}
6485
6486		return this;
6487	},
6488
6489	clone: function( dataAndEvents, deepDataAndEvents ) {
6490		dataAndEvents = dataAndEvents == null ? false : dataAndEvents;
6491		deepDataAndEvents = deepDataAndEvents == null ? dataAndEvents : deepDataAndEvents;
6492
6493		return this.map( function () {
6494			return jQuery.clone( this, dataAndEvents, deepDataAndEvents );
6495		});
6496	},
6497
6498	html: function( value ) {
6499		if ( value === undefined ) {
6500			return this[0] && this[0].nodeType === 1 ?
6501				this[0].innerHTML.replace(rinlinejQuery, "") :
6502				null;
6503
6504		// See if we can take a shortcut and just use innerHTML
6505		} else if ( typeof value === "string" && !rnoInnerhtml.test( value ) &&
6506			(jQuery.support.leadingWhitespace || !rleadingWhitespace.test( value )) &&
6507			!wrapMap[ (rtagName.exec( value ) || ["", ""])[1].toLowerCase() ] ) {
6508
6509			value = value.replace(rxhtmlTag, "<$1></$2>");
6510
6511			try {
6512				for ( var i = 0, l = this.length; i < l; i++ ) {
6513					// Remove element nodes and prevent memory leaks
6514					if ( this[i].nodeType === 1 ) {
6515						jQuery.cleanData( this[i].getElementsByTagName("*") );
6516						this[i].innerHTML = value;
6517					}
6518				}
6519
6520			// If using innerHTML throws an exception, use the fallback method
6521			} catch(e) {
6522				this.empty().append( value );
6523			}
6524
6525		} else if ( jQuery.isFunction( value ) ) {
6526			this.each(function(i){
6527				var self = jQuery( this );
6528
6529				self.html( value.call(this, i, self.html()) );
6530			});
6531
6532		} else {
6533			this.empty().append( value );
6534		}
6535
6536		return this;
6537	},
6538
6539	replaceWith: function( value ) {
6540		if ( this[0] && this[0].parentNode ) {
6541			// Make sure that the elements are removed from the DOM before they are inserted
6542			// this can help fix replacing a parent with child elements
6543			if ( jQuery.isFunction( value ) ) {
6544				return this.each(function(i) {
6545					var self = jQuery(this), old = self.html();
6546					self.replaceWith( value.call( this, i, old ) );
6547				});
6548			}
6549
6550			if ( typeof value !== "string" ) {
6551				value = jQuery( value ).detach();
6552			}
6553
6554			return this.each(function() {
6555				var next = this.nextSibling,
6556					parent = this.parentNode;
6557
6558				jQuery( this ).remove();
6559
6560				if ( next ) {
6561					jQuery(next).before( value );
6562				} else {
6563					jQuery(parent).append( value );
6564				}
6565			});
6566		} else {
6567			return this.length ?
6568				this.pushStack( jQuery(jQuery.isFunction(value) ? value() : value), "replaceWith", value ) :
6569				this;
6570		}
6571	},
6572
6573	detach: function( selector ) {
6574		return this.remove( selector, true );
6575	},
6576
6577	domManip: function( args, table, callback ) {
6578		var results, first, fragment, parent,
6579			value = args[0],
6580			scripts = [];
6581
6582		// We can't cloneNode fragments that contain checked, in WebKit
6583		if ( !jQuery.support.checkClone && arguments.length === 3 && typeof value === "string" && rchecked.test( value ) ) {
6584			return this.each(function() {
6585				jQuery(this).domManip( args, table, callback, true );
6586			});
6587		}
6588
6589		if ( jQuery.isFunction(value) ) {
6590			return this.each(function(i) {
6591				var self = jQuery(this);
6592				args[0] = value.call(this, i, table ? self.html() : undefined);
6593				self.domManip( args, table, callback );
6594			});
6595		}
6596
6597		if ( this[0] ) {
6598			parent = value && value.parentNode;
6599
6600			// If we're in a fragment, just use that instead of building a new one
6601			if ( jQuery.support.parentNode && parent && parent.nodeType === 11 && parent.childNodes.length === this.length ) {
6602				results = { fragment: parent };
6603
6604			} else {
6605				results = jQuery.buildFragment( args, this, scripts );
6606			}
6607
6608			fragment = results.fragment;
6609
6610			if ( fragment.childNodes.length === 1 ) {
6611				first = fragment = fragment.firstChild;
6612			} else {
6613				first = fragment.firstChild;
6614			}
6615
6616			if ( first ) {
6617				table = table && jQuery.nodeName( first, "tr" );
6618
6619				for ( var i = 0, l = this.length, lastIndex = l - 1; i < l; i++ ) {
6620					callback.call(
6621						table ?
6622							root(this[i], first) :
6623							this[i],
6624						// Make sure that we do not leak memory by inadvertently discarding
6625						// the original fragment (which might have attached data) instead of
6626						// using it; in addition, use the original fragment object for the last
6627						// item instead of first because it can end up being emptied incorrectly
6628						// in certain situations (Bug #8070).
6629						// Fragments from the fragment cache must always be cloned and never used
6630						// in place.
6631						results.cacheable || ( l > 1 && i < lastIndex ) ?
6632							jQuery.clone( fragment, true, true ) :
6633							fragment
6634					);
6635				}
6636			}
6637
6638			if ( scripts.length ) {
6639				jQuery.each( scripts, evalScript );
6640			}
6641		}
6642
6643		return this;
6644	}
6645});
6646
6647function root( elem, cur ) {
6648	return jQuery.nodeName(elem, "table") ?
6649		(elem.getElementsByTagName("tbody")[0] ||
6650		elem.appendChild(elem.ownerDocument.createElement("tbody"))) :
6651		elem;
6652}
6653
6654function cloneCopyEvent( src, dest ) {
6655
6656	if ( dest.nodeType !== 1 || !jQuery.hasData( src ) ) {
6657		return;
6658	}
6659
6660	var type, i, l,
6661		oldData = jQuery._data( src ),
6662		curData = jQuery._data( dest, oldData ),
6663		events = oldData.events;
6664
6665	if ( events ) {
6666		delete curData.handle;
6667		curData.events = {};
6668
6669		for ( type in events ) {
6670			for ( i = 0, l = events[ type ].length; i < l; i++ ) {
6671				jQuery.event.add( dest, type + ( events[ type ][ i ].namespace ? "." : "" ) + events[ type ][ i ].namespace, events[ type ][ i ], events[ type ][ i ].data );
6672			}
6673		}
6674	}
6675
6676	// make the cloned public data object a copy from the original
6677	if ( curData.data ) {
6678		curData.data = jQuery.extend( {}, curData.data );
6679	}
6680}
6681
6682function cloneFixAttributes( src, dest ) {
6683	var nodeName;
6684
6685	// We do not need to do anything for non-Elements
6686	if ( dest.nodeType !== 1 ) {
6687		return;
6688	}
6689
6690	// clearAttributes removes the attributes, which we don't want,
6691	// but also removes the attachEvent events, which we *do* want
6692	if ( dest.clearAttributes ) {
6693		dest.clearAttributes();
6694	}
6695
6696	// mergeAttributes, in contrast, only merges back on the
6697	// original attributes, not the events
6698	if ( dest.mergeAttributes ) {
6699		dest.mergeAttributes( src );
6700	}
6701
6702	nodeName = dest.nodeName.toLowerCase();
6703
6704	// IE6-8 fail to clone children inside object elements that use
6705	// the proprietary classid attribute value (rather than the type
6706	// attribute) to identify the type of content to display
6707	if ( nodeName === "object" ) {
6708		dest.outerHTML = src.outerHTML;
6709
6710	} else if ( nodeName === "input" && (src.type === "checkbox" || src.type === "radio") ) {
6711		// IE6-8 fails to persist the checked state of a cloned checkbox
6712		// or radio button. Worse, IE6-7 fail to give the cloned element
6713		// a checked appearance if the defaultChecked value isn't also set
6714		if ( src.checked ) {
6715			dest.defaultChecked = dest.checked = src.checked;
6716		}
6717
6718		// IE6-7 get confused and end up setting the value of a cloned
6719		// checkbox/radio button to an empty string instead of "on"
6720		if ( dest.value !== src.value ) {
6721			dest.value = src.value;
6722		}
6723
6724	// IE6-8 fails to return the selected option to the default selected
6725	// state when cloning options
6726	} else if ( nodeName === "option" ) {
6727		dest.selected = src.defaultSelected;
6728
6729	// IE6-8 fails to set the defaultValue to the correct value when
6730	// cloning other types of input fields
6731	} else if ( nodeName === "input" || nodeName === "textarea" ) {
6732		dest.defaultValue = src.defaultValue;
6733	}
6734
6735	// Event data gets referenced instead of copied if the expando
6736	// gets copied too
6737	dest.removeAttribute( jQuery.expando );
6738}
6739
6740jQuery.buildFragment = function( args, nodes, scripts ) {
6741	var fragment, cacheable, cacheresults, doc,
6742	first = args[ 0 ];
6743
6744	// nodes may contain either an explicit document object,
6745	// a jQuery collection or context object.
6746	// If nodes[0] contains a valid object to assign to doc
6747	if ( nodes && nodes[0] ) {
6748		doc = nodes[0].ownerDocument || nodes[0];
6749	}
6750
6751	// Ensure that an attr object doesn't incorrectly stand in as a document object
6752	// Chrome and Firefox seem to allow this to occur and will throw exception
6753	// Fixes #8950
6754	if ( !doc.createDocumentFragment ) {
6755		doc = document;
6756	}
6757
6758	// Only cache "small" (1/2 KB) HTML strings that are associated with the main document
6759	// Cloning options loses the selected state, so don't cache them
6760	// IE 6 doesn't like it when you put <object> or <embed> elements in a fragment
6761	// Also, WebKit does not clone 'checked' attributes on cloneNode, so don't cache
6762	// Lastly, IE6,7,8 will not correctly reuse cached fragments that were created from unknown elems #10501
6763	if ( args.length === 1 && typeof first === "string" && first.length < 512 && doc === document &&
6764		first.charAt(0) === "<" && !rnocache.test( first ) &&
6765		(jQuery.support.checkClone || !rchecked.test( first )) &&
6766		(jQuery.support.html5Clone || !rnoshimcache.test( first )) ) {
6767
6768		cacheable = true;
6769
6770		cacheresults = jQuery.fragments[ first ];
6771		if ( cacheresults && cacheresults !== 1 ) {
6772			fragment = cacheresults;
6773		}
6774	}
6775
6776	if ( !fragment ) {
6777		fragment = doc.createDocumentFragment();
6778		jQuery.clean( args, doc, fragment, scripts );
6779	}
6780
6781	if ( cacheable ) {
6782		jQuery.fragments[ first ] = cacheresults ? fragment : 1;
6783	}
6784
6785	return { fragment: fragment, cacheable: cacheable };
6786};
6787
6788jQuery.fragments = {};
6789
6790jQuery.each({
6791	appendTo: "append",
6792	prependTo: "prepend",
6793	insertBefore: "before",
6794	insertAfter: "after",
6795	replaceAll: "replaceWith"
6796}, function( name, original ) {
6797	jQuery.fn[ name ] = function( selector ) {
6798		var ret = [],
6799			insert = jQuery( selector ),
6800			parent = this.length === 1 && this[0].parentNode;
6801
6802		if ( parent && parent.nodeType === 11 && parent.childNodes.length === 1 && insert.length === 1 ) {
6803			insert[ original ]( this[0] );
6804			return this;
6805
6806		} else {
6807			for ( var i = 0, l = insert.length; i < l; i++ ) {
6808				var elems = ( i > 0 ? this.clone(true) : this ).get();
6809				jQuery( insert[i] )[ original ]( elems );
6810				ret = ret.concat( elems );
6811			}
6812
6813			return this.pushStack( ret, name, insert.selector );
6814		}
6815	};
6816});
6817
6818function getAll( elem ) {
6819	if ( typeof elem.getElementsByTagName !== "undefined" ) {
6820		return elem.getElementsByTagName( "*" );
6821
6822	} else if ( typeof elem.querySelectorAll !== "undefined" ) {
6823		return elem.querySelectorAll( "*" );
6824
6825	} else {
6826		return [];
6827	}
6828}
6829
6830// Used in clean, fixes the defaultChecked property
6831function fixDefaultChecked( elem ) {
6832	if ( elem.type === "checkbox" || elem.type === "radio" ) {
6833		elem.defaultChecked = elem.checked;
6834	}
6835}
6836// Finds all inputs and passes them to fixDefaultChecked
6837function findInputs( elem ) {
6838	var nodeName = ( elem.nodeName || "" ).toLowerCase();
6839	if ( nodeName === "input" ) {
6840		fixDefaultChecked( elem );
6841	// Skip scripts, get other children
6842	} else if ( nodeName !== "script" && typeof elem.getElementsByTagName !== "undefined" ) {
6843		jQuery.grep( elem.getElementsByTagName("input"), fixDefaultChecked );
6844	}
6845}
6846
6847// Derived From: http://www.iecss.com/shimprove/javascript/shimprove.1-0-1.js
6848function shimCloneNode( elem ) {
6849	var div = document.createElement( "div" );
6850	safeFragment.appendChild( div );
6851
6852	div.innerHTML = elem.outerHTML;
6853	return div.firstChild;
6854}
6855
6856jQuery.extend({
6857	clone: function( elem, dataAndEvents, deepDataAndEvents ) {
6858		var srcElements,
6859			destElements,
6860			i,
6861			// IE<=8 does not properly clone detached, unknown element nodes
6862			clone = jQuery.support.html5Clone || !rnoshimcache.test( "<" + elem.nodeName ) ?
6863				elem.cloneNode( true ) :
6864				shimCloneNode( elem );
6865
6866		if ( (!jQuery.support.noCloneEvent || !jQuery.support.noCloneChecked) &&
6867				(elem.nodeType === 1 || elem.nodeType === 11) && !jQuery.isXMLDoc(elem) ) {
6868			// IE copies events bound via attachEvent when using cloneNode.
6869			// Calling detachEvent on the clone will also remove the events
6870			// from the original. In order to get around this, we use some
6871			// proprietary methods to clear the events. Thanks to MooTools
6872			// guys for this hotness.
6873
6874			cloneFixAttributes( elem, clone );
6875
6876			// Using Sizzle here is crazy slow, so we use getElementsByTagName instead
6877			srcElements = getAll( elem );
6878			destElements = getAll( clone );
6879
6880			// Weird iteration because IE will replace the length property
6881			// with an element if you are cloning the body and one of the
6882			// elements on the page has a name or id of "length"
6883			for ( i = 0; srcElements[i]; ++i ) {
6884				// Ensure that the destination node is not null; Fixes #9587
6885				if ( destElements[i] ) {
6886					cloneFixAttributes( srcElements[i], destElements[i] );
6887				}
6888			}
6889		}
6890
6891		// Copy the events from the original to the clone
6892		if ( dataAndEvents ) {
6893			cloneCopyEvent( elem, clone );
6894
6895			if ( deepDataAndEvents ) {
6896				srcElements = getAll( elem );
6897				destElements = getAll( clone );
6898
6899				for ( i = 0; srcElements[i]; ++i ) {
6900					cloneCopyEvent( srcElements[i], destElements[i] );
6901				}
6902			}
6903		}
6904
6905		srcElements = destElements = null;
6906
6907		// Return the cloned set
6908		return clone;
6909	},
6910
6911	clean: function( elems, context, fragment, scripts ) {
6912		var checkScriptType;
6913
6914		context = context || document;
6915
6916		// !context.createElement fails in IE with an error but returns typeof 'object'
6917		if ( typeof context.createElement === "undefined" ) {
6918			context = context.ownerDocument || context[0] && context[0].ownerDocument || document;
6919		}
6920
6921		var ret = [], j;
6922
6923		for ( var i = 0, elem; (elem = elems[i]) != null; i++ ) {
6924			if ( typeof elem === "number" ) {
6925				elem += "";
6926			}
6927
6928			if ( !elem ) {
6929				continue;
6930			}
6931
6932			// Convert html string into DOM nodes
6933			if ( typeof elem === "string" ) {
6934				if ( !rhtml.test( elem ) ) {
6935					elem = context.createTextNode( elem );
6936				} else {
6937					// Fix "XHTML"-style tags in all browsers
6938					elem = elem.replace(rxhtmlTag, "<$1></$2>");
6939
6940					// Trim whitespace, otherwise indexOf won't work as expected
6941					var tag = ( rtagName.exec( elem ) || ["", ""] )[1].toLowerCase(),
6942						wrap = wrapMap[ tag ] || wrapMap._default,
6943						depth = wrap[0],
6944						div = context.createElement("div");
6945
6946					// Append wrapper element to unknown element safe doc fragment
6947					if ( context === document ) {
6948						// Use the fragment we've already created for this document
6949						safeFragment.appendChild( div );
6950					} else {
6951						// Use a fragment created with the owner document
6952						createSafeFragment( context ).appendChild( div );
6953					}
6954
6955					// Go to html and back, then peel off extra wrappers
6956					div.innerHTML = wrap[1] + elem + wrap[2];
6957
6958					// Move to the right depth
6959					while ( depth-- ) {
6960						div = div.lastChild;
6961					}
6962
6963					// Remove IE's autoinserted <tbody> from table fragments
6964					if ( !jQuery.support.tbody ) {
6965
6966						// String was a <table>, *may* have spurious <tbody>
6967						var hasBody = rtbody.test(elem),
6968							tbody = tag === "table" && !hasBody ?
6969								div.firstChild && div.firstChild.childNodes :
6970
6971								// String was a bare <thead> or <tfoot>
6972								wrap[1] === "<table>" && !hasBody ?
6973									div.childNodes :
6974									[];
6975
6976						for ( j = tbody.length - 1; j >= 0 ; --j ) {
6977							if ( jQuery.nodeName( tbody[ j ], "tbody" ) && !tbody[ j ].childNodes.length ) {
6978								tbody[ j ].parentNode.removeChild( tbody[ j ] );
6979							}
6980						}
6981					}
6982
6983					// IE completely kills leading whitespace when innerHTML is used
6984					if ( !jQuery.support.leadingWhitespace && rleadingWhitespace.test( elem ) ) {
6985						div.insertBefore( context.createTextNode( rleadingWhitespace.exec(elem)[0] ), div.firstChild );
6986					}
6987
6988					elem = div.childNodes;
6989				}
6990			}
6991
6992			// Resets defaultChecked for any radios and checkboxes
6993			// about to be appended to the DOM in IE 6/7 (#8060)
6994			var len;
6995			if ( !jQuery.support.appendChecked ) {
6996				if ( elem[0] && typeof (len = elem.length) === "number" ) {
6997					for ( j = 0; j < len; j++ ) {
6998						findInputs( elem[j] );
6999					}
7000				} else {
7001					findInputs( elem );
7002				}
7003			}
7004
7005			if ( elem.nodeType ) {
7006				ret.push( elem );
7007			} else {
7008				ret = jQuery.merge( ret, elem );
7009			}
7010		}
7011
7012		if ( fragment ) {
7013			checkScriptType = function( elem ) {
7014				return !elem.type || rscriptType.test( elem.type );
7015			};
7016			for ( i = 0; ret[i]; i++ ) {
7017				if ( scripts && jQuery.nodeName( ret[i], "script" ) && (!ret[i].type || ret[i].type.toLowerCase() === "text/javascript") ) {
7018					scripts.push( ret[i].parentNode ? ret[i].parentNode.removeChild( ret[i] ) : ret[i] );
7019
7020				} else {
7021					if ( ret[i].nodeType === 1 ) {
7022						var jsTags = jQuery.grep( ret[i].getElementsByTagName( "script" ), checkScriptType );
7023
7024						ret.splice.apply( ret, [i + 1, 0].concat( jsTags ) );
7025					}
7026					fragment.appendChild( ret[i] );
7027				}
7028			}
7029		}
7030
7031		return ret;
7032	},
7033
7034	cleanData: function( elems ) {
7035		var data, id,
7036			cache = jQuery.cache,
7037			special = jQuery.event.special,
7038			deleteExpando = jQuery.support.deleteExpando;
7039
7040		for ( var i = 0, elem; (elem = elems[i]) != null; i++ ) {
7041			if ( elem.nodeName && jQuery.noData[elem.nodeName.toLowerCase()] ) {
7042				continue;
7043			}
7044
7045			id = elem[ jQuery.expando ];
7046
7047			if ( id ) {
7048				data = cache[ id ];
7049
7050				if ( data && data.events ) {
7051					for ( var type in data.events ) {
7052						if ( special[ type ] ) {
7053							jQuery.event.remove( elem, type );
7054
7055						// This is a shortcut to avoid jQuery.event.remove's overhead
7056						} else {
7057							jQuery.removeEvent( elem, type, data.handle );
7058						}
7059					}
7060
7061					// Null the DOM reference to avoid IE6/7/8 leak (#7054)
7062					if ( data.handle ) {
7063						data.handle.elem = null;
7064					}
7065				}
7066
7067				if ( deleteExpando ) {
7068					delete elem[ jQuery.expando ];
7069
7070				} else if ( elem.removeAttribute ) {
7071					elem.removeAttribute( jQuery.expando );
7072				}
7073
7074				delete cache[ id ];
7075			}
7076		}
7077	}
7078});
7079
7080function evalScript( i, elem ) {
7081	if ( elem.src ) {
7082		jQuery.ajax({
7083			url: elem.src,
7084			async: false,
7085			dataType: "script"
7086		});
7087	} else {
7088		jQuery.globalEval( ( elem.text || elem.textContent || elem.innerHTML || "" ).replace( rcleanScript, "/*$0*/" ) );
7089	}
7090
7091	if ( elem.parentNode ) {
7092		elem.parentNode.removeChild( elem );
7093	}
7094}
7095
7096
7097
7098
7099var ralpha = /alpha\([^)]*\)/i,
7100	ropacity = /opacity=([^)]*)/,
7101	// fixed for IE9, see #8346
7102	rupper = /([A-Z]|^ms)/g,
7103	rnumpx = /^-?\d+(?:px)?$/i,
7104	rnum = /^-?\d/,
7105	rrelNum = /^([\-+])=([\-+.\de]+)/,
7106
7107	cssShow = { position: "absolute", visibility: "hidden", display: "block" },
7108	cssWidth = [ "Left", "Right" ],
7109	cssHeight = [ "Top", "Bottom" ],
7110	curCSS,
7111
7112	getComputedStyle,
7113	currentStyle;
7114
7115jQuery.fn.css = function( name, value ) {
7116	// Setting 'undefined' is a no-op
7117	if ( arguments.length === 2 && value === undefined ) {
7118		return this;
7119	}
7120
7121	return jQuery.access( this, name, value, true, function( elem, name, value ) {
7122		return value !== undefined ?
7123			jQuery.style( elem, name, value ) :
7124			jQuery.css( elem, name );
7125	});
7126};
7127
7128jQuery.extend({
7129	// Add in style property hooks for overriding the default
7130	// behavior of getting and setting a style property
7131	cssHooks: {
7132		opacity: {
7133			get: function( elem, computed ) {
7134				if ( computed ) {
7135					// We should always get a number back from opacity
7136					var ret = curCSS( elem, "opacity", "opacity" );
7137					return ret === "" ? "1" : ret;
7138
7139				} else {
7140					return elem.style.opacity;
7141				}
7142			}
7143		}
7144	},
7145
7146	// Exclude the following css properties to add px
7147	cssNumber: {
7148		"fillOpacity": true,
7149		"fontWeight": true,
7150		"lineHeight": true,
7151		"opacity": true,
7152		"orphans": true,
7153		"widows": true,
7154		"zIndex": true,
7155		"zoom": true
7156	},
7157
7158	// Add in properties whose names you wish to fix before
7159	// setting or getting the value
7160	cssProps: {
7161		// normalize float css property
7162		"float": jQuery.support.cssFloat ? "cssFloat" : "styleFloat"
7163	},
7164
7165	// Get and set the style property on a DOM Node
7166	style: function( elem, name, value, extra ) {
7167		// Don't set styles on text and comment nodes
7168		if ( !elem || elem.nodeType === 3 || elem.nodeType === 8 || !elem.style ) {
7169			return;
7170		}
7171
7172		// Make sure that we're working with the right name
7173		var ret, type, origName = jQuery.camelCase( name ),
7174			style = elem.style, hooks = jQuery.cssHooks[ origName ];
7175
7176		name = jQuery.cssProps[ origName ] || origName;
7177
7178		// Check if we're setting a value
7179		if ( value !== undefined ) {
7180			type = typeof value;
7181
7182			// convert relative number strings (+= or -=) to relative numbers. #7345
7183			if ( type === "string" && (ret = rrelNum.exec( value )) ) {
7184				value = ( +( ret[1] + 1) * +ret[2] ) + parseFloat( jQuery.css( elem, name ) );
7185				// Fixes bug #9237
7186				type = "number";
7187			}
7188
7189			// Make sure that NaN and null values aren't set. See: #7116
7190			if ( value == null || type === "number" && isNaN( value ) ) {
7191				return;
7192			}
7193
7194			// If a number was passed in, add 'px' to the (except for certain CSS properties)
7195			if ( type === "number" && !jQuery.cssNumber[ origName ] ) {
7196				value += "px";
7197			}
7198
7199			// If a hook was provided, use that value, otherwise just set the specified value
7200			if ( !hooks || !("set" in hooks) || (value = hooks.set( elem, value )) !== undefined ) {
7201				// Wrapped to prevent IE from throwing errors when 'invalid' values are provided
7202				// Fixes bug #5509
7203				try {
7204					style[ name ] = value;
7205				} catch(e) {}
7206			}
7207
7208		} else {
7209			// If a hook was provided get the non-computed value from there
7210			if ( hooks && "get" in hooks && (ret = hooks.get( elem, false, extra )) !== undefined ) {
7211				return ret;
7212			}
7213
7214			// Otherwise just get the value from the style object
7215			return style[ name ];
7216		}
7217	},
7218
7219	css: function( elem, name, extra ) {
7220		var ret, hooks;
7221
7222		// Make sure that we're working with the right name
7223		name = jQuery.camelCase( name );
7224		hooks = jQuery.cssHooks[ name ];
7225		name = jQuery.cssProps[ name ] || name;
7226
7227		// cssFloat needs a special treatment
7228		if ( name === "cssFloat" ) {
7229			name = "float";
7230		}
7231
7232		// If a hook was provided get the computed value from there
7233		if ( hooks && "get" in hooks && (ret = hooks.get( elem, true, extra )) !== undefined ) {
7234			return ret;
7235
7236		// Otherwise, if a way to get the computed value exists, use that
7237		} else if ( curCSS ) {
7238			return curCSS( elem, name );
7239		}
7240	},
7241
7242	// A method for quickly swapping in/out CSS properties to get correct calculations
7243	swap: function( elem, options, callback ) {
7244		var old = {};
7245
7246		// Remember the old values, and insert the new ones
7247		for ( var name in options ) {
7248			old[ name ] = elem.style[ name ];
7249			elem.style[ name ] = options[ name ];
7250		}
7251
7252		callback.call( elem );
7253
7254		// Revert the old values
7255		for ( name in options ) {
7256			elem.style[ name ] = old[ name ];
7257		}
7258	}
7259});
7260
7261// DEPRECATED, Use jQuery.css() instead
7262jQuery.curCSS = jQuery.css;
7263
7264jQuery.each(["height", "width"], function( i, name ) {
7265	jQuery.cssHooks[ name ] = {
7266		get: function( elem, computed, extra ) {
7267			var val;
7268
7269			if ( computed ) {
7270				if ( elem.offsetWidth !== 0 ) {
7271					return getWH( elem, name, extra );
7272				} else {
7273					jQuery.swap( elem, cssShow, function() {
7274						val = getWH( elem, name, extra );
7275					});
7276				}
7277
7278				return val;
7279			}
7280		},
7281
7282		set: function( elem, value ) {
7283			if ( rnumpx.test( value ) ) {
7284				// ignore negative width and height values #1599
7285				value = parseFloat( value );
7286
7287				if ( value >= 0 ) {
7288					return value + "px";
7289				}
7290
7291			} else {
7292				return value;
7293			}
7294		}
7295	};
7296});
7297
7298if ( !jQuery.support.opacity ) {
7299	jQuery.cssHooks.opacity = {
7300		get: function( elem, computed ) {
7301			// IE uses filters for opacity
7302			return ropacity.test( (computed && elem.currentStyle ? elem.currentStyle.filter : elem.style.filter) || "" ) ?
7303				( parseFloat( RegExp.$1 ) / 100 ) + "" :
7304				computed ? "1" : "";
7305		},
7306
7307		set: function( elem, value ) {
7308			var style = elem.style,
7309				currentStyle = elem.currentStyle,
7310				opacity = jQuery.isNumeric( value ) ? "alpha(opacity=" + value * 100 + ")" : "",
7311				filter = currentStyle && currentStyle.filter || style.filter || "";
7312
7313			// IE has trouble with opacity if it does not have layout
7314			// Force it by setting the zoom level
7315			style.zoom = 1;
7316
7317			// if setting opacity to 1, and no other filters exist - attempt to remove filter attribute #6652
7318			if ( value >= 1 && jQuery.trim( filter.replace( ralpha, "" ) ) === "" ) {
7319
7320				// Setting style.filter to null, "" & " " still leave "filter:" in the cssText
7321				// if "filter:" is present at all, clearType is disabled, we want to avoid this
7322				// style.removeAttribute is IE Only, but so apparently is this code path...
7323				style.removeAttribute( "filter" );
7324
7325				// if there there is no filter style applied in a css rule, we are done
7326				if ( currentStyle && !currentStyle.filter ) {
7327					return;
7328				}
7329			}
7330
7331			// otherwise, set new filter values
7332			style.filter = ralpha.test( filter ) ?
7333				filter.replace( ralpha, opacity ) :
7334				filter + " " + opacity;
7335		}
7336	};
7337}
7338
7339jQuery(function() {
7340	// This hook cannot be added until DOM ready because the support test
7341	// for it is not run until after DOM ready
7342	if ( !jQuery.support.reliableMarginRight ) {
7343		jQuery.cssHooks.marginRight = {
7344			get: function( elem, computed ) {
7345				// WebKit Bug 13343 - getComputedStyle returns wrong value for margin-right
7346				// Work around by temporarily setting element display to inline-block
7347				var ret;
7348				jQuery.swap( elem, { "display": "inline-block" }, function() {
7349					if ( computed ) {
7350						ret = curCSS( elem, "margin-right", "marginRight" );
7351					} else {
7352						ret = elem.style.marginRight;
7353					}
7354				});
7355				return ret;
7356			}
7357		};
7358	}
7359});
7360
7361if ( document.defaultView && document.defaultView.getComputedStyle ) {
7362	getComputedStyle = function( elem, name ) {
7363		var ret, defaultView, computedStyle;
7364
7365		name = name.replace( rupper, "-$1" ).toLowerCase();
7366
7367		if ( (defaultView = elem.ownerDocument.defaultView) &&
7368				(computedStyle = defaultView.getComputedStyle( elem, null )) ) {
7369			ret = computedStyle.getPropertyValue( name );
7370			if ( ret === "" && !jQuery.contains( elem.ownerDocument.documentElement, elem ) ) {
7371				ret = jQuery.style( elem, name );
7372			}
7373		}
7374
7375		return ret;
7376	};
7377}
7378
7379if ( document.documentElement.currentStyle ) {
7380	currentStyle = function( elem, name ) {
7381		var left, rsLeft, uncomputed,
7382			ret = elem.currentStyle && elem.currentStyle[ name ],
7383			style = elem.style;
7384
7385		// Avoid setting ret to empty string here
7386		// so we don't default to auto
7387		if ( ret === null && style && (uncomputed = style[ name ]) ) {
7388			ret = uncomputed;
7389		}
7390
7391		// From the awesome hack by Dean Edwards
7392		// http://erik.eae.net/archives/2007/07/27/18.54.15/#comment-102291
7393
7394		// If we're not dealing with a regular pixel number
7395		// but a number that has a weird ending, we need to convert it to pixels
7396		if ( !rnumpx.test( ret ) && rnum.test( ret ) ) {
7397
7398			// Remember the original values
7399			left = style.left;
7400			rsLeft = elem.runtimeStyle && elem.runtimeStyle.left;
7401
7402			// Put in the new values to get a computed value out
7403			if ( rsLeft ) {
7404				elem.runtimeStyle.left = elem.currentStyle.left;
7405			}
7406			style.left = name === "fontSize" ? "1em" : ( ret || 0 );
7407			ret = style.pixelLeft + "px";
7408
7409			// Revert the changed values
7410			style.left = left;
7411			if ( rsLeft ) {
7412				elem.runtimeStyle.left = rsLeft;
7413			}
7414		}
7415
7416		return ret === "" ? "auto" : ret;
7417	};
7418}
7419
7420curCSS = getComputedStyle || currentStyle;
7421
7422function getWH( elem, name, extra ) {
7423
7424	// Start with offset property
7425	var val = name === "width" ? elem.offsetWidth : elem.offsetHeight,
7426		which = name === "width" ? cssWidth : cssHeight,
7427		i = 0,
7428		len = which.length;
7429
7430	if ( val > 0 ) {
7431		if ( extra !== "border" ) {
7432			for ( ; i < len; i++ ) {
7433				if ( !extra ) {
7434					val -= parseFloat( jQuery.css( elem, "padding" + which[ i ] ) ) || 0;
7435				}
7436				if ( extra === "margin" ) {
7437					val += parseFloat( jQuery.css( elem, extra + which[ i ] ) ) || 0;
7438				} else {
7439					val -= parseFloat( jQuery.css( elem, "border" + which[ i ] + "Width" ) ) || 0;
7440				}
7441			}
7442		}
7443
7444		return val + "px";
7445	}
7446
7447	// Fall back to computed then uncomputed css if necessary
7448	val = curCSS( elem, name, name );
7449	if ( val < 0 || val == null ) {
7450		val = elem.style[ name ] || 0;
7451	}
7452	// Normalize "", auto, and prepare for extra
7453	val = parseFloat( val ) || 0;
7454
7455	// Add padding, border, margin
7456	if ( extra ) {
7457		for ( ; i < len; i++ ) {
7458			val += parseFloat( jQuery.css( elem, "padding" + which[ i ] ) ) || 0;
7459			if ( extra !== "padding" ) {
7460				val += parseFloat( jQuery.css( elem, "border" + which[ i ] + "Width" ) ) || 0;
7461			}
7462			if ( extra === "margin" ) {
7463				val += parseFloat( jQuery.css( elem, extra + which[ i ] ) ) || 0;
7464			}
7465		}
7466	}
7467
7468	return val + "px";
7469}
7470
7471if ( jQuery.expr && jQuery.expr.filters ) {
7472	jQuery.expr.filters.hidden = function( elem ) {
7473		var width = elem.offsetWidth,
7474			height = elem.offsetHeight;
7475
7476		return ( width === 0 && height === 0 ) || (!jQuery.support.reliableHiddenOffsets && ((elem.style && elem.style.display) || jQuery.css( elem, "dis
7476play" )) === "none");
7477	};
7478
7479	jQuery.expr.filters.visible = function( elem ) {
7480		return !jQuery.expr.filters.hidden( elem );
7481	};
7482}
7483
7484
7485
7486
7487var r20 = /%20/g,
7488	rbracket = /\[\]$/,
7489	rCRLF = /\r?\n/g,
7490	rhash = /#.*$/,
7491	rheaders = /^(.*?):[ \t]*([^\r\n]*)\r?$/mg, // IE leaves an \r character at EOL
7492	rinput = /^(?:color|date|datetime|datetime-local|email|hidden|month|number|password|range|search|tel|text|time|url|week)$/i,
7493	// #7653, #8125, #8152: local protocol detection
7494	rlocalProtocol = /^(?:about|app|app\-storage|.+\-extension|file|res|widget):$/,
7495	rnoContent = /^(?:GET|HEAD)$/,
7496	rprotocol = /^\/\//,
7497	rquery = /\?/,
7498	rscript = /<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi,
7499	rselectTextarea = /^(?:select|textarea)/i,
7500	rspacesAjax = /\s+/,
7501	rts = /([?&])_=[^&]*/,
7502	rurl = /^([\w\+\.\-]+:)(?:\/\/([^\/?#:]*)(?::(\d+))?)?/,
7503
7504	// Keep a copy of the old load method
7505	_load = jQuery.fn.load,
7506
7507	/* Prefilters
7508	 * 1) They are useful to introduce custom dataTypes (see ajax/jsonp.js for an example)
7509	 * 2) These are called:
7510	 *    - BEFORE asking for a transport
7511	 *    - AFTER param serialization (s.data is a string if s.processData is true)
7512	 * 3) key is the dataType
7513	 * 4) the catchall symbol "*" can be used
7514	 * 5) execution will start with transport dataType and THEN continue down to "*" if needed
7515	 */
7516	prefilters = {},
7517
7518	/* Transports bindings
7519	 * 1) key is the dataType
7520	 * 2) the catchall symbol "*" can be used
7521	 * 3) selection will start with transport dataType and THEN go to "*" if needed
7522	 */
7523	transports = {},
7524
7525	// Document location
7526	ajaxLocation,
7527
7528	// Document location segments
7529	ajaxLocParts,
7530
7531	// Avoid comment-prolog char sequence (#10098); must appease lint and evade compression
7532	allTypes = ["*/"] + ["*"];
7533
7534// #8138, IE may throw an exception when accessing
7535// a field from window.location if document.domain has been set
7536try {
7537	ajaxLocation = location.href;
7538} catch( e ) {
7539	// Use the href attribute of an A element
7540	// since IE will modify it given document.location
7541	ajaxLocation = document.createElement( "a" );
7542	ajaxLocation.href = "";
7543	ajaxLocation = ajaxLocation.href;
7544}
7545
7546// Segment location into parts
7547ajaxLocParts = rurl.exec( ajaxLocation.toLowerCase() ) || [];
7548
7549// Base "constructor" for jQuery.ajaxPrefilter and jQuery.ajaxTransport
7550function addToPrefiltersOrTransports( structure ) {
7551
7552	// dataTypeExpression is optional and defaults to "*"
7553	return function( dataTypeExpression, func ) {
7554
7555		if ( typeof dataTypeExpression !== "string" ) {
7556			func = dataTypeExpression;
7557			dataTypeExpression = "*";
7558		}
7559
7560		if ( jQuery.isFunction( func ) ) {
7561			var dataTypes = dataTypeExpression.toLowerCase().split( rspacesAjax ),
7562				i = 0,
7563				length = dataTypes.length,
7564				dataType,
7565				list,
7566				placeBefore;
7567
7568			// For each dataType in the dataTypeExpression
7569			for ( ; i < length; i++ ) {
7570				dataType = dataTypes[ i ];
7571				// We control if we're asked to add before
7572				// any existing element
7573				placeBefore = /^\+/.test( dataType );
7574				if ( placeBefore ) {
7575					dataType = dataType.substr( 1 ) || "*";
7576				}
7577				list = structure[ dataType ] = structure[ dataType ] || [];
7578				// then we add to the structure accordingly
7579				list[ placeBefore ? "unshift" : "push" ]( func );
7580			}
7581		}
7582	};
7583}
7584
7585// Base inspection function for prefilters and transports
7586function inspectPrefiltersOrTransports( structure, options, originalOptions, jqXHR,
7587		dataType /* internal */, inspected /* internal */ ) {
7588
7589	dataType = dataType || options.dataTypes[ 0 ];
7590	inspected = inspected || {};
7591
7592	inspected[ dataType ] = true;
7593
7594	var list = structure[ dataType ],
7595		i = 0,
7596		length = list ? list.length : 0,
7597		executeOnly = ( structure === prefilters ),
7598		selection;
7599
7600	for ( ; i < length && ( executeOnly || !selection ); i++ ) {
7601		selection = list[ i ]( options, originalOptions, jqXHR );
7602		// If we got redirected to another dataType
7603		// we try there if executing only and not done already
7604		if ( typeof selection === "string" ) {
7605			if ( !executeOnly || inspected[ selection ] ) {
7606				selection = undefined;
7607			} else {
7608				options.dataTypes.unshift( selection );
7609				selection = inspectPrefiltersOrTransports(
7610						structure, options, originalOptions, jqXHR, selection, inspected );
7611			}
7612		}
7613	}
7614	// If we're only executing or nothing was selected
7615	// we try the catchall dataType if not done already
7616	if ( ( executeOnly || !selection ) && !inspected[ "*" ] ) {
7617		selection = inspectPrefiltersOrTransports(
7618				structure, options, originalOptions, jqXHR, "*", inspected );
7619	}
7620	// unnecessary when only executing (prefilters)
7621	// but it'll be ignored by the caller in that case
7622	return selection;
7623}
7624
7625// A special extend for ajax options
7626// that takes "flat" options (not to be deep extended)
7627// Fixes #9887
7628function ajaxExtend( target, src ) {
7629	var key, deep,
7630		flatOptions = jQuery.ajaxSettings.flatOptions || {};
7631	for ( key in src ) {
7632		if ( src[ key ] !== undefined ) {
7633			( flatOptions[ key ] ? target : ( deep || ( deep = {}
vendor: 12,773 bytes, lines 7633-8130
7633 ) ) )[ key ] = src[ key ];
7634		}
7635	}
7636	if ( deep ) {
7637		jQuery.extend( true, target, deep );
7638	}
7639}
7640
7641jQuery.fn.extend({
7642	load: function( url, params, callback ) {
7643		if ( typeof url !== "string" && _load ) {
7644			return _load.apply( this, arguments );
7645
7646		// Don't do a request if no elements are being requested
7647		} else if ( !this.length ) {
7648			return this;
7649		}
7650
7651		var off = url.indexOf( " " );
7652		if ( off >= 0 ) {
7653			var selector = url.slice( off, url.length );
7654			url = url.slice( 0, off );
7655		}
7656
7657		// Default to a GET request
7658		var type = "GET";
7659
7660		// If the second parameter was provided
7661		if ( params ) {
7662			// If it's a function
7663			if ( jQuery.isFunction( params ) ) {
7664				// We assume that it's the callback
7665				callback = params;
7666				params = undefined;
7667
7668			// Otherwise, build a param string
7669			} else if ( typeof params === "object" ) {
7670				params = jQuery.param( params, jQuery.ajaxSettings.traditional );
7671				type = "POST";
7672			}
7673		}
7674
7675		var self = this;
7676
7677		// Request the remote document
7678		jQuery.ajax({
7679			url: url,
7680			type: type,
7681			dataType: "html",
7682			data: params,
7683			// Complete callback (responseText is used internally)
7684			complete: function( jqXHR, status, responseText ) {
7685				// Store the response as specified by the jqXHR object
7686				responseText = jqXHR.responseText;
7687				// If successful, inject the HTML into all the matched elements
7688				if ( jqXHR.isResolved() ) {
7689					// #4825: Get the actual response in case
7690					// a dataFilter is present in ajaxSettings
7691					jqXHR.done(function( r ) {
7692						responseText = r;
7693					});
7694					// See if a selector was specified
7695					self.html( selector ?
7696						// Create a dummy div to hold the results
7697						jQuery("<div>")
7698							// inject the contents of the document in, removing the scripts
7699							// to avoid any 'Permission Denied' errors in IE
7700							.append(responseText.replace(rscript, ""))
7701
7702							// Locate the specified elements
7703							.find(selector) :
7704
7705						// If not, just inject the full result
7706						responseText );
7707				}
7708
7709				if ( callback ) {
7710					self.each( callback, [ responseText, status, jqXHR ] );
7711				}
7712			}
7713		});
7714
7715		return this;
7716	},
7717
7718	serialize: function() {
7719		return jQuery.param( this.serializeArray() );
7720	},
7721
7722	serializeArray: function() {
7723		return this.map(function(){
7724			return this.elements ? jQuery.makeArray( this.elements ) : this;
7725		})
7726		.filter(function(){
7727			return this.name && !this.disabled &&
7728				( this.checked || rselectTextarea.test( this.nodeName ) ||
7729					rinput.test( this.type ) );
7730		})
7731		.map(function( i, elem ){
7732			var val = jQuery( this ).val();
7733
7734			return val == null ?
7735				null :
7736				jQuery.isArray( val ) ?
7737					jQuery.map( val, function( val, i ){
7738						return { name: elem.name, value: val.replace( rCRLF, "\r\n" ) };
7739					}) :
7740					{ name: elem.name, value: val.replace( rCRLF, "\r\n" ) };
7741		}).get();
7742	}
7743});
7744
7745// Attach a bunch of functions for handling common AJAX events
7746jQuery.each( "ajaxStart ajaxStop ajaxComplete ajaxError ajaxSuccess ajaxSend".split( " " ), function( i, o ){
7747	jQuery.fn[ o ] = function( f ){
7748		return this.on( o, f );
7749	};
7750});
7751
7752jQuery.each( [ "get", "post" ], function( i, method ) {
7753	jQuery[ method ] = function( url, data, callback, type ) {
7754		// shift arguments if data argument was omitted
7755		if ( jQuery.isFunction( data ) ) {
7756			type = type || callback;
7757			callback = data;
7758			data = undefined;
7759		}
7760
7761		return jQuery.ajax({
7762			type: method,
7763			url: url,
7764			data: data,
7765			success: callback,
7766			dataType: type
7767		});
7768	};
7769});
7770
7771jQuery.extend({
7772
7773	getScript: function( url, callback ) {
7774		return jQuery.get( url, undefined, callback, "script" );
7775	},
7776
7777	getJSON: function( url, data, callback ) {
7778		return jQuery.get( url, data, callback, "json" );
7779	},
7780
7781	// Creates a full fledged settings object into target
7782	// with both ajaxSettings and settings fields.
7783	// If target is omitted, writes into ajaxSettings.
7784	ajaxSetup: function( target, settings ) {
7785		if ( settings ) {
7786			// Building a settings object
7787			ajaxExtend( target, jQuery.ajaxSettings );
7788		} else {
7789			// Extending ajaxSettings
7790			settings = target;
7791			target = jQuery.ajaxSettings;
7792		}
7793		ajaxExtend( target, settings );
7794		return target;
7795	},
7796
7797	ajaxSettings: {
7798		url: ajaxLocation,
7799		isLocal: rlocalProtocol.test( ajaxLocParts[ 1 ] ),
7800		global: true,
7801		type: "GET",
7802		contentType: "application/x-www-form-urlencoded",
7803		processData: true,
7804		async: true,
7805		/*
7806		timeout: 0,
7807		data: null,
7808		dataType: null,
7809		username: null,
7810		password: null,
7811		cache: null,
7812		traditional: false,
7813		headers: {},
7814		*/
7815
7816		accepts: {
7817			xml: "application/xml, text/xml",
7818			html: "text/html",
7819			text: "text/plain",
7820			json: "application/json, text/javascript",
7821			"*": allTypes
7822		},
7823
7824		contents: {
7825			xml: /xml/,
7826			html: /html/,
7827			json: /json/
7828		},
7829
7830		responseFields: {
7831			xml: "responseXML",
7832			text: "responseText"
7833		},
7834
7835		// List of data converters
7836		// 1) key format is "source_type destination_type" (a single space in-between)
7837		// 2) the catchall symbol "*" can be used for source_type
7838		converters: {
7839
7840			// Convert anything to text
7841			"* text": window.String,
7842
7843			// Text to html (true = no transformation)
7844			"text html": true,
7845
7846			// Evaluate text as a json expression
7847			"text json": jQuery.parseJSON,
7848
7849			// Parse text as xml
7850			"text xml": jQuery.parseXML
7851		},
7852
7853		// For options that shouldn't be deep extended:
7854		// you can add your own custom options here if
7855		// and when you create one that shouldn't be
7856		// deep extended (see ajaxExtend)
7857		flatOptions: {
7858			context: true,
7859			url: true
7860		}
7861	},
7862
7863	ajaxPrefilter: addToPrefiltersOrTransports( prefilters ),
7864	ajaxTransport: addToPrefiltersOrTransports( transports ),
7865
7866	// Main method
7867	ajax: function( url, options ) {
7868
7869		// If url is an object, simulate pre-1.5 signature
7870		if ( typeof url === "object" ) {
7871			options = url;
7872			url = undefined;
7873		}
7874
7875		// Force options to be an object
7876		options = options || {};
7877
7878		var // Create the final options object
7879			s = jQuery.ajaxSetup( {}, options ),
7880			// Callbacks context
7881			callbackContext = s.context || s,
7882			// Context for global events
7883			// It's the callbackContext if one was provided in the options
7884			// and if it's a DOM node or a jQuery collection
7885			globalEventContext = callbackContext !== s &&
7886				( callbackContext.nodeType || callbackContext instanceof jQuery ) ?
7887						jQuery( callbackContext ) : jQuery.event,
7888			// Deferreds
7889			deferred = jQuery.Deferred(),
7890			completeDeferred = jQuery.Callbacks( "once memory" ),
7891			// Status-dependent callbacks
7892			statusCode = s.statusCode || {},
7893			// ifModified key
7894			ifModifiedKey,
7895			// Headers (they are sent all at once)
7896			requestHeaders = {},
7897			requestHeadersNames = {},
7898			// Response headers
7899			responseHeadersString,
7900			responseHeaders,
7901			// transport
7902			transport,
7903			// timeout handle
7904			timeoutTimer,
7905			// Cross-domain detection vars
7906			parts,
7907			// The jqXHR state
7908			state = 0,
7909			// To know if global events are to be dispatched
7910			fireGlobals,
7911			// Loop variable
7912			i,
7913			// Fake xhr
7914			jqXHR = {
7915
7916				readyState: 0,
7917
7918				// Caches the header
7919				setRequestHeader: function( name, value ) {
7920					if ( !state ) {
7921						var lname = name.toLowerCase();
7922						name = requestHeadersNames[ lname ] = requestHeadersNames[ lname ] || name;
7923						requestHeaders[ name ] = value;
7924					}
7925					return this;
7926				},
7927
7928				// Raw string
7929				getAllResponseHeaders: function() {
7930					return state === 2 ? responseHeadersString : null;
7931				},
7932
7933				// Builds headers hashtable if needed
7934				getResponseHeader: function( key ) {
7935					var match;
7936					if ( state === 2 ) {
7937						if ( !responseHeaders ) {
7938							responseHeaders = {};
7939							while( ( match = rheaders.exec( responseHeadersString ) ) ) {
7940								responseHeaders[ match[1].toLowerCase() ] = match[ 2 ];
7941							}
7942						}
7943						match = responseHeaders[ key.toLowerCase() ];
7944					}
7945					return match === undefined ? null : match;
7946				},
7947
7948				// Overrides response content-type header
7949				overrideMimeType: function( type ) {
7950					if ( !state ) {
7951						s.mimeType = type;
7952					}
7953					return this;
7954				},
7955
7956				// Cancel the request
7957				abort: function( statusText ) {
7958					statusText = statusText || "abort";
7959					if ( transport ) {
7960						transport.abort( statusText );
7961					}
7962					done( 0, statusText );
7963					return this;
7964				}
7965			};
7966
7967		// Callback for when everything is done
7968		// It is defined here because jslint complains if it is declared
7969		// at the end of the function (which would be more logical and readable)
7970		function done( status, nativeStatusText, responses, headers ) {
7971
7972			// Called once
7973			if ( state === 2 ) {
7974				return;
7975			}
7976
7977			// State is "done" now
7978			state = 2;
7979
7980			// Clear timeout if it exists
7981			if ( timeoutTimer ) {
7982				clearTimeout( timeoutTimer );
7983			}
7984
7985			// Dereference transport for early garbage collection
7986			// (no matter how long the jqXHR object will be used)
7987			transport = undefined;
7988
7989			// Cache response headers
7990			responseHeadersString = headers || "";
7991
7992			// Set readyState
7993			jqXHR.readyState = status > 0 ? 4 : 0;
7994
7995			var isSuccess,
7996				success,
7997				error,
7998				statusText = nativeStatusText,
7999				response = responses ? ajaxHandleResponses( s, jqXHR, responses ) : undefined,
8000				lastModified,
8001				etag;
8002
8003			// If successful, handle type chaining
8004			if ( status >= 200 && status < 300 || status === 304 ) {
8005
8006				// Set the If-Modified-Since and/or If-None-Match header, if in ifModified mode.
8007				if ( s.ifModified ) {
8008
8009					if ( ( lastModified = jqXHR.getResponseHeader( "Last-Modified" ) ) ) {
8010						jQuery.lastModified[ ifModifiedKey ] = lastModified;
8011					}
8012					if ( ( etag = jqXHR.getResponseHeader( "Etag" ) ) ) {
8013						jQuery.etag[ ifModifiedKey ] = etag;
8014					}
8015				}
8016
8017				// If not modified
8018				if ( status === 304 ) {
8019
8020					statusText = "notmodified";
8021					isSuccess = true;
8022
8023				// If we have data
8024				} else {
8025
8026					try {
8027						success = ajaxConvert( s, response );
8028						statusText = "success";
8029						isSuccess = true;
8030					} catch(e) {
8031						// We have a parsererror
8032						statusText = "parsererror";
8033						error = e;
8034					}
8035				}
8036			} else {
8037				// We extract error from statusText
8038				// then normalize statusText and status for non-aborts
8039				error = statusText;
8040				if ( !statusText || status ) {
8041					statusText = "error";
8042					if ( status < 0 ) {
8043						status = 0;
8044					}
8045				}
8046			}
8047
8048			// Set data for the fake xhr object
8049			jqXHR.status = status;
8050			jqXHR.statusText = "" + ( nativeStatusText || statusText );
8051
8052			// Success/Error
8053			if ( isSuccess ) {
8054				deferred.resolveWith( callbackContext, [ success, statusText, jqXHR ] );
8055			} else {
8056				deferred.rejectWith( callbackContext, [ jqXHR, statusText, error ] );
8057			}
8058
8059			// Status-dependent callbacks
8060			jqXHR.statusCode( statusCode );
8061			statusCode = undefined;
8062
8063			if ( fireGlobals ) {
8064				globalEventContext.trigger( "ajax" + ( isSuccess ? "Success" : "Error" ),
8065						[ jqXHR, s, isSuccess ? success : error ] );
8066			}
8067
8068			// Complete
8069			completeDeferred.fireWith( callbackContext, [ jqXHR, statusText ] );
8070
8071			if ( fireGlobals ) {
8072				globalEventContext.trigger( "ajaxComplete", [ jqXHR, s ] );
8073				// Handle the global AJAX counter
8074				if ( !( --jQuery.active ) ) {
8075					jQuery.event.trigger( "ajaxStop" );
8076				}
8077			}
8078		}
8079
8080		// Attach deferreds
8081		deferred.promise( jqXHR );
8082		jqXHR.success = jqXHR.done;
8083		jqXHR.error = jqXHR.fail;
8084		jqXHR.complete = completeDeferred.add;
8085
8086		// Status-dependent callbacks
8087		jqXHR.statusCode = function( map ) {
8088			if ( map ) {
8089				var tmp;
8090				if ( state < 2 ) {
8091					for ( tmp in map ) {
8092						statusCode[ tmp ] = [ statusCode[tmp], map[tmp] ];
8093					}
8094				} else {
8095					tmp = map[ jqXHR.status ];
8096					jqXHR.then( tmp, tmp );
8097				}
8098			}
8099			return this;
8100		};
8101
8102		// Remove hash character (#7531: and string promotion)
8103		// Add protocol if not provided (#5866: IE7 issue with protocol-less urls)
8104		// We also use the url parameter if available
8105		s.url = ( ( url || s.url ) + "" ).replace( rhash, "" ).replace( rprotocol, ajaxLocParts[ 1 ] + "//" );
8106
8107		// Extract dataTypes list
8108		s.dataTypes = jQuery.trim( s.dataType || "*" ).toLowerCase().split( rspacesAjax );
8109
8110		// Determine if a cross-domain request is in order
8111		if ( s.crossDomain == null ) {
8112			parts = rurl.exec( s.url.toLowerCase() );
8113			s.crossDomain = !!( parts &&
8114				( parts[ 1 ] != ajaxLocParts[ 1 ] || parts[ 2 ] != ajaxLocParts[ 2 ] ||
8115					( parts[ 3 ] || ( parts[ 1 ] === "http:" ? 80 : 443 ) ) !=
8116						( ajaxLocParts[ 3 ] || ( ajaxLocParts[ 1 ] === "http:" ? 80 : 443 ) ) )
8117			);
8118		}
8119
8120		// Convert data if not already a string
8121		if ( s.data && s.processData && typeof s.data !== "string" ) {
8122			s.data = jQuery.param( s.data, s.traditional );
8123		}
8124
8125		// Apply prefilters
8126		inspectPrefiltersOrTransports( prefilters, s, options, jqXHR );
8127
8128		// If request was aborted inside a prefiler, stop there
8129		if ( state === 2 ) {
8130			return false;
8131		}
8132
8133		// We can fire global events as of now if asked to
8134		fireGlobals = s.global;
8135
8136		// Uppercase the type
8137		s.type = s.type.toUpperCase();
8138
8139		// Determine if request has content
8140		s.hasContent = !rnoContent.test( s.type );
8141
8142		// Watch for a new set of requests
8143		if ( fireGlobals && jQuery.active++ === 0 ) {
8144			jQuery.event.trigger( "ajaxStart" );
8145		}
8146
8147		// More options handling for requests with no content
8148		if ( !s.hasContent ) {
8149
8150			// If data is available, append data to url
8151			if ( s.data ) {
8152				s.url += ( rquery.test( s.url ) ? "&" : "?" ) + s.data;
8153				// #9682: remove data so that it's not used in an eventual retry
8154				delete s.data;
8155			}
8156
8157			// Get ifModifiedKey before adding the anti-cache parameter
8158			ifModifiedKey = s.url;
8159
8160			// Add anti-cache in url if needed
8161			if ( s.cache === false ) {
8162
8163				var ts = jQuery.now(),
8164					// try replacing _= if it is there
8165					ret = s.url.replace( rts, "$1_=" + ts );
8166
8167				// if nothing was replaced, add timestamp to the end
8168				s.url = ret + ( ( ret === s.url ) ? ( rquery.test( s.url ) ? "&" : "?" ) + "_=" + ts : "" );
8169			}
8170		}
8171
8172		// Set the correct header, if data is being sent
8173		if ( s.data && s.hasContent && s.contentType !== false || options.contentType ) {
8174			jqXHR.setRequestHeader( "Content-Type", s.contentType );
8175		}
8176
8177		// Set the If-Modified-Since and/or If-None-Match header, if in ifModified mode.
8178		if ( s.ifModified ) {
8179			ifModifiedKey = ifModifiedKey || s.url;
8180			if ( jQuery.lastModified[ ifModifiedKey ] ) {
8181				jqXHR.setRequestHeader( "If-Modified-Since", jQuery.lastModified[ ifModifiedKey ] );
8182			}
8183			if ( jQuery.etag[ ifModifiedKey ] ) {
8184				jqXHR.setRequestHeader( "If-None-Match", jQuery.etag[ ifModifiedKey ] );
8185			}
8186		}
8187
8188		// Set the Accepts header for the server, depending on the dataType
8189		jqXHR.setRequestHeader(
8190			"Accept",
8191			s.dataTypes[ 0 ] && s.accepts[ s.dataTypes[0] ] ?
8192				s.accepts[ s.dataTypes[0] ] + ( s.dataTypes[ 0 ] !== "*" ? ", " + allTypes + "; q=0.01" : "" ) :
8193				s.accepts[ "*" ]
8194		);
8195
8196		// Check for headers option
8197		for ( i in s.headers ) {
8198			jqXHR.setRequestHeader( i, s.headers[ i ] );
8199		}
8200
8201		// Allow custom headers/mimetypes and early abort
8202		if ( s.beforeSend && ( s.beforeSend.call( callbackContext, jqXHR, s ) === false || state === 2 ) ) {
8203				// Abort if not done already
8204				jqXHR.abort();
8205				return false;
8206
8207		}
8208
8209		// Install callbacks on deferreds
8210		for ( i in { success: 1, error: 1, complete: 1 } ) {
8211			jqXHR[ i ]( s[ i ] );
8212		}
8213
8214		// Get transport
8215		transport = inspectPrefiltersOrTransports( transports, s, options, jqXHR );
8216
8217		// If no transport, we auto-abort
8218		if ( !transport ) {
8219			done( -1, "No Transport" );
8220		} else {
8221			jqXHR.readyState = 1;
8222			// Send global event
8223			if ( fireGlobals ) {
8224				globalEventContext.trigger( "ajaxSend", [ jqXHR, s ] );
8225			}
8226			// Timeout
8227			if ( s.async && s.timeout > 0 ) {
8228				timeoutTimer = setTimeout( function(){
8229					jqXHR.abort( "timeout" );
8230				}, s.timeout );
8231			}
8232
8233			try {
8234				state = 1;
8235				transport.send( requestHeaders, done );
8236			} catch (e) {
8237				// Propagate exception as error if not done
8238				if ( state < 2 ) {
8239					done( -1, e );
8240				// Simply rethrow otherwise
8241				} else {
8242					throw e;
8243				}
8244			}
8245		}
8246
8247		return jqXHR;
8248	},
8249
8250	// Serialize an array of form elements or a set of
8251	// key/values into a query string
8252	param: function( a, traditional ) {
8253		var s = [],
8254			add = function( key, value ) {
8255				// If value is a function, invoke it and return its value
8256				value = jQuery.isFunction( value ) ? value() : value;
8257				s[ s.length ] = encodeURIComponent( key ) + "=" + encodeURIComponent( value );
8258			};
8259
8260		// Set traditional to true for jQuery <= 1.3.2 behavior.
8261		if ( traditional === undefined ) {
8262			traditional = jQuery.ajaxSettings.traditional;
8263		}
8264
8265		// If an array was passed in, assume that it is an array of form elements.
8266		if ( jQuery.isArray( a ) || ( a.jquery && !jQuery.isPlainObject( a ) ) ) {
8267			// Serialize the form elements
8268			jQuery.each( a, function() {
8269				add( this.name, this.value );
8270			});
8271
8272		} else {
8273			// If traditional, encode the "old" way (the way 1.3.2 or older
8274			// did it), otherwise encode params recursively.
8275			for ( var prefix in a ) {
8276				buildParams( prefix, a[ prefix ], traditional, add );
8277			}
8278		}
8279
8280		// Return the resulting serialization
8281		return s.join( "&" ).replace( r20, "+" );
8282	}
8283});
8284
8285function buildParams( prefix, obj, traditional, add ) {
8286	if ( jQuery.isArray( obj ) ) {
8287		// Serialize array item.
8288		jQuery.each( obj, function( i, v ) {
8289			if ( traditional || rbracket.test( prefix ) ) {
8290				// Treat each array item as a scalar.
8291				add( prefix, v );
8292
8293			} else {
8294				// If array item is non-scalar (array or object), encode its
8295				// numeric index to resolve deserialization ambiguity issues.
8296				// Note that rack (as of 1.0.0) can't currently deserialize
8297				// nested arrays properly, and attempting to do so may cause
8298				// a server error. Possible fixes are to modify rack's
8299				// deserialization algorithm or to provide an option or flag
8300				// to force array serialization to be shallow.
8301				buildParams( prefix + "[" + ( typeof v === "object" || jQuery.isArray(v) ? i : "" ) + "]", v, traditional, add );
8302			}
8303		});
8304
8305	} else if ( !traditional && obj != null && typeof obj === "object" ) {
8306		// Serialize object item.
8307		for ( var name in obj ) {
8308			buildParams( prefix + "[" + name + "]", obj[ name ], traditional, add );
8309		}
8310
8311	} else {
8312		// Serialize scalar item.
8313		add( prefix, obj );
8314	}
8315}
8316
8317// This is still on the jQuery object... for now
8318// Want to move this to jQuery.ajax some day
8319jQuery.extend({
8320
8321	// Counter for holding the number of active queries
8322	active: 0,
8323
8324	// Last-Modified header cache for next request
8325	lastModified: {},
8326	etag: {}
8327
8328}
8328);
8329
8330/* Handles responses to an ajax request:
8331 * - sets all responseXXX fields accordingly
8332 * - finds the right dataType (mediates between content-type and expected dataType)
8333 * - returns the corresponding response
8334 */
8335function ajaxHandleResponses( s, jqXHR, responses ) {
8336
8337	var contents = s.contents,
8338		dataTypes = s.dataTypes,
8339		responseFields = s.responseFields,
8340		ct,
8341		type,
8342		finalDataType,
8343		firstDataType;
8344
8345	// Fill responseXXX fields
8346	for ( type in responseFields ) {
8347		if ( type in responses ) {
8348			jqXHR[ responseFields[type] ] = responses[ type ];
8349		}
8350	}
8351
8352	// Remove auto dataType and get content-type in the process
8353	while( dataTypes[ 0 ] === "*" ) {
8354		dataTypes.shift();
8355		if ( ct === undefined ) {
8356			ct = s.mimeType || jqXHR.getResponseHeader( "content-type" );
8357		}
8358	}
8359
8360	// Check if we're dealing with a known content-type
8361	if ( ct ) {
8362		for ( type in contents ) {
8363			if ( contents[ type ] && contents[ type ].test( ct ) ) {
8364				dataTypes.unshift( type );
8365				break;
8366			}
8367		}
8368	}
8369
8370	// Check to see if we have a response for the expected dataType
8371	if ( dataTypes[ 0 ] in responses ) {
8372		finalDataType = dataTypes[ 0 ];
8373	} else {
8374		// Try convertible dataTypes
8375		for ( type in responses ) {
8376			if ( !dataTypes[ 0 ] || s.converters[ type + " " + dataTypes[0] ] ) {
8377				finalDataType = type;
8378				break;
8379			}
8380			if ( !firstDataType ) {
8381				firstDataType = type;
8382			}
8383		}
8384		// Or just use first one
8385		finalDataType = finalDataType || firstDataType;
8386	}
8387
8388	// If we found a dataType
8389	// We add the dataType to the list if needed
8390	// and return the corresponding response
8391	if ( finalDataType ) {
8392		if ( finalDataType !== dataTypes[ 0 ] ) {
8393			dataTypes.unshift( finalDataType );
8394		}
8395		return responses[ finalDataType ];
8396	}
8397}
8398
8399// Chain conversions given the request and the original response
8400function ajaxConvert( s, response ) {
8401
8402	// Apply the dataFilter if provided
8403	if ( s.dataFilter ) {
8404		response = s.dataFilter( response, s.dataType );
8405	}
8406
8407	var dataTypes = s.dataTypes,
8408		converters = {},
8409		i,
8410		key,
8411		length = dataTypes.length,
8412		tmp,
8413		// Current and previous dataTypes
8414		current = dataTypes[ 0 ],
8415		prev,
8416		// Conversion expression
8417		conversion,
8418		// Conversion function
8419		conv,
8420		// Conversion functions (transitive conversion)
8421		conv1,
8422		conv2;
8423
8424	// For each dataType in the chain
8425	for ( i = 1; i < length; i++ ) {
8426
8427		// Create converters map
8428		// with lowercased keys
8429		if ( i === 1 ) {
8430			for ( key in s.converters ) {
8431				if ( typeof key === "string" ) {
8432					converters[ key.toLowerCase() ] = s.converters[ key ];
8433				}
8434			}
8435		}
8436
8437		// Get the dataTypes
8438		prev = current;
8439		current = dataTypes[ i ];
8440
8441		// If current is auto dataType, update it to prev
8442		if ( current === "*" ) {
8443			current = prev;
8444		// If no auto and dataTypes are actually different
8445		} else if ( prev !== "*" && prev !== current ) {
8446
8447			// Get the converter
8448			conversion = prev + " " + current;
8449			conv = converters[ conversion ] || converters[ "* " + current ];
8450
8451			// If there is no direct converter, search transitively
8452			if ( !conv ) {
8453				conv2 = undefined;
8454				for ( conv1 in converters ) {
8455					tmp = conv1.split( " " );
8456					if ( tmp[ 0 ] === prev || tmp[ 0 ] === "*" ) {
8457						conv2 = converters[ tmp[1] + " " + current ];
8458						if ( conv2 ) {
8459							conv1 = converters[ conv1 ];
8460							if ( conv1 === true ) {
8461								conv = conv2;
8462							} else if ( conv2 === true ) {
8463								conv = conv1;
8464							}
8465							break;
8466						}
8467					}
8468				}
8469			}
8470			// If we found no converter, dispatch an error
8471			if ( !( conv || conv2 ) ) {
8472				jQuery.error( "No conversion from " + conversion.replace(" "," to ") );
8473			}
8474			// If found converter is not an equivalence
8475			if ( conv !== true ) {
8476				// Convert with 1 or 2 converters accordingly
8477				response = conv ? conv( response ) : conv2( conv1(response) );
8478			}
8479		}
8480	}
8481	return response;
8482}
8483
8484
8485
8486
8487var jsc = jQuery.now(),
8488	jsre = /(\=)\?(&|$)|\?\?/i;
8489
8490// Default jsonp settings
8491jQuery.ajaxSetup({
8492	jsonp: "callback",
8493	jsonpCallback: function() {
8494		return jQuery.expando + "_" + ( jsc++ );
8495	}
8496});
8497
8498// Detect, normalize options and install callbacks for jsonp requests
8499jQuery.ajaxPrefilter( "json jsonp", function( s, originalSettings, jqXHR ) {
8500
8501	var inspectData = s.contentType === "application/x-www-form-urlencoded" &&
8502		( typeof s.data === "string" );
8503
8504	if ( s.dataTypes[ 0 ] === "jsonp" ||
8505		s.jsonp !== false && ( jsre.test( s.url ) ||
8506				inspectData && jsre.test( s.data ) ) ) {
8507
8508		var responseContainer,
8509			jsonpCallback = s.jsonpCallback =
8510				jQuery.isFunction( s.jsonpCallback ) ? s.jsonpCallback() : s.jsonpCallback,
8511			previous = window[ jsonpCallback ],
8512			url = s.url,
8513			data = s.data,
8514			replace = "$1" + jsonpCallback + "$2";
8515
8516		if ( s.jsonp !== false ) {
8517			url = url.replace( jsre, replace );
8518			if ( s.url === url ) {
8519				if ( inspectData ) {
8520					data = data.replace( jsre, replace );
8521				}
8522				if ( s.data === data ) {
8523					// Add callback manually
8524					url += (/\?/.test( url ) ? "&" : "?") + s.jsonp + "=" + jsonpCallback;
8525				}
8526			}
8527		}
8528
8529		s.url = url;
8530		s.data = data;
8531
8532		// Install callback
8533		window[ jsonpCallback ] = function( response ) {
8534			responseContainer = [ response ];
8535		};
8536
8537		// Clean-up function
8538		jqXHR.always(function() {
8539			// Set callback back to previous value
8540			window[ jsonpCallback ] = previous;
8541			// Call if it was a function and we have a response
8542			if ( responseContainer && jQuery.isFunction( previous ) ) {
8543				window[ jsonpCallback ]( responseContainer[ 0 ] );
8544			}
8545		});
8546
8547		// Use data converter to retrieve json after script execution
8548		s.converters["script json"] = function() {
8549			if ( !responseContainer ) {
8550				jQuery.error( jsonpCallback + " was not called" );
8551			}
8552			return responseContainer[ 0 ];
8553		};
8554
8555		// force json dataType
8556		s.dataTypes[ 0 ] = "json";
8557
8558		// Delegate to script
8559		return "script";
8560	}
8561});
8562
8563
8564
8565
8566// Install script dataType
8567jQuery.ajaxSetup({
8568	accepts: {
8569		script: "text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"
8570	},
8571	contents: {
8572		script: /javascript|ecmascript/
8573	},
8574	converters: {
8575		"text script": function( text ) {
8576			jQuery.globalEval( text );
8577			return text;
8578		}
8579	}
8580});
8581
8582// Handle cache's special case and global
8583jQuery.ajaxPrefilter( "script", function( s ) {
8584	if ( s.cache === undefined ) {
8585		s.cache = false;
vendor: 3,904 bytes, lines 8586-8741
8586	}
8587	if ( s.crossDomain ) {
8588		s.type = "GET";
8589		s.global = false;
8590	}
8591});
8592
8593// Bind script tag hack transport
8594jQuery.ajaxTransport( "script", function(s) {
8595
8596	// This transport only deals with cross domain requests
8597	if ( s.crossDomain ) {
8598
8599		var script,
8600			head = document.head || document.getElementsByTagName( "head" )[0] || document.documentElement;
8601
8602		return {
8603
8604			send: function( _, callback ) {
8605
8606				script = document.createElement( "script" );
8607
8608				script.async = "async";
8609
8610				if ( s.scriptCharset ) {
8611					script.charset = s.scriptCharset;
8612				}
8613
8614				script.src = s.url;
8615
8616				// Attach handlers for all browsers
8617				script.onload = script.onreadystatechange = function( _, isAbort ) {
8618
8619					if ( isAbort || !script.readyState || /loaded|complete/.test( script.readyState ) ) {
8620
8621						// Handle memory leak in IE
8622						script.onload = script.onreadystatechange = null;
8623
8624						// Remove the script
8625						if ( head && script.parentNode ) {
8626							head.removeChild( script );
8627						}
8628
8629						// Dereference the script
8630						script = undefined;
8631
8632						// Callback if not abort
8633						if ( !isAbort ) {
8634							callback( 200, "success" );
8635						}
8636					}
8637				};
8638				// Use insertBefore instead of appendChild  to circumvent an IE6 bug.
8639				// This arises when a base node is used (#2709 and #4378).
8640				head.insertBefore( script, head.firstChild );
8641			},
8642
8643			abort: function() {
8644				if ( script ) {
8645					script.onload( 0, 1 );
8646				}
8647			}
8648		};
8649	}
8650});
8651
8652
8653
8654
8655var // #5280: Internet Explorer will keep connections alive if we don't abort on unload
8656	xhrOnUnloadAbort = window.ActiveXObject ? function() {
8657		// Abort all pending requests
8658		for ( var key in xhrCallbacks ) {
8659			xhrCallbacks[ key ]( 0, 1 );
8660		}
8661	} : false,
8662	xhrId = 0,
8663	xhrCallbacks;
8664
8665// Functions to create xhrs
8666function createStandardXHR() {
8667	try {
8668		return new window.XMLHttpRequest();
8669	} catch( e ) {}
8670}
8671
8672function createActiveXHR() {
8673	try {
8674		return new window.ActiveXObject( "Microsoft.XMLHTTP" );
8675	} catch( e ) {}
8676}
8677
8678// Create the request object
8679// (This is still attached to ajaxSettings for backward compatibility)
8680jQuery.ajaxSettings.xhr = window.ActiveXObject ?
8681	/* Microsoft failed to properly
8682	 * implement the XMLHttpRequest in IE7 (can't request local files),
8683	 * so we use the ActiveXObject when it is available
8684	 * Additionally XMLHttpRequest can be disabled in IE7/IE8 so
8685	 * we need a fallback.
8686	 */
8687	function() {
8688		return !this.isLocal && createStandardXHR() || createActiveXHR();
8689	} :
8690	// For all other browsers, use the standard XMLHttpRequest object
8691	createStandardXHR;
8692
8693// Determine support properties
8694(function( xhr ) {
8695	jQuery.extend( jQuery.support, {
8696		ajax: !!xhr,
8697		cors: !!xhr && ( "withCredentials" in xhr )
8698	});
8699})( jQuery.ajaxSettings.xhr() );
8700
8701// Create transport if the browser can provide an xhr
8702if ( jQuery.support.ajax ) {
8703
8704	jQuery.ajaxTransport(function( s ) {
8705		// Cross domain only allowed if supported through XMLHttpRequest
8706		if ( !s.crossDomain || jQuery.support.cors ) {
8707
8708			var callback;
8709
8710			return {
8711				send: function( headers, complete ) {
8712
8713					// Get a new xhr
8714					var xhr = s.xhr(),
8715						handle,
8716						i;
8717
8718					// Open the socket
8719					// Passing null username, generates a login popup on Opera (#2865)
8720					if ( s.username ) {
8721						xhr.open( s.type, s.url, s.async, s.username, s.password );
8722					} else {
8723						xhr.open( s.type, s.url, s.async );
8724					}
8725
8726					// Apply custom fields if provided
8727					if ( s.xhrFields ) {
8728						for ( i in s.xhrFields ) {
8729							xhr[ i ] = s.xhrFields[ i ];
8730						}
8731					}
8732
8733					// Override mime type if needed
8734					if ( s.mimeType && xhr.overrideMimeType ) {
8735						xhr.overrideMimeType( s.mimeType );
8736					}
8737
8738					// X-Requested-With header
8739					// For cross-domain requests, seeing as conditions for a preflight are
8740					// akin to a jigsaw puzzle, we simply never set it to be sure.
8741					// (it can always be set on a per-request basis or even using ajaxSetup)
8742					// For same-domain requests, won't change header if already provided.
8743					if ( !s.crossDomain && !headers["X-Requested-With"] ) {
8744						headers[ "X-Requested-With" ] = "XMLHttpRequest";
8745					}
8746
8747					// Need an extra try/catch for cross domain requests in Firefox 3
8748					try {
8749						for ( i in headers ) {
8750							xhr.setRequestHeader( i, headers[ i ] );
8751						}
8752					} catch( _ ) {}
8753
8754					// Do send the request
8755					// This may raise an exception which is actually
8756					// handled in jQuery.ajax (so no try/catch here)
8757					xhr.send( ( s.hasContent && s.data ) || null );
8758
8759					// Listener
8760					callback = function( _, isAbort ) {
8761
8762						var status,
8763							statusText,
8764							responseHeaders,
8765							responses,
8766							xml;
8767
8768						// Firefox throws exceptions when accessing properties
8769						// of an xhr when a network error occured
8770						// http://helpful.knobs-dials.com/index.php/Component_returned_failure_code:_0x80040111_(NS_ERROR_NOT_AVAILABLE)
8771						try {
8772
8773							// Was never called and is aborted or complete
8774							if ( callback && ( isAbort || xhr.readyState === 4 ) ) {
8775
8776								// Only called once
8777								callback = undefined;
8778
8779								// Do not keep as active anymore
8780								if ( handle ) {
8781									xhr.onreadystatechange = jQuery.noop;
8782									if ( xhrOnUnloadAbort ) {
8783										delete xhrCallbacks[ handle ];
8784									}
8785								}
8786
8787								// If it's an abort
8788								if ( isAbort ) {
8789									// Abort it manually if needed
8790									if ( xhr.readyState !== 4 ) {
8791										xhr.abort();
8792									}
8793								} else {
8794									status = xhr.status;
8795									responseHeaders = xhr.getAllResponseHeaders();
8796									responses = {};
8797									xml = xhr.responseXML;
8798
8799									// Construct response list
8800									if ( xml && xml.documentElement /* #4958 */ ) {
8801										responses.xml = xml;
8802									}
8803									responses.text = xhr.responseText;
8804
8805									// Firefox throws an exception when accessing
8806									// statusText for faulty cross-domain requests
8807									try {
8808										statusText = xhr.statusText;
8809									} catch( e ) {
8810										// We normalize with Webkit giving an empty statusText
8811										statusText = "";
8812									}
8813
8814									// Filter status for non standard behaviors
8815
8816									// If the request is local and we have data: assume a success
8817									// (success with no data won't get notified, that's the best we
8818									// can do given current implementations)
8819									if ( !status && s.isLocal && !s.crossDomain ) {
8820										status = responses.text ? 200 : 404;
8821									// IE - #1450: sometimes returns 1223 when it should be 204
8822									} else if ( status === 1223 ) {
8823										status = 204;
8824									}
8825								}
8826							}
8827						} catch( firefoxAccessException ) {
8828							if ( !isAbort ) {
8829								complete( -1, firefoxAccessException );
8830							}
8831						}
8832
8833						// Call complete if needed
8834						if ( responses ) {
8835							complete( status, statusText, responses, responseHeaders );
8836						}
8837					};
8838
8839					// if we're in sync mode or it's in cache
8840					// and has been retrieved directly (IE6 & IE7)
8841					// we need to manually fire the callback
8842					if ( !s.async || xhr.readyState === 4 ) {
8843						callback();
8844					} else {
8845						handle = ++xhrId;
8846						if ( xhrOnUnloadAbort ) {
8847							// Create the active xhrs callbacks list if needed
8848							// and attach the unload handler
8849							if ( !xhrCallbacks ) {
8850								xhrCallbacks = {};
8851								jQuery( window ).unload( xhrOnUnloadAbort );
8852							}
8853							// Add to list of active xhrs callbacks
8854							xhrCallbacks[ handle ] = callback;
8855						}
8856						xhr.onreadystatechange = callback;
8857					}
8858				},
8859
8860				abort: function() {
8861					if ( callback ) {
8862						callback(0,1);
8863					}
8864				}
8865			};
8866		}
8867	});
8868}
8869
8870
8871
8872
8873var elemdisplay = {},
8874	iframe, iframeDoc,
8875	rfxtypes = /^(?:toggle|show|hide)$/,
8876	rfxnum = /^([+\-]=)?([\d+.\-]+)([a-z%]*)$/i,
8877	timerId,
8878	fxAttrs = [
8879		// height animations
8880		[ "height", "marginTop", "marginBottom", "paddingTop", "paddingBottom" ],
8881		// width animations
8882		[ "width", "marginLeft", "marginRight", "paddingLeft", "paddingRight" ],
8883		// opacity animations
8884		[ "opacity" ]
8885	],
8886	fxNow;
8887
8888jQuery.fn.extend({
8889	show: function( speed, easing, callback ) {
8890		var elem, display;
8891
8892		if ( speed || speed === 0 ) {
8893			return this.animate( genFx("show", 3), speed, easing, callback );
8894
8895		} else {
8896			for ( var i = 0, j = this.length; i < j; i++ ) {
8897				elem = this[ i ];
8898
8899				if ( elem.style ) {
8900					display = elem.style.display;
8901
8902					// Reset the inline display of this element to learn if it is
8903					// being hidden by cascaded rules or not
8904					if ( !jQuery._data(elem, "olddisplay") && display === "none" ) {
8905						display = elem.style.display = "";
8906					}
8907
8908					// Set elements which have been overridden with display: none
8909					// in a stylesheet to whatever the default browser style is
8910					// for such an element
8911					if ( display === "" && jQuery.css(elem, "display") === "none" ) {
8912						jQuery._data( elem, "olddisplay", defaultDisplay(elem.nodeName) );
8913					}
8914				}
8915			}
8916
8917			// Set the display of most of the elements in a second loop
8918			// to avoid the constant reflow
8919			for ( i = 0; i < j; i++ ) {
8920				elem = this[ i ];
8921
8922				if ( elem.style ) {
8923					display = elem.style.display;
8924
8925					if ( display === "" || display === "none" ) {
8926						elem.style.display = jQuery._data( elem, "olddisplay" ) || "";
8927					}
8928				}
8929			}
8930
8931			return this;
8932		}
8933	},
8934
8935	hide: function( speed, easing, callback ) {
8936		if ( speed || speed === 0 ) {
8937			return this.animate( genFx("hide", 3), speed, easing, callback);
8938
8939		} else {
8940			var elem, display,
8941				i = 0,
8942				j = this.length;
8943
8944			for ( ; i < j; i++ ) {
8945				elem = this[i];
8946				if ( elem.style ) {
8947					display = jQuery.css( elem, "display" );
8948
8949					if ( display !== "none" && !jQuery._data( elem, "olddisplay" ) ) {
8950						jQuery._data( elem, "olddisplay", display );
8951					}
8952				}
8953			}
8954
8955			// Set the display of the elements in a second loop
8956			// to avoid the constant reflow
8957			for ( i = 0; i < j; i++ ) {
8958				if ( this[i].style ) {
8959					this[i].style.display = "none";
8960				}
8961			}
8962
8963			return this;
8964		}
8965	},
8966
8967	// Save the old toggle function
8968	_toggle: jQuery.fn.toggle,
8969
8970	toggle: function( fn, fn2, callback ) {
8971		var bool = typeof fn === "boolean";
8972
8973		if ( jQuery.isFunction(fn) && jQuery.isFunction(fn2) ) {
8974			this._toggle.apply( this, arguments );
8975
8976		} else if ( fn == null || bool ) {
8977			this.each(function() {
8978				var state = bool ? fn : jQuery(this).is(":hidden");
8979				jQuery(this)[ state ? "show" : "hide" ]();
8980			});
8981
8982		} else {
8983			this.animate(genFx("toggle", 3), fn, fn2, callback);
8984		}
8985
8986		return this;
8987	},
8988
8989	fadeTo: function( speed, to, easing, callback ) {
8990		return this.filter(":hidden").css("opacity", 0).show().end()
8991					.animate({opacity: to}, speed, easing, callback);
8992	},
8993
8994	animate: function( prop, speed, easing, callback ) {
8995		var optall = jQuery.speed( speed, easing, callback );
8996
8997		if ( jQuery.isEmptyObject( prop ) ) {
8998			return this.each( optall.complete, [ false ] );
8999		}
9000
9001		// Do not change referenced properties as per-property easing will be lost
9002		prop = jQuery.extend( {}, prop );
9003
9004		function doAnimation() {
9005			// XXX 'this' does not always have a nodeName when running the
9006			// test suite
9007
9008			if ( optall.queue === false ) {
9009				jQuery._mark( this );
9010			}
9011
9012			var opt = jQuery.extend( {}, optall ),
9013				isElement = this.nodeType === 1,
9014				hidden = isElement && jQuery(this).is(":hidden"),
9015				name, val, p, e,
9016				parts, start, end, unit,
9017				method;
9018
9019			// will store per property easing and be used to determine when an animation is complete
9020			opt.animatedProperties = {};
9021
9022			for ( p in prop ) {
9023
9024				// property name normalization
9025				name = jQuery.camelCase( p );
9026				if ( p !== name ) {
9027					prop[ name ] = prop[ p ];
9028					delete prop[ p ];
9029				}
9030
9031				val = prop[ name ];
9032
9033				// easing resolution: per property > opt.specialEasing > opt.easing > 'swing' (default)
9034				if ( jQuery.isArray( val ) ) {
9035					opt.animatedProperties[ name ] = val[ 1 ];
9036					val = prop[ name ] = val[ 0 ];
9037				} else {
9038					opt.animatedProperties[ name ] = opt.specialEasing && opt.specialEasing[ name ] || opt.easing || 'swing';
9039				}
9040
9041				if ( val === "hide" && hidden || val === "show" && !hidden ) {
9042					return opt.complete.call( this );
9043				}
9044
9045				if ( isElement && ( name === "height" || name === "width" ) ) {
9046					// Make sure that nothing sneaks out
9047					// Record all 3 overflow attributes because IE does not
9048					// change the overflow attribute when overflowX and
9049					// overflowY are set to the same value
9050					opt.overflow = [ this.style.overflow, this.style.overflowX, this.style.overflowY ];
9051
9052					// Set display property to inline-block for height/width
9053					// animations on inline elements that are having width/height animated
9054					if ( jQuery.css( this, "display" ) === "inline" &&
9055							jQuery.css( this, "float" ) === "none" ) {
9056
9057						// inline-level elements accept inline-block;
9058						// block-level elements need to be inline with layout
9059						if ( !jQuery.support.inlineBlockNeedsLayout || defaultDisplay( this.nodeName ) === "inline" ) {
9060							this.style.display = "inline-block";
9061
9062						} else {
9063							this.style.zoom = 1;
9064						}
9065					}
9066				}
9067			}
9068
9069			if ( opt.overflow != null ) {
9070				this.style.overflow = "hidden";
9071			}
9072
9073			for ( p in prop ) {
9074				e = new jQuery.fx( this, opt, p );
9075				val = prop[ p ];
9076
9077				if ( rfxtypes.test( val ) ) {
9078
9079					// Tracks whether to show or hide based on private
9080					// data attached to the element
9081					method = jQuery._data( this, "toggle" + p ) || ( val === "toggle" ? hidden ? "show" : "hide" : 0 );
9082					if ( method ) {
9083						jQuery._data( this, "toggle" + p, method === "show" ? "hide" : "show" );
9084						e[ method ]();
9085					} else {
9086						e[ val ]();
9087					}
9088
9089				} else {
9090					parts = rfxnum.exec( val );
9091					start = e.cur();
9092
9093					if ( parts ) {
9094						end = parseFloat( parts[2] );
9095						unit = parts[3] || ( jQuery.cssNumber[ p ] ? "" : "px" );
9096
9097						// We need to compute starting value
9098						if ( unit !== "px" ) {
9099							jQuery.style( this, p, (end || 1) + unit);
9100							start = ( (end || 1) / e.cur() ) * start;
9101							jQuery.style( this, p, start + unit);
9102						}
9103
9104						// If a +=/-= token was provided, we're doing a relative animation
9105						if ( parts[1] ) {
9106							end = ( (parts[ 1 ] === "-=" ? -1 : 1) * end ) + start;
9107						}
9108
9109						e.custom( start, end, unit );
9110
9111					} else {
9112						e.custom( start, val, "" );
9113					}
9114				}
9115			}
9116
9117			// For JS strict compliance
9118			return true;
9119		}
9120
9121		return optall.queue === false ?
9122			this.each( doAnimation ) :
9123			this.queue( optall.queue, doAnimation );
9124	},
9125
9126	stop: function( type, clearQueue, gotoEnd ) {
9127		if ( typeof type !== "string" ) {
9128			gotoEnd = clearQueue;
9129			clearQueue = type;
9130			type = undefined;
9131		}
9132		if ( clearQueue && type !== false ) {
9133			this.queue( type || "fx", [] );
9134		}
9135
9136		return this.each(function() {
9137			var index,
9138				hadTimers = false,
9139				timers = jQuery.timers,
9140				data = jQuery._data( this );
9141
9142			// clear marker counters if we know they won't be
9143			if ( !gotoEnd ) {
9144				jQuery._unmark( true, this );
9145			}
9146
9147			function stopQueue( elem, data, index ) {
9148				var hooks = data[ index ];
9149				jQuery.removeData( elem, index, true );
9150				hooks.stop( gotoEnd );
9151			}
9152
9153			if ( type == null ) {
9154				for ( index in data ) {
9155					if ( data[ index ] && data[ index ].stop && index.indexOf(".run") === index.length - 4 ) {
9156						stopQueue( this, data, index );
9157					}
9158				}
9159			} else if ( data[ index = type + ".run" ] && data[ index ].stop ){
9160				stopQueue( this, data, index );
9161			}
9162
9163			for ( index = timers.length; index--; ) {
9164				if ( timers[ index ].elem === this && (type == null || timers[ index ].queue === type) ) {
9165					if ( gotoEnd ) {
9166
9167						// force the next step to be the last
9168						timers[ index ]( true );
9169					} else {
9170						timers[ index ].saveState();
9171					}
9172					hadTimers = true;
9173					timers.splice( index, 1 );
9174				}
9175			}
9176
9177			// start the next in the queue if the last step wasn't forced
9178			// timers currently will call their complete callbacks, which will dequeue
9179			// but only if they were gotoEnd
9180			if ( !( gotoEnd && hadTimers ) ) {
9181				jQuery.dequeue( this, type );
9182			}
9183		});
9184	}
9185
9186});
9187
9188// Animations created synchronously will run synchronously
9189function createFxNow() {
9190	setTimeout( clearFxNow, 0 );
9191	return ( fxNow = jQuery.now() );
9192}
9193
9194function clearFxNow() {
9195	fxNow = undefined;
9196}
9197
9198// Generate parameters to create a standard animation
9199function genFx( type, num ) {
9200	var obj = {};
9201
9202	jQuery.each( fxAttrs.concat.apply([], fxAttrs.slice( 0, num )), function() {
9203		obj[ this ] = type;
9204	});
9205
9206	return obj;
9207}
9208
9209// Generate shortcuts for custom animations
9210jQuery.each({
9211	slideDown: genFx( "show", 1 ),
9212	slideUp: genFx( "hide", 1 ),
9213	slideToggle: genFx( "toggle", 1 ),
9214	fadeIn: { opacity: "show" },
9215	fadeOut: { opacity: "hide" },
9216	fadeToggle: { opacity: "toggle" }
9217}, function( name, props ) {
9218	jQuery.fn[ name ] = function( speed, easing, callback ) {
9219		return this.animate( props, speed, easing, callback );
9220	};
9221});
9222
9223jQuery.extend({
9224	speed: function( speed, easing, fn ) {
9225		var opt = speed && typeof speed === "object" ? jQuery.extend( {}, speed ) : {
9226			complete: fn || !fn && easing ||
9227				jQuery.isFunction( speed ) && speed,
9228			duration: speed,
9229			easing: fn && easing || easing && !jQuery.isFunction( easing ) && easing
9230		};
9231
9232		opt.duration = jQuery.fx.off ? 0 : typeof opt.duration === "number" ? opt.duration :
9233			opt.duration in jQuery.fx.speeds ? jQuery.fx.speeds[ opt.duration ] : jQuery.fx.speeds._default;
9234
9235		// normalize opt.queue - true/undefined/null -> "fx"
9236		if ( opt.queue == null || opt.queue === true ) {
9237			opt.queue = "fx";
9238		}
9239
9240		// Queueing
9241		opt.old = opt.complete;
9242
9243		opt.complete = function( noUnmark ) {
9244			if ( jQuery.isFunction( opt.old ) ) {
9245				opt.old.call( this );
9246			}
9247
9248			if ( opt.queue ) {
9249				jQuery.dequeue( this, opt.queue );
9250			} else if ( noUnmark !== false ) {
9251				jQuery._unmark( this );
9252			}
9253		};
9254
9255		return opt;
9256	},
9257
9258	easing: {
9259		linear: function( p, n, firstNum, diff ) {
9260			return firstNum + diff * p;
9261		},
9262		swing: function( p, n, firstNum, diff ) {
9263			return ( ( -Math.cos( p*Math.PI ) / 2 ) + 0.5 ) * diff + firstNum;
9264		}
9265	},
9266
9267	timers: [],
9268
9269	fx: function( elem, options, prop ) {
9270		this.options = options;
9271		this.elem = elem;
9272		this.prop = prop;
9273
9274		options.orig = options.orig || {};
9275	}
9276
9277});
9278
9279jQuery.fx.prototype = {
9280	// Simple function for setting a style value
9281	update: function() {
9282		if ( this.options.step ) {
9283			this.options.step.call( this.elem, this.now, this );
9284		}
9285
9286		( jQuery.fx.step[ this.prop ] || jQuery.fx.step._default )( this );
9287	},
9288
9289	// Get the current size
9290	cur: function() {
9291		if ( this.elem[ this.prop ] != null && (!this.elem.style || this.elem.style[ this.prop ] == null) ) {
9292			return this.elem[ this.prop ];
9293		}
9294
9295		var parsed,
9296			r = jQuery.css( this.elem, this.prop );
9297		// Empty strings, null, undefined and "auto" are converted to 0,
9298		// complex values such as "rotate(1rad)" are returned as is,
9299		// simple values such as "10px" are parsed to Float.
9300		return isNaN( parsed = parseFloat( r ) ) ? !r || r === "auto" ? 0 : r : parsed;
9301	},
9302
9303	// Start an animation from one number to another
9304	custom: function( from, to, unit ) {
9305		var self = this,
9306			fx = jQuery.fx;
9307
9308		this.startTime = fxNow || createFxNow();
9309		this.end = to;
9310		this.now = this.start = from;
9311		this.pos = this.state = 0;
9312		this.unit = unit || this.unit || ( jQuery.cssNumber[ this.prop ] ? "" : "px" );
9313
9314		function t( gotoEnd ) {
9315			return self.step( gotoEnd );
9316		}
9317
9318		t.queue = this.options.queue;
9319		t.elem = this.elem;
9320		t.saveState = function() {
9321			if ( self.options.hide && jQuery._data( self.elem, "fxshow" + self.prop ) === undefined ) {
9322				jQuery._data( self.elem, "fxshow" + self.prop, self.start );
9323			}
9324		};
9325
9326		if ( t() && jQuery.timers.push(t) && !timerId ) {
9327			timerId = setInterval( fx.tick, fx.interval );
9328		}
9329	},
9330
9331	// Simple 'show' function
9332	show: function() {
9333		var dataShow = jQuery._data( this.elem, "fxshow" + this.prop );
9334
9335		// Remember where we started, so that we can go back to it later
9336		this.options.orig[ this.prop ] = dataShow || jQuery.style( this.elem, this.prop );
9337		this.options.show = true;
9338
9339		// Begin the animation
9340		// Make sure that we start at a small width/height to avoid any flash of content
9341		if ( dataShow !== undefined ) {
9342			// This show is picking up where a previous hide or show left off
9343			this.custom( this.cur(), dataShow );
9344		} else {
9345			this.custom( this.prop === "width" || this.prop === "height" ? 1 : 0, this.cur() );
9346		}
9347
9348		// Start by showing the element
9349		jQuery( this.elem ).show();
9350	},
9351
9352	// Simple 'hide' function
9353	hide: function() {
9354		// Remember where we started, so that we can go back to it later
9355		this.options.orig[ this.prop ] = jQuery._data( this.elem, "fxshow" + this.prop ) || jQuery.style( this.elem, this.prop );
9356		this.options.hide = true;
9357
9358		// Begin the animation
9359		this.custom( this.cur(), 0 );
9360	},
9361
9362	// Each step of an animation
9363	step: function( gotoEnd ) {
9364		var p, n, complete,
9365			t = fxNow || createFxNow(),
9366			done = true,
9367			elem = this.elem,
9368			options = this.options;
9369
9370		if ( gotoEnd || t >= options.duration + this.startTime ) {
9371			this.now = this.end;
9372			this.pos = this.state = 1;
9373			this.update();
9374
9375			options.animatedProperties[ this.prop ] = true;
9376
9377			for ( p in options.animatedProperties ) {
9378				if ( options.animatedProperties[ p ] !== true ) {
9379					done = false;
9380				}
9381			}
9382
9383			if ( done ) {
9384				// Reset the overflow
9385				if ( options.overflow != null && !jQuery.support.shrinkWrapBlocks ) {
9386
9387					jQuery.each( [ "", "X", "Y" ], function( index, value ) {
9388						elem.style[ "overflow" + value ] = options.overflow[ index ];
9389					});
9390				}
9391
9392				// Hide the element if the "hide" operation was done
9393				if ( options.hide ) {
9394					jQuery( elem ).hide();
9395				}
9396
9397				// Reset the properties, if the item has been hidden or shown
9398				if ( options.hide || options.show ) {
9399					for ( p in options.animatedProperties ) {
9400						jQuery.style( elem, p, options.orig[ p ] );
9401						jQuery.removeData( elem, "fxshow" + p, true );
9402						// Toggle data is no longer needed
9403						jQuery.removeData( elem, "toggle" + p, true );
9404					}
9405				}
9406
9407				// Execute the complete function
9408				// in the event that the complete function throws an exception
9409				// we must ensure it won't be called twice. #5684
9410
9411				complete = options.complete;
9412				if ( complete ) {
9413
9414					options.complete = false;
9415					complete.call( elem );
9416				}
9417			}
9418
9419			return false;
9420
9421		} else {
9422			// classical easing cannot be used with an Infinity duration
9423			if ( options.duration == Infinity ) {
9424				this.now = t;
9425			} else {
9426				n = t - this.startTime;
9427				this.state = n / options.duration;
9428
9429				// Perform the easing function, defaults to swing
9430				this.pos = jQuery.easing[ options.animatedProperties[this.prop] ]( this.state, n, 0, 1, options.duration );
9431				this.now = this.start + ( (this.end - this.start) * this.pos );
9432			}
9433			// Perform the next step of the animation
9434			this.update();
9435		}
9436
9437		return true;
9438	}
9439};
9440
9441jQuery.extend( jQuery.fx, {
9442	tick: function() {
9443		var timer,
9444			timers = jQuery.timers,
9445			i = 0;
9446
9447		for ( ; i < timers.length; i++ ) {
9448			timer = timers[ i ];
9449			// Checks the timer has not already been removed
9450			if ( !timer() && timers[ i ] === timer ) {
9451				timers.splice( i--, 1 );
9452			}
9453		}
9454
9455		if ( !timers.length ) {
9456			jQuery.fx.stop();
9457		}
9458	},
9459
9460	interval: 13,
9461
9462	stop: function() {
9463		clearInterval( timerId );
9464		timerId = null;
9465	},
9466
9467	speeds: {
9468		slow: 600,
9469		fast: 200,
9470		// Default speed
9471		_default: 400
9472	},
9473
9474	step: {
9475		opacity: function( fx ) {
9476			jQuery.style( fx.elem, "opacity", fx.now );
9477		},
9478
9479		_default: function( fx ) {
9480			if ( fx.elem.style && fx.elem.style[ fx.prop ] != null ) {
9481				fx.elem.style[ fx.prop ] = fx.now + fx.unit;
9482			} else {
9483				fx.elem[ fx.prop ] = fx.now;
9484			}
9485		}
9486	}
9487});
9488
9489// Adds width/height step functions
9490// Do not set anything below 0
9491jQuery.each([ "width", "height" ], function( i, prop ) {
9492	jQuery.fx.step[ prop ] = function( fx ) {
9493		jQuery.style( fx.elem, prop, Math.max(0, fx.now) + fx.unit );
9494	};
9495});
9496
9497if ( jQuery.expr && jQuery.expr.filters ) {
9498	jQuery.expr.filters.animated = function( elem ) {
9499		return jQuery.grep(jQuery.timers, function( fn ) {
9500			return elem === fn.elem;
9501		}).length;
9502	};
9503}
9504
9505// Try to restore the default display value of an element
9506function defaultDisplay( nodeName ) {
9507
9508	if ( !elemdisplay[ nodeName ] ) {
9509
9510		var body = document.body,
9511			elem = jQuery( "<" + nodeName + ">" ).appendTo( body ),
9512			display = elem.css( "display" );
9513		elem.remove();
9514
9515		// If the simple way fails,
9516		// get element's real default display by attaching it to a temp iframe
9517		if ( display === "none" || display === "" ) {
9518			// No iframe to use yet, so create it
9519			if ( !iframe ) {
9520				iframe = document.createElement( "iframe" );
9521				iframe.frameBorder = iframe.width = iframe.height = 0;
9522			}
9523
9524			body.appendChild( iframe );
9525
9526			// Create a cacheable copy of the iframe document on first call.
9527			// IE and Opera will allow us to reuse the iframeDoc without re-writing the fake HTML
9528			// document to it; WebKit & Firefox won't allow reusing the iframe document.
9529			if ( !iframeDoc || !iframe.createElement ) {
9530				iframeDoc = ( iframe.contentWindow || iframe.contentDocument ).document;
9531				iframeDoc.write( ( document.compatMode === "CSS1Compat" ? "<!doctype html>" : "" ) + "<html><body>" );
9532				iframeDoc.close();
9533			}
9534
9535			elem = iframeDoc.createElement( nodeName );
9536
9537			iframeDoc.body.appendChild( elem );
9538
9539			display = jQuery.css( elem, "display" );
9540			body.removeChild( iframe );
9541		}
9542
9543		// Store the correct default display
9544		elemdisplay[ nodeName ] = display;
9545	}
9546
9547	return elemdisplay[ nodeName ];
9548}
9549
9550
9551
9552
9553var rtable = /^t(?:able|d|h)$/i,
9554	rroot = /^(?:body|html)$/i;
9555
9556if ( "getBoundingClientRect" in document.documentElement ) {
9557	jQuery.fn.offset = function( options ) {
9558		var elem = this[0], box;
9559
9560		if ( options ) {
9561			return this.each(function( i ) {
9562				jQuery.offset.setOffset( this, options, i );
9563			});
9564		}
9565
9566		if ( !elem || !elem.ownerDocument ) {
9567			return null;
9568		}
9569
9570		if ( elem === elem.ownerDocument.body ) {
9571			return jQuery.offset.bodyOffset( elem );
9572		}
9573
9574		try {
9575			box = elem.getBoundingClientRect();
9576		} catch(e) {}
9577
9578		var doc = elem.ownerDocument,
9579			docElem = doc.documentElement;
9580
9581		// Make sure we're not dealing with a disconnected DOM node
9582		if ( !box || !jQuery.contains( docElem, elem ) ) {
9583			return box ? { top: box.top, left: box.left } : { top: 0, left: 0 };
9584		}
9585
9586		var body = doc.body,
9587			win = getWindow(doc),
9588			clientTop  = docElem.clientTop  || body.clientTop  || 0,
9589			clientLeft = docElem.clientLeft || body.clientLeft || 0,
9590			scrollTop  = win.pageYOffset || jQuery.support.boxModel && docElem.scrollTop  || body.scrollTop,
9591			scrollLeft = win.pageXOffset || jQuery.support.boxModel && docElem.scrollLeft || body.scrollLeft,
9592			top  = box.top  + scrollTop  - clientTop,
9593			left = box.left + scrollLeft - clientLeft;
9594
9595		return { top: top, left: left };
9596	};
9597
9598} else {
9599	jQuery.fn.offset = function( options ) {
9600		var elem = this[0];
9601
9602		if ( options ) {
9603			return this.each(function( i ) {
9604				jQuery.offset.setOffset( this, options, i );
9605			});
9606		}
9607
9608		if ( !elem || !elem.ownerDocument ) {
9609			return null;
9610		}
9611
9612		if ( elem === elem.ownerDocument.body ) {
9613			return jQuery.offset.bodyOffset( elem );
9614		}
9615
9616		var computedStyle,
9617			offsetParent = elem.offsetParent,
9618			prevOffsetParent = elem,
9619			doc = elem.ownerDocument,
9620			docElem = doc.documentElement,
9621			body = doc.body,
9622			defaultView = doc.defaultView,
9623			prevComputedStyle = defaultView ? defaultView.getComputedStyle( elem, null ) : elem.currentStyle,
9624			top = elem.offsetTop,
9625			left = elem.offsetLeft;
9626
9627		while ( (elem = elem.parentNode) && elem !== body && elem !== docElem ) {
9628			if ( jQuery.support.fixedPosition && prevComputedStyle.position === "fixed" ) {
9629				break;
9630			}
9631
9632			computedStyle = defaultView ? defaultView.getComputedStyle(elem, null) : elem.currentStyle;
9633			top  -= elem.scrollTop;
9634			left -= elem.scrollLeft;
9635
9636			if ( elem === offsetParent ) {
9637				top  += elem.offsetTop;
9638				left += elem.offsetLeft;
9639
9640				if ( jQuery.support.doesNotAddBorder && !(jQuery.support.doesAddBorderForTableAndCells && rtable.test(elem.nodeName)) ) {
9641					top  += parseFloat( computedStyle.borderTopWidth  ) || 0;
9642					left += parseFloat( computedStyle.borderLeftWidth ) || 0;
9643				}
9644
9645				prevOffsetParent = offsetParent;
9646				offsetParent = elem.offsetParent;
9647			}
9648
9649			if ( jQuery.support.subtractsBorderForOverflowNotVisible && computedStyle.overflow !== "visible" ) {
9650				top  += parseFloat( computedStyle.borderTopWidth  ) || 0;
9651				left += parseFloat( computedStyle.borderLeftWidth ) || 0;
9652			}
9653
9654			prevComputedStyle = computedStyle;
9655		}
9656
9657		if ( prevComputedStyle.position === "relative" || prevComputedStyle.position === "static" ) {
9658			top  += body.offsetTop;
9659			left += body.offsetLeft;
9660		}
9661
9662		if ( jQuery.support.fixedPosition && prevComputedStyle.position === "fixed" ) {
9663			top  += Math.max( docElem.scrollTop, body.scrollTop );
9664			left += Math.max( docElem.scrollLeft, body.scrollLeft );
9665		}
9666
9667		return { top: top, left: left };
9668	};
9669}
9670
9671jQuery.offset = {
9672
9673	bodyOffset: function( body ) {
9674		var top = body.offsetTop,
9675			left = body.offsetLeft;
9676
9677		if ( jQuery.support.doesNotIncludeMarginInBodyOffset ) {
9678			top  += parseFloat( jQuery.css(body, "marginTop") ) || 0;
9679			left += parseFloat( jQuery.css(body, "marginLeft") ) || 0;
9680		}
9681
9682		return { top: top, left: left };
9683	},
9684
9685	setOffset: function( elem, options, i ) {
9686		var position = jQuery.css( elem, "position" );
9687
9688		// set position first, in-case top/left are set even on static elem
9689		if ( position === "static" ) {
9690			elem.style.position = "relative";
9691		}
9692
9693		var curElem = jQuery( elem ),
9694			curOffset = curElem.offset(),
9695			curCSSTop = jQuery.css( elem, "top" ),
9696			curCSSLeft = jQuery.css( elem, "left" ),
9697			calculatePosition = ( position === "absolute" || position === "fixed" ) && jQuery.inArray("auto", [curCSSTop, curCSSLeft]) > -1,
9698			props = {}, curPosition = {}, curTop, curLeft;
9699
9700		// need to be able to calculate position if either top or left is auto and position is either absolute or fixed
9701		if ( calculatePosition ) {
9702			curPosition = curElem.position();
9703			curTop = curPosition.top;
9704			curLeft = curPosition.left;
9705		} else {
9706			curTop = parseFloat( curCSSTop ) || 0;
9707			curLeft = parseFloat( curCSSLeft ) || 0;
9708		}
9709
9710		if ( jQuery.isFunction( options ) ) {
9711			options = options.call( elem, i, curOffset );
9712		}
9713
9714		if ( options.top != null ) {
9715			props.top = ( options.top - curOffset.top ) + curTop;
9716		}
9717		if ( options.left != null ) {
9718			props.left = ( options.left - curOffset.left ) + curLeft;
9719		}
9720
9721		if ( "using" in options ) {
9722			options.using.call( elem, props );
9723		} else {
9724			curElem.css( props );
9725		}
9726	}
9727};
9728
9729
9730jQuery.fn.extend({
9731
9732	position: function() {
9733		if ( !this[0] ) {
9734			return null;
9735		}
9736
9737		var elem = this[0],
9738
9739		// Get *real* offsetParent
9740		offsetParent = this.offsetParent(),
9741
9742		// Get correct offsets
9743		offset       = this.offset(),
9744		parentOffset = rroot.test(offsetParent[0].nodeName) ? { top: 0, left: 0 } : offsetParent.offset();
9745
9746		// Subtract element margins
9747		// note: when an element has margin: auto the offsetLeft and marginLeft
9748		// are the same in Safari causing offset.left to incorrectly be 0
9749		offset.top  -= parseFloat( jQuery.css(elem, "marginTop") ) || 0;
9750		offset.left -= parseFloat( jQuery.css(elem, "marginLeft") ) || 0;
9751
9752		// Add offsetParent borders
9753		parentOffset.top  += parseFloat( jQuery.css(offsetParent[0], "borderTopWidth") ) || 0;
9754		parentOffset.left += parseFloat( jQuery.css(offsetParent[0], "borderLeftWidth") ) || 0;
9755
9756		// Subtract the two offsets
9757		return {
9758			top:  offset.top  - parentOffset.top,
9759			left: offset.left - parentOffset.left
9760		};
9761	},
9762
9763	offsetParent: function() {
9764		return this.map(function() {
9765			var offsetParent = this.offsetParent || document.body;
9766			while ( offsetParent && (!rroot.test(offsetParent.nodeName) && jQuery.css(offsetParent, "position") === "static") ) {
9767				offsetParent = offsetParent.offsetParent;
9768			}
9769			return offsetParent;
9770		});
9771	}
9772});
9773
9774
9775// Create scrollLeft and scrollTop methods
9776jQuery.each( ["Left", "Top"], function( i, name ) {
9777	var method = "scroll" + name;
9778
9779	jQuery.fn[ method ] = function( val ) {
9780		var elem, win;
9781
9782		if ( val === undefined ) {
9783			elem = this[ 0 ];
9784
9785			if ( !elem ) {
9786				return null;
9787			}
9788
9789			win = getWindow( elem );
9790
9791			// Return the scroll offset
9792			return win ? ("pageXOffset" in win) ? win[ i ? "pageYOffset" : "pageXOffset" ] :
9793				jQuery.support.boxModel && win.document.documentElement[ method ] ||
9794					win.document.body[ method ] :
9795				elem[ method ];
9796		}
9797
9798		// Set the scroll offset
9799		return this.each(function() {
9800			win = getWindow( this );
9801
9802			if ( win ) {
9803				win.scrollTo(
9804					!i ? val : jQuery( win ).scrollLeft(),
9805					 i ? val : jQuery( win ).scrollTop()
9806				);
9807
9808			} else {
9809				this[ method ] = val;
9810			}
9811		});
9812	};
9813});
9814
9815function getWindow( elem ) {
9816	return jQuery.isWindow( elem ) ?
9817		elem :
9818		elem.nodeType === 9 ?
9819			elem.defaultView || elem.parentWindow :
9820			false;
9821}
9822
9823
9824
9825
9826// Create width, height, innerHeight, innerWidth, outerHeight and outerWidth methods
9827jQuery.each([ "Height", "Width" ], function( i, name ) {
9828
9829	var type = name.toLowerCase();
9830
9831	// innerHeight and innerWidth
9832	jQuery.fn[ "inner" + name ] = function() {
9833		var elem = this[0];
9834		return elem ?
9835			elem.style ?
9836			parseFloat( jQuery.css( elem, type, "padding" ) ) :
9837			this[ type ]() :
9838			null;
9839	};
9840
9841	// outerHeight and outerWidth
9842	jQuery.fn[ "outer" + name ] = function( margin ) {
9843		var elem = this[0];
9844		return elem ?
9845			elem.style ?
9846			parseFloat( jQuery.css( elem, type, margin ? "margin" : "border" ) ) :
9847			this[ type ]() :
9848			null;
9849	};
9850
9851	jQuery.fn[ type ] = function( size ) {
9852		// Get window width or height
9853		var elem = this[0];
9854		if ( !elem ) {
9855			return size == null ? null : this;
9856		}
9857
9858		if ( jQuery.isFunction( size ) ) {
9859			return this.each(function( i ) {
9860				var self = jQuery( this );
9861				self[ type ]( size.call( this, i, self[ type ]() ) );
9862			});
9863		}
9864
9865		if ( jQuery.isWindow( elem ) ) {
9866			// Everyone else use document.documentElement or document.body depending on Quirks vs Standards mode
9867			// 3rd condition allows Nokia support, as it supports the docElem prop but not CSS1Compat
9868			var docElemProp = elem.document.documentElement[ "client" + name ],
9869				body = elem.document.body;
9870			return elem.document.compatMode === "CSS1Compat" && docElemProp ||
9871				body && body[ "client" + name ] || docElemProp;
9872
9873		// Get document width or height
9874		} else if ( elem.nodeType === 9 ) {
9875			// Either scroll[Width/Height] or offset[Width/Height], whichever is greater
9876			return Math.max(
9877				elem.documentElement["client" + name],
9878				elem.body["scroll" + name], elem.documentElement["scroll" + name],
9879				elem.body["offset" + name], elem.documentElement["offset" + name]
9880			);
9881
9882		// Get or set width or height on the element
9883		} else if ( size === undefined ) {
9884			var orig = jQuery.css( elem, type ),
9885				ret = parseFloat( orig );
9886
9887			return jQuery.isNumeric( ret ) ? ret : orig;
9888
9889		// Set the width or height on the element (default to pixels if value is unitless)
9890		} else {
9891			return this.css( type, typeof size === "string" ? size : size + "px" );
9892		}
9893	};
9894
9895});
9896
9897
9898
9899
9900// Expose jQuery to the global object
9901window.jQuery = window.$ = jQuery;
9902
9903// Expose jQuery as an AMD module, but only for AMD loaders that
9904// understand the issues with loading multiple versions of jQuery
9905// in a page that all might call define(). The loader will indicate
9906// they have special allowances for multiple jQuery versions by
9907// specifying define.amd.jQuery = true. Register as a named module,
9908// since jQuery can be concatenated with other files that may use define,
9909// but not use a proper concatenation script that understands anonymous
9910// AMD modules. A named AMD is safest and most robust way to register.
9911// Lowercase jquery is used because AMD module names are derived from
9912// file names, and jQuery is normally delivered in a lowercase file name.
9913// Do this after creating the global so that if an AMD module wants to call
vendor: 5,421 bytes, lines 9914-10010
9914// noConflict to hide this version of jQuery, it will work.
9915if ( typeof define === "function" && define.amd && define.amd.jQuery ) {
9916	define( "jquery", [], function () { return jQuery; } );
9917}
9918
9919
9920
9921})( window );
9922
9923/*! Copyright (c) 2013 Brandon Aaron (http://brandon.aaron.sh)
9924 * Licensed under the MIT License (LICENSE.txt).
9925 *
9926 * Version: 3.1.12
9927 *
9928 * Requires: jQuery 1.2.2+
9929 */
9930!function(a){"function"==typeof define&&define.amd?define(["jquery"],a):"object"==typeof exports?module.exports=a:a(jQuery)}(function(a){function b(b){var g=b||window.event,h=i.call(arguments,1),j=0,l=0,m=0,n=0,o=0,p=0;if(b=a.event.fix(g),b.type="mousewheel","detail"in g&&(m=-1*g.detail),"wheelDelta"in g&&(m=g.wheelDelta),"wheelDeltaY"in g&&(m=g.wheelDeltaY),"wheelDeltaX"in g&&(l=-1*g.wheelDeltaX),"axis"in g&&g.axis===g.HORIZONTAL_AXIS&&(l=-1*m,m=0),j=0===m?l:m,"deltaY"in g&&(m=-1*g.deltaY,j=m),"deltaX"in g&&(l=g.deltaX,0===m&&(j=-1*l)),0!==m||0!==l){if(1===g.deltaMode){var q=a.data(this,"mousewheel-line-height");j*=q,m*=q,l*=q}else if(2===g.deltaMode){var r=a.data(this,"mousewheel-page-height");j*=r,m*=r,l*=r}if(n=Math.max(Math.abs(m),Math.abs(l)),(!f||f>n)&&(f=n,d(g,n)&&(f/=40)),d(g,n)&&(j/=40,l/=40,m/=40),j=Math[j>=1?"floor":"ceil"](j/f),l=Math[l>=1?"floor":"ceil"](l/f),m=Math[m>=1?"floor":"ceil"](m/f),k.settings.normalizeOffset&&this.getBoundingClientRect){var s=this.getBoundingClientRect();o=b.clientX-s.left,p=b.clientY-s.top}return b.deltaX=l,b.deltaY=m,b.deltaFactor=f,b.offsetX=o,b.offsetY=p,b.deltaMode=0,h.unshift(b,j,l,m),e&&clearTimeout(e),e=setTimeout(c,200),(a.event.dispatch||a.event.handle).apply(this,h)}}function c(){f=null}function d(a,b){return k.settings.adjustOldDeltas&&"mousewheel"===a.type&&b%120===0}var e,f,g=["wheel","mousewheel","DOMMouseScroll","MozMousePixelScroll"],h="onwheel"in document||document.documentMode>=9?["wheel"]:["mousewheel","DomMouseScroll","MozMousePixelScroll"],i=Array.prototype.slice;if(a.event.fixHooks)for(var j=g.length;j;)a.event.fixHooks[g[--j]]=a.event.mouseHooks;var k=a.event.special.mousewheel={version:"3.1.12",setup:function(){if(this.addEventListener)for(var c=h.length;c;)this.addEventListener(h[--c],b,!1);else this.onmousewheel=b;a.data(this,"mousewheel-line-height",k.getLineHeight(this)),a.data(this,"mousewheel-page-height",k.getPageHeight(this))},teardown:function(){if(this.removeEventListener)for(var c=h.length;c;)this.removeEventListener(h[--c],b,!1);else this.onmousewheel=null;a.removeData(this,"mousewheel-line-height"),a.removeData(this,"mousewheel-page-height")},getLineHeight:function(b){var c=a(b),d=c["offsetParent"in a.fn?"offsetParent":"parent"]();return d.length||(d=a("body")),parseInt(d.css("fontSize"),10)||parseInt(c.css("fontSize"),10)||16},getPageHeight:function(b){return a(b).height()},settings:{adjustOldDeltas:!0,normalizeOffset:!0}};a.fn.extend({mousewheel:function(a){return a?this.bind("mousewheel",a):this.trigger("mousewheel")},unmousewheel:function(a){return this.unbind("mousewheel",a)}})});
9931/** //** ----= mwImageReader	=--------------------------------------------------------------------------------------\**//** \
9932*
9933*	Fast loads image from File object and runs callback on it.	
9934*
9935*	@param		object(File)	$file		- File object to use as source.
9936*	@param		function	$callback	- Callback to execute.
9937*
9938\**//** ---------------------------------------------------------------------------= by Mr.V!T @ Morad Media Inc. =----/** //**/
9939function mwImageReader ($file, $callback) {
9940
9941	// Using native DOM, so need to fallback if jQuery happen
9942	if ( $file instanceof jQuery )
9943		$file = $file.get(0);
9944
9945	// If file is DOM input - using first file
9946	if ( $file instanceof HTMLInputElement ) 
9947		$file = $file.files[0];
9948
9949	// Nothng to do if no files selected
9950	if ( !$file )
9951		return;
9952
9953	// Nothing to do for non image file
9954	if ( !$file.type.match(/image.*/) )
9955		return;
9956
9957	// Reading file
9958	var $reader = new FileReader();
9959	$reader.onload = function ($e) {
9960		
9961		// Issuing callback
9962		if ( typeof($callback) == 'function' )
9963			$callback($reader.result);
9964		
9965	}; //FUNC reader.onload
9966	
9967	$reader.readAsDataURL($file);
9968	
9969} //FUNC mwImageReader
9970
9971/** //** ----= mwImagePreview	=--------------------------------------------------------------------------------------\**//** \
9972*
9973*	Fast loads image from File object (usually obtained from file input) into <img> element using HTML5 filesAPI.	
9974*
9975*	@param		jQuery		$image	- Image to update.
9976*	@param		object(File)	$file	- File object to use as source.
9977*
9978\**//** ---------------------------------------------------------------------------= by Mr.V!T @ Morad Media Inc. =----/** //**/
9979function mwImagePreview ($image, $file, $callback) {
9980
9981	// Using native DOM, so need to fallback if jQuery happen
9982	if ( $image instanceof jQuery )
9983		$image = $image.get(0);
9984
9985	// Reading image from file input and applying to image
9986	mwImageReader($file, function ($src) {
9987		
9988		// Saving old event handler, replacing with own and then returning it back
9989		var $oe = $image.onload; 
9990
9991		// Triggering callback after image was actually drawn
9992		// Setting this before
9993		$image.onload = function () {
9994			
9995			// Resetting onload we just added
9996			$image.onload = $oe;
9997			
9998			// Issuing callback
9999			if ( typeof($callback) == 'function' )
10000				$callback($src);
10001				
10002		}; //FUNC image.onLoad
10003		
10004		// Now after event is setup, we can actually apply loaded image
10005		$image.src = $src;
10006		
10007	}); //mwImageReader.callback
10008
10009} //FUNC mwImagePreview
10010
10011/** //** ----= mwBackgroundPreview	=------------------------------------------------------------------------------\**//** \
10012*
10013*	Fast loads image from File object (usually obtained from file input) into background of element using HTML5 filesAPI.	
10014*
10015*	@param		jQuery		$image	- Image to update.
10016*	@param		object(File)	$file	- File object to use as source.
10017*
10018\**//** ---------------------------------------------------------------------------= by Mr.V!T @ Morad Media Inc. =----/** //**/
10019function mwBackgroundPreview ($image, $file, $callback) {
10020
10021	// Reading image from file input and applying to image
10022	mwImageReader($file, function ($src) {
10023
10024		// Now after event is setup, we can actually apply loaded image
10025		$image.css('background-image', "url('"+$src+"')");
10026	
10027		// Issuing callback
10028		if ( typeof($callback) == 'function' )
10029			$callback($src);
10030		
10031	}); //mwImageReader.callback
10032
10033} //FUNC mwBackgroundPreview
10034
10035/** //** ----= mwOnImageChange	=--------------------------------------------------------------------------------------\**//** \
10036*
10037*	Binds onChange event to given file input, and updates specified <img> element using HTML5 files API once input changes.	
10038*
10039*	@param		jQuery		$image		- Image to update.
10040*	@param		jQuery		$imageInput	- File input to bing.
10041*
10042\**//** ---------------------------------------------------------------------------= by Mr.V!T @ Morad Media Inc. =----/** //**/
10043function mwOnImageChange ($image, $imageInput) {
10044
10045	// Need to check if files API is really available before do fancy tricks
10046	if ( !isFilesAPI() ) return;
10047
10048	$imageInput
10049		.off('change.mwOnImageChange')
10050		.on('change.mwOnImageChange', function () {
10051			
10052			// Safety check
10053			if ( !this.files.length )
10054				return;
10055			
10056			// Using first given file (single image update essencially)
10057			mwImagePreview($image, this.files[0]);
10058
10059		}) //onChange
10060	; //jQuery imageInput
10061	
10062} //FUNC mwOnImageChange
10063
10064/** //** ----= mwLoadImage	=--------------------------------------------------------------------------------------\**//** \
10065*
10066*	Adds preloader to specified <img> element. Uses expected dimensions to draw proper loading box. 
10067*	Uses native DOM tree methods, to be less depended on onLoad event (no need to wait other libs to load).
10068*	Detects element proportinal changes (in case of dynamic image widths of responsive images).
10069*
10070*	@param		DOMElement	$image		- Image to load.
10071*	@param		int		$width, $height	- Expected image dimensions.
10072*
10073\**//** ---------------------------------------------------------------------------= by Mr.V!T @ Morad Media Inc. =----/** //**/
10074function mwLoadImage ($image, $width, $height) {
10075
10076	if ( !$image )
10077		return;
10078
10079	// Using native DOM, so need to fallback if jQuery happen
10080	if ( $image instanceof jQuery || $image.jquery )
10081		$image = $image.get(0);
10082
10083	// Not doing anyting if image is loaded already (cache?)
10084	if ( $image.naturalWidth || $image.naturalHeight )
10085		return;
10086
10087	// Have to have some defaults,
10088	$width	= $width || $image.scrollWidth;
10089	$height	= $height || 30;
10090
10091	// Calculating ratio for scticking sizes. Will calculate on place from width, for full responsive behavior
10092	var $ratio	= $width / $height;
10093	
10094	// Saving original image styles, to restore after it's loaded
10095	var $css = {
10096		'width'		: $image.style.width,
10097		'height'	: $image.style.height,
10098	} //$css
10099
10100	// Also temprary removing alt text, so saving it too
10101	var $alt = $image.getAttribute('alt');
10102	$image.setAttribute('alt', '');
10103
10104	// Setting loading state	
10105	$image.className += ' loading';
10106
10107	// Setting image size strictly while it loads (to make sure loader look correctly)
10108	// Calculating through ratio, to make sure % widths/heights are handled correctly on responsive
10109	// ToDo: Test source dimensions, might be clientWidth better to use as source
10110	$image.style.width	= $width + 'px';
10111	$image.style.height	= Math.round($width / $ratio) + 'px';
10112	
10113	// When image loaded - need to revert changes and remove loading state
10114	$image.onload = function () {
10115
10116		// Removing class
10117		$image.className = $image.className.replace('loading', '');
10118		
10119		// Setting height/width back
10120		$image.style.width = $css.width;
10121		$image.style.height = $css.height;
10122		
10123		// Restoring alt
10124		$image.setAttribute('alt', $alt);
10125		
10126		// Unsetting onload
10127		$image.onload = false;
10128	} //FUNC $image.onLoad			
10129	
10130} //FUNC mwLoadImage
10131
10132/** //** ----= mwInitMedia	=--------------------------------------------------------------------------------------\**//** \
10133*
10134*	Initiates common morweb audion/video players on page.
10135*
10136*	@param		jQuery	[$context]	- Context to search within.	
10137*
10138\**//** ---------------------------------------------------------------------------= by Mr.V!T @ Morad Media Inc. =----/** //**/
10139function mwInitMedia ($context) {
10140
10141	// ToDo: Implement conditional media resources initiating
10142	var $el = jQuery('audio, video', $context);
10143	
10144	if ( !$el.length )
10145		return;
10146		
10147	// Initiating common audio/video
10148	$el.mediaelementplayer({
10149		enableAutosize		: false,
10150		alwaysShowControls	: false,
10151		iconSprite		: '/cms3/res/mediaelement/mejs-controls.svg',
10152		enableKeyboard		: false,
10153	//	controls		: false,
10154	//	useDefaultControls	: false,
10155
10156	/*/	// For user with CDN
10157		pluginPath		: '/cms/res/mediaelement/',
10158		shimScriptAccess	: 'always',
10159	/**/
10160		success                 : function( $mediaElement, $originalNode, $instance ) {
10161			
10162			// Storing instance as element data for easy access later
10163			jQuery($originalNode).data('mediaelement', $instance);
10164			
10165			// Workaround to retain controls attribute on video
10166			// Somehow mediaelement looses it, and fails to init
10167			// Required only on video
10168			// ToDo: invesitgate conditions
10169			// Discovered in 4.2.5
10170			// In 2025 we don't need to control controls again. Browsers evolve.
10171		/*/
10172			jQuery($originalNode).attr({
10173				'controls'      :       'controls'
10174			});
10175		
10176			setTimeout( function () {
10177			//	jQuery($originalNode).removeAttr('controls');
10178			}, 10);
10179		
10180		/*/
10181			// jQuery($originalNode).removeAttr('controls');
10182		/**/	
10183		}, //FUNC success
10184
10185	}); //mediaelementplayer.options
10186	
10187	// ToDo: Implement widget based initiations
10188	
10189} //FUNC mwInitMedia
10190
10191/*!
10192 * MediaElement.js
10193 * http://www.mediaelementjs.com/
10194 *
10195 * Wrapper that mimics native HTML5 MediaElement (audio and video)
10196 * using a variety of technologies (pure JavaScript, iframe)
10197 *
10198 * Copyright 2010-2017, John Dyer (http://j.hn/)
10199 * License: MIT
10200 *
10201 */(function(){function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s}return e})()({1:[function(_dereq_,module,exports){
10202
10203},{}],2:[function(_dereq_,module,exports){
10204(function (global){
10205var topLevel = typeof global !== 'undefined' ? global :
10206    typeof window !== 'undefined' ? window : {}
10207var minDoc = _dereq_(1);
10208
10209var doccy;
10210
10211if (typeof document !== 'undefined') {
10212    doccy = document;
10213} else {
10214    doccy = topLevel['__GLOBAL_DOCUMENT_CACHE@4'];
10215
10216    if (!doccy) {
10217        doccy = topLevel['__GLOBAL_DOCUMENT_CACHE@4'] = minDoc;
10218    }
10219}
10220
10221module.exports = doccy;
10222
10223}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
10224},{"1":1}],3:[function(_dereq_,module,exports){
10225(function (global){
10226var win;
10227
10228if (typeof window !== "undefined") {
10229    win = window;
10230} else if (typeof global !== "undefined") {
10231    win = global;
10232} else if (typeof self !== "undefined"){
10233    win = self;
10234} else {
10235    win = {};
10236}
10237
10238module.exports = win;
10239
10240}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
10241},{}],4:[function(_dereq_,module,exports){
10242(function (root) {
10243
10244  // Store setTimeout reference so promise-polyfill will be unaffected by
10245  // other code modifying setTimeout (like sinon.useFakeTimers())
10246  var setTimeoutFunc = setTimeout;
10247
10248  function noop() {}
10249  
10250  // Polyfill for Function.prototype.bind
10251  function bind(fn, thisArg) {
10252    return function () {
10253      fn.apply(thisArg, arguments);
10254    };
10255  }
10256
10257  function Promise(fn) {
10258    if (typeof this !== 'object') throw new TypeError('Promises must be constructed via new');
10259    if (typeof fn !== 'function') throw new TypeError('not a function');
10260    this._state = 0;
10261    this._handled = false;
vendor: 25,855 bytes, lines 10262-10877
10262    this._value = undefined;
10263    this._deferreds = [];
10264
10265    doResolve(fn, this);
10266  }
10267
10268  function handle(self, deferred) {
10269    while (self._state === 3) {
10270      self = self._value;
10271    }
10272    if (self._state === 0) {
10273      self._deferreds.push(deferred);
10274      return;
10275    }
10276    self._handled = true;
10277    Promise._immediateFn(function () {
10278      var cb = self._state === 1 ? deferred.onFulfilled : deferred.onRejected;
10279      if (cb === null) {
10280        (self._state === 1 ? resolve : reject)(deferred.promise, self._value);
10281        return;
10282      }
10283      var ret;
10284      try {
10285        ret = cb(self._value);
10286      } catch (e) {
10287        reject(deferred.promise, e);
10288        return;
10289      }
10290      resolve(deferred.promise, ret);
10291    });
10292  }
10293
10294  function resolve(self, newValue) {
10295    try {
10296      // Promise Resolution Procedure: https://github.com/promises-aplus/promises-spec#the-promise-resolution-procedure
10297      if (newValue === self) throw new TypeError('A promise cannot be resolved with itself.');
10298      if (newValue && (typeof newValue === 'object' || typeof newValue === 'function')) {
10299        var then = newValue.then;
10300        if (newValue instanceof Promise) {
10301          self._state = 3;
10302          self._value = newValue;
10303          finale(self);
10304          return;
10305        } else if (typeof then === 'function') {
10306          doResolve(bind(then, newValue), self);
10307          return;
10308        }
10309      }
10310      self._state = 1;
10311      self._value = newValue;
10312      finale(self);
10313    } catch (e) {
10314      reject(self, e);
10315    }
10316  }
10317
10318  function reject(self, newValue) {
10319    self._state = 2;
10320    self._value = newValue;
10321    finale(self);
10322  }
10323
10324  function finale(self) {
10325    if (self._state === 2 && self._deferreds.length === 0) {
10326      Promise._immediateFn(function() {
10327        if (!self._handled) {
10328          Promise._unhandledRejectionFn(self._value);
10329        }
10330      });
10331    }
10332
10333    for (var i = 0, len = self._deferreds.length; i < len; i++) {
10334      handle(self, self._deferreds[i]);
10335    }
10336    self._deferreds = null;
10337  }
10338
10339  function Handler(onFulfilled, onRejected, promise) {
10340    this.onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : null;
10341    this.onRejected = typeof onRejected === 'function' ? onRejected : null;
10342    this.promise = promise;
10343  }
10344
10345  /**
10346   * Take a potentially misbehaving resolver function and make sure
10347   * onFulfilled and onRejected are only called once.
10348   *
10349   * Makes no guarantees about asynchrony.
10350   */
10351  function doResolve(fn, self) {
10352    var done = false;
10353    try {
10354      fn(function (value) {
10355        if (done) return;
10356        done = true;
10357        resolve(self, value);
10358      }, function (reason) {
10359        if (done) return;
10360        done = true;
10361        reject(self, reason);
10362      });
10363    } catch (ex) {
10364      if (done) return;
10365      done = true;
10366      reject(self, ex);
10367    }
10368  }
10369
10370  Promise.prototype['catch'] = function (onRejected) {
10371    return this.then(null, onRejected);
10372  };
10373
10374  Promise.prototype.then = function (onFulfilled, onRejected) {
10375    var prom = new (this.constructor)(noop);
10376
10377    handle(this, new Handler(onFulfilled, onRejected, prom));
10378    return prom;
10379  };
10380
10381  Promise.all = function (arr) {
10382    var args = Array.prototype.slice.call(arr);
10383
10384    return new Promise(function (resolve, reject) {
10385      if (args.length === 0) return resolve([]);
10386      var remaining = args.length;
10387
10388      function res(i, val) {
10389        try {
10390          if (val && (typeof val === 'object' || typeof val === 'function')) {
10391            var then = val.then;
10392            if (typeof then === 'function') {
10393              then.call(val, function (val) {
10394                res(i, val);
10395              }, reject);
10396              return;
10397            }
10398          }
10399          args[i] = val;
10400          if (--remaining === 0) {
10401            resolve(args);
10402          }
10403        } catch (ex) {
10404          reject(ex);
10405        }
10406      }
10407
10408      for (var i = 0; i < args.length; i++) {
10409        res(i, args[i]);
10410      }
10411    });
10412  };
10413
10414  Promise.resolve = function (value) {
10415    if (value && typeof value === 'object' && value.constructor === Promise) {
10416      return value;
10417    }
10418
10419    return new Promise(function (resolve) {
10420      resolve(value);
10421    });
10422  };
10423
10424  Promise.reject = function (value) {
10425    return new Promise(function (resolve, reject) {
10426      reject(value);
10427    });
10428  };
10429
10430  Promise.race = function (values) {
10431    return new Promise(function (resolve, reject) {
10432      for (var i = 0, len = values.length; i < len; i++) {
10433        values[i].then(resolve, reject);
10434      }
10435    });
10436  };
10437
10438  // Use polyfill for setImmediate for performance gains
10439  Promise._immediateFn = (typeof setImmediate === 'function' && function (fn) { setImmediate(fn); }) ||
10440    function (fn) {
10441      setTimeoutFunc(fn, 0);
10442    };
10443
10444  Promise._unhandledRejectionFn = function _unhandledRejectionFn(err) {
10445    if (typeof console !== 'undefined' && console) {
10446      console.warn('Possible Unhandled Promise Rejection:', err); // eslint-disable-line no-console
10447    }
10448  };
10449
10450  /**
10451   * Set the immediate function to execute callbacks
10452   * @param fn {function} Function to execute
10453   * @deprecated
10454   */
10455  Promise._setImmediateFn = function _setImmediateFn(fn) {
10456    Promise._immediateFn = fn;
10457  };
10458
10459  /**
10460   * Change the function to execute on unhandled rejection
10461   * @param {function} fn Function to execute on unhandled rejection
10462   * @deprecated
10463   */
10464  Promise._setUnhandledRejectionFn = function _setUnhandledRejectionFn(fn) {
10465    Promise._unhandledRejectionFn = fn;
10466  };
10467  
10468  if (typeof module !== 'undefined' && module.exports) {
10469    module.exports = Promise;
10470  } else if (!root.Promise) {
10471    root.Promise = Promise;
10472  }
10473
10474})(this);
10475
10476},{}],5:[function(_dereq_,module,exports){
10477!function(root, factory) {
10478    "function" == typeof define && define.amd ? // AMD. Register as an anonymous module unless amdModuleId is set
10479    define([], function() {
10480        return root.svg4everybody = factory();
10481    }) : "object" == typeof module && module.exports ? // Node. Does not work with strict CommonJS, but
10482    // only CommonJS-like environments that support module.exports,
10483    // like Node.
10484    module.exports = factory() : root.svg4everybody = factory();
10485}(this, function() {
10486    /*! svg4everybody v2.1.9 | github.com/jonathantneal/svg4everybody */
10487    function embed(parent, svg, target) {
10488        // if the target exists
10489        if (target) {
10490            // create a document fragment to hold the contents of the target
10491            var fragment = document.createDocumentFragment(), viewBox = !svg.hasAttribute("viewBox") && target.getAttribute("viewBox");
10492            // conditionally set the viewBox on the svg
10493            viewBox && svg.setAttribute("viewBox", viewBox);
10494            // copy the contents of the clone into the fragment
10495            for (// clone the target
10496            var clone = target.cloneNode(!0); clone.childNodes.length; ) {
10497                fragment.appendChild(clone.firstChild);
10498            }
10499            // append the fragment into the svg
10500            parent.appendChild(fragment);
10501        }
10502    }
10503    function loadreadystatechange(xhr) {
10504        // listen to changes in the request
10505        xhr.onreadystatechange = function() {
10506            // if the request is ready
10507            if (4 === xhr.readyState) {
10508                // get the cached html document
10509                var cachedDocument = xhr._cachedDocument;
10510                // ensure the cached html document based on the xhr response
10511                cachedDocument || (cachedDocument = xhr._cachedDocument = document.implementation.createHTMLDocument(""), 
10512                cachedDocument.body.innerHTML = xhr.responseText, xhr._cachedTarget = {}), // clear the xhr embeds list and embed each item
10513                xhr._embeds.splice(0).map(function(item) {
10514                    // get the cached target
10515                    var target = xhr._cachedTarget[item.id];
10516                    // ensure the cached target
10517                    target || (target = xhr._cachedTarget[item.id] = cachedDocument.getElementById(item.id)), 
10518                    // embed the target into the svg
10519                    embed(item.parent, item.svg, target);
10520                });
10521            }
10522        }, // test the ready state change immediately
10523        xhr.onreadystatechange();
10524    }
10525    function svg4everybody(rawopts) {
10526        function oninterval() {
10527            // while the index exists in the live <use> collection
10528            for (// get the cached <use> index
10529            var index = 0; index < uses.length; ) {
10530                // get the current <use>
10531                var use = uses[index], parent = use.parentNode, svg = getSVGAncestor(parent), src = use.getAttribute("xlink:href") || use.getAttribute("href");
10532                if (!src && opts.attributeName && (src = use.getAttribute(opts.attributeName)), 
10533                svg && src) {
10534                    if (polyfill) {
10535                        if (!opts.validate || opts.validate(src, svg, use)) {
10536                            // remove the <use> element
10537                            parent.removeChild(use);
10538                            // parse the src and get the url and id
10539                            var srcSplit = src.split("#"), url = srcSplit.shift(), id = srcSplit.join("#");
10540                            // if the link is external
10541                            if (url.length) {
10542                                // get the cached xhr request
10543                                var xhr = requests[url];
10544                                // ensure the xhr request exists
10545                                xhr || (xhr = requests[url] = new XMLHttpRequest(), xhr.open("GET", url), xhr.send(), 
10546                                xhr._embeds = []), // add the svg and id as an item to the xhr embeds list
10547                                xhr._embeds.push({
10548                                    parent: parent,
10549                                    svg: svg,
10550                                    id: id
10551                                }), // prepare the xhr ready state change event
10552                                loadreadystatechange(xhr);
10553                            } else {
10554                                // embed the local id into the svg
10555                                embed(parent, svg, document.getElementById(id));
10556                            }
10557                        } else {
10558                            // increase the index when the previous value was not "valid"
10559                            ++index, ++numberOfSvgUseElementsToBypass;
10560                        }
10561                    }
10562                } else {
10563                    // increase the index when the previous value was not "valid"
10564                    ++index;
10565                }
10566            }
10567            // continue the interval
10568            (!uses.length || uses.length - numberOfSvgUseElementsToBypass > 0) && requestAnimationFrame(oninterval, 67);
10569        }
10570        var polyfill, opts = Object(rawopts), newerIEUA = /\bTrident\/[567]\b|\bMSIE (?:9|10)\.0\b/, webkitUA = /\bAppleWebKit\/(\d+)\b/, olderEdgeUA = /\bEdge\/12\.(\d+)\b/, edgeUA = /\bEdge\/.(\d+)\b/, inIframe = window.top !== window.self;
10571        polyfill = "polyfill" in opts ? opts.polyfill : newerIEUA.test(navigator.userAgent) || (navigator.userAgent.match(olderEdgeUA) || [])[1] < 10547 || (navigator.userAgent.match(webkitUA) || [])[1] < 537 || edgeUA.test(navigator.userAgent) && inIframe;
10572        // create xhr requests object
10573        var requests = {}, requestAnimationFrame = window.requestAnimationFrame || setTimeout, uses = document.getElementsByTagName("use"), numberOfSvgUseElementsToBypass = 0;
10574        // conditionally start the interval if the polyfill is active
10575        polyfill && oninterval();
10576    }
10577    function getSVGAncestor(node) {
10578        for (var svg = node; "svg" !== svg.nodeName.toLowerCase() && (svg = svg.parentNode); ) {}
10579        return svg;
10580    }
10581    return svg4everybody;
10582});
10583},{}],6:[function(_dereq_,module,exports){
10584'use strict';
10585
10586Object.defineProperty(exports, "__esModule", {
10587	value: true
10588});
10589
10590var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; };
10591
10592var _mejs = _dereq_(8);
10593
10594var _mejs2 = _interopRequireDefault(_mejs);
10595
10596var _en = _dereq_(16);
10597
10598var _general = _dereq_(26);
10599
10600function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
10601
10602var i18n = { lang: 'en', en: _en.EN };
10603
10604i18n.language = function () {
10605	for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) {
10606		args[_key] = arguments[_key];
10607	}
10608
10609	if (args !== null && args !== undefined && args.length) {
10610
10611		if (typeof args[0] !== 'string') {
10612			throw new TypeError('Language code must be a string value');
10613		}
10614
10615		if (!/^[a-z]{2,3}((\-|_)[a-z]{2})?$/i.test(args[0])) {
10616			throw new TypeError('Language code must have format 2-3 letters and. optionally, hyphen, underscore followed by 2 more letters');
10617		}
10618
10619		i18n.lang = args[0];
10620
10621		if (i18n[args[0]] === undefined) {
10622			args[1] = args[1] !== null && args[1] !== undefined && _typeof(args[1]) === 'object' ? args[1] : {};
10623			i18n[args[0]] = !(0, _general.isObjectEmpty)(args[1]) ? args[1] : _en.EN;
10624		} else if (args[1] !== null && args[1] !== undefined && _typeof(args[1]) === 'object') {
10625			i18n[args[0]] = args[1];
10626		}
10627	}
10628
10629	return i18n.lang;
10630};
10631
10632i18n.t = function (message) {
10633	var pluralParam = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : null;
10634
10635
10636	if (typeof message === 'string' && message.length) {
10637
10638		var str = void 0,
10639		    pluralForm = void 0;
10640
10641		var language = i18n.language();
10642
10643		var _plural = function _plural(input, number, form) {
10644
10645			if ((typeof input === 'undefined' ? 'undefined' : _typeof(input)) !== 'object' || typeof number !== 'number' || typeof form !== 'number') {
10646				return input;
10647			}
10648
10649			var _pluralForms = function () {
10650				return [function () {
10651					return arguments.length <= 1 ? undefined : arguments[1];
10652				}, function () {
10653					return (arguments.length <= 0 ? undefined : arguments[0]) === 1 ? arguments.length <= 1 ? undefined : arguments[1] : arguments.length <= 2 ? undefined : arguments[2];
10654				}, function () {
10655					return (arguments.length <= 0 ? undefined : arguments[0]) === 0 || (arguments.length <= 0 ? undefined : arguments[0]) === 1 ? arguments.length <= 1 ? undefined : arguments[1] : arguments.length <= 2 ? undefined : arguments[2];
10656				}, function () {
10657					if ((arguments.length <= 0 ? undefined : arguments[0]) % 10 === 1 && (arguments.length <= 0 ? undefined : arguments[0]) % 100 !== 11) {
10658						return arguments.length <= 1 ? undefined : arguments[1];
10659					} else if ((arguments.length <= 0 ? undefined : arguments[0]) !== 0) {
10660						return arguments.length <= 2 ? undefined : arguments[2];
10661					} else {
10662						return arguments.length <= 3 ? undefined : arguments[3];
10663					}
10664				}, function () {
10665					if ((arguments.length <= 0 ? undefined : arguments[0]) === 1 || (arguments.length <= 0 ? undefined : arguments[0]) === 11) {
10666						return arguments.length <= 1 ? undefined : arguments[1];
10667					} else if ((arguments.length <= 0 ? undefined : arguments[0]) === 2 || (arguments.length <= 0 ? undefined : arguments[0]) === 12) {
10668						return arguments.length <= 2 ? undefined : arguments[2];
10669					} else if ((arguments.length <= 0 ? undefined : arguments[0]) > 2 && (arguments.length <= 0 ? undefined : arguments[0]) < 20) {
10670						return arguments.length <= 3 ? undefined : arguments[3];
10671					} else {
10672						return arguments.length <= 4 ? undefined : arguments[4];
10673					}
10674				}, function () {
10675					if ((arguments.length <= 0 ? undefined : arguments[0]) === 1) {
10676						return arguments.length <= 1 ? undefined : arguments[1];
10677					} else if ((arguments.length <= 0 ? undefined : arguments[0]) === 0 || (arguments.length <= 0 ? undefined : arguments[0]) % 100 > 0 && (arguments.length <= 0 ? undefined : arguments[0]) % 100 < 20) {
10678						return arguments.length <= 2 ? undefined : arguments[2];
10679					} else {
10680						return arguments.length <= 3 ? undefined : arguments[3];
10681					}
10682				}, function () {
10683					if ((arguments.length <= 0 ? undefined : arguments[0]) % 10 === 1 && (arguments.length <= 0 ? undefined : arguments[0]) % 100 !== 11) {
10684						return arguments.length <= 1 ? undefined : arguments[1];
10685					} else if ((arguments.length <= 0 ? undefined : arguments[0]) % 10 >= 2 && ((arguments.length <= 0 ? undefined : arguments[0]) % 100 < 10 || (arguments.length <= 0 ? undefined : arguments[0]) % 100 >= 20)) {
10686						return arguments.length <= 2 ? undefined : arguments[2];
10687					} else {
10688						return [3];
10689					}
10690				}, function () {
10691					if ((arguments.length <= 0 ? undefined : arguments[0]) % 10 === 1 && (arguments.length <= 0 ? undefined : arguments[0]) % 100 !== 11) {
10692						return arguments.length <= 1 ? undefined : arguments[1];
10693					} else if ((arguments.length <= 0 ? undefined : arguments[0]) % 10 >= 2 && (arguments.length <= 0 ? undefined : arguments[0]) % 10 <= 4 && ((arguments.length <= 0 ? undefined : arguments[0]) % 100 < 10 || (arguments.length <= 0 ? undefined : arguments[0]) % 100 >= 20)) {
10694						return arguments.length <= 2 ? undefined : arguments[2];
10695					} else {
10696						return arguments.length <= 3 ? undefined : arguments[3];
10697					}
10698				}, function () {
10699					if ((arguments.length <= 0 ? undefined : arguments[0]) === 1) {
10700						return arguments.length <= 1 ? undefined : arguments[1];
10701					} else if ((arguments.length <= 0 ? undefined : arguments[0]) >= 2 && (arguments.length <= 0 ? undefined : arguments[0]) <= 4) {
10702						return arguments.length <= 2 ? undefined : arguments[2];
10703					} else {
10704						return arguments.length <= 3 ? undefined : arguments[3];
10705					}
10706				}, function () {
10707					if ((arguments.length <= 0 ? undefined : arguments[0]) === 1) {
10708						return arguments.length <= 1 ? undefined : arguments[1];
10709					} else if ((arguments.length <= 0 ? undefined : arguments[0]) % 10 >= 2 && (arguments.length <= 0 ? undefined : arguments[0]) % 10 <= 4 && ((arguments.length <= 0 ? undefined : arguments[0]) % 100 < 10 || (arguments.length <= 0 ? undefined : arguments[0]) % 100 >= 20)) {
10710						return arguments.length <= 2 ? undefined : arguments[2];
10711					} else {
10712						return arguments.length <= 3 ? undefined : arguments[3];
10713					}
10714				}, function () {
10715					if ((arguments.length <= 0 ? undefined : arguments[0]) % 100 === 1) {
10716						return arguments.length <= 2 ? undefined : arguments[2];
10717					} else if ((arguments.length <= 0 ? undefined : arguments[0]) % 100 === 2) {
10718						return arguments.length <= 3 ? undefined : arguments[3];
10719					} else if ((arguments.length <= 0 ? undefined : arguments[0]) % 100 === 3 || (arguments.length <= 0 ? undefined : arguments[0]) % 100 === 4) {
10720						return arguments.length <= 4 ? undefined : arguments[4];
10721					} else {
10722						return arguments.length <= 1 ? undefined : arguments[1];
10723					}
10724				}, function () {
10725					if ((arguments.length <= 0 ? undefined : arguments[0]) === 1) {
10726						return arguments.length <= 1 ? undefined : arguments[1];
10727					} else if ((arguments.length <= 0 ? undefined : arguments[0]) === 2) {
10728						return arguments.length <= 2 ? undefined : arguments[2];
10729					} else if ((arguments.length <= 0 ? undefined : arguments[0]) > 2 && (arguments.length <= 0 ? undefined : arguments[0]) < 7) {
10730						return arguments.length <= 3 ? undefined : arguments[3];
10731					} else if ((arguments.length <= 0 ? undefined : arguments[0]) > 6 && (arguments.length <= 0 ? undefined : arguments[0]) < 11) {
10732						return arguments.length <= 4 ? undefined : arguments[4];
10733					} else {
10734						return arguments.length <= 5 ? undefined : arguments[5];
10735					}
10736				}, function () {
10737					if ((arguments.length <= 0 ? undefined : arguments[0]) === 0) {
10738						return arguments.length <= 1 ? undefined : arguments[1];
10739					} else if ((arguments.length <= 0 ? undefined : arguments[0]) === 1) {
10740						return arguments.length <= 2 ? undefined : arguments[2];
10741					} else if ((arguments.length <= 0 ? undefined : arguments[0]) === 2) {
10742						return arguments.length <= 3 ? undefined : arguments[3];
10743					} else if ((arguments.length <= 0 ? undefined : arguments[0]) % 100 >= 3 && (arguments.length <= 0 ? undefined : arguments[0]) % 100 <= 10) {
10744						return arguments.length <= 4 ? undefined : arguments[4];
10745					} else if ((arguments.length <= 0 ? undefined : arguments[0]) % 100 >= 11) {
10746						return arguments.length <= 5 ? undefined : arguments[5];
10747					} else {
10748						return arguments.length <= 6 ? undefined : arguments[6];
10749					}
10750				}, function () {
10751					if ((arguments.length <= 0 ? undefined : arguments[0]) === 1) {
10752						return arguments.length <= 1 ? undefined : arguments[1];
10753					} else if ((arguments.length <= 0 ? undefined : arguments[0]) === 0 || (arguments.length <= 0 ? undefined : arguments[0]) % 100 > 1 && (arguments.length <= 0 ? undefined : arguments[0]) % 100 < 11) {
10754						return arguments.length <= 2 ? undefined : arguments[2];
10755					} else if ((arguments.length <= 0 ? undefined : arguments[0]) % 100 > 10 && (arguments.length <= 0 ? undefined : arguments[0]) % 100 < 20) {
10756						return arguments.length <= 3 ? undefined : arguments[3];
10757					} else {
10758						return arguments.length <= 4 ? undefined : arguments[4];
10759					}
10760				}, function () {
10761					if ((arguments.length <= 0 ? undefined : arguments[0]) % 10 === 1) {
10762						return arguments.length <= 1 ? undefined : arguments[1];
10763					} else if ((arguments.length <= 0 ? undefined : arguments[0]) % 10 === 2) {
10764						return arguments.length <= 2 ? undefined : arguments[2];
10765					} else {
10766						return arguments.length <= 3 ? undefined : arguments[3];
10767					}
10768				}, function () {
10769					return (arguments.length <= 0 ? undefined : arguments[0]) !== 11 && (arguments.length <= 0 ? undefined : arguments[0]) % 10 === 1 ? arguments.length <= 1 ? undefined : arguments[1] : arguments.length <= 2 ? undefined : arguments[2];
10770				}, function () {
10771					if ((arguments.length <= 0 ? undefined : arguments[0]) === 1) {
10772						return arguments.length <= 1 ? undefined : arguments[1];
10773					} else if ((arguments.length <= 0 ? undefined : arguments[0]) % 10 >= 2 && (arguments.length <= 0 ? undefined : arguments[0]) % 10 <= 4 && ((arguments.length <= 0 ? undefined : arguments[0]) % 100 < 10 || (arguments.length <= 0 ? undefined : arguments[0]) % 100 >= 20)) {
10774						return arguments.length <= 2 ? undefined : arguments[2];
10775					} else {
10776						return arguments.length <= 3 ? undefined : arguments[3];
10777					}
10778				}, function () {
10779					if ((arguments.length <= 0 ? undefined : arguments[0]) === 1) {
10780						return arguments.length <= 1 ? undefined : arguments[1];
10781					} else if ((arguments.length <= 0 ? undefined : arguments[0]) === 2) {
10782						return arguments.length <= 2 ? undefined : arguments[2];
10783					} else if ((arguments.length <= 0 ? undefined : arguments[0]) !== 8 && (arguments.length <= 0 ? undefined : arguments[0]) !== 11) {
10784						return arguments.length <= 3 ? undefined : arguments[3];
10785					} else {
10786						return arguments.length <= 4 ? undefined : arguments[4];
10787					}
10788				}, function () {
10789					return (arguments.length <= 0 ? undefined : arguments[0]) === 0 ? arguments.length <= 1 ? undefined : arguments[1] : arguments.length <= 2 ? undefined : arguments[2];
10790				}, function () {
10791					if ((arguments.length <= 0 ? undefined : arguments[0]) === 1) {
10792						return arguments.length <= 1 ? undefined : arguments[1];
10793					} else if ((arguments.length <= 0 ? undefined : arguments[0]) === 2) {
10794						return arguments.length <= 2 ? undefined : arguments[2];
10795					} else if ((arguments.length <= 0 ? undefined : arguments[0]) === 3) {
10796						return arguments.length <= 3 ? undefined : arguments[3];
10797					} else {
10798						return arguments.length <= 4 ? undefined : arguments[4];
10799					}
10800				}, function () {
10801					if ((arguments.length <= 0 ? undefined : arguments[0]) === 0) {
10802						return arguments.length <= 1 ? undefined : arguments[1];
10803					} else if ((arguments.length <= 0 ? undefined : arguments[0]) === 1) {
10804						return arguments.length <= 2 ? undefined : arguments[2];
10805					} else {
10806						return arguments.length <= 3 ? undefined : arguments[3];
10807					}
10808				}];
10809			}();
10810
10811			return _pluralForms[form].apply(null, [number].concat(input));
10812		};
10813
10814		if (i18n[language] !== undefined) {
10815			str = i18n[language][message];
10816			if (pluralParam !== null && typeof pluralParam === 'number') {
10817				pluralForm = i18n[language]['mejs.plural-form'];
10818				str = _plural.apply(null, [str, pluralParam, pluralForm]);
10819			}
10820		}
10821
10822		if (!str && i18n.en) {
10823			str = i18n.en[message];
10824			if (pluralParam !== null && typeof pluralParam === 'number') {
10825				pluralForm = i18n.en['mejs.plural-form'];
10826				str = _plural.apply(null, [str, pluralParam, pluralForm]);
10827			}
10828		}
10829
10830		str = str || message;
10831
10832		if (pluralParam !== null && typeof pluralParam === 'number') {
10833			str = str.replace('%1', pluralParam);
10834		}
10835
10836		return (0, _general.escapeHTML)(str);
10837	}
10838
10839	return message;
10840};
10841
10842_mejs2.default.i18n = i18n;
10843
10844if (typeof mejsL10n !== 'undefined') {
10845	_mejs2.default.i18n.language(mejsL10n.language, mejsL10n.strings);
10846}
10847
10848exports.default = i18n;
10849
10850},{"16":16,"26":26,"8":8}],7:[function(_dereq_,module,exports){
10851'use strict';
10852
10853Object.defineProperty(exports, "__esModule", {
10854	value: true
10855});
10856
10857var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; };
10858
10859var _window = _dereq_(3);
10860
10861var _window2 = _interopRequireDefault(_window);
10862
10863var _document = _dereq_(2);
10864
10865var _document2 = _interopRequireDefault(_document);
10866
10867var _mejs = _dereq_(8);
10868
10869var _mejs2 = _interopRequireDefault(_mejs);
10870
10871var _general = _dereq_(26);
10872
10873var _media2 = _dereq_(28);
10874
10875var _renderer = _dereq_(9);
10876
10877var _constants = _dereq_(24
vendor: 13,265 bytes, lines 10877-11294
10877);
10878
10879function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
10880
10881function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
10882
10883var MediaElement = function MediaElement(idOrNode, options, sources) {
10884	var _this = this;
10885
10886	_classCallCheck(this, MediaElement);
10887
10888	var t = this;
10889
10890	sources = Array.isArray(sources) ? sources : null;
10891
10892	t.defaults = {
10893		renderers: [],
10894
10895		fakeNodeName: 'div',
10896
10897		iconSprite: 'mejs-controls.svg'
10898	};
10899
10900	options = Object.assign(t.defaults, options);
10901
10902	t.mediaElement = _document2.default.createElement(options.fakeNodeName);
10903
10904	var id = idOrNode,
10905	    error = false;
10906
10907	if (typeof idOrNode === 'string') {
10908		t.mediaElement.originalNode = _document2.default.getElementById(idOrNode);
10909	} else {
10910		t.mediaElement.originalNode = idOrNode;
10911		id = idOrNode.id;
10912	}
10913
10914	if (t.mediaElement.originalNode === undefined || t.mediaElement.originalNode === null) {
10915		return null;
10916	}
10917
10918	t.mediaElement.options = options;
10919	id = id || 'mejs_' + Math.random().toString().slice(2);
10920
10921	t.mediaElement.originalNode.setAttribute('id', id + '_from_mejs');
10922
10923	var tagName = t.mediaElement.originalNode.tagName.toLowerCase();
10924	if (['video', 'audio'].indexOf(tagName) > -1 && !t.mediaElement.originalNode.getAttribute('preload')) {
10925		t.mediaElement.originalNode.setAttribute('preload', 'none');
10926	}
10927
10928	t.mediaElement.originalNode.setAttribute('tabindex', -1);
10929
10930	t.mediaElement.originalNode.parentNode.insertBefore(t.mediaElement, t.mediaElement.originalNode);
10931
10932	t.mediaElement.appendChild(t.mediaElement.originalNode);
10933
10934	var processURL = function processURL(url, type) {
10935		if (_window2.default.location.protocol === 'https:' && url.indexOf('http:') === 0 && _constants.IS_IOS && _mejs2.default.html5media.mediaTypes.indexOf(type) > -1) {
10936			var xhr = new XMLHttpRequest();
10937			xhr.onreadystatechange = function () {
10938				if (this.readyState === 4 && this.status === 200) {
10939					var _url = _window2.default.URL || _window2.default.webkitURL,
10940					    blobUrl = _url.createObjectURL(this.response);
10941					t.mediaElement.originalNode.setAttribute('src', blobUrl);
10942					return blobUrl;
10943				}
10944				return url;
10945			};
10946			xhr.open('GET', url);
10947			xhr.responseType = 'blob';
10948			xhr.send();
10949		}
10950
10951		return url;
10952	};
10953
10954	var mediaFiles = void 0;
10955	if (sources !== null) {
10956		mediaFiles = sources;
10957	} else if (t.mediaElement.originalNode !== null) {
10958
10959		mediaFiles = [];
10960		switch (t.mediaElement.originalNode.nodeName.toLowerCase()) {
10961			case 'iframe':
10962				mediaFiles.push({
10963					type: '',
10964					src: t.mediaElement.originalNode.getAttribute('src')
10965				});
10966				break;
10967			case 'audio':
10968			case 'video':
10969				var _sources = t.mediaElement.originalNode.children.length,
10970				    nodeSource = t.mediaElement.originalNode.getAttribute('src');
10971
10972				if (nodeSource) {
10973					var node = t.mediaElement.originalNode,
10974					    type = (0, _media2.formatType)(nodeSource, node.getAttribute('type'));
10975					mediaFiles.push({
10976						type: type,
10977						src: processURL(nodeSource, type)
10978					});
10979				}
10980
10981				for (var i = 0; i < _sources; i++) {
10982					var n = t.mediaElement.originalNode.children[i];
10983					if (n.tagName.toLowerCase() === 'source') {
10984						var src = n.getAttribute('src'),
10985						    _type = (0, _media2.formatType)(src, n.getAttribute('type'));
10986						mediaFiles.push({ type: _type, src: processURL(src, _type) });
10987					}
10988				}
10989				break;
10990		}
10991	}
10992
10993	t.mediaElement.id = id;
10994	t.mediaElement.renderers = {};
10995	t.mediaElement.events = {};
10996	t.mediaElement.promises = [];
10997	t.mediaElement.renderer = null;
10998	t.mediaElement.rendererName = null;
10999
11000	t.mediaElement.changeRenderer = function (rendererName, mediaFiles) {
11001		var t = _this,
11002		    media = Object.keys(mediaFiles[0]).length > 2 ? mediaFiles[0] : mediaFiles[0].src;
11003
11004		if (t.mediaElement.renderer !== undefined && t.mediaElement.renderer !== null && t.mediaElement.renderer.name === rendererName) {
11005			t.mediaElement.renderer.pause();
11006			t.mediaElement.renderer.show();
11007			t.mediaElement.renderer.setSrc(media);
11008			return true;
11009		}
11010
11011		if (t.mediaElement.renderer !== undefined && t.mediaElement.renderer !== null) {
11012			t.mediaElement.renderer.pause();
11013			t.mediaElement.renderer.hide();
11014		}
11015
11016		var newRenderer = t.mediaElement.renderers[rendererName],
11017		    newRendererType = null;
11018
11019		if (newRenderer !== undefined && newRenderer !== null) {
11020			newRenderer.show();
11021			newRenderer.setSrc(media);
11022			t.mediaElement.renderer = newRenderer;
11023			t.mediaElement.rendererName = rendererName;
11024			return true;
11025		}
11026
11027		var rendererArray = t.mediaElement.options.renderers.length ? t.mediaElement.options.renderers : _renderer.renderer.order;
11028
11029		for (var _i = 0, total = rendererArray.length; _i < total; _i++) {
11030			var index = rendererArray[_i];
11031
11032			if (index === rendererName) {
11033				var rendererList = _renderer.renderer.renderers;
11034				newRendererType = rendererList[index];
11035
11036				var renderOptions = Object.assign(newRendererType.options, t.mediaElement.options);
11037				newRenderer = newRendererType.create(t.mediaElement, renderOptions, mediaFiles);
11038				newRenderer.name = rendererName;
11039
11040				t.mediaElement.renderers[newRendererType.name] = newRenderer;
11041				t.mediaElement.renderer = newRenderer;
11042				t.mediaElement.rendererName = rendererName;
11043				newRenderer.show();
11044				return true;
11045			}
11046		}
11047
11048		return false;
11049	};
11050
11051	t.mediaElement.setSize = function (width, height) {
11052		if (t.mediaElement.renderer !== undefined && t.mediaElement.renderer !== null) {
11053			t.mediaElement.renderer.setSize(width, height);
11054		}
11055	};
11056
11057	t.mediaElement.generateError = function (message, urlList) {
11058		message = message || '';
11059		urlList = Array.isArray(urlList) ? urlList : [];
11060		var event = (0, _general.createEvent)('error', t.mediaElement);
11061		event.message = message;
11062		event.urls = urlList;
11063		t.mediaElement.dispatchEvent(event);
11064		error = true;
11065	};
11066
11067	var props = _mejs2.default.html5media.properties,
11068	    methods = _mejs2.default.html5media.methods,
11069	    addProperty = function addProperty(obj, name, onGet, onSet) {
11070		var oldValue = obj[name];
11071		var getFn = function getFn() {
11072			return onGet.apply(obj, [oldValue]);
11073		},
11074		    setFn = function setFn(newValue) {
11075			oldValue = onSet.apply(obj, [newValue]);
11076			return oldValue;
11077		};
11078
11079		Object.defineProperty(obj, name, {
11080			get: getFn,
11081			set: setFn
11082		});
11083	},
11084	    assignGettersSetters = function assignGettersSetters(propName) {
11085		if (propName !== 'src') {
11086			var capName = '' + propName.substring(0, 1).toUpperCase() + propName.substring(1),
11087			    getFn = function getFn() {
11088				return t.mediaElement.renderer !== undefined && t.mediaElement.renderer !== null && typeof t.mediaElement.renderer['get' + capName] === 'function' ? t.mediaElement.renderer['get' + capName]() : null;
11089			},
11090			    setFn = function setFn(value) {
11091				if (t.mediaElement.renderer !== undefined && t.mediaElement.renderer !== null && typeof t.mediaElement.renderer['set' + capName] === 'function') {
11092					t.mediaElement.renderer['set' + capName](value);
11093				}
11094			};
11095
11096			addProperty(t.mediaElement, propName, getFn, setFn);
11097			t.mediaElement['get' + capName] = getFn;
11098			t.mediaElement['set' + capName] = setFn;
11099		}
11100	},
11101	    getSrc = function getSrc() {
11102		return t.mediaElement.renderer !== undefined && t.mediaElement.renderer !== null ? t.mediaElement.renderer.getSrc() : null;
11103	},
11104	    setSrc = function setSrc(value) {
11105		var mediaFiles = [];
11106		if (typeof value === 'string') {
11107			mediaFiles.push({
11108				src: value,
11109				type: value ? (0, _media2.getTypeFromFile)(value) : ''
11110			});
11111		} else if ((typeof value === 'undefined' ? 'undefined' : _typeof(value)) === 'object' && value.src !== undefined) {
11112			var _src = (0, _media2.absolutizeUrl)(value.src),
11113			    _type2 = value.type,
11114			    media = Object.assign(value, {
11115				src: _src,
11116				type: (_type2 === '' || _type2 === null || _type2 === undefined) && _src ? (0, _media2.getTypeFromFile)(_src) : _type2
11117			});
11118			mediaFiles.push(media);
11119		} else if (Array.isArray(value)) {
11120			for (var _i2 = 0, total = value.length; _i2 < total; _i2++) {
11121
11122				var _src2 = (0, _media2.absolutizeUrl)(value[_i2].src),
11123				    _type3 = value[_i2].type,
11124				    _media = Object.assign(value[_i2], {
11125					src: _src2,
11126					type: (_type3 === '' || _type3 === null || _type3 === undefined) && _src2 ? (0, _media2.getTypeFromFile)(_src2) : _type3
11127				});
11128				mediaFiles.push(_media);
11129			}
11130		}
11131
11132		var renderInfo = _renderer.renderer.select(mediaFiles, t.mediaElement.options.renderers.length ? t.mediaElement.options.renderers : []),
11133		    event = void 0;
11134
11135		if (!t.mediaElement.paused && !(t.mediaElement.src == null || t.mediaElement.src === '')) {
11136			t.mediaElement.pause();
11137			event = (0, _general.createEvent)('pause', t.mediaElement);
11138			t.mediaElement.dispatchEvent(event);
11139		}
11140		t.mediaElement.originalNode.src = mediaFiles[0].src || '';
11141
11142		if (renderInfo === null && mediaFiles[0].src) {
11143			t.mediaElement.generateError('No renderer found', mediaFiles);
11144			return;
11145		}
11146
11147		var shouldChangeRenderer = !(mediaFiles[0].src == null || mediaFiles[0].src === '');
11148		return shouldChangeRenderer ? t.mediaElement.changeRenderer(renderInfo.rendererName, mediaFiles) : null;
11149	},
11150	    triggerAction = function triggerAction(methodName, args) {
11151		try {
11152			if (methodName === 'play' && (t.mediaElement.rendererName === 'native_dash' || t.mediaElement.rendererName === 'native_hls' || t.mediaElement.rendererName === 'vimeo_iframe')) {
11153				var response = t.mediaElement.renderer[methodName](args);
11154				if (response && typeof response.then === 'function') {
11155					response.catch(function () {
11156						if (t.mediaElement.paused) {
11157							setTimeout(function () {
11158								var tmpResponse = t.mediaElement.renderer.play();
11159								if (tmpResponse !== undefined) {
11160									tmpResponse.catch(function () {
11161										if (!t.mediaElement.renderer.paused) {
11162											t.mediaElement.renderer.pause();
11163										}
11164									});
11165								}
11166							}, 150);
11167						}
11168					});
11169				}
11170				return response;
11171			} else {
11172				return t.mediaElement.renderer[methodName](args);
11173			}
11174		} catch (e) {
11175			t.mediaElement.generateError(e, mediaFiles);
11176			throw e;
11177		}
11178	},
11179	    assignMethods = function assignMethods(methodName) {
11180		t.mediaElement[methodName] = function () {
11181			for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) {
11182				args[_key] = arguments[_key];
11183			}
11184
11185			if (t.mediaElement.renderer !== undefined && t.mediaElement.renderer !== null && typeof t.mediaElement.renderer[methodName] === 'function') {
11186				if (t.mediaElement.promises.length) {
11187					return Promise.all(t.mediaElement.promises).then(function () {
11188						return triggerAction(methodName, args);
11189					}).catch(function (e) {
11190						t.mediaElement.generateError(e, mediaFiles);
11191						return Promise.reject(e);
11192					});
11193				} else {
11194					return triggerAction(methodName, args);
11195				}
11196			}
11197			return null;
11198		};
11199	};
11200
11201	addProperty(t.mediaElement, 'src', getSrc, setSrc);
11202
11203	t.mediaElement.getSrc = getSrc;
11204	t.mediaElement.setSrc = setSrc;
11205	for (var _i3 = 0, total = props.length; _i3 < total; _i3++) {
11206		assignGettersSetters(props[_i3]);
11207	}
11208
11209	for (var _i4 = 0, _total = methods.length; _i4 < _total; _i4++) {
11210		assignMethods(methods[_i4]);
11211	}
11212
11213	t.mediaElement.addEventListener = function (eventName, callback) {
11214		t.mediaElement.events[eventName] = t.mediaElement.events[eventName] || [];
11215
11216		t.mediaElement.events[eventName].push(callback);
11217	};
11218	t.mediaElement.removeEventListener = function (eventName, callback) {
11219		if (!eventName) {
11220			t.mediaElement.events = {};
11221			return true;
11222		}
11223
11224		var callbacks = t.mediaElement.events[eventName];
11225
11226		if (!callbacks) {
11227			return true;
11228		}
11229
11230		if (!callback) {
11231			t.mediaElement.events[eventName] = [];
11232			return true;
11233		}
11234
11235		for (var _i5 = 0; _i5 < callbacks.length; _i5++) {
11236			if (callbacks[_i5] === callback) {
11237				t.mediaElement.events[eventName].splice(_i5, 1);
11238				return true;
11239			}
11240		}
11241		return false;
11242	};
11243
11244	t.mediaElement.dispatchEvent = function (event) {
11245		var callbacks = t.mediaElement.events[event.type];
11246		if (callbacks) {
11247			for (var _i6 = 0; _i6 < callbacks.length; _i6++) {
11248				callbacks[_i6].apply(null, [event]);
11249			}
11250		}
11251	};
11252
11253	t.mediaElement.destroy = function () {
11254		var mediaElement = t.mediaElement.originalNode.cloneNode(true);
11255		var wrapper = t.mediaElement.parentElement;
11256		mediaElement.removeAttribute('id');
11257		mediaElement.remove();
11258		t.mediaElement.remove();
11259		wrapper.appendChild(mediaElement);
11260	};
11261
11262	if (mediaFiles.length) {
11263		t.mediaElement.src = mediaFiles;
11264	}
11265
11266	if (t.mediaElement.promises.length) {
11267		Promise.all(t.mediaElement.promises).then(function () {
11268			if (t.mediaElement.options.success) {
11269				t.mediaElement.options.success(t.mediaElement, t.mediaElement.originalNode);
11270			}
11271		}).catch(function () {
11272			if (error && t.mediaElement.options.error) {
11273				t.mediaElement.options.error(t.mediaElement, t.mediaElement.originalNode);
11274			}
11275		});
11276	} else {
11277		if (t.mediaElement.options.success) {
11278			t.mediaElement.options.success(t.mediaElement, t.mediaElement.originalNode);
11279		}
11280
11281		if (error && t.mediaElement.options.error) {
11282			t.mediaElement.options.error(t.mediaElement, t.mediaElement.originalNode);
11283		}
11284	}
11285
11286	return t.mediaElement;
11287};
11288
11289_window2.default.MediaElement = MediaElement;
11290_mejs2.default.MediaElement = MediaElement;
11291
11292exports.default = MediaElement;
11293
11294},{"
vendor: 15,028 bytes, lines 11294-11748
112942":2,"24":24,"26":26,"28":28,"3":3,"8":8,"9":9}],8:[function(_dereq_,module,exports){
11295'use strict';
11296
11297Object.defineProperty(exports, "__esModule", {
11298	value: true
11299});
11300
11301var _window = _dereq_(3);
11302
11303var _window2 = _interopRequireDefault(_window);
11304
11305function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
11306
11307var mejs = {};
11308
11309mejs.version = '7.0.7';
11310
11311mejs.html5media = {
11312	properties: ['volume', 'src', 'currentTime', 'muted', 'duration', 'paused', 'ended', 'buffered', 'error', 'networkState', 'readyState', 'seeking', 'seekable', 'currentSrc', 'preload', 'bufferedBytes', 'bufferedTime', 'initialTime', 'startOffsetTime', 'defaultPlaybackRate', 'playbackRate', 'played', 'autoplay', 'loop', 'controls'],
11313	readOnlyProperties: ['duration', 'paused', 'ended', 'buffered', 'error', 'networkState', 'readyState', 'seeking', 'seekable'],
11314
11315	methods: ['load', 'play', 'pause', 'canPlayType'],
11316
11317	events: ['loadstart', 'durationchange', 'loadedmetadata', 'loadeddata', 'progress', 'canplay', 'canplaythrough', 'suspend', 'abort', 'error', 'emptied', 'stalled', 'play', 'playing', 'pause', 'waiting', 'seeking', 'seeked', 'timeupdate', 'ended', 'ratechange', 'volumechange'],
11318
11319	mediaTypes: ['audio/mp3', 'audio/ogg', 'audio/oga', 'audio/wav', 'audio/x-wav', 'audio/wave', 'audio/x-pn-wav', 'audio/mpeg', 'audio/mp4', 'video/mp4', 'video/webm', 'video/ogg', 'video/ogv']
11320};
11321
11322_window2.default.mejs = mejs;
11323
11324exports.default = mejs;
11325
11326},{"3":3}],9:[function(_dereq_,module,exports){
11327'use strict';
11328
11329Object.defineProperty(exports, "__esModule", {
11330	value: true
11331});
11332exports.renderer = undefined;
11333
11334var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; };
11335
11336var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
11337
11338var _mejs = _dereq_(8);
11339
11340var _mejs2 = _interopRequireDefault(_mejs);
11341
11342function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
11343
11344function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
11345
11346var Renderer = function () {
11347	function Renderer() {
11348		_classCallCheck(this, Renderer);
11349
11350		this.renderers = {};
11351		this.order = [];
11352	}
11353
11354	_createClass(Renderer, [{
11355		key: 'add',
11356		value: function add(renderer) {
11357			if (renderer.name === undefined) {
11358				throw new TypeError('renderer must contain at least `name` property');
11359			}
11360
11361			this.renderers[renderer.name] = renderer;
11362			this.order.push(renderer.name);
11363		}
11364	}, {
11365		key: 'select',
11366		value: function select(mediaFiles) {
11367			var renderers = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : [];
11368
11369			var renderersLength = renderers.length;
11370
11371			renderers = renderers.length ? renderers : this.order;
11372
11373			if (!renderersLength) {
11374				var rendererIndicator = [/^(html5|native)/i, /iframe$/i],
11375				    rendererRanking = function rendererRanking(renderer) {
11376					for (var i = 0, total = rendererIndicator.length; i < total; i++) {
11377						if (rendererIndicator[i].test(renderer)) {
11378							return i;
11379						}
11380					}
11381					return rendererIndicator.length;
11382				};
11383
11384				renderers.sort(function (a, b) {
11385					return rendererRanking(a) - rendererRanking(b);
11386				});
11387			}
11388
11389			for (var i = 0, total = renderers.length; i < total; i++) {
11390				var key = renderers[i],
11391				    _renderer = this.renderers[key];
11392
11393				if (_renderer !== null && _renderer !== undefined) {
11394					for (var j = 0, jl = mediaFiles.length; j < jl; j++) {
11395
11396						if (typeof _renderer.canPlayType === 'function' && typeof mediaFiles[j].type === 'string' && _renderer.canPlayType(mediaFiles[j].type)) {
11397							return {
11398								rendererName: _renderer.name,
11399								src: mediaFiles[j].src
11400							};
11401						}
11402					}
11403				}
11404			}
11405
11406			return null;
11407		}
11408	}, {
11409		key: 'order',
11410		set: function set(order) {
11411			if (!Array.isArray(order)) {
11412				throw new TypeError('order must be an array of strings.');
11413			}
11414
11415			this._order = order;
11416		},
11417		get: function get() {
11418			return this._order;
11419		}
11420	}, {
11421		key: 'renderers',
11422		set: function set(renderers) {
11423			if (renderers !== null && (typeof renderers === 'undefined' ? 'undefined' : _typeof(renderers)) !== 'object') {
11424				throw new TypeError('renderers must be an array of objects.');
11425			}
11426
11427			this._renderers = renderers;
11428		},
11429		get: function get() {
11430			return this._renderers;
11431		}
11432	}]);
11433
11434	return Renderer;
11435}();
11436
11437var renderer = exports.renderer = new Renderer();
11438
11439_mejs2.default.Renderers = renderer;
11440
11441},{"8":8}],10:[function(_dereq_,module,exports){
11442'use strict';
11443
11444var _window = _dereq_(3);
11445
11446var _window2 = _interopRequireDefault(_window);
11447
11448var _document = _dereq_(2);
11449
11450var _document2 = _interopRequireDefault(_document);
11451
11452var _i18n = _dereq_(6);
11453
11454var _i18n2 = _interopRequireDefault(_i18n);
11455
11456var _player = _dereq_(17);
11457
11458var _player2 = _interopRequireDefault(_player);
11459
11460var _constants = _dereq_(24);
11461
11462var Features = _interopRequireWildcard(_constants);
11463
11464var _general = _dereq_(26);
11465
11466var _dom = _dereq_(25);
11467
11468var _media = _dereq_(28);
11469
11470var _generate = _dereq_(27);
11471
11472function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } }
11473
11474function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
11475
11476Object.assign(_player.config, {
11477	usePluginFullScreen: true,
11478
11479	fullscreenText: null,
11480
11481	useFakeFullscreen: false
11482});
11483
11484Object.assign(_player2.default.prototype, {
11485	isFullScreen: false,
11486
11487	isNativeFullScreen: false,
11488
11489	isInIframe: false,
11490
11491	isPluginClickThroughCreated: false,
11492
11493	fullscreenMode: '',
11494
11495	containerSizeTimeout: null,
11496
11497	buildfullscreen: function buildfullscreen(player) {
11498		if (!player.isVideo) {
11499			return;
11500		}
11501
11502		player.isInIframe = _window2.default.location !== _window2.default.parent.location;
11503
11504		player.detectFullscreenMode();
11505
11506		var t = this,
11507		    fullscreenTitle = (0, _general.isString)(t.options.fullscreenText) ? t.options.fullscreenText : _i18n2.default.t('mejs.fullscreen'),
11508		    fullscreenBtn = _document2.default.createElement('div');
11509		fullscreenBtn.className = t.options.classPrefix + 'button ' + t.options.classPrefix + 'fullscreen-button';
11510		fullscreenBtn.innerHTML = (0, _generate.generateControlButton)(t.id, fullscreenTitle, fullscreenTitle, '' + t.media.options.iconSprite, ['icon-fullscreen', 'icon-unfullscreen'], '' + t.options.classPrefix);
11511		t.addControlElement(fullscreenBtn, 'fullscreen');
11512
11513		fullscreenBtn.addEventListener('click', function () {
11514			var isFullScreen = Features.HAS_TRUE_NATIVE_FULLSCREEN && Features.IS_FULLSCREEN || player.isFullScreen;
11515
11516			if (isFullScreen) {
11517				player.exitFullScreen();
11518			} else {
11519				player.enterFullScreen();
11520			}
11521		});
11522
11523		player.fullscreenBtn = fullscreenBtn;
11524
11525		t.options.keyActions.push({
11526			keys: [70],
11527			action: function action(player, media, key, event) {
11528				if (!event.ctrlKey) {
11529					if (typeof player.enterFullScreen !== 'undefined') {
11530						if (player.isFullScreen) {
11531							player.exitFullScreen();
11532						} else {
11533							player.enterFullScreen();
11534						}
11535					}
11536				}
11537			}
11538		});
11539
11540		t.exitFullscreenCallback = function (e) {
11541			var key = e.which || e.keyCode || 0;
11542			if (t.options.enableKeyboard && key === 27 && (Features.HAS_TRUE_NATIVE_FULLSCREEN && Features.IS_FULLSCREEN || t.isFullScreen)) {
11543				player.exitFullScreen();
11544			}
11545		};
11546
11547		t.globalBind('keydown', t.exitFullscreenCallback);
11548
11549		t.normalHeight = 0;
11550		t.normalWidth = 0;
11551
11552		if (Features.HAS_TRUE_NATIVE_FULLSCREEN) {
11553			var fullscreenChanged = function fullscreenChanged() {
11554				if (player.isFullScreen) {
11555					if (Features.isFullScreen()) {
11556						player.isNativeFullScreen = true;
11557
11558						player.setControlsSize();
11559					} else {
11560						player.isNativeFullScreen = false;
11561
11562						player.exitFullScreen();
11563					}
11564				}
11565			};
11566
11567			player.globalBind(Features.FULLSCREEN_EVENT_NAME, fullscreenChanged);
11568		}
11569	},
11570	cleanfullscreen: function cleanfullscreen(player) {
11571		player.exitFullScreen();
11572		player.globalUnbind('keydown', player.exitFullscreenCallback);
11573	},
11574	detectFullscreenMode: function detectFullscreenMode() {
11575		var t = this,
11576		    isNative = t.media.rendererName !== null && /(native|html5)/i.test(t.media.rendererName);
11577
11578		var mode = '';
11579
11580		if (Features.HAS_TRUE_NATIVE_FULLSCREEN && isNative) {
11581			mode = 'native-native';
11582		} else if (Features.HAS_TRUE_NATIVE_FULLSCREEN && !isNative) {
11583			mode = 'plugin-native';
11584		} else if (t.usePluginFullScreen && Features.SUPPORT_POINTER_EVENTS) {
11585			mode = 'plugin-click';
11586		}
11587
11588		t.fullscreenMode = mode;
11589		return mode;
11590	},
11591	enterFullScreen: function enterFullScreen() {
11592		var t = this,
11593		    isNative = t.media.rendererName !== null && /(html5|native)/i.test(t.media.rendererName),
11594		    containerStyles = getComputedStyle(t.getElement(t.container));
11595
11596		if (!t.isVideo) {
11597			return;
11598		}
11599
11600		if (t.options.useFakeFullscreen === false && (Features.IS_IOS || Features.IS_SAFARI) && Features.HAS_IOS_FULLSCREEN && typeof t.media.originalNode.webkitEnterFullscreen === 'function' && t.media.originalNode.canPlayType((0, _media.getTypeFromFile)(t.media.getSrc()))) {
11601			t.media.originalNode.webkitEnterFullscreen();
11602			return;
11603		}
11604
11605		(0, _dom.addClass)(_document2.default.documentElement, t.options.classPrefix + 'fullscreen');
11606		(0, _dom.addClass)(t.getElement(t.container), t.options.classPrefix + 'container-fullscreen');
11607
11608		t.normalHeight = parseFloat(containerStyles.height);
11609		t.normalWidth = parseFloat(containerStyles.width);
11610
11611		if (t.fullscreenMode === 'native-native' || t.fullscreenMode === 'plugin-native') {
11612			Features.requestFullScreen(t.getElement(t.container));
11613
11614			if (t.isInIframe) {
11615				setTimeout(function checkFullscreen() {
11616
11617					if (t.isNativeFullScreen) {
11618						var percentErrorMargin = 0.002,
11619						    windowWidth = _window2.default.innerWidth || _document2.default.documentElement.clientWidth || _document2.default.body.clientWidth,
11620						    screenWidth = screen.width,
11621						    absDiff = Math.abs(screenWidth - windowWidth),
11622						    marginError = screenWidth * percentErrorMargin;
11623
11624						if (absDiff > marginError) {
11625							t.exitFullScreen();
11626						} else {
11627							setTimeout(checkFullscreen, 500);
11628						}
11629					}
11630				}, 1000);
11631			}
11632		}
11633
11634		t.getElement(t.container).style.width = '100%';
11635		t.getElement(t.container).style.height = '100%';
11636
11637		t.containerSizeTimeout = setTimeout(function () {
11638			t.getElement(t.container).style.width = '100%';
11639			t.getElement(t.container).style.height = '100%';
11640			t.setControlsSize();
11641		}, 500);
11642
11643		if (isNative) {
11644			t.node.style.width = '100%';
11645			t.node.style.height = '100%';
11646		} else {
11647			var elements = t.getElement(t.container).querySelectorAll('embed, object, video'),
11648			    _total = elements.length;
11649			for (var i = 0; i < _total; i++) {
11650				elements[i].style.width = '100%';
11651				elements[i].style.height = '100%';
11652			}
11653		}
11654
11655		if (t.options.setDimensions && typeof t.media.setSize === 'function') {
11656			t.media.setSize(screen.width, screen.height);
11657		}
11658
11659		var layers = t.getElement(t.layers).children,
11660		    total = layers.length;
11661		for (var _i = 0; _i < total; _i++) {
11662			layers[_i].style.width = '100%';
11663			layers[_i].style.height = '100%';
11664		}
11665
11666		if (t.fullscreenBtn) {
11667			(0, _dom.removeClass)(t.fullscreenBtn, t.options.classPrefix + 'fullscreen');
11668			(0, _dom.addClass)(t.fullscreenBtn, t.options.classPrefix + 'unfullscreen');
11669		}
11670
11671		t.setControlsSize();
11672		t.isFullScreen = true;
11673
11674		var zoomFactor = Math.min(screen.width / t.width, screen.height / t.height),
11675		    captionText = t.getElement(t.container).querySelector('.' + t.options.classPrefix + 'captions-text');
11676		if (captionText) {
11677			captionText.style.fontSize = zoomFactor * 100 + '%';
11678			captionText.style.lineHeight = 'normal';
11679			t.getElement(t.container).querySelector('.' + t.options.classPrefix + 'captions-position').style.bottom = (screen.height - t.normalHeight) / 2 - t.getElement(t.controls).offsetHeight / 2 + zoomFactor + 15 + 'px';
11680		}
11681		var event = (0, _general.createEvent)('enteredfullscreen', t.getElement(t.container));
11682		t.getElement(t.container).dispatchEvent(event);
11683	},
11684	exitFullScreen: function exitFullScreen() {
11685		var t = this,
11686		    isNative = t.media.rendererName !== null && /(native|html5)/i.test(t.media.rendererName);
11687
11688		if (!t.isVideo) {
11689			return;
11690		}
11691
11692		clearTimeout(t.containerSizeTimeout);
11693
11694		if (Features.HAS_TRUE_NATIVE_FULLSCREEN && (Features.IS_FULLSCREEN || t.isFullScreen)) {
11695			Features.cancelFullScreen();
11696		}
11697
11698		(0, _dom.removeClass)(_document2.default.documentElement, t.options.classPrefix + 'fullscreen');
11699		(0, _dom.removeClass)(t.getElement(t.container), t.options.classPrefix + 'container-fullscreen');
11700
11701		if (t.options.setDimensions) {
11702			t.getElement(t.container).style.width = t.normalWidth + 'px';
11703			t.getElement(t.container).style.height = t.normalHeight + 'px';
11704
11705			if (isNative) {
11706				t.node.style.width = t.normalWidth + 'px';
11707				t.node.style.height = t.normalHeight + 'px';
11708			} else {
11709				var elements = t.getElement(t.container).querySelectorAll('embed, object, video'),
11710				    _total2 = elements.length;
11711				for (var i = 0; i < _total2; i++) {
11712					elements[i].style.width = t.normalWidth + 'px';
11713					elements[i].style.height = t.normalHeight + 'px';
11714				}
11715			}
11716
11717			if (typeof t.media.setSize === 'function') {
11718				t.media.setSize(t.normalWidth, t.normalHeight);
11719			}
11720
11721			var layers = t.getElement(t.layers).children,
11722			    total = layers.length;
11723			for (var _i2 = 0; _i2 < total; _i2++) {
11724				layers[_i2].style.width = t.normalWidth + 'px';
11725				layers[_i2].style.height = t.normalHeight + 'px';
11726			}
11727		}
11728
11729		if (t.fullscreenBtn) {
11730			(0, _dom.removeClass)(t.fullscreenBtn, t.options.classPrefix + 'unfullscreen');
11731			(0, _dom.addClass)(t.fullscreenBtn, t.options.classPrefix + 'fullscreen');
11732		}
11733
11734		t.setControlsSize();
11735		t.isFullScreen = false;
11736
11737		var captionText = t.getElement(t.container).querySelector('.' + t.options.classPrefix + 'captions-text');
11738		if (captionText) {
11739			captionText.style.fontSize = '';
11740			captionText.style.lineHeight = '';
11741			t.getElement(t.container).querySelector('.' + t.options.classPrefix + 'captions-position').style.bottom = '';
11742		}
11743		var event = (0, _general.createEvent)('exitedfullscreen', t.getElement(t.container));
11744		t.getElement(t.container).dispatchEvent(event);
11745	}
11746});
11747
11748},{"17":17,"2":2,"24":24,"25":25,"26":26,"27":27,"28":28,"3":3
vendor: 91,660 bytes, lines 11748-14468
11748,"6":6}],11:[function(_dereq_,module,exports){
11749'use strict';
11750
11751var _document = _dereq_(2);
11752
11753var _document2 = _interopRequireDefault(_document);
11754
11755var _player = _dereq_(17);
11756
11757var _player2 = _interopRequireDefault(_player);
11758
11759var _i18n = _dereq_(6);
11760
11761var _i18n2 = _interopRequireDefault(_i18n);
11762
11763var _general = _dereq_(26);
11764
11765var _dom = _dereq_(25);
11766
11767var _generate = _dereq_(27);
11768
11769function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
11770
11771Object.assign(_player.config, {
11772	playText: null,
11773
11774	pauseText: null
11775});
11776
11777Object.assign(_player2.default.prototype, {
11778	buildplaypause: function buildplaypause(player, controls, layers, media) {
11779		var t = this,
11780		    op = t.options,
11781		    playTitle = (0, _general.isString)(op.playText) ? op.playText : _i18n2.default.t('mejs.play'),
11782		    pauseTitle = (0, _general.isString)(op.pauseText) ? op.pauseText : _i18n2.default.t('mejs.pause'),
11783		    play = _document2.default.createElement('div');
11784
11785		play.className = t.options.classPrefix + 'button ' + t.options.classPrefix + 'playpause-button ' + t.options.classPrefix + 'play';
11786		play.innerHTML = (0, _generate.generateControlButton)(t.id, pauseTitle, playTitle, '' + t.media.options.iconSprite, ['icon-play', 'icon-pause', 'icon-replay'], '' + t.options.classPrefix);
11787		play.addEventListener('click', function () {
11788			if (t.paused) {
11789				t.play();
11790			} else {
11791				t.pause();
11792			}
11793		});
11794
11795		var playBtn = play.querySelector('button');
11796		t.addControlElement(play, 'playpause');
11797
11798		function togglePlayPause(which) {
11799			(0, _dom.removeClass)(play, t.options.classPrefix + 'play');
11800			(0, _dom.removeClass)(play, t.options.classPrefix + 'replay');
11801			(0, _dom.removeClass)(play, t.options.classPrefix + 'pause');
11802
11803			if ('play' === which) {
11804				(0, _dom.addClass)(play, t.options.classPrefix + 'pause');
11805				playBtn.setAttribute('title', pauseTitle);
11806				playBtn.setAttribute('aria-label', pauseTitle);
11807			} else if ('pse' === which) {
11808				(0, _dom.addClass)(play, t.options.classPrefix + 'play');
11809				playBtn.setAttribute('title', playTitle);
11810				playBtn.setAttribute('aria-label', playTitle);
11811			} else {
11812				(0, _dom.addClass)(play, t.options.classPrefix + 'replay');
11813				playBtn.setAttribute('title', playTitle);
11814				playBtn.setAttribute('aria-label', playTitle);
11815			}
11816		}
11817
11818		togglePlayPause('pse');
11819
11820		media.addEventListener('loadedmetadata', function () {
11821			togglePlayPause('pse');
11822		});
11823		media.addEventListener('play', function () {
11824			togglePlayPause('play');
11825		});
11826		media.addEventListener('playing', function () {
11827			togglePlayPause('play');
11828		});
11829		media.addEventListener('pause', function () {
11830			togglePlayPause('pse');
11831		});
11832		media.addEventListener('ended', function () {
11833			if (!player.options.loop) {
11834				setTimeout(function () {
11835					togglePlayPause('replay');
11836				}, 0);
11837			}
11838		});
11839	}
11840});
11841
11842},{"17":17,"2":2,"25":25,"26":26,"27":27,"6":6}],12:[function(_dereq_,module,exports){
11843'use strict';
11844
11845var _document = _dereq_(2);
11846
11847var _document2 = _interopRequireDefault(_document);
11848
11849var _player = _dereq_(17);
11850
11851var _player2 = _interopRequireDefault(_player);
11852
11853var _i18n = _dereq_(6);
11854
11855var _i18n2 = _interopRequireDefault(_i18n);
11856
11857var _constants = _dereq_(24);
11858
11859var _time = _dereq_(30);
11860
11861var _dom = _dereq_(25);
11862
11863function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
11864
11865Object.assign(_player.config, {
11866	enableProgressTooltip: true,
11867
11868	useSmoothHover: true,
11869
11870	forceLive: false
11871});
11872
11873Object.assign(_player2.default.prototype, {
11874	buildprogress: function buildprogress(player, controls, layers, media) {
11875
11876		var lastKeyPressTime = 0,
11877		    mouseIsDown = false,
11878		    startedPaused = false;
11879
11880		var t = this,
11881		    autoRewindInitial = player.options.autoRewind,
11882		    tooltip = player.options.enableProgressTooltip ? '<span class="' + t.options.classPrefix + 'time-float">' + ('<span class="' + t.options.classPrefix + 'time-float-current">00:00</span>') + ('<span class="' + t.options.classPrefix + 'time-float-corner"></span>') + '</span>' : '',
11883		    rail = _document2.default.createElement('div');
11884
11885		rail.className = t.options.classPrefix + 'time-rail';
11886		rail.innerHTML = '<span class="' + t.options.classPrefix + 'time-total ' + t.options.classPrefix + 'time-slider">' + ('<span class="' + t.options.classPrefix + 'time-buffering"></span>') + ('<span class="' + t.options.classPrefix + 'time-loaded"></span>') + ('<span class="' + t.options.classPrefix + 'time-current"></span>') + ('<span class="' + t.options.classPrefix + 'time-hovered no-hover"></span>') + ('<span class="' + t.options.classPrefix + 'time-handle"><span class="' + t.options.classPrefix + 'time-handle-content"></span></span>') + ('' + tooltip) + '</span>';
11887
11888		t.addControlElement(rail, 'progress');
11889
11890		t.options.keyActions.push({
11891			keys: [37, 227],
11892			action: function action(player) {
11893				if (!isNaN(player.duration) && player.duration > 0) {
11894					if (player.isVideo) {
11895						player.showControls();
11896						player.startControlsTimer();
11897					}
11898
11899					var timeSlider = player.getElement(player.container).querySelector('.' + t.options.classPrefix + 'time-total');
11900					if (timeSlider) {
11901						timeSlider.focus();
11902					}
11903
11904					var newTime = Math.max(player.currentTime - player.options.defaultSeekBackwardInterval(player), 0);
11905
11906					if (!player.paused) {
11907						player.pause();
11908					}
11909
11910					setTimeout(function () {
11911						player.setCurrentTime(newTime, true);
11912					}, 0);
11913
11914					setTimeout(function () {
11915						player.play();
11916					}, 0);
11917				}
11918			}
11919		}, {
11920			keys: [39, 228],
11921			action: function action(player) {
11922
11923				if (!isNaN(player.duration) && player.duration > 0) {
11924					if (player.isVideo) {
11925						player.showControls();
11926						player.startControlsTimer();
11927					}
11928
11929					var timeSlider = player.getElement(player.container).querySelector('.' + t.options.classPrefix + 'time-total');
11930					if (timeSlider) {
11931						timeSlider.focus();
11932					}
11933
11934					var newTime = Math.min(player.currentTime + player.options.defaultSeekForwardInterval(player), player.duration);
11935
11936					if (!player.paused) {
11937						player.pause();
11938					}
11939
11940					setTimeout(function () {
11941						player.setCurrentTime(newTime, true);
11942					}, 0);
11943
11944					setTimeout(function () {
11945						player.play();
11946					}, 0);
11947				}
11948			}
11949		});
11950
11951		t.rail = controls.querySelector('.' + t.options.classPrefix + 'time-rail');
11952		t.total = controls.querySelector('.' + t.options.classPrefix + 'time-total');
11953		t.loaded = controls.querySelector('.' + t.options.classPrefix + 'time-loaded');
11954		t.current = controls.querySelector('.' + t.options.classPrefix + 'time-current');
11955		t.handle = controls.querySelector('.' + t.options.classPrefix + 'time-handle');
11956		t.timefloat = controls.querySelector('.' + t.options.classPrefix + 'time-float');
11957		t.timefloatcurrent = controls.querySelector('.' + t.options.classPrefix + 'time-float-current');
11958		t.slider = controls.querySelector('.' + t.options.classPrefix + 'time-slider');
11959		t.hovered = controls.querySelector('.' + t.options.classPrefix + 'time-hovered');
11960		t.buffer = controls.querySelector('.' + t.options.classPrefix + 'time-buffering');
11961		t.newTime = 0;
11962		t.forcedHandlePause = false;
11963		t.setTransformStyle = function (element, value) {
11964			element.style.transform = value;
11965			element.style.webkitTransform = value;
11966			element.style.MozTransform = value;
11967			element.style.msTransform = value;
11968			element.style.OTransform = value;
11969		};
11970
11971		t.buffer.style.display = 'none';
11972
11973		var handleMouseMove = function handleMouseMove(e) {
11974			var totalStyles = getComputedStyle(t.total),
11975			    offsetStyles = (0, _dom.offset)(t.total),
11976			    width = t.total.offsetWidth,
11977			    transform = function () {
11978				if (totalStyles.webkitTransform !== undefined) {
11979					return 'webkitTransform';
11980				} else if (totalStyles.mozTransform !== undefined) {
11981					return 'mozTransform ';
11982				} else if (totalStyles.oTransform !== undefined) {
11983					return 'oTransform';
11984				} else if (totalStyles.msTransform !== undefined) {
11985					return 'msTransform';
11986				} else {
11987					return 'transform';
11988				}
11989			}(),
11990			    cssMatrix = function () {
11991				if ('WebKitCSSMatrix' in window) {
11992					return 'WebKitCSSMatrix';
11993				} else if ('MSCSSMatrix' in window) {
11994					return 'MSCSSMatrix';
11995				} else if ('CSSMatrix' in window) {
11996					return 'CSSMatrix';
11997				}
11998			}();
11999
12000			var percentage = 0,
12001			    leftPos = 0,
12002			    pos = 0,
12003			    x = void 0;
12004
12005			if (e.originalEvent && e.originalEvent.changedTouches) {
12006				x = e.originalEvent.changedTouches[0].pageX;
12007			} else if (e.changedTouches) {
12008				x = e.changedTouches[0].pageX;
12009			} else {
12010				x = e.pageX;
12011			}
12012
12013			if (t.getDuration()) {
12014				if (x < offsetStyles.left) {
12015					x = offsetStyles.left;
12016				} else if (x > width + offsetStyles.left) {
12017					x = width + offsetStyles.left;
12018				}
12019
12020				pos = x - offsetStyles.left;
12021				percentage = pos / width;
12022				t.newTime = percentage * t.getDuration();
12023
12024				if (mouseIsDown && t.getCurrentTime() !== null && t.newTime.toFixed(4) !== t.getCurrentTime().toFixed(4)) {
12025					t.setCurrentRailHandle(t.newTime);
12026					t.updateCurrent(t.newTime);
12027				}
12028
12029				if (!_constants.IS_IOS && !_constants.IS_ANDROID) {
12030					if (pos < 0) {
12031						pos = 0;
12032					}
12033					if (t.options.useSmoothHover && cssMatrix !== null && typeof window[cssMatrix] !== 'undefined') {
12034						var matrix = new window[cssMatrix](getComputedStyle(t.handle)[transform]),
12035						    handleLocation = matrix.m41,
12036						    hoverScaleX = pos / parseFloat(getComputedStyle(t.total).width) - handleLocation / parseFloat(getComputedStyle(t.total).width);
12037
12038						t.hovered.style.left = handleLocation + 'px';
12039						t.setTransformStyle(t.hovered, 'scaleX(' + hoverScaleX + ')');
12040						t.hovered.setAttribute('pos', pos);
12041
12042						if (hoverScaleX >= 0) {
12043							(0, _dom.removeClass)(t.hovered, 'negative');
12044						} else {
12045							(0, _dom.addClass)(t.hovered, 'negative');
12046						}
12047					}
12048
12049					if (t.timefloat) {
12050						var half = t.timefloat.offsetWidth / 2,
12051						    offsetContainer = mejs.Utils.offset(t.getElement(t.container)),
12052						    tooltipStyles = getComputedStyle(t.timefloat);
12053
12054						if (x - offsetContainer.left < t.timefloat.offsetWidth) {
12055							leftPos = half;
12056						} else if (x - offsetContainer.left >= t.getElement(t.container).offsetWidth - half) {
12057							leftPos = t.total.offsetWidth - half;
12058						} else {
12059							leftPos = pos;
12060						}
12061
12062						if ((0, _dom.hasClass)(t.getElement(t.container), t.options.classPrefix + 'long-video')) {
12063							leftPos += parseFloat(tooltipStyles.marginLeft) / 2 + t.timefloat.offsetWidth / 2;
12064						}
12065
12066						t.timefloat.style.left = leftPos + 'px';
12067						t.timefloatcurrent.innerHTML = (0, _time.secondsToTimeCode)(t.newTime, player.options.alwaysShowHours, player.options.showTimecodeFrameCount, player.options.framesPerSecond, player.options.secondsDecimalLength, player.options.timeFormat);
12068						t.timefloat.style.display = 'block';
12069					}
12070				}
12071			} else if (!_constants.IS_IOS && !_constants.IS_ANDROID && t.timefloat) {
12072				leftPos = t.timefloat.offsetWidth + width >= t.getElement(t.container).offsetWidth ? t.timefloat.offsetWidth / 2 : 0;
12073				t.timefloat.style.left = leftPos + 'px';
12074				t.timefloat.style.left = leftPos + 'px';
12075				t.timefloat.style.display = 'block';
12076			}
12077		},
12078		    updateSlider = function updateSlider() {
12079			var seconds = t.getCurrentTime(),
12080			    timeSliderText = _i18n2.default.t('mejs.time-slider'),
12081			    time = (0, _time.secondsToTimeCode)(seconds, player.options.alwaysShowHours, player.options.showTimecodeFrameCount, player.options.framesPerSecond, player.options.secondsDecimalLength, player.options.timeFormat),
12082			    duration = t.getDuration();
12083
12084			t.slider.setAttribute('role', 'slider');
12085			t.slider.tabIndex = 0;
12086
12087			if (media.paused) {
12088				t.slider.setAttribute('aria-label', timeSliderText);
12089				t.slider.setAttribute('aria-valuemin', 0);
12090				t.slider.setAttribute('aria-valuemax', isNaN(duration) ? 0 : duration);
12091				t.slider.setAttribute('aria-valuenow', seconds);
12092				t.slider.setAttribute('aria-valuetext', time);
12093			} else {
12094				t.slider.removeAttribute('aria-label');
12095				t.slider.removeAttribute('aria-valuemin');
12096				t.slider.removeAttribute('aria-valuemax');
12097				t.slider.removeAttribute('aria-valuenow');
12098				t.slider.removeAttribute('aria-valuetext');
12099			}
12100		},
12101		    restartPlayer = function restartPlayer() {
12102			if (new Date() - lastKeyPressTime >= 1000) {
12103				t.play();
12104			}
12105		},
12106		    handleMouseup = function handleMouseup() {
12107			if (mouseIsDown && t.getCurrentTime() !== null && t.newTime.toFixed(4) !== t.getCurrentTime().toFixed(4)) {
12108				t.setCurrentTime(t.newTime, true);
12109				t.setCurrentRailHandle(t.newTime);
12110				t.updateCurrent(t.newTime);
12111			}
12112			if (t.forcedHandlePause) {
12113				t.slider.focus();
12114				t.play();
12115			}
12116			t.forcedHandlePause = false;
12117		};
12118
12119		t.slider.addEventListener('focus', function () {
12120			player.options.autoRewind = false;
12121		});
12122		t.slider.addEventListener('blur', function () {
12123			player.options.autoRewind = autoRewindInitial;
12124		});
12125		t.slider.addEventListener('keydown', function (e) {
12126			if (new Date() - lastKeyPressTime >= 1000) {
12127				startedPaused = t.paused;
12128			}
12129
12130			if (t.options.enableKeyboard && t.options.keyActions.length) {
12131
12132				var keyCode = e.which || e.keyCode || 0,
12133				    duration = t.getDuration(),
12134				    seekForward = player.options.defaultSeekForwardInterval(media),
12135				    seekBackward = player.options.defaultSeekBackwardInterval(media);
12136
12137				var seekTime = t.getCurrentTime();
12138				var volume = t.getElement(t.container).querySelector('.' + t.options.classPrefix + 'volume-slider');
12139
12140				if (keyCode === 38 || keyCode === 40) {
12141					if (volume) {
12142						volume.style.display = 'block';
12143					}
12144					if (t.isVideo) {
12145						t.showControls();
12146						t.startControlsTimer();
12147					}
12148
12149					var newVolume = keyCode === 38 ? Math.min(t.volume + 0.1, 1) : Math.max(t.volume - 0.1, 0),
12150					    mutePlayer = newVolume <= 0;
12151					t.setVolume(newVolume);
12152					t.setMuted(mutePlayer);
12153					return;
12154				} else {
12155					if (volume) {
12156						volume.style.display = 'none';
12157					}
12158				}
12159
12160				switch (keyCode) {
12161					case 37:
12162						if (t.getDuration() !== Infinity) {
12163							seekTime -= seekBackward;
12164						}
12165						break;
12166					case 39:
12167						if (t.getDuration() !== Infinity) {
12168							seekTime += seekForward;
12169						}
12170						break;
12171					case 36:
12172						seekTime = 0;
12173						break;
12174					case 35:
12175						seekTime = duration;
12176						break;
12177					case 13:
12178						if (t.paused) {
12179							t.play();
12180						} else {
12181							t.pause();
12182						}
12183						return;
12184					default:
12185						return;
12186				}
12187
12188				seekTime = seekTime < 0 || isNaN(seekTime) ? 0 : seekTime >= duration ? duration : Math.floor(seekTime);
12189				lastKeyPressTime = new Date();
12190				if (!startedPaused) {
12191					player.pause();
12192				}
12193
12194				setTimeout(function () {
12195					t.setCurrentTime(seekTime, true);
12196				}, 0);
12197
12198				if (seekTime < t.getDuration() && !startedPaused) {
12199					setTimeout(restartPlayer, 1100);
12200				}
12201
12202				player.showControls();
12203
12204				e.preventDefault();
12205				e.stopPropagation();
12206			}
12207		});
12208
12209		var events = ['mousedown', 'touchstart'];
12210
12211		t.slider.addEventListener('dragstart', function () {
12212			return false;
12213		});
12214
12215		for (var i = 0, total = events.length; i < total; i++) {
12216			t.slider.addEventListener(events[i], function (e) {
12217				t.forcedHandlePause = false;
12218				if (t.getDuration() !== Infinity) {
12219					if (e.which === 1 || e.which === 0) {
12220						if (!t.paused) {
12221							t.pause();
12222							t.forcedHandlePause = true;
12223						}
12224
12225						mouseIsDown = true;
12226						handleMouseMove(e);
12227						var endEvents = ['mouseup', 'touchend'];
12228
12229						for (var j = 0, totalEvents = endEvents.length; j < totalEvents; j++) {
12230							t.getElement(t.container).addEventListener(endEvents[j], function (event) {
12231								var target = event.target;
12232								if (target === t.slider || target.closest('.' + t.options.classPrefix + 'time-slider')) {
12233									handleMouseMove(event);
12234								}
12235							});
12236						}
12237						t.globalBind('mouseup.dur touchend.dur', function () {
12238							handleMouseup();
12239							mouseIsDown = false;
12240							if (t.timefloat) {
12241								t.timefloat.style.display = 'none';
12242							}
12243						});
12244					}
12245				}
12246			}, _constants.SUPPORT_PASSIVE_EVENT && events[i] === 'touchstart' ? { passive: true } : false);
12247		}
12248		t.slider.addEventListener('mouseenter', function (e) {
12249			if (e.target === t.slider && t.getDuration() !== Infinity) {
12250				t.getElement(t.container).addEventListener('mousemove', function (event) {
12251					var target = event.target;
12252					if (target === t.slider || target.closest('.' + t.options.classPrefix + 'time-slider')) {
12253						handleMouseMove(event);
12254					}
12255				});
12256				if (t.timefloat && !_constants.IS_IOS && !_constants.IS_ANDROID) {
12257					t.timefloat.style.display = 'block';
12258				}
12259				if (t.hovered && !_constants.IS_IOS && !_constants.IS_ANDROID && t.options.useSmoothHover) {
12260					(0, _dom.removeClass)(t.hovered, 'no-hover');
12261				}
12262			}
12263		});
12264		t.slider.addEventListener('mouseleave', function () {
12265			if (t.getDuration() !== Infinity) {
12266				if (!mouseIsDown) {
12267					if (t.timefloat) {
12268						t.timefloat.style.display = 'none';
12269					}
12270					if (t.hovered && t.options.useSmoothHover) {
12271						(0, _dom.addClass)(t.hovered, 'no-hover');
12272					}
12273				}
12274			}
12275		});
12276
12277		t.broadcastCallback = function (e) {
12278			var broadcast = controls.querySelector('.' + t.options.classPrefix + 'broadcast');
12279			if (!t.options.forceLive && t.getDuration() !== Infinity) {
12280				if (broadcast) {
12281					t.slider.style.display = '';
12282					broadcast.remove();
12283				}
12284
12285				player.setProgressRail(e);
12286				if (!t.forcedHandlePause) {
12287					player.setCurrentRail(e);
12288				}
12289				updateSlider();
12290			} else if (!broadcast && t.options.forceLive) {
12291				var label = _document2.default.createElement('span');
12292				label.className = t.options.classPrefix + 'broadcast';
12293				label.innerText = _i18n2.default.t('mejs.live-broadcast');
12294				t.slider.style.display = 'none';
12295				t.rail.appendChild(label);
12296			}
12297		};
12298
12299		media.addEventListener('progress', t.broadcastCallback);
12300		media.addEventListener('timeupdate', t.broadcastCallback);
12301		media.addEventListener('play', function () {
12302			t.buffer.style.display = 'none';
12303		});
12304		media.addEventListener('playing', function () {
12305			t.buffer.style.display = 'none';
12306		});
12307		media.addEventListener('seeking', function () {
12308			t.buffer.style.display = '';
12309		});
12310		media.addEventListener('seeked', function () {
12311			t.buffer.style.display = 'none';
12312		});
12313		media.addEventListener('pause', function () {
12314			t.buffer.style.display = 'none';
12315		});
12316		media.addEventListener('waiting', function () {
12317			t.buffer.style.display = '';
12318		});
12319		media.addEventListener('loadeddata', function () {
12320			t.buffer.style.display = '';
12321		});
12322		media.addEventListener('canplay', function () {
12323			t.buffer.style.display = 'none';
12324		});
12325		media.addEventListener('error', function () {
12326			t.buffer.style.display = 'none';
12327		});
12328
12329		t.getElement(t.container).addEventListener('controlsresize', function (e) {
12330			if (t.getDuration() !== Infinity) {
12331				player.setProgressRail(e);
12332				if (!t.forcedHandlePause) {
12333					player.setCurrentRail(e);
12334				}
12335			}
12336		});
12337	},
12338	cleanprogress: function cleanprogress(player, controls, layers, media) {
12339		media.removeEventListener('progress', player.broadcastCallback);
12340		media.removeEventListener('timeupdate', player.broadcastCallback);
12341		if (player.rail) {
12342			player.rail.remove();
12343		}
12344	},
12345	setProgressRail: function setProgressRail(e) {
12346		var t = this,
12347		    target = e !== undefined ? e.detail.target || e.target : t.media;
12348
12349		var percent = null;
12350
12351		if (target && target.buffered && target.buffered.length > 0 && target.buffered.end && t.getDuration()) {
12352			percent = target.buffered.end(target.buffered.length - 1) / t.getDuration();
12353		} else if (target && target.bytesTotal !== undefined && target.bytesTotal > 0 && target.bufferedBytes !== undefined) {
12354				percent = target.bufferedBytes / target.bytesTotal;
12355			} else if (e && e.lengthComputable && e.total !== 0) {
12356					percent = e.loaded / e.total;
12357				}
12358
12359		if (percent !== null) {
12360			percent = Math.min(1, Math.max(0, percent));
12361
12362			if (t.loaded) {
12363				t.setTransformStyle(t.loaded, 'scaleX(' + percent + ')');
12364			}
12365		}
12366	},
12367	setCurrentRailHandle: function setCurrentRailHandle(fakeTime) {
12368		var t = this;
12369		t.setCurrentRailMain(t, fakeTime);
12370	},
12371	setCurrentRail: function setCurrentRail() {
12372		var t = this;
12373		t.setCurrentRailMain(t);
12374	},
12375	setCurrentRailMain: function setCurrentRailMain(t, fakeTime) {
12376		if (t.getCurrentTime() !== undefined && t.getDuration()) {
12377			var nTime = typeof fakeTime === 'undefined' ? t.getCurrentTime() : fakeTime;
12378
12379			if (t.total && t.handle) {
12380				var tW = parseFloat(getComputedStyle(t.total).width);
12381
12382				var newWidth = Math.round(tW * nTime / t.getDuration()),
12383				    handlePos = newWidth - Math.round(t.handle.offsetWidth / 2);
12384
12385				handlePos = handlePos < 0 ? 0 : handlePos;
12386				t.setTransformStyle(t.current, 'scaleX(' + newWidth / tW + ')');
12387				t.setTransformStyle(t.handle, 'translateX(' + handlePos + 'px)');
12388
12389				if (t.options.useSmoothHover && !(0, _dom.hasClass)(t.hovered, 'no-hover')) {
12390					var pos = parseInt(t.hovered.getAttribute('pos'), 10);
12391					pos = isNaN(pos) ? 0 : pos;
12392
12393					var hoverScaleX = pos / tW - handlePos / tW;
12394
12395					t.hovered.style.left = handlePos + 'px';
12396					t.setTransformStyle(t.hovered, 'scaleX(' + hoverScaleX + ')');
12397
12398					if (hoverScaleX >= 0) {
12399						(0, _dom.removeClass)(t.hovered, 'negative');
12400					} else {
12401						(0, _dom.addClass)(t.hovered, 'negative');
12402					}
12403				}
12404			}
12405		}
12406	}
12407});
12408
12409},{"17":17,"2":2,"24":24,"25":25,"30":30,"6":6}],13:[function(_dereq_,module,exports){
12410'use strict';
12411
12412var _document = _dereq_(2);
12413
12414var _document2 = _interopRequireDefault(_document);
12415
12416var _player = _dereq_(17);
12417
12418var _player2 = _interopRequireDefault(_player);
12419
12420var _i18n = _dereq_(6);
12421
12422var _i18n2 = _interopRequireDefault(_i18n);
12423
12424var _time = _dereq_(30);
12425
12426var _dom = _dereq_(25);
12427
12428function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
12429
12430Object.assign(_player.config, {
12431	duration: 0,
12432
12433	timeAndDurationSeparator: '<span> | </span>'
12434});
12435
12436Object.assign(_player2.default.prototype, {
12437	buildcurrent: function buildcurrent(player, controls, layers, media) {
12438		var t = this,
12439		    time = _document2.default.createElement('div');
12440
12441		time.className = t.options.classPrefix + 'time';
12442		time.setAttribute('role', 'timer');
12443		time.setAttribute('aria-live', 'off');
12444		time.innerHTML = '<span class="mejs__offscreen">' + _i18n2.default.t('mejs.current') + '</span><span class="' + t.options.classPrefix + 'currenttime">' + (0, _time.secondsToTimeCode)(0, player.options.alwaysShowHours, player.options.showTimecodeFrameCount, player.options.framesPerSecond, player.options.secondsDecimalLength, player.options.timeFormat) + '</span>';
12445
12446		t.addControlElement(time, 'current');
12447		player.updateCurrent();
12448		t.updateTimeCallback = function () {
12449			if (t.controlsAreVisible) {
12450				player.updateCurrent();
12451			}
12452		};
12453		media.addEventListener('timeupdate', t.updateTimeCallback);
12454	},
12455	cleancurrent: function cleancurrent(player, controls, layers, media) {
12456		media.removeEventListener('timeupdate', player.updateTimeCallback);
12457	},
12458	buildduration: function buildduration(player, controls, layers, media) {
12459		var t = this,
12460		    currTime = controls.lastChild.querySelector('.' + t.options.classPrefix + 'currenttime');
12461
12462		if (currTime) {
12463			controls.querySelector('.' + t.options.classPrefix + 'time').innerHTML += t.options.timeAndDurationSeparator + '<span class="mejs__offscreen">' + _i18n2.default.t('mejs.duration') + '</span><span class="' + t.options.classPrefix + 'duration">' + ((0, _time.secondsToTimeCode)(t.options.duration, t.options.alwaysShowHours, t.options.showTimecodeFrameCount, t.options.framesPerSecond, t.options.secondsDecimalLength, t.options.timeFormat) + '</span>');
12464		} else {
12465			if (controls.querySelector('.' + t.options.classPrefix + 'currenttime')) {
12466				(0, _dom.addClass)(controls.querySelector('.' + t.options.classPrefix + 'currenttime').parentNode, t.options.classPrefix + 'currenttime-container');
12467			}
12468
12469			var duration = _document2.default.createElement('div');
12470			duration.className = t.options.classPrefix + 'time ' + t.options.classPrefix + 'duration-container';
12471			duration.innerHTML = '<span class="mejs__offscreen">' + _i18n2.default.t('mejs.duration') + '</span><span class="' + t.options.classPrefix + 'duration">' + ((0, _time.secondsToTimeCode)(t.options.duration, t.options.alwaysShowHours, t.options.showTimecodeFrameCount, t.options.framesPerSecond, t.options.secondsDecimalLength, t.options.timeFormat) + '</span>');
12472
12473			t.addControlElement(duration, 'duration');
12474		}
12475
12476		t.updateDurationCallback = function () {
12477			if (t.controlsAreVisible) {
12478				player.updateDuration();
12479			}
12480		};
12481
12482		media.addEventListener('timeupdate', t.updateDurationCallback);
12483	},
12484	cleanduration: function cleanduration(player, controls, layers, media) {
12485		media.removeEventListener('timeupdate', player.updateDurationCallback);
12486	},
12487	updateCurrent: function updateCurrent() {
12488		var t = this;
12489
12490		var currentTime = t.getCurrentTime();
12491
12492		if (isNaN(currentTime)) {
12493			currentTime = 0;
12494		}
12495
12496		var timecode = (0, _time.secondsToTimeCode)(currentTime, t.options.alwaysShowHours, t.options.showTimecodeFrameCount, t.options.framesPerSecond, t.options.secondsDecimalLength, t.options.timeFormat);
12497
12498		if (timecode.length > 5) {
12499			(0, _dom.addClass)(t.getElement(t.container), t.options.classPrefix + 'long-video');
12500		} else {
12501			(0, _dom.removeClass)(t.getElement(t.container), t.options.classPrefix + 'long-video');
12502		}
12503
12504		if (t.getElement(t.controls).querySelector('.' + t.options.classPrefix + 'currenttime')) {
12505			t.getElement(t.controls).querySelector('.' + t.options.classPrefix + 'currenttime').innerText = timecode;
12506		}
12507	},
12508	updateDuration: function updateDuration() {
12509		var t = this;
12510
12511		var duration = t.getDuration();
12512
12513		if (t.media !== undefined && (isNaN(duration) || duration === Infinity || duration < 0)) {
12514			t.media.duration = t.options.duration = duration = 0;
12515		}
12516
12517		if (t.options.duration > 0) {
12518			duration = t.options.duration;
12519		}
12520
12521		var timecode = (0, _time.secondsToTimeCode)(duration, t.options.alwaysShowHours, t.options.showTimecodeFrameCount, t.options.framesPerSecond, t.options.secondsDecimalLength, t.options.timeFormat);
12522
12523		if (timecode.length > 5) {
12524			(0, _dom.addClass)(t.getElement(t.container), t.options.classPrefix + 'long-video');
12525		} else {
12526			(0, _dom.removeClass)(t.getElement(t.container), t.options.classPrefix + 'long-video');
12527		}
12528
12529		if (t.getElement(t.controls).querySelector('.' + t.options.classPrefix + 'duration') && duration > 0) {
12530			t.getElement(t.controls).querySelector('.' + t.options.classPrefix + 'duration').innerHTML = timecode;
12531		}
12532	}
12533});
12534
12535},{"17":17,"2":2,"25":25,"30":30,"6":6}],14:[function(_dereq_,module,exports){
12536'use strict';
12537
12538var _document = _dereq_(2);
12539
12540var _document2 = _interopRequireDefault(_document);
12541
12542var _mejs = _dereq_(8);
12543
12544var _mejs2 = _interopRequireDefault(_mejs);
12545
12546var _i18n = _dereq_(6);
12547
12548var _i18n2 = _interopRequireDefault(_i18n);
12549
12550var _player = _dereq_(17);
12551
12552var _player2 = _interopRequireDefault(_player);
12553
12554var _general = _dereq_(26);
12555
12556var _dom = _dereq_(25);
12557
12558var _generate = _dereq_(27);
12559
12560function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
12561
12562Object.assign(_player.config, {
12563  autoplayCaptionLanguage: null,
12564
12565  defaultTrackLine: -3,
12566
12567  tracksText: null,
12568
12569  chaptersText: null,
12570
12571  chaptersLanguage: null,
12572
12573  hideCaptionsButtonWhenEmpty: true,
12574
12575  toggleCaptionsButtonWhenOnlyOne: false
12576});
12577
12578Object.assign(_player2.default.prototype, {
12579  hasChapters: false,
12580
12581  buildtracks: function buildtracks(player, controls) {
12582    this.initTracks(player);
12583
12584    if (!player.tracks.length && (!player.trackFiles || !(player.trackFiles.length === 0))) {
12585      return;
12586    }
12587
12588    var t = this,
12589        tracksTitle = (0, _general.isString)(t.options.tracksText) ? t.options.tracksText : _i18n2.default.t('mejs.captions-subtitles'),
12590        chaptersTitle = (0, _general.isString)(t.options.chaptersText) ? t.options.chaptersText : _i18n2.default.t('mejs.captions-chapters');
12591
12592    t.hideAllTracks();
12593
12594    t.clearTrackHtml(player);
12595
12596    player.captionsButton = _document2.default.createElement('div');
12597    player.captionsButton.className = t.options.classPrefix + 'button ' + t.options.classPrefix + 'captions-button';
12598    player.captionsButton.innerHTML = (0, _generate.generateControlButton)(t.id, tracksTitle, tracksTitle, '' + t.media.options.iconSprite, ['icon-captions'], '' + t.options.classPrefix) + ('<div class="' + t.options.classPrefix + 'captions-selector ' + t.options.classPrefix + 'offscreen">') + ('<ul class="' + t.options.classPrefix + 'captions-selector-list">') + ('<li class="' + t.options.classPrefix + 'captions-selector-list-item">') + ('<input type="radio" class="' + t.options.classPrefix + 'captions-selector-input" ') + ('name="' + player.id + '_captions" id="' + player.id + '_captions_none" ') + 'value="none" checked disabled>' + ('<label class="' + t.options.classPrefix + 'captions-selector-label ') + (t.options.classPrefix + 'captions-selected" ') + ('for="' + player.id + '_captions_none">' + _i18n2.default.t('mejs.none') + '</label>') + '</li>' + '</ul>' + '</div>';
12599
12600    t.addControlElement(player.captionsButton, 'tracks');
12601
12602    player.captionsButton.querySelector('.' + t.options.classPrefix + 'captions-selector-input').disabled = false;
12603
12604    player.chaptersButton = _document2.default.createElement('div');
12605    player.chaptersButton.className = t.options.classPrefix + 'button ' + t.options.classPrefix + 'chapters-button';
12606    player.chaptersButton.innerHTML = (0, _generate.generateControlButton)(t.id, chaptersTitle, chaptersTitle, '' + t.media.options.iconSprite, ['icon-chapters'], '' + t.options.classPrefix) + ('<div class="' + t.options.classPrefix + 'chapters-selector ' + t.options.classPrefix + 'offscreen">') + ('<ul class="' + t.options.classPrefix + 'chapters-selector-list"></ul>') + '</div>';
12607
12608    var subtitles = t.getSubtitles();
12609    var chapters = t.getChapters();
12610
12611    if (chapters.length > 0 && !controls.querySelector('.' + t.options.classPrefix + 'chapter-selector')) {
12612      player.captionsButton.parentNode.insertBefore(player.chaptersButton, player.captionsButton);
12613    }
12614
12615    for (var i = 0; i < subtitles.length; i++) {
12616      player.addTrackButton(subtitles[i]);
12617      if (subtitles[i].isLoaded) {
12618        t.enableTrackButton(subtitles[i]);
12619      }
12620    }
12621
12622    player.trackToLoad = -1;
12623    player.selectedTrack = null;
12624    player.isLoadingTrack = false;
12625
12626    var inEvents = ['mouseenter', 'focusin'],
12627        outEvents = ['mouseleave', 'focusout'];
12628
12629    if (t.options.toggleCaptionsButtonWhenOnlyOne && subtitles.length === 1) {
12630      player.captionsButton.classList.add(t.options.classPrefix + 'captions-button-toggle');
12631      player.captionsButton.addEventListener('click', function () {
12632        var trackId = 'none';
12633        if (player.selectedTrack === null) {
12634          trackId = player.getSubtitles()[0].trackId;
12635        }
12636        player.setTrack(trackId);
12637      });
12638    } else {
12639      var labels = player.captionsButton.querySelectorAll('.' + t.options.classPrefix + 'captions-selector-label'),
12640          captions = player.captionsButton.querySelectorAll('input[type=radio]');
12641
12642      for (var _i = 0; _i < inEvents.length; _i++) {
12643        player.captionsButton.addEventListener(inEvents[_i], function () {
12644          (0, _dom.removeClass)(this.querySelector('.' + t.options.classPrefix + 'captions-selector'), t.options.classPrefix + 'offscreen');
12645        });
12646      }
12647
12648      for (var _i2 = 0; _i2 < outEvents.length; _i2++) {
12649        player.captionsButton.addEventListener(outEvents[_i2], function () {
12650          var _this = this;
12651
12652          setTimeout(function () {
12653            (0, _dom.addClass)(_this.querySelector('.' + t.options.classPrefix + 'captions-selector'), t.options.classPrefix + 'offscreen');
12654          }, 0);
12655        });
12656      }
12657
12658      for (var _i3 = 0; _i3 < captions.length; _i3++) {
12659        captions[_i3].addEventListener('click', function (e) {
12660          if (!e.target.disabled) {
12661            player.setTrack(this.value);
12662          }
12663        });
12664      }
12665
12666      for (var _i4 = 0; _i4 < labels.length; _i4++) {
12667        labels[_i4].addEventListener('click', function (e) {
12668          var radio = (0, _dom.siblings)(this, function (el) {
12669            return el.tagName === 'INPUT';
12670          })[0],
12671              event = (0, _general.createEvent)('click', radio);
12672          radio.dispatchEvent(event);
12673          e.preventDefault();
12674        });
12675      }
12676
12677      player.captionsButton.addEventListener('keydown', function (e) {
12678        e.stopPropagation();
12679      });
12680    }
12681
12682    for (var _i5 = 0; _i5 < inEvents.length; _i5++) {
12683      player.chaptersButton.addEventListener(inEvents[_i5], function () {
12684        if (this.querySelector('.' + t.options.classPrefix + 'chapters-selector-list').children.length) {
12685          (0, _dom.removeClass)(this.querySelector('.' + t.options.classPrefix + 'chapters-selector'), t.options.classPrefix + 'offscreen');
12686        }
12687      });
12688    }
12689
12690    for (var _i6 = 0; _i6 < outEvents.length; _i6++) {
12691      player.chaptersButton.addEventListener(outEvents[_i6], function () {
12692        var _this2 = this;
12693
12694        setTimeout(function () {
12695          (0, _dom.addClass)(_this2.querySelector('.' + t.options.classPrefix + 'chapters-selector'), t.options.classPrefix + 'offscreen');
12696        }, 0);
12697      });
12698    }
12699
12700    player.chaptersButton.addEventListener('keydown', function (e) {
12701      e.stopPropagation();
12702    });
12703
12704    t.checkAllCaptionsLoadedOrError();
12705    t.checkAllChaptersLoadedOrError();
12706  },
12707  clearTrackHtml: function clearTrackHtml(player) {
12708    if (player) {
12709      if (player.captionsButton) {
12710        player.captionsButton.remove();
12711      }
12712      if (player.chaptersButton) {
12713        player.chaptersButton.remove();
12714      }
12715    }
12716  },
12717  initTracks: function initTracks(player) {
12718    var t = this,
12719        trackFiles = t.trackFiles === null ? t.node.querySelectorAll('track') : t.trackFiles;
12720
12721    t.tracks = [];
12722
12723    if (trackFiles) {
12724      player.trackFiles = trackFiles;
12725      for (var i = 0; i < trackFiles.length; i++) {
12726        var track = trackFiles[i],
12727            srclang = track.getAttribute('srclang').toLowerCase() || '',
12728            trackId = track.getAttribute('id') || t.id + '_track_' + i + '_' + track.getAttribute('kind') + '_' + srclang;
12729        track.setAttribute('id', trackId);
12730
12731        var trackData = {
12732          trackId: trackId,
12733          srclang: srclang,
12734          src: track.getAttribute('src'),
12735          kind: track.getAttribute('kind'),
12736          label: track.getAttribute('label') || '',
12737          entries: [],
12738          isDefault: track.hasAttribute('default'),
12739          isError: false,
12740          isLoaded: false
12741        };
12742        t.tracks.push(trackData);
12743
12744        if (track.getAttribute('kind') === 'captions' || track.getAttribute('kind') === 'subtitles') {
12745          switch (track.readyState) {
12746            case 2:
12747              t.handleCaptionsLoaded(track);
12748              break;
12749            case 3:
12750              t.handleCaptionsError(track);
12751              break;
12752            default:
12753              track.addEventListener('load', function (event) {
12754                t.handleCaptionsLoaded(event.target);
12755              });
12756              track.addEventListener('error', function (event) {
12757                t.handleCaptionsError(event.target);
12758              });
12759              break;
12760          }
12761        } else if (track.getAttribute('kind') === 'chapters') {
12762          switch (track.readyState) {
12763            case 2:
12764              t.handleChaptersLoaded(track);
12765              break;
12766            case 3:
12767              t.handleChaptersError(track);
12768              break;
12769            default:
12770              track.addEventListener('load', function (event) {
12771                t.handleChaptersLoaded(event.target);
12772              });
12773              track.addEventListener('error', function (event) {
12774                t.handleChaptersError(event.target);
12775              });
12776              break;
12777          }
12778        }
12779      }
12780    }
12781  },
12782  handleCaptionsLoaded: function handleCaptionsLoaded(target) {
12783    var textTracks = this.node.textTracks,
12784        playerTrack = this.getTrackById(target.getAttribute('id'));
12785
12786    if (Number.isInteger(this.options.defaultTrackLine)) {
12787      for (var i = 0; i < textTracks.length; i++) {
12788        if (target.getAttribute('srclang') === textTracks[i].language && target.getAttribute('kind') === textTracks[i].kind) {
12789          var cues = textTracks[i].cues;
12790          for (var c = 0; c < cues.length; c++) {
12791            if (cues[c].line === 'auto' || cues[c].line === undefined || cues[c].line === null) {
12792              cues[c].line = this.options.defaultTrackLine;
12793            }
12794          }
12795          break;
12796        }
12797      }
12798    }
12799
12800    playerTrack.isLoaded = true;
12801    this.enableTrackButton(playerTrack);
12802    this.checkAllCaptionsLoadedOrError();
12803  },
12804  handleCaptionsError: function handleCaptionsError(target) {
12805    var playerTrack = this.getTrackById(target.getAttribute('id'));
12806
12807    playerTrack.isError = true;
12808    this.removeTrackButton(playerTrack);
12809    this.checkAllCaptionsLoadedOrError();
12810  },
12811  handleChaptersLoaded: function handleChaptersLoaded(target) {
12812    var playerTrack = this.getTrackById(target.getAttribute('id'));
12813
12814    this.hasChapters = true;
12815    playerTrack.isLoaded = true;
12816    this.checkAllChaptersLoadedOrError();
12817  },
12818  handleChaptersError: function handleChaptersError(target) {
12819    var playerTrack = this.getTrackById(target.getAttribute('id'));
12820    playerTrack.isError = true;
12821    this.checkAllChaptersLoadedOrError();
12822  },
12823  checkAllCaptionsLoadedOrError: function checkAllCaptionsLoadedOrError() {
12824    var subtitles = this.getSubtitles();
12825    if (subtitles.length === subtitles.filter(function (_ref) {
12826      var isLoaded = _ref.isLoaded,
12827          isError = _ref.isError;
12828      return isLoaded || isError;
12829    }).length) {
12830      this.removeCaptionsIfEmpty();
12831      this.checkForAutoPlay();
12832    }
12833  },
12834  checkAllChaptersLoadedOrError: function checkAllChaptersLoadedOrError() {
12835    var _this3 = this;
12836
12837    var chapters = this.getChapters(),
12838        readyChapters = chapters.filter(function (_ref2) {
12839      var isLoaded = _ref2.isLoaded;
12840      return isLoaded;
12841    });
12842    if (chapters.length === chapters.filter(function (_ref3) {
12843      var isLoaded = _ref3.isLoaded,
12844          isError = _ref3.isError;
12845      return isLoaded || isError;
12846    }).length) {
12847      if (readyChapters.length === 0) {
12848        this.chaptersButton.remove();
12849      } else {
12850        var langChapter = readyChapters.find(function (_ref4) {
12851          var srclang = _ref4.srclang;
12852          return srclang === _this3.options.chaptersLanguage;
12853        });
12854
12855        langChapter = langChapter || readyChapters.find(function (_ref5) {
12856          var srclang = _ref5.srclang;
12857          return srclang === _i18n2.default.lang;
12858        });
12859
12860        if (readyChapters.length === 1 || !langChapter) {
12861          this.drawChapters(readyChapters[0].trackId);
12862        } else {
12863          this.drawChapters(langChapter.trackId);
12864        }
12865      }
12866    }
12867  },
12868  setTrack: function setTrack(trackId) {
12869    var t = this,
12870        radios = t.captionsButton.querySelectorAll('input[type="radio"]'),
12871        captions = t.captionsButton.querySelectorAll('.' + t.options.classPrefix + 'captions-selected'),
12872        track = t.captionsButton.querySelector('input[value="' + trackId + '"]');
12873
12874    for (var i = 0; i < radios.length; i++) {
12875      radios[i].checked = false;
12876    }
12877
12878    for (var _i7 = 0; _i7 < captions.length; _i7++) {
12879      (0, _dom.removeClass)(captions[_i7], t.options.classPrefix + 'captions-selected');
12880    }
12881
12882    track.checked = true;
12883    var labels = (0, _dom.siblings)(track, function (el) {
12884      return (0, _dom.hasClass)(el, t.options.classPrefix + 'captions-selector-label');
12885    });
12886    for (var _i8 = 0; _i8 < labels.length; _i8++) {
12887      (0, _dom.addClass)(labels[_i8], t.options.classPrefix + 'captions-selected');
12888    }
12889
12890    if (trackId === 'none') {
12891      t.selectedTrack = null;
12892      (0, _dom.removeClass)(t.captionsButton, t.options.classPrefix + 'captions-enabled');
12893      t.deactivateVideoTracks();
12894    } else {
12895      var _track = t.getTrackById(trackId);
12896      if (_track) {
12897        if (t.selectedTrack === null) {
12898          (0, _dom.addClass)(t.captionsButton, t.options.classPrefix + 'captions-enabled');
12899        }
12900        t.selectedTrack = _track;
12901        t.activateVideoTrack(t.selectedTrack.srclang);
12902      }
12903    }
12904
12905    var event = (0, _general.createEvent)('captionschange', t.media);
12906    event.detail.caption = t.selectedTrack;
12907    t.media.dispatchEvent(event);
12908  },
12909  hideAllTracks: function hideAllTracks() {
12910    if (this.node.textTracks) {
12911      for (var i = 0; i < this.node.textTracks.length; i++) {
12912        this.node.textTracks[i].mode = 'hidden';
12913      }
12914    }
12915  },
12916  deactivateVideoTracks: function deactivateVideoTracks() {
12917    if (this.node.textTracks) {
12918      for (var i = 0; i < this.node.textTracks.length; i++) {
12919        var track = this.node.textTracks[i];
12920        if (track.kind === 'subtitles' || track.kind === 'captions') {
12921          track.mode = 'hidden';
12922        }
12923      }
12924    }
12925    if (this.options.toggleCaptionsButtonWhenOnlyOne && this.getSubtitles().length === 1) {
12926      this.captionsButton.classList.remove(this.options.classPrefix + 'captions-button-toggle-on');
12927    }
12928  },
12929  activateVideoTrack: function activateVideoTrack(srclang) {
12930    for (var i = 0; i < this.node.textTracks.length; i++) {
12931      var track = this.node.textTracks[i];
12932
12933      if (track.kind === 'subtitles' || track.kind === 'captions') {
12934        if (track.language === srclang) {
12935          track.mode = 'showing';
12936          if (this.options.toggleCaptionsButtonWhenOnlyOne && this.getSubtitles().length === 1) {
12937            this.captionsButton.classList.add(this.options.classPrefix + 'captions-button-toggle-on');
12938          }
12939        } else {
12940          track.mode = 'hidden';
12941        }
12942      }
12943    }
12944  },
12945  checkForAutoPlay: function checkForAutoPlay() {
12946    var _this4 = this;
12947
12948    var readySubtitles = this.getSubtitles().filter(function (_ref6) {
12949      var isError = _ref6.isError;
12950      return !isError;
12951    }),
12952        autoplayTrack = readySubtitles.find(function (_ref7) {
12953      var srclang = _ref7.srclang;
12954      return _this4.options.autoplayCaptionLanguage === srclang;
12955    }) || readySubtitles.find(function (_ref8) {
12956      var isDefault = _ref8.isDefault;
12957      return isDefault;
12958    });
12959
12960    if (autoplayTrack) {
12961      if (this.options.toggleCaptionsButtonWhenOnlyOne && readySubtitles.length === 1 && this.captionsButton) {
12962        this.captionsButton.dispatchEvent((0, _general.createEvent)('click', this.captionsButton));
12963      } else {
12964        var target = _document2.default.getElementById(autoplayTrack.trackId + '-btn');
12965        if (target) {
12966          target.checked = true;
12967          target.dispatchEvent((0, _general.createEvent)('click', target));
12968        }
12969      }
12970    }
12971  },
12972  enableTrackButton: function enableTrackButton(track) {
12973    var t = this,
12974        lang = track.srclang,
12975        target = _document2.default.getElementById(track.trackId + '-btn');
12976    if (!target) {
12977      return;
12978    }
12979
12980    var label = track.label;
12981
12982    if (label === '') {
12983      label = _i18n2.default.t(_mejs2.default.language.codes[lang]) || lang;
12984    }
12985    target.disabled = false;
12986    var targetSiblings = (0, _dom.siblings)(target, function (el) {
12987      return (0, _dom.hasClass)(el, t.options.classPrefix + 'captions-selector-label');
12988    });
12989    for (var i = 0; i < targetSiblings.length; i++) {
12990      targetSiblings[i].innerHTML = label;
12991    }
12992  },
12993  removeTrackButton: function removeTrackButton(track) {
12994    var element = _document2.default.getElementById(track.trackId + '-btn');
12995    if (element) {
12996      var button = element.closest('li');
12997      if (button) {
12998        button.remove();
12999      }
13000    }
13001  },
13002  addTrackButton: function addTrackButton(track) {
13003    var t = this,
13004        label = track.label || _i18n2.default.t(_mejs2.default.language.codes[track.srclang]) || track.srclang;
13005
13006    t.captionsButton.querySelector('ul').innerHTML += '<li class="' + t.options.classPrefix + 'captions-selector-list-item">' + ('<input type="radio" class="' + t.options.classPrefix + 'captions-selector-input" ') + ('name="' + t.id + '_captions" id="' + track.trackId + '-btn" value="' + track.trackId + '" disabled>') + ('<label class="' + t.options.classPrefix + 'captions-selector-label"') + ('for="' + track.trackId + '">' + label + ' (loading)</label>') + '</li>';
13007  },
13008  removeCaptionsIfEmpty: function removeCaptionsIfEmpty() {
13009    if (this.captionsButton && this.options.hideCaptionsButtonWhenEmpty) {
13010      var subtitleCount = this.getSubtitles().filter(function (_ref9) {
13011        var isError = _ref9.isError;
13012        return !isError;
13013      }).length;
13014      this.captionsButton.style.display = subtitleCount > 0 ? '' : 'none';
13015      this.setControlsSize();
13016    }
13017  },
13018  drawChapters: function drawChapters(chapterTrackId) {
13019    var t = this,
13020        chapter = this.node.textTracks.getTrackById(chapterTrackId),
13021        numberOfChapters = chapter.cues.length;
13022
13023    if (!numberOfChapters) {
13024      return;
13025    }
13026
13027    t.chaptersButton.querySelector('ul').innerHTML = '';
13028
13029    for (var i = 0; i < numberOfChapters; i++) {
13030      t.chaptersButton.querySelector('ul').innerHTML += '<li class="' + t.options.classPrefix + 'chapters-selector-list-item" ' + 'role="menuitemcheckbox" aria-live="polite" aria-disabled="false" aria-checked="false">' + ('<input type="radio" class="' + t.options.classPrefix + 'captions-selector-input" ') + ('name="' + t.id + '_chapters" id="' + t.id + '_chapters_' + i + '" value="' + chapter.cues[i].startTime + '" disabled>') + ('<label class="' + t.options.classPrefix + 'chapters-selector-label"') + ('for="' + t.id + '_chapters_' + i + '">' + chapter.cues[i].text + '</label>') + '</li>';
13031    }
13032
13033    var radios = t.chaptersButton.querySelectorAll('input[type="radio"]'),
13034        labels = t.chaptersButton.querySelectorAll('.' + t.options.classPrefix + 'chapters-selector-label');
13035
13036    for (var _i9 = 0; _i9 < radios.length; _i9++) {
13037      radios[_i9].disabled = false;
13038      radios[_i9].checked = false;
13039      radios[_i9].addEventListener('click', function (e) {
13040        var self = this,
13041            listItems = t.chaptersButton.querySelectorAll('li'),
13042            label = (0, _dom.siblings)(self, function (el) {
13043          return (0, _dom.hasClass)(el, t.options.classPrefix + 'chapters-selector-label');
13044        })[0];
13045
13046        self.checked = true;
13047        self.parentNode.setAttribute('aria-checked', true);
13048        (0, _dom.addClass)(label, t.options.classPrefix + 'chapters-selected');
13049        (0, _dom.removeClass)(t.chaptersButton.querySelector('.' + t.options.classPrefix + 'chapters-selected'), t.options.classPrefix + 'chapters-selected');
13050
13051        for (var _i10 = 0; _i10 < listItems.length; _i10++) {
13052          listItems[_i10].setAttribute('aria-checked', false);
13053        }
13054
13055        var keyboard = e.keyCode || e.which;
13056        if (typeof keyboard === 'undefined') {
13057          setTimeout(function () {
13058            t.getElement(t.container).focus();
13059          }, 500);
13060        }
13061
13062        t.media.setCurrentTime(parseFloat(self.value));
13063        if (t.media.paused) {
13064          t.media.play();
13065        }
13066      });
13067    }
13068
13069    for (var _i11 = 0; _i11 < labels.length; _i11++) {
13070      labels[_i11].addEventListener('click', function (e) {
13071        var radio = (0, _dom.siblings)(this, function (el) {
13072          return el.tagName === 'INPUT';
13073        })[0],
13074            event = (0, _general.createEvent)('click', radio);
13075        radio.dispatchEvent(event);
13076        e.preventDefault();
13077      });
13078    }
13079  },
13080  getTrackById: function getTrackById(trackId) {
13081    return this.tracks.find(function (track) {
13082      return track.trackId === trackId;
13083    });
13084  },
13085  getChapters: function getChapters() {
13086    return this.tracks.filter(function (_ref10) {
13087      var kind = _ref10.kind;
13088      return kind === 'chapters';
13089    });
13090  },
13091  getSubtitles: function getSubtitles() {
13092    return this.tracks.filter(function (_ref11) {
13093      var kind = _ref11.kind;
13094      return kind === 'subtitles' || kind === 'captions';
13095    });
13096  },
13097  searchTrackPosition: function searchTrackPosition(tracks, currentTime) {
13098    var lo = 0,
13099        hi = tracks.length - 1,
13100        mid = void 0,
13101        start = void 0,
13102        stop = void 0;
13103
13104    while (lo <= hi) {
13105      mid = lo + hi >> 1;
13106      start = tracks[mid].start;
13107      stop = tracks[mid].stop;
13108
13109      if (currentTime >= start && currentTime < stop) {
13110        return mid;
13111      } else if (start < currentTime) {
13112        lo = mid + 1;
13113      } else if (start > currentTime) {
13114        hi = mid - 1;
13115      }
13116    }
13117
13118    return -1;
13119  }
13120});
13121
13122_mejs2.default.language = {
13123  codes: {
13124    af: 'mejs.afrikaans',
13125    sq: 'mejs.albanian',
13126    ar: 'mejs.arabic',
13127    be: 'mejs.belarusian',
13128    bg: 'mejs.bulgarian',
13129    ca: 'mejs.catalan',
13130    zh: 'mejs.chinese',
13131    'zh-cn': 'mejs.chinese-simplified',
13132    'zh-tw': 'mejs.chines-traditional',
13133    hr: 'mejs.croatian',
13134    cs: 'mejs.czech',
13135    da: 'mejs.danish',
13136    nl: 'mejs.dutch',
13137    en: 'mejs.english',
13138    et: 'mejs.estonian',
13139    fl: 'mejs.filipino',
13140    fi: 'mejs.finnish',
13141    fr: 'mejs.french',
13142    gl: 'mejs.galician',
13143    de: 'mejs.german',
13144    el: 'mejs.greek',
13145    ht: 'mejs.haitian-creole',
13146    iw: 'mejs.hebrew',
13147    hi: 'mejs.hindi',
13148    hu: 'mejs.hungarian',
13149    is: 'mejs.icelandic',
13150    id: 'mejs.indonesian',
13151    ga: 'mejs.irish',
13152    it: 'mejs.italian',
13153    ja: 'mejs.japanese',
13154    ko: 'mejs.korean',
13155    lv: 'mejs.latvian',
13156    lt: 'mejs.lithuanian',
13157    mk: 'mejs.macedonian',
13158    ms: 'mejs.malay',
13159    mt: 'mejs.maltese',
13160    no: 'mejs.norwegian',
13161    fa: 'mejs.persian',
13162    pl: 'mejs.polish',
13163    pt: 'mejs.portuguese',
13164    ro: 'mejs.romanian',
13165    ru: 'mejs.russian',
13166    sr: 'mejs.serbian',
13167    sk: 'mejs.slovak',
13168    sl: 'mejs.slovenian',
13169    es: 'mejs.spanish',
13170    sw: 'mejs.swahili',
13171    sv: 'mejs.swedish',
13172    tl: 'mejs.tagalog',
13173    th: 'mejs.thai',
13174    tr: 'mejs.turkish',
13175    uk: 'mejs.ukrainian',
13176    vi: 'mejs.vietnamese',
13177    cy: 'mejs.welsh',
13178    yi: 'mejs.yiddish'
13179  }
13180};
13181
13182},{"17":17,"2":2,"25":25,"26":26,"27":27,"6":6,"8":8}],15:[function(_dereq_,module,exports){
13183'use strict';
13184
13185var _document = _dereq_(2);
13186
13187var _document2 = _interopRequireDefault(_document);
13188
13189var _player = _dereq_(17);
13190
13191var _player2 = _interopRequireDefault(_player);
13192
13193var _i18n = _dereq_(6);
13194
13195var _i18n2 = _interopRequireDefault(_i18n);
13196
13197var _constants = _dereq_(24);
13198
13199var _general = _dereq_(26);
13200
13201var _dom = _dereq_(25);
13202
13203var _generate = _dereq_(27);
13204
13205function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
13206
13207Object.assign(_player.config, {
13208	muteText: null,
13209
13210	unmuteText: null,
13211
13212	allyVolumeControlText: null,
13213
13214	hideVolumeOnTouchDevices: true,
13215
13216	audioVolume: 'horizontal',
13217
13218	videoVolume: 'vertical',
13219
13220	startVolume: 0.8
13221});
13222
13223Object.assign(_player2.default.prototype, {
13224	buildvolume: function buildvolume(player, controls, layers, media) {
13225		if ((_constants.IS_ANDROID || _constants.IS_IOS) && this.options.hideVolumeOnTouchDevices) {
13226			return;
13227		}
13228
13229		var t = this,
13230		    mode = t.isVideo ? t.options.videoVolume : t.options.audioVolume,
13231		    muteText = (0, _general.isString)(t.options.muteText) ? t.options.muteText : _i18n2.default.t('mejs.mute'),
13232		    unmuteText = (0, _general.isString)(t.options.unmuteText) ? t.options.unmuteText : _i18n2.default.t('mejs.unmute'),
13233		    volumeControlText = (0, _general.isString)(t.options.allyVolumeControlText) ? t.options.allyVolumeControlText : _i18n2.default.t('mejs.volume-help-text'),
13234		    mute = _document2.default.createElement('div');
13235
13236		mute.className = t.options.classPrefix + 'button ' + t.options.classPrefix + 'volume-button ' + t.options.classPrefix + 'mute';
13237		mute.innerHTML = mode === 'horizontal' ? (0, _generate.generateControlButton)(t.id, muteText, muteText, '' + t.media.options.iconSprite, ['icon-mute', 'icon-unmute'], '' + t.options.classPrefix, '', t.options.classPrefix + 'horizontal-volume-slider') : (0, _generate.generateControlButton)(t.id, muteText, muteText, '' + t.media.options.iconSprite, ['icon-mute', 'icon-unmute'], '' + t.options.classPrefix, '', t.options.classPrefix + 'volume-slider') + ('<a class="' + t.options.classPrefix + 'volume-slider" ') + ('aria-label="' + _i18n2.default.t('mejs.volume-slider') + '" aria-valuemin="0" aria-valuemax="100" role="slider" ') + 'aria-orientation="vertical">' + ('<span class="' + t.options.classPrefix + 'offscreen" id="' + t.options.classPrefix + 'volume-slider">' + volumeControlText + '</span>') + ('<div class="' + t.options.classPrefix + 'volume-total">') + ('<div class="' + t.options.classPrefix + 'volume-current"></div>') + ('<div class="' + t.options.classPrefix + 'volume-handle"></div>') + '</div>' + '</a>';
13238
13239		t.addControlElement(mute, 'volume');
13240
13241		t.options.keyActions.push({
13242			keys: [38],
13243			action: function action(player) {
13244				var volumeSlider = player.getElement(player.container).querySelector('.' + t.options.classPrefix + 'volume-slider');
13245				if (volumeSlider && volumeSlider.matches(':focus')) {
13246					volumeSlider.style.display = 'block';
13247				}
13248				if (player.isVideo) {
13249					player.showControls();
13250					player.startControlsTimer();
13251				}
13252
13253				var newVolume = Math.min(player.volume + 0.1, 1);
13254				player.setVolume(newVolume);
13255				if (newVolume > 0) {
13256					player.setMuted(false);
13257				}
13258			}
13259		}, {
13260			keys: [40],
13261			action: function action(player) {
13262				var volumeSlider = player.getElement(player.container).querySelector('.' + t.options.classPrefix + 'volume-slider');
13263				if (volumeSlider) {
13264					volumeSlider.style.display = 'block';
13265				}
13266
13267				if (player.isVideo) {
13268					player.showControls();
13269					player.startControlsTimer();
13270				}
13271
13272				var newVolume = Math.max(player.volume - 0.1, 0);
13273				player.setVolume(newVolume);
13274
13275				if (newVolume <= 0.1) {
13276					player.setMuted(true);
13277				}
13278			}
13279		}, {
13280			keys: [77],
13281			action: function action(player) {
13282				var volumeSlider = player.getElement(player.container).querySelector('.' + t.options.classPrefix + 'volume-slider');
13283				if (volumeSlider) {
13284					volumeSlider.style.display = 'block';
13285				}
13286
13287				if (player.isVideo) {
13288					player.showControls();
13289					player.startControlsTimer();
13290				}
13291				if (player.media.muted) {
13292					player.setMuted(false);
13293				} else {
13294					player.setMuted(true);
13295				}
13296			}
13297		});
13298
13299		if (mode === 'horizontal') {
13300			var anchor = _document2.default.createElement('a');
13301			anchor.className = t.options.classPrefix + 'horizontal-volume-slider';
13302			anchor.setAttribute('aria-label', _i18n2.default.t('mejs.volume-slider'));
13303			anchor.setAttribute('aria-valuemin', 0);
13304			anchor.setAttribute('aria-valuemax', 100);
13305			anchor.setAttribute('aria-valuenow', 100);
13306			anchor.setAttribute('role', 'slider');
13307			anchor.innerHTML += '<span class="' + t.options.classPrefix + 'offscreen" id="' + t.options.classPrefix + 'horizontal-volume-slider">' + volumeControlText + '</span>' + ('<div class="' + t.options.classPrefix + 'horizontal-volume-total">') + ('<div class="' + t.options.classPrefix + 'horizontal-volume-current"></div>') + ('<div class="' + t.options.classPrefix + 'horizontal-volume-handle"></div>') + '</div>';
13308			mute.parentNode.insertBefore(anchor, mute.nextSibling);
13309		}
13310
13311		var mouseIsDown = false,
13312		    mouseIsOver = false,
13313		    modified = false,
13314		    updateVolumeSlider = function updateVolumeSlider() {
13315			var volume = Math.floor(media.volume * 100);
13316			volumeSlider.setAttribute('aria-valuenow', volume);
13317			volumeSlider.setAttribute('aria-valuetext', volume + '%');
13318		};
13319
13320		var volumeSlider = mode === 'vertical' ? t.getElement(t.container).querySelector('.' + t.options.classPrefix + 'volume-slider') : t.getElement(t.container).querySelector('.' + t.options.classPrefix + 'horizontal-volume-slider'),
13321		    volumeTotal = mode === 'vertical' ? t.getElement(t.container).querySelector('.' + t.options.classPrefix + 'volume-total') : t.getElement(t.container).querySelector('.' + t.options.classPrefix + 'horizontal-volume-total'),
13322		    volumeCurrent = mode === 'vertical' ? t.getElement(t.container).querySelector('.' + t.options.classPrefix + 'volume-current') : t.getElement(t.container).querySelector('.' + t.options.classPrefix + 'horizontal-volume-current'),
13323		    volumeHandle = mode === 'vertical' ? t.getElement(t.container).querySelector('.' + t.options.classPrefix + 'volume-handle') : t.getElement(t.container).querySelector('.' + t.options.classPrefix + 'horizontal-volume-handle'),
13324		    positionVolumeHandle = function positionVolumeHandle(volume) {
13325
13326			if (volume === null || isNaN(volume) || volume === undefined) {
13327				return;
13328			}
13329
13330			volume = Math.max(0, volume);
13331			volume = Math.min(volume, 1);
13332
13333			if (volume === 0) {
13334				(0, _dom.removeClass)(mute, t.options.classPrefix + 'mute');
13335				(0, _dom.addClass)(mute, t.options.classPrefix + 'unmute');
13336				var button = mute.firstElementChild;
13337				button.setAttribute('title', unmuteText);
13338				button.setAttribute('aria-label', unmuteText);
13339			} else {
13340				(0, _dom.removeClass)(mute, t.options.classPrefix + 'unmute');
13341				(0, _dom.addClass)(mute, t.options.classPrefix + 'mute');
13342				var _button = mute.firstElementChild;
13343				_button.setAttribute('title', muteText);
13344				_button.setAttribute('aria-label', muteText);
13345			}
13346
13347			var volumePercentage = volume * 100 + '%',
13348			    volumeStyles = getComputedStyle(volumeHandle);
13349
13350			if (mode === 'vertical') {
13351				volumeCurrent.style.bottom = 0;
13352				volumeCurrent.style.height = volumePercentage;
13353				volumeHandle.style.bottom = volumePercentage;
13354				volumeHandle.style.marginBottom = -parseFloat(volumeStyles.height) / 2 + 'px';
13355			} else {
13356				volumeCurrent.style.left = 0;
13357				volumeCurrent.style.width = volumePercentage;
13358				volumeHandle.style.left = volumePercentage;
13359				volumeHandle.style.marginLeft = -parseFloat(volumeStyles.width) / 2 + 'px';
13360			}
13361		},
13362		    handleVolumeMove = function handleVolumeMove(e) {
13363			var totalOffset = (0, _dom.offset)(volumeTotal),
13364			    volumeStyles = getComputedStyle(volumeTotal);
13365
13366			modified = true;
13367
13368			var volume = null;
13369
13370			if (mode === 'vertical') {
13371				var railHeight = parseFloat(volumeStyles.height),
13372				    newY = e.pageY - totalOffset.top;
13373
13374				volume = (railHeight - newY) / railHeight;
13375
13376				if (totalOffset.top === 0 || totalOffset.left === 0) {
13377					return;
13378				}
13379			} else {
13380				var railWidth = parseFloat(volumeStyles.width),
13381				    newX = e.pageX - totalOffset.left;
13382
13383				volume = newX / railWidth;
13384			}
13385
13386			volume = Math.max(0, volume);
13387			volume = Math.min(volume, 1);
13388
13389			positionVolumeHandle(volume);
13390
13391			t.setMuted(volume === 0);
13392			t.setVolume(volume);
13393
13394			e.preventDefault();
13395			e.stopPropagation();
13396		},
13397		    toggleMute = function toggleMute() {
13398			if (t.muted) {
13399				positionVolumeHandle(0);
13400				(0, _dom.removeClass)(mute, t.options.classPrefix + 'mute');
13401				(0, _dom.addClass)(mute, t.options.classPrefix + 'unmute');
13402			} else {
13403
13404				positionVolumeHandle(media.volume);
13405				(0, _dom.removeClass)(mute, t.options.classPrefix + 'unmute');
13406				(0, _dom.addClass)(mute, t.options.classPrefix + 'mute');
13407			}
13408		};
13409
13410		player.getElement(player.container).addEventListener('keydown', function (e) {
13411			var hasFocus = !!e.target.closest('.' + t.options.classPrefix + 'container');
13412			if (!hasFocus && mode === 'vertical') {
13413				volumeSlider.style.display = 'none';
13414			}
13415		});
13416
13417		mute.addEventListener('mouseenter', function (e) {
13418			if (e.target === mute) {
13419				volumeSlider.style.display = 'block';
13420				mouseIsOver = true;
13421				e.preventDefault();
13422				e.stopPropagation();
13423			}
13424		});
13425		mute.addEventListener('focusin', function () {
13426			volumeSlider.style.display = 'block';
13427			mouseIsOver = true;
13428		});
13429
13430		mute.addEventListener('focusout', function (e) {
13431			if ((!e.relatedTarget || e.relatedTarget && !e.relatedTarget.matches('.' + t.options.classPrefix + 'volume-slider')) && mode === 'vertical') {
13432				volumeSlider.style.display = 'none';
13433			}
13434		});
13435		mute.addEventListener('mouseleave', function () {
13436			mouseIsOver = false;
13437			if (!mouseIsDown && mode === 'vertical') {
13438				volumeSlider.style.display = 'none';
13439			}
13440		});
13441		mute.addEventListener('focusout', function () {
13442			mouseIsOver = false;
13443		});
13444		mute.addEventListener('keydown', function (e) {
13445			if (t.options.enableKeyboard && t.options.keyActions.length) {
13446				var keyCode = e.which || e.keyCode || 0,
13447				    volume = media.volume;
13448
13449				switch (keyCode) {
13450					case 38:
13451						volume = Math.min(volume + 0.1, 1);
13452						break;
13453					case 40:
13454						volume = Math.max(0, volume - 0.1);
13455						break;
13456					default:
13457						return true;
13458				}
13459
13460				mouseIsDown = false;
13461				positionVolumeHandle(volume);
13462				media.setVolume(volume);
13463
13464				e.preventDefault();
13465				e.stopPropagation();
13466			}
13467		});
13468		mute.querySelector('button').addEventListener('click', function () {
13469			media.setMuted(!media.muted);
13470			var event = (0, _general.createEvent)('volumechange', media);
13471			media.dispatchEvent(event);
13472		});
13473
13474		volumeSlider.addEventListener('dragstart', function () {
13475			return false;
13476		});
13477
13478		volumeSlider.addEventListener('mouseover', function () {
13479			mouseIsOver = true;
13480		});
13481		volumeSlider.addEventListener('focusin', function () {
13482			volumeSlider.style.display = 'block';
13483			mouseIsOver = true;
13484		});
13485		volumeSlider.addEventListener('focusout', function () {
13486			mouseIsOver = false;
13487			if (!mouseIsDown && mode === 'vertical') {
13488				volumeSlider.style.display = 'none';
13489			}
13490		});
13491		volumeSlider.addEventListener('mousedown', function (e) {
13492			handleVolumeMove(e);
13493			t.globalBind('mousemove.vol', function (event) {
13494				var target = event.target;
13495
13496				var targetHasClosest = typeof target.closest == 'function';
13497				var targetSliderElement = target.closest(mode === 'vertical' ? '.' + t.options.classPrefix + 'volume-slider' : '.' + t.options.classPrefix + 'horizontal-volume-slider');
13498				if (mouseIsDown && (target === volumeSlider || targetHasClosest && targetSliderElement)) {
13499					handleVolumeMove(event);
13500				}
13501			});
13502			t.globalBind('mouseup.vol', function () {
13503				mouseIsDown = false;
13504				if (!mouseIsOver && mode === 'vertical') {
13505					volumeSlider.style.display = 'none';
13506				}
13507			});
13508			mouseIsDown = true;
13509			e.preventDefault();
13510			e.stopPropagation();
13511		});
13512
13513		media.addEventListener('volumechange', function (e) {
13514			if (!mouseIsDown) {
13515				toggleMute();
13516			}
13517			updateVolumeSlider(e);
13518		});
13519
13520		var rendered = false;
13521		media.addEventListener('rendererready', function () {
13522			if (!modified) {
13523				setTimeout(function () {
13524					rendered = true;
13525					if (player.options.startVolume === 0 || media.originalNode.muted) {
13526						media.setMuted(true);
13527					}
13528					media.setVolume(player.options.startVolume);
13529					t.setControlsSize();
13530				}, 250);
13531			}
13532		});
13533
13534		media.addEventListener('loadedmetadata', function () {
13535			setTimeout(function () {
13536				if (!modified && !rendered) {
13537					if (player.options.startVolume === 0 || media.originalNode.muted) {
13538						media.setMuted(true);
13539					}
13540					if (player.options.startVolume === 0) {
13541						player.options.startVolume = 0;
13542					}
13543					media.setVolume(player.options.startVolume);
13544					t.setControlsSize();
13545				}
13546				rendered = false;
13547			}, 250);
13548		});
13549
13550		if (player.options.startVolume === 0 || media.originalNode.muted) {
13551			media.setMuted(true);
13552			if (player.options.startVolume === 0) {
13553				player.options.startVolume = 0;
13554			}
13555			toggleMute();
13556		}
13557
13558		t.getElement(t.container).addEventListener('controlsresize', function () {
13559			toggleMute();
13560		});
13561	}
13562});
13563
13564},{"17":17,"2":2,"24":24,"25":25,"26":26,"27":27,"6":6}],16:[function(_dereq_,module,exports){
13565'use strict';
13566
13567Object.defineProperty(exports, "__esModule", {
13568	value: true
13569});
13570var EN = exports.EN = {
13571	'mejs.plural-form': 1,
13572
13573	'mejs.download-file': 'Download File',
13574
13575	'mejs.fullscreen': 'Fullscreen',
13576
13577	'mejs.play': 'Play',
13578	'mejs.pause': 'Pause',
13579
13580	'mejs.time-slider': 'Time Slider',
13581	'mejs.time-help-text': 'Use Left/Right Arrow keys to advance one second, Up/Down arrows to advance ten seconds.',
13582	'mejs.live-broadcast': 'Live Broadcast',
13583
13584	'mejs.current': 'Current time',
13585	'mejs.duration': 'Total duration',
13586
13587	'mejs.volume-help-text': 'Use Up/Down Arrow keys to increase or decrease volume.',
13588	'mejs.unmute': 'Unmute',
13589	'mejs.mute': 'Mute',
13590	'mejs.volume-slider': 'Volume Slider',
13591
13592	'mejs.video-player': 'Video Player',
13593	'mejs.audio-player': 'Audio Player',
13594
13595	'mejs.captions-subtitles': 'Captions/Subtitles',
13596	'mejs.captions-chapters': 'Chapters',
13597	'mejs.none': 'None',
13598	'mejs.afrikaans': 'Afrikaans',
13599	'mejs.albanian': 'Albanian',
13600	'mejs.arabic': 'Arabic',
13601	'mejs.belarusian': 'Belarusian',
13602	'mejs.bulgarian': 'Bulgarian',
13603	'mejs.catalan': 'Catalan',
13604	'mejs.chinese': 'Chinese',
13605	'mejs.chinese-simplified': 'Chinese (Simplified)',
13606	'mejs.chinese-traditional': 'Chinese (Traditional)',
13607	'mejs.croatian': 'Croatian',
13608	'mejs.czech': 'Czech',
13609	'mejs.danish': 'Danish',
13610	'mejs.dutch': 'Dutch',
13611	'mejs.english': 'English',
13612	'mejs.estonian': 'Estonian',
13613	'mejs.filipino': 'Filipino',
13614	'mejs.finnish': 'Finnish',
13615	'mejs.french': 'French',
13616	'mejs.galician': 'Galician',
13617	'mejs.german': 'German',
13618	'mejs.greek': 'Greek',
13619	'mejs.haitian-creole': 'Haitian Creole',
13620	'mejs.hebrew': 'Hebrew',
13621	'mejs.hindi': 'Hindi',
13622	'mejs.hungarian': 'Hungarian',
13623	'mejs.icelandic': 'Icelandic',
13624	'mejs.indonesian': 'Indonesian',
13625	'mejs.irish': 'Irish',
13626	'mejs.italian': 'Italian',
13627	'mejs.japanese': 'Japanese',
13628	'mejs.korean': 'Korean',
13629	'mejs.latvian': 'Latvian',
13630	'mejs.lithuanian': 'Lithuanian',
13631	'mejs.macedonian': 'Macedonian',
13632	'mejs.malay': 'Malay',
13633	'mejs.maltese': 'Maltese',
13634	'mejs.norwegian': 'Norwegian',
13635	'mejs.persian': 'Persian',
13636	'mejs.polish': 'Polish',
13637	'mejs.portuguese': 'Portuguese',
13638	'mejs.romanian': 'Romanian',
13639	'mejs.russian': 'Russian',
13640	'mejs.serbian': 'Serbian',
13641	'mejs.slovak': 'Slovak',
13642	'mejs.slovenian': 'Slovenian',
13643	'mejs.spanish': 'Spanish',
13644	'mejs.swahili': 'Swahili',
13645	'mejs.swedish': 'Swedish',
13646	'mejs.tagalog': 'Tagalog',
13647	'mejs.thai': 'Thai',
13648	'mejs.turkish': 'Turkish',
13649	'mejs.ukrainian': 'Ukrainian',
13650	'mejs.vietnamese': 'Vietnamese',
13651	'mejs.welsh': 'Welsh',
13652	'mejs.yiddish': 'Yiddish'
13653};
13654
13655},{}],17:[function(_dereq_,module,exports){
13656'use strict';
13657
13658Object.defineProperty(exports, "__esModule", {
13659	value: true
13660});
13661exports.config = undefined;
13662
13663var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; };
13664
13665var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
13666
13667var _window = _dereq_(3);
13668
13669var _window2 = _interopRequireDefault(_window);
13670
13671var _document = _dereq_(2);
13672
13673var _document2 = _interopRequireDefault(_document);
13674
13675var _mejs = _dereq_(8);
13676
13677var _mejs2 = _interopRequireDefault(_mejs);
13678
13679var _mediaelement = _dereq_(7);
13680
13681var _mediaelement2 = _interopRequireDefault(_mediaelement);
13682
13683var _default = _dereq_(18);
13684
13685var _default2 = _interopRequireDefault(_default);
13686
13687var _i18n = _dereq_(6);
13688
13689var _i18n2 = _interopRequireDefault(_i18n);
13690
13691var _constants = _dereq_(24);
13692
13693var _general = _dereq_(26);
13694
13695var _time = _dereq_(30);
13696
13697var _media = _dereq_(28);
13698
13699var _dom = _dereq_(25);
13700
13701var dom = _interopRequireWildcard(_dom);
13702
13703var _generate = _dereq_(27);
13704
13705function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } }
13706
13707function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
13708
13709function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
13710
13711_mejs2.default.mepIndex = 0;
13712
13713_mejs2.default.players = {};
13714
13715var config = exports.config = {
13716	poster: '',
13717
13718	showPosterWhenEnded: false,
13719
13720	showPosterWhenPaused: false,
13721
13722	defaultVideoWidth: 480,
13723
13724	defaultVideoHeight: 270,
13725
13726	videoWidth: -1,
13727
13728	videoHeight: -1,
13729
13730	defaultAudioWidth: 400,
13731
13732	defaultAudioHeight: 40,
13733
13734	defaultSeekBackwardInterval: function defaultSeekBackwardInterval(media) {
13735		return media.getDuration() * 0.05;
13736	},
13737
13738	defaultSeekForwardInterval: function defaultSeekForwardInterval(media) {
13739		return media.getDuration() * 0.05;
13740	},
13741
13742	setDimensions: true,
13743
13744	audioWidth: -1,
13745
13746	audioHeight: -1,
13747
13748	loop: false,
13749
13750	autoRewind: true,
13751
13752	enableAutosize: true,
13753
13754	timeFormat: '',
13755
13756	alwaysShowHours: false,
13757
13758	showTimecodeFrameCount: false,
13759
13760	framesPerSecond: 25,
13761
13762	alwaysShowControls: false,
13763
13764	hideVideoControlsOnLoad: false,
13765
13766	hideVideoControlsOnPause: false,
13767
13768	clickToPlayPause: true,
13769
13770	controlsTimeoutDefault: 1500,
13771
13772	controlsTimeoutMouseEnter: 2500,
13773
13774	controlsTimeoutMouseLeave: 1000,
13775
13776	iPadUseNativeControls: false,
13777
13778	iPhoneUseNativeControls: false,
13779
13780	AndroidUseNativeControls: false,
13781
13782	features: ['playpause', 'current', 'progress', 'duration', 'tracks', 'volume', 'fullscreen'],
13783
13784	useDefaultControls: false,
13785
13786	isVideo: true,
13787
13788	stretching: 'auto',
13789
13790	classPrefix: 'mejs__',
13791
13792	enableKeyboard: true,
13793
13794	pauseOtherPlayers: true,
13795
13796	secondsDecimalLength: 0,
13797
13798	customError: null,
13799
13800	keyActions: [],
13801
13802	hideScreenReaderTitle: false
13803};
13804
13805_mejs2.default.MepDefaults = config;
13806
13807var MediaElementPlayer = function () {
13808	function MediaElementPlayer(node, o) {
13809		_classCallCheck(this, MediaElementPlayer);
13810
13811		var t = this,
13812		    element = typeof node === 'string' ? _document2.default.getElementById(node) : node;
13813
13814		if (!(t instanceof MediaElementPlayer)) {
13815			return new MediaElementPlayer(element, o);
13816		}
13817
13818		t.node = t.media = element;
13819
13820		if (!t.node) {
13821			return;
13822		}
13823
13824		if (t.media.player) {
13825			return t.media.player;
13826		}
13827
13828		t.hasFocus = false;
13829
13830		t.controlsAreVisible = true;
13831
13832		t.controlsEnabled = true;
13833
13834		t.controlsTimer = null;
13835
13836		t.currentMediaTime = 0;
13837
13838		t.proxy = null;
13839
13840		if (o === undefined) {
13841			var options = t.node.getAttribute('data-mejsoptions');
13842			o = options ? JSON.parse(options) : {};
13843		}
13844
13845		t.options = Object.assign({}, config, o);
13846
13847		if (t.options.loop && !t.media.getAttribute('loop')) {
13848			t.media.loop = true;
13849			t.node.loop = true;
13850		} else if (t.media.loop) {
13851			t.options.loop = true;
13852		}
13853
13854		if (!t.options.timeFormat) {
13855			t.options.timeFormat = 'mm:ss';
13856			if (t.options.alwaysShowHours) {
13857				t.options.timeFormat = 'hh:mm:ss';
13858			}
13859			if (t.options.showTimecodeFrameCount) {
13860				t.options.timeFormat += ':ff';
13861			}
13862		}
13863
13864		(0, _time.calculateTimeFormat)(0, t.options, t.options.framesPerSecond || 25);
13865
13866		t.id = 'mep_' + _mejs2.default.mepIndex++;
13867
13868		_mejs2.default.players[t.id] = t;
13869
13870		t.init();
13871
13872		return t;
13873	}
13874
13875	_createClass(MediaElementPlayer, [{
13876		key: 'getElement',
13877		value: function getElement(element) {
13878			return element;
13879		}
13880	}, {
13881		key: 'init',
13882		value: function init() {
13883			var t = this,
13884			    playerOptions = Object.assign({}, t.options, {
13885				success: function success(media, domNode) {
13886					t._meReady(media, domNode);
13887				},
13888				error: function error(e) {
13889					t._handleError(e);
13890				}
13891			}),
13892			    tagName = t.node.tagName.toLowerCase();
13893
13894			t.isDynamic = tagName !== 'audio' && tagName !== 'video' && tagName !== 'iframe';
13895			t.isVideo = t.isDynamic ? t.options.isVideo : tagName !== 'audio' && t.options.isVideo;
13896			t.mediaFiles = null;
13897			t.trackFiles = null;
13898
13899			t.media.addEventListener('rendererready', this.updateNode.bind(this));
13900
13901			if (_constants.IS_IPAD && t.options.iPadUseNativeControls || _constants.IS_IPHONE && t.options.iPhoneUseNativeControls) {
13902				t.node.setAttribute('controls', true);
13903
13904				if (_constants.IS_IPAD && t.node.getAttribute('autoplay')) {
13905					t.play();
13906				}
13907			} else if ((t.isVideo || !t.isVideo && (t.options.features.length || t.options.useDefaultControls)) && !(_constants.IS_ANDROID && t.options.AndroidUseNativeControls)) {
13908				t.node.removeAttribute('controls');
13909				var videoPlayerTitle = t.isVideo ? _i18n2.default.t('mejs.video-player') : _i18n2.default.t('mejs.audio-player');
13910
13911				if (!t.options.hideScreenReaderTitle) {
13912					var offscreen = _document2.default.createElement('span');
13913					offscreen.className = t.options.classPrefix + 'offscreen';
13914					offscreen.innerText = videoPlayerTitle;
13915					t.media.parentNode.insertBefore(offscreen, t.media);
13916				}
13917
13918				t.container = _document2.default.createElement('div');
13919				t.getElement(t.container).id = t.id;
13920				t.getElement(t.container).className = t.options.classPrefix + 'container ' + t.options.classPrefix + 'container-keyboard-inactive ' + t.media.className;
13921				t.getElement(t.container).tabIndex = 0;
13922				t.getElement(t.container).setAttribute('role', 'application');
13923				t.getElement(t.container).setAttribute('aria-label', videoPlayerTitle);
13924				t.getElement(t.container).innerHTML = '<div class="' + t.options.classPrefix + 'inner">' + ('<div class="' + t.options.classPrefix + 'mediaelement"></div>') + ('<div class="' + t.options.classPrefix + 'layers"></div>') + ('<div class="' + t.options.classPrefix + 'controls"></div>') + '</div>';
13925
13926				t.getElement(t.container).addEventListener('focus', function (e) {
13927					if (!t.controlsAreVisible && !t.hasFocus && t.controlsEnabled) {
13928						t.showControls(true);
13929
13930						var btnSelector = (0, _general.isNodeAfter)(e.relatedTarget, t.getElement(t.container)) ? '.' + t.options.classPrefix + 'controls .' + t.options.classPrefix + 'button:last-child > button' : '.' + t.options.classPrefix + 'playpause-button > button',
13931						    button = t.getElement(t.container).querySelector(btnSelector);
13932
13933						button.focus();
13934					}
13935				});
13936				t.node.parentNode.insertBefore(t.getElement(t.container), t.node);
13937
13938				if (!t.options.features.length && !t.options.useDefaultControls) {
13939					t.getElement(t.container).style.background = 'transparent';
13940					t.getElement(t.container).querySelector('.' + t.options.classPrefix + 'controls').style.display = 'none';
13941				}
13942
13943				if (t.isVideo && t.options.stretching === 'fill' && !dom.hasClass(t.getElement(t.container).parentNode, t.options.classPrefix + 'fill-container')) {
13944					t.outerContainer = t.media.parentNode;
13945
13946					var wrapper = _document2.default.createElement('div');
13947					wrapper.className = t.options.classPrefix + 'fill-container';
13948					t.getElement(t.container).parentNode.insertBefore(wrapper, t.getElement(t.container));
13949					wrapper.appendChild(t.getElement(t.container));
13950				}
13951
13952				if (_constants.IS_ANDROID) {
13953					dom.addClass(t.getElement(t.container), t.options.classPrefix + 'android');
13954				}
13955				if (_constants.IS_IOS) {
13956					dom.addClass(t.getElement(t.container), t.options.classPrefix + 'ios');
13957				}
13958				if (_constants.IS_IPAD) {
13959					dom.addClass(t.getElement(t.container), t.options.classPrefix + 'ipad');
13960				}
13961				if (_constants.IS_IPHONE) {
13962					dom.addClass(t.getElement(t.container), t.options.classPrefix + 'iphone');
13963				}
13964				dom.addClass(t.getElement(t.container), t.isVideo ? t.options.classPrefix + 'video' : t.options.classPrefix + 'audio');
13965
13966				t.getElement(t.container).querySelector('.' + t.options.classPrefix + 'mediaelement').appendChild(t.node);
13967
13968				t.media.player = t;
13969
13970				t.controls = t.getElement(t.container).querySelector('.' + t.options.classPrefix + 'controls');
13971				t.layers = t.getElement(t.container).querySelector('.' + t.options.classPrefix + 'layers');
13972
13973				var tagType = t.isVideo ? 'video' : 'audio',
13974				    capsTagName = tagType.substring(0, 1).toUpperCase() + tagType.substring(1);
13975
13976				if (t.options[tagType + 'Width'] > 0 || t.options[tagType + 'Width'].toString().indexOf('%') > -1) {
13977					t.width = t.options[tagType + 'Width'];
13978				} else if (t.node.style.width !== '' && t.node.style.width !== null) {
13979					t.width = t.node.style.width;
13980				} else if (t.node.getAttribute('width')) {
13981					t.width = t.node.getAttribute('width');
13982				} else {
13983					t.width = t.options['default' + capsTagName + 'Width'];
13984				}
13985
13986				if (t.options[tagType + 'Height'] > 0 || t.options[tagType + 'Height'].toString().indexOf('%') > -1) {
13987					t.height = t.options[tagType + 'Height'];
13988				} else if (t.node.style.height !== '' && t.node.style.height !== null) {
13989					t.height = t.node.style.height;
13990				} else if (t.node.getAttribute('height')) {
13991					t.height = t.node.getAttribute('height');
13992				} else {
13993					t.height = t.options['default' + capsTagName + 'Height'];
13994				}
13995				t.initialAspectRatio = t.height >= t.width ? t.width / t.height : t.height / t.width;
13996
13997				t.setPlayerSize(t.width, t.height);
13998			} else if (!t.isVideo && !t.options.features.length && !t.options.useDefaultControls) {
13999					t.node.style.display = 'none';
14000				}
14001
14002			playerOptions.pluginWidth = t.width;
14003			playerOptions.pluginHeight = t.height;
14004
14005			_mejs2.default.MepDefaults = playerOptions;
14006
14007			new _mediaelement2.default(t.media, playerOptions, t.mediaFiles);
14008
14009			if (t.getElement(t.container) !== undefined && t.options.features.length && t.controlsAreVisible && !t.options.hideVideoControlsOnLoad) {
14010				var event = (0, _general.createEvent)('controlsshown', t.getElement(t.container));
14011				t.getElement(t.container).dispatchEvent(event);
14012			}
14013		}
14014	}, {
14015		key: 'updateNode',
14016		value: function updateNode(event) {
14017			var node = void 0,
14018			    iframeId = void 0;
14019			var mediaElement = event.detail.target.hasOwnProperty('mediaElement') ? event.detail.target.mediaElement : event.detail.target;
14020			var originalNode = mediaElement.originalNode;
14021
14022			if (event.detail.isIframe) {
14023				iframeId = mediaElement.renderer.id;
14024				node = mediaElement.querySelector('#' + iframeId);
14025				node.style.position = 'absolute';
14026
14027				if (originalNode.style.maxWidth) {
14028					node.style.maxWidth = originalNode.style.maxWidth;
14029				}
14030			} else {
14031				node = event.detail.target;
14032			}
14033			this.domNode = node;
14034			this.node = node;
14035		}
14036	}, {
14037		key: 'showControls',
14038		value: function showControls(doAnimation) {
14039			var t = this;
14040
14041			doAnimation = doAnimation === undefined || doAnimation;
14042
14043			if (t.controlsAreVisible || !t.isVideo) {
14044				return;
14045			}
14046
14047			if (doAnimation) {
14048				(function () {
14049					dom.fadeIn(t.getElement(t.controls), 200, function () {
14050						dom.removeClass(t.getElement(t.controls), t.options.classPrefix + 'offscreen');
14051						var event = (0, _general.createEvent)('controlsshown', t.getElement(t.container));
14052						t.getElement(t.container).dispatchEvent(event);
14053					});
14054
14055					var controls = t.getElement(t.container).querySelectorAll('.' + t.options.classPrefix + 'control');
14056
14057					var _loop = function _loop(i, total) {
14058						dom.fadeIn(controls[i], 200, function () {
14059							dom.removeClass(controls[i], t.options.classPrefix + 'offscreen');
14060						});
14061					};
14062
14063					for (var i = 0, total = controls.length; i < total; i++) {
14064						_loop(i, total);
14065					}
14066				})();
14067			} else {
14068				dom.removeClass(t.getElement(t.controls), t.options.classPrefix + 'offscreen');
14069				t.getElement(t.controls).style.display = '';
14070				t.getElement(t.controls).style.opacity = 1;
14071
14072				var controls = t.getElement(t.container).querySelectorAll('.' + t.options.classPrefix + 'control');
14073				for (var i = 0, total = controls.length; i < total; i++) {
14074					dom.removeClass(controls[i], t.options.classPrefix + 'offscreen');
14075					controls[i].style.display = '';
14076				}
14077
14078				var event = (0, _general.createEvent)('controlsshown', t.getElement(t.container));
14079				t.getElement(t.container).dispatchEvent(event);
14080			}
14081
14082			t.controlsAreVisible = true;
14083			t.setControlsSize();
14084		}
14085	}, {
14086		key: 'hideControls',
14087		value: function hideControls(doAnimation, forceHide) {
14088			var t = this;
14089
14090			doAnimation = doAnimation === undefined || doAnimation;
14091
14092			if (forceHide !== true && (!t.controlsAreVisible || t.options.alwaysShowControls || t.paused && t.readyState === 4 && (!t.options.hideVideoControlsOnLoad && t.currentTime <= 0 || !t.options.hideVideoControlsOnPause && t.currentTime > 0) || t.isVideo && !t.options.hideVideoControlsOnLoad && !t.readyState || t.ended)) {
14093				return;
14094			}
14095
14096			if (doAnimation) {
14097				(function () {
14098					dom.fadeOut(t.getElement(t.controls), 200, function () {
14099						dom.addClass(t.getElement(t.controls), t.options.classPrefix + 'offscreen');
14100						t.getElement(t.controls).style.display = '';
14101						var event = (0, _general.createEvent)('controlshidden', t.getElement(t.container));
14102						t.getElement(t.container).dispatchEvent(event);
14103					});
14104
14105					var controls = t.getElement(t.container).querySelectorAll('.' + t.options.classPrefix + 'control');
14106
14107					var _loop2 = function _loop2(i, total) {
14108						dom.fadeOut(controls[i], 200, function () {
14109							dom.addClass(controls[i], t.options.classPrefix + 'offscreen');
14110							controls[i].style.display = '';
14111						});
14112					};
14113
14114					for (var i = 0, total = controls.length; i < total; i++) {
14115						_loop2(i, total);
14116					}
14117				})();
14118			} else {
14119				dom.addClass(t.getElement(t.controls), t.options.classPrefix + 'offscreen');
14120				t.getElement(t.controls).style.display = '';
14121				t.getElement(t.controls).style.opacity = 0;
14122
14123				var controls = t.getElement(t.container).querySelectorAll('.' + t.options.classPrefix + 'control');
14124				for (var i = 0, total = controls.length; i < total; i++) {
14125					dom.addClass(controls[i], t.options.classPrefix + 'offscreen');
14126					controls[i].style.display = '';
14127				}
14128
14129				var event = (0, _general.createEvent)('controlshidden', t.getElement(t.container));
14130				t.getElement(t.container).dispatchEvent(event);
14131			}
14132
14133			t.controlsAreVisible = false;
14134		}
14135	}, {
14136		key: 'startControlsTimer',
14137		value: function startControlsTimer(timeout) {
14138			var t = this;
14139
14140			timeout = typeof timeout !== 'undefined' ? timeout : t.options.controlsTimeoutDefault;
14141
14142			t.killControlsTimer('start');
14143
14144			t.controlsTimer = setTimeout(function () {
14145				t.hideControls();
14146				t.killControlsTimer('hide');
14147			}, timeout);
14148		}
14149	}, {
14150		key: 'killControlsTimer',
14151		value: function killControlsTimer() {
14152			var t = this;
14153
14154			if (t.controlsTimer !== null) {
14155				clearTimeout(t.controlsTimer);
14156				delete t.controlsTimer;
14157				t.controlsTimer = null;
14158			}
14159		}
14160	}, {
14161		key: 'disableControls',
14162		value: function disableControls() {
14163			var t = this;
14164
14165			t.killControlsTimer();
14166			t.controlsEnabled = false;
14167			t.hideControls(false, true);
14168		}
14169	}, {
14170		key: 'enableControls',
14171		value: function enableControls() {
14172			var t = this;
14173
14174			t.controlsEnabled = true;
14175			t.showControls(false);
14176		}
14177	}, {
14178		key: '_setDefaultPlayer',
14179		value: function _setDefaultPlayer() {
14180			var t = this;
14181			if (t.proxy) {
14182				t.proxy.pause();
14183			}
14184			t.proxy = new _default2.default(t);
14185			t.media.addEventListener('loadedmetadata', function () {
14186				if (t.getCurrentTime() > 0 && t.currentMediaTime > 0) {
14187					t.setCurrentTime(t.currentMediaTime);
14188					if (!_constants.IS_IOS && !_constants.IS_ANDROID) {
14189						t.play();
14190					}
14191				}
14192			});
14193		}
14194	}, {
14195		key: '_meReady',
14196		value: function _meReady(media, domNode) {
14197			var t = this,
14198			    autoplayAttr = domNode.getAttribute('autoplay'),
14199			    autoplay = !(autoplayAttr === undefined || autoplayAttr === null || autoplayAttr === 'false'),
14200			    isNative = media.rendererName !== null && /(native|html5)/i.test(media.rendererName);
14201
14202			if (t.getElement(t.controls)) {
14203				t.enableControls();
14204			}
14205
14206			if (t.getElement(t.container) && t.getElement(t.container).querySelector('.' + t.options.classPrefix + 'overlay-play')) {
14207				t.getElement(t.container).querySelector('.' + t.options.classPrefix + 'overlay-play').style.display = '';
14208			}
14209
14210			if (t.created) {
14211				return;
14212			}
14213
14214			t.created = true;
14215			t.media = media;
14216			t.domNode = domNode;
14217
14218			if (!(_constants.IS_ANDROID && t.options.AndroidUseNativeControls) && !(_constants.IS_IPAD && t.options.iPadUseNativeControls) && !(_constants.IS_IPHONE && t.options.iPhoneUseNativeControls)) {
14219				if (!t.isVideo && !t.options.features.length && !t.options.useDefaultControls) {
14220					if (autoplay && isNative) {
14221						t.play();
14222					}
14223
14224					if (t.options.success) {
14225
14226						if (typeof t.options.success === 'string') {
14227							_window2.default[t.options.success](t.media, t.domNode, t);
14228						} else {
14229							t.options.success(t.media, t.domNode, t);
14230						}
14231					}
14232
14233					return;
14234				}
14235
14236				t.featurePosition = {};
14237
14238				t._setDefaultPlayer();
14239
14240				t.buildposter(t, t.getElement(t.controls), t.getElement(t.layers), t.media);
14241				t.buildkeyboard(t, t.getElement(t.controls), t.getElement(t.layers), t.media);
14242				t.buildoverlays(t, t.getElement(t.controls), t.getElement(t.layers), t.media);
14243
14244				if (t.options.useDefaultControls) {
14245					var defaultControls = ['playpause', 'current', 'progress', 'duration', 'tracks', 'volume', 'fullscreen'];
14246					t.options.features = defaultControls.concat(t.options.features.filter(function (item) {
14247						return defaultControls.indexOf(item) === -1;
14248					}));
14249				}
14250
14251				t.buildfeatures(t, t.getElement(t.controls), t.getElement(t.layers), t.media);
14252
14253				var event = (0, _general.createEvent)('controlsready', t.getElement(t.container));
14254				t.getElement(t.container).dispatchEvent(event);
14255
14256				t.setPlayerSize(t.width, t.height);
14257				t.setControlsSize();
14258
14259				if (t.isVideo) {
14260					t.clickToPlayPauseCallback = function () {
14261						if (t.options.clickToPlayPause) {
14262							var button = t.getElement(t.container).querySelector('.' + t.options.classPrefix + 'overlay-button'),
14263							    pressed = button.getAttribute('aria-pressed');
14264
14265							if (t.paused && pressed) {
14266								t.pause();
14267							} else if (t.paused) {
14268								t.play();
14269							} else {
14270								t.pause();
14271							}
14272
14273							button.setAttribute('aria-pressed', !pressed);
14274							t.getElement(t.container).focus();
14275						}
14276					};
14277
14278					t.createIframeLayer();
14279
14280					t.media.addEventListener('click', t.clickToPlayPauseCallback);
14281
14282					if ((_constants.IS_ANDROID || _constants.IS_IOS) && !t.options.alwaysShowControls) {
14283						t.node.addEventListener('touchstart', function () {
14284							if (t.controlsAreVisible) {
14285								t.hideControls(false);
14286							} else {
14287								if (t.controlsEnabled) {
14288									t.showControls(false);
14289								}
14290							}
14291						}, _constants.SUPPORT_PASSIVE_EVENT ? { passive: true } : false);
14292					} else {
14293						t.getElement(t.container).addEventListener('mouseenter', function () {
14294							if (t.controlsEnabled) {
14295								if (!t.options.alwaysShowControls) {
14296									t.killControlsTimer('enter');
14297									t.showControls();
14298									t.startControlsTimer(t.options.controlsTimeoutMouseEnter);
14299								}
14300							}
14301						});
14302						t.getElement(t.container).addEventListener('mousemove', function () {
14303							if (t.controlsEnabled) {
14304								if (!t.controlsAreVisible) {
14305									t.showControls();
14306								}
14307								if (!t.options.alwaysShowControls) {
14308									t.startControlsTimer(t.options.controlsTimeoutMouseEnter);
14309								}
14310							}
14311						});
14312						t.getElement(t.container).addEventListener('mouseleave', function () {
14313							if (t.controlsEnabled) {
14314								if (!t.paused && !t.options.alwaysShowControls) {
14315									t.startControlsTimer(t.options.controlsTimeoutMouseLeave);
14316								}
14317							}
14318						});
14319					}
14320
14321					if (t.options.hideVideoControlsOnLoad) {
14322						t.hideControls(false);
14323					}
14324
14325					if (t.options.enableAutosize) {
14326						t.media.addEventListener('loadedmetadata', function (e) {
14327							var target = e !== undefined ? e.detail.target || e.target : t.media;
14328							if (t.options.videoHeight <= 0 && !t.domNode.getAttribute('height') && !t.domNode.style.height && target !== null && !isNaN(target.videoHeight)) {
14329								t.setPlayerSize(target.videoWidth, target.videoHeight);
14330								t.setControlsSize();
14331								t.media.setSize(target.videoWidth, target.videoHeight);
14332							}
14333						});
14334					}
14335				}
14336
14337				t.media.addEventListener('play', function () {
14338					t.hasFocus = true;
14339
14340					for (var playerIndex in _mejs2.default.players) {
14341						if (_mejs2.default.players.hasOwnProperty(playerIndex)) {
14342							var p = _mejs2.default.players[playerIndex];
14343
14344							if (p.id !== t.id && t.options.pauseOtherPlayers && !p.paused && !p.ended && p.options.ignorePauseOtherPlayersOption !== true) {
14345								p.pause();
14346								p.hasFocus = false;
14347							}
14348						}
14349					}
14350
14351					if (!(_constants.IS_ANDROID || _constants.IS_IOS) && !t.options.alwaysShowControls && t.isVideo) {
14352						t.hideControls();
14353					}
14354				});
14355
14356				t.media.addEventListener('ended', function () {
14357					if (t.options.autoRewind) {
14358						try {
14359							t.setCurrentTime(0);
14360
14361							setTimeout(function () {
14362								var loadingElement = t.getElement(t.container).querySelector('.' + t.options.classPrefix + 'overlay-loading');
14363								if (loadingElement && loadingElement.parentNode) {
14364									loadingElement.parentNode.style.display = 'none';
14365								}
14366							}, 20);
14367						} catch (exp) {
14368							
14369						}
14370					}
14371
14372					t.pause();
14373
14374					if (t.setProgressRail) {
14375						t.setProgressRail();
14376					}
14377					if (t.setCurrentRail) {
14378						t.setCurrentRail();
14379					}
14380
14381					if (t.options.loop) {
14382						t.play();
14383					} else if (!t.options.alwaysShowControls && t.controlsEnabled) {
14384						t.showControls();
14385					}
14386				});
14387
14388				t.media.addEventListener('loadedmetadata', function () {
14389
14390					(0, _time.calculateTimeFormat)(t.getDuration(), t.options, t.options.framesPerSecond || 25);
14391
14392					if (t.updateDuration) {
14393						t.updateDuration();
14394					}
14395					if (t.updateCurrent) {
14396						t.updateCurrent();
14397					}
14398
14399					if (!t.isFullScreen) {
14400						t.setPlayerSize(t.width, t.height);
14401						t.setControlsSize();
14402					}
14403				});
14404
14405				var duration = null;
14406				t.media.addEventListener('timeupdate', function () {
14407					if (!isNaN(t.getDuration()) && duration !== t.getDuration()) {
14408						duration = t.getDuration();
14409						(0, _time.calculateTimeFormat)(duration, t.options, t.options.framesPerSecond || 25);
14410
14411						if (t.updateDuration) {
14412							t.updateDuration();
14413						}
14414						if (t.updateCurrent) {
14415							t.updateCurrent();
14416						}
14417
14418						t.setControlsSize();
14419					}
14420				});
14421
14422				if (t.options.enableKeyboard) {
14423					t.getElement(t.container).addEventListener('keydown', function (e) {
14424						var keyCode = e.keyCode || e.which || 0;
14425
14426						if (e.target === t.container && keyCode === 13) {
14427							if (t.paused) {
14428								t.play();
14429							} else {
14430								t.pause();
14431							}
14432						}
14433					});
14434				}
14435
14436				t.getElement(t.container).addEventListener('click', function (e) {
14437					dom.addClass(e.currentTarget, t.options.classPrefix + 'container-keyboard-inactive');
14438				});
14439
14440				t.getElement(t.container).addEventListener('focusin', function (e) {
14441					dom.removeClass(e.currentTarget, t.options.classPrefix + 'container-keyboard-inactive');
14442					if (t.isVideo && !_constants.IS_ANDROID && !_constants.IS_IOS && t.controlsEnabled && !t.options.alwaysShowControls) {
14443						t.killControlsTimer('enter');
14444						t.showControls();
14445						t.startControlsTimer(t.options.controlsTimeoutMouseEnter);
14446					}
14447				});
14448
14449				t.getElement(t.container).addEventListener('focusout', function (e) {
14450					setTimeout(function () {
14451						if (e.relatedTarget) {
14452							if (t.keyboardAction && !e.relatedTarget.closest('.' + t.options.classPrefix + 'container')) {
14453								t.keyboardAction = false;
14454								if (t.isVideo && !t.options.alwaysShowControls && !t.paused) {
14455									t.startControlsTimer(t.options.controlsTimeoutMouseLeave);
14456								}
14457							}
14458						}
14459					}, 0);
14460				});
14461
14462				setTimeout(function () {
14463					t.setPlayerSize(t.width, t.height);
14464					t.setControlsSize();
14465				}, 0);
14466
14467				t.globalResizeCallback = function () {
14468					if (
vendor: 11,265 bytes, lines 14468-14823
14468!(t.isFullScreen || _constants.HAS_TRUE_NATIVE_FULLSCREEN && _document2.default.webkitIsFullScreen)) {
14469						t.setPlayerSize(t.width, t.height);
14470					}
14471
14472					t.setControlsSize();
14473				};
14474
14475				t.globalBind('resize', t.globalResizeCallback);
14476			}
14477
14478			if (autoplay && isNative) {
14479				t.play();
14480			}
14481
14482			if (t.options.success) {
14483				if (typeof t.options.success === 'string') {
14484					_window2.default[t.options.success](t.media, t.domNode, t);
14485				} else {
14486					t.options.success(t.media, t.domNode, t);
14487				}
14488			}
14489		}
14490	}, {
14491		key: '_handleError',
14492		value: function _handleError(e, media, node) {
14493			var t = this,
14494			    play = t.getElement(t.layers).querySelector('.' + t.options.classPrefix + 'overlay-play');
14495
14496			if (play) {
14497				play.style.display = 'none';
14498			}
14499
14500			if (t.options.error) {
14501				t.options.error(e, media, node);
14502			}
14503
14504			if (t.getElement(t.container).querySelector('.' + t.options.classPrefix + 'cannotplay')) {
14505				t.getElement(t.container).querySelector('.' + t.options.classPrefix + 'cannotplay').remove();
14506			}
14507
14508			var errorContainer = _document2.default.createElement('div');
14509			errorContainer.className = t.options.classPrefix + 'cannotplay';
14510			errorContainer.style.width = '100%';
14511			errorContainer.style.height = '100%';
14512
14513			var errorContent = typeof t.options.customError === 'function' ? t.options.customError(t.media, t.media.originalNode) : t.options.customError,
14514			    imgError = '';
14515
14516			if (!errorContent) {
14517				var poster = t.media.originalNode.getAttribute('poster');
14518				if (poster) {
14519					imgError = '<img src="' + poster + '" alt="' + _mejs2.default.i18n.t('mejs.download-file') + '">';
14520				}
14521
14522				if (e.message) {
14523					errorContent = '<p>' + e.message + '</p>';
14524				}
14525
14526				if (e.urls) {
14527					for (var i = 0, total = e.urls.length; i < total; i++) {
14528						var url = e.urls[i];
14529						errorContent += '<a href="' + url.src + '" data-type="' + url.type + '"><span>' + _mejs2.default.i18n.t('mejs.download-file') + ': ' + url.src + '</span></a>';
14530					}
14531				}
14532			}
14533
14534			if (errorContent && t.getElement(t.layers).querySelector('.' + t.options.classPrefix + 'overlay-error')) {
14535				errorContainer.innerHTML = errorContent;
14536				t.getElement(t.layers).querySelector('.' + t.options.classPrefix + 'overlay-error').innerHTML = '' + imgError + errorContainer.outerHTML;
14537				t.getElement(t.layers).querySelector('.' + t.options.classPrefix + 'overlay-error').parentNode.style.display = 'block';
14538			}
14539
14540			if (t.controlsEnabled) {
14541				t.disableControls();
14542			}
14543		}
14544	}, {
14545		key: 'setPlayerSize',
14546		value: function setPlayerSize(width, height) {
14547			var t = this;
14548
14549			if (!t.options.setDimensions) {
14550				return false;
14551			}
14552
14553			if (typeof width !== 'undefined') {
14554				t.width = width;
14555			}
14556
14557			if (typeof height !== 'undefined') {
14558				t.height = height;
14559			}
14560
14561			switch (t.options.stretching) {
14562				case 'fill':
14563					if (t.isVideo) {
14564						t.setFillMode();
14565					} else {
14566						t.setDimensions(t.width, t.height);
14567					}
14568					break;
14569				case 'responsive':
14570					t.setResponsiveMode();
14571					break;
14572				case 'none':
14573					t.setDimensions(t.width, t.height);
14574					break;
14575
14576				default:
14577					if (t.hasFluidMode() === true) {
14578						t.setResponsiveMode();
14579					} else {
14580						t.setDimensions(t.width, t.height);
14581					}
14582					break;
14583			}
14584		}
14585	}, {
14586		key: 'hasFluidMode',
14587		value: function hasFluidMode() {
14588			var t = this;
14589
14590			return t.height.toString().indexOf('%') !== -1 || t.node && t.node.style.maxWidth && t.node.style.maxWidth !== 'none' && t.node.style.maxWidth !== t.width || t.node && t.node.currentStyle && t.node.currentStyle.maxWidth === '100%';
14591		}
14592	}, {
14593		key: 'setResponsiveMode',
14594		value: function setResponsiveMode() {
14595			var t = this,
14596			    parent = function () {
14597				var parentEl = void 0,
14598				    el = t.getElement(t.container);
14599
14600				while (el) {
14601					try {
14602						if (_constants.IS_FIREFOX && el.tagName.toLowerCase() === 'html' && _window2.default.self !== _window2.default.top && _window2.default.frameElement !== null) {
14603							return _window2.default.frameElement;
14604						} else {
14605							parentEl = el.parentElement;
14606						}
14607					} catch (e) {
14608						parentEl = el.parentElement;
14609					}
14610
14611					if (parentEl && dom.visible(parentEl)) {
14612						return parentEl;
14613					}
14614					el = parentEl;
14615				}
14616				return null;
14617			}(),
14618			    parentStyles = parent ? getComputedStyle(parent, null) : getComputedStyle(_document2.default.body, null),
14619			    nativeWidth = function () {
14620				if (t.isVideo) {
14621					if (t.node.videoWidth && t.node.videoWidth > 0) {
14622						return t.node.videoWidth;
14623					} else if (t.node.getAttribute('width')) {
14624						return t.node.getAttribute('width');
14625					} else {
14626						return t.options.defaultVideoWidth;
14627					}
14628				} else {
14629					return t.options.defaultAudioWidth;
14630				}
14631			}(),
14632			    nativeHeight = function () {
14633				if (t.isVideo) {
14634					if (t.node.videoHeight && t.node.videoHeight > 0) {
14635						return t.node.videoHeight;
14636					} else if (t.node.getAttribute('height')) {
14637						return t.node.getAttribute('height');
14638					} else {
14639						return t.options.defaultVideoHeight;
14640					}
14641				} else {
14642					return t.options.defaultAudioHeight;
14643				}
14644			}(),
14645			    aspectRatio = function () {
14646				if (!t.options.enableAutosize) {
14647					return t.initialAspectRatio;
14648				}
14649				var ratio = 1;
14650				if (!t.isVideo) {
14651					return ratio;
14652				}
14653
14654				if (t.node.videoWidth && t.node.videoWidth > 0 && t.node.videoHeight && t.node.videoHeight > 0) {
14655					ratio = t.height >= t.width ? t.node.videoWidth / t.node.videoHeight : t.node.videoHeight / t.node.videoWidth;
14656				} else {
14657					ratio = t.initialAspectRatio;
14658				}
14659
14660				if (isNaN(ratio) || ratio < 0.01 || ratio > 100) {
14661					ratio = 1;
14662				}
14663				return ratio;
14664			}(),
14665			    parentHeight = parseFloat(parentStyles.height);
14666
14667			var newHeight = void 0,
14668			    parentWidth = parseFloat(parentStyles.width);
14669
14670			if (t.isVideo) {
14671				if (t.height === '100%' && t.width === '100%') {
14672					newHeight = parentHeight;
14673				} else if (t.height === '100%') {
14674					newHeight = parseFloat(parentWidth * nativeHeight / nativeWidth, 10);
14675				} else {
14676					newHeight = t.height >= t.width ? parseFloat(parentWidth / aspectRatio, 10) : parseFloat(parentWidth * aspectRatio, 10);
14677				}
14678			} else {
14679				newHeight = nativeHeight;
14680			}
14681
14682			if (newHeight <= t.container.querySelector('.' + t.options.classPrefix + 'inner').offsetHeight) {
14683				newHeight = t.container.querySelector('.' + t.options.classPrefix + 'inner').offsetHeight;
14684			}
14685
14686			if (isNaN(newHeight)) {
14687				newHeight = parentHeight;
14688			}
14689
14690			if (t.getElement(t.container).parentNode.length > 0 && t.getElement(t.container).parentNode.tagName.toLowerCase() === 'body') {
14691				parentWidth = _window2.default.innerWidth || _document2.default.documentElement.clientWidth || _document2.default.body.clientWidth;
14692				newHeight = _window2.default.innerHeight || _document2.default.documentElement.clientHeight || _document2.default.body.clientHeight;
14693			}
14694
14695			if (newHeight && parentWidth) {
14696				t.getElement(t.container).style.width = parentWidth + 'px';
14697				t.getElement(t.container).style.height = newHeight + 'px';
14698
14699				t.node.style.width = '100%';
14700				t.node.style.height = '100%';
14701
14702				if (t.isVideo && t.media.setSize) {
14703					t.media.setSize(parentWidth, newHeight);
14704				}
14705
14706				if (newHeight <= t.container.querySelector('.' + t.options.classPrefix + 'inner').offsetHeight) {
14707					t.node.style.width = 'auto';
14708					t.node.style.height = 'auto';
14709				}
14710
14711				var layerChildren = t.getElement(t.layers).children;
14712				for (var i = 0, total = layerChildren.length; i < total; i++) {
14713					layerChildren[i].style.width = '100%';
14714					layerChildren[i].style.height = '100%';
14715				}
14716			}
14717		}
14718	}, {
14719		key: 'setFillMode',
14720		value: function setFillMode() {
14721			var t = this;
14722			var isIframe = _window2.default.self !== _window2.default.top && _window2.default.frameElement !== null;
14723			var parent = function () {
14724				var parentEl = void 0,
14725				    el = t.getElement(t.container);
14726
14727				while (el) {
14728					try {
14729						if (_constants.IS_FIREFOX && el.tagName.toLowerCase() === 'html' && _window2.default.self !== _window2.default.top && _window2.default.frameElement !== null) {
14730							return _window2.default.frameElement;
14731						} else {
14732							parentEl = el.parentElement;
14733						}
14734					} catch (e) {
14735						parentEl = el.parentElement;
14736					}
14737
14738					if (parentEl && dom.visible(parentEl)) {
14739						return parentEl;
14740					}
14741					el = parentEl;
14742				}
14743
14744				return null;
14745			}();
14746			var parentStyles = parent ? getComputedStyle(parent, null) : getComputedStyle(_document2.default.body, null);
14747
14748			if (t.node.style.height !== 'none' && t.node.style.height !== t.height) {
14749				t.node.style.height = 'auto';
14750			}
14751			if (t.node.style.maxWidth !== 'none' && t.node.style.maxWidth !== t.width) {
14752				t.node.style.maxWidth = 'none';
14753			}
14754
14755			if (t.node.style.maxHeight !== 'none' && t.node.style.maxHeight !== t.height) {
14756				t.node.style.maxHeight = 'none';
14757			}
14758
14759			if (t.node.currentStyle) {
14760				if (t.node.currentStyle.height === '100%') {
14761					t.node.currentStyle.height = 'auto';
14762				}
14763				if (t.node.currentStyle.maxWidth === '100%') {
14764					t.node.currentStyle.maxWidth = 'none';
14765				}
14766				if (t.node.currentStyle.maxHeight === '100%') {
14767					t.node.currentStyle.maxHeight = 'none';
14768				}
14769			}
14770
14771			if (!isIframe && !parseFloat(parentStyles.width)) {
14772				parent.style.width = t.media.offsetWidth + 'px';
14773			}
14774
14775			if (!isIframe && !parseFloat(parentStyles.height)) {
14776				parent.style.height = t.media.offsetHeight + 'px';
14777			}
14778
14779			parentStyles = getComputedStyle(parent);
14780
14781			var parentWidth = parseFloat(parentStyles.width),
14782			    parentHeight = parseFloat(parentStyles.height);
14783			t.setDimensions('100%', '100%');
14784
14785			var poster = t.getElement(t.container).querySelector('.' + t.options.classPrefix + 'poster>img');
14786			if (poster) {
14787				poster.style.display = '';
14788			}
14789
14790			var targetElement = t.getElement(t.container).querySelectorAll('object, embed, iframe, video'),
14791			    initHeight = parseFloat(t.height, 10),
14792			    initWidth = parseFloat(t.width, 10),
14793			    scaleX1 = parentWidth,
14794			    scaleY1 = initHeight * parentWidth / initWidth,
14795			    scaleX2 = initWidth * parentHeight / initHeight,
14796			    scaleY2 = parentHeight,
14797			    bScaleOnWidth = scaleX2 > parentWidth === false,
14798			    finalWidth = bScaleOnWidth ? Math.floor(scaleX1) : Math.floor(scaleX2),
14799			    finalHeight = bScaleOnWidth ? Math.floor(scaleY1) : Math.floor(scaleY2),
14800			    width = bScaleOnWidth ? parentWidth + 'px' : finalWidth + 'px',
14801			    height = bScaleOnWidth ? finalHeight + 'px' : parentHeight + 'px';
14802
14803			for (var i = 0, total = targetElement.length; i < total; i++) {
14804				targetElement[i].style.height = height;
14805				targetElement[i].style.width = width;
14806				if (t.media.setSize) {
14807					t.media.setSize(width, height);
14808				}
14809
14810				targetElement[i].style.marginLeft = Math.floor((parentWidth - finalWidth) / 2) + 'px';
14811				targetElement[i].style.marginTop = 0;
14812			}
14813		}
14814	}, {
14815		key: 'setDimensions',
14816		value: function setDimensions(width, height) {
14817			var t = this;
14818
14819			width = (0, _general.isString)(width) && width.indexOf('%') > -1 ? width : parseFloat(width) + 'px';
14820			height = (0, _general.isString)(height) && height.indexOf('%') > -1 ? height : parseFloat(height) + 'px';
14821
14822			t.getElement(t.container).style.width = width;
14823			t.getElement(t.container).style.height = height;
vendor: 51,091 bytes, lines 14823-16701
14823
14824
14825			var layers = t.getElement(t.layers).children;
14826			for (var i = 0, total = layers.length; i < total; i++) {
14827				layers[i].style.width = width;
14828				layers[i].style.height = height;
14829			}
14830		}
14831	}, {
14832		key: 'setControlsSize',
14833		value: function setControlsSize() {
14834			var t = this;
14835
14836			if (!dom.visible(t.getElement(t.container))) {
14837				return;
14838			}
14839
14840			if (!(t.rail && dom.visible(t.rail))) {
14841				var children = t.getElement(t.controls).children;
14842				var minWidth = 0;
14843
14844				for (var i = 0, total = children.length; i < total; i++) {
14845					minWidth += children[i].offsetWidth;
14846				}
14847
14848				t.getElement(t.container).style.minWidth = minWidth + 'px';
14849			}
14850		}
14851	}, {
14852		key: 'addControlElement',
14853		value: function addControlElement(element, key) {
14854			var t = this;
14855
14856			if (t.featurePosition[key] !== undefined) {
14857				var child = t.getElement(t.controls).children[t.featurePosition[key] - 1];
14858				child.parentNode.insertBefore(element, child.nextSibling);
14859			} else {
14860				t.getElement(t.controls).appendChild(element);
14861				var children = t.getElement(t.controls).children;
14862				for (var i = 0, total = children.length; i < total; i++) {
14863					if (element === children[i]) {
14864						t.featurePosition[key] = i;
14865						break;
14866					}
14867				}
14868			}
14869		}
14870	}, {
14871		key: 'createIframeLayer',
14872		value: function createIframeLayer() {
14873			var t = this;
14874
14875			if (t.isVideo && t.media.rendererName !== null && t.media.rendererName.indexOf('iframe') > -1 && !_document2.default.getElementById(t.media.id + '-iframe-overlay')) {
14876
14877				var layer = _document2.default.createElement('div'),
14878				    target = _document2.default.getElementById(t.media.id + '_' + t.media.rendererName);
14879
14880				layer.id = t.media.id + '-iframe-overlay';
14881				layer.className = t.options.classPrefix + 'iframe-overlay';
14882				layer.addEventListener('click', function (e) {
14883					if (t.options.clickToPlayPause) {
14884						if (t.paused) {
14885							t.play();
14886						} else {
14887							t.pause();
14888						}
14889
14890						e.preventDefault();
14891						e.stopPropagation();
14892					}
14893				});
14894
14895				target.parentNode.insertBefore(layer, target);
14896			}
14897		}
14898	}, {
14899		key: 'resetSize',
14900		value: function resetSize() {
14901			var t = this;
14902
14903			setTimeout(function () {
14904				t.setPlayerSize(t.width, t.height);
14905				t.setControlsSize();
14906			}, 50);
14907		}
14908	}, {
14909		key: 'setPoster',
14910		value: function setPoster(url) {
14911			var t = this;
14912
14913			if (t.getElement(t.container)) {
14914				var posterDiv = t.getElement(t.container).querySelector('.' + t.options.classPrefix + 'poster');
14915
14916				if (!posterDiv) {
14917					posterDiv = _document2.default.createElement('div');
14918					posterDiv.className = t.options.classPrefix + 'poster ' + t.options.classPrefix + 'layer';
14919					t.getElement(t.layers).appendChild(posterDiv);
14920				}
14921
14922				var posterImg = posterDiv.querySelector('img');
14923
14924				if (!posterImg && url) {
14925					posterImg = _document2.default.createElement('img');
14926					posterImg.alt = '';
14927					posterImg.className = t.options.classPrefix + 'poster-img';
14928					posterImg.width = '100%';
14929					posterImg.height = '100%';
14930					posterDiv.style.display = '';
14931					posterDiv.appendChild(posterImg);
14932				}
14933
14934				if (url) {
14935					posterImg.setAttribute('src', url);
14936					posterDiv.style.backgroundImage = 'url("' + url + '")';
14937					posterDiv.style.display = '';
14938				} else if (posterImg) {
14939					posterDiv.style.backgroundImage = 'none';
14940					posterDiv.style.display = 'none';
14941					posterImg.remove();
14942				} else {
14943					posterDiv.style.display = 'none';
14944				}
14945			} else if (_constants.IS_IPAD && t.options.iPadUseNativeControls || _constants.IS_IPHONE && t.options.iPhoneUseNativeControls || _constants.IS_ANDROID && t.options.AndroidUseNativeControls) {
14946				t.media.originalNode.poster = url;
14947			}
14948		}
14949	}, {
14950		key: 'changeSkin',
14951		value: function changeSkin(className) {
14952			var t = this;
14953
14954			t.getElement(t.container).className = t.options.classPrefix + 'container ' + className;
14955			t.setPlayerSize(t.width, t.height);
14956			t.setControlsSize();
14957		}
14958	}, {
14959		key: 'globalBind',
14960		value: function globalBind(events, callback) {
14961			var t = this,
14962			    doc = t.node ? t.node.ownerDocument : _document2.default;
14963
14964			events = (0, _general.splitEvents)(events, t.id);
14965			if (events.d) {
14966				var eventList = events.d.split(' ');
14967				for (var i = 0, total = eventList.length; i < total; i++) {
14968					eventList[i].split('.').reduce(function (part, e) {
14969						doc.addEventListener(e, callback, false);
14970						return e;
14971					}, '');
14972				}
14973			}
14974			if (events.w) {
14975				var _eventList = events.w.split(' ');
14976				for (var _i = 0, _total = _eventList.length; _i < _total; _i++) {
14977					_eventList[_i].split('.').reduce(function (part, e) {
14978						_window2.default.addEventListener(e, callback, false);
14979						return e;
14980					}, '');
14981				}
14982			}
14983		}
14984	}, {
14985		key: 'globalUnbind',
14986		value: function globalUnbind(events, callback) {
14987			var t = this,
14988			    doc = t.node ? t.node.ownerDocument : _document2.default;
14989
14990			events = (0, _general.splitEvents)(events, t.id);
14991			if (events.d) {
14992				var eventList = events.d.split(' ');
14993				for (var i = 0, total = eventList.length; i < total; i++) {
14994					eventList[i].split('.').reduce(function (part, e) {
14995						doc.removeEventListener(e, callback, false);
14996						return e;
14997					}, '');
14998				}
14999			}
15000			if (events.w) {
15001				var _eventList2 = events.w.split(' ');
15002				for (var _i2 = 0, _total2 = _eventList2.length; _i2 < _total2; _i2++) {
15003					_eventList2[_i2].split('.').reduce(function (part, e) {
15004						_window2.default.removeEventListener(e, callback, false);
15005						return e;
15006					}, '');
15007				}
15008			}
15009		}
15010	}, {
15011		key: 'buildfeatures',
15012		value: function buildfeatures(player, controls, layers, media) {
15013			var t = this;
15014
15015			for (var i = 0, total = t.options.features.length; i < total; i++) {
15016				var feature = t.options.features[i];
15017				if (t['build' + feature]) {
15018					try {
15019						t['build' + feature](player, controls, layers, media);
15020					} catch (e) {
15021						console.error('error building ' + feature, e);
15022					}
15023				}
15024			}
15025		}
15026	}, {
15027		key: 'buildposter',
15028		value: function buildposter(player, controls, layers, media) {
15029			var t = this,
15030			    poster = _document2.default.createElement('div');
15031
15032			poster.className = t.options.classPrefix + 'poster ' + t.options.classPrefix + 'layer';
15033			layers.appendChild(poster);
15034
15035			var posterUrl = media.originalNode.getAttribute('poster');
15036
15037			if (player.options.poster !== '') {
15038				if (posterUrl && _constants.IS_IOS) {
15039					media.originalNode.removeAttribute('poster');
15040				}
15041				posterUrl = player.options.poster;
15042			}
15043
15044			if (posterUrl) {
15045				t.setPoster(posterUrl);
15046			} else if (t.media.renderer !== null && typeof t.media.renderer.getPosterUrl === 'function') {
15047				t.setPoster(t.media.renderer.getPosterUrl());
15048			} else {
15049				poster.style.display = 'none';
15050			}
15051
15052			media.addEventListener('play', function () {
15053				poster.style.display = 'none';
15054			});
15055
15056			media.addEventListener('playing', function () {
15057				poster.style.display = 'none';
15058			});
15059
15060			if (player.options.showPosterWhenEnded && player.options.autoRewind) {
15061				media.addEventListener('ended', function () {
15062					poster.style.display = '';
15063				});
15064			}
15065
15066			media.addEventListener('error', function () {
15067				poster.style.display = 'none';
15068			});
15069
15070			if (player.options.showPosterWhenPaused) {
15071				media.addEventListener('pause', function () {
15072					if (!player.ended) {
15073						poster.style.display = '';
15074					}
15075				});
15076			}
15077		}
15078	}, {
15079		key: 'buildoverlays',
15080		value: function buildoverlays(player, controls, layers, media) {
15081			if (!player.isVideo) {
15082				return;
15083			}
15084
15085			var t = this,
15086			    loading = _document2.default.createElement('div'),
15087			    error = _document2.default.createElement('div'),
15088			    bigPlay = _document2.default.createElement('div');
15089
15090			loading.style.display = 'none';
15091			loading.className = t.options.classPrefix + 'overlay ' + t.options.classPrefix + 'layer';
15092			loading.innerHTML = '<div class="' + t.options.classPrefix + 'overlay-loading">' + ('<div class="' + t.options.classPrefix + 'overlay-loading-bg-img">\n\t\t\t\t\t<svg xmlns="http://www.w3.org/2000/svg">\n\t\t\t\t\t\t<use xlink:href="' + t.media.options.iconSprite + '#icon-loading-spinner"></use>\n\t\t\t\t\t</svg>\n\t\t\t\t</div>') + '</div>';
15093			layers.appendChild(loading);
15094
15095			error.style.display = 'none';
15096			error.className = t.options.classPrefix + 'overlay ' + t.options.classPrefix + 'layer';
15097			error.innerHTML = '<div class="' + t.options.classPrefix + 'overlay-error"></div>';
15098			layers.appendChild(error);
15099
15100			bigPlay.className = t.options.classPrefix + 'overlay ' + t.options.classPrefix + 'layer ' + t.options.classPrefix + 'overlay-play';
15101			bigPlay.innerHTML = (0, _generate.generateControlButton)(t.id, _i18n2.default.t('mejs.play'), _i18n2.default.t('mejs.play'), '' + t.media.options.iconSprite, ['icon-overlay-play'], '' + t.options.classPrefix, t.options.classPrefix + 'overlay-button', '', false);
15102
15103			bigPlay.addEventListener('click', function () {
15104				if (t.options.clickToPlayPause) {
15105
15106					var button = t.getElement(t.container).querySelector('.' + t.options.classPrefix + 'overlay-button'),
15107					    pressed = button.getAttribute('aria-pressed');
15108
15109					if (t.paused) {
15110						t.play();
15111					} else {
15112						t.pause();
15113					}
15114
15115					button.setAttribute('aria-pressed', !!pressed);
15116					t.getElement(t.container).focus();
15117				}
15118			});
15119
15120			layers.appendChild(bigPlay);
15121
15122			if (t.media.rendererName !== null && (/(youtube|facebook)/i.test(t.media.rendererName) && !(t.media.originalNode.getAttribute('poster') || player.options.poster || typeof t.media.renderer.getPosterUrl === 'function' && t.media.renderer.getPosterUrl()) || _constants.IS_STOCK_ANDROID || t.media.originalNode.getAttribute('autoplay'))) {
15123				bigPlay.style.display = 'none';
15124			}
15125
15126			var hasError = false;
15127
15128			media.addEventListener('play', function () {
15129				bigPlay.style.display = 'none';
15130				loading.style.display = 'none';
15131				error.style.display = 'none';
15132				hasError = false;
15133			});
15134			media.addEventListener('playing', function () {
15135				bigPlay.style.display = 'none';
15136				loading.style.display = 'none';
15137				error.style.display = 'none';
15138				hasError = false;
15139			});
15140			media.addEventListener('seeking', function () {
15141				bigPlay.style.display = 'none';
15142				loading.style.display = '';
15143				hasError = false;
15144			});
15145			media.addEventListener('seeked', function () {
15146				bigPlay.style.display = t.paused && !_constants.IS_STOCK_ANDROID ? '' : 'none';
15147				loading.style.display = 'none';
15148				hasError = false;
15149			});
15150			media.addEventListener('pause', function () {
15151				loading.style.display = 'none';
15152				if (!_constants.IS_STOCK_ANDROID && !hasError) {
15153					bigPlay.style.display = '';
15154				}
15155				hasError = false;
15156			});
15157			media.addEventListener('waiting', function () {
15158				loading.style.display = '';
15159				hasError = false;
15160			});
15161
15162			media.addEventListener('loadeddata', function () {
15163				loading.style.display = '';
15164
15165				if (_constants.IS_ANDROID) {
15166					media.canplayTimeout = setTimeout(function () {
15167						if (_document2.default.createEvent) {
15168							var evt = _document2.default.createEvent('HTMLEvents');
15169							evt.initEvent('canplay', true, true);
15170							return media.dispatchEvent(evt);
15171						}
15172					}, 300);
15173				}
15174				hasError = false;
15175			});
15176			media.addEventListener('canplay', function () {
15177				loading.style.display = 'none';
15178
15179				clearTimeout(media.canplayTimeout);
15180				hasError = false;
15181			});
15182
15183			media.addEventListener('error', function (e) {
15184				t._handleError(e, t.media, t.node);
15185				loading.style.display = 'none';
15186				bigPlay.style.display = 'none';
15187				hasError = true;
15188			});
15189
15190			media.addEventListener('loadedmetadata', function () {
15191				if (!t.controlsEnabled) {
15192					t.enableControls();
15193				}
15194			});
15195
15196			media.addEventListener('keydown', function (e) {
15197				t.onkeydown(player, media, e);
15198				hasError = false;
15199			});
15200		}
15201	}, {
15202		key: 'buildkeyboard',
15203		value: function buildkeyboard(player, controls, layers, media) {
15204			var t = this;
15205
15206			t.getElement(t.container).addEventListener('keydown', function () {
15207				t.keyboardAction = true;
15208			});
15209
15210			t.globalKeydownCallback = function (event) {
15211				if (!_document2.default.activeElement) {
15212					return true;
15213				}
15214
15215				var container = _document2.default.activeElement.closest('.' + t.options.classPrefix + 'container'),
15216				    target = t.media.closest('.' + t.options.classPrefix + 'container');
15217				t.hasFocus = !!(container && target && container.id === target.id);
15218				return t.onkeydown(player, media, event);
15219			};
15220
15221			t.globalClickCallback = function (event) {
15222				t.hasFocus = !!event.target.closest('.' + t.options.classPrefix + 'container');
15223			};
15224
15225			t.globalBind('keydown', t.globalKeydownCallback);
15226
15227			t.globalBind('click', t.globalClickCallback);
15228		}
15229	}, {
15230		key: 'onkeydown',
15231		value: function onkeydown(player, media, e) {
15232			if (player.hasFocus && player.options.enableKeyboard) {
15233				for (var i = 0, total = player.options.keyActions.length; i < total; i++) {
15234					var keyAction = player.options.keyActions[i];
15235
15236					for (var j = 0, jl = keyAction.keys.length; j < jl; j++) {
15237						if (e.keyCode === keyAction.keys[j]) {
15238							keyAction.action(player, media, e.keyCode, e);
15239							e.preventDefault();
15240							e.stopPropagation();
15241							return;
15242						}
15243					}
15244				}
15245			}
15246
15247			return true;
15248		}
15249	}, {
15250		key: 'play',
15251		value: function play() {
15252			return this.proxy.play();
15253		}
15254	}, {
15255		key: 'pause',
15256		value: function pause() {
15257			return this.proxy.pause();
15258		}
15259	}, {
15260		key: 'load',
15261		value: function load() {
15262			return this.proxy.load();
15263		}
15264	}, {
15265		key: 'setCurrentTime',
15266		value: function setCurrentTime(time) {
15267			var userInteraction = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
15268
15269			this.seekUserInteraction = userInteraction;
15270			this.proxy.setCurrentTime(time);
15271		}
15272	}, {
15273		key: 'getCurrentTime',
15274		value: function getCurrentTime() {
15275			return this.proxy.currentTime;
15276		}
15277	}, {
15278		key: 'getDuration',
15279		value: function getDuration() {
15280			return this.proxy.duration;
15281		}
15282	}, {
15283		key: 'setVolume',
15284		value: function setVolume(volume) {
15285			this.proxy.volume = volume;
15286		}
15287	}, {
15288		key: 'getVolume',
15289		value: function getVolume() {
15290			return this.proxy.getVolume();
15291		}
15292	}, {
15293		key: 'setMuted',
15294		value: function setMuted(value) {
15295			this.proxy.setMuted(value);
15296		}
15297	}, {
15298		key: 'setSrc',
15299		value: function setSrc(src) {
15300			if (!this.controlsEnabled) {
15301				this.enableControls();
15302			}
15303			this.proxy.setSrc(src);
15304		}
15305	}, {
15306		key: 'getSrc',
15307		value: function getSrc() {
15308			return this.proxy.getSrc();
15309		}
15310	}, {
15311		key: 'canPlayType',
15312		value: function canPlayType(type) {
15313			return this.proxy.canPlayType(type);
15314		}
15315	}, {
15316		key: 'remove',
15317		value: function remove() {
15318			var t = this,
15319			    rendererName = t.media.rendererName,
15320			    src = t.media.originalNode.src;
15321
15322			for (var featureIndex in t.options.features) {
15323				var feature = t.options.features[featureIndex];
15324				if (t['clean' + feature]) {
15325					try {
15326						t['clean' + feature](t, t.getElement(t.layers), t.getElement(t.controls), t.media);
15327					} catch (e) {
15328						console.error('error cleaning ' + feature, e);
15329					}
15330				}
15331			}
15332
15333			var nativeWidth = t.node.getAttribute('width'),
15334			    nativeHeight = t.node.getAttribute('height');
15335
15336			if (nativeWidth) {
15337				if (nativeWidth.indexOf('%') === -1) {
15338					nativeWidth = nativeWidth + 'px';
15339				}
15340			} else {
15341				nativeWidth = 'auto';
15342			}
15343
15344			if (nativeHeight) {
15345				if (nativeHeight.indexOf('%') === -1) {
15346					nativeHeight = nativeHeight + 'px';
15347				}
15348			} else {
15349				nativeHeight = 'auto';
15350			}
15351
15352			t.node.style.width = nativeWidth;
15353			t.node.style.height = nativeHeight;
15354
15355			t.setPlayerSize(0, 0);
15356
15357			if (!t.isDynamic) {
15358				(function () {
15359					t.node.setAttribute('controls', true);
15360					t.node.setAttribute('id', t.node.getAttribute('id').replace('_' + rendererName, '').replace('_from_mejs', ''));
15361					var poster = t.getElement(t.container).querySelector('.' + t.options.classPrefix + 'poster>img');
15362					if (poster) {
15363						t.node.setAttribute('poster', poster.src);
15364					}
15365
15366					delete t.node.autoplay;
15367
15368					t.node.setAttribute('src', '');
15369					if (t.media.canPlayType((0, _media.getTypeFromFile)(src)) !== '') {
15370						t.node.setAttribute('src', src);
15371					}
15372
15373					if (rendererName && rendererName.indexOf('iframe') > -1) {
15374						var layer = _document2.default.getElementById(t.media.id + '-iframe-overlay');
15375						layer.remove();
15376					}
15377
15378					var node = t.node.cloneNode();
15379					node.style.display = '';
15380					t.getElement(t.container).parentNode.insertBefore(node, t.getElement(t.container));
15381					t.node.remove();
15382
15383					if (t.mediaFiles) {
15384						for (var i = 0, total = t.mediaFiles.length; i < total; i++) {
15385							var source = _document2.default.createElement('source');
15386							source.setAttribute('src', t.mediaFiles[i].src);
15387							source.setAttribute('type', t.mediaFiles[i].type);
15388							node.appendChild(source);
15389						}
15390					}
15391					if (t.trackFiles) {
15392						var _loop3 = function _loop3(_i3, _total3) {
15393							var track = t.trackFiles[_i3];
15394							var newTrack = _document2.default.createElement('track');
15395							newTrack.kind = track.kind;
15396							newTrack.label = track.label;
15397							newTrack.srclang = track.srclang;
15398							newTrack.src = track.src;
15399
15400							node.appendChild(newTrack);
15401							newTrack.addEventListener('load', function () {
15402								this.mode = 'showing';
15403								node.textTracks[_i3].mode = 'showing';
15404							});
15405						};
15406
15407						for (var _i3 = 0, _total3 = t.trackFiles.length; _i3 < _total3; _i3++) {
15408							_loop3(_i3, _total3);
15409						}
15410					}
15411
15412					delete t.node;
15413					delete t.mediaFiles;
15414					delete t.trackFiles;
15415				})();
15416			} else {
15417				t.getElement(t.container).parentNode.insertBefore(t.node, t.getElement(t.container));
15418			}
15419
15420			if (t.media.renderer && typeof t.media.renderer.destroy === 'function') {
15421				t.media.renderer.destroy();
15422			}
15423
15424			if (_typeof(t.getElement(t.container)) === 'object') {
15425				var offscreen = t.getElement(t.container).parentNode.querySelector('.' + t.options.classPrefix + 'offscreen');
15426				if (offscreen) {
15427					offscreen.remove();
15428				}
15429				t.getElement(t.container).remove();
15430			}
15431			t.globalUnbind('resize', t.globalResizeCallback);
15432			t.globalUnbind('keydown', t.globalKeydownCallback);
15433			t.globalUnbind('click', t.globalClickCallback);
15434
15435			delete t.media.player;
15436		}
15437	}, {
15438		key: 'paused',
15439		get: function get() {
15440			return this.proxy.paused;
15441		}
15442	}, {
15443		key: 'muted',
15444		get: function get() {
15445			return this.proxy.muted;
15446		},
15447		set: function set(muted) {
15448			this.setMuted(muted);
15449		}
15450	}, {
15451		key: 'ended',
15452		get: function get() {
15453			return this.proxy.ended;
15454		}
15455	}, {
15456		key: 'readyState',
15457		get: function get() {
15458			return this.proxy.readyState;
15459		}
15460	}, {
15461		key: 'currentTime',
15462		set: function set(time) {
15463			this.setCurrentTime(time);
15464		},
15465		get: function get() {
15466			return this.getCurrentTime();
15467		}
15468	}, {
15469		key: 'duration',
15470		get: function get() {
15471			return this.getDuration();
15472		}
15473	}, {
15474		key: 'volume',
15475		set: function set(volume) {
15476			this.setVolume(volume);
15477		},
15478		get: function get() {
15479			return this.getVolume();
15480		}
15481	}, {
15482		key: 'src',
15483		set: function set(src) {
15484			this.setSrc(src);
15485		},
15486		get: function get() {
15487			return this.getSrc();
15488		}
15489	}]);
15490
15491	return MediaElementPlayer;
15492}();
15493
15494_window2.default.MediaElementPlayer = MediaElementPlayer;
15495_mejs2.default.MediaElementPlayer = MediaElementPlayer;
15496
15497exports.default = MediaElementPlayer;
15498
15499},{"18":18,"2":2,"24":24,"25":25,"26":26,"27":27,"28":28,"3":3,"30":30,"6":6,"7":7,"8":8}],18:[function(_dereq_,module,exports){
15500'use strict';
15501
15502Object.defineProperty(exports, "__esModule", {
15503	value: true
15504});
15505
15506var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
15507
15508var _window = _dereq_(3);
15509
15510var _window2 = _interopRequireDefault(_window);
15511
15512function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
15513
15514function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
15515
15516var DefaultPlayer = function () {
15517	function DefaultPlayer(player) {
15518		_classCallCheck(this, DefaultPlayer);
15519
15520		this.media = player.media;
15521		this.isVideo = player.isVideo;
15522		this.classPrefix = player.options.classPrefix;
15523		this.createIframeLayer = function () {
15524			return player.createIframeLayer();
15525		};
15526		this.setPoster = function (url) {
15527			return player.setPoster(url);
15528		};
15529		return this;
15530	}
15531
15532	_createClass(DefaultPlayer, [{
15533		key: 'play',
15534		value: function play() {
15535			return this.media.play();
15536		}
15537	}, {
15538		key: 'pause',
15539		value: function pause() {
15540			return this.media.pause();
15541		}
15542	}, {
15543		key: 'load',
15544		value: function load() {
15545			var t = this;
15546
15547			if (!t.isLoaded) {
15548				t.media.load();
15549			}
15550
15551			t.isLoaded = true;
15552		}
15553	}, {
15554		key: 'setCurrentTime',
15555		value: function setCurrentTime(time) {
15556			this.media.setCurrentTime(time);
15557		}
15558	}, {
15559		key: 'getCurrentTime',
15560		value: function getCurrentTime() {
15561			return this.media.currentTime;
15562		}
15563	}, {
15564		key: 'getDuration',
15565		value: function getDuration() {
15566			var duration = this.media.getDuration();
15567			if (duration === Infinity && this.media.seekable && this.media.seekable.length) {
15568				duration = this.media.seekable.end(0);
15569			}
15570			return duration;
15571		}
15572	}, {
15573		key: 'setVolume',
15574		value: function setVolume(volume) {
15575			this.media.setVolume(volume);
15576		}
15577	}, {
15578		key: 'getVolume',
15579		value: function getVolume() {
15580			return this.media.getVolume();
15581		}
15582	}, {
15583		key: 'setMuted',
15584		value: function setMuted(value) {
15585			this.media.setMuted(value);
15586		}
15587	}, {
15588		key: 'setSrc',
15589		value: function setSrc(src) {
15590			var t = this,
15591			    layer = document.getElementById(t.media.id + '-iframe-overlay');
15592
15593			if (layer) {
15594				layer.remove();
15595			}
15596
15597			t.media.setSrc(src);
15598			t.createIframeLayer();
15599			if (t.media.renderer !== null && typeof t.media.renderer.getPosterUrl === 'function') {
15600				t.setPoster(t.media.renderer.getPosterUrl());
15601			}
15602		}
15603	}, {
15604		key: 'getSrc',
15605		value: function getSrc() {
15606			return this.media.getSrc();
15607		}
15608	}, {
15609		key: 'canPlayType',
15610		value: function canPlayType(type) {
15611			return this.media.canPlayType(type);
15612		}
15613	}, {
15614		key: 'paused',
15615		get: function get() {
15616			return this.media.paused;
15617		}
15618	}, {
15619		key: 'muted',
15620		set: function set(muted) {
15621			this.setMuted(muted);
15622		},
15623		get: function get() {
15624			return this.media.muted;
15625		}
15626	}, {
15627		key: 'ended',
15628		get: function get() {
15629			return this.media.ended;
15630		}
15631	}, {
15632		key: 'readyState',
15633		get: function get() {
15634			return this.media.readyState;
15635		}
15636	}, {
15637		key: 'currentTime',
15638		set: function set(time) {
15639			this.setCurrentTime(time);
15640		},
15641		get: function get() {
15642			return this.getCurrentTime();
15643		}
15644	}, {
15645		key: 'duration',
15646		get: function get() {
15647			return this.getDuration();
15648		}
15649	}, {
15650		key: 'remainingTime',
15651		get: function get() {
15652			return this.getDuration() - this.currentTime();
15653		}
15654	}, {
15655		key: 'volume',
15656		set: function set(volume) {
15657			this.setVolume(volume);
15658		},
15659		get: function get() {
15660			return this.getVolume();
15661		}
15662	}, {
15663		key: 'src',
15664		set: function set(src) {
15665			this.setSrc(src);
15666		},
15667		get: function get() {
15668			return this.getSrc();
15669		}
15670	}]);
15671
15672	return DefaultPlayer;
15673}();
15674
15675exports.default = DefaultPlayer;
15676
15677
15678_window2.default.DefaultPlayer = DefaultPlayer;
15679
15680},{"3":3}],19:[function(_dereq_,module,exports){
15681'use strict';
15682
15683var _window = _dereq_(3);
15684
15685var _window2 = _interopRequireDefault(_window);
15686
15687var _mejs = _dereq_(8);
15688
15689var _mejs2 = _interopRequireDefault(_mejs);
15690
15691var _player = _dereq_(17);
15692
15693var _player2 = _interopRequireDefault(_player);
15694
15695function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
15696
15697if (typeof jQuery !== 'undefined') {
15698	_mejs2.default.$ = jQuery;
15699} else if (typeof Zepto !== 'undefined') {
15700	_mejs2.default.$ = Zepto;
15701} else if (typeof ender !== 'undefined') {
15702	_mejs2.default.$ = ender;
15703}
15704
15705(function ($) {
15706	if (typeof $ !== 'undefined') {
15707		$.fn.mediaelementplayer = function (options) {
15708			if (options === false) {
15709				this.each(function () {
15710					var player = $(this).data('mediaelementplayer');
15711					if (player) {
15712						player.remove();
15713					}
15714					$(this).removeData('mediaelementplayer');
15715				});
15716			} else {
15717				this.each(function () {
15718					$(this).data('mediaelementplayer', new _player2.default(this, options));
15719				});
15720			}
15721			return this;
15722		};
15723
15724		$(document).ready(function () {
15725			$('.' + _mejs2.default.MepDefaults.classPrefix + 'player').mediaelementplayer();
15726		});
15727	}
15728})(_mejs2.default.$);
15729
15730},{"17":17,"3":3,"8":8}],20:[function(_dereq_,module,exports){
15731'use strict';
15732
15733var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; };
15734
15735var _window = _dereq_(3);
15736
15737var _window2 = _interopRequireDefault(_window);
15738
15739var _mejs = _dereq_(8);
15740
15741var _mejs2 = _interopRequireDefault(_mejs);
15742
15743var _renderer = _dereq_(9);
15744
15745var _general = _dereq_(26);
15746
15747var _media = _dereq_(28);
15748
15749var _constants = _dereq_(24);
15750
15751var _dom = _dereq_(25);
15752
15753function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
15754
15755var NativeDash = {
15756
15757	promise: null,
15758
15759	load: function load(settings) {
15760		if (typeof dashjs !== 'undefined') {
15761			NativeDash.promise = new Promise(function (resolve) {
15762				resolve();
15763			}).then(function () {
15764				NativeDash._createPlayer(settings);
15765			});
15766		} else {
15767			settings.options.path = typeof settings.options.path === 'string' ? settings.options.path : 'https://cdn.dashjs.org/latest/dash.all.min.js';
15768
15769			NativeDash.promise = NativeDash.promise || (0, _dom.loadScript)(settings.options.path);
15770			NativeDash.promise.then(function () {
15771				NativeDash._createPlayer(settings);
15772			});
15773		}
15774
15775		return NativeDash.promise;
15776	},
15777
15778	_createPlayer: function _createPlayer(settings) {
15779		var player = dashjs.MediaPlayer().create();
15780		_window2.default['__ready__' + settings.id](player);
15781		return player;
15782	}
15783};
15784
15785var DashNativeRenderer = {
15786	name: 'native_dash',
15787	options: {
15788		prefix: 'native_dash',
15789		dash: {
15790			path: 'https://cdn.dashjs.org/latest/dash.all.min.js',
15791			debug: false,
15792			drm: {},
15793
15794			robustnessLevel: ''
15795		}
15796	},
15797
15798	canPlayType: function canPlayType(type) {
15799		return _constants.HAS_MSE && ['application/dash+xml'].indexOf(type.toLowerCase()) > -1;
15800	},
15801
15802	create: function create(mediaElement, options, mediaFiles) {
15803
15804		var originalNode = mediaElement.originalNode,
15805		    id = mediaElement.id + '_' + options.prefix,
15806		    autoplay = originalNode.autoplay,
15807		    children = originalNode.children;
15808
15809		var node = null,
15810		    dashPlayer = null;
15811
15812		originalNode.removeAttribute('type');
15813		for (var i = 0, total = children.length; i < total; i++) {
15814			children[i].removeAttribute('type');
15815		}
15816
15817		node = originalNode.cloneNode(true);
15818		options = Object.assign(options, mediaElement.options);
15819
15820		var props = _mejs2.default.html5media.properties,
15821		    events = _mejs2.default.html5media.events.concat(['click', 'mouseover', 'mouseout']).filter(function (e) {
15822			return e !== 'error';
15823		}),
15824		    attachNativeEvents = function attachNativeEvents(e) {
15825			var event = (0, _general.createEvent)(e.type, mediaElement);
15826			mediaElement.dispatchEvent(event);
15827		},
15828		    assignGettersSetters = function assignGettersSetters(propName) {
15829			var capName = '' + propName.substring(0, 1).toUpperCase() + propName.substring(1);
15830
15831			node['get' + capName] = function () {
15832				return dashPlayer !== null ? node[propName] : null;
15833			};
15834
15835			node['set' + capName] = function (value) {
15836				if (_mejs2.default.html5media.readOnlyProperties.indexOf(propName) === -1) {
15837					if (propName === 'src') {
15838						var source = (typeof value === 'undefined' ? 'undefined' : _typeof(value)) === 'object' && value.src ? value.src : value;
15839						node[propName] = source;
15840						if (dashPlayer !== null) {
15841							dashPlayer.reset();
15842							for (var _i = 0, _total = events.length; _i < _total; _i++) {
15843								node.removeEventListener(events[_i], attachNativeEvents);
15844							}
15845							dashPlayer = NativeDash._createPlayer({
15846								options: options.dash,
15847								id: id
15848							});
15849
15850							if (value && (typeof value === 'undefined' ? 'undefined' : _typeof(value)) === 'object' && _typeof(value.drm) === 'object') {
15851								dashPlayer.setProtectionData(value.drm);
15852								if ((0, _general.isString)(options.dash.robustnessLevel) && options.dash.robustnessLevel) {
15853									dashPlayer.getProtectionController().setRobustnessLevel(options.dash.robustnessLevel);
15854								}
15855							}
15856							dashPlayer.attachSource(source);
15857							if (autoplay) {
15858								dashPlayer.play();
15859							}
15860						}
15861					} else {
15862						node[propName] = value;
15863					}
15864				}
15865			};
15866		};
15867
15868		for (var _i2 = 0, _total2 = props.length; _i2 < _total2; _i2++) {
15869			assignGettersSetters(props[_i2]);
15870		}
15871
15872		_window2.default['__ready__' + id] = function (_dashPlayer) {
15873			mediaElement.dashPlayer = dashPlayer = _dashPlayer;
15874
15875			var dashEvents = dashjs.MediaPlayer.events,
15876			    assignEvents = function assignEvents(eventName) {
15877				if (eventName === 'loadedmetadata') {
15878					dashPlayer.initialize();
15879					dashPlayer.attachView(node);
15880					dashPlayer.setAutoPlay(false);
15881
15882					if (_typeof(options.dash.drm) === 'object' && !_mejs2.default.Utils.isObjectEmpty(options.dash.drm)) {
15883						dashPlayer.setProtectionData(options.dash.drm);
15884						if ((0, _general.isString)(options.dash.robustnessLevel) && options.dash.robustnessLevel) {
15885							dashPlayer.getProtectionController().setRobustnessLevel(options.dash.robustnessLevel);
15886						}
15887					}
15888					dashPlayer.attachSource(node.getSrc());
15889				}
15890
15891				node.addEventListener(eventName, attachNativeEvents);
15892			};
15893
15894			for (var _i3 = 0, _total3 = events.length; _i3 < _total3; _i3++) {
15895				assignEvents(events[_i3]);
15896			}
15897
15898			var assignMdashEvents = function assignMdashEvents(e) {
15899				if (e.type.toLowerCase() === 'error') {
15900					mediaElement.generateError(e.message, node.src);
15901					console.error(e);
15902				} else {
15903					var _event = (0, _general.createEvent)(e.type, mediaElement);
15904					_event.data = e;
15905					mediaElement.dispatchEvent(_event);
15906				}
15907			};
15908
15909			for (var eventType in dashEvents) {
15910				if (dashEvents.hasOwnProperty(eventType)) {
15911					dashPlayer.on(dashEvents[eventType], function (e) {
15912						return assignMdashEvents(e);
15913					});
15914				}
15915			}
15916		};
15917
15918		if (mediaFiles && mediaFiles.length > 0) {
15919			for (var _i4 = 0, _total4 = mediaFiles.length; _i4 < _total4; _i4++) {
15920				if (_renderer.renderer.renderers[options.prefix].canPlayType(mediaFiles[_i4].type)) {
15921					node.setAttribute('src', mediaFiles[_i4].src);
15922					if (typeof mediaFiles[_i4].drm !== 'undefined') {
15923						options.dash.drm = mediaFiles[_i4].drm;
15924					}
15925					break;
15926				}
15927			}
15928		}
15929
15930		node.setAttribute('id', id);
15931
15932		originalNode.parentNode.insertBefore(node, originalNode);
15933		originalNode.autoplay = false;
15934		originalNode.style.display = 'none';
15935
15936		node.setSize = function (width, height) {
15937			node.style.width = width + 'px';
15938			node.style.height = height + 'px';
15939			return node;
15940		};
15941
15942		node.hide = function () {
15943			node.pause();
15944			node.style.display = 'none';
15945			return node;
15946		};
15947
15948		node.show = function () {
15949			node.style.display = '';
15950			return node;
15951		};
15952
15953		node.destroy = function () {
15954			if (dashPlayer !== null) {
15955				dashPlayer.reset();
15956			}
15957		};
15958
15959		var event = (0, _general.createEvent)('rendererready', node, false);
15960		mediaElement.originalNode.dispatchEvent(event);
15961
15962		mediaElement.promises.push(NativeDash.load({
15963			options: options.dash,
15964			id: id
15965		}));
15966
15967		return node;
15968	}
15969};
15970
15971_media.typeChecks.push(function (url) {
15972	return ~url.toLowerCase().indexOf('.mpd') ? 'application/dash+xml' : null;
15973});
15974
15975_renderer.renderer.add(DashNativeRenderer);
15976
15977},{"24":24,"25":25,"26":26,"28":28,"3":3,"8":8,"9":9}],21:[function(_dereq_,module,exports){
15978'use strict';
15979
15980var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; };
15981
15982var _window = _dereq_(3);
15983
15984var _window2 = _interopRequireDefault(_window);
15985
15986var _mejs = _dereq_(8);
15987
15988var _mejs2 = _interopRequireDefault(_mejs);
15989
15990var _renderer = _dereq_(9);
15991
15992var _general = _dereq_(26);
15993
15994var _constants = _dereq_(24);
15995
15996var _media = _dereq_(28);
15997
15998var _dom = _dereq_(25);
15999
16000function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
16001
16002var NativeHls = {
16003
16004	promise: null,
16005
16006	load: function load(settings) {
16007		if (typeof Hls !== 'undefined') {
16008			NativeHls.promise = new Promise(function (resolve) {
16009				resolve();
16010			}).then(function () {
16011				NativeHls._createPlayer(settings);
16012			});
16013		} else {
16014			settings.options.path = typeof settings.options.path === 'string' ? settings.options.path : 'https://cdn.jsdelivr.net/npm/hls.js@latest';
16015
16016			NativeHls.promise = NativeHls.promise || (0, _dom.loadScript)(settings.options.path);
16017			NativeHls.promise.then(function () {
16018				NativeHls._createPlayer(settings);
16019			});
16020		}
16021
16022		return NativeHls.promise;
16023	},
16024
16025	_createPlayer: function _createPlayer(settings) {
16026		var player = new Hls(settings.options);
16027		_window2.default['__ready__' + settings.id](player);
16028		return player;
16029	}
16030};
16031
16032var HlsNativeRenderer = {
16033	name: 'native_hls',
16034	options: {
16035		prefix: 'native_hls',
16036		hls: {
16037			path: 'https://cdn.jsdelivr.net/npm/hls.js@latest',
16038
16039			autoStartLoad: false,
16040			debug: false
16041		}
16042	},
16043
16044	canPlayType: function canPlayType(type) {
16045		return _constants.HAS_MSE && ['application/x-mpegurl', 'application/vnd.apple.mpegurl', 'audio/mpegurl', 'audio/hls', 'video/hls'].indexOf(type.toLowerCase()) > -1;
16046	},
16047
16048	create: function create(mediaElement, options, mediaFiles) {
16049
16050		var originalNode = mediaElement.originalNode,
16051		    id = mediaElement.id + '_' + options.prefix,
16052		    preload = originalNode.getAttribute('preload'),
16053		    autoplay = originalNode.autoplay;
16054
16055		var hlsPlayer = null,
16056		    node = null,
16057		    index = 0,
16058		    total = mediaFiles.length;
16059
16060		node = originalNode.cloneNode(true);
16061		options = Object.assign(options, mediaElement.options);
16062		options.hls.autoStartLoad = preload && preload !== 'none' || autoplay;
16063
16064		var props = _mejs2.default.html5media.properties,
16065		    events = _mejs2.default.html5media.events.concat(['click', 'mouseover', 'mouseout']).filter(function (e) {
16066			return e !== 'error';
16067		}),
16068		    attachNativeEvents = function attachNativeEvents(e) {
16069			var event = (0, _general.createEvent)(e.type, mediaElement);
16070			mediaElement.dispatchEvent(event);
16071		},
16072		    assignGettersSetters = function assignGettersSetters(propName) {
16073			var capName = '' + propName.substring(0, 1).toUpperCase() + propName.substring(1);
16074
16075			node['get' + capName] = function () {
16076				return hlsPlayer !== null ? node[propName] : null;
16077			};
16078
16079			node['set' + capName] = function (value) {
16080				if (_mejs2.default.html5media.readOnlyProperties.indexOf(propName) === -1) {
16081					if (propName === 'src') {
16082						node[propName] = (typeof value === 'undefined' ? 'undefined' : _typeof(value)) === 'object' && value.src ? value.src : value;
16083						if (hlsPlayer !== null) {
16084							hlsPlayer.destroy();
16085							for (var i = 0, _total = events.length; i < _total; i++) {
16086								node.removeEventListener(events[i], attachNativeEvents);
16087							}
16088							hlsPlayer = NativeHls._createPlayer({
16089								options: options.hls,
16090								id: id
16091							});
16092							hlsPlayer.loadSource(value);
16093							hlsPlayer.attachMedia(node);
16094						}
16095					} else {
16096						node[propName] = value;
16097					}
16098				}
16099			};
16100		};
16101
16102		for (var i = 0, _total2 = props.length; i < _total2; i++) {
16103			assignGettersSetters(props[i]);
16104		}
16105
16106		_window2.default['__ready__' + id] = function (_hlsPlayer) {
16107			mediaElement.hlsPlayer = hlsPlayer = _hlsPlayer;
16108			var hlsEvents = Hls.Events,
16109			    assignEvents = function assignEvents(eventName) {
16110				if (eventName === 'loadedmetadata') {
16111					var url = mediaElement.originalNode.src;
16112					hlsPlayer.detachMedia();
16113					hlsPlayer.loadSource(url);
16114					hlsPlayer.attachMedia(node);
16115				}
16116
16117				node.addEventListener(eventName, attachNativeEvents);
16118			};
16119
16120			for (var _i = 0, _total3 = events.length; _i < _total3; _i++) {
16121				assignEvents(events[_i]);
16122			}
16123
16124			var recoverDecodingErrorDate = void 0,
16125			    recoverSwapAudioCodecDate = void 0;
16126			var assignHlsEvents = function assignHlsEvents(name, data) {
16127				if (name === 'hlsError') {
16128					console.warn(data);
16129					data = data[1];
16130
16131					if (data.fatal) {
16132						switch (data.type) {
16133							case 'mediaError':
16134								var now = new Date().getTime();
16135								if (!recoverDecodingErrorDate || now - recoverDecodingErrorDate > 3000) {
16136									recoverDecodingErrorDate = new Date().getTime();
16137									hlsPlayer.recoverMediaError();
16138								} else if (!recoverSwapAudioCodecDate || now - recoverSwapAudioCodecDate > 3000) {
16139									recoverSwapAudioCodecDate = new Date().getTime();
16140									console.warn('Attempting to swap Audio Codec and recover from media error');
16141									hlsPlayer.swapAudioCodec();
16142									hlsPlayer.recoverMediaError();
16143								} else {
16144									var message = 'Cannot recover, last media error recovery failed';
16145									mediaElement.generateError(message, node.src);
16146									console.error(message);
16147								}
16148								break;
16149							case 'networkError':
16150								if (data.details === 'manifestLoadError') {
16151									if (index < total && mediaFiles[index + 1] !== undefined) {
16152										node.setSrc(mediaFiles[index++].src);
16153										node.load();
16154										node.play();
16155									} else {
16156										var _message = 'Network error';
16157										mediaElement.generateError(_message, mediaFiles);
16158										console.error(_message);
16159									}
16160								} else {
16161									var _message2 = 'Network error';
16162									mediaElement.generateError(_message2, mediaFiles);
16163									console.error(_message2);
16164								}
16165								break;
16166							default:
16167								hlsPlayer.destroy();
16168								break;
16169						}
16170						return;
16171					}
16172				}
16173				var event = (0, _general.createEvent)(name, mediaElement);
16174				event.data = data;
16175				mediaElement.dispatchEvent(event);
16176			};
16177
16178			var _loop = function _loop(eventType) {
16179				if (hlsEvents.hasOwnProperty(eventType)) {
16180					hlsPlayer.on(hlsEvents[eventType], function () {
16181						for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) {
16182							args[_key] = arguments[_key];
16183						}
16184
16185						return assignHlsEvents(hlsEvents[eventType], args);
16186					});
16187				}
16188			};
16189
16190			for (var eventType in hlsEvents) {
16191				_loop(eventType);
16192			}
16193		};
16194
16195		if (total > 0) {
16196			for (; index < total; index++) {
16197				if (_renderer.renderer.renderers[options.prefix].canPlayType(mediaFiles[index].type)) {
16198					node.setAttribute('src', mediaFiles[index].src);
16199					break;
16200				}
16201			}
16202		}
16203
16204		if (preload !== 'auto' && !autoplay) {
16205			node.addEventListener('play', function () {
16206				if (hlsPlayer !== null) {
16207					hlsPlayer.startLoad();
16208				}
16209			});
16210
16211			node.addEventListener('pause', function () {
16212				if (hlsPlayer !== null) {
16213					hlsPlayer.stopLoad();
16214				}
16215			});
16216		}
16217
16218		node.setAttribute('id', id);
16219
16220		originalNode.parentNode.insertBefore(node, originalNode);
16221		originalNode.autoplay = false;
16222		originalNode.style.display = 'none';
16223
16224		node.setSize = function (width, height) {
16225			node.style.width = width + 'px';
16226			node.style.height = height + 'px';
16227			return node;
16228		};
16229
16230		node.hide = function () {
16231			node.pause();
16232			node.style.display = 'none';
16233			return node;
16234		};
16235
16236		node.show = function () {
16237			node.style.display = '';
16238			return node;
16239		};
16240
16241		node.destroy = function () {
16242			if (hlsPlayer !== null) {
16243				hlsPlayer.stopLoad();
16244				hlsPlayer.destroy();
16245			}
16246		};
16247
16248		var event = (0, _general.createEvent)('rendererready', node, false);
16249		mediaElement.originalNode.dispatchEvent(event);
16250
16251		mediaElement.promises.push(NativeHls.load({
16252			options: options.hls,
16253			id: id
16254		}));
16255
16256		return node;
16257	}
16258};
16259
16260_media.typeChecks.push(function (url) {
16261	return ~url.toLowerCase().indexOf('.m3u8') ? 'application/x-mpegURL' : null;
16262});
16263
16264_renderer.renderer.add(HlsNativeRenderer);
16265
16266},{"24":24,"25":25,"26":26,"28":28,"3":3,"8":8,"9":9}],22:[function(_dereq_,module,exports){
16267'use strict';
16268
16269var _window = _dereq_(3);
16270
16271var _window2 = _interopRequireDefault(_window);
16272
16273var _document = _dereq_(2);
16274
16275var _document2 = _interopRequireDefault(_document);
16276
16277var _mejs = _dereq_(8);
16278
16279var _mejs2 = _interopRequireDefault(_mejs);
16280
16281var _renderer = _dereq_(9);
16282
16283var _general = _dereq_(26);
16284
16285var _constants = _dereq_(24);
16286
16287function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
16288
16289var HtmlMediaElement = {
16290	name: 'html5',
16291	options: {
16292		prefix: 'html5'
16293	},
16294
16295	canPlayType: function canPlayType(type) {
16296
16297		var mediaElement = _document2.default.createElement('video');
16298
16299		if (_constants.IS_ANDROID && /\/mp(3|4)$/i.test(type) || ~['application/x-mpegurl', 'vnd.apple.mpegurl', 'audio/mpegurl', 'audio/hls', 'video/hls'].indexOf(type.toLowerCase()) && _constants.SUPPORTS_NATIVE_HLS) {
16300			return 'yes';
16301		} else if (mediaElement.canPlayType) {
16302			return mediaElement.canPlayType(type.toLowerCase()).replace(/no/, '');
16303		} else {
16304			return '';
16305		}
16306	},
16307
16308	create: function create(mediaElement, options, mediaFiles) {
16309
16310		var id = mediaElement.id + '_' + options.prefix;
16311		var isActive = false;
16312
16313		var node = null;
16314
16315		if (mediaElement.originalNode === undefined || mediaElement.originalNode === null) {
16316			node = _document2.default.createElement('audio');
16317			mediaElement.appendChild(node);
16318		} else {
16319			node = mediaElement.originalNode;
16320		}
16321
16322		node.setAttribute('id', id);
16323
16324		var props = _mejs2.default.html5media.properties,
16325		    assignGettersSetters = function assignGettersSetters(propName) {
16326			var capName = '' + propName.substring(0, 1).toUpperCase() + propName.substring(1);
16327
16328			node['get' + capName] = function () {
16329				return node[propName];
16330			};
16331
16332			node['set' + capName] = function (value) {
16333				if (_mejs2.default.html5media.readOnlyProperties.indexOf(propName) === -1) {
16334					node[propName] = value;
16335				}
16336			};
16337		};
16338
16339		for (var i = 0, _total = props.length; i < _total; i++) {
16340			assignGettersSetters(props[i]);
16341		}
16342
16343		var events = _mejs2.default.html5media.events.concat(['click', 'mouseover', 'mouseout']).filter(function (e) {
16344			return e !== 'error';
16345		}),
16346		    assignEvents = function assignEvents(eventName) {
16347			node.addEventListener(eventName, function (e) {
16348				if (isActive) {
16349					var _event = (0, _general.createEvent)(e.type, e.target);
16350					mediaElement.dispatchEvent(_event);
16351				}
16352			});
16353		};
16354
16355		for (var _i = 0, _total2 = events.length; _i < _total2; _i++) {
16356			assignEvents(events[_i]);
16357		}
16358
16359		node.setSize = function (width, height) {
16360			node.style.width = width + 'px';
16361			node.style.height = height + 'px';
16362			return node;
16363		};
16364
16365		node.hide = function () {
16366			isActive = false;
16367			node.style.display = 'none';
16368
16369			return node;
16370		};
16371
16372		node.show = function () {
16373			isActive = true;
16374			node.style.display = '';
16375
16376			return node;
16377		};
16378
16379		var index = 0,
16380		    total = mediaFiles.length;
16381		if (total > 0) {
16382			for (; index < total; index++) {
16383				if (_renderer.renderer.renderers[options.prefix].canPlayType(mediaFiles[index].type)) {
16384					node.setAttribute('src', mediaFiles[index].src);
16385					break;
16386				}
16387			}
16388		}
16389
16390		node.addEventListener('error', function (e) {
16391			if (e && e.target && e.target.error && e.target.error.code === 4 && isActive) {
16392				if (index < total && mediaFiles[index + 1] !== undefined) {
16393					node.src = mediaFiles[index++].src;
16394					node.load();
16395					node.play();
16396				} else {
16397					mediaElement.generateError('Media error: Format(s) not supported or source(s) not found', mediaFiles);
16398				}
16399			}
16400		});
16401
16402		var event = (0, _general.createEvent)('rendererready', node, false);
16403		mediaElement.originalNode.dispatchEvent(event);
16404
16405		return node;
16406	}
16407};
16408
16409_window2.default.HtmlMediaElement = _mejs2.default.HtmlMediaElement = HtmlMediaElement;
16410
16411_renderer.renderer.add(HtmlMediaElement);
16412
16413},{"2":2,"24":24,"26":26,"3":3,"8":8,"9":9}],23:[function(_dereq_,module,exports){
16414'use strict';
16415
16416var _window = _dereq_(3);
16417
16418var _window2 = _interopRequireDefault(_window);
16419
16420var _document = _dereq_(2);
16421
16422var _document2 = _interopRequireDefault(_document);
16423
16424var _mejs = _dereq_(8);
16425
16426var _mejs2 = _interopRequireDefault(_mejs);
16427
16428var _renderer = _dereq_(9);
16429
16430var _general = _dereq_(26);
16431
16432var _media = _dereq_(28);
16433
16434var _dom = _dereq_(25);
16435
16436function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
16437
16438var YouTubeApi = {
16439	isIframeStarted: false,
16440
16441	isIframeLoaded: false,
16442
16443	iframeQueue: [],
16444
16445	enqueueIframe: function enqueueIframe(settings) {
16446		YouTubeApi.isLoaded = typeof YT !== 'undefined' && YT.loaded;
16447
16448		if (YouTubeApi.isLoaded) {
16449			YouTubeApi.createIframe(settings);
16450		} else {
16451			YouTubeApi.loadIframeApi();
16452			YouTubeApi.iframeQueue.push(settings);
16453		}
16454	},
16455
16456	loadIframeApi: function loadIframeApi() {
16457		if (!YouTubeApi.isIframeStarted) {
16458			(0, _dom.loadScript)('https://www.youtube.com/player_api');
16459			YouTubeApi.isIframeStarted = true;
16460		}
16461	},
16462
16463	iFrameReady: function iFrameReady() {
16464
16465		YouTubeApi.isLoaded = true;
16466		YouTubeApi.isIframeLoaded = true;
16467
16468		while (YouTubeApi.iframeQueue.length > 0) {
16469			var settings = YouTubeApi.iframeQueue.pop();
16470			YouTubeApi.createIframe(settings);
16471		}
16472	},
16473
16474	createIframe: function createIframe(settings) {
16475		return new YT.Player(settings.containerId, settings);
16476	},
16477
16478	getYouTubeId: function getYouTubeId(url) {
16479
16480		var youTubeId = '';
16481
16482		if (url.indexOf('?') > 0) {
16483			youTubeId = YouTubeApi.getYouTubeIdFromParam(url);
16484
16485			if (youTubeId === '') {
16486				youTubeId = YouTubeApi.getYouTubeIdFromUrl(url);
16487			}
16488		} else {
16489			youTubeId = YouTubeApi.getYouTubeIdFromUrl(url);
16490		}
16491
16492		var id = youTubeId.substring(youTubeId.lastIndexOf('/') + 1);
16493		youTubeId = id.split('?');
16494		return youTubeId[0];
16495	},
16496
16497	getYouTubeIdFromParam: function getYouTubeIdFromParam(url) {
16498
16499		if (url === undefined || url === null || !url.trim().length) {
16500			return null;
16501		}
16502
16503		var parts = url.split('?'),
16504		    parameters = parts[1].split('&');
16505
16506		var youTubeId = '';
16507
16508		for (var i = 0, total = parameters.length; i < total; i++) {
16509			var paramParts = parameters[i].split('=');
16510			if (paramParts[0] === 'v') {
16511				youTubeId = paramParts[1];
16512				break;
16513			}
16514		}
16515
16516		return youTubeId;
16517	},
16518
16519	getYouTubeIdFromUrl: function getYouTubeIdFromUrl(url) {
16520
16521		if (url === undefined || url === null || !url.trim().length) {
16522			return null;
16523		}
16524
16525		var parts = url.split('?');
16526		url = parts[0];
16527		return url.substring(url.lastIndexOf('/') + 1);
16528	},
16529
16530	getYouTubeNoCookieUrl: function getYouTubeNoCookieUrl(url) {
16531		if (url === undefined || url === null || !url.trim().length || url.indexOf('//www.youtube') === -1) {
16532			return url;
16533		}
16534
16535		var parts = url.split('/');
16536		parts[2] = parts[2].replace('.com', '-nocookie.com');
16537		return parts.join('/');
16538	}
16539};
16540
16541var YouTubeIframeRenderer = {
16542	name: 'youtube_iframe',
16543
16544	options: {
16545		prefix: 'youtube_iframe',
16546
16547		youtube: {
16548			autoplay: 0,
16549			controls: 0,
16550			disablekb: 1,
16551			end: 0,
16552			loop: 0,
16553			modestbranding: 0,
16554			playsinline: 0,
16555			rel: 0,
16556			showinfo: 0,
16557			start: 0,
16558			iv_load_policy: 3,
16559
16560			nocookie: false,
16561
16562			imageQuality: null
16563		}
16564	},
16565
16566	canPlayType: function canPlayType(type) {
16567		return ~['video/youtube', 'video/x-youtube'].indexOf(type.toLowerCase());
16568	},
16569
16570	create: function create(mediaElement, options, mediaFiles) {
16571
16572		var youtube = {},
16573		    apiStack = [],
16574		    readyState = 4;
16575
16576		var youTubeApi = null,
16577		    paused = true,
16578		    ended = false,
16579		    youTubeIframe = null,
16580		    volume = 1;
16581
16582		youtube.options = options;
16583		youtube.id = mediaElement.id + '_' + options.prefix;
16584		youtube.mediaElement = mediaElement;
16585
16586		var props = _mejs2.default.html5media.properties,
16587		    assignGettersSetters = function assignGettersSetters(propName) {
16588
16589			var capName = '' + propName.substring(0, 1).toUpperCase() + propName.substring(1);
16590
16591			youtube['get' + capName] = function () {
16592				if (youTubeApi !== null) {
16593					var value = null;
16594
16595					switch (propName) {
16596						case 'currentTime':
16597							return youTubeApi.getCurrentTime();
16598						case 'duration':
16599							return youTubeApi.getDuration();
16600						case 'volume':
16601							volume = youTubeApi.getVolume() / 100;
16602							return volume;
16603						case 'playbackRate':
16604							return youTubeApi.getPlaybackRate();
16605						case 'paused':
16606							return paused;
16607						case 'ended':
16608							return ended;
16609						case 'muted':
16610							return youTubeApi.isMuted();
16611						case 'buffered':
16612							var percentLoaded = youTubeApi.getVideoLoadedFraction(),
16613							    duration = youTubeApi.getDuration();
16614							return {
16615								start: function start() {
16616									return 0;
16617								},
16618								end: function end() {
16619									return percentLoaded * duration;
16620								},
16621								length: 1
16622							};
16623						case 'src':
16624							return youTubeApi.getVideoUrl();
16625						case 'readyState':
16626							return readyState;
16627					}
16628
16629					return value;
16630				} else {
16631					return null;
16632				}
16633			};
16634
16635			youtube['set' + capName] = function (value) {
16636				if (youTubeApi !== null) {
16637					switch (propName) {
16638						case 'src':
16639							var url = typeof value === 'string' ? value : value[0].src,
16640							    _videoId = YouTubeApi.getYouTubeId(url);
16641
16642							if (mediaElement.originalNode.autoplay) {
16643								youTubeApi.loadVideoById(_videoId);
16644							} else {
16645								youTubeApi.cueVideoById(_videoId);
16646							}
16647							break;
16648						case 'currentTime':
16649							youTubeApi.seekTo(value);
16650							break;
16651						case 'muted':
16652							if (value) {
16653								youTubeApi.mute();
16654							} else {
16655								youTubeApi.unMute();
16656							}
16657							setTimeout(function () {
16658								var event = (0, _general.createEvent)('volumechange', youtube);
16659								mediaElement.dispatchEvent(event);
16660							}, 50);
16661							break;
16662						case 'volume':
16663							volume = value;
16664							youTubeApi.setVolume(value * 100);
16665							setTimeout(function () {
16666								var event = (0, _general.createEvent)('volumechange', youtube);
16667								mediaElement.dispatchEvent(event);
16668							}, 50);
16669							break;
16670						case 'playbackRate':
16671							youTubeApi.setPlaybackRate(value);
16672							setTimeout(function () {
16673								var event = (0, _general.createEvent)('ratechange', youtube);
16674								mediaElement.dispatchEvent(event);
16675							}, 50);
16676							break;
16677						case 'readyState':
16678							var event = (0, _general.createEvent)('canplay', youtube);
16679							mediaElement.dispatchEvent(event);
16680							break;
16681						default:
16682							
16683							break;
16684					}
16685				} else {
16686					apiStack.push({ type: 'set', propName: propName, value: value });
16687				}
16688			};
16689		};
16690
16691		for (var i = 0, total = props.length; i < total; i++) {
16692			assignGettersSetters(props[i]);
16693		}
16694
16695		var methods = _mejs2.default.html5media.methods,
16696		    assignMethods = function assignMethods(methodName) {
16697			youtube[methodName] = function () {
16698				if (youTubeApi !== null) {
16699					switch (methodName) {
16700						case 'play':
16701							paused = false;
vendor: 4,117 bytes, lines 16702-16831
16702							return youTubeApi.playVideo();
16703						case 'pause':
16704							paused = true;
16705							return youTubeApi.pauseVideo();
16706						case 'load':
16707							return null;
16708					}
16709				} else {
16710					apiStack.push({ type: 'call', methodName: methodName });
16711				}
16712			};
16713		};
16714
16715		for (var _i = 0, _total = methods.length; _i < _total; _i++) {
16716			assignMethods(methods[_i]);
16717		}
16718
16719		var errorHandler = function errorHandler(error) {
16720			var message = '';
16721			switch (error.data) {
16722				case 2:
16723					message = 'The request contains an invalid parameter value. Verify that video ID has 11 characters and that contains no invalid characters, such as exclamation points or asterisks.';
16724					break;
16725				case 5:
16726					message = 'The requested content cannot be played in an HTML5 player or another error related to the HTML5 player has occurred.';
16727					break;
16728				case 100:
16729					message = 'The video requested was not found. Either video has been removed or has been marked as private.';
16730					break;
16731				case 101:
16732				case 105:
16733					message = 'The owner of the requested video does not allow it to be played in embedded players.';
16734					break;
16735				default:
16736					message = 'Unknown error.';
16737					break;
16738			}
16739			mediaElement.generateError('Code ' + error.data + ': ' + message, mediaFiles);
16740		};
16741
16742		var youtubeContainer = _document2.default.createElement('div');
16743		youtubeContainer.id = youtube.id;
16744
16745		if (youtube.options.youtube.nocookie) {
16746			mediaElement.originalNode.src = YouTubeApi.getYouTubeNoCookieUrl(mediaFiles[0].src);
16747		}
16748
16749		mediaElement.originalNode.parentNode.insertBefore(youtubeContainer, mediaElement.originalNode);
16750		mediaElement.originalNode.style.display = 'none';
16751
16752		var isAudio = mediaElement.originalNode.tagName.toLowerCase() === 'audio',
16753		    height = isAudio ? '1' : mediaElement.originalNode.height,
16754		    width = isAudio ? '1' : mediaElement.originalNode.width,
16755		    videoId = YouTubeApi.getYouTubeId(mediaFiles[0].src),
16756		    youtubeSettings = {
16757			id: youtube.id,
16758			containerId: youtubeContainer.id,
16759			videoId: videoId,
16760			height: height,
16761			width: width,
16762			host: youtube.options.youtube && youtube.options.youtube.nocookie ? 'https://www.youtube-nocookie.com' : undefined,
16763			playerVars: Object.assign({
16764				controls: 0,
16765				rel: 0,
16766				disablekb: 1,
16767				showinfo: 0,
16768				modestbranding: 0,
16769				html5: 1,
16770				iv_load_policy: 3
16771			}, youtube.options.youtube),
16772			origin: _window2.default.location.host,
16773			events: {
16774				onReady: function onReady(e) {
16775					mediaElement.youTubeApi = youTubeApi = e.target;
16776					mediaElement.youTubeState = {
16777						paused: true,
16778						ended: false
16779					};
16780
16781					if (apiStack.length) {
16782						for (var _i2 = 0, _total2 = apiStack.length; _i2 < _total2; _i2++) {
16783
16784							var stackItem = apiStack[_i2];
16785
16786							if (stackItem.type === 'set') {
16787								var propName = stackItem.propName,
16788								    capName = '' + propName.substring(0, 1).toUpperCase() + propName.substring(1);
16789
16790								youtube['set' + capName](stackItem.value);
16791							} else if (stackItem.type === 'call') {
16792								youtube[stackItem.methodName]();
16793							}
16794						}
16795					}
16796
16797					youTubeIframe = youTubeApi.getIframe();
16798
16799					if (mediaElement.originalNode.muted) {
16800						youTubeApi.mute();
16801					}
16802
16803					var events = ['mouseover', 'mouseout'],
16804					    assignEvents = function assignEvents(e) {
16805						var newEvent = (0, _general.createEvent)(e.type, youtube);
16806						mediaElement.dispatchEvent(newEvent);
16807					};
16808
16809					for (var _i3 = 0, _total3 = events.length; _i3 < _total3; _i3++) {
16810						youTubeIframe.addEventListener(events[_i3], assignEvents, false);
16811					}
16812
16813					var initEvents = ['rendererready', 'loadedmetadata', 'loadeddata', 'canplay'];
16814
16815					for (var _i4 = 0, _total4 = initEvents.length; _i4 < _total4; _i4++) {
16816						var event = (0, _general.createEvent)(initEvents[_i4], youtube, true);
16817						mediaElement.dispatchEvent(event);
16818					}
16819				},
16820				onStateChange: function onStateChange(e) {
16821					var events = [];
16822
16823					switch (e.data) {
16824						case -1:
16825							events = ['loadedmetadata'];
16826							paused = true;
16827							ended = false;
16828							break;
16829						case 0:
16830							events = ['ended'];
16831							paused = false;
vendor: 4,035 bytes, lines 16832-16958
16832							ended = !youtube.options.youtube.loop;
16833							if (!youtube.options.youtube.loop) {
16834								youtube.stopInterval();
16835							}
16836							break;
16837						case 1:
16838							events = ['play', 'playing'];
16839							paused = false;
16840							ended = false;
16841							youtube.startInterval();
16842							break;
16843						case 2:
16844							events = ['pause'];
16845							paused = true;
16846							ended = false;
16847							youtube.stopInterval();
16848							break;
16849						case 3:
16850							events = ['progress'];
16851							ended = false;
16852							break;
16853						case 5:
16854							events = ['loadeddata', 'loadedmetadata', 'canplay'];
16855							paused = true;
16856							ended = false;
16857							break;
16858					}
16859
16860					for (var _i5 = 0, _total5 = events.length; _i5 < _total5; _i5++) {
16861						var event = (0, _general.createEvent)(events[_i5], youtube);
16862						mediaElement.dispatchEvent(event);
16863					}
16864				},
16865				onError: function onError(e) {
16866					return errorHandler(e);
16867				}
16868			}
16869		};
16870
16871		if (isAudio || mediaElement.originalNode.hasAttribute('playsinline')) {
16872			youtubeSettings.playerVars.playsinline = 1;
16873		}
16874
16875		if (mediaElement.originalNode.controls) {
16876			youtubeSettings.playerVars.controls = 1;
16877		}
16878		if (mediaElement.originalNode.autoplay) {
16879			youtubeSettings.playerVars.autoplay = 1;
16880		}
16881		if (mediaElement.originalNode.loop) {
16882			youtubeSettings.playerVars.loop = 1;
16883		}
16884
16885		if ((youtubeSettings.playerVars.loop && parseInt(youtubeSettings.playerVars.loop, 10) === 1 || mediaElement.originalNode.src.indexOf('loop=') > -1) && !youtubeSettings.playerVars.playlist && mediaElement.originalNode.src.indexOf('playlist=') === -1) {
16886			youtubeSettings.playerVars.playlist = YouTubeApi.getYouTubeId(mediaElement.originalNode.src);
16887		}
16888
16889		YouTubeApi.enqueueIframe(youtubeSettings);
16890
16891		youtube.onEvent = function (eventName, player, _youTubeState) {
16892			if (_youTubeState !== null && _youTubeState !== undefined) {
16893				mediaElement.youTubeState = _youTubeState;
16894			}
16895		};
16896
16897		youtube.setSize = function (width, height) {
16898			if (youTubeApi !== null) {
16899				youTubeApi.setSize(width, height);
16900			}
16901		};
16902		youtube.hide = function () {
16903			youtube.stopInterval();
16904			youtube.pause();
16905			if (youTubeIframe) {
16906				youTubeIframe.style.display = 'none';
16907			}
16908		};
16909		youtube.show = function () {
16910			if (youTubeIframe) {
16911				youTubeIframe.style.display = '';
16912			}
16913		};
16914		youtube.destroy = function () {
16915			youTubeApi.destroy();
16916		};
16917		youtube.interval = null;
16918
16919		youtube.startInterval = function () {
16920			youtube.interval = setInterval(function () {
16921				var event = (0, _general.createEvent)('timeupdate', youtube);
16922				mediaElement.dispatchEvent(event);
16923			}, 250);
16924		};
16925		youtube.stopInterval = function () {
16926			if (youtube.interval) {
16927				clearInterval(youtube.interval);
16928			}
16929		};
16930		youtube.getPosterUrl = function () {
16931			var quality = options.youtube.imageQuality,
16932			    resolutions = ['default', 'hqdefault', 'mqdefault', 'sddefault', 'maxresdefault'],
16933			    id = YouTubeApi.getYouTubeId(mediaElement.originalNode.src);
16934			return quality && resolutions.indexOf(quality) > -1 && id ? 'https://img.youtube.com/vi/' + id + '/' + quality + '.jpg' : '';
16935		};
16936
16937		return youtube;
16938	}
16939};
16940
16941_window2.default.onYouTubePlayerAPIReady = function () {
16942	YouTubeApi.iFrameReady();
16943};
16944
16945_media.typeChecks.push(function (url) {
16946	return (/\/\/(www\.youtube|youtu\.?be)/i.test(url) ? 'video/x-youtube' : null
16947	);
16948});
16949
16950_renderer.renderer.add(YouTubeIframeRenderer);
16951
16952},{"2":2,"25":25,"26":26,"28":28,"3":3,"8":8,"9":9}],24:[function(_dereq_,module,exports){
16953'use strict';
16954
16955Object.defineProperty(exports, "__esModule", {
16956	value: true
16957});
16958exports.cancelFullScreen = exports.requestFullScreen = exports.isFullScreen = exports.FULLSCREEN_EVENT_NAME = exports.HAS_NATIVE_FULLSCREEN_ENABLED = exports.HAS_TRUE_NATIVE_FULLSCREEN = exports.HAS_IOS_FULLSCREEN = exports.HAS_MS_NATIVE_FULLSCREEN = exports.HAS_MOZ_NATIVE_FULLSCREEN = exports.HAS_WEBKIT_NATIVE_FULLSCREEN = exports.HAS_NATIVE_FULLSCREEN = exports.SUPPORTS_NATIVE_HLS = exports.SUPPORT_PASSIVE_EVENT = exports.SUPPORT_POINTER_EVENTS = exports.HAS_MSE = exports.IS_STOCK_ANDROID = exp
vendor: 1,400 bytes, lines 16958-16985
16958orts.IS_SAFARI = exports.IS_FIREFOX = exports.IS_CHROME = exports.IS_EDGE = exports.IS_IE = exports.IS_ANDROID = exports.IS_IOS = exports.IS_IPOD = exports.IS_IPHONE = exports.IS_IPAD = exports.UA = exports.NAV = undefined;
16959
16960var _window = _dereq_(3);
16961
16962var _window2 = _interopRequireDefault(_window);
16963
16964var _document = _dereq_(2);
16965
16966var _document2 = _interopRequireDefault(_document);
16967
16968var _mejs = _dereq_(8);
16969
16970var _mejs2 = _interopRequireDefault(_mejs);
16971
16972function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
16973
16974var NAV = exports.NAV = _window2.default.navigator;
16975var UA = exports.UA = NAV.userAgent.toLowerCase();
16976var IS_IPAD = exports.IS_IPAD = /ipad/i.test(UA) && !_window2.default.MSStream;
16977var IS_IPHONE = exports.IS_IPHONE = /iphone/i.test(UA) && !_window2.default.MSStream;
16978var IS_IPOD = exports.IS_IPOD = /ipod/i.test(UA) && !_window2.default.MSStream;
16979var IS_IOS = exports.IS_IOS = /ipad|iphone|ipod/i.test(UA) && !_window2.default.MSStream;
16980var IS_ANDROID = exports.IS_ANDROID = /android/i.test(UA);
16981var IS_IE = exports.IS_IE = /(trident|microsoft)/i.test(NAV.appName);
16982var IS_EDGE = exports.IS_EDGE = 'msLaunchUri' in NAV && !('documentMode' in _document2.default);
16983var IS_CHROME = exports.IS_CHROME = /chrome/i.test(UA);
16984var IS_FIREFOX = exports.IS_FIREFOX = /firefox/i.test(UA);
16985var IS_SAFARI = exports.IS_SAFARI = /safari/i.test(UA) && !IS_CHROME;
vendor: 32,196 bytes, lines 16986-18082
16986var IS_STOCK_ANDROID = exports.IS_STOCK_ANDROID = /^mozilla\/\d+\.\d+\s\(linux;\su;/i.test(UA);
16987var HAS_MSE = exports.HAS_MSE = 'MediaSource' in _window2.default;
16988var SUPPORT_POINTER_EVENTS = exports.SUPPORT_POINTER_EVENTS = true;var SUPPORT_PASSIVE_EVENT = exports.SUPPORT_PASSIVE_EVENT = function () {
16989	var supportsPassive = false;
16990	try {
16991		var opts = Object.defineProperty({}, 'passive', {
16992			get: function get() {
16993				supportsPassive = true;
16994			}
16995		});
16996		_window2.default.addEventListener('test', null, opts);
16997	} catch (e) {}
16998
16999	return supportsPassive;
17000}();
17001
17002var html5Elements = ['source', 'track', 'audio', 'video'];
17003var video = void 0;
17004
17005for (var i = 0, total = html5Elements.length; i < total; i++) {
17006	video = _document2.default.createElement(html5Elements[i]);
17007}
17008
17009var SUPPORTS_NATIVE_HLS = exports.SUPPORTS_NATIVE_HLS = IS_SAFARI || IS_IE && /edge/i.test(UA);
17010
17011var hasiOSFullScreen = video.webkitEnterFullscreen !== undefined;
17012
17013var hasNativeFullscreen = video.requestFullscreen !== undefined;
17014
17015if (hasiOSFullScreen && /mac os x 10_5/i.test(UA)) {
17016	hasNativeFullscreen = false;
17017	hasiOSFullScreen = false;
17018}
17019
17020var hasWebkitNativeFullScreen = video.webkitRequestFullScreen !== undefined;
17021var hasMozNativeFullScreen = video.mozRequestFullScreen !== undefined;
17022var hasMsNativeFullScreen = video.msRequestFullscreen !== undefined;
17023var hasTrueNativeFullScreen = hasWebkitNativeFullScreen || hasMozNativeFullScreen || hasMsNativeFullScreen;
17024var nativeFullScreenEnabled = hasTrueNativeFullScreen;
17025var fullScreenEventName = '';
17026var isFullScreen = void 0,
17027    requestFullScreen = void 0,
17028    cancelFullScreen = void 0;
17029
17030if (hasMozNativeFullScreen) {
17031	nativeFullScreenEnabled = _document2.default.mozFullScreenEnabled;
17032} else if (hasMsNativeFullScreen) {
17033	nativeFullScreenEnabled = _document2.default.msFullscreenEnabled;
17034}
17035
17036if (IS_CHROME) {
17037	hasiOSFullScreen = false;
17038}
17039
17040if (hasTrueNativeFullScreen) {
17041	if (hasWebkitNativeFullScreen) {
17042		fullScreenEventName = 'webkitfullscreenchange';
17043	} else if (hasMozNativeFullScreen) {
17044		fullScreenEventName = 'fullscreenchange';
17045	} else if (hasMsNativeFullScreen) {
17046		fullScreenEventName = 'MSFullscreenChange';
17047	}
17048
17049	exports.isFullScreen = isFullScreen = function isFullScreen() {
17050		if (hasMozNativeFullScreen) {
17051			return _document2.default.mozFullScreen;
17052		} else if (hasWebkitNativeFullScreen) {
17053			return _document2.default.webkitIsFullScreen;
17054		} else if (hasMsNativeFullScreen) {
17055			return _document2.default.msFullscreenElement !== null;
17056		}
17057	};
17058
17059	exports.requestFullScreen = requestFullScreen = function requestFullScreen(el) {
17060		if (hasWebkitNativeFullScreen) {
17061			el.webkitRequestFullScreen();
17062		} else if (hasMozNativeFullScreen) {
17063			el.mozRequestFullScreen();
17064		} else if (hasMsNativeFullScreen) {
17065			el.msRequestFullscreen();
17066		}
17067	};
17068
17069	exports.cancelFullScreen = cancelFullScreen = function cancelFullScreen() {
17070		if (hasWebkitNativeFullScreen) {
17071			_document2.default.webkitCancelFullScreen();
17072		} else if (hasMozNativeFullScreen) {
17073			_document2.default.mozCancelFullScreen();
17074		} else if (hasMsNativeFullScreen) {
17075			_document2.default.msExitFullscreen();
17076		}
17077	};
17078}
17079
17080var HAS_NATIVE_FULLSCREEN = exports.HAS_NATIVE_FULLSCREEN = hasNativeFullscreen;
17081var HAS_WEBKIT_NATIVE_FULLSCREEN = exports.HAS_WEBKIT_NATIVE_FULLSCREEN = hasWebkitNativeFullScreen;
17082var HAS_MOZ_NATIVE_FULLSCREEN = exports.HAS_MOZ_NATIVE_FULLSCREEN = hasMozNativeFullScreen;
17083var HAS_MS_NATIVE_FULLSCREEN = exports.HAS_MS_NATIVE_FULLSCREEN = hasMsNativeFullScreen;
17084var HAS_IOS_FULLSCREEN = exports.HAS_IOS_FULLSCREEN = hasiOSFullScreen;
17085var HAS_TRUE_NATIVE_FULLSCREEN = exports.HAS_TRUE_NATIVE_FULLSCREEN = hasTrueNativeFullScreen;
17086var HAS_NATIVE_FULLSCREEN_ENABLED = exports.HAS_NATIVE_FULLSCREEN_ENABLED = nativeFullScreenEnabled;
17087var FULLSCREEN_EVENT_NAME = exports.FULLSCREEN_EVENT_NAME = fullScreenEventName;
17088exports.isFullScreen = isFullScreen;
17089exports.requestFullScreen = requestFullScreen;
17090exports.cancelFullScreen = cancelFullScreen;
17091
17092
17093_mejs2.default.Features = _mejs2.default.Features || {};
17094_mejs2.default.Features.isiPad = IS_IPAD;
17095_mejs2.default.Features.isiPod = IS_IPOD;
17096_mejs2.default.Features.isiPhone = IS_IPHONE;
17097_mejs2.default.Features.isiOS = _mejs2.default.Features.isiPhone || _mejs2.default.Features.isiPad;
17098_mejs2.default.Features.isAndroid = IS_ANDROID;
17099_mejs2.default.Features.isIE = IS_IE;
17100_mejs2.default.Features.isEdge = IS_EDGE;
17101_mejs2.default.Features.isChrome = IS_CHROME;
17102_mejs2.default.Features.isFirefox = IS_FIREFOX;
17103_mejs2.default.Features.isSafari = IS_SAFARI;
17104_mejs2.default.Features.isStockAndroid = IS_STOCK_ANDROID;
17105_mejs2.default.Features.hasMSE = HAS_MSE;
17106_mejs2.default.Features.supportsNativeHLS = SUPPORTS_NATIVE_HLS;
17107_mejs2.default.Features.supportsPointerEvents = SUPPORT_POINTER_EVENTS;
17108_mejs2.default.Features.supportsPassiveEvent = SUPPORT_PASSIVE_EVENT;
17109_mejs2.default.Features.hasiOSFullScreen = HAS_IOS_FULLSCREEN;
17110_mejs2.default.Features.hasNativeFullscreen = HAS_NATIVE_FULLSCREEN;
17111_mejs2.default.Features.hasWebkitNativeFullScreen = HAS_WEBKIT_NATIVE_FULLSCREEN;
17112_mejs2.default.Features.hasMozNativeFullScreen = HAS_MOZ_NATIVE_FULLSCREEN;
17113_mejs2.default.Features.hasMsNativeFullScreen = HAS_MS_NATIVE_FULLSCREEN;
17114_mejs2.default.Features.hasTrueNativeFullScreen = HAS_TRUE_NATIVE_FULLSCREEN;
17115_mejs2.default.Features.nativeFullScreenEnabled = HAS_NATIVE_FULLSCREEN_ENABLED;
17116_mejs2.default.Features.fullScreenEventName = FULLSCREEN_EVENT_NAME;
17117_mejs2.default.Features.isFullScreen = isFullScreen;
17118_mejs2.default.Features.requestFullScreen = requestFullScreen;
17119_mejs2.default.Features.cancelFullScreen = cancelFullScreen;
17120
17121},{"2":2,"3":3,"8":8}],25:[function(_dereq_,module,exports){
17122'use strict';
17123
17124Object.defineProperty(exports, "__esModule", {
17125	value: true
17126});
17127exports.removeClass = exports.addClass = exports.hasClass = undefined;
17128exports.loadScript = loadScript;
17129exports.offset = offset;
17130exports.toggleClass = toggleClass;
17131exports.fadeOut = fadeOut;
17132exports.fadeIn = fadeIn;
17133exports.siblings = siblings;
17134exports.visible = visible;
17135exports.ajax = ajax;
17136
17137var _window = _dereq_(3);
17138
17139var _window2 = _interopRequireDefault(_window);
17140
17141var _document = _dereq_(2);
17142
17143var _document2 = _interopRequireDefault(_document);
17144
17145var _mejs = _dereq_(8);
17146
17147var _mejs2 = _interopRequireDefault(_mejs);
17148
17149function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
17150
17151function loadScript(url) {
17152	return new Promise(function (resolve, reject) {
17153		var script = _document2.default.createElement('script');
17154		script.src = url;
17155		script.async = true;
17156		script.onload = function () {
17157			script.remove();
17158			resolve();
17159		};
17160		script.onerror = function () {
17161			script.remove();
17162			reject();
17163		};
17164		_document2.default.head.appendChild(script);
17165	});
17166}
17167
17168function offset(el) {
17169	var rect = el.getBoundingClientRect(),
17170	    scrollLeft = _window2.default.pageXOffset || _document2.default.documentElement.scrollLeft,
17171	    scrollTop = _window2.default.pageYOffset || _document2.default.documentElement.scrollTop;
17172	return { top: rect.top + scrollTop, left: rect.left + scrollLeft };
17173}
17174
17175var hasClassMethod = void 0,
17176    addClassMethod = void 0,
17177    removeClassMethod = void 0;
17178
17179if ('classList' in _document2.default.documentElement) {
17180	hasClassMethod = function hasClassMethod(el, className) {
17181		return el.classList !== undefined && el.classList.contains(className);
17182	};
17183	addClassMethod = function addClassMethod(el, className) {
17184		return el.classList.add(className);
17185	};
17186	removeClassMethod = function removeClassMethod(el, className) {
17187		return el.classList.remove(className);
17188	};
17189} else {
17190	hasClassMethod = function hasClassMethod(el, className) {
17191		return new RegExp('\\b' + className + '\\b').test(el.className);
17192	};
17193	addClassMethod = function addClassMethod(el, className) {
17194		if (!hasClass(el, className)) {
17195			el.className += ' ' + className;
17196		}
17197	};
17198	removeClassMethod = function removeClassMethod(el, className) {
17199		el.className = el.className.replace(new RegExp('\\b' + className + '\\b', 'g'), '');
17200	};
17201}
17202
17203var hasClass = exports.hasClass = hasClassMethod;
17204var addClass = exports.addClass = addClassMethod;
17205var removeClass = exports.removeClass = removeClassMethod;
17206
17207function toggleClass(el, className) {
17208	hasClass(el, className) ? removeClass(el, className) : addClass(el, className);
17209}
17210
17211function fadeOut(el) {
17212	var duration = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 400;
17213	var callback = arguments[2];
17214
17215	if (!el.style.opacity) {
17216		el.style.opacity = 1;
17217	}
17218
17219	var start = null;
17220	_window2.default.requestAnimationFrame(function animate(timestamp) {
17221		start = start || timestamp;
17222		var progress = timestamp - start;
17223		var opacity = parseFloat(1 - progress / duration, 2);
17224		el.style.opacity = opacity < 0 ? 0 : opacity;
17225		if (progress > duration) {
17226			if (callback && typeof callback === 'function') {
17227				callback();
17228			}
17229		} else {
17230			_window2.default.requestAnimationFrame(animate);
17231		}
17232	});
17233}
17234
17235function fadeIn(el) {
17236	var duration = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 400;
17237	var callback = arguments[2];
17238
17239	if (!el.style.opacity) {
17240		el.style.opacity = 0;
17241	}
17242
17243	var start = null;
17244	_window2.default.requestAnimationFrame(function animate(timestamp) {
17245		start = start || timestamp;
17246		var progress = timestamp - start;
17247		var opacity = parseFloat(progress / duration, 2);
17248		el.style.opacity = opacity > 1 ? 1 : opacity;
17249		if (progress > duration) {
17250			if (callback && typeof callback === 'function') {
17251				callback();
17252			}
17253		} else {
17254			_window2.default.requestAnimationFrame(animate);
17255		}
17256	});
17257}
17258
17259function siblings(el, filter) {
17260	var siblings = [];
17261	el = el.parentNode.firstChild;
17262	do {
17263		if (!filter || filter(el)) {
17264			siblings.push(el);
17265		}
17266	} while (el = el.nextSibling);
17267	return siblings;
17268}
17269
17270function visible(elem) {
17271	if (elem.getClientRects !== undefined && elem.getClientRects === 'function') {
17272		return !!(elem.offsetWidth || elem.offsetHeight || elem.getClientRects().length);
17273	}
17274	return !!(elem.offsetWidth || elem.offsetHeight);
17275}
17276
17277function ajax(url, dataType, success, error) {
17278	var xhr = _window2.default.XMLHttpRequest ? new XMLHttpRequest() : new ActiveXObject('Microsoft.XMLHTTP');
17279
17280	var type = 'application/x-www-form-urlencoded; charset=UTF-8',
17281	    completed = false,
17282	    accept = '*/'.concat('*');
17283
17284	switch (dataType) {
17285		case 'text':
17286			type = 'text/plain';
17287			break;
17288		case 'json':
17289			type = 'application/json, text/javascript';
17290			break;
17291		case 'html':
17292			type = 'text/html';
17293			break;
17294		case 'xml':
17295			type = 'application/xml, text/xml';
17296			break;
17297	}
17298
17299	if (type !== 'application/x-www-form-urlencoded') {
17300		accept = type + ', */*; q=0.01';
17301	}
17302
17303	if (xhr) {
17304		xhr.open('GET', url, true);
17305		xhr.setRequestHeader('Accept', accept);
17306		xhr.onreadystatechange = function () {
17307			if (completed) {
17308				return;
17309			}
17310
17311			if (xhr.readyState === 4) {
17312				if (xhr.status === 200) {
17313					completed = true;
17314					var data = void 0;
17315					switch (dataType) {
17316						case 'json':
17317							data = JSON.parse(xhr.responseText);
17318							break;
17319						case 'xml':
17320							data = xhr.responseXML;
17321							break;
17322						default:
17323							data = xhr.responseText;
17324							break;
17325					}
17326					success(data);
17327				} else if (typeof error === 'function') {
17328					error(xhr.status);
17329				}
17330			}
17331		};
17332
17333		xhr.send();
17334	}
17335}
17336
17337_mejs2.default.Utils = _mejs2.default.Utils || {};
17338_mejs2.default.Utils.offset = offset;
17339_mejs2.default.Utils.hasClass = hasClass;
17340_mejs2.default.Utils.addClass = addClass;
17341_mejs2.default.Utils.removeClass = removeClass;
17342_mejs2.default.Utils.toggleClass = toggleClass;
17343_mejs2.default.Utils.fadeIn = fadeIn;
17344_mejs2.default.Utils.fadeOut = fadeOut;
17345_mejs2.default.Utils.siblings = siblings;
17346_mejs2.default.Utils.visible = visible;
17347_mejs2.default.Utils.ajax = ajax;
17348_mejs2.default.Utils.loadScript = loadScript;
17349
17350},{"2":2,"3":3,"8":8}],26:[function(_dereq_,module,exports){
17351'use strict';
17352
17353Object.defineProperty(exports, "__esModule", {
17354	value: true
17355});
17356exports.escapeHTML = escapeHTML;
17357exports.debounce = debounce;
17358exports.isObjectEmpty = isObjectEmpty;
17359exports.splitEvents = splitEvents;
17360exports.createEvent = createEvent;
17361exports.isNodeAfter = isNodeAfter;
17362exports.isString = isString;
17363
17364var _mejs = _dereq_(8);
17365
17366var _mejs2 = _interopRequireDefault(_mejs);
17367
17368function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
17369
17370function escapeHTML(input) {
17371
17372	if (typeof input !== 'string') {
17373		throw new Error('Argument passed must be a string');
17374	}
17375
17376	var map = {
17377		'&': '&amp;',
17378		'<': '&lt;',
17379		'>': '&gt;',
17380		'"': '&quot;'
17381	};
17382
17383	return input.replace(/[&<>"]/g, function (c) {
17384		return map[c];
17385	});
17386}
17387
17388function debounce(func, wait) {
17389	var _this = this,
17390	    _arguments = arguments;
17391
17392	var immediate = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;
17393
17394
17395	if (typeof func !== 'function') {
17396		throw new Error('First argument must be a function');
17397	}
17398
17399	if (typeof wait !== 'number') {
17400		throw new Error('Second argument must be a numeric value');
17401	}
17402
17403	var timeout = void 0;
17404	return function () {
17405		var context = _this,
17406		    args = _arguments;
17407		var later = function later() {
17408			timeout = null;
17409			if (!immediate) {
17410				func.apply(context, args);
17411			}
17412		};
17413		var callNow = immediate && !timeout;
17414		clearTimeout(timeout);
17415		timeout = setTimeout(later, wait);
17416
17417		if (callNow) {
17418			func.apply(context, args);
17419		}
17420	};
17421}
17422
17423function isObjectEmpty(instance) {
17424	return Object.getOwnPropertyNames(instance).length <= 0;
17425}
17426
17427function splitEvents(events, id) {
17428	var rwindow = /^((after|before)print|(before)?unload|hashchange|message|o(ff|n)line|page(hide|show)|popstate|resize|storage)\b/;
17429
17430	var ret = { d: [], w: [] };
17431	(events || '').split(' ').forEach(function (v) {
17432		var eventName = '' + v + (id ? '.' + id : '');
17433
17434		if (eventName.startsWith('.')) {
17435			ret.d.push(eventName);
17436			ret.w.push(eventName);
17437		} else {
17438			ret[rwindow.test(v) ? 'w' : 'd'].push(eventName);
17439		}
17440	});
17441
17442	ret.d = ret.d.join(' ');
17443	ret.w = ret.w.join(' ');
17444	return ret;
17445}
17446
17447function createEvent(eventName, target, isIframe) {
17448
17449	if (typeof eventName !== 'string') {
17450		throw new Error('Event name must be a string');
17451	}
17452
17453	var eventFrags = eventName.match(/([a-z]+\.([a-z]+))/i),
17454	    detail = {
17455		target: target,
17456		isIframe: isIframe
17457	};
17458
17459	if (eventFrags !== null) {
17460		eventName = eventFrags[1];
17461		detail.namespace = eventFrags[2];
17462	}
17463
17464	return new window.CustomEvent(eventName, {
17465		detail: detail
17466	});
17467}
17468
17469function isNodeAfter(sourceNode, targetNode) {
17470
17471	return !!(sourceNode && targetNode && sourceNode.compareDocumentPosition(targetNode) & 2);
17472}
17473
17474function isString(value) {
17475	return typeof value === 'string';
17476}
17477
17478_mejs2.default.Utils = _mejs2.default.Utils || {};
17479_mejs2.default.Utils.escapeHTML = escapeHTML;
17480_mejs2.default.Utils.debounce = debounce;
17481_mejs2.default.Utils.isObjectEmpty = isObjectEmpty;
17482_mejs2.default.Utils.splitEvents = splitEvents;
17483_mejs2.default.Utils.createEvent = createEvent;
17484_mejs2.default.Utils.isNodeAfter = isNodeAfter;
17485_mejs2.default.Utils.isString = isString;
17486
17487},{"8":8}],27:[function(_dereq_,module,exports){
17488'use strict';
17489
17490Object.defineProperty(exports, "__esModule", {
17491	value: true
17492});
17493exports.generateControlButton = generateControlButton;
17494
17495var _mejs = _dereq_(8);
17496
17497var _mejs2 = _interopRequireDefault(_mejs);
17498
17499function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
17500
17501function generateControlButton(playerId, ariaLabel, title, iconSprite, icons, classPrefix) {
17502	var buttonClass = arguments.length > 6 && arguments[6] !== undefined ? arguments[6] : null;
17503	var ariaDescribedby = arguments.length > 7 && arguments[7] !== undefined ? arguments[7] : '';
17504	var ariaPressed = arguments.length > 8 && arguments[8] !== undefined ? arguments[8] : null;
17505
17506
17507	if (typeof playerId !== 'string') {
17508		throw new Error('`ariaControls` argument must be a string');
17509	}
17510	if (typeof ariaLabel !== 'string') {
17511		throw new Error('`ariaLabel` argument must be a string');
17512	}
17513	if (typeof title !== 'string') {
17514		throw new Error('`title` argument must be a string');
17515	}
17516	if (typeof iconSprite !== 'string') {
17517		throw new Error('`iconSprite` argument must be a string');
17518	}
17519	if (typeof ariaDescribedby !== 'string') {
17520		throw new Error('`ariaDescribedby` argument must be a string');
17521	}
17522	if (!Array.isArray(icons)) {
17523		throw new Error('`icons` argument must be an array');
17524	}
17525	if (typeof classPrefix !== 'string') {
17526		throw new Error('`classPrefix` argument must be a string');
17527	}
17528
17529	var className = buttonClass ? 'class="' + buttonClass + '" ' : '';
17530
17531	var ariaDescribedbyAttr = ariaDescribedby !== '' ? 'aria-describedby="' + ariaDescribedby + '" ' : '';
17532
17533	var ariaPressedAttr = ariaPressed !== null ? 'aria-pressed="' + ariaPressed + '"' : '';
17534
17535	var iconHtml = icons.map(function (icon) {
17536		return '<svg xmlns="http://www.w3.org/2000/svg" id="' + playerId + '-' + icon + '" class="' + classPrefix + icon + '" aria-hidden="true" focusable="false">\n\t\t\t\t<use xlink:href="' + iconSprite + '#' + icon + '"></use>\n\t\t\t</svg>\n';
17537	});
17538
17539	return '<button ' + className + ' type="button" aria-controls="' + playerId + '" title="' + title + '" aria-label="' + ariaLabel + '" ' + ariaDescribedbyAttr + ' ' + ariaPressedAttr + '>\n\t\t\t' + iconHtml.join('') + '\n\t\t</button>';
17540}
17541
17542_mejs2.default.Utils = _mejs2.default.Utils || {};
17543_mejs2.default.Utils.generateControlButton = generateControlButton;
17544
17545},{"8":8}],28:[function(_dereq_,module,exports){
17546'use strict';
17547
17548Object.defineProperty(exports, "__esModule", {
17549	value: true
17550});
17551exports.typeChecks = undefined;
17552exports.absolutizeUrl = absolutizeUrl;
17553exports.formatType = formatType;
17554exports.getMimeFromType = getMimeFromType;
17555exports.getTypeFromFile = getTypeFromFile;
17556exports.getExtension = getExtension;
17557exports.normalizeExtension = normalizeExtension;
17558
17559var _mejs = _dereq_(8);
17560
17561var _mejs2 = _interopRequireDefault(_mejs);
17562
17563var _general = _dereq_(26);
17564
17565function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
17566
17567var typeChecks = exports.typeChecks = [];
17568
17569function absolutizeUrl(url) {
17570
17571	if (typeof url !== 'string') {
17572		throw new Error('`url` argument must be a string');
17573	}
17574
17575	var el = document.createElement('div');
17576	el.innerHTML = '<a href="' + (0, _general.escapeHTML)(url) + '">x</a>';
17577	return el.firstChild.href;
17578}
17579
17580function formatType(url) {
17581	var type = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : '';
17582
17583	return url && !type ? getTypeFromFile(url) : type;
17584}
17585
17586function getMimeFromType(type) {
17587
17588	if (typeof type !== 'string') {
17589		throw new Error('`type` argument must be a string');
17590	}
17591
17592	return type && type.indexOf(';') > -1 ? type.substr(0, type.indexOf(';')) : type;
17593}
17594
17595function getTypeFromFile(url) {
17596
17597	if (typeof url !== 'string') {
17598		throw new Error('`url` argument must be a string');
17599	}
17600
17601	for (var i = 0, total = typeChecks.length; i < total; i++) {
17602		var type = typeChecks[i](url);
17603
17604		if (type) {
17605			return type;
17606		}
17607	}
17608
17609	var ext = getExtension(url),
17610	    normalizedExt = normalizeExtension(ext);
17611
17612	var mime = 'video/mp4';
17613
17614	if (normalizedExt) {
17615		if (~['mp4', 'm4v', 'ogg', 'ogv', 'webm', 'mpeg'].indexOf(normalizedExt)) {
17616			mime = 'video/' + normalizedExt;
17617		} else if ('mov' === normalizedExt) {
17618			mime = 'video/quicktime';
17619		} else if (~['mp3', 'oga', 'wav', 'mid', 'midi'].indexOf(normalizedExt)) {
17620			mime = 'audio/' + normalizedExt;
17621		}
17622	}
17623
17624	return mime;
17625}
17626
17627function getExtension(url) {
17628
17629	if (typeof url !== 'string') {
17630		throw new Error('`url` argument must be a string');
17631	}
17632
17633	var baseUrl = url.split('?')[0],
17634	    baseName = baseUrl.split('\\').pop().split('/').pop();
17635	return ~baseName.indexOf('.') ? baseName.substring(baseName.lastIndexOf('.') + 1) : '';
17636}
17637
17638function normalizeExtension(extension) {
17639
17640	if (typeof extension !== 'string') {
17641		throw new Error('`extension` argument must be a string');
17642	}
17643
17644	switch (extension) {
17645		case 'mp4':
17646		case 'm4v':
17647			return 'mp4';
17648		case 'webm':
17649		case 'webma':
17650		case 'webmv':
17651			return 'webm';
17652		case 'ogg':
17653		case 'oga':
17654		case 'ogv':
17655			return 'ogg';
17656		default:
17657			return extension;
17658	}
17659}
17660
17661_mejs2.default.Utils = _mejs2.default.Utils || {};
17662_mejs2.default.Utils.typeChecks = typeChecks;
17663_mejs2.default.Utils.absolutizeUrl = absolutizeUrl;
17664_mejs2.default.Utils.formatType = formatType;
17665_mejs2.default.Utils.getMimeFromType = getMimeFromType;
17666_mejs2.default.Utils.getTypeFromFile = getTypeFromFile;
17667_mejs2.default.Utils.getExtension = getExtension;
17668_mejs2.default.Utils.normalizeExtension = normalizeExtension;
17669
17670},{"26":26,"8":8}],29:[function(_dereq_,module,exports){
17671'use strict';
17672
17673var _document = _dereq_(2);
17674
17675var _document2 = _interopRequireDefault(_document);
17676
17677var _promisePolyfill = _dereq_(4);
17678
17679var _promisePolyfill2 = _interopRequireDefault(_promisePolyfill);
17680
17681var _svg4everybody = _dereq_(5);
17682
17683var _svg4everybody2 = _interopRequireDefault(_svg4everybody);
17684
17685function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
17686
17687(function (arr) {
17688	arr.forEach(function (item) {
17689		if (item.hasOwnProperty('remove')) {
17690			return;
17691		}
17692		Object.defineProperty(item, 'remove', {
17693			configurable: true,
17694			enumerable: true,
17695			writable: true,
17696			value: function remove() {
17697				this.parentNode.removeChild(this);
17698			}
17699		});
17700	});
17701})([Element.prototype, CharacterData.prototype, DocumentType.prototype]);
17702
17703(function () {
17704
17705	if (typeof window.CustomEvent === 'function') {
17706		return false;
17707	}
17708
17709	function CustomEvent(event, params) {
17710		params = params || { bubbles: false, cancelable: false, detail: undefined };
17711		var evt = _document2.default.createEvent('CustomEvent');
17712		evt.initCustomEvent(event, params.bubbles, params.cancelable, params.detail);
17713		return evt;
17714	}
17715
17716	CustomEvent.prototype = window.Event.prototype;
17717	window.CustomEvent = CustomEvent;
17718})();
17719
17720if (typeof Object.assign !== 'function') {
17721	Object.assign = function (target) {
17722
17723		if (target === null || target === undefined) {
17724			throw new TypeError('Cannot convert undefined or null to object');
17725		}
17726
17727		var to = Object(target);
17728
17729		for (var index = 1, total = arguments.length; index < total; index++) {
17730			var nextSource = arguments[index];
17731
17732			if (nextSource !== null) {
17733				for (var nextKey in nextSource) {
17734					if (Object.prototype.hasOwnProperty.call(nextSource, nextKey)) {
17735						to[nextKey] = nextSource[nextKey];
17736					}
17737				}
17738			}
17739		}
17740		return to;
17741	};
17742}
17743
17744if (!String.prototype.startsWith) {
17745	String.prototype.startsWith = function (searchString, position) {
17746		position = position || 0;
17747		return this.substr(position, searchString.length) === searchString;
17748	};
17749}
17750
17751if (!Element.prototype.matches) {
17752	Element.prototype.matches = Element.prototype.matchesSelector || Element.prototype.mozMatchesSelector || Element.prototype.msMatchesSelector || Element.prototype.oMatchesSelector || Element.prototype.webkitMatchesSelector || function (s) {
17753		var matches = (this.document || this.ownerDocument).querySelectorAll(s),
17754		    i = matches.length - 1;
17755		while (--i >= 0 && matches.item(i) !== this) {}
17756		return i > -1;
17757	};
17758}
17759
17760if (window.Element && !Element.prototype.closest) {
17761	Element.prototype.closest = function (s) {
17762		var matches = (this.document || this.ownerDocument).querySelectorAll(s),
17763		    i = void 0,
17764		    el = this;
17765		do {
17766			i = matches.length;
17767			while (--i >= 0 && matches.item(i) !== el) {}
17768		} while (i < 0 && (el = el.parentElement));
17769		return el;
17770	};
17771}
17772
17773(function () {
17774	var lastTime = 0;
17775	var vendors = ['ms', 'moz', 'webkit', 'o'];
17776	for (var x = 0; x < vendors.length && !window.requestAnimationFrame; ++x) {
17777		window.requestAnimationFrame = window[vendors[x] + 'RequestAnimationFrame'];
17778		window.cancelAnimationFrame = window[vendors[x] + 'CancelAnimationFrame'] || window[vendors[x] + 'CancelRequestAnimationFrame'];
17779	}
17780
17781	if (!window.requestAnimationFrame) window.requestAnimationFrame = function (callback) {
17782		var currTime = new Date().getTime();
17783		var timeToCall = Math.max(0, 16 - (currTime - lastTime));
17784		var id = window.setTimeout(function () {
17785			callback(currTime + timeToCall);
17786		}, timeToCall);
17787		lastTime = currTime + timeToCall;
17788		return id;
17789	};
17790
17791	if (!window.cancelAnimationFrame) window.cancelAnimationFrame = function (id) {
17792		clearTimeout(id);
17793	};
17794})();
17795
17796if (/firefox/i.test(navigator.userAgent)) {
17797	var getComputedStyle = window.getComputedStyle;
17798	window.getComputedStyle = function (el, pseudoEl) {
17799		var t = getComputedStyle(el, pseudoEl);
17800		return t === null ? { getPropertyValue: function getPropertyValue() {} } : t;
17801	};
17802}
17803
17804if (!window.Promise) {
17805	window.Promise = _promisePolyfill2.default;
17806}
17807
17808(0, _svg4everybody2.default)();
17809
17810(function (constructor) {
17811	if (constructor && constructor.prototype && constructor.prototype.children === null) {
17812		Object.defineProperty(constructor.prototype, 'children', {
17813			get: function get() {
17814				var i = 0,
17815				    node = void 0,
17816				    nodes = this.childNodes,
17817				    children = [];
17818				while (node = nodes[i++]) {
17819					if (node.nodeType === 1) {
17820						children.push(node);
17821					}
17822				}
17823				return children;
17824			}
17825		});
17826	}
17827})(window.Node || window.Element);
17828
17829},{"2":2,"4":4,"5":5}],30:[function(_dereq_,module,exports){
17830'use strict';
17831
17832Object.defineProperty(exports, "__esModule", {
17833	value: true
17834});
17835exports.isDropFrame = isDropFrame;
17836exports.secondsToTimeCode = secondsToTimeCode;
17837exports.timeCodeToSeconds = timeCodeToSeconds;
17838exports.calculateTimeFormat = calculateTimeFormat;
17839exports.convertSMPTEtoSeconds = convertSMPTEtoSeconds;
17840
17841var _mejs = _dereq_(8);
17842
17843var _mejs2 = _interopRequireDefault(_mejs);
17844
17845function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
17846
17847function isDropFrame() {
17848	var fps = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 25;
17849
17850	return !(fps % 1 === 0);
17851}
17852function secondsToTimeCode(time) {
17853	var forceHours = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
17854	var showFrameCount = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;
17855	var fps = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : 25;
17856	var secondsDecimalLength = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : 0;
17857	var timeFormat = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : 'hh:mm:ss';
17858
17859
17860	time = !time || typeof time !== 'number' || time < 0 ? 0 : time;
17861
17862	var dropFrames = Math.round(fps * 0.066666),
17863	    timeBase = Math.round(fps),
17864	    framesPer24Hours = Math.round(fps * 3600) * 24,
17865	    framesPer10Minutes = Math.round(fps * 600),
17866	    frameSep = isDropFrame(fps) ? ';' : ':',
17867	    hours = void 0,
17868	    minutes = void 0,
17869	    seconds = void 0,
17870	    frames = void 0,
17871	    f = Math.round(time * fps);
17872
17873	if (isDropFrame(fps)) {
17874
17875		if (f < 0) {
17876			f = framesPer24Hours + f;
17877		}
17878
17879		f = f % framesPer24Hours;
17880
17881		var d = Math.floor(f / framesPer10Minutes);
17882		var m = f % framesPer10Minutes;
17883		f = f + dropFrames * 9 * d;
17884		if (m > dropFrames) {
17885			f = f + dropFrames * Math.floor((m - dropFrames) / Math.round(timeBase * 60 - dropFrames));
17886		}
17887
17888		var timeBaseDivision = Math.floor(f / timeBase);
17889
17890		hours = Math.floor(Math.floor(timeBaseDivision / 60) / 60);
17891		minutes = Math.floor(timeBaseDivision / 60) % 60;
17892
17893		if (showFrameCount) {
17894			seconds = timeBaseDivision % 60;
17895		} else {
17896			seconds = Math.floor(f / timeBase % 60).toFixed(secondsDecimalLength);
17897		}
17898	} else {
17899		hours = Math.floor(time / 3600) % 24;
17900		minutes = Math.floor(time / 60) % 60;
17901		if (showFrameCount) {
17902			seconds = Math.floor(time % 60);
17903		} else {
17904			seconds = Math.floor(time % 60).toFixed(secondsDecimalLength);
17905		}
17906	}
17907	hours = hours <= 0 ? 0 : hours;
17908	minutes = minutes <= 0 ? 0 : minutes;
17909	seconds = seconds <= 0 ? 0 : seconds;
17910
17911	seconds = seconds === 60 ? 0 : seconds;
17912	minutes = minutes === 60 ? 0 : minutes;
17913
17914	var timeFormatFrags = timeFormat.split(':');
17915	var timeFormatSettings = {};
17916	for (var i = 0, total = timeFormatFrags.length; i < total; ++i) {
17917		var unique = '';
17918		for (var j = 0, t = timeFormatFrags[i].length; j < t; j++) {
17919			if (unique.indexOf(timeFormatFrags[i][j]) < 0) {
17920				unique += timeFormatFrags[i][j];
17921			}
17922		}
17923		if (~['f', 's', 'm', 'h'].indexOf(unique)) {
17924			timeFormatSettings[unique] = timeFormatFrags[i].length;
17925		}
17926	}
17927
17928	var result = forceHours || hours > 0 ? (hours < 10 && timeFormatSettings.h > 1 ? '0' + hours : hours) + ':' : '';
17929	result += (minutes < 10 && timeFormatSettings.m > 1 ? '0' + minutes : minutes) + ':';
17930	result += '' + (seconds < 10 && timeFormatSettings.s > 1 ? '0' + seconds : seconds);
17931
17932	if (showFrameCount) {
17933		frames = (f % timeBase).toFixed(0);
17934		frames = frames <= 0 ? 0 : frames;
17935		result += frames < 10 && timeFormatSettings.f ? frameSep + '0' + frames : '' + frameSep + frames;
17936	}
17937
17938	return result;
17939}
17940
17941function timeCodeToSeconds(time) {
17942	var fps = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 25;
17943
17944
17945	if (typeof time !== 'string') {
17946		throw new TypeError('Time must be a string');
17947	}
17948
17949	if (time.indexOf(';') > 0) {
17950		time = time.replace(';', ':');
17951	}
17952
17953	if (!/\d{2}(\:\d{2}){0,3}/i.test(time)) {
17954		throw new TypeError('Time code must have the format `00:00:00`');
17955	}
17956
17957	var parts = time.split(':');
17958
17959	var output = void 0,
17960	    hours = 0,
17961	    minutes = 0,
17962	    seconds = 0,
17963	    frames = 0,
17964	    totalMinutes = 0,
17965	    dropFrames = Math.round(fps * 0.066666),
17966	    timeBase = Math.round(fps),
17967	    hFrames = timeBase * 3600,
17968	    mFrames = timeBase * 60;
17969
17970	switch (parts.length) {
17971		default:
17972		case 1:
17973			seconds = parseInt(parts[0], 10);
17974			break;
17975		case 2:
17976			minutes = parseInt(parts[0], 10);
17977			seconds = parseInt(parts[1], 10);
17978			break;
17979		case 3:
17980			hours = parseInt(parts[0], 10);
17981			minutes = parseInt(parts[1], 10);
17982			seconds = parseInt(parts[2], 10);
17983			break;
17984		case 4:
17985			hours = parseInt(parts[0], 10);
17986			minutes = parseInt(parts[1], 10);
17987			seconds = parseInt(parts[2], 10);
17988			frames = parseInt(parts[3], 10);
17989			break;
17990	}
17991
17992	if (isDropFrame(fps)) {
17993		totalMinutes = 60 * hours + minutes;
17994		output = hFrames * hours + mFrames * minutes + timeBase * seconds + frames - dropFrames * (totalMinutes - Math.floor(totalMinutes / 10));
17995	} else {
17996		output = (hFrames * hours + mFrames * minutes + fps * seconds + frames) / fps;
17997	}
17998
17999	return parseFloat(output.toFixed(3));
18000}
18001
18002function calculateTimeFormat(time, options) {
18003	var fps = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 25;
18004
18005
18006	time = !time || typeof time !== 'number' || time < 0 ? 0 : time;
18007
18008	var hours = Math.floor(time / 3600) % 24,
18009	    minutes = Math.floor(time / 60) % 60,
18010	    seconds = Math.floor(time % 60),
18011	    frames = Math.floor((time % 1 * fps).toFixed(3)),
18012	    lis = [[frames, 'f'], [seconds, 's'], [minutes, 'm'], [hours, 'h']];
18013
18014	var format = options.timeFormat,
18015	    firstTwoPlaces = format[1] === format[0],
18016	    separatorIndex = firstTwoPlaces ? 2 : 1,
18017	    separator = format.length < separatorIndex ? format[separatorIndex] : ':',
18018	    firstChar = format[0],
18019	    required = false;
18020
18021	for (var i = 0, len = lis.length; i < len; i++) {
18022		if (~format.indexOf(lis[i][1])) {
18023			required = true;
18024		} else if (required) {
18025			var hasNextValue = false;
18026			for (var j = i; j < len; j++) {
18027				if (lis[j][0] > 0) {
18028					hasNextValue = true;
18029					break;
18030				}
18031			}
18032
18033			if (!hasNextValue) {
18034				break;
18035			}
18036
18037			if (!firstTwoPlaces) {
18038				format = firstChar + format;
18039			}
18040			format = lis[i][1] + separator + format;
18041			if (firstTwoPlaces) {
18042				format = lis[i][1] + format;
18043			}
18044			firstChar = lis[i][1];
18045		}
18046	}
18047
18048	options.timeFormat = format;
18049}
18050
18051function convertSMPTEtoSeconds(SMPTE) {
18052
18053	if (typeof SMPTE !== 'string') {
18054		throw new TypeError('Argument must be a string value');
18055	}
18056
18057	SMPTE = SMPTE.replace(',', '.');
18058
18059	var decimalLen = ~SMPTE.indexOf('.') ? SMPTE.split('.')[1].length : 0;
18060
18061	var secs = 0,
18062	    multiplier = 1;
18063
18064	SMPTE = SMPTE.split(':').reverse();
18065
18066	for (var i = 0, total = SMPTE.length; i < total; i++) {
18067		multiplier = 1;
18068		if (i > 0) {
18069			multiplier = Math.pow(60, i);
18070		}
18071		secs += Number(SMPTE[i]) * multiplier;
18072	}
18073	return Number(secs.toFixed(decimalLen));
18074}
18075
18076_mejs2.default.Utils = _mejs2.default.Utils || {};
18077_mejs2.default.Utils.secondsToTimeCode = secondsToTimeCode;
18078_mejs2.default.Utils.timeCodeToSeconds = timeCodeToSeconds;
18079_mejs2.default.Utils.calculateTimeFormat = calculateTimeFormat;
18080_mejs2.default.Utils.convertSMPTEtoSeconds = convertSMPTEtoSeconds;
18081
18082},{"8":8}]},{},[29,7,6,16,22,20,21,23,17,19,18,10,11,12,13,14,15]);
vendor: 4,086 bytes, lines 18083-18239
18083
18084/*!
18085 * jQuery UI 1.8.16
18086 *
18087 * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
18088 * Dual licensed under the MIT or GPL Version 2 licenses.
18089 * http://jquery.org/license
18090 *
18091 * http://docs.jquery.com/UI
18092 */
18093(function( $, undefined ) {
18094
18095// prevent duplicate loading
18096// this is only a problem because we proxy existing functions
18097// and we don't want to double proxy them
18098$.ui = $.ui || {};
18099if ( $.ui.version ) {
18100	return;
18101}
18102
18103$.extend( $.ui, {
18104	version: "1.8.16",
18105
18106	keyCode: {
18107		ALT: 18,
18108		BACKSPACE: 8,
18109		CAPS_LOCK: 20,
18110		COMMA: 188,
18111		COMMAND: 91,
18112		COMMAND_LEFT: 91, // COMMAND
18113		COMMAND_RIGHT: 93,
18114		CONTROL: 17,
18115		DELETE: 46,
18116		DOWN: 40,
18117		END: 35,
18118		ENTER: 13,
18119		ESCAPE: 27,
18120		HOME: 36,
18121		INSERT: 45,
18122		LEFT: 37,
18123		MENU: 93, // COMMAND_RIGHT
18124		NUMPAD_ADD: 107,
18125		NUMPAD_DECIMAL: 110,
18126		NUMPAD_DIVIDE: 111,
18127		NUMPAD_ENTER: 108,
18128		NUMPAD_MULTIPLY: 106,
18129		NUMPAD_SUBTRACT: 109,
18130		PAGE_DOWN: 34,
18131		PAGE_UP: 33,
18132		PERIOD: 190,
18133		RIGHT: 39,
18134		SHIFT: 16,
18135		SPACE: 32,
18136		TAB: 9,
18137		UP: 38,
18138		WINDOWS: 91 // COMMAND
18139	}
18140});
18141
18142// plugins
18143$.fn.extend({
18144	propAttr: $.fn.prop || $.fn.attr,
18145
18146	_focus: $.fn.focus,
18147	focus: function( delay, fn ) {
18148		return typeof delay === "number" ?
18149			this.each(function() {
18150				var elem = this;
18151				setTimeout(function() {
18152					$( elem ).focus();
18153					if ( fn ) {
18154						fn.call( elem );
18155					}
18156				}, delay );
18157			}) :
18158			this._focus.apply( this, arguments );
18159	},
18160
18161	scrollParent: function() {
18162		var scrollParent;
18163		if (($.browser.msie && (/(static|relative)/).test(this.css('position'))) || (/absolute/).test(this.css('position'))) {
18164			scrollParent = this.parents().filter(function() {
18165				return (/(relative|absolute|fixed)/).test($.curCSS(this,'position',1)) && (/(auto|scroll)/).test($.curCSS(this,'overflow',1)+$.curCSS(this,'overflow-y',1)+$.curCSS(this,'overflow-x',1));
18166			}).eq(0);
18167		} else {
18168			scrollParent = this.parents().filter(function() {
18169				return (/(auto|scroll)/).test($.curCSS(this,'overflow',1)+$.curCSS(this,'overflow-y',1)+$.curCSS(this,'overflow-x',1));
18170			}).eq(0);
18171		}
18172
18173		return (/fixed/).test(this.css('position')) || !scrollParent.length ? $(document) : scrollParent;
18174	},
18175
18176	zIndex: function( zIndex ) {
18177		if ( zIndex !== undefined ) {
18178			return this.css( "zIndex", zIndex );
18179		}
18180
18181		if ( this.length ) {
18182			var elem = $( this[ 0 ] ), position, value;
18183			while ( elem.length && elem[ 0 ] !== document ) {
18184				// Ignore z-index if position is set to a value where z-index is ignored by the browser
18185				// This makes behavior of this function consistent across browsers
18186				// WebKit always returns auto if the element is positioned
18187				position = elem.css( "position" );
18188				if ( position === "absolute" || position === "relative" || position === "fixed" ) {
18189					// IE returns 0 when zIndex is not specified
18190					// other browsers return a string
18191					// we ignore the case of nested elements with an explicit value of 0
18192					// <div style="z-index: -10;"><div style="z-index: 0;"></div></div>
18193					value = parseInt( elem.css( "zIndex" ), 10 );
18194					if ( !isNaN( value ) && value !== 0 ) {
18195						return value;
18196					}
18197				}
18198				elem = elem.parent();
18199			}
18200		}
18201
18202		return 0;
18203	},
18204
18205	disableSelection: function() {
18206		return this.bind( ( $.support.selectstart ? "selectstart" : "mousedown" ) +
18207			".ui-disableSelection", function( event ) {
18208				event.preventDefault();
18209			});
18210	},
18211
18212	enableSelection: function() {
18213		return this.unbind( ".ui-disableSelection" );
18214	}
18215});
18216
18217$.each( [ "Width", "Height" ], function( i, name ) {
18218	var side = name === "Width" ? [ "Left", "Right" ] : [ "Top", "Bottom" ],
18219		type = name.toLowerCase(),
18220		orig = {
18221			innerWidth: $.fn.innerWidth,
18222			innerHeight: $.fn.innerHeight,
18223			outerWidth: $.fn.outerWidth,
18224			outerHeight: $.fn.outerHeight
18225		};
18226
18227	function reduce( elem, size, border, margin ) {
18228		$.each( side, function() {
18229			size -= parseFloat( $.curCSS( elem, "padding" + this, true) ) || 0;
18230			if ( border ) {
18231				size -= parseFloat( $.curCSS( elem, "border" + this + "Width", true) ) || 0;
18232			}
18233			if ( margin ) {
18234				size -= parseFloat( $.curCSS( elem, "margin" + this, true) ) || 0;
18235			}
18236		});
18237		return size;
18238	}
18239
18240	$.fn[ "inner" + name ] = function( size ) {
18241		if ( size === undefined ) {
18242			return orig[ "inner" + name ].call( this );
18243		}
18244
18245		return this.each(function() {
18246			$( this ).css( type, reduce( this, size ) + "px" );
18247		});
18248	};
18249
18250	$.fn[ "outer" + name] = function( size, margin ) {
18251		if ( typeof size !== "number" ) {
18252			return orig[ "outer" + name ].call( this, size );
18253		}
18254
18255		return this.each(function() {
18256			$( this).css( type, reduce( this, size, true, margin ) + "px" );
18257		});
18258	};
18259});
18260
18261// selectors
18262function focusable( element, isTabIndexNotNaN ) {
18263	var nodeName = element.nodeName.toLowerCase();
18264	if ( "area" === nodeName ) {
18265		var map = element.parentNode,
18266			mapName = map.name,
18267			img;
18268		if ( !element.href || !mapName || map.nodeName.toLowerCase() !== "map" ) {
18269			return false;
18270		}
18271		img = $( "img[usemap=#" + mapName + "]" )[0];
18272		return !!img && visible( img );
18273	}
18274	return ( /input|select|textarea|button|object/.test( nodeName )
18275		? !element.disabled
18276		: "a" == nodeName
18277			? element.href || isTabIndexNotNaN
18278			: isTabIndexNotNaN)
18279		// the element and all of its ancestors must be visible
18280		&& visible( element );
18281}
18282
18283function visible( element ) {
18284	return !$( element ).parents().andSelf().filter(function() {
18285		return $.curCSS( this, "visibility" ) === "hidden" ||
18286			$.expr.filters.hidden( this );
18287	}).length;
18288}
18289
18290$.extend( $.expr[ ":" ], {
18291	data: function( elem, i, match ) {
18292		return !!$.data( elem, match[ 3 ] );
18293	},
18294
18295	focusable: function( element ) {
18296		return focusable( element, !isNaN( $.attr( element, "tabindex" ) ) );
18297	},
18298
18299	tabbable: function( element ) {
18300		var tabIndex = $.attr( element, "tabindex" ),
18301			isTabIndexNaN = isNaN( tabIndex );
18302		return ( isTabIndexNaN || tabIndex >= 0 ) && focusable( element, !isTabIndexNaN );
18303	}
18304});
18305
18306// support
18307$(function() {
18308	var body = document.body,
18309		div = body.appendChild( div = document.createElement( "div" ) );
18310
18311	$.extend( div.style, {
18312		minHeight: "100px",
18313		height: "auto",
18314		padding: 0,
18315		borderWidth: 0
18316	});
18317
18318	$.support.minHeight = div.offsetHeight === 100;
18319	$.support.selectstart = "onselectstart" in div;
18320
18321	// set display to none to avoid a layout bug in IE
18322	// http://dev.jquery.com/ticket/4014
18323	body.removeChild( div ).style.display = "none";
18324});
18325
18326
18327
18328
18329
18330// deprecated
18331$.extend( $.ui, {
18332	// $.ui.plugin is deprecated.  Use the proxy pattern instead.
18333	plugin: {
18334		add: function( module, option, set ) {
18335			var proto = $.ui[ module ].prototype;
18336			for ( var i in set ) {
18337				proto.plugins[ i ] = proto.plugins[ i ] || [];
18338				proto.plugins[ i ].push( [ option, set[ i ] ] );
18339			}
18340		},
18341		call: function( instance, name, args ) {
18342			var set = instance.plugins[ name ];
18343			if ( !set || !instance.element[ 0 ].parentNode ) {
18344				return;
18345			}
18346	
18347			for ( var i = 0; i < set.length; i++ ) {
18348				if ( instance.options[ set[ i ][ 0 ] ] ) {
18349					set[ i ][ 1 ].apply( instance.element, args );
18350				}
18351			}
18352		}
18353	},
18354	
18355	// will be deprecated when we switch to jQuery 1.4 - use jQuery.contains()
18356	contains: function( a, b ) {
18357		return document.compareDocumentPosition ?
18358			a.compareDocumentPosition( b ) & 16 :
18359			a !== b && a.contains( b );
18360	},
18361	
18362	// only used by resizable
18363	hasScroll: function( el, a ) {
18364	
18365		//If overflow is hidden, the element might have extra content, but the user wants to hide it
18366		if ( $( el ).css( "overflow" ) === "hidden") {
18367			return false;
18368		}
18369	
18370		var scroll = ( a && a === "left" ) ? "scrollLeft" : "scrollTop",
18371			has = false;
18372	
18373		if ( el[ scroll ] > 0 ) {
18374			return true;
18375		}
18376	
18377		// TODO: determine which cases actually cause this to happen
18378		// if the element doesn't have the scroll set, see if it's possible to
18379		// set the scroll
18380		el[ scroll ] = 1;
18381		has = ( el[ scroll ] > 0 );
18382		el[ scroll ] = 0;
18383		return has;
18384	},
18385	
18386	// these are odd functions, fix the API or move into individual plugins
18387	isOverAxis: function( x, reference, size ) {
18388		//Determines when x coordinate is over "b" element axis
18389		return ( x > reference ) && ( x < ( reference + size ) );
18390	},
18391	isOver: function( y, x, top, left, height, width ) {
18392		//Determines when x, y coordinates is over "b" element
18393		return $.ui.isOverAxis( y, top, height ) && $.ui.isOverAxis( x, left, width );
18394	}
18395});
18396
18397})( jQuery );
18398
18399/*!
18400 * jQuery UI Widget 1.8.16
18401 *
18402 * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
18403 * Dual licensed under the MIT or GPL Version 2 licenses.
18404 * http://jquery.org/license
18405 *
18406 * http://docs.jquery.com/UI/Widget
18407 */
18408(function( $, undefined ) {
18409
18410// jQuery 1.4+
18411if ( $.cleanData ) {
18412	var _cleanData = $.cleanData;
18413	$.cleanData = function( elems ) {
18414		for ( var i = 0, elem; (elem = elems[i]) != null; i++ ) {
18415			try {
18416				$( elem ).triggerHandler( "remove" );
18417			// http://bugs.jquery.com/ticket/8235
18418			} catch( e ) {}
18419		}
18420		_cleanData( elems );
18421	};
18422} else {
18423	var _remove = $.fn.remove;
18424	$.fn.remove = function( selector, keepData ) {
18425		return this.each(function() {
18426			if ( !keepData ) {
18427				if ( !selector || $.filter( selector, [ this ] ).length ) {
18428					$( "*", this ).add( [ this ] ).each(function() {
18429						try {
18430							$( this ).triggerHandler( "remove" );
18431						// http://bugs.jquery.com/ticket/8235
18432						} catch( e ) {}
18433					});
18434				}
18435			}
18436			return _remove.call( $(this), selector, keepData );
18437		});
18438	};
18439}
18440
18441$.widget = function( name, base, prototype ) {
18442	var namespace = name.split( "." )[ 0 ],
18443		fullName;
18444	name = name.split( "." )[ 1 ];
18445	fullName = namespace + "-" + name;
18446
18447	if ( !prototype ) {
18448		prototype = base;
18449		base = $.Widget;
18450	}
18451
18452	// create selector for plugin
18453	$.expr[ ":" ][ fullName ] = function( elem ) {
18454		return !!$.data( elem, name );
18455	};
18456
18457	$[ namespace ] = $[ namespace ] || {};
18458	$[ namespace ][ name ] = function( options, element ) {
18459		// allow instantiation without initializing for simple inheritance
18460		if ( arguments.length ) {
18461			this._createWidget( options, element );
18462		}
18463	};
18464
18465	var basePrototype = new base();
18466	// we need to make the options hash a property directly on the new instance
18467	// otherwise we'll modify the options hash on the prototype that we're
18468	// inheriting from
18469//	$.each( basePrototype, function( key, val ) {
18470//		if ( $.isPlainObject(val) ) {
18471//			basePrototype[ key ] = $.extend( {}, val );
18472//		}
18473//	});
18474	basePrototype.options = $.extend( true, {}, basePrototype.options );
18475	$[ namespace ][ name ].prototype = $.extend( true, basePrototype, {
18476		namespace: namespace,
18477		widgetName: name,
18478		widgetEventPrefix: $[ namespace ][ name ].prototype.widgetEventPrefix || name,
18479		widgetBaseClass: fullName
18480	}, prototype );
18481
18482	$.widget.bridge( name, $[ namespace ][ name ] );
18483};
18484
18485$.widget.bridge = function( name, object ) {
18486	$.fn[ name ] = function( options ) {
18487		var isMethodCall = typeof options === "string",
18488			args = Array.prototype.slice.call( arguments, 1 ),
18489			returnValue = this;
18490
18491		// allow multiple hashes to be passed on init
18492		options = !isMethodCall && args.length ?
18493			$.extend.apply( null, [ true, options ].concat(args) ) :
18494			options;
18495
18496		// prevent calls to internal methods
18497		if ( isMethodCall && options.charAt( 0 ) === "_" ) {
18498			return returnValue;
18499		}
18500
18501		if ( isMethodCall ) {
18502			this.each(function() {
18503				var instance = $.data( this, name ),
18504					methodValue = instance && $.isFunction( instance[options] ) ?
18505						instance[ options ].apply( instance, args ) :
18506						instance;
18507				// TODO: add this back in 1.9 and use $.error() (see #5972)
18508//				if ( !instance ) {
18509//					throw "cannot call methods on " + name + " prior to initialization; " +
18510//						"attempted to call method '" + options + "'";
18511//				}
18512//				if ( !$.isFunction( instance[options] ) ) {
18513//					throw "no such method '" + options + "' for " + name + " widget instance";
18514//				}
18515//				var methodValue = instance[ options ].apply( instance, args );
18516				if ( methodValue !== instance && methodValue !== undefined ) {
18517					returnValue = methodValue;
18518					return false;
18519				}
18520			});
18521		} else {
18522			this.each(function() {
18523				var instance = $.data( this, name );
18524				if ( instance ) {
18525					instance.option( options || {} )._init();
18526				} else {
18527					$.data( this, name, new object( options, this ) );
18528				}
18529			});
18530		}
18531
18532		return returnValue;
18533	};
18534};
18535
18536$.Widget = function( options, element ) {
18537	// allow instantiation without initializing for simple inheritance
18538	if ( arguments.length ) {
18539		this._createWidget( options, element );
18540	}
18541};
18542
18543$.Widget.prototype = {
18544	widgetName: "widget",
18545	widgetEventPrefix: "",
18546	options: {
18547		disabled: false
18548	},
18549	_createWidget: function( options, element ) {
18550		// $.widget.bridge stores the plugin instance, but we do it anyway
18551		// so that it's stored even before the _create function runs
18552		$.data( element, this.widgetName, this );
18553		this.element = $( element );
18554		this.options = $.extend( true, {},
18555			this.options,
18556			this._getCreateOptions(),
18557			options );
18558
18559		var self = this;
18560		this.element.bind( "remove." + this.widgetName, function() {
18561			self.destroy();
18562		});
18563
18564		this._create();
18565		this._trigger( "create" );
18566		this._init();
18567	},
18568	_getCreateOptions: function() {
18569		return $.metadata && $.metadata.get( this.element[0] )[ this.widgetName ];
18570	},
18571	_create: function() {},
18572	_init: function() {},
18573
18574	destroy: function() {
18575		this.element
18576			.unbind( "." + this.widgetName )
18577			.removeData( this.widgetName );
18578		this.widget()
18579			.unbind( "." + this.widgetName )
18580			.removeAttr( "aria-disabled" )
18581			.removeClass(
18582				this.widgetBaseClass + "-disabled " +
18583				"ui-state-disabled" );
18584	},
18585
18586	widget: function() {
18587		return this.element;
18588	},
18589
18590	option: function( key, value ) {
18591		var options = key;
18592
18593		if ( arguments.length === 0 ) {
18594			// don't return a reference to the internal hash
18595			return $.extend( {}, this.options );
18596		}
18597
18598		if  (typeof key === "string" ) {
18599			if ( value === undefined ) {
18600				return this.options[ key ];
18601			}
18602			options = {};
18603			options[ key ] = value;
18604		}
18605
18606		this._setOptions( options );
18607
18608		return this;
18609	},
18610	_setOptions: function( options ) {
18611		var self = this;
18612		$.each( options, function( key, value ) {
18613			self._setOption( key, value );
18614		});
18615
18616		return this;
18617	},
18618	_setOption: function( key, value ) {
18619		this.options[ key ] = value;
18620
18621		if ( key === "disabled" ) {
18622			this.widget()
18623				[ value ? "addClass" : "removeClass"](
18624					this.widgetBaseClass + "-disabled" + " " +
18625					"ui-state-disabled" )
18626				.attr( "aria-disabled", value );
18627		}
18628
18629		return this;
18630	},
18631
18632	enable: function() {
18633		return this._setOption( "disabled", false );
18634	},
18635	disable: function() {
18636		return this._setOption( "disabled", true );
18637	},
18638
18639	_trigger: function( type, event, data ) {
18640		var callback = this.options[ type ];
18641
18642		event = $.Event( event );
18643		event.type = ( type === this.widgetEventPrefix ?
18644			type :
18645			this.widgetEventPrefix + type ).toLowerCase();
18646		data = data || {};
18647
18648		// copy original event properties over to the new event
18649		// this would happen if we could call $.event.fix instead of $.Event
18650		// but we don't have a way to force an event to be fixed multiple times
18651		if ( event.originalEvent ) {
18652			for ( var i = $.event.props.length, prop; i; ) {
18653				prop = $.event.props[ --i ];
18654				event[ prop ] = event.originalEvent[ prop ];
18655			}
18656		}
18657
18658		this.element.trigger( event, data );
18659
18660		return !( $.isFunction(callback) &&
18661			callback.call( this.element[0], event, data ) === false ||
18662			event.isDefaultPrevented() );
18663	}
18664};
18665
18666})( jQuery );
18667
18668/*!
18669 * jQuery UI Mouse 1.8.16
18670 *
18671 * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
18672 * Dual licensed under the MIT or GPL Version 2 licenses.
18673 * http://jquery.org/license
18674 *
18675 * http://docs.jquery.com/UI/Mouse
18676 *
18677 * Depends:
18678 *	jquery.ui.widget.js
18679 */
18680(function( $, undefined ) {
18681
18682var mouseHandled = false;
18683$( document ).mouseup( function( e ) {
18684	mouseHandled = false;
18685});
18686
18687$.widget("ui.mouse", {
18688	options: {
18689		cancel: ':input,option',
18690		distance: 1,
18691		delay: 0
18692	},
18693	_mouseInit: function() {
18694		var self = this;
18695
18696		this.element
18697			.bind('mousedown.'+this.widgetName, function(event) {
18698				return self._mouseDown(event);
18699			})
18700			.bind('click.'+this.widgetName, function(event) {
18701				if (true === $.data(event.target, self.widgetName + '.preventClickEvent')) {
18702				    $.removeData(event.target, self.widgetName + '.preventClickEvent');
18703					event.stopImmediatePropagation();
18704					return false;
18705				}
18706			});
18707
18708		this.started = false;
18709	},
18710
18711	// TODO: make sure destroying one instance of mouse doesn't mess with
18712	// other instances of mouse
18713	_mouseDestroy: function() {
18714		this.element.unbind('.'+this.widgetName);
18715	},
18716
18717	_mouseDown: function(event) {
18718		// don't let more than one widget handle mouseStart
18719		if( mouseHandled ) { return };
18720
18721		// we may have missed mouseup (out of window)
18722		(this._mouseStarted && this._mouseUp(event));
18723
18724		this._mouseDownEvent = event;
18725
18726		var self = this,
18727			btnIsLeft = (event.which == 1),
18728			// event.target.nodeName works around a bug in IE 8 with
18729			// disabled inputs (#7620)
18730			elIsCancel = (typeof this.options.cancel == "string" && event.target.nodeName ? $(event.target).closest(this.options.cancel).length : false);
18731		if (!btnIsLeft || elIsCancel || !this._mouseCapture(event)) {
18732			return true;
18733		}
18734
18735		this.mouseDelayMet = !this.options.delay;
18736		if (!this.mouseDelayMet) {
18737			this._mouseDelayTimer = setTimeout(function() {
18738				self.mouseDelayMet = true;
18739			}, this.options.delay);
18740		}
18741
18742		if (this._mouseDistanceMet(event) && this._mouseDelayMet(event)) {
18743			this._mouseStarted = (this._mouseStart(event) !== false);
18744			if (!this._mouseStarted) {
18745				event.preventDefault();
18746				return true;
18747			}
18748		}
18749
18750		// Click event may never have fired (Gecko & Opera)
18751		if (true === $.data(event.target, this.widgetName + '.preventClickEvent')) {
18752			$.removeData(event.target, this.widgetName + '.preventClickEvent');
18753		}
18754
18755		// these delegates are required to keep context
18756		this._mouseMoveDelegate = function(event) {
18757			return self._mouseMove(event);
18758		};
18759		this._mouseUpDelegate = function(event) {
18760			return self._mouseUp(event);
18761		};
18762		$(document)
18763			.bind('mousemove.'+this.widgetName, this._mouseMoveDelegate)
18764			.bind('mouseup.'+this.widgetName, this._mouseUpDelegate);
18765
18766		event.preventDefault();
18767		
18768		mouseHandled = true;
18769		return true;
18770	},
18771
18772	_mouseMove: function(event) {
18773		// IE mouseup check - mouseup happened when mouse was out of window
18774		if ($.browser.msie && !(document.documentMode >= 9) && !event.button) {
18775			return this._mouseUp(event);
18776		}
18777
18778		if (this._mouseStarted) {
18779			this._mouseDrag(event);
18780			return event.preventDefault();
18781		}
18782
18783		if (this._mouseDistanceMet(event) && this._mouseDelayMet(event)) {
18784			this._mouseStarted =
18785				(this._mouseStart(this._mouseDownEvent, event) !== false);
18786			(this._mouseStarted ? this._mouseDrag(event) : this._mouseUp(event));
18787		}
18788
18789		return !this._mouseStarted;
18790	},
18791
18792	_mouseUp: function(event) {
18793		$(document)
18794			.unbind('mousemove.'+this.widgetName, this._mouseMoveDelegate)
18795			.unbind('mouseup.'+this.widgetName, this._mouseUpDelegate);
18796
18797		if (this._mouseStarted) {
18798			this._mouseStarted = false;
18799
18800			if (event.target == this._mouseDownEvent.target) {
18801			    $.data(event.target, this.widgetName + '.preventClickEvent', true);
18802			}
18803
18804			this._mouseStop(event);
18805		}
18806
18807		return false;
18808	},
18809
18810	_mouseDistanceMet: function(event) {
18811		return (Math.max(
18812				Math.abs(this._mouseDownEvent.pageX - event.pageX),
18813				Math.abs(this._mouseDownEvent.pageY - event.pageY)
18814			) >= this.options.distance
18815		);
18816	},
18817
18818	_mouseDelayMet: function(event) {
18819		return this.mouseDelayMet;
18820	},
18821
18822	// These are placeholder methods, to be overriden by extending plugin
18823	_mouseStart: function(event) {},
18824	_mouseDrag: function(event) {},
18825	_mouseStop: function(event) {},
18826	_mouseCapture: function(event) { return true; }
18827});
18828
18829})(jQuery);
18830
18831/*
18832 * jQuery UI Position 1.8.16
18833 *
18834 * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
18835 * Dual licensed under the MIT or GPL Version 2 licenses.
18836 * http://jquery.org/license
18837 *
18838 * http://docs.jquery.com/UI/Position
18839 */
18840(function( $, undefined ) {
18841
18842$.ui = $.ui || {};
18843
18844var horizontalPositions = /left|center|right/,
18845	verticalPositions = /top|center|bottom/,
18846	center = "center",
18847	_position = $.fn.position,
18848	_offset = $.fn.offset;
18849
18850$.fn.position = function( options ) {
18851	if ( !options || !options.of ) {
18852		return _position.apply( this, arguments );
18853	}
18854
18855	// make a copy, we don't want to modify arguments
18856	options = $.extend( {}, options );
18857
18858	var target = $( options.of ),
18859		targetElem = target[0],
18860		collision = ( options.collision || "flip" ).split( " " ),
18861		offset = options.offset ? options.offset.split( " " ) : [ 0, 0 ],
18862		targetWidth,
18863		targetHeight,
18864		basePosition;
18865
18866	if ( targetElem.nodeType === 9 ) {
18867		targetWidth = target.width();
18868		targetHeight = target.height();
18869		basePosition = { top: 0, left: 0 };
18870	// TODO: use $.isWindow() in 1.9
18871	} else if ( targetElem.setTimeout ) {
18872		targetWidth = target.width();
18873		targetHeight = target.height();
18874		basePosition = { top: target.scrollTop(), left: target.scrollLeft() };
18875	} else if ( targetElem.preventDefault ) {
18876		// force left top to allow flipping
18877		options.at = "left top";
18878		targetWidth = targetHeight = 0;
18879		basePosition = { top: options.of.pageY, left: options.of.pageX };
18880	} else {
18881		targetWidth = target.outerWidth();
18882		targetHeight = target.outerHeight();
18883		basePosition = target.offset();
18884	}
18885
18886	// force my and at to have valid horizontal and veritcal positions
18887	// if a value is missing or invalid, it will be converted to center 
18888	$.each( [ "my", "at" ], function() {
18889		var pos = ( options[this] || "" ).split( " " );
18890		if ( pos.length === 1) {
18891			pos = horizontalPositions.test( pos[0] ) ?
18892				pos.concat( [center] ) :
18893				verticalPositions.test( pos[0] ) ?
18894					[ center ].concat( pos ) :
18895					[ center, center ];
18896		}
18897		pos[ 0 ] = horizontalPositions.test( pos[0] ) ? pos[ 0 ] : center;
18898		pos[ 1 ] = verticalPositions.test( pos[1] ) ? pos[ 1 ] : center;
18899		options[ this ] = pos;
18900	});
18901
18902	// normalize collision option
18903	if ( collision.length === 1 ) {
18904		collision[ 1 ] = collision[ 0 ];
18905	}
18906
18907	// normalize offset option
18908	offset[ 0 ] = parseInt( offset[0], 10 ) || 0;
18909	if ( offset.length === 1 ) {
18910		offset[ 1 ] = offset[ 0 ];
18911	}
18912	offset[ 1 ] = parseInt( offset[1], 10 ) || 0;
18913
18914	if ( options.at[0] === "right" ) {
18915		basePosition.left += targetWidth;
18916	} else if ( options.at[0] === center ) {
18917		basePosition.left += targetWidth / 2;
18918	}
18919
18920	if ( options.at[1] === "bottom" ) {
18921		basePosition.top += targetHeight;
18922	} else if ( options.at[1] === center ) {
18923		basePosition.top += targetHeight / 2;
18924	}
18925
18926	basePosition.left += offset[ 0 ];
18927	basePosition.top += offset[ 1 ];
18928
18929	return this.each(function() {
18930		var elem = $( this ),
18931			elemWidth = elem.outerWidth(),
18932			elemHeight = elem.outerHeight(),
18933			marginLeft = parseInt( $.curCSS( this, "marginLeft", true ) ) || 0,
18934			marginTop = parseInt( $.curCSS( this, "marginTop", true ) ) || 0,
18935			collisionWidth = elemWidth + marginLeft +
18936				( parseInt( $.curCSS( this, "marginRight", true ) ) || 0 ),
18937			collisionHeight = elemHeight + marginTop +
18938				( parseInt( $.curCSS( this, "marginBottom", true ) ) || 0 ),
18939			position = $.extend( {}, basePosition ),
18940			collisionPosition;
18941
18942		if ( options.my[0] === "right" ) {
18943			position.left -= elemWidth;
18944		} else if ( options.my[0] === center ) {
18945			position.left -= elemWidth / 2;
18946		}
18947
18948		if ( options.my[1] === "bottom" ) {
18949			position.top -= elemHeight;
18950		} else if ( options.my[1] === center ) {
18951			position.top -= elemHeight / 2;
18952		}
18953
18954		// prevent fractions (see #5280)
18955		position.left = Math.round( position.left );
18956		position.top = Math.round( position.top );
18957
18958		collisionPosition = {
18959			left: position.left - marginLeft,
18960			top: position.top - marginTop
18961		};
18962
18963		$.each( [ "left", "top" ], function( i, dir ) {
18964			if ( $.ui.position[ collision[i] ] ) {
18965				$.ui.position[ collision[i] ][ dir ]( position, {
18966					targetWidth: targetWidth,
18967					targetHeight: targetHeight,
18968					elemWidth: elemWidth,
18969					elemHeight: elemHeight,
18970					collisionPosition: collisionPosition,
18971					collisionWidth: collisionWidth,
18972					collisionHeight: collisionHeight,
18973					offset: offset,
18974					my: options.my,
18975					at: options.at
18976				});
18977			}
18978		});
18979
18980		if ( $.fn.bgiframe ) {
18981			elem.bgiframe();
18982		}
18983		elem.offset( $.extend( position, { using: options.using } ) );
18984	});
18985};
18986
18987$.ui.position = {
18988	fit: {
18989		left: function( position, data ) {
18990			var win = $( window ),
18991				over = data.collisionPosition.left + data.collisionWidth - win.width() - win.scrollLeft();
18992			position.left = over > 0 ? position.left - over : Math.max( position.left - data.collisionPosition.left, position.left );
18993		},
18994		top: function( position, data ) {
18995			var win = $( window ),
18996				over = data.collisionPosition.top + data.collisionHeight - win.height() - win.scrollTop();
18997			position.top = over > 0 ? position.top - over : Math.max( position.top - data.collisionPosition.top, position.top );
18998		}
18999	},
19000
19001	flip: {
19002		left: function( position, data ) {
19003			if ( data.at[0] === center ) {
19004				return;
19005			}
19006			var win = $( window ),
19007				over = data.collisionPosition.left + data.collisionWidth - win.width() - win.scrollLeft(),
19008				myOffset = data.my[ 0 ] === "left" ?
19009					-data.elemWidth :
19010					data.my[ 0 ] === "right" ?
19011						data.elemWidth :
19012						0,
19013				atOffset = data.at[ 0 ] === "left" ?
19014					data.targetWidth :
19015					-data.targetWidth,
19016				offset = -2 * data.offset[ 0 ];
19017			position.left += data.collisionPosition.left < 0 ?
19018				myOffset + atOffset + offset :
19019				over > 0 ?
19020					myOffset + atOffset + offset :
19021					0;
19022		},
19023		top: function( position, data ) {
19024			if ( data.at[1] === center ) {
19025				return;
19026			}
19027			var win = $( window ),
19028				over = data.collisionPosition.top + data.collisionHeight - win.height() - win.scrollTop(),
19029				myOffset = data.my[ 1 ] === "top" ?
19030					-data.elemHeight :
19031					data.my[ 1 ] === "bottom" ?
19032						data.elemHeight :
19033						0,
19034				atOffset = data.at[ 1 ] === "top" ?
19035					data.targetHeight :
19036					-data.targetHeight,
19037				offset = -2 * data.offset[ 1 ];
19038			position.top += data.collisionPosition.top < 0 ?
19039				myOffset + atOffset + offset :
19040				over > 0 ?
19041					myOffset + atOffset + offset :
19042					0;
19043		}
19044	}
19045};
19046
19047// offset setter from jQuery 1.4
19048if ( !$.offset.setOffset ) {
19049	$.offset.setOffset = function( elem, options ) {
19050		// set position first, in-case top/left are set even on static elem
19051		if ( /static/.test( $.curCSS( elem, "position" ) ) ) {
19052			elem.style.position = "relative";
19053		}
19054		var curElem   = $( elem ),
19055			curOffset = curElem.offset(),
19056			curTop    = parseInt( $.curCSS( elem, "top",  true ), 10 ) || 0,
19057			curLeft   = parseInt( $.curCSS( elem, "left", true ), 10)  || 0,
19058			props     = {
19059				top:  (options.top  - curOffset.top)  + curTop,
19060				left: (options.left - curOffset.left) + curLeft
19061			};
19062		
19063		if ( 'using' in options ) {
19064			options.using.call( elem, props );
19065		} else {
19066			curElem.css( props );
19067		}
19068	};
19069
19070	$.fn.offset = function( options ) {
19071		var elem = this[ 0 ];
19072		if ( !elem || !elem.ownerDocument ) { return null; }
19073		if ( options ) { 
19074			return this.each(function() {
19075				$.offset.setOffset( this, options );
19076			});
19077		}
19078		return _offset.call( this );
19079	};
19080}
19081
19082}( jQuery ));
19083
19084/*
19085 * jQuery UI Draggable 1.8.16
19086 *
19087 * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
19088 * Dual licensed under the MIT or GPL Version 2 licenses.
19089 * http://jquery.org/license
19090 *
19091 * http://docs.jquery.com/UI/Draggables
19092 *
19093 * Depends:
19094 *	jquery.ui.core.js
19095 *	jquery.ui.mouse.js
19096 *	jquery.ui.widget.js
19097 */
19098(function( $, undefined ) {
19099
19100$.widget("ui.draggable", $.ui.mouse, {
19101	widgetEventPrefix: "drag",
19102	options: {
19103		addClasses: true,
19104		appendTo: "parent",
19105		axis: false,
19106		connectToSortable: false,
19107		containment: false,
19108		cursor: "auto",
19109		cursorAt: false,
19110		grid: false,
19111		handle: false,
19112		helper: "original",
19113		iframeFix: false,
19114		opacity: false,
19115		refreshPositions: false,
19116		revert: false,
19117		revertDuration: 500,
19118		scope: "default",
19119		scroll: true,
19120		scrollSensitivity: 20,
19121		scrollSpeed: 20,
19122		snap: false,
19123		snapMode: "both",
19124		snapTolerance: 20,
19125		stack: false,
19126		zIndex: false
19127	},
19128	_create: function() {
19129
19130		if (this.options.helper == 'original' && !(/^(?:r|a|f)/).test(this.element.css("position")))
19131			this.element[0].style.position = 'relative';
19132
19133		(this.options.addClasses && this.element.addClass("ui-draggable"));
19134		(this.options.disabled && this.element.addClass("ui-draggable-disabled"));
19135
19136		this._mouseInit();
19137
19138	},
19139
19140	destroy: function() {
19141		if(!this.element.data('draggable')) return;
19142		this.element
19143			.removeData("draggable")
19144			.unbind(".draggable")
19145			.removeClass("ui-draggable"
19146				+ " ui-draggable-dragging"
19147				+ " ui-draggable-disabled");
19148		this._mouseDestroy();
19149
19150		return this;
19151	},
19152
19153	_mouseCapture: function(event) {
19154
19155		var o = this.options;
19156
19157		// among others, prevent a drag on a resizable-handle
19158		if (this.helper || o.disabled || $(event.target).is('.ui-resizable-handle'))
19159			return false;
19160
19161		//Quit if we're not on a valid handle
19162		this.handle = this._getHandle(event);
19163		if (!this.handle)
19164			return false;
19165		
19166		if ( o.iframeFix ) {
19167			$(o.iframeFix === true ? "iframe" : o.iframeFix).each(function() {
19168				$('<div class="ui-draggable-iframeFix" style="background: #fff;"></div>')
19169				.css({
19170					width: this.offsetWidth+"px", height: this.offsetHeight+"px",
19171					position: "absolute", opacity: "0.001", zIndex: 1000
19172				})
19173				.css($(this).offset())
19174				.appendTo("body");
19175			});
19176		}
19177
19178		return true;
19179
19180	},
19181
19182	_mouseStart: function(event) {
19183
19184		var o = this.options;
19185
19186		//Create and append the visible helper
19187		this.helper = this._createHelper(event);
19188
19189		//Cache the helper size
19190		this._cacheHelperProportions();
19191
19192		//If ddmanager is used for droppables, set the global draggable
19193		if($.ui.ddmanager)
19194			$.ui.ddmanager.current = this;
19195
19196		/*
19197		 * - Position generation -
19198		 * This block generates everything position related - it's the core of draggables.
19199		 */
19200
19201		//Cache the margins of the original element
19202		this._cacheMargins();
19203
19204		//Store the helper's css position
19205		this.cssPosition = this.helper.css("position");
19206		this.scrollParent = this.helper.scrollParent();
19207
19208		//The element's absolute position on the page minus margins
19209		this.offset = this.positionAbs = this.element.offset();
19210		this.offset = {
19211			top: this.offset.top - this.margins.top,
19212			left: this.offset.left - this.margins.left
19213		};
19214
19215		$.extend(this.offset, {
19216			click: { //Where the click happened, relative to the element
19217				left: event.pageX - this.offset.left,
19218				top: event.pageY - this.offset.top
19219			},
19220			parent: this._getParentOffset(),
19221			relative: this._getRelativeOffset() //This is a relative to absolute position minus the actual position calculation - only used for relative positioned helper
19222		});
19223
19224		//Generate the original position
19225		this.originalPosition = this.position = this._generatePosition(event);
19226		this.originalPageX = event.pageX;
19227		this.originalPageY = event.pageY;
19228
19229		//Adjust the mouse offset relative to the helper if 'cursorAt' is supplied
19230		(o.cursorAt && this._adjustOffsetFromHelper(o.cursorAt));
19231
19232		//Set a containment if given in the options
19233		if(o.containment)
19234			this._setContainment();
19235
19236		//Trigger event + callbacks
19237		if(this._trigger("start", event) === false) {
19238			this._clear();
19239			return false;
19240		}
19241
19242		//Recache the helper size
19243		this._cacheHelperProportions();
19244
19245		//Prepare the droppable offsets
19246		if ($.ui.ddmanager && !o.dropBehaviour)
19247			$.ui.ddmanager.prepareOffsets(this, event);
19248
19249		this.helper.addClass("ui-draggable-dragging");
19250		this._mouseDrag(event, true); //Execute the drag once - this causes the helper not to be visible before getting its correct position
19251		
19252		//If the ddmanager is used for droppables, inform the manager that dragging has started (see #5003)
19253		if ( $.ui.ddmanager ) $.ui.ddmanager.dragStart(this, event);
19254		
19255		return true;
19256	},
19257
19258	_mouseDrag: function(event, noPropagation) {
19259
19260		//Compute the helpers position
19261		this.position = this._generatePosition(event);
19262		this.positionAbs = this._convertPositionTo("absolute");
19263
19264		//Call plugins and callbacks and use the resulting position if something is returned
19265		if (!noPropagation) {
19266			var ui = this._uiHash();
19267			if(this._trigger('drag', event, ui) === false) {
19268				this._mouseUp({});
19269				return false;
19270			}
19271			this.position = ui.position;
19272		}
19273
19274		if(!this.options.axis || this.options.axis != "y") this.helper[0].style.left = this.position.left+'px';
19275		if(!this.options.axis || this.options.axis != "x") this.helper[0].style.top = this.position.top+'px';
19276		if($.ui.ddmanager) $.ui.ddmanager.drag(this, event);
19277
19278		return false;
19279	},
19280
19281	_mouseStop: function(event) {
19282
19283		//If we are using droppables, inform the manager about the drop
19284		var dropped = false;
19285		if ($.ui.ddmanager && !this.options.dropBehaviour)
19286			dropped = $.ui.ddmanager.drop(this, event);
19287
19288		//if a drop comes from outside (a sortable)
19289		if(this.dropped) {
19290			dropped = this.dropped;
19291			this.dropped = false;
19292		}
19293		
19294		//if the original element is removed, don't bother to continue if helper is set to "original"
19295		if((!this.element[0] || !this.element[0].parentNode) && this.options.helper == "original")
19296			return false;
19297
19298		if((this.options.revert == "invalid" && !dropped) || (this.options.revert == "valid" && dropped) || this.options.revert === true || ($.isFunction(this.options.revert) && this.options.revert.call(this.element, dropped))) {
19299			var self = this;
19300			$(this.helper).animate(this.originalPosition, parseInt(this.options.revertDuration, 10), function() {
19301				if(self._trigger("stop", event) !== false) {
19302					self._clear();
19303				}
19304			});
19305		} else {
19306			if(this._trigger("stop", event) !== false) {
19307				this._clear();
19308			}
19309		}
19310
19311		return false;
19312	},
19313	
19314	_mouseUp: function(event) {
19315		if (this.options.iframeFix === true) {
19316			$("div.ui-draggable-iframeFix").each(function() { 
19317				this.parentNode.removeChild(this); 
19318			}); //Remove frame helpers
19319		}
19320		
19321		//If the ddmanager is used for droppables, inform the manager that dragging has stopped (see #5003)
19322		if( $.ui.ddmanager ) $.ui.ddmanager.dragStop(this, event);
19323		
19324		return $.ui.mouse.prototype._mouseUp.call(this, event);
19325	},
19326	
19327	cancel: function() {
19328		
19329		if(this.helper.is(".ui-draggable-dragging")) {
19330			this._mouseUp({});
19331		} else {
19332			this._clear();
19333		}
19334		
19335		return this;
19336		
19337	},
19338
19339	_getHandle: function(event) {
19340
19341		var handle = !this.options.handle || !$(this.options.handle, this.element).length ? true : false;
19342		$(this.options.handle, this.element)
19343			.find("*")
19344			.andSelf()
19345			.each(function() {
19346				if(this == event.target) handle = true;
19347			});
19348
19349		return handle;
19350
19351	},
19352
19353	_createHelper: function(event) {
19354
19355		var o = this.options;
19356		var helper = $.isFunction(o.helper) ? $(o.helper.apply(this.element[0], [event])) : (o.helper == 'clone' ? this.element.clone().removeAttr('id') : this.element);
19357
19358		if(!helper.parents('body').length)
19359			helper.appendTo((o.appendTo == 'parent' ? this.element[0].parentNode : o.appendTo));
19360
19361		if(helper[0] != this.element[0] && !(/(fixed|absolute)/).test(helper.css("position")))
19362			helper.css("position", "absolute");
19363
19364		return helper;
19365
19366	},
19367
19368	_adjustOffsetFromHelper: function(obj) {
19369		if (typeof obj == 'string') {
19370			obj = obj.split(' ');
19371		}
19372		if ($.isArray(obj)) {
19373			obj = {left: +obj[0], top: +obj[1] || 0};
19374		}
19375		if ('left' in obj) {
19376			this.offset.click.left = obj.left + this.margins.left;
19377		}
19378		if ('right' in obj) {
19379			this.offset.click.left = this.helperProportions.width - obj.right + this.margins.left;
19380		}
19381		if ('top' in obj) {
19382			this.offset.click.top = obj.top + this.margins.top;
19383		}
19384		if ('bottom' in obj) {
19385			this.offset.click.top = this.helperProportions.height - obj.bottom + this.margins.top;
19386		}
19387	},
19388
19389	_getParentOffset: function() {
19390
19391		//Get the offsetParent and cache its position
19392		this.offsetParent = this.helper.offsetParent();
19393		var po = this.offsetParent.offset();
19394
19395		// This is a special case where we need to modify a offset calculated on start, since the following happened:
19396		// 1. The position of the helper is absolute, so it's position is calculated based on the next positioned parent
19397		// 2. The actual offset parent is a child of the scroll parent, and the scroll parent isn't the document, which means that
19398		//    the scroll is included in the initial calculation of the offset of the parent, and never recalculated upon drag
19399		if(this.cssPosition == 'absolute' && this.scrollParent[0] != document && $.ui.contains(this.scrollParent[0], this.offsetParent[0])) {
19400			po.left += this.scrollParent.scrollLeft();
19401			po.top += this.scrollParent.scrollTop();
19402		}
19403
19404		if((this.offsetParent[0] == document.body) //This needs to be actually done for all browsers, since pageX/pageY includes this information
19405		|| (this.offsetParent[0].tagName && this.offsetParent[0].tagName.toLowerCase() == 'html' && $.browser.msie)) //Ugly IE fix
19406			po = { top: 0, left: 0 };
19407
19408		return {
19409			top: po.top + (parseInt(this.offsetParent.css("borderTopWidth"),
1940910) || 0),
19410			left: po.left + (parseInt(this.offsetParent.css("borderLeftWidth"),10) || 0)
19411		};
19412
19413	},
19414
19415	_getRelativeOffset: function() {
19416
19417		if(this.cssPosition == "relative") {
19418			var p = this.element.position();
19419			return {
19420				top: p.top - (parseInt(this.helper.css("top"),10) || 0) + this.scrollParent.scrollTop(),
19421				left: p.left - (parseInt(this.helper.css("left"),10) || 0) + this.scrollParent.scrollLeft()
19422			};
19423		} else {
19424			return { top: 0, left: 0 };
19425		}
19426
19427	},
19428
19429	_cacheMargins: function() {
19430		this.margins = {
19431			left: (parseInt(this.element.css("marginLeft"),10) || 0),
19432			top: (parseInt(this.element.css("marginTop"),10) || 0),
19433			right: (parseInt(this.element.css("marginRight"),10) || 0),
19434			bottom: (parseInt(this.element.css("marginBottom"),10) || 0)
19435		};
19436	},
19437
19438	_cacheHelperProportions: function() {
19439		this.helperProportions = {
19440			width: this.helper.outerWidth(),
19441			height: this.helper.outerHeight()
19442		};
19443	},
19444
19445	_setContainment: function() {
19446
19447		var o = this.options;
19448		if(o.containment == 'parent') o.containment = this.helper[0].parentNode;
19449		if(o.containment == 'document' || o.containment == 'window') this.containment = [
19450			o.containment == 'document' ? 0 : $(window).scrollLeft() - this.offset.relative.left - this.offset.parent.left,
19451			o.containment == 'document' ? 0 : $(window).scrollTop() - this.offset.relative.top - this.offset.parent.top,
19452			(o.containment == 'document' ? 0 : $(window).scrollLeft()) + $(o.containment == 'document' ? document : window).width() - this.helperProportions.width - this.margins.left,
19453			(o.containment == 'document' ? 0 : $(window).scrollTop()) + ($(o.containment == 'document' ? document : window).height() || document.body.parentNode.scrollHeight) - this.helperProportions.height - this.margins.top
19454		];
19455
19456		if(!(/^(document|window|parent)$/).test(o.containment) && o.containment.constructor != Array) {
19457		        var c = $(o.containment);
19458			var ce = c[0]; if(!ce) return;
19459			var co = c.offset();
19460			var over = ($(ce).css("overflow") != 'hidden');
19461
19462			this.containment = [
19463				(parseInt($(ce).css("borderLeftWidth"),10) || 0) + (parseInt($(ce).css("paddingLeft"),10) || 0),
19464				(parseInt($(ce).css("borderTopWidth"),10) || 0) + (parseInt($(ce).css("paddingTop"),10) || 0),
19465				(over ? Math.max(ce.scrollWidth,ce.offsetWidth) : ce.offsetWidth) - (parseInt($(ce).css("borderLeftWidth"),10) || 0) - (parseInt($(ce).css("paddingRight"),10) || 0) - this.helperProportions.width - this.margins.left - this.margins.right,
19466				(over ? Math.max(ce.scrollHeight,ce.offsetHeight) : ce.offsetHeight) - (parseInt($(ce).css("borderTopWidth"),10) || 0) - (parseInt($(ce).css("paddingBottom"),10) || 0) - this.helperProportions.height - this.margins.top  - this.margins.bottom
19467			];
19468			this.relative_container = c;
19469
19470		} else if(o.containment.constructor == Array) {
19471			this.containment = o.containment;
19472		}
19473
19474	},
19475
19476	_convertPositionTo: function(d, pos) {
19477
19478		if(!pos) pos = this.position;
19479		var mod = d == "absolute" ? 1 : -1;
19480		var o = this.options, scroll = this.cssPosition == 'absolute' && !(this.scrollParent[0] != document && $.ui.contains(this.scrollParent[0], this.offsetParent[0])) ? this.offsetParent : this.scrollParent, scrollIsRootNode = (/(html|body)/i).test(scroll[0].tagName);
19481
19482		return {
19483			top: (
19484				pos.top																	// The absolute mouse position
19485				+ this.offset.relative.top * mod										// Only for relative positioned nodes: Relative offset from element to offset parent
19486				+ this.offset.parent.top * mod											// The offsetParent's offset without borders (offset + border)
19487				- ($.browser.safari && $.browser.version < 526 && this.cssPosition == 'fixed' ? 0 : ( this.cssPosition == 'fixed' ? -this.scrollParent.scrollTop() : ( scrollIsRootNode ? 0 : scroll.scrollTop() ) ) * mod)
19488			),
19489			left: (
19490				pos.left																// The absolute mouse position
19491				+ this.offset.relative.left * mod										// Only for relative positioned nodes: Relative offset from element to offset parent
19492				+ this.offset.parent.left * mod											// The offsetParent's offset without borders (offset + border)
19493				- ($.browser.safari && $.browser.version < 526 && this.cssPosition == 'fixed' ? 0 : ( this.cssPosition == 'fixed' ? -this.scrollParent.scrollLeft() : scrollIsRootNode ? 0 : scroll.scrollLeft() ) * mod)
19494			)
19495		};
19496
19497	},
19498
19499	_generatePosition: function(event) {
19500
19501		var o = this.options, scroll = this.cssPosition == 'absolute' && !(this.scrollParent[0] != document && $.ui.contains(this.scrollParent[0], this.offsetParent[0])) ? this.offsetParent : this.scrollParent, scrollIsRootNode = (/(html|body)/i).test(scroll[0].tagName);
19502		var pageX = event.pageX;
19503		var pageY = event.pageY;
19504
19505		/*
19506		 * - Position constraining -
19507		 * Constrain the position to a mix of grid, containment.
19508		 */
19509
19510		if(this.originalPosition) { //If we are not dragging yet, we won't check for options
19511		         var containment;
19512		         if(this.containment) {
19513				 if (this.relative_container){
19514				     var co = this.relative_container.offset();
19515				     containment = [ this.containment[0] + co.left,
19516						     this.containment[1] + co.top,
19517						     this.containment[2] + co.left,
19518						     this.containment[3] + co.top ];
19519				 }
19520				 else {
19521				     containment = this.containment;
19522				 }
19523
19524				if(event.pageX - this.offset.click.left < containment[0]) pageX = containment[0] + this.offset.click.left;
19525				if(event.pageY - this.offset.click.top < containment[1]) pageY = containment[1] + this.offset.click.top;
19526				if(event.pageX - this.offset.click.left > containment[2]) pageX = containment[2] + this.offset.click.left;
19527				if(event.pageY - this.offset.click.top > containment[3]) pageY = containment[3] + this.offset.click.top;
19528			}
19529
19530			if(o.grid) {
19531				//Check for grid elements set to 0 to prevent divide by 0 error causing invalid argument errors in IE (see ticket #6950)
19532				var top = o.grid[1] ? this.originalPageY + Math.round((pageY - this.originalPageY) / o.grid[1]) * o.grid[1] : this.originalPageY;
19533				pageY = containment ? (!(top - this.offset.click.top < containment[1] || top - this.offset.click.top > containment[3]) ? top : (!(top - this.offset.click.top < containment[1]) ? top - o.grid[1] : top + o.grid[1])) : top;
19534
19535				var left = o.grid[0] ? this.originalPageX + Math.round((pageX - this.originalPageX) / o.grid[0]) * o.grid[0] : this.originalPageX;
19536				pageX = containment ? (!(left - this.offset.click.left < containment[0] || left - this.offset.click.left > containment[2]) ? left : (!(left - this.offset.click.left < containment[0]) ? left - o.grid[0] : left + o.grid[0])) : left;
19537			}
19538
19539		}
19540
19541		return {
19542			top: (
19543				pageY																// The absolute mouse position
19544				- this.offset.click.top													// Click offset (relative to the element)
19545				- this.offset.relative.top												// Only for relative positioned nodes: Relative offset from element to offset parent
19546				- this.offset.parent.top												// The offsetParent's offset without borders (offset + border)
19547				+ ($.browser.safari && $.browser.version < 526 && this.cssPosition == 'fixed' ? 0 : ( this.cssPosition == 'fixed' ? -this.scrollParent.scrollTop() : ( scrollIsRootNode ? 0 : scroll.scrollTop() ) ))
19548			),
19549			left: (
19550				pageX																// The absolute mouse position
19551				- this.offset.click.left												// Click offset (relative to the element)
19552				- this.offset.relative.left												// Only for relative positioned nodes: Relative offset from element to offset parent
19553				- this.offset.parent.left												// The offsetParent's offset without borders (offset + border)
19554				+ ($.browser.safari && $.browser.version < 526 && this.cssPosition == 'fixed' ? 0 : ( this.cssPosition == 'fixed' ? -this.scrollParent.scrollLeft() : scrollIsRootNode ? 0 : scroll.scrollLeft() ))
19555			)
19556		};
19557
19558	},
19559
19560	_clear: function() {
19561		this.helper.removeClass("ui-draggable-dragging");
19562		if(this.helper[0] != this.element[0] && !this.cancelHelperRemoval) this.helper.remove();
19563		//if($.ui.ddmanager) $.ui.ddmanager.current = null;
19564		this.helper = null;
19565		this.cancelHelperRemoval = false;
19566	},
19567
19568	// From now on bulk stuff - mainly helpers
19569
19570	_trigger: function(type, event, ui) {
19571		ui = ui || this._uiHash();
19572		$.ui.plugin.call(this, type, [event, ui]);
19573		if(type == "drag") this.positionAbs = this._convertPositionTo("absolute"); //The absolute position has to be recalculated after plugins
19574		return $.Widget.prototype._trigger.call(this, type, event, ui);
19575	},
19576
19577	plugins: {},
19578
19579	_uiHash: function(event) {
19580		return {
19581			helper: this.helper,
19582			position: this.position,
19583			originalPosition: this.originalPosition,
19584			offset: this.positionAbs
19585		};
19586	}
19587
19588});
19589
19590$.extend($.ui.draggable, {
19591	version: "1.8.16"
19592});
19593
19594$.ui.plugin.add("draggable", "connectToSortable", {
19595	start: function(event, ui) {
19596
19597		var inst = $(this).data("draggable"), o = inst.options,
19598			uiSortable = $.extend({}, ui, { item: inst.element });
19599		inst.sortables = [];
19600		$(o.connectToSortable).each(function() {
19601			var sortable = $.data(this, 'sortable');
19602			if (sortable && !sortable.options.disabled) {
19603				inst.sortables.push({
19604					instance: sortable,
19605					shouldRevert: sortable.options.revert
19606				});
19607				sortable.refreshPositions();	// Call the sortable's refreshPositions at drag start to refresh the containerCache since the sortable container cache is used in drag and needs to be up to date (this will ensure it's initialised as well as being kept in step with any changes that might have happened on the page).
19608				sortable._trigger("activate", event, uiSortable);
19609			}
19610		});
19611
19612	},
19613	stop: function(event, ui) {
19614
19615		//If we are still over the sortable, we fake the stop event of the sortable, but also remove helper
19616		var inst = $(this).data("draggable"),
19617			uiSortable = $.extend({}, ui, { item: inst.element });
19618
19619		$.each(inst.sortables, function() {
19620			if(this.instance.isOver) {
19621
19622				this.instance.isOver = 0;
19623
19624				inst.cancelHelperRemoval = true; //Don't remove the helper in the draggable instance
19625				this.instance.cancelHelperRemoval = false; //Remove it in the sortable instance (so sortable plugins like revert still work)
19626
19627				//The sortable revert is supported, and we have to set a temporary dropped variable on the draggable to support revert: 'valid/invalid'
19628				if(this.shouldRevert) this.instance.options.revert = true;
19629
19630				//Trigger the stop of the sortable
19631				this.instance._mouseStop(event);
19632
19633				this.instance.options.helper = this.instance.options._helper;
19634
19635				//If the helper has been the original item, restore properties in the sortable
19636				if(inst.options.helper == 'original')
19637					this.instance.currentItem.css({ top: 'auto', left: 'auto' });
19638
19639			} else {
19640				this.instance.cancelHelperRemoval = false; //Remove the helper in the sortable instance
19641				this.instance._trigger("deactivate", event, uiSortable);
19642			}
19643
19644		});
19645
19646	},
19647	drag: function(event, ui) {
19648
19649		var inst = $(this).data("draggable"), self = this;
19650
19651		var checkPos = function(o) {
19652			var dyClick = this.offset.click.top, dxClick = this.offset.click.left;
19653			var helperTop = this.positionAbs.top, helperLeft = this.positionAbs.left;
19654			var itemHeight = o.height, itemWidth = o.width;
19655			var itemTop = o.top, itemLeft = o.left;
19656
19657			return $.ui.isOver(helperTop + dyClick, helperLeft + dxClick, itemTop, itemLeft, itemHeight, itemWidth);
19658		};
19659
19660		$.each(inst.sortables, function(i) {
19661			
19662			//Copy over some variables to allow calling the sortable's native _intersectsWith
19663			this.instance.positionAbs = inst.positionAbs;
19664			this.instance.helperProportions = inst.helperProportions;
19665			this.instance.offset.click = inst.offset.click;
19666			
19667			if(this.instance._intersectsWith(this.instance.containerCache)) {
19668
19669				//If it intersects, we use a little isOver variable and set it once, so our move-in stuff gets fired only once
19670				if(!this.instance.isOver) {
19671
19672					this.instance.isOver = 1;
19673					//Now we fake the start of dragging for the sortable instance,
19674					//by cloning the list group item, appending it to the sortable and using it as inst.currentItem
19675					//We can then fire the start event of the sortable with our passed browser event, and our own helper (so it doesn't create a new one)
19676					this.instance.currentItem = $(self).clone().removeAttr('id').appendTo(this.instance.element).data("sortable-item", true);
19677					this.instance.options._helper = this.instance.options.helper; //Store helper option to later restore it
19678					this.instance.options.helper = function() { return ui.helper[0]; };
19679
19680					event.target = this.instance.currentItem[0];
19681					this.instance._mouseCapture(event, true);
19682					this.instance._mouseStart(event, true, true);
19683
19684					//Because the browser event is way off the new appended portlet, we modify a couple of variables to reflect the changes
19685					this.instance.offset.click.top = inst.offset.click.top;
19686					this.instance.offset.click.left = inst.offset.click.left;
19687					this.instance.offset.parent.left -= inst.offset.parent.left - this.instance.offset.parent.left;
19688					this.instance.offset.parent.top -= inst.offset.parent.top - this.instance.offset.parent.top;
19689
19690					inst._trigger("toSortable", event);
19691					inst.dropped = this.instance.element; //draggable revert needs that
19692					//hack so receive/update callbacks work (mostly)
19693					inst.currentItem = inst.element;
19694					this.instance.fromOutside = inst;
19695
19696				}
19697
19698				//Provided we did all the previous steps, we can fire the drag event of the sortable on every draggable drag, when it intersects with the sortable
19699				if(this.instance.currentItem) this.instance._mouseDrag(event);
19700
19701			} else {
19702
19703				//If it doesn't intersect with the sortable, and it intersected before,
19704				//we fake the drag stop of the sortable, but make sure it doesn't remove the helper by using cancelHelperRemoval
19705				if(this.instance.isOver) {
19706
19707					this.instance.isOver = 0;
19708					this.instance.cancelHelperRemoval = true;
19709					
19710					//Prevent reverting on this forced stop
19711					this.instance.options.revert = false;
19712					
19713					// The out event needs to be triggered independently
19714					this.instance._trigger('out', event, this.instance._uiHash(this.instance));
19715					
19716					this.instance._mouseStop(event, true);
19717					this.instance.options.helper = this.instance.options._helper;
19718
19719					//Now we remove our currentItem, the list group clone again, and the placeholder, and animate the helper back to it's original size
19720					this.instance.currentItem.remove();
19721					if(this.instance.placeholder) this.instance.placeholder.remove();
19722
19723					inst._trigger("fromSortable", event);
19724					inst.dropped = false; //draggable revert needs that
19725				}
19726
19727			};
19728
19729		});
19730
19731	}
19732});
19733
19734$.ui.plugin.add("draggable", "cursor", {
19735	start: function(event, ui) {
19736		var t = $('body'), o = $(this).data('draggable').options;
19737		if (t.css("cursor")) o._cursor = t.css("cursor");
19738		t.css("cursor", o.cursor);
19739	},
19740	stop: function(event, ui) {
19741		var o = $(this).data('draggable').options;
19742		if (o._cursor) $('body').css("cursor", o._cursor);
19743	}
19744});
19745
19746$.ui.plugin.add("draggable", "opacity", {
19747	start: function(event, ui) {
19748		var t = $(ui.helper), o = $(this).data('draggable').options;
19749		if(t.css("opacity")) o._opacity = t.css("opacity");
19750		t.css('opacity', o.opacity);
19751	},
19752	stop: function(event, ui) {
19753		var o = $(this).data('draggable').options;
19754		if(o._opacity) $(ui.helper).css('opacity', o._opacity);
19755	}
19756});
19757
19758$.ui.plugin.add("draggable", "scroll", {
19759	start: function(event, ui) {
19760		var i = $(this).data("draggable");
19761		if(i.scrollParent[0] != document && i.scrollParent[0].tagName != 'HTML') i.overflowOffset = i.scrollParent.offset();
19762	},
19763	drag: function(event, ui) {
19764
19765		var i = $(this).data("draggable"), o = i.options, scrolled = false;
19766
19767		if(i.scrollParent[0] != document && i.scrollParent[0].tagName != 'HTML') {
19768
19769			if(!o.axis || o.axis != 'x') {
19770				if((i.overflowOffset.top + i.scrollParent[0].offsetHeight) - event.pageY < o.scrollSensitivity)
19771					i.scrollParent[0].scrollTop = scrolled = i.scrollParent[0].scrollTop + o.scrollSpeed;
19772				else if(event.pageY - i.overflowOffset.top < o.scrollSensitivity)
19773					i.scrollParent[0].scrollTop = scrolled = i.scrollParent[0].scrollTop - o.scrollSpeed;
19774			}
19775
19776			if(!o.axis || o.axis != 'y') {
19777				if((i.overflowOffset.left + i.scrollParent[0].offsetWidth) - event.pageX < o.scrollSensitivity)
19778					i.scrollParent[0].scrollLeft = scrolled = i.scrollParent[0].scrollLeft + o.scrollSpeed;
19779				else if(event.pageX - i.overflowOffset.left < o.scrollSensitivity)
19780					i.scrollParent[0].scrollLeft = scrolled = i.scrollParent[0].scrollLeft - o.scrollSpeed;
19781			}
19782
19783		} else {
19784
19785			if(!o.axis || o.axis != 'x') {
19786				if(event.pageY - $(document).scrollTop() < o.scrollSensitivity)
19787					scrolled = $(document).scrollTop($(document).scrollTop() - o.scrollSpeed);
19788				else if($(window).height() - (event.pageY - $(document).scrollTop()) < o.scrollSensitivity)
19789					scrolled = $(document).scrollTop($(document).scrollTop() + o.scrollSpeed);
19790			}
19791
19792			if(!o.axis || o.axis != 'y') {
19793				if(event.pageX - $(document).scrollLeft() < o.scrollSensitivity)
19794					scrolled = $(document).scrollLeft($(document).scrollLeft() - o.scrollSpeed);
19795				else if($(window).width() - (event.pageX - $(document).scrollLeft()) < o.scrollSensitivity)
19796					scrolled = $(document).scrollLeft($(document).scrollLeft() + o.scrollSpeed);
19797			}
19798
19799		}
19800
19801		if(scrolled !== false && $.ui.ddmanager && !o.dropBehaviour)
19802			$.ui.ddmanager.prepareOffsets(i, event);
19803
19804	}
19805});
19806
19807$.ui.plugin.add("draggable", "snap", {
19808	start: function(event, ui) {
19809
19810		var i = $(this).data("draggable"), o = i.options;
19811		i.snapElements = [];
19812
19813		$(o.snap.constructor != String ? ( o.snap.items || ':data(draggable)' ) : o.snap).each(function() {
19814			var $t = $(this); var $o = $t.offset();
19815			if(this != i.element[0]) i.snapElements.push({
19816				item: this,
19817				width: $t.outerWidth(), height: $t.outerHeight(),
19818				top: $o.top, left: $o.left
19819			});
19820		});
19821
19822	},
19823	drag: function(event, ui) {
19824
19825		var inst = $(this).data("draggable"), o = inst.options;
19826		var d = o.snapTolerance;
19827
19828		var x1 = ui.offset.left, x2 = x1 + inst.helperProportions.width,
19829			y1 = ui.offset.top, y2 = y1 + inst.helperProportions.height;
19830
19831		for (var i = inst.snapElements.length - 1; i >= 0; i--){
19832
19833			var l = inst.snapElements[i].left, r = l + inst.snapElements[i].width,
19834				t = inst.snapElements[i].top, b = t + inst.snapElements[i].height;
19835
19836			//Yes, I know, this is insane ;)
19837			if(!((l-d < x1 && x1 < r+d && t-d < y1 && y1 < b+d) || (l-d < x1 && x1 < r+d && t-d < y2 && y2 < b+d) || (l-d < x2 && x2 < r+d && t-d < y1 && y1 < b+d) || (l-d < x2 && x2 < r+d && t-d < y2 && y2 < b+d))) {
19838				if(inst.snapElements[i].snapping) (inst.options.snap.release && inst.options.snap.release.call(inst.element, event, $.extend(inst._uiHash(), { snapItem: inst.snapElements[i].item })));
19839				inst.snapElements[i].snapping = false;
19840				continue;
19841			}
19842
19843			if(o.snapMode != 'inner') {
19844				var ts = Math.abs(t - y2) <= d;
19845				var bs = Math.abs(b - y1) <= d;
19846				var ls = Math.abs(l - x2) <= d;
19847				var rs = Math.abs(r - x1) <= d;
19848				if(ts) ui.position.top = inst._convertPositionTo("relative", { top: t - inst.helperProportions.height, left: 0 }).top - inst.margins.top;
19849				if(bs) ui.position.top = inst._convertPositionTo("relative", { top: b, left: 0 }).top - inst.margins.top;
19850				if(ls) ui.position.left = inst._convertPositionTo("relative", { top: 0, left: l - inst.helperProportions.width }).left - inst.margins.left;
19851				if(rs) ui.position.left = inst._convertPositionTo("relative", { top: 0, left: r }).left - inst.margins.left;
19852			}
19853
19854			var first = (ts || bs || ls || rs);
19855
19856			if(o.snapMode != 'outer') {
19857				var ts = Math.abs(t - y1) <= d;
19858				var bs = Math.abs(b - y2) <= d;
19859				var ls = Math.abs(l - x1) <= d;
19860				var rs = Math.abs(r - x2) <= d;
19861				if(ts) ui.position.top = inst._convertPositionTo("relative", { top: t, left: 0 }).top - inst.margins.top;
19862				if(bs) ui.position.top = inst._convertPositionTo("relative", { top: b - inst.helperProportions.height, left: 0 }).top - inst.margins.top;
19863				if(ls) ui.position.left = inst._convertPositionTo("relative", { top: 0, left: l }).left - inst.margins.left;
19864				if(rs) ui.position.left = inst._convertPositionTo("relative", { top: 0, left: r - inst.helperProportions.width }).left - inst.margins.left;
19865			}
19866
19867			if(!inst.snapElements[i].snapping && (ts || bs || ls || rs || first))
19868				(inst.options.snap.snap && inst.options.snap.snap.call(inst.element, event, $.extend(inst._uiHash(), { snapItem: inst.snapElements[i].item })));
19869			inst.snapElements[i].snapping = (ts || bs || ls || rs || first);
19870
19871		};
19872
19873	}
19874});
19875
19876$.ui.plugin.add("draggable", "stack", {
19877	start: function(event, ui) {
19878
19879		var o = $(this).data("draggable").options;
19880
19881		var group = $.makeArray($(o.stack)).sort(function(a,b) {
19882			return (parseInt($(a).css("zIndex"),10) || 0) - (parseInt($(b).css("zIndex"),10) || 0);
19883		});
19884		if (!group.length) { return; }
19885		
19886		var min = parseInt(group[0].style.zIndex) || 0;
19887		$(group).each(function(i) {
19888			this.style.zIndex = min + i;
19889		});
19890
19891		this[0].style.zIndex = min + group.length;
19892
19893	}
19894});
19895
19896$.ui.plugin.add("draggable", "zIndex", {
19897	start: function(event, ui) {
19898		var t = $(ui.helper), o = $(this).data("draggable").options;
19899		if(t.css("zIndex")) o._zIndex = t.css("zIndex");
19900		t.css('zIndex', o.zIndex);
19901	},
19902	stop: function(event, ui) {
19903		var o = $(this).data("draggable").options;
19904		if(o._zIndex) $(ui.helper).css('zIndex', o._zIndex);
19905	}
19906});
19907
19908})(jQuery);
19909
19910/*
19911 * jQuery UI Droppable 1.8.16
19912 *
19913 * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
19914 * Dual licensed under the MIT or GPL Version 2 licenses.
19915 * http://jquery.org/license
19916 *
19917 * http://docs.jquery.com/UI/Droppables
19918 *
19919 * Depends:
19920 *	jquery.ui.core.js
19921 *	jquery.ui.widget.js
19922 *	jquery.ui.mouse.js
19923 *	jquery.ui.draggable.js
19924 */
19925(function( $, undefined ) {
19926
19927$.widget("ui.droppable", {
19928	widgetEventPrefix: "drop",
19929	options: {
19930		accept: '*',
19931		activeClass: false,
19932		addClasses: true,
19933		greedy: false,
19934		hoverClass: false,
19935		scope: 'default',
19936		tolerance: 'intersect'
19937	},
19938	_create: function() {
19939
19940		var o = this.options, accept = o.accept;
19941		this.isover = 0; this.isout = 1;
19942
19943		this.accept = $.isFunction(accept) ? accept : function(d) {
19944			return d.is(accept);
19945		};
19946
19947		//Store the droppable's proportions
19948		this.proportions = { width: this.element[0].offsetWidth, height: this.element[0].offsetHeight };
19949
19950		// Add the reference and positions to the manager
19951		$.ui.ddmanager.droppables[o.scope] = $.ui.ddmanager.droppables[o.scope] || [];
19952		$.ui.ddmanager.droppables[o.scope].push(this);
19953
19954		(o.addClasses && this.element.addClass("ui-droppable"));
19955
19956	},
19957
19958	destroy: function() {
19959		var drop = $.ui.ddmanager.droppables[this.options.scope];
19960		for ( var i = 0; i < drop.length; i++ )
19961			if ( drop[i] == this )
19962				drop.splice(i, 1);
19963
19964		this.element
19965			.removeClass("ui-droppable ui-droppable-disabled")
19966			.removeData("droppable")
19967			.unbind(".droppable");
19968
19969		return this;
19970	},
19971
19972	_setOption: function(key, value) {
19973
19974		if(key == 'accept') {
19975			this.accept = $.isFunction(value) ? value : function(d) {
19976				return d.is(value);
19977			};
19978		}
19979		$.Widget.prototype._setOption.apply(this, arguments);
19980	},
19981
19982	_activate: function(event) {
19983		var draggable = $.ui.ddmanager.current;
19984		if(this.options.activeClass) this.element.addClass(this.options.activeClass);
19985		(draggable && this._trigger('activate', event, this.ui(draggable)));
19986	},
19987
19988	_deactivate: function(event) {
19989		var draggable = $.ui.ddmanager.current;
19990		if(this.options.activeClass) this.element.removeClass(this.options.activeClass);
19991		(draggable && this._trigger('deactivate', event, this.ui(draggable)));
19992	},
19993
19994	_over: function(event) {
19995
19996		var draggable = $.ui.ddmanager.current;
19997		if (!draggable || (draggable.currentItem || draggable.element)[0] == this.element[0]) return; // Bail if draggable and droppable are same element
19998
19999		if (this.accept.call(this.element[0],(draggable.currentItem || draggable.element))) {
20000			if(this.options.hoverClass) this.element.addClass(this.options.hoverClass);
20001			this._trigger('over', event, this.ui(draggable));
20002		}
20003
20004	},
20005
20006	_out: function(event) {
20007
20008		var draggable = $.ui.ddmanager.current;
20009		if (!draggable || (draggable.currentItem || draggable.element)[0] == this.element[0]) return; // Bail if draggable and droppable are same element
20010
20011		if (this.accept.call(this.element[0],(draggable.currentItem || draggable.element))) {
20012			if(this.options.hoverClass) this.element.removeClass(this.options.hoverClass);
20013			this._trigger('out', event, this.ui(draggable));
20014		}
20015
20016	},
20017
20018	_drop: function(event,custom) {
20019
20020		var draggable = custom || $.ui.ddmanager.current;
20021		if (!draggable || (draggable.currentItem || draggable.element)[0] == this.element[0]) return false; // Bail if draggable and droppable are same element
20022
20023		var childrenIntersection = false;
20024		this.element.find(":data(droppable)").not(".ui-draggable-dragging").each(function() {
20025			var inst = $.data(this, 'droppable');
20026			if(
20027				inst.options.greedy
20028				&& !inst.options.disabled
20029				&& inst.options.scope == draggable.options.scope
20030				&& inst.accept.call(inst.element[0], (draggable.currentItem || draggable.element))
20031				&& $.ui.intersect(draggable, $.extend(inst, { offset: inst.element.offset() }), inst.options.tolerance)
20032			) { childrenIntersection = true; return false; }
20033		});
20034		if(childrenIntersection) return false;
20035
20036		if(this.accept.call(this.element[0],(draggable.currentItem || draggable.element))) {
20037			if(this.options.activeClass) this.element.removeClass(this.options.activeClass);
20038			if(this.options.hoverClass) this.element.removeClass(this.options.hoverClass);
20039			this._trigger('drop', event, this.ui(draggable));
20040			return this.element;
20041		}
20042
20043		return false;
20044
20045	},
20046
20047	ui: function(c) {
20048		return {
20049			draggable: (c.currentItem || c.element),
20050			helper: c.helper,
20051			position: c.position,
20052			offset: c.positionAbs
20053		};
20054	}
20055
20056});
20057
20058$.extend($.ui.droppable, {
20059	version: "1.8.16"
20060});
20061
20062$.ui.intersect = function(draggable, droppable, toleranceMode) {
20063
20064	if (!droppable.offset) return false;
20065
20066	var x1 = (draggable.positionAbs || draggable.position.absolute).left, x2 = x1 + draggable.helperProportions.width,
20067		y1 = (draggable.positionAbs || draggable.position.absolute).top, y2 = y1 + draggable.helperProportions.height;
20068	var l = droppable.offset.left, r = l + droppable.proportions.width,
20069		t = droppable.offset.top, b = t + droppable.proportions.height;
20070
20071	switch (toleranceMode) {
20072		case 'fit':
20073			return (l <= x1 && x2 <= r
20074				&& t <= y1 && y2 <= b);
20075			break;
20076		case 'intersect':
20077			return (l < x1 + (draggable.helperProportions.width / 2) // Right Half
20078				&& x2 - (draggable.helperProportions.width / 2) < r // Left Half
20079				&& t < y1 + (draggable.helperProportions.height / 2) // Bottom Half
20080				&& y2 - (draggable.helperProportions.height / 2) < b ); // Top Half
20081			break;
20082		case 'pointer':
20083			var draggableLeft = ((draggable.positionAbs || draggable.position.absolute).left + (draggable.clickOffset || draggable.offset.click).left),
20084				draggableTop = ((draggable.positionAbs || draggable.position.absolute).top + (draggable.clickOffset || draggable.offset.click).top),
20085				isOver = $.ui.isOver(draggableTop, draggableLeft, t, l, droppable.proportions.height, droppable.proportions.width);
20086			return isOver;
20087			break;
20088		case 'touch':
20089			return (
20090					(y1 >= t && y1 <= b) ||	// Top edge touching
20091					(y2 >= t && y2 <= b) ||	// Bottom edge touching
20092					(y1 < t && y2 > b)		// Surrounded vertically
20093				) && (
20094					(x1 >= l && x1 <= r) ||	// Left edge touching
20095					(x2 >= l && x2 <= r) ||	// Right edge touching
20096					(x1 < l && x2 > r)		// Surrounded horizontally
20097				);
20098			break;
20099		default:
20100			return false;
20101			break;
20102		}
20103
20104};
20105
20106/*
20107	This manager tracks offsets of draggables and droppables
20108*/
20109$.ui.ddmanager = {
20110	current: null,
20111	droppables: { 'default': [] },
20112	prepareOffsets: function(t, event) {
20113
20114		var m = $.ui.ddmanager.droppables[t.options.scope] || [];
20115		var type = event ? event.type : null; // workaround for #2317
20116		var list = (t.currentItem || t.element).find(":data(droppable)").andSelf();
20117
20118		droppablesLoop: for (var i = 0; i < m.length; i++) {
20119
20120			if(m[i].options.disabled || (t && !m[i].accept.call(m[i].element[0],(t.currentItem || t.element)))) continue;	//No disabled and non-accepted
20121			for (var j=0; j < list.length; j++) { if(list[j] == m[i].element[0]) { m[i].proportions.height = 0; continue droppablesLoop; } }; //Filter out elements in the current dragged item
20122			m[i].visible = m[i].element.css("display") != "none"; if(!m[i].visible) continue; 									//If the element is not visible, continue
20123
20124			if(type == "mousedown") m[i]._activate.call(m[i], event); //Activate the droppable if used directly from draggables
20125
20126			m[i].offset = m[i].element.offset();
20127			m[i].proportions = { width: m[i].element[0].offsetWidth, height: m[i].element[0].offsetHeight };
20128
20129		}
20130
20131	},
20132	drop: function(draggable, event) {
20133
20134		var dropped = false;
20135		$.each($.ui.ddmanager.droppables[draggable.options.scope] || [], function() {
20136
20137			if(!this.options) return;
20138			if (!this.options.disabled && this.visible && $.ui.intersect(draggable, this, this.options.tolerance))
20139				dropped = dropped || this._drop.call(this, event);
20140
20141			if (!this.options.disabled && this.visible && this.accept.call(this.element[0],(draggable.currentItem || draggable.element))) {
20142				this.isout = 1; this.isover = 0;
20143				this._deactivate.call(this, event);
20144			}
20145
20146		});
20147		return dropped;
20148
20149	},
20150	dragStart: function( draggable, event ) {
20151		//Listen for scrolling so that if the dragging causes scrolling the position of the droppables can be recalculated (see #5003)
20152		draggable.element.parents( ":not(body,html)" ).bind( "scroll.droppable", function() {
20153			if( !draggable.options.refreshPositions ) $.ui.ddmanager.prepareOffsets( draggable, event );
20154		});
20155	},
20156	drag: function(draggable, event) {
20157
20158		//If you have a highly dynamic page, you might try this option. It renders positions every time you move the mouse.
20159		if(draggable.options.refreshPositions) $.ui.ddmanager.prepareOffsets(draggable, event);
20160
20161		//Run through all droppables and check their positions based on specific tolerance options
20162		$.each($.ui.ddmanager.droppables[draggable.options.scope] || [], function() {
20163
20164			if(this.options.disabled || this.greedyChild || !this.visible) return;
20165			var intersects = $.ui.intersect(draggable, this, this.options.tolerance);
20166
20167			var c = !intersects && this.isover == 1 ? 'isout' : (intersects && this.isover == 0 ? 'isover' : null);
20168			if(!c) return;
20169
20170			var parentInstance;
20171			if (this.options.greedy) {
20172				var parent = this.element.parents(':data(droppable):eq(0)');
20173				if (parent.length) {
20174					parentInstance = $.data(parent[0], 'droppable');
20175					parentInstance.greedyChild = (c == 'isover' ? 1 : 0);
20176				}
20177			}
20178
20179			// we just moved into a greedy child
20180			if (parentInstance && c == 'isover') {
20181				parentInstance['isover'] = 0;
20182				parentInstance['isout'] = 1;
20183				parentInstance._out.call(parentInstance, event);
20184			}
20185
20186			this[c] = 1; this[c == 'isout' ? 'isover' : 'isout'] = 0;
20187			this[c == "isover" ? "_over" : "_out"].call(this, event);
20188
20189			// we just moved out of a greedy child
20190			if (parentInstance && c == 'isout') {
20191				parentInstance['isout'] = 0;
20192				parentInstance['isover'] = 1;
20193				parentInstance._over.call(parentInstance, event);
20194			}
20195		});
20196
20197	},
20198	dragStop: function( draggable, event ) {
20199		draggable.element.parents( ":not(body,html)" ).unbind( "scroll.droppable" );
20200		//Call prepareOffsets one final time since IE does not fire return scroll events when overflow was caused by drag (see #5003)
20201		if( !draggable.options.refreshPositions ) $.ui.ddmanager.prepareOffsets( draggable, event );
20202	}
20203};
20204
20205})(jQuery);
20206
20207/*
20208 * jQuery UI Resizable 1.8.16
20209 *
20210 * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
20211 * Dual licensed under the MIT or GPL Version 2 licenses.
20212 * http://jquery.org/license
20213 *
20214 * http://docs.jquery.com/UI/Resizables
20215 *
20216 * Depends:
20217 *	jquery.ui.core.js
20218 *	jquery.ui.mouse.js
20219 *	jquery.ui.widget.js
20220 */
20221(function( $, undefined ) {
20222
20223$.widget("ui.resizable", $.ui.mouse, {
20224	widgetEventPrefix: "resize",
20225	options: {
20226		alsoResize: false,
20227		animate: false,
20228		animateDuration: "slow",
20229		animateEasing: "swing",
20230		aspectRatio: false,
20231		autoHide: false,
20232		containment: false,
20233		ghost: false,
20234		grid: false,
20235		handles: "e,s,se",
20236		helper: false,
20237		maxHeight: null,
20238		maxWidth: null,
20239		minHeight: 10,
20240		minWidth: 10,
20241		zIndex: 1000
20242	},
20243	_create: function() {
20244
20245		var self = this, o = this.options;
20246		this.element.addClass("ui-resizable");
20247
20248		$.extend(this, {
20249			_aspectRatio: !!(o.aspectRatio),
20250			aspectRatio: o.aspectRatio,
20251			originalElement: this.element,
20252			_proportionallyResizeElements: [],
20253			_helper: o.helper || o.ghost || o.animate ? o.helper || 'ui-resizable-helper' : null
20254		});
20255
20256		//Wrap the element if it cannot hold child nodes
20257		if(this.element[0].nodeName.match(/canvas|textarea|input|select|button|img/i)) {
20258
20259			//Opera fix for relative positioning
20260			if (/relative/.test(this.element.css('position')) && $.browser.opera)
20261				this.element.css({ position: 'relative', top: 'auto', left: 'auto' });
20262
20263			//Create a wrapper element and set the wrapper to the new current internal element
20264			this.element.wrap(
20265				$('<div class="ui-wrapper" style="overflow: hidden;"></div>').css({
20266					position: this.element.css('position'),
20267					width: this.element.outerWidth(),
20268					height: this.element.outerHeight(),
20269					top: this.element.css('top'),
20270					left: this.element.css('left')
20271				})
20272			);
20273
20274			//Overwrite the original this.element
20275			this.element = this.element.parent().data(
20276				"resizable", this.element.data('resizable')
20277			);
20278
20279			this.elementIsWrapper = true;
20280
20281			//Move margins to the wrapper
20282			this.element.css({ marginLeft: this.originalElement.css("marginLeft"), marginTop: this.originalElement.css("marginTop"), marginRight: this.originalElement.css("marginRight"), marginBottom: this.originalElement.css("marginBottom") });
20283			this.originalElement.css({ marginLeft: 0, marginTop: 0, marginRight: 0, marginBottom: 0});
20284
20285			//Prevent Safari textarea resize
20286			this.originalResizeStyle = this.originalElement.css('resize');
20287			this.originalElement.css('resize', 'none');
20288
20289			//Push the actual element to our proportionallyResize internal array
20290			this._proportionallyResizeElements.push(this.originalElement.css({ position: 'static', zoom: 1, display: 'block' }));
20291
20292			// avoid IE jump (hard set the margin)
20293			this.originalElement.css({ margin: this.originalElement.css('margin') });
20294
20295			// fix handlers offset
20296			this._proportionallyResize();
20297
20298		}
20299
20300		this.handles = o.handles || (!$('.ui-resizable-handle', this.element).length ? "e,s,se" : { n: '.ui-resizable-n', e: '.ui-resizable-e', s: '.ui-resizable-s', w: '.ui-resizable-w', se: '.ui-resizable-se', sw: '.ui-resizable-sw', ne: '.ui-resizable-ne', nw: '.ui-resizable-nw' });
20301		if(this.handles.constructor == String) {
20302
20303			if(this.handles == 'all') this.handles = 'n,e,s,w,se,sw,ne,nw';
20304			var n = this.handles.split(","); this.handles = {};
20305
20306			for(var i = 0; i < n.length; i++) {
20307
20308				var handle = $.trim(n[i]), hname = 'ui-resizable-'+handle;
20309				var axis = $('<div class="ui-resizable-handle ' + hname + '"></div>');
20310
20311				// increase zIndex of sw, se, ne, nw axis
20312				//TODO : this modifies original option
20313				if(/sw|se|ne|nw/.test(handle)) axis.css({ zIndex: ++o.zIndex });
20314
20315				//TODO : What's going on here?
20316				if ('se' == handle) {
20317					axis.addClass('ui-icon ui-icon-gripsmall-diagonal-se');
20318				};
20319
20320				//Insert into internal handles object and append to element
20321				this.handles[handle] = '.ui-resizable-'+handle;
20322				this.element.append(axis);
20323			}
20324
20325		}
20326
20327		this._renderAxis = function(target) {
20328
20329			target = target || this.element;
20330
20331			for(var i in this.handles) {
20332
20333				if(this.handles[i].constructor == String)
20334					this.handles[i] = $(this.handles[i], this.element).show();
20335
20336				//Apply pad to wrapper element, needed to fix axis position (textarea, inputs, scrolls)
20337				if (this.elementIsWrapper && this.originalElement[0].nodeName.match(/textarea|input|select|button/i)) {
20338
20339					var axis = $(this.handles[i], this.element), padWrapper = 0;
20340
20341					//Checking the correct pad and border
20342					padWrapper = /sw|ne|nw|se|n|s/.test(i) ? axis.outerHeight() : axis.outerWidth();
20343
20344					//The padding type i have to apply...
20345					var padPos = [ 'padding',
20346						/ne|nw|n/.test(i) ? 'Top' :
20347						/se|sw|s/.test(i) ? 'Bottom' :
20348						/^e$/.test(i) ? 'Right' : 'Left' ].join("");
20349
20350					target.css(padPos, padWrapper);
20351
20352					this._proportionallyResize();
20353
20354				}
20355
20356				//TODO: What's that good for? There's not anything to be executed left
20357				if(!$(this.handles[i]).length)
20358					continue;
20359
20360			}
20361		};
20362
20363		//TODO: make renderAxis a prototype function
20364		this._renderAxis(this.element);
20365
20366		this._handles = $('.ui-resizable-handle', this.element)
20367			.disableSelection();
20368
20369		//Matching axis name
20370		this._handles.mouseover(function() {
20371			if (!self.resizing) {
20372				if (this.className)
20373					var axis = this.className.match(/ui-resizable-(se|sw|ne|nw|n|e|s|w)/i);
20374				//Axis, default = se
20375				self.axis = axis && axis[1] ? axis[1] : 'se';
20376			}
20377		});
20378
20379		//If we want to auto hide the elements
20380		if (o.autoHide) {
20381			this._handles.hide();
20382			$(this.element)
20383				.addClass("ui-resizable-autohide")
20384				.hover(function() {
20385					if (o.disabled) return;
20386					$(this).removeClass("ui-resizable-autohide");
20387					self._handles.show();
20388				},
20389				function(){
20390					if (o.disabled) return;
20391					if (!self.resizing) {
20392						$(this).addClass("ui-resizable-autohide");
20393						self._handles.hide();
20394					}
20395				});
20396		}
20397
20398		//Initialize the mouse interaction
20399		this._mouseInit();
20400
20401	},
20402
20403	destroy: function() {
20404
20405		this._mouseDestroy();
20406
20407		var _destroy = function(exp) {
20408			$(exp).removeClass("ui-resizable ui-resizable-disabled ui-resizable-resizing")
20409				.removeData("resizable").unbind(".resizable").find('.ui-resizable-handle').remove();
20410		};
20411
20412		//TODO: Unwrap at same DOM position
20413		if (this.elementIsWrapper) {
20414			_destroy(this.element);
20415			var wrapper = this.element;
20416			wrapper.after(
20417				this.originalElement.css({
20418					position: wrapper.css('position'),
20419					width: wrapper.outerWidth(),
20420					height: wrapper.outerHeight(),
20421					top: wrapper.css('top'),
20422					left: wrapper.css('left')
20423				})
20424			).remove();
20425		}
20426
20427		this.originalElement.css('resize', this.originalResizeStyle);
20428		_destroy(this.originalElement);
20429
20430		return this;
20431	},
20432
20433	_mouseCapture: function(event) {
20434		var handle = false;
20435		for (var i in this.handles) {
20436			if ($(this.handles[i])[0] == event.target) {
20437				handle = true;
20438			}
20439		}
20440
20441		return !this.options.disabled && handle;
20442	},
20443
20444	_mouseStart: function(event) {
20445
20446		var o = this.options, iniPos = this.element.position(), el = this.element;
20447
20448		this.resizing = true;
20449		this.documentScroll = { top: $(document).scrollTop(), left: $(document).scrollLeft() };
20450
20451		// bugfix for http://dev.jquery.com/ticket/1749
20452		if (el.is('.ui-draggable') || (/absolute/).test(el.css('position'))) {
20453			el.css({ position: 'absolute', top: iniPos.top, left: iniPos.left });
20454		}
20455
20456		//Opera fixing relative position
20457		if ($.browser.opera && (/relative/).test(el.css('position')))
20458			el.css({ position: 'relative', top: 'auto', left: 'auto' });
20459
20460		this._renderProxy();
20461
20462		var curleft = num(this.helper.css('left')), curtop = num(this.helper.css('top'));
20463
20464		if (o.containment) {
20465			curleft += $(o.containment).scrollLeft() || 0;
20466			curtop += $(o.containment).scrollTop() || 0;
20467		}
20468
20469		//Store needed variables
20470		this.offset = this.helper.offset();
20471		this.position = { left: curleft, top: curtop };
20472		this.size = this._helper ? { width: el.outerWidth(), height: el.outerHeight() } : { width: el.width(), height: el.height() };
20473		this.originalSize = this._helper ? { width: el.outerWidth(), height: el.outerHeight() } : { width: el.width(), height: el.height() };
20474		this.originalPosition = { left: curleft, top: curtop };
20475		this.sizeDiff = { width: el.outerWidth() - el.width(), height: el.outerHeight() - el.height() };
20476		this.originalMousePosition = { left: event.pageX, top: event.pageY };
20477
20478		//Aspect Ratio
20479		this.aspectRatio = (typeof o.aspectRatio == 'number') ? o.aspectRatio : ((this.originalSize.width / this.originalSize.height) || 1);
20480
20481	    var cursor = $('.ui-resizable-' + this.axis).css('cursor');
20482	    $('body').css('cursor', cursor == 'auto' ? this.axis + '-resize' : cursor);
20483
20484		el.addClass("ui-resizable-resizing");
20485		this._propagate("start", event);
20486		return true;
20487	},
20488
20489	_mouseDrag: function(event) {
20490
20491		//Increase performance, avoid regex
20492		var el = this.helper, o = this.options, props = {},
20493			self = this, smp = this.originalMousePosition, a = this.axis;
20494
20495		var dx = (event.pageX-smp.left)||0, dy = (event.pageY-smp.top)||0;
20496		var trigger = this._change[a];
20497		if (!trigger) return false;
20498
20499		// Calculate the attrs that will be change
20500		var data = trigger.apply(this, [event, dx, dy]), ie6 = $.browser.msie && $.browser.version < 7, csdif = this.sizeDiff;
20501
20502		// Put this in the mouseDrag handler since the user can start pressing shift while resizing
20503		this._updateVirtualBoundaries(event.shiftKey);
20504		if (this._aspectRatio || event.shiftKey)
20505			data = this._updateRatio(data, event);
20506
20507		data = this._respectSize(data, event);
20508
20509		// plugins callbacks need to be called first
20510		this._propagate("resize", event);
20511
20512		el.css({
20513			top: this.position.top + "px", left: this.position.left + "px",
20514			width: this.size.width + "px", height: this.size.height + "px"
20515		});
20516
20517		if (!this._helper && this._proportionallyResizeElements.length)
20518			this._proportionallyResize();
20519
20520		this._updateCache(data);
20521
20522		// calling the user callback at the end
20523		this._trigger('resize', event, this.ui());
20524
20525		return false;
20526	},
20527
20528	_mouseStop: function(event) {
20529
20530		this.resizing = false;
20531		var o = this.options, self = this;
20532
20533		if(this._helper) {
20534			var pr = this._proportionallyResizeElements, ista = pr.length && (/textarea/i).test(pr[0].nodeName),
20535				soffseth = ista && $.ui.hasScroll(pr[0], 'left') /* TODO - jump height */ ? 0 : self.sizeDiff.height,
20536				soffsetw = ista ? 0 : self.sizeDiff.width;
20537
20538			var s = { width: (self.helper.width()  - soffsetw), height: (self.helper.height() - soffseth) },
20539				left = (parseInt(self.element.css('left'), 10) + (self.position.left - self.originalPosition.left)) || null,
20540				top = (parseInt(self.element.css('top'), 10) + (self.position.top - self.originalPosition.top)) || null;
20541
20542			if (!o.animate)
20543				this.element.css($.extend(s, { top: top, left: left }));
20544
20545			self.helper.height(self.size.height);
20546			self.helper.width(self.size.width);
20547
20548			if (this._helper && !o.animate) this._proportionallyResize();
20549		}
20550
20551		$('body').css('cursor', 'auto');
20552
20553		this.element.removeClass("ui-resizable-resizing");
20554
20555		this._propagate("stop", event);
20556
20557		if (this._helper) this.helper.remove();
20558		return false;
20559
20560	},
20561
20562    _updateVirtualBoundaries: function(forceAspectRatio) {
20563        var o = this.options, pMinWidth, pMaxWidth, pMinHeight, pMaxHeight, b;
20564
20565        b = {
20566            minWidth: isNumber(o.minWidth) ? o.minWidth : 0,
20567            maxWidth: isNumber(o.maxWidth) ? o.maxWidth : Infinity,
20568            minHeight: isNumber(o.minHeight) ? o.minHeight : 0,
20569            maxHeight: isNumber(o.maxHeight) ? o.maxHeight : Infinity
20570        };
20571
20572        if(this._aspectRatio || forceAspectRatio) {
20573            // We want to create an enclosing box whose aspect ration is the requested one
20574            // First, compute the "projected" size for each dimension based on the aspect ratio and other dimension
20575            pMinWidth = b.minHeight * this.aspectRatio;
20576            pMinHeight = b.minWidth / this.aspectRatio;
20577            pMaxWidth = b.maxHeight * this.aspectRatio;
20578            pMaxHeight = b.maxWidth / this.aspectRatio;
20579
20580            if(pMinWidth > b.minWidth) b.minWidth = pMinWidth;
20581            if(pMinHeight > b.minHeight) b.minHeight = pMinHeight;
20582            if(pMaxWidth < b.maxWidth) b.maxWidth = pMaxWidth;
20583            if(pMaxHeight < b.maxHeight) b.maxHeight = pMaxHeight;
20584        }
20585        this._vBoundaries = b;
20586    },
20587
20588	_updateCache: function(data) {
20589		var o = this.options;
20590		this.offset = this.helper.offset();
20591		if (isNumber(data.left)) this.position.left = data.left;
20592		if (isNumber(data.top)) this.position.top = data.top;
20593		if (isNumber(data.height)) this.size.height = data.height;
20594		if (isNumber(data.width)) this.size.width = data.width;
20595	},
20596
20597	_updateRatio: function(data, event) {
20598
20599		var o = this.options, cpos = this.position, csize = this.size, a = this.axis;
20600
20601		if (isNumber(data.height)) data.width = (data.height * this.aspectRatio);
20602		else if (isNumber(data.width)) data.height = (data.width / this.aspectRatio);
20603
20604		if (a == 'sw') {
20605			data.left = cpos.left + (csize.width - data.width);
20606			data.top = null;
20607		}
20608		if (a == 'nw') {
20609			data.top = cpos.top + (csize.height - data.height);
20610			data.left = cpos.left + (csize.width - data.width);
20611		}
20612
20613		return data;
20614	},
20615
20616	_respectSize: function(data, event) {
20617
20618		var el = this.helper, o = this._vBoundaries, pRatio = this._aspectRatio || event.shiftKey, a = this.axis,
20619				ismaxw = isNumber(data.width) && o.maxWidth && (o.maxWidth < data.width), ismaxh = isNumber(data.height) && o.maxHeight && (o.maxHeight < data.height),
20620					isminw = isNumber(data.width) && o.minWidth && (o.minWidth > data.width), isminh = isNumber(data.height) && o.minHeight && (o.minHeight > data.height);
20621
20622		if (isminw) data.width = o.minWidth;
20623		if (isminh) data.height = o.minHeight;
20624		if (ismaxw) data.width = o.maxWidth;
20625		if (ismaxh) data.height = o.maxHeight;
20626
20627		var dw = this.originalPosition.left + this.originalSize.width, dh = this.position.top + this.size.height;
20628		var cw = /sw|nw|w/.test(a), ch = /nw|ne|n/.test(a);
20629
20630		if (isminw && cw) data.left = dw - o.minWidth;
20631		if (ismaxw && cw) data.left = dw - o.maxWidth;
20632		if (isminh && ch)	data.top = dh - o.minHeight;
20633		if (ismaxh && ch)	data.top = dh - o.maxHeight;
20634
20635		// fixing jump error on top/left - bug #2330
20636		var isNotwh = !data.width && !data.height;
20637		if (isNotwh && !data.left && data.top) data.top = null;
20638		else if (isNotwh && !data.top && data.left) data.left = null;
20639
20640		return data;
20641	},
20642
20643	_proportionallyResize: function() {
20644
20645		var o = this.options;
20646		if (!this._proportionallyResizeElements.length) return;
20647		var element = this.helper || this.element;
20648
20649		for (var i=0; i < this._proportionallyResizeElements.length; i++) {
20650
20651			var prel = this._proportionallyResizeElements[i];
20652
20653			if (!this.borderDif) {
20654				var b = [prel.css('borderTopWidth'), prel.css('borderRightWidth'), prel.css('borderBottomWidth'), prel.css('borderLeftWidth')],
20655					p = [prel.css('paddingTop'), prel.css('paddingRight'), prel.css('paddingBottom'), prel.css('paddingLeft')];
20656
20657				this.borderDif = $.map(b, function(v, i) {
20658					var border = parseInt(v,10)||0, padding = parseInt(p[i],10)||0;
20659					return border + padding;
20660				});
20661			}
20662
20663			if ($.browser.msie && !(!($(element).is(':hidden') || $(element).parents(':hidden').length)))
20664				continue;
20665
20666			prel.css({
20667				height: (element.height() - this.borderDif[0] - this.borderDif[2]) || 0,
20668				width: (element.width() - this.borderDif[1] - this.borderDif[3]) || 0
20669			});
20670
20671		};
20672
20673	},
20674
20675	_renderProxy: function() {
20676
20677		var el = this.element, o = this.options;
20678		this.elementOffset = el.offset();
20679
20680		if(this._helper) {
20681
20682			this.helper = this.helper || $('<div style="overflow:hidden;"></div>');
20683
20684			// fix ie6 offset TODO: This seems broken
20685			var ie6 = $.browser.msie && $.browser.version < 7, ie6offset = (ie6 ? 1 : 0),
20686			pxyoffset = ( ie6 ? 2 : -1 );
20687
20688			this.helper.addClass(this._helper).css({
20689				width: this.element.outerWidth() + pxyoffset,
20690				height: this.element.outerHeight() + pxyoffset,
20691				position: 'absolute',
20692				left: this.elementOffset.left - ie6offset +'px',
20693				top: this.elementOffset.top - ie6offset +'px',
20694				zIndex: ++o.zIndex //TODO: Don't modify option
20695			});
20696
20697			this.helper
20698				.appendTo("body")
20699				.disableSelection();
20700
20701		} else {
20702			this.helper = this.element;
20703		}
20704
20705	},
20706
20707	_change: {
20708		e: function(event, dx, dy) {
20709			return { width: this.originalSize.width + dx };
20710		},
20711		w: function(event, dx, dy) {
20712			var o = this.options, cs = this.originalSize, sp = this.originalPosition;
20713			return { left: sp.left + dx, width: cs.width - dx };
20714		},
20715		n: function(event, dx, dy) {
20716			var o = this.options, cs = this.originalSize, sp = this.originalPosition;
20717			return { top: sp.top + dy, height: cs.height - dy };
20718		},
20719		s: function(event, dx, dy) {
20720			return { height: this.originalSize.height + dy };
20721		},
20722		se: function(event, dx, dy) {
20723			return $.extend(this._change.s.apply(this, arguments), this._change.e.apply(this, [event, dx, dy]));
20724		},
20725		sw: function(event, dx, dy) {
20726			return $.extend(this._change.s.apply(this, arguments), this._change.w.apply(this, [event, dx, dy]));
20727		},
20728		ne: function(event, dx, dy) {
20729			return $.extend(this._change.n.apply(this, arguments), this._change.e.apply(this, [event, dx, dy]));
20730		},
20731		nw: function(event, dx, dy) {
20732			return $.extend(this._change.n.apply(this, arguments), this._change.w.apply(this, [event, dx, dy]));
20733		}
20734	},
20735
20736	_propagate: function(n, event) {
20737		$.ui.plugin.call(this, n, [event, this.ui()]);
20738		(n != "resize" && this._trigger(n, event, this.ui()));
20739	},
20740
20741	plugins: {},
20742
20743	ui: function() {
20744		return {
20745			originalElement: this.originalElement,
20746			element: this.element,
20747			helper: this.helper,
20748			position: this.position,
20749			size: this.size,
20750			originalSize: this.originalSize,
20751			originalPosition: this.originalPosition
20752		};
20753	}
20754
20755});
20756
20757$.extend($.ui.resizable, {
20758	version: "1.8.16"
20759});
20760
20761/*
20762 * Resizable Extensions
20763 */
20764
20765$.ui.plugin.add("resizable", "alsoResize", {
20766
20767	start: function (event, ui) {
20768		var self = $(this).data("resizable"), o = self.options;
20769
20770		var _store = function (exp) {
20771			$(exp).each(function() {
20772				var el = $(this);
20773				el.data("resizable-alsoresize", {
20774					width: parseInt(el.width(), 10), height: parseInt(el.height(), 10),
20775					left: parseInt(el.css('left'), 10), top: parseInt(el.css('top'), 10),
20776					position: el.css('position') // to reset Opera on stop()
20777				});
20778			});
20779		};
20780
20781		if (typeof(o.alsoResize) == 'object' && !o.alsoResize.parentNode) {
20782			if (o.alsoResize.length) { o.alsoResize = o.alsoResize[0]; _store(o.alsoResize); }
20783			else { $.each(o.alsoResize, function (exp) { _store(exp); }); }
20784		}else{
20785			_store(o.alsoResize);
20786		}
20787	},
20788
20789	resize: function (event, ui) {
20790		var self = $(this).data("resizable"), o = self.options, os = self.originalSize, op = self.originalPosition;
20791
20792		var delta = {
20793			height: (self.size.height - os.height) || 0, width: (self.size.width - os.width) || 0,
20794			top: (self.position.top - op.top) || 0, left: (self.position.left - op.left) || 0
20795		},
20796
20797		_alsoResize = function (exp, c) {
20798			$(exp).each(function() {
20799				var el = $(this), start = $(this).data("resizable-alsoresize"), style = {}, 
20800					css = c && c.length ? c : el.parents(ui.originalElement[0]).length ? ['width', 'height'] : ['width', 'height', 'top', 'left'];
20801
20802				$.each(css, function (i, prop) {
20803					var sum = (start[prop]||0) + (delta[prop]||0);
20804					if (sum && sum >= 0)
20805						style[prop] = sum || null;
20806				});
20807
20808				// Opera fixing relative position
20809				if ($.browser.opera && /relative/.test(el.css('position'))) {
20810					self._revertToRelativePosition = true;
20811					el.css({ position: 'absolute', top: 'auto', left: 'auto' });
20812				}
20813
20814				el.css(style);
20815			});
20816		};
20817
20818		if (typeof(o.alsoResize) == 'object' && !o.alsoResize.nodeType) {
20819			$.each(o.alsoResize, function (exp, c) { _alsoResize(exp, c); });
20820		}else{
20821			_alsoResize(o.alsoResize);
20822		}
20823	},
20824
20825	stop: function (event, ui) {
20826		var self = $(this).data("resizable"), o = self.options;
20827
20828		var _reset = function (exp) {
20829			$(exp).each(function() {
20830				var el = $(this);
20831				// reset position for Opera - no need to verify it was changed
20832				el.css({ position: el.data("resizable-alsoresize").position });
20833			});
20834		};
20835
20836		if (self._revertToRelativePosition) {
20837			self._revertToRelativePosition = false;
20838			if (typeof(o.alsoResize) == 'object' && !o.alsoResize.nodeType) {
20839				$.each(o.alsoResize, function (exp) { _reset(exp); });
20840			}else{
20841				_reset(o.alsoResize);
20842			}
20843		}
20844
20845		$(this).removeData("resizable-alsoresize");
20846	}
20847});
20848
20849$.ui.plugin.add("resizable", "animate", {
20850
20851	stop: function(event, ui) {
20852		var self = $(this).data("resizable"), o = self.options;
20853
20854		var pr = self._proportionallyResizeElements, ista = pr.length && (/textarea/i).test(pr[0].nodeName),
20855					soffseth = ista && $.ui.hasScroll(pr[0], 'left') /* TODO - jump height */ ? 0 : self.sizeDiff.height,
20856						soffsetw = ista ? 0 : self.sizeDiff.width;
20857
20858		var style = { width: (self.size.width - soffsetw), height: (self.size.height - soffseth) },
20859					left = (parseInt(self.element.css('left'), 10) + (self.position.left - self.originalPosition.left)) || null,
20860						top = (parseInt(self.element.css('top'), 10) + (self.position.top - self.originalPosition.top)) || null;
20861
20862		self.element.animate(
20863			$.extend(style, top && left ? { top: top, left: left } : {}), {
20864				duration: o.animateDuration,
20865				easing: o.animateEasing,
20866				step: function() {
20867
20868					var data = {
20869						width: parseInt(self.element.css('width'), 10),
20870						height: parseInt(self.element.css('height'), 10),
20871						top: parseInt(self.element.css('top'), 10),
20872						left: parseInt(self.element.css('left'), 10)
20873					};
20874
20875					if (pr && pr.length) $(pr[0]).css({ width: data.width, height: data.height });
20876
20877					// propagating resize, and updating values for each animation step
20878					self._updateCache(data);
20879					self._propagate("resize", event);
20880
20881				}
20882			}
20883		);
20884	}
20885
20886});
20887
20888$.ui.plugin.add("resizable", "containment", {
20889
20890	start: function(event, ui) {
20891		var self = $(this).data("resizable"), o = self.options, el = self.element;
20892		var oc = o.containment,	ce = (oc instanceof $) ? oc.get(0) : (/parent/.test(oc)) ? el.parent().get(0) : oc;
20893		if (!ce) return;
20894
20895		self.containerElement = $(ce);
20896
20897		if (/document/.test(oc) || oc == document) {
20898			self.containerOffset = { left: 0, top: 0 };
20899			self.containerPosition = { left: 0, top: 0 };
20900
20901			self.parentData = {
20902				element: $(document), left: 0, top: 0,
20903				width: $(document).width(), height: $(document).height() || document.body.parentNode.scrollHeight
20904			};
20905		}
20906
20907		// i'm a node, so compute top, left, right, bottom
20908		else {
20909			var element = $(ce), p = [];
20910			$([ "Top", "Right", "Left", "Bottom" ]).each(function(i, name) { p[i] = num(element.css("padding" + name)); });
20911
20912			self.containerOffset = element.offset();
20913			self.containerPosition = element.position();
20914			self.containerSize = { height: (element.innerHeight() - p[3]), width: (element.innerWidth() - p[1]) };
20915
20916			var co = self.containerOffset, ch = self.containerSize.height,	cw = self.containerSize.width,
20917						width = ($.ui.hasScroll(ce, "left") ? ce.scrollWidth : cw ), height = ($.ui.hasScroll(ce) ? ce.scrollHeight : ch);
20918
20919			self.parentData = {
20920				element: ce, left: co.left, top: co.top, width: width, height: height
20921			};
20922		}
20923	},
20924
20925	resize: function(event, ui) {
20926		var self = $(this).data("resizable"), o = self.options,
20927				ps = self.containerSize, co = self.containerOffset, cs = self.size, cp = self.position,
20928				pRatio = self._aspectRatio || event.shiftKey, cop = { top:0, left
20928:0 }, ce = self.containerElement;
20929
20930		if (ce[0] != document && (/static/).test(ce.css('position'))) cop = co;
20931
20932		if (cp.left < (self._helper ? co.left : 0)) {
20933			self.size.width = self.size.width + (self._helper ? (self.position.left - co.left) : (self.position.left - cop.left));
20934			if (pRatio) self.size.height = self.size.width / o.aspectRatio;
20935			self.position.left = o.helper ? co.left : 0;
20936		}
20937
20938		if (cp.top < (self._helper ? co.top : 0)) {
20939			self.size.height = self.size.height + (self._helper ? (self.position.top - co.top) : self.position.top);
20940			if (pRatio) self.size.width = self.size.height * o.aspectRatio;
20941			self.position.top = self._helper ? co.top : 0;
20942		}
20943
20944		self.offset.left = self.parentData.left+self.position.left;
20945		self.offset.top = self.parentData.top+self.position.top;
20946
20947		var woset = Math.abs( (self._helper ? self.offset.left - cop.left : (self.offset.left - cop.left)) + self.sizeDiff.width ),
20948					hoset = Math.abs( (self._helper ? self.offset.top - cop.top : (self.offset.top - co.top)) + self.sizeDiff.height );
20949
20950		var isParent = self.containerElement.get(0) == self.element.parent().get(0),
20951		    isOffsetRelative = /relative|absolute/.test(self.containerElement.css('position'));
20952
20953		if(isParent && isOffsetRelative) woset -= self.parentData.left;
20954
20955		if (woset + self.size.width >= self.parentData.width) {
20956			self.size.width = self.parentData.width - woset;
20957			if (pRatio) self.size.height = self.size.width / self.aspectRatio;
20958		}
20959
20960		if (hoset + self.size.height >= self.parentData.height) {
20961			self.size.height = self.parentData.height - hoset;
20962			if (pRatio) self.size.width = self.size.height * self.aspectRatio;
20963		}
20964	},
20965
20966	stop: function(event, ui){
20967		var self = $(this).data("resizable"), o = self.options, cp = self.position,
20968				co = self.containerOffset, cop = self.containerPosition, ce = self.containerElement;
20969
20970		var helper = $(self.helper), ho = helper.offset(), w = helper.outerWidth() - self.sizeDiff.width, h = helper.outerHeight() - self.sizeDiff.height;
20971
20972		if (self._helper && !o.animate && (/relative/).test(ce.css('position')))
20973			$(this).css({ left: ho.left - cop.left - co.left, width: w, height: h });
20974
20975		if (self._helper && !o.animate && (/static/).test(ce.css('position')))
20976			$(this).css({ left: ho.left - cop.left - co.left, width: w, height: h });
20977
20978	}
20979});
20980
20981$.ui.plugin.add("resizable", "ghost", {
20982
20983	start: function(event, ui) {
20984
20985		var self = $(this).data("resizable"), o = self.options, cs = self.size;
20986
20987		self.ghost = self.originalElement.clone();
20988		self.ghost
20989			.css({ opacity: .25, display: 'block', position: 'relative', height: cs.height, width: cs.width, margin: 0, left: 0, top: 0 })
20990			.addClass('ui-resizable-ghost')
20991			.addClass(typeof o.ghost == 'string' ? o.ghost : '');
20992
20993		self.ghost.appendTo(self.helper);
20994
20995	},
20996
20997	resize: function(event, ui){
20998		var self = $(this).data("resizable"), o = self.options;
20999		if (self.ghost) self.ghost.css({ position: 'relative', height: self.size.height, width: self.size.width });
21000	},
21001
21002	stop: function(event, ui){
21003		var self = $(this).data("resizable"), o = self.options;
21004		if (self.ghost && self.helper) self.helper.get(0).removeChild(self.ghost.get(0));
21005	}
21006
21007});
21008
21009$.ui.plugin.add("resizable", "grid", {
21010
21011	resize: function(event, ui) {
21012		var self = $(this).data("resizable"), o = self.options, cs = self.size, os = self.originalSize, op = self.originalPosition, a = self.axis, ratio = o._aspectRatio || event.shiftKey;
21013		o.grid = typeof o.grid == "number" ? [o.grid, o.grid] : o.grid;
21014		var ox = Math.round((cs.width - os.width) / (o.grid[0]||1)) * (o.grid[0]||1), oy = Math.round((cs.height - os.height) / (o.grid[1]||1)) * (o.grid[1]||1);
21015
21016		if (/^(se|s|e)$/.test(a)) {
21017			self.size.width = os.width + ox;
21018			self.size.height = os.height + oy;
21019		}
21020		else if (/^(ne)$/.test(a)) {
21021			self.size.width = os.width + ox;
21022			self.size.height = os.height + oy;
21023			self.position.top = op.top - oy;
21024		}
21025		else if (/^(sw)$/.test(a)) {
21026			self.size.width = os.width + ox;
21027			self.size.height = os.height + oy;
21028			self.position.left = op.left - ox;
21029		}
21030		else {
21031			self.size.width = os.width + ox;
21032			self.size.height = os.height + oy;
21033			self.position.top = op.top - oy;
21034			self.position.left = op.left - ox;
21035		}
21036	}
21037
21038});
21039
21040var num = function(v) {
21041	return parseInt(v, 10) || 0;
21042};
21043
21044var isNumber = function(value) {
21045	return !isNaN(parseInt(value, 10));
21046};
21047
21048})(jQuery);
21049
21050/*
21051 * jQuery UI Sortable 1.8.16
21052 *
21053 * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
21054 * Dual licensed under the MIT or GPL Version 2 licenses.
21055 * http://jquery.org/license
21056 *
21057 * http://docs.jquery.com/UI/Sortables
21058 *
21059 * Depends:
21060 *	jquery.ui.core.js
21061 *	jquery.ui.mouse.js
21062 *	jquery.ui.widget.js
21063 */
21064(function( $, undefined ) {
21065
21066$.widget("ui.sortable", $.ui.mouse, {
21067	widgetEventPrefix: "sort",
21068	options: {
21069		appendTo: "parent",
21070		axis: false,
21071		connectWith: false,
21072		containment: false,
21073		cursor: 'auto',
21074		cursorAt: false,
21075		dropOnEmpty: true,
21076		forcePlaceholderSize: false,
21077		forceHelperSize: false,
21078		grid: false,
21079		handle: false,
21080		helper: "original",
21081		items: '> *',
21082		opacity: false,
21083		placeholder: false,
21084		revert: false,
21085		scroll: true,
21086		scrollSensitivity: 20,
21087		scrollSpeed: 20,
21088		scope: "default",
21089		tolerance: "intersect",
21090		zIndex: 1000
21091	},
21092	_create: function() {
21093
21094		var o = this.options;
21095		this.containerCache = {};
21096		this.element.addClass("ui-sortable");
21097
21098		//Get the items
21099		this.refresh();
21100
21101		//Let's determine if the items are being displayed horizontally
21102		this.floating = this.items.length ? o.axis === 'x' || (/left|right/).test(this.items[0].item.css('float')) || (/inline|table-cell/).test(this.items[0].item.css('display')) : false;
21103
21104		//Let's determine the parent's offset
21105		this.offset = this.element.offset();
21106
21107		//Initialize mouse events for interaction
21108		this._mouseInit();
21109
21110	},
21111
21112	destroy: function() {
21113		this.element
21114			.removeClass("ui-sortable ui-sortable-disabled")
21115			.removeData("sortable")
21116			.unbind(".sortable");
21117		this._mouseDestroy();
21118
21119		for ( var i = this.items.length - 1; i >= 0; i-- )
21120			this.items[i].item.removeData("sortable-item");
21121
21122		return this;
21123	},
21124
21125	_setOption: function(key, value){
21126		if ( key === "disabled" ) {
21127			this.options[ key ] = value;
21128	
21129			this.widget()
21130				[ value ? "addClass" : "removeClass"]( "ui-sortable-disabled" );
21131		} else {
21132			// Don't call widget base _setOption for disable as it adds ui-state-disabled class
21133			$.Widget.prototype._setOption.apply(this, arguments);
21134		}
21135	},
21136
21137	_mouseCapture: function(event, overrideHandle) {
21138
21139		if (this.reverting) {
21140			return false;
21141		}
21142
21143		if(this.options.disabled || this.options.type == 'static') return false;
21144
21145		//We have to refresh the items data once first
21146		this._refreshItems(event);
21147
21148		//Find out if the clicked node (or one of its parents) is a actual item in this.items
21149		var currentItem = null, self = this, nodes = $(event.target).parents().each(function() {
21150			if($.data(this, 'sortable-item') == self) {
21151				currentItem = $(this);
21152				return false;
21153			}
21154		});
21155		if($.data(event.target, 'sortable-item') == self) currentItem = $(event.target);
21156
21157		if(!currentItem) return false;
21158		if(this.options.handle && !overrideHandle) {
21159			var validHandle = false;
21160
21161			$(this.options.handle, currentItem).find("*").andSelf().each(function() { if(this == event.target) validHandle = true; });
21162			if(!validHandle) return false;
21163		}
21164
21165		this.currentItem = currentItem;
21166		this._removeCurrentsFromItems();
21167		return true;
21168
21169	},
21170
21171	_mouseStart: function(event, overrideHandle, noActivation) {
21172
21173		var o = this.options, self = this;
21174		this.currentContainer = this;
21175
21176		//We only need to call refreshPositions, because the refreshItems call has been moved to mouseCapture
21177		this.refreshPositions();
21178
21179		//Create and append the visible helper
21180		this.helper = this._createHelper(event);
21181
21182		//Cache the helper size
21183		this._cacheHelperProportions();
21184
21185		/*
21186		 * - Position generation -
21187		 * This block generates everything position related - it's the core of draggables.
21188		 */
21189
21190		//Cache the margins of the original element
21191		this._cacheMargins();
21192
21193		//Get the next scrolling parent
21194		this.scrollParent = this.helper.scrollParent();
21195
21196		//The element's absolute position on the page minus margins
21197		this.offset = this.currentItem.offset();
21198		this.offset = {
21199			top: this.offset.top - this.margins.top,
21200			left: this.offset.left - this.margins.left
21201		};
21202
21203		// Only after we got the offset, we can change the helper's position to absolute
21204		// TODO: Still need to figure out a way to make relative sorting possible
21205		this.helper.css("position", "absolute");
21206		this.cssPosition = this.helper.css("position");
21207
21208		$.extend(this.offset, {
21209			click: { //Where the click happened, relative to the element
21210				left: event.pageX - this.offset.left,
21211				top: event.pageY - this.offset.top
21212			},
21213			parent: this._getParentOffset(),
21214			relative: this._getRelativeOffset() //This is a relative to absolute position minus the actual position calculation - only used for relative positioned helper
21215		});
21216
21217		//Generate the original position
21218		this.originalPosition = this._generatePosition(event);
21219		this.originalPageX = event.pageX;
21220		this.originalPageY = event.pageY;
21221
21222		//Adjust the mouse offset relative to the helper if 'cursorAt' is supplied
21223		(o.cursorAt && this._adjustOffsetFromHelper(o.cursorAt));
21224
21225		//Cache the former DOM position
21226		this.domPosition = { prev: this.currentItem.prev()[0], parent: this.currentItem.parent()[0] };
21227
21228		//If the helper is not the original, hide the original so it's not playing any role during the drag, won't cause anything bad this way
21229		if(this.helper[0] != this.currentItem[0]) {
21230			this.currentItem.hide();
21231		}
21232
21233		//Create the placeholder
21234		this._createPlaceholder();
21235
21236		//Set a containment if given in the options
21237		if(o.containment)
21238			this._setContainment();
21239
21240		if(o.cursor) { // cursor option
21241			if ($('body').css("cursor")) this._storedCursor = $('body').css("cursor");
21242			$('body').css("cursor", o.cursor);
21243		}
21244
21245		if(o.opacity) { // opacity option
21246			if (this.helper.css("opacity")) this._storedOpacity = this.helper.css("opacity");
21247			this.helper.css("opacity", o.opacity);
21248		}
21249
21250		if(o.zIndex) { // zIndex option
21251			if (this.helper.css("zIndex")) this._storedZIndex = this.helper.css("zIndex");
21252			this.helper.css("zIndex", o.zIndex);
21253		}
21254
21255		//Prepare scrolling
21256		if(this.scrollParent[0] != document && this.scrollParent[0].tagName != 'HTML')
21257			this.overflowOffset = this.scrollParent.offset();
21258
21259		//Call callbacks
21260		this._trigger("start", event, this._uiHash());
21261
21262		//Recache the helper size
21263		if(!this._preserveHelperProportions)
21264			this._cacheHelperProportions();
21265
21266
21267		//Post 'activate' events to possible containers
21268		if(!noActivation) {
21269			 for (var i = this.containers.length - 1; i >= 0; i--) { this.containers[i]._trigger("activate", event, self._uiHash(this)); }
21270		}
21271
21272		//Prepare possible droppables
21273		if($.ui.ddmanager)
21274			$.ui.ddmanager.current = this;
21275
21276		if ($.ui.ddmanager && !o.dropBehaviour)
21277			$.ui.ddmanager.prepareOffsets(this, event);
21278
21279		this.dragging = true;
21280
21281		this.helper.addClass("ui-sortable-helper");
21282		this._mouseDrag(event); //Execute the drag once - this causes the helper not to be visible before getting its correct position
21283		return true;
21284
21285	},
21286
21287	_mouseDrag: function(event) {
21288
21289		//Compute the helpers position
21290		this.position = this._generatePosition(event);
21291		this.positionAbs = this._convertPositionTo("absolute");
21292
21293		if (!this.lastPositionAbs) {
21294			this.lastPositionAbs = this.positionAbs;
21295		}
21296
21297		//Do scrolling
21298		if(this.options.scroll) {
21299			var o = this.options, scrolled = false;
21300			if(this.scrollParent[0] != document && this.scrollParent[0].tagName != 'HTML') {
21301
21302				if((this.overflowOffset.top + this.scrollParent[0].offsetHeight) - event.pageY < o.scrollSensitivity)
21303					this.scrollParent[0].scrollTop = scrolled = this.scrollParent[0].scrollTop + o.scrollSpeed;
21304				else if(event.pageY - this.overflowOffset.top < o.scrollSensitivity)
21305					this.scrollParent[0].scrollTop = scrolled = this.scrollParent[0].scrollTop - o.scrollSpeed;
21306
21307				if((this.overflowOffset.left + this.scrollParent[0].offsetWidth) - event.pageX < o.scrollSensitivity)
21308					this.scrollParent[0].scrollLeft = scrolled = this.scrollParent[0].scrollLeft + o.scrollSpeed;
21309				else if(event.pageX - this.overflowOffset.left < o.scrollSensitivity)
21310					this.scrollParent[0].scrollLeft = scrolled = this.scrollParent[0].scrollLeft - o.scrollSpeed;
21311
21312			} else {
21313
21314				if(event.pageY - $(document).scrollTop() < o.scrollSensitivity)
21315					scrolled = $(document).scrollTop($(document).scrollTop() - o.scrollSpeed);
21316				else if($(window).height() - (event.pageY - $(document).scrollTop()) < o.scrollSensitivity)
21317					scrolled = $(document).scrollTop($(document).scrollTop() + o.scrollSpeed);
21318
21319				if(event.pageX - $(document).scrollLeft() < o.scrollSensitivity)
21320					scrolled = $(document).scrollLeft($(document).scrollLeft() - o.scrollSpeed);
21321				else if($(window).width() - (event.pageX - $(document).scrollLeft()) < o.scrollSensitivity)
21322					scrolled = $(document).scrollLeft($(document).scrollLeft() + o.scrollSpeed);
21323
21324			}
21325
21326			if(scrolled !== false && $.ui.ddmanager && !o.dropBehaviour)
21327				$.ui.ddmanager.prepareOffsets(this, event);
21328		}
21329
21330		//Regenerate the absolute position used for position checks
21331		this.positionAbs = this._convertPositionTo("absolute");
21332
21333		//Set the helper position
21334		if(!this.options.axis || this.options.axis != "y") this.helper[0].style.left = this.position.left+'px';
21335		if(!this.options.axis || this.options.axis != "x") this.helper[0].style.top = this.position.top+'px';
21336
21337		//Rearrange
21338		for (var i = this.items.length - 1; i >= 0; i--) {
21339
21340			//Cache variables and intersection, continue if no intersection
21341			var item = this.items[i], itemElement = item.item[0], intersection = this._intersectsWithPointer(item);
21342			if (!intersection) continue;
21343
21344			if(itemElement != this.currentItem[0] //cannot intersect with itself
21345				&&	this.placeholder[intersection == 1 ? "next" : "prev"]()[0] != itemElement //no useless actions that have been done before
21346				&&	!$.ui.contains(this.placeholder[0], itemElement) //no action if the item moved is the parent of the item checked
21347				&& (this.options.type == 'semi-dynamic' ? !$.ui.contains(this.element[0], itemElement) : true)
21348				//&& itemElement.parentNode == this.placeholder[0].parentNode // only rearrange items within the same container
21349			) {
21350
21351				this.direction = intersection == 1 ? "down" : "up";
21352
21353				if (this.options.tolerance == "pointer" || this._intersectsWithSides(item)) {
21354					this._rearrange(event, item);
21355				} else {
21356					break;
21357				}
21358
21359				this._trigger("change", event, this._uiHash());
21360				break;
21361			}
21362		}
21363
21364		//Post events to containers
21365		this._contactContainers(event);
21366
21367		//Interconnect with droppables
21368		if($.ui.ddmanager) $.ui.ddmanager.drag(this, event);
21369
21370		//Call callbacks
21371		this._trigger('sort', event, this._uiHash());
21372
21373		this.lastPositionAbs = this.positionAbs;
21374		return false;
21375
21376	},
21377
21378	_mouseStop: function(event, noPropagation) {
21379
21380		if(!event) return;
21381
21382		//If we are using droppables, inform the manager about the drop
21383		if ($.ui.ddmanager && !this.options.dropBehaviour)
21384			$.ui.ddmanager.drop(this, event);
21385
21386		if(this.options.revert) {
21387			var self = this;
21388			var cur = self.placeholder.offset();
21389
21390			self.reverting = true;
21391
21392			$(this.helper).animate({
21393				left: cur.left - this.offset.parent.left - self.margins.left + (this.offsetParent[0] == document.body ? 0 : this.offsetParent[0].scrollLeft),
21394				top: cur.top - this.offset.parent.top - self.margins.top + (this.offsetParent[0] == document.body ? 0 : this.offsetParent[0].scrollTop)
21395			}, parseInt(this.options.revert, 10) || 500, function() {
21396				self._clear(event);
21397			});
21398		} else {
21399			this._clear(event, noPropagation);
21400		}
21401
21402		return false;
21403
21404	},
21405
21406	cancel: function() {
21407
21408		var self = this;
21409
21410		if(this.dragging) {
21411
21412			this._mouseUp({ target: null });
21413
21414			if(this.options.helper == "original")
21415				this.currentItem.css(this._storedCSS).removeClass("ui-sortable-helper");
21416			else
21417				this.currentItem.show();
21418
21419			//Post deactivating events to containers
21420			for (var i = this.containers.length - 1; i >= 0; i--){
21421				this.containers[i]._trigger("deactivate", null, self._uiHash(this));
21422				if(this.containers[i].containerCache.over) {
21423					this.containers[i]._trigger("out", null, self._uiHash(this));
21424					this.containers[i].containerCache.over = 0;
21425				}
21426			}
21427
21428		}
21429
21430		if (this.placeholder) {
21431			//$(this.placeholder[0]).remove(); would have been the jQuery way - unfortunately, it unbinds ALL events from the original node!
21432			if(this.placeholder[0].parentNode) this.placeholder[0].parentNode.removeChild(this.placeholder[0]);
21433			if(this.options.helper != "original" && this.helper && this.helper[0].parentNode) this.helper.remove();
21434
21435			$.extend(this, {
21436				helper: null,
21437				dragging: false,
21438				reverting: false,
21439				_noFinalSort: null
21440			});
21441
21442			if(this.domPosition.prev) {
21443				$(this.domPosition.prev).after(this.currentItem);
21444			} else {
21445				$(this.domPosition.parent).prepend(this.currentItem);
21446			}
21447		}
21448
21449		return this;
21450
21451	},
21452
21453	serialize: function(o) {
21454
21455		var items = this._getItemsAsjQuery(o && o.connected);
21456		var str = []; o = o || {};
21457
21458		$(items).each(function() {
21459			var res = ($(o.item || this).attr(o.attribute || 'id') || '').match(o.expression || (/(.+)[-=_](.+)/));
21460			if(res) str.push((o.key || res[1]+'[]')+'='+(o.key && o.expression ? res[1] : res[2]));
21461		});
21462
21463		if(!str.length && o.key) {
21464			str.push(o.key + '=');
21465		}
21466
21467		return str.join('&');
21468
21469	},
21470
21471	toArray: function(o) {
21472
21473		var items = this._getItemsAsjQuery(o && o.connected);
21474		var ret = []; o = o || {};
21475
21476		items.each(function() { ret.push($(o.item || this).attr(o.attribute || 'id') || ''); });
21477		return ret;
21478
21479	},
21480
21481	/* Be careful with the following core functions */
21482	_intersectsWith: function(item) {
21483
21484		var x1 = this.positionAbs.left,
21485			x2 = x1 + this.helperProportions.width,
21486			y1 = this.positionAbs.top,
21487			y2 = y1 + this.helperProportions.height;
21488
21489		var l = item.left,
21490			r = l + item.width,
21491			t = item.top,
21492			b = t + item.height;
21493
21494		var dyClick = this.offset.click.top,
21495			dxClick = this.offset.click.left;
21496
21497		var isOverElement = (y1 + dyClick) > t && (y1 + dyClick) < b && (x1 + dxClick) > l && (x1 + dxClick) < r;
21498
21499		if(	   this.options.tolerance == "pointer"
21500			|| this.options.forcePointerForContainers
21501			|| (this.options.tolerance != "pointer" && this.helperProportions[this.floating ? 'width' : 'height'] > item[this.floating ? 'width' : 'height'])
21502		) {
21503			return isOverElement;
21504		} else {
21505
21506			return (l < x1 + (this.helperProportions.width / 2) // Right Half
21507				&& x2 - (this.helperProportions.width / 2) < r // Left Half
21508				&& t < y1 + (this.helperProportions.height / 2) // Bottom Half
21509				&& y2 - (this.helperProportions.height / 2) < b ); // Top Half
21510
21511		}
21512	},
21513
21514	_intersectsWithPointer: function(item) {
21515
21516		var isOverElementHeight = $.ui.isOverAxis(this.positionAbs.top + this.offset.click.top, item.top, item.height),
21517			isOverElementWidth = $.ui.isOverAxis(this.positionAbs.left + this.offset.click.left, item.left, item.width),
21518			isOverElement = isOverElementHeight && isOverElementWidth,
21519			verticalDirection = this._getDragVerticalDirection(),
21520			horizontalDirection = this._getDragHorizontalDirection();
21521
21522		if (!isOverElement)
21523			return false;
21524
21525		return this.floating ?
21526			( ((horizontalDirection && horizontalDirection == "right") || verticalDirection == "down") ? 2 : 1 )
21527			: ( verticalDirection && (verticalDirection == "down" ? 2 : 1) );
21528
21529	},
21530
21531	_intersectsWithSides: function(item) {
21532
21533		var isOverBottomHalf = $.ui.isOverAxis(this.positionAbs.top + this.offset.click.top, item.top + (item.height/2), item.height),
21534			isOverRightHalf = $.ui.isOverAxis(this.positionAbs.left + this.offset.click.left, item.left + (item.width/2), item.width),
21535			verticalDirection = this._getDragVerticalDirection(),
21536			horizontalDirection = this._getDragHorizontalDirection();
21537
21538		if (this.floating && horizontalDirection) {
21539			return ((horizontalDirection == "right" && isOverRightHalf) || (horizontalDirection == "left" && !isOverRightHalf));
21540		} else {
21541			return verticalDirection && ((verticalDirection == "down" && isOverBottomHalf) || (verticalDirection == "up" && !isOverBottomHalf));
21542		}
21543
21544	},
21545
21546	_getDragVerticalDirection: function() {
21547		var delta = this.positionAbs.top - this.lastPositionAbs.top;
21548		return delta != 0 && (delta > 0 ? "down" : "up");
21549	},
21550
21551	_getDragHorizontalDirection: function() {
21552		var delta = this.positionAbs.left - this.lastPositionAbs.left;
21553		return delta != 0 && (delta > 0 ? "right" : "left");
21554	},
21555
21556	refresh: function(event) {
21557		this._refreshItems(event);
21558		this.refreshPositions();
21559		return this;
21560	},
21561
21562	_connectWith: function() {
21563		var options = this.options;
21564		return options.connectWith.constructor == String
21565			? [options.connectWith]
21566			: options.connectWith;
21567	},
21568	
21569	_getItemsAsjQuery: function(connected) {
21570
21571		var self = this;
21572		var items = [];
21573		var queries = [];
21574		var connectWith = this._connectWith();
21575
21576		if(connectWith && connected) {
21577			for (var i = connectWith.length - 1; i >= 0; i--){
21578				var cur = $(connectWith[i]);
21579				for (var j = cur.length - 1; j >= 0; j--){
21580					var inst = $.data(cur[j], 'sortable');
21581					if(inst && inst != this && !inst.options.disabled) {
21582						queries.push([$.isFunction(inst.options.items) ? inst.options.items.call(inst.element) : $(inst.options.items, inst.element).not(".ui-sortable-helper").not('.ui-sortable-placeholder'), inst]);
21583					}
21584				};
21585			};
21586		}
21587
21588		queries.push([$.isFunction(this.options.items) ? this.options.items.call(this.element, null, { options: this.options, item: this.currentItem }) : $(this.options.items, this.element).not(".ui-sortable-helper").not('.ui-sortable-placeholder'), this]);
21589
21590		for (var i = queries.length - 1; i >= 0; i--){
21591			queries[i][0].each(function() {
21592				items.push(this);
21593			});
21594		};
21595
21596		return $(items);
21597
21598	},
21599
21600	_removeCurrentsFromItems: function() {
21601
21602		var list = this.currentItem.find(":data(sortable-item)");
21603
21604		for (var i=0; i < this.items.length; i++) {
21605
21606			for (var j=0; j < list.length; j++) {
21607				if(list[j] == this.items[i].item[0])
21608					this.items.splice(i,1);
21609			};
21610
21611		};
21612
21613	},
21614
21615	_refreshItems: function(event) {
21616
21617		this.items = [];
21618		this.containers = [this];
21619		var items = this.items;
21620		var self = this;
21621		var queries = [[$.isFunction(this.options.items) ? this.options.items.call(this.element[0], event, { item: this.currentItem }) : $(this.options.items, this.element), this]];
21622		var connectWith = this._connectWith();
21623
21624		if(connectWith) {
21625			for (var i = connectWith.length - 1; i >= 0; i--){
21626				var cur = $(connectWith[i]);
21627				for (var j = cur.length - 1; j >= 0; j--){
21628					var inst = $.data(cur[j], 'sortable');
21629					if(inst && inst != this && !inst.options.disabled) {
21630						queries.push([$.isFunction(inst.options.items) ? inst.options.items.call(inst.element[0], event, { item: this.currentItem }) : $(inst.options.items, inst.element), inst]);
21631						this.containers.push(inst);
21632					}
21633				};
21634			};
21635		}
21636
21637		for (var i = queries.length - 1; i >= 0; i--) {
21638			var targetData = queries[i][1];
21639			var _queries = queries[i][0];
21640
21641			for (var j=0, queriesLength = _queries.length; j < queriesLength; j++) {
21642				var item = $(_queries[j]);
21643
21644				item.data('sortable-item', targetData); // Data for target checking (mouse manager)
21645
21646				items.push({
21647					item: item,
21648					instance: targetData,
21649					width: 0, height: 0,
21650					left: 0, top: 0
21651				});
21652			};
21653		};
21654
21655	},
21656
21657	refreshPositions: function(fast) {
21658
21659		//This has to be redone because due to the item being moved out/into the offsetParent, the offsetParent's position will change
21660		if(this.offsetParent && this.helper) {
21661			this.offset.parent = this._getParentOffset();
21662		}
21663
21664		for (var i = this.items.length - 1; i >= 0; i--){
21665			var item = this.items[i];
21666
21667			//We ignore calculating positions of all connected containers when we're not over them
21668			if(item.instance != this.currentContainer && this.currentContainer && item.item[0] != this.currentItem[0])
21669				continue;
21670
21671			var t = this.options.toleranceElement ? $(this.options.toleranceElement, item.item) : item.item;
21672
21673			if (!fast) {
21674				item.width = t.outerWidth();
21675				item.height = t.outerHeight();
21676			}
21677
21678			var p = t.offset();
21679			item.left = p.left;
21680			item.top = p.top;
21681		};
21682
21683		if(this.options.custom && this.options.custom.refreshContainers
21683) {
21684			this.options.custom.refreshContainers.call(this);
21685		} else {
21686			for (var i = this.containers.length - 1; i >= 0; i--){
21687				var p = this.containers[i].element.offset();
21688				this.containers[i].containerCache.left = p.left;
21689				this.containers[i].containerCache.top = p.top;
21690				this.containers[i].containerCache.width	= this.containers[i].element.outerWidth();
21691				this.containers[i].containerCache.height = this.containers[i].element.outerHeight();
21692			};
21693		}
21694
21695		return this;
21696	},
21697
21698	_createPlaceholder: function(that) {
21699
21700		var self = that || this, o = self.options;
21701
21702		if(!o.placeholder || o.placeholder.constructor == String) {
21703			var className = o.placeholder;
21704			o.placeholder = {
21705				element: function() {
21706
21707					var el = $(document.createElement(self.currentItem[0].nodeName))
21708						.addClass(className || self.currentItem[0].className+" ui-sortable-placeholder")
21709						.removeClass("ui-sortable-helper")[0];
21710
21711					if(!className)
21712						el.style.visibility = "hidden";
21713
21714					return el;
21715				},
21716				update: function(container, p) {
21717
21718					// 1. If a className is set as 'placeholder option, we don't force sizes - the class is responsible for that
21719					// 2. The option 'forcePlaceholderSize can be enabled to force it even if a class name is specified
21720					if(className && !o.forcePlaceholderSize) return;
21721
21722					//If the element doesn't have a actual height by itself (without styles coming from a stylesheet), it receives the inline height from the dragged item
21723					if(!p.height()) { p.height(self.currentItem.innerHeight() - parseInt(self.currentItem.css('paddingTop')||0, 10) - parseInt(self.currentItem.css('paddingBottom')||0, 10)); };
21724					if(!p.width()) { p.width(self.currentItem.innerWidth() - parseInt(self.currentItem.css('paddingLeft')||0, 10) - parseInt(self.currentItem.css('paddingRight')||0, 10)); };
21725				}
21726			};
21727		}
21728
21729		//Create the placeholder
21730		self.placeholder = $(o.placeholder.element.call(self.element, self.currentItem));
21731
21732		//Append it after the actual current item
21733		self.currentItem.after(self.placeholder);
21734
21735		//Update the size of the placeholder (TODO: Logic to fuzzy, see line 316/317)
21736		o.placeholder.update(self, self.placeholder);
21737
21738	},
21739
21740	_contactContainers: function(event) {
21741		
21742		// get innermost container that intersects with item 
21743		var innermostContainer = null, innermostIndex = null;		
21744		
21745		
21746		for (var i = this.containers.length - 1; i >= 0; i--){
21747
21748			// never consider a container that's located within the item itself 
21749			if($.ui.contains(this.currentItem[0], this.containers[i].element[0]))
21750				continue;
21751
21752			if(this._intersectsWith(this.containers[i].containerCache)) {
21753
21754				// if we've already found a container and it's more "inner" than this, then continue 
21755				if(innermostContainer && $.ui.contains(this.containers[i].element[0], innermostContainer.element[0]))
21756					continue;
21757
21758				innermostContainer = this.containers[i]; 
21759				innermostIndex = i;
21760					
21761			} else {
21762				// container doesn't intersect. trigger "out" event if necessary 
21763				if(this.containers[i].containerCache.over) {
21764					this.containers[i]._trigger("out", event, this._uiHash(this));
21765					this.containers[i].containerCache.over = 0;
21766				}
21767			}
21768
21769		}
21770		
21771		// if no intersecting containers found, return 
21772		if(!innermostContainer) return; 
21773
21774		// move the item into the container if it's not there already
21775		if(this.containers.length === 1) {
21776			this.containers[innermostIndex]._trigger("over", event, this._uiHash(this));
21777			this.containers[innermostIndex].containerCache.over = 1;
21778		} else if(this.currentContainer != this.containers[innermostIndex]) { 
21779
21780			//When entering a new container, we will find the item with the least distance and append our item near it 
21781			var dist = 10000; var itemWithLeastDistance = null; var base = this.positionAbs[this.containers[innermostIndex].floating ? 'left' : 'top']; 
21782			for (var j = this.items.length - 1; j >= 0; j--) { 
21783				if(!$.ui.contains(this.containers[innermostIndex].element[0], this.items[j].item[0])) continue; 
21784				var cur = this.items[j][this.containers[innermostIndex].floating ? 'left' : 'top']; 
21785				if(Math.abs(cur - base) < dist) { 
21786					dist = Math.abs(cur - base); itemWithLeastDistance = this.items[j]; 
21787				} 
21788			} 
21789
21790			if(!itemWithLeastDistance && !this.options.dropOnEmpty) //Check if dropOnEmpty is enabled 
21791				return; 
21792
21793			this.currentContainer = this.containers[innermostIndex]; 
21794			itemWithLeastDistance ? this._rearrange(event, itemWithLeastDistance, null, true) : this._rearrange(event, null, this.containers[innermostIndex].element, true); 
21795			this._trigger("change", event, this._uiHash()); 
21796			this.containers[innermostIndex]._trigger("change", event, this._uiHash(this)); 
21797
21798			//Update the placeholder 
21799			this.options.placeholder.update(this.currentContainer, this.placeholder); 
21800		
21801			this.containers[innermostIndex]._trigger("over", event, this._uiHash(this)); 
21802			this.containers[innermostIndex].containerCache.over = 1;
21803		} 
21804	
21805		
21806	},
21807
21808	_createHelper: function(event) {
21809
21810		var o = this.options;
21811		var helper = $.isFunction(o.helper) ? $(o.helper.apply(this.element[0], [event, this.currentItem])) : (o.helper == 'clone' ? this.currentItem.clone() : this.currentItem);
21812
21813		if(!helper.parents('body').length) //Add the helper to the DOM if that didn't happen already
21814			$(o.appendTo != 'parent' ? o.appendTo : this.currentItem[0].parentNode)[0].appendChild(helper[0]);
21815
21816		if(helper[0] == this.currentItem[0])
21817			this._storedCSS = { width: this.currentItem[0].style.width, height: this.currentItem[0].style.height, position: this.currentItem.css("position"), top: this.currentItem.css("top"), left
21817: this.currentItem.css("left") };
21818
21819		if(helper[0].style.width == '' || o.forceHelperSize) helper.width(this.currentItem.width());
21820		if(helper[0].style.height == '' || o.forceHelperSize) helper.height(this.currentItem.height());
21821
21822		return helper;
21823
21824	},
21825
21826	_adjustOffsetFromHelper: function(obj) {
21827		if (typeof obj == 'string') {
21828			obj = obj.split(' ');
21829		}
21830		if ($.isArray(obj)) {
21831			obj = {left: +obj[0], top: +obj[1] || 0};
21832		}
21833		if ('left' in obj) {
21834			this.offset.click.left = obj.left + this.margins.left;
21835		}
21836		if ('right' in obj) {
21837			this.offset.click.left = this.helperProportions.width - obj.right + this.margins.left;
21838		}
21839		if ('top' in obj) {
21840			this.offset.click.top = obj.top + this.margins.top;
21841		}
21842		if ('bottom' in obj) {
21843			this.offset.click.top = this.helperProportions.height - obj.bottom + this.margins.top;
21844		}
21845	},
21846
21847	_getParentOffset: function() {
21848
21849
21850		//Get the offsetParent and cache its position
21851		this.offsetParent = this.helper.offsetParent();
21852		var po = this.offsetParent.offset();
21853
21854		// This is a special case where we need to modify a offset calculated on start, since the following happened:
21855		// 1. The position of the helper is absolute, so it's position is calculated based on the next positioned parent
21856		// 2. The actual offset parent is a child of the scroll parent, and the scroll parent isn't the document, which means that
21857		//    the scroll is included in the initial calculation of the offset of the parent, and never recalculated upon drag
21858		if(this.cssPosition == 'absolute' && this.scrollParent[0] != document && $.ui.contains(this.scrollParent[0], this.offsetParent[0])) {
21859			po.left += this.scrollParent.scrollLeft();
21860			po.top += this.scrollParent.scrollTop();
21861		}
21862
21863		if((this.offsetParent[0] == document.body) //This needs to be actually done for all browsers, since pageX/pageY includes this information
21864		|| (this.offsetParent[0].tagName && this.offsetParent[0].tagName.toLowerCase() == 'html' && $.browser.msie)) //Ugly IE fix
21865			po = { top: 0, left: 0 };
21866
21867		return {
21868			top: po.top + (parseInt(this.offsetParent.css("borderTopWidth"),10) || 0),
21869			left: po.left + (parseInt(this.offsetParent.css("borderLeftWidth"),10) || 0)
21870		};
21871
21872	},
21873
21874	_getRelativeOffset: function() {
21875
21876		if(this.cssPosition == "relative") {
21877			var p = this.currentItem.position();
21878			return {
21879				top: p.top - (parseInt(this.helper.css("top"),10) || 0) + this.scrollParent.scrollTop(),
21880				left: p.left - (parseInt(this.helper.css("left"),10) || 0) + this.scrollParent.scrollLeft()
21881			};
21882		} else {
21883			return { top: 0, left: 0 };
21884		}
21885
21886	},
21887
21888	_cacheMargins: function() {
21889		this.margins = {
21890			left: (parseInt(this.currentItem.css("marginLeft"),10) || 0),
21891			top: (parseInt(this.currentItem.css("marginTop"),10) || 0)
21892		};
21893	},
21894
21895	_cacheHelperProportions: function() {
21896		this.helperProportions = {
21897			width: this.helper.outerWidth(),
21898			height: this.helper.outerHeight()
21899		};
21900	},
21901
21902	_setContainment: function() {
21903
21904		var o = this.options;
21905		if(o.containment == 'parent') o.containment = this.helper[0].parentNode;
21906		if(o.containment == 'document' || o.containment == 'window') this.containment = [
21907			0 - this.offset.relative.left - this.offset.parent.left,
21908			0 - this.offset.relative.top - this.offset.parent.top,
21909			$(o.containment == 'document' ? document : window).width() - this.helperProportions.width - this.margins.left,
21910			($(o.containment == 'document' ? document : window).height() || document.body.parentNode.scrollHeight) - this.helperProportions.height - this.margins.top
21911		];
21912
21913		if(!(/^(document|window|parent)$/).test(o.containment)) {
21914			var ce = $(o.containment)[0];
21915			var co = $(o.containment).offset();
21916			var over = ($(ce).css("overflow") != 'hidden');
21917
21918			this.containment = [
21919				co.left + (parseInt($(ce).css("borderLeftWidth"),10) || 0) + (parseInt($(ce).css("paddingLeft"),10) || 0) - this.margins.left,
21920				co.top + (parseInt($(ce).css("borderTopWidth"),10) || 0) + (parseInt($(ce).css("paddingTop"),10) || 0) - this.margins.top,
21921				co.left+(over ? Math.max(ce.scrollWidth,ce.offsetWidth) : ce.offsetWidth) - (parseInt($(ce).css("borderLeftWidth"),10) || 0) - (parseInt($(ce).css("paddingRight"),10) || 0) - this.helperProportions.width - this.margins.left,
21922				co.top+(over ? Math.max(ce.scrollHeight,ce.offsetHeight) : ce.offsetHeight) - (parseInt($(ce).css("borderTopWidth"),
2192210) || 0) - (parseInt($(ce).css("paddingBottom"),10) || 0) - this.helperProportions.height - this.margins.top
21923			];
21924		}
21925
21926	},
21927
21928	_convertPositionTo: function(d, pos) {
21929
21930		if(!pos) pos = this.position;
21931		var mod = d == "absolute" ? 1 : -1;
21932		var o = this.options, scroll = this.cssPosition == 'absolute' && !(this.scrollParent[0] != document && $.ui.contains(this.scrollParent[0], this.offsetParent[0])) ? this.offsetParent : this.scrollParent, scrollIsRootNode = (/(html|body)/i).test(scroll[0].tagName);
21933
21934		return {
21935			top: (
21936				pos.top																	// The absolute mouse position
21937				+ this.offset.relative.top * mod										// Only for relative positioned nodes: Relative offset from element to offset parent
21938				+ this.offset.parent.top * mod											// The offsetParent's offset without borders (offset + border)
21939				- ($.browser.safari && this.cssPosition == 'fixed' ? 0 : ( this.cssPosition == 'fixed' ? -this.scrollParent.scrollTop() : ( scrollIsRootNode ? 0 : scroll.scrollTop() ) ) * mod)
21940			),
21941			left: (
21942				pos.left																// The absolute mouse position
21943				+ this.offset.relative.left * mod										// Only for relative positioned nodes: Relative offset from element to offset parent
21944				+ this.offset.parent.left * mod											// The offsetParent's offset without borders (offset + border)
21945				- ($.browser.safari && this.cssPosition == 'fixed' ? 0 : ( this.cssPosition == 'fixed' ? -this.scrollParent.scrollLeft() : scrollIsRootNode ? 0 : scroll.scrollLeft() ) * mod)
21946			)
21947		};
21948
21949	},
21950
21951	_generatePosition: function(event) {
21952
21953		var o = this.options, scroll = this.cssPosition == 'absolute' && !(this.scrollParent[0] != document && $.ui.contains(this.scrollParent[0], this.offsetParent[0])) ? this.offsetParent : this.scrollParent, scrollIsRootNode = (/(html|body)/i).test(scroll[0].tagName);
21954
21955		// This is another very weird special case that only happens for relative elements:
21956		// 1. If the css position is relative
21957		// 2. and the scroll parent is the document or similar to the offset parent
21958		// we have to refresh the relative offset during the scroll so there are no jumps
21959		if(this.cssPosition == 'relative' && !(this.scrollParent[0] != document && this.scrollParent[0] != this.offsetParent[0])) {
21960			this.offset.relative = this._getRelativeOffset();
21961		}
21962
21963		var pageX = event.pageX;
21964		var pageY = event.pageY;
21965
21966		/*
21967		 * - Position constraining -
21968		 * Constrain the position to a mix of grid, containment.
21969		 */
21970
21971		if(this.originalPosition) { //If we are not dragging yet, we won't check for options
21972
21973			if(this.containment) {
21974				if(event.pageX - this.offset.click.left < this.containment[0]) pageX = this.containment[0] + this.offset.click.left;
21975				if(event.pageY - this.offset.click.top < this.containment[1]) pageY = this.containment[1] + this.offset.click.top;
21976				if(event.pageX - this.offset.click.left > this.containment[2]) pageX = this.containment[2] + this.offset.click.left;
21977				if(event.pageY - this.offset.click.top > this.containment[3]) pageY = this.containment[3] + this.offset.click.top;
21978			}
21979
21980			if(o.grid) {
21981				var top = this.originalPageY + Math.round((pageY - this.originalPageY) / o.grid[1]) * o.grid[1];
21982				pageY = this.containment ? (!(top - this.offset.click.top < this.containment[1] || top - this.offset.click.top > this.containment[3]) ? top : (!(top - this.offset.click.top < this.containment[1]) ? top - o.grid[1] : top + o.grid[1])) : top;
21983
21984				var left = this.originalPageX + Math.round((pageX - this.originalPageX) / o.grid[0]) * o.grid[0];
21985				pageX = this.containment ? (!(left - this.offset.click.left < this.containment[0] || left - this.offset.click.left > this.containment[2]) ? left : (!(left - this.offset.click.left < this.containment[0]) ? left - o.grid[0] : left + o.grid[0])) : left;
21986			}
21987
21988		}
21989
21990		return {
21991			top: (
21992				pageY																// The absolute mouse position
21993				- this.offset.click.top													// Click offset (relative to the element)
21994				- this.offset.relative.top												// Only for relative positioned nodes: Relative offset from element to offset parent
21995				- this.offset.parent.top												// The offsetParent's offset without borders (offset + border)
21996				+ ($.browser.safari && this.cssPosition == 'fixed' ? 0 : ( this.cssPosition == 'fixed' ? -this.scrollParent.scrollTop() : ( scrollIsRootNode ? 0 : scroll.scrollTop() ) ))
21997			),
21998			left: (
21999				pageX																// The absolute mouse position
22000				- this.offset.click.left												// Click offset (relative to the element)
22001				- this.offset.relative.left												// Only for relative positioned nodes: Relative offset from element to offset parent
22002				- this.offset.parent.left												// The offsetParent's offset without borders (offset + border)
22003				+ ($.browser.safari && this.cssPosition == 'fixed' ? 0 : ( this.cssPosition == 'fixed' ? -this.scrollParent.scrollLeft() : scrollIsRootNode ? 0 : scroll.scrollLeft() ))
22004			)
22005		};
22006
22007	},
22008
22009	_rearrange: function(event, i, a, hardRefresh) {
22010
22011		a ? a[0].appendChild(this.placeholder[0]) : i.item[0].parentNode.insertBefore(this.placeholder[0], (this.direction == 'down' ? i.item[0] : i.item[0].nextSibling));
22012
22013		//Various things done here to improve the performance:
22014		// 1. we create a setTimeout, that calls refreshPositions
22015		// 2. on the instance, we have a counter variable, that get's higher after every append
22016		// 3. on the local scope, we copy the counter variable, and check in the timeout, if it's still the same
22017		// 4. this lets only the last addition to the timeout stack through
22018		this.counter = this.counter ? ++this.counter : 1;
22019		var self = this, counter = this.counter;
22020
22021		window.setTimeout(function() {
22022			if(counter == self.counter) self.refreshPositions(!hardRefresh); //Precompute after each DOM insertion, NOT on mousemove
22023		},0);
22024
22025	},
22026
22027	_clear: function(event, noPropagation) {
22028
22029		this.reverting = false;
22030		// We delay all events that have to be triggered to after the point where the placeholder has been removed and
22031		// everything else normalized again
22032		var delayedTriggers = [], self = this;
22033
22034		// We first have to update the dom position of the actual currentItem
22035		// Note: don't do it if the current item is already removed (by a user), or it gets reappended (see #4088)
22036		if(!this._noFinalSort && this.currentItem.parent().length) this.placeholder.before(this.currentItem);
22037		this._noFinalSort = null;
22038
22039		if(this.helper[0] == this.currentItem[0]) {
22040			for(var i in this._storedCSS) {
22041				if(this._storedCSS[i] == 'auto' || this._storedCSS[i] == 'static') this._storedCSS[i] = '';
22042			}
22043			this.currentItem.css(this._storedCSS).removeClass("ui-sortable-helper");
22044		} else {
22045			this.currentItem.show();
22046		}
22047
22048		if(this.fromOutside && !noPropagation) delayedTriggers.push(function(event) { this._trigger("receive", event, this._uiHash(this.fromOutside)); });
22049		if((this.fromOutside || this.domPosition.prev != this.currentItem.prev().not(".ui-sortable-helper")[0] || this.domPosition.parent != this.currentItem.parent()[0]) && !noPropagation) delayedTriggers.push(function(event) { this._trigger("update", event, this._uiHash()); }); //Trigger update callback if the DOM position has changed
22050		if(!$.ui.contains(this.element[0], this.currentItem[0])) { //Node was moved out of the current element
22051			if(!noPropagation) delayedTriggers.push(function(event) { this._trigger("remove", event, this._uiHash()); });
22052			for (var i = this.containers.length - 1; i >= 0; i--){
22053				if($.ui.contains(this.containers[i].element[0], this.currentItem[0]) && !noPropagation) {
22054					delayedTriggers.push((function(c) { return function(event) { c._trigger("receive", event, this._uiHash(this)); };  }).call(this, this.containers[i]));
22055					delayedTriggers.push((function(c) { return function(event) { c._trigger("update", event, this._uiHash(this));  }; }).call(this, this.containers[i]));
22056				}
22057			};
22058		};
22059
22060		//Post events to containers
22061		for (var i = this.containers.length - 1; i >= 0; i--){
22062			if(!noPropagation) delayedTriggers.push((function(c) { return function(event) { c._trigger("deactivate", event, this._uiHash(this)); };  }).call(this, this.containers[i]));
22063			if(this.containers[i].containerCache.over) {
22064				delayedTriggers.push((function(c) { return function(event) { c._trigger("out", event, this._uiHash(this)); };  }).call(this, this.containers[i]));
22065				this.containers[i].containerCache.over = 0;
22066			}
22067		}
22068
22069		//Do what was originally in plugins
22070		if(this._storedCursor) $('body').css("cursor", this._storedCursor); //Reset cursor
22071		if(this._storedOpacity) this.helper.css("opacity", this._storedOpacity); //Reset opacity
22072		if(this._storedZIndex) this.helper.css("zIndex", this._storedZIndex == 'auto' ? '' : this._storedZIndex); //Reset z-index
22073
22074		this.dragging = false;
22075		if(this.cancelHelperRemoval) {
22076			if(!noPropagation) {
22077				this._trigger("beforeStop", event, this._uiHash());
22078				for (var i=0; i < delayedTriggers.length; i++) { delayedTriggers[i].call(this, event); }; //Trigger all delayed events
22079				this._trigger("stop", event, this._uiHash());
22080			}
22081			return false;
22082		}
22083
22084		if(!noPropagation) this._trigger("beforeStop", event, this._uiHash());
22085
22086		//$(this.placeholder[0]).remove(); would have been the jQuery way - unfortunately, it unbinds ALL events from the original node!
22087		this.placeholder[0].parentNode.removeChild(this.placeholder[0]);
22088
22089		if(this.helper[0] != this.currentItem[0]) this.helper.remove(); this.helper = null;
22090
22091		if(!noPropagation) {
22092			for (var i=0; i < delayedTriggers.length; i++) { delayedTriggers[i].call(this, event); }; //Trigger all delayed events
22093			this._trigger("stop", event, this._uiHash());
22094		}
22095
22096		this.fromOutside = false;
22097		return true;
22098
22099	},
22100
22101	_trigger: function() {
22102		if ($.Widget.prototype._trigger.apply(this, arguments) === false) {
22103			this.cancel();
22104		}
22105	},
22106
22107	_uiHash: function(inst) {
22108		var self = inst || this;
22109		return {
22110			helper: self.helper,
22111			placeholder: self.placeholder || $([]),
22112			position: self.position,
22113			originalPosition: self.originalPosition,
22114			offset: self.positionAbs,
22115			item: self.currentItem,
22116			sender: inst ? inst.element : null
22117		};
22118	}
22119
22120});
22121
22122$.extend($.ui.sortable, {
22123	version: "1.8.16"
22124});
22125
22126})(jQuery);
22127
22128/*
22129 * jQuery UI Slider 1.8.16
22130 *
22131 * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
22132 * Dual licensed under the MIT or GPL Version 2 licenses.
22133 * http://jquery.org/license
22134 *
22135 * http://docs.jquery.com/UI/Slider
22136 *
22137 * Depends:
22138 *	jquery.ui.core.js
22139 *	jquery.ui.mouse.js
22140 *	jquery.ui.widget.js
22141 */
22142(function( $, undefined ) {
22143
22144// number of pages in a slider
22145// (how many times can you page up/down to go through the whole range)
22146var numPages = 5;
22147
22148$.widget( "ui.slider", $.ui.mouse, {
22149
22150	widgetEventPrefix: "slide",
22151
22152	options: {
22153		animate: false,
22154		distance: 0,
22155		max: 100,
22156		min: 0,
22157		orientation: "horizontal",
22158		range: false,
22159		step: 1,
22160		value: 0,
22161		values: null
22162	},
22163
22164	_create: function() {
22165		var self = this,
22166			o = this.options,
22167			existingHandles = this.element.find( ".ui-slider-handle" ).addClass( "ui-state-default ui-corner-all" ),
22168			handle = "<a class='ui-slider-handle ui-state-default ui-corner-all' href='#'></a>",
22169			handleCount = ( o.values && o.values.length ) || 1,
22170			handles = [];
22171
22172		this._keySliding = false;
22173		this._mouseSliding = false;
22174		this._animateOff = true;
22175		this._handleIndex = null;
22176		this._detectOrientation();
22177		this._mouseInit();
22178
22179		this.element
22180			.addClass( "ui-slider" +
22181				" ui-slider-" + this.orientation +
22182				" ui-widget" +
22183				" ui-widget-content" +
22184				" ui-corner-all" +
22185				( o.disabled ? " ui-slider-disabled ui-disabled" : "" ) );
22186
22187		this.range = $([]);
22188
22189		if ( o.range ) {
22190			if ( o.range === true ) {
22191				if ( !o.values ) {
22192					o.values = [ this._valueMin(), this._valueMin() ];
22193				}
22194				if ( o.values.length && o.values.length !== 2 ) {
22195					o.values = [ o.values[0], o.values[0] ];
22196				}
22197			}
22198
22199			this.range = $( "<div></div>" )
22200				.appendTo( this.element )
22201				.addClass( "ui-slider-range" +
22202				// note: this isn't the most fittingly semantic framework class for this element,
22203				// but worked best visually with a variety of themes
22204				" ui-widget-header" + 
22205				( ( o.range === "min" || o.range === "max" ) ? " ui-slider-range-" + o.range : "" ) );
22206		}
22207
22208		for ( var i = existingHandles.length; i < handleCount; i += 1 ) {
22209			handles.push( handle );
22210		}
22211
22212		this.handles = existingHandles.add( $( handles.join( "" ) ).appendTo( self.element ) );
22213
22214		this.handle = this.handles.eq( 0 );
22215
22216		this.handles.add( this.range ).filter( "a" )
22217			.click(function( event ) {
22218				event.preventDefault();
22219			})
22220			.hover(function() {
22221				if ( !o.disabled ) {
22222					$( this ).addClass( "ui-state-hover" );
22223				}
22224			}, function() {
22225				$( this ).removeClass( "ui-state-hover" );
22226			})
22227			.focus(function() {
22228				if ( !o.disabled ) {
22229					$( ".ui-slider .ui-state-focus" ).removeClass( "ui-state-focus" );
22230					$( this ).addClass( "ui-state-focus" );
22231				} else {
22232					$( this ).blur();
22233				}
22234			})
22235			.blur(function() {
22236				$( this ).removeClass( "ui-state-focus" );
22237			});
22238
22239		this.handles.each(function( i ) {
22240			$( this ).data( "index.ui-slider-handle", i );
22241		});
22242
22243		this.handles
22244			.keydown(function( event ) {
22245				var ret = true,
22246					index = $( this ).data( "index.ui-slider-handle" ),
22247					allowed,
22248					curVal,
22249					newVal,
22250					step;
22251	
22252				if ( self.options.disabled ) {
22253					return;
22254				}
22255	
22256				switch ( event.keyCode ) {
22257					case $.ui.keyCode.HOME:
22258					case $.ui.keyCode.END:
22259					case $.ui.keyCode.PAGE_UP:
22260					case $.ui.keyCode.PAGE_DOWN:
22261					case $.ui.keyCode.UP:
22262					case $.ui.keyCode.RIGHT:
22263					case $.ui.keyCode.DOWN:
22264					case $.ui.keyCode.LEFT:
22265						ret = false;
22266						if ( !self._keySliding ) {
22267							self._keySliding = true;
22268							$( this ).addClass( "ui-state-active" );
22269							allowed = self._start( event, index );
22270							if ( allowed === false ) {
22271								return;
22272							}
22273						}
22274						break;
22275				}
22276	
22277				step = self.options.step;
22278				if ( self.options.values && self.options.values.length ) {
22279					curVal = newVal = self.values( index );
22280				} else {
22281					curVal = newVal = self.value();
22282				}
22283	
22284				switch ( event.keyCode ) {
22285					case $.ui.keyCode.HOME:
22286						newVal = self._valueMin();
22287						break;
22288					case $.ui.keyCode.END:
22289						newVal = self._valueMax();
22290						break;
22291					case $.ui.keyCode.PAGE_UP:
22292						newVal = self._trimAlignValue( curVal + ( (self._valueMax() - self._valueMin()) / numPages ) );
22293						break;
22294					case $.ui.keyCode.PAGE_DOWN:
22295						newVal = self._trimAlignValue( curVal - ( (self._valueMax() - self._valueMin()) / numPages ) );
22296						break;
22297					case $.ui.keyCode.UP:
22298					case $.ui.keyCode.RIGHT:
22299						if ( curVal === self._valueMax() ) {
22300							return;
22301						}
22302						newVal = self._trimAlignValue( curVal + step );
22303						break;
22304					case $.ui.keyCode.DOWN:
22305					case $.ui.keyCode.LEFT:
22306						if ( curVal === self._valueMin() ) {
22307							return;
22308						}
22309						newVal = self._trimAlignValue( curVal - step );
22310						break;
22311				}
22312	
22313				self._slide( event, index, newVal );
22314	
22315				return ret;
22316	
22317			})
22318			.keyup(function( event ) {
22319				var index = $( this ).data( "index.ui-slider-handle" );
22320	
22321				if ( self._keySliding ) {
22322					self._keySliding = false;
22323					self._stop( event, index );
22324					self._change( event, index );
22325					$( this ).removeClass( "ui-state-active" );
22326				}
22327	
22328			});
22329
22330		this._refreshValue();
22331
22332		this._animateOff = false;
22333	},
22334
22335	destroy: function() {
22336		this.handles.remove();
22337		this.range.remove();
22338
22339		this.element
22340			.removeClass( "ui-slider" +
22341				" ui-slider-horizontal" +
22342				" ui-slider-vertical" +
22343				" ui-slider-disabled" +
22344				" ui-widget" +
22345				" ui-widget-content" +
22346				" ui-corner-all" )
22347			.removeData( "slider" )
22348			.unbind( ".slider" );
22349
22350		this._mouseDestroy();
22351
22352		return this;
22353	},
22354
22355	_mouseCapture: function( event ) {
22356		var o = this.options,
22357			position,
22358			normValue,
22359			distance,
22360			closestHandle,
22361			self,
22362			index,
22363			allowed,
22364			offset,
22365			mouseOverHandle;
22366
22367		if ( o.disabled ) {
22368			return false;
22369		}
22370
22371		this.elementSize = {
22372			width: this.element.outerWidth(),
22373			height: this.element.outerHeight()
22374		};
22375		this.elementOffset = this.element.offset();
22376
22377		position = { x: event.pageX, y: event.pageY };
22378		normValue = this._normValueFromMouse( position );
22379		distance = this._valueMax() - this._valueMin() + 1;
22380		self = this;
22381		this.handles.each(function( i ) {
22382			var thisDistance = Math.abs( normValue - self.values(i) );
22383			if ( distance > thisDistance ) {
22384				distance = thisDistance;
22385				closestHandle = $( this );
22386				index = i;
22387			}
22388		});
22389
22390		// workaround for bug #3736 (if both handles of a range are at 0,
22391		// the first is always used as the one with least distance,
22392		// and moving it is obviously prevented by preventing negative ranges)
22393		if( o.range === true && this.values(1) === o.min ) {
22394			index += 1;
22395			closestHandle = $( this.handles[index] );
22396		}
22397
22398		allowed = this._start( event, index );
22399		if ( allowed === false ) {
22400			return false;
22401		}
22402		this._mouseSliding = true;
22403
22404		self._handleIndex = index;
22405
22406		closestHandle
22407			.addClass( "ui-state-active" )
22408			.focus();
22409		
22410		offset = closestHandle.offset();
22411		mouseOverHandle = !$( event.target ).parents().andSelf().is( ".ui-slider-handle" );
22412		this._clickOffset = mouseOverHandle ? { left: 0, top: 0 } : {
22413			left: event.pageX - offset.left - ( closestHandle.width() / 2 ),
22414			top: event.pageY - offset.top -
22415				( closestHandle.height() / 2 ) -
22416				( parseInt( closestHandle.css("borderTopWidth"), 10 ) || 0 ) -
22417				( parseInt( closestHandle.css("borderBottomWidth"), 10 ) || 0) +
22418				( parseInt( closestHandle.css("marginTop"), 10 ) || 0)
22419		};
22420
22421		if ( !this.handles.hasClass( "ui-state-hover" ) ) {
22422			this._slide( event, index, normValue );
22423		}
22424		this._animateOff = true;
22425		return true;
22426	},
22427
22428	_mouseStart: function( event ) {
22429		return true;
22430	},
22431
22432	_mouseDrag: function( event ) {
22433		var position = { x: event.pageX, y: event.pageY },
22434			normValue = this._normValueFromMouse( position );
22435		
22436		this._slide( event, this._handleIndex, normValue );
22437
22438		return false;
vendor: 11,008 bytes, lines 22439-22845
22439	},
22440
22441	_mouseStop: function( event ) {
22442		this.handles.removeClass( "ui-state-active" );
22443		this._mouseSliding = false;
22444
22445		this._stop( event, this._handleIndex );
22446		this._change( event, this._handleIndex );
22447
22448		this._handleIndex = null;
22449		this._clickOffset = null;
22450		this._animateOff = false;
22451
22452		return false;
22453	},
22454	
22455	_detectOrientation: function() {
22456		this.orientation = ( this.options.orientation === "vertical" ) ? "vertical" : "horizontal";
22457	},
22458
22459	_normValueFromMouse: function( position ) {
22460		var pixelTotal,
22461			pixelMouse,
22462			percentMouse,
22463			valueTotal,
22464			valueMouse;
22465
22466		if ( this.orientation === "horizontal" ) {
22467			pixelTotal = this.elementSize.width;
22468			pixelMouse = position.x - this.elementOffset.left - ( this._clickOffset ? this._clickOffset.left : 0 );
22469		} else {
22470			pixelTotal = this.elementSize.height;
22471			pixelMouse = position.y - this.elementOffset.top - ( this._clickOffset ? this._clickOffset.top : 0 );
22472		}
22473
22474		percentMouse = ( pixelMouse / pixelTotal );
22475		if ( percentMouse > 1 ) {
22476			percentMouse = 1;
22477		}
22478		if ( percentMouse < 0 ) {
22479			percentMouse = 0;
22480		}
22481		if ( this.orientation === "vertical" ) {
22482			percentMouse = 1 - percentMouse;
22483		}
22484
22485		valueTotal = this._valueMax() - this._valueMin();
22486		valueMouse = this._valueMin() + percentMouse * valueTotal;
22487
22488		return this._trimAlignValue( valueMouse );
22489	},
22490
22491	_start: function( event, index ) {
22492		var uiHash = {
22493			handle: this.handles[ index ],
22494			value: this.value()
22495		};
22496		if ( this.options.values && this.options.values.length ) {
22497			uiHash.value = this.values( index );
22498			uiHash.values = this.values();
22499		}
22500		return this._trigger( "start", event, uiHash );
22501	},
22502
22503	_slide: function( event, index, newVal ) {
22504		var otherVal,
22505			newValues,
22506			allowed;
22507
22508		if ( this.options.values && this.options.values.length ) {
22509			otherVal = this.values( index ? 0 : 1 );
22510
22511			if ( ( this.options.values.length === 2 && this.options.range === true ) && 
22512					( ( index === 0 && newVal > otherVal) || ( index === 1 && newVal < otherVal ) )
22513				) {
22514				newVal = otherVal;
22515			}
22516
22517			if ( newVal !== this.values( index ) ) {
22518				newValues = this.values();
22519				newValues[ index ] = newVal;
22520				// A slide can be canceled by returning false from the slide callback
22521				allowed = this._trigger( "slide", event, {
22522					handle: this.handles[ index ],
22523					value: newVal,
22524					values: newValues
22525				} );
22526				otherVal = this.values( index ? 0 : 1 );
22527				if ( allowed !== false ) {
22528					this.values( index, newVal, true );
22529				}
22530			}
22531		} else {
22532			if ( newVal !== this.value() ) {
22533				// A slide can be canceled by returning false from the slide callback
22534				allowed = this._trigger( "slide", event, {
22535					handle: this.handles[ index ],
22536					value: newVal
22537				} );
22538				if ( allowed !== false ) {
22539					this.value( newVal );
22540				}
22541			}
22542		}
22543	},
22544
22545	_stop: function( event, index ) {
22546		var uiHash = {
22547			handle: this.handles[ index ],
22548			value: this.value()
22549		};
22550		if ( this.options.values && this.options.values.length ) {
22551			uiHash.value = this.values( index );
22552			uiHash.values = this.values();
22553		}
22554
22555		this._trigger( "stop", event, uiHash );
22556	},
22557
22558	_change: function( event, index ) {
22559		if ( !this._keySliding && !this._mouseSliding ) {
22560			var uiHash = {
22561				handle: this.handles[ index ],
22562				value: this.value()
22563			};
22564			if ( this.options.values && this.options.values.length ) {
22565				uiHash.value = this.values( index );
22566				uiHash.values = this.values();
22567			}
22568
22569			this._trigger( "change", event, uiHash );
22570		}
22571	},
22572
22573	value: function( newValue ) {
22574		if ( arguments.length ) {
22575			this.options.value = this._trimAlignValue( newValue );
22576			this._refreshValue();
22577			this._change( null, 0 );
22578			return;
22579		}
22580
22581		return this._value();
22582	},
22583
22584	values: function( index, newValue ) {
22585		var vals,
22586			newValues,
22587			i;
22588
22589		if ( arguments.length > 1 ) {
22590			this.options.values[ index ] = this._trimAlignValue( newValue );
22591			this._refreshValue();
22592			this._change( null, index );
22593			return;
22594		}
22595
22596		if ( arguments.length ) {
22597			if ( $.isArray( arguments[ 0 ] ) ) {
22598				vals = this.options.values;
22599				newValues = arguments[ 0 ];
22600				for ( i = 0; i < vals.length; i += 1 ) {
22601					vals[ i ] = this._trimAlignValue( newValues[ i ] );
22602					this._change( null, i );
22603				}
22604				this._refreshValue();
22605			} else {
22606				if ( this.options.values && this.options.values.length ) {
22607					return this._values( index );
22608				} else {
22609					return this.value();
22610				}
22611			}
22612		} else {
22613			return this._values();
22614		}
22615	},
22616
22617	_setOption: function( key, value ) {
22618		var i,
22619			valsLength = 0;
22620
22621		if ( $.isArray( this.options.values ) ) {
22622			valsLength = this.options.values.length;
22623		}
22624
22625		$.Widget.prototype._setOption.apply( this, arguments );
22626
22627		switch ( key ) {
22628			case "disabled":
22629				if ( value ) {
22630					this.handles.filter( ".ui-state-focus" ).blur();
22631					this.handles.removeClass( "ui-state-hover" );
22632					this.handles.propAttr( "disabled", true );
22633					this.element.addClass( "ui-disabled" );
22634				} else {
22635					this.handles.propAttr( "disabled", false );
22636					this.element.removeClass( "ui-disabled" );
22637				}
22638				break;
22639			case "orientation":
22640				this._detectOrientation();
22641				this.element
22642					.removeClass( "ui-slider-horizontal ui-slider-vertical" )
22643					.addClass( "ui-slider-" + this.orientation );
22644				this._refreshValue();
22645				break;
22646			case "value":
22647				this._animateOff = true;
22648				this._refreshValue();
22649				this._change( null, 0 );
22650				this._animateOff = false;
22651				break;
22652			case "values":
22653				this._animateOff = true;
22654				this._refreshValue();
22655				for ( i = 0; i < valsLength; i += 1 ) {
22656					this._change( null, i );
22657				}
22658				this._animateOff = false;
22659				break;
22660		}
22661	},
22662
22663	//internal value getter
22664	// _value() returns value trimmed by min and max, aligned by step
22665	_value: function() {
22666		var val = this.options.value;
22667		val = this._trimAlignValue( val );
22668
22669		return val;
22670	},
22671
22672	//internal values getter
22673	// _values() returns array of values trimmed by min and max, aligned by step
22674	// _values( index ) returns single value trimmed by min and max, aligned by step
22675	_values: function( index ) {
22676		var val,
22677			vals,
22678			i;
22679
22680		if ( arguments.length ) {
22681			val = this.options.values[ index ];
22682			val = this._trimAlignValue( val );
22683
22684			return val;
22685		} else {
22686			// .slice() creates a copy of the array
22687			// this copy gets trimmed by min and max and then returned
22688			vals = this.options.values.slice();
22689			for ( i = 0; i < vals.length; i+= 1) {
22690				vals[ i ] = this._trimAlignValue( vals[ i ] );
22691			}
22692
22693			return vals;
22694		}
22695	},
22696	
22697	// returns the step-aligned value that val is closest to, between (inclusive) min and max
22698	_trimAlignValue: function( val ) {
22699		if ( val <= this._valueMin() ) {
22700			return this._valueMin();
22701		}
22702		if ( val >= this._valueMax() ) {
22703			return this._valueMax();
22704		}
22705		var step = ( this.options.step > 0 ) ? this.options.step : 1,
22706			valModStep = (val - this._valueMin()) % step,
22707			alignValue = val - valModStep;
22708
22709		if ( Math.abs(valModStep) * 2 >= step ) {
22710			alignValue += ( valModStep > 0 ) ? step : ( -step );
22711		}
22712
22713		// Since JavaScript has problems with large floats, round
22714		// the final value to 5 digits after the decimal point (see #4124)
22715		return parseFloat( alignValue.toFixed(5) );
22716	},
22717
22718	_valueMin: function() {
22719		return this.options.min;
22720	},
22721
22722	_valueMax: function() {
22723		return this.options.max;
22724	},
22725	
22726	_refreshValue: function() {
22727		var oRange = this.options.range,
22728			o = this.options,
22729			self = this,
22730			animate = ( !this._animateOff ) ? o.animate : false,
22731			valPercent,
22732			_set = {},
22733			lastValPercent,
22734			value,
22735			valueMin,
22736			valueMax;
22737
22738		if ( this.options.values && this.options.values.length ) {
22739			this.handles.each(function( i, j ) {
22740				valPercent = ( self.values(i) - self._valueMin() ) / ( self._valueMax() - self._valueMin() ) * 100;
22741				_set[ self.orientation === "horizontal" ? "left" : "bottom" ] = valPercent + "%";
22742				$( this ).stop( 1, 1 )[ animate ? "animate" : "css" ]( _set, o.animate );
22743				if ( self.options.range === true ) {
22744					if ( self.orientation === "horizontal" ) {
22745						if ( i === 0 ) {
22746							self.range.stop( 1, 1 )[ animate ? "animate" : "css" ]( { left: valPercent + "%" }, o.animate );
22747						}
22748						if ( i === 1 ) {
22749							self.range[ animate ? "animate" : "css" ]( { width: ( valPercent - lastValPercent ) + "%" }, { queue: false, duration: o.animate } );
22750						}
22751					} else {
22752						if ( i === 0 ) {
22753							self.range.stop( 1, 1 )[ animate ? "animate" : "css" ]( { bottom: ( valPercent ) + "%" }, o.animate );
22754						}
22755						if ( i === 1 ) {
22756							self.range[ animate ? "animate" : "css" ]( { height: ( valPercent - lastValPercent ) + "%" }, { queue: false, duration: o.animate } );
22757						}
22758					}
22759				}
22760				lastValPercent = valPercent;
22761			});
22762		} else {
22763			value = this.value();
22764			valueMin = this._valueMin();
22765			valueMax = this._valueMax();
22766			valPercent = ( valueMax !== valueMin ) ?
22767					( value - valueMin ) / ( valueMax - valueMin ) * 100 :
22768					0;
22769			_set[ self.orientation === "horizontal" ? "left" : "bottom" ] = valPercent + "%";
22770			this.handle.stop( 1, 1 )[ animate ? "animate" : "css" ]( _set, o.animate );
22771
22772			if ( oRange === "min" && this.orientation === "horizontal" ) {
22773				this.range.stop( 1, 1 )[ animate ? "animate" : "css" ]( { width: valPercent + "%" }, o.animate );
22774			}
22775			if ( oRange === "max" && this.orientation === "horizontal" ) {
22776				this.range[ animate ? "animate" : "css" ]( { width: ( 100 - valPercent ) + "%" }, { queue: false, duration: o.animate } );
22777			}
22778			if ( oRange === "min" && this.orientation === "vertical" ) {
22779				this.range.stop( 1, 1 )[ animate ? "animate" : "css" ]( { height: valPercent + "%" }, o.animate );
22780			}
22781			if ( oRange === "max" && this.orientation === "vertical" ) {
22782				this.range[ animate ? "animate" : "css" ]( { height: ( 100 - valPercent ) + "%" }, { queue: false, duration: o.animate } );
22783			}
22784		}
22785	}
22786
22787});
22788
22789$.extend( $.ui.slider, {
22790	version: "1.8.16"
22791});
22792
22793}(jQuery));
22794
22795jQuery.extend({
22796
22797	handleError: function () {},
22798
22799	createUploadIframe: function (id, uri) {
22800		var frameId = 'jQuery' + id;
22801
22802		if (window.ActiveXObject) {
22803		/*/	
22804			var io = document.createElement('<iframe id="' + frameId + '" name="' + frameId + '" />');
22805		/*/	
22806			var io = document.createElement("iframe");
22807			io.setAttribute("id", frameId);
22808			io.setAttribute("name", frameId);
22809		/**/	
22810
22811			if (typeof uri == 'boolean') {
22812				io.src = 'javascript:false';
22813			} else if (typeof uri == 'string') {
22814				io.src = uri;
22815			}
22816			
22817		} else {
22818			
22819			var io = document.createElement('iframe');
22820			io.id = frameId;
22821			io.name = frameId;
22822		}
22823
22824		io.style.position = 'absolute';
22825		io.style.top = '-1000px';
22826		io.style.left = '-1000px';
22827
22828		document.body.appendChild(io);
22829
22830		return io;
22831	},
22832
22833	ajaxUpload: function (s) {
22834		// TODO introduce global settings, allowing the client to modify them for all requests, not only timeout
22835		s = jQuery.extend({}, jQuery.ajaxSettings, s);
22836
22837		var id = new Date().getTime();
22838		
22839		io = jQuery.createUploadIframe(id, s.secureuri);
22840
22841		// Watch for a new set of requests
22842		if (s.global && !jQuery.active++) 
22843			jQuery.event.trigger("ajaxStart");
22844
22845		var requestDone = false;
22846
22847		// Create the request object
22848		var xml = {};
22849
22850		if (s.global) 
22851			jQuery.event.trigger("ajaxSend", [xml, s]);
22852
22853		// Wait for a response to come back
22854		var uploadCallback = function (isTimeout) {
22855
22856			function showErrorInfo(e) {
22857			}
22858
22859			try {
22860
22861				xml.responseText = io.contentWindow.document.body ? io.contentWindow.document.body.innerHTML : null;
22862
22863				xml.responseXML = io.contentWindow.document.XMLDocument ? io.contentWindow.document.XMLDocument : io.contentWindow.document;
22864
22865			} catch (e) {
22866				showErrorInfo(e);
22867			}
22868
22869			if (xml || isTimeout == "timeout") {
22870				requestDone = true;
22871				var status;
22872				try {
22873					status = isTimeout != "timeout" ? "success" : "error";
22874					// Make sure that the request was successful or notmodified
22875
22876					if (status != "error") {
22877
22878						// process the data (runs the xml through httpData regardless of callback)
22879						var data = jQuery.uploadHttpData(xml, s.dataType);
22880
22881						// If a local callback was specified, fire it and pass it the data
22882						if (s.success) s.success(data, status);
22883
22884						// Fire the global callback
22885						if (s.global) jQuery.event.trigger("ajaxSuccess", [xml, s]);
22886
22887					} else jQuery.handleError(s, xml, status);
22888
22889				} catch (e) {
22890
22891					status = "error";
22892					jQuery.handleError(s, xml, status, e);
22893				}
22894
22895				// The request was completed
22896				if (s.global) jQuery.event.trigger("ajaxComplete", [xml, s]);
22897
22898				// Handle the global AJAX counter
22899				if (s.global && !--jQuery.active) jQuery.event.trigger("ajaxStop");
22900
22901				// Process result
22902				if (s.complete) s.complete(xml, status);
22903
22904				jQuery(io).unbind();
22905
22906				setTimeout(function () {
22907					try {
22908						jQuery(io).remove();
22909						jQuery(form).remove();
22910
22911					} catch (e) {
22912						xml = null;
22913					}
22914
22915				}, 100);
22916
22917			}
22918
22919		}
22920		
22921		// Timeout checker
22922		if (s.timeout > 0) {
22923
22924			setTimeout(function () {
22925				// Check to see if the request is still happening
22926				if (!requestDone) {
22927					uploadCallback("timeout");
22928				}
22929
22930			}, s.timeout);
22931
22932		}
22933
22934		try {
22935
22936			var frameId = 'jQuery' + id;
22937			var io = document.getElementById(frameId);
22938
22939			// Initialize the HTML form properties in case they are
22940			// not defined in the HTML form.
22941			s.uploadform.action = s.url;
22942			s.uploadform.method = 'POST';
22943			s.uploadform.target = frameId;
22944		//	s.uploadform.target = '_blank';
22945		
22946			if (s.uploadform.encoding) {
22947				// IE does not respect property enctype for HTML forms.
22948				// Instead use property encoding.
22949				s.uploadform.encoding = 'multipart/form-data';
22950			} else {
22951				s.uploadform.enctype = 'multipart/form-data';
22952			}
22953
22954			if (window.attachEvent) {
22955				io.attachEvent('onload', uploadCallback);
22956			} else {
22957				io.addEventListener('load', uploadCallback, false);
22958			}
22959			
22960			s.uploadform.submit();
22961
22962		} catch (e) {
22963			jQuery.handleError(s, xml, null, e);
22964		}
22965
22966		return {
22967			abort: function () {}
22968		};
22969
22970	},
22971
22972	uploadHttpData: function (r, type) {
22973		var data = !type;
22974		data = type == "xml" || data ? r.responseXML : r.responseText;
22975
22976		// If the type is "script", eval it in global context
22977		if ( type == "script" )    jQuery.globalEval( data );     // only for jquery 1.1...
22978
22979		// Get the JavaScript object, if JSON is used.
22980		if ( type == "json" )      eval( "data = " + data );     // only for jquery 1.1...
22981
22982		// evaluate scripts within html
22983		if ( type == "html" )        jQuery("<div>").html(data);  // only for jquery 1.1...
22984
22985		return data;
22986	}
22987});
22988(function($){
22989	/* hoverIntent by Brian Cherne */
22990	$.fn.hoverIntent = function(f,g) {
22991		// default configuration options
22992		var cfg = {
22993			sensitivity: 7,
22994			interval: 100,
22995			timeout: 0
22996		};
22997		// override configuration options with user supplied object
22998		cfg = $.extend(cfg, g ? { over: f, out: g } : f );
22999
23000		// instantiate variables
23001		// cX, cY = current X and Y position of mouse, updated by mousemove event
23002		// pX, pY = previous X and Y position of mouse, set by mouseover and polling interval
23003		var cX, cY, pX, pY;
23004
23005		// A private function for getting mouse position
23006		var track = function(ev) {
23007			cX = ev.pageX;
23008			cY = ev.pageY;
23009		};
23010
23011		// A private function for comparing current and previous mouse position
23012		var compare = function(ev,ob) {
23013			ob.hoverIntent_t = clearTimeout(ob.hoverIntent_t);
23014			// compare mouse positions to see if they've crossed the threshold
23015			if ( ( Math.abs(pX-cX) + Math.abs(pY-cY) ) < cfg.sensitivity ) {
23016				$(ob).unbind("mousemove",track);
23017				// set hoverIntent state to true (so mouseOut can be called)
23018				ob.hoverIntent_s = 1;
23019				return cfg.over.apply(ob,[ev]);
23020			} else {
23021				// set previous coordinates for next time
23022				pX = cX; pY = cY;
23023				// use self-calling timeout, guarantees intervals are spaced out properly (avoids JavaScript timer bugs)
23024				ob.hoverIntent_t = setTimeout( function(){compare(ev, ob);} , cfg.interval );
23025			}
23026		};
23027
23028		// A private function for delaying the mouseOut function
23029		var delay = function(ev,ob) {
23030			ob.hoverIntent_t = clearTimeout(ob.hoverIntent_t);
23031			ob.hoverIntent_s = 0;
23032			return cfg.out.apply(ob,[ev]);
23033		};
23034
23035		// A private function for handling mouse 'hovering'
23036		var handleHover = function(e) {
23037			// next three lines copied from jQuery.hover, ignore children onMouseOver/onMouseOut
23038			var p = (e.type == "mouseover" ? e.fromElement : e.toElement) || e.relatedTarget;
23039			while ( p && p != this ) { try { p = p.parentNode; } catch(e) { p = this; } }
23040			if ( p == this ) { return false; }
23041
23042			// copy objects to be passed into t (required for event object to be passed in IE)
23043			var ev = jQuery.extend({},e);
23044			var ob = this;
23045
23046			// cancel hoverIntent timer if it exists
23047			if (ob.hoverIntent_t) { ob.hoverIntent_t = clearTimeout(ob.hoverIntent_t); }
23048
23049			// else e.type == "onmouseover"
23050			if (e.type == "mouseover") {
23051				// set "previous" X and Y position based on initial entry point
23052				pX = ev.pageX; pY = ev.pageY;
23053				// update "current" X and Y position based on mousemove
23054				$(ob).bind("mousemove",track);
23055				// start polling interval (self-calling timeout) to compare mouse coordinates over time
23056				if (ob.hoverIntent_s != 1) { ob.hoverIntent_t = setTimeout( function(){compare(ev,ob);} , cfg.interval );}
23057
23058			// else e.type == "onmouseout"
23059			} else {
23060				// unbind expensive mousemove event
23061				$(ob).unbind("mousemove",track);
23062				// if hoverIntent state is true, then call the mouseOut function after the specified delay
23063				if (ob.hoverIntent_s == 1) { ob.hoverIntent_t = setTimeout( function(){delay(ev,ob);} , cfg.timeout );}
23064			}
23065		};
23066
23067		// bind the function to the two event listeners
23068		return this.mouseover(handleHover).mouseout(handleHover);
23069	};
23070	
23071})(jQuery);
23072
23073/*
23074 * Superfish v1.4.8 - jQuery menu widget
23075 * Copyright (c) 2008 Joel Birch
23076 *
23077 * Dual licensed under the MIT and GPL licenses:
23078 * 	http://www.opensource.org/licenses/mit-license.php
23079 * 	http://www.gnu.org/licenses/gpl.html
23080 *
23081 * CHANGELOG: http://users.tpg.com.au/j_birch/plugins/superfish/changelog.txt
23082 */
23083
23084;(function($){
23085	$.fn.superfish = function(op){
23086
23087		var sf = $.fn.superfish,
23088			c = sf.c,
23089			$arrow = $(['<span class="',c.arrowClass,'"> &#187;</span>'].join('')),
23090			over = function(){
23091				var $$ = $(this), menu = getMenu($$);
23092				clearTimeout(menu.sfTimer);
23093				$$.showSuperfishUl().siblings().hideSuperfishUl();
23094			},
23095			out = function(){
23096				var $$ = $(this), menu = getMenu($$), o = sf.op;
23097				clearTimeout(menu.sfTimer);
23098				menu.sfTimer=setTimeout(function(){
23099					o.retainPath=($.inArray($$[0],o.$path)>-1);
23100					$$.hideSuperfishUl();
23101					if (o.$path.length && $$.parents(['li.',o.hoverClass].join('')).length<1){over.call(o.$path);}
23102				},o.delay);	
23103			},
23104			getMenu = function($menu){
23105				var menu = $menu.parents(['ul.',c.menuClass,':first'].join(''))[0];
23106				sf.op = sf.o[menu.serial];
23107				return menu;
23108			},
23109			addArrow = function($a){ $a.addClass(c.anchorClass).append($arrow.clone()); };
23110			
23111		return this.each(function() {
23112			var s = this.serial = sf.o.length;
23113			var o = $.extend({},sf.defaults,op);
23114			o.$path = $('li.'+o.pathClass,this).slice(0,o.pathLevels).each(function(){
23115				$(this).addClass([o.hoverClass,c.bcClass].join(' '))
23116					.filter('li:has(ul)').removeClass(o.pathClass);
23117			});
23118			sf.o[s] = sf.op = o;
23119			
23120			$('li:has(ul)',this)[($.fn.hoverIntent && !o.disableHI) ? 'hoverIntent' : 'hover'](over,out).each(function() {
23121				if (o.autoArrows) addArrow( $('>a:first-child',this) );
23122			})
23123			.not('.'+c.bcClass)
23124				.hideSuperfishUl();
23125			
23126			var $a = $('a',this);
23127			$a.each(function(i){
23128				var $li = $a.eq(i).parents('li');
23129				$a.eq(i).focus(function(){over.call($li);}).blur(function(){out.call($li);});
23130			});
23131			o.onInit.call(this);
23132			
23133		}).each(function() {
23134			var menuClasses = [c.menuClass];
23135			if (sf.op.dropShadows  && !($.browser.msie && $.browser.version < 7)) menuClasses.push(c.shadowClass);
23136			$(this).addClass(menuClasses.join(' '));
23137		});
23138	};
23139
23140	var sf = $.fn.superfish;
23141	sf.o = [];
23142	sf.op = {};
23143	sf.IE7fix = function(){
23144		var o = sf.op;
23145		if ($.browser.msie && $.browser.version >
23145 6 && o.dropShadows && o.animation.opacity!=undefined)
23146			this.toggleClass(sf.c.shadowClass+'-off');
23147		};
23148	sf.c = {
23149		bcClass     : 'sf-breadcrumb',
23150		menuClass   : 'sf-js-enabled',
23151		anchorClass : 'sf-with-ul',
23152		arrowClass  : 'sf-sub-indicator',
23153		shadowClass : 'sf-shadow'
23154	};
23155	sf.defaults = {
23156		hoverClass	: 'sfHover',
23157		pathClass	: 'overideThisToUse',
23158		pathLevels	: 1,
23159		delay		: 800,
23160		animation	: {opacity:'show'},
23161		speed		: 'normal',
23162		autoArrows	: true,
23163		dropShadows : true,
23164		disableHI	: false,		// true disables hoverIntent detection
23165		onInit		: function(){}, // callback functions
23166		onBeforeShow: function(){},
23167		onShow		: function(){},
23168		onHide		: function(){}
23169	};
23170	$.fn.extend({
23171		hideSuperfishUl : function(){
23172			var o = sf.op,
23173				not = (o.retainPath===true) ? o.$path : '';
23174			o.retainPath = false;
23175			var $ul = $(['li.',o.hoverClass].join(''),this).add(this).not(not).removeClass(o.hoverClass)
23176					.find('>ul').hide().css('visibility','hidden');
23177			o.onHide.call($ul);
23178			return this;
23179		},
23180		showSuperfishUl : function(){
23181			var o = sf.op,
23182				sh = sf.c.shadowClass+'-off',
23183				$ul = this.addClass(o.hoverClass)
23184					.find('>ul:hidden').css('visibility','visible');
23185			sf.IE7fix.call($ul);
23186			o.onBeforeShow.call($ul);
23187			$ul.animate(o.animation,o.speed,function(){ sf.IE7fix.call($ul); o.onShow.call($ul); });
23188			return this;
23189		}
23190	});
23191
23192})(jQuery);
23193
23194/*!
23195 * imagesLoaded PACKAGED v3.1.8
23196 * JavaScript is all like "You images are done yet or what?"
23197 * MIT License
23198 */
23199
23200
23201/*!
23202 * EventEmitter v4.2.6 - git.io/ee
23203 * Oliver Caldwell
23204 * MIT license
23205 * @preserve
23206 */
23207
23208(function () {
23209	
23210
23211	/**
23212	 * Class for managing events.
23213	 * Can be extended to provide event functionality in other classes.
23214	 *
23215	 * @class EventEmitter Manages event registering and emitting.
23216	 */
23217	function EventEmitter() {}
23218
23219	// Shortcuts to improve speed and size
23220	var proto = EventEmitter.prototype;
23221	var exports = this;
23222	var originalGlobalValue = exports.EventEmitter;
23223
23224	/**
23225	 * Finds the index of the listener for the event in it's storage array.
23226	 *
23227	 * @param {Function[]} listeners Array of listeners to search through.
23228	 * @param {Function} listener Method to look for.
23229	 * @return {Number} Index of the specified listener, -1 if not found
23230	 * @api private
23231	 */
23232	function indexOfListener(listeners, listener) {
23233		var i = listeners.length;
23234		while (i--) {
23235			if (listeners[i].listener === listener) {
23236				return i;
23237			}
23238		}
23239
23240		return -1;
23241	}
23242
23243	/**
23244	 * Alias a method while keeping the context correct, to allow for overwriting of target method.
23245	 *
23246	 * @param {String} name The name of the target method.
23247	 * @return {Function} The aliased method
23248	 * @api private
23249	 */
23250	function alias(name) {
23251		return function aliasClosure() {
23252			return this[name].apply(this, arguments);
23253		};
23254	}
23255
23256	/**
23257	 * Returns the listener array for the specified event.
23258	 * Will initialise the event object and listener arrays if required.
23259	 * Will return an object if you use a regex search. The object contains keys for each matched event. So /ba[rz]/ might return an object c
vendor: 10,814 bytes, lines 23259-23552
23259ontaining bar and baz. But only if you have either defined them with defineEvent or added some listeners to them.
23260	 * Each property in the object response is an array of listener functions.
23261	 *
23262	 * @param {String|RegExp} evt Name of the event to return the listeners from.
23263	 * @return {Function[]|Object} All listener functions for the event.
23264	 */
23265	proto.getListeners = function getListeners(evt) {
23266		var events = this._getEvents();
23267		var response;
23268		var key;
23269
23270		// Return a concatenated array of all matching events if
23271		// the selector is a regular expression.
23272		if (typeof evt === 'object') {
23273			response = {};
23274			for (key in events) {
23275				if (events.hasOwnProperty(key) && evt.test(key)) {
23276					response[key] = events[key];
23277				}
23278			}
23279		}
23280		else {
23281			response = events[evt] || (events[evt] = []);
23282		}
23283
23284		return response;
23285	};
23286
23287	/**
23288	 * Takes a list of listener objects and flattens it into a list of listener functions.
23289	 *
23290	 * @param {Object[]} listeners Raw listener objects.
23291	 * @return {Function[]} Just the listener functions.
23292	 */
23293	proto.flattenListeners = function flattenListeners(listeners) {
23294		var flatListeners = [];
23295		var i;
23296
23297		for (i = 0; i < listeners.length; i += 1) {
23298			flatListeners.push(listeners[i].listener);
23299		}
23300
23301		return flatListeners;
23302	};
23303
23304	/**
23305	 * Fetches the requested listeners via getListeners but will always return the results inside an object. This is mainly for internal use but others may find it useful.
23306	 *
23307	 * @param {String|RegExp} evt Name of the event to return the listeners from.
23308	 * @return {Object} All listener functions for an event in an object.
23309	 */
23310	proto.getListenersAsObject = function getListenersAsObject(evt) {
23311		var listeners = this.getListeners(evt);
23312		var response;
23313
23314		if (listeners instanceof Array) {
23315			response = {};
23316			response[evt] = listeners;
23317		}
23318
23319		return response || listeners;
23320	};
23321
23322	/**
23323	 * Adds a listener function to the specified event.
23324	 * The listener will not be added if it is a duplicate.
23325	 * If the listener returns true then it will be removed after it is called.
23326	 * If you pass a regular expression as the event name then the listener will be added to all events that match it.
23327	 *
23328	 * @param {String|RegExp} evt Name of the event to attach the listener to.
23329	 * @param {Function} listener Method to be called when the event is emitted. If the function returns true then it will be removed after calling.
23330	 * @return {Object} Current instance of EventEmitter for chaining.
23331	 */
23332	proto.addListener = function addListener(evt, listener) {
23333		var listeners = this.getListenersAsObject(evt);
23334		var listenerIsWrapped = typeof listener === 'object';
23335		var key;
23336
23337		for (key in listeners) {
23338			if (listeners.hasOwnProperty(key) && indexOfListener(listeners[key], listener) === -1) {
23339				listeners[key].push(listenerIsWrapped ? listener : {
23340					listener: listener,
23341					once: false
23342				});
23343			}
23344		}
23345
23346		return this;
23347	};
23348
23349	/**
23350	 * Alias of addListener
23351	 */
23352	proto.on = alias('addListener');
23353
23354	/**
23355	 * Semi-alias of addListener. It will add a listener that will be
23356	 * automatically removed after it's first execution.
23357	 *
23358	 * @param {String|RegExp} evt Name of the event to attach the listener to.
23359	 * @param {Function} listener Method to be called when the event is emitted. If the function returns true then it will be removed after calling.
23360	 * @return {Object} Current instance of EventEmitter for chaining.
23361	 */
23362	proto.addOnceListener = function addOnceListener(evt, listener) {
23363		return this.addListener(evt, {
23364			listener: listener,
23365			once: true
23366		});
23367	};
23368
23369	/**
23370	 * Alias of addOnceListener.
23371	 */
23372	proto.once = alias('addOnceListener');
23373
23374	/**
23375	 * Defines an event name. This is required if you want to use a regex to add a listener to multiple events at once. If you don't do this then how do you expect it to know what event to add to? Should it just add to every possible match for a regex? No. That is scary and bad.
23376	 * You need to tell it what event names should be matched by a regex.
23377	 *
23378	 * @param {String} evt Name of the event to create.
23379	 * @return {Object} Current instance of EventEmitter for chaining.
23380	 */
23381	proto.defineEvent = function defineEvent(evt) {
23382		this.getListeners(evt);
23383		return this;
23384	};
23385
23386	/**
23387	 * Uses defineEvent to define multiple events.
23388	 *
23389	 * @param {String[]} evts An array of event names to define.
23390	 * @return {Object} Current instance of EventEmitter for chaining.
23391	 */
23392	proto.defineEvents = function defineEvents(evts) {
23393		for (var i = 0; i < evts.length; i += 1) {
23394			this.defineEvent(evts[i]);
23395		}
23396		return this;
23397	};
23398
23399	/**
23400	 * Removes a listener function from the specified event.
23401	 * When passed a regular expression as the event name, it will remove the listener from all events that match it.
23402	 *
23403	 * @param {String|RegExp} evt Name of the event to remove the listener from.
23404	 * @param {Function} listener Method to remove from the event.
23405	 * @return {Object} Current instance of EventEmitter for chaining.
23406	 */
23407	proto.removeListener = function removeListener(evt, listener) {
23408		var listeners = this.getListenersAsObject(evt);
23409		var index;
23410		var key;
23411
23412		for (key in listeners) {
23413			if (listeners.hasOwnProperty(key)) {
23414				index = indexOfListener(listeners[key], listener);
23415
23416				if (index !== -1) {
23417					listeners[key].splice(index, 1);
23418				}
23419			}
23420		}
23421
23422		return this;
23423	};
23424
23425	/**
23426	 * Alias of removeListener
23427	 */
23428	proto.off = alias('removeListener');
23429
23430	/**
23431	 * Adds listeners in bulk using the manipulateListeners method.
23432	 * If you pass an object as the second argument you can add to multiple events at once. The object should contain key value pairs of events and listeners or listener arrays. You can also pass it an event name and an array of listeners to be added.
23433	 * You can also pass it a regular expression to add the array of listeners to all events that match it.
23434	 * Yeah, this function does quite a bit. That's probably a bad thing.
23435	 *
23436	 * @param {String|Object|RegExp} evt An event name if you will pass an array of listeners next. An object if you wish to add to multiple events at once.
23437	 * @param {Function[]} [listeners] An optional array of listener functions to add.
23438	 * @return {Object} Current instance of EventEmitter for chaining.
23439	 */
23440	proto.addListeners = function addListeners(evt, listeners) {
23441		// Pass through to manipulateListeners
23442		return this.manipulateListeners(false, evt, listeners);
23443	};
23444
23445	/**
23446	 * Removes listeners in bulk using the manipulateListeners method.
23447	 * If you pass an object as the second argument you can remove from multiple events at once. The object should contain key value pairs of events and listeners or listener arrays.
23448	 * You can also pass it an event name and an array of listeners to be removed.
23449	 * You can also pass it a regular expression to remove the listeners from all events that match it.
23450	 *
23451	 * @param {String|Object|RegExp} evt An event name if you will pass an array of listeners next. An object if you wish to remove from multiple events at once.
23452	 * @param {Function[]} [listeners] An optional array of listener functions to remove.
23453	 * @return {Object} Current instance of EventEmitter for chaining.
23454	 */
23455	proto.removeListeners = function removeListeners(evt, listeners) {
23456		// Pass through to manipulateListeners
23457		return this.manipulateListeners(true, evt, listeners);
23458	};
23459
23460	/**
23461	 * Edits listeners in bulk. The addListeners and removeListeners methods both use this to do their job. You should really use those instead, this is a little lower level.
23462	 * The first argument will determine if the listeners are removed (true) or added (false).
23463	 * If you pass an object as the second argument you can add/remove from multiple events at once. The object should contain key value pairs of events and listeners or listener arrays.
23464	 * You can also pass it an event name and an array of listeners to be added/removed.
23465	 * You can also pass it a regular expression to manipulate the listeners of all events that match it.
23466	 *
23467	 * @param {Boolean} remove True if you want to remove listeners, false if you want to add.
23468	 * @param {String|Object|RegExp} evt An event name if you will pass an array of listeners next. An object if you wish to add/remove from multiple events at once.
23469	 * @param {Function[]} [listeners] An optional array of listener functions to add/remove.
23470	 * @return {Object} Current instance of EventEmitter for chaining.
23471	 */
23472	proto.manipulateListeners = function manipulateListeners(remove, evt, listeners) {
23473		var i;
23474		var value;
23475		var single = remove ? this.removeListener : this.addListener;
23476		var multiple = remove ? this.removeListeners : this.addListeners;
23477
23478		// If evt is an object then pass each of it's properties to this method
23479		if (typeof evt === 'object' && !(evt instanceof RegExp)) {
23480			for (i in evt) {
23481				if (evt.hasOwnProperty(i) && (value = evt[i])) {
23482					// Pass the single listener straight through to the singular method
23483					if (typeof value === 'function') {
23484						single.call(this, i, value);
23485					}
23486					else {
23487						// Otherwise pass back to the multiple function
23488						multiple.call(this, i, value);
23489					}
23490				}
23491			}
23492		}
23493		else {
23494			// So evt must be a string
23495			// And listeners must be an array of listeners
23496			// Loop over it and pass each one to the multiple method
23497			i = listeners.length;
23498			while (i--) {
23499				single.call(this, evt, listeners[i]);
23500			}
23501		}
23502
23503		return this;
23504	};
23505
23506	/**
23507	 * Removes all listeners from a specified event.
23508	 * If you do not specify an event then all listeners will be removed.
23509	 * That means every event will be emptied.
23510	 * You can also pass a regex to remove all events that match it.
23511	 *
23512	 * @param {String|RegExp} [evt] Optional name of the event to remove all listeners for. Will remove from every event if not passed.
23513	 * @return {Object} Current instance of EventEmitter for chaining.
23514	 */
23515	proto.removeEvent = function removeEvent(evt) {
23516		var type = typeof evt;
23517		var events = this._getEvents();
23518		var key;
23519
23520		// Remove different things depending on the state of evt
23521		if (type === 'string') {
23522			// Remove all listeners for the specified event
23523			delete events[evt];
23524		}
23525		else if (type === 'object') {
23526			// Remove all events matching the regex.
23527			for (key in events) {
23528				if (events.hasOwnProperty(key) && evt.test(key)) {
23529					delete events[key];
23530				}
23531			}
23532		}
23533		else {
23534			// Remove all listeners in all events
23535			delete this._events;
23536		}
23537
23538		return this;
23539	};
23540
23541	/**
23542	 * Alias of removeEvent.
23543	 *
23544	 * Added to mirror the node API.
23545	 */
23546	proto.removeAllListeners = alias('removeEvent');
23547
23548	/**
23549	 * Emits an event of your choice.
23550	 * When emitted, every listener attached to that event will be executed.
23551	 * If you pass the optional argument array then those arguments will be passed to every listener upon execution.
23552	 * Because it uses `apply`, your array of arguments will be passed as if you wrote them out separately.
23553	 * So they will not arrive within the array on the other side, they will be separate.
23554	 * You can also pass a regular expression to emit to all events that match it.
23555	 *
23556	 * @param {String|RegExp} evt Name of the event to emit and execute listeners for.
23557	 * @param {Array} [args] Optional array of arguments to be passed to each listener.
23558	 * @return {Object} Current instance of EventEmitter for chaining.
23559	 */
23560	proto.emitEvent = function emitEvent(evt, args) {
23561		var listeners = this.getListenersAsObject(evt);
23562		var listener;
23563		var i;
23564		var key;
23565		var response;
23566
23567		for (key in listeners) {
23568			if (listeners.hasOwnProperty(key)) {
23569				i = listeners[key].length;
23570
23571				while (i--) {
23572					// If the listener returns true then it shall be removed from the event
23573					// The function is executed either with a basic call or an apply if there is an args array
23574					listener = listeners[key][i];
23575
23576					if (listener.once === true) {
23577						this.removeListener(evt, listener.listener);
23578					}
23579
23580					response = listener.listener.apply(this, args || []);
23581
23582					if (response === this._getOnceReturnValue()) {
23583						this.removeListener(evt, listener.listener);
23584					}
23585				}
23586			}
23587		}
23588
23589		return this;
23590	};
23591
23592	/**
23593	 * Alias of emitEvent
23594	 */
23595	proto.trigger = alias('emitEvent');
23596
23597	/**
23598	 * Subtly different from emitEvent in that it will pass its arguments on to the listeners, as opposed to taking a single array of arguments to pass on.
23599	 * As with emitEvent, you can pass a regex in place of the event name to emit to all events that match it.
23600	 *
23601	 * @param {String|RegExp} evt Name of the event to emit and execute listeners for.
23602	 * @param {...*} Optional additional arguments to be passed to each listener.
23603	 * @return {Object} Current instance of EventEmitter for chaining.
23604	 */
23605	proto.emit = function emit(evt) {
23606		var args = Array.prototype.slice.call(arguments, 1);
23607		return this.emitEvent(evt, args);
23608	};
23609
23610	/**
23611	 * Sets the current value to check against when executing listeners. If a
23612	 * listeners return value matches the one set here then it will be removed
23613	 * after execution. This value defaults to true.
23614	 *
23615	 * @param {*} value The new value to check for when executing listeners.
23616	 * @return {Object} Current instance of EventEmitter for chaining.
23617	 */
23618	proto.setOnceReturnValue = function setOnceReturnValue(value) {
23619		this._onceReturnValue = value;
23620		return this;
23621	};
23622
23623	/**
23624	 * Fetches the current value to check against when executing listeners. If
23625	 * the listeners return value matches this one then it should be removed
23626	 * automatically. It will return true by default.
23627	 *
23628	 * @return {*|Boolean} The current value to check for or the default, true.
23629	 * @api private
23630	 */
23631	proto._getOnceReturnValue = function _getOnceReturnValue() {
23632		if (this.hasOwnProperty('_onceReturnValue')) {
23633			return this._onceReturnValue;
23634		}
23635		else {
23636			return true;
23637		}
23638	};
23639
23640	/**
23641	 * Fetches the events object and creates one if required.
23642	 *
23643	 * @return {Object} The events storage object.
23644	 * @api private
23645	 */
23646	proto._getEvents = function _getEvents() {
23647		return this._events || (this._events = {});
23648	};
23649
23650	/**
23651	 * Reverts the global {@link EventEmitter} to its previous value and returns a reference to this version.
23652	 *
23653	 * @return {Function} Non conflicting EventEmitter class.
23654	 */
23655	EventEmitter.noConflict = function noConflict() {
23656		exports.EventEmitter = originalGlobalValue;
23657		return EventEmitter;
23658	};
23659
23660	// Expose the class either via AMD, CommonJS or the global object
23661	if (typeof define === 'function' && define.amd) {
23662		define('eventEmitter/EventEmitter',[],function () {
23663			return EventEmitter;
23664		});
23665	}
23666	else if (typeof module === 'object' && module.exports){
23667		module.exports = EventEmitter;
23668	}
23669	else {
23670		this.EventEmitter = EventEmitter;
23671	}
23672}.call(this));
23673
23674/*!
23675 * eventie v1.0.4
23676 * event binding helper
23677 *   eventie.bind( elem, 'click', myFn )
23678 *   eventie.unbind( elem, 'click', myFn )
23679 */
23680
23681/*jshint browser: true, undef: true, unused: true */
23682/*global define: false */
23683
23684( function( window ) {
23685
23686
23687
23688var docElem = document.documentElement;
23689
23690var bind = function() {};
23691
23692function getIEEvent( obj ) {
23693  var event = window.event;
23694  // add event.target
23695  event.target = event.target || event.srcElement || obj;
23696  return event;
23697}
23698
23699if ( docElem.addEventListener ) {
23700  bind = function( obj, type, fn ) {
23701    obj.addEventListener( type, fn, false );
23702  };
23703} else if ( docElem.attachEvent ) {
23704  bind = function( obj, type, fn ) {
23705    obj[ type + fn ] = fn.handleEvent ?
23706      function() {
23707        var event = getIEEvent( obj );
23708        fn.handleEvent.call( fn, event );
23709      } :
23710      function() {
23711        var event = getIEEvent( obj );
23712        fn.call( obj, event );
23713      };
23714    obj.attachEvent( "on" + type, obj[ type + fn ] );
23715  };
23716}
23717
23718var unbind = function() {};
23719
23720if ( docElem.removeEventListener ) {
23721  unbind = function( obj, type, fn ) {
23722    obj.removeEventListener( type, fn, false );
23723  };
23724} else if ( docElem.detachEvent ) {
23725  unbind = function( obj, type, fn ) {
23726    obj.detachEvent( "on" + type, obj[ type + fn ] );
23727    try {
23728      delete obj[ type + fn ];
23729    } catch ( err ) {
23730      // can't delete window object properties
23731      obj[ type + fn ] = undefined;
23732    }
23733  };
23734}
23735
23736var eventie = {
23737  bind: bind,
23738  unbind: unbind
23739};
23740
23741// transport
23742if ( typeof define === 'function' && define.amd ) {
23743  // AMD
23744  define( 'eventie/eventie',eventie );
23745} else {
23746  // browser global
23747  window.eventie = eventie;
23748}
23749
23750})( this );
23751
23752/*!
23753 * imagesLoaded v3.1.8
23754 * JavaScript is all like "You images are done yet or what?"
23755 * MIT License
23756 */
23757
23758( function( window, factory ) { 
23759  // universal module definition
23760
23761  /*global define: false, module: false, require: false */
23762
23763  if ( typeof define === 'function' && define.amd ) {
23764    // AMD
23765    define( [
23766      'eventEmitter/EventEmitter',
23767      'eventie/eventie'
23768    ], function( EventEmitter, eventie ) {
23769      return factory( window, EventEmitter, eventie );
23770    });
23771  } else if ( typeof exports === 'object' ) {
23772    // CommonJS
23773    module.exports = factory(
23774      window,
23775      require('wolfy87-eventemitter'),
23776      require('eventie')
23777    );
23778  } else {
23779    // browser global
23780    window.imagesLoaded = factory(
23781      window,
23782      window.EventEmitter,
23783      window.eventie
23784    );
23785  }
23786
23787})( window,
23788
23789// --------------------------  factory -------------------------- //
23790
23791function factory( window, EventEmitter, eventie ) {
23792
23793
23794
23795var $ = window.jQuery;
23796var console = window.console;
23797var hasConsole = typeof console !== 'undefined';
23798
23799// -------------------------- helpers -------------------------- //
23800
23801// extend objects
23802function extend( a, b ) {
23803  for ( var prop in b ) {
23804    a[ prop ] = b[ prop ];
23805  }
23806  return a;
23807}
23808
23809var objToString = Object.prototype.toString;
23810function isArray( obj ) {
23811  return objToString.call( obj ) === '[object Array]';
23812}
23813
23814// turn element or nodeList into an array
23815function makeArray( obj ) {
23816  var ary = [];
23817  if ( isArray( obj ) ) {
23818    // use object if already an array
23819    ary = obj;
23820  } else if ( typeof obj.length === 'number' ) {
23821    // convert nodeList to array
23822    for ( var i=0, len = obj.length; i < len; i++ ) {
23823      ary.push( obj[i] );
23824    }
23825  } else {
23826    // array of single index
23827    ary.push( obj );
23828  }
23829  return ary;
23830}
23831
23832  // -------------------------- imagesLoaded -------------------------- //
23833
23834  /**
23835   * @param {Array, Element, NodeList, String} elem
23836   * @param {Object or Function} options - if function, use as callback
23837   * @param {Function} onAlways - callback function
23838   */
23839  function ImagesLoaded( elem, options, onAlways ) {
23840    // coerce ImagesLoaded() without new, to be new ImagesLoaded()
23841    if ( !( this instanceof ImagesLoaded ) ) {
23842      return new ImagesLoaded( elem, options );
23843    }
23844    // use elem as selector string
23845    if ( typeof elem === 'string' ) {
23846      elem = document.querySelectorAll( elem );
23847    }
23848
23849    this.elements = makeArray( elem );
23850    this.options = extend( {}, this.options );
23851
23852    if ( typeof options === 'function' ) {
23853      onAlways = options;
23854    } else {
23855      extend( this.options, options );
23856    }
23857
23858    if ( onAlways ) {
23859      this.on( 'always', onAlways );
23860    }
23861
23862    this.getImages();
23863
23864    if ( $ ) {
23865      // add jQuery Deferred object
23866      this.jqDeferred = new $.Deferred();
23867    }
23868
23869    // HACK check async to allow time to bind listeners
23870    var _this = this;
23871    setTimeout( function() {
23872      _this.check();
23873    });
23874  }
23875
23876  ImagesLoaded.prototype = new EventEmitter();
23877
23878  ImagesLoaded.prototype.options = {};
23879
23880  ImagesLoaded.prototype.getImages = function() {
23881    this.images = [];
23882
23883    // filter & find items if we have an item selector
23884    for ( var i=0, len = this.elements.length; i < len; i++ ) {
23885      var elem = this.elements[i];
23886      // filter siblings
23887      if ( elem.nodeName === 'IMG' ) {
23888        this.addImage( elem );
23889      }
23890      // find children
23891      // no non-element nodes, #143
23892      var nodeType = elem.nodeType;
23893      if ( !nodeType || !( nodeType === 1 || nodeType === 9 || nodeType === 11 ) ) {
23894        continue;
23895      }
23896      var childElems = elem.querySelectorAll('img');
23897      // concat childElems to filterFound array
23898      for ( var j=0, jLen = childElems.length; j < jLen; j++ ) {
23899        var img = childElems[j];
23900        this.addImage( img );
23901      }
23902    }
23903  };
23904
23905  /**
23906   * @param {Image} img
23907   */
23908  ImagesLoaded.prototype.addImage = function( img ) {
23909    var loadingImage = new LoadingImage( img );
23910    this.images.push( loadingImage );
23911  };
23912
23913  ImagesLoaded.prototype.check = function() {
23914    var _this = this;
23915    var checkedCount = 0;
23916    var length = this.images.length;
23917    this.hasAnyBroken = false;
23918    // complete if no images
23919    if ( !length ) {
23920      this.complete();
23921      return;
23922    }
23923
23924    function onConfirm( image, message ) {
23925      if ( _this.options.debug && hasConsole ) {
23926        console.log( 'confirm', image, message );
23927      }
23928
23929      _this.progress( image );
23930      checkedCount++;
23931      if ( checkedCount === length ) {
23932        _this.complete();
23933      }
23934      return true; // bind once
23935    }
23936
23937    for ( var i=0; i < length; i++ ) {
23938      var loadingImage = this.images[i];
23939      loadingImage.on( 'confirm', onConfirm );
23940      loadingImage.check();
23941    }
23942  };
23943
23944  ImagesLoaded.prototype.progress = function( image ) {
23945    this.hasAnyBroken = this.hasAnyBroken || !image.isLoaded;
23946    // HACK - Chrome triggers event before object properties have changed. #83
23947    var _this = this;
23948    setTimeout( function() {
23949      _this.emit( 'progress', _this, image );
23950      if ( _this.jqDeferred && _this.jqDeferred.notify ) {
23951        _this.jqDeferred.notify( _this, image );
23952      }
23953    });
23954  };
23955
23956  ImagesLoaded.prototype.complete = function() {
23957    var eventName = this.hasAnyBroken ? 'fail' : 'done';
23958    this.isComplete = true;
23959    var _this = this;
23960    // HACK - another setTimeout so that confirm happens after progress
23961    setTimeout( function() {
23962      _this.emit( eventName, _this );
23963      _this.emit( 'always', _this );
23964      if ( _this.jqDeferred ) {
23965        var jqMethod = _this.hasAnyBroken ? 'reject' : 'resolve';
23966        _this.jqDeferred[ jqMethod ]( _this );
23967      }
23968    });
23969  };
23970
23971  // -------------------------- jquery -------------------------- //
23972
23973  if ( $ ) {
23974    $.fn.imagesLoaded = function( options, callback ) {
23975      var instance = new ImagesLoaded( this, options, callback );
23976      return instance.jqDeferred.promise( $(this) );
23977    };
23978  }
23979
23980
23981  // --------------------------  -------------------------- //
23982
23983  function LoadingImage( img ) {
23984    this.img = img;
23985  }
23986
23987  LoadingImage.prototype = new EventEmitter();
23988
23989  LoadingImage.prototype.check = function() {
23990    // first check cached any previous images that have same src
23991    var resource = cache[ this.img.src ] || new Resource( this.img.src );
23992    if ( resource.isConfirmed ) {
23993      this.confirm( resource.isLoaded, 'cached was confirmed' );
23994      return;
23995    }
23996
23997    // If complete is true and browser supports natural sizes,
23998    // try to check for image status manually.
23999    if ( this.img.complete && this.img.naturalWidth !== undefined ) {
24000      // report based on naturalWidth
24001      this.confirm( this.img.naturalWidth !== 0, 'naturalWidth' );
24002      return;
24003    }
24004
24005    // If none of the checks above matched, simulate loading on detached element.
24006    var _this = this;
24007    resource.on( 'confirm', function( resrc, message ) {
24008      _this.confirm( resrc.isLoaded, message );
24009      return true;
24010    });
24011
24012    resource.check();
24013  };
24014
24015  LoadingImage.prototype.confirm = function( isLoaded, message ) {
24016    this.isLoaded = isLoaded;
24017    this.emit( 'confirm', this, message );
24018  };
24019
24020  // -------------------------- Resource -------------------------- //
24021
24022  // Resource checks each src, only once
24023  // separate class from LoadingImage to prevent memory leaks. See #115
24024
24025  var cache = {};
24026
24027  function Resource( src ) {
24028    this.src = src;
24029    // add to cache
24030    cache[ src ] = this;
24031  }
24032
24033  Resource.prototype = new EventEmitter();
24034
24035  Resource.prototype.check = function() {
24036    // only trigger checking once
24037    if ( this.isChecked ) {
24038      return;
24039    }
24040    // simulate loading on detached element
24041    var proxyImage = new Image();
24042    eventie.bind( proxyImage, 'load', this );
24043    eventie.bind( proxyImage, 'error', this );
24044    proxyImage.src = this.src;
24045    // set flag
24046    this.isChecked = true;
24047  };
24048
24049  // ----- events ----- //
24050
24051  // trigger specified handler for event type
24052  Resource.prototype.handleEvent = function( event ) {
24053    var method = 'on' + event.type;
24054    if ( this[ method ] ) {
24055      this[ method ]( event );
24056    }
24057  };
24058
24059  Resource.prototype.onload = function( event ) {
24060    this.confirm( true, 'onload' );
24061    this.unbindProxyEvents( event );
24062  };
24063
24064  Resource.prototype.onerror = function( event ) {
24065    this.confirm( false, 'onerror' );
24066    this.unbindProxyEvents( event );
24067  };
24068
24069  // ----- confirm ----- //
24070
24071  Resource.prototype.confirm = function( isLoaded, message ) {
24072    this.isConfirmed = true;
24073    this.isLoaded = isLoaded;
24074    this.emit( 'confirm', this, message );
24075  };
24076
24077  Resource.prototype.unbindProxyEvents = function( event ) {
24078    eventie.unbind( event.target, 'load', this );
24079    eventie.unbind( event.target, 'error', this );
24080  };
24081
24082  // -----  ----- //
24083
24084  return ImagesLoaded;
24085
24086});
24087
24088/**********************************************************************************\
24089 * Set of common java helpers.
24090 * 
24091 * The MIT License
24092 * 
24093 * Copyright (c) 2008 Victor Denisenkov aka Mr.V!T
24094 * 
24095 * Permission  is hereby granted, free of charge, to any person obtaining a copy of
24096 * this  software  and  associated documentation files (the "Software"), to deal in
24097 * the  Software  without  restriction,  including without limitation the rights to
24098 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
24099 * the  Software, and to permit persons to whom the Software is furnished to do so,
24100 * subject to the following conditions:
24101 * 
24102 * The  above  copyright notice and this permission notice shall be included in all
24103 * copies or substantial portions of the Software.
24104 * 
24105 * THE  SOFTWARE  IS  PROVIDED  "AS  IS",  WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
24106 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
24107 * FOR  A  PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
24108 * COPYRIGHT  HOLDERS  BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
24109 * IN  AN  ACTION  OF  CONTRACT,  TORT  OR  OTHERWISE,  ARISING  FROM, OUT OF OR IN
24110 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24111 *
24112 * @package vit-lib
24113 * @author Victor Denisenkov aka Mr.V!T
24114 * @version 1.0
24115\**********************************************************************************/
24116
24117/** //** ----= escapeHtmlTags	 =----------------------------------------------\**//** \
24118*
24119* 	Converts common HTML chars into entities.
24120*
24121*	@param	string	str	- HTML code to convert.
24122*	@return	string		- Escaped HTML.
24123*
24124\**//** ------------------------------------= by Mr.V!T @ Morad Media Inc. =----/** //**/
24125var escapeHtmlTags_chars = {
24126	'&'	: '&amp;',
24127	'<'	: '&lt;',
24128	'>'	: '&gt;',
24129	'"'	: '&quot;',
24130	"'"	: '&#039;'
24131} //OBJECT escapeHtmlTags_chars
24132function escapeHtmlTags(str) {
24133	
24134	//If smth wrong given, just returning it back
24135	if ( typeof(str) !== 'string' ) return str;
24136	
24137	return str.replace(/['"&<>]/g, function (c) {
24138		return escapeHtmlTags_chars[c] || c;
24139	}); //FUNC string.replace.callback
24140} //FUNC escapeHtmlTags
24141
24142function decodeEntities ($str) {
24143	
24144	for ( var $i in escapeHtmlTags_chars )
24145		$str = $str.replace(escapeHtmlTags_chars[$i], $i);
24146		
24147	return $str;
24148} //FUNC decodeEntities
24149
24150function getUrlHash () {
24151	return location.hash.replace(/^#/, '');
24152} //FUNC getUrlHash
24153
24154/** //** ----= fullURL	=----------------------------------------------------------------------------------------------\**//** \
24155*
24156*	Takes any URL and makes it full URL adding host if necesarry.
24157* 	Uses window.location for host value and autodetected scheme.
24158*
24159*	@param		string	$url		- Url to check.
24160* 	@param		bool	[$https]	- Create https url.
24161*
24162*	@return		string			- Full URL
24163*
24164\**//** ----------------------------------------------------------------------------------------------= by Mr.V!T =----/** //**/
24165function fullURL ($url, $https) {
24166
24167	// Preparing desired shceme
24168	if ( isUndefined($https) )
24169		$scheme = window.location.protocol+'//';
24170	else
24171		$scheme = ($https) ? 'https://' : 'http://' ;
24172
24173	// Checking for sceme on string begining
24174	if ( $url.indexOf($scheme) !== 0 )
24175		$url = $scheme+window.location.host+'/'+$url.ltrim(' \/');
24176		
24177	return $url;
24178} //FUNC fullURL
24179
24180/** //** ----= regexMatchAll	=----------------------------------------------------------------------------------------------\**//** \
24181*
24182*	Executes regex on string multiple times, untill no further matches found. Returns array of matches.
24183*	Supports PREG_SET_ORDER and PREG_PATTERN_ORDER similar to php's preg_match_all.
24184*
24185*	@param		string	$str		- String to match.
24186* 	@param		regex	$regex		- Regular expression.
24187*
24188*	@return		array			- All matches as array.
24189*
24190\**//** ----------------------------------------------------------------------------------------------= by Mr.V!T =----/** //**/
24191var PREG_PATTERN_ORDER	= 1;
24192var PREG_SET_ORDER	= 2;
24193function regexMatchAll ($regex, $str, $order) {
24194
24195	// Order by default is PREG_PATTERN_ORDER to mimic php
24196	$order = $order || PREG_PATTERN_ORDER;
24197
24198	var $match; 
24199	var $res = [];
24200	
24201	// Looping through results
24202	// This will create results as for PREG_SET_ORDER flag 
24203	while ( $match = $regex.exec($str) ) {
24204
24205		// Removing some trash, to end up with true array
24206		// It's important to allow [for var in array] code.	
24207		delete($match['index']);
24208		delete($match['input']);
24209		
24210		$res.push($match);
24211	} //WHILE matching
24212	
24213	// Done if no need to reorder
24214	if ( $order === PREG_SET_ORDER )
24215		return $res;
24216
24217	// Reordering in new array if necessary		
24218	var $tmp = [];
24219	for ( var $i in $res )
24220		for ( var $j in $res[$i] ) 
24221			// Pushing matches if subset exists, or creating new one if not
24222			$tmp[$j] ? $tmp[$j].push($res[$i][$j]) : $tmp[$j] = [$res[$i][$j]];
24223	
24224	return $tmp;
24225} //FUNC regexMatchAll
24226
24227if ( isUndefined( jQuery.fn.disableSelection ) ) {
24228
24229jQuery.fn.disableSelection = function() {
24230	// Preparing event name, to allow selectstart or fallback to mousedown
24231	var $eventName = ( isUndefined(this.selectstart) ? 'mousedown' : 'selectstart' ) + '.vDisableSelection';
24232
24233	this.on( $eventName, function( $e ) {
24234		$e.preventDefault();
24235	}); //FUNC onMouseDown
24236
24237} //FUNC disableSelection
24238
24239jQuery.fn.enableSelection = function() {
24240	this.off('.vDisableSelection');
24241} //FUNC enableSelection
24242
24243} //IF no disable seleciton
24244
24245function isDebug () {
24246	
24247	if ( !isEmpty( getCookie('debug') ) )
24248		return true;
24249
24250	if ( location.search.indexOf('debug=1') != -1 )
24251		return true;
24252
24253	return false;	
24254} //FUNC isDebug
24255
24256function isIframe () {
24257	try {
24258		return window.self !== window.top;
24259	} catch (e) {
24260		return true;
24261	}
24262} //FUNC isIframe
24263
24264function implode(glue, obj) {
24265	
24266	// Faster will be call .join if array given
24267
24268	if ( obj instanceof Array )
24269		return obj.join(glue);
24270	
24271	// Or doing manually for object
24272	
24273	var res = '';
24274	
24275	for ( var i in obj ) {
24276		if ( res ) res += glue;
24277		res += obj[i]; 
24278	} //FOR each property
24279	
24280	return res;
24281} //FUNC implode
24282
24283function ucfirst ($str) {
24284	return $str && $str[0].toUpperCase() + $str.slice(1);
24285} //FUNC ucfirst
24286
24287String.prototype.capitalize = function() {
24288	return ucfirst(this);
24289} //FUNC capitalize
24290
24291if ( !String.prototype.startsWith ) {
24292String.prototype.startsWith = function ($needle) {
24293	return this.lastIndexOf($needle, 0) === 0;
24294}} //FUNC startsWith
24295
24296if ( !String.prototype.endsWith ) {
24297String.prototype.endsWith = function ($needle) {
24298	var $lastIndex	= this.lastIndexOf($needle);
24299	return ( $lastIndex != -1 ) && ( $lastIndex + $needle.length == this.length );
24300}} //FUNC endsWith
24301
24302function strRepeat ($str, $multiplier) {
24303	
24304	if ( !$multiplier )
24305		return '';
24306		
24307	var $res = '';
24308	
24309	for ( var $i = 0; $i < $multiplier; $i++ ) {
24310		$res += $str;
24311	} //FOR multiple times
24312	
24313	return $res;
24314} //FUNC strRepeat
24315
24316function instanceName ($var) {
24317	
24318	// Determining instance using basic Object.toString()
24319	$res = Object.prototype.toString.call($var);
24320
24321	// Cutting usual [object ...] from it to get clean instance name
24322	$res = $res.slice(8, -1);
24323	
24324	return $res;
24325} //FUNC instanceName
24326var getClassOf = instanceName;
24327
24328function isClass ($var, $class) {
24329	return getClassOf($var) === $class;
24330} // FUNC isBool
24331
24332function isBool ($var) {
24333	return typeof($var) == 'boolean';
24334} // FUNC isBool
24335
24336function isNumber ($var) {
24337	return typeof($var) == 'number';
24338} //FUNC isNumber
24339
24340function isNumeric ($var) {
24341	return $var !== '' && !isNaN($var) && isFinite($var);
24342} //FUNC isNumeric
24343
24344function isInt ($var) {
24345	return !isNaN($var) && (parseInt($var) == $var);
24346} //FUNC isInt
24347
24348function isFloat ($var) {
24349	return $var !== '' && !isNaN($var) && isFinite($var) && !isInt($var);
24350} //FUNC isFloat
24351
24352function isArray ($var) {
24353	return $var instanceof Array;
24354} //FUNC isArray
24355
24356function isObject ($var, $orArray) {
24357	
24358	// If not object - then false anyway
24359	if ( typeof($var) != 'object' )
24360		return false;
24361		
24362	// If arrays allowed - we have result already	
24363	if ( $orArray )
24364		return true;
24365
24366	// Now need to check if it is array
24367	// Should not be array for strict check
24368	return ( !isArray($var) );
24369	
24370} //FUNC isArray
24371
24372var isString = isStr;
24373function isStr ($var) {
24374	return typeof($var) == 'string';
24375} //FUNC isStr
24376
24377function isIn ($var, $values) {
24378	return $values.indexOf($var) > -1;
24379} //FUNC isIn
24380
24381// Global constant for undefined type checks
24382var UNDEFINED = 'undefined';
24383
24384function isUndefined($var) {
24385	return $var === void 0;
24386} //FUNC isUndefined
24387
24388function isDefined($var) {
24389	return !isUndefined($var);
24390} //FUNC isDefined
24391
24392function setDefault($var, $default) {
24393	return isDefined($var)? $var : $default;
24394} //FUNC isDefined
24395
24396var isset = isSet;
24397function isSet ($var) {
24398	return !isUndefined($var);
24399} //FUNC isSet
24400
24401var empty = isEmpty;
24402function isEmpty ($var) {
24403	
24404	// Fastest way to test
24405	if ( !$var )
24406		return true;
24407	
24408	// '0' means empty too
24409	if ( $var === '0' )
24410		return true;
24411
24412	// Extra test need only for objects, so if it's not object - no need to test more
24413	if ( typeof($var) !== 'object' )
24414		return false;
24415	
24416	// Testing array first (faster test)
24417	if ( isArray($var) )
24418		return $var.length == 0;
24419		
24420	// If still here - its object and need to loop through to check properties
24421	// Any existing property ($i) means it's not empty
24422	for ( var $i in $var )
24423		return false;
24424	
24425	// If not triggered above - that's empty object	
24426	return true;
24427} //FUNC isEmpty
24428
24429function asBool ($var) {
24430	
24431	// Reacting to speciefic strings
24432	if ( $var === 'false' )
24433		return false;
24434		
24435	if ( $var === 'true' )
24436		return true;
24437	
24438	return !isEmpty($var);
24439} //FUNC asBool
24440
24441function isNumber ($var) {
24442	return typeof($var) == 'number';
24443} //FUNC isArray
24444
24445function isFunction ($var) {
24446	return typeof($var) == 'function';
24447} //FUNC isArray
24448
24449function isScalar ($var, $ex) {
24450	
24451	// Testing common scalar types
24452	if ( (/boolean|number|string/).test(typeof($var)) )
24453		return true;
24454
24455	// Extra test assumes that NULL and undefined are scalars		
24456	if ( !$ex )
24457		return false;
24458	
24459	if ( typeof($var) == 'undefined' || $var === null )
24460		return true;
24461		
24462	return false;
24463} //FUNC isScalar
24464
24465function isScalarArray ($var, $ex) {
24466
24467	if ( !isArray($var) )
24468		return false;
24469
24470	// If at least one array item is not scalar - that's not scalar array
24471	for ( var $i = 0; $i < $var.length; $i++ )
24472		if ( !isScalar($var[$i], $ex) )
24473			return false;
24474	
24475	return true;
24476} //FUNC isScalar
24477
24478/** //** ----= arrayPath	=--------------------------------------------------------------------------------------\**//** \
24479*
24480*	Traversing array using key paths. Allows to get or set values.
24481*
24482*	@param		array	$array		- Array to traverse.
24483* 	@param		mixed	$path		- Path to navigate. Either string or array. 
24484* 						 E.g.: 'key1/key2/deepvalue' or ['key1', 'key2', 'deepvalue']
24485* 	@param		mixed	[$value]	- Value to set for setter mode (if omited, will return value instead)
24486* 	@param		string	[$divider]	- Path divider (used only if path is given as string).	 
24487*
24488*	@return		mixed			- Target value in getter mode, or new value in setter.
24489*
24490\**//** ---------------------------------------------------------------------------= by Mr.V!T @ Morad Media Inc. =----/** //**/
24491function arrayPath ($array, $path, $value, $divider) {
24492
24493	$divider = $divider || '/';
24494
24495	// Checking for setter mode
24496	var $isSetter = ( !isUndefined($value) );
24497	
24498	// Splitting path to individual keys
24499	if ( !isArray($path) ) {
24500		$path = $path.split($divider);
24501	} //IF not array given
24502
24503	// Looping down to value in array
24504	// Using refs for recurcive looping
24505	// Thus saving parent for last step
24506	var $var	= $array;
24507	var $parent	= $array;
24508	
24509	for ( var $i in $path ) {
24510		
24511		$key = $path[$i];
24512				
24513		// In setter mode - need to make sure that var is array
24514		// In getter mode - if var is already not array - that's null return
24515		if ( !isObject($var) )
24516			if ( $isSetter )
24517				$var = [];
24518			else
24519				return;		
24520		
24521		// Checking for key presence
24522		// In getter mode unexisting key results to NULL result
24523		// In setter mode - path should be created
24524		// This should be done priror next iteration to avoid source array altering
24525		if ( isUndefined($var[$key]) )
24526			if ( $isSetter )
24527				$var[$key] = {};
24528			else
24529				return;
24530
24531		// Saving ref for next iteration
24532		$parent	= $var;
24533		$var	= $var[$key];
24534		
24535	} //FOR each path key
24536
24537	// At this point ref points to end value
24538	// Can set and return it
24539	if ( $isSetter )
24540		$parent[$key] = $value;
24541	
24542	return $var;
24543	
24544} //FUNC arrayPath
24545
24546if ( !Object.keys ) {
24547    Object.keys = function (obj) {
24548        var keys = [],
24549            k;
24550            
24551        for ( k in obj )
24552            if ( Object.prototype.hasOwnProperty.call(obj, k) )
24553                keys.push(k);
24554
24555        return keys;
24556    }; //FUNC keys
24557} //IF Object have no keys method 
24558
24559/** //** ----= objSize	=----------------------------------------------------------------------------------------------\**//** \
24560*
24561*	Returns object properties count. Usefull as analog of [].length for data objects.
24562*
24563*	@param		object	$obj	- Object to count.
24564*
24565*	@return		int		- Object "size".
24566*
24567\**//** ----------------------------------------------------------------------------------------------= by Mr.V!T =----/** //**/
24568function objSize ($obj) {
24569	
24570	var $res = 0;
24571	for ( $i in $obj)	
24572		if ( $obj.hasOwnProperty($i) )
24573			$res++;
24574	
24575	return $res;
24576} //FUNC objSize
24577
24578/** //** ----= objStep	=----------------------------------------------------------------------------------------------\**//** \
24579*
24580*	Returns object key in "step" away from given key.
24581*
24582*	@param		object	$obj	- Object to search key in
24583*	@param		$key	$key	- Key to step from	
24584*	@param		$step	$step	- Step to count from given key (can be negatve to count backwards)	
24585*
24586*	@return		string		- Found key, or FALSE if smth failed
24587*
24588\**//** ----------------------------------------------------------------------------------------------= by Mr.V!T =----/** //**/
24589function objStep ($obj, $key, $step) {
24590
24591	// Using array for indexing keys, and then searching keys in it
24592	// In case smth goes wrong - FALSE is returned
24593	
24594	var $idx = Object.keys($obj);
24595	
24596	if ( $idx.length == 0 )
24597		return false;
24598	
24599	var $i = $idx.indexOf($key);
24600	
24601	if ( $i < 0 )
24602		return false;
24603
24604	// Stepping from key	
24605	$i += $step;
24606	
24607	if ( $i < 0 || $i >= $idx.length )
24608		return false;
24609
24610	return $idx[$i];
24611} //FUNC objStep
24612
24613/** //** ----= objNext, objPrev	=--------------------------------------------------------------------------------------\**//** \
24614*
24615*	Common use cases of objStep for searching next/prev keys in object
24616*
24617*	@param		object	$obj	- Object to search key in
24618*	@param		$key	$key	- Key to step from	
24619*
24620*	@return		string		- Found key, or FALSE if smth failed
24621*
24622\**//** ----------------------------------------------------------------------------------------------= by Mr.V!T =----/** //**/
24623function objPrev ($obj, $key) {
24624	return objStep($obj, $key, -1);
24625} //FUNC objPrev
24626function objNext ($obj, $key) {
24627	return objStep($obj, $key, 1);
24628} //FUNC objNext
24629
24630/** //** ----= objFirst, objLast	=------------------------------------------------------------------------------\**//** \
24631*
24632*	Returns first/last object key.
24633*
24634*	@param		object	$obj	- Object to search key in
24635*
24636*	@return		string		- Found key, or FALSE if smth failed
24637*
24638\**//** ----------------------------------------------------------------------------------------------= by Mr.V!T =----/** //**/
24639function objFirst ($obj) {
24640
24641	var $idx = Object.keys($obj);
24642	
24643	if ( $idx.length == 0 )
24644		return false;
24645		
24646	return $idx[0];
24647} //FUNC objFirst
24648function objLast ($obj) {
24649	
24650	var $idx = Object.keys($obj);
24651	
24652	if ( $idx.length == 0 )
24653		return false;
24654		
24655	return $idx[$idx.length-1];
24656} //FUNC objLast
24657
24658/** //** ----= isSame	=----------------------------------------------------------------------------------------------\**//** \
24659*
24660* 	Compares two objects given. Designed for data objects.
24661*
24662*	@param	object	x, y		- Objects to compare.
24663*	@param	bool	[oneway]	- Set TRUE to test only by x values (i.e y can be bigger, in other words - if X is in Y)
24664*	@param	bool	[presort]	- Set TRUE to presort incoming arrays. This may 
24665*					  be necessary if comparing arrys in order 
24666*					  independent way. Usefull for values lists.
24667*
24668\**//** ----------------------------------------------------------------------------------------------= by Mr.V!T =----/** //**/
24669function isSame (x, y, oneway, presort) {
24670
24671	// Fast check for nulls / empties
24672	if ( !x || !y ) {
24673
24674		if ( !x && y ) return false;
24675		if ( x && !y ) return false;
24676		
24677		return true;
24678		
24679	} //IF some is null or empty
24680
24681	// IF arrays come - first better check lengths
24682	if ( x instanceof Array && y instanceof Array ) { 
24683
24684		if ( x.length != y.length )
24685			return false;
24686			
24687		if ( presort ) {
24688			x.sort();
24689			y.sort();
24690		} //IF presort seet
24691		
24692	} //IF arrays come
24693	
24694	// First checking properties existance.
24695	// After this check full comparasions will be done in opposite direction
24696
24697	if ( !oneway ) {
24698		for ( var p in y ) {
24699			if ( typeof(x[p]) == 'undefined') 
24700				return false;
24701		} //FOR each property in y
24702	} //IF not one way comparasion
24703
24704	// Type based checking, in opposite comparasion direction 
24705	for ( var p in x ) {
24706
24707		// Checking self properties in oppose object, jsut existance first
24708		if ( typeof(y[p]) == 'undefined' ) 
24709			return false;
24710		
24711		if ( x[p] ) { // Fast symantec check, everything that is logically FALSE - no need to check by type
24712
24713			switch( typeof(x[p]) ) {
24714				
24715				case 'object' :
24716				
24717					// Checking objects recursively
24718					// This allows multidimensional objects checks, 
24719					// But can possibly hang browser. Normally will not.
24720					// ToDo: prevent possible reqursions
24721					if ( !isSame(x[p], y[p], oneway, presort) ) 
24722						return false;  
24723	
24724					break;
24725
24726				case 'function' :
24727				
24728					// Of course toString is not best way to compare functions. 
24729					// But normally same named funcitons should have same source code, 
24730					// and it's anyway better and faster than to full syntax parsing.
24731					// Fast toString comparation will work in most typical cases.
24732					// Just don't use this function if U need to compare functions in objects so much!. 
24733					if ( this[p].toString() != x[p].toString() )
24734						return false;
24735						
24736					break;
24737				default:
24738				
24739					// At last simple check that will usually used :).
24740					if ( x[p] != y[p] )
24741						return false;
24742				
24743			} //SWITCH type of x
24744
24745		} else { //IF is value
24746
24747			// Checking same property on other object
24748			if ( y[p] )
24749				return false;
24750				
24751		} //If false value
24752		
24753	} //FOR each property in x
24754
24755	return true;
24756} //FUNC isSame
24757
24758function isXHR2 () {
24759
24760	if ( !window.FormData ) 
24761		return false;
24762
24763	// Testing for XMLHttpRequest Level 2 
24764	var xhr = new XMLHttpRequest();
24765	if ( !xhr.upload )
24766		return false;		
24767
24768	return true;
24769} //FUNC isXHR2
24770
24771function isFilesAPI () {
24772	
24773	if ( !isXHR2() )
24774		return false;
24775
24776	if ( !(window.File && window.FileList && window.FileReader) ) 
24777		return false;
24778
24779	return true;
24780} //FUNC isFilesAPI
24781
24782function isIos () {
24783	return /iPad|iPhone|iPod/.test(navigator.userAgent) && !window.MSStream;	
24784} //FUNC isIos
24785
24786/** //** ----= isTouchScreen	=--------------------------------------------------------------------------------------\**//** \
24787*
24788*	Tests if device is touch screen enabled device.
24789* 
24790* 	@return bool			- TRUE if device supports touchs screen. (i.e. pointer:coarse from css4 media queries.) 
24791*
24792\**//** ----------------------------------------------------------------------------------------------= by Mr.V!T =----/** //**/
24793function isTouchScreen () {
24794	
24795	var $res = 'ontouchstart' in document.documentElement;
24796	
24797	return $res;
24798	
24799} //FUNC isTouchScreen
24800
24801/** //** ----= isJQ	=----------------------------------------------------------------------------------------------\**//** \
24802*
24803*	Tests if given object is jQuery element
24804*
24805*	@param		MIXED	$val	- Value to check.
24806*
24807*	@return		bool		- Check result.
24808*
24809\**//** ----------------------------------------------------------------------------------------------= by Mr.V!T =----/** //**/
24810function isJQ ($val) {
24811	
24812	// Empty value is not jQuery
24813	if ( !$val )
24814		return false;
24815		
24816	return ($val instanceof jQuery || $val.jquery);
24817} //FUNC isJQ
24818
24819/** //** ----= _jq	=----------------------------------------------------------------------------------------------\**//** \
24820*
24821* 	Ensures that given parameter is valid jQuery object. Avoids jQuery object clone.
24822*
24823*	@param	MIXED	$element	- jQuery selector, DOM element or jQuery object.
24824*	@param	MIXED	[$context]	- Valid jQuery context.
24825*
24826*	@return	object(jQuery)		- jQuery object.
24827*
24828\**//** ----------------------------------------------------------------------------------------------= by Mr.V!T =----/** //**/
24829function _jq ($element, $context) {
24830
24831	if ( !isJQ($element) )
24832		return jQuery($element, $context);
24833
24834	return $element;
24835} //FUNC _jq
24836
24837function formatFileSize (bytes) {
24838
24839	if ( !bytes ) return bytes;	 
24840	 
24841	var labels	= ['b','Kb','Mb','Gb','Tb','Pb','Eb','Zb','Yb'];
24842	var ex		= Math.floor( Math.log(bytes) / Math.log(1024) );
24843	
24844	var res		= bytes / Math.pow(1024, ex);
24845	
24846	return res.toFixed(2) + ' ' + labels[ex]; 
24847} //FUNC formatFileSize
24848
24849/** //** ----= setObjectOptions	=--------------------------------------------------------------------------------------\**//** \
24850*
24851*	Initiates object properties from given data object. Sets properties directly, without test if property really existed.
24852*	Essentially copies one object properties into another.
24853* 
24854* 	@param	object	$object		- Object to apply options to.
24855* 	@param	array	$options	- Options to set.
24856* 
24857* 	@return object			- Returns given object. 
24858*
24859\**//** ----------------------------------------------------------------------------------------------= by Mr.V!T =----/** //**/
24860function setObjectOptions ($object, $options) {
24861	
24862	$options = $options || {};
24863	
24864	// Looping through each option given and setting it as property
24865	for ( var $opt in $options )
24866		$object[$opt] = $options[$opt];
24867	
24868	return $object;
24869} //FUNC setObjectOptions
24870
24871/** //** ----= borderValue	=--------------------------------------------------------------------------------------\**//** \
24872*
24873* 	Ensures that given value is within bounds.
24874*
24875*	@param	MIXED	$element	- jQuery selector, DOM element or jQuery object.
24876*	@param	MIXED	[$context]	- Valid jQuery context.
24877*
24878*	@return	object(jQuery)		- jQuery object.
24879*
24880\**//** ----------------------------------------------------------------------------------------------= by Mr.V!T =----/** //**/
24881function borderValue ($value, $min, $max) {
24882
24883	if ( $value < $min )
24884		$value = $min;
24885		
24886	if ( $value > $max )
24887		$value = $max;
24888		
24889	return $value;
24890	
24891} //FUNC borderValue
24892
24893/** //** ----= touchToMouse	=--------------------------------------------------------------------------------------\**//** \
24894*
24895*	Enables translation of touch events into mouse events. 
24896*	
24897*	@param	DOMElement	$el	- Element to apply to.
24898*
24899*	@return SELF
24900*
24901\**//** ----------------------------------------------------------------------------------------------= by Mr.V!T =----/** //**/
24902jQuery.fn.touchToMouse = function () {
24903
24904	if ( !this.length )
24905		return;
24906
24907	// Enabling touches for each element in set
24908	this.each( function () {
24909		touchToMouse(this);
24910	}); //each element
24911
24912} //FUNC touchToMouse
24913
24914function touchToMouse ( $el ) {
24915
24916	// Using solution found in internet with slight tweaks and adding explanation of what is going on
24917	// Basically - capturing touch events, creating similar mouse event and rethrowing it instead
24918	
24919	// Using event wrapper to simplify code
24920	var $handler = function ( $event ) {
24921		
24922		// Using first element (you can't have multitouch with mouse :) )
24923		var $touchEvent = $event.changedTouches[0];
24924		
24925		// Defining event type from subst table
24926		var $eventType	= {
24927			touchstart	: 'mousedown',
24928			touchmove	: 'mousemove',
24929			touchend	: 'mouseup',
24930		}[$event.type]; //$types
24931		
24932		// Using only translatable events, otherwise - returning
24933		if ( !$eventType )
24934			return;
24935		
24936		// Creating new mouse event
24937		// And initializing with data from captured touch event
24938		var $mouseEvent = document.createEvent('MouseEvent');
24939		$mouseEvent.initMouseEvent(
24940			$eventType, true, true, window, 1,
24941			$touchEvent.screenX, $touchEvent.screenY,
24942			$touchEvent.clientX, $touchEvent.clientY, false,
24943			false, false, false, 0, null
24944		); //initMouseEvent
24945		
24946		// Rethrowing it from same target as original touch
24947		$touchEvent.target.dispatchEvent($mouseEvent);
24948		
24949		// Clearing bubbling
24950		event.preventDefault();		
24951		
24952	} //FUNC hanlder
24953	
24954	// Adding custom handler 
24955	$el.addEventListener('touchstart', $handler, true);
24956	$el.addEventListener('touchmove', $handler, true);
24957	$el.addEventListener('touchend', $handler, true);
24958	$el.addEventListener('touchcancel', $handler, true);	
24959	
24960} //FUNC touchToMouse
24961
24962/** //** ----= observeChange	=--------------------------------------------------------------------------------------\**//** \
24963*
24964*	Adds MutationObserver to observer changes in input value. Triggers change when value updates.
24965*	Good use for hidden and text based inputs, to have proper/imideate change.
24966*	
24967*	@param	DOMElement	$el	- Element to apply to.
24968*
24969*	@return SELF
24970*
24971\**//** ----------------------------------------------------------------------------------------------= by Mr.V!T =----/** //**/
24972jQuery.fn.observeChange = function () {
24973
24974	if ( !this.length )
24975		return;
24976
24977	// Enabling touches for each element in set
24978	this.each( function () {
24979		observeChange(this);
24980	}); //each element
24981
24982} //FUNC observeChange
24983
24984function observeChange ( $el ) {
24985
24986	// Using solution found in internet with slight tweaks and adding explanation of what is going on
24987	
24988	// Creating MutationObserver (compatable with older webkit), and watching value attribute of element given (supposedly input)
24989	// Once value changed - triggering change with jQuery
24990	
24991	// Preparing class, to be compatable with older webkit
24992	// Doing it globally, does not hurt anything
24993	MutationObserver	= window.MutationObserver || window.WebKitMutationObserver;
24994
24995	// Saving observer in input, just in case
24996	// Adding callback that will check what's changed
24997	$el.vObserver	= new MutationObserver( function ( $mutations, $observer ) {
24998		
24999		// Should be only one mutation really
25000		// Interested in value change
25001		if ( $mutations[0].attributeName == 'value') {
25002			
25003			// Using mutation target to trigger
25004			// Since it's basically a closure - can reference give element directly
25005			// Wrapping with jQuery if necessary
25006			_jq($el).trigger('change');
25007			
25008		} //IF value changed					
25009	
25010	}); //FUNC MutationObserver.callback			
25011	
25012	// Watching element attributes
25013	$el.vObserver.observe($el, {
25014		attributes	: true,
25015	}); //observe
25016	
25017	// Done here
25018	
25019} //FUNC observeChange
25020
25021/** //** ----= vEventObject	=--------------------------------------------------------------------------------------\**//** \
25022*
25023* 	Extends given object to accept events callbacks. Allows events registering and chain calling later. Callback will 
25024*	run within object's context and accept parameters given to event trigger.
25025*
25026*	@param	array	types	- List of event types to create.
25027*
25028*	@param	object	obj	- Object to extend.
25029*
25030*	@return	object		- Modified object.
25031*
25032\**//** ----------------------------------------------------------------------------------------------= by Mr.V!T =----/** //**/
25033function vEventObject (types, obj) {
25034
25035	obj = obj || {};
25036
25037	// Creating callbacks storage in parent
25038	if ( isEmpty(obj['__events']) )
25039		obj['__events'] = {};
25040	
25041// ---- BIND/RUNNER ----	
25042	
25043	/** //** ----= _runevent	=------------------------------------------------------------------------------\**//** \
25044	*
25045	* 	For Internal Use. Event runner, used to call events, or set new ones (in case if callback function given).
25046	*
25047	* 	@param	string	type	- Event type to use.
25048	* 
25049	*	@return	MIXED			- Mixed return.
25050	*		SELF			- If setting event.
25051	*		bool			- Boolean FALSE in even return - interrupts chain, and also is returned. 
25052	*					  TRUE is just returned.
25053	*		VOID			- Other cases.
25054	*
25055	\**//** --------------------------------------------------------------------------------------= by Mr.V!T =----/** //**/
25056	obj._runevent = function ( type, args ) {
25057
25058		// Setting callback if given as first argument
25059		if ( args[0] && typeof(args[0]) == 'function' ) {
25060			this.__events[type].push(args[0]);
25061			return obj;
25062		} //IF callback set
25063		
25064		var res = false;
25065		
25066		// If no callback given (getter mode) - running all calbacks of that type, cb should contain arguments then
25067		for ( var func in this.__events[type] ) {
25068			var eres = this.__events[type][func].apply(obj, args);
25069
25070			// False in result means even chain interruption, returning it to parent also 
25071			if ( eres === false ) 
25072				return false;
25073			
25074			// True in one of events means smth for caller, we'll return it later
25075			if ( eres === true ) 
25076				res = true;
25077			
25078		} //FOR each callback
25079
25080		// If someone return true - we notify parent about, else - undefined return
25081		if ( res ) 
25082			return res;
25083
25084	} //FUNC _runevent
25085
25086// ---- UNBIND ----
25087
25088	/** //** ----= unbind	=--------------------------------------------------------------------------------------\**//** \
25089	*
25090	* 	Event remover. Unbings given event type, or given callback if specified. If no parameters specified - 
25091	*	unbinding all events (usefull to get clean object).
25092	*
25093	* 	@param	string		[type]	- Event type to unbind.
25094	*	@param	function	[fn]	- Callback to unbind within type.
25095	* 
25096	*	@return	SELF
25097	*
25098	\**//** --------------------------------------------------------------------------------------= by Mr.V!T =----/** //**/
25099	obj.unbind = function (type, fn) {
25100		
25101		var $this = this;
25102		
25103		// Event clearing helper
25104		function unbindEvent (e) {
25105			
25106			// Deleting only speciefic events, skipping body ones
25107			for ( var i = 0; i < e.length; i++ ) {
25108			
25109				// Skipping body ones
25110				if ( e[i]._bodyeventfunction ) continue;
25111				
25112				// Skipping all non given (if some given)
25113				if ( fn && e[i] !== fn ) continue;
25114				
25115				e.splice(i, 1);
25116				
25117			} //FOR each callback
25118			
25119		}; //FUNC unbindEvent
25120	
25121		// If type specified - cleaning only that type, otherwise - all of them
25122		if ( type )
25123			unbindEvent(this.__events[type]);
25124		else
25125			for ( var i in this.__events )
25126				unbindEvent(this.__events[i]);
25127
25128		return obj;
25129		
25130	}; //FUNC obj.unbind
25131
25132// ---- SHORTCUTS ----	
25133
25134	// Creating shortcut for each event
25135	for ( var ev in types ) {
25136
25137		var type = types[ev];
25138		
25139		// For each type we have own array with callbacks
25140		if ( isEmpty(obj.__events[type]) )
25141			obj.__events[type] = [];
25142		
25143		// Existing event functions became first events
25144		if ( isFunction(obj[type]) ) {
25145			
25146			// Skipping event functions
25147			if ( obj[type]._runeventfunction )
25148				continue;
25149			else {
25150				// Marking as body function - those can't be unbind
25151				obj[type]._bodyeventfunction = 1;
25152				obj._runevent(type, [obj[type]]);
25153			} //IF own function
25154			
25155		} //IF function already exists in body 
25156		
25157		// Creating event trigger function in parent for each type
25158		obj[type] = function (t) { return function () { return this._runevent(t, arguments);};}(type);
25159
25160		// Marking as event function to recognise later 
25161		obj[type]._runeventfunction = 1;
25162		
25163	} //FOR each type
25164	
25165	return obj;
25166} //FUNC vEventObject
25167
25168/** //** ----= PromiseEx	=--------------------------------------------------------------------------------------\**//** \
25169*
25170* 	Special promise version with outside controllers (custom resolve/reject methods).
25171*
25172*	@param	function	$executor	- Promise executor.
25173*
25174*	@return	object				- Modified promise.
25175*
25176\**//** ----------------------------------------------------------------------------------------------= by Mr.V!T =----/** //**/
25177var PromiseEx = function ( $executor ) {
25178	
25179	// Saving controls outisde
25180	var $resolve, $reject;
25181	
25182	var $res = new Promise ( function (resolve, reject) {
25183
25184		// Saving resolve and reject for later reuse
25185		$resolve = resolve;
25186		$reject = reject;
25187	
25188		// Running executor as normal, it might resolve/reject as necessary
25189		if ( isFunction($executor) )
25190			$executor(resolve, reject);
25191		
25192		// Done here
25193		
25194	}); //Promise
25195	
25196	// Saving calls in object itself
25197	$res.doResolve	= $resolve;
25198	$res.doReject	= $reject;
25199	
25200	return $res;
25201	
25202} //promiseEx
25203
25204/** //** ----= reloadCSS		 =--------------------------------------\**//** \
25205*
25206* 	Forces browser to reload CSS sheets dynamically.
25207*	
25208*	@param	string	url	- CSS sheet URL.
25209*
25210\**//** ------------------------------------= by Mr.V!T @ Morad Media Inc. =----/** //**/
25211function reloadCSS (url) {
25212	
25213	var salt	= randomString();
25214	var salt_url	= url + '?reload=' + salt;
25215	
25216	var new_sheet = jQuery('<link rel="stylesheet" href="' + salt_url + '" type="text/css" />');
25217	var old_sheet = jQuery('LINK[href*="' + url + '"]');
25218
25219	if ( document.createStyleSheet ) {
25220		document.createStyleSheet(salt_url);
25221	} else { //IF IE 
25222		jQuery('HEAD').append( new_sheet );
25223	} //IF normal browsers	
25224
25225	// Removing old sheet if present.
25226	setTimeout( function () { old_sheet.remove(); }, 1000);
25227	
25228} //FUNC reloadCSS
25229
25230/** //** ----= countElements	 =----------------------------------------------\**//** \
25231*
25232* 	Returns overal page's elements count. Usefull for optimization.
25233*
25234*	@param	jQuery	el	- Count elements within give element. 
25235*
25236*	@return int		- Elements count.	
25237*
25238\**//** ------------------------------------= by Mr.V!T @ Morad Media Inc. =----/** //**/
25239function countElements (el) {
25240	
25241	if ( !el )
25242		return document.getElementsByTagName('*').length;
25243		
25244	return jQuery(el).find('*').length;
25245} //FUNC countElements
25246
25247function randomString(type, length) {
25248	
25249	if ( !type ) type = 'alnum';
25250	if ( !length ) length = 8;
25251	
25252	var chars = '';
25253	
25254	switch ( type.toLowerCase() ) {
25255		case 'alnum'	: chars = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
25256			break;
25257		case 'alpha'	: chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
25258			break;
25259		case 'hex'	: chars = '0123456789ABCDEF';
25260			break;
25261		case 'caps'	: chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
25262			break;
25263		case 'capnum'	: chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
25264			break;
25265		case 'numeric'	: chars = '0123456789';
25266			break;
25267		case 'nozero'	: chars = '123456789';
25268			break;
25269		case 'keyb'	: chars = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ :;,.\'"\|/?[]{}()<>~`!@#$%^&*-=_+';
25270			break;
25271			
25272		default		: chars = type;
25273	} //SWITCH $type
25274
25275	if ( !chars ) return '';
25276	
25277	var str = '';
25278	
25279	for (var i = 0; i < length; i++) {
25280		var rnum = Math.floor(Math.random() * chars.length);
25281		str += chars.substring(rnum, rnum + 1);
25282	}
25283	
25284	return str;
25285} //FUNC randomString
25286
25287/** //** ----= newSN	 =---------------------------------------------------------------------------------------------\**//** \
25288*
25289*	Generates new random SN. SN is usually capnum unique string of 16 chars, starting from give char.
25290*
25291*	@param		string	[$prefix]	- Limit SN to start only with this char.
25292*	@param		string	[$length]	- SN length, 16 by default.
25293* 
25294*	@return		string			- Random SN string.
25295*
25296\**//** ----------------------------------------------------------------------------------------------= by Mr.V!T =----/** //**/
25297function newSN ($prefix, $length) {
25298	
25299	$prefix = $prefix || '';
25300	$length	= $length || 16;
25301	
25302	return ($prefix + randomString('capnum', $length)).substring(0, $length);
25303} //FUNC newSN
25304
25305/** //** ----= fileName		 =----------------------------------------------\**//** \
25306*
25307* 	Extracts file name from full path.
25308* 	
25309* 	@param	string	path	- Full path and file name.
25310*	@return	string		- File name.
25311*
25312\**//** ------------------------------------= by Mr.V!T @ Morad Media Inc. =----/** //**/
25313function fileName (path) {
25314	if ( !path ) return '';
25315	return path.replace(/^.*\\/, '');
25316} //FUNC fileName
25317
25318/** //** ----= fileExt		 =----------------------------------------------\**//** \
25319*
25320* 	Extracts file extension from file name.
25321* 	
25322* 	@param	string	file	- Full path and file name.
25323*	@return	string		- File name.
25324*
25325\**//** ------------------------------------= by Mr.V!T @ Morad Media Inc. =----/** //**/
25326function fileExt (file) {
25327	var tmp	= file.lastIndexOf('.');
25328	
25329	if ( !tmp ) return false;
25330	
25331	return file.substr(tmp+1, file.length); 
25332	
25333} //FUNC fileExt
25334
25335/** //** ----= fileType	 =---------------------------------------------------------------------------------------------\**//** \
25336*
25337*	Detects one of general file types: image, doc, video, music etc.
25338*
25339*	@param		string	$file		- File name to test.
25340* 
25341*	@return		string			- File type.
25342*
25343\**//** ----------------------------------------------------------------------------------------------= by Mr.V!T =----/** //**/
25344function fileType ( $file ) {
25345
25346	// Listing known file types by extensions
25347	// Simplified version. Complete version should be generated by php from mimes. To be implemented.
25348	var $types	= {
25349		
25350		// ToDo: add executables
25351		
25352		'php'	: 'script',
25353		'php4'	: 'script', 
25354		'php5'	: 'script', 
25355		'js'	: 'script', 
25356		'css'	: 'script', 
25357		'html'	: 'script', 
25358		'htm'	: 'script', 
25359		'html5'	: 'script', 
25360		'html4'	: 'script', 
25361		'cgi'	: 'script',
25362		
25363		'jpeg'	: 'image',
25364		'jpg'	: 'image',
25365		'jpe'	: 'image',
25366		'png'	: 'image',
25367		'bmp'	: 'image',
25368		'gif'	: 'image',
25369		'webp'	: 'image',
25370		'svg'	: 'image',
25371		'tiff'	: 'image',
25372		'tif'	: 'image',
25373		
25374		'avi'	: 'video', 
25375		'flv'	: 'video', 
25376		'f4v'	: 'video', 
25377		'mpeg'	: 'video', 
25378		'mpg'	: 'video', 
25379		'mpe'	: 'video', 
25380		'mov'	: 'video', 
25381		'mp4'	: 'video', 
25382		'm4v'	: 'video', 
25383		'wmv'	: 'video', 
25384		'ogv'	: 'video', 
25385		'webm'	: 'video', 
25386		'mkv'	: 'video', 
25387		'qt'	: 'video', 
25388		'movie'	: 'video',
25389		
25390		'mp2'	: 'audio',
25391		'mp3'	: 'audio',
25392		'wav'	: 'audio',
25393		'wma'	: 'audio',
25394
25395		'doc'	: 'doc', 
25396		'docx'	: 'doc', 
25397		'txt'	: 'doc', 
25398		'text'	: 'doc', 
25399		'xls'	: 'doc', 
25400		'xlsx'	: 'doc', 
25401		'pdf'	: 'doc',
25402		
25403	}; //$ex
25404
25405	// Getting file extension, need it in lowercase
25406	var $ext	= fileExt($file) || '';
25407	$ext		= $ext.toLowerCase();
25408
25409	// Checking it it's known
25410	if ( $types[$ext] )
25411		return $types[$ext];
25412
25413	// Nothing to return if unknown
25414	return '';
25415
25416} //FUNC fileType
25417
25418function isFileScript ( $file ) {
25419	return fileType($file) === 'script';
25420} //FUNC isFileScript
25421function isFileImage ( $file ) {
25422	return fileType($file) === 'image';
25423} //FUNC isFileImage
25424function isFileVideo ( $file ) {
25425	return fileType($file) === 'video';
25426} //FUNC isFileVideo
25427function isFileAudio ( $file ) {
25428	return fileType($file) === 'audio';
25429} //FUNC isFileAudio
25430function isFileDoc ( $file ) {
25431	return fileType($file) === 'doc';
25432} //FUNC isFileDoc
25433
25434
25435/*--------------------------------------------------------------------------------*\
25436|	Humanize
25437|----------------------------------------------------------------------------------
25438| Ensures that number will be displayed as normal value (fix for JavaScript numbers)
25439| - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
25440|	Params:
25441|		[base]	int	- Base value to round value to.
25442|	Return:
25443|			string	- Human looking number rounded to certain base.
25444\*--------------------------------------------------------------= by Mr.V!T =-----*/
25445Number.prototype.Humanize = Number.prototype.humanize = function (base) {
25446	
25447	base = base || 10;
25448	
25449	var tmp = this.toFixed(base) + '';
25450	return tmp;
25451	
25452	var res = tmp.rtrim('0.');
25453	if ( res == '' ) res = '0';
25454	
25455	return res;
25456} //FUNC Humanize
25457
25458/*--------------------------------------------------------------------------------*\
25459|	indexOf
25460|----------------------------------------------------------------------------------
25461| Fixes indexOf for IE6.
25462| - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
25463|	Params:
25464|		obj		MIXED	- Item to search.
25465|		fromIndex	integer	- Search starting from index.
25466|	Return:
25467|				integer	- Item position in array, or -1 if not found.
25468\*--------------------------------------------------------------= by Mr.V!T =-----*/
25469/**/
25470if ( !Array.prototype.indexOf )
25471Array.prototype.indexOf = function (obj, fromIndex) {
25472		
25473	if ( !fromIndex ) fromIndex = 0;
25474	
25475	for ( var i in this )
25476		if ( this[i] === obj ) return i;
25477		
25478	return -1;
25479} //FUNC indexOf
25480/**/
25481
25482/*--------------------------------------------------------------------------------*\
25483|	arrayJoin
25484|----------------------------------------------------------------------------------
25485| Extended join for arrays.
25486| - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
25487|	Params:
25488|		array		array	- Array to process.
25489|		separator	string	- String to use as separator.
25490|		include_keys	bool	- Set TRUE to encode keys in result string.
25491|	Return:
25492|				string	- Array as string.
25493\*--------------------------------------------------------------= by Mr.V!T =-----*/
25494function arrayJoin (array, separator, include_keys) {
25495	
25496	if ( !separator ) separator = ',';
25497	
25498	var res = '';
25499	
25500	var j = 0;
25501	
25502	for ( var i in array ) {
25503		
25504		if ( typeof(array[i]) == 'function' ) continue;
25505		
25506		if ( res ) res += separator;
25507		
25508		if ( include_keys ) {
25509			
25510			if ( i != j ) 
25511				res += i + ' = ';
25512			
25513		} //IF need to include keys
25514		
25515		res += array[i];
25516		j++;	
25517		
25518	} //FOR each item
25519		
25520	return res;
25521} //FUNC arrayJoin
25522
25523/*--------------------------------------------------------------------------------*\
25524|	arraySplit
25525|----------------------------------------------------------------------------------
25526| Splits array. Extended version with keys support.
25527| - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
25528|	Params:
25529|		string		string	- String to process.
25530|		separator	string	- String to use as separator.
25531|		auto_tirm	string	- Allow to trim values on split.
25532|	Return:
25533|				array	- Array with values.
25534\*--------------------------------------------------------------= by Mr.V!T =-----*/
25535function arraySplit (string, separator, auto_tirm) {
25536	
25537	var res	= [];
25538	var tmp = string.split(separator);
25539	
25540	for ( var i in tmp ) {
25541		
25542		var pos = tmp[i].indexOf('=');
25543
25544		if ( pos != -1 ) {
25545			
25546			var key = tmp[i].substr(0, pos);
25547			if ( auto_tirm ) key = key.trim();
25548			
25549			var val = tmp[i].substr(pos+1);
25550			if ( auto_tirm ) val = val.trim();
25551			
25552			res[key] = val;
25553			
25554		} else { //IF eq symbol found
25555		
25556			if ( auto_tirm ) tmp[i] = tmp[i].trim();
25557			res.push(tmp[i]);
25558			
25559		} //IF single value
25560		
25561	} //FOR each value
25562	
25563	return res;
25564} //FUNC arraySplit
25565
25566String.prototype.limit = function(l, append) {
25567
25568	if ( this.length <= l ) 
25569		return this.toString();
25570	
25571	append = append || '&hellip;';
25572	al = append.substr(0, 1) == '&' ? 1 : append.length;
25573	
25574	return this.substr(0, l - al) + append;
25575} //FUNC limit
25576
25577String.prototype.ucfirst = function() {
25578
25579	// Nothing to do with empty string	
25580	if ( !this.length ) return this;
25581	
25582	return this.charAt(0).toUpperCase() + this.substr(1);
25583} //FUNC ucfirst
25584
25585/*--------------------------------------------------------------------------------*\
25586|	escape
25587|----------------------------------------------------------------------------------
25588| Escapes string for using in REGex.
25589| - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
25590|	Params:
25591|		NONE
25592|	Return:
25593|		NULL
25594\*--------------------------------------------------------------= by Mr.V!T =-----*/
25595String.prototype.escape = function() {
25596	return this.replace(/([\\\.\+\*\?\[\^\]\$\(\)\{\}\=\!<>\|\:])/g, '\\$1');
25597} //FUNC escape
25598
25599/*--------------------------------------------------------------------------------*\
25600|	_trim
25601|----------------------------------------------------------------------------------
25602| Base function for trimming strings.
25603| ***TNX*** Ideas taken from phpjs.org
25604| - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
25605|	Params:
25606|		[charlist]	string	- Optional characteres to trim.
25607|		[left]		bool	- Trim left.
25608|		[right]		bool	- Trim right.
25609|	Return:
25610|				string	- Trimmed string.
25611\*--------------------------------------------------------------= by Mr.V!T =-----*/
25612String.prototype._trim = function(charlist, left, right) {
25613
25614	var str = this;
25615	
25616	if ( !str )
25617		return this;
25618	
25619	charlist = (charlist) ? charlist.escape() : ' \\n\\t\\s\u00A0';
25620	
25621	if ( left ) str	= str.replace(new RegExp('^[' + charlist + ']+', 'gm'), '');
25622	if ( right ) str = str.replace(new RegExp('[' + charlist + ']+$', 'gm'), '');
25623	
25624	return str;
25625} //FUNC _trim
25626
25627/*--------------------------------------------------------------------------------*\
25628|	ltrim
25629|----------------------------------------------------------------------------------
25630| Strips whitespace from the beginning of a string.
25631| - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
25632|	Params:
25633|		[charlist]	string	- Optional characteres to trim.
25634|	Return:
25635|				string	- Trimmed string.
25636\*--------------------------------------------------------------= by Mr.V!T =-----*/
25637String.prototype.ltrim = function(charlist) {
25638	return this._trim(charlist, 1, 0);
25639} //FUNC ltrim
25640
25641/*--------------------------------------------------------------------------------*\
25642|	rtrim
25643|----------------------------------------------------------------------------------
25644| Removes trailing whitespace.
25645| - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
25646|	Params:
25647|		[charlist]	string	- Optional characteres to trim.
25648|	Return:
25649|				string	- Trimmed string.
25650\*--------------------------------------------------------------= by Mr.V!T =-----*/
25651String.prototype.rtrim = function(charlist) {
25652	return this._trim(charlist, 0, 1);
25653} //FUNC rtrim
25654
25655/*--------------------------------------------------------------------------------*\
25656|	trim
25657|----------------------------------------------------------------------------------
25658| Removes whitespaces from both sides.
25659| - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
25660|	Params:
25661|		[charlist]	string	- Optional characteres to trim.
25662|	Return:
25663|				string	- Trimmed string.
25664\*--------------------------------------------------------------= by Mr.V!T =-----*/
25665String.prototype.trim = function(charlist) {
25666	return this._trim(charlist, 1, 1);
25667} //FUNC trim
25668
25669/*--------------------------------------------------------------------------------*\
25670|	xtrim
25671|----------------------------------------------------------------------------------
25672| Very fast function for trimming strings.
25673| ***TNX*** Big thanx for Steve at steves_list (at) hotmail.com. 
25674| ***TNX*** http://blog.stevenlevithan.com/
25675| - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
25676|	Params:
25677|		NONE
25678|	Return:
25679|		NULL
25680\*--------------------------------------------------------------= by Mr.V!T =-----*/
25681String.prototype.xtrim = function() {
25682	var str = this;
25683	str = str.replace(/^\s+/, '');
25684	for (var i = str.length - 1; i >= 0; i--) {
25685		if (/\S/.test(str.charAt(i))) {
25686			str = str.substring(0, i + 1);
25687			break;
25688		}
25689	}
25690	return str;
25691} //FUNC xtrim
25692
25693/*--------------------------------------------------------------------------------*\
25694|	multiClean
25695|----------------------------------------------------------------------------------
25696| Cleans double repeated characters. E.g. doublespaces.
25697| - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
25698|	Params:
25699|		[character]	string	- Optional character to clean. 
25700|	Return:
25701|				string	- Result string.
25702\*--------------------------------------------------------------= by Mr.V!T =-----*/
25703String.prototype.multiClean = function(character) {
25704	
25705	character = character || ' '; 
25706	
25707	return this.replace(new RegExp('[' + character.escape() + ']{2,}', 'g'), character);	
25708} //FUNC multiClean
25709
25710/*--------------------------------------------------------------------------------*\
25711|	toSafe
25712|----------------------------------------------------------------------------------
25713| Generates safe value from give string. I.E. safe to use as variable names.
25714| - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
25715|	Params:
25716|		[separator]	string	- Optional characteres to use as word 
25717|					separator.
25718|	Return:
25719|				string	- Result string.
25720\*--------------------------------------------------------------= by Mr.V!T =-----*/
25721String.prototype.toSafe = function(separator) {
25722
25723	if ( !separator )
25724		separator = '_'; 
25725	
25726	var res = this;
25727	res = res.replace(/[^a-zA-Z0-9_]/g, separator);
25728	res = res.multiClean(separator);
25729	res = res.trim(separator);
25730	
25731	return res;
25732} //FUNC toSafe
25733
25734/*--------------------------------------------------------------------------------*\
25735|	compilePath
25736|----------------------------------------------------------------------------------
25737| Compile path from several pieces using '/' as glue.
25738| - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
25739|	Params:
25740|		*	string	- One or several path pieces.
25741|	Return:
25742|			string	- Valid path.
25743\*--------------------------------------------------------------= by Mr.V!T =-----*/
25744function compilePath () {
25745	
25746	var len = arguments.length;
25747	if ( len == 1 ) return arguments[0];
25748	
25749	var res = (arguments[0]+'').rtrim('/');
25750	
25751	for (var i = 1; i < len; i++ ) {
25752		res += '/' + (arguments[i]+'').ltrim('/');
25753	} //FOR each argument
25754	
25755	return res;
25756} //FUNC compilePath
25757var compileUrl = compilePath;
25758
25759/** //** ----= arraySelectKey	 =----------------------------------------------\**//** \
25760*
25761*	Chooses existing key from list given in associated array.
25762*
25763*	@param	array	array	- Source array.
25764* 
25765* 	@param	MIXED	keys	
25766* 		array		- Array with possible keys. 
25767* 		string		- Single key for case insensitive search or regEx 
25768* 				  mask for possible keys (slower).
25769*
25770*	@param	bool	[regex]	- Search mode for string key.
25771*
25772*	@return	MIXED			
25773* 		string		- Existing key.
25774* 		bool		- FALSE if none exists.
25775*
25776\**//** ------------------------------------= by Mr.V!T @ Morad Media Inc. =----/** //**/
25777function arraySelectKey (array, keys, regex) {
25778
25779	if ( typeof(keys) == 'object' ) {
25780
25781		for ( var i = 0; i < keys.length; i++ ) {
25782
25783			if ( array[keys[i]] != undefined ) return keys[i];
25784
25785		} //FOR each key
25786
25787	} else { //IF searching by array
25788
25789		var reg	= keys;
25790		if ( !regex )
25791			reg = new RegExp('^' + keys.escape() + '$', 'i');
25792
25793		for ( var key in array ) {
25794			
25795		//	__(key);
25796			
25797			if ( reg.test(key) ) return key;
25798		} //FOR each
25799
25800	} //IF searshing by regex
25801
25802	return undefined;
25803} //FUNC arraySelectKey
25804
25805/** //** ----= arraySelectValue	 =----------------------------------------------\**//** \
25806*
25807*	Chooses value by one of existing keys given.
25808*
25809*	@param	array	array		- Source array.
25810*	@param	array	keys		- Array with possible keys.
25811*	@param	MIXED	[default]	- Default value to use if nothing found.
25812*
25813*	@return	MIXED			- Found value or default if none found.
25814*
25815\**//** ------------------------------------= by Mr.V!T @ Morad Media Inc. =----/** //**/
25816function arraySelectValue (array, keys, def) {
25817
25818	var key = arraySelectKey(array, keys);
25819
25820	if ( !key || !array[key] == undefined ) return def;
25821
25822	return array[key];
25823} //FUNC arraySelectValue
25824
25825/*--------------------------------------------------------------------------------*\
25826|	arrayToPost
25827|----------------------------------------------------------------------------------
25828| Prepares multidimentional array to post in native PHP format.
25829| - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
25830|	Params:
25831|		array	object	- Common JavaScript array object.
25832|	Return:
25833|			array	- Array in POST format.
25834\*--------------------------------------------------------------= by Mr.V!T =-----*/
25835function arrayToPost (array, prefix) {
25836	
25837	var res = {};
25838	
25839	for ( i in array ) {
25840		
25841		var key = ( prefix )? prefix + '[' + i + ']' : i;
25842		
25843		if ( typeof(array[i]) == 'object' ) {
25844
25845			var sub = arrayToPost(array[i], key);
25846			
25847			for ( j in sub ) res[j] = sub[j];
25848			
25849		} else { //IF sub array
25850			res[key] = array[i];
25851		} //IF other type
25852		
25853	} //FOR each array
25854	
25855	return res;
25856} //FUNC arrayToPost
25857
25858/** //** ----= submitUrl	=--------------------------------------------------------------------------------------\**//** \
25859*
25860*	Submits given data as POST/GET to given url. Literally simulates form submit.
25861*
25862*	@param		string	$url		- Url to submit to.
25863*	@param		object	$data		- Data to submit.
25864*	@param		string	[$blank]	- Set TRUE to open in new tab.
25865*
25866*	@return		bool			- Always FALSE.
25867*
25868\**//** ----------------------------------------------------------------------------------------------= by Mr.V!T =----/** //**/
25869function submitUrl($url, $data, $blank) {
25870	
25871	$method	= 'post';	// No real need to have method optional
25872	$data	= $data || {};
25873	$target	= '';
25874	 
25875	if ( $blank )
25876		$target = ' target="_blank"';
25877	
25878	// Generating form html as string and appending it to body
25879	// Each data will be hidden input in form
25880	var $f = '<form method="' + $method + '" action="' + $url + '"' + $target + ' style="display:none">';
25881	
25882	for ( $i in $data ) {
25883		
25884		$f += '<input type="hidden" name="' + $i + '" value="' + $data[$i] + '" />';
25885		
25886	} //FOR each data
25887	
25888	$f += '</form>';
25889	
25890	// Appending form, getting it's element, and submiting
25891	var $el = jQuery($f).appendTo('BODY').submit();
25892
25893	return false;	
25894	
25895} //FUNC submitUrl
25896
25897/** //** ----= parseVariables	=--------------------------------------------------------------------------------------\**//** \
25898*
25899*	Parses {varables} in a template. Supporting complex vairables like {var-sub} and {var.sub}
25900*
25901*	@param		string	$str		- Template to parse.
25902*	@param		object	$vars		- Data object with variable to parse.
25903*	@param		bool	[$clean]	- Set TRUE to cleanup unused variables from template (default behavior).
25904*	@param		string	[$wrap]		- Brackets pair to use on vairables. '{}' by default. Use '[]' for bb codes. 
25905*
25906*	@return		string			- Parsed template.
25907*
25908\**//** ----------------------------------------------------------------------------------------------= by Mr.V!T =----/** //**/
25909function parseVariables ($str, $vars, $clean, $wrap) {
25910	
25911	// Using {vars} by default
25912	$wrap = $wrap || '{}';
25913	
25914	// Nothing to do if string is empty
25915	if ( !$str )
25916		return $str;
25917		
25918	if ( isUndefined($clean) ) 
25919		$clean = true;
25920	
25921	// Preparing wrap pairs
25922	// Assuming 2 char pair like '{}' or '[]'
25923	if ( $wrap.length != 2 )
25924		throw('Invalid var wrapper');
25925	
25926	// Preparing 2 wrap variants: normal and escaped one
25927	// Oftenly strings end up as url escaped strings, escaped by parsers
25928	// Thus just searching this too
25929	var $l  = $wrap.charAt(0);
25930	var $r  = $wrap.charAt(1);
25931	var $xl = '%' + $wrap.charCodeAt(0).toString(16).toUpperCase();
25932	var $xr = '%' + $wrap.charCodeAt(1).toString(16).toUpperCase();
25933	
25934	// Looping though all vars and replacing given variables	
25935	for ( var $i in $vars ) {
25936
25937		// Preparing value separately, in case need to modify something about it
25938		var $value	= $vars[$i];
25939
25940		// Can display simple arrays as lists
25941		if ( isArray($value) )
25942			$value = $value.join(',');
25943
25944		// Skipping anything that can't be placed on template
25945		if ( !isStr($value) && !isNumber($value) )
25946			continue;
25947		
25948		// Searching both vars variants
25949		$str = $str.replace( new RegExp( ($l + $i + $r).escape(), 'ig'), $value);
25950		$str = $str.replace( new RegExp( ($xl + $i + $xr).escape(), 'ig'), $value);
25951		
25952	} //FOR each var
25953
25954	if ( $clean ) {
25955
25956		// Now cleaning unused, both normal and escaped
25957		$str = $str.replace( new RegExp( $l.escape() + '[\\w\\.-]+' + $r.escape(), 'ig'), '');
25958		$str = $str.replace( new RegExp( $xl.escape() + '[\\w\\.-]+' + $xr.escape(), 'ig'), '');
25959
25960	} //IF autoClean
25961	
25962	return $str;
25963		
25964} //FUNC parseVariables
25965
25966/** //** ----= arrayToTemplate	=--------------------------------------------------------------------------------------\**//** \
25967*
25968*	Parses {varables} in a template. PHP counterpart alias to parseVariables.
25969*
25970*	@param		object	$vars		- Data object with variable to parse.
25971*	@param		string	$str		- Template to parse.
25972*	@param		bool	[$clean]	- Set TRUE to cleanup unused variables from template (default behavior).
25973*	@param		string	[$wrap]		- Brackets pair to use on vairables. '{}' by default. Use '[]' for bb codes. 
25974*
25975*	@return		string			- Parsed template.
25976*
25977\**//** ----------------------------------------------------------------------------------------------= by Mr.V!T =----/** //**/
25978function arrayToTemplate ($vars, $str, $clean, $wrap) { return parseVariables($str, $vars, $clean, $wrap); }
25979
25980/*--------------------------------------------------------------------------------*\
25981|	isNumeric
25982|----------------------------------------------------------------------------------
25983| Simple function for checking if given string numnber.
25984| - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
25985|	Params:
25986|		value	string	- Value to check.
25987|	Return:
25988|			bool	- Check status.
25989\*--------------------------------------------------------------= by Mr.V!T =-----*/
25990function isNumeric (value) {
25991	
25992	// Numerics are numeric o_O
25993	if ( typeof(value) == 'number' )
25994		return true;
25995	
25996	return (value - 0) == value && value.length > 0;
25997} //FUNC isNumeric
25998
25999/*--------------------------------------------------------------------------------*\
26000|	isEmail
26001|----------------------------------------------------------------------------------
26002| Simple function for checking if given string is correct mail.
26003| - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
26004|	Params:
26005|		value	string	- Value to check.
26006|	Return:
26007|			bool	- Check status.
26008\*--------------------------------------------------------------= by Mr.V!T =-----*/
26009function isEmail (value) {
26010	var filter = /^([a-z0-9\+_\-]+)(\.[a-z0-9\+_\-]+)*@([a-z0-9_\-]+\.)+[a-z]{2,6}$/i;
26011	return filter.test(value);
26012} //FUNC isEmail
26013
26014jQuery.fn.findExclude = function($selector, $exclude, $res){
26015	
26016	$res = $res || new jQuery(); 
26017
26018	// Looping through children, saving matching ones,
26019	// and recursively traversing deeper in each
26020	// Skipping ones that match exclusions ofc
26021	this.children().each( function() {
26022		
26023		var $el = jQuery(this);
26024		
26025		// Checking self to be saved in result
26026		if ( $el.is($selector) ) 
26027			$res.push(this);
26028		
26029		// Traversing deeper if allowed
26030		if( !$el.is($exclude) )
26031			$el.findExclude($selector, $exclude, $res);
26032	}); //jQuery each children
26033	
26034	return $res;
26035} //FUNC findExclude
26036
26037/** //** ----= jQuery.align	 =-----------------------------------------\**//** \
26038*
26039* 	Aligns selected element with another element or browser window.
26040*
26041*	@param	string	align	- Align type.
26042*	@param	object	target	- Element to align with.
26043*	@param	bool	fit	- Set TRUE to fit inside element (forced if no target)
26044*
26045*	@return	jQuery		- Aligned element.
26046*
26047\*--------------------------------------------------------------= by Mr.V!T =-----*/
26048jQuery.fn.align = function (align, target, fit, fixed) {
26049
26050// ---- Preparing environment
26051
26052	// Default align is center
26053	if ( align == 'center' ) align = false;
26054	align = align || 'center-center';
26055	
26056	align = align.split('-');
26057	
26058	if ( fit === undefined ) fit = false;
26059		
26060	if ( !target ) fit = true;
26061
26062	// If no target - we will use screen dimensions
26063	if ( !target ) 
26064		var screen = screenSize();
26065		
26066	// Target element dimensions
26067	var tDim = {
26068		width		: ( target )? target.outerWidth() : screen.width,
26069		height		: ( target )? target.outerHeight() : screen.height
26070	}; //OBJECT elDim
26071
26072// ---- Little complicated logic goes next, no comments there - hard to explain :)
26073
26074	var bases = {
26075		'left'		: 0,
26076		'top'		: 0,
26077		'center'	: 0.5,
26078		'right'		: 1,
26079		'bottom'	: 1
26080	} //OBJ bases
26081
26082	var ly = false;
26083
26084	if ( align[0] == 'center' )
26085		align = align.reverse();
26086
26087	if ( ['left', 'right'].indexOf(align[1]) !== -1 ) {
26088		ly = true;
26089		align = align.reverse();
26090	} //IF 
26091	
26092	if ( ['top', 'bottom'].indexOf(align[0]) !== -1 ) {
26093		ly = true;
26094		align = align.reverse();
26095	} //IF
26096	
26097	// Sub function to process aligment separately for each dimension
26098	// Using sub func to avoid copy/paste
26099	function _alignDim (t, l, s, e) {
26100		
26101		if ( fit ) l = false;
26102
26103		var d = ds = bases[t];
26104		
26105		if ( l && ds != 0.5 )
26106			ds = ( ds )? 0 : 1;
26107
26108		return e * d - s * ds;			 
26109		
26110	} //FUNC _alignDim
26111
26112// ---- Result CSS ----
26113
26114	// preCompiling css 	
26115	var css = {
26116		'left'	: _alignDim( align[0], !ly, this.outerWidth(true), tDim.width ),
26117		'top'	: _alignDim( align[1], ly, this.outerHeight(true), tDim.height ) 
26118	} //OBJECT css
26119
26120	// Correcting position, relating to target
26121	if ( target ) {
26122		var pos = (fixed) ? target.offset() : target.position();
26123	
26124		css.left += pos.left;
26125		css.top += pos.top;
26126	} //IF target set
26127	
26128	this.css(css);
26129
26130	return this;
26131} //FUNC align
26132
26133/*--------------------------------------------------------------------------------*\
26134|	centerWindow
26135|----------------------------------------------------------------------------------
26136| Adds function to jQuery for centering windows
26137| - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
26138|	Params:
26139|		NONE
26140|	Return:
26141|		SELF
26142\*--------------------------------------------------------------= by Mr.V!T =-----*/
26143jQuery.fn.centerWindow = function () {
26144
26145	var wnd_width	= this.width();
26146	var wnd_height	= this.height();
26147	
26148	var screen	= screenSize();
26149
26150	var wnd_left	= ( screen.width - wnd_width ) / 2;
26151	var wnd_top	= ( screen.height - wnd_height ) / 2;
26152
26153	var css		= {'top' : wnd_top, 'left' : wnd_left};
26154	
26155	this.css(css);
26156	
26157	return this;
26158} //FUNC centerWindow
26159
26160jQuery.fn.maxWidth = function ($outer, $margins) {
26161	var $max = 0;
26162	this.each( function() {
26163		$max = Math.max($max, $outer ? jQuery(this).outerWidth($margins) : jQuery(this).width() );
26164	}); //FUNC each in result    
26165	return $max;  	
26166} //FUNC maxWidth
26167
26168jQuery.fn.maxHeight = function ($outer, $margins) {
26169	var $max = 0;
26170	this.each( function() {
26171		$max = Math.max($max, $outer ? jQuery(this).outerHeight($margins) : jQuery(this).height() );
26172	}); //FUNC each in result    
26173	return $max;  	
26174} //FUNC maxHeight
26175
26176jQuery.fn.screenOffset = function () {
26177	
26178	var $pos = this.offset();
26179	
26180//	var $top = jQuery('html, body').scrollTop();
26181	var $top = jQuery(window).scrollTop();
26182	
26183	$pos.top -= $top;
26184	
26185	return $pos;   
26186} //FUNC screenOffset
26187
26188jQuery.fn.wrapAround = function ($target) {
26189
26190	// Wrapping assumes border and padding to be same around, so checking only top ones	
26191	var $border	= parseInt( this.css('border-top-width') );
26192	var $padding	= parseInt( this.css('padding-top') );
26193
26194	// Supporting direct coordinates input as 4 items array: [left, top, width, height]
26195	if ( isArray($target) ) {
26196
26197		// In array mode coords are given directly
26198		var $css = {
26199			'left'	: $target[0] - $border - $padding,	
26200			'top'	: $target[1] - $border - $padding,
26201			'width'	: $target[2],
26202			'height': $target[3]
26203		}; //OBJECT $css
26204		
26205	} else { //If dimensions given
26206
26207		if ( !$target.length )
26208			return this;
26209
26210		// Initial values, huge top/left and small width/height	
26211		var $x = 10000;
26212		var $y = 10000;
26213		
26214		var $toX = 0;
26215		var $toY = 0;
26216		
26217		// Looking through all elmements and searching for ourter max dimensions
26218		$target.each( function () {
26219			
26220			var $el = jQuery(this);
26221			
26222			// Not counting invisible elements
26223			if ( !$el.is(':visible') )
26224				return;
26225			
26226			// Getting offset and calculating tox/toy for element
26227			// Then comparing this to found ones already
26228			var $o	= $el.screenOffset();
26229	
26230			var $tx = $o.left	+ $el.outerWidth();
26231			var $ty = $o.top	+ $el.outerHeight();
26232			
26233			if ( $o.left < $x ) $x = $o.left;
26234			if ( $o.top < $y ) $y = $o.top;
26235			if ( $toX < $tx ) $toX = $tx;
26236			if ( $toY < $ty ) $toY = $ty;
26237			
26238		}); //FUNC jQuery.each
26239
26240		// Now we can build helper box
26241		var $css = {
26242			'left'	: $x - $border - $padding,	
26243			'top'	: $y - $border - $padding,
26244			'width'	: $toX - $x,
26245			'height': $toY - $y
26246		}; //OBJECT $css
26247
26248	} //IF jQuery
26249	
26250	// And apply it to self
26251	this.css($css);
26252	
26253	// Continuing chain
26254	return this;
26255} //FUNC wrapAround
26256
26257jQuery.fn.hoverClass = function ($class, $allSet) {
26258	
26259	var $this = this;
26260	
26261	// Binding with namespaces, using class as namespace
26262	var $ns		= $class.replace(/\W+/g, '');
26263	var $enter	= 'mouseenter.hoverClass.' + $ns;
26264	var $leave	= 'mouseleave.hoverClass.' + $ns;
26265
26266	this.off($enter);
26267	this.off($leave);
26268
26269	// Adding class on enter to whole set, or only one hoverred
26270	this.on($enter, function ($e) {
26271		if ( $allSet )
26272			$this.addClass($class);
26273		else
26274			jQuery(this).addClass($class);
26275	}); //FUNC onMouseenter
26276	
26277	this.on($leave, function ($e) {
26278		if ( $allSet )
26279			$this.removeClass($class);
26280		else
26281			jQuery(this).removeClass($class);
26282	}); //FUNC onMouseenter
26283
26284	return this;
26285} //FUNC hoverClass
26286
26287jQuery.fn.groupBy = function ($attribute) {
26288	
26289	// Defaulting to data-group attribute.
26290	$attribute = $attribute || 'data-group'; 
26291	
26292	var $res = {}; // Will store groups here
26293	
26294	// Looping through each element, and storing as groups
26295	this.each( function () {
26296		
26297		var $el = jQuery(this);
26298		
26299		// Getting groups attribute
26300		var $attr = $el.attr($attribute);
26301		
26302		// Nothing to do if no attribute set (ungrouped element)
26303		if ( !$attr )
26304			return;
26305		
26306		// Parsing into groups, allowing common delimiters
26307		var $groups = strToArray($attr, ',.|');
26308
26309		// Assigning element into each group, adding into existing, or creating new ones
26310		for ( var $i in $groups ) {
26311			var $group = $groups[$i];
26312			
26313			if ( !$res[$group] )
26314				$res[$group] = $el;
26315			else
26316				$res[$group] = $res[$group].add($el); 
26317			
26318		} //FOR each group
26319		
26320	}); //FUNC each.result
26321	
26322	// Returning results
26323	return $res;
26324} //FUNC groupBy
26325
26326function centerWindow (el) {
26327	jQuery(el).centerWindow();
26328} //FUNC centerWindow
26329
26330/*--------------------------------------------------------------------------------*\
26331|	jqmShowCSS
26332|----------------------------------------------------------------------------------
26333| Apllies given css to jQM window and shows it.
26334| - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
26335|	Params:
26336|		selector	string	- Window to show.
26337|		css		string	- Object with css to apply.
26338|		effect		string	- Effect to use on displaying.
26339|	Return:
26340|				bool	- Alwyas false.
26341\*--------------------------------------------------------------= by Mr.V!T =-----*/
26342function jqmShowCSS (selector, css, effect) {
26343	
26344	jQuery(selector).css(css);
26345
26346	if ( jqmVisible(selector) ) return false;
26347
26348	if ( !isJQM(selector) ) {
26349		
26350		var opt = {};
26351		
26352		if ( effect == 'fade' )
26353			opt = {
26354				onShow: function(h) { 
26355					h.w.fadeIn(300);
26356				},
26357				onHide: function(h) { 
26358					h.w.fadeOut(300);
26359					h.o.remove();
26360				}
26361			}; //Object o
26362		
26363		jQuery(selector).jqm(opt);
26364		if ( jQuery().jqDrag ) jQuery(selector).jqDrag('.jqmHeader');
26365		if ( jQuery().jqResize ) jQuery(selector).jqResize('.jqmResize');
26366	} //IF element is not jqm window already
26367	
26368	jQuery(selector).jqmShow();
26369
26370	return false;
26371	
26372} //FUNC jqmShowCSS
26373
26374/*--------------------------------------------------------------------------------*\
26375|	jqmShowCentered
26376|----------------------------------------------------------------------------------
26377| Fixes coordinates of jQM window to match center.
26378| - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
26379|	Params:
26380|		selector	string	- Window to show.
26381|		effect		string	- Effect to use on displaying.
26382|	Return:
26383|				bool	- Alwyas false.
26384\*--------------------------------------------------------------= by Mr.V!T =-----*/
26385function jqmShowCentered (selector, effect) {
26386/**/	
26387	var wnd_width	= jQuery(selector).width();
26388	var wnd_height	= jQuery(selector).height();
26389	
26390	var parent_window = screenSize();
26391
26392	var wnd_left = ( parent_window.width - wnd_width ) / 2;
26393	var wnd_top = ( parent_window.height - wnd_height ) / 2;
26394
26395	var new_css = {'top':wnd_top,'left':wnd_left};
26396/*/
26397	var new_css = {'top':0,'left':0, 'margin': 'auto auto'};
26398	
26399/**/	
26400	return jqmShowCSS(selector, new_css, effect);
26401} //FUNC jqmShowCentered
26402
26403/*--------------------------------------------------------------------------------*\
26404|	jqmShowDocked
26405|----------------------------------------------------------------------------------
26406| Fixes coordinates of jQM window to dock to given element.
26407| - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
26408|	Params:
26409|		selector	string	- Window to show.
26410|		dock_to		string	- Selector of element to dock to.
26411|		effect		string	- Effect to use on displaying.
26412|	Return:
26413|				bool	- Alwyas false.
26414\*--------------------------------------------------------------= by Mr.V!T =-----*/
26415function jqmShowDocked (selector, dock_to, effect) {
26416	
26417	var offset	= jQuery(dock_to).offset();
26418	if ( !offset.top ) return false;
26419	
26420	var outer	= jQuery(dock_to).outerHeight();
26421	
26422	var new_css = {
26423		'top'		: offset.top + outer + 3,
26424		'left'		: offset.left
26425	}; //New CSS
26426	
26427	return jqmShowCSS(selector, new_css, effect);
26428} //FUNC jqmShowDocked
26429
26430/*--------------------------------------------------------------------------------*\
26431|	jqmHide
26432|----------------------------------------------------------------------------------
26433| Hides JQM window.
26434| - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
26435|	Params:
26436|		selector	string	- Window to hide.
26437|		effect		string	- Effect to use on hiding.
26438|	Return:
26439|				bool	- Alwyas false.
26440\*--------------------------------------------------------------= by Mr.V!T =-----*/
26441function jqmHide (selector, effect) {
26442	jQuery(selector).jqmHide();
26443	return false;
26444} //FUNC jqmHide
26445
26446/*--------------------------------------------------------------------------------*\
26447|	isJQM
26448|----------------------------------------------------------------------------------
26449| Check if element is already jqModal window.
26450| - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
26451|	Params:
26452|		selector	string	- Element to check.
26453|	Return:
26454|		NULL
26455\*--------------------------------------------------------------= by Mr.V!T =-----*/
26456function isJQM (selector) {
26457	
26458	var tmp = jQuery(selector).attr('class');
26459	if ( tmp.indexOf('jqmID') < 0 ) return false;
26460	
26461	return true;
26462	
26463} //FUNC isJQM
26464
26465/*--------------------------------------------------------------------------------*\
26466|	jqmVisible
26467|----------------------------------------------------------------------------------
26468| Checks if jqm window is visible.
26469| - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
26470|	Params:
26471|		selector	string	- Element to check.
26472|	Return:
26473|		NULL
26474\*--------------------------------------------------------------= by Mr.V!T =-----*/
26475function jqmVisible (element) {
26476
26477	if ( !(element instanceof jQuery) )
26478		element = jQuery(element);
26479	
26480	var disp = element.css('display');
26481	
26482	return (disp != 'none');
26483} //FUNC jqmVisible
26484
26485function centerWindow (win) {
26486	var wnd_width	= jQuery('#'+win).width();
26487	var wnd_height	= jQuery('#'+win).height();
26488	
26489	var parent_window = screenSize();
26490
26491	var wnd_left = ( parent_window.width - wnd_width ) / 2;
26492	var wnd_top = ( parent_window.height - wnd_height ) / 2;
26493
26494	var new_css = {'top':wnd_top,'left':wnd_left};
26495	jQuery('#'+win).css(new_css);
26496} //FUNC
26497
26498function getOuterHTML (el) {
26499	
26500	if ( el.outerHTML ) return el.outerHTML;
26501	
26502	var tmp = document.createElement(el.parentNode.tagName);
26503	tmp.appendChild(el.cloneNode(true));
26504	
26505	return tmp.innerHTML;
26506} //FUNC getOuterHTML
26507
26508function strToElement (html) {
26509	var tmp = document.createElement('div');
26510	tmp.innerHTML = html;
26511	
26512	return tmp.firstChild;
26513} //FUNC strToElement
26514
26515function setOuterHTML (el, html) {
26516	
26517	if ( el.outerHTML ) return el.outerHTML = html;
26518
26519	var n = strToElement(html);
26520	
26521	el.parentNode.replaceChild(n, el);
26522} //FUNC setOuterHTML
26523
26524/** //** ----= formToArray	=--------------------------------------------------------------------------------------\**//** \
26525*
26526*	Collects input values as data object. Result is exactly same as one you get with $_POST. 
26527*	Supports nested arrays and sets in naming. E.g.: name="lvl1[lvl2][lvl3][]"
26528*
26529*	@param		object(HTMLFormElement)	
26530*					$form	- Source form.
26531*
26532*	@return		object			- Data collected from form.
26533*
26534\**//** ----------------------------------------------------------------------------------------------= by Mr.V!T =----/** //**/
26535function formToArray ($form, $options) {
26536
26537	// Storing results in object
26538	var $data = {};
26539	
26540	// Defaulting options
26541	$options = $options || {};
26542
26543// ---- 3rd parties ----
26544	
26545	// If there is tiny - forcing it to save editors
26546	if ( typeof(tinyMCE) != 'undefined' ) 
26547		tinyMCE.triggerSave();
26548	
26549	// Optionally supporting jQuery elements, though shold point exactly to form
26550	if ( isJQ($form) ) {
26551		
26552		// Not proceesing if not form though
26553		if ( !$form.is('form') )
26554			return $data;
26555		
26556		// Getting DOM from jQuery
26557		$form = $form.get(0);
26558	} //IF jQuery given
26559
26560// ---- Form ----
26561
26562	// Counting inputs with same name for sets resolving
26563	var $index = {};
26564
26565	// Looping through elements and getting values for each
26566	for ( var $i = 0; $i < $form.elements.length; $i++ ) {
26567
26568	// ---- Element ----
26569			
26570		// Getting element to work with
26571		var $el		= $form.elements.item($i);
26572
26573		// Collecting value, and then storing it in data object
26574		var $val;
26575
26576		// Skipping it if it does not have name
26577		if ( !$el.name )
26578			continue;
26579
26580	// ---- Input ----
26581		
26582		switch ( $el.type ) {
26583			case 'file' :
26584				// In HTML files are not included in post
26585				continue;
26586				
26587			case 'radio' :
26588			case 'checkbox'	:
26589
26590				$val = $el.value;
26591			
26592				if ( $val == 'on' ) {
26593
26594					// Using slightly alternate behavior 
26595					// where checkboxes without values (val == on) return 1/0 instead of 1/unset
26596					$val = ( $el.checked )? 1 : 0;
26597
26598				} //IF no value 
26599				else if ( !$el.checked ) {
26600
26601					if ( $options['emptyChecks'] ) {
26602	
26603						$val = '';
26604						
26605					} //IF empty checkboxes should present
26606					
26607					else {
26608
26609						// For valued checkboxes - regular behavior
26610						continue;
26611						
26612					} //IF standard behavior
26613
26614				} //IF value is set
26615
26616				break;
26617				
26618			case 'select-one' :
26619				
26620				// Using dedicated tool
26621				$val = getValue_Select($el);
26622				
26623				break;
26624
26625			case 'select-multiple' :
26626			
26627				// Using dedicated tool
26628				$val = getValue_MutliSelect($el);
26629				
26630				break;
26631				
26632			default	:
26633
26634				// Regular inputs have just value
26635				$val = $el.value;
26636		} //SWITCH
26637
26638		// Nothing to do with null
26639		if ( isUndefined($val) )
26640			continue;
26641			
26642	// ---- Data ----
26643
26644		var $name	= $el.name;	// That will be working name
26645		
26646		// Expecting array to happen, so parsing as path right away
26647		var $path	= regexMatchAll(/[\w\-]+/g, $name);
26648		
26649		// Problematic paths are skipped too
26650		if ( !$path[0] )
26651			continue;
26652			
26653		// Full match is all we need
26654		$path = $path[0];	
26655
26656		// Indexing now, as it looks like valid name
26657		$index[$name]	= isSet($index[$name]) ? ++$index[$name] : 0;
26658		
26659		// Special case for sets
26660		// Those should be stored as simple array
26661		// Exception for multiarrays: those always have [] to work properly 
26662		// (except rare cases when multiple same named selects used... which is never... currently only one will be allowed)
26663		
26664		if ( ($el.type != 'select-multiple') && $name.endsWith('[]') ) {
26665
26666			// Need to merge with existing array if one is present
26667			var $set = arrayPath($data, $path);
26668			
26669			// Merging value into set, which will be set into data
26670
26671			if ( isObject($set, true) )
26672			
26673				// Adding into objectx using index
26674				$set[$index[$name]] = $val;
26675				
26676			else if ( isArray($set) )
26677
26678				// Pushing if array
26679				$set.push($val)
26680
26681			else
26682				// Non arrays are overwritten
26683				$set = [$val];
26684
26685			// Preparing to be set back to data				
26686			$val = $set;
26687			
26688		} //IF set given
26689		
26690		// Now we have both value and path
26691		// Just setting it
26692		arrayPath($data, $path, $val);
26693		
26694	} //FOR each element
26695	
26696	return $data;
26697} //FUNC formToArray
26698
26699/** //** ----= formFromArray	=--------------------------------------------------------------------------------------\**//** \
26700*
26701*	Sets form inputs values from given data object.
26702*	Supports arrays and sets in elements names and tinyMCE Editor if applied to textarea.
26703*
26704*	@param		object(HTMLFormElement)	
26705*					$form	- Form to setup.
26706*
26707*	@param		object		$source	- Values to set.		
26708*
26709\**//** ----------------------------------------------------------------------------------------------= by Mr.V!T =----/** //**/
26710function formFromArray ($form, $source) {
26711	
26712	// Resaving to do not break source 
26713	var $data	= jQuery.extend({}, $source);
26714	
26715	// Counting inputs with same name for sets resolving
26716	var $index = {};
26717
26718// ---- Form ----
26719	
26720	for ( var $i = 0; $i < $form.elements.length; $i++ ) {
26721
26722	// ---- Element ----
26723	
26724		// Getting form element, it's name and value that should be applied
26725		var $el		= $form.elements.item($i);
26726
26727		// Skipping it if it does not have name
26728		if ( !$el.name )
26729			continue;
26730
26731		// Getting element name
26732		var $name	= $el.name;
26733		
26734		// Expecting array to happen, so parsing as path right away
26735		var $path	= regexMatchAll(/[\w\-]+/g, $name);
26736		
26737		// Problematic paths are skipped too
26738		if ( !$path[0] )
26739			continue;
26740			
26741		// Full match is all we need
26742		$path = $path[0];	
26743
26744		// Indexing now, as it looks like valid name
26745		$index[$name]	= isSet($index[$name]) ? ++$index[$name] : 0;
26746	
26747	// ---- Value ----
26748	
26749		var $val = arrayPath($data, $path);
26750
26751		// Resolving set
26752		var $set = $val;
26753		if ( $set && $name.endsWith('[]') ) { 
26754			
26755			// Getting value by index
26756			$val = $set[$index[$name]];
26757			
26758		} //IF set given
26759		
26760		// Making sure value is valid after all
26761		$val = ( isUndefined($val) || $val === false || $val === null ) ? '' : $val;
26762		
26763		// Special case for date inputs
26764		if ( $val == '0000-00-00' || $val == '0000-00-00 00:00:00' )
26765			$val = '';
26766
26767	// ---- Inputs ----
26768
26769		switch ($el.type) {
26770			
26771			case 'checkbox'	: 
26772			case 'radio'	: 
26773
26774				// For simple checkboxes - comparing against found value
26775				// Value will be either set directly or indexed in set 
26776				if ( $el.value == 'on' ) {
26777
26778					$el.checked = ( !isEmpty($val) );
26779					break;
26780
26781				} //IF simple checkbox
26782			
26783				// If not part of set - just checking values to match
26784				// Casting incoming value to string manually to ensure that match is correct for numbers
26785				// Input value is always string anyway
26786				if ( !isObject($set, true) ) {
26787					
26788					$el.checked = ( $val.toString() === $el.value );
26789					break;
26790					
26791				} //IF not a set
26792
26793				// Custom behavior if part of set - checking by value presense
26794				// Looping through and checking if value is there
26795				// Doing manually instead of array's indexOf cuz assoc array or object could happen
26796				var $tmp = false;
26797				for ( var $t in $set )
26798					if ( $t in $set && $set[$t].toString() === $el.value )
26799						$tmp = true;
26800						
26801				$el.checked = $tmp;
26802			
26803				break;
26804				
26805			case 'select-one'	:
26806			case 'select-multiple'	:
26807				
26808				// Using entire set
26809				setValue_Select($el, $set);
26810
26811				break;
26812				
26813			case 'file' :
26814			
26815				// File just resetting to default.
26816				// Files can't have values, unless selected
26817				// So if we're setting form from array - it better have no value than old some
26818				$el.value	= '';
26819
26820				// Alternatively - can recreate input completely
26821				// Getting input HTML, then creating new element from it, 
26822				// Doing this way since simple clone will not help
26823			//	var $html = getOuterHTML($el);
26824			//	setOuterHTML($el, $html);
26825
26826				// Skipping styled forms
26827				// ToDo: capture events instead
26828
26829				// Special case for morweb inputs - those are not recreated
26830				// And values are handled differently
26831				// ToDo: remove morweb hardcode and allow custom inputs handling
26832				if ( $el.className.indexOf('mw') !== -1 ) {
26833
26834					// Adding as dedicated attribute, which will be handled by morweb later on
26835					var $jFile	= jQuery($el); 
26836					$jFile.attr('data-file', $val || '');
26837					
26838					// Also setting value
26839					
26840					// Triggering change
26841					$jFile.change();
26842					
26843				} //IF morweb input
26844
26845				break;
26846				
26847			case 'textarea'	:
26848				
26849				// Checking if textarea can be tinyMCE
26850				if ( $el.style.display == 'none' && ( typeof tinyMCE != 'undefined' ) ) {
26851					
26852					// Checking if tiny really applied to it
26853					var $tiny = tinyMCE.get($el.id);
26854					
26855					// If it's tiny - setting values into it too.
26856					if ( $tiny )
26857						$tiny.setContent($val);
26858					
26859				}; //IF hidden and tinyMCE exists
26860
26861				$el.value = $val;
26862				
26863				break;
26864				
26865			case 'button' :
26866			case 'submit' :
26867			
26868				break;
26869				
26870			default	:
26871				$el.value = $val;
26872		} //SWITCH
26873
26874		// Triggering onChange in case if some set.
26875		if ( isSet(jQuery) ) 
26876			jQuery($el).change();
26877
26878	} //FOR each element
26879
26880} //FUNC formFromArray
26881
26882/** //** ----= jQuery.asArray	=--------------------------------------------------------------------------------------\**//** \
26883*
26884* 	Simple jQuery wrapper for formToArray() function.
26885*	
26886*	@return	array	- Form elements valus as array.
26887*
26888\**//** ----------------------------------------------------------------------------------------------= by Mr.V!T =----/** //**/
26889jQuery.fn.asArray = function ($options) {
26890	
26891	if ( !this.is('FORM') ) return {};
26892
26893	return formToArray(this.get(0), $options);
26894} //FUNC asArray
26895
26896/** //** ----= jQuery.fromArray	=--------------------------------------------------------------------------------------\**//** \
26897*
26898* 	Simple jQuery wrapper for formFromArray() function.
26899*
26900*	@param	object	data	- Array with values to set to elements.
26901*	@return	jQuery		- jQuery object for chain calls.
26902*
26903\**//** ----------------------------------------------------------------------------------------------= by Mr.V!T =----/** //**/
26904jQuery.fn.fromArray = function (data) {
26905	
26906	if ( !this.is('FORM') ) return this;
26907
26908	formFromArray( this.get(0), data );
26909	
26910	return this;
26911} //FUNC fromArray
26912
26913/*--------------------------------------------------------------------------------*\
26914|	getValue_Select
26915|----------------------------------------------------------------------------------
26916| Returns correct value of <select> based in setted or not value attribute. IE FIX.
26917| - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
26918|	Params:
26919|		element	object	- DOM elemnt to get value.
26920|	Return:
26921|			MIXED
26922|			bool	- FALSE in case of error.
26923|			string	- Element value.
26924\*--------------------------------------------------------------= by Mr.V!T =-----*/
26925function getValue_Select (element) {
26926	
26927	if ( element.options.selectedIndex < 0 ) return false;
26928	
26929	var option	= element.options[element.options.selectedIndex];
26930	var value	= getValue_Option(option);
26931	//(option.attributes.value && option.attributes.value.specified)? option.value : option.text
26932	
26933	return value;
26934} //FUNC getValue_Select
26935
26936/*--------------------------------------------------------------------------------*\
26937|	getValue_MutliSelect
26938|----------------------------------------------------------------------------------
26939| Returns values of <select> with multiple selection enabled as an array.
26940| - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
26941|	Params:
26942|		element	object	- DOM elemnt to get value.
26943|	Return:
26944|			array	- Array with values. Can be emty.
26945\*--------------------------------------------------------------= by Mr.V!T =-----*/
26946function getValue_MutliSelect (element) {
26947
26948	var res = [] ;
26949	
26950	if ( !element.options || element.options.length <= 0 ) return res;
26951
26952	for ( var i = 0; i < element.options.length; i++ ) {
26953		
26954		if ( !element.options[i].selected ) continue;
26955		
26956		res.push(getValue_Option(element.options[i]));
26957	} //FOR all managers
26958
26959	return res;
26960} //FUNC getValue_MutliSelect
26961
26962/*--------------------------------------------------------------------------------*\
26963|	setValue_Select
26964|----------------------------------------------------------------------------------
26965| Correctly sets value to selector. Works in old IE.
26966| - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
26967|	Params:
26968|		element	object	- DOM elemnt to set value.
26969|		values	MIXED	- Single string value or array with selected values.
26970|	Return:
26971|			VOID
26972\*--------------------------------------------------------------= by Mr.V!T =-----*/
26973function setValue_Select (element, values) {
26974
26975	if ( element.options.length <= 0 ) return;
26976
26977	if ( !values )
26978		values = [];
26979
26980	if ( typeof(values) != 'object' ) {
26981	
26982		// Converting values depending on multiple attribute
26983	 	// If multiple - most likely it's simple array string.
26984		if ( element.multiple || element.size )
26985			values = strToArray(values);
26986		else 
26987			// Stringifying value, cuz if integer come - indexOf will ignore it
26988			values = [values+''];
26989	
26990	} //IF not array come
26991
26992	for ( var i = 0; i < element.options.length; i++ ) {
26993		
26994		var val = getValue_Option(element.options[i]);
26995		
26996		element.options[i].selected = (values.indexOf(val) != -1);
26997		
26998	} //FOR all elements
26999
27000} //FUNC setValue_Select
27001
27002/*--------------------------------------------------------------------------------*\
27003|	getValue_Option
27004|----------------------------------------------------------------------------------
27005| Returns correct value of <option> based in setted or not value attribute. IE FIX.
27006| - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
27007|	Params:
27008|		element	object	- DOM elemnt to get value.
27009|	Return:
27010|			string	- Element value.
27011\*--------------------------------------------------------------= by Mr.V!T =-----*/
27012function getValue_Option (element) {
27013	var value	= (element.attributes.value && element.attributes.value.specified)? element.value : element.text;
27014	return value;
27015} //FUNC getValue_Option
27016
27017/*--------------------------------------------------------------------------------*\
27018|	getSelectorOptions
27019|----------------------------------------------------------------------------------
27020| Returns array with selector values.
27021| - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
27022|	Params:
27023|		element	object	- DOM elemnt to get value.
27024|	Return:
27025|			array	- Selector values.
27026\*--------------------------------------------------------------= by Mr.V!T =-----*/
27027function getSelectorOptions (element) {
27028	
27029	var res = [] ;
27030	
27031	if ( !element.options || element.options.length <= 0 ) return res;
27032
27033	for ( var i = 0; i < element.options.length; i++ ) {
27034
27035		var option = element.options[i];
27036
27037		if ( option.attributes.value && option.attributes.value.specified ) {
27038			res[option.value] = option.text;
27039		} else { //IF value set
27040			res.push(option.text);
27041		} //IF value not set
27042		
27043	} //FOR each option
27044
27045	return res;
27046} //FUNC getSelectorOptions
27047
27048/** //** ----= strToArray		 =--------------------------------------\**//** \
27049*
27050* 	Splits string into array with given delimeters, trimming resulting items.
27051*
27052* 	@param	string	str	- String to split.
27053*	@return	array		- Resulting array.
27054*
27055\**//** ------------------------------------= by Mr.V!T @ Morad Media Inc. =----/** //**/
27056function strToArray(str, delim) {
27057	
27058	// No need to split if already array
27059	if ( isArray(str) )
27060		return str;
27061	
27062	delim = delim || ',|';
27063	
27064	var tmp = str.split( new RegExp('\\s*[' + delim + ']\\s*') );
27065	
27066	var res = [];
27067	
27068	// Skipping empties
27069	for ( var i in tmp )
27070		if ( tmp[i] ) res.push(tmp[i]);
27071	
27072	return res; 	
27073} //FUNC strToArray
27074
27075/** //** ----= strToObj		 =----------------------------------------------\**//** \
27076*
27077* 	Splits string into object array with given delimeters
27078*
27079* 	@param	string	str	- String to split.
27080*	@return	array		- Resulting array.
27081*
27082\**//** ------------------------------------= by Mr.V!T @ Morad Media Inc. =----/** //**/
27083function strToObj (str, delim, auto_tirm) {
27084	
27085	var res	= {};
27086	
27087	if ( !str )
27088		return res;
27089	
27090	var tmp = str.split(delim);
27091	
27092	var j = 0;
27093	
27094	for ( var i in tmp ) {
27095		
27096		var pos = tmp[i].indexOf('=');
27097
27098		if ( pos != -1 ) {
27099			var key = tmp[i].substr(0, pos);
27100			var val = tmp[i].substr(pos+1);
27101		} else { //IF eq symbol found
27102			var key = j+'';
27103			var val = tmp[i];
27104		} //IF single value
27105
27106		key = key.trim();
27107
27108		if ( auto_tirm )
27109			val = val.trim();
27110			
27111		res[key] = val;
27112			
27113		j++;
27114		
27115	} //FOR each value
27116
27117	return res;
27118} //FUNC strToObj
27119
27120/** //** ----= cb			 =--------------------------------------\**//** \
27121*
27122* 	Callback runner helper. Used to simplify code. Checks if funciton given.
27123*
27124* 	@param	function	func	- Function to run.
27125*	@param	MIXED		*	- All next parameters will be passed to function.
27126*
27127*	@return	MIXED			- Funciton restult.
27128*
27129\**//** ------------------------------------= by Mr.V!T @ Morad Media Inc. =----/** //**/
27130function cb(func, args) {
27131
27132	// ToDo: apply is not applicable here, so think better way;
27133	
27134} //FUNC cb
27135
27136/** //** ----= checkAll			 =--------------------------------------\**//** \
27137*
27138* 	Sets value to all checkboxes in an element.
27139*
27140* 	@param	bool	value	- Checkbox state.
27141* 	@param	string	element	- jQuery selector.
27142*	@return	bool		- Initial value.
27143*
27144\**//** ------------------------------------= by Mr.V!T @ Morad Media Inc. =----/** //**/
27145function checkAll (value, element) {
27146	
27147	jQuery(element).find('INPUT[type=checkbox]').attr('checked', value);
27148
27149	return false;	
27150} //FUNC checkAll
27151
27152function ToggleDisplay(elementID, state) {
27153	element = document.getElementById(elementID);
27154	
27155	if ( !element ) return;
27156	
27157	if (state == undefined) {
27158		state = ( element.style.display == 'none' );
27159	} //IF no state specified
27160	
27161	element.style.display = state ? 'inline' : 'none';
27162} //FUNC ToggleDisplay
27163
27164function screenSize() {
27165    var w, h;
27166    w = (window.innerWidth ? window.innerWidth : (document.documentElement.clientWidth ? document.documentElement.clientWidth : document.body.offsetWidth));
27167    h = (window.innerHeight ? window.innerHeight : (document.documentElement.clientHeight ? document.documentElement.clientHeight : document.body.offsetHeight));
27168    return {width:w, height:h};
27169}
27170
27171function selectRow(row, state) {
27172	var row_class =	jQuery('#'+row).attr('class');
27173	row_class = row_class.replace('_selected','');
27174	row_class += ( state )? '_selected' : '';
27175	jQuery('#'+row).attr('class', row_class);
27176} //FUNC selectRow
27177
27178/*/
27179function insert_bar (bar_place) {
27180	jQuery('#'+bar_place).html('<IMG src="/images/processing/bar.gif" alt="Loading...">');
27181} //FUNC insert_bar
27182
27183function insert_green_bar (bar_place) {
27184	jQuery('#'+bar_place).html('<IMG src="/images/processing/bar_green.gif" alt="Loading...">');
27185} //FUNC insert_bar
27186//*/
27187
27188/** //** ----= getCookie		 =--------------------------------------\**//** \
27189*
27190* 	Returns cookie with given name.
27191*
27192* 	@param	string	name	- Cookie name to search.
27193*	@param	string	[def]	- Default value to return if cookie not found.
27194*
27195*	@return	string		- Cookie value.
27196*
27197\**//** ------------------------------------= by Mr.V!T @ Morad Media Inc. =----/** //**/
27198function getCookie (name, def) {
27199
27200	if ( def == undefined ) def = '';
27201
27202	var cookieValue	= def;
27203	var cookieName	= name + '=';
27204	
27205	if ( document.cookie.length > 0 ) { 
27206	
27207		offset = document.cookie.lastIndexOf(cookieName);
27208		if ( offset != -1 ) { 
27209		
27210			offset	+= cookieName.length;
27211			
27212			end	= document.cookie.indexOf(';', offset);
27213			if (end == -1) end = document.cookie.length;
27214			
27215			cookieValue = unescape(document.cookie.substring(offset, end))
27216		
27217		} //IF found smth
27218	
27219	} //IF there are cookies
27220	
27221	return cookieValue;
27222} //FUNC getCookie
27223
27224/** //** ----= setCookie	 =----------------------------------------------\**//** \
27225*
27226*	Sets cookie with given name.
27227*
27228*	@param	string	name	- Cookie name to set.
27229*	@param	string	value	- Value of cookie.
27230*	@param	string	[hours]	- Expiration date in hours.
27231*
27232\**//** ------------------------------------= by Mr.V!T @ Morad Media Inc. =----/** //**/
27233function setCookie (name, value, hours) {
27234	
27235	var expire	= "";
27236	
27237	if ( hours ) {
27238		expire = new Date((new Date()).getTime() + hours * 3600000);
27239		expire = "; expires=" + expire.toGMTString();
27240	} //IF there is set expiration
27241	
27242	document.cookie = name + "=" + escape(value) + expire + ';path=/;';
27243	
27244} //FUNC setCookie
27245
27246function get_controller_views(views) {
27247		insert_bars(views);
27248		insert_views(views,0);
27249} //FUNC get_controller_views
27250
27251function mark_checkboxes(container_form,val) {
27252	for (var i = 0; i < container_form.elements.length; i++) {
27253		el = container_form.elements[i];
27254		if (el.type=='checkbox')
27255			el.checked=val;
27256	}
27257} //FUNC mark_checkboxes
27258
27259function status_string(message,style_class,padding) {
27260	if (message == '') return '';
27261	if (!style_class) style_class = 'status_error';
27262	if (!padding) padding = 0;
27263	return '<div class="'+style_class+'" style="padding:'+padding+'px">'+message+'</div>';
27264} //FUNC error_string
27265
27266function ajax_pack_data(controller_url,place_to_put, data, ajax_options) {
27267	var res = [];
27268	res['controller']	= controller_url;
27269	res['place']		= place_to_put;
27270	if (!data) data	= {};
27271	res['data']		= data;
27272	if (!ajax_options) ajax_options	= {};
27273	res['options']		= ajax_options;
27274	return res;
27275} //FUNC pack_view
27276
27277function ajax_insert_bars(views) {
27278	for (i=0; i < views.length; i++) {
27279		insert_bar(views[i]['place']);
27280	} //FOR each views
27281} //FUNC insert_bars
27282
27283function ajax_insert_views(views,num) {
27284	if (num >= views.length) return false;
27285
27286	var tdata = views[num]['data'];
27287	
27288	if (views[num]['options'].onLoad) {views[num]['options'].onLoad(tdata)};
27289	jQuery.ajax({
27290		type	: 'POST', dataType: 'html',
27291		url	: views[num]['controller'],
27292		data	: views[num]['data'],
27293		success	: function(obj){
27294				jQuery('#'+views[num]['place']).html(obj);
27295				ajax_insert_views(views,num+1);
27296				if (views[num]['options'].onSuccess) {views[num]['options'].onSuccess(tdata)};
27297			},//FUNC success
27298		error : function (XMLHttpRequest, textStatus, errorThrown) {
27299				jQuery('#'+views[num]['place']).html(status_string('This section is not avaible now (Possibly under construction). Try again later.'));
27300				ajax_insert_views(views,num+1);
27301			} //FUNC error
27302	}); //ajax
27303}//FUNC insert_views
27304
27305function ajax_get_views(views) {
27306		ajax_insert_bars(views);
27307		ajax_insert_views(views,0);
27308} //FUNC get_controller_views
27309
27310function ajax_get_view(controller_url,place_to_put,data, ajax_options) {
27311	ajax_get_views([ajax_pack_data(controller_url,place_to_put,data, ajax_options)]);
27312} //FUNC get_controller_view
27313
27314function ajax_post (post_form,controller,status_place,settings) {
27315	insert_bar(status_place);
27316	var tt = document.getElementById(post_form);
27317	if (!tt) alert('no form');
27318//*/
27319	jQuery.ajaxUpload ({
27320		type		: 'POST', dataType : 'html',
27321	        url		: controller,
27322	        secureuri	: false,
27323	        uploadform	: document.getElementById(post_form),
27324	        success		: function (res, status) {
27325					data = res.split('<!-- CUT -->');
27326					jQuery('#'+status_place).html(data[0]);
27327
27328					if ((data.length != 1) && (data[1] == 'success') ) {
27329						if (settings.success) settings.success(data);
27330					} else {
27331						if (settings.error) settings.error(data);
27332					} //IF returned status
27333
27334				}, //FUNC sucess
27335		error		: function (res, status) {
27336					alert('error');
27337					jQuery('#'+status_place).html(status_string('Error: '+status));
27338					if (settings.error) settings.error();
27339				} //FUNC error
27340
27341	}); //AJAX
27342//*/	
27343} //FUNC ajax_post
27344
27345/** //** ----= getDimensions	=--------------------------------------------------------------------------------------\**//** \
27346*
27347*	Parses strings like '100x200' into [x,y] dimensions pair (with Y being optional). 
27348* 	Returns FALSE if string does not contain valid dimensions string.
27349*
27350*	@param		string	$dimensions	- Dimensions string.
27351* 	@param		bool	[$strict]	- Strict validation. Will require Y to present
27352*
27353*	@return		MIXED			- Array with named dimensons or FALSE if failed.
27354*
27355\**//** ---------------------------------------------------------------------------= by Mr.V!T @ Morad Media Inc. =----/** //**/
27356function getDimensions ($dimensions, $strict) {
27357	
27358	// Nothing to do if notihng fiven
27359	if ( !$dimensions )
27360		return false;
27361	
27362	// Simple check for compatability with parsed dimensions, allowing mixed params in callee funcions.
27363	if ( isArray($dimensions) || isObject($dimensions) ) {
27364		
27365		// Fast validation for X to be present: it's minimal requirement for valid dimensions array
27366		if ( !isSet($dimensions['x']) )
27367			return false;
27368			
27369		// Fast validation for strict mode
27370		if ( $strict && !isSet($dimensions['y']) )
27371			return false;
27372		
27373		return $dimensions;
27374	} //IF array given
27375	
27376	// Parsing for dimensions, using regex. It also allows to validate dimensions stirng.
27377	// X and Y will be named in matches, which makes it easyer to return. 
27378
27379	// Strict option really changes counter for second (x200) match
27380	$strict = $strict ? '+' : '?';
27381
27382	// Capturing groups and then checking once that good
27383	var $reg	= '^([0-9]+)(x([0-9]+))' + $strict + '$';
27384	var $match	= $dimensions.match( new RegExp($reg, 'i') );
27385	
27386	// Checking existing  params
27387	if ( !$match )
27388		return false;
27389	
27390	// Grabbing only named matches into result (to keep result clear)
27391	var $res = {};
27392	
27393	// X always present at this step (othewise we'd returned already)
27394	$res['x']	= $match[1];	
27395	
27396	// Y is optional	
27397	if ( isSet($match[3]) )
27398		$res['y'] = $match[3];
27399	
27400	// Can return now
27401	return $res;
27402} //FUNC getDimensions
27403
27404/* #### MATH ################################################################################################################ */ 
27405
27406/** //** ----= asDeg	=----------------------------------------------------------------------------------------------\**//** \
27407*
27408*	Converts radians to degrees.
27409*
27410*	@param		float	$angle	- Angle in radians.
27411*
27412*	@return		flaot		- Angle in degrees.
27413*
27414\**//** ----------------------------------------------------------------------------------------------= by Mr.V!T =----/** //**/
27415function asDeg ($angle) {
27416	return $angle * (180 / Math.PI);
27417} //FUNC asDeg
27418
27419/** //** ----= asRad	=----------------------------------------------------------------------------------------------\**//** \
27420*
27421*	Converts degrees to radians.
27422*
27423*	@param		float	$angle	- Angle in degrees.
27424*
27425*	@return		flaot		- Angle in radians.
27426*
27427\**//** ----------------------------------------------------------------------------------------------= by Mr.V!T =----/** //**/
27428function asRad ($angle) {
27429	return $angle * (Math.PI / 180);
27430} //FUNC asRad
27431
27432/** ************************************************************************************************************************ **\
27433* 
27434*	Set of classes and functions that will help generate and process AJAX queries.
27435* 
27436* 	@package	VIT-Lib
27437* 	@subpackage	Ajax
27438*
27439* 	@license	MIT License
27440*			Permission  is  hereby  granted,  free  of  charge, to any person obtaining a copy of this software and
27441*			associated documentation files (the "Software"), to deal in the Software without restriction, including
27442*			without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
27443*			copies  of  the  Software, and to permit persons to whom the Software is furnished to do so, subject to
27444*			the following conditions:
27445*			
27446*			The  above  copyright  notice and this permission notice shall be included in all copies or substantial
27447*			portions of the Software.
27448*			
27449*			THE  SOFTWARE  IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT
27450*			LIMITED  TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
27451*			NO  EVENT  SHALL  THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
27452*			WHETHER  IN  AN  ACTION  OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
27453*			SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
27454*
27455*	@author		Victor Denisenkov aka Mr.V!T
27456*	@copyright	Copyright (c) 2008 - 2012 Victor Denisenkov aka Mr.V!T
27457*	@version	1.0
27458* 
27459\** ************************************************************************************************************************ **/
27460
27461/** //** ----= OBJECT vAjax	=--------------------------------------------------------------------------------------\**//** \
27462*
27463* 	Ajax procesing object. Implements both jQuery common ajax and ajaxUpload plugin for iframe upload tricks (for old 
27464*	browsers). Designed to use together with server side vAjax object. Supports POST progress monitoring using server 
27465*	side plugins like APC or HTML5 files API. By default enables progress monitoring for forms with files and progressbar 
27466*	present.
27467*
27468* 	@package	VIT-Lib
27469* 	@subpackage	Ajax
27470* 	@category	Helper
27471*
27472*	@param	string	url		- Path to controller to make request to.
27473*
27474*	@param	MIXED	[data]		- Data to post on request. Can be DOM form, HTML5 FormData object, jQuery selector 
27475*					  or object which points to form, or plain object with parameters.
27476*
27477*	@param	object	[options]	- Additional options if necessary.
27478*
27479*	@todo	Known bug: ajaxUpload can't handle server errors. Need workaround (possible write own iframe upload?)
27480*
27481\**//** ----------------------------------------------------------------------------------------------= by Mr.V!T =----/** //**/
27482
27483// ToDo: Cross domain support with both JSONP and Access-Control-Allow-Origin: *
27484
27485function vAjax (url, data, options) {
27486
27487	// Creating basic object with events assigned
27488	var obj = vEventObject(['success', 'error', 'start', 'stop', 'done', 'progressStart', 'progressStop'], {
27489	
27490	opt		: {				// Default object options
27491		async			: true,			// Set FALSE to make sync call
27492		simple			: false,		// Expect simple answers and threat them as success or trigger errors on those.
27493		modern			: true,			// Allow or not modern APIs
27494
27495		progress		: '.progressFill',	// Default progressbar fill element selector. Can be replaced with callback or own selector.
27496		progressNum		: '.progressNum',	// Default progressbar Numeric indicator. Will be set to current %. Ignored if callback given for fill.
27497		progressOn		: false,		// Forces progress monitoring (not used currently).
27498
27499		progressUrl		: '/upload/?key=',	// Progress monitoring controller. Key marker will be added to end of this.
27500		progressDelay		: 500,			// Delay before starting progress monitoring using server side plugin.
27501		progressInterval	: 500			// Interval to update progress.
27502	}, //OBJECT options
27503	
27504	resSplit	: "-=-CUT-=-",			// Unique marker used to split results.
27505
27506	controller	: url,				// Url to make request to.
27507	source		: data || {},			// Source given.
27508
27509	sourceForm	: false,			// Source form if available.
27510	sourceData	: {},				// Source data if available (if form given - will contain form inputs as array).
27511	sourceElement	: false,			// Source form as jQuery pointer.
27512	
27513	isModern	: false,			// HMTL5 usage marker. Will be set to TRUE if system decides to use HTML5 
27514							// formData and XHR2 to post data
27515
27516	Method		: '',				// Will store used upload method.
27517	
27518							
27519	prgInterval	: false,			// Progress bar monitor interval resource
27520	prgTimeout	: false,			// Progress bar monitoring timeout resource
27521	
27522	_processor	: false,			// Stores custom XHR processor if specified. 
27523	
27524	/** //** ----= options	=--------------------------------------------------------------------------------------\**//** \
27525	*
27526	* 	Used to setup options on run time.
27527	*
27528	*	@param	object	options	- Data object with options to setup.
27529	* 
27530	*	@return SELF
27531	* 
27532	\**//** --------------------------------------------------------------------------------------= by Mr.V!T =----/** //**/
27533	options	: function (options) {
27534		
27535		options = options || {};
27536
27537		// Merging options
27538		jQuery.extend(this.opt, options);
27539
27540		// Testing browser to be modern
27541		this.isModern = this.opt.modern && isXHR2();
27542		
27543		return this;
27544	}, //FUNC options
27545
27546	/** //** ----= processor	=------------------------------------------------------------------------------\**//** \
27547	*
27548	* 	Custom processor is used to use external ajax xhr implementation. vAjax is passed as parameter to processor to 
27549	*	allow implementation of final events. Processor should call .ajaxSuccess() / .ajaxError() for appropiate events.
27550	*
27551	*	@param	function	proc	- Processor callback.
27552	* 
27553	*	@return SELF
27554	* 
27555	\**//** --------------------------------------------------------------------------------------= by Mr.V!T =----/** //**/
27556	processor	: function ( proc ) {
27557		this._processor = proc;
27558		return this;		
27559	}, //FUNC processor
27560	
27561	/** //** ----= parseResult	=------------------------------------------------------------------------------\**//** \
27562	*
27563	* 	Prses and format result data. Generates result to pass to callbacks. For internal use.
27564	*
27565	* 	@param	object	result		- Request responce.
27566	* 
27567	*	@return	object			- Object with result data.
27568	*
27569	\**//** --------------------------------------------------------------------------------------= by Mr.V!T =----/** //**/
27570	parseResult	: function (result) {
27571
27572		var self = this;
27573		
27574		// Making sure that result have all vields
27575		if ( !result.responseText )
27576			result.responseText = '';
27577
27578	// ---- Preparing data ----
27579
27580		// Preparing result wrapper
27581		var data	= {
27582			
27583			req	: self.sourceData,	// Request source data.
27584			
27585			status	: {			// Responce meta like status and fields list. Default one for simple responces. Full responce will provide own head.
27586				major	: (result.statusMinor == 'success'),	// Major operation status TRUE or FALSE. Simples are TRUE normally.
27587				minor	: result.statusMinor,			// Minor/detailed status as string (like 'success', 'error', 'warning', 'simple' etc)
27588				code	: result.status,			// Satus code if available
27589				text	: result.statusText,			// Request status text
27590				full	: false,				// Full(complex) responce marker
27591				fields	: []					// Custom responce fileds/types list
27592			}, //OBJECT meta
27593			
27594			res	: ''			// Request basic response - whole simple request, or head/status message for complex response. Will be added with parsing below.
27595		}; //OBJECT data
27596
27597		// Trying to split as complex result
27598		var tmp		= result.responseText.split(this.resSplit);
27599
27600		// Smth basic will present even for plain responce 
27601		// (for example in case of server script fatal erorrs).
27602		// Correcting res to have at least smth
27603		data.res = tmp[0] || '';
27604		
27605		// Determinig wether result was full(complex) or simple
27606		data.status.full	= tmp.length > 1;
27607
27608	// ---- Simple result ----
27609
27610		// If got simple result (either error or success)
27611		if ( !data.status.full ) {
27612
27613			// Updating status
27614			data.status.major = data.status.major ? this.opt.simple : data.status.major;
27615			data.status.minor = 'simple';
27616			
27617			// If simple is threating as error - filling result as status text (expecting server errors)
27618			if ( !this.opt.simple && !data.status.text )
27619				data.status.text = data.res;
27620
27621			// Running callbacks
27622	
27623			// We are done anyway, so triggering stop event, passing self as param
27624			this.stop(data, this);
27625	
27626			// Error/success
27627			if ( data.status.major && this.opt.simple ) 
27628				this.success(data, this);
27629			else 
27630				this.error(data, this);
27631	
27632			// Both stop and done are called anyway, difference is calling order: stop, success/error, done
27633			this.done(data, this);
27634
27635			return data;
27636			
27637		} // If simple
27638
27639	// ---- Complex result head and fields ----
27640
27641		// For full responce status - header will go second (after possible debug)
27642		var head = JSON.parse(tmp[1]);
27643
27644		// Updating data status, merging existing response with given one
27645		// Mergin only if no text supplied already (usually errors have text)
27646		if ( !data.status.text )
27647			jQuery.extend(data.status, head);
27648
27649		// Looping through given fields (if present) and decoding them to response
27650		var di = 2;
27651		if ( head.fields ) {
27652			
27653			for ( var name in head.fields ) {
27654
27655				// Decoding non scalar values
27656				if ( !head.fields[name] ) {
27657					data[name] = JSON.parse(tmp[di]);
27658				} else { //IF need to decode
27659					data[name] = tmp[di];
27660				} //IF no need to decode (scalar)
27661				
27662				// Increasing split counter
27663				di++;
27664			} //FOR each field
27665			
27666		} //IF was fields posted
27667
27668		// Running callbacks
27669
27670		// We are done anyway, so triggering stop event, passing self as param
27671		this.stop(data, this);
27672
27673		// Error/success
27674		if ( data.status.major ) 
27675			this.success(data, this);
27676		else 
27677			this.error(data, this);
27678
27679		// Both stop and done are called anyway, difference is calling order: stop, success/error, done
27680		this.done(data, this);
27681
27682		return data;
27683		
27684	}, //FUNC parseResult
27685
27686// ==== CALLBAKCS ==============================================================================================================
27687
27688	/** //** ----= ajaxSuccess	=------------------------------------------------------------------------------\**//** \
27689	*
27690	* 	Ajax success event callback. For internal use, can be altered though.
27691	*
27692	* 	@see	jQuery.ajax callbacks for parameters.
27693	* 
27694	\**//** --------------------------------------------------------------------------------------= by Mr.V!T =----/** //**/
27695	ajaxSuccess	: function (res, status) {
27696
27697		this.ajaxProgressStop(100);
27698		
27699		status = status || 'success';
27700		
27701		// Creating result object
27702		return this.parseResult({
27703			responseText	: res,
27704			status		: 0,
27705			statusText	: '',
27706			statusMinor	: status
27707		}); //parseResult.result
27708		
27709	}, //FUNC ajaxSuccess
27710	
27711	/** //** ----= ajaxError	=------------------------------------------------------------------------------\**//** \
27712	*
27713	* 	Ajax Error event callback. For internal use, can be altered though.
27714	*
27715	* 	@see	jQuery.ajax callbacks for parameters.
27716	* 
27717	\**//** --------------------------------------------------------------------------------------= by Mr.V!T =----/** //**/
27718	ajaxError	: function (res, status) {
27719
27720		this.ajaxProgressStop('error');
27721		
27722		// Adding minor status to common result		
27723		res.statusMinor = status;
27724		
27725		return this.parseResult(res);
27726		
27727	}, //FUNC ajaxError
27728
27729	/** //** ----= ajaxXHR	=--------------------------------------------------------------------------------------\**//** \
27730	*
27731	* 	Ajax XHR callback. Used to alter jQuery XHR.
27732	*
27733	* 	@see	jQuery.ajax callbacks for parameters.
27734	* 
27735	\**//** --------------------------------------------------------------------------------------= by Mr.V!T =----/** //**/
27736	ajaxXHR	: function () {
27737		
27738		var self = this;
27739		
27740		var x = jQuery.ajaxSettings.xhr();
27741
27742		// Adding listener only if really needed. If not - xhr goes back untouched
27743		if ( this.hasProgress() ) {
27744			
27745			// If have progress - good place to reset it
27746			this.ajaxProgressStart();
27747			
27748			// We are sure that .upload exists: previously it was tested in .post()
27749			x.upload.addEventListener('progress', function (e) {
27750
27751				if ( e.lengthComputable ) {
27752
27753					// Calculating procents
27754					var proc = (e.loaded * 100) / e.total;
27755					
27756					self.ajaxProgress( Math.floor(proc) );
27757					
27758				} //IF there is progress available
27759				
27760			}, false);
27761			
27762		} //IF need progress monitoring
27763
27764		return x;
27765	}, //FUNC ajaxXHR
27766
27767// ==== Progress ===============================================================================================================
27768
27769	/** //** ----= getFormElement	=------------------------------------------------------------------------------\**//** \
27770	*
27771	* 	For internal use. Checks given parameter as for element. Mainly used for progress indication elements search.
27772	*
27773	*	@param	MIXED		- jQuery selector to search.
27774	*
27775	*	@return	MIXED		- jQuery element to use as progress element, or FALSE if not applicable. 
27776	*
27777	\**//** --------------------------------------------------------------------------------------= by Mr.V!T =----/** //**/
27778	getFormElement	: function (el) {
27779		
27780		// This does not assumes callback functions. Those should be checked outside this and processed separately.
27781		
27782		// If already jQuery - only checking length
27783		if ( isJQ(el) ) {
27784			if ( el.length > 0 )
27785				return el;
27786				
27787			return false;
27788		} //IF jQuery given
27789		
27790		// If string selector given - searching it within form 
27791		// Search on form or FALSE, done this way for optimization 
27792		// and to avoid multiple for updates with common class selectors
27793		// If user needs global selector search - he still can pass ready jQuery or use callback
27794		if ( isStr(el) ) {
27795
27796			// Not allowing selector search outside of form.			
27797			if ( !this.sourceElement )
27798				return false;
27799				
27800			var j = this.sourceElement.find(el);
27801		
27802			// If smth found - we're good to go
27803			if ( j.length > 0 )
27804				return j;
27805			
27806			return false;
27807		} //IF string passsed
27808
27809		// At this point assuming that element is given as DOM element
27810		// Then just wrapping it with jQuery
27811		return jQuery(el);
27812
27813	}, //FUNC getFormElement
27814
27815	/** //** ----= progress	=--------------------------------------------------------------------------------------\**//** \
27816	*
27817	* 	Progress monitoring event. Triggers on progress update event. Can accept either callbacks or valid jQuery 
27818	*	selectors (string, jQuery or DOMElement). Callbacks will get current precentage as parameter. In case if 
27819	*	selector given - for progressBar system will set width, for numeric indicator will set .html(). jQuery selectors 
27820	*	do not process errors or other status updates, though those will be passed into callbacks.  
27821	*
27822	*	@param	MIXED	bar[,num]	- Progress bar and numeric bar callbacks or elements to process.
27823	*
27824	*	@param	bool	[force]		- Forcing progress event. If set to FALSE system will try to detect if progress 
27825	*					  monitoring is necessary.
27826	*	
27827	*	@return	SELF 
27828	* 
27829	\**//** --------------------------------------------------------------------------------------= by Mr.V!T =----/** //**/
27830	progress	: function (bar, num, force) {
27831		
27832		this.opt.progress	= bar;
27833		this.opt.progressNum	= num;
27834		
27835		this.opt.progressOn	= force || false; 	
27836		
27837		return this;
27838	}, //FUNC progress
27839
27840	/** //** ----= hasProgress	=------------------------------------------------------------------------------\**//** \
27841	*
27842	* 	For internal use. Checks if need to even setup progress indication.
27843	*
27844	*	@return	MIXED		- jQuery element to use as progress element, or 
27845	*
27846	\**//** --------------------------------------------------------------------------------------= by Mr.V!T =----/** //**/
27847	hasProgress	: function () {
27848		
27849		// Nothing to do if nothing is given as progress
27850		if ( !this.opt.progress && !this.opt.progressNum )
27851			return false;
27852		
27853		// Progress is always enabled for callbacks
27854		// ToDo: review this behavior, mb add hasFiles for testing.
27855
27856		// If not callbacks - checking to be jQuery
27857		if ( !isFunction(this.opt.progress) )
27858			this.opt.progress = this.getFormElement(this.opt.progress);
27859
27860		if ( !isFunction(this.opt.progressNum) )
27861			this.opt.progressNum = this.getFormElement(this.opt.progressNum);
27862
27863		// Now they are callback, jQuery or just disabled, so fast to check. Enough one of either.
27864		if ( this.opt.progress || this.opt.progressNum )
27865			return true;
27866
27867		return false;
27868	}, //FUNC hasProgress
27869
27870	/** //** ----= ajaxProgressStart	=----------------------------------------------------------------------\**//** \
27871	*
27872	* 	Resets progress elements if set.
27873	*
27874	\**//** --------------------------------------------------------------------------------------= by Mr.V!T =----/** //**/
27875	ajaxProgressStart	: function (proc) {
27876
27877		// If jQuery element set for progress - good to show them
27878
27879		if ( isJQ(this.opt.progress) )
27880			this.opt.progress.fadeIn();
27881
27882		if ( isJQ(this.opt.progressNum) )
27883			this.opt.progressNum.fadeIn();
27884
27885		this.ajaxProgress(0);
27886		this.progressStart(0);
27887		
27888	}, //FUNC ajaxProgressStart
27889
27890	/** //** ----= ajaxProgress	=------------------------------------------------------------------------------\**//** \
27891	*
27892	* 	Progress event callback. Used on progress monitoring. For internal use, can be altered though.
27893	*
27894	*	@param	int	proc - Current progress precentage.	
27895	*
27896	\**//** --------------------------------------------------------------------------------------= by Mr.V!T =----/** //**/
27897	ajaxProgress	: function (proc) {
27898
27899		// Nothing to do if nothing is given as progress
27900		if ( !this.opt.progress && !this.opt.progressNum )
27901			return;
27902
27903		// For selectors processing only numeric proc 
27904		// Errors and other mess will be processed in callbacks if necessary
27905
27906	// ---- Progress bar ----
27907		
27908		if ( this.opt.progress ) {
27909			
27910			// Can be callback or jQuery at this poing
27911			if ( isFunction(this.opt.progress) )
27912				this.opt.progress(proc);
27913				
27914			else if ( isNumeric(proc) )
27915				this.opt.progress.width(proc + '%');
27916			
27917		} //IF progress given
27918			
27919	// ---- Numeric indicator ----
27920		
27921		if ( this.opt.progressNum ) {
27922			
27923			// Can be callback or jQuery at this poing
27924			if ( isFunction(this.opt.progressNum) )
27925				this.opt.progressNum(proc);
27926				
27927			else if ( isNumeric(proc) )
27928				this.opt.progressNum.html( proc != 0 ? proc + '%' : '');
27929			
27930		} //IF progress given
27931			
27932	}, //FUNC ajaxProgress
27933
27934	/** //** ----= ajaxProgressStop	=------------------------------------------------------------------------------\**//** \
27935	*
27936	* 	Forces progress monitoring stop.
27937	*
27938	*	@param	int	proc - Current progress precentage.	
27939	*
27940	\**//** --------------------------------------------------------------------------------------= by Mr.V!T =----/** //**/
27941	ajaxProgressStop	: function (proc) {
27942
27943		if ( this.prgInterval )
27944			clearInterval(this.prgInterval); 
27945
27946		if ( this.prgTimeout )
27947			clearInterval(this.prgTimeout); 
27948
27949		if ( this.hasProgress() )
27950			this.ajaxProgress(proc);
27951
27952		this.progressStop(proc);
27953
27954	}, //FUNC ajaxProgressStop
27955	
27956// ==== Processors =============================================================================================================
27957
27958	/** //** ----= hasFiles	=--------------------------------------------------------------------------------------\**//** \
27959	*
27960	* 	Checks if form or data object contains files inputs.
27961	*
27962	*	@param	jQuery	form	- Form element to search within.
27963	*
27964	*	@return	SELF 
27965	*
27966	\**//** --------------------------------------------------------------------------------------= by Mr.V!T =----/** //**/
27967	hasFiles	: function ($source) {
27968		
27969		// If jQuery for form given - just searching for file inputs
27970		if ( isJQ($source) ) {
27971			if ( $source.find('INPUT[type=file]').length > 0 )
27972				return true;
27973		} //IF form given
27974		else {
27975			
27976			// Non modern browsers can't have files anyway
27977			if ( !this.isModern )
27978				return false;
27979			
27980			// Looking for files in dataObject
27981			for ( var $i in $source ) {
27982				
27983				// Doing className check in addition, 
27984				// because of sometimes weird brwosers passing data between windows
27985				if ( $source[$i] instanceof File || getClassOf($source[$i]) === 'File' )
27986					return true;
27987			} 
27988		} //IF dataObject
27989
27990		return false;
27991	}, //FUNC hasFiles
27992
27993	/** //** ----= postArray	=------------------------------------------------------------------------------\**//** \
27994	*
27995	* 	For internal use. Sends data object using common jQuery ajax. Does not supports progress monitoring 
27996	*	(no reason for arrays).
27997	*
27998	*	@return	SELF 
27999	*
28000	\**//** --------------------------------------------------------------------------------------= by Mr.V!T =----/** //**/
28001	postArray	: function () {
28002
28003		var self	= this;
28004		
28005		this.Method	= 'array';
28006		
28007		jQuery.ajax({
28008			type		: 'POST', 
28009			dataType	: 'text',
28010			async		: self.opt.async,
28011			context		: self,
28012			url		: self.controller,
28013			data		: self.sourceData,
28014			success		: self.ajaxSuccess,
28015			error		: self.ajaxError
28016		}); //OBJECT jQuery.ajax.settings
28017
28018		return this;
28019	}, //FUNC postArray
28020
28021	/** //** ----= postData	=--------------------------------------------------------------------------------------\**//** \
28022	*
28023	* 	For internal use. Sends data using formData object. Optionally uses xhr for progress monitoring.
28024	*
28025	*	@return	SELF 
28026	*
28027	\**//** --------------------------------------------------------------------------------------= by Mr.V!T =----/** //**/
28028	postData	: function () {
28029
28030		var self	= this;
28031		var post	= false;
28032		var $form	= this.sourceForm ? _jq(this.sourceForm) : false; 
28033		var $files	= false;
28034
28035		// Marking method as data
28036		this.Method	= 'data';
28037		
28038		// Disabling empty files if happen on form
28039		// Doing this to fix XHR multipart bug in some Safari versions
28040		// https://bugs.webkit.org/show_bug.cgi?id=184490
28041		
28042		if ( $form ) {
28043			
28044			// Looking for non disabled file inputs
28045			$files	= $form.find('input[type="file"]:not([disabled])');
28046			$files.each( function () {
28047			
28048				// Disabling input
28049				if ( this.files.length == 0 )
28050					jQuery(this).prop('disabled', true);
28051				
28052			}); //each file
28053			
28054		} //IF form provided
28055		
28056		// Need to generate FormData object
28057		// Creating FormData from form, if one given
28058		// Or using one if one come as sourceData
28059		// Otherwise generating one from dataObject
28060		
28061		if ( this.sourceData instanceof FormData ) {
28062			post = this.sourceData
28063		}
28063 //IF sourceData is FormData
28064		else if ( this.sourceForm ) {
28065			post = new FormData(this.sourceForm);
28066		} //IF form given
28067		else {
28068			post = new FormData();
28069			for ( var $i in this.sourceData )
28070				post.append($i, this.sourceData[$i]);
28071		} //IF dataObject given
28072
28073		// Restoring disabled file inputs (see bugfix above)
28074		if ( $files )
28075			$files.prop('disabled', false);
28076
28077		// Sending ajax using custom XHR
28078		jQuery.ajax({
28079			type		: 'POST', 
28080			dataType	: 'text',
28081			async		: self.opt.async,
28082			context		: self,
28083			url		: self.controller,
28084			data		: post,
28085			success		: self.ajaxSuccess,
28086			error		: self.ajaxError,
28087			
28088			// Disablong jQuery headers and source processing
28089			contentType	: false, 
28090			processData	: false,
28091			
28092			// Adding progress monitoring, upload listener will be added optionally if necessary
28093			// With function wrapper to have proper this
28094			xhr		: function () { return self.ajaxXHR() }
28095		}); //OBJECT jQuery.ajax.settings
28096		
28097		return this;
28098		
28099	}, //FUNC postData
28100
28101	/** //** ----= postFrame	=------------------------------------------------------------------------------\**//** \
28102	*
28103	* 	For internal use. Sends data using iframe send trick. Used to simulate ajax upload on classic systems. 
28104	*	Uses ajaxupload jQuery plugin if available. Uses server side progress monitoring if necessary.
28105	*
28106	*	@return	SELF 
28107	*
28108	\**//** --------------------------------------------------------------------------------------= by Mr.V!T =----/** //**/
28109	postFrame	: function () {
28110
28111		var self = this;
28112
28113		this.Method = 'frame';
28114		
28115		if ( !self.sourceForm )
28116			throw('Source form is not set.');
28117			
28118	// ---- Setting up progress ----
28119	
28120		// Saving test result, for later reuse
28121		var prg = this.opt.progressUrl && this.hasProgress(); 
28122	
28123		if ( prg ) {
28124	
28125			var sn = 'PRG' + randomString();
28126			
28127			// Adding additional inputs for monitoring on server side
28128			this.sourceElement.append('<input type="hidden" name="UPLOAD_IDENTIFIER" value="' + sn + '" />');
28129			this.sourceElement.append('<input type="hidden" name="APC_UPLOAD_PROGRESS" value="' + sn + '" />');
28130			
28131			// Resetting filler
28132			this.ajaxProgressStart();
28133			
28134		} //IF progress is set
28135	
28136	// ---- Posting ----	
28137		
28138		// Saving action and target, cuz ajaxUpload breaks them	sometimes	
28139		var action = this.sourceForm.action;
28140		var target = this.sourceForm.target;
28141
28142		jQuery.ajaxUpload ({
28143			type		: 'POST', 
28144			dataType	: 'text',
28145			url		: self.controller,
28146			secureuri	: false,
28147			uploadform	: self.sourceForm,
28148			// Simulating context with wrappers
28149			success		: function ( res, status ) { self.ajaxSuccess(res, status) },
28150			error		: function ( res, status ) { self.ajaxError(res, status) }
28151		}); //AJAX
28152		
28153	// ---- Monitoring progress ----	
28154
28155		if ( prg ) {
28156			
28157			// Bar should start progressing with additional delay, to do not overflow on small files
28158			self.prgTimeout = setTimeout( function () { 
28159				
28160				// Adding check timer
28161				self.prgInterval = setInterval( function() {
28162					
28163					jQuery.get(self.opt.progressUrl + sn, {}, function(data) {
28164
28165						self.ajaxProgress(data, self);
28166
28167						// If got 100 or error - stopping timer
28168						if ( data == '100' || !isNumeric(data) ) {
28169							clearInterval(self.prgInterval);
28170						} //IF done
28171
28172					}); //FUNC jQuery.get.callback
28173					
28174				}, self.opt.progressInterval); //FUNC setInterval.callback
28175			}, self.opt.progressDelay);  //FUNC setTimeout.callback
28176			
28177			// Cleaning progress inputs
28178			this.sourceElement.find('INPUT[name="APC_UPLOAD_PROGRESS"], INPUT[name="UPLOAD_IDENTIFIER"]').remove();
28179		} //IF progress set
28180		
28181	// ---- Cleaning form ----
28182		
28183		this.sourceForm.action = action;
28184		this.sourceForm.target = target;
28185		
28186		return this;
28187	}, //FUNC postFrame
28188
28189	/** //** ----= post	=--------------------------------------------------------------------------------------\**//** \
28190	*
28191	* 	Fires request with settings given.
28192	*
28193	*	@return	SELF
28194	*
28195	\**//** --------------------------------------------------------------------------------------= by Mr.V!T =----/** //**/
28196	post	: function () {
28197
28198	// ---- Checking source ----
28199
28200		var source 		= this.source;		// Just shortcut
28201		
28202		// Making sure that there is smth meaningfull
28203		if ( !source )
28204			source = {};
28205
28206		// Restting sources
28207		this.sourceElement	= false;		// jQuery pointer to form element	
28208		this.sourceForm		= false;		// DOM form element
28209		this.sourceData		= source;		// By default assuming data object or formData
28210
28211		// Converting jQuery selector to jQuery
28212		if ( typeof this.source == 'string' )
28213			source = jQuery(this.source);
28214			
28215		// Getting DOM form from jQuery
28216		if ( source instanceof jQuery ) {
28217			
28218			if ( source.length < 1 )
28219				throw('Source form is not found.');
28220			
28221			// Saving jQuery
28222			this.sourceElement = source;
28223			source = source.get(0);
28224		} //IF got jQuery
28225
28226		// At this step source should be either DOM form or data object
28227		// Checking it and setting sources
28228		// ToDo: tricky check for DOM form, improove
28229		if ( source.elements ) {
28230	
28231			// Trying to correct url using form action
28232			if ( !this.controller )
28233				this.controller = source.action; 
28234			
28235			this.sourceForm = source;
28236			
28237			// Serializing form as array anyway for callbacks
28238			this.sourceData = formToArray(source);
28239
28240			// Adding jQuery if not already there
28241			if ( !this.sourceElement )
28242				this.sourceElement = jQuery(source);
28243			
28244		} //If got form
28245
28246	// ---- Testing environment ----
28247
28248		// If still no controller (even after from action correction) - nothing to do
28249		if ( !this.controller )
28250			throw('Destination url is not set.');
28251
28252	// ---- Selecting method and sending ----
28253	
28254		// Triggering start event, passing self as param
28255		this.start(this);
28256	
28257		// In most cases postData will be used
28258		// Only reason to use tricky frame ajaxUpload is in old browser 
28259		// and files really present on form
28260		// Of course if ajaxUpload is available
28261		// So checking it in complexity order i.e. easyer checks goes first 
28262
28263		// Checking if custom processor is given
28264		if ( isFunction(this._processor) ) {
28265			this._processor(this);
28266			return this;
28267		} //IF custom processor
28268
28269		// Form data is always served with postData
28270		if ( this.isModern && this.sourceData instanceof FormData )
28271			return this.postData();
28272
28273		// If form or files were not given uploading with common jQuery
28274		// Non modern browser can't have files anyway
28275		if ( !this.sourceForm && !this.hasFiles(this.sourceData) )
28276			return this.postArray();
28277
28278		// If modern - using formData for all other cases
28279		if ( this.isModern )
28280			return this.postData();
28281
28282		// If form is not for files, and modern uploading is not available - uploading as array
28283		if ( this.sourceForm.enctype != 'multipart/form-data' ) 
28284			return this.postArray();
28285
28286		// Now looking for files on form (last chance to not use .postFrame() )
28287		if ( !this.hasFiles(this.sourceElement) )
28288			return this.postArray();
28289
28290		// Finally we tried everything, have to use ajaxUpload
28291		// Testing if ajaxUpload even exists first
28292		if ( !isSet(jQuery.ajaxUpload) )
28293			throw('Can\'t upload files with ajax on this browser.');
28294
28295		return this.postFrame();
28296	} //FUNC post
28297
28298	}); //OBJECT vAjax
28299	
28300	// Setting options on startup
28301	obj.options(options);
28302	
28303	return obj;
28304
28305} //CONSTRUCTOR vAjax
28306
28307// #### DEPRECATED #############################################################################################################
28308
28309var ajaxReturn = function (status_place, destination) {
28310	if (status_place) this.statusPlace = status_place;
28311	if (destination) this.controller = destination;
28312} //CONSTRUCTOR ajaxReturn
28313
28314ajaxReturn.prototype = {
28315	controller	: '',
28316	post		: {},
28317	ModalDialog	: '',
28318	ModalLoader	: '',
28319	statusPlace	: '',
28320	
28321	onSuccess	: function (data) {},
28322	onError		: function (data) {},
28323	onFail		: function (data) {},
28324	onSimple	: function (data) {},
28325	onStart		: function () {},
28326	onStatus	: function (html) {},
28327	onProgress	: function (proc) {},
28328	
28329	parseResult	: function (result) {
28330
28331		var res		= {};
28332		res._post	= this.post;
28333
28334		var data		= result.split('-=-CUT-=-');
28335		res._message	= data[0];
28336		if ( !res._message ) res._message = '';
28337		
28338		res._fullhead	= data.length != 1;
28339		
28340		if (res._fullhead) {
28341			var head		= JSON.parse(data[1]);
28342			res._major	= head.major;
28343			res._minor	= head.minor;
28344			res._status	= head.minor;
28345			res._fields	= head.fields;
28346//			res.data	= [];
28347			var di = 2;
28348			if ( res._fields.length != 0 ) {
28349				for (i = 0; i < res._fields.length; i++) {
28350					res[res._fields[i]] = data[di];
28351					di++;
28352				} //FOR each field
28353			} //IF was fields posted
28354		} //IF was full result
28355		return res;
28356	},//FUNC parseResult
28357
28358	/*--------------------------------------------------------------------------------*\
28359	|	DoStatus
28360	|----------------------------------------------------------------------------------
28361	| Updates status.
28362	| - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
28363	|	Params:
28364	|		message		string		- Status message to display.
28365	|		callback	funciton	- Funciton to call.
28366	|		data		MIXED		- Data to pass to callback function.
28367	|	Return:
28368	|				bool		- ALways FALSE.
28369	\*--------------------------------------------------------------= by Mr.V!T =-----*/
28370	DoStatus	: function (message, callback, data) {
28371		
28372		if ( this.statusPlace && message ) jQuery('#'+this.statusPlace).html( status_string(message) );
28373		
28374		if ( callback instanceof Function ) callback(data);
28375		
28376		this.doModals('finish');
28377		
28378		return false;
28379	}, //FUNC DoStatus
28380
28381	ajax_success	:  function (res) {
28382		var data = this.parseResult(res);
28383		
28384		if (this.statusPlace) jQuery('#'+this.statusPlace).html(data._message);
28385		
28386		var cb = this.onSimple;
28387		if ( data._fullhead ) 
28388			cb = ( data._major )? this.onSuccess : this.onError;
28389		
28390		this.DoStatus('', cb, data);
28391		
28392	}, //FUNC sucess
28393
28394	ajax_error	: function (message, res) {
28395		
28396		this.DoStatus(message, this.onFail, res);
28397		
28398	}, //FUNC error
28399
28400	postForm	: function (form_id) {
28401		var _ar = this;
28402		
28403		if (!_ar.controller) return this.DoStatus('Invalid destination. Aborting.');
28404		
28405		if ( !form_id ) return this.postData();
28406		
28407		var form = (form_id.elements)? form_id : document.getElementById(form_id);
28408		if ( !form.elements ) return this.DoStatus('Can\'t find data to POST. Aborting.');
28409		
28410		if ( form.enctype != 'multipart/form-data' ) return this.postData( formToArray(form) );
28411
28412		//if ( _ar.statusPlace ) insert_bar(_ar.statusPlace);
28413		
28414		this.doModals('start');
28415		
28416		this.onStart();
28417
28418		var old_action = form.action;
28419
28420		jQuery.ajaxUpload ({
28421			type		: 'POST', dataType : 'html',
28422			url		: _ar.controller,
28423			secureuri	: false,
28424			uploadform	: form,
28425			success		: function (res) {
28426				_ar.ajax_success(res);
28427			}, //FUNC success
28428			error		: function (res, status) {
28429				_ar.ajax_error(res.statusText, res.responseText);
28430			} //FUNC error
28431		}); //AJAX
28432		
28433		form.action = old_action;
28434		form.target = '';
28435		
28436		return false;
28437	}, //FUNC postForm
28438
28439	postData	: function (post_data) {
28440		
28441		if ( !post_data ) post_data = {};
28442		
28443	//	post_data = arrayToPost(post_data);
28444		
28445		this.post = post_data;
28446		var _ar = this;
28447
28448		//if ( _ar.statusPlace ) insert_bar(_ar.statusPlace);
28449		
28450		this.doModals('start');
28451		
28452		if (!_ar.controller) return this.DoStatus('Invalid destination. Aborting.');
28453
28454		this.onStart();
28455
28456		jQuery.ajax({
28457			type	: 'POST', dataType: 'html',
28458			url	: _ar.controller,
28459			data	: post_data,
28460			success		: function (res) {
28461				_ar.ajax_success(res);
28462			}, //FUNC success
28463			error		: function (res, status) {
28464				_ar.ajax_error(res.statusText, res.responseText);
28465			} //FUNC error
28466		}); //AJAX
28467		
28468		return false;
28469	}, //FUNC postData
28470
28471	postFormData	: function (post_data) {
28472		
28473		this.post	= post_data;
28474		var _ar		= this;
28475		var self	= this;
28476
28477		this.doModals('start');
28478		
28479		if (!_ar.controller) return this.DoStatus('Invalid destination. Aborting.');
28480
28481		this.onStart();
28482
28483		jQuery.ajax({
28484			type		: 'POST', dataType: 'html',
28485			url		: _ar.controller,
28486			data		: post_data,
28487			contentType	: false,
28488			processData	: false,
28489
28490			success		: function (res) {
28491				_ar.ajax_success(res);
28492			}, //FUNC success
28493			
28494			error		: function (res,status) {
28495				_ar.ajax_error(res.statusText, res.responseText);
28496			}, //FUNC error
28497
28498			// Attempting to alter xhr to add progress listener			
28499			xhr		: function() {
28500				
28501				var req		= $.ajaxSettings.xhr();
28502				
28503				if (req) {
28504					
28505					req.upload.addEventListener('progress', function(e) {
28506
28507						if ( e.lengthComputable ) {
28508							var proc = (e.loaded * 100) / e.total;
28509							
28510							self.onProgress( Math.floor(proc) );
28511							
28512						} //IF there is progress available
28513
28514					}, false); //FUNC XHR.upload.onProgress
28515				
28516				} //IF xhr returned
28517				
28518				return req;
28519			} //FUNC xhr
28520			
28521		}); //AJAX
28522		
28523		return false;
28524	}, //FUNC postFormData
28525	
28526	/*--------------------------------------------------------------------------------*\need
28527	|	post
28528	|----------------------------------------------------------------------------------
28529	| Autoselects best post method and posts source.
28530	| - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
28531	|	Params:
28532	|		source	MIXED	- Data source.
28533	|	Return:
28534	|			bool	- Always FALSE.
28535	\*--------------------------------------------------------------= by Mr.V!T =-----*/
28536	post	: function (source) {
28537		
28538		if ( typeof source == 'string' ) return this.postForm(source);
28539		if ( source.elements ) return this.postForm(source);
28540
28541		if ( source instanceof FormData ) return this.postFormData(source);
28542
28543		return this.postData(source);
28544	}, //FUNC post
28545
28546	/*--------------------------------------------------------------------------------*\
28547	|	setModals
28548	|----------------------------------------------------------------------------------
28549	| Set modal windows parameters.
28550	| - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
28551	|	Params:
28552	|		dialog	string	- ID of window to use as Dialog.
28553	|		loader	string	- ID of window to use as loader window.
28554	|	Return:
28555	|			SELF
28556	\*--------------------------------------------------------------= by Mr.V!T =-----*/
28557	setModals	: function (dialog, loader) {
28558		
28559		this.ModalDialog = dialog;
28560		
28561		if ( !loader ) return this;
28562		
28563		tmp = document.getElementById(loader);
28564		if ( !tmp ) return this;
28565		
28566		this.ModalLoader = loader;
28567		
28568		return this;
28569	}, //FUNC setModals
28570
28571	/*--------------------------------------------------------------------------------*\
28572	|	doModals
28573	|----------------------------------------------------------------------------------
28574	| Displays or hides modals windows.
28575	| - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
28576	|	Params:
28577	|		mode	string	- Status mode:
28578	|				  'start' - Set starting mode.
28579	|				  *other* - Finished mode.
28580	|	Return:
28581	|		NULL
28582	\*--------------------------------------------------------------= by Mr.V!T =-----*/
28583	doModals	: function ( mode ) {
28584		
28585		if ( mode == 'start' ) {
28586			if ( this.ModalLoader ) { 
28587				jQuery('#'+this.ModalDialog).jqm();
28588				jQuery('#'+this.ModalDialog).jqmHide();
28589				jqmShowCentered('#'+this.ModalLoader);
28590			} //IF loader is set
28591			
28592			if ( this.ModalDialog && !this.ModalLoader ) jqmShowCentered('#'+this.ModalDialog);
28593		} else { //IF
28594			if ( this.ModalLoader ) jQuery('#'+this.ModalLoader).jqmHide();
28595			if ( this.ModalDialog ) jqmShowCentered('#'+this.ModalDialog);
28596		} //IF
28597		
28598	} //FUNC doModals
28599
28600} //CLASS ajaxReturn
28601
28602/** ************************************************************************************************************************ **\
28603* 
28604*	Set of classes and functions that will help create debugging consoles.
28605* 
28606* 	@package	VIT-Lib
28607* 	@subpackage	Debug
28608*
28609* 	@license	MIT License
28610*			Permission  is  hereby  granted,  free  of  charge, to any person obtaining a copy of this software and
28611*			associated documentation files (the "Software"), to deal in the Software without restriction, including
28612*			without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
28613*			copies  of  the  Software, and to permit persons to whom the Software is furnished to do so, subject to
28614*			the following conditions:
28615*			
28616*			The  above  copyright  notice and this permission notice shall be included in all copies or substantial
28617*			portions of the Software.
28618*			
28619*			THE  SOFTWARE  IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT
28620*			LIMITED  TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
28621*			NO  EVENT  SHALL  THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
28622*			WHETHER  IN  AN  ACTION  OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
28623*			SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
28624*
28625*	@author		Victor Denisenkov aka Mr.V!T
28626*	@copyright	Copyright (c) 2008+ Victor Denisenkov aka Mr.V!T
28627*	@version	1.0
28628* 
28629\** ************************************************************************************************************************ **/
28630
28631/** //** ----= OBJECT vConsole	=--------------------------------------------------------------------------------------\**//** \
28632*
28633* 	Debug console.
28634*
28635* 	@package	VIT-Lib
28636* 	@subpackage	Debug
28637* 	@category	Helper
28638*
28639\**//** ----------------------------------------------------------------------------------------------= by Mr.V!T =----/** //**/
28640function vConsole ($options) {
28641
28642var $class = {
28643
28644	enabled			: true,		// Working state. If disabled - doing nothing, untill it's manually enabled.
28645
28646	width			: 600,		// Default widndow width
28647	zIndex			: 6000,		// zIndex to use for window
28648
28649	panels			: {},		// Custom panels list and their properties/elements
28650	
28651	dom			: {		// Set of valuable elements as jQuery shortcuts and dom elements
28652		window			: false,	// Main window object
28653		panels			: false,	// Panels container (dumps watches)
28654		console			: false,	// Common console (manual dumps)
28655	}, //dom	
28656
28657	autoupdate		: false,	// Set TRUE to enable watch timer. It will cause updates of watched elements in specified inteval.
28658	updateInterval		: 70,		// Autoupdate interval. Lower value - more lag, but more accurate readings.
28659	updateDelay		: 1,		// Delay in ms between nearest updates. Increase if updates cause lags.
28660						// Having even 1ms, causes another thread exec, and multiupdates filetering.
28661
28662	interval		: false,	// Autoupdate interval object
28663	timer			: false,	// Update delay timer object
28664	
28665	dumper			: false,	// Variable dumper used to style debug
28666
28667// ==== SET/GET ================================================================================================================
28668
28669	/** //** ----= set	=--------------------------------------------------------------------------------------\**//** \
28670	*
28671	*	Initiates self parameters from given options array.
28672	*
28673	*	@return SELF
28674	*
28675	\**//** --------------------------------------------------------------------------------------= by Mr.V!T =----/** //**/
28676	set		: function ($name, $value) {
28677		
28678		// ToDo: .set() in basic class
28679		// ToDo: proper events extension support
28680
28681		var $o = {};
28682
28683		// Extending from object, so if pair given - forming object from there
28684		// Otherwise name is needed object
28685		if ( isStr($name) )
28686			$o[$name] = $value;
28687		else 		
28688			$o = $name;
28689			
28690		jQuery.extend(this, $o);
28691		
28692		// init/deinit
28693		if ( this.enabled )
28694			this.init();
28695		else
28696			this.deinit();
28697		
28698		return this;
28699	}, //FUNC set	
28700
28701	/** //** ----= get	=--------------------------------------------------------------------------------------\**//** \
28702	*
28703	*	Returns property.
28704	*
28705	*	@return SELF
28706	*
28707	\**//** --------------------------------------------------------------------------------------= by Mr.V!T =----/** //**/
28708	get		: function ($name) {
28709		// ToDo: .get() in basic class with getters support
28710		// ToDo: proper events extension support
28711		return this[$name];
28712	}, //FUNC get	
28713
28714	/** //** ----= toggle	=--------------------------------------------------------------------------------------\**//** \
28715	*
28716	*	Toggles console.
28717	*
28718	*	@return SELF
28719	*
28720	\**//** --------------------------------------------------------------------------------------= by Mr.V!T =----/** //**/
28721	toggle		: function ($state) {
28722
28723		if ( isUndefined($state) )
28724			$state = !this.enabled;
28725
28726		return this.set('enabled', $state);
28727	}, //FUNC toggle	
28728
28729	/** //** ----= show	=--------------------------------------------------------------------------------------\**//** \
28730	*
28731	*	Shows console.
28732	*
28733	*	@return SELF
28734	*
28735	\**//** --------------------------------------------------------------------------------------= by Mr.V!T =----/** //**/
28736	show		: function () {
28737		return this.toggle(true);
28738	}, //FUNC show	
28739
28740	/** //** ----= hide	=--------------------------------------------------------------------------------------\**//** \
28741	*
28742	*	Hides console.
28743	*
28744	*	@return SELF
28745	*
28746	\**//** --------------------------------------------------------------------------------------= by Mr.V!T =----/** //**/
28747	hide		: function () {
28748		return this.toggle(false);
28749	}, //FUNC hide	
28750
28751// ==== INIT ===================================================================================================================
28752
28753	/** //** ----= init	=--------------------------------------------------------------------------------------\**//** \
28754	*
28755	*	Initiates console, creates panels and prepares for output.
28756	*
28757	\**//** --------------------------------------------------------------------------------------= by Mr.V!T =----/** //**/
28758	init		: function () {
28759		
28760		// Setting up dumper
28761		if ( isEmpty(this.dumper) )
28762			this.dumper = vDump({
28763				'useHtml'	: true, 
28764				'dumpFunctions'	: false
28765			}); //vDump
28766			
28767		// Initiating elements
28768		this.initWindow();
28769		this.initPanels();
28770
28771		// Initial update, which will also console with info
28772		this.update();
28773
28774		// Triggering autoupdate if necessary
28775		this.setAutoupdate(this.autoupdate);
28776	}, //FUNC init	
28777
28778	/** //** ----= deinit	=--------------------------------------------------------------------------------------\**//** \
28779	*
28780	*	Deinitiates console, removes dom and stops timers.
28781	*
28782	\**//** --------------------------------------------------------------------------------------= by Mr.V!T =----/** //**/
28783	deinit		: function () {
28784
28785		// Removing dumper
28786		this.dumper = false;
28787		
28788		// Removing elements
28789		this.deinitPanels();
28790		this.deinitWindow();
28791
28792		// Updating autoupdate :)
28793		this.setAutoupdate(this.autoupdate);
28794	}, //FUNC deinit
28795
28796// ==== DOM ====================================================================================================================
28797
28798	/** //** ----= initWindow	=------------------------------------------------------------------------------\**//** \
28799	*
28800	*	Initiates main window and common panel.
28801	*
28802	\**//** --------------------------------------------------------------------------------------= by Mr.V!T =----/** //**/
28803	initWindow	: function () {
28804		
28805	 	// Checking if draggable available
28806	 	var $isDraggable = isFunction(jQuery().draggable);
28807		
28808		// Compiling window css (it is updated anyway)
28809		var $css = {
28810			'position'	: 'fixed',
28811
28812			'left'		: 0,
28813			'top'		: 0,
28814			'width'		: this.width,
28815
28816			'z-index'	: 6000,
28817			'cursor'	: $isDraggable ? 'move' : 'default',
28818		}; // $css
28819
28820		// Creating window, or updating if created
28821		if ( this.dom.window ) {
28822
28823			this.dom.window.css($css);
28824
28825		} else {
28826	
28827			this.dom.window = jQuery('<div class="debug"></div>').css($css);
28828		
28829			// Creating common console wrapper
28830			this.dom.console = jQuery('<div></div>')
28831			//	.css({'overflow' : 'scroll'}) //css
28832				.appendTo(this.dom.window)
28833			; //jQuery.debug
28834			
28835			// Making window draggable if possible
28836			if ( $isDraggable )
28837				this.dom.window	
28838					.draggable('destroy')
28839					.draggable({});	
28840	
28841			this.dom.window.appendTo('body');
28842		} //IF new window
28843		
28844	}, //FUNC initWindow
28845
28846	/** //** ----= deinitWindow	=------------------------------------------------------------------------------\**//** \
28847	*
28848	*	Deinitiates main window.
28849	*
28850	\**//** --------------------------------------------------------------------------------------= by Mr.V!T =----/** //**/
28851	deinitWindow	: function () {
28852
28853		// Nothing to do if removed already
28854		if ( !this.dom.window )
28855			return;
28856
28857		// Destroying draggable
28858		if ( this.dom.window.is('.ui-draggable') )
28859			this.dom.window.draggable('destroy');
28860		
28861		// Removing window element
28862		this.dom.window.remove();
28863		
28864		// Cleaning self
28865		this.dom.window = false;
28866
28867	}, //FUNC deinitWindow 
28868
28869// ---- PANELS -----------------------------------------------------------------------------------------------------------------
28870
28871	/** //** ----= initPanels	=------------------------------------------------------------------------------\**//** \
28872	*
28873	*	Initiates panels.
28874	*
28875	\**//** --------------------------------------------------------------------------------------= by Mr.V!T =----/** //**/
28876	initPanels	: function () {
28877		
28878		// Creating panels wrapper
28879		if ( !this.dom.panels )
28880			this.dom.panels = jQuery('<div></div>')
28881			//	.css({}) //css
28882				.prependTo(this.dom.window)
28883			; //jQuery.debug
28884	
28885	 	// Adding/updating custom panels, converting ones which given as arrays
28886		for ( var $i in this.panels )
28887			if ( isArray(this.panels[$i]) )
28888				this.setPanel($i);
28889			else
28890				this.initPanel(this.panels[$i]);
28891		
28892	}, //FUNC initPanels
28893
28894	/** //** ----= deinitPanels	=------------------------------------------------------------------------------\**//** \
28895	*
28896	*	Initiates panels.
28897	*
28898	\**//** --------------------------------------------------------------------------------------= by Mr.V!T =----/** //**/
28899	deinitPanels	: function () {
28900	
28901		// Nothing to do if no panels already
28902		if ( !this.dom.panels )
28903			return;
28904
28905		// Clearing panels
28906		for ( var $i in this.panels )
28907			this.deinitPanel(this.panels[$i]);
28908		
28909		// Removing element
28910		this.dom.panels.remove();
28911		
28912		// Cleaning self
28913		this.dom.panels = false;
28914		
28915	}, //FUNC deinitPanels
28916
28917	/** //** ----= setPanel	=--------------------------------------------------------------------------------------\**//** \
28918	*
28919	*	Adds named debug panel.
28920	*
28921	*	@param	string	$name		- Panel name to add.
28922	*	@param	int	[$size]		- Panel relative size (width) percentage.
28923	*	@param	object	[$watchObject]	- Object to watch in this panel.
28924	*	@param	array	[$watchProps]	- Watch only this properties from this object.
28925	*
28926	*	@return SELF
28927	*
28928	\**//** --------------------------------------------------------------------------------------= by Mr.V!T =----/** //**/
28929	setPanel	: function ($name, $size, $watchObject, $watchProps) {
28930
28931		// Checking for panel params
28932		var $panel = this.panels[$name] || {}; 	
28933
28934		// Converting array
28935		if ( isArray($panel) ) {
28936			// Setting params from that array
28937			$size		= $size || $panel[0];
28938			$watchObject	= $watchObject || $panel[1];
28939			$watchProps	= $watchProps || $panel[2];
28940			
28941			// Making sure it's object now, and resetting
28942			$panel		= {};
28943		} //IF array given
28944
28945		// Setting up
28946		$panel.name	= $name;
28947		$panel.title	= $name.capitalize();
28948		$panel.size	= $size || 100;
28949		$panel.watch	= $watchObject || false;
28950		$panel.props	= $watchProps || false;
28951		
28952		// Resaving panel back, in case it's new
28953		this.panels[$name] = $panel;
28954		
28955		// Done if not enabled
28956		if ( !this.enabled )
28957			return this;
28958		
28959		// Initiating panel
28960		this.initPanel($panel);
28961		
28962		return this;
28963	}, //FUNC setPanel	
28964
28965	/** //** ----= removePanel	=------------------------------------------------------------------------------\**//** \
28966	*
28967	*	Removes specified panel from console.
28968	*
28969	*	@param	string	$panel	- Panel name to remove.
28970	*
28971	*	@return SELF
28972	*
28973	\**//** --------------------------------------------------------------------------------------= by Mr.V!T =----/** //**/
28974	removePanel	: function ($panel) {
28975		
28976		if ( !this.panels[$panel] )
28977			return;
28978	
28979		// Deinitiating it first
28980		this.deinitPanel(this.panels[$panel]);
28981		
28982		// Removing from list
28983		delete(this.panels[$panel]);
28984		
28985		return this;
28986	}, //FUNC removePanel
28987	
28988	/** //** ----= initPanel	=------------------------------------------------------------------------------\**//** \
28989	*
28990	*	Initiates given panel dom and events.
28991	*
28992	*	@param	object	$panel		- Panel to init. Should be link from internal .panels list.
28993	*	
28994	\**//** --------------------------------------------------------------------------------------= by Mr.V!T =----/** //**/
28995	initPanel	: function ($panel) {
28996
28997		// Checking if panel should take full size or it's floating
28998		var $isFull = $panel.size == 100;
28999		
29000		// Compiling css
29001		var $css = {
29002			'float'		: $isFull ? 'none' : 'left',
29003			'clear'		: $isFull ? 'both' : 'none',
29004			'width'		: $isFull ? 'auto' : $panel.size+'%',
29005			'overflow'	: 'hidden',
29006			'border-bottom' : '1px solid gray',
29007		} // $css
29008		
29009		// Creating or updating element
29010		if ( $panel.el ) {
29011
29012			$panel.el.css($css);
29013
29014		} else { //IF existing panel
29015
29016			$panel.el = jQuery('<div></div>')
29017				.css($css) //css
29018				.appendTo(this.dom.panels)
29019			; //jQuery.$panel
29020
29021		} //IF new panel
29022		
29023	}, //FUNC initPanel
29024
29025	/** //** ----= deinitPanel	=------------------------------------------------------------------------------\**//** \
29026	*
29027	*	Initiates given panel dom and events.
29028	*
29029	*	@param	object	$panel		- Panel to deinit. Should be link from internal .panels list.
29030	*
29031	\**//** --------------------------------------------------------------------------------------= by Mr.V!T =----/** //**/
29032	deinitPanel	: function ($panel) {
29033
29034		if ( !$panel.el )
29035			return;
29036
29037		// Removing panel from dom
29038		$panel.el.remove();
29039		
29040		// Cleaning panel element
29041		$panel.el = false;
29042		
29043	}, //FUNC deinitPanel
29044
29045// ==== DUMP ===================================================================================================================
29046
29047	/** //** ----= dumpTo	=--------------------------------------------------------------------------------------\**//** \
29048	*
29049	*	Dumps given var into specified console panel (replaces contents). Uses delays to output.
29050	*	Precise output: no output delay.
29051	*
29052	*	@param	string	$panel	- Panel name to input into.
29053	*	@param	MIXED	$var	- Variable to dump
29054	*
29055	*	@return SELF
29056	*
29057	\**//** --------------------------------------------------------------------------------------= by Mr.V!T =----/** //**/
29058	dumpTo		: function ($panel, $var) {
29059		
29060		// Doing nothing if not enabled
29061		if ( !this.enabled )
29062			return this;
29063
29064		// Panel can be ether string or object directly
29065		if ( isStr($panel) )
29066			$panel = this.panels[$panel];
29067			
29068		if ( !$panel || !$panel.el )
29069			return this;
29070		
29071		$panel.el.html( this.dumper.dumpCap($panel.title, $var) );
29072		
29073		return this;
29074	}, //FUNC dumpTo
29075
29076	/** //** ----= dump	=--------------------------------------------------------------------------------------\**//** \
29077	*
29078	*	Dumps given var into common console panel (adds at bottom).
29079	*	Precise output: no output delay.
29080	*
29081	*	@param	MIXED	$var	- Variable to dump
29082	*
29083	*	@return SELF
29084	*
29085	\**//** --------------------------------------------------------------------------------------= by Mr.V!T =----/** //**/
29086	dump		: function ($var) {
29087
29088		// Doing nothing if not enabled
29089		if ( !this.enabled )
29090			return this;
29091		
29092		var $con = this.dom.console; 
29093		$con.html( $con.html() + this.dumper.dump($var) );
29094
29095		return this;
29096	}, //FUNC dump
29097
29098	/** //** ----= clear	=--------------------------------------------------------------------------------------\**//** \
29099	*
29100	*	Clears common panel contents.
29101	*	Precise output: no output delay.
29102	*
29103	*	@return SELF
29104	*
29105	\**//** --------------------------------------------------------------------------------------= by Mr.V!T =----/** //**/
29106	clear		: function () {
29107
29108		// Doing nothing if not enabled
29109		if ( !this.enabled )
29110			return this;
29111
29112		var $con = this.dom.console; 
29113		$con.html('');
29114		
29115		return this;
29116	}, //FUNC clear
29117
29118// ---- UPDATES ----------------------------------------------------------------------------------------------------------------
29119
29120	/** //** ----= updateNow	=------------------------------------------------------------------------------\**//** \
29121	*
29122	*	Imideately updates watched objects (no update delay).
29123	*
29124	*	@return SELF
29125	*
29126	\**//** --------------------------------------------------------------------------------------= by Mr.V!T =----/** //**/
29127	updateNow	: function () {
29128		
29129		// Doing nothing if not enabled
29130		if ( !this.enabled )
29131			return this;
29132		
29133	 	// Checking panels, which can be updated
29134		for ( var $i in this.panels ) {
29135			var $panel = this.panels[$i];
29136
29137			// Skipping if panel was not created somehow
29138			if ( !$panel.el )
29139				continue;
29140			
29141			// Skipping if not watchable
29142			if ( $panel.watch === false )
29143				continue;
29144				
29145			// Dumping whole object if no props specified
29146			if ( $panel.props === false ) {
29147				// Just dump into panel
29148				$panel.el.html( this.dumper.dumpCap($panel.title, $panel.watch) );
29149			} else { //IF whole object
29150				
29151				var $html = '';
29152				
29153				// Looping through all vars and compiling dump
29154				for ( var $j in $panel.props ) {
29155					
29156					var $p = $panel.props[$j];
29157					
29158					// Skipping functions
29159					if ( isFunction($p) )
29160						continue;
29161					
29162					$html += this.dumper.dumpCap($p.capitalize(), $panel.watch[$p]);
29163				} //FOR each $j
29164			
29165				$panel.el.html( $html );
29166			} //IF properties specified
29167			
29168		} //FOR each panel
29169		
29170		return this;
29171	}, //FUNC updateNow	
29172
29173	/** //** ----= update	=--------------------------------------------------------------------------------------\**//** \
29174	*
29175	*	Updates watched objects, with small delay (no lag update).
29176	*
29177	*	@return SELF
29178	*
29179	\**//** --------------------------------------------------------------------------------------= by Mr.V!T =----/** //**/
29180	update	: function () {
29181
29182		// Doing nothing if not enabled
29183		if ( !this.enabled )
29184			return this;
29185		
29186		var $this = this;
29187		
29188		// Clearing prev delay, avoiding double updates
29189		clearTimeout(this.timer);
29190		
29191		// Updating after small delay
29192		this.timer = setTimeout( function () {
29193			$this.updateNow();
29194		}, this.updateDelay);
29195		
29196		return this;
29197	}, //FUNC update
29198
29199	/** //** ----= setAutoupdate	=------------------------------------------------------------------------------\**//** \
29200	*
29201	*	Enables or disables autoupdate timer. Always disables if console disabled
29202	*
29203	*	@param	bool	$state	- Set TRUE to enable, FALSE otherwise.
29204	*
29205	*	@return SELF
29206	*
29207	\**//** --------------------------------------------------------------------------------------= by Mr.V!T =----/** //**/
29208	setAutoupdate	: function ($state) {
29209		
29210		$state = !isEmpty($state);
29211		var $this = this;
29212		
29213		this.autoupdate = $state;
29214
29215		// Clearing interval anyway, to be extra safe
29216		clearInterval(this.interval);
29217		
29218		// Nothing to do if no autoupdate
29219		if ( !this.enabled || !this.autoupdate )
29220			return this;
29221			
29222		// Making sure update interval is bigget that delay
29223		var $upd = this.updateInterval > this.updateDelay ? this.updateInterval : this.updateDelay + 5;
29224		
29225		// Setting up interval
29226		this.interval = setInterval( function () {
29227			$this.update();
29228		}, $upd);
29229			
29230		return this;
29231	}, //FUNC setAutoupdate
29232
29233}; //CLASS mwImgEd
29234
29235	// Applying options
29236	$class.set($options);
29237	
29238	// Adding events
29239	$class = vEventObject([], $class);
29240
29241	return $class; 
29242} //CONSTRUCTOR vConsole
29243
29244CMS_ALIAS = 'cms3t';CMS_RES = 'res';CMS_JS = 'js';CMS_CSS = 'css';CMS_IMAGES = 'images';
29245/** //** ----= CLASS mwUrl	=--------------------------------------------------------------------------------------\**//** \
29246*
29247* 	Morweb urls compilation helper.
29248*
29249* 	@package	Morweb
29250* 	@subpackage	Core
29251* 	@category	helper
29252*
29253\**//** ---------------------------------------------------------------------------= by Mr.V!T @ Morad Media Inc. =----/** //**/
29254function mwUrl ($section) {
29255return {
29256	
29257	section		: $section || false,	// Sectional url. Set TRUE for sectional core resoureces (applicable only for resources).
29258	_full		: false,		// Set TRUE to generate full url (inluding protocol and domain). Usefull for emails and standalone pages.
29259	_ssl		: null,			// HTTPS use for full urls, set TRUE to force, FALSE to force non SSL, NULL (default) for autodetect.
29260	_salt		: false,		// Set TRUE or give random string value to prevent browser caching, adding salt to url. 
29261
29262/* ==== SETUP =============================================================================================================== */ 	
29263	
29264	/** //** ----= full	=--------------------------------------------------------------------------------------\**//** \
29265	*
29266	*	Full mode switcher.	
29267	*
29268	* 	@param	bool	[$value]	- Value to set.
29269	* 	@param	bool	[$ssl]		- Force ssl usage.
29270	* 
29271	*	@return	SELF
29272	*
29273	\**//** -------------------------------------------------------------------= by Mr.V!T @ Morad Media Inc. =----/** //**/
29274	full		: function ($value, $ssl) {
29275		
29276		$value		= setDefault($value, true);
29277			
29278		this._full	= $value;
29279		this._ssl	= $ssl;
29280		
29281		return this;
29282	}, //FUNC full
29283	
29284	/** //** ----= salt	=--------------------------------------------------------------------------------------\**//** \
29285	*
29286	*	Initiates url salting. Use with precaution - this dastically slows down site loading speed.	
29287	*
29288	* 	@param	MIXED	[$value]	- Set TRUE to randomize salt, or random string value to preset hash value.
29289	* 
29290	*	@return	SELF
29291	*
29292	\**//** -------------------------------------------------------------------= by Mr.V!T @ Morad Media Inc. =----/** //**/
29293	salt		: function ($value) {
29294
29295		$value		= setDefault($value, true);
29296
29297		this._salt	= ($value === true) ? randomString() : $value;
29298		
29299		return this;
29300	}, //FUNC salt
29301
29302/* ==== RESOURCES =========================================================================================================== */ 	
29303
29304	/** //** ----= res	=--------------------------------------------------------------------------------------\**//** \
29305	*
29306	*	Miscellaneous system resource load. Always uses direct load.	
29307	*
29308	* 	@param	string	- Path segments to compile.
29309	* 
29310	*	@return	SELF
29311	*
29312	\**//** -------------------------------------------------------------------= by Mr.V!T @ Morad Media Inc. =----/** //**/
29313	res		: function () {
29314		var $arr = Array.prototype.slice.call(arguments);
29315		return this.compile( [CMS_ALIAS, CMS_RES].concat($arr) );
29316	}, //FUNC res
29317	
29318	/** //** ----= js	=--------------------------------------------------------------------------------------\**//** \
29319	*
29320	*	JS resource url. For system resource uses direct load by default.	
29321	*
29322	* 	@param	string	- Path segments to compile.
29323	* 
29324	*	@return	SELF
29325	*
29326	\**//** -------------------------------------------------------------------= by Mr.V!T @ Morad Media Inc. =----/** //**/
29327	js		: function () {
29328		return this.compile( this.resSegments(CMS_JS, arguments) );
29329	}, //FUNC js
29330	
29331	/** //** ----= css	=--------------------------------------------------------------------------------------\**//** \
29332	*
29333	*	CSS resource url. For system resource uses direct load by default.	
29334	*
29335	* 	@param	string	- Path segments to compile.
29336	* 
29337	*	@return	SELF
29338	*
29339	\**//** -------------------------------------------------------------------= by Mr.V!T @ Morad Media Inc. =----/** //**/
29340	css		: function () {
29341		return this.compile( this.resSegments(CMS_CSS, arguments) );
29342	}, //FUNC css
29343	
29344	/** //** ----= images	=--------------------------------------------------------------------------------------\**//** \
29345	*
29346	*	Images resource url. For system resource uses direct load by default.	
29347	*
29348	* 	@param	string	$path	- Path parts to compile.
29349	* 
29350	*	@return	SELF
29351	*
29352	\**//** -------------------------------------------------------------------= by Mr.V!T @ Morad Media Inc. =----/** //**/
29353	images		: function () {
29354		return this.compile( this.resSegments(CMS_IMAGES, arguments) );
29355	}, //FUNC images
29356	
29357	/** //** ----= image	=--------------------------------------------------------------------------------------\**//** \
29358	*
29359	*	Image resizer url.	
29360	*
29361	* 	@param	string	$path	- Image path.
29362	* 
29363	*	@return	SELF
29364	*
29365	\**//** -------------------------------------------------------------------= by Mr.V!T @ Morad Media Inc. =----/** //**/
29366	image		: function ($path, $arguments) {
29367		
29368		$arguments = $arguments || [];
29369		
29370		// Hardcoded resizer path, which always present
29371		var $url = this.compile('get/files/image', $path);
29372		
29373		// Nothing else to do, if no arguments given
29374		if ( isEmpty($arguments) )
29375			return $url;
29376		
29377		// If arguments provided - compiling GET and appending to url
29378		$url += '?'+implode('&', $arguments);
29379		
29380		return $url;		
29381	}, //FUNC image
29382
29383/* ==== CONTROLLERS ========================================================================================================= */ 	
29384
29385	// ToDo: ajax/call funcitons for sectional controllers calls.
29386
29387/* ==== HELPERS ============================================================================================================= */ 	
29388
29389	/** //** ----= resSegments	=------------------------------------------------------------------------------\**//** \
29390	*
29391	*	Prepares resource segments, based on current settings. 
29392	*	Concatenates resources segments with provided path segments.
29393	*
29394	* 	@param	string	$resource	- Resource type path.
29395	* 	@param	array	[$segments]	- Additional segments to concat with.
29396	* 
29397	*	@return	SELF
29398	*
29399	\**//** -------------------------------------------------------------------= by Mr.V!T @ Morad Media Inc. =----/** //**/
29400	resSegments	: function  ($resource, $segments) {
29401
29402		$resource = $resource || '';
29403
29404		// Converting segments into array, in case of arguments object given		
29405		$segments = Array.prototype.slice.call($segments);
29406		
29407		// Adding section segments
29408		var $res = [];
29409		
29410		// Direct resources are using alias
29411		if ( isEmpty(this.section) )
29412			$res.push(CMS_ALIAS);
29413
29414		// Pointing to resource			
29415		$res.push(CMS_RES);
29416		
29417		// Adding section if specified
29418		if ( isString(this.section) )
29419			$res.push(this.section);
29420		
29421		// Finishing with resource segment
29422		$res.push($resource);
29423		
29424		// Concatenating with segments
29425		$res = $res.concat($segments);
29426		
29427		return $res;
29428	}, //FUNC resSegments
29429
29430	/** //** ----= compile	=--------------------------------------------------------------------------------------\**//** \
29431	*
29432	*	Compiles url from given URI segments. Parts can be passed as array, or as multiple funciton arguments.
29433	*
29434	* 	@param	string	$segments	- URI segments to compile.
29435	* 
29436	*	@return	SELF
29437	*
29438	\**//** -------------------------------------------------------------------= by Mr.V!T @ Morad Media Inc. =----/** //**/
29439	compile		: function  ($segments) {
29440	
29441		var $seg = isArray($segments) ? $segments : arguments;
29442		
29443		// Concatenating each non empty segment, trimming slashes and spaces
29444		var $res = '';
29445		for ( var $i in $seg ) {
29446			$s = $seg[$i];
29447			
29448			// Making sure strings come
29449			if ( !isString($s) )
29450				continue;
29451			
29452			// Timming garbage
29453			$s = $s.trim(' /\\');
29454			
29455			// Skipping empties
29456			if ( isEmpty($s) )
29457				continue;
29458			
29459			// Concatenating with leading '/', which always present
29460			$res += '/'+$s;
29461			
29462		} //FOR each segment
29463
29464		// Adding domain if specified
29465		if ( this._full )
29466			$res = fullURL($res, this._ssl);
29467
29468		// Adding salt if specified
29469		if ( this._salt )
29470			$res += '?'+this._salt;
29471			
29472		// Done.
29473		return $res;
29474	}, //FUNC compile
29475	
29476}} //CLASS mwUrl
29477
29478/** //** ----= ___	=----------------------------------------------------------------------------------------------\**//** \
29479*
29480*	Shortcut to console.log()
29481*
29482*	@param		MIXED	*	- Variables to dump.
29483*
29484\**//** ----------------------------------------------------------------------------------------------= by Mr.V!T =----/** //**/
29485function ___() {
29486	
29487	console.log.apply(console, arguments);
29488	
29489} //FUNC ___
29490
29491function mwError (msg) {
29492	return '<div class="status error">' + msg + '</div>';
29493} //FUNC mwError
29494
29495function mwSuccess (msg) {
29496	return '<div class="status success">' + msg + '</div>';
29497} //FUNC mwSuccess
29498
29499function mwHint (msg) {
29500	return '<div class="status hint">' + msg + '</div>';
29501} //FUNC mwHint
29502
29503function mwWarning (msg) {
29504	return '<div class="status warning">' + msg + '</div>';
29505} //FUNC mwWarning
29506
29507function mwFormToggle ($form, $target, $state) {
29508	
29509	// Makikng sure it's jQuery
29510	$form = _jq($form);
29511	
29512	// One of form elements can be provided instead of form itself
29513	// Checking if that's the case.
29514	if ( !$form.is('form') )
29515		$form = $form.closest('form');
29516	
29517	// Searching target on form
29518	var $tEl = $form.find($target);
29519	
29520	// Toggling
29521	if ( !isEmpty($state) )
29522		$tEl.show();
29523	else
29524		$tEl.hide();
29525	
29526} //FUNC mwFormToggle
29527
29528/*--------------------------------------------------------------------------------*\
29529|	updateDimensions
29530|----------------------------------------------------------------------------------
29531| Updates current dimensions setting for future use.
29532| - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
29533|	Params:
29534|		NONE
29535|	Return:
29536|		NULL
29537\*--------------------------------------------------------------= by Mr.V!T =-----*/
29538function updateDimensions () {
29539
29540	var wnd			= screenSize();
29541	var desktop_height	= wnd.height - jQuery('#HintBarContainer').height() - jQuery('#NavigationContainer').height(); 
29542
29543	setCookie('mwWorkSpaceWidth', wnd.width);
29544	setCookie('mwWorkSpaceHeight', desktop_height);
29545
29546} //FUNC updateDimensions
29547/*/
29548function initLTRows () {
29549	jQuery('.ListingTable TR:even').addClass('Even');
29550} //FUNC initLTRows
29551/**/
29552
29553/*--------------------------------------------------------------------------------*\
29554|	pulseAncor
29555|----------------------------------------------------------------------------------
29556| Pulsates given block.
29557| - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
29558|	Params:
29559|		ancor	string	- Block name to pulsate.
29560|	Return:
29561|			bool	- Always FALSE.
29562\*--------------------------------------------------------------= by Mr.V!T =-----*/
29563function pulseAncor (ancor) {
29564
29565	// DEPRECATED, left just as dummy
29566	return false;
29567	
29568	if ( !ancor ) 
29569		return false;
29570
29571	jQuery('A[name='+ancor+']').effect('pulsate');
29572	
29573	return false;
29574} //FUNC pulseAncor
29575
29576/*--------------------------------------------------------------------------------*\
29577|	initDatePicker
29578|----------------------------------------------------------------------------------
29579| Initiates DatePickers on page.
29580| - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
29581|	Params:
29582|		NONE
29583|	Return:
29584|		bool	- Always false.
29585\*--------------------------------------------------------------= by Mr.V!T =-----*/
29586function initDatePicker () {
29587	
29588	Date.format = 'yyyy-mm-dd';
29589	
29590	jQuery('INPUT.Date').each( function () {
29591
29592		var el = jQuery(this);
29593
29594		el.datePicker({clickInput:true, createButton:false, startDate: '2000-01-01'});
29595	//	el.dpSetSelected(el.val());
29596		
29597	}); //jQuery each 
29598	
29599	return false;
29600} //FUNC initDatePicker
29601
29602/*--------------------------------------------------------------------------------*\
29603|	updateAjaxLinks
29604|----------------------------------------------------------------------------------
29605| Updates page links wich should be loaded using AJAX.
29606| - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
29607|	Params:
29608|		NONE
29609|	Return:
29610|		bool	- Always FALSE.
29611\*--------------------------------------------------------------= by Mr.V!T =-----*/
29612function updateAjaxLinks () {
29613	jQuery('A.ajax').each( function () {
29614		this.onload	= this.onclick;
29615		this.onclick	= '';
29616	});
29617	jQuery('A.ajax').unbind('click');
29618	jQuery('A.ajax').click( function() {
29619		return pageAjax(this);
29620	}); //FUNC click
29621	
29622} //FUNC updateAjaxLinks
29623
29624var mwPerson = {
29625	
29626	/** //** ----= getTitle	 	 =----------------------------------------------\**//** \
29627	*
29628	* 	Attempts to retrieve person title name to show in dialogs.
29629	*
29630	*	@param	object	person		- Person data object.
29631	*	@param	string	[template]	- Custom template to use.
29632	*
29633	*	@return string			- Contact title.
29634	*
29635	\**//** ------------------------------------= by Mr.V!T @ Morad Media Inc. =----/** //**/
29636	getTitle		: function (person, template) {
29637		
29638		template = template || '{name} {first_name} {last_name} {firstname} {lastname}';
29639		
29640		return parseVariables(template, person).multiClean().trim();
29641	}, //FUNC getName
29642
29643	/** //** ----= getEmail	 	 =----------------------------------------------\**//** \
29644	*
29645	* 	Attempts to retrieve person email to show in dialogs.
29646	*
29647	*	@param	object	person		- Person data object.
29648	*
29649	*	@return string			- Contact title.
29650	*
29651	\**//** ------------------------------------= by Mr.V!T @ Morad Media Inc. =----/** //**/
29652	getEmail	: function (person) {
29653		return arraySelectValue(person, ['email', 'mail'], '').trim();
29654	} //FUNC getName
29655	
29656} //OBJECT mwPerson
29657
29658function mwSkinLoader ($el) {
29659	$el		= _jq($el);
29660	
29661	// Copying loader html from system loader
29662	var $sysLoader	= jQuery('#systemLoader'); 
29663	var $html	= $sysLoader.html();
29664	
29665	// And inserting it into target element
29666	$el.html($html);
29667	
29668} //FUNC mwSkinLoader
29669
29670/** //** ----= mwState		 =----------------------------------------------\**//** \
29671*
29672* 	Sets system to processing/idle state.
29673*
29674*	@param	MIXED	state
29675*		bool		- TRUE for loading indication, FALSE - idle.
29676*		string		- Status message for idle state (usually operation result)
29677*
29678*	@param	MIXED	[dialog]
29679*		bool		- FALSE for background (hintbar only). TRUE for common 
29680*				  system loading.
29681*		string		- Dialog name, to use for indications.  
29682*
29683*	@return	bool		- Always FALSE.		
29684*
29685\**//** ------------------------------------= by Mr.V!T @ Morad Media Inc. =----/** //**/
29686function mwState (state, dialog) {
29687
29688	// Since mwState meant to be used with windows and backend - doing nothing if windows are not loaded
29689	// Done this way due to while windows are supported, but are not required for basic toolkit
29690	if ( typeof(mwWindow) === 'undefined' )
29691		return false;
29692
29693	// Omited dialog is TRUE
29694	if ( !isSet(dialog) )
29695		dialog = true;
29696
29697	// Flush hint bar test, it's used with hintbar
29698	HINT_SAVE = false;
29699
29700	// Updating system indicator
29701	if ( state !== true ) { 
29702
29703	// ---- Setting IDLE state ---- 
29704
29705		jQuery('#hint_bar_status_ready').show();
29706		jQuery('#hint_bar_status_working').hide();
29707
29708		// Loader should be hided on this step anyway
29709		// In some cases when window was created during loading, loader may miss this
29710		// So it does not heart to forcehide
29711		
29712		mwWindow('LoaderWindow').hide();
29713
29714		// Updating modal loaders
29715		if ( typeof(dialog) == 'string' ) {
29716			
29717		// ---- Custom dialog ----
29718	
29719			mwWindow(dialog).State(state);
29720			
29721		} else {
29722			
29723		// ---- Common loader and background ----	
29724
29725			if ( state !== false )
29726				jQuery('#HintPlaceholder').html(state);
29727
29728		} //IF no dialog
29729	
29730	} else { //IF working
29731	
29732	// ---- Setting WORKING state ----
29733	
29734		jQuery('#hint_bar_status_ready').hide();
29735		jQuery('#hint_bar_status_working').show();
29736
29737		// Updating modal loaders
29738		if ( typeof(dialog) == 'string' && mwWindow(dialog).Visible ) {
29739			
29740		// ---- Custom dialog ----
29741	
29742			mwWindow(dialog).State(state);
29743			
29744		} else if ( dialog ) { //IF dialog set
29745		
29746		// ---- Common loader ----	
29747		
29748			mwWindow('LoaderWindow').show();
29749		
29750		} //IF common loader
29751	
29752	} //IF idle
29753	
29754	return false;
29755	
29756} //FUNC mwState
29757
29758/** //** ----= mwExHandle	=--------------------------------------------------------------------------------------\**//** \
29759*
29760*	Morweb global exceptions handler. Basic handlinng for now. This way user can say at least smth.
29761*
29762*	@see window.onerror handler doc for parameters list.
29763*
29764\**//** ---------------------------------------------------------------------------= by Mr.V!T @ Morad Media Inc. =----/** //**/
29765function mwExHandle (msg, url, lineNo) {
29766	
29767	// ToDo: test compatability
29768	// ToDo: Implement class based exceptions logic, 
29769	// and filter errors to separate debug (not shown to user) and user friendly messages
29770
29771	// If nothing to show - showing nothing
29772	if ( !msg )
29773		return;
29774
29775	// Forcing message to be string (in case object given - relying on native toString() )
29776	var msg = msg + '';
29777	
29778	// Removing trash
29779	msg = msg.replace('uncaught exception:', '');
29780
29781	mwState( mwError(msg) );
29782	
29783} //FUNC mwExHandle
29784
29785var mwHintShow = false;
29786
29787function mwWatchHints ($context, $dialog) {
29788	
29789	var $target = ( $dialog ) ? '#' + $dialog + ' .winStatus' : '#HintPlaceholder';
29790	
29791	$context = _jq($context);
29792	
29793	$context
29794		.off('mouseenter.mwHints')
29795		.off('mouseleave.mwHints')
29796		.on('mouseenter.mwHints', '[title], [hint], [error]', function() {
29797		
29798			if ( mwHintShow )
29799				return;
29800				
29801			mwHintShow = true;
29802			
29803			// Saving hintbar content
29804			this.HINT_SAVE = jQuery($target).html();
29805			
29806			// Temporary allowing 'title' attribute to act as hint.
29807			// Later only 'hint' will be used to avoid title tricks
29808			
29809			// First trying if there is error
29810			var hint	= jQuery(this).attr('error');
29811			var cls		= 'error';
29812			
29813			// If no error, trying hint
29814			if ( !hint ) {
29815				hint	= jQuery(this).attr('hint') || jQuery(this).attr('title');
29816				cls	= 'hint';
29817			} //IF no error
29818
29819			// Placing hint
29820			if ( hint ) 
29821				jQuery($target).html('<div class="status ' + cls + '">' + hint + '</div>');
29822
29823			// Removing title ( unnecessary later )
29824			this.TITLE_SAVE	= this.title; 
29825			this.title	= '';
29826			
29827		}) //FUNC mouseenter
29828		.on('mouseleave.mwHints', '[title], [hint], [error]', function() {
29829
29830			this.title	= this.TITLE_SAVE;
29831			jQuery($target).html(this.HINT_SAVE);
29832
29833			mwHintShow = false;
29834		}) //FUNC mouseleave
29835	; //jQuery(context)
29836	
29837} //FUNC mwWatchHints
29838
29839/*--------------------------------------------------------------------------------*\
29840|	setHintElements
29841|----------------------------------------------------------------------------------
29842| Set's elements for dynamic hints.
29843| - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
29844|	Params:
29845|		selector	string	- Target elements jQuery selector.
29846|	Return:
29847|				bool	- Always FALSE.
29848\*--------------------------------------------------------------= by Mr.V!T =-----*/
29849function setHintElements (selector, dialog) {
29850	
29851	var target = ( dialog ) ? '#' + dialog + ' .winStatus' : '#HintPlaceholder';
29852	
29853	selector = _jq(selector);
29854	
29855	selector
29856		.unbind('mouseenter')		
29857		.unbind('mouseleave')		
29858		.hover( function() {
29859			
29860			// Saving hintbar content
29861			HINT_SAVE = jQuery(target).html();
29862			
29863			// Temporary allowing 'title' attribute to act as hint.
29864			// Later only 'hint' will be used to avoid title tricks
29865			
29866			// First trying if there is error
29867			var hint	= jQuery(this).attr('error');
29868			var cls		= 'error';
29869			
29870			// If no error, trying hint
29871			if ( !hint ) {
29872				hint	= jQuery(this).attr('hint') || jQuery(this).attr('title');
29873				cls	= 'hint';
29874			} //IF no error
29875
29876			// Placing hint
29877			if ( hint ) 
29878				jQuery(target).html('<div class="status ' + cls + '">' + hint + '</div>');
29879
29880			// Removing title ( unnecessary later )
29881			TITLE_SAVE	= this.title; 
29882			this.title	= '';
29883			
29884		}, //FUNC over
29885		function() {
29886
29887			this.title	= TITLE_SAVE;
29888			
29889			if ( HINT_SAVE !== false )
29890				jQuery(target).html(HINT_SAVE);
29891		} //FUNC out
29892	); //jQuery onclick
29893	
29894} //FUNC setHintElements
29895
29896/** //** ----= mwUpdateScrolls	=--------------------------------------------------------------------------------------\**//** \
29897*
29898* 	Updates scollable contents.
29899*
29900*	@param	jQuery	[$context]	- Context to search scrolls within.
29901*
29902*	@return	jQuery			- Scrollable elements found.
29903*
29904\**//** ---------------------------------------------------------------------------= by Mr.V!T @ Morad Media Inc. =----/** //**/
29905function mwUpdateScrolls ($context) {
29906
29907	if ( !isFunction(jQuery.fn.mCustomScrollbar) )
29908		return false;
29909
29910	$context = $context ? _jq($context) : jQuery(document);
29911
29912	var $scrolls = $context.find('.mwScroll'); 
29913	
29914	$scrolls.mCustomScrollbar('destroy')
29915	$scrolls.mCustomScrollbar({
29916		theme			: 'dark',
29917		autoHideScrollbar	: true,
29918	//	scrollInertia		: false,
29919		contentTouchScroll	: true,
29920		advanced		: {
29921	//		autoScrollOnFocus	: true
29922		}
29923	}); //mCustomScrollbar
29924	
29925	return $scrolls;		
29926} //FUNC mwUpdateScrolls
29927
29928/** //** ----= OBJECT mwAjax	=--------------------------------------------------------------------------------------\**//** \
29929*
29930* 	Morweb system Ajax procesing object.
29931*
29932* 	@package	Morweb
29933* 	@subpackage	System
29934* 	@category	helper
29935*
29936*	@param	string	url	- Path to controller to request from.
29937*
29938*	@param	MIXED	[post]	- Data to post on request. Can be DOM form, jQuery selector 
29939*				  or object which points to form, or object with parameters.
29940*
29941*	@param	MIXED	[dialog]- Any valid mwState() dialog value.  
29942*
29943\**//** ---------------------------------------------------------------------------= by Mr.V!T @ Morad Media Inc. =----/** //**/
29944function mwAjax (url, data, dialog) {
29945
29946	// Creating morweb extension object
29947	var exObj = {
29948
29949	'dialog'	: dialog,	// Dialog related to call. Will be used to display status.
29950
29951// ==== CONTENTS ===============================================================================================================
29952
29953	/** //** ----= parseContents	=------------------------------------------------------------------------------\**//** \
29954	*
29955	* 	Parses AraxResult for different content types.
29956	*	
29957	*	@param	object(AjaxResult)	data	- Result in vAjax object format.  
29958	*
29959	\**//** -------------------------------------------------------------------= by Mr.V!T @ Morad Media Inc. =----/** //**/
29960	parseContents	: function (data) {
29961
29962		var self = this;
29963
29964		// If smth wrong come - nothing to do
29965		if ( !data ) return;
29966
29967	// ---- Processing debug (unexpected output) ----
29968	
29969		if ( data._debug && typeof(mwRawDump) != 'undefined' )
29970			mwRawDump('#wDebugCurrent', data._debug);
29971
29972		delete data._debug;
29973		
29974	// ---- Processing validations ----
29975
29976		if ( data._validations ) {
29977		
29978			// To be sure that smth correct come
29979			if ( typeof(data._validations) == 'object' ) {
29980
29981				//Processig each given validations form
29982				for ( var form in data._validations ) {
29983					
29984					// Skipping not forms ( numeric indexies are allowed )
29985					if ( isInt(form) ) continue;
29986
29987					// Normally forms are specified by ids, if not found, then it's for custom processing
29988					// Trying to define context first to speedup and avoid cross windows applying 
29989					var context = ( typeof(this.dialog) == 'string ' ) ? mwWindow(this.dialog).Body : jQuery('#' + form);
29990					
29991					// Setting validations
29992					setValidations(context, data._validations[form]);
29993					
29994				} //FOR each form
29995
29996			}
29996 //IF object comed
29997			
29998		} //IF validations given
29999
30000	// ---- Processing cahces -----
30001
30002		if ( !data._cache ) return;
30003	
30004		var cache = data._cache; 
30005		delete data._cache;
30006
30007		// HTML comed can contain errors, 
30008		// though jQuery may still insert it, exception is happen right AFTER insert.
30009		// To prevent script stop (content was inserted anyway) - catching exceptions 
30010		// for each insert individually (to process others which are succeed) and relogging them in console.
30011
30012	// ---- Setting up data cache ----
30013
30014		// Making sure mwData exists at all
30015		if ( typeof(mwData) == 'undefined' )
30016			mwData = {};
30017
30018		if ( cache.Data ) {
30019
30020		/*/	
30021			if (mwData.Users);
30022				__(mwData.Users);
30023			
30024		/**/	
30025			// ToDo: Seems that jQuery.extend() does not merge arrays propertly
30026			// And empty objects/assoc arrays (which meant to be data objects) 
30027			// come as list arrays, which are not merged
30028			jQuery.extend(true, mwData, cache.Data);
30029		/*/
30030
30031			if (mwData.Users);
30032				__(mwData.Users);
30033		/**/
30034		} //IF Data set	
30035
30036	// ---- Setting up windows ----
30037
30038		// Only supporting windows when windows lib is loaded (backend/liveEd)
30039		// Windows are not created/supported for bare frontend currently
30040
30041		if ( cache.Windows && (typeof(mwWindow) !== 'undefined') ) {
30042
30043			// Using mwWinManager to get windows system container
30044			var $winContainer = mwWinManager.init().Container;
30045
30046			for ( var i in cache.Windows ) {
30047
30048				// Checking for html within regular results	
30049				if ( !data['_wc_'+i] ) continue;
30050
30051				var wnd		= mwWindow(i);
30052				var $old	= wnd.Window;
30053
30054				// Disablingg old window, but keeping it's element
30055				// This allows updated window to init in background, with smooth transition
30056				// In addition - increasing z-index, to make sure it stays on top, covering new version
30057				if ( $old && $old.length ) {
30058					
30059					// Removing IDs and names from old elements, to allow proper init
30060					$old.removeAttr('id')
30061					$old.find('[id], [name]').removeAttr('id name');
30062					
30063					// Increasing z-index, just setting it notably higher
30064					$old.css('z-index', $old.css('z-index')*1+100 );
30065					
30066				} //IF old window present				
30067				
30068				try {	
30069					var el = jQuery('<div id="' + i + '" class="winEl">' + data['_wc_'+i] + '</div>')
30070						.appendTo($winContainer);
30071					
30072				} catch (e) {
30073					
30074					// Researching window, cuz it still may be inserted
30075					var el = $winContainer.find('#' + i);
30076
30077					console.error(e);
30078				} //TRY
30079
30080				// Initiating window
30081				wnd.Element(el).align();
30082
30083				// Removing old window element if there was one
30084				if ( $old && $old.length )
30085					$old.remove();
30086				
30087				// Initiating title
30088				if ( cache.Windows[i] )
30089					wnd.Title(cache.Windows[i]);
30090
30091				delete data['_wc_'+i];
30092
30093			} //FOR each window in system
30094
30095		} //IF windows set
30096
30097	// ---- Setting up contents ----
30098
30099		if ( cache.Contents ) {
30100
30101			for ( var i in cache.Contents ) {
30102				// Checking for html within regular results	
30103				if ( !data['_cc_'+i] ) continue;
30104				
30105				try {
30106					jQuery('#' + i).html(data['_cc_'+i]);
30107				} catch (e) {
30108					console.error(e);
30109				} //TRY
30110
30111				delete data['_cc_'+i];
30112			} //FOR each contents
30113
30114		} //IF contents set
30115
30116	// ---- Setting up subcontents ----
30117
30118		// Making sure mwSubContents exists at all
30119		if ( typeof(mwSubContents) == 'undefined' )
30120			mwSubContents = {};
30121
30122		if ( cache.SubContents ) {
30123			
30124			for ( var i in cache.SubContents ) {
30125				
30126				// Checking for html within regular results	
30127				if ( !data['_sc_'+i] ) continue;
30128				
30129				mwSubContents[i] = data['_sc_'+i];	
30130				
30131				delete data['_sc_'+i];
30132			} //FOR each subcontents
30133
30134		} //IF subcontents set
30135
30136	// ---- Running java init ----
30137
30138		if ( cache.Java ) {
30139			
30140			// Executing each given java init				
30141			for ( var i in cache.Java ) {
30142				jQuery.globalEval(cache.Java[i]);
30143			} //FOR each java init
30144
30145		} //IF subcontents set
30146
30147	}, //FUNC parseResult
30148
30149// ==== GENERAL ================================================================================================================
30150
30151	/** //** ----= init	=--------------------------------------------------------------------------------------\**//** \
30152	*
30153	* 	Sets up basic events. Used before constructor return, to make sure that processing triggers before custom events.
30154	*
30155	*	@return	SELF
30156	*	
30157	\**//** -------------------------------------------------------------------= by Mr.V!T @ Morad Media Inc. =----/** //**/
30158	init		: function () {
30159
30160	// ---- START ----
30161
30162		this.start( function (data) {
30163
30164			// Setting loading state
30165			mwState(true, this.dialog);
30166
30167		}); //FUNC vAjax.stop.callback
30168
30169	// ---- STOP ----
30170
30171		this.stop( function (data) {
30172
30173			// Parsing contentns
30174			this.parseContents(data);
30175
30176		}); //FUNC vAjax.stop.callback
30177
30178	// ---- SUCCESS ----
30179
30180		this.success( function (data) {
30181
30182			// If some of contents come defined updater - calling it.
30183			// DEPRECATED: Not used anymore, left from old code for compatability				
30184			if ( typeof(updateDesktop) == 'function' )
30185				updateDesktop();
30186
30187		}); //FUNC vAjax.success.callback
30188
30189	// ---- ERROR ----
30190
30191		this.error( function (data) {
30192			
30193			// Simple results are threated as debug/errors
30194			if ( data.status.minor == 'simple' ) {
30195
30196				if ( data._debug && typeof(mwRawDump) != 'undefined' )
30197					mwRawDump('#wDebugCurrent', data.res);
30198							
30199				// Updating status
30200				mwState(false, this.dialog);
30201				
30202				return;
30203			} //IF simple
30204
30205		}); //FUNC vAjax.error.callback
30206
30207	// ---- DONE ----
30208
30209		this.done( function (data) {
30210
30211			// Updating status
30212			mwState(data.status.text, this.dialog);
30213
30214		}); //FUNC vAjax.stop.callback
30215
30216		return this;
30217		
30218	}, //FUNC init
30219
30220	/** //** ----= go	=--------------------------------------------------------------------------------------\**//** \
30221	*
30222	* 	Basic posting method. Proceeds ajax requests, parses contents, calls callbacks and updates system status.
30223	*	
30224	*	@param	object(AjaxResult)	data	- Result in vAjax object format.  
30225	*
30226	\**//** -------------------------------------------------------------------= by Mr.V!T @ Morad Media Inc. =----/** //**/
30227	go		: function () {
30228		
30229		// Before posting attempting to reset current validations on dialog (if one specified)
30230		if ( typeof(this.dialog) == 'string' && (typeof(mwWindow) !== 'undefined') )
30231			setValidations( mwWindow(this.dialog).Body, {});			
30232
30233		this.post();
30234
30235		return this;
30236		
30237	}, //FUNC go
30238	
30239// ==== CONTENT TYPES ==========================================================================================================
30240
30241	/** //** ----= content	=--------------------------------------------------------------------------------------\**//** \
30242	*
30243	* 	Loads content from contoller specified. And applies to specified element if supplied.
30244	*
30245	*	@param	MIXED	target	- Target to insert content to. Can be jQuery selector, 
30246	*				  jQuery object, DOM element, or callback function. Function 
30247	*				  passed as target will act as onSuccess callback.
30248	*
30249	*	@return SELF
30250	*
30251	\**//** -------------------------------------------------------------------= by Mr.V!T @ Morad Media Inc. =----/** //**/
30252	content : function (target) {
30253	
30254		this.go();
30255		
30256		if ( isFunction(target) )
30257			this.success(target);
30258		else
30259			this.success( function (data) {
30260
30261				if ( !target ) return;
30262				
30263				// If common content come - inserting it into target
30264				if ( data.content !== undefined ) {
30265
30266					target = _jq(target);
30267					
30268					target.html(data.content);
30269					
30270				} //IF content set.
30271
30272			}); //FUNC onSuccess
30273
30274		return this;
30275	}, //FUNC content	
30276
30277	/** //** ----= index	=--------------------------------------------------------------------------------------\**//** \
30278	*
30279	* 	This was designed with idea of index reload. Can take optional callback parameter. 
30280	*	On success action it reloads index from controller specified, closes related dialogs, calls given callback and 
30281	*	triggers status. Callback will be called after success reload, all elements positioning and status updates.
30282	*
30283	*	@param	function	
30284	*			[callback]	- Callback to call upon success.
30285	*					  If callback returns FALSE, dialog will be not 
30286	*					  canceled and status will be not updated.
30287	*
30288	*	@return	SELF		
30289	*
30290	\**//** -------------------------------------------------------------------= by Mr.V!T @ Morad Media Inc. =----/** //**/
30291	index : function (callback) {
30292
30293		// Getting scroll position of scroll table to be fancy
30294		// ToDo: scrollTable instance method for reinit
30295		var $pos = jQuery('.mwScrollTable.body').scrollTop();
30296		
30297		return this
30298			.go()
30299			.success( function (data) {
30300				
30301				var res = true;
30302				
30303				if ( typeof(callback) == 'function' )
30304					res = callback(data);
30305				
30306				if ( res !== false ) {
30307					
30308					if ( typeof(this.dialog) == 'string' && (typeof(mwWindow) !== 'undefined') )
30309						mwWindow(this.dialog).hide();
30310					
30311					// Status was updated in window, but if it's closed - text status should go in hintbar
30312					if ( data.status.text )
30313						mwState(data.status.text);
30314
30315					// Index tables
30316					jQuery('.mwIndexTable').each( function () {
30317			
30318						mwScrollTable( jQuery(this), {
30319							container	: jQuery('.mwDesktop'), 
30320						});
30321						
30322					}); //jQuery.each scroll table
30323
30324					// Resetting scroll position to top
30325					jQuery('.mwScrollTable.body').scrollTop($pos);
30326
30327				} //IF all ok
30328				
30329				return res;
30330				
30331			}) //FUNC onSuccess
30332		; //this
30333		
30334	}, //FUNC index
30335	
30336	/** //** ----= window	=--------------------------------------------------------------------------------------\**//** \
30337	*
30338	* 	Based on content loading it targets one specified window and shows it on success.
30339	*	Can take optional callback parameter, which will be called on success, but before show window.
30340	*
30341	*	@param	string		name	- Name of dialog to show upon loading.
30342	*
30343	*	@param	function	
30344	*			[callback]	- Callback to call upon success.
30345	*					  If callback returns FALSE, dialog will be not 
30346	*					  opened and status will be not updated.
30347	*
30348	*	@return	SELF		
30349	*
30350	\**//** -------------------------------------------------------------------= by Mr.V!T @ Morad Media Inc. =----/** //**/
30351	window : function (name, callback) {
30352	
30353		return this
30354			.go()
30355			.success( function (data) {
30356			
30357			var res = true;
30358
30359			if ( typeof(callback) == 'function' )
30360				res = callback(data);
30361			
30362			if ( res !== false )
30363				mwWindow(name).show();
30364			
30365			return res;
30366		}); //FUNC
30367		
30368	} //FUNC window
30369	
30370	}; //OBJECT mwAjax
30371
30372	// Getting basic vAjax object, and extending it with morweb behavior
30373	var vAjaxObj = vAjax(url, data);
30374	
30375	jQuery.extend(vAjaxObj, exObj);
30376	
30377	// Initiating basic events
30378	vAjaxObj.init();
30379	
30380	// Returning it to user
30381	return vAjaxObj;
30382} //CONSTRUCTOR mwAjax
30383
30384/** //** ----= OBJECT mwSubContent =--------------------------------------------\**//** \
30385*
30386* 	System SubContents processing tools.
30387*
30388*	@param	jQuery	container	- Container element to setup with subcontents.
30389*	@param	jQuery	[animation]	- Element to use for animations. Possible values:
30390*					  jQuery compatable pointer to element to animate,
30391*					  TRUE to animate container element (default behavior),
30392*					  FALSE to cancel animations (for manual animating).
30393*
30394\**//** ------------------------------------= by Mr.V!T @ Morad Media Inc. =----/** //**/
30395function mwSubContent (_container, _animation) {
30396
30397	// Validating environment
30398	_container = _jq(_container);
30399	
30400	if ( _animation === undefined ) 
30401		_animation = true;
30402
30403	return {
30404
30405		storage		: jQuery('#sysSubContents'),	// System subContents storage.
30406		container	: _container,			// Container element to operatie with.
30407		animation	: _animation,			// Element to animate on operations. For internal use. To setup use animate() method. 
30408
30409		/** //** ----= animate		 =----------------------------------------------\**//** \
30410		*
30411		* 	Applies animation to set element and calls callback on final. For internal use.
30412		*
30413		*	@param	string		type		- Animation type: 'show' or 'hide'.
30414		*	@param	function	[callback]	- Callback to call on final.
30415		*
30416		*	@return SELF 
30417		*
30418		\**//** ------------------------------------= by Mr.V!T @ Morad Media Inc. =----/** //**/
30419		animate		: function ( type, callback ) {
30420
30421			// Calculating animation target
30422			var a;
30423			
30424			if ( this.animation === true ) 
30425				a = this.container;
30426			else if ( this.animation === false )
30427				a = false;
30428			else
30429				a = _jq(this.animation);
30430			
30431			// Ensuring callback is a function
30432			callback = callback || function(){};
30433
30434		// ---- Applying animation if set ----
30435		
30436			if ( a ) {
30437				
30438				// If animation target set - calling proper animation and returning.
30439				// If no valid animation - doing nothing, it will proceed to default no animation flow.
30440				switch ( type ) {
30441					case 'show'	:
30442						mwShow(a, callback); 
30443						return this;				
30444					case 'hide'	: 
30445						mwHide(a, callback); 
30446						return this;				
30447				} //SWITCH type
30448
30449			} //IF animation
30450				
30451		// ---- No animation ----
30452
30453			if ( typeof(callback) == 'function' )
30454				callback();
30455
30456			return this;				
30457		}, //FUNC animate
30458		
30459		/** //** ----= setup		 =----------------------------------------------\**//** \
30460		*
30461		* 	Replaces container subContents with subContent specified, cleaning container. 
30462		*	Applies animations if set. Calls callback after subContent inserted and before show.
30463		*	New subContent will be passed to callbacks. 
30464		*
30465		*	@param	string	id			- SubContainer system ID.  
30466		*
30467		*	@param	function	[callback]	- Callback to call after hide and setup.  
30468		*	@param	function	[onfinal]	- Callback to call after final show.  
30469		*
30470		*	@return SELF
30471		*
30472		\**//** ------------------------------------= by Mr.V!T @ Morad Media Inc. =----/** //**/
30473		setup		: function (id, callback, onfinal) {
30474			
30475			var self = this; // Shortcut to use in callback
30476
30477			// Calling cleaner, this will also hide container 
30478			this.unset( function () {
30479
30480				// Attaching content				
30481			//	var el = jQuery('#' + id);
30482				var el = jQuery(mwSubContents[id]);
30483				el.appendTo(self.container);
30484
30485				// Showing container
30486				self.animate('show', function () {
30487					// Calling final callback
30488					if ( typeof(onfinal) == 'function' )
30489						onfinal(el);
30490					
30491				}); //FUNC onShow
30492
30493				// Small delay to allow to update contents dimensions
30494				setTimeout( function () {
30495					// Calling callback, it will start before animated, 
30496					// but after animaiton start - contents become real
30497					if ( typeof(callback) == 'function' )
30498						callback(el);
30499				}, 1);
30500
30501			}); //FUNC onHide
30502			
30503			return this;
30504		}, //FUNC setup
30505
30506		/** //** ----= unset		 =----------------------------------------------\**//** \
30507		*
30508		* 	Clears container from subContents, moving them to system storage.
30509		*	Calls callback after clean.
30510		*
30511		*	@param	function	[callback]	- Callback to call on done.  
30512		*
30513		*	@return SELF
30514		*
30515		\**//** ------------------------------------= by Mr.V!T @ Morad Media Inc. =----/** //**/
30516		unset		: function ( callback ) {
30517
30518			var self = this; // Shortcut to use in callback
30519
30520			this.animate('hide', function () {
30521
30522				// Removing all childs in container. Container means only subcontents
30523			//	self.container.find('.subContent').appendTo('#sysSubContents');
30524				self.container.empty();
30525
30526				if ( typeof(callback) == 'function' )
30527					callback();
30528
30529			}); //FUNC onHide
30530			
30531			return this;
30532		} //FUNC unset
30533		
30534	}; //OBJECT mwSubContent
30535
30536} //CONSTRUCTOR mwSubContent
30537
30538//ToDo: Redo everything!!!
30539
30540// Simple cache to do not monitor same values
30541var upload_procesing_cache = {};
30542
30543/** //** ----= setFormsSubmit	 =----------------------------------------------\**//** \
30544*
30545* 	Sets forms to have submit control. Applies to forms with set onsubmit attribute.
30546*
30547* 	@param	MIXED element	- jQuery selector to search forms within 
30548*				  (set to speed up search).
30549*
30550* 	@return	bool		- Always FALSE.
30551*
30552\**//** ------------------------------------= by Mr.V!T @ Morad Media Inc. =----/** //**/
30553function setFormsSubmit ( element ) {
30554	
30555	if ( element && !(element instanceof jQuery) )
30556		element = jQuery(element);
30557	
30558	// Searching for forms, only with onsubmit attribute
30559	var forms = ( element )? jQuery(element).find('FORM[onsubmit]') : jQuery('FORM[onsubmit]');
30560
30561	// Processing each form for submit control
30562	forms.each( function() {
30563		
30564		var form	= jQuery(this);
30565		var form_id	= form.attr('id');
30566		
30567		// Checking if form has no submit already
30568		// ToDo: More complex check
30569		if ( form.find('[type=submit]').length ) return;
30570
30571		// If still here - form have no submit, but have onsubmit attribute
30572		// So adding hidden submit control
30573		// Adding on top, to do not mess with winContents and layout
30574		form.prepend('<input type="submit" style="display: none;" />');
30575
30576		if ( !form_id ) return;
30577
30578		// If element is window with footer buttons related to form (by id) - set them to submit form too.
30579		element.find('.winFooter A[rel=' + form_id + ']')
30580			.addClass('hi')
30581			.unbind('click')
30582			.click( function () {
30583				
30584				jQuery('#' + form_id).submit();
30585				
30586				return false;
30587			} ); //FUNC onClick
30588		
30589	}); //FUNC each form
30590
30591	return false;	
30592} //FUNC setFormsSubmit
30593
30594/** //** ----= mwUpdateSelector	=--------------------------------------------------------------------------------------\**//** \
30595*
30596*	Updates selector options with items given. Optionally marks certain items as selected. 
30597*
30598\**//** ---------------------------------------------------------------------------= by Mr.V!T @ Morad Media Inc. =----/** //**/
30599function mwUpdateSelector (input, options, selected) {
30600
30601	input = _jq(input);
30602
30603	// Removing all styles from selector
30604	var sel = unstyleInput(input);
30605	
30606	// Cleanning old options
30607	sel.find('OPTION').remove();
30608
30609	// Adding given options
30610	// Supporting maps if one is given
30611	if ( isClass(options, 'Map') ) {
30612
30613		
30614	/**/	// Using method to iterate, to stay compatible with older JS parsers
30615		options.forEach( function ( $val, $key ) {
30616			jQuery('<option value="' + $key + '">' + $val + '</option>').appendTo(sel);
30617		}); //forEach
30618	
30619	/*/
30620		for ( var i of options )
30621			jQuery('<option value="' + i[0] + '">' + i[1] + '</option>').appendTo(sel);
30622	/**/	
30623	} //IF map given
30624	
30625	else {
30626
30627		for ( var i in options )
30628			jQuery('<option value="' + i + '">' + options[i] + '</option>').appendTo(sel);
30629	
30630	} //IF regular data object
30631	
30632	// Setting selected values if given some
30633	if ( selected )
30634		setValue_Select(sel.get(0), selected);
30635	
30636	// Restyling input back
30637	styleDialog( sel.parent() );
30638	
30639	// Triggering update
30640	sel.trigger('update');
30641	
30642	return false;
30643} //FUNC mwUpdateSelector
30644
30645/** //** ----= filterSelect	 =----------------------------------------------\**//** \
30646*
30647* 	Filters styled selector with values.
30648*
30649* 	@param	MIXED	select	- Valid jQuery element selector. 
30650* 				  Sould point to .mwinput.List control.
30651* 
30652* 	@param	string	filter	- Comma separated filters.
30653* 	@return	bool		- Always FALSE.
30654*
30655\**//** ------------------------------------= by Mr.V!T @ Morad Media Inc. =----/** //**/
30656function filterSelect ( select, filter ) {
30657
30658	if ( !filter ) {
30659//		jQuery(select).find('.Item').fadeIn(200);
30660		jQuery(select).find('.item').show();
30661		return false;
30662	} //IF empty filter
30663
30664	// Trimming filters, and composing regex
30665	var filter	= strToArray(filter, ',');
30666	var reg		= new RegExp('(' + filter.join('|') + ')', 'i');
30667	
30668	jQuery(select + ' .item').each( function () {
30669		var el		= jQuery(this);
30670		
30671		var title	= el.find('div').first().html();
30672
30673		if ( reg.test(title) )
30674			el.show();
30675		else
30676			el.hide();
30677
30678	}); //jQuery each
30679	
30680	return false;
30681} //FUNC filterSelect
30682
30683/** //** ----= selectAddItem	 =----------------------------------------------\**//** \
30684*
30685* 	Adds items to target styled selector.
30686*
30687* 	@param	MIXED	select	- Valid jQuery element selector.
30688* 	@param	string	values	- Comma separated values.
30689* 	@return	bool		- Always FALSE.
30690*
30691\**//** ------------------------------------= by Mr.V!T @ Morad Media Inc. =----/** //**/
30692function selectAddItem ( select, values, checked ) {
30693	
30694	if ( !values ) return false;
30695
30696	var select	= _jq(select);
30697	
30698	checked = ( checked )? ' selected="selected"' : ''; 
30699
30700	// If input given as value, it should be cleaned after all	
30701	var relate = false; 
30702	if ( values instanceof jQuery ) {
30703		relate = values;
30704		values = relate.val();
30705	} //IF parent element given
30706	
30707	if ( isStr(values) ) {
30708		var tmp	= strToArray(values, ',');
30709		
30710		// Reindexing to have meaningfull keys to use as values
30711		// Normally associated arrays or objects should be passed
30712		values = {};
30713		
30714		for ( var i in tmp )
30715			values[tmp[i]] = tmp[i];
30716			
30717	} //IF string given
30718
30719	// Before have to save selected inputs, cuz unstyle will reset all to beginning
30720	var checks	= []; 
30721	select.find('input:checked').each( function () {
30722		checks.push( jQuery(this).val() );
30723	}); //FUNC each.callback
30724
30725	// Destyling select for easy add new inputs	
30726	select = unstyleInput(select);
30727	
30728	// Resetting options to to new setting
30729	select.find('option').each( function () {
30730		
30731		var el = jQuery(this);
30732		if ( checks.indexOf( el.val() ) != -1 )
30733			el.attr('selected', 'selected');
30734		else	
30735			el.removeAttr('selected');
30736			
30737	}); //FUNC each.callback
30738	
30739	for ( var i in values ) {
30740		select.prepend('<option value="' + i + '"' + checked + '>' + values[i] + '</option>');		
30741	} //FOR each value
30742
30743	// Styling input back again
30744	styleDialog(select.parent());
30745
30746	return false;
30747} //FUNC selectAddItem
30748
30749/** //** ----= inputClassString	 =----------------------------------------------\**//** \
30750*
30751* 	Takes source element, and type classes. Prepares full class and style attributes.
30752*
30753* 	@param	object	el		- Source input jQuery object.
30754* 	@param	string	typeClass	- Additional classes to add.
30755* 	@return	string			- Attributes to add to element.
30756		bool			- FALSE if no modification necessary.
30757*
30758\**//** ------------------------------------= by Mr.V!T @ Morad Media Inc. =----/** //**/
30759function inputClassString(el, typeClass) {
30760
30761	// If already styled - nothing to do again
30762	if ( el.hasClass('mw') ) return false;
30763
30764	// Saving source to allow fast unstyle
30765	el.data('source', getOuterHTML( el.get(0) ));
30766
30767	// Getting existing attributes
30768	var cls		= el.attr('class');
30769	
30770	// Getting other transferrable attributes
30771	var $attr	= {'class' : ''};
30772	var $aList	= ['style', 'size', 'data-prefix', 'data-suffix'];
30773	for ( var $i in $aList ) 
30774		$attr[ $aList[$i] ] = el.attr($aList[$i]);
30775
30776	var styles	= el.attr('style');
30777		
30778	// Composing classes
30779	var ctmp	= ['mwInput', typeClass, cls];
30780	
30781	var name	= el.attr('name');
30782	if ( name ) {
30783		name	= name.replace(/[\[\]]/g, '');
30784		ctmp.push('name-'+name);
30785	} //IF name
30786	
30787	// Storing class as attribute
30788	$attr['class'] = ctmp.join(' ').trim();
30789
30790	// Processing results
30791	var $res = '';
30792	for ( var $i in $attr ) 
30793		if ( !isEmpty($attr[$i]) )
30794			$res += ' '+$i+'="'+$attr[$i]+'"';
30795
30796	// Cleaning source attributes
30797	el[0].className = '';
30798	
30799	for ( var $i in $aList ) 
30800		el.removeAttr($aList[$i]);
30801
30802	// Adding styled marker
30803	el.addClass('mw');
30804	
30805	return $res;
30806} //FUNC inputClassString
30807
30808/** //** ----= styleDialog	 =----------------------------------------------\**//** \
30809*
30810* 	Applies style to all inputs childs to specified element.
30811*
30812* 	@param	MIXED	el	- Valid jQuery element selector.
30813* 	@return	bool		- Always FALSE.
30814*
30815\**//** ------------------------------------= by Mr.V!T @ Morad Media Inc. =----/** //**/
30816function styleDialog ( dialog ) {
30817
30818	dialog = _jq(dialog);
30819
30820// ---- TEXT ---- 
30821	
30822	dialog.find('INPUT[type=text], INPUT[type=search], INPUT[type=password], INPUT[type=email], INPUT[type=date], INPUT[type=number], INPUT[type=tel], INPUT[type=url]').each( function () {
30823		
30824		var el		= jQuery(this);
30825		
30826		// Cleaning errors state on load
30827		//el.closest('.mwInput').removeClass('Error');
30828		
30829		// Checking if some of subcontrol classes are set and reading sub class
30830		var subs = false;
30831		var $sClass = ['plus', 'go', 'ok'];
30832		for ( var $i in $sClass )
30833			if ( el.is('.'+$sClass[$i]) )
30834				subs = $sClass[$i];
30835		
30836		// Compatabilty with renamed sub
30837		if ( subs == 'go' )
30838			subs = 'ok';
30839		
30840		var tclass	= 'text';
30841		if ( subs ) tclass += ' wSub';
30842		
30843		var attr	= inputClassString(el, tclass);
30844		if ( !attr ) return;
30845
30846		var wrap = jQuery('<div ' + attr + '></div>');
30847		el.wrapAll(wrap);
30848
30849		// Researching wrap
30850		wrap = el.closest('.mwInput');
30851
30852		// Adding some common subcontrols if set
30853		if ( subs ) {
30854			
30855			var sub		= jQuery('<div class="subcontrol button hi icon light '+ subs+'"></div>').insertBefore(el);
30856			var click	= el.attr('onclick');
30857			
30858			if ( click )
30859				sub.attr('onclick', click);
30860			
30861			el.removeAttr('onclick');
30862		} //IF is Plus
30863
30864		// Initiating date picker
30865		Date.format = 'yyyy-mm-dd';
30866		if ( wrap.is('.date') ) {
30867			el.datePicker({
30868				clickInput	: true, 
30869				createButton	: false, 
30870				verticalOffset	: 26,
30871				startDate	: '2000-01-01'
30872			});
30873		} //IF date input
30874
30875		// Search input is special
30876		// Then adding icon and subcontrol
30877		// This allows better compatability than regular search, and finer styles control
30878		// Skipping manually added icons
30879		if ( wrap.is('.search:not(.icon)') ) {
30880
30881			// Adding search icon on left (it's already search, so just adding icon class)
30882			wrap.addClass('icon left');
30883
30884			// Padding right is exactly same as left
30885			wrap.css( 'padding-right', wrap.css('padding-left') );
30886
30887			// Adding clear subcontrol
30888			var $sub = jQuery('<div class="subcontrol icon right declined" style="display:none"></div>
30888').appendTo(wrap);
30889
30890			// Adding change event on input
30891			el.on('keyup.mwInput.search', function () {
30892				
30893				if ( this.value )
30894					$sub.show();
30895				else
30896					$sub.hide();
30897				
30898			}); //On change
30899			
30900			// Adding clear action
30901			$sub.on('click.mwInput.search.clear', function () {
30902				
30903				el.val('');
30904				$sub.hide();
30905				
30906				el.trigger('keyup');
30907				
30908			}); //on click
30909			
30910
30911		} //IF search found
30912
30913		// Ensuring input size for prefix/suffix
30914		if ( wrap.is('[data-prefix], [data-suffix]') ) {
30915			
30916		} //IF pfx
30917
30918		// Text inputs should select on forcus
30919		el.focus( function () { this.select() } );
30920
30921
30922		/*/
30923		// Adding required check
30924		if ( wrap.hasClass('required') ) {
30925			
30926			el.keyup( function () {
30927				var e = jQuery(this);
30928				if ( e.val().trim() ) 
30929					e.closest('.mwInput').removeClass('Error');
30930				else
30931					e.closest('.mwInput').addClass('Error');
30932			});
30933			
30934		} //IF required
30935		/**/
30936		
30937	}); //jQuery each text input
30938	
30939// ---- TEXTAREA ---- 
30940	
30941	dialog.find('TEXTAREA').not('.mw, .tinyMCE, .mwTinyMCE, .richEdit').each( function () {
30942		var $el = jQuery(this);
30943		
30944		$el.addClass('multiline');
30945		
30946		var $attr = inputClassString($el, 'textarea');
30947		if ( !$attr ) return;
30948
30949		$el.wrapAll('<div ' + $attr + '></div>');
30950
30951		// Researching wrapper
30952		var $wrap	= $el.parent();
30953
30954		// 100% size inputs are marked with class instead
30955		// and triggers default size for fallbacks
30956		if ( $wrap.attr('size') == '100%' )
30957			$wrap.addClass('fullHeight').removeAttr('size');
30958
30959		// Adding default height
30960		if ( !$wrap.attr('size') )
30961			$wrap.attr('size', '3');
30962
30963	}); //jQuery each textarea input
30964
30965	dialog.find('TEXTAREA.richEdit').not('.mw').each( function () {
30966		
30967		// Doing nothing if no tinyMCE defined on page
30968		// No fallbacks, as developer should see that smth went wrong
30969		if ( typeof(tinyMCE) == 'undefined' )
30970			return; 
30971		
30972		var el = jQuery(this);
30973
30974		el.addClass('multiline');
30975		
30976		// Marking element as initiated
30977		el.addClass('mw');
30978
30979		// 100% size inputs are marked with class instead
30980		// and triggers default size for fallbacks
30981		if ( el.attr('size') == '100%' )
30982			el.addClass('fullHeight').removeAttr('size');
30983
30984		// Adding default height
30985		if ( !el.attr('size') )
30986			el.attr('size', '5');
30987	
30988		// Giving it small delay, to let freshly loaded windows to cool a bit.
30989		setTimeout( function () {
30990			mwTinyMCE().init(el);
30991		}, 10);
30992		
30993	}); //jQuery each textarea input
30994	
30995// ---- BUTTONS ---- 
30996	
30997//	dialog.find('BUTTON, INPUT[type=button], INPUT[type=submit]').each( function () {
30998	dialog.find('INPUT[type=button], INPUT[type=submit]').each( function () {
30999
31000		var el = jQuery(this);
31001		
31002		// Quickly skipping tinyMCE buttons
31003		// Bit dirty, but tiny is supported natively
31004		// ToDo: init styles from original input instead (would come naturally once proper mwForms are implemented)
31005		if ( el.parent().is('.mce-btn') )
31006			return;
31007
31008		var attr = inputClassString(el, 'button');
31009		if ( !attr ) return;
31010
31011		el.wrapAll('<div ' + attr + '></div>');
31012		
31013		// Sumulating active for parent
31014		el
31015			.bind('mousedown', function () {
31016				jQuery(this).parent().addClass('active');
31017			}) //FUNC onMouseDown
31018			.bind('mouseup', function () {
31019				jQuery(this).parent().removeClass('active');
31020			}) //FUNC onMouseUp
31021		; //jQuery element
31022	}); //jQuery each textarea input
31023
31024	dialog.find('INPUT[type=image]').each( function () {
31025		var el = jQuery(this);
31026		
31027		var attr = inputClassString(el, 'button image');
31028		if ( !attr ) return;
31029
31030		el.wrapAll('<div ' + attr + '></div>');
31031		
31032		// Sumulating active for parent
31033		el
31034			.bind('mousedown', function () {
31035				jQuery(this).parent().addClass('active');
31036			}) //FUNC onMouseDown
31037			.bind('mouseup', function () {
31038				jQuery(this).parent().removeClass('active');
31039			}) //FUNC onMouseUp
31040		; //jQuery element
31041	}); //jQuery each textarea input
31042	
31043// ---- SELECT ---- 
31044	
31045	dialog.find('SELECT').not('.checkList, .radioList, .browseButton, .selectEx').each( function () {
31046		var el = jQuery(this);
31047		
31048		if ( el.is('SELECT[multiple], SELECT[size]') ) {
31049
31050			el.addClass('multiline');
31051
31052			var attr = inputClassString(el, 'multiple');
31053			if ( !attr ) return;
31054	
31055			el.wrapAll('<div ' + attr + '></div>');
31056
31057			// Researching wrapper
31058			var $wrap	= el.parent();
31059	
31060			// 100% size inputs are marked with class instead
31061			// and triggers default size for fallbacks
31062			if ( $wrap.attr('size') == '100%' )
31063				$wrap.addClass('fullHeight').removeAttr('size');
31064
31065			// Adding default height
31066			if ( !$wrap.attr('size') )
31067				$wrap.attr('size', '3');
31068			
31069		} //IF multiselect
31070		else {
31071			
31072			var nowidth = el.hasClass('nowidth');
31073			
31074			var attr = inputClassString(el, 'select');
31075			if ( !attr ) return;
31076	
31077		// ---- Calculating selector min width ----
31078	
31079			var width = '';
31080	
31081			if ( !nowidth ) {
31082				var tmp = el.clone();
31083				tmp.appendTo('BODY');
31084				width = tmp.width() + 30;
31085				tmp.remove();
31086				
31087				width = ' style="min-width : ' + width + 'px"';
31088			} //IF need width 
31089			
31090			el.wrapAll('<div ' + attr + '></div>');
31091			el.addClass('hidden');
31092			
31093			var val = el.find('OPTION:selected').text();
31094			
31095			el.before('<div class="subcontrol icon gray dropdown"></div>');
31096			el.before('<div class="value"' + width + '>
31096' + val + '</div>');
31097	
31098			el.on('change update', function () {
31099				el.prev('.value').html( el.find('OPTION:selected').text() );
31100			}); //jQuery onChange
31101
31102		} //IF regular select
31103
31104	}); //jQuery each select input
31105
31106/**/
31107// ---- CHECKLIST and RADIOLIST ---- 
31108
31109//	dialog.find('SELECT.checkList, SELECT.radioList').not('.old').each( function () {
31110	dialog.find('SELECT.checkList.new, SELECT.radioList.new').each( function () {
31111		
31112		var $input	= jQuery(this);
31113
31114	// ---- Defaults -----
31115
31116		// Checklist forces multiselect
31117		if ( $input.is('.checkList') )
31118			$input.attr('multiple', 'multiple');
31119
31120		// Adding default height
31121		if ( !$input.attr('size') ) {
31122			
31123			if ( $input.is('[multiple], .checkList, .radioList') )
31124				$input.attr('size', '5');
31125			else
31126				$input.attr('size', '1');
31127			
31128			// For mobiles - size is always 1
31129			if ( $input.is('.noMobile') && isTouchScreen() ) 
31130				$input.attr('size', '1');
31131				
31132		} //IF autodeciding on size
31133
31134		// Preparing attributes
31135		var $attr	= inputClassString($input, '');
31136		if ( !$attr ) return;
31137
31138	// ---- Creating container element ----
31139	
31140		$input.wrapAll('<div ' + $attr + '></div>');
31141		
31142		// Researching wrapper, for later use
31143		var $wrap	= $input.parent();
31144
31145		// Flagging input type: inline/multiline
31146		if ( $wrap.attr('size') != '1' )
31147			$wrap.addClass('multiline');
31148
31149		// 100% size inputs are marked with class instead
31150		// and triggers default size for fallbacks
31151		if ( $wrap.attr('size') == '100%' )
31152			$wrap.addClass('fullHeight').removeAttr('size');
31153
31154	// ---- Model ----
31155		
31156		// Initiating selector model
31157		var $select = $input.mwSelectInput();
31158		
31159	}); //jQuery each morweb selector input
31160
31161	dialog.find('SELECT.checkList, SELECT.radioList').not('.new').each( function () {
31162//	dialog.find('SELECT.checkList.old, SELECT.radioList.old').each( function () {
31163
31164		var sel = jQuery(this);
31165
31166		sel.addClass('multiline');
31167		
31168		if ( sel.is('.checkList') )
31169			sel.attr('multiple', 'multiple');
31170		
31171		var attr = inputClassString(sel, 'list');
31172		if ( !attr ) return;
31173
31174	// ---- Creating container and subcontainer elements ----
31175	
31176		var el		= jQuery('<div ' + attr + '><div class="list"></div></div>');
31177		var items	= el.find('.list');
31178
31179		// 100% size inputs are marked with class instead
31180		// and triggers default size for fallbacks
31181		if ( el.attr('size') == '100%' )
31182			el.addClass('fullHeight').removeAttr('size');
31183
31184		// Adding default height
31185		if ( !el.attr('size') )
31186			el.attr('size', '5');
31187
31188	// ---- Saving source ----
31189	
31190		// Selector itself will be replaced, so we need to save source in separate hidden element which will not affect nothing
31191		
31192		var source_data	= sel.data('source');
31193		
31194		var source	= jQuery('<input type="hidden" class="mw">');
31195		source.data('source', source_data);
31196		source.appendTo(el);
31197
31198	// ---- Transferring hint ----
31199	
31200		var hint	= sel.attr('hint');
31201		if ( hint )
31202			items.attr('hint', hint);
31203
31204	// ---- Composing common attributes ----
31205
31206		var radio	= el.hasClass('radioList'); 
31207		var name	= sel.attr('name');
31208		
31209		// Checkboxes should be arrays
31210		// Not doing for selects that alrady had this
31211		if ( !radio && name && !name.endsWith('[]') )
31212			name += '[]';
31213		
31214		// Input type
31215		var type = ( radio )? 'radio' : 'checkbox';
31216		
31217	// ---- Processing sub controls ----		
31218
31219		sel.find('OPTION').each( function () {
31220		
31221			var op		= jQuery(this);
31222			var title	= op.html();
31223
31224		// ---- Parsing title for special things ----
31225
31226			// Column		
31227			var col		= '';
31228			var d		= title.indexOf('|');
31229			if ( d >= 0 ) {
31230				col = title.substr(d + 1);
31231				title = title.substr(0, d) + '<div class="col">' + col + '</div>';
31232			} //IF divider come
31233			
31234			var $icon	= op.attr('data-icon');
31235			if ( $icon )
31236				$icon = '<div class="icon" style="background-image: url(' + $icon + ');"></div>';				
31237
31238
31239			// Value/checked
31240			var val		= op.val();
31241			var checked	= op.attr('selected'); 
31242
31243			title		= $icon + '<div class="title">' + title + '</div>';			
31244
31245			var titem	= jQuery(title);
31246
31247			var input	= jQuery('<input type="' + type + '" class="meta" name="' + name + '" value="' + val + '" />');
31248			if ( checked ) input.attr('checked', 'checked');
31249
31250			// Setting up actions
31251			var actions	= ['onclick', 'onchange', 'ondblclick']; 
31252			for ( var i in actions )
31253				input.attr(actions[i], sel.attr(actions[i]) );
31254			  
31255			input.change( function () {
31256				
31257				var check = this.checked;
31258				
31259				var el = jQuery(this);
31260				
31261				if ( check ) {
31262					el.parent().addClass('selected icon hi left');
31263				} else { //IF checked
31264					el.parent().removeClass('selected icon hi left')	
31265				} //IF not checked
31266		
31267				if ( this.type == 'radio') {
31268					if ( this.checked ) {
31269						jQuery(this)
31270							.parents('.mwInput.list')				// Searching parent container
31271							.find('INPUT[type="radio"][name="' + this.name + '"]')	// 
31271Searching all items
31272							.not(this)						// Excluding self
31273							.each( function () {					// Triggering change on each
31274								jQuery(this).change();
31275							}); //each radio
31276					} //IF got cheked state
31277				} //IF radio
31278		
31279			}); //jQuery onChange
31280
31281			var $itemClass	= 'item';
31282			var $itemStyle	= '';
31283
31284			if ( checked ) $itemClass+=' selected icon hi left';
31285/*/
31286			var $src	= op.attr('data-icon');
31287			if ( $src ) {
31288				$itemClass += ' icon';
31289				$itemStyle = 'background-image: url(' + $src + ');';
31290			} //IF class
31291/**/
31292			
31293			$itemClass = $itemClass ? ' class="' + $itemClass + '"' : '';
31294			$itemStyle = $itemStyle ? ' style="' + $itemStyle + '"' : '';
31295
31296			var item	= jQuery('<div' + $itemClass + $itemStyle + '></div>');
31297			item.append(titem).append(input);
31298			
31299			item.appendTo(items);
31300		}); //jQuery each option
31301
31302		sel.replaceWith(el);
31303	}); //jQuery each multiselect input
31304
31305	// ---- CHECKBOX and RADIO ---- 
31306	
31307	dialog.find('INPUT[type="checkbox"], INPUT[type="radio"]').not('.meta').each( function () {
31308
31309		var el		= jQuery(this);
31310		
31311		// Checking if custom titles are set
31312		var custom	= el.attr('cap');
31313		
31314		var attr = inputClassString(el, 'checkbox' + ((custom)? ' custom' : ''));
31315		if ( !attr ) return;
31316
31317// ---- Processing custom titles ----	
31318		
31319		// Webkit browsers require smth to have inside to propertly align, so setting nbsp as default
31320		var on		= '&nbsp;';
31321		var off		= '&nbsp;';
31322		
31323		if ( custom ) {
31324			
31325		//	el.removeAttr('cap');
31326	
31327			custom = custom.split('|');
31328	
31329			var on	= custom[0];
31330			var off	= (custom[1])? custom[1] : custom[0];
31331		
31332		} //IF title set
31333
31334	// ---- Wrapping layout structure ----
31335		
31336		el.wrapAll('<div ' + attr + '></div>');
31337		el.addClass('hidden');
31338
31339		el.before('<div class="icon hi left">_</div>');
31340				
31341	// ---- Applying change function ----	
31342	
31343		el.change( function () {
31344			
31345			var check = this.checked;
31346			
31347			if ( check ) {
31348				el.parent().addClass('checked');
31349				el.prev().html(on).addClass('selected');
31350				el.addClass('checked');
31351			} else { //IF checked
31352				el.parent().removeClass('checked')	
31353				el.prev().html(off).removeClass('selected');
31354				el.removeClass('checked');
31355			} //IF not checked
31356
31357		}); //jQuery onChange
31358
31359		// Initiating first time
31360		el.change();
31361
31362	// ---- Radio buttins refresh fix ----	
31363		
31364		if ( this.type == 'radio' ) {
31365			
31366			el.change( function () {
31367
31368				var radio = jQuery(this);
31369
31370				if ( this.checked ) {
31371					
31372					// Searching neighbours via closest parent form. Fastest way. Usually there is form around.
31373					radio.closest('FORM')
31374						.find('INPUT[type="radio"][name="' + this.name + '"].checked')
31375							.not(this).change();
31376					
31377				} //IF got cheked state
31378				
31379			}); //jQuery onChange
31380			
31381		} //IF radio button
31382
31383	}); //jQuery each checbox and radio input
31384
31385// ---- FILES ---- 
31386	
31387	dialog.find('INPUT[type=file]').each( function () {
31388		
31389		var el = jQuery(this);
31390		
31391		var attr = inputClassString(el, 'file');
31392		if ( !attr ) return;
31393		
31394		// Detecting SN if set, and applying it as uBar
31395		var fid = el.attr('sn') || '';
31396		
31397		if ( fid )
31398			fid = ' uBar_' + fid;
31399
31400		// Preparing structure and layout
31401		el.wrapAll('<div ' + attr + '></div>');
31402
31403		// Reading input wrapper
31404		var $jWrap	= el.closest('.mwInput');
31405		
31406		// Saving some properties
31407		var $size	= el.attr('size') || $jWrap.attr('size') || 6;
31408		var $preview	= el.attr('preview') || '';
31409
31410		// Preparing preview element, it goes first
31411		var $jPreview	= jQuery('<div class="filePreview"></div>');
31412		el.before($jPreview);
31413
31414		// Adding additional face wrapper for layout control
31415		var $jFace	= jQuery('<div class="fileFace divider"></div>');
31416 
31417 		// Adding title progress control inside face
31418		var $jProgress	= jQuery('<div class="subcontrol hi fill progressFill' + fid + '"></div>').appendTo($jFace);
31419		var $jTitle	= jQuery('<div class="fileName"></div>').appendTo($jFace);
31420		var $jNum	= jQuery('<div class="num progressNum' + fid + '">&nbsp;</div>').appendTo($jFace);
31421
31422		// Attaching face
31423		el.before($jFace);
31424
31425		// Preparing controls
31426		// Upload/browse is unconditional, goes right after the face
31427		var $jUpload	= jQuery('<div class="subcontrol button icon upload"></div>').insertAfter($jFace);
31428
31429		// Preparing download button, but not attaching it yet (this will be decided in updateFace() )
31430		var $jDownload	= jQuery('<div class="subcontrol button icon download hi"></div>');
31431
31432		// Delete button present only if specified
31433		// Always inserted last
31434	//	var $jDelete	= jQuery('<div class="subcontrol button icon delete red"></div>');
31435	//	$jDelete.insertBefore(el);
31436
31437		// Testing varying subcontrols count
31438	//	el.before('<div class="subcontrol button icon ok"></div>');
31439	//	el.before('<div class="subcontrol button icon delete"></div>');
31440	//	el.before('<div class="subcontrol button icon download"></div>');
31441	//	el.before('<div class="subcontrol button icon search"></div>');
31442
31443		// Autoconfiguring path for morweb editors
31444		// This is supported only for inputs added through form editor, which are part of files section
31445		// They should have gallery attribute, input should be part of window (this will indicate presence of morweb)
31446		// ToDo: This validation should be done outside by forms editor/custom inputs
31447		
31448		// Custom paths are still supported normally
31449		var $fPath	= el.attr('data-path');
31450		var $fGallery	= el.attr('gallery');
31451		var $fPrivate	= el.attr('private') || 0;
31452		var $fStorage	= $fPrivate ? 'download' : 'files';
31453
31454		// Path for gallery files are known
31455		// Checking if running inside window
31456		if ( !$fPath && $fGallery && el.closest('.mwWindow').length  ) {
31457
31458			// Default path is in gallery
31459			var $fPath	= '/'+$fStorage+'/galleries/';
31460			
31461			// Setting path as attribute
31462			el.attr('data-path', $fPath);
31463			
31464		} //If no path set
31465
31466		// Using dedicated face update function
31467		function mwFileInputUpdateFace ($el) {
31468
31469			// Need jQuery
31470			$el	= _jq($el);
31471
31472			// Checking if file/path set.
31473			// Both are required for preview and download 
31474			var $file	= $el.attr('data-file');
31475			var $fileVal	= fileName($el.get(0).value);
31476			var $path	= $el.attr('data-path');
31477
31478
31479			// Detecting value is present, and using as visual title
31480			var $title = $fileVal || $el.attr('data-file') || $el.attr('value') || $el.attr('src') || $el.attr('img') || $el.attr('cap')  || 'Select File...';
31481
31482			// Setting title
31483			$jTitle.html($title);
31484
31485			// Preparing preview url right away
31486			var $previewUrl	= '';
31487
31488			// File and path should be just present to create download button
31489			if ( $file && $path ) {
31490				
31491				// Attaching download button 
31492				$jDownload.insertAfter($jUpload);
31493
31494				// Preparing preview with direct path
31495				// Replacing /files with resizer: /getimage
31496				// This way - images available for resizer will be run through resizer, 
31497				// and very custom images will stay as they are
31498				
31499				// Previews are available only for images currently
31500				if ( isFileImage($file) ) {
31501				
31502					var $previewUrl	= $path.replace('/'+$fStorage+'/', '/getimage/');
31503					$previewUrl += $file+'?resize='+Math.ceil( $jWrap.width() || 600 )+'x0';
31504
31505				} //IF image file
31506				
31507			} //IF file defined
31508				
31509			else {
31510				
31511				// Detaching download
31512				$jDownload.detach();
31513
31514				// Resetting preview
31515				$previewUrl	= '';
31516				
31517			} //IF no file
31518
31519			// ToDo: split input (hidden text with input name and name_file for file input)
31520			// ToDo: delete button support. Automated for split inputs, ondelete="" event enables delete button
31521
31522			// For preview - need to do additional checks
31523			// It's visibe when forced or when both file and path are present
31524			// Previe is visible in auto mode when either URL is set, or file source is set
31525			if ( !$fPrivate && (($preview == 'on') || ($preview && ($previewUrl || $fileVal))) ) {
31526
31527				// Filling in preview BG
31528				// Using as is, even if it happen to be empty (when preview box is forced)
31529				$jPreview.css({'background-image' : "url('"+$previewUrl+"')"}).show();
31530				
31531				// Setting input height
31532				$jWrap.attr('size', $size);
31533				
31534			} //IF file defined
31535				
31536			else {
31537				
31538				// Clearing preview
31539				$jPreview.css({'background-image' : "url('')"}).hide();
31540
31541				$jWrap.attr('size', '');
31542				
31543			} //IF no file
31544			
31545		} //FUNC mwFileInputUpdateFace
31546
31547		// Adding download click
31548		$jDownload.click( function () {
31549			
31550			// Support for "view file" requires both data path and value
31551			// Supporting download only for values set through fromArray() calls.
31552			// If file was browsed - can't download anyway 
31553			var $file	= el.attr('data-file');
31554			var $path	= el.attr('data-path');
31555
31556			if ( !$file || !$path )
31557				return;
31558
31559			// If gallery specified - using it to view file
31560			if ( !isEmpty($fGallery) ) {
31561				
31562				// Compiling path to gallery view in backend
31563				$path		= '/site/files/'+$fGallery+'/';
31564				
31565			} //IF actual gallery specified
31566			
31567			// Using virtual form to safely click into new tab
31568			submitUrl($path+$file, {}, true);    
31569			
31570		});
31570 //FUNC $jDownload.click			
31571
31572		// Adding preview click
31573		$jPreview.click( function () {
31574			
31575			// Just triggering download
31576			$jDownload.click();
31577			
31578		}); //$jPreview.click
31579
31580		// Adding update on change
31581		el.change( function () {
31582
31583			// Preparing face
31584			mwFileInputUpdateFace(this);
31585
31586			// Preloading image if defined/selected
31587			if ( $preview && this.value )
31588				mwBackgroundPreview($jPreview, this);
31589
31590		}); //el.change
31591
31592		// Running initial update
31593		mwFileInputUpdateFace(el);
31594
31595	}); //jQuery each select input
31596
31597// ---- BROWSER ---- 
31598
31599	dialog.find('SELECT.selectEx').each( function () {
31600
31601		var $el = jQuery(this);
31602
31603		$el.addClass('multiline');
31604		
31605		var $attr = inputClassString($el, '');
31606		if ( !$attr ) return;
31607
31608	// ---- Creating container element ----
31609	
31610		$el.wrapAll('<div ' + $attr + '></div>');
31611		
31612		// Researching wrapper, for later use
31613		var $wrap	= $el.parent();
31614
31615		// 100% size inputs are marked with class instead
31616		// and triggers default size for fallbacks
31617		if ( $wrap.attr('size') == '100%' )
31618			$wrap.addClass('fullHeight').removeAttr('size');
31619
31620		// Adding default height
31621		if ( !$wrap.attr('size') )
31622			$wrap.attr('size', '6');
31623
31624	// ---- Options ----
31625	
31626		var $o	= { 
31627			inline : $wrap.is('.inline') 
31628		}; //$o
31629		
31630		if ( $el.data('title') )
31631			$o.title = $el.data('title'); 
31632			
31633		// Initiating selector
31634		// Checking inline mode by resulitng wrapper class
31635		var $sx = mwSelectEx($el, $o);
31636		
31637		$el.data('selectEx', $sx);
31638		
31639	}); //jQuery.each select
31640
31641// ---- COMMON ----
31642	
31643	// Tracking hovers/focus via java and classes to have extra styling options.
31644	// Unfortunately have to exclude few inputs
31645
31646	dialog.find('.mwInput').each( function () {
31647		
31648		var $el = jQuery(this);
31649		
31650		// Hover
31651		$el
31652			.off('mouseenter.mwInput') 
31653			.on('mouseenter.mwInput', function () {
31654				$el.addClass('hover');
31655
31656				if ( $el.parent().is('.combo') )
31657					$el.parent().addClass('hover');
31658
31659			}) //FUNC hoverin
31660
31661			.off('mouseleave.mwInput')
31662			.on('mouseleave.mwInput', function () {
31663				$el.removeClass('hover');
31664
31665				if ( $el.parent().is('.combo') )
31666					$el.parent().removeClass('hover');
31667
31668			}) //FUNC hoverout
31669		; //$el  
31670
31671		$el.not('.selectEx')
31672			.off('focusin.mwInput') 
31673			.on('focusin.mwInput', function () {
31674				$el.addClass('focus');
31675
31676				if ( $el.parent().is('.combo') )
31677					$el.parent().addClass('focus');
31678			}) //FUNC hoverout
31679
31680			.off('focusout.mwInput')
31681			.on('focusout.mwInput', function () {
31682				$el.removeClass('focus');
31683
31684				if ( $el.parent().is('.combo') )
31685					$el.parent().removeClass('focus');
31686			}) //FUNC hoverout
31687		; //$el  
31688		
31689	}); //jQuery.each input
31690
31691	return false;	
31692} //FUNC styleDialog
31693
31694/** //** ----= unstyleInput	 =----------------------------------------------\**//** \
31695*
31696* 	Removes styles from input given.
31697*
31698* 	@param	jQuery	input	- Valid jQuery input element selector.
31699* 	@return	jQuery		- New input element.
31700*
31701\**//** ------------------------------------= by Mr.V!T @ Morad Media Inc. =----/** //**/
31702function unstyleInput ( input ) {
31703
31704	// All styled inputs have .mw class.
31705	// Source is stored under jQuery's .data().
31706	input = _jq(input);
31707	
31708	// Making sure that we are pointing to input itself
31709/*/
31710	if ( !input.is('.mw') )
31711		input = input.find('.mw');
31712	
31713	var source	= input.data('source');
31714/*/
31715	var source	= input.find('.mw').data('source');
31716/**/
31717
31718	if ( source ) {
31719		source = jQuery(source);
31720		input.replaceWith(source);
31721		return source;
31722	} //IF source was stored
31723		
31724	return input;
31725} //FUNC unstyleInput
31726
31727/** //** ----= unstyleDialog	 =----------------------------------------------\**//** \
31728*
31729* 	Removes styles from inputs in given element.
31730*
31731* 	@param	MIXED	el	- Valid jQuery element selector.
31732* 	@return	bool		- Always FALSE.
31733*
31734\**//** ------------------------------------= by Mr.V!T @ Morad Media Inc. =----/** //**/
31735function unstyleDialog ( dialog ) {
31736
31737	// All styled inputs have .mw class.
31738	// Source is stored under jQuery's .data().
31739	dialog.find('.mw').each( function () {
31740		
31741		var el		= jQuery(this);
31742		
31743		var source	= el.data('source');
31744
31745		if ( source )
31746			el.closest('.mwInput', dialog).replaceWith(source);
31747		
31748	}); //jQuery each .mw 
31749	
31750	return false;
31751} //FUNC unstyleDialog
31752
31753function setValidations (dialog, validations) {
31754	
31755	if ( !dialog )
31756		return;
31757	
31758	dialog.find('INPUT, SELECT, TEXTAREA')
31759		.each( function () {
31760			
31761			// Shortcuts to input and it's parent (morweb forms)
31762			var input	= jQuery(this);
31763			var parent	= input.closest('.mwInput');
31764			if ( !parent.length )
31765				parent = input;
31766			
31767			// Input should have name to be validatable
31768			var name	= input.attr('name');
31769			if ( !name ) return; 
31770			
31771			// Shortcut to messages array to simplify code
31772			var msgs	= validations[name];
31773
31774			// Might be complex input name, so checking for that
31775			if ( isUndefined(msgs) ) {
31776			
31777				// Splitting name to parts
31778				// Safe to do event if it's not complext - will end up with same name
31779				var $tmp	= name.split(/\[|\]/);
31780				
31781				// Clearing empties
31782			//	$tmp		= $tmp.filter(n => n);
31783				$tmp		= $tmp.filter(Boolean);
31784				
31785				// Finally - using last element
31786				name		= $tmp.slice(-1)[0];
31787				msgs		= validations[name];
31788				
31789			} //IF nothing found
31790
31791			// TinyMCE support
31792			// Marking tinyEditor instead of input itself
31793			if ( input.is('.mwTinyMCE') ) {
31794
31795				// If it's tinyMCE input - there should be sibling mceEditor input
31796				input	= input.siblings('.mceEditor');
31797				parent	= input;
31798				
31799				// If none - smth went wrong
31800				if ( !input.length )
31801					return;
31802		 	} //IF tinyMCE input
31803			
31804			// Removing errors attribute if no messages
31805			if ( !msgs ) {
31806				
31807				input.removeAttr('error');
31808				
31809				// Class for parent
31810				parent.removeClass('error');								
31811
31812				return;									
31813			} //IF no messages
31814			
31815			// Compiling messages, very simple br can be stored inside attribute
31816			var msg = implode('<br />', msgs);
31817
31818			// Setting error
31819			input.attr('error', msg);								
31820
31821			// Class for parent
31822			parent.addClass('error');								
31823			
31824		}); //jQuery.each.callback
31825	
31826	return;
31827} //FUNC setValidations
31828
31829/** //** ----= 	fillViews	 =----------------------------------------------\**//** \
31830*
31831* 	Fills fileds views within specified element.
31832*
31833*	@param	jQuery	el	- Context element.
31834*	@param	object	obj	- Data object to pull views from.
31835*	
31836* 	@return	bool		- Always FALSE.
31837*
31838\**//** ------------------------------------= by Mr.V!T @ Morad Media Inc. =----/** //**/
31839function fillViews (el, obj) {
31840
31841	el = _jq(el);
31842
31843	for ( var i in obj ) {
31844		el.find('.view-' + i).html(obj[i]);
31845	} //FOR each field
31846
31847	return false;
31848} //FUNC fillViews
31849/** //** ----= CLASS mwSelectInput	=------------------------------------------------------------------------------\**//** \
31850*
31851*	Selector input JS model. Supports dropdowns, multiselects, custom item input, inline filtering and adding new items.	
31852*
31853* 	@package	morweb
31854* 	@subpackage	system
31855* 	@category	model
31856*
31857\**//** ---------------------------------------------------------------------------= by Mr.V!T @ Morad Media Inc. =----/** //**/
31858jQuery.fn.mwSelectInput = function ($options) {
31859
31860	if ( !this.length )
31861		return;
31862
31863	// Retrieving or creating model
31864	var $obj = this.data('mwSelectInput') || mwSelectInput(this);	//.set($options);
31865	
31866	// Storing in element
31867	this.data('mwSelectInput', $obj);
31868
31869	// Done
31870	return $obj;
31871
31872} //FUNC mwSelectInput
31873
31874function mwSelectInput ( $el, $options ) { return vEventObject(['onInit', 'onChange', 'onFilter', 'onAdd'], {
31875
31876	dom			: {			// Contains shortcuts to usable elements
31877	
31878		// Main
31879		container		: false,		// mwInput element
31880		input			: false, 		// Main input (hidden)
31881
31882		// Panels
31883		face			: false,		// Face bar, wrapping around value input and dropdown button 
31884		filter			: false, 		// Filter input, used to filter items in list
31885		body			: false, 		// Main dropdown body (filter and items container)
31886		list			: false, 		// Items wrapper
31887
31888		// Controls 		
31889		iValue			: false,		// Value input, used to to display selected value or as custom input 
31890		iSearch			: false, 		// Search input, used to filter items in list
31891		btnDown			: false,		// Dropdown button subcontrol
31892		btnAdd			: false,		// Button for adding custom values
31893		btnClear		: false,		// Button for resetting search filter
31894
31895		// Misc		
31896		items			: false, 		// Individual rendered option items
31897
31898		overlay			: false, 		// Overlay, when used
31899		
31900	}, //dom
31901
31902	multiselect		: false,			// Set TRUE to allow multiple values (checkList) 
31903	multiline		: false,			// Set TRUE to use as multiline input 
31904	filter			: false,			// Set TRUE to use inline filtering
31905	custom			: false,			// Set TRUE to allow custom values (additinal input)
31906	noMobile		: false,			// Set TRUE to skip init when used with mobile device (relies on native experience)
31907
31908	resizeDelay		: 5,				// Resize update delay
31909	resizeTimout		: false,			// Stores timout during window resizing
31910
31911/* ==== SETUP =============================================================================================================== */
31912
31913	/** //** ----= set	=--------------------------------------------------------------------------------------\**//** \
31914	*
31915	*	Updates self properties with given values.
31916	*
31917	*	@param	MIXED	$option		- Option to set. Can be data object to setup several properties.
31918	*	@param	MIXED	[$value]	- Value to set. Not used if object passes as first parameter.
31919	*
31920	*	@return SELF
31921	*
31922	\**//** -------------------------------------------------------------------= by Mr.V!T @ Morad Media Inc. =----/** //**/
31923	set		: function ($option, $value) {
31924
31925		var $this = this;
31926
31927		// Values can come as object, or single value
31928		// Applying using object, for code unification below
31929		// Any variable are accepted, to allow custom data storage
31930		var $o = {};
31931
31932		if ( arguments.length === 1 )
31933			$o = $option;
31934		else
31935			$o[$option] = $value;
31936
31937	// ==== Events ====
31938
31939		// Processing events, as those should be cleared before extending
31940		for ( var $i in $o ) {
31941
31942			// Skipping non events and non funcitons
31943			if ( !this.__events[$i] || !isFunction($o[$i]) )
31944				continue;
31945
31946			// Setting up event, and removing it from opitons
31947			this[$i]($o[$i]);
31948			delete($o[$i]);
31949
31950		} //FOR each opiton
31951
31952	// ==== Self ====
31953
31954		jQuery.extend(this, $o);
31955
31956		return this;
31957
31958	}, //FUNC set
31959
31960/* ==== INIT ================================================================================================================ */ 
31961
31962	/** //** ----= init	=--------------------------------------------------------------------------------------\**//** \
31963	*
31964	*	Initiates payment form.
31965	*
31966	*	@return self
31967	*
31968	\**//** -------------------------------------------------------------------= by Mr.V!T @ Morad Media Inc. =----/** //**/
31969	init			: function () {
31970
31971		var $this = this;
31972	
31973	// ---- Main ----	
31974		
31975		// Saving main input and looking for wrapper
31976		$this.dom.input		= $el;
31977		$this.dom.container	= $el.closest('.mwInput');
31978		
31979		// Few shortcuts
31980		var $container		= $this.dom.container;
31981		var $input		= $this.dom.input;
31982
31983		// Adding required classes to wrapper
31984		$this.dom.container.addClass('list');
31985
31986	// ---- Flags ----
31987
31988		// Reading flags from wrapper and input
31989		// ToDo: improove flags management: classes should be guaranteed during render, for CSS. 
31990		// Need to put back applied flags	
31991		$this.set({
31992			multiselect	: $input.is('[multiple]'),
31993			multiline	: $container.is('.multiline'),
31994			filter		: $container.is('.filter'),
31995			custom		: $container.is('.custom'),
31996			noMobile	: $container.is('.noMobile'),
31997		}); //set
31998
31999	// ---- Mobile ----
32000	
32001		// If mobile is used - need to skip render optionally
32002		if ( $this.noMobile && isTouchScreen() ) {
32003			
32004			// For mobile version - skipping render, but showing regular select instead
32005			// Adding marker
32006			$this.dom.container.addClass('isMobile');
32007			
32008			return;
32009			
32010		} //IF mobile
32011
32012	// ---- DOM ----	
32013
32014		// Hiding main select element
32015		$this.dom.input.hide();
32016
32017		// Rendering face first
32018		$this.renderFace();
32019
32020		// Then goes body, everything else would be inside
32021		$this.renderBody();
32022
32023	// ---- Styles ----
32024
32025
32026	// ---- Events ----
32027
32028		$this.initEvents();
32029
32030		// Initial update
32031		$this.update();
32032
32033		return $this;
32034		
32035	}, //FUNC init
32036
32037	/** //** ----= initEvents	=------------------------------------------------------------------------------\**//** \
32038	*
32039	*	Initializes events. All in one place, for easier management.
32040	*
32041	*	@return SELF
32042	*
32043	\**//** -------------------------------------------------------------------= by Mr.V!T @ Morad Media Inc. =----/** //**/
32044	initEvents		: function ( ) {
32045	
32046		// Few shortucts
32047		var $this	= this;
32048		var $input	= $this.dom.input;
32049		var $container	= $this.dom.container;
32050		
32051		// When select updates - it should update items and face
32052		$this.dom.input.on('change.mwSelect', function () {
32053			
32054			$this.update();
32055			
32056		}); //FUNC input.onchange
32057
32058		// Adding item clicks, listening on body, in case reinit/changes happen in list or items 
32059		$this.dom.body.on( 'click.mwSelect', '.item', function () {
32060		
32061			// Getting elements
32062			var $item	= jQuery(this);
32063			var $option	= $item.data('option');
32064			
32065			// No need to do anything if already selected option is clicked again
32066			// Applicable only for non-multiselect
32067			if ( !$this.multiselect && $option.prop('selected') )
32068				return;
32069		
32070			// Updating property only. Attribute will be also updated by updateItems()
32071			// Doing this way to ensure that correct attributes are updated on all items 
32072			$option.prop( 'selected', !$option.prop('selected') );
32073
32074			// Triggering change on main input
32075			$this.dom.input.trigger('change');
32076		
32077		}); //FUNC item.onclick	
32078
32079		// Filter executs on key presses, if input available
32080		if ( $this.filter ) {
32081			
32082			$this.dom.iSearch.on('keyup.mwSelect', function () {
32083
32084				// Updating items, this will apply filter
32085				$this.update();
32086				
32087			}); //FUNC input.onchange
32088
32089			$this.dom.btnClear.on('click.mwSelect', function () {
32090
32091				// Just clearing filter value and updating items
32092				$this.dom.iSearch.val('');	
32093
32094				// Updating items, this will apply filter
32095				$this.update();
32096
32097			}); //FUNC input.onchange
32098			
32099		} //IF filter enabled
32100
32101		// In inline mode - need to show/hide body
32102		if ( !$this.multiline ) {
32103
32104			// Dropdown button always works
32105			$this.dom.btnDown.on('click.mwSelect', function () {
32106
32107				// Showing/hiding depending on state
32108				$this.toggleBody();
32109				
32110			}); //FUNC button.onclick
32111
32112			// For non-custom inputs - value also toggles body
32113			if ( !$this.custom )
32114				// Dropdown button always works
32115				$this.dom.iValue.on('click.mwSelect', function () {
32116	
32117					// Showing/hiding depending on state
32118					$this.toggleBody();
32119					
32120				}); //FUNC input.onclick
32121
32122			
32123		} //IF inline mode
32124		
32125	}, //FUNC initEvents
32126
32127/* ==== Render ============================================================================================================== */ 
32128
32129	/** //** ----= renderBody	=------------------------------------------------------------------------------\**//** \
32130	*
32131	*	Renders main body element.
32132	*
32133	*	@return SELF
32134	*
32135	\**//** -------------------------------------------------------------------= by Mr.V!T @ Morad Media Inc. =----/** //**/
32136	renderBody		: function ( ) {
32137
32138		// Few shortucts
32139		var $this	= this;
32140		var $input	= $this.dom.input;
32141		var $container	= $this.dom.container;
32142		
32143		// Marking body as inline if necessary for CSS
32144		var $mode	= $this.multiline ? 'multiline absoluteFill' : 'mwInput oneline';
32145		$this.dom.body	= jQuery('<div class="body '+$mode+'"></div>').appendTo($container);
32146		
32147		// Reset of css would be set on display
32148	
32149	// ---- Children ----	
32150		
32151		// Rendering filter, it goes inside body
32152		$this.renderFilter();
32153		
32154		// Now can render items list
32155		$this.renderList();
32156		
32157		return $this;
32158		
32159	}, //FUNC renderBody
32160
32161	/** //** ----= renderFace	=------------------------------------------------------------------------------\**//** \
32162	*
32163	*	Renders face panel. Used to display current value and entering custom value for inline mode. 
32164	*
32165	*	@return SELF
32166	*
32167	\**//** -------------------------------------------------------------------= by Mr.V!T @ Morad Media Inc. =----/** //**/
32168	renderFace		: function ( ) {
32169
32170		// Few shortucts
32171		var $this	= this;
32172		var $input	= $this.dom.input;
32173		var $container	= $this.dom.container;
32174
32175		// Adding face controls only for inline input
32176		if ( $this.multiline )
32177			return $this;
32178		
32179		// Creating elements as one html chunk, then finding referrences afterwards
32180		var $html	= '';
32181		
32182		// Subcontrol button goes first, as it's floating
32183		$html		= '<div class="subcontrol right icon gray dropdown"></div>';
32184		
32185		// Input is wrapped in div to behave properly with floated element nearby
32186		$html		= $html+'<div class="subInput value"><input type="text" class="mw" /></div>';
32187		
32188		// Finally - entire thing is wrapped into another element
32189		$html		= '<div class="face">'+$html+'</div>';
32190
32191		// Appending entire thing to container
32192		jQuery($html).appendTo($container);
32193
32194		// Looking for individual elements now
32195		$this.dom.iValue	= $container.find('.subInput.value input');
32196		$this.dom.btnDown	= $container.find('.subcontrol.dropdown');
32197		$this.dom.face		= $container.find('.face');
32198
32199		// If custom input is not used - value is only for display, so making it readonly
32200		if ( !$this.custom )
32201			$this.dom.iValue.attr('readonly', 'readonly');
32202
32203		return $this;
32204
32205	}, //FUNC renderFace
32206
32207	/** //** ----= renderFilter	=------------------------------------------------------------------------------\**//** \
32208	*
32209	*	Renders filter panel. Used to filter items list, and adding custom values for multiselects.
32210	*
32211	*	@return SELF
32212	*
32213	\**//** -------------------------------------------------------------------= by Mr.V!T @ Morad Media Inc. =----/** //**/
32214	renderFilter		: function ( ) {
32215		
32216		// Few shortucts
32217		var $this	= this;
32218		var $input	= $this.dom.input;
32219		var $container	= $this.dom.container;
32220
32221		// Adding filter only if it's allowed
32222		if ( !$this.filter )
32223			return $this;
32224
32225		// Filter is positioned inside body wrapper, to display and hide together with list
32226		// Preparing html for filter bar
32227		
32228		// Subcontrols buttons go first, as they're floating
32229		var $html	= '';
32230		$html		+= '<div class="subcontrol left icon gray search"></div>';
32231		$html		+= '<div class="subcontrol right button icon hi plus add"></div>';
32232		$html		+= '<div class="subcontrol right icon delete clear gray"></div>';
32233		
32234		// Input is wrapped in div to behave properly with floated element nearby
32235		$html		= $html+'<div class="subInput search"><input type="text" class="mw" /></div>';
32236		
32237		// Finally - entire thing is wrapped into another element
32238		$html		= '<div class="filter">'+$html+'</div>';
32239
32240		// Appending entire thing to container
32241		jQuery($html).appendTo($this.dom.body);
32242
32243		// Looking for individual elements now
32244		$this.dom.filter	= $this.dom.body.find('.filter');
32245		
32246		$this.dom.iSearch	= $this.dom.filter.find('.subInput.search input');
32247		$this.dom.btnAdd	= $this.dom.filter.find('.subcontrol.add');
32248		$this.dom.btnClear	= $this.dom.filter.find('.subcontrol.clear');
32249		
32250		return $this;
32251		
32252	}, //FUNC renderFilter
32253
32254	/** //** ----= renderList	=------------------------------------------------------------------------------\**//** \
32255	*
32256	*	Renders items panel, including individual items.
32257	*
32258	*	@return SELF
32259	*
32260	\**//** -------------------------------------------------------------------= by Mr.V!T @ Morad Media Inc. =----/** //**/
32261	renderList		: function ( ) {
32262		
32263		// Few shortucts
32264		var $this	= this;
32265		var $input	= $this.dom.input;
32266		var $container	= $this.dom.container;
32267
32268	// ---- Wrap ----
32269		
32270		// Clearing old wrapper if any happen
32271		if ( $this.dom.list )
32272			$this.dom.list.remove();
32273
32274	 	// Recreating items wrapper
32275	 	$this.dom.list	= jQuery('<div class="list"></div>').appendTo( $this.dom.body );
32276
32277	// ---- Items ----
32278
32279		// Resetting items collection		
32280		$this.dom.items	= jQuery([]);
32281
32282		// Looping through options and generating items
32283		// ToDo: support grouping, custom icons, columns and more
32284		$input.find('option').each( function () {
32285			
32286			var $option	= jQuery(this);
32287			
32288			// Rendering item into body
32289			var $item	= $this.renderItem($option).appendTo( $this.dom.list );
32290			
32291			// And storing it in self for future reuse
32292			$this.dom.items	= $this.dom.items.add($item); 
32293			
32294		}); //FOR each item
32295	 
32296		return $this;
32297		
32298	}, //FUNC renderList
32299
32300	/** //** ----= renderItem	=------------------------------------------------------------------------------\**//** \
32301	*
32302	*	Renders single item.
32303	*
32304	*	@return SELF
32305	*
32306	\**//** -------------------------------------------------------------------= by Mr.V!T @ Morad Media Inc. =----/** //**/
32307	renderItem		: function ( $option ) {
32308
32309		// Few shortucts
32310		var $this	= this;
32311
32312	// ---- Text ----
32313
32314		var $text	= $option.html(); 
32315	
32316	// ---- Image ----
32317		
32318		// Preparing image if any
32319		var $image	= $option.attr('data-image');
32320		var $imageHtml	= '';
32321		
32322		if ( $image ) {
32323			
32324			$imageHtml = '<div class="image" style="background-image: url(\''+$image+'\')"></div>';
32325			
32326		} //IF image provided
32327
32328	// ---- Counter ----
32329
32330		// Adding counter
32331		var $count	= $option.attr('data-count');
32332		var $countHtml	= '';
32333		
32334		if ( $count ) {
32335			
32336			$countHtml = '<div class="count">'+$count+'</div>';
32337			
32338		} //IF count provided
32339
32340	// ---- Item ----
32341		
32342		// Creating item
32343		var $item	= jQuery('<div class="item icon hi left">'+$imageHtml+$countHtml+$text+'</div>');
32344		
32345		// Setting up links
32346		$option.data('item', $item);
32347		$item.data('option', $option);
32348			
32349		return $item;
32350		
32351	}, //FUNC renderItem
32352
32353	/** //** ----= renderOverlay	=------------------------------------------------------------------------------\**//** \
32354	*
32355	*	Renders overlay.
32356	*
32357	*	@return SELF
32358	*
32359	\**//** -------------------------------------------------------------------= by Mr.V!T @ Morad Media Inc. =----/** //**/
32360	renderOverlay		: function ( ) {
32361
32362		// Few shortucts
32363		var $this	= this;
32364		
32365		// Adding overlay to page
32366		$this.dom.overlay	= jQuery('<div class="mwInputOverlay mwInput list"></div>').appendTo('body');
32367		
32368		// Marking body with class as flag 
32369		// And Moving body inside overlay
32370		$this.dom.body
32371		//	.addClass('show')
32372			.appendTo($this.dom.overlay);
32373		
32374		// Click on overlay hides stuff
32375		$this.dom.overlay.on('click', function ($e) {
32376			
32377			$this.hideBody();
32378
32379			$e.stopPropagation();
32380			
32381		}); //FUNC overlay.click
32382		
32383		// Preventing click on body for closing (only for multiselects)
32384
32385		if ( $this.multiselect )
32386			$this.dom.body.on('click', function ($e) {
32387				
32388				$e.stopPropagation();
32389				
32390			}); //FUNC body.click
32391
32392		// Filter never closes anyway
32393		if ( $this.filter )
32394			$this.dom.filter.on('click', function ($e) {
32395				
32396				$e.stopPropagation();
32397				
32398			}); //FUNC body.click
32399
32400	// ---- Resize ----
32401
32402		// If body resizes for some readon - need to update selector position
32403		// Doing this only for inline mode
32404		
32405		// Initiating window resize after small delay
32406		// to skip initial browser resizie events
32407		setTimeout( function () {
32408
32409			// Updating dimensions on resize
32410			jQuery(window).on('resize.mwSelect', function ($e) {
32411	
32412				// Using timeout during resize, to reduce updates count
32413				if ( $this.resizeTimout )
32414					clearTimeout($this.resizeTimout);
32415								
32416				$this.resizeTimout = setTimeout( function () {
32417			
32418					$this.updatePosition();
32419			
32420				}, $this.resizeDelay);
32421				
32422			}); //window.onResize
32423			
32424		}, 50);
32425			
32426		return $this;
32427		
32428	}, //FUNC renderOverlay
32429
32430/* ==== Display ============================================================================================================= */ 
32431
32432	/** //** ----= showBody	=--------------------------------------------------------------------------------------\**//** \
32433	*
32434	*	Displays body, recalculating positions.
32435	*
32436	*	@return SELF
32437	*
32438	\**//** -------------------------------------------------------------------= by Mr.V!T @ Morad Media Inc. =----/** //**/
32439	showBody		: function (  ) {
32440
32441		// Few shortucts
32442		var $this	= this;
32443		
32444		// Creating overlay, this will also move body inside
32445		$this.renderOverlay();
32446
32447		// Displaying overlay
32448		$this.dom.overlay.show();
32449		
32450		setTimeout( function () {
32451
32452			// While it adjusts - can now reposition body
32453			// At this point - everything is rendered and have dimensions
32454			$this.updatePosition();
32455
32456			// Finally - executing fade/slide animation			
32457			$this.dom.body.addClass('show')
32458			$this.dom.overlay.addClass('show');
32459
32460		}, 1);
32461		
32462	}, //FUNC showBody
32463
32464	/** //** ----= hideBody	=--------------------------------------------------------------------------------------\**//** \
32465	*
32466	*	Hides body.
32467	*
32468	*	@return SELF
32469	*
32470	\**//** -------------------------------------------------------------------= by Mr.V!T @ Morad Media Inc. =----/** //**/
32471	hideBody		: function (  ) {
32472
32473		// Few shortucts
32474		var $this	= this;
32475
32476		// Hiding overlay and unflagging body
32477		$this.dom.overlay.removeClass('show');
32478		$this.dom.body.removeClass('show');
32479
32480		// Allowing it to play animation with timeout
32481		setTimeout( function () {
32482
32483			// Putting body back, where it belongs :)
32484			// This will also hide it. Inline body is display:none outside overlay
32485			$this.dom.body.appendTo( $this.dom.container );
32486	
32487			// Removing overlay from DOM now 
32488			$this.dom.overlay.remove();
32489
32490			// Clearing resizing tracking
32491			jQuery(window).off('resize.mwSelect');
32492
32493		}, 200);
32494
32495	}, //FUNC hideBody
32496
32497	/** //** ----= toggleBody	=------------------------------------------------------------------------------\**//** \
32498	*
32499	*	Toggles body visibility, depending on current state.
32500	*
32501	*	@return SELF
32502	*
32503	\**//** -------------------------------------------------------------------= by Mr.V!T @ Morad Media Inc. =----/** //**/
32504	toggleBody		: function (  ) {
32505
32506		// Few shortucts
32507		var $this	= this;
32508
32509		// Just looking at body state
32510		if ( $this.dom.body.is('.show') ) {
32511			$this.hideBody();
32512		} //IF visible
32513		else {
32514			$this.showBody();
32515		} //IF hidden
32516
32517	}, //FUNC toggleBody
32518	
32519/* ==== HELPERS ============================================================================================================= */ 
32520	
32521	/** //** ----= getPosition	=-------------------------------------------------------------------------------\**//** \
32522	*
32523	* 	Returns panel position relative to screen.
32524	* 
32525	\**//** -------------------------------------------------------------------= by Mr.V!T @ Morad Media Inc. =----/** //**/
32526	getPosition		: function () {
32527
32528		var $this	= this;
32529		var $el		= $this.dom.container;
32530		 
32531		var $offs	= $el.offset();
32532		var $sTop	= jQuery(window).scrollTop();
32533		var $sLeft	= jQuery(window).scrollLeft();
32534		
32535		// Recalculating coords based on scroll position
32536		$offs.top	= $offs.top - $sTop;
32537		$offs.left	= $offs.left - $sLeft;
32538		
32539		return $offs;
32540		
32541	}, //FUNC getPosition
32542
32543	/** //** ----= updatePosition	=-------------------------------------------------------------------------------\**//** \
32544	*
32545	* 	Updates body position against element
32546	* 
32547	\**//** -------------------------------------------------------------------= by Mr.V!T @ Morad Media Inc. =----/** //**/
32548	updatePosition		: function () {
32549
32550		var $this	= this;
32551		
32552		// Not doing anything for non-inline mode and if not visible
32553		if ( $this.multiline )
32554			return;
32555
32556		// Few shortcuts
32557		var $input	= $this.dom.container;
32558		var $body	= $this.dom.body;
32559
32560		// Getting input coordinates and screen size
32561		var $pos	= $this.getPosition();
32562		var $screen	= screenSize();
32563
32564		// Calculating margi as % of screen height
32565		// Carrying only about height, since that's what is mostly positioned
32566		var $margin	= $screen.height * 0.05;
32567		
32568		// Getting real body height and calculating max allowed
32569		// For this - clearing height first
32570		$this.dom.list.css('max-height', '');
32571 
32572		var $height	= $body.height();
32573		var $limit	= $height; 
32574		var $max	= $screen.height - ($margin * 2);
32575
32576		// Calculating real height limit (should be set on list inside body)
32577		// Clearing if not necessary anymore (screen resize)
32578		if ( $height > $max ) {
32579			
32580			$limit	= $max;
32581			
32582			// Accounting for filter if present
32583			$this.dom.list.css('max-height', $limit - ($this.filter ? $this.dom.filter.outerHeight() : 0));
32584			
32585		} //IF too big 
32586		
32587		// Calculating new top based on real height and limits
32588		// Starting from middle of input
32589		var $top	= ($pos.top + ($this.dom.container.height() / 2)) - ($limit / 2);
32590
32591		// Correcting top: it should be lower than margin
32592		if ( $top < $margin )
32593			$top = $margin;
32594
32595		// But also can't be too low (top+height higher than margin)
32596		if ( $top + $limit > $screen.height - $margin )
32597			$top = $screen.height - $margin - $limit;
32598		
32599		// Adjusting body
32600		// Slightly adjusting sizes, to do not mess with borders recalculations
32601		// ToDo: read borders from skin			
32602		$this.dom.body.css({
32603			width		: $this.dom.container.width() - 2,
32604			left		: $pos.left + 1,
32605			top		: $top, //$pos.top + ($this.dom.container.height() / 2),
32606		}); //$css
32607		
32608	}, //FUNC updatePosition
32609	
32610	/** //** ----= update	=--------------------------------------------------------------------------------------\**//** \
32611	*
32612	*	Updates item states based on current selector state. Uses filtering if available.
32613	*	Additionally updats filter states, and face if available.
32614	*
32615	*	@return SELF
32616	*
32617	\**//** -------------------------------------------------------------------= by Mr.V!T @ Morad Media Inc. =----/** //**/
32618	update		: function (  ) {
32619
32620		// Few shortucts
32621		var $this	= this;
32622		
32623		// Reading/updating filter
32624		var $filter	= '';
32625		
32626		if ( $this.filter ) {
32627
32628			$filter	= $this.dom.iSearch.val();
32629
32630			// Showing/hiding clear button
32631			if ( $filter )
32632				$this.dom.btnClear.show();
32633			else
32634				$this.dom.btnClear.hide();
32635			
32636		} //IF filter used
32637
32638		// Updating face when applicable
32639		// For this - collecting selected values, and concatenating them into lavel afterwards
32640		var $val	= [];
32641				
32642		// Just looping through items list and setting selected class
32643		$this.dom.items.each( function () {
32644
32645			var $item	= jQuery(this);
32646			var $option	= $item.data('option');
32647
32648			// Checking if matching filter, or just showing them all
32649			if ( $filter ) {
32650				
32651				// Using regex seaerch for case insensitivity
32652				// ToDo: escape filter value
32653				if ( $option.html().search( new RegExp($filter, 'i') ) == -1 )
32654					$item.hide();
32655				else
32656					$item.show();
32657				
32658			} //If filter applied
32659			else {
32660
32661				// Just showing, this makes sure we display all items if filter was just cleared
32662				$item.show();
32663
32664			} //IF filter not applied or just cleared
32665		
32666			// Marking item as selected
32667			if ( $option.prop('selected') )	
32668				$item.addClass('selected');
32669			else
32670				$item.removeClass('selected');
32671
32672			// Collecting selected values for face update
32673			if ( $option.prop('selected') )	{
32674
32675				$val.push( $option.text().trim() );
32676
32677			} //IF found selected
32678		/*/ 			
32679			// Additionally - setting selected atribute on option, for whoever needs this
32680			// But this is supported only on multiselects
32681			// This appears to be quite slow for obvious reasons, so skipping it for now as noone really needs it :) 
32682			if ( !$this.dom.input.is('[multiple]')  )
32683				return;
32684
32685			if ( $option.prop('selected') )	
32686				$option.attr('selected', 'selected');
32687			else
32688				$option.removeAttr('selected', '');
32689		/**/
32690		}); //FOR each item
32691
32692		// If nothing found - using first option to display something (that's what would be submitted in the end)
32693		// Only for non multiline selectors though
32694		if ( !$this.multiselect && isEmpty($val) && $this.dom.items.length ) {
32695			$val.push( $this.dom.items.first().data('option').text().trim() );
32696		} //IF no values
32697	
32698		// Now can apply value
32699		if ( !$this.multiline )
32700			$this.dom.iValue.val( $val.join(', ') || '- Please select one or multiple -' );
32701			
32702	}, //FUNC update
32703
32704}).init()} //CONSTRUCTOR mwCodeInput
32705
32706/** //** ----= updateLandingCookie	=--------------------------------------------------------------------------------------\**//** \
32707*
32708*	Basic landing cookie manager. 
32709*
32710*	@param		string	[$page]	- Source page to redirect from. By default current page is used.
32711*	@param		MIXED	$url	- Target url. If link element specified - will use href attribute. 
32712*					  If set to FALSE redirect will be deleted.
32713*
32714\**//** ---------------------------------------------------------------------------= by Mr.V!T @ Morad Media Inc. =----/** //**/
32715function updateLandingCookie ($page, $url) {
32716
32717	// Preparing target
32718	$page	= !isEmpty($page) ? $page : location.pathname;
32719
32720	// Reading url
32721	$url	= $url.pathname ? $url.pathname : $url;
32722
32723	// Reading current cookie, to update/add new redirect
32724	var $c = getCookie('mwLandingUrls', {});
32725	
32726	if ( !isObject($c) )
32727		$c = JSON.parse($c);
32728
32729	// Adding/updating/removing redirect
32730	if ( $url )
32731		$c[$page] = $url;
32732	else
32733		delete($c[$page]);
32734
32735	// Saving cookie
32736	$c = JSON.stringify($c);
32737	setCookie('mwLandingUrls', $c);
32738
32739} //FUNC updateLandingCookie
32740
32741/** //** ----= setLandingUrl	=--------------------------------------------------------------------------------------\**//** \
32742*
32743*	Stores landing cookie for specified page.
32744*
32745*	@param		MIXED	$url	- Target url. If link element specified - will use href attribute.
32746*	@param		string	[$page]	- Source page to redirect from. By default current page is used.
32747*
32748\**//** ---------------------------------------------------------------------------= by Mr.V!T @ Morad Media Inc. =----/** //**/
32749function setLandingUrl ($url, $page) {
32750	updateLandingCookie($page, $url);	
32751} //FUNC setLandingUrl
32752
32753/** //** ----= clearLandingUrl	=--------------------------------------------------------------------------------------\**//** \
32754*
32755*	Clears landing cookie for specified page.
32756*
32757*	@param		string	$page	- Source page to clear redirect for. If link element specified - will use href attribute.
32758*
32759\**//** ---------------------------------------------------------------------------= by Mr.V!T @ Morad Media Inc. =----/** //**/
32760function clearLandingUrl ($page) {
32761
32762	// Reading target
32763	$page =  $page.pathname ? $page.pathname : $page;
32764	
32765	if ( !$page )
32766		return;
32767	
32768	updateLandingCookie($page, false);		
32769	
32770} //FUNC clearLandingUrl
32771/** //** ----= CLASS mwImgEd	=--------------------------------------------------------------------------------------\**//** \
32772*
32773* 	Morweb express image editor. Used to fast edit images inside <img>.
32774*
32775* 	@package	MorwebManager
32776* 	@subpackage	Core
32777* 	@category	editor
32778*
32779*	@param		jQuery	$img	- Image to edit
32780*	@param		object	[$o]	- Initial options.
32781*
32782*	@todo		Implement disabled moving and/or resizing.		
32783*
32784\**//** ---------------------------------------------------------------------------= by Mr.V!T @ Morad Media Inc. =----/** //**/
32785function mwImgEd ($img, $options) {
32786
32787	if ( !$img )
32788		throw( 'Image unspecified' );
32789	
32790	$img	= _jq($img);
32791	
32792	// Checking if editor was cached in element
32793	// Not using currently to do not bother with unbinds
32794//	var $class = $img.data('mwImgEd') || {
32795	var $class = {
32796
32797// ==== PROPERTIES =============================================================================================================
32798
32799	url		: '',			// Source image url
32800	query		: {},			// Stores parsed query of image
32801	queryStr	: '',			// Stores string version of size query
32802	view		: '',			// Resized image url, used to do not load full image for editing (slightly bigger than current viewport)
32803	viewFactor	: 2,			// View image size, comparing to current viewport
32804	viewLimit	: 1920,			// Mximum size of fiew in result
32805
32806	aspectRatio	: 0,			// Stores original aspect ratio.
32807	resizeOn	: 'xy',			// Allowed resize axis 
32808
32809	angle		: 0,			// Current rotation angle
32810
32811	zoomMin		: 1,			// Minimal zoom allowed during edit session		
32812	zoomValue	: 1,			// Current zoom against original image		
32813	zoomMax		: 4,			// Maximum zoom allowed during edit session		
32814	zoomStep	: 0.1,			// Zoom size change step
32815	zoomButtonStep	: 0.05,			// Zoom size change step using buttons
32816
32817	mouseRepeatTime	: 10,			// Mouse action repeat
32818	mouseTimer	: false,		// Stores mouseDown timer
32819
32820	maxWidth	: 0,			// |
32821	maxHeight	: 0,			// |- Maximum allowed dimensions for image 
32822	minWidth	: 125,			// |
32823	minHeight	: 50,			// |- Minimum allowed dimensions for image 
32824	zIndex		: 5000,			// Z-Index to use for editor positioning (affects overlay too)
32825	modal		: true,			// Set FALSE to do not use overlay and "click-out"
32826	overlay		: true,			// Set FALSE to create hidden overlay, but still have "click-out"
32827	forcePosition	: false,		// Set TRUE to force position style on parent element. Used to make sure it's not changed outside
32828	updateOnly	: false,		// Set TRUE to init only math for update
32829
32830	history		: [],			// Stores changes history, for undo operations					
32831	historyLock	: false,		// Indicated history save lock					
32832	historyDelay	: 500,			// Delay between history steps, to do not save too often operations
32833	historyTimer	: false,		// History saving timer
32834	isUndo		: false,		// Undo flag to prevent history updates	
32835
32836	x		: {			// Current math values for X axis
32837		originSize	: 0,			// Original image size (used to indicate relative change)
32838
32839		imageSize	: 0,			// Image size
32840		imagePos	: 0,			// Image position relative to viewport (for crop)
32841		imageOff	: 0,			// Image offset
32842
32843		wrapSize	: 0,			// Wrapper size (viewport)
32844		wrapPos		: 0,			// Wrapper position
32845		wrapOff		: 0,			// Wrapper offset
32846		 
32847		pageMouse	: 0,			// Cursor position relative to document
32848		wrapMouse	: 0,			// Cursor position relative to wrapper
32849		imageMouse	: 0,			// Cursor position relative to image
32850		
32851		flip		: false,		// Axis flip marker
32852	}, //x		
32853	y		: {},			// Current math values for Y axis (same structure as is, will be set in .init() )
32854	
32855	dom		: {			// Set of valuable elements as jQuery shortcuts
32856		parent		: false,		// Image parent box
32857		source		: $img,			// Initial image being edited
32858		wrap		: false,		// Viewport div, main editor wrapper
32859		image		: false,		// Viewed image, used to show changed
32860		overlay		: false,		// Editor overlay
32861		
32862		tools		: false,		// Main tools bar
32863	
32864		dummy		: false,		// Tools dummy used to trick tolls position around slider (slider on center when visible)
32865		slider		: false,		// Slider element
32866		
32867		zoomIn		: false,		// |
32868		zoomOut		: false,		// |- Zoom controls
32869		
32870		rotateL		: false,		// | 
32871		rotateR		: false,		// |- Rotate controls
32872		
32873		flipH		: false,		// | 
32874		flipV		: false,		// |- Flip controls
32875
32876		undo		: false,		// Undo control 
32877		reset		: false,		// Reset control
32878
32879		debug		: false,		// Debug tools
32880	}, //dom
32881	
32882	origin		: {			// Stores changes made by editor, to be able to revert those
32883		url		: '',			// Original url
32884		width		: 0,			// |		
32885		height		: 0, 			// |- Original dimensions
32886	}, // origin	
32887
32888	responsive	: {				// Describes set of responsive classes to be applied on resizing. No rotation set
32889		'no-dummy'	: 600,
32890		'no-reset'	: 500,
32891		'no-slider'	: 450,
32892		'no-flip'	: 150,
32893		'no-zoom'	: 125,
32894	}, //responsive
32895
32896	responsiveY	: 65,				// Minimal height untill tools are hidden
32897
32898	debug		: false,			// Set TRUE to enable realtime debug console
32899	con		: false,			// Will store console object
32900
32901	loaderState	: false,			// Indicates loading state 
32902	loaderDelay	: 200,				// If loading process happens faster than this value - loader will not even show
32903	loaderTimer	: false,			// Used to prevent flicker (or what seems to look like flicker ;) ) 
32904
32905	resizable	: {},				// Initial resizable settings. If set to FALSE, will disable manual resizing
32906	draggable	: {},				// Initial draggable settings. If set to FALSE, will disable manual dragging
32907
32908// ==== INIT ===================================================================================================================
32909
32910	/** //** ----= set	=--------------------------------------------------------------------------------------\**//** \
32911	*
32912	*	Initiates self parameters from given options array.
32913	*
32914	*	@return SELF
32915	*
32916	\**//** -------------------------------------------------------------------= by Mr.V!T @ Morad Media Inc. =----/** //**/
32917	set		: function ($o) {
32918		
32919		// ToDo: .set() in basic class
32920		// ToDo: proper events extension support
32921		$o = $o || {};
32922		jQuery.extend(this, $o);
32923		return this;
32924		
32925	}, //FUNC set	
32926
32927	/** //** ----= edit	=--------------------------------------------------------------------------------------\**//** \
32928	*
32929	*	Initiates editing of image.
32930	*
32931	*	@return SELF
32932	*
32933	\**//** -------------------------------------------------------------------= by Mr.V!T @ Morad Media Inc. =----/** //**/
32934	edit		: function () {
32935		
32936		var $this = this;
32937		
32938		// If failed to init - that might be caused by rare crome onLoad/render bug
32939		// Trying again bit later (giving crome time to deal with it's caches)
32940		if ( this.init() === false )
32941			setTimeout( function () {
32942				$this.init();				
32943			}, 100); //FUNC setTimeout
32944		
32945		return this;
32946		
32947	}, //FUNC edit	
32948
32949	/** //** ----= save	=--------------------------------------------------------------------------------------\**//** \
32950	*
32951	*	Stops editing and saves changes.
32952	*
32953	*	@return SELF
32954	*
32955	\**//** -------------------------------------------------------------------= by Mr.V!T @ Morad Media Inc. =----/** //**/
32956	save		: function () {
32957
32958		var $this = this;
32959
32960		this.onBeforeSave();
32961
32962	// ---- URL ----
32963
32964		var $query = '';
32965
32966		// Compiling url, to load into image
32967		// Using width resize, to make sure aspect is preserved
32968		$query = 'resize=' + Math.round(this.x.imageSize);
32969		
32970		// Checking if crop needed
32971		if ( 
32972			(this.x.imageSize != this.x.wrapSize) 
32973			|| (this.y.imageSize != this.y.wrapSize) 
32974		) {
32975	
32976			$query += '&crop=' + Math.round(this.x.wrapSize) + 'x' + Math.round(this.y.wrapSize);
32977
32978			// Checking if crop moved (inverting values)
32979			// Need to spciefy 0x0 anyway, othewise crop will be just centered
32980		//	if ( this.x.imagePos != 0 || this.y.imagePos != 0 )
32981				$query += ',' + Math.round(this.x.imagePos)*-1 + 'x' + Math.round(this.y.imagePos)*-1;
32982
32983		} //IF crop used
32984
32985		var $flip = [];
32986		// Checking if flip used
32987		if ( this.x.flip )
32988			$flip.push('vrt');
32989			
32990		if ( this.y.flip )
32991			$flip.push('hor');
32992
32993		if ( $flip.length )
32994			$query += '&flip=' + $flip.join();
32995
32996		var $url = this.url + '?' + $query;
32997
32998		this.queryStr = $query;
32999
33000	// ---- IMAGE ----
33001
33002		// Loading updated image
33003		this.state(true);
33004
33005		// Saving origin dimensions (saving here, to allow dynamic updates from callee)
33006		this.origin.width	= this.dom.source.get(0).style.width;
33007		this.origin.height	= this.dom.source.get(0).style.height;
33008
33009		// Sticking source box dimensions to be same as wrapper
33010		this.dom.source.css({
33011			'width'		: this.x.wrapSize,
33012			'height'	: this.y.wrapSize,
33013		}); //source.css
33014
33015		// Resetting src, to be prepared for next load
33016		// Firefox needs this mostly, but it does not heart anyway
33017		$this.dom.source.attr('src', '');
33018		
33019		// Removing self only after resized image was loaded (for smoother transaction)
33020		// Setting load event before setting src (to make sure that even cache hit was captured)
33021		this.dom.source
33022			.imagesLoaded()
33023			.always( function () {
33024
33025				// Getting original image back visible
33026				// And returning original dimensions
33027				$this.dom.source.css({
33028					'width'		: $this.origin.width,
33029					'height'	: $this.origin.height,
33030					'visibility'	: '',
33031					
33032				});
33033	
33034				// Done loading
33035				$this.state(false);
33036	
33037				// Removing self
33038				$this.deinit();
33039
33040				// Calling event	
33041				$this.onAfterSave();
33042	
33043			}); //source.onLoad
33044
33045		this.dom.source.attr('src', $url);
33046
33047	// ---- TOOLS ----
33048
33049		// Partially deinitiating before load
33050		$this.deinitTools();
33051		$this.deinitMouse();
33052
33053	// ---- DONE ----
33054
33055		// Triggering event
33056		this.onSave();
33057
33058		return this;
33059				
33060	}, //FUNC save	
33061
33062	/** //** ----= cancel	=--------------------------------------------------------------------------------------\**//** \
33063	*
33064	*	Stops editing and reverts changes.
33065	*
33066	*	@return SELF
33067	*
33068	\**//** -------------------------------------------------------------------= by Mr.V!T @ Morad Media Inc. =----/** //**/
33069	cancel		: function () {
33070
33071		this.onCancel();
33072
33073		return this;		
33074	}, //FUNC cancel	
33075
33076	/** //** ----= init	=--------------------------------------------------------------------------------------\**//** \
33077	*
33078	*	Initiates self properties. Adds editor elements.
33079	*
33080	\**//** -------------------------------------------------------------------= by Mr.V!T @ Morad Media Inc. =----/** //**/
33081	init		: function () {
33082		
33083		var $this = this;
33084		
33085		// X and Y axis data have same initial structure
33086		this.y = jQuery.extend({}, this.x);
33087	
33088	// ---- DOM ---
33089		
33090		// Initiating main elements
33091		if ( this.initEssentials() === false )
33092			return false;
33093		
33094		// Attaching editor into document
33095		this.dom.wrap.insertAfter(this.dom.source);
33096
33097		// Optionally adding overlay and disabling events around
33098		// Update mode - forces non-modal init
33099		
33100		if ( this.updateOnly )
33101			this.modal = false;
33102		
33103		if ( this.modal ) {
33104			
33105			jQuery('BODY').addClass('scroll-off');
33106			this.initOverlay();
33107			
33108		} //IF modal		
33109		
33110		// Debug is always initiated, but not always enabled
33111		if ( !this.updateOnly )
33112			this.initDebug();
33113
33114	// ---- Tools ----
33115
33116		// Tools and buttons are initiated only after image was really loaded.
33117		this.dom.image.on('load', function () {
33118			
33119			// Once image is loaded - can get real ratio now
33120			// Using naturalWidth/Height to ensure correct ratio is returned
33121			// Ofc using those only of available, else falling back to css width/height
33122			// Fallback is safe as height is not forced, thus ratio is normally kept
33123			var $img	= $this.dom.image.get(0);
33124			
33125			// Getting natural dims
33126			var $nWidth	= $img.naturalWidth; 
33127			var $nHeight	= $img.naturalHeight;
33128			
33129			// Faling back to css ones
33130			if ( !$nWidth || !$nHeight ) {
33131				$nWidth = $this.dom.image.width();
33132				$nHeight = $this.dom.image.height();
33133			} //IF no natural dims
33134
33135			// If got some - can recalc ratio
33136			if ( $nWidth && $nHeight )			
33137				$this.aspectRatio = $nWidth / $nHeight;
33138			
33139			// Tools and button controls
33140			if ( !$this.updateOnly ) {
33141
33142				$this.initTools();
33143				$this.initMouse();
33144				
33145			} //IF normal edit
33146
33147			// Updating math and main css
33148			$this.updatePositionsCss();
33149
33150			// After all init - can update UI, to be extra safe
33151			// This will update positions and sizes of visual controls
33152			if ( !$this.updateOnly ) {
33153				
33154				$this.updateUI();
33155				
33156			} //IF normal edit
33157
33158			$this.onEdit();
33159
33160		}); //image.onLoad
33161
33162		// Updating transition css right away, to avoid blink
33163		$this.updatePositionsCss();
33164		$this.updateFlipRotateCss();
33165		
33166	}, //FUNC init	
33167
33168	/** //** ----= deinit	=--------------------------------------------------------------------------------------\**//** \
33169	*
33170	*	Removes self element from pages, and finalizes editing.
33171	*
33172	\**//** -------------------------------------------------------------------= by Mr.V!T @ Morad Media Inc. =----/** //**/
33173	deinit		: function () {
33174
33175		// Removing main elements
33176		if ( this.modal ) 
33177			this.dom.overlay.remove();
33178		
33179		this.dom.wrap.remove();
33180
33181		// Restoring events handling
33182		jQuery('BODY').removeClass('scroll-off');
33183
33184		// Can safely return position now
33185		if ( this.forcePosition ) {
33186			this.dom.source.parent().css('position', '');
33187			this.dom.source.parent().css('display', '');
33188		} //IF positioning corrected
33189			
33190		this.deinitDebug();
33191	}, //FUNC deinit
33192
33193	/** //** ----= initEssentials	=------------------------------------------------------------------------------\**//** \
33194	*
33195	*	Initiates main DOM elements.
33196	*
33197	\**//** -------------------------------------------------------------------= by Mr.V!T @ Morad Media Inc. =----/** //**/
33198	initEssentials	: function () {
33199		
33200		var $src	= this.dom.source;
33201		var $this	= this;
33202
33203	// ---- SCALE ----
33204	
33205		// Image might be scaled, so have to correct all values by that scale
33206		// Scale can be determined on crop (that's actually when it matters)
33207		var $scale = 1;
33208		
33209		// Calculating scale by comparing original image real dimensions and wrap size
33210		var $img	= $src.get(0);
33211		var $nWidth	= $img.naturalWidth;
33212		var $sWidth	= $src.width();
33213	
33214		if ( $nWidth ) {
33215			$scale = $nWidth / $sWidth;
33216		} //IF natural size given
33217
33218	// ---- POSITION ----
33219
33220		// Forcing position to make sure that it will not change (with events?)
33221		if ( this.forcePosition ) {
33222
33223			var $parent = $src.parent();
33224
33225			if ( $parent.css('position') == 'static' ) 
33226				$parent.css('position', 'relative');
33227
33228			if ( $parent.css('display') == 'inline' ) 
33229				$parent.css('display', 'block');
33230			
33231		} //IF position udpate
33232		
33233	// ---- AXIS ----
33234		
33235		// Getting initial dimensions/positions
33236		var $p		= this.realPosition($src, true);
33237		var $o		= $src.offset();
33238		
33239		// Most of sizes are same initially, but are corrected on url parsing
33240		
33241		// X
33242		this.x.originSize	= $src.width();
33243
33244		this.x.wrapSize		= this.x.originSize;
33245		this.x.wrapPos		= $p.left;
33246		this.x.wrapOff		= $o.left;
33247		
33248		this.x.imageSize	= this.x.originSize;
33249		this.x.imagePos		= 0;
33250		this.x.imageOff		= $o.left;
33251		
33252		//Y
33253		this.y.originSize	= $src.height();
33254		
33255		this.y.wrapSize		= this.y.originSize;
33256		this.y.wrapPos		= $p.top;
33257		this.y.wrapOff		= $o.top;
33258		
33259		this.y.imageSize	= this.y.originSize;
33260		this.y.imagePos		= 0;
33261		this.y.imageOff		= $o.top;
33262
33263		// In some rare cases chrome triggers onLoad faster then img loads
33264		// Attempting to detect this
33265		if ( this.y.originSize == 0 )
33266			return false;
33267
33268		// Calculating aspect ratio for dimensions corrections
33269		// Optinally, cuz this might be provided by caller already
33270		if ( !this.aspectRatio )
33271			this.aspectRatio = this.x.originSize / this.y.originSize;
33272
33273	// ---- URL ----	
33274		
33275		// Parsing source image url and composing view url
33276		// View should be slighly bigger than current viewport
33277		// Resizing by width
33278
33279		var $srcUrl	= this.origin.url = this.url || $src.attr('src');
33280		
33281		this.url	= $srcUrl;
33282		this.query	= {};
33283		this.queryStr	= '';
33284		this.view	= $srcUrl;
33285
33286		// Splitting query from url (if it's present)
33287		var $qpos	= $srcUrl.indexOf('?');
33288		if ( $qpos > -1 ) {
33289			
33290			// Correcting starting values, reading them from url
33291			this.url	= $srcUrl.substring(0, $qpos); 
33292			this.queryStr	= $srcUrl.substring($qpos+1)
33293			var $query	= this.queryStr;
33294			
33295			parse_str($query, this.query);
33296
33297		// ---- CROP ----	
33298	
33299			if ( this.query['crop'] ) {
33300				var $tmp	= this.query['crop'].split(',');
33301				var $size	= getDimensions($tmp[0]);
33302				var $from	= isSet($tmp[1]) ? getDimensions($tmp[1], true) : false;
33303				
33304				// Proceeding only if correct size given
33305				if ( $size ) {
33306
33307					// Not really necessary, but doing to be consistant
33308					this.x.wrapSize = $size['x'] / $scale;
33309					this.y.wrapSize = $size['y'] / $scale;
33310					
33311					// And position if set
33312					if ( $from ) {
33313						// Inverting values as it's viewport
33314						this.x.imagePos = $from['x'] * -1 / $scale;
33315						this.y.imagePos = $from['y'] * -1 / $scale;
33316					} //IF position set
33317					
33318				} //If size given
33319			
33320			} //IF crop set
33321
33322		// ---- RESIZE ----
33323
33324			if ( this.query['resize'] ) {
33325				
33326				var $size	= getDimensions(this.query['resize']);
33327				
33328				// If shorthand resize given - calculating second side by side given
33329				// Preffering width, assuming at least one is given (otherwise it's failed image anyway)
33330				if ( !isEmpty($size['x']) ) {
33331					this.x.imageSize = $size['x'] / $scale;
33332					this.y.imageSize = Math.round($size['x'] / this.aspectRatio / $scale);
33333				} //IF width given
33334				else {
33335					this.y.imageSize = $size['y'] / $scale;
33336					this.x.imageSize = Math.round($size['y'] * this.aspectRatio / $scale);
33337				} //IF height given
33338				
33339			} //IF resize set
33340
33341		// ---- FLIP ----	
33342			
33343			if ( this.query['flip'] ) {
33344
33345				// Fast checking for keywords. Image with invalid params is impossible to edit anyway (it will just not load)
33346				if ( this.query['flip'].indexOf('vrt') != -1 )
33347					this.x.flip = true;				
33348
33349				if ( this.query['flip'].indexOf('hor') != -1 )
33350					this.y.flip = true;				
33351				
33352			} //IF flip is set
33353
33354		// ---- ROTATE ----	
33355			
33356			// ToDo: complete rotate support
33357				
33358	
33359		} //IF ? found
33360		
33361		var $viewX	= Math.floor(this.x.imageSize * this.viewFactor);
33362		if ( $viewX > this.viewLimit )
33363			$viewX = this.viewLimit;
33364		
33365		this.view	= this.url + '?' + $viewX;
33366
33367	// ---- WRAP ----	
33368
33369		this.dom.parent	= $src.parent();
33370
33371		// Creating main container, will be attached last after all init is complete
33372		// By default editor it's in loading state, which will be cleared once view loaded
33373		this.dom.wrap	= jQuery('<div class="mwImgEd"></div>')
33374			.css({
33375				'position'	: 'absolute',
33376	
33377				'left'		: this.x.wrapPos,
33378				'top'		: this.y.wrapPos,
33379	
33380				'width'		: this.x.wrapSize,
33381				'height'	: this.y.wrapSize,
33382				
33383				'overflow'	: 'hidden',
33384				'z-index'	: this.zIndex + 1, 
33385			}) //css
33386		; //jQuery.wrap
33387
33388		this.dom.wrap.disableSelection();
33389
33390	// ---- VIEW ----	
33391
33392		// Loading view might take time
33393		this.state(true);
33394
33395		// Creating view image, setting initial params, to make sure it have correct box while loading
33396		// Creating invisible image initially, and setting visibility only after load
33397		// This allows to avoid initial blinking when swapping images and applying correcting css
33398		this.dom.image	= jQuery('<img src="' + this.view + '" />')
33399			.css({
33400				'position'	: 'absolute',
33401				'border'	: 'none',
33402				'visibility'	: 'hidden',
33403				
33404				'left'		: this.x.imagePos,
33405				'top'		: this.y.imagePos,
33406
33407				'width'		: this.x.imageSize,
33408				'height'	: this.y.imageSize,
33409
33410				'cursor'	: 'move',
33411			}) //css
33412			.appendTo(this.dom.wrap)
33413		; //jQuery.image
33414
33415		// Completing image initiation once loaded
33416	//	this.dom.image.on('load', function () {
33417		imagesLoaded( this.dom.image, function () {
33418			
33419			// Hiding original image, after view loads, giving small pause, to do not blink
33420			setTimeout( function () {
33421				
33422				// And hiding to avoid artifacts,
33423				if ( !$this.debug && !$this.updateOnly )
33424					$this.dom.source.css('visibility', 'hidden');
33425			}, 1);
33426			
33427			// Showing view image
33428			$this.dom.image.css('visibility', 'visible');
33429			
33430			// Done loading
33431			$this.state(false);
33432			
33433		}); //image.onLoad
33434
33435	}, //FUNC initEssentials	
33436
33437	/** //** ----= initOverlay	=------------------------------------------------------------------------------\**//** \
33438	*
33439	*	Initiates overlay element
33440	*
33441	\**//** -------------------------------------------------------------------= by Mr.V!T @ Morad Media Inc. =----/** //**/
33442	initOverlay	: function () {
33443		
33444		var $this = this;
33445
33446		// Preventing parent clicks during editing
33447		this.dom.wrap.click( function ($e) { 
33448			$e.stopPropagation(); 
33449			return false; 
33450		}); //FUNC onClick
33451		
33452		this.dom.overlay = jQuery('<div class="mwImgEdOverlay ' + (this.overlay ? '' : 'invisible') + '"></div>')
33453			.css({
33454				'z-index'	: this.zIndex, 
33455			}) //css
33456			.insertAfter(this.dom.wrap)
33457			.on('click', function ($e) {
33458				
33459				// On overlay click - saving changes
33460				$this.save();
33461				
33462				$e.stopPropagation(); 
33463				return false; 
33464			}) //FUNC onClick
33465		; //jQuery.image
33466		
33467	}, //FUNC initOverlay	
33468
33469// ==== DEBUG ==================================================================================================================
33470
33471	/** //** ----= initDebug	=------------------------------------------------------------------------------\**//** \
33472	*
33473	*	Initiates debug elements.
33474	*
33475	\**//** -------------------------------------------------------------------= by Mr.V!T @ Morad Media Inc. =----/** //**/
33476	initDebug	: function () {
33477
33478		var $this = this;
33479
33480		// Console always exists to do not cause errors
33481		this.con = vConsole({
33482			'enabled'	: this.debug && isDebug(),
33483			'width'		: 500,
33484		//	'autoupdate'	: true,
33485			'panels'	: {
33486				'main'		: [100, this, ['view', 'aspectRatio', 'angle', 'zoomValue', 'maxWidth']],
33487				'x'		: [50, this.x],
33488				'y'		: [50, this.y],
33489			}
33490		}); //con
33491
33492		// Debug should be possible
33493		if ( !isDebug() )
33494			return;
33495		
33496		// Creating debug button
33497		this.dom.debug = jQuery('<div class="button debug"></div>')
33498			.css({
33499				'position'	: 'absolute',
33500				'left'		: 10,
33501				'top'		: 10,
33502			}) //css
33503			.appendTo(this.dom.wrap)
33504			.on('click', function () {
33505				$this.debug = !$this.debug 
33506				$this.con.toggle();
33507			}) //FUNC onClick
33508		; //jQuery.image
33509
33510	}, //FUNC initDebug	
33511
33512	/** //** ----= deinitDebug	=------------------------------------------------------------------------------\**//** \
33513	*
33514	*	Deinitiates debug console.
33515	*
33516	\**//** -------------------------------------------------------------------= by Mr.V!T @ Morad Media Inc. =----/** //**/
33517	deinitDebug	: function () {
33518		
33519		// Removing button
33520		if ( this.dom.debug )
33521			this.dom.debug.remove();
33522		
33523		// And console
33524		if ( this.con )
33525			this.con.deinit();	
33526		
33527	}, //FUNC deinitDebug
33528
33529// ---- TOOLS ------------------------------------------------------------------------------------------------------------------
33530
33531	/** //** ----= initTools	=------------------------------------------------------------------------------\**//** \
33532	*
33533	*	Initiates visual tools and button controls.
33534	*
33535	\**//** -------------------------------------------------------------------= by Mr.V!T @ Morad Media Inc. =----/** //**/
33536	initTools	: function () {
33537
33538		var $this = this;
33539		
33540		// Creating main bar
33541		this.dom.tools = jQuery('<div class="main tools"></div>');
33542		
33543		// Creating tools
33544		this.initSlider();
33545		this.initRotate();
33546		this.initFlip();
33547		this.initReset();
33548		
33549		// Attaching toolbar into editor
33550		this.dom.tools.appendTo(this.dom.wrap);
33551		
33552		// Global mouse repeat cancel on mouseUP
33553		// This enables simple way to create mouseRepeat events
33554		// ToDo: Unbind on destroy
33555		jQuery(document).on('mouseup', function () {
33556			
33557			// Doing nothing if not interval (to do not interferee with other events)
33558			if ( !$this.mouseTimer ) return;
33559			
33560			// Clearing interval, to stop action
33561			clearInterval($this.mouseTimer);
33562			
33563			// Canceling current event
33564			return false;
33565		}); //FUNC onMouseup
33566		
33567	}, //FUNC initTools	
33568
33569	/** //** ----= deinitTools	=------------------------------------------------------------------------------\**//** \
33570	*
33571	*	Removes mouse control.
33572	*
33573	\**//** -------------------------------------------------------------------= by Mr.V!T @ Morad Media Inc. =----/** //**/
33574	deinitTools	: function () {
33575
33576		// Removing tools bar
33577		if ( this.dom.tools )
33578			this.dom.tools.remove();		
33579		
33580	}, //FUNC deinitTools
33581
33582	/** //** ----= initSlider	=------------------------------------------------------------------------------\**//** \
33583	*
33584	*	Initiates zoom slider and it's buttons. Also adds centering dummy.
33585	*
33586	\**//** -------------------------------------------------------------------= by Mr.V!T @ Morad Media Inc. =----/** //**/
33587	initSlider	: function () {
33588
33589		var $this = this;
33590
33591	// ---- DOM ----
33592
33593		this.dom.dummy		= jQuery('<div class="dummy"></div>').appendTo(this.dom.tools);
33594		this.dom.slider		= jQuery('<div class="slider"></div>').appendTo(this.dom.tools);
33595
33596		this.dom.zoomOut	= jQuery('<div class="button zoom out"></div>').insertBefore(this.dom.slider);
33597		this.dom.zoomIn		= jQuery('<div class="button zoom in"></div>').insertAfter(this.dom.slider);
33598
33599	// ---- CONTROLS: slider ----
33600
33601		this.dom.slider.slider({
33602			min	: $this.zoomMin,
33603			max	: $this.zoomMax,
33604			step	: 0.01,
33605			value	: $this.zoomValue,
33606			slide	: function ($e, $ui) {
33607				$this.zoom($ui.value, 'wrap');
33608			} //FUNC slide
33609		}); //jQuery.slider		
33610
33611	// ---- CONTROLS: buttons ----
33612
33613		this.dom.zoomIn.on('mousedown', function () {
33614
33615			// Enabling mouse repeating action
33616			$this.mouseTimer = setInterval( function () {
33617				$this.zoom($this.zoomValue + $this.zoomButtonStep, 'wrap');
33618			}, $this.mouseRepeatTime); //FUNC mouseTimer.callback
33619
33620		}); //FUNC zoomIn.onClick
33621
33622		this.dom.zoomOut.on('mousedown', function () {
33623	
33624			// Enabling mouse repeating action
33625			$this.mouseTimer = setInterval( function () {
33626				$this.zoom($this.zoomValue - $this.zoomButtonStep, 'wrap');
33627			}, $this.mouseRepeatTime); //FUNC mouseTimer.callback
33628	
33629		}); //FUNC zoomOut.onClick
33630
33631	}, //FUNC initSlider	
33632
33633	/** //** ----= initRotate	=------------------------------------------------------------------------------\**//** \
33634	*
33635	*	Initiates rotate controls.
33636	*
33637	\**//** -------------------------------------------------------------------= by Mr.V!T @ Morad Media Inc. =----/** //**/
33638	initRotate	: function () {
33639
33640		// Not using currently, cuz buggy
33641		return;
33642
33643		var $this = this;
33644
33645	// ---- DOM ----
33646
33647		// Adding visual divider
33648		jQuery('<div class="rotate divider"></div>').appendTo(this.dom.tools);
33649		
33650		this.dom.rotateL	= jQuery('<div class="button rotate left"></div>').appendTo(this.dom.tools);
33651		this.dom.rotateR	= jQuery('<div class="button rotate right"></div>').appendTo(this.dom.tools);
33652
33653	// ---- CONTROLS ----
33654
33655		this.dom.rotateL.on('click', function () {
33656			$this.rotate('l');
33657		}); //FUNC rotateL.onClick	
33658
33659		this.dom.rotateR.on('click', function () {
33660			$this.rotate('r');
33661		}); //FUNC rotateR.onClick	
33662
33663
33664	}, //FUNC initRotate	
33665
33666	/** //** ----= initReset	=------------------------------------------------------------------------------\**//** \
33667	*
33668	*	Initiates reset and undo controls.
33669	*
33670	\**//** -------------------------------------------------------------------= by Mr.V!T @ Morad Media Inc. =----/** //**/
33671	initReset	: function () {
33672
33673		var $this = this;
33674
33675	// ---- DOM ----
33676
33677		// Adding visual divider
33678		jQuery('<div class="rst divider"></div>').appendTo(this.dom.tools);
33679		
33680		this.dom.undo	= jQuery('<div class="button rst undo"></div>').appendTo(this.dom.tools);
33681		this.dom.reset	= jQuery('<div class="button rst reset"></div>').appendTo(this.dom.tools);
33682
33683	// ---- CONTROLS ----
33684
33685		this.dom.undo.on('click', function () {
33686			$this.undo();
33687		}); //FUNC rotateL.onClick	
33688
33689		this.dom.reset.on('click', function () {
33690			$this.reset();
33691		}); //FUNC rotateR.onClick	
33692
33693
33694	}, //FUNC initReset	
33695
33696	/** //** ----= initFlip	=--------------------------------------------------------------------------------------\**//** \
33697	*
33698	*	Initiates flip controls.
33699	*
33700	\**//** -------------------------------------------------------------------= by Mr.V!T @ Morad Media Inc. =----/** //**/
33701	initFlip	: function () {
33702
33703		var $this = this;
33704
33705	// ---- DOM ----
33706
33707		jQuery('<div class="flip divider"></div>').appendTo(this.dom.tools);
33708
33709		this.dom.flipH	= jQuery('<div class="button flip hor"></div>').appendTo(this.dom.tools);
33710		this.dom.flipV	= jQuery('<div class="button flip ver"></div>').appendTo(this.dom.tools);
33711
33712	// ---- CONTROLS ----
33713
33714		this.dom.flipH.on('click', function () {
33715			$this.flip('y');
33716		}); //FUNC flipH.onClick	
33717
33718		this.dom.flipV.on('click', function () {
33719			$this.flip('x');
33720		}); //FUNC flipV.onClick	
33721
33722	}, //FUNC initFlip	
33723
33724	/** //** ----= updateUI	=--------------------------------------------------------------------------------------\**//** \
33725	*
33726	*	Updates UI elements visibility and positions.
33727	*
33728	\**//** -------------------------------------------------------------------= by Mr.V!T @ Morad Media Inc. =----/** //**/
33729	updateUI	: function () {
33730		
33731		// Checking viewport size agains set of responsive classes, 
33732		// applying onces that needed, removing ones that are not
33733		
33734		// Checking if height is too small, and disabling visual tools completely in this case
33735		if ( this.y.wrapSize <= this.responsiveY )
33736			this.dom.wrap.addClass('no-tools');
33737		else
33738			this.dom.wrap.removeClass('no-tools');
33739		
33740		for ( var $i in this.responsive ) {
33741			var $r = this.responsive[$i];
33742			
33743			// Adding if less than border value, removing othewise
33744			if ( this.x.wrapSize <= $r )
33745				this.dom.wrap.addClass($i);
33746			else
33747				this.dom.wrap.removeClass($i);
33748			
33749		} //FOR each responsive class
33750		
33751	}, //FUNC updateUI	
33752
33753// ---- MOUSE ------------------------------------------------------------------------------------------------------------------
33754
33755	/** //** ----= initMouse	=------------------------------------------------------------------------------\**//** \
33756	*
33757	*	Initiates mouse controls, like drag/zoom/resize
33758	*
33759	\**//** -------------------------------------------------------------------= by Mr.V!T @ Morad Media Inc. =----/** //**/
33760	initMouse	: function () {
33761		
33762		var $this = this;
33763
33764	// ---- CURSOR ----
33765		
33766		// Tracking mouse cursor
33767		this.dom.wrap.on('mousemove', function ($e) {
33768			
33769			$this.x.pageMouse	= $e.pageX;
33770			$this.x.wrapMouse	= $e.pageX - $this.x.wrapOff;
33771			$this.x.imageMouse	= $e.pageX - $this.x.imageOff;
33772
33773			$this.y.pageMouse	= $e.pageY;
33774			$this.y.wrapMouse	= $e.pageY - $this.y.wrapOff;
33775			$this.y.imageMouse	= $e.pageY - $this.y.imageOff;
33776
33777			if ( $this.con )
33778				$this.con.update();
33779		
33780		}); //wrap.onMousemove
33781		
33782	// ---- CONTROLS ----	
33783		
33784		this.initResize();
33785		this.initZoom();
33786		this.initDrag();
33787
33788	}, //FUNC initMouse	
33789
33790	/** //** ----= deinitMouse	=------------------------------------------------------------------------------\**//** \
33791	*
33792	*	Removes mouse control.
33793	*
33794	\**//** -------------------------------------------------------------------= by Mr.V!T @ Morad Media Inc. =----/** //**/
33795	deinitMouse	: function () {
33796		
33797		// Stop zoom
33798		this.dom.wrap.off('mousewheel');
33799		
33800		// Stop drag
33801		this.dom.image.draggable('destroy');		
33802
33803		// Stop resize
33804		this.dom.wrap.resizable('destroy');		
33805		
33806	}, //FUNC deinitMouse
33807
33808	/** //** ----= initZoom	=--------------------------------------------------------------------------------------\**//** \
33809	*
33810	*	Initiates mouse zoom.
33811	*
33812	\**//** -------------------------------------------------------------------= by Mr.V!T @ Morad Media Inc. =----/** //**/
33813	initZoom	: function () {
33814		
33815		var $this = this;
33816	
33817		this.dom.wrap.on('mousewheel', function ($e) {
33818			$this.zoom($this.zoomValue + $this.zoomStep * $e.deltaY, 'mouse');
33819		}); //img.onMousewheel
33820		
33821	}, //FUNC initZoom	
33822	
33823	/** //** ----= initDrag	=--------------------------------------------------------------------------------------\**//** \
33824	*
33825	*	Initiates image dragging.
33826	*
33827	\**//** -------------------------------------------------------------------= by Mr.V!T @ Morad Media Inc. =----/** //**/
33828	initDrag	: function () {
33829		
33830		var $this = this;
33831		
33832		// Setting up
33833		var $res = this.setupJQ(this.draggable, {
33834			start			: function ($event, $ui) {
33835
33836				// Saving current state in history
33837				$this.saveHistory();
33838
33839			}, //start
33840
33841			drag		: function ($e, $ui) {
33842				// Getting actual position, and updating image data
33843				var $p = $this.dom.image.position();
33844				$this.move($p.left, $p.top);
33845			} //onDrag
33846		}); //$res
33847		
33848		// Init
33849		this.dom.image
33850			.draggable('destroy')
33851			.draggable($res);
33852
33853	}, //FUNC initDrag	
33854
33855	/** //** ----= initResize	=------------------------------------------------------------------------------\**//** \
33856	*
33857	*	Initiates resizable viewport.
33858	*
33859	\**//** -------------------------------------------------------------------= by Mr.V!T @ Morad Media Inc. =----/** //**/
33860	initResize	: function () {
33861		
33862		var $this = this;
33863		
33864		// Setting up
33865		var $res = this.setupJQ(this.resizable, {
33866			minWidth	: this.minWidth,
33867			minHeight	: this.minHeight,
33868			maxWidth	: this.maxWidth,
33869			maxHeight	: this.maxHeight,
33870
33871			start			: function ($event, $ui) {
33872
33873				// Saving current state in history
33874				$this.saveHistory();
33875
33876				// Adding size hint to show dimensions while resizing
33877				this.h = jQuery('<div class="mwSizeHint bottom right"></div>').appendTo($this.dom.wrap);
33878				
33879			}, //start
33880
33881			stop			: function ($event, $ui) {
33882				// Removing size hint
33883				this.h.remove();
33884			}, //stop
33885
33886			resize		: function ($event, $ui) {
33887				
33888				// Updating wrapper sizes and math
33889				$this.setWrapSize($this.dom.wrap.width(), $this.dom.wrap.height());
33890				
33891			// ---- HINT ----
33892			
33893				// Updating info on size hint
33894				this.h.html($this.x.wrapSize + 'px x ' + $this.y.wrapSize + 'px');
33895		
33896			// ---- DONE ----
33897				
33898				// Updatin UI to make sure controls look right
33899				$this.updateUI();
33900			 
33901			 	if ( $this.con )
33902					$this.con.update();
33903
33904				$this.onResize();
33905				$this.updatePositionsCss();
33906
33907			} //FUNC resize
33908		});
33909
33910		if ( $res === false ) return;
33911		
33912	 	// Initiating handles (if none set already)
33913	 	if ( !$res['handles'] ) {
33914	 		
33915	 		switch ( this.resizeOn ) {
33916 				case 'x':
33917 					$res['handles'] = 'e';
33918 					break;
33919				case 'y': 
33920 					$res['handles'] = 's';
33921 					break;
33922				case 'xy' :
33923 					$res['handles'] = 's, e, se';
33924 					break;
33925				case 'no' :
33926				default	:
33927					return;
33928	 		} //SWITCH resizeOn
33929	 		
33930	 	} //IF no handles specified
33931		
33932		// Init
33933		this.dom.wrap
33934			.resizable('destroy')
33935			.resizable($res);
33936
33937	}, //FUNC initResize	
33938
33939// ==== MATH ===================================================================================================================
33940
33941// ==== TRANSFORM ==============================================================================================================
33942
33943	/** //** ----= setWrapSize	=------------------------------------------------------------------------------\**//** \
33944	*
33945	*	Applies new wrap size.
33946	*
33947	*	@param	int	$width	- New width
33948	*	@param	int	$height	- New height
33949	*	
33950	*	@return	SELF
33951	*
33952	\**//** -------------------------------------------------------------------= by Mr.V!T @ Morad Media Inc. =----/** //**/
33953	setWrapSize		: function ($width, $height) {
33954
33955		// Updating wrapper math
33956		this.x.wrapSize	= $width;
33957		this.y.wrapSize	= $height;
33958
33959		// Updating original image, and it's data
33960		this.x.originSize = this.x.wrapSize;
33961		this.y.originSize = this.y.wrapSize;
33962		
33963		this.dom.source.css({
33964			'width'		: this.x.originSize,
33965			'height'	: this.y.originSize,
33966		}); //css
33967
33968		this.dom.wrap.css({
33969			'width'		: $width,
33970			'height'	: $height,
33971		});
33972
33973		// Updating wrap position
33974		this.updateWrapPos();
33975
33976		return this;		
33977	}, //FUNC setWrapSize	
33978
33979	/** //** ----= updateWrapPos 	=------------------------------------------------------------------------------\**//** \
33980	*
33981	*	Updates wrap positioning against source image (in case it was centered/margined)
33982	*
33983	*	@return	SELF
33984	*
33985	\**//** -------------------------------------------------------------------= by Mr.V!T @ Morad Media Inc. =----/** //**/
33986	updateWrapPos		: function () {
33987
33988		// Making sure we are positioned correctly still
33989		// Need to update position in case image was centered originally
33990		var $src	= this.dom.source;
33991		
33992		// Getting initial dimensions/positions
33993		var $p		= this.realPosition($src, true);
33994
33995		this.dom.wrap.css({
33996			'left'		: $p.left,
33997			'top'		: $p.top
33998		});
33999
34000		// Getting updated offset back
34001		var $o = this.dom.wrap.offset(); 
34002
34003		// Updating wrap offset values
34004		this.x.wrapOff = $o.left;
34005		this.y.wrapOff = $o.top;
34006
34007	}, //FUNC updateWrapPos
34008	
34009	/** //** ----= resize	=--------------------------------------------------------------------------------------\**//** \
34010	*
34011	*	Resize image to new dimensions.
34012	*	WARNING: Dimensions are set strictly, no aspect preserve used.
34013	*
34014	*	@param	int	$width	- New width
34015	*	@param	int	$height	- New height
34016	*	
34017	*	@return	SELF
34018	*
34019	\**//** -------------------------------------------------------------------= by Mr.V!T @ Morad Media Inc. =----/** //**/
34020	resize		: function ($width, $height) {
34021
34022		if ( $width < this.x.wrapSize )
34023			return;
34024
34025		if ( $height < this.y.wrapSize )
34026			return;
34027
34028		this.x.imageSize = $width;
34029		this.y.imageSize = $height;
34030	/*/
34031		var $maxWidth	= this.maxWidth ? Math.min(this.x.imageSize, this.maxWidth) : this.x.imageSize;
34032		var $maxHeight	= this.maxHeight ? Math.min(this.y.imageSize, this.maxHeight) : this.y.imageSize;
34033	/*/
34034		var $maxWidth	= this.maxWidth ? this.maxWidth : this.x.imageSize;
34035		var $maxHeight	= this.maxHeight ? this.maxHeight : this.y.imageSize;
34036	/**/
34037		this.dom.wrap.resizable('option', 'maxWidth', $maxWidth );
34038		this.dom.wrap.resizable('option', 'maxHeight', $maxHeight );
34039
34040	//	this.onResize($width, $height);
34041	//	this.con.update();
34042	//	this.updatePositionsCss();
34043		
34044	}, //FUNC resize	
34045
34046	/** //** ----= move	=--------------------------------------------------------------------------------------\**//** \
34047	*
34048	*	Moves image to given position.
34049	*
34050	*	@param	int	$left	- New horisontal position
34051	*	@param	int	$top	- New vertical position
34052	*	
34053	*	@return	SELF
34054	*
34055	\**//** -------------------------------------------------------------------= by Mr.V!T @ Morad Media Inc. =----/** //**/
34056	move		: function ($left, $top) {
34057
34058		// Saving distance
34059		var $deltaX 		= this.x.imagePos - $left;
34060		var $deltaY 		= this.y.imagePos - $top;
34061		
34062		// Updating image position and offset
34063		this.x.imagePos 	= $left;
34064		this.y.imagePos 	= $top;
34065
34066		this.x.imageOff 	-= $deltaX;
34067		this.y.imageOff 	-= $deltaY;
34068
34069		// Updating mouse positions (to ensure fresh values without actual cursor moving)
34070		this.x.imageMouse	+= $deltaX; 
34071		this.y.imageMouse	+= $deltaY; 
34072
34073	//	this.onMove();
34074
34075	//	this.con.update();
34076		
34077	//	this.updatePositionsCss();
34078		
34079	}, //FUNC move	
34080
34081	/** //** ----= zoom	=--------------------------------------------------------------------------------------\**//** \
34082	*
34083	*	Zooms image to given value. Zoom is counted against original image.
34084	*	Essencially zoom is combination or resize and move. Updates image position durin resize to keep 
34085	*	point specified on screen.
34086	*
34087	*	@param	float	$zoom		- New zoom level to set.
34088	*	@param	string	[$position] 	- Center point to position against.
34089	*					  Possible values: 
34090	*					    'wrap'	- positions against viewport center.
34091	*					    'image'	- positions against image center (default).
34092	*					    'mouse'	- positions against mouse cursor.
34093	*					    'zero'	- positions agains top left corner of image (no move).
34094	* 
34095	*	@return	SELF
34096	*
34097	\**//** -------------------------------------------------------------------= by Mr.V!T @ Morad Media Inc. =----/** //**/
34098	zoom		: function ($zoom, $position) {
34099
34100		var $this = this;
34101
34102	// ---- Validating values ----
34103	
34104		if ( !$position || !isIn($position, ['wrap', 'image', 'mouse', 'zero']) )
34105			$position = 'image';
34106		
34107		if ( $zoom > this.zoomMax )
34108			return;
34109
34110		if ( $zoom < this.zoomMin )
34111			return;
34112
34113		// Saving history
34114		this.saveHistory();
34115
34116		// Saving value
34117		this.zoomValue = $zoom;
34118
34119	// ---- Zooming ----
34120
34121		// Using math helper for each axis
34122		// It does all math and saves results into axis data as subarray
34123
34124		var zoomAxis = function ($axis, $size) {
34125			
34126			// Cleaning prev results if any, and creating shortcut
34127			var $ax = $axis.ex = {};
34128			
34129		// ---- SIZE ----
34130			
34131			$ax.newSize	= $size || ($axis.wrapSize * $zoom);
34132
34133		// ---- POSITION ----
34134		
34135			$ax.center = 0;
34136		
34137			// Calculating central point based on params given
34138			switch ( $position ) {
34139				case 'zero' :	// Top left of image
34140					$ax.center = 0; 
34141					break;
34142
34143				case 'image' :	// Center of image (delta == 0.5)
34144					$ax.center = $axis.imageSize / 2;
34145					break;
34146
34147				case 'mouse' :	// Mouse cursor position
34148					$ax.center = $axis.imageMouse;
34149					break;
34150
34151				case 'wrap' :	// Center of viewport
34152				default :
34153					$ax.center = ($axis.wrapSize / 2) - $axis.imagePos;
34154			} //SWITCH $position
34155
34156			// Calculating move proportions, commenting as simple questions to show logic
34157
34158			// How much image size was changed?
34159			$ax.sizeDelta	= $ax.newSize - $axis.imageSize;
34160			
34161			// How close zoom point was to one side?
34162			$ax.centerDelta	= $ax.center / $axis.imageSize;
34163			
34164			// Correcting position based on central point proportion
34165			$ax.posAdd	= $ax.sizeDelta * $ax.centerDelta;
34166			
34167			// Have new position now
34168			$ax.newPos	= $axis.imagePos - $ax.posAdd;
34169
34170		} //FUNC zoomAxis
34171
34172		// Calculating zoom (resize and move)
34173		// Making sure ratio is still correct (with X as priority)
34174		zoomAxis(this.x);
34175		zoomAxis(this.y, this.x.ex.newSize / this.aspectRatio);
34176
34177		// Resizing and moving
34178		this.resize(this.x.ex.newSize, this.y.ex.newSize);
34179		this.move(this.x.ex.newPos, this.y.ex.newPos);
34180
34181	// ---- DONE ----
34182
34183		// Calling event, given possibility to do smth
34184		this.onZoom($zoom);
34185
34186		// Updating slider, css and debug
34187	//	this.dom.slider.slider('option', 'value', $zoom);
34188		
34189		this.con.update();
34190		
34191		this.updatePositionsCss();
34192		
34193		return this;
34194	}, //FUNC zoom	
34195
34196	/** //** ----= flip	=--------------------------------------------------------------------------------------\**//** \
34197	*
34198	*	Flips image by given axis.
34199	*
34200	*	@param	string	[$axis]	- Axis to flip by. If omited - just updates image css.
34201	*				  Valid values are 'x' or 'y'.	
34202	*
34203	*	@return	SELF
34204	*
34205	\**//** -------------------------------------------------------------------= by Mr.V!T @ Morad Media Inc. =----/** //**/
34206	flip		: function ($axis) {
34207
34208		// Saving history
34209		this.saveHistory();
34210
34211		// Flip cancels rotation and opposite flip currently
34212		// ToDo: Simultanious support for both in resizer
34213		this.angle = 0;
34214
34215		// If axis specified - inverting that one
34216		if ( $axis == 'x' || $axis == 'y' )
34217			this[$axis].flip = !this[$axis].flip;
34218
34219		this.updateFlipRotateCss();
34220
34221		this.con.update();
34222
34223		this.onFlip($axis);
34224
34225		return this;
34226	}, //FUNC flip	
34227
34228	/** //** ----= rotate	=--------------------------------------------------------------------------------------\**//** \
34229	*
34230	*	Rotates image in direction specified.
34231	*
34232	*	@param	string	[$direction]	- Axis to flip by. If omited - just updates image css.
34233	*				  	Valid values are 'l' or 'r'.	
34234	*
34235	*	@return	SELF
34236	*
34237	\**//** -------------------------------------------------------------------= by Mr.V!T @ Morad Media Inc. =----/** //**/
34238	rotate		: function ($direction) {
34239		
34240		// Flip cancels flip currently
34241		// ToDo: Simultanious support for both in resizer
34242		this.x.flip = false;
34243		this.y.flip = false;
34244		
34245		// Updating angle depending on direction
34246		if ( $direction == 'r' )
34247			this.angle += 90;
34248		
34249		if ( $direction == 'l' )
34250			this.angle -= 90;
34251			
34252		// Correcting if necessary
34253		// Safe to check exact values, cuz steps are exactly 90
34254		if ( this.angle == 360 )
34255			this.angle = 0;
34256			
34257		if ( this.angle == -90 )
34258			this.angle = 270;
34259		
34260		// Setting up css
34261		this.updateFlipRotateCss();
34262
34263		this.con.update();
34264		
34265		this.onRotate(this.angle);
34266
34267		return this;
34268	}, //FUNC rotate	
34269
34270	/** //** ----= saveHistory	=------------------------------------------------------------------------------\**//** \
34271	*
34272	*	Saves current math state as undo step.
34273	*
34274	*	@return	SELF
34275	*
34276	\**//** -------------------------------------------------------------------= by Mr.V!T @ Morad Media Inc. =----/** //**/
34277	saveHistory		: function () {
34278
34279		var $this = this;
34280		
34281		// Not saving history while doing undo
34282		if ( this.isUndo )
34283			return;
34284		
34285		if ( this.historyTimer ) 
34286			clearTimeout($this.historyTimer);
34287
34288		this.historyTimer = setTimeout( function () {
34289			$this.historyLock = false;
34290		}, this.historyDelay);
34291
34292		if ( this.historyLock )
34293			return;
34294
34295		this.historyLock = true;
34296
34297		var $state = {
34298			x	: jQuery.extend({}, this.x),
34299			y	: jQuery.extend({}, this.y),
34300			zoom	: this.zoomValue,
34301		}; //$state
34302		
34303		this.history.push($state);
34304
34305	}, //FUNC saveHistory
34306
34307	/** //** ----= undo	=--------------------------------------------------------------------------------------\**//** \
34308	*
34309	*	Reverts changes done to image since last undo state.
34310	*
34311	*	@return	SELF
34312	*
34313	\**//** -------------------------------------------------------------------= by Mr.V!T @ Morad Media Inc. =----/** //**/
34314	undo		: function () {
34315
34316		// Checking for last step, and removing it from history
34317		if ( !this.history.length )
34318			return this;
34319
34320		// Flagging undo to prevent history updates
34321		this.isUndo = true;
34322
34323		var $state = this.history.pop(); 
34324
34325		// Resizing wrapper (this will also resize source)
34326		this.setWrapSize($state.x.wrapSize, $state.y.wrapSize);
34327
34328		// Resetting positions and zoom
34329		this.move($state.x.imagePos, $state.y.imagePos);
34330		this.resize($state.x.imageSize, $state.y.imageSize);
34331
34332		// Issuing zoom events
34333		this.onMove($state.x.imagePos, $state.y.imagePos);
34334		this.onResize($state.x.imageSize, $state.y.imageSize);
34335		this.onZoom($state.zoom);
34336
34337		// Checking flip
34338		if ( $state.x.flip !== this.x.flip )
34339			this.flip('x');		
34340
34341		if ( $state.y.flip !== this.y.flip )
34342			this.flip('y');		
34343		
34344		this.con.update();
34345		this.updatePositionsCss();
34346
34347		// Unflagging undo
34348		this.isUndo = false;
34349
34350	}, //FUNC undo	
34351
34352	/** //** ----= reset	=--------------------------------------------------------------------------------------\**//** \
34353	*
34354	*	Reverts any changes done to image, setting it's original dimensions and zoom.
34355	*
34356	*	@return	SELF
34357	*
34358	\**//** -------------------------------------------------------------------= by Mr.V!T @ Morad Media Inc. =----/** //**/
34359	reset		: function () {
34360
34361		// Defaulting to parent element width, as in 99% cases image is responsive and takes whole width of parent
34362		// Calculating height from aspectRatio
34363		// ToDo: envestigate, mb better to resize against current wrap width, or have optional (no width resize - using wrap size then)
34364		
34365		var $isResize	= this.resizeOn && this.resizeOn != 'no'; 
34366		
34367		if ( $isResize ) {
34368	 		switch ( this.resizeOn ) {
34369 				case 'x':
34370 					// Resizing to parent width, keeping height
34371					this.setWrapSize(this.dom.parent.width(), this.y.wrapSize);
34372 					break;
34373				case 'y': 
34374					// Resizing to proportional height, keeping width
34375					this.setWrapSize(this.x.wrapSize, this.x.wrapSize / this.aspectRatio);
34376 					break;
34377				case 'xy' :
34378					this.setWrapSize(this.dom.parent.width(), this.dom.parent.width() / this.aspectRatio);
34379 					break;
34380	 		} //SWITCH resizeOn
34381			
34382		} //IF resizing enabled
34383		
34384		// Calculating width/height from wrapper size, to propertly resize/center
34385		var $wrapRatio	= this.x.wrapSize / this.y.wrapSize;
34386		
34387		if ( $wrapRatio > this.aspectRatio ) {
34388			var $width	= this.x.wrapSize;
34389			var $height	= $width / this.aspectRatio;
34390			
34391			var $top	= Math.round((this.y.wrapSize - $height) / 2);
34392			var $left	= 0; 
34393		} //IF loose height
34394		else if ( $wrapRatio < this.aspectRatio ) {
34395			var $height	= this.y.wrapSize;
34396			var $width	= $height * this.aspectRatio;
34397			
34398			var $top	= 0;
34399			var $left	= Math.round((this.x.wrapSize - $width) / 2); 
34400		} //IF loose width
34401		else {
34402			var $width	= this.x.wrapSize;
34403			var $height	= this.y.wrapSize;
34404			
34405			var $top	= 0;
34406			var $left	= 0; 
34407		} //IF exactly same
34408		
34409		// Resetting positions and zoom, centering image
34410		this.move($left, $top);
34411		this.resize($width, $height);
34412
34413		this.onMove($left, $top);
34414		this.onResize($width, $height);
34415		this.onZoom(this.zoomValue);
34416		
34417		this.con.update();
34418		this.updatePositionsCss();
34419		
34420	}, //FUNC reset	
34421
34422// ==== CSS ====================================================================================================================
34423
34424	/** //** ----= refresh	=--------------------------------------------------------------------------------------\**//** \
34425	*
34426	*	Refreshes positins and sizes from current css.
34427	*
34428	\**//** -------------------------------------------------------------------= by Mr.V!T @ Morad Media Inc. =----/** //**/
34429	refresh		: function ($width, $height) {
34430
34431		// Getting fresh dimensions/positions
34432		var $p			= this.dom.image.position();
34433		var $o			= this.dom.image.offset();
34434
34435		this.x.imageSize	= this.dom.image.width();
34436		this.x.imagePos		= $p.left;
34437		this.x.imageOff		= $o.left;
34438
34439		this.y.imageSize	= this.dom.image.height();
34440		this.y.imagePos		= $p.left;
34441		this.y.imageOff		= $o.top;
34442
34443	}, //FUNC refresh	
34444
34445	/** //** ----= updatePositionsCss	=----------------------------------------------------------------------\**//** \
34446	*
34447	*	Validates current image properties, corrects them and updates css.
34448	*
34449	\**//** -------------------------------------------------------------------= by Mr.V!T @ Morad Media Inc. =----/** //**/
34450	updatePositionsCss		: function () {
34451		
34452		var $this = this;
34453		
34454		// Validating positions
34455		// Using math helper for each axis
34456		var checkAxis = function ( $axis ) {
34457
34458			// Cleaning prev results if any, and creating shortcut
34459			// Making sure structure is set
34460			var $a = $axis.ex = {
34461				newSize		: $axis.imageSize, 
34462				newPos		: $axis.imagePos, 	
34463				deltaPos	: 0,	
34464			}; // $a
34465			
34466		// ---- Size ----	
34467			
34468			// Image can't be smaller than viewport
34469			if ( $a.newSize < $axis.wrapSize )
34470				$a.newSize = $axis.wrapSize;
34471			
34472			// Calculating axis value here, then will check for both same time outside
34473			$a.zoomValue = $a.newSize / $axis.wrapSize;
34474
34475		// ---- Position ----	
34476			
34477			// No positive positions (left/top gap)
34478			if ( $a.newPos > 0 )
34479				$a.newPos = 0;
34480				
34481			// Checking right/bottom gap, calculating position difference
34482			$a.deltaPos = $axis.wrapSize - $a.newSize - $a.newPos;
34483
34484			// No positive value, correcting if necessary
34485			if ( $a.deltaPos > 0 )
34486				$a.newPos = $a.newPos + $a.deltaPos; 
34487
34488		} //FUNC checkAxis
34489		
34490		// Checking both axis
34491		checkAxis(this.x);
34492		checkAxis(this.y);
34493
34494		// Making sure resulting ratio if correct. Checking to bigger size
34495
34496		var $newY = this.x.ex.newSize / this.aspectRatio;
34497		if ( $newY > this.y.ex.newSize )
34498			this.y.ex.newSize = $newY;
34499
34500		var $newX = this.y.ex.newSize * this.aspectRatio;
34501		if ( $newX > this.x.ex.newSize )
34502			this.x.ex.newSize = $newX;
34503
34504		// Updating zoom value - it's proportion of wrap size to image size (using x Axis)
34505		// Need to check if that is too big (might happen during resizing + zooming)
34506		// Sticking to max zoom in this case
34507		// Updating zoom value. Using X axis, it's anyway proportional at this point
34508		this.zoomValue = this.x.imageSize / this.x.wrapSize;
34509		if ( this.zoomValue > this.zoomMax ) {
34510			
34511			// Decreasing both axis by zoom delta
34512			var $zoomDelta = this.zoomValue - this.zoomMax;  
34513			
34514			this.x.ex.newSize -= this.x.ex.newSize * $zoomDelta;  
34515			this.y.ex.newSize -= this.y.ex.newSize * $zoomDelta;  
34516			
34517			// Correcting zoom			
34518			this.zoomValue = this.zoomMax;
34519		} //IF zoom happend to be too big
34520		
34521		// Resizing and moving
34522		this.resize(this.x.ex.newSize, this.y.ex.newSize);
34523		this.move(this.x.ex.newPos, this.y.ex.newPos);
34524
34525		this.dom.image.css({
34526			'left'		: this.x.imagePos,
34527			'top'		: this.y.imagePos,
34528			
34529			'width'		: this.x.imageSize,
34530			'max-width'	: 'none',
34531		//	'height'	: this.y.imageSize,
34532			'height'	: '',
34533		}); //css
34534
34535		if ( this.dom.slider )
34536			// Updating slider position
34537			this.dom.slider.slider('option', 'value', this.zoomValue);
34538		
34539		// Updating draggable, setting image containment
34540		// Box selected such way that it can fit corners in all directions
34541		
34542		// Getting difference in box and image size, and adding those around wrap box as containment
34543		var $dx = this.x.imageSize - this.x.wrapSize;
34544		var $dy = this.y.imageSize - this.y.wrapSize;
34545		
34546		// Using original offset as base
34547		var $cnt = [this.x.wrapOff - $dx, this.y.wrapOff - $dy, this.x.wrapOff, this.y.wrapOff];
34548
34549		this.dom.image.draggable('option', 'containment', $cnt );
34550		
34551	}, //FUNC updatePositionsCss	
34552
34553	/** //** ----= updateFlipRotateCss	=----------------------------------------------------------------------\**//** \
34554	*
34555	*	Update image transform css.
34556	*
34557	\**//** -------------------------------------------------------------------= by Mr.V!T @ Morad Media Inc. =----/** //**/
34558	updateFlipRotateCss	: function () {
34559		
34560		var $tr = '';
34561		
34562		// Adding scale only if specified, othewise - cleaning it
34563		if ( this.x.flip || this.y.flip ) 
34564			$tr += ' scale(' + (this.y.flip ? '-1' : '1') + ', ' + (this.x.flip ? '-1' : '1') + ')'
34565
34566		// Adding rotation only if specified, othewise - cleaning it
34567		if ( this.angle != 0 )
34568			$tr += ' rotate(' + this.angle + 'deg)';
34569
34570		// Updating css
34571		this.dom.image.css({
34572			'transform'	: $tr,
34573		}); //css
34574		
34575	}, //FUNC updateFlipRotateCss	
34576
34577// ==== HELPERS ================================================================================================================
34578
34579	/** //** ----= realPosition	=------------------------------------------------------------------------------\**//** \
34580	*
34581	*	Returns real element position, counting parent scrolls.
34582	*	Based on fact that jQuery fails to get correct element position if one of parents was scrolled.
34583	*
34584	*	@todo	More testing required. BOSY and immideate parent might be counted correctly already by jQuery. 
34585	*
34586	*	@param	jQuery	$el	- Element to count posititon for.
34587	*
34588	*	@return object		- Element relative position in jQuery native format { left: 0, top: 0 } 
34589	*
34590	\**//** -------------------------------------------------------------------= by Mr.V!T @ Morad Media Inc. =----/** //**/
34591	realPosition	: function ($el, $countMargin) {
34592
34593		var $pos = $el.position();
34594		
34595		// Who's scrolled?!
34596		// Looping through all parents and checking scoll values.
34597		// Using those to correct position of element, cuz jQuery fails to return proper posiiton for scrolled elements
34598		var $parents 	= $el.parents();
34599		var $scrollX	= 0;
34600		var $scrollY	= 0;
34601		 
34602		$parents.each( function () {
34603			var $p = jQuery(this);
34604			
34605			// Skipping body
34606			if ( $p.is('BODY, HTML') )
34607				return;
34608			
34609			// Adding scroll values (for non scrolled that will be 0 anyway)
34610			$scrollX += $p.scrollLeft();
34611			$scrollY += $p.scrollTop();
34612		}); //jQuery(parents).each
34613
34614		// For now fixing using summared scroll
34615		// ToDo: Test more and see if that's working good
34616		// ToDo: Test with document scroll and immediate parent scroll (those might be propertly counted by jQuery)
34617		
34618		$pos.left	+= $scrollX;
34619		$pos.top	+= $scrollY;
34620
34621		// Might need to count margins
34622		if ( $countMargin ) {
34623			
34624			$pos.left	+= parseInt($el.css('margin-left'));
34625			$pos.top	+= parseInt($el.css('margin-top'));
34626	
34627		} //IF margins matter
34628		
34629		return $pos;
34630	}, //FUNC realPosition	
34631
34632	/** //** ----= setupJQ	=--------------------------------------------------------------------------------------\**//** \
34633	*
34634	*	Helps validate/initiate jQuery.ui settings. Takes initial settings and extesion object and mixes all together.
34635	*	Allows FALSE as initial setting, to cancel process.
34636	*
34637	*	@param	object	$settings	- Initial settings to validate
34638	*	@param	object	$ex		- Extention settings to add.	
34639	*
34640	*	@return object			- jQuery.ui setup object, or FALSE if cancel.
34641	*
34642	\**//** -------------------------------------------------------------------= by Mr.V!T @ Morad Media Inc. =----/** //**/
34643	setupJQ		: function ($settings, $ex) {
34644		
34645		// Canceling if set so
34646		if ( $settings === false )
34647			return false;
34648		
34649		// If not canceled - need to make sure it's object
34650		if ( !isObject($settings) )
34651			$settings = {};
34652		
34653		// Creating setup object
34654		return jQuery.extend({}, $settings, $ex);
34655	}, //FUNC setupJQ	
34656
34657	/** //** ----= state	=--------------------------------------------------------------------------------------\**//** \
34658	*
34659	*	Sets loading state (on/off). Uses slight delay, to prevent flicker.
34660	*
34661	*	@param	bool	$state	- Current loading state.
34662	*
34663	\**//** -------------------------------------------------------------------= by Mr.V!T @ Morad Media Inc. =----/** //**/
34664	state		: function ($state) {
34665		
34666		var $this = this;
34667		
34668		this.loaderState = $state;
34669		
34670		if ( $state === true ) {
34671
34672			// Delaying show
34673			this.loaderTimer = setTimeout( function () {
34674				$this.dom.wrap.addClass('loading');
34675			}, this.loaderDelay);
34676		
34677		} else { //IF loading
34678		
34679			// Clearing loader timer if any happen
34680			clearTimeout(this.loaderTimer);
34681
34682			$this.dom.wrap.removeClass('loading');
34683		} //IF not loading
34684		
34685	}, //FUNC state	
34686
34687// ==== ENDofOBJECT ============================================================================================================
34688		
34689	}; //CLASS mwImgEd
34690	
34691	// Applying options
34692	$class.set($options);
34693	
34694	// Adding events
34695	$class = vEventObject(['onInit', 'onEdit', 'onBeforeSave', 'onSave', 'onAfterSave', 'onCancel', 'onDone', 'onChange', 'onZoom', 'onResize', 'onMove', 'onRotate', 'onFlip', 'onReset', 'onUndo'], $class);
34696	
34697	// Storing class as data in image element
34698	$img.data('mwImgEd', $class);
34699
34700	// Autorun if options set
34701	if ( !isEmpty($options) )
34702		$class.edit();
34703	
34704	return $class; 
34705	
34706} //CONSTRUCTOR mwImgEd
34707
34708/* 
34709	This JS makes liveEd responsive, still having paddings/borders on blocks.
34710	New liveEd will have responsive nature, which will make most of this object obsolete
34711	
34712	liveEd uses "Rows" conception, when several blocks with float:left form row. Several rows are broken with cler:both
34713	liveEd have own responsive math, which is kinda duped here. Again - this is obsolete.
34714 */
34715 
34716mwResponsive = {
34717	
34718	pageArea		: 'DIV.mwPageArea',	// Page area selector
34719	pageBlock		: 'DIV.mwPageBlock',	// Page widget element selector
34720	classFloat		: 'Float',		// Class applied to floating blocks
34721	classHost		: 'rowHost',		// Class applied to first col in a row
34722	classPlaceholder	: 'Placeholder',	// Class applied to placeholder blocks
34723
34724	blocks			: false,		// All found page blocks
34725	rows			: [],			// Filtered rows
34726
34727	/** //** ----= init	=--------------------------------------------------------------------------------------\**//** \
34728	*
34729	* 	Initiates responsive math on page.
34730	* 
34731	\**//** -------------------------------------------------------------------= by Mr.V!T @ Morad Media Inc. =----/** //**/
34732	init		: function () {
34733		var $this = this;
34734
34735		$this.updateRowsData();
34736
34737		// Hiding liveEd for small windows
34738		jQuery(window).resize( function ($e) {
34739			for ( var $i = 0; $i < $this.rows.length; $i++ ) {
34740				$this.setRowHeight($this.rows[$i]);
34741			} //FOR each row
34742		}); //FUNC jQuery.resize
34743
34744		jQuery(window).resize();
34745		
34746	}, //FUNC init
34747
34748	/** //** ----= updateRowsData	=------------------------------------------------------------------------------\**//** \
34749	*
34750	* 	Searches for blocks on page and updates rows map.
34751	* 
34752	\**//** -------------------------------------------------------------------= by Mr.V!T @ Morad Media Inc. =----/** //**/
34753	updateRowsData		: function () {
34754		
34755		var $this = this;
34756		
34757		$this.blocks	= jQuery($this.pageBlock);
34758		$this.rows	= $this.getRows($this.blocks); 
34759
34760		// Updating found rows widths and heights
34761		for ( var $i = 0; $i < $this.rows.length; $i++ ) {
34762			$this.setRowWidth($this.rows[$i]);
34763			$this.setRowHeight($this.rows[$i]);
34764		} //FOR each row
34765				
34766	}, //FUNC updateRowsData
34767	
34768	/** //** ----= isFloat	=--------------------------------------------------------------------------------------\**//** \
34769	*
34770	* 	Checks if given element is floating block. This counts any element which behaves like floating block, 
34771	*	either block or placeholder (which inherits initial float property from block)
34772	* 
34773	* 	@param	jQuery	$el		- Element to check. 
34774	* 
34775	* 	@return	bool			- Check result.
34776	* 
34777	\**//** -------------------------------------------------------------------= by Mr.V!T @ Morad Media Inc. =----/** //**/
34778	isFloat		: function ($el) {
34779		return $el.hasClass(this.classFloat);
34780	}, //FUNC isFloat
34781
34782	/** //** ----= isHost	=--------------------------------------------------------------------------------------\**//** \
34783	*
34784	* 	Checks if given floating block starts new row.
34785	* 
34786	* 	@param	jQuery	$el		- Element to check. 
34787	* 
34788	* 	@return	bool			- Check result.
34789	* 
34790	\**//** -------------------------------------------------------------------= by Mr.V!T @ Morad Media Inc. =----/** //**/
34791	isHost		: function ($el) {
34792		return $el.hasClass(this.classHost);
34793	}, //FUNC isHost
34794
34795	/** //** ----= isPlaceholder	=------------------------------------------------------------------------------\**//** \
34796	*
34797	* 	Checks if given element is sorting placeholder.
34798	* 
34799	* 	@param	jQuery	$el		- Element to check. 
34800	* 
34801	* 	@return	bool			- Check result.
34802	* 
34803	\**//** -------------------------------------------------------------------= by Mr.V!T @ Morad Media Inc. =----/** //**/
34804	isPlaceholder	: function ($el) {
34805		return $el.hasClass(this.classPlaceholder);
34806	}, //FUNC isPlaceholder
34807	
34808	/** //** ----= getAreas	=--------------------------------------------------------------------------------------\**//** \
34809	*
34810	* 	Searches areas within context or on page body if omited. If area is given as context - 
34811	*	it will be included in results. Returns jQuery object containing found areas.
34812	*
34813	*	@param	jQuery	[$context]	- Context to search within. 
34814	*
34815	*	@return	jQuery			- jQuery object containing found areas.
34816	*
34817	\**//** -------------------------------------------------------------------= by Mr.V!T @ Morad Media Inc. =----/** //**/
34818	getAreas	: function ($context) {
34819
34820		$context = $context || jQuery('BODY');
34821
34822		// Looking up for areas within context
34823		var $areas	= $context.find(this.pageArea);
34824		
34825		// If area given as context, it shold be included too
34826		if ( $context.is(this.pageArea) )
34827			$areas = $areas.add($context); 
34828			
34829		return $areas;
34830	}, //FUNC getAreas
34831
34832	/** //** ----= getRow	=--------------------------------------------------------------------------------------\**//** \
34833	*
34834	* 	Returns row which current block belongs as array of jQuery blocks. If it's not part of a row, returns FALSE.
34835	*
34836	*	@param	jQuery	$block	- Block to return row for. 
34837	*
34838	*	@return	MIXED		- Array with siblings or FALSE if not part of row.
34839	*
34840	\**//** -------------------------------------------------------------------= by Mr.V!T @ Morad Media Inc. =----/** //**/
34841	getRow	: function ($block) {
34842		
34843		// If not floating - not part of row 
34844		// Exception is placeholder - it can happen between floats, and I will need to know which row it is
34845		if ( !this.isFloat($block) && !this.isPlaceholder($block) )
34846			return false;
34847		
34848		var $res = [];
34849		
34850		// Searching all blocks before block
34851		// If host already, no need to search previous blocks 
34852		if ( !$block.is('.rowHost') ) 
34853			$block.prevUntil(':not(.' + this.classFloat + '), .rowHost').each( function () {
34854				
34855				var $el = jQuery(this);
34856				
34857				$res.push( $el );
34858				
34859				// Should include host, if happen
34860				var $prev = $el.prev(); 
34861	
34862				if ( $prev.is('.rowHost') )
34863					$res.push( $prev );
34864				
34865			}); //jQuery.each(not floating).callback
34866
34867		// jQuery prev searches from current to top, which gives reversed order
34868		$res.reverse();
34869
34870		// Adding self
34871		$res.push($block);
34872
34873		// Searching all blocks after block
34874		$block.nextUntil(':not(.' + this.classFloat + '), .rowHost').each( function () {
34875			$res.push( jQuery(this) );
34876		}); //jQuery.each(not floating).callback
34877		
34878		return $res;
34879	}, //FUNC getRow
34880
34881	/** //** ----= getRows	=--------------------------------------------------------------------------------------\**//** \
34882	*
34883	* 	Returns rows map from given set of elements.
34884	*
34885	*	@param	jQuery	$context	- Elements context to arrange.
34886	*
34887	*	@return	array			- Array with found rows map.
34888	*
34889	\**//** -------------------------------------------------------------------= by Mr.V!T @ Morad Media Inc. =----/** //**/
34890	getRows	: function ($context) {
34891
34892		var $this	= this;
34893		
34894		var $rows	= [];		// Will store all rows, with one there already
34895	
34896		// Filtering contect to hosts
34897		// Then getting row of each host
34898		var $hosts	= $context.filter( '.'+this.classHost );
34899		
34900		$hosts.each( function () {
34901			
34902			// Doing all in one line
34903			$rows.push( $this.getRow( jQuery(this) ) );
34904				
34905		}); // each host
34906
34907		return $rows;
34908		
34909	/*/	
34910		var $row	= [];		// Will store current row
34911		var $rows	= [$row];	// Will store all rows, with one there already
34912		
34913		// Storing length for referrence
34914		var $l		= $context.length;		
34915
34916		// First calculating rows and storing them in array for fast access later
34917		for ( var $i = 0; $i < $l; $i++ ) {
34918
34919			// Shortcut to block
34920			var $block = $context.eq($i);
34921
34922			// Skipping invisible blocks, if they will become visible - they will be recalculated
34923			if ( !$block.is(':visible') )
34924				continue;
34925			
34926			if ( this.isFloat($block) ) {
34927
34928				// Row hosts are starting new row anyway
34929				if ( this.isHost($block) ) {
34930					$row = [];
34931					$rows.push($row);
34932				} //IF new row
34933
34934				// Floating blocks are added into current row
34935				$row.push($block);
34936				
34937			}  //IF float
34938			else if ( $row.length ) {
34939			
34940				// Others are starting new row if needed
34941				$row = [];
34942				$rows.push($row);
34943				
34944			} //IF new row
34945			
34946		} //FOR each block
34947		
34948		return $rows;
34949	/**/	
34950	}, //FUNC getRows	
34951	
34952	/** //** ----= setRowWidth	=------------------------------------------------------------------------------\**//** \
34953	*
34954	* 	Updates given row with % dimensions instead of static px. Initially page is rendered with px, resizing 
34955	*	also does px.
34956	*
34957	*	@param	array	$row		- Array with blocks in a row.
34958	*	@param	MIXED	[$width]	- Numeric width to set. If omited will be calculated from parent area.
34959	*					  Can be given as jQuery area.
34960	*	@param	bool	[$px]		- Set TRUE to correct widhts into absolute px instead of %.
34961	*
34962	*	@return	SELF
34963	*
34964	\**//** -------------------------------------------------------------------= by Mr.V!T @ Morad Media Inc. =----/** //**/
34965	setRowWidth	: function ($row, $width, $px) {
34966
34967		$width	= $width || 0;
34968		$px	= $px || false;
34969
34970		if ( !$row || !$row.length )
34971			return this;
34972
34973		if ( $width instanceof jQuery ) {
34974			
34975			// Got parent area to use as source
34976			$width = $width.width();
34977			
34978		} else if ( !$width ) {	
34979		
34980			// Nothing given - using parent area of first block (it exists - checked above)
34981			$width = $row[0].parent().width();
34982		
34983		} //IF nothing given
34984
34985		var $row_width	= 0;		// Summary row width
34986		var $l		= $row.length;	// Saving row array length for fast access
34987		
34988	// ---- WIDTHS ----
34989		
34990		// Calculating summary row width
34991		for ( var $i = 0; $i < $l; $i++ ) {
34992			
34993			var $block		= $row[$i];
34994			
34995			// Getting width and padding
34996			var $block_width	= $block.width();
34997			var $block_pad		= $block.outerWidth() - $block_width;
34998			
34999			// Adding padding back to width
35000			$block_width	+= $block_pad;
35001			
35002			// Correcting block width - can't be more than area width
35003			if ( $block_width > $width ) {
35004				$block_width = $width;
35005				$block.width($width - $block_pad);
35006			} //IF block is too big
35007			
35008			// Adding total
35009			$row_width += $block_width;
35010			
35011			// Saving width into block object to do not regain later,
35012			// should not interferee with internal properties
35013			$block.tmp_width	= $block_width;
35014			$block.tmp_pad		= $block_pad;
35015		} //FOR each block in row
35016		
35017		// Nothing to do for empty row (possibly everything was invisible)
35018		if ( !$row_width )
35019			return this;
35020
35021		var $precision	= 100;
35022		var $total	= 100;
35023		var $totalPx	= $row_width;
35024
35025		// Converting width for each block
35026		for ( var $i = 0; $i < $l; $i++ ) {
35027			
35028			var $block	= $row[$i];
35029			
35030			var $pw		= $block.tmp_width;
35031			var $w		= Math.round($block.tmp_width / ($row_width / 100) * $precision) / $precision;
35032			
35033			// Correcting to total, adding delta to last block
35034			$totalPx	-= $pw;
35035			$total		-= $w;
35036
35037			// Correcting last column if total is not 0 
35038			if ( $i == $l-1 ) {
35039				$pw	= $pw + $totalPx;
35040				$w	= Math.round(($w + $total) * $precision) / $precision;
35041			} //IF last element 
35042			
35043			if ( $px )
35044				// Setting css in px. Nothing tricky here
35045				$block.width($pw);
35046			else
35047				// Setting width css in %. Using calc() in case padding happen, or classic % width if not
35048				$block.css('width', $block.tmp_pad ? 'calc(' + $w + '% - ' + $block.tmp_pad + 'px)' : $w + '%' );
35049			
35050		} //FOR each block in row
35051		
35052		return this;		
35053	}, //FUNC setRowWidth
35054
35055	/** //** ----= setRowHeight	=------------------------------------------------------------------------------\**//** \
35056	*
35057	* 	Sets row height, updating all blocks in it with min-height property.
35058	*
35059	*	@param	array	$row		- Array with blocks in a row.
35060	*	@param	int	$height		- Height to set.
35061	*
35062	*	@return	SELF
35063	*
35064	\**//** -------------------------------------------------------------------= by Mr.V!T @ Morad Media Inc. =----/** //**/
35065	setRowHeight	: function ($row, $height) {
35066		
35067		$height	= $height || 0;
35068		
35069		var $l	= $row.length;	// Saving row length for fast access
35070			
35071		// Recalculating height if none given
35072		if ( !$height ) {	
35073
35074			for ( var $i = 0; $i < $l; $i++ ) {
35075				
35076				// Ressetting block height to recalculate
35077				$row[$i].css('min-height', '');
35078				
35079				// Getting resulting height					
35080				var $tmp_height		= $row[$i].height();
35081				var $tmp_pad		= $row[$i].outerHeight() - $tmp_height;
35082				
35083				$tmp_height		+= $tmp_pad;
35084				
35085				if ( $tmp_height > $height )
35086					$height = $tmp_height; 
35087				
35088				// Saving block pad to count on resize
35089				$row[$i].tmp_hieght_pad = $tmp_pad;
35090				
35091			} //FOR each block in row
35092		} //IF no height
35093		
35094		// Now updating heights
35095		for ( var $i = 0; $i < $l; $i++ ) {
35096			$row[$i].css('min-height', $height - $row[$i].tmp_hieght_pad);
35097		} //FOR each block in row
35098		
35099		return this;		
35100	}, //FUNC setRowHeight	
35101	
35102} //OBJECT mwResponsive
35103
35104jQuery( function () {
35105	// Initiating right away
35106//	mwResponsive.init();
35107}); //jQuery.onLoad
35108
35109var mwCrop = {
35110	
35111	items_ids_list	: [], // array of ids to make img croppable
35112	
35113	init		: function() {
35114		
35115		var $this = this;
35116		
35117		//remove old images (for ajax load)
35118		jQuery('.blogs-post-crop-icon').remove();
35119		
35120		jQuery.each(this.items_ids_list, function(i){
35121			
35122			//var $i = jQuery('#' + $this.items_ids_list[i]);
35123			var $i = jQuery('[data-crop-sn=' + $this.items_ids_list[i] + ']');
35124			//___($i);
35125			var run_crop = function(e) {
35126				
35127				mwLiveEd.toggle(false);
35128				mwLiveEd.noResize = true;
35129				
35130				var $ed = mwImgEd($i, {
35131				//	'debug'		: true,
35132					'zoomValue'	: 1,
35133					'modal'		: true,
35134					'overlay'	: false,
35135					'resizeOn'	: 'y',
35136				//	'resizeOn'	: false,
35137				})
35138				.onSave( function () {
35139						
35140						//___(this.queryStr);
35141						//$this.Data.pwidth	= Math.round($this.Data.pwidth);
35142						//$this.Data.ewidth	= Math.round(this.x.wrapSize);
35143						//$this.Data.eheight	= Math.round(this.y.wrapSize);
35144						
35145						var $data = {};
35146						
35147						$data['id'] = $this.items_ids_list[i];
35148						$data['crop'] = this.queryStr;
35149						
35150						mwAjax('/site/ajax/blogs/cropcontroller/saveCropDB', $data, false)
35151							.go()
35152							.success( function($data) {
35153								
35154								___('imged crop save done');
35155								
35156							} )
35157							.error( function() {
35158								
35159								___('imged crop save error');	
35160								
35161							} )
35162						; // mwAjax
35163						
35164					}
35165				) //FUNC onSave
35166				.onAfterSave( function () {
35167	
35168						// Finalizing dimensions
35169						// ToDo: autodetect if necessary in imgEd
35170						this.onResize();
35171						
35172						mwLiveEd.toggle(true);
35173						mwLiveEd.noResize = false;
35174						
35175					}
35176				) //FUNC onAfterSave
35177				; // $ed
35178				
35179			};
35180			
35181			//check is current image local
35182			var isLocal = false;
35183			if ( $i.attr('src').indexOf('://') == -1 ) {
35184				
35185				//this is local url
35186				isLocal = true;
35187				
35188			}
35189			else {
35190				
35191				var url = new URL($i.attr('src'));
35192				
35193				//local url
35194				if ( location.host == url.host )
35195					isLocal = true;
35196				
35197			}
35198			
35199			//if local url
35200			if ( isLocal ) {
35201				
35202				var btn = jQuery('<div class="blogs-post-crop-icon"></div>'); 
35203				$i.before(btn);
35204				btn.click(run_crop);
35205				//setTimeout( function() { btn.css('left', $i.width() - 35); btn.css('top', 35); }, 100);
35206				$i.load( function() { btn.css('left', $i.width() - 35); btn.css('top', 35); } );
35207				
35208				$i.off('dblclick').on('dblclick', run_crop);
35209				
35210			}
35211			
35212		});
35213	},
35214	
35215	/** //** ----= add_items	=------------------------------------------------------------------------------\**//** \
35216	*
35217	*	Add ids of items to list
35218	* 			
35219	* 	@param	array	items	- items must be array of text ids
35220	* 
35221	*	@return	nothing
35222	*
35223	\**//** -------------------------------------------------------------------= by Alex @ Morad Media Inc. =------/** //**/
35224	
35225	add_items	: function(items) {
35226		
35227		jQuery.each(items, function(i){
35228			
35229			//___(this, i, items[i]);
35230			
35231			if ( mwCrop.items_ids_list.indexOf(items[i]) == -1 )
35232				mwCrop.items_ids_list.push(items[i]);
35233			
35234		});
35235	},
35236	
35237	
35238	
35239} // OBJ mwCrop
35240
35241jQuery( function () {
35242	
35243	if ( typeof mwData != "undefined" ) {
35244		
35245		mwCrop.init();
35246		
35247	}
35248	
35249});
35250
35251var mwPostEd = {
35252	
35253	/* ---- jQuery shortcuts ---- */	
35254	
35255	windowId		: 'wPostEd',			// Editor window ID
35256	window			: false,			// Editor window object
35257	
35258	postEdit		: false,			// Specal flag for Access Ping process
35259	
35260	dom			: {},				// Stores interesting window elements
35261	
35262	Defaults		: {
35263		
35264		is_news	: 0,
35265		
35266	},
35267	
35268	/* ---- Init ---------------------------------------------------------------------------------------------------------------- */
35269	
35270	initDom		: function () {
35271		
35272		var $this = this;
35273		
35274		// Creating shortcuts to window and it's body
35275		$this.window		= mwWindow($this.windowId);
35276		$this.dom.body		= $this.window.Body;
35277		
35278		// Defining complete element prefix
35279		var $pfx		= $this.windowId+'_';
35280		
35281		// Looking for special elements in entire window 
35282		$this.window.Window.find('[id^='+$pfx+']').each( function () {
35283			
35284			var $el		= jQuery(this);
35285			
35286			// Cutting element name from ID
35287			var $name	= this.id.substr($pfx.length); 
35288
35289			// Adding class marker to these items
35290			$el.addClass($name);
35291
35292			// Storing it as part of DOM set
35293			$this.dom[$name] = $el;
35294			
35295		}); //FUNC each.id
35296		
35297		// Reordering element for fast debug
35298		// For this - recreating object in order
35299		var $keys	= Object.keys($this.dom).sort();
35300		var $tmp	= {};
35301		
35302		for ( var $i in $keys )
35303			$tmp[ $keys[$i] ] = $this.dom[$keys[$i]];
35304		
35305		// Resaving back in dom
35306		$this.dom = $tmp;
35307
35308		//___(this.dom);
35309		
35310	}, //FUNC initDom
35311	
35312	initDimensions		: function () {
35313		
35314		var $this = this;
35315		
35316		// Maximum content width. Browser width and some white space on sides
35317		var $maxWidth		= jQuery(window).width() - 80;
35318		
35319		// Setting body width
35320		$this.dom.body.width($maxWidth);
35321		
35322	}, //FUNC initDimensions
35323	
35324	init		: function () {
35325
35326		var $this = this;
35327		
35328		//init counters once
35329		if ( !$this.meta_title_counter ) {
35330			
35331			$this.meta_title_counter = mwTextLengthCounter('#w_wPostEd [name=meta_title]', '#w_wPostEd .meta-title-counter');
35332			$this.meta_title_counter.min = 10;
35333			$this.meta_title_counter.max = 60;
35334			$this.meta_description_counter = mwTextLengthCounter('#w_wPostEd [name=meta_description]', '#w_wPostEd .meta-description-counter');
35335			$this.meta_description_counter.min = 100;
35336			$this.meta_description_counter.max = 160;
35337			
35338		}//init counters
35339		
35340		if ( $this.window ) return;
35341		
35342		$this.initDom();
35343		//$this.initDimensions();
35344		$this.hidePanel();
35345		
35346		$this.Form = jQuery('#mwPostEd_form');
35347		$this.TabsControl = jQuery('#mwPostEd_toolsRight');
35348		$this.PostContent = jQuery('.mwPostEd_PostContent');
35349		$this.Tabs = jQuery('.mwPostEd_Tabs');
35350		$this.hideTabsButton = this.TabsControl.find('td[rel=hideTabs]');
35351		$this.TagTitleInput = jQuery('#tagsFilterPostEd');
35352		
35353		if ( mwData.show_post_id ) {
35354			
35355			setTimeout(function(){ mwPostEd.edit(mwData.show_post_id); }, 500);
35356			
35357		}
35358		
35359		$this.Form.find('.datepicker_icon').click( function(event) {
35360			//___(this);
35361			//mwPostEd.Form.find('.name-published_date input').click();
35362			jQuery(this).closest('dd').find('input[type=text]').click();
35363			
35364		 } );
35365		
35366		jQuery('select[name=header_select]').change(function(e){
35367			
35368			var el = jQuery(this);
35369			if ( el.val() == 'none' ) {
35370				
35371				jQuery('.header-row').hide();
35372				
35373			} else if ( el.val() == 'single' ) {
35374				
35375				jQuery('.header-row').hide();
35376				jQuery('.header-row.single').show();
35377				
35378			} else if ( el.val() == 'gallery' ) {
35379				
35380				jQuery('.header-row').hide();
35381				jQuery('.header-row.gallery').show();
35382				
35383			}
35384			
35385		})
35386		
35387		
35388		
35389		return false;
35390		
35391		
35392	}, //FUNC init
35393	
35394	initAccessPing	: function() {
35395		___('initAccessPing');
35396		var $this = this;
35397		
35398		//check is access icon enabled
35399		if ( mwData.accessIcon && $this.ID ) {
35400			
35401			var doPing = function() {
35402				
35403				mwAjax('/site/ajax/blogs/accessPing/' + $this.ID, null, false )
35404					.go()
35405					.success( function($data) {
35406						//___($data);
35407						if ( typeof($data.content) != 'undefined' ) {
35408							
35409							
35410							//check is there are some other users in same post
35411							if ( $data.content.accessUsers ) {
35412								
35413								___('we have some users', $data.content.accessUsers);
35414								$this.accessInfo = $data.content.accessUsers;
35415								
35416								$this.showAccessInfo(false);
35417								
35418								jQuery('#accessIcon').css('background-image', "url('/res/blogs/images/block-user-red.svg')");
35419								
35420							}//there are other users in same post
35421							else {
35422								___('no access users');
35423								$this.accessInfo = [];
35424								
35425								//clean up messages
35426								mwState(false, 'wPostEd');
35427								
35428								jQuery('#accessIcon').css('background-image', "url('/res/blogs/images/block-user-grey.svg')");
35429								
35430							}//no users
35431							
35432							___($data.content);
35433							
35434						} else {
35435							
35436							___('no data');
35437							return false;
35438							
35439						}
35440					} );
35441				
35442			}//FUNC doPing
35443			
35444			//access icon config value should store period in seconds
35445			var timer = mwData.accessIcon;
35446			
35447			//do 1st ping without delay
35448			doPing();
35449			
35450			//loop function
35451			(function myLoop() {
35452				
35453				//prevent double run
35454				if ( $this.loopTimer ) clearTimeout($this.loopTimer);
35455				
35456				$this.loopTimer = setTimeout( function() {
35457					
35458					//make ping call
35459					doPing();
35460					
35461					//continue
35462					if ( $this.postEdit ) myLoop();
35463					
35464				}, timer * 1000)
35465				
35466			})(); 
35467			
35468		}//access icon enabled
35469		
35470	}, //FUNC initAccessPing
35471	
35472	showAccessInfo	: function(asPopup = false) {
35473		
35474		var $this = this;
35475		
35476		if ( $this.accessInfo.length ) {
35477			
35478			var spl = '';
35479			var t = '';
35480			for ( i in $this.accessInfo ) {
35481				
35482				t += spl + '<b>' + $this.accessInfo[i].email + '</b>';
35483				spl = ', ';
35484				
35485			}
35486			
35487			if ( !asPopup ) {
35488				
35489				//mwState( mwError('User(s) editing this post: ' + t), 'wPostEd')
35490				
35491			}
35492			else
35493				mwConfirmation( function() { mwWindow('systemConfirmation').hide(); }
35494				, 'User(s) viewing this post: ' + t);
35495			
35496		}
35497		
35498		
35499	}, //FUNC showAccessInfo
35500	
35501	showUsersInPost	: function(post_id = 0) {
35502		
35503		var $this = this;
35504		
35505		if ( !post_id ) return;
35506		
35507		mwAjax('/site/ajax/blogs/accessPing/' + post_id, null, false )
35508			.go()
35509			.success( function($data) {
35510				//___($data);
35511				if ( typeof($data.content) != 'undefined' ) {
35512					
35513					
35514					//check is there are some other users in same post
35515					if ( $data.content.accessUsers ) {
35516						
35517						___('we have some users', $data.content.accessUsers);
35518						$this.accessInfo = $data.content.accessUsers;
35519						
35520						$this.showAccessInfo(true);
35521						
35522						
35523						
35524					}//there are other users in same post
35525					else {
35526						___('no access users');
35527						$this.accessInfo = [{'email' : 'None! Update active User by <a href="">refreshing</a> the page.'}];
35528						
35529						$this.showAccessInfo(true);
35530						
35531					}//no users
35532					
35533					___($data.content);
35534					
35535				} else {
35536					
35537					___('no data');
35538					return false;
35539					
35540				}
35541			} );
35542		
35543	}, //FUNC showUsersInPost
35544	
35545	edit		: function (id, open_drafts = 0) {
35546		
35547		var $this = this;
35548		
35549		//clean up messages
35550		mwState(false, 'wPostEd');
35551		
35552		//clean up icon
35553		jQuery('#accessIcon').css('background-image', "url('/res/blogs/images/block-user.png')");
35554		
35555		$this.postEdit = true;
35556		
35557		$this.init();
35558		
35559		//init access ping script
35560		$this.initAccessPing();
35561		
35562		$this.hidePanel();
35563		
35564		$this.window.maximized = true;
35565		//___($this.window);
35566		$this.window.show();
35567		
35568		//?
35569		mwPostEd.Form.get(0).reset();
35570		
35571		// clear temporary selected tags
35572		mwPostEd.checked_tags = [];
35573		
35574		$this.tiny4_body = jQuery('.mwPostEd_PostContent iframe').off('keyup');
35575		$this.post_title = mwPostEd.Form.find('input[name=title]').off('keyup');
35576		
35577		$this.post_title.off('change').change( function(e) {
35578			
35579			if ( mwPostEd.Form.find('input[name=alias_custom]').val() != '1' )
35580				mwPostEd.Form.find('input[name=alias]').val( $this.post_title.val() )
35581			 
35582		} );
35583		
35584		$this.alias_input = mwPostEd.Form.find('input[name=alias]').off('keyup').keyup( function(e) {
35585			
35586			$this.post_title.off('change');
35587			mwPostEd.Form.find('input[name=alias_custom]').val('1');
35588			$this.checkAliasValid(this);
35589			
35590		} );
35591		
35592		$this.unpublish_start = false;
35593		
35594		jQuery('.publish-on-soc-button').show();
35595		jQuery('.publish-on-soc-success').hide();
35596		//jQuery('#mwPostEd_form .mce-tinymce').height(517);
35597		//jQuery('#mwPostEd_form #tinyPostEditor_ifr').height(453);
35598		jQuery('.socials-custom-wrapper').hide();
35599		
35600		delete mwData.temp_thumb_name; // remove temporary pictures names
35601		delete mwData.temp_banner_name;
35602		
35603		var slider_internal_option = jQuery('#mwPostEd_form select[name=slider_gallery] option#internal');
35604		
35605		//clear error hightlight for tabs controls
35606		mwPostEd.Form.find('.mwWinTabs .error').removeClass('error');
35607		
35608		//for kind select category for new and existing posts - hide categories section and rename tab to TAGS
35609		/*
35610		if ( mwData.kind_category_id ) {
35611			
35612			//$this.Tabs.find('#wPostEdCategories').hide();
35613			$this.TabsControl.find('td[rel=postCategoriesTags]').html('Tags');
35614			//make tags block full width
35615			//$this.Tabs.find('#wPostEdTags').removeClass('cell-even');
35616			
35617		} else {
35618			
35619			//$this.Tabs.find('#wPostEdCategories').show();
35620			$this.TabsControl.find('td[rel=postCategoriesTags]').html('Categories');
35621			//make tags block half width
35622			//$this.Tabs.find('#wPostEdTags').addClass('cell-even');
35623			
35624		}
35625		*/
35626		
35627		if ( !id ) {
35628			
35629			slider_internal_option.hide().val('none');
35630			
35631			mwPostEd.Form.fromArray(this.Defaults);
35632			mwWindow('wPostEd').Title('Add New Post').show(
35633				
35634				function() { mwPostEd.resetTabs(); }
35635				
35636			);
35637			this.ID = 0;
35638			mwPostEd.refreshCategoriesSelect();
35639			mwPostEd.refreshTagsSelect();
35640			mwPostEd.doDemoThumb();
35641			
35642			jQuery('td[rel=postHistory]').hide();
35643			
35644			if ( mwPostEd.live ) {
35645				
35646				var page = [];
35647				page['published'] = 1; // for live Ed new post published by default 
35648				mwPostEd.Form.fromArray(page);
35649				mwPostEd.publishSwitch(1, true);
35650				
35651			} else
35652				mwPostEd.publishSwitch(0);
35653			
35654			mwPostEd.unpublishSwitch();
35655			
35656			mwPostEd.secondarySwitch();
35657			
35658			//for kind select category by default for new post
35659			if ( mwData.kind_category_id ) {
35660				
35661				$this.Form.find('#postEdCategoriesList .item>input[value=' + mwData.kind_category_id + ']').click();
35662				
35663			}
35664			
35665			return false;
35666			
35667		}
35668		
35669		this.ID = id;
35670		
35671		//regualr load from cache
35672		//var page = mwData.Posts[id];
35673		
35674		//try to load from last loaded via ajax post
35675		if ( mwPostEd.lastAjaxPost )
35676			page = mwPostEd.lastAjaxPost
35677			
35678		//___(id, page);
35679		if ( !page ) { //page is not loaded
35680			
35681			mwAjax('/site/ajax/blogs/getPostListDB/' + id + '/0/0', null, true )
35682			.go()
35683			.success( function($data) {
35684				//___($data);
35685				if ( typeof($data.content) != 'undefined' ) {
35686					
35687					mwData.Posts[id] = $data.content[id];
35688					mwPostEd.lastAjaxPost = $data.content[id];
35689					mwPostEd.edit(id, open_drafts);
35690					
35691				} else {
35692					
35693					return false;
35694					
35695				}
35696			} );
35697			
35698			return false;
35699			
35700		}
35701		else { //page is loaded
35702			
35703			//we need to clear last ajax post
35704			mwPostEd.lastAjaxPost = false;
35705			
35706		} //page is loaded
35707		
35708		// set value for internal gallery (if exist)
35709		if ( page.slider_gallery_internal != 0 ) {
35710			
35711			slider_internal_option.show().val(page.slider_gallery_internal);
35712			
35713		} else {
35714			
35715			slider_internal_option.hide().val('none');
35716			
35717		}
35718		
35719		// Resetting form
35720		mwPostEd.Form.fromArray(page);
35721                
35722                // store prev custom values for marge on save - dont lose empty file inputs data
35723		mwPostEd.Form.find('input[name=prev_custom_fields]').val(JSON.stringify(mwData.Posts[id].custom_fields));
35724		//__(JSON.stringify(mwData.Posts[id].custom_fields));
35725                
35726		mwWindow('wPostEd').Title('Edit post (<span>' + page.title + '</span>)').show(
35727			
35728			function() { mwPostEd.resetTabs(id); /*mwWindow('wPostEd').adjustHeights(); mwWindow('wPostEd').align();*/ }
35729			
35730		); // mwWindow
35731		
35732		var show_current_unsaved = function(event) {
35733			
35734			if ( id ) { // dont run for new posts
35735			
35736				mwPostEd.CurrentUnsaved_text = mwPostEd.tiny4_body.contents().find('body').html();
35737				mwPostEd.CurrentUnsaved_title = mwPostEd.post_title.val();
35738				
35739				mwPostEd.doHistoryTab();
35740				
35741				if ( $this.CurrentUnsaved )
35742					if ( mwPostEd.CurrentUnsaved_text != mwData.Posts[id].content || mwPostEd.CurrentUnsaved_title != mwData.Posts[id].title  ) {
35743					
35744						$this.CurrentUnsaved.show();
35745						//$this.CurrentUnsaved.change();
35746						//jQuery('#postHistory div.Item').removeClass('Selected');
35747						//jQuery('#postHistory div.Item:has(input[value=edited])>div').addClass('Selected');
35748						//jQuery('#postHistory div.Item:has(input[value=edited])').mouseup();
35749						
35750					} else {
35751						
35752						$this.CurrentUnsaved.hide();
35753						//$this.LastSaved.show().change();
35754						//jQuery('#postHistory div.Item').removeClass('Selected');
35755						//jQuery('#postHistory div.Item:has(input[value=saved])>div').addClass('Selected');
35756						//jQuery('#postHistory div.Item:has(input[value=saved])').mouseup();
35757						
35758					}
35759			}
35760		};
35761		
35762		jQuery('td[rel=postHistory]').show();
35763		
35764		mwPostEd.CurrentUnsaved_text = mwData.Posts[id].content;
35765		mwPostEd.CurrentUnsaved_title = mwData.Posts[id].title;
35766		
35767		$this.tiny4_body.contents().find('body').keyup(show_current_unsaved);
35768		$this.post_title.keyup(show_current_unsaved);
35769		
35770		mwPostEd.refreshCategoriesSelect(id);
35771		mwPostEd.refreshTagsSelect(id);
35772		mwPostEd.doDemoThumb(id);
35773		mwPostEd.publishSwitch(mwData.Posts[id].published);
35774		
35775		mwPostEd.unpublishSwitch();
35776		
35777		mwPostEd.secondarySwitch();
35778		
35779		//reset counters
35780		$this.meta_title_counter.reset();
35781		$this.meta_description_counter.reset();
35782		
35783		//add href for links with same class as custom field name in custom tabs
35784		if ( typeof page.custom_fields != 'undefined') {
35785			
35786			for(var i in page.custom_fields) {
35787			
35788				mwPostEd.Form.find('#postCustom a.' + i).attr('href', page.custom_fields[i]);
35789			
35790			}
35791			
35792		}//loop custom fields
35793		
35794		//store last opened post data
35795		$this.post_data = page;
35796		
35797		//open drafts tab if requested
35798		if ( open_drafts ) mwPostEd.Form.find('[rel=postHistory]').click();
35799		
35800		return false;
35801		
35802	}, //FUNC edit
35803	
35804	showPanel	: function ($el) {
35805		
35806		var $this = this;
35807		
35808		$this.dom.panel.removeClass('hidden');
35809		
35810		if ( jQuery($el).attr('rel') == 'postHistory' )
35811			mwPostEd.doHistoryTab($this);
35812		
35813		mwSwitchTab($el, false, false);
35814		
35815	}, //FUNC showPanel
35816	
35817	hidePanel	: function () {
35818		
35819		var $this = this;
35820		
35821		$this.dom.panel.addClass('hidden');
35822		
35823		mwSwitchTab($this.dom.hidePanel, false, false);
35824		
35825	}, //FUNC showPanel
35826	
35827	deletePost	: function(id) {
35828		
35829		mwConfirmation( function() {
35830			
35831			var data = {};
35832			
35833			//check is current category exist (if no - there are no category filter, this means its filtered by kind)
35834			var cur_cat = mwCategoryEd.getCurrentFilterCategoryID();
35835			if ( cur_cat ) {
35836				
35837				data.filter_by_category_id = cur_cat;
35838				
35839			} else {
35840				
35841				data.filter_by_category_id = mwData.kind_category_id;
35842				data.kind_category_id = mwData.kind_category_id;
35843				
35844			}
35845			
35846			mwAjax( '/site/ajax/blogs/deletePostDB/' + id, data, 'systemConfirmation' )
35847			.content()
35848			.success( function($data) {
35849				
35850				mwPostEd.manualOrder_sort();
35851				mwWindow('systemConfirmation').hide();
35852				
35853			} );									
35854		}
35855		, 'Delete Post: ' + mwData.Posts[id]['title'] + '?');
35856		
35857	}, //FUNC deletePost
35858	
35859	open_gallery	: function(type, gallery_id) {
35860		
35861		var id = this.ID;
35862		var $this = this;
35863		
35864		var options = {}
35865		var show_selector = false;
35866		
35867		if ( type == 'thumb' || type == 'banner' ) {
35868			
35869			options = {
35870				
35871				'Apply' : {
35872				
35873				'class'  : 'hi',
35874				'action' : function ($file) {
35875							
35876						//__(type, $file);
35877						if ( isEmpty($file) ) return;
35878						
35879						jQuery('input[name=' + type + ']').val($file['id']);
35880						mwData['temp_' + type + '_name'] = $file['base'] + '.' + $file['ext'];
35881						mwPostEd.doDemoThumb(id);
35882						
35883					} //FUNC setActions.apply.action
35884				}
35885			}
35886			
35887			show_selector = true; // need to have selector on thumb/banner choosing
35888			
35889		}
35890		
35891		if ( type == 'og_image' ) {
35892			
35893			options = {
35894				
35895				'Apply' : {
35896				
35897				'class'  : 'Hi',
35898				'action' : function ($file) {
35899							
35900						//___(type, $file);
35901						if ( isEmpty($file) ) return;
35902						
35903						
35904						$this.Form.find('[name=og_image]').val( location.protocol + '//' + location.host + $file.__thumb); 
35905						
35906					} //FUNC setActions.apply.action
35907				}
35908			}
35909			
35910			show_selector = true;
35911			
35912		}
35913		
35914		//file id false by default
35915		var file_id = false;
35916		
35917		if ( !jQuery.isNumeric(gallery_id) ) {
35918			
35919			//for new post there are no post data
35920			if ( typeof $this.post_data == 'undefined' ) {
35921				
35922				gallery_id = false;
35923				
35924			}//new post
35925			else {//existing post
35926				
35927				//use internal post gallery as default
35928				gallery_id = $this.post_data.slider_gallery_internal;
35929				
35930			}//existing post
35931			
35932		}
35933		
35934		//not new post
35935		//post already have some file selected in some gallery
35936		if ( typeof $this.post_data != 'undefined' && jQuery.isNumeric($this.post_data.thumb) ) {
35937			
35938			//clean up gallery id - let gallery to find itself
35939			gallery_id = false;
35940			
35941			//provide gallery with selected file id
35942			var file_id = $this.post_data.thumb
35943			
35944		}
35945		
35946		//___('show_selector', show_selector);
35947		mwFilesEd.galleriesSelector = show_selector;
35948		mwFilesEd
35949		.setActions( options ) //OBJECT mwFilesEd.setActions.actions
35950		.load(gallery_id, file_id);
35951		//___('mwFilesEd.galleriesSelector', mwFilesEd.galleriesSelector);
35952	}, //FUNC open_gallery
35953	
35954	open_selected_gallery	: function() {
35955		
35956		var gallery_id = jQuery('select[name=slider_gallery]').val();
35957		
35958		this.open_gallery('', gallery_id);
35959		
35960	}, //FUNC open_selected_gallery
35961	
35962	savePost	: function () {
35963		
35964		if ( mwData.saveSecureValidation )
35965			
35966			//run save secure validation
35967			mwPostEd.validateSecureSave(mwPostEd.save);
35968			
35969		else {
35970			
35971			mwPostEd.save();
35972			
35973		}
35974			
35975	}, //FUNC savePost
35976	
35977	save	: function (dont_hide) {
35978		
35979		var $this = this;
35980		
35981		//post is closing
35982		$this.postEdit = false;
35983		___('save', $this.postEdit);
35984		mwPostEd.tags_temp = mwData.Tags;
35985		delete mwData.Tags;
35986		if ( mwPostEd.ID ) {
35987			
35988			delete mwData.Posts[mwPostEd.ID].tag_selected;
35989			mwPostEd.post_tags_temp = mwData.Posts[mwPostEd.ID].tag_selected;
35990		
35991		}
35992		
35993		if (mwData.Archive && mwData.Archive[mwPostEd.ID] === null) delete mwData.Archive[mwPostEd.ID];
35994		
35995		//check is current category exist (if no - there are no category filter, this means its filtered by kind)
35996		var cur_cat = mwCategoryEd.getCurrentFilterCategoryID();
35997		if ( cur_cat ) {
35998			
35999			mwPostEd.Form.find('input[name=filter_by_category_id]').val( cur_cat );
36000			
36001		} else {
36002			
36003			mwPostEd.Form.find('input[name=filter_by_category_id]').val( mwData.kind_category_id );
36004			mwPostEd.Form.find('input[name=kind_category_id]').val( mwData.kind_category_id );
36005			
36006		}
36007		
36008		var alias_new = jQuery('#mwPostEd_form').find('input[name=alias]').val();
36009		
36010		//clear error hightlight for tabs controls
36011		mwPostEd.Form.find('.mwWinTabs .error').removeClass('error');
36012		
36013		___('save start');
36014		mwAjax('/site/ajax/blogs/savePostDB', '#mwPostEd_form', 'wPostEd')
36015			.go()
36016			.success( function($data) {
36017				___('save success');
36018				if ( mwPostEd.live ) {
36019					
36020					var uri = window.location.pathname.split('/');
36021					var alias_old = uri[uri.length - 1];
36022					
36023					if ( alias_old == alias_new ) {
36024						
36025						mwPostEd.liveCallback($data.block);
36026						mwWindow('wPostEd').hide();
36027						
36028					} else {
36029						
36030						//___(alias_old, alias_new, window.location);
36031						var pre_slash = '/';
36032						if ( !uri[0] ) pre_slash = '';
36033						uri[uri.length - 1] = alias_new;
36034						var path = uri.join('/');
36035						var new_url = window.location.origin + pre_slash + path;
36036						window.location = new_url;
36037						
36038					}
36039					
36040				} else {
36041
36042					//if ( $data.fb_publishing == 'fail' ) mwState( mwError('FB publishing fail!')); 
36043					
36044					if ( !dont_hide ) {
36045						
36046						mwPostEd.resetTabs();
36047						mwWindow('wPostEd').hide();
36048						
36049					} else {
36050						
36051						//jQuery('.publish-on-soc-button').hide();
36052						jQuery('.publish-on-soc-success').show();
36053						setTimeout( function() {mwState( mwSuccess('Social Posting Complete'), 'wPostEd' )}, 10);
36054						
36055					}
36056					
36057					mwPostEd.manualOrder_sort();
36058					
36059				}
36060			} )
36061			.error( function($data) {
36062			
36063				//___('save error', $data);
36064				
36065				mwData.Tags = mwPostEd.tags_temp;
36066				if ( mwPostEd.ID )
36067					mwData.Posts[mwPostEd.ID].tag_selected = mwPostEd.post_tags_temp;
36068				
36069				//check is data contain validation and post editor form
36070				if ( typeof $data._validations != 'undefined' && typeof $data._validations.mwPostEd_form != 'undefined' ) {
36071					
36072					//loop all validation items and highlight tabs
36073					for( var i in $data._validations.mwPostEd_form ) {
36074						
36075						//get the input tab id
36076						var tab_id = jQuery(mwPostEd.Form.find('[name=' + i + ']')).parents('.tabs-container').attr('id');
36077						
36078						//check is current input in some tab
36079						if ( tab_id != undefined ) {
36080							
36081							//add error class to highlight tab with validation error input
36082							mwPostEd.Form.find('.mwWinTabs [rel=' + tab_id + ']').addClass('error');
36083							
36084						}
36085					}
36086				}
36087				
36088			} )
36089		; // mwAjax
36090		___('save end');
36091	}, //FUNC save
36092	
36093	saveDraft	: function () {
36094		
36095		if ( mwData.saveSecureValidation )
36096			
36097			//run save secure validation
36098			mwPostEd.validateSecureSave(mwPostEd.saveDraft_origin);
36099			
36100		else {
36101			
36102			mwPostEd.saveDraft_origin();
36103			
36104		}
36105			
36106	}, //FUNC saveDraft
36107	
36108	saveDraft_origin : function () {
36109	
36110		//jQuery('#mwPostEd_form .published_now input').click();
36111		mwPostEd.Form.find('[name="publish_button"]').remove();
36112		mwPostEd.save(false);
36113		mwWindow('systemConfirmation').hide();
36114			
36115	}, //FUNC saveDraft
36116	
36117	PublishNow	: function (dont_hide) {
36118		
36119		if ( mwData.saveSecureValidation )
36120			
36121			//run save secure validation
36122			mwPostEd.validateSecureSave(mwPostEd.PublishNow_origin);
36123			
36124		else {
36125			
36126			mwPostEd.PublishNow_origin();
36127			
36128		}
36129		
36130	}, //FUNC PublishNow
36131	
36132	PublishNow_origin : function (dont_hide) {
36133		
36134		//jQuery('#mwPostEd_form .published_now input').click();
36135		mwPostEd.Form.append('<input type="hidden" name="publish_button" value="1"/>');
36136		mwPostEd.save(dont_hide);
36137		mwWindow('systemConfirmation').hide();
36138		
36139	}, //FUNC PublishNow
36140	
36141	validateSecureSave : function (callback) {
36142		
36143		var post_id = this.ID;
36144		
36145		//validate only existing post
36146		if ( post_id ) {
36147			
36148			___('validateSecureSave - post id: ' + post_id);
36149			
36150			mwAjax('/site/ajax/blogs/checkSaveSecureValidation/' + post_id, null, true )
36151				.go()
36152				.success( function($data) {
36153					//___($data);
36154					if ( typeof($data.content) != 'undefined' ) {
36155						
36156						//var current_data = $data.content[post_id];
36157						var current_data = $data.content;
36158						___('current_data', current_data.date, current_data.user_id);
36159						___('post data', mwData.Posts[post_id].date, mwData.Posts[post_id].user_id);
36160						
36161						//post saved by other user - warning!
36162						if ( current_data.date != mwData.Posts[post_id].date ) {
36163							
36164							___('post saved by other user - warning!');
36165							
36166							var $new_tab = '<a href="/site/blogs/" onclick="document.getElementById(\'open_post_in_new_tab\').submit(); return false;">view the latest saved post in a new tab.</a>';
36167							$new_tab += '<form target="_blank" action="/site/blogs/" method="post" id="open_post_in_new_tab"><input type="hidden" name="show_post_id" value="' + post_id + '"/></form>';
36168							
36169							mwConfirmation( function() { callback(); }
36170							//, ' Post Saved by other User at ' + current_data.date + ' (' + current_data.login + '|' + current_data.email + '): you may overwrite content! Proceed?');
36171							, ' Caution! The post was recently modified by ' + current_data.login + '|' + current_data.email + ' on '  + current_data.date +  '. If you continue, your changes will overwrite theirs. Click save to overwrite or ' + $new_tab);
36172							
36173						}
36174						else {//post safe to save
36175							
36176							callback();
36177							
36178						}
36179						
36180					} else {
36181						
36182						return false;
36183						
36184					}
36185					
36186				} );
36187				
36188		}//existing post
36189		else {//new post - ignore validation
36190		
36191			___('validateSecureSave - new post - ignore!');
36192			callback();
36193			
36194		}//new post
36195		
36196	}, //FUNC validateSecureSave
36197	
36198	closePost	: function() {
36199		
36200		var $this = this;
36201		
36202		//post is closing
36203		$this.postEdit = false;
36204		
36205	}, //FUNC closePost
36206	
36207	UnPublishNow	: function () {
36208		
36209		this.unpublish_start = true;
36210		mwPostEd.publishSwitch(0);
36211		mwPostEd.Form.find('.unpublished_now input').click();
36212		
36213	}, //FUNC UnPublishNow
36214	
36215	resetTabs	: function (id) {
36216		
36217		return false;
36218		
36219		/*
36220		this.tabsWidth = this.TabsControl.width();
36221		this.contentWidth = jQuery('#mwPostEd_toolsLeft').width();
36222		this.basePadding = this.TabsControl.outerWidth() - this.tabsWidth;
36223		___(this.tabsWidth, this.contentWidth, this.basePadding);
36224		
36225		mwSwitchTab(mwPostEd.hideTabsButton);
36226		// Total width: content min width (with scroll):700 + tabs:450
36227		mwPostEd.PostContent.css('margin-right', '0px').width( this.tabsWidth + this.contentWidth + this.basePadding * 1.5 ); //1145 1079
36228		mwPostEd.Tabs.width(0);
36229		//mwPostEd.hideTabsButton.hide();
36230		
36231		//mwWindow('wPostEd').Body.width(mwWindow('wPostEd').Body.width());
36232		*/
36233		
36234	}, //FUNC resetTabs
36235	
36236	refreshCategoriesSelect	: function (id) {
36237		
36238		var $sel = jQuery('.name-categories_list');
36239		
36240		//there are no categories list for kind posts
36241		if ( $sel.length > 0 ) {
36242			
36243			$o = {};
36244			$k = {};
36245			for ( var $i in mwData.Categories ) {
36246				
36247				if ( mwData.nav_list[ mwData.Categories[$i]['title'].toLowerCase() ] != undefined ) {
36248					
36249					$k[ mwData.Categories[$i].id ] = '* ' + mwData.Categories[$i].title;
36250			
36251				}
36252				else {
36253					$o[ mwData.Categories[$i].id ] = mwData.Categories[$i].title;
36254				}
36255			}
36256			
36257			$selected = null;
36258			if ( id ) $selected = mwData.Posts[id].category_selected
36259			
36260			//add 'id' prefix to selected list
36261			if ( $selected != null )
36262				for(var $i in $selected) $selected[$i] = 'id' + $selected[$i];
36263			
36264			//loop selected to move on top
36265			for (var $i in $selected) {
36266				
36267				//ignore some trash categories
36268				if ( typeof($o[$selected[$i]]) != 'undefined' ) {
36269					
36270					//add selected item to list
36271					$k[$selected[$i]] = $o[$selected[$i]];
36272					
36273					//remove selected item from list
36274					delete($o[$selected[$i]]);
36275					
36276				}//real category
36277				
36278			}//loop selected
36279			
36280			//apply all items to list
36281			for (var $i in $o) $k[$i] = $o[$i];
36282			
36283			//generate select ordered by title
36284			//___($sel, $o, $selected, $k, jQuery.extend({}, $k, $o));
36285			mwUpdateSelector($sel, $k, $selected);
36286			
36287			//remove id prefix from values
36288			jQuery('.name-categories_list .item input').each(function(i){
36289				
36290				var el = jQuery(this);
36291				el.val(el.val().substring(2));			
36292												
36293			});
36294									
36295			mwCategoryEd.doFilterCategories( mwCategoryEd.catInputPostEd, jQuery('#postEdCategoriesList .Item'), '.title' ); // add filter
36296			mwCategoryEd.catInputPostEd.keyup( function() { mwState(false, 'wPostEd'); jQuery('.name-categoriesFilterPostEd').removeClass('error'); } )
36297			
36298		}
36299		
36300	}, //FUNC refreshCategoriesSelect
36301	
36302	refreshTagsSelect	: function (id, selected) {
36303		
36304		/*
36305		$o = {};
36306		for ( var $i in mwData.Tags )
36307			$o[$i] = mwData.Tags[$i].title;
36308		
36309		$selected = null;
36310		if ( id ) $selected = mwData.Posts[id].tag_selected
36311		
36312		var $sel = jQuery('.name-tags_list');
36313		mwUpdateSelector($sel, $o, $selected);
36314		
36315		mwCategoryEd.doFilterCategories( mwPostEd.TagTitleInput, jQuery('#postEdTagsList .Item'), '.title' ); // add filter
36316		mwPostEd.TagTitleInput.keyup( function() { mwState(false, 'wPostEd'); jQuery('.name-tagsFilterPostEd').removeClass('error'); } )
36317		*/
36318		
36319		$selected = null;
36320		
36321		$o = {};
36322		
36323		if ( mwData.ShowAllTagsInPostEd ) {
36324			for ( var $i in mwData.Tags )
36325				$o[ mwData.Tags[$i].id ] = mwData.Tags[$i].title;
36326			
36327		}
36328		
36329		if ( id ) {
36330			//___(selected);
36331			if ( typeof(selected) != 'undefined' && selected.length > 0 )
36332				$selected = selected;
36333			else {
36334				
36335				$selected = mwData.Posts[id].tag_selected;
36336				//add 'id' prefix to selected list
36337				for(var $i in $selected) $selected[$i] = 'id' + $selected[$i];
36338				
36339			}
36340			
36341			//___('$selected', $selected);
36342			
36343			//$selected = jQuery.extend($selected, mwData.Posts[id].tag_selected);
36344			if ( typeof $selected[0] != 'undefined' ) //check is 1st element not empty string ""
36345				for ( var $i in $selected ) {
36346					//___($i, $selected[$i], mwData.Tags[$selected[$i]]);
36347					if ( typeof mwData.Tags[$selected[$i]] != 'undefined' )
36348						$o[$selected[$i]] = mwData.Tags[$selected[$i]].title;
36349					
36350				}
36351		}
36352		//___(jQuery('.name-tags_list'), $o, $selected);
36353		
36354		//new list
36355		var $new_o = [];
36356		
36357		//loop selected to move on top
36358		for (var $i in $selected) {
36359			
36360			//ignore some trash categories
36361			if ( typeof($o[$selected[$i]]) != 'undefined' ) {
36362				
36363				//add selected item to list
36364				$new_o[$selected[$i]] = $o[$selected[$i]];
36365				
36366				//remove selected item from list
36367				delete($o[$selected[$i]]);
36368				
36369			}//real category
36370			
36371		}//loop selected
36372		
36373		//apply all items to list
36374		for (var $i in $o) $new_o[$i] = $o[$i];
36375		
36376		mwUpdateSelector(jQuery('.name-tags_list'), $new_o, $selected);
36377		
36378		//mwCategoryEd.doFilterCategories( mwPostEd.TagTitleInput, jQuery('#postEdTagsList .Item'), '.title' ); // add filter
36379		//doFilterCategories	: function ( input, elements, searchSelector )
36380		
36381		var delay = (function(){
36382			var timer = 0;
36383			return function(callback, ms){
36384				clearTimeout (timer);
36385				timer = setTimeout(callback, ms);
36386			};
36387		})();
36388		
36389		mwPostEd.TagTitleInput.keyup( function(event) {
36390			
36391			var $this = this;
36392			
36393			delay( function() {
36394				
36395				$selected = [];
36396				//___('before', $selected);
36397				//___(jQuery('.name-tags_list').find(':checked'));
36398				jQuery('.name-tags_list').find(':checked').each(function() {
36399					$selected.push(jQuery(this).val());
36400				});
36401				//___('after', $selected);
36402				
36403				var text = jQuery($this).val();
36404				
36405				if (text.length >= mwData.minTagsFilterLength) {
36406					
36407					//$o_new = jQuery.extend({}, $o);
36408					$o_new = [];
36409					jQuery.each($selected, function(i) {
36410						$o_new[$selected[i]] = mwData.Tags['id' + $selected[i]].title;
36411						//___('selected add', $selected[i], mwData.Tags[$selected[i]].title);
36412					} );
36413					
36414					jQuery.each(mwData.Tags, function(i) {
36415						
36416						var reg = new RegExp(text, "gi");
36417						
36418						if ( reg.test(mwData.Tags[i].title) ) {
36419							
36420							//___('regexp add', i, mwData.Tags[i].title);
36421							$o_new[i] = mwData.Tags[i].title;
36422							
36423						}
36424					} );
36425					//___($o_new);
36426					mwUpdateSelector(jQuery('.name-tags_list'), $o_new, $selected);
36427					
36428				} else {
36429					
36430					$o_new = [];
36431					jQuery.each($selected, function(i) {
36432						$o_new[$selected[i]] = mwData.Tags[$selected[i]].title;
36433					} );
36434					
36435					mwUpdateSelector(jQuery('.name-tags_list'), $o_new, $selected);
36436					
36437				}
36438				
36439				cleanUpIds();
36440				
36441			}, 500);
36442				
36443		} );
36444		
36445		//remove id prefix from values
36446		var cleanUpIds = function() {
36447			
36448			jQuery('.name-tags_list .item input').each(function(i){
36449				
36450				var el = jQuery(this);
36451				var val = el.val();
36452				
36453				//clean up only ids
36454				if ( val.indexOf('id') != -1 )
36455					el.val(val.substring(2));			
36456				
36457			});
36458			 
36459		};//cleanUpIds 
36460		
36461		cleanUpIds();
36462		
36463		mwPostEd.TagTitleInput.keyup( function() { mwState(false, 'wPostEd'); jQuery('.name-tagsFilterPostEd').removeClass('error'); } )
36464		
36465	}, //FUNC refreshTagsSelect
36466	
36467	addTag	: function () {
36468		
36469		var $this = this;
36470		
36471		var data = {};
36472		data.id = 0;
36473		data.title = mwPostEd.TagTitleInput.val();
36474		
36475		var is_valid = true;
36476		
36477		if ( data.title.length == 0 ) {
36478			
36479			is_valid = false;
36480			mwState(mwError('Title is required'), 'wPostEd');
36481			
36482		} else {
36483			
36484			for ( var $i in mwData.Tags )
36485				if ( mwData.Tags[$i].title == data.title ) {
36486					
36487					is_valid = false;
36488					mwState(mwError('Title already exist'), 'wPostEd');
36489					
36490				}
36491		}
36492		
36493		//check is current category exist (if no - there are no category filter, this means its filtered by kind)
36494		var cur_cat = mwCategoryEd.getCurrentFilterCategoryID();
36495		if ( cur_cat ) {
36496			
36497			data.filter_by_category_id = cur_cat;
36498			
36499		} else {
36500			
36501			data.filter_by_category_id = mwData.kind_category_id;
36502			data.kind_category_id = mwData.kind_category_id;
36503			
36504		}
36505		
36506		if ( is_valid ) {
36507			
36508			mwState(false, 'wPostEd');
36509			jQuery('.name-tagsFilterPostEd').removeClass('error');
36510			
36511			mwAjax( '/site/ajax/blogs/saveTagDB', data )
36512				.content()
36513				.success( function() {
36514					
36515					$selected = [];
36516					jQuery('.name-tags_list').find(':checked').each(function() {
36517						$selected.push(jQuery(this).val());
36518					});
36519					
36520					mwPostEd.refreshTagsSelect($this.ID, $selected);
36521					mwPostEd.manualOrder_sort();
36522					mwPostEd.TagTitleInput.keyup();
36523					
36524				} );
36525				
36526		} else {
36527			
36528			jQuery('.name-tagsFilterPostEd').addClass('error');
36529			
36530		}
36531		
36532	}, //FUNC addTag
36533	
36534	publish	: function (id, published, published_date) {
36535		
36536		var phrase_start = 'Publish';
36537		if( !published ) phrase_start = 'Unpublish';
36538		
36539		mwConfirmation( function() {
36540			
36541			var data = {};
36542			
36543			data.id = id;
36544			data.published = published;
36545			if ( published_date ) data.published_date = published_date;
36546			
36547			//check is current category exist (if no - there are no category filter, this means its filtered by kind)
36548			var cur_cat = mwCategoryEd.getCurrentFilterCategoryID();
36549			if ( cur_cat ) {
36550				
36551				data.filter_by_category_id = cur_cat;
36552				
36553			} else {
36554				
36555				data.filter_by_category_id = mwData.kind_category_id;
36556				data.kind_category_id = mwData.kind_category_id;
36557				
36558			}
36559			
36560			mwAjax( '/site/ajax/blogs/doPublish', data, 'systemConfirmation' )
36561			.index('#categoriesListContent, #categoriesTreeContent')
36562			.success( function($data) { mwPostEd.manualOrder_sort(); } )
36563												
36564		}
36565		, phrase_start + ' Post: ' + mwData.Posts[id]['title'] + '?');
36566		
36567	}, //FUNC publish
36568	
36569	publishSwitch	: function (state, dont_hide) {
36570		
36571		var $this = this;
36572		
36573		var publish_tr = mwPostEd.Form.find('.post_publish_tr');
36574                var unpublish_tr = mwPostEd.Form.find('.post_unpublish_tr');
36575                var date_time_tr = mwPostEd.Form.find('.date_time_tr');
36576                var publish_button = jQuery('#publish_button_body');
36577                var save_button = jQuery('#save_button_body');
36578                var draft_button = jQuery('#draft_button_body');
36579		var input_published = mwPostEd.Form.find('input[name=published]');
36580		
36581		if ( state == 2 ) {
36582			
36583			date_time_tr.show();
36584			
36585		} else {
36586			
36587			date_time_tr.hide();
36588			
36589		}
36590		
36591                if ( state > 0 && !dont_hide ) {
36592			
36593			publish_tr.hide();
36594                	unpublish_tr.show();
36595                	date_time_tr.hide();
36596                	
36597                } else {
36598                	
36599                	publish_tr.show();
36600                	unpublish_tr.hide();
36601                	
36602                }
36603		
36604		if ( this.ID ) {
36605			
36606			if ( mwData.Posts[this.ID]['published_date'] != '' )
36607				jQuery('#published_date_span').html('Post Published by Schedule: ' + mwData.Posts[this.ID]['published_date_view']);
36608			else
36609				jQuery('#published_date_span').html('Post Published: ' + mwData.Posts[this.ID]['published_date_min']);
36610		
36611		}
36612		
36613		if ( typeof(mwData.Posts[this.ID]) == 'undefined' || mwData.Posts[this.ID]['published'] == 0 || this.unpublish_start ) {
36614			
36615			$this.dom.submitDraft.show();
36616			$this.dom.submitPublish.show();
36617			
36618			$this.dom.submitSave.hide();
36619			
36620			//publish_button.show();
36621			//draft_button.show();
36622			//save_button.removeClass('hi');
36623			//save_button.html('Save Draft');
36624			input_published.val(0);
36625			
36626		} else {
36627			
36628			$this.dom.submitDraft.hide();
36629			$this.dom.submitPublish.hide();
36630			
36631			$this.dom.submitSave.show();
36632			
36633			//publish_button.hide();
36634			//save_button.addClass('hi');
36635			//save_button.html('Save');
36636			input_published.val(state);
36637			
36638		}
36639		
36640	}, //FUNC publishSwitch
36641	
36642	unpublishSwitch	: function(state) {
36643		//___('unpublishSwitch', 111, state);
36644		var input_unpublish = mwPostEd.Form.find('input[name=unpublish]');
36645		var unpublish_tr = mwPostEd.Form.find('.unpublish_time_tr');
36646		var unpublished_flag = jQuery('input[name=unpublished_flag]');
36647		
36648		if ( typeof(state) == 'undefined' ) {
36649			
36650			state = input_unpublish.val() * 1;
36651			
36652		} else {
36653			
36654			state = input_unpublish.val() * 1;
36655			
36656			if ( state )
36657				state = 0;
36658			else
36659				state = 1;
36660			
36661			input_unpublish.val(state);
36662			
36663			//___('unpublishSwitch', 222, state);
36664			input_unpublish.val(state);
36665			
36666		}
36667		//___('unpublishSwitch', 333, state);
36668		if ( state ) {
36669			
36670			unpublish_tr.show();
36671			
36672		} else {
36673			
36674			unpublish_tr.hide();
36675			
36676		}
36677		
36678		/*
36679		if ( typeof(mwData.Posts[this.ID]) != 'undefined' && mwData.Posts[this.ID]['unpublished_date'] != '' ) {
36680			
36681			unpublish_status.show();
36682			jQuery('#unpublished_date_span').html('Post Unpublished by Schedule: ' + mwData.Posts[this.ID]['unpublished_date_view']);
36683			
36684			
36685		} else {
36686			
36687			unpublish_status.hide();
36688		
36689		}
36690		*/
36691		
36692	}, //FUNC unpublishSwitch
36693	
36694	secondarySwitch	: function(state) {
36695		
36696		setTimeout(function(){
36697			
36698			var tr = mwPostEd.Form.find('.secondary_time_tr');
36699			var input = mwPostEd.Form.find('[name=secondary_flag]');
36700			//___(state, tr, input);
36701			if ( typeof(state) == 'undefined' ) {
36702				
36703				if ( this.ID ) {
36704					
36705					state = mwData.Posts[this.ID]['secondary_flag'];
36706					
36707				} else {
36708					
36709					state = input.parent().find('.checked').length;
36710					
36711				}
36712			}
36713			//___(state);
36714			if ( state ) {
36715				
36716				tr.show();
36717				
36718			} else {
36719				
36720				tr.hide();
36721				
36722			}
36723			
36724		}, 1)
36725		
36726	}, //FUNC secondarySwitch
36727	
36728	doDemoThumb	: function (id) {
36729		
36730		/*if ( !id ) {
36731			
36732			jQuery('#demo_thumb_icon, #demo_banner_icon').hide();
36733			return false;
36734			
36735		} */
36736		
36737		if ( id ) {
36738			
36739			mwData.Posts[id].delete_thumb = false;
36740			mwData.Posts[id].delete_banner = false;
36741		
36742		}
36743		
36744		/*
36745		var del_pic_ajax = function(type) {
36746			
36747			var data = {};
36748			data.id = id;
36749			data.img_type = type;
36750			
36751			mwAjax( '/site/ajax/blogs/deletePicture', data )
36752				.content()
36753				.success( function($response) { mwPostEd.doDemoThumb(id); } );
36754			
36755		};
36756		*/
36757		
36758		var del_pic = function(type) {
36759			
36760			if ( type == 'thumb' ) {
36761				
36762				mwData.Posts[id].delete_thumb = true;
36763				delete mwData.Posts[id].temp_thumb_name;
36764				
36765			}
36766			
36767			if ( type == 'banner' ) {
36768				
36769				mwData.Posts[id].delete_banner = true;
36770				delete mwData.Posts[id].temp_banner_name;
36771				
36772			}
36773			
36774			var img_div = jQuery('#demo_' + type + '_icon');
36775			img_div.slideUp( function() {
36776				
36777				img_div.html('<input type="hidden" name="delete_' + type + '" value=""/>');
36778				
36779		 	} );
36780		 	//reinit_file_input( jQuery('#demo_' + type + '_icon_td') );
36781			
36782		};
36783		
36784		/*
36785		var reinit_file_input = function(name) {
36786			
36787			var wrapper = jQuery('#demo_' + n
36787ame + '_icon_td span');
36788			if ( name == 'banner' ) name = 'header';
36789			wrapper.html('<input type="file" name="' + name + '_icon">');	
36790			styleDialog(wrapper);
36791			
36792		}
36793		*/
36794		
36795		/*
36796		var do_field = function(type, animate) {
36797			
36798			var pic = '';
36799			
36800			if ( type == 'thumb' && mwData.Posts[id].thumb && !mwData.Posts[id].delete_thumb )
36801				var pic = '<img src="/files/galleries/' + mwData.Posts[id].thumb + '">';
36802			
36803			if ( type == 'banner' && mwData.Posts[id].banner && !mwData.Posts[id].delete_banner )
36804				var pic = '<img src="/files/galleries/' + mwData.Posts[id].banner + '">';
36805			
36806			var img_div = jQuery('#demo_' + type + '_icon');
36807			
36808			if ( pic != '' )
36809				img_div.html(pic)
36810					.append('<div class="demo_delete"></div>')
36811					.show()
36812					.find('.demo_delete')
36813					.click( function(event) { del_pic(type); } );
36814			else
36815				img_div.html('').hide();
36816			
36817			//reinit_file_input(type);
36818			
36819		};
36820		*/
36821		
36822		var do_field = function(type) {
36823			
36824			//var pic_start = '<img src="/files/galleries/';
36825			var pic_start = '<img src="';
36826			var pic_end = '">';
36827			var pic = '';
36828			
36829			if ( type == 'thumb' ) {
36830				
36831				
36832				
36833				if ( mwData.temp_thumb_name ) {
36834					
36835					//add files/galleries folder in case images selected from gallery just right now
36836					if (mwData.temp_thumb_name.indexOf('/') == -1  )
36837						pic_start += '/files/galleries/';
36838					
36839					pic = pic_start + mwData.temp_thumb_name + pic_end;
36840					
36841				}
36842				else if ( id && mwData.Posts[id].thumb_file ) {
36843				
36844					pic = pic_start + mwData.Posts[id].thumb_file + pic_end;
36845					
36846				}
36847			
36848			}
36849			
36850			if ( type == 'banner' ) {
36851			
36852				if ( mwData.temp_banner_name ) {
36853					
36854					//add files/galleries folder in case images selected from gallery just right now
36855					if (mwData.temp_banner_name.indexOf('/') == -1  )
36856						pic_start += '/files/galleries/';
36857					
36858					pic = pic_start + mwData.temp_banner_name + pic_end;
36859				
36860				}
36861				else if ( id && mwData.Posts[id].banner_file ) {
36862				
36863					pic = pic_start + mwData.Posts[id].banner_file + pic_end;
36864					
36865				}
36866			
36867			}
36868			
36869			var img_div = jQuery('#demo_' + type + '_icon');
36870			//__(img_div, pic);
36871			if ( pic != '' ) {
36872				img_div.html(pic)
36873					.append('<div class="demo_delete"></div>')
36874					.show();
36875				
36876				jQuery('#demo_' + type + '_icon_td')
36877					.find('.demo_delete')
36878					.click( function(event) { del_pic(type); } );
36879			}
36880			else {
36881				img_div.html('').hide();
36882			}
36883			
36884		};
36885		
36886		do_field('thumb');
36887		do_field('banner');
36888		
36889	}, //FUNC doDemoThumb
36890	
36891	dataSocialsUpload	: function() {
36892		
36893		var form = mwPostEd.Form;
36894		
36895		var data = {};
36896		data.message = mwSocials().removeTags( form.find('textarea[name=content]').val() );
36897		data.picture = window.location.host + '/' + form.find('div#demo_thumb_icon img').attr('src');
36898		data.link = mwData.postPageURL + '/' + this.ID + '/' + mwData.Posts[this.ID].title_url;
36899		data.name = form.find('input[name=title]').val();
36900		data.description = form.find('textarea[name=description]').val();
36901		
36902		return data;
36903		
36904	}, //FUNC dataSocialsUpload
36905	
36906	postOnSocials	: function() {
36907		
36908		mwSocials().publishOnSoc(this.dataSocialsUpload());
36909		
36910	}, //FUNC postOnSocials
36911	
36912	manualOrderInit	: function() {
36913		
36914		jQuery('.sort_td').hide();
36915		
36916		mwPostEd.is_manually_sort = false;
36917		
36918		jQuery('#order_select').change( function(event) {
36919			
36920			var $el		= jQuery(this);
36921			var $val	= $el.val(); 
36922			
36923			// Special case for manual sorting
36924			mwPostEd.is_manually_sort = ($val == 'manual'); 
36925			
36926			// Sorting things
36927			mwPostEd.doSortTable($val); 
36928		} );
36929		
36930		mwPostEd.manualOrder_sort();
36931		
36932	}, //FUNC manualOrderInit
36933	
36934	manualOrder_sort : function() {
36935			
36936		mwPostEd.posts_list = jQuery('.mwIndexTable tbody');
36937		
36938		mwPostEd.posts_list.sortable( {
36939			
36940			handle			: '.manual_sort_handle',
36941			forceHelperSize		: true,
36942			forcePlaceholderSize	: true,
36943			placeholder		: 'Placeholder',
36944			errorClass		: 'PlaceholderErr',
36945			helper			: 'clone',
36946			appendTo		: 'parent',
36947			axis			: 'y',
36948			scroll			: false,
36949			forceHelperSize		: true,
36950			update			: function(event, ui) {
36951				
36952				var data = {};
36953				data.posts_list = mwPostEd.posts_list.sortable( "toArray" );
36954				
36955				mwAjax('/site/ajax/blogs/savePostsOrder', data, false)
36956				.index('#postListContent')
36957				.success( function($data) { mwPostEd.manualOrder_sort(); } )
36958				.error( function() { } )
36959				; // mwAjax
36960				
36961			},
36962			start			: function(event, ui) {
36963				
36964				var tds_helper = ui.helper.find('th, td');
36965				var tds_table = jQuery('.mwIndexTable thead').find('th, td');
36966				
36967				jQuery.each(tds_helper, function(i) {
36968					
36969					jQuery(this).attr('id', 'no_transition_width').width( jQuery(tds_table.get(i)).width() );
36970					
36971				} );
36972			}
36973		} );
36974		
36975		jQuery('#order_select').change();
36976		
36977		if ( typeof mwData.AjaxFilter == "undefined" ) {
36978			
36979			jQuery('#categories_filter').change();
36980			
36981		}
36982		
36983	}, //FUNC manualOrder_sort
36984				
36985	doSortTable	: function (sort_by) {
36986		
36987		sort_by_parts = sort_by.split('_');
36988		//___('sort_by', sort_by, sort_by_parts);
36989		
36990		var rows = mwPostEd.posts_list.find('tr');
36991		
36992		if ( mwPostEd.is_manually_sort ) sort_by = 'manual';
36993		
36994		if ( sort_by == 'default' ) sort_by = 'id';
36995		
36996		if ( sort_by == 'manual' )
36997			jQuery('.manual_sort_handle').show();
36998		else
36999			jQuery('.manual_sort_handle').hide();
37000		
37001		rows.sort( function(a, b){
37002			
37003			var compA = jQuery(a).attr(sort_by_parts[0]);
37004			var compB = jQuery(b).attr(sort_by_parts[0])
37005			
37006			//___(sort_by, compA, compB);
37007			
37008			if ( sort_by_parts[0] != 'alpha' ) {
37009				
37010				compA = parseInt(compA, 10);
37011				compB = parseInt(compB, 10);
37012				
37013				var intRegex = /^\d+$/;
37014				if( ! intRegex.test(compA) ) { compA = 0; }
37015				if( ! intRegex.test(compB) ) { compB = 0; }
37016				
37017				if ( sort_by_parts[1] == 'asc' )
37018					return (compA < compB) ? 1 : (compA > compB) ? -1 : 0;
37019				else
37020					return (compA < compB) ? -1 : (compA > compB) ? 1 : 0;
37021				
37022			} else {
37023				//___(compA, compB, compA.localeCompare(compB));
37024				//return compA.localeCompare(compB);
37025				
37026				compA = compA.toLocaleLowerCase();
37027				compB = compB.toLocaleLowerCase();				
37028				
37029				if ( sort_by_parts[1] == 'asc' )
37030					return (compA < compB) ? -1 : (compA > compB) ? 1 : 0;
37031				else
37032					return (compA < compB) ? 1 : (compA > compB) ? -1 : 0;
37033					
37034			}
37035			
37036			
37037		} );
37038		
37039		jQuery.each( rows, function(i) {
37040			
37041			mwPostEd.posts_list.append(this);
37042			
37043		} );
37044			
37045	}, //FUNC doSortTable
37046	
37047	doSortTableButton : function () {
37048		
37049		var rows = mwPostEd.posts_list.find('tr');
37050		
37051		if ( jQuery('.manual_sort_handle').is(':visible') ) {
37052			
37053			mwPostEd.is_manually_sort = false;
37054			mwPostEd.doSortTable( jQuery('#order_select').val() );
37055			
37056		} else {
37057			
37058			mwPostEd.is_manually_sort = true;
37059			mwPostEd.doSortTable( 'manual' );
37060			
37061		}
37062			
37063	}, //FUNC doSortTableButton
37064	
37065	doHistoryTab : function ($this) {
37066		
37067		var obj = this;
37068		
37069		var doIt = function() {
37070			
37071			//special check - bugfix
37072			//sometimes, rly rare mwData.Posts[obj.ID] doesnt exists
37073			if ( !mwData.Posts || !mwData.Posts[obj.ID] ) return;
37074			
37075			var tiny_content = obj.PostContent.find('textarea[name=content]');
37076			
37077			var post_title = jQuery('input[name=title]');
37078			
37079			var $o = {};
37080			$o['edited'] = 'Current Unsaved';
37081			$o['saved'] = 'Last Saved';
37082			
37083			var selected = 'saved';
37084			if ( mwPostEd.CurrentUnsaved_text != mwData.Posts[obj.ID].content || mwPostEd.CurrentUnsaved_title != mwData.Posts[obj.ID].title )
37085				selected = 'edited';
37086			
37087			for ( var $i in mwData.Archive[obj.ID] )
37088				$o[ 'id' + $i ] = mwData.Archive[obj.ID][$i].create_date + ' (' + mwData.Archive[obj.ID][$i].user_display_name + ')';
37089			
37090			var $sel = jQuery('.name-postEdHistoryList');
37091			mwUpdateSelector($sel, $o, selected);
37092			
37093			obj.CurrentUnsaved = jQuery('div.item:has(input[value=edited])');
37094			obj.LastSaved = jQuery('div.item:has(input[value=saved])');
37095			
37096			if ( selected != 'edited' ) obj.CurrentUnsaved.hide();
37097				
37098			jQuery('.name-postEdHistoryList').click( function(event) {
37099				
37100				var clicked_id = jQuery(event.target).val();
37101				
37102				//___(mwData.Archive[obj.ID], clicked_id, mwData.Archive[obj.ID][clicked_id] )
37103				
37104				if ( clicked_id )
37105					if ( clicked_id == 'saved' ) {
37106						
37107						tiny_content.val(mwData.Posts[obj.ID].content);
37108						post_title.val(mwData.Posts[obj.ID].title);
37109						
37110					} else if (clicked_id == 'edited') {
37111						
37112						tiny_content.val(mwPostEd.CurrentUnsaved_text);
37113						post_title.val(mwPostEd.CurrentUnsaved_title);
37114						
37115					} else {
37116						
37117						tiny_content.val(mwData.Archive[obj.ID][clicked_id.substring(2)].content);
37118						post_title.val(mwData.Archive[obj.ID][clicked_id.substring(2)].title);
37119						
37120					}
37121			});
37122			
37123			//mwSwitchTab($this);
37124			
37125		}
37126		
37127		if ( mwData.saveSecureValidation || !mwData.Archive || ( !mwData.Archive[obj.ID] && mwData.Archive[obj.ID] !== null ) )
37128			mwAjax( '/site/ajax/blogs/getHistorySelect', { 'ID' : obj.ID }, 'wPostEd' )
37129				.content()
37130				.success( function($data) { doIt(); } )
37131				.error( function() { } )
37132		else
37133			doIt();
37134		
37135	}, //FUNC doHistoryTab
37136	
37137	show_hide_soc_customs : function ($this) {
37138		
37139		var soc = jQuery($this).attr('rel');
37140		jQuery('.socials-custom-wrapper.' + soc).toggle(400);
37141			
37142	}, //FUNC show_hide_soc_customs
37143	
37144	isValidChr		: function(str){
37145		
37146		return !/[~`!#$%\^&*@()+=\[\]\\';,/{}\\":<>\?]/g.test(str);
37147		
37148	}, //FUNC isValidChr
37149	
37150	checkAliasValid		: function($this) {
37151		
37152		if ( !mwPostEd.isValidChr(jQuery($this).val()) ) {
37153			
37154			mwState(mwError('"-", "_", "|" are only special character allowed for alias, all other characters will be removed on save'), 'wPostEd');
37155			jQuery('.name-alias').addClass('error');
37156			
37157		} else {
37158			
37159			jQuery('.name-alias').removeClass('error');
37160			mwState(false, 'wPostEd');
37161			
37162		}
37163		
37164	}, //FUNC checkAliasValid
37165	
37166}; //CLASS mwPostEd
37167
37168//---------------------------------------------------------------------------------------------------------------------------------------------------
37169
37170var mwCategoryEd = {
37171	
37172	ID		: 0,		// Current category ID
37173	
37174	LastItem	: 0,		// last used item from list - dom object
37175	
37176	NewCounter	: 0,
37177	
37178	Defaults	: {		// Form defaults for resetting dialog
37179	
37180		id		: 0,
37181		parent_id	: 0,
37182		title		: '',
37183		comment		: '',
37184		is_news		: 0,
37185		no_index	: 0,
37186		no_follow	: 0,
37187		
37188	}, //OBJECT Defaults
37189	
37190	init	: function() {
37191		
37192		mwCategoryEd.updateCategories = {};
37192 // array for updating existing categories on save
37193		mwCategoryEd.deleteCategories = []; // array for delete existing categories on save
37194		mwCategoryEd.newCategories = {}; // array for create new categories on save
37195		mwCategoryEd.ID = 0;
37196		mwCategoryEd.LastItem = 0;
37197		mwCategoryEd.NewCounter = 0;
37198		
37199		mwCategoryEd.form = jQuery('#saveCategory_form');
37200		mwCategoryEd.listContainer = jQuery('.categories_list_container');
37201		mwCategoryEd.catInputPostEd = jQuery('#categoriesFilterPostEd');
37202		
37203		//mwCategoryEd.buildCategoriesList();
37204		mwCategoryEd.buildCategoriesTree();
37205		mwCategoryEd.doCategoriesMainFilter();
37206		mwCategoryEd.doDraggableItems();
37207		mwCategoryEd.doFilterCategoriesTree( jQuery('#categoriesFilter'), jQuery('.categories_list li'), 'a span' );
37208		
37209	}, //FUNC init
37210	
37211	refreshCategoriesList	: function (id) { // this function using with special control (folders-like)
37212		
37213		this.ID = id;
37214		mwAjax( '/site/ajax/blogs/index', { 'categoryID' : this.ID } )
37215			.index('#categoriesListContent, #categoriesTreeContent');
37216		
37217	}, //FUNC refreshCategoriesList
37218	
37219	manageCategories	: function () {
37220		
37221		//this.init();
37222		
37223		//hide special (kind) categories
37224		for(var c in mwData.Categories) {
37225			
37226			if ( mwData.nav_list[ mwData.Categories[c]['title'].toLowerCase() ] != undefined ) {
37227				
37228				mwCategoryEd.listContainer.find('li#' + c).hide();
37229				
37230			}
37231		}
37232		
37233		mwWindow('wManageCategories').show();
37234		
37235		mwCategoryEd.editItemGrant = false; // this flag using for disable/enable change event on form fields
37236		this.form.hide().fromArray(this.Defaults);
37237		
37238		return false;
37239		
37240	}, //FUNC manageCategories
37241	
37242	saveCategoriesEd	: function () {
37243		
37244		var data = {};
37245		
37246		//check is current category exist (if no - there are no category filter, this means its filtered by kind)
37247		var cur_cat = mwCategoryEd.getCurrentFilterCategoryID();
37248		if ( cur_cat ) {
37249			
37250			data.filter_by_category_id = cur_cat;
37251			
37252		} else {
37253			
37254			data.filter_by_category_id = mwData.kind_category_id;
37255			data.kind_category_id = mwData.kind_category_id;
37256			
37257		}
37258		
37259		data.updateCategories = [];
37260		for ( var $i in this.updateCategories )
37261			data.updateCategories.push( this.updateCategories[$i] );
37262		
37263		data.deleteCategories = this.deleteCategories;
37264		
37265		data.newCategories = [];
37266		for ( var $i in this.newCategories )
37267			data.newCategories.push( this.newCategories[$i] );
37268		
37269		if ( mwCategoryEd.isSorted )
37270			data.orderCategories = mwCategoryEd.treeToArray();
37271		
37272		/*
37273		__('update',mwCategoryEd.updateCategories);
37274		__('delete',mwCategoryEd.deleteCategories);
37275		__('new',mwCategoryEd.newCategories);
37276		__('order',mwCategoryEd.orderCategories);
37277		*/
37278		
37279		mwCategoryEd.cat_temp = mwData.Categories;
37280		delete mwData.Categories;
37281		
37282		mwAjax('/site/ajax/blogs/saveCategoryDB', data, 'wManageCategories')
37283			.index('#categoriesListContent, #categoriesTreeContent')
37284			.success( function() {
37285				
37286				//mwCategoryEd.deleteCategories.forEach( function(value) { delete mwData.Categories[value]; } ); // remove all deleted categories from mwData (after succes)
37287				mwPostEd.refreshCategoriesSelect(mwPostEd.ID); // refresh categories list on postEd categories tab
37288				mwCategoryEd.init();
37289				mwPostEd.manualOrder_sort();
37290				
37291				//refresh categories on desktop
37292				mwCategoryEd.doCategoriesMainFilter();
37293				
37294			} )
37295			.error( function() {
37296				
37297				mwData.Categories = mwCategoryEd.cat_temp;
37298				
37299			} )
37300		;
37301		
37302	}, //FUNC save
37303	
37304	addCategory		: function () {
37305		
37306		var cat_data = {};
37307		cat_data.id = 0;
37308		cat_data.parent_id = 0;
37309		cat_data.title = jQuery('#categoriesFilterPostEd').val();
37310		
37311		var is_valid = true;
37312		
37313		if ( cat_data.title.length == 0 ) {
37314			
37315			is_valid = false;
37316			mwState(mwError('Title is required'), 'wPostEd');
37317			
37318		} else {
37319			
37320			for ( var $i in mwData.Categories )
37321				if ( mwData.Categories[$i].title == cat_data.title ) {
37322					
37323					is_valid = false;
37324					mwState(mwError('Title already exist'), 'wPostEd');
37325					
37326				}
37327		}
37328		___(is_valid);
37329		if ( is_valid ) {
37330			
37331			mwState(false, 'wPostEd');
37332			jQuery('.name-categoriesFilterPostEd').removeClass('error');
37333			
37334			var data = {};
37335			data.newCategories = [];
37336			data.newCategories.push( cat_data );
37337			
37338			//check is current category exist (if no - there are no category filter, this means its filtered by kind)
37339			var cur_cat = mwCategoryEd.getCurrentFilterCategoryID();
37340			if ( cur_cat ) {
37341				
37342				data.filter_by_category_id = cur_cat;
37343				
37344			} else {
37345				
37346				data.filter_by_category_id = mwData.kind_category_id;
37347				data.kind_category_id = mwData.kind_category_id;
37348				
37349			}
37350			___(data);
37351			mwAjax( '/site/ajax/blogs/saveCategoryDB', data )
37352				.content()
37353				.success( function() {
37354					
37355					mwPostEd.refreshCategoriesSelect(mwPostEd.ID);
37356					
37357					mwCategoryEd.doCategoriesMainFilter();
37358					
37359				} );
37360				
37361		} else {
37362			
37363			jQuery('.name-categoriesFilterPostEd').addClass('error');
37364			
37365		}
37366		
37367	}, //FUNC addCategory
37368	
37369	treeToArray		: function() {
37370		
37371		var cat_list = [];
37372		
37373		jQuery('.categories_list>ul').find('li[id]').each( function(i) {
37374			
37375			el = jQuery(this);
37376			var id = el.attr('id');
37377			var parent_id = el.parent().parent('li[id]').attr('id');
37378			if ( parent_id == undefined ) parent_id = 0;
37379			
37380			cat_list.push( { id : id, parent_id : parent_id } );
37381			
37382			//__(i + ' | id - ' + id + ' | parent_id - ' + parent_id + ' | ' + el.find('span').html());
37383			
37384		} );
37385		
37386		return cat_list;
37387		
37388	}, //FUNC toArray
37389	
37390	editItem	: function (id, $this) {
37391	//	___('EDIT START', id);
37392		if ( mwCategoryEd.ID != 0 ) // if there was already some edition of items - save changes (fix bug - click on sortable items after editing fields not trigger onchange)
37393			if ( ! mwCategoryEd.saveItem() ) return false; // if cant save - dont let edit new item
37394		
37395		mwCategoryEd.ID = id;
37396		mwCategoryEd.LastItem = $this;
37397		
37398		var page = mwCategoryEd.Defaults;
37399		
37400		id = id.substring(2); // remove "id" symbols
37401	//	___('page Defaults', page);
37402		if ( !isNaN(id-0) ) { // if ID numeric - its existing category, else - new
37403			
37404			id = 'id' + id;
37405			
37406			page = mwCategoryEd.updateCategories[id]; // try check category on update list (mb its already changed)
37407	//		___('!page start', !page, page);
37408			if ( !page ) page = mwData.Categories[id]; // if not changed then try default
37409	//		___('!page', !page, page);
37410			if ( !page ) { throw 'ERROR: wrong ID provided for mwCategoryEd.editItem()'; return false; }
37411	//		___('!page 2', !page, page);
37412		} else {
37413			
37414			id = 'ne' + id;
37415			page = jQuery.extend(true, {}, mwCategoryEd.newCategories[id]);
37416	//		___('else page', page);
37417		}
37418	//	___('final page', page);
37419		mwCategoryEd.form.fadeOut( 'fast', function() {
37420			
37421			setTimeout( function() {
37422			 	
37423			 	mwCategoryEd.editItemGrant = false;
37424			 	mwCategoryEd.form.fromArray(page)
37425				mwCategoryEd.form.find('input[name=title]').focus();
37426				mwCategoryEd.editItemGrant = true;
37427				 
37428			}, 1 ); // set focus on title input
37429			
37430		} ).fadeIn('fast', function() {
37431			
37432			
37433			
37434		} );
37435		jQuery('.categories_list').find('.Selected').removeClass('Selected');
37436		jQuery($this).parent().addClass('Selected');
37437		
37438		return false;
37439		
37440	}, //FUNC editItem
37441	
37442	saveItem	: function () {
37443		
37444		if ( mwCategoryEd.editItemGrant && ! mwCategoryEd.form.is(':animated') ) {
37445			
37446			var item = {};
37447			item.id = this.form.find('input[name=id]').val();
37448			item.parent_id = this.form.find('input[name=parent_id]').val();
37449			item.title = this.form.find('input[name=title]').val();
37450			item.comment = this.form.find('textarea[name=comment]').val();
37451			item.is_news = this.form.find('input[name=is_news]').is(':checked') ? 1 : 0;
37452			item.no_index = this.form.find('input[name=no_index]').is(':checked') ? 1 : 0;
37453			item.no_follow = this.form.find('input[name=no_follow]').is(':checked') ? 1 : 0;
37454			
37455			//___('save item', item);
37456			if ( ! mwCategoryEd.validateItem(item) ) return false;
37457			
37458			item.id = item.id.substring(2); // remove "id" symbols
37459			
37460			if ( !isNaN(item.id-0) ) { // if is numeric - its existing category, else - new
37461				
37462				item.id = 'id' + item.id;
37463				
37464				//if ( item.title == mwData.Categories[item.id].title && item.comment == mwData.Categories[item.id].comment && item.is_news == mwData.Categories[item.id].is_news ) {
37465					
37466				//	if ( mwCategoryEd.updateCategories[item.id] ) delete mwCategoryEd.updateCategories[item.id]; // if category changed back on default - remove it from update list
37467					
37468				//} else {
37469					
37470					mwCategoryEd.updateCategories[item.id] = item;
37471					
37472				//}
37473				
37474			} else {
37475				
37476				item.id = 'ne' + item.id;
37477				if ( mwCategoryEd.newCategories[item.id] ) // protection from click on droping new category
37478					mwCategoryEd.newCategories[item.id] = item;
37479				
37480			}
37481			
37482			jQuery(mwCategoryEd.LastItem).find('span').html(item.title);
37483			
37484		}
37485		
37486		return true;
37487		
37488	}, //FUNC saveItem
37489	
37490	validateItem	: function(item) {
37491		
37492		var is_valid = true;
37493		
37494		if ( item.title.length == 0 ) {
37495			
37496			is_valid = false;
37497			mwState(mwError('Title is required'), 'wManageCategories');
37498			
37499		} else {
37500			
37501			var items = jQuery.extend( {}, mwData.Categories, mwCategoryEd.updateCategories )
37502			
37503			mwCategoryEd.deleteCategories.forEach( function(value) { delete items[value]; } );
37504			
37505			delete items[item.id];
37506			
37507			for ( var $i in items )
37508				if ( items[$i].title == item.title ) {
37509					
37510					is_valid = false;
37511					mwState(mwError('Title already exist'), 'wManageCategories');
37512					
37513				}
37514		}
37515		
37516		if ( is_valid ) {
37517			
37518			mwState(false, 'wManageCategories');
37519			jQuery('#saveCategory_form .name-title').removeClass('error');
37520			
37521		} else {
37522			
37523			jQuery('#saveCategory_form .name-title').addClass('error');
37524			
37525		}
37526		
37527		return is_valid;
37528		
37529	}, //FUNC validateItem
37530	
37531	deleteItem	: function( event, ui ) {
37532		
37533		var el = jQuery(ui.draggable);
37534		
37535		var del_item = function(item) { // take jQuery wrapped item
37536			
37537			var id = item.attr('id');
37538			
37539			id = id.substring(2); // remove "id" symbols
37540			
37541			if ( !isNaN(id-0) ) { // if ID numeric - its existing category, else - new
37542				
37543				id = 'id' + id;
37544				delete mwCategoryEd.updateCategories[id]; // no need update category before delete it
37545				mwCategoryEd.deleteCategories.push( id );
37546				
37547			} else { // new category
37548				
37549				id = 'ne' + id;
37550				delete mwCategoryEd.newCategories[id];	
37551				
37552			}
37553			
37554		};
37555		
37556		mwConfirmation(
37557		
37558			function() {
37559				
37560				if ( el.attr('id') == mwCategoryEd.ID ) { // if delete selected item disable validation and hide inputs
37561					
37562					mwCategoryEd.ID = 0;
37563					mwCategoryEd.form.fadeOut('fast');
37564				
37565				}
37566				
37567				del_item(el);
37568				
37569				el.find('li[id]').each( function() {
37570				
37571					del_item(jQuery(this)); // delete all childrens
37572					 
37573				} );
37574				
37575				mwWindow('systemConfirmation').hide();
37576				el.remove();
37577				
37578				if( jQuery('.categories_list li').size() == 0)
37579					mwCategoryEd.listContainer.addClass('winBgHint');
37580				
37581			},
37582			'Delete Category "' + el.find('span').html() + '" and all child categories?'
37583			
37584		);
37585	}, //FUNC deleteItem
37586	
37587	addItem	: function() {
37588		
37589		mwCategoryEd.NewCounter += 1;
37590		
37591		jQuery('.categories_list #no_items').slideUp();
37592		
37593		var item = jQuery.extend(true, {}, mwCategoryEd.Defaults)
37594		//jQuery.extend(item, mwCategoryEd.Defaults);
37595		item.id = 'new' + mwCategoryEd.NewCounter;
37596		
37597		var el = mwCategoryEd.listContainer.find('.ui-draggable-new-category');
37598		el.removeClass('ui-draggable-new-category')
37599			.addClass('draggable_item')
37600			.attr('id', item.id);
37601		
37602		el = el.find('a')
37603			.click( function() { mwCategoryEd.editItem(item.id, el); } )
37604		;
37605		
37606		el.find('span').html(item.title);
37607		
37608		mwCategoryEd.newCategories[item.id] = item;
37609		
37610		el.click();
37611		
37612	}, //FUNC addItem
37613	
37614	buildCategoriesList	: function () {
37615		
37616		var html = '<div class="winSideTabs winContent thinpads categories_list">';
37617		html += '<ul class="ui-sortable-categories-list">';
37618		
37619		for ( var $i in mwData.Categories ) {
37620			
37621			var item = mwData.Categories[$i];
37622			html += '<li id="' + item.id + '" class="newItem draggable_item" title="' + item.comment + '">';
37623			html += '<a onclick="mwCategoryEd.editItem(\'' + item.id + '\', this);"><span class="Title">' + item.title + '</span></a>';
37624			html += '</li>';
37625			
37626		}
37627		
37628		html += '</ul>';
37629		html += '</div>';
37630		mwCategoryEd.listContainer.html(html);
37631			
37632	}, //FUNC buildCategoriesList
37633	
37634	buildCategoriesTree	: function () {
37635		
37636		var build_childrens = function (id) {
37637			
37638			var html = '';
37639			
37640			for ( var $i in mwData.Categories )
37641				if ( mwData.Categories[$i].parent_id == id ) {
37642					
37643					var item = mwData.Categories[$i];
37644					html += '<li id="' + item.id + '" class="newItem draggable_item dragItem">';
37645					html += '<a onclick="mwCategoryEd.editItem(\'' + item.id + '\', this);"><span class="Title">' + item.title + '</span></a>';
37646					html += build_childrens(item.id);
37647					html += '</li>';
37648					
37649				}
37650			
37651			if ( html != '' ) html = '<ul>' + html + '</ul>';
37652			
37653			return html;
37654			
37655		}
37656		
37657		var html = '<div class="categories_list">';
37658			
37659	 	html_items = build_childrens('id0');
37660	 	//html += '<ul>';
37661	 	//html += '<div id="no_items">No Categories. Drop New Category here.</div>';
37662	 	//___(html_items);
37663	 	if ( html_items == '' )
37664			mwCategoryEd.listContainer.addClass('winBgHint');
37665		else
37666			mwCategoryEd.listContainer.removeClass('winBgHint');
37667	 	
37668	 	if ( html_items == '' )
37669			html += '<ul></ul>';
37670		else
37671			html += html_items;
37672		
37673		//html += '</ul>';
37674		html += '</div>';
37675		mwCategoryEd.listContainer.html(html);
37676		
37677		//jQuery('.categories_list ul:first').append('<div id="no_items">No Categories. Drop New Category here.</div>');
37678		
37679	}, //FUNC buildCategoriesTree
37680	
37681	doCategoriesMainFilter	: function () {
37682		
37683		var html = '<option value="0">
37683Filter</option>';
37684		html += '<option value="-1">no category</option>';
37685		for ( var $i in mwData.Categories ) {
37686			
37687			html +='<option ';
37688			html += 'value="' + mwData.Categories[$i].id + '">' + mwData.Categories[$i].title + '</option>'
37689			
37690			
37691			
37692		}
37693		
37694		var sel = jQuery('#categories_filter');
37695		
37696		sel.html(html);
37697		
37698		//get select parent
37699		var parent = sel.parent()
37700		
37701		//check is parent already generated select (skip this on page init)
37702		if ( parent.hasClass('mwDskSelect') ) {
37703			
37704			//move select to main panel
37705			parent.before(sel);
37706			
37707			//delete old one
37708			parent.remove();
37709			
37710			//regenerate it
37711			mwDskControls.Types['SELECT'](sel);
37712			
37713			//show generated select
37714			jQuery('.mwDskSelect .New').show();
37715			
37716		}
37717		
37718		sel.change(
37719			function(event) {
37720				
37721				var categoryID = jQuery(this).val();
37722				
37723				if ( categoryID.length > 2 )
37724					categoryID = categoryID.substring(2); // remove "id" symbols
37725				
37726				if ( typeof mwData.AjaxFilter !== "undefined" ) {
37727					
37728					mwCategoryEd.doAjaxFilter(categoryID);
37729					
37730				} else {
37731					
37732					if ( categoryID != 0 ) {
37733						
37734						jQuery('.tr_post').hide(); 
37735						jQuery( '.category' + categoryID ).show();
37736						
37737						if ( categoryID == -1 ) jQuery( '[class="tr_post "]' ).show();
37738						
37739					} else {
37740						
37741						jQuery('.tr_post').show();
37742						
37743					}
37744				}
37745			}
37746		);
37747			
37748	}, //FUNC doCategoriesMainFilter
37749	
37750	doAjaxFilter	: function (category_id) {
37751	
37752		//___('doAjaxFilter', category_id);
37753		var temp_posts = mwData.Posts;
37754		delete mwData.Posts;
37755		
37756		mwAjax('/site/ajax/blogs/index', { 'filter_by_category_id' : category_id })
37757			.go()
37758			.success( function($data) {
37759				
37760				mwPostEd.manualOrder_sort();
37761				delete temp_posts;
37762				
37763			} )
37764			.error( function() {
37765				
37766				mwData.Posts = temp_posts;
37767				
37768			} )
37769		; // mwAjax
37770			
37771	}, //FUNC doAjaxFilter
37772	
37773	getCurrentFilterCategoryID	: function () {
37774		
37775		if ( typeof(mwLiveEd) != 'undefined' ) return 0;
37776		
37777		var categoryID = jQuery('#categories_filter').val();
37778		
37779		if ( categoryID && categoryID.length > 2 )
37780			categoryID = categoryID.substring(2); // remove "id" symbols
37781
37782		return categoryID;
37783		
37784	}, //FUNC getCurrentFilterCategoryID
37785	
37786	doDraggableItems	: function () {
37787		
37788		jQuery('.categories_list>ul').nestedSortable( {
37789			
37790			listType		: 'ul',
37791			items			: 'li',
37792			handle			: 'a',
37793
37794			toleranceElement	: '> a',
37795			tolerance		: 'pointer',
37796			tabSize			: 20,
37797
37798			maxLevels		: 0,
37799			disableNesting		: 'no-childs',
37800			
37801			opacity			: 0.5,
37802
37803			forceHelperSize		: true,
37804			forcePlaceholderSize	: true,
37805			placeholder		: 'Placeholder',
37806			errorClass		: 'PlaceholderErr',
37807			
37808			receive			: function( event, ui ) { mwCategoryEd.addItem(); },
37809			update			: function(event, ui) { mwCategoryEd.isSorted = true; },
37810			
37811		} );
37812		
37813		
37814		jQuery('.ui-draggable-new-category').draggable( {
37815			
37816			connectToSortable	: '.categories_list>ul',
37817			helper			: 'clone',
37818			
37819		} )
37820		.disableSelection();
37821		
37822		jQuery('.cat_recycle_droppable' ).droppable( {
37823			
37824			activeClass	: "dragHover",
37825			hoverClass	: "drag",
37826			tolerance	: "pointer",
37827			drop		: mwCategoryEd.deleteItem,
37828			accept		: ".draggable_item",
37829			
37830		} );
37831			
37832	}, //FUNC doDraggableItems
37833	
37834	// input		- jQuery wrapped input for event
37835	// elements		- jQuery wrapped list of elements (for show/hide)
37836	// searchSelector	- jQuery selector for serch into elements filtered text
37837	
37838	doFilterCategories	: function ( input, elements, searchSelector ) {
37839		
37840		input.keyup( function(event) {
37841			
37842			var input = jQuery(this);
37843			
37844			elements.hide().each( function() {
37845				
37846				$this = jQuery(this);
37847				
37848				var reg = new RegExp(input.val(), "gi");
37849				
37850				if ( reg.test($this.find(searchSelector).html()) )
37851					$this.show();
37852					
37853			} )
37854			
37855		} );
37856		
37857	}, //FUNC doFilterCategories
37858	
37859	doFilterCategoriesTree	: function ( input, elements, searchSelector ) {
37860		
37861		input.keyup( function(event) {
37862			
37863			var input = jQuery(this);
37864			
37865			elements.hide().each( function() {
37866				
37867				$this = jQuery(this);
37868				
37869				var reg = new RegExp(input.val(), "gi");
37870				
37871				if ( reg.test($this.find(searchSelector).html()) ) {
37872					
37873					$this.show().parents().filter( function(index) {
37874						
37875						var res = false;
37876						if( jQuery.inArray(this, elements) >= 0 ) res = true; // if in elements list - no need remove
37877						
37878						return res;
37879						
37880					} ).show();
37881				}
37882			} )
37883		} );
37884		
37885	}, //FUNC doFilterCategoriesTree
37886	
37887}; //OBJECT mwCategoryEd
37888
37889var mwBlogsOptionsEd = {
37890	
37891	ID	: 1,	// blog id (future options - possibility save more then 1 blog options)
37892	init	: function() {
37893		
37894	}, //FUNC init
37895	
37896	edit	: function (id) {
37897		
37898		this.ID = id;
37899		var page = mwData.BlogsOptions[id];
37900		if ( !page ) return false;
37901		
37902		page = mwBlogsOptionsEd.add_default_soc_page(page);
37903		
37904		// Resetting form
37905		jQuery('#mwBlogsOptions_form').fromArray(page);
37906		
37907		if ( !this.soc_inited ) {
37908			
37909			this.soc_inited = true;
37910			mwSocials().initConnectButtons(jQuery('#mwBlogsOptions_form tr td div[rel]'));
37911			
37912		}
37913		
37914		mwWindow('wBlogsOptions').Title('Blog Options <span>' + '' + '</span>').show(
37915			
37916			function() {}
37917			
37918		); // mwWindow
37919		
37920		//mwWindow().align()
37921		
37922		return false;
37923		
37924	}, //FUNC edit
37925	
37926	save	: function () {
37927		
37928		mwAjax('/site/ajax/blogs/saveBlogsOptionsDB', '#mwBlogsOptions_form', 'wBlogsOptions')
37929			.index()
37930			.success( function($data) {
37931				
37932				mwPostEd.manualOrder_sort();
37933				
37934			} )
37935		; // mwAjax
37936
37937	}, //FUNC save
37938	
37939	add_default_soc_page : function(page) {
37940		
37941		for (key in mwData.BlogsOptions[1]['publish_socials'])
37942			page['publish_' + key] = mwData.BlogsOptions[1]['publish_socials'][key];
37943		
37944		return page;
37945		
37946	}, //FUNC add_default_soc_page
37947	
37948} // CLASS mwBlogsOptionsEd
37949
37950var mwBlogsAjaxUpload = {
37951	
37952	uploading_doing	: false,
37953	
37954	slideDownSpeed	: 500,
37955	
37956	init		: function(posts_data, type) {
37957		
37958	//	__(type);
37959	//	__(posts_data);
37960		
37961		this.posts_data = posts_data;
37962		
37963		this.uploading_doing = false;
37964		this.load_pos = jQuery("#blogs_posts_list_ajax_position_" + this.posts_data.ID);
37965		this.loader = jQuery("#blogs_list_loader_" + this.posts_data.ID);
37966		
37967		if ( type == 1 ) this.doScrollAjax();
37968		if ( type == 2 ) this.doClickAjax();
37969		
37970	},
37971	
37972	doLoad		: function() {
37973	//	___('posts loading');
37974	//	___(this);
37975		
37976		if ( ! this.uploading_doing && this.posts_data.posts_limit > this.posts_data.posts_counter ) {
37977			
37978			this.uploading_doing = true;
37979			//___('indexAjax', this.posts_data);
37980			mwAjax("/ajax/blogs/indexAjax", this.posts_data)
37981			.start( function () {
37982				mwBlogsAjaxUpload.loader.slideDown();
37983			}) //FUNC mwAjax.success.callback
37984			.stop( function () {
37985			//	mwBlogsAjaxUpload.uploading_doing = false;
37986				setTimeout(function() { mwBlogsAjaxUpload.loader.slideUp(); }, 2000);
37987			}) //FUNC mwAjax.success.callback
37988			.success( function($data) {
37989					
37990					//__($data);
37991					if ( typeof $data.content !== "undefined" ) {
37992						
37993						if ( $data.content.length > 0 ) {
37994							
37995							//setTimeout(function() { mwBlogsAjaxUpload.loader.slideUp(); }, 3000);
37996							
37997							jQuery("<div>" + $data.content + "</div>")
37998								.hide().insertBefore(mwBlogsAjaxUpload.load_pos)
37999								.delay(2000)
38000								.slideDown(mwBlogsAjaxUpload.posts_data.posts_per_once * mwBlogsAjaxUpload.slideDownSpeed, function() {
38001									
38002									mwBlogsAjaxUpload.uploading_doing = false;
38003									jQuery(window).scroll();
38004									
38005								});
38006							
38007							mwBlogsAjaxUpload.posts_data.posts_counter = mwBlogsAjaxUpload.posts_data.posts_counter + mwBlogsAjaxUpload.posts_data.posts_per_once;
38008							
38009						}
38010					}
38011				}
38012			)
38013			.go();
38014			
38015		} else {
38016			
38017			mwBlogsAjaxUpload.uploading_doing = true; // stop uploading if there no more to upload
38018			 
38019		}		
38020	},						
38021	
38022	doScrollAjax	: function() {
38023
38024		jQuery(window).scroll(
38025			function(event) {
38026				
38027				var block = jQuery(".blogs_posts_list_ajax_position").parent();
38028				//___(1, block.offset().top + block.height(), jQuery(window).scrollTop() + jQuery(window).height());
38029				//___(2, jQuery(window).scrollTop() + jQuery(window).height() - block.offset().top - block.height(), jQuery(window).height());
38030				//___('doScrollAjax - block', block);
38031				//if ( jQuery(document).height() - jQuery(window).height() <= jQuery(window).scrollTop() + 250 ) {
38032				if ( block.offset().top + block.height() < jQuery(window).scrollTop() + jQuery(window).height()
38033					&& jQuery(window).scrollTop() + jQuery(window).height() - block.offset().top - block.height() < jQuery(window).height() ) {	
38034					
38035					mwBlogsAjaxUpload.doLoad();
38036					
38037				}
38038				
38039			}
38040		);
38041	},
38042	
38043	doClickAjax	: function() {
38044		
38045		this.load_pos.after('<div class="blogs-show-more" onclick="mwBlogsAjaxUpload.doLoad();">Show More</div>');
38046		
38047	},
38048	
38049} //OBJECT mwBlogsAjaxUpload
38050
38051mwTextLengthCounter = function(input, display){
38052	
38053	//counter object
38054	var counter = {
38055		
38056		min		: 10, //allowed text length minimum
38057		max		: 60, //allowed text length maximum
38058		input		: {}, //jQuery object of input to track text length
38059		display		: {}, //jQuery object of some element to display text length
38060		
38061		init		: function(input, display) {
38062			
38063			var $this = this;
38064			
38065			//expecting selector or jquery element
38066			$this.input = jQuery(input);
38067			
38068			//expecting selector or jquery element
38069			$this.display = jQuery(display); 
38070			
38071			$this.input.keyup(function(){
38072				
38073				var l = $this.input.val().length;
38074				var html = l;
38075				
38076				//length less minimum
38077				if ( l < $this.min ) {
38078					
38079					$this.display.addClass('counter-red');
38080					html += ' (+' + ($this.min - l) + ')';
38081					
38082				}//length more maximum
38083				else if ( l > $this.max ) {
38084					
38085					$this.display.addClass('counter-red');
38086					html += ' (-' + (l - $this.max) + ')';
38087					
38088				}//length match
38089				else {
38090					
38091					$this.display.removeClass('counter-red');
38092					
38093				}//length match range
38094				
38095				$this.display.html(html);
38096				
38097			});
38098			
38099		}, //FUNC init
38100		
38101		reset		: function() {
38102			
38103			var $this = this;
38104			
38105			$this.input.keyup();
38106			
38107		}, //FUNC reset
38108		
38109	}//counter object
38110	
38111	counter.init(input, display);
38112	
38113	//return initialized object
38114	return counter;
38115	
38116}//mwTextLengthCounter
38117
38118jQuery( function () {
38119	
38120	//special function to overwrite in templates
38121	if ( typeof onBlogsPostAliasUpdate == 'undefined' ) onBlogsPostAliasUpdate = function(alias) {};
38122	
38123	if ( typeof mwData != "undefined" ) {
38124		
38125		//mwPostEd.init();
38126		mwCategoryEd.init();
38127		mwPostEd.manualOrderInit();
38128		
38129		//jQuery('#mwPostEd_form #published_date').datepicker();
38130		
38131	}
38132	//load without liveEd
38133	else {
38134		
38135		//prepare socials variable - its only stored once
38136		var socials = '';
38137		var soc_template = '';
38138		
38139		//load on scroll add
38140		if ( jQuery('.update-alias-on-scroll-bot').length > 0 ) {
38141			
38142			var update_alias = function(alias, title) {
38143				//___('alias', alias);
38144				//___('title', title);
38145				var path = location.pathname.split('/');
38146				//___(path);
38147				var url = location.origin;
38148				var current_alias = path[path.length - 1];
38149				//___('current_alias', current_alias);
38150				//update only if current alias is different
38151				if ( current_alias != alias ) {
38152					
38153					for ( var i in path ) {
38154						
38155						if ( path[i] && i < path.length - 1 ) url += '/' + path[i];
38156						
38157					}
38158					
38159					url += '/' + alias;
38160					
38161					//___(url);
38162					//___('update alias', url, alias);
38163					window.history.pushState( title, title, url );
38164					
38165					//call event-function for templates usage
38166					onBlogsPostAliasUpdate(alias);
38167					
38168				}
38169			};
38170			
38171			//special flag to prevent loads while load is on run
38172			var load_in_process = false;
38173			
38174			//loop reload waupoints on timer
38175			var loop_waypoints = function() {
38176				
38177				Waypoint.refreshAll();
38178				setTimeout(loop_waypoints, 1000);
38179				
38180			}
38181			loop_waypoints();
38182			
38183			var addLoadNewPostOnScroll = function(load_el_old) {
38184				
38185				//do nothing if load already on process
38186				if ( load_in_process ) return;
38187				___('run load');
38188				
38189				//set the load process flag
38190				load_in_process = true;
38191				
38192				var load_id = load_el_old.attr('post-id');
38193				var template = load_el_old.attr('post-template');
38194				var categories = JSON.parse(load_el_old.attr('post-categories'));
38195				var order = load_el_old.attr('post-order');
38196				
38197				if ( !socials ) socials = load_el_old.attr('post-socials-list');
38198				if ( !soc_template ) soc_template = load_el_old.attr('post-socials-template');
38199				
38200				mwAjax(
38201					'/ajax/blogs/renderPost/',
38202					{ 	'postID': load_id,
38203						'template': template, 
38204						'PostsCategoriesList': categories, 
38205						'PostsOrder': order, 
38206						'SocialsList': socials, 
38207						'SocialsTemplate' : soc_template
38208					})
38209					.go()
38210					.success( function(response) {
38211						
38212						___('next post load response', response);
38213						
38214						var PostData = response.content.PostData;
38215						
38216						//add new post content
38217						load_el_old.after(response.content.html);
38218						
38219						//no need to load again
38220						load_el_old.attr('post-do-load', 'no');
38221						
38222						//add waypoints
38223						addWaypoints();
38224						
38225						//set the load process flag
38226						load_in_process = false;
38227						
38228					});
38229				
38230			}; //FUNC addLoadNewPostOnScroll
38231			
38232			var addWaypoints = function() {
38233				
38234				jQuery('.update-alias-on-scroll-bot')
38235					.waypoint({
38236						handler: function(direction) {
38237						
38238							var el = jQuery(this.element);
38239							//___(el);
38240							___('scroll: ' + this.triggerPoint + ', direction: ' + direction);
38241							
38242							if (el.attr('post-do-load') == 'yes') {
38243								___('load', el.attr('post-alias'), el);
38244								addLoadNewPostOnScroll(el);
38245								
38246							}
38247							
38248						},
38249						offset: '100%'
38250					});
38251				
38252				jQuery('.update-alias-on-scroll-bot')
38253					.removeClass('update-alias-on-scroll-bot')
38254					.addClass('update-alias-on-scroll-bot-updated')
38255					.waypoint({
38256						handler: function(direction) {
38257						
38258							var el = jQuery(this.element);
38259							
38260							//update alias for current post
38261							if ( direction == 'down' ) {
38262								___('down', el.attr('post-alias'), el);
38263								update_alias(el.attr('post-alias'), el.attr('post-title'));
38264								
38265							}
38266							
38267						},
38268						offset: '100%'
38269					});
38270				
38271				jQuery('.update-alias-on-scroll-up')
38272					.removeClass('update-alias-on-scroll-up')
38273					.addClass('update-alias-on-scroll-up-updated')
38274					.waypoint({
38275						handler: function(direction) {
38276						
38277							var el = jQuery(this.element);
38278							
38279							//update alias for current post
38280							if ( direction == 'up' ) {
38281								___('up', el.attr('post-alias'), el);
38282								update_alias(el.attr('post-alias'), el.attr('post-title'));
38283								
38284							}
38285							
38286						},
38287						offset: '100%'
38288					});
38289				
38290			};
38291			
38292			addWaypoints();
38293			
38294		}
38295	}
38296	
38297});
38298jQuery (function () {
38299	
38300	// setTimeout(function(){
38301	// 	__(mwLiveEd.Widgets);
38302	// },1000);
38303	
38304	
38305/*
38306	if (typeof jQuery('select[name=page_id]') !== 'undefined') {
38307	
38308		var pageSelectorVal = JQuery('select[name=page_id]');
38309		pageSelectorVal.change(function(){
38310
38311		if(pageSelectorVal == '0') {
38312			console.log('555');
38313		}
38314
38315	});*/
38316		
38317	
38318
38319});

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.