PageSourceSearch

https://4agc.com/assets/donors-81589d9f17928929f755516ebcd70a51e0b3b08b782a45244e3480f8a3e0ddf5.js

js 4agc.com collected 2026-09-24 10:06:23 UTC 1,739,337 bytes, 58,714 lines download raw bytes

vendor: 5,201 bytes, lines 1-136
1/**
2 * Copyright (c) 2014-present, Facebook, Inc.
3 *
4 * This source code is licensed under the MIT license found in the
5 * LICENSE file in the root directory of this source tree.
6 */
7
8var runtime = (function (exports) {
9  "use strict";
10
11  var Op = Object.prototype;
12  var hasOwn = Op.hasOwnProperty;
13  var defineProperty = Object.defineProperty || function (obj, key, desc) { obj[key] = desc.value; };
14  var undefined; // More compressible than void 0.
15  var $Symbol = typeof Symbol === "function" ? Symbol : {};
16  var iteratorSymbol = $Symbol.iterator || "@@iterator";
17  var asyncIteratorSymbol = $Symbol.asyncIterator || "@@asyncIterator";
18  var toStringTagSymbol = $Symbol.toStringTag || "@@toStringTag";
19
20  function define(obj, key, value) {
21    Object.defineProperty(obj, key, {
22      value: value,
23      enumerable: true,
24      configurable: true,
25      writable: true
26    });
27    return obj[key];
28  }
29  try {
30    // IE 8 has a broken Object.defineProperty that only works on DOM objects.
31    define({}, "");
32  } catch (err) {
33    define = function(obj, key, value) {
34      return obj[key] = value;
35    };
36  }
37
38  function wrap(innerFn, outerFn, self, tryLocsList) {
39    // If outerFn provided and outerFn.prototype is a Generator, then outerFn.prototype instanceof Generator.
40    var protoGenerator = outerFn && outerFn.prototype instanceof Generator ? outerFn : Generator;
41    var generator = Object.create(protoGenerator.prototype);
42    var context = new Context(tryLocsList || []);
43
44    // The ._invoke method unifies the implementations of the .next,
45    // .throw, and .return methods.
46    defineProperty(generator, "_invoke", { value: makeInvokeMethod(innerFn, self, context) });
47
48    return generator;
49  }
50  exports.wrap = wrap;
51
52  // Try/catch helper to minimize deoptimizations. Returns a completion
53  // record like context.tryEntries[i].completion. This interface could
54  // have been (and was previously) designed to take a closure to be
55  // invoked without arguments, but in all the cases we care about we
56  // already have an existing method we want to call, so there's no need
57  // to create a new function object. We can even get away with assuming
58  // the method takes exactly one argument, since that happens to be true
59  // in every case, so we don't have to touch the arguments object. The
60  // only additional allocation required is the completion record, which
61  // has a stable shape and so hopefully should be cheap to allocate.
62  function tryCatch(fn, obj, arg) {
63    try {
64      return { type: "normal", arg: fn.call(obj, arg) };
65    } catch (err) {
66      return { type: "throw", arg: err };
67    }
68  }
69
70  var GenStateSuspendedStart = "suspendedStart";
71  var GenStateSuspendedYield = "suspendedYield";
72  var GenStateExecuting = "executing";
73  var GenStateCompleted = "completed";
74
75  // Returning this object from the innerFn has the same effect as
76  // breaking out of the dispatch switch statement.
77  var ContinueSentinel = {};
78
79  // Dummy constructor functions that we use as the .constructor and
80  // .constructor.prototype properties for functions that return Generator
81  // objects. For full spec compliance, you may wish to configure your
82  // minifier not to mangle the names of these two functions.
83  function Generator() {}
84  function GeneratorFunction() {}
85  function GeneratorFunctionPrototype() {}
86
87  // This is a polyfill for %IteratorPrototype% for environments that
88  // don't natively support it.
89  var IteratorPrototype = {};
90  define(IteratorPrototype, iteratorSymbol, function () {
91    return this;
92  });
93
94  var getProto = Object.getPrototypeOf;
95  var NativeIteratorPrototype = getProto && getProto(getProto(values([])));
96  if (NativeIteratorPrototype &&
97      NativeIteratorPrototype !== Op &&
98      hasOwn.call(NativeIteratorPrototype, iteratorSymbol)) {
99    // This environment has a native %IteratorPrototype%; use it instead
100    // of the polyfill.
101    IteratorPrototype = NativeIteratorPrototype;
102  }
103
104  var Gp = GeneratorFunctionPrototype.prototype =
105    Generator.prototype = Object.create(IteratorPrototype);
106  GeneratorFunction.prototype = GeneratorFunctionPrototype;
107  defineProperty(Gp, "constructor", { value: GeneratorFunctionPrototype, configurable: true });
108  defineProperty(
109    GeneratorFunctionPrototype,
110    "constructor",
111    { value: GeneratorFunction, configurable: true }
112  );
113  GeneratorFunction.displayName = define(
114    GeneratorFunctionPrototype,
115    toStringTagSymbol,
116    "GeneratorFunction"
117  );
118
119  // Helper for defining the .next, .throw, and .return methods of the
120  // Iterator interface in terms of a single ._invoke method.
121  function defineIteratorMethods(prototype) {
122    ["next", "throw", "return"].forEach(function(method) {
123      define(prototype, method, function(arg) {
124        return this._invoke(method, arg);
125      });
126    });
127  }
128
129  exports.isGeneratorFunction = function(genFun) {
130    var ctor = typeof genFun === "function" && genFun.constructor;
131    return ctor
132      ? ctor === GeneratorFunction ||
133        // For the native GeneratorFunction constructor, the best we can
134        // do is to check its .name property.
135        (ctor.displayName || ctor.name) === "GeneratorFunction"
136      : false;
vendor: 20,105 bytes, lines 137-761
137  };
138
139  exports.mark = function(genFun) {
140    if (Object.setPrototypeOf) {
141      Object.setPrototypeOf(genFun, GeneratorFunctionPrototype);
142    } else {
143      genFun.__proto__ = GeneratorFunctionPrototype;
144      define(genFun, toStringTagSymbol, "GeneratorFunction");
145    }
146    genFun.prototype = Object.create(Gp);
147    return genFun;
148  };
149
150  // Within the body of any async function, `await x` is transformed to
151  // `yield regeneratorRuntime.awrap(x)`, so that the runtime can test
152  // `hasOwn.call(value, "__await")` to determine if the yielded value is
153  // meant to be awaited.
154  exports.awrap = function(arg) {
155    return { __await: arg };
156  };
157
158  function AsyncIterator(generator, PromiseImpl) {
159    function invoke(method, arg, resolve, reject) {
160      var record = tryCatch(generator[method], generator, arg);
161      if (record.type === "throw") {
162        reject(record.arg);
163      } else {
164        var result = record.arg;
165        var value = result.value;
166        if (value &&
167            typeof value === "object" &&
168            hasOwn.call(value, "__await")) {
169          return PromiseImpl.resolve(value.__await).then(function(value) {
170            invoke("next", value, resolve, reject);
171          }, function(err) {
172            invoke("throw", err, resolve, reject);
173          });
174        }
175
176        return PromiseImpl.resolve(value).then(function(unwrapped) {
177          // When a yielded Promise is resolved, its final value becomes
178          // the .value of the Promise<{value,done}> result for the
179          // current iteration.
180          result.value = unwrapped;
181          resolve(result);
182        }, function(error) {
183          // If a rejected Promise was yielded, throw the rejection back
184          // into the async generator function so it can be handled there.
185          return invoke("throw", error, resolve, reject);
186        });
187      }
188    }
189
190    var previousPromise;
191
192    function enqueue(method, arg) {
193      function callInvokeWithMethodAndArg() {
194        return new PromiseImpl(function(resolve, reject) {
195          invoke(method, arg, resolve, reject);
196        });
197      }
198
199      return previousPromise =
200        // If enqueue has been called before, then we want to wait until
201        // all previous Promises have been resolved before calling invoke,
202        // so that results are always delivered in the correct order. If
203        // enqueue has not been called before, then it is important to
204        // call invoke immediately, without waiting on a callback to fire,
205        // so that the async generator function has the opportunity to do
206        // any necessary setup in a predictable way. This predictability
207        // is why the Promise constructor synchronously invokes its
208        // executor callback, and why async functions synchronously
209        // execute code before the first await. Since we implement simple
210        // async functions in terms of async generators, it is especially
211        // important to get this right, even though it requires care.
212        previousPromise ? previousPromise.then(
213          callInvokeWithMethodAndArg,
214          // Avoid propagating failures to Promises returned by later
215          // invocations of the iterator.
216          callInvokeWithMethodAndArg
217        ) : callInvokeWithMethodAndArg();
218    }
219
220    // Define the unified helper method that is used to implement .next,
221    // .throw, and .return (see defineIteratorMethods).
222    defineProperty(this, "_invoke", { value: enqueue });
223  }
224
225  defineIteratorMethods(AsyncIterator.prototype);
226  define(AsyncIterator.prototype, asyncIteratorSymbol, function () {
227    return this;
228  });
229  exports.AsyncIterator = AsyncIterator;
230
231  // Note that simple async functions are implemented on top of
232  // AsyncIterator objects; they just return a Promise for the value of
233  // the final result produced by the iterator.
234  exports.async = function(innerFn, outerFn, self, tryLocsList, PromiseImpl) {
235    if (PromiseImpl === void 0) PromiseImpl = Promise;
236
237    var iter = new AsyncIterator(
238      wrap(innerFn, outerFn, self, tryLocsList),
239      PromiseImpl
240    );
241
242    return exports.isGeneratorFunction(outerFn)
243      ? iter // If outerFn is a generator, return the full iterator.
244      : iter.next().then(function(result) {
245          return result.done ? result.value : iter.next();
246        });
247  };
248
249  function makeInvokeMethod(innerFn, self, context) {
250    var state = GenStateSuspendedStart;
251
252    return function invoke(method, arg) {
253      if (state === GenStateExecuting) {
254        throw new Error("Generator is already running");
255      }
256
257      if (state === GenStateCompleted) {
258        if (method === "throw") {
259          throw arg;
260        }
261
262        // Be forgiving, per GeneratorResume behavior specified since ES2015:
263        // ES2015 spec, step 3: https://262.ecma-international.org/6.0/#sec-generatorresume
264        // Latest spec, step 2: https://tc39.es/ecma262/#sec-generatorresume
265        return doneResult();
266      }
267
268      context.method = method;
269      context.arg = arg;
270
271      while (true) {
272        var delegate = context.delegate;
273        if (delegate) {
274          var delegateResult = maybeInvokeDelegate(delegate, context);
275          if (delegateResult) {
276            if (delegateResult === ContinueSentinel) continue;
277            return delegateResult;
278          }
279        }
280
281        if (context.method === "next") {
282          // Setting context._sent for legacy support of Babel's
283          // function.sent implementation.
284          context.sent = context._sent = context.arg;
285
286        } else if (context.method === "throw") {
287          if (state === GenStateSuspendedStart) {
288            state = GenStateCompleted;
289            throw context.arg;
290          }
291
292          context.dispatchException(context.arg);
293
294        } else if (context.method === "return") {
295          context.abrupt("return", context.arg);
296        }
297
298        state = GenStateExecuting;
299
300        var record = tryCatch(innerFn, self, context);
301        if (record.type === "normal") {
302          // If an exception is thrown from innerFn, we leave state ===
303          // GenStateExecuting and loop back for another invocation.
304          state = context.done
305            ? GenStateCompleted
306            : GenStateSuspendedYield;
307
308          if (record.arg === ContinueSentinel) {
309            continue;
310          }
311
312          return {
313            value: record.arg,
314            done: context.done
315          };
316
317        } else if (record.type === "throw") {
318          state = GenStateCompleted;
319          // Dispatch the exception by looping back around to the
320          // context.dispatchException(context.arg) call above.
321          context.method = "throw";
322          context.arg = record.arg;
323        }
324      }
325    };
326  }
327
328  // Call delegate.iterator[context.method](context.arg) and handle the
329  // result, either by returning a { value, done } result from the
330  // delegate iterator, or by modifying context.method and context.arg,
331  // setting context.delegate to null, and returning the ContinueSentinel.
332  function maybeInvokeDelegate(delegate, context) {
333    var methodName = context.method;
334    var method = delegate.iterator[methodName];
335    if (method === undefined) {
336      // A .throw or .return when the delegate iterator has no .throw
337      // method, or a missing .next method, always terminate the
338      // yield* loop.
339      context.delegate = null;
340
341      // Note: ["return"] must be used for ES3 parsing compatibility.
342      if (methodName === "throw" && delegate.iterator["return"]) {
343        // If the delegate iterator has a return method, give it a
344        // chance to clean up.
345        context.method = "return";
346        context.arg = undefined;
347        maybeInvokeDelegate(delegate, context);
348
349        if (context.method === "throw") {
350          // If maybeInvokeDelegate(context) changed context.method from
351          // "return" to "throw", let that override the TypeError below.
352          return ContinueSentinel;
353        }
354      }
355      if (methodName !== "return") {
356        context.method = "throw";
357        context.arg = new TypeError(
358          "The iterator does not provide a '" + methodName + "' method");
359      }
360
361      return ContinueSentinel;
362    }
363
364    var record = tryCatch(method, delegate.iterator, context.arg);
365
366    if (record.type === "throw") {
367      context.method = "throw";
368      context.arg = record.arg;
369      context.delegate = null;
370      return ContinueSentinel;
371    }
372
373    var info = record.arg;
374
375    if (! info) {
376      context.method = "throw";
377      context.arg = new TypeError("iterator result is not an object");
378      context.delegate = null;
379      return ContinueSentinel;
380    }
381
382    if (info.done) {
383      // Assign the result of the finished delegate to the temporary
384      // variable specified by delegate.resultName (see delegateYield).
385      context[delegate.resultName] = info.value;
386
387      // Resume execution at the desired location (see delegateYield).
388      context.next = delegate.nextLoc;
389
390      // If context.method was "throw" but the delegate handled the
391      // exception, let the outer generator proceed normally. If
392      // context.method was "next", forget context.arg since it has been
393      // "consumed" by the delegate iterator. If context.method was
394      // "return", allow the original .return call to continue in the
395      // outer generator.
396      if (context.method !== "return") {
397        context.method = "next";
398        context.arg = undefined;
399      }
400
401    } else {
402      // Re-yield the result returned by the delegate method.
403      return info;
404    }
405
406    // The delegate iterator is finished, so forget it and continue with
407    // the outer generator.
408    context.delegate = null;
409    return ContinueSentinel;
410  }
411
412  // Define Generator.prototype.{next,throw,return} in terms of the
413  // unified ._invoke helper method.
414  defineIteratorMethods(Gp);
415
416  define(Gp, toStringTagSymbol, "Generator");
417
418  // A Generator should always return itself as the iterator object when the
419  // @@iterator function is called on it. Some browsers' implementations of the
420  // iterator prototype chain incorrectly implement this, causing the Generator
421  // object to not be returned from this call. This ensures that doesn't happen.
422  // See https://github.com/facebook/regenerator/issues/274 for more details.
423  define(Gp, iteratorSymbol, function() {
424    return this;
425  });
426
427  define(Gp, "toString", function() {
428    return "[object Generator]";
429  });
430
431  function pushTryEntry(locs) {
432    var entry = { tryLoc: locs[0] };
433
434    if (1 in locs) {
435      entry.catchLoc = locs[1];
436    }
437
438    if (2 in locs) {
439      entry.finallyLoc = locs[2];
440      entry.afterLoc = locs[3];
441    }
442
443    this.tryEntries.push(entry);
444  }
445
446  function resetTryEntry(entry) {
447    var record = entry.completion || {};
448    record.type = "normal";
449    delete record.arg;
450    entry.completion = record;
451  }
452
453  function Context(tryLocsList) {
454    // The root entry object (effectively a try statement without a catch
455    // or a finally block) gives us a place to store values thrown from
456    // locations where there is no enclosing try statement.
457    this.tryEntries = [{ tryLoc: "root" }];
458    tryLocsList.forEach(pushTryEntry, this);
459    this.reset(true);
460  }
461
462  exports.keys = function(val) {
463    var object = Object(val);
464    var keys = [];
465    for (var key in object) {
466      keys.push(key);
467    }
468    keys.reverse();
469
470    // Rather than returning an object with a next method, we keep
471    // things simple and return the next function itself.
472    return function next() {
473      while (keys.length) {
474        var key = keys.pop();
475        if (key in object) {
476          next.value = key;
477          next.done = false;
478          return next;
479        }
480      }
481
482      // To avoid creating an additional object, we just hang the .value
483      // and .done properties off the next function object itself. This
484      // also ensures that the minifier will not anonymize the function.
485      next.done = true;
486      return next;
487    };
488  };
489
490  function values(iterable) {
491    if (iterable != null) {
492      var iteratorMethod = iterable[iteratorSymbol];
493      if (iteratorMethod) {
494        return iteratorMethod.call(iterable);
495      }
496
497      if (typeof iterable.next === "function") {
498        return iterable;
499      }
500
501      if (!isNaN(iterable.length)) {
502        var i = -1, next = function next() {
503          while (++i < iterable.length) {
504            if (hasOwn.call(iterable, i)) {
505              next.value = iterable[i];
506              next.done = false;
507              return next;
508            }
509          }
510
511          next.value = undefined;
512          next.done = true;
513
514          return next;
515        };
516
517        return next.next = next;
518      }
519    }
520
521    throw new TypeError(typeof iterable + " is not iterable");
522  }
523  exports.values = values;
524
525  function doneResult() {
526    return { value: undefined, done: true };
527  }
528
529  Context.prototype = {
530    constructor: Context,
531
532    reset: function(skipTempReset) {
533      this.prev = 0;
534      this.next = 0;
535      // Resetting context._sent for legacy support of Babel's
536      // function.sent implementation.
537      this.sent = this._sent = undefined;
538      this.done = false;
539      this.delegate = null;
540
541      this.method = "next";
542      this.arg = undefined;
543
544      this.tryEntries.forEach(resetTryEntry);
545
546      if (!skipTempReset) {
547        for (var name in this) {
548          // Not sure about the optimal order of these conditions:
549          if (name.charAt(0) === "t" &&
550              hasOwn.call(this, name) &&
551              !isNaN(+name.slice(1))) {
552            this[name] = undefined;
553          }
554        }
555      }
556    },
557
558    stop: function() {
559      this.done = true;
560
561      var rootEntry = this.tryEntries[0];
562      var rootRecord = rootEntry.completion;
563      if (rootRecord.type === "throw") {
564        throw rootRecord.arg;
565      }
566
567      return this.rval;
568    },
569
570    dispatchException: function(exception) {
571      if (this.done) {
572        throw exception;
573      }
574
575      var context = this;
576      function handle(loc, caught) {
577        record.type = "throw";
578        record.arg = exception;
579        context.next = loc;
580
581        if (caught) {
582          // If the dispatched exception was caught by a catch block,
583          // then let that catch block handle the exception normally.
584          context.method = "next";
585          context.arg = undefined;
586        }
587
588        return !! caught;
589      }
590
591      for (var i = this.tryEntries.length - 1; i >= 0; --i) {
592        var entry = this.tryEntries[i];
593        var record = entry.completion;
594
595        if (entry.tryLoc === "root") {
596          // Exception thrown outside of any try block that could handle
597          // it, so set the completion value of the entire function to
598          // throw the exception.
599          return handle("end");
600        }
601
602        if (entry.tryLoc <= this.prev) {
603          var hasCatch = hasOwn.call(entry, "catchLoc");
604          var hasFinally = hasOwn.call(entry, "finallyLoc");
605
606          if (hasCatch && hasFinally) {
607            if (this.prev < entry.catchLoc) {
608              return handle(entry.catchLoc, true);
609            } else if (this.prev < entry.finallyLoc) {
610              return handle(entry.finallyLoc);
611            }
612
613          } else if (hasCatch) {
614            if (this.prev < entry.catchLoc) {
615              return handle(entry.catchLoc, true);
616            }
617
618          } else if (hasFinally) {
619            if (this.prev < entry.finallyLoc) {
620              return handle(entry.finallyLoc);
621            }
622
623          } else {
624            throw new Error("try statement without catch or finally");
625          }
626        }
627      }
628    },
629
630    abrupt: function(type, arg) {
631      for (var i = this.tryEntries.length - 1; i >= 0; --i) {
632        var entry = this.tryEntries[i];
633        if (entry.tryLoc <= this.prev &&
634            hasOwn.call(entry, "finallyLoc") &&
635            this.prev < entry.finallyLoc) {
636          var finallyEntry = entry;
637          break;
638        }
639      }
640
641      if (finallyEntry &&
642          (type === "break" ||
643           type === "continue") &&
644          finallyEntry.tryLoc <= arg &&
645          arg <= finallyEntry.finallyLoc) {
646        // Ignore the finally entry if control is not jumping to a
647        // location outside the try/catch block.
648        finallyEntry = null;
649      }
650
651      var record = finallyEntry ? finallyEntry.completion : {};
652      record.type = type;
653      record.arg = arg;
654
655      if (finallyEntry) {
656        this.method = "next";
657        this.next = finallyEntry.finallyLoc;
658        return ContinueSentinel;
659      }
660
661      return this.complete(record);
662    },
663
664    complete: function(record, afterLoc) {
665      if (record.type === "throw") {
666        throw record.arg;
667      }
668
669      if (record.type === "break" ||
670          record.type === "continue") {
671        this.next = record.arg;
672      } else if (record.type === "return") {
673        this.rval = this.arg = record.arg;
674        this.method = "return";
675        this.next = "end";
676      } else if (record.type === "normal" && afterLoc) {
677        this.next = afterLoc;
678      }
679
680      return ContinueSentinel;
681    },
682
683    finish: function(finallyLoc) {
684      for (var i = this.tryEntries.length - 1; i >= 0; --i) {
685        var entry = this.tryEntries[i];
686        if (entry.finallyLoc === finallyLoc) {
687          this.complete(entry.completion, entry.afterLoc);
688          resetTryEntry(entry);
689          return ContinueSentinel;
690        }
691      }
692    },
693
694    "catch": function(tryLoc) {
695      for (var i = this.tryEntries.length - 1; i >= 0; --i) {
696        var entry = this.tryEntries[i];
697        if (entry.tryLoc === tryLoc) {
698          var record = entry.completion;
699          if (record.type === "throw") {
700            var thrown = record.arg;
701            resetTryEntry(entry);
702          }
703          return thrown;
704        }
705      }
706
707      // The context.catch method must only be called with a location
708      // argument that corresponds to a known catch block.
709      throw new Error("illegal catch attempt");
710    },
711
712    delegateYield: function(iterable, resultName, nextLoc) {
713      this.delegate = {
714        iterator: values(iterable),
715        resultName: resultName,
716        nextLoc: nextLoc
717      };
718
719      if (this.method === "next") {
720        // Deliberately forget the last sent value so that we don't
721        // accidentally pass it on to the delegate.
722        this.arg = undefined;
723      }
724
725      return ContinueSentinel;
726    }
727  };
728
729  // Regardless of whether this script is executing as a CommonJS module
730  // or not, return the runtime object so that we can declare the variable
731  // regeneratorRuntime in the outer scope, which allows this module to be
732  // injected easily by `bin/regenerator --include-runtime script.js`.
733  return exports;
734
735}(
736  // If this script is executing as a CommonJS module, use module.exports
737  // as the regeneratorRuntime namespace. Otherwise create a new empty
738  // object. Either way, the resulting object will be used to initialize
739  // the regeneratorRuntime variable at the top of this file.
740  typeof module === "object" ? module.exports : {}
741));
742
743try {
744  regeneratorRuntime = runtime;
745} catch (accidentalStrictMode) {
746  // This module should not be running in strict mode, so the above
747  // assignment should always work unless something is misconfigured. Just
748  // in case runtime.js accidentally runs in strict mode, in modern engines
749  // we can explicitly access globalThis. In older engines we can escape
750  // strict mode using a global Function call. This could conceivably fail
751  // if a Content Security Policy forbids using Function, but in that case
752  // the proper solution is to fix the accidental strict mode problem. If
753  // you've misconfigured your bundler to force strict mode and applied a
754  // CSP to forbid Function, and you're not willing to fix either of those
755  // problems, please detail your unique predicament in a GitHub issue.
756  if (typeof globalThis === "object") {
757    globalThis.regeneratorRuntime = runtime;
758  } else {
759    Function("r", "regeneratorRuntime = r")(runtime);
760  }
761}
vendor: 2,117 bytes, lines 761-762
761;
762!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?module.exports=e():"function"==typeof define&&define.amd?define(e):t.ES6Promise=e()}(this,function(){"use strict";function t(t){var e=typeof t;return null!==t&&("object"===e||"function"===e)}function e(t){return"function"==typeof t}function n(t){B=t}function r(t){G=t}function o(){return function(){return process.nextTick(a)}}function i(){return"undefined"!=typeof z?function(){z(a)}:c()}function s(){var t=0,e=new J(a),n=document.createTextNode("");return e.observe(n,{characterData:!0}),function(){n.data=t=++t%2}}function u(){var t=new MessageChannel;return t.port1.onmessage=a,function(){return t.port2.postMessage(0)}}function c(){var t=setTimeout;return function(){return t(a,1)}}function a(){for(var t=0;t<W;t+=2){var e=V[t],n=V[t+1];e(n),V[t]=void 0,V[t+1]=void 0}W=0}function f(){try{var t=Function("return this")().require("vertx");return z=t.runOnLoop||t.runOnContext,i()}catch(e){return c()}}function l(t,e){var n=this,r=new this.constructor(p);void 0===r[Z]&&O(r);var o=n._state;if(o){var i=arguments[o-1];G(function(){return P(o,r,i,n._result)})}else E(n,r,t,e);return r}function h(t){var e=this;if(t&&"object"==typeof t&&t.constructor===e)return t;var n=new e(p);return g(n,t),n}function p(){}function v(){return new TypeError("You cannot resolve a promise with itself")}function d(){return new TypeError("A promises callback cannot return that same promise.")}function _(t){try{return t.then}catch(e){return nt.error=e,nt}}function y(t,e,n,r){try{t.call(e,n,r)}catch(o){return o}}function m(t,e,n){G(function(t){var r=!1,o=y(n,e,function(n){r||(r=!0,e!==n?g(t,n):S(t,n))},function(e){r||(r=!0,j(t,e))},"Settle: "+(t._label||" unknown promise"));!r&&o&&(r=!0,j(t,o))},t)}function b(t,e){e._state===tt?S(t,e._result):e._state===et?j(t,e._result):E(e,void 0,function(e){return g(t,e)},function(e){return j(t,e)})}function w(t,n,r){n.constructor===t.constructor&&r===l&&n.constructor.resolve===h?b(t,n):r===nt?(j(t,nt.error),nt.error=null):void 0===r?S(t,n):e(r)?m(t,n,r):S(t,n)}function g(e,n){e===n?j(e,v()):t(n)?w(e,n,_(n)):S(e,n)}
vendor: 1,569 bytes, line 762
762function A(t){t._onerror&&t._onerror(t._result),T(t)}function S(t,e){t._state===$&&(t._result=e,t._state=tt,0!==t._subscribers.length&&G(T,t))}function j(t,e){t._state===$&&(t._state=et,t._result=e,G(A,t))}function E(t,e,n,r){var o=t._subscribers,i=o.length;t._onerror=null,o[i]=e,o[i+tt]=n,o[i+et]=r,0===i&&t._state&&G(T,t)}function T(t){var e=t._subscribers,n=t._state;if(0!==e.length){for(var r=void 0,o=void 0,i=t._result,s=0;s<e.length;s+=3)r=e[s],o=e[s+n],r?P(n,r,o,i):o(i);t._subscribers.length=0}}function M(t,e){try{return t(e)}catch(n){return nt.error=n,nt}}function P(t,n,r,o){var i=e(r),s=void 0,u=void 0,c=void 0,a=void 0;if(i){if(s=M(r,o),s===nt?(a=!0,u=s.error,s.error=null):c=!0,n===s)return void j(n,d())}else s=o,c=!0;n._state!==$||(i&&c?g(n,s):a?j(n,u):t===tt?S(n,s):t===et&&j(n,s))}function x(t,e){try{e(function(e){g(t,e)},function(e){j(t,e)})}catch(n){j(t,n)}}function C(){return rt++}function O(t){t[Z]=rt++,t._state=void 0,t._result=void 0,t._subscribers=[]}function k(){return new Error("Array Methods must be provided an Array")}function F(t){return new ot(this,t).promise}function Y(t){var e=this;return new e(U(t)?function(n,r){for(var o=t.length,i=0;i<o;i++)e.resolve(t[i]).then(n,r)}:function(t,e){return e(new TypeError("You must pass an array to race."))})}function q(t){var e=this,n=new e(p);return j(n,t),n}function D(){throw new TypeError("You must pass a resolver function as the first argument to the promise constructor")}function K(){throw new TypeError("Failed to construct 'Promise': Please use the 'new' operator, this object c
762onstructor cannot be called as a function.")}function L(){var t=void 0;if("undefined"!=typeof global)t=global;else if("undefined"!=typeof self)t=self;else try{t=Function("return this")()}catch(e){throw new Error("polyfill failed because global object is unavailable in this environment")}var n=t.Promise;if(n){var r=null;try{r=Object.prototype.toString.call(n.resolve())}catch(e){}if("[object Promise]"===r&&!n.cast)return}t.Promise=it}var N=void 0;N=Array.isArray?Array.isArray:function(t){return"[object Array]"===Object.prototype.toString.call(t)};var U=N,W=0,z=void 0,B=void 0,G=function(t,e){V[W]=t,V[W+1]=e,W+=2,2===W&&(B?B(a):X())},H="undefined"!=typeof window?window:void 0,I=H||{},J=I.MutationObserver||I.WebKitMutationObserver,Q="undefined"==typeof self&&"undefined"!=typeof process&&"[object process]"==={}.toString.call(process),R="undefined"!=typeof Uint8ClampedArray&&"undefined"!=typeof importScripts&&"undefined"!=typeof MessageChannel,V=new Array(1e3),X=void 0;X=Q?o():J?s():R?u():void 0===H&&"function"==typeof require?f():c();var Z=Math.random().toString(36).substring(2),$=void 0,tt=1,et=2,nt={error:null},rt=0,ot=function(){function t(t,e){this._instanceConstructor=t,this.promise=new t(p),this.promise[Z]||O(this.promise),U(e)?(this.length=e.length,this._remaining=e.length,this._result=new Array(this.length),0===this.length?S(this.promise,this._result):(this.length=this.length||0,this._enumerate(e),0===this._remaining&&S(this.promise,this._result))):j(this.promise,k())}return t.prototype._enumerate=function(t){for(var e=0;this._state===$&&e<t.length;e++)this._eachEntry(t[e],e)},t.prototype._eachEntry=function(t,e){var n=this._instanceConstructor,r=n.resolve;if(r===h){var o=_(t);if(o===l&&t._state!==$)this._settledAt(t._state,e,t._result);else if("function"!=typeof o)this._remaining--,this._result[e]=t;else if(n===it){var i=new n(p);w(i,t,o),this._willSettleAt(i,e)}else this._willSettleAt(new n(function(e){return e(t)}),e)}else this._willSettleAt(r(t),e)},t.prototype._settledAt=function(t,e,n){var r=this.promise;r._state===$&&(this._remaining--,t===et?j(r,n):this._result[e]=n),0===this._remaining&&S(r,this._result)},t.prototype._willSettleAt=function(t,e){var n=this;E(t,void 0,function(t){return n._settledAt(tt,e,t)},function(t){return n._settledAt(et,e,t)})},t}(),it=function(){function t(e){this[Z]=C(),this._result=this._state=void 0,this._subscribers=[],p!==e&&("function"!=typeof e&&D(),this instanceof t?x(this,e):K())}return t.prototype["catch"]=function(t){return this.then(null,t)},t.prototype["finally"]=function(t){var e=this,n=e.constructor;return e.then(function(e){return n.resolve(t()).then(function(){return e})},function(e){return n.resolve(t()).then(function(){throw e})})},t}();return it.prototype.then=l,it.all=F,it.race=Y,it.resolve=h,it.reject=q,it._setScheduler=n,it._setAsap=r,it._asap=G,it.polyfill=L,it.Promise=it,it.polyfill(),it});
vendor: 87,657 bytes, lines 763-3990
763/*!
764 * jQuery JavaScript Library v3.7.1
765 * https://jquery.com/
766 *
767 * Copyright OpenJS Foundation and other contributors
768 * Released under the MIT license
769 * https://jquery.org/license
770 *
771 * Date: 2023-08-28T13:37Z
772 */
773( function( global, factory ) {
774
775	"use strict";
776
777	if ( typeof module === "object" && typeof module.exports === "object" ) {
778
779		// For CommonJS and CommonJS-like environments where a proper `window`
780		// is present, execute the factory and get jQuery.
781		// For environments that do not have a `window` with a `document`
782		// (such as Node.js), expose a factory as module.exports.
783		// This accentuates the need for the creation of a real `window`.
784		// e.g. var jQuery = require("jquery")(window);
785		// See ticket trac-14549 for more info.
786		module.exports = global.document ?
787			factory( global, true ) :
788			function( w ) {
789				if ( !w.document ) {
790					throw new Error( "jQuery requires a window with a document" );
791				}
792				return factory( w );
793			};
794	} else {
795		factory( global );
796	}
797
798// Pass this if window is not defined yet
799} )( typeof window !== "undefined" ? window : this, function( window, noGlobal ) {
800
801// Edge <= 12 - 13+, Firefox <=18 - 45+, IE 10 - 11, Safari 5.1 - 9+, iOS 6 - 9.1
802// throw exceptions when non-strict code (e.g., ASP.NET 4.5) accesses strict mode
803// arguments.callee.caller (trac-13335). But as of jQuery 3.0 (2016), strict mode should be common
804// enough that all such attempts are guarded in a try block.
805"use strict";
806
807var arr = [];
808
809var getProto = Object.getPrototypeOf;
810
811var slice = arr.slice;
812
813var flat = arr.flat ? function( array ) {
814	return arr.flat.call( array );
815} : function( array ) {
816	return arr.concat.apply( [], array );
817};
818
819
820var push = arr.push;
821
822var indexOf = arr.indexOf;
823
824var class2type = {};
825
826var toString = class2type.toString;
827
828var hasOwn = class2type.hasOwnProperty;
829
830var fnToString = hasOwn.toString;
831
832var ObjectFunctionString = fnToString.call( Object );
833
834var support = {};
835
836var isFunction = function isFunction( obj ) {
837
838		// Support: Chrome <=57, Firefox <=52
839		// In some browsers, typeof returns "function" for HTML <object> elements
840		// (i.e., `typeof document.createElement( "object" ) === "function"`).
841		// We don't want to classify *any* DOM node as a function.
842		// Support: QtWeb <=3.8.5, WebKit <=534.34, wkhtmltopdf tool <=0.12.5
843		// Plus for old WebKit, typeof returns "function" for HTML collections
844		// (e.g., `typeof document.getElementsByTagName("div") === "function"`). (gh-4756)
845		return typeof obj === "function" && typeof obj.nodeType !== "number" &&
846			typeof obj.item !== "function";
847	};
848
849
850var isWindow = function isWindow( obj ) {
851		return obj != null && obj === obj.window;
852	};
853
854
855var document = window.document;
856
857
858
859	var preservedScriptAttributes = {
860		type: true,
861		src: true,
862		nonce: true,
863		noModule: true
864	};
865
866	function DOMEval( code, node, doc ) {
867		doc = doc || document;
868
869		var i, val,
870			script = doc.createElement( "script" );
871
872		script.text = code;
873		if ( node ) {
874			for ( i in preservedScriptAttributes ) {
875
876				// Support: Firefox 64+, Edge 18+
877				// Some browsers don't support the "nonce" property on scripts.
878				// On the other hand, just using `getAttribute` is not enough as
879				// the `nonce` attribute is reset to an empty string whenever it
880				// becomes browsing-context connected.
881				// See https://github.com/whatwg/html/issues/2369
882				// See https://html.spec.whatwg.org/#nonce-attributes
883				// The `node.getAttribute` check was added for the sake of
884				// `jQuery.globalEval` so that it can fake a nonce-containing node
885				// via an object.
886				val = node[ i ] || node.getAttribute && node.getAttribute( i );
887				if ( val ) {
888					script.setAttribute( i, val );
889				}
890			}
891		}
892		doc.head.appendChild( script ).parentNode.removeChild( script );
893	}
894
895
896function toType( obj ) {
897	if ( obj == null ) {
898		return obj + "";
899	}
900
901	// Support: Android <=2.3 only (functionish RegExp)
902	return typeof obj === "object" || typeof obj === "function" ?
903		class2type[ toString.call( obj ) ] || "object" :
904		typeof obj;
905}
906/* global Symbol */
907// Defining this global in .eslintrc.json would create a danger of using the global
908// unguarded in another place, it seems safer to define global only for this module
909
910
911
912var version = "3.7.1",
913
914	rhtmlSuffix = /HTML$/i,
915
916	// Define a local copy of jQuery
917	jQuery = function( selector, context ) {
918
919		// The jQuery object is actually just the init constructor 'enhanced'
920		// Need init if jQuery is called (just allow error to be thrown if not included)
921		return new jQuery.fn.init( selector, context );
922	};
923
924jQuery.fn = jQuery.prototype = {
925
926	// The current version of jQuery being used
927	jquery: version,
928
929	constructor: jQuery,
930
931	// The default length of a jQuery object is 0
932	length: 0,
933
934	toArray: function() {
935		return slice.call( this );
936	},
937
938	// Get the Nth element in the matched element set OR
939	// Get the whole matched element set as a clean array
940	get: function( num ) {
941
942		// Return all the elements in a clean array
943		if ( num == null ) {
944			return slice.call( this );
945		}
946
947		// Return just the one element from the set
948		return num < 0 ? this[ num + this.length ] : this[ num ];
949	},
950
951	// Take an array of elements and push it onto the stack
952	// (returning the new matched element set)
953	pushStack: function( elems ) {
954
955		// Build a new jQuery matched element set
956		var ret = jQuery.merge( this.constructor(), elems );
957
958		// Add the old object onto the stack (as a reference)
959		ret.prevObject = this;
960
961		// Return the newly-formed element set
962		return ret;
963	},
964
965	// Execute a callback for every element in the matched set.
966	each: function( callback ) {
967		return jQuery.each( this, callback );
968	},
969
970	map: function( callback ) {
971		return this.pushStack( jQuery.map( this, function( elem, i ) {
972			return callback.call( elem, i, elem );
973		} ) );
974	},
975
976	slice: function() {
977		return this.pushStack( slice.apply( this, arguments ) );
978	},
979
980	first: function() {
981		return this.eq( 0 );
982	},
983
984	last: function() {
985		return this.eq( -1 );
986	},
987
988	even: function() {
989		return this.pushStack( jQuery.grep( this, function( _elem, i ) {
990			return ( i + 1 ) % 2;
991		} ) );
992	},
993
994	odd: function() {
995		return this.pushStack( jQuery.grep( this, function( _elem, i ) {
996			return i % 2;
997		} ) );
998	},
999
1000	eq: function( i ) {
1001		var len = this.length,
1002			j = +i + ( i < 0 ? len : 0 );
1003		return this.pushStack( j >= 0 && j < len ? [ this[ j ] ] : [] );
1004	},
1005
1006	end: function() {
1007		return this.prevObject || this.constructor();
1008	},
1009
1010	// For internal use only.
1011	// Behaves like an Array's method, not like a jQuery method.
1012	push: push,
1013	sort: arr.sort,
1014	splice: arr.splice
1015};
1016
1017jQuery.extend = jQuery.fn.extend = function() {
1018	var options, name, src, copy, copyIsArray, clone,
1019		target = arguments[ 0 ] || {},
1020		i = 1,
1021		length = arguments.length,
1022		deep = false;
1023
1024	// Handle a deep copy situation
1025	if ( typeof target === "boolean" ) {
1026		deep = target;
1027
1028		// Skip the boolean and the target
1029		target = arguments[ i ] || {};
1030		i++;
1031	}
1032
1033	// Handle case when target is a string or something (possible in deep copy)
1034	if ( typeof target !== "object" && !isFunction( target ) ) {
1035		target = {};
1036	}
1037
1038	// Extend jQuery itself if only one argument is passed
1039	if ( i === length ) {
1040		target = this;
1041		i--;
1042	}
1043
1044	for ( ; i < length; i++ ) {
1045
1046		// Only deal with non-null/undefined values
1047		if ( ( options = arguments[ i ] ) != null ) {
1048
1049			// Extend the base object
1050			for ( name in options ) {
1051				copy = options[ name ];
1052
1053				// Prevent Object.prototype pollution
1054				// Prevent never-ending loop
1055				if ( name === "__proto__" || target === copy ) {
1056					continue;
1057				}
1058
1059				// Recurse if we're merging plain objects or arrays
1060				if ( deep && copy && ( jQuery.isPlainObject( copy ) ||
1061					( copyIsArray = Array.isArray( copy ) ) ) ) {
1062					src = target[ name ];
1063
1064					// Ensure proper type for the source value
1065					if ( copyIsArray && !Array.isArray( src ) ) {
1066						clone = [];
1067					} else if ( !copyIsArray && !jQuery.isPlainObject( src ) ) {
1068						clone = {};
1069					} else {
1070						clone = src;
1071					}
1072					copyIsArray = false;
1073
1074					// Never move original objects, clone them
1075					target[ name ] = jQuery.extend( deep, clone, copy );
1076
1077				// Don't bring in undefined values
1078				} else if ( copy !== undefined ) {
1079					target[ name ] = copy;
1080				}
1081			}
1082		}
1083	}
1084
1085	// Return the modified object
1086	return target;
1087};
1088
1089jQuery.extend( {
1090
1091	// Unique for each copy of jQuery on the page
1092	expando: "jQuery" + ( version + Math.random() ).replace( /\D/g, "" ),
1093
1094	// Assume jQuery is ready without the ready module
1095	isReady: true,
1096
1097	error: function( msg ) {
1098		throw new Error( msg );
1099	},
1100
1101	noop: function() {},
1102
1103	isPlainObject: function( obj ) {
1104		var proto, Ctor;
1105
1106		// Detect obvious negatives
1107		// Use toString instead of jQuery.type to catch host objects
1108		if ( !obj || toString.call( obj ) !== "[object Object]" ) {
1109			return false;
1110		}
1111
1112		proto = getProto( obj );
1113
1114		// Objects with no prototype (e.g., `Object.create( null )`) are plain
1115		if ( !proto ) {
1116			return true;
1117		}
1118
1119		// Objects with prototype are plain iff they were constructed by a global Object function
1120		Ctor = hasOwn.call( proto, "constructor" ) && proto.constructor;
1121		return typeof Ctor === "function" && fnToString.call( Ctor ) === ObjectFunctionString;
1122	},
1123
1124	isEmptyObject: function( obj ) {
1125		var name;
1126
1127		for ( name in obj ) {
1128			return false;
1129		}
1130		return true;
1131	},
1132
1133	// Evaluates a script in a provided context; falls back to the global one
1134	// if not specified.
1135	globalEval: function( code, options, doc ) {
1136		DOMEval( code, { nonce: options && options.nonce }, doc );
1137	},
1138
1139	each: function( obj, callback ) {
1140		var length, i = 0;
1141
1142		if ( isArrayLike( obj ) ) {
1143			length = obj.length;
1144			for ( ; i < length; i++ ) {
1145				if ( callback.call( obj[ i ], i, obj[ i ] ) === false ) {
1146					break;
1147				}
1148			}
1149		} else {
1150			for ( i in obj ) {
1151				if ( callback.call( obj[ i ], i, obj[ i ] ) === false ) {
1152					break;
1153				}
1154			}
1155		}
1156
1157		return obj;
1158	},
1159
1160
1161	// Retrieve the text value of an array of DOM nodes
1162	text: function( elem ) {
1163		var node,
1164			ret = "",
1165			i = 0,
1166			nodeType = elem.nodeType;
1167
1168		if ( !nodeType ) {
1169
1170			// If no nodeType, this is expected to be an array
1171			while ( ( node = elem[ i++ ] ) ) {
1172
1173				// Do not traverse comment nodes
1174				ret += jQuery.text( node );
1175			}
1176		}
1177		if ( nodeType === 1 || nodeType === 11 ) {
1178			return elem.textContent;
1179		}
1180		if ( nodeType === 9 ) {
1181			return elem.documentElement.textContent;
1182		}
1183		if ( nodeType === 3 || nodeType === 4 ) {
1184			return elem.nodeValue;
1185		}
1186
1187		// Do not include comment or processing instruction nodes
1188
1189		return ret;
1190	},
1191
1192	// results is for internal usage only
1193	makeArray: function( arr, results ) {
1194		var ret = results || [];
1195
1196		if ( arr != null ) {
1197			if ( isArrayLike( Object( arr ) ) ) {
1198				jQuery.merge( ret,
1199					typeof arr === "string" ?
1200						[ arr ] : arr
1201				);
1202			} else {
1203				push.call( ret, arr );
1204			}
1205		}
1206
1207		return ret;
1208	},
1209
1210	inArray: function( elem, arr, i ) {
1211		return arr == null ? -1 : indexOf.call( arr, elem, i );
1212	},
1213
1214	isXMLDoc: function( elem ) {
1215		var namespace = elem && elem.namespaceURI,
1216			docElem = elem && ( elem.ownerDocument || elem ).documentElement;
1217
1218		// Assume HTML when documentElement doesn't yet exist, such as inside
1219		// document fragments.
1220		return !rhtmlSuffix.test( namespace || docElem && docElem.nodeName || "HTML" );
1221	},
1222
1223	// Support: Android <=4.0 only, PhantomJS 1 only
1224	// push.apply(_, arraylike) throws on ancient WebKit
1225	merge: function( first, second ) {
1226		var len = +second.length,
1227			j = 0,
1228			i = first.length;
1229
1230		for ( ; j < len; j++ ) {
1231			first[ i++ ] = second[ j ];
1232		}
1233
1234		first.length = i;
1235
1236		return first;
1237	},
1238
1239	grep: function( elems, callback, invert ) {
1240		var callbackInverse,
1241			matches = [],
1242			i = 0,
1243			length = elems.length,
1244			callbackExpect = !invert;
1245
1246		// Go through the array, only saving the items
1247		// that pass the validator function
1248		for ( ; i < length; i++ ) {
1249			callbackInverse = !callback( elems[ i ], i );
1250			if ( callbackInverse !== callbackExpect ) {
1251				matches.push( elems[ i ] );
1252			}
1253		}
1254
1255		return matches;
1256	},
1257
1258	// arg is for internal usage only
1259	map: function( elems, callback, arg ) {
1260		var length, value,
1261			i = 0,
1262			ret = [];
1263
1264		// Go through the array, translating each of the items to their new values
1265		if ( isArrayLike( elems ) ) {
1266			length = elems.length;
1267			for ( ; i < length; i++ ) {
1268				value = callback( elems[ i ], i, arg );
1269
1270				if ( value != null ) {
1271					ret.push( value );
1272				}
1273			}
1274
1275		// Go through every key on the object,
1276		} else {
1277			for ( i in elems ) {
1278				value = callback( elems[ i ], i, arg );
1279
1280				if ( value != null ) {
1281					ret.push( value );
1282				}
1283			}
1284		}
1285
1286		// Flatten any nested arrays
1287		return flat( ret );
1288	},
1289
1290	// A global GUID counter for objects
1291	guid: 1,
1292
1293	// jQuery.support is not used in Core but other projects attach their
1294	// properties to it so it needs to exist.
1295	support: support
1296} );
1297
1298if ( typeof Symbol === "function" ) {
1299	jQuery.fn[ Symbol.iterator ] = arr[ Symbol.iterator ];
1300}
1301
1302// Populate the class2type map
1303jQuery.each( "Boolean Number String Function Array Date RegExp Object Error Symbol".split( " " ),
1304	function( _i, name ) {
1305		class2type[ "[object " + name + "]" ] = name.toLowerCase();
1306	} );
1307
1308function isArrayLike( obj ) {
1309
1310	// Support: real iOS 8.2 only (not reproducible in simulator)
1311	// `in` check used to prevent JIT error (gh-2145)
1312	// hasOwn isn't used here due to false negatives
1313	// regarding Nodelist length in IE
1314	var length = !!obj && "length" in obj && obj.length,
1315		type = toType( obj );
1316
1317	if ( isFunction( obj ) || isWindow( obj ) ) {
1318		return false;
1319	}
1320
1321	return type === "array" || length === 0 ||
1322		typeof length === "number" && length > 0 && ( length - 1 ) in obj;
1323}
1324
1325
1326function nodeName( elem, name ) {
1327
1328	return elem.nodeName && elem.nodeName.toLowerCase() === name.toLowerCase();
1329
1330}
1331var pop = arr.pop;
1332
1333
1334var sort = arr.sort;
1335
1336
1337var splice = arr.splice;
1338
1339
1340var whitespace = "[\\x20\\t\\r\\n\\f]";
1341
1342
1343var rtrimCSS = new RegExp(
1344	"^" + whitespace + "+|((?:^|[^\\\\])(?:\\\\.)*)" + whitespace + "+$",
1345	"g"
1346);
1347
1348
1349
1350
1351// Note: an element does not contain itself
1352jQuery.contains = function( a, b ) {
1353	var bup = b && b.parentNode;
1354
1355	return a === bup || !!( bup && bup.nodeType === 1 && (
1356
1357		// Support: IE 9 - 11+
1358		// IE doesn't have `contains` on SVG.
1359		a.contains ?
1360			a.contains( bup ) :
1361			a.compareDocumentPosition && a.compareDocumentPosition( bup ) & 16
1362	) );
1363};
1364
1365
1366
1367
1368// CSS string/identifier serialization
1369// https://drafts.csswg.org/cssom/#common-serializing-idioms
1370var rcssescape = /([\0-\x1f\x7f]|^-?\d)|^-$|[^\x80-\uFFFF\w-]/g;
1371
1372function fcssescape( ch, asCodePoint ) {
1373	if ( asCodePoint ) {
1374
1375		// U+0000 NULL becomes U+FFFD REPLACEMENT CHARACTER
1376		if ( ch === "\0" ) {
1377			return "\uFFFD";
1378		}
1379
1380		// Control characters and (dependent upon position) numbers get escaped as code points
1381		return ch.slice( 0, -1 ) + "\\" + ch.charCodeAt( ch.length - 1 ).toString( 16 ) + " ";
1382	}
1383
1384	// Other potentially-special ASCII characters get backslash-escaped
1385	return "\\" + ch;
1386}
1387
1388jQuery.escapeSelector = function( sel ) {
1389	return ( sel + "" ).replace( rcssescape, fcssescape );
1390};
1391
1392
1393
1394
1395var preferredDoc = document,
1396	pushNative = push;
1397
1398( function() {
1399
1400var i,
1401	Expr,
1402	outermostContext,
1403	sortInput,
1404	hasDuplicate,
1405	push = pushNative,
1406
1407	// Local document vars
1408	document,
1409	documentElement,
1410	documentIsHTML,
1411	rbuggyQSA,
1412	matches,
1413
1414	// Instance-specific data
1415	expando = jQuery.expando,
1416	dirruns = 0,
1417	done = 0,
1418	classCache = createCache(),
1419	tokenCache = createCache(),
1420	compilerCache = createCache(),
1421	nonnativeSelectorCache = createCache(),
1422	sortOrder = function( a, b ) {
1423		if ( a === b ) {
1424			hasDuplicate = true;
1425		}
1426		return 0;
1427	},
1428
1429	booleans = "checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|" +
1430		"loop|multiple|open|readonly|required|scoped",
1431
1432	// Regular expressions
1433
1434	// https://www.w3.org/TR/css-syntax-3/#ident-token-diagram
1435	identifier = "(?:\\\\[\\da-fA-F]{1,6}" + whitespace +
1436		"?|\\\\[^\\r\\n\\f]|[\\w-]|[^\0-\\x7f])+",
1437
1438	// Attribute selectors: https://www.w3.org/TR/selectors/#attribute-selectors
1439	attributes = "\\[" + whitespace + "*(" + identifier + ")(?:" + whitespace +
1440
1441		// Operator (capture 2)
1442		"*([*^$|!~]?=)" + whitespace +
1443
1444		// "Attribute values must be CSS identifiers [capture 5] or strings [capture 3 or capture 4]"
1445		"*(?:'((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\"|(" + identifier + "))|)" +
1446		whitespace + "*\\]",
1447
1448	pseudos = ":(" + identifier + ")(?:\\((" +
1449
1450		// To reduce the number of selectors needing tokenize in the preFilter, prefer arguments:
1451		// 1. quoted (capture 3; capture 4 or capture 5)
1452		"('((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\")|" +
1453
1454		// 2. simple (capture 6)
1455		"((?:\\\\.|[^\\\\()[\\]]|" + attributes + ")*)|" +
1456
1457		// 3. anything else (capture 2)
1458		".*" +
1459		")\\)|)",
1460
1461	// Leading and non-escaped trailing whitespace, capturing some non-whitespace characters preceding the latter
1462	rwhitespace = new RegExp( whitespace + "+", "g" ),
1463
1464	rcomma = new RegExp( "^" + whitespace + "*," + whitespace + "*" ),
1465	rleadingCombinator = new RegExp( "^" + whitespace + "*([>+~]|" + whitespace + ")" +
1466		whitespace + "*" ),
1467	rdescend = new RegExp( whitespace + "|>" ),
1468
1469	rpseudo = new RegExp( pseudos ),
1470	ridentifier = new RegExp( "^" + identifier + "$" ),
1471
1472	matchExpr = {
1473		ID: new RegExp( "^#(" + identifier + ")" ),
1474		CLASS: new RegExp( "^\\.(" + identifier + ")" ),
1475		TAG: new RegExp( "^(" + identifier + "|[*])" ),
1476		ATTR: new RegExp( "^" + attributes ),
1477		PSEUDO: new RegExp( "^" + pseudos ),
1478		CHILD: new RegExp(
1479			"^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\(" +
1480				whitespace + "*(even|odd|(([+-]|)(\\d*)n|)" + whitespace + "*(?:([+-]|)" +
1481				whitespace + "*(\\d+)|))" + whitespace + "*\\)|)", "i" ),
1482		bool: new RegExp( "^(?:" + booleans + ")$", "i" ),
1483
1484		// For use in libraries implementing .is()
1485		// We use this for POS matching in `select`
1486		needsContext: new RegExp( "^" + whitespace +
1487			"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\(" + whitespace +
1488			"*((?:-\\d)?\\d*)" + whitespace + "*\\)|)(?=[^-]|$)", "i" )
1489	},
1490
1491	rinputs = /^(?:input|select|textarea|button)$/i,
1492	rheader = /^h\d$/i,
1493
1494	// Easily-parseable/retrievable ID or TAG or CLASS selectors
1495	rquickExpr = /^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,
1496
1497	rsibling = /[+~]/,
1498
1499	// CSS escapes
1500	// https://www.w3.org/TR/CSS21/syndata.html#escaped-characters
1501	runescape = new RegExp( "\\\\[\\da-fA-F]{1,6}" + whitespace +
1502		"?|\\\\([^\\r\\n\\f])", "g" ),
1503	funescape = function( escape, nonHex ) {
1504		var high = "0x" + escape.slice( 1 ) - 0x10000;
1505
1506		if ( nonHex ) {
1507
1508			// Strip the backslash prefix from a non-hex escape sequence
1509			return nonHex;
1510		}
1511
1512		// Replace a hexadecimal escape sequence with the encoded Unicode code point
1513		// Support: IE <=11+
1514		// For values outside the Basic Multilingual Plane (BMP), manually construct a
1515		// surrogate pair
1516		return high < 0 ?
1517			String.fromCharCode( high + 0x10000 ) :
1518			String.fromCharCode( high >> 10 | 0xD800, high & 0x3FF | 0xDC00 );
1519	},
1520
1521	// Used for iframes; see `setDocument`.
1522	// Support: IE 9 - 11+, Edge 12 - 18+
1523	// Removing the function wrapper causes a "Permission Denied"
1524	// error in IE/Edge.
1525	unloadHandler = function() {
1526		setDocument();
1527	},
1528
1529	inDisabledFieldset = addCombinator(
1530		function( elem ) {
1531			return elem.disabled === true && nodeName( elem, "fieldset" );
1532		},
1533		{ dir: "parentNode", next: "legend" }
1534	);
1535
1536// Support: IE <=9 only
1537// Accessing document.activeElement can throw unexpectedly
1538// https://bugs.jquery.com/ticket/13393
1539function safeActiveElement() {
1540	try {
1541		return document.activeElement;
1542	} catch ( err ) { }
1543}
1544
1545// Optimize for push.apply( _, NodeList )
1546try {
1547	push.apply(
1548		( arr = slice.call( preferredDoc.childNodes ) ),
1549		preferredDoc.childNodes
1550	);
1551
1552	// Support: Android <=4.0
1553	// Detect silently failing push.apply
1554	// eslint-disable-next-line no-unused-expressions
1555	arr[ preferredDoc.childNodes.length ].nodeType;
1556} catch ( e ) {
1557	push = {
1558		apply: function( target, els ) {
1559			pushNative.apply( target, slice.call( els ) );
1560		},
1561		call: function( target ) {
1562			pushNative.apply( target, slice.call( arguments, 1 ) );
1563		}
1564	};
1565}
1566
1567function find( selector, context, results, seed ) {
1568	var m, i, elem, nid, match, groups, newSelector,
1569		newContext = context && context.ownerDocument,
1570
1571		// nodeType defaults to 9, since context defaults to document
1572		nodeType = context ? context.nodeType : 9;
1573
1574	results = results || [];
1575
1576	// Return early from calls with invalid selector or context
1577	if ( typeof selector !== "string" || !selector ||
1578		nodeType !== 1 && nodeType !== 9 && nodeType !== 11 ) {
1579
1580		return results;
1581	}
1582
1583	// Try to shortcut find operations (as opposed to filters) in HTML documents
1584	if ( !seed ) {
1585		setDocument( context );
1586		context = context || document;
1587
1588		if ( documentIsHTML ) {
1589
1590			// If the selector is sufficiently simple, try using a "get*By*" DOM method
1591			// (excepting DocumentFragment context, where the methods don't exist)
1592			if ( nodeType !== 11 && ( match = rquickExpr.exec( selector ) ) ) {
1593
1594				// ID selector
1595				if ( ( m = match[ 1 ] ) ) {
1596
1597					// Document context
1598					if ( nodeType === 9 ) {
1599						if ( ( elem = context.getElementById( m ) ) ) {
1600
1601							// Support: IE 9 only
1602							// getElementById can match elements by name instead of ID
1603							if ( elem.id === m ) {
1604								push.call( results, elem );
1605								return results;
1606							}
1607						} else {
1608							return results;
1609						}
1610
1611					// Element context
1612					} else {
1613
1614						// Support: IE 9 only
1615						// getElementById can match elements by name instead of ID
1616						if ( newContext && ( elem = newContext.getElementById( m ) ) &&
1617							find.contains( context, elem ) &&
1618							elem.id === m ) {
1619
1620							push.call( results, elem );
1621							return results;
1622						}
1623					}
1624
1625				// Type selector
1626				} else if ( match[ 2 ] ) {
1627					push.apply( results, context.getElementsByTagName( selector ) );
1628					return results;
1629
1630				// Class selector
1631				} else if ( ( m = match[ 3 ] ) && context.getElementsByClassName ) {
1632					push.apply( results, context.getElementsByClassName( m ) );
1633					return results;
1634				}
1635			}
1636
1637			// Take advantage of querySelectorAll
1638			if ( !nonnativeSelectorCache[ selector + " " ] &&
1639				( !rbuggyQSA || !rbuggyQSA.test( selector ) ) ) {
1640
1641				newSelector = selector;
1642				newContext = context;
1643
1644				// qSA considers elements outside a scoping root when evaluating child or
1645				// descendant combinators, which is not what we want.
1646				// In such cases, we work around the behavior by prefixing every selector in the
1647				// list with an ID selector referencing the scope context.
1648				// The technique has to be used as well when a leading combinator is used
1649				// as such selectors are not recognized by querySelectorAll.
1650				// Thanks to Andrew Dupont for this technique.
1651				if ( nodeType === 1 &&
1652					( rdescend.test( selector ) || rleadingCombinator.test( selector ) ) ) {
1653
1654					// Expand context for sibling selectors
1655					newContext = rsibling.test( selector ) && testContext( context.parentNode ) ||
1656						context;
1657
1658					// We can use :scope instead of the ID hack if the browser
1659					// supports it & if we're not changing the context.
1660					// Support: IE 11+, Edge 17 - 18+
1661					// IE/Edge sometimes throw a "Permission denied" error when
1662					// strict-comparing two documents; shallow comparisons work.
1663					// eslint-disable-next-line eqeqeq
1664					if ( newContext != context || !support.scope ) {
1665
1666						// Capture the context ID, setting it first if necessary
1667						if ( ( nid = context.getAttribute( "id" ) ) ) {
1668							nid = jQuery.escapeSelector( nid );
1669						} else {
1670							context.setAttribute( "id", ( nid = expando ) );
1671						}
1672					}
1673
1674					// Prefix every selector in the list
1675					groups = tokenize( selector );
1676					i = groups.length;
1677					while ( i-- ) {
1678						groups[ i ] = ( nid ? "#" + nid : ":scope" ) + " " +
1679							toSelector( groups[ i ] );
1680					}
1681					newSelector = groups.join( "," );
1682				}
1683
1684				try {
1685					push.apply( results,
1686						newContext.querySelectorAll( newSelector )
1687					);
1688					return results;
1689				} catch ( qsaError ) {
1690					nonnativeSelectorCache( selector, true );
1691				} finally {
1692					if ( nid === expando ) {
1693						context.removeAttribute( "id" );
1694					}
1695				}
1696			}
1697		}
1698	}
1699
1700	// All others
1701	return select( selector.replace( rtrimCSS, "$1" ), context, results, seed );
1702}
1703
1704/**
1705 * Create key-value caches of limited size
1706 * @returns {function(string, object)} Returns the Object data after storing it on itself with
1707 *	property name the (space-suffixed) string and (if the cache is larger than Expr.cacheLength)
1708 *	deleting the oldest entry
1709 */
1710function createCache() {
1711	var keys = [];
1712
1713	function cache( key, value ) {
1714
1715		// Use (key + " ") to avoid collision with native prototype properties
1716		// (see https://github.com/jquery/sizzle/issues/157)
1717		if ( keys.push( key + " " ) > Expr.cacheLength ) {
1718
1719			// Only keep the most recent entries
1720			delete cache[ keys.shift() ];
1721		}
1722		return ( cache[ key + " " ] = value );
1723	}
1724	return cache;
1725}
1726
1727/**
1728 * Mark a function for special use by jQuery selector module
1729 * @param {Function} fn The function to mark
1730 */
1731function markFunction( fn ) {
1732	fn[ expando ] = true;
1733	return fn;
1734}
1735
1736/**
1737 * Support testing using an element
1738 * @param {Function} fn Passed the created element and returns a boolean result
1739 */
1740function assert( fn ) {
1741	var el = document.createElement( "fieldset" );
1742
1743	try {
1744		return !!fn( el );
1745	} catch ( e ) {
1746		return false;
1747	} finally {
1748
1749		// Remove from its parent by default
1750		if ( el.parentNode ) {
1751			el.parentNode.removeChild( el );
1752		}
1753
1754		// release memory in IE
1755		el = null;
1756	}
1757}
1758
1759/**
1760 * Returns a function to use in pseudos for input types
1761 * @param {String} type
1762 */
1763function createInputPseudo( type ) {
1764	return function( elem ) {
1765		return nodeName( elem, "input" ) && elem.type === type;
1766	};
1767}
1768
1769/**
1770 * Returns a function to use in pseudos for buttons
1771 * @param {String} type
1772 */
1773function createButtonPseudo( type ) {
1774	return function( elem ) {
1775		return ( nodeName( elem, "input" ) || nodeName( elem, "button" ) ) &&
1776			elem.type === type;
1777	};
1778}
1779
1780/**
1781 * Returns a function to use in pseudos for :enabled/:disabled
1782 * @param {Boolean} disabled true for :disabled; false for :enabled
1783 */
1784function createDisabledPseudo( disabled ) {
1785
1786	// Known :disabled false positives: fieldset[disabled] > legend:nth-of-type(n+2) :can-disable
1787	return function( elem ) {
1788
1789		// Only certain elements can match :enabled or :disabled
1790		// https://html.spec.whatwg.org/multipage/scripting.html#selector-enabled
1791		// https://html.spec.whatwg.org/multipage/scripting.html#selector-disabled
1792		if ( "form" in elem ) {
1793
1794			// Check for inherited disabledness on relevant non-disabled elements:
1795			// * listed form-associated elements in a disabled fieldset
1796			//   https://html.spec.whatwg.org/multipage/forms.html#category-listed
1797			//   https://html.spec.whatwg.org/multipage/forms.html#concept-fe-disabled
1798			// * option elements in a disabled optgroup
1799			//   https://html.spec.whatwg.org/multipage/forms.html#concept-option-disabled
1800			// All such elements have a "form" property.
1801			if ( elem.parentNode && elem.disabled === false ) {
1802
1803				// Option elements defer to a parent optgroup if present
1804				if ( "label" in elem ) {
1805					if ( "label" in elem.parentNode ) {
1806						return elem.parentNode.disabled === disabled;
1807					} else {
1808						return elem.disabled === disabled;
1809					}
1810				}
1811
1812				// Support: IE 6 - 11+
1813				// Use the isDisabled shortcut property to check for disabled fieldset ancestors
1814				return elem.isDisabled === disabled ||
1815
1816					// Where there is no isDisabled, check manually
1817					elem.isDisabled !== !disabled &&
1818						inDisabledFieldset( elem ) === disabled;
1819			}
1820
1821			return elem.disabled === disabled;
1822
1823		// Try to winnow out elements that can't be disabled before trusting the disabled property.
1824		// Some victims get caught in our net (label, legend, menu, track), but it shouldn't
1825		// even exist on them, let alone have a boolean value.
1826		} else if ( "label" in elem ) {
1827			return elem.disabled === disabled;
1828		}
1829
1830		// Remaining elements are neither :enabled nor :disabled
1831		return false;
1832	};
1833}
1834
1835/**
1836 * Returns a function to use in pseudos for positionals
1837 * @param {Function} fn
1838 */
1839function createPositionalPseudo( fn ) {
1840	return markFunction( function( argument ) {
1841		argument = +argument;
1842		return markFunction( function( seed, matches ) {
1843			var j,
1844				matchIndexes = fn( [], seed.length, argument ),
1845				i = matchIndexes.length;
1846
1847			// Match elements found at the specified indexes
1848			while ( i-- ) {
1849				if ( seed[ ( j = matchIndexes[ i ] ) ] ) {
1850					seed[ j ] = !( matches[ j ] = seed[ j ] );
1851				}
1852			}
1853		} );
1854	} );
1855}
1856
1857/**
1858 * Checks a node for validity as a jQuery selector context
1859 * @param {Element|Object=} context
1860 * @returns {Element|Object|Boolean} The input node if acceptable, otherwise a falsy value
1861 */
1862function testContext( context ) {
1863	return context && typeof context.getElementsByTagName !== "undefined" && context;
1864}
1865
1866/**
1867 * Sets document-related variables once based on the current document
1868 * @param {Element|Object} [node] An element or document object to use to set the document
1869 * @returns {Object} Returns the current document
1870 */
1871function setDocument( node ) {
1872	var subWindow,
1873		doc = node ? node.ownerDocument || node : preferredDoc;
1874
1875	// Return early if doc is invalid or already selected
1876	// Support: IE 11+, Edge 17 - 18+
1877	// IE/Edge sometimes throw a "Permission denied" error when strict-comparing
1878	// two documents; shallow comparisons work.
1879	// eslint-disable-next-line eqeqeq
1880	if ( doc == document || doc.nodeType !== 9 || !doc.documentElement ) {
1881		return document;
1882	}
1883
1884	// Update global variables
1885	document = doc;
1886	documentElement = document.documentElement;
1887	documentIsHTML = !jQuery.isXMLDoc( document );
1888
1889	// Support: iOS 7 only, IE 9 - 11+
1890	// Older browsers didn't support unprefixed `matches`.
1891	matches = documentElement.matches ||
1892		documentElement.webkitMatchesSelector ||
1893		documentElement.msMatchesSelector;
1894
1895	// Support: IE 9 - 11+, Edge 12 - 18+
1896	// Accessing iframe documents after unload throws "permission denied" errors
1897	// (see trac-13936).
1898	// Limit the fix to IE & Edge Legacy; despite Edge 15+ implementing `matches`,
1899	// all IE 9+ and Edge Legacy versions implement `msMatchesSelector` as well.
1900	if ( documentElement.msMatchesSelector &&
1901
1902		// Support: IE 11+, Edge 17 - 18+
1903		// IE/Edge sometimes throw a "Permission denied" error when strict-comparing
1904		// two documents; shallow comparisons work.
1905		// eslint-disable-next-line eqeqeq
1906		preferredDoc != document &&
1907		( subWindow = document.defaultView ) && subWindow.top !== subWindow ) {
1908
1909		// Support: IE 9 - 11+, Edge 12 - 18+
1910		subWindow.addEventListener( "unload", unloadHandler );
1911	}
1912
1913	// Support: IE <10
1914	// Check if getElementById returns elements by name
1915	// The broken getElementById methods don't pick up programmatically-set names,
1916	// so use a roundabout getElementsByName test
1917	support.getById = assert( function( el ) {
1918		documentElement.appendChild( el ).id = jQuery.expando;
1919		return !document.getElementsByName ||
1920			!document.getElementsByName( jQuery.expando ).length;
1921	} );
1922
1923	// Support: IE 9 only
1924	// Check to see if it's possible to do matchesSelector
1925	// on a disconnected node.
1926	support.disconnectedMatch = assert( function( el ) {
1927		return matches.call( el, "*" );
1928	} );
1929
1930	// Support: IE 9 - 11+, Edge 12 - 18+
1931	// IE/Edge don't support the :scope pseudo-class.
1932	support.scope = assert( function() {
1933		return document.querySelectorAll( ":scope" );
1934	} );
1935
1936	// Support: Chrome 105 - 111 only, Safari 15.4 - 16.3 only
1937	// Make sure the `:has()` argument is parsed unforgivingly.
1938	// We include `*` in the test to detect buggy implementations that are
1939	// _selectively_ forgiving (specifically when the list includes at least
1940	// one valid selector).
1941	// Note that we treat complete lack of support for `:has()` as if it were
1942	// spec-compliant support, which is fine because use of `:has()` in such
1943	// environments will fail in the qSA path and fall back to jQuery traversal
1944	// anyway.
1945	support.cssHas = assert( function() {
1946		try {
1947			document.querySelector( ":has(*,:jqfake)" );
1948			return false;
1949		} catch ( e ) {
1950			return true;
1951		}
1952	} );
1953
1954	// ID filter and find
1955	if ( support.getById ) {
1956		Expr.filter.ID = function( id ) {
1957			var attrId = id.replace( runescape, funescape );
1958			return function( elem ) {
1959				return elem.getAttribute( "id" ) === attrId;
1960			};
1961		};
1962		Expr.find.ID = function( id, context ) {
1963			if ( typeof context.getElementById !== "undefined" && documentIsHTML ) {
1964				var elem = context.getElementById( id );
1965				return elem ? [ elem ] : [];
1966			}
1967		};
1968	} else {
1969		Expr.filter.ID =  function( id ) {
1970			var attrId = id.replace( runescape, funescape );
1971			return function( elem ) {
1972				var node = typeof elem.getAttributeNode !== "undefined" &&
1973					elem.getAttributeNode( "id" );
1974				return node && node.value === attrId;
1975			};
1976		};
1977
1978		// Support: IE 6 - 7 only
1979		// getElementById is not reliable as a find shortcut
1980		Expr.find.ID = function( id, context ) {
1981			if ( typeof context.getElementById !== "undefined" && documentIsHTML ) {
1982				var node, i, elems,
1983					elem = context.getElementById( id );
1984
1985				if ( elem ) {
1986
1987					// Verify the id attribute
1988					node = elem.getAttributeNode( "id" );
1989					if ( node && node.value === id ) {
1990						return [ elem ];
1991					}
1992
1993					// Fall back on getElementsByName
1994					elems = context.getElementsByName( id );
1995					i = 0;
1996					while ( ( elem = elems[ i++ ] ) ) {
1997						node = elem.getAttributeNode( "id" );
1998						if ( node && node.value === id ) {
1999							return [ elem ];
2000						}
2001					}
2002				}
2003
2004				return [];
2005			}
2006		};
2007	}
2008
2009	// Tag
2010	Expr.find.TAG = function( tag, context ) {
2011		if ( typeof context.getElementsByTagName !== "undefined" ) {
2012			return context.getElementsByTagName( tag );
2013
2014		// DocumentFragment nodes don't have gEBTN
2015		} else {
2016			return context.querySelectorAll( tag );
2017		}
2018	};
2019
2020	// Class
2021	Expr.find.CLASS = function( className, context ) {
2022		if ( typeof context.getElementsByClassName !== "undefined" && documentIsHTML ) {
2023			return context.getElementsByClassName( className );
2024		}
2025	};
2026
2027	/* QSA/matchesSelector
2028	---------------------------------------------------------------------- */
2029
2030	// QSA and matchesSelector support
2031
2032	rbuggyQSA = [];
2033
2034	// Build QSA regex
2035	// Regex strategy adopted from Diego Perini
2036	assert( function( el ) {
2037
2038		var input;
2039
2040		documentElement.appendChild( el ).innerHTML =
2041			"<a id='" + expando + "' href='' disabled='disabled'></a>" +
2042			"<select id='" + expando + "-\r\\' disabled='disabled'>" +
2043			"<option selected=''></option></select>";
2044
2045		// Support: iOS <=7 - 8 only
2046		// Boolean attributes and "value" are not treated correctly in some XML documents
2047		if ( !el.querySelectorAll( "[selected]" ).length ) {
2048			rbuggyQSA.push( "\\[" + whitespace + "*(?:value|" + booleans + ")" );
2049		}
2050
2051		// Support: iOS <=7 - 8 only
2052		if ( !el.querySelectorAll( "[id~=" + expando + "-]" ).length ) {
2053			rbuggyQSA.push( "~=" );
2054		}
2055
2056		// Support: iOS 8 only
2057		// https://bugs.webkit.org/show_bug.cgi?id=136851
2058		// In-page `selector#id sibling-combinator selector` fails
2059		if ( !el.querySelectorAll( "a#" + expando + "+*" ).length ) {
2060			rbuggyQSA.push( ".#.+[+~]" );
2061		}
2062
2063		// Support: Chrome <=105+, Firefox <=104+, Safari <=15.4+
2064		// In some of the document kinds, these selectors wouldn't work natively.
2065		// This is probably OK but for backwards compatibility we want to maintain
2066		// handling them through jQuery traversal in jQuery 3.x.
2067		if ( !el.querySelectorAll( ":checked" ).length ) {
2068			rbuggyQSA.push( ":checked" );
2069		}
2070
2071		// Support: Windows 8 Native Apps
2072		// The type and name attributes are restricted during .innerHTML assignment
2073		input = document.createElement( "input" );
2074		input.setAttribute( "type", "hidden" );
2075		el.appendChild( input ).setAttribute( "name", "D" );
2076
2077		// Support: IE 9 - 11+
2078		// IE's :disabled selector does not pick up the children of disabled fieldsets
2079		// Support: Chrome <=105+, Firefox <=104+, Safari <=15.4+
2080		// In some of the document kinds, these selectors wouldn't work natively.
2081		// This is probably OK but for backwards compatibility we want to maintain
2082		// handling them through jQuery traversal in jQuery 3.x.
2083		documentElement.appendChild( el ).disabled = true;
2084		if ( el.querySelectorAll( ":disabled" ).length !== 2 ) {
2085			rbuggyQSA.push( ":enabled", ":disabled" );
2086		}
2087
2088		// Support: IE 11+, Edge 15 - 18+
2089		// IE 11/Edge don't find elements on a `[name='']` query in some cases.
2090		// Adding a temporary attribute to the document before the selection works
2091		// around the issue.
2092		// Interestingly, IE 10 & older don't seem to have the issue.
2093		input = document.createElement( "input" );
2094		input.setAttribute( "name", "" );
2095		el.appendChild( input );
2096		if ( !el.querySelectorAll( "[name='']" ).length ) {
2097			rbuggyQSA.push( "\\[" + whitespace + "*name" + whitespace + "*=" +
2098				whitespace + "*(?:''|\"\")" );
2099		}
2100	} );
2101
2102	if ( !support.cssHas ) {
2103
2104		// Support: Chrome 105 - 110+, Safari 15.4 - 16.3+
2105		// Our regular `try-catch` mechanism fails to detect natively-unsupported
2106		// pseudo-classes inside `:has()` (such as `:has(:contains("Foo"))`)
2107		// in browsers that parse the `:has()` argument as a forgiving selector list.
2108		// https://drafts.csswg.org/selectors/#relational now requires the argument
2109		// to be parsed unforgivingly, but browsers have not yet fully adjusted.
2110		rbuggyQSA.push( ":has" );
2111	}
2112
2113	rbuggyQSA = rbuggyQSA.length && new RegExp( rbuggyQSA.join( "|" ) );
2114
2115	/* Sorting
2116	---------------------------------------------------------------------- */
2117
2118	// Document order sorting
2119	sortOrder = function( a, b ) {
2120
2121		// Flag for duplicate removal
2122		if ( a === b ) {
2123			hasDuplicate = true;
2124			return 0;
2125		}
2126
2127		// Sort on method existence if only one input has compareDocumentPosition
2128		var compare = !a.compareDocumentPosition - !b.compareDocumentPosition;
2129		if ( compare ) {
2130			return compare;
2131		}
2132
2133		// Calculate position if both inputs belong to the same document
2134		// Support: IE 11+, Edge 17 - 18+
2135		// IE/Edge sometimes throw a "Permission denied" error when strict-comparing
2136		// two documents; shallow comparisons work.
2137		// eslint-disable-next-line eqeqeq
2138		compare = ( a.ownerDocument || a ) == ( b.ownerDocument || b ) ?
2139			a.compareDocumentPosition( b ) :
2140
2141			// Otherwise we know they are disconnected
2142			1;
2143
2144		// Disconnected nodes
2145		if ( compare & 1 ||
2146			( !support.sortDetached && b.compareDocumentPosition( a ) === compare ) ) {
2147
2148			// Choose the first element that is related to our preferred document
2149			// Support: IE 11+, Edge 17 - 18+
2150			// IE/Edge sometimes throw a "Permission denied" error when strict-comparing
2151			// two documents; shallow comparisons work.
2152			// eslint-disable-next-line eqeqeq
2153			if ( a === document || a.ownerDocument == preferredDoc &&
2154				find.contains( preferredDoc, a ) ) {
2155				return -1;
2156			}
2157
2158			// Support: IE 11+, Edge 17 - 18+
2159			// IE/Edge sometimes throw a "Permission denied" error when strict-comparing
2160			// two documents; shallow comparisons work.
2161			// eslint-disable-next-line eqeqeq
2162			if ( b === document || b.ownerDocument == preferredDoc &&
2163				find.contains( preferredDoc, b ) ) {
2164				return 1;
2165			}
2166
2167			// Maintain original order
2168			return sortInput ?
2169				( indexOf.call( sortInput, a ) - indexOf.call( sortInput, b ) ) :
2170				0;
2171		}
2172
2173		return compare & 4 ? -1 : 1;
2174	};
2175
2176	return document;
2177}
2178
2179find.matches = function( expr, elements ) {
2180	return find( expr, null, null, elements );
2181};
2182
2183find.matchesSelector = function( elem, expr ) {
2184	setDocument( elem );
2185
2186	if ( documentIsHTML &&
2187		!nonnativeSelectorCache[ expr + " " ] &&
2188		( !rbuggyQSA || !rbuggyQSA.test( expr ) ) ) {
2189
2190		try {
2191			var ret = matches.call( elem, expr );
2192
2193			// IE 9's matchesSelector returns false on disconnected nodes
2194			if ( ret || support.disconnectedMatch ||
2195
2196					// As well, disconnected nodes are said to be in a document
2197					// fragment in IE 9
2198					elem.document && elem.document.nodeType !== 11 ) {
2199				return ret;
2200			}
2201		} catch ( e ) {
2202			nonnativeSelectorCache( expr, true );
2203		}
2204	}
2205
2206	return find( expr, document, null, [ elem ] ).length > 0;
2207};
2208
2209find.contains = function( context, elem ) {
2210
2211	// Set document vars if needed
2212	// Support: IE 11+, Edge 17 - 18+
2213	// IE/Edge sometimes throw a "Permission denied" error when strict-comparing
2214	// two documents; shallow comparisons work.
2215	// eslint-disable-next-line eqeqeq
2216	if ( ( context.ownerDocument || context ) != document ) {
2217		setDocument( context );
2218	}
2219	return jQuery.contains( context, elem );
2220};
2221
2222
2223find.attr = function( elem, name ) {
2224
2225	// Set document vars if needed
2226	// Support: IE 11+, Edge 17 - 18+
2227	// IE/Edge sometimes throw a "Permission denied" error when strict-comparing
2228	// two documents; shallow comparisons work.
2229	// eslint-disable-next-line eqeqeq
2230	if ( ( elem.ownerDocument || elem ) != document ) {
2231		setDocument( elem );
2232	}
2233
2234	var fn = Expr.attrHandle[ name.toLowerCase() ],
2235
2236		// Don't get fooled by Object.prototype properties (see trac-13807)
2237		val = fn && hasOwn.call( Expr.attrHandle, name.toLowerCase() ) ?
2238			fn( elem, name, !documentIsHTML ) :
2239			undefined;
2240
2241	if ( val !== undefined ) {
2242		return val;
2243	}
2244
2245	return elem.getAttribute( name );
2246};
2247
2248find.error = function( msg ) {
2249	throw new Error( "Syntax error, unrecognized expression: " + msg );
2250};
2251
2252/**
2253 * Document sorting and removing duplicates
2254 * @param {ArrayLike} results
2255 */
2256jQuery.uniqueSort = function( results ) {
2257	var elem,
2258		duplicates = [],
2259		j = 0,
2260		i = 0;
2261
2262	// Unless we *know* we can detect duplicates, assume their presence
2263	//
2264	// Support: Android <=4.0+
2265	// Testing for detecting duplicates is unpredictable so instead assume we can't
2266	// depend on duplicate detection in all browsers without a stable sort.
2267	hasDuplicate = !support.sortStable;
2268	sortInput = !support.sortStable && slice.call( results, 0 );
2269	sort.call( results, sortOrder );
2270
2271	if ( hasDuplicate ) {
2272		while ( ( elem = results[ i++ ] ) ) {
2273			if ( elem === results[ i ] ) {
2274				j = duplicates.push( i );
2275			}
2276		}
2277		while ( j-- ) {
2278			splice.call( results, duplicates[ j ], 1 );
2279		}
2280	}
2281
2282	// Clear input after sorting to release objects
2283	// See https://github.com/jquery/sizzle/pull/225
2284	sortInput = null;
2285
2286	return results;
2287};
2288
2289jQuery.fn.uniqueSort = function() {
2290	return this.pushStack( jQuery.uniqueSort( slice.apply( this ) ) );
2291};
2292
2293Expr = jQuery.expr = {
2294
2295	// Can be adjusted by the user
2296	cacheLength: 50,
2297
2298	createPseudo: markFunction,
2299
2300	match: matchExpr,
2301
2302	attrHandle: {},
2303
2304	find: {},
2305
2306	relative: {
2307		">": { dir: "parentNode", first: true },
2308		" ": { dir: "parentNode" },
2309		"+": { dir: "previousSibling", first: true },
2310		"~": { dir: "previousSibling" }
2311	},
2312
2313	preFilter: {
2314		ATTR: function( match ) {
2315			match[ 1 ] = match[ 1 ].replace( runescape, funescape );
2316
2317			// Move the given value to match[3] whether quoted or unquoted
2318			match[ 3 ] = ( match[ 3 ] || match[ 4 ] || match[ 5 ] || "" )
2319				.replace( runescape, funescape );
2320
2321			if ( match[ 2 ] === "~=" ) {
2322				match[ 3 ] = " " + match[ 3 ] + " ";
2323			}
2324
2325			return match.slice( 0, 4 );
2326		},
2327
2328		CHILD: function( match ) {
2329
2330			/* matches from matchExpr["CHILD"]
2331				1 type (only|nth|...)
2332				2 what (child|of-type)
2333				3 argument (even|odd|\d*|\d*n([+-]\d+)?|...)
2334				4 xn-component of xn+y argument ([+-]?\d*n|)
2335				5 sign of xn-component
2336				6 x of xn-component
2337				7 sign of y-component
2338				8 y of y-component
2339			*/
2340			match[ 1 ] = match[ 1 ].toLowerCase();
2341
2342			if ( match[ 1 ].slice( 0, 3 ) === "nth" ) {
2343
2344				// nth-* requires argument
2345				if ( !match[ 3 ] ) {
2346					find.error( match[ 0 ] );
2347				}
2348
2349				// numeric x and y parameters for Expr.filter.CHILD
2350				// remember that false/true cast respectively to 0/1
2351				match[ 4 ] = +( match[ 4 ] ?
2352					match[ 5 ] + ( match[ 6 ] || 1 ) :
2353					2 * ( match[ 3 ] === "even" || match[ 3 ] === "odd" )
2354				);
2355				match[ 5 ] = +( ( match[ 7 ] + match[ 8 ] ) || match[ 3 ] === "odd" );
2356
2357			// other types prohibit arguments
2358			} else if ( match[ 3 ] ) {
2359				find.error( match[ 0 ] );
2360			}
2361
2362			return match;
2363		},
2364
2365		PSEUDO: function( match ) {
2366			var excess,
2367				unquoted = !match[ 6 ] && match[ 2 ];
2368
2369			if ( matchExpr.CHILD.test( match[ 0 ] ) ) {
2370				return null;
2371			}
2372
2373			// Accept quoted arguments as-is
2374			if ( match[ 3 ] ) {
2375				match[ 2 ] = match[ 4 ] || match[ 5 ] || "";
2376
2377			// Strip excess characters from unquoted arguments
2378			} else if ( unquoted && rpseudo.test( unquoted ) &&
2379
2380				// Get excess from tokenize (recursively)
2381				( excess = tokenize( unquoted, true ) ) &&
2382
2383				// advance to the next closing parenthesis
2384				( excess = unquoted.indexOf( ")", unquoted.length - excess ) - unquoted.length ) ) {
2385
2386				// excess is a negative index
2387				match[ 0 ] = match[ 0 ].slice( 0, excess );
2388				match[ 2 ] = unquoted.slice( 0, excess );
2389			}
2390
2391			// Return only captures needed by the pseudo filter method (type and argument)
2392			return match.slice( 0, 3 );
2393		}
2394	},
2395
2396	filter: {
2397
2398		TAG: function( nodeNameSelector ) {
2399			var expectedNodeName = nodeNameSelector.replace( runescape, funescape ).toLowerCase();
2400			return nodeNameSelector === "*" ?
2401				function() {
2402					return true;
2403				} :
2404				function( elem ) {
2405					return nodeName( elem, expectedNodeName );
2406				};
2407		},
2408
2409		CLASS: function( className ) {
2410			var pattern = classCache[ className + " " ];
2411
2412			return pattern ||
2413				( pattern = new RegExp( "(^|" + whitespace + ")" + className +
2414					"(" + whitespace + "|$)" ) ) &&
2415				classCache( className, function( elem ) {
2416					return pattern.test(
2417						typeof elem.className === "string" && elem.className ||
2418							typeof elem.getAttribute !== "undefined" &&
2419								elem.getAttribute( "class" ) ||
2420							""
2421					);
2422				} );
2423		},
2424
2425		ATTR: function( name, operator, check ) {
2426			return function( elem ) {
2427				var result = find.attr( elem, name );
2428
2429				if ( result == null ) {
2430					return operator === "!=";
2431				}
2432				if ( !operator ) {
2433					return true;
2434				}
2435
2436				result += "";
2437
2438				if ( operator === "=" ) {
2439					return result === check;
2440				}
2441				if ( operator === "!=" ) {
2442					return result !== check;
2443				}
2444				if ( operator === "^=" ) {
2445					return check && result.indexOf( check ) === 0;
2446				}
2447				if ( operator === "*=" ) {
2448					return check && result.indexOf( check ) > -1;
2449				}
2450				if ( operator === "$=" ) {
2451					return check && result.slice( -check.length ) === check;
2452				}
2453				if ( operator === "~=" ) {
2454					return ( " " + result.replace( rwhitespace, " " ) + " " )
2455						.indexOf( check ) > -1;
2456				}
2457				if ( operator === "|=" ) {
2458					return result === check || result.slice( 0, check.length + 1 ) === check + "-";
2459				}
2460
2461				return false;
2462			};
2463		},
2464
2465		CHILD: function( type, what, _argument, first, last ) {
2466			var simple = type.slice( 0, 3 ) !== "nth",
2467				forward = type.slice( -4 ) !== "last",
2468				ofType = what === "of-type";
2469
2470			return first === 1 && last === 0 ?
2471
2472				// Shortcut for :nth-*(n)
2473				function( elem ) {
2474					return !!elem.parentNode;
2475				} :
2476
2477				function( elem, _context, xml ) {
2478					var cache, outerCache, node, nodeIndex, start,
2479						dir = simple !== forward ? "nextSibling" : "previousSibling",
2480						parent = elem.parentNode,
2481						name = ofType && elem.nodeName.toLowerCase(),
2482						useCache = !xml && !ofType,
2483						diff = false;
2484
2485					if ( parent ) {
2486
2487						// :(first|last|only)-(child|of-type)
2488						if ( simple ) {
2489							while ( dir ) {
2490								node = elem;
2491								while ( ( node = node[ dir ] ) ) {
2492									if ( ofType ?
2493										nodeName( node, name ) :
2494										node.nodeType === 1 ) {
2495
2496										return false;
2497									}
2498								}
2499
2500								// Reverse direction for :only-* (if we haven't yet done so)
2501								start = dir = type === "only" && !start && "nextSibling";
2502							}
2503							return true;
2504						}
2505
2506						start = [ forward ? parent.firstChild : parent.lastChild ];
2507
2508						// non-xml :nth-child(...) stores cache data on `parent`
2509						if ( forward && useCache ) {
2510
2511							// Seek `elem` from a previously-cached index
2512							outerCache = parent[ expando ] || ( parent[ expando ] = {} );
2513							cache = outerCache[ type ] || [];
2514							nodeIndex = cache[ 0 ] === dirruns && cache[ 1 ];
2515							diff = nodeIndex && cache[ 2 ];
2516							node = nodeIndex && parent.childNodes[ nodeIndex ];
2517
2518							while ( ( node = ++nodeIndex && node && node[ dir ] ||
2519
2520								// Fallback to seeking `elem` from the start
2521								( diff = nodeIndex = 0 ) || start.pop() ) ) {
2522
2523								// When found, cache indexes on `parent` and break
2524								if ( node.nodeType === 1 && ++diff && node === elem ) {
2525									outerCache[ type ] = [ dirruns, nodeIndex, diff ];
2526									break;
2527								}
2528							}
2529
2530						} else {
2531
2532							// Use previously-cached element index if available
2533							if ( useCache ) {
2534								outerCache = elem[ expando ] || ( elem[ expando ] = {} );
2535								cache = outerCache[ type ] || [];
2536								nodeIndex = cache[ 0 ] === dirruns && cache[ 1 ];
2537								diff = nodeIndex;
2538							}
2539
2540							// xml :nth-child(...)
2541							// or :nth-last-child(...) or :nth(-last)?-of-type(...)
2542							if ( diff === false ) {
2543
2544								// Use the same loop as above to seek `elem` from the start
2545								while ( ( node = ++nodeIndex && node && node[ dir ] ||
2546									( diff = nodeIndex = 0 ) || start.pop() ) ) {
2547
2548									if ( ( ofType ?
2549										nodeName( node, name ) :
2550										node.nodeType === 1 ) &&
2551										++diff ) {
2552
2553										// Cache the index of each encountered element
2554										if ( useCache ) {
2555											outerCache = node[ expando ] ||
2556												( node[ expando ] = {} );
2557											outerCache[ type ] = [ dirruns, diff ];
2558										}
2559
2560										if ( node === elem ) {
2561											break;
2562										}
2563									}
2564								}
2565							}
2566						}
2567
2568						// Incorporate the offset, then check against cycle size
2569						diff -= last;
2570						return diff === first || ( diff % first === 0 && diff / first >= 0 );
2571					}
2572				};
2573		},
2574
2575		PSEUDO: function( pseudo, argument ) {
2576
2577			// pseudo-class names are case-insensitive
2578			// https://www.w3.org/TR/selectors/#pseudo-classes
2579			// Prioritize by case sensitivity in case custom pseudos are added with uppercase letters
2580			// Remember that setFilters inherits from pseudos
2581			var args,
2582				fn = Expr.pseudos[ pseudo ] || Expr.setFilters[ pseudo.toLowerCase() ] ||
2583					find.error( "unsupported pseudo: " + pseudo );
2584
2585			// The user may use createPseudo to indicate that
2586			// arguments are needed to create the filter function
2587			// just as jQuery does
2588			if ( fn[ expando ] ) {
2589				return fn( argument );
2590			}
2591
2592			// But maintain support for old signatures
2593			if ( fn.length > 1 ) {
2594				args = [ pseudo, pseudo, "", argument ];
2595				return Expr.setFilters.hasOwnProperty( pseudo.toLowerCase() ) ?
2596					markFunction( function( seed, matches ) {
2597						var idx,
2598							matched = fn( seed, argument ),
2599							i = matched.length;
2600						while ( i-- ) {
2601							idx = indexOf.call( seed, matched[ i ] );
2602							seed[ idx ] = !( matches[ idx ] = matched[ i ] );
2603						}
2604					} ) :
2605					function( elem ) {
2606						return fn( elem, 0, args );
2607					};
2608			}
2609
2610			return fn;
2611		}
2612	},
2613
2614	pseudos: {
2615
2616		// Potentially complex pseudos
2617		not: markFunction( function( selector ) {
2618
2619			// Trim the selector passed to compile
2620			// to avoid treating leading and trailing
2621			// spaces as combinators
2622			var input = [],
2623				results = [],
2624				matcher = compile( selector.replace( rtrimCSS, "$1" ) );
2625
2626			return matcher[ expando ] ?
2627				markFunction( function( seed, matches, _context, xml ) {
2628					var elem,
2629						unmatched = matcher( seed, null, xml, [] ),
2630						i = seed.length;
2631
2632					// Match elements unmatched by `matcher`
2633					while ( i-- ) {
2634						if ( ( elem = unmatched[ i ] ) ) {
2635							seed[ i ] = !( matches[ i ] = elem );
2636						}
2637					}
2638				} ) :
2639				function( elem, _context, xml ) {
2640					input[ 0 ] = elem;
2641					matcher( input, null, xml, results );
2642
2643					// Don't keep the element
2644					// (see https://github.com/jquery/sizzle/issues/299)
2645					input[ 0 ] = null;
2646					return !results.pop();
2647				};
2648		} ),
2649
2650		has: markFunction( function( selector ) {
2651			return function( elem ) {
2652				return find( selector, elem ).length > 0;
2653			};
2654		} ),
2655
2656		contains: markFunction( function( text ) {
2657			text = text.replace( runescape, funescape );
2658			return function( elem ) {
2659				return ( elem.textContent || jQuery.text( elem ) ).indexOf( text ) > -1;
2660			};
2661		} ),
2662
2663		// "Whether an element is represented by a :lang() selector
2664		// is based solely on the element's language value
2665		// being equal to the identifier C,
2666		// or beginning with the identifier C immediately followed by "-".
2667		// The matching of C against the element's language value is performed case-insensitively.
2668		// The identifier C does not have to be a valid language name."
2669		// https://www.w3.org/TR/selectors/#lang-pseudo
2670		lang: markFunction( function( lang ) {
2671
2672			// lang value must be a valid identifier
2673			if ( !ridentifier.test( lang || "" ) ) {
2674				find.error( "unsupported lang: " + lang );
2675			}
2676			lang = lang.replace( runescape, funescape ).toLowerCase();
2677			return function( elem ) {
2678				var elemLang;
2679				do {
2680					if ( ( elemLang = documentIsHTML ?
2681						elem.lang :
2682						elem.getAttribute( "xml:lang" ) || elem.getAttribute( "lang" ) ) ) {
2683
2684						elemLang = elemLang.toLowerCase();
2685						return elemLang === lang || elemLang.indexOf( lang + "-" ) === 0;
2686					}
2687				} while ( ( elem = elem.parentNode ) && elem.nodeType === 1 );
2688				return false;
2689			};
2690		} ),
2691
2692		// Miscellaneous
2693		target: function( elem ) {
2694			var hash = window.location && window.location.hash;
2695			return hash && hash.slice( 1 ) === elem.id;
2696		},
2697
2698		root: function( elem ) {
2699			return elem === documentElement;
2700		},
2701
2702		focus: function( elem ) {
2703			return elem === safeActiveElement() &&
2704				document.hasFocus() &&
2705				!!( elem.type || elem.href || ~elem.tabIndex );
2706		},
2707
2708		// Boolean properties
2709		enabled: createDisabledPseudo( false ),
2710		disabled: createDisabledPseudo( true ),
2711
2712		checked: function( elem ) {
2713
2714			// In CSS3, :checked should return both checked and selected elements
2715			// https://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked
2716			return ( nodeName( elem, "input" ) && !!elem.checked ) ||
2717				( nodeName( elem, "option" ) && !!elem.selected );
2718		},
2719
2720		selected: function( elem ) {
2721
2722			// Support: IE <=11+
2723			// Accessing the selectedIndex property
2724			// forces the browser to treat the default option as
2725			// selected when in an optgroup.
2726			if ( elem.parentNode ) {
2727				// eslint-disable-next-line no-unused-expressions
2728				elem.parentNode.selectedIndex;
2729			}
2730
2731			return elem.selected === true;
2732		},
2733
2734		// Contents
2735		empty: function( elem ) {
2736
2737			// https://www.w3.org/TR/selectors/#empty-pseudo
2738			// :empty is negated by element (1) or content nodes (text: 3; cdata: 4; entity ref: 5),
2739			//   but not by others (comment: 8; processing instruction: 7; etc.)
2740			// nodeType < 6 works because attributes (2) do not appear as children
2741			for ( elem = elem.firstChild; elem; elem = elem.nextSibling ) {
2742				if ( elem.nodeType < 6 ) {
2743					return false;
2744				}
2745			}
2746			return true;
2747		},
2748
2749		parent: function( elem ) {
2750			return !Expr.pseudos.empty( elem );
2751		},
2752
2753		// Element/input types
2754		header: function( elem ) {
2755			return rheader.test( elem.nodeName );
2756		},
2757
2758		input: function( elem ) {
2759			return rinputs.test( elem.nodeName );
2760		},
2761
2762		button: function( elem ) {
2763			return nodeName( elem, "input" ) && elem.type === "button" ||
2764				nodeName( elem, "button" );
2765		},
2766
2767		text: function( elem ) {
2768			var attr;
2769			return nodeName( elem, "input" ) && elem.type === "text" &&
2770
2771				// Support: IE <10 only
2772				// New HTML5 attribute values (e.g., "search") appear
2773				// with elem.type === "text"
2774				( ( attr = elem.getAttribute( "type" ) ) == null ||
2775					attr.toLowerCase() === "text" );
2776		},
2777
2778		// Position-in-collection
2779		first: createPositionalPseudo( function() {
2780			return [ 0 ];
2781		} ),
2782
2783		last: createPositionalPseudo( function( _matchIndexes, length ) {
2784			return [ length - 1 ];
2785		} ),
2786
2787		eq: createPositionalPseudo( function( _matchIndexes, length, argument ) {
2788			return [ argument < 0 ? argument + length : argument ];
2789		} ),
2790
2791		even: createPositionalPseudo( function( matchIndexes, length ) {
2792			var i = 0;
2793			for ( ; i < length; i += 2 ) {
2794				matchIndexes.push( i );
2795			}
2796			return matchIndexes;
2797		} ),
2798
2799		odd: createPositionalPseudo( function( matchIndexes, length ) {
2800			var i = 1;
2801			for ( ; i < length; i += 2 ) {
2802				matchIndexes.push( i );
2803			}
2804			return matchIndexes;
2805		} ),
2806
2807		lt: createPositionalPseudo( function( matchIndexes, length, argument ) {
2808			var i;
2809
2810			if ( argument < 0 ) {
2811				i = argument + length;
2812			} else if ( argument > length ) {
2813				i = length;
2814			} else {
2815				i = argument;
2816			}
2817
2818			for ( ; --i >= 0; ) {
2819				matchIndexes.push( i );
2820			}
2821			return matchIndexes;
2822		} ),
2823
2824		gt: createPositionalPseudo( function( matchIndexes, length, argument ) {
2825			var i = argument < 0 ? argument + length : argument;
2826			for ( ; ++i < length; ) {
2827				matchIndexes.push( i );
2828			}
2829			return matchIndexes;
2830		} )
2831	}
2832};
2833
2834Expr.pseudos.nth = Expr.pseudos.eq;
2835
2836// Add button/input type pseudos
2837for ( i in { radio: true, checkbox: true, file: true, password: true, image: true } ) {
2838	Expr.pseudos[ i ] = createInputPseudo( i );
2839}
2840for ( i in { submit: true, reset: true } ) {
2841	Expr.pseudos[ i ] = createButtonPseudo( i );
2842}
2843
2844// Easy API for creating new setFilters
2845function setFilters() {}
2846setFilters.prototype = Expr.filters = Expr.pseudos;
2847Expr.setFilters = new setFilters();
2848
2849function tokenize( selector, parseOnly ) {
2850	var matched, match, tokens, type,
2851		soFar, groups, preFilters,
2852		cached = tokenCache[ selector + " " ];
2853
2854	if ( cached ) {
2855		return parseOnly ? 0 : cached.slice( 0 );
2856	}
2857
2858	soFar = selector;
2859	groups = [];
2860	preFilters = Expr.preFilter;
2861
2862	while ( soFar ) {
2863
2864		// Comma and first run
2865		if ( !matched || ( match = rcomma.exec( soFar ) ) ) {
2866			if ( match ) {
2867
2868				// Don't consume trailing commas as valid
2869				soFar = soFar.slice( match[ 0 ].length ) || soFar;
2870			}
2871			groups.push( ( tokens = [] ) );
2872		}
2873
2874		matched = false;
2875
2876		// Combinators
2877		if ( ( match = rleadingCombinator.exec( soFar ) ) ) {
2878			matched = match.shift();
2879			tokens.push( {
2880				value: matched,
2881
2882				// Cast descendant combinators to space
2883				type: match[ 0 ].replace( rtrimCSS, " " )
2884			} );
2885			soFar = soFar.slice( matched.length );
2886		}
2887
2888		// Filters
2889		for ( type in Expr.filter ) {
2890			if ( ( match = matchExpr[ type ].exec( soFar ) ) && ( !preFilters[ type ] ||
2891				( match = preFilters[ type ]( match ) ) ) ) {
2892				matched = match.shift();
2893				tokens.push( {
2894					value: matched,
2895					type: type,
2896					matches: match
2897				} );
2898				soFar = soFar.slice( matched.length );
2899			}
2900		}
2901
2902		if ( !matched ) {
2903			break;
2904		}
2905	}
2906
2907	// Return the length of the invalid excess
2908	// if we're just parsing
2909	// Otherwise, throw an error or return tokens
2910	if ( parseOnly ) {
2911		return soFar.length;
2912	}
2913
2914	return soFar ?
2915		find.error( selector ) :
2916
2917		// Cache the tokens
2918		tokenCache( selector, groups ).slice( 0 );
2919}
2920
2921function toSelector( tokens ) {
2922	var i = 0,
2923		len = tokens.length,
2924		selector = "";
2925	for ( ; i < len; i++ ) {
2926		selector += tokens[ i ].value;
2927	}
2928	return selector;
2929}
2930
2931function addCombinator( matcher, combinator, base ) {
2932	var dir = combinator.dir,
2933		skip = combinator.next,
2934		key = skip || dir,
2935		checkNonElements = base && key === "parentNode",
2936		doneName = done++;
2937
2938	return combinator.first ?
2939
2940		// Check against closest ancestor/preceding element
2941		function( elem, context, xml ) {
2942			while ( ( elem = elem[ dir ] ) ) {
2943				if ( elem.nodeType === 1 || checkNonElements ) {
2944					return matcher( elem, context, xml );
2945				}
2946			}
2947			return false;
2948		} :
2949
2950		// Check against all ancestor/preceding elements
2951		function( elem, context, xml ) {
2952			var oldCache, outerCache,
2953				newCache = [ dirruns, doneName ];
2954
2955			// We can't set arbitrary data on XML nodes, so they don't benefit from combinator caching
2956			if ( xml ) {
2957				while ( ( elem = elem[ dir ] ) ) {
2958					if ( elem.nodeType === 1 || checkNonElements ) {
2959						if ( matcher( elem, context, xml ) ) {
2960							return true;
2961						}
2962					}
2963				}
2964			} else {
2965				while ( ( elem = elem[ dir ] ) ) {
2966					if ( elem.nodeType === 1 || checkNonElements ) {
2967						outerCache = elem[ expando ] || ( elem[ expando ] = {} );
2968
2969						if ( skip && nodeName( elem, skip ) ) {
2970							elem = elem[ dir ] || elem;
2971						} else if ( ( oldCache = outerCache[ key ] ) &&
2972							oldCache[ 0 ] === dirruns && oldCache[ 1 ] === doneName ) {
2973
2974							// Assign to newCache so results back-propagate to previous elements
2975							return ( newCache[ 2 ] = oldCache[ 2 ] );
2976						} else {
2977
2978							// Reuse newcache so results back-propagate to previous elements
2979							outerCache[ key ] = newCache;
2980
2981							// A match means we're done; a fail means we have to keep checking
2982							if ( ( newCache[ 2 ] = matcher( elem, context, xml ) ) ) {
2983								return true;
2984							}
2985						}
2986					}
2987				}
2988			}
2989			return false;
2990		};
2991}
2992
2993function elementMatcher( matchers ) {
2994	return matchers.length > 1 ?
2995		function( elem, context, xml ) {
2996			var i = matchers.length;
2997			while ( i-- ) {
2998				if ( !matchers[ i ]( elem, context, xml ) ) {
2999					return false;
3000				}
3001			}
3002			return true;
3003		} :
3004		matchers[ 0 ];
3005}
3006
3007function multipleContexts( selector, contexts, results ) {
3008	var i = 0,
3009		len = contexts.length;
3010	for ( ; i < len; i++ ) {
3011		find( selector, contexts[ i ], results );
3012	}
3013	return results;
3014}
3015
3016function condense( unmatched, map, filter, context, xml ) {
3017	var elem,
3018		newUnmatched = [],
3019		i = 0,
3020		len = unmatched.length,
3021		mapped = map != null;
3022
3023	for ( ; i < len; i++ ) {
3024		if ( ( elem = unmatched[ i ] ) ) {
3025			if ( !filter || filter( elem, context, xml ) ) {
3026				newUnmatched.push( elem );
3027				if ( mapped ) {
3028					map.push( i );
3029				}
3030			}
3031		}
3032	}
3033
3034	return newUnmatched;
3035}
3036
3037function setMatcher( preFilter, selector, matcher, postFilter, postFinder, postSelector ) {
3038	if ( postFilter && !postFilter[ expando ] ) {
3039		postFilter = setMatcher( postFilter );
3040	}
3041	if ( postFinder && !postFinder[ expando ] ) {
3042		postFinder = setMatcher( postFinder, postSelector );
3043	}
3044	return markFunction( function( seed, results, context, xml ) {
3045		var temp, i, elem, matcherOut,
3046			preMap = [],
3047			postMap = [],
3048			preexisting = results.length,
3049
3050			// Get initial elements from seed or context
3051			elems = seed ||
3052				multipleContexts( selector || "*",
3053					context.nodeType ? [ context ] : context, [] ),
3054
3055			// Prefilter to get matcher input, preserving a map for seed-results synchronization
3056			matcherIn = preFilter && ( seed || !selector ) ?
3057				condense( elems, preMap, preFilter, context, xml ) :
3058				elems;
3059
3060		if ( matcher ) {
3061
3062			// If we have a postFinder, or filtered seed, or non-seed postFilter
3063			// or preexisting results,
3064			matcherOut = postFinder || ( seed ? preFilter : preexisting || postFilter ) ?
3065
3066				// ...intermediate processing is necessary
3067				[] :
3068
3069				// ...otherwise use results directly
3070				results;
3071
3072			// Find primary matches
3073			matcher( matcherIn, matcherOut, context, xml );
3074		} else {
3075			matcherOut = matcherIn;
3076		}
3077
3078		// Apply postFilter
3079		if ( postFilter ) {
3080			temp = condense( matcherOut, postMap );
3081			postFilter( temp, [], context, xml );
3082
3083			// Un-match failing elements by moving them back to matcherIn
3084			i = temp.length;
3085			while ( i-- ) {
3086				if ( ( elem = temp[ i ] ) ) {
3087					matcherOut[ postMap[ i ] ] = !( matcherIn[ postMap[ i ] ] = elem );
3088				}
3089			}
3090		}
3091
3092		if ( seed ) {
3093			if ( postFinder || preFilter ) {
3094				if ( postFinder ) {
3095
3096					// Get the final matcherOut by condensing this intermediate into postFinder contexts
3097					temp = [];
3098					i = matcherOut.length;
3099					while ( i-- ) {
3100						if ( ( elem = matcherOut[ i ] ) ) {
3101
3102							// Restore matcherIn since elem is not yet a final match
3103							temp.push( ( matcherIn[ i ] = elem ) );
3104						}
3105					}
3106					postFinder( null, ( matcherOut = [] ), temp, xml );
3107				}
3108
3109				// Move matched elements from seed to results to keep them synchronized
3110				i = matcherOut.length;
3111				while ( i-- ) {
3112					if ( ( elem = matcherOut[ i ] ) &&
3113						( temp = postFinder ? indexOf.call( seed, elem ) : preMap[ i ] ) > -1 ) {
3114
3115						seed[ temp ] = !( results[ temp ] = elem );
3116					}
3117				}
3118			}
3119
3120		// Add elements to results, through postFinder if defined
3121		} else {
3122			matcherOut = condense(
3123				matcherOut === results ?
3124					matcherOut.splice( preexisting, matcherOut.length ) :
3125					matcherOut
3126			);
3127			if ( postFinder ) {
3128				postFinder( null, results, matcherOut, xml );
3129			} else {
3130				push.apply( results, matcherOut );
3131			}
3132		}
3133	} );
3134}
3135
3136function matcherFromTokens( tokens ) {
3137	var checkContext, matcher, j,
3138		len = tokens.length,
3139		leadingRelative = Expr.relative[ tokens[ 0 ].type ],
3140		implicitRelative = leadingRelative || Expr.relative[ " " ],
3141		i = leadingRelative ? 1 : 0,
3142
3143		// The foundational matcher ensures that elements are reachable from top-level context(s)
3144		matchContext = addCombinator( function( elem ) {
3145			return elem === checkContext;
3146		}, implicitRelative, true ),
3147		matchAnyContext = addCombinator( function( elem ) {
3148			return indexOf.call( checkContext, elem ) > -1;
3149		}, implicitRelative, true ),
3150		matchers = [ function( elem, context, xml ) {
3151
3152			// Support: IE 11+, Edge 17 - 18+
3153			// IE/Edge sometimes throw a "Permission denied" error when strict-comparing
3154			// two documents; shallow comparisons work.
3155			// eslint-disable-next-line eqeqeq
3156			var ret = ( !leadingRelative && ( xml || context != outermostContext ) ) || (
3157				( checkContext = context ).nodeType ?
3158					matchContext( elem, context, xml ) :
3159					matchAnyContext( elem, context, xml ) );
3160
3161			// Avoid hanging onto element
3162			// (see https://github.com/jquery/sizzle/issues/299)
3163			checkContext = null;
3164			return ret;
3165		} ];
3166
3167	for ( ; i < len; i++ ) {
3168		if ( ( matcher = Expr.relative[ tokens[ i ].type ] ) ) {
3169			matchers = [ addCombinator( elementMatcher( matchers ), matcher ) ];
3170		} else {
3171			matcher = Expr.filter[ tokens[ i ].type ].apply( null, tokens[ i ].matches );
3172
3173			// Return special upon seeing a positional matcher
3174			if ( matcher[ expando ] ) {
3175
3176				// Find the next relative operator (if any) for proper handling
3177				j = ++i;
3178				for ( ; j < len; j++ ) {
3179					if ( Expr.relative[ tokens[ j ].type ] ) {
3180						break;
3181					}
3182				}
3183				return setMatcher(
3184					i > 1 && elementMatcher( matchers ),
3185					i > 1 && toSelector(
3186
3187						// If the preceding token was a descendant combinator, insert an implicit any-element `*`
3188						tokens.slice( 0, i - 1 )
3189							.concat( { value: tokens[ i - 2 ].type === " " ? "*" : "" } )
3190					).replace( rtrimCSS, "$1" ),
3191					matcher,
3192					i < j && matcherFromTokens( tokens.slice( i, j ) ),
3193					j < len && matcherFromTokens( ( tokens = tokens.slice( j ) ) ),
3194					j < len && toSelector( tokens )
3195				);
3196			}
3197			matchers.push( matcher );
3198		}
3199	}
3200
3201	return elementMatcher( matchers );
3202}
3203
3204function matcherFromGroupMatchers( elementMatchers, setMatchers ) {
3205	var bySet = setMatchers.length > 0,
3206		byElement = elementMatchers.length > 0,
3207		superMatcher = function( seed, context, xml, results, outermost ) {
3208			var elem, j, matcher,
3209				matchedCount = 0,
3210				i = "0",
3211				unmatched = seed && [],
3212				setMatched = [],
3213				contextBackup = outermostContext,
3214
3215				// We must always have either seed elements or outermost context
3216				elems = seed || byElement && Expr.find.TAG( "*", outermost ),
3217
3218				// Use integer dirruns iff this is the outermost matcher
3219				dirrunsUnique = ( dirruns += contextBackup == null ? 1 : Math.random() || 0.1 ),
3220				len = elems.length;
3221
3222			if ( outermost ) {
3223
3224				// Support: IE 11+, Edge 17 - 18+
3225				// IE/Edge sometimes throw a "Permission denied" error when strict-comparing
3226				// two documents; shallow comparisons work.
3227				// eslint-disable-next-line eqeqeq
3228				outermostContext = context == document || context || outermost;
3229			}
3230
3231			// Add elements passing elementMatchers directly to results
3232			// Support: iOS <=7 - 9 only
3233			// Tolerate NodeList properties (IE: "length"; Safari: <number>) matching
3234			// elements by id. (see trac-14142)
3235			for ( ; i !== len && ( elem = elems[ i ] ) != null; i++ ) {
3236				if ( byElement && elem ) {
3237					j = 0;
3238
3239					// Support: IE 11+, Edge 17 - 18+
3240					// IE/Edge sometimes throw a "Permission denied" error when strict-comparing
3241					// two documents; shallow comparisons work.
3242					// eslint-disable-next-line eqeqeq
3243					if ( !context && elem.ownerDocument != document ) {
3244						setDocument( elem );
3245						xml = !documentIsHTML;
3246					}
3247					while ( ( matcher = elementMatchers[ j++ ] ) ) {
3248						if ( matcher( elem, context || document, xml ) ) {
3249							push.call( results, elem );
3250							break;
3251						}
3252					}
3253					if ( outermost ) {
3254						dirruns = dirrunsUnique;
3255					}
3256				}
3257
3258				// Track unmatched elements for set filters
3259				if ( bySet ) {
3260
3261					// They will have gone through all possible matchers
3262					if ( ( elem = !matcher && elem ) ) {
3263						matchedCount--;
3264					}
3265
3266					// Lengthen the array for every element, matched or not
3267					if ( seed ) {
3268						unmatched.push( elem );
3269					}
3270				}
3271			}
3272
3273			// `i` is now the count of elements visited above, and adding it to `matchedCount`
3274			// makes the latter nonnegative.
3275			matchedCount += i;
3276
3277			// Apply set filters to unmatched elements
3278			// NOTE: This can be skipped if there are no unmatched elements (i.e., `matchedCount`
3279			// equals `i`), unless we didn't visit _any_ elements in the above loop because we have
3280			// no element matchers and no seed.
3281			// Incrementing an initially-string "0" `i` allows `i` to remain a string only in that
3282			// case, which will result in a "00" `matchedCount` that differs from `i` but is also
3283			// numerically zero.
3284			if ( bySet && i !== matchedCount ) {
3285				j = 0;
3286				while ( ( matcher = setMatchers[ j++ ] ) ) {
3287					matcher( unmatched, setMatched, context, xml );
3288				}
3289
3290				if ( seed ) {
3291
3292					// Reintegrate element matches to eliminate the need for sorting
3293					if ( matchedCount > 0 ) {
3294						while ( i-- ) {
3295							if ( !( unmatched[ i ] || setMatched[ i ] ) ) {
3296								setMatched[ i ] = pop.call( results );
3297							}
3298						}
3299					}
3300
3301					// Discard index placeholder values to get only actual matches
3302					setMatched = condense( setMatched );
3303				}
3304
3305				// Add matches to results
3306				push.apply( results, setMatched );
3307
3308				// Seedless set matches succeeding multiple successful matchers stipulate sorting
3309				if ( outermost && !seed && setMatched.length > 0 &&
3310					( matchedCount + setMatchers.length ) > 1 ) {
3311
3312					jQuery.uniqueSort( results );
3313				}
3314			}
3315
3316			// Override manipulation of globals by nested matchers
3317			if ( outermost ) {
3318				dirruns = dirrunsUnique;
3319				outermostContext = contextBackup;
3320			}
3321
3322			return unmatched;
3323		};
3324
3325	return bySet ?
3326		markFunction( superMatcher ) :
3327		superMatcher;
3328}
3329
3330function compile( selector, match /* Internal Use Only */ ) {
3331	var i,
3332		setMatchers = [],
3333		elementMatchers = [],
3334		cached = compilerCache[ selector + " " ];
3335
3336	if ( !cached ) {
3337
3338		// Generate a function of recursive functions that can be used to check each element
3339		if ( !match ) {
3340			match = tokenize( selector );
3341		}
3342		i = match.length;
3343		while ( i-- ) {
3344			cached = matcherFromTokens( match[ i ] );
3345			if ( cached[ expando ] ) {
3346				setMatchers.push( cached );
3347			} else {
3348				elementMatchers.push( cached );
3349			}
3350		}
3351
3352		// Cache the compiled function
3353		cached = compilerCache( selector,
3354			matcherFromGroupMatchers( elementMatchers, setMatchers ) );
3355
3356		// Save selector and tokenization
3357		cached.selector = selector;
3358	}
3359	return cached;
3360}
3361
3362/**
3363 * A low-level selection function that works with jQuery's compiled
3364 *  selector functions
3365 * @param {String|Function} selector A selector or a pre-compiled
3366 *  selector function built with jQuery selector compile
3367 * @param {Element} context
3368 * @param {Array} [results]
3369 * @param {Array} [seed] A set of elements to match against
3370 */
3371function select( selector, context, results, seed ) {
3372	var i, tokens, token, type, find,
3373		compiled = typeof selector === "function" && selector,
3374		match = !seed && tokenize( ( selector = compiled.selector || selector ) );
3375
3376	results = results || [];
3377
3378	// Try to minimize operations if there is only one selector in the list and no seed
3379	// (the latter of which guarantees us context)
3380	if ( match.length === 1 ) {
3381
3382		// Reduce context if the leading compound selector is an ID
3383		tokens = match[ 0 ] = match[ 0 ].slice( 0 );
3384		if ( tokens.length > 2 && ( token = tokens[ 0 ] ).type === "ID" &&
3385				context.nodeType === 9 && documentIsHTML && Expr.relative[ tokens[ 1 ].type ] ) {
3386
3387			context = ( Expr.find.ID(
3388				token.matches[ 0 ].replace( runescape, funescape ),
3389				context
3390			) || [] )[ 0 ];
3391			if ( !context ) {
3392				return results;
3393
3394			// Precompiled matchers will still verify ancestry, so step up a level
3395			} else if ( compiled ) {
3396				context = context.parentNode;
3397			}
3398
3399			selector = selector.slice( tokens.shift().value.length );
3400		}
3401
3402		// Fetch a seed set for right-to-left matching
3403		i = matchExpr.needsContext.test( selector ) ? 0 : tokens.length;
3404		while ( i-- ) {
3405			token = tokens[ i ];
3406
3407			// Abort if we hit a combinator
3408			if ( Expr.relative[ ( type = token.type ) ] ) {
3409				break;
3410			}
3411			if ( ( find = Expr.find[ type ] ) ) {
3412
3413				// Search, expanding context for leading sibling combinators
3414				if ( ( seed = find(
3415					token.matches[ 0 ].replace( runescape, funescape ),
3416					rsibling.test( tokens[ 0 ].type ) &&
3417						testContext( context.parentNode ) || context
3418				) ) ) {
3419
3420					// If seed is empty or no tokens remain, we can return early
3421					tokens.splice( i, 1 );
3422					selector = seed.length && toSelector( tokens );
3423					if ( !selector ) {
3424						push.apply( results, seed );
3425						return results;
3426					}
3427
3428					break;
3429				}
3430			}
3431		}
3432	}
3433
3434	// Compile and execute a filtering function if one is not provided
3435	// Provide `match` to avoid retokenization if we modified the selector above
3436	( compiled || compile( selector, match ) )(
3437		seed,
3438		context,
3439		!documentIsHTML,
3440		results,
3441		!context || rsibling.test( selector ) && testContext( context.parentNode ) || context
3442	);
3443	return results;
3444}
3445
3446// One-time assignments
3447
3448// Support: Android <=4.0 - 4.1+
3449// Sort stability
3450support.sortStable = expando.split( "" ).sort( sortOrder ).join( "" ) === expando;
3451
3452// Initialize against the default document
3453setDocument();
3454
3455// Support: Android <=4.0 - 4.1+
3456// Detached nodes confoundingly follow *each other*
3457support.sortDetached = assert( function( el ) {
3458
3459	// Should return 1, but returns 4 (following)
3460	return el.compareDocumentPosition( document.createElement( "fieldset" ) ) & 1;
3461} );
3462
3463jQuery.find = find;
3464
3465// Deprecated
3466jQuery.expr[ ":" ] = jQuery.expr.pseudos;
3467jQuery.unique = jQuery.uniqueSort;
3468
3469// These have always been private, but they used to be documented as part of
3470// Sizzle so let's maintain them for now for backwards compatibility purposes.
3471find.compile = compile;
3472find.select = select;
3473find.setDocument = setDocument;
3474find.tokenize = tokenize;
3475
3476find.escape = jQuery.escapeSelector;
3477find.getText = jQuery.text;
3478find.isXML = jQuery.isXMLDoc;
3479find.selectors = jQuery.expr;
3480find.support = jQuery.support;
3481find.uniqueSort = jQuery.uniqueSort;
3482
3483	/* eslint-enable */
3484
3485} )();
3486
3487
3488var dir = function( elem, dir, until ) {
3489	var matched = [],
3490		truncate = until !== undefined;
3491
3492	while ( ( elem = elem[ dir ] ) && elem.nodeType !== 9 ) {
3493		if ( elem.nodeType === 1 ) {
3494			if ( truncate && jQuery( elem ).is( until ) ) {
3495				break;
3496			}
3497			matched.push( elem );
3498		}
3499	}
3500	return matched;
3501};
3502
3503
3504var siblings = function( n, elem ) {
3505	var matched = [];
3506
3507	for ( ; n; n = n.nextSibling ) {
3508		if ( n.nodeType === 1 && n !== elem ) {
3509			matched.push( n );
3510		}
3511	}
3512
3513	return matched;
3514};
3515
3516
3517var rneedsContext = jQuery.expr.match.needsContext;
3518
3519var rsingleTag = ( /^<([a-z][^\/\0>:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i );
3520
3521
3522
3523// Implement the identical functionality for filter and not
3524function winnow( elements, qualifier, not ) {
3525	if ( isFunction( qualifier ) ) {
3526		return jQuery.grep( elements, function( elem, i ) {
3527			return !!qualifier.call( elem, i, elem ) !== not;
3528		} );
3529	}
3530
3531	// Single element
3532	if ( qualifier.nodeType ) {
3533		return jQuery.grep( elements, function( elem ) {
3534			return ( elem === qualifier ) !== not;
3535		} );
3536	}
3537
3538	// Arraylike of elements (jQuery, arguments, Array)
3539	if ( typeof qualifier !== "string" ) {
3540		return jQuery.grep( elements, function( elem ) {
3541			return ( indexOf.call( qualifier, elem ) > -1 ) !== not;
3542		} );
3543	}
3544
3545	// Filtered directly for both simple and complex selectors
3546	return jQuery.filter( qualifier, elements, not );
3547}
3548
3549jQuery.filter = function( expr, elems, not ) {
3550	var elem = elems[ 0 ];
3551
3552	if ( not ) {
3553		expr = ":not(" + expr + ")";
3554	}
3555
3556	if ( elems.length === 1 && elem.nodeType === 1 ) {
3557		return jQuery.find.matchesSelector( elem, expr ) ? [ elem ] : [];
3558	}
3559
3560	return jQuery.find.matches( expr, jQuery.grep( elems, function( elem ) {
3561		return elem.nodeType === 1;
3562	} ) );
3563};
3564
3565jQuery.fn.extend( {
3566	find: function( selector ) {
3567		var i, ret,
3568			len = this.length,
3569			self = this;
3570
3571		if ( typeof selector !== "string" ) {
3572			return this.pushStack( jQuery( selector ).filter( function() {
3573				for ( i = 0; i < len; i++ ) {
3574					if ( jQuery.contains( self[ i ], this ) ) {
3575						return true;
3576					}
3577				}
3578			} ) );
3579		}
3580
3581		ret = this.pushStack( [] );
3582
3583		for ( i = 0; i < len; i++ ) {
3584			jQuery.find( selector, self[ i ], ret );
3585		}
3586
3587		return len > 1 ? jQuery.uniqueSort( ret ) : ret;
3588	},
3589	filter: function( selector ) {
3590		return this.pushStack( winnow( this, selector || [], false ) );
3591	},
3592	not: function( selector ) {
3593		return this.pushStack( winnow( this, selector || [], true ) );
3594	},
3595	is: function( selector ) {
3596		return !!winnow(
3597			this,
3598
3599			// If this is a positional/relative selector, check membership in the returned set
3600			// so $("p:first").is("p:last") won't return true for a doc with two "p".
3601			typeof selector === "string" && rneedsContext.test( selector ) ?
3602				jQuery( selector ) :
3603				selector || [],
3604			false
3605		).length;
3606	}
3607} );
3608
3609
3610// Initialize a jQuery object
3611
3612
3613// A central reference to the root jQuery(document)
3614var rootjQuery,
3615
3616	// A simple way to check for HTML strings
3617	// Prioritize #id over <tag> to avoid XSS via location.hash (trac-9521)
3618	// Strict HTML recognition (trac-11290: must start with <)
3619	// Shortcut simple #id case for speed
3620	rquickExpr = /^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]+))$/,
3621
3622	init = jQuery.fn.init = function( selector, context, root ) {
3623		var match, elem;
3624
3625		// HANDLE: $(""), $(null), $(undefined), $(false)
3626		if ( !selector ) {
3627			return this;
3628		}
3629
3630		// Method init() accepts an alternate rootjQuery
3631		// so migrate can support jQuery.sub (gh-2101)
3632		root = root || rootjQuery;
3633
3634		// Handle HTML strings
3635		if ( typeof selector === "string" ) {
3636			if ( selector[ 0 ] === "<" &&
3637				selector[ selector.length - 1 ] === ">" &&
3638				selector.length >= 3 ) {
3639
3640				// Assume that strings that start and end with <> are HTML and skip the regex check
3641				match = [ null, selector, null ];
3642
3643			} else {
3644				match = rquickExpr.exec( selector );
3645			}
3646
3647			// Match html or make sure no context is specified for #id
3648			if ( match && ( match[ 1 ] || !context ) ) {
3649
3650				// HANDLE: $(html) -> $(array)
3651				if ( match[ 1 ] ) {
3652					context = context instanceof jQuery ? context[ 0 ] : context;
3653
3654					// Option to run scripts is true for back-compat
3655					// Intentionally let the error be thrown if parseHTML is not present
3656					jQuery.merge( this, jQuery.parseHTML(
3657						match[ 1 ],
3658						context && context.nodeType ? context.ownerDocument || context : document,
3659						true
3660					) );
3661
3662					// HANDLE: $(html, props)
3663					if ( rsingleTag.test( match[ 1 ] ) && jQuery.isPlainObject( context ) ) {
3664						for ( match in context ) {
3665
3666							// Properties of context are called as methods if possible
3667							if ( isFunction( this[ match ] ) ) {
3668								this[ match ]( context[ match ] );
3669
3670							// ...and otherwise set as attributes
3671							} else {
3672								this.attr( match, context[ match ] );
3673							}
3674						}
3675					}
3676
3677					return this;
3678
3679				// HANDLE: $(#id)
3680				} else {
3681					elem = document.getElementById( match[ 2 ] );
3682
3683					if ( elem ) {
3684
3685						// Inject the element directly into the jQuery object
3686						this[ 0 ] = elem;
3687						this.length = 1;
3688					}
3689					return this;
3690				}
3691
3692			// HANDLE: $(expr, $(...))
3693			} else if ( !context || context.jquery ) {
3694				return ( context || root ).find( selector );
3695
3696			// HANDLE: $(expr, context)
3697			// (which is just equivalent to: $(context).find(expr)
3698			} else {
3699				return this.constructor( context ).find( selector );
3700			}
3701
3702		// HANDLE: $(DOMElement)
3703		} else if ( selector.nodeType ) {
3704			this[ 0 ] = selector;
3705			this.length = 1;
3706			return this;
3707
3708		// HANDLE: $(function)
3709		// Shortcut for document ready
3710		} else if ( isFunction( selector ) ) {
3711			return root.ready !== undefined ?
3712				root.ready( selector ) :
3713
3714				// Execute immediately if ready is not present
3715				selector( jQuery );
3716		}
3717
3718		return jQuery.makeArray( selector, this );
3719	};
3720
3721// Give the init function the jQuery prototype for later instantiation
3722init.prototype = jQuery.fn;
3723
3724// Initialize central reference
3725rootjQuery = jQuery( document );
3726
3727
3728var rparentsprev = /^(?:parents|prev(?:Until|All))/,
3729
3730	// Methods guaranteed to produce a unique set when starting from a unique set
3731	guaranteedUnique = {
3732		children: true,
3733		contents: true,
3734		next: true,
3735		prev: true
3736	};
3737
3738jQuery.fn.extend( {
3739	has: function( target ) {
3740		var targets = jQuery( target, this ),
3741			l = targets.length;
3742
3743		return this.filter( function() {
3744			var i = 0;
3745			for ( ; i < l; i++ ) {
3746				if ( jQuery.contains( this, targets[ i ] ) ) {
3747					return true;
3748				}
3749			}
3750		} );
3751	},
3752
3753	closest: function( selectors, context ) {
3754		var cur,
3755			i = 0,
3756			l = this.length,
3757			matched = [],
3758			targets = typeof selectors !== "string" && jQuery( selectors );
3759
3760		// Positional selectors never match, since there's no _selection_ context
3761		if ( !rneedsContext.test( selectors ) ) {
3762			for ( ; i < l; i++ ) {
3763				for ( cur = this[ i ]; cur && cur !== context; cur = cur.parentNode ) {
3764
3765					// Always skip document fragments
3766					if ( cur.nodeType < 11 && ( targets ?
3767						targets.index( cur ) > -1 :
3768
3769						// Don't pass non-elements to jQuery#find
3770						cur.nodeType === 1 &&
3771							jQuery.find.matchesSelector( cur, selectors ) ) ) {
3772
3773						matched.push( cur );
3774						break;
3775					}
3776				}
3777			}
3778		}
3779
3780		return this.pushStack( matched.length > 1 ? jQuery.uniqueSort( matched ) : matched );
3781	},
3782
3783	// Determine the position of an element within the set
3784	index: function( elem ) {
3785
3786		// No argument, return index in parent
3787		if ( !elem ) {
3788			return ( this[ 0 ] && this[ 0 ].parentNode ) ? this.first().prevAll().length : -1;
3789		}
3790
3791		// Index in selector
3792		if ( typeof elem === "string" ) {
3793			return indexOf.call( jQuery( elem ), this[ 0 ] );
3794		}
3795
3796		// Locate the position of the desired element
3797		return indexOf.call( this,
3798
3799			// If it receives a jQuery object, the first element is used
3800			elem.jquery ? elem[ 0 ] : elem
3801		);
3802	},
3803
3804	add: function( selector, context ) {
3805		return this.pushStack(
3806			jQuery.uniqueSort(
3807				jQuery.merge( this.get(), jQuery( selector, context ) )
3808			)
3809		);
3810	},
3811
3812	addBack: function( selector ) {
3813		return this.add( selector == null ?
3814			this.prevObject : this.prevObject.filter( selector )
3815		);
3816	}
3817} );
3818
3819function sibling( cur, dir ) {
3820	while ( ( cur = cur[ dir ] ) && cur.nodeType !== 1 ) {}
3821	return cur;
3822}
3823
3824jQuery.each( {
3825	parent: function( elem ) {
3826		var parent = elem.parentNode;
3827		return parent && parent.nodeType !== 11 ? parent : null;
3828	},
3829	parents: function( elem ) {
3830		return dir( elem, "parentNode" );
3831	},
3832	parentsUntil: function( elem, _i, until ) {
3833		return dir( elem, "parentNode", until );
3834	},
3835	next: function( elem ) {
3836		return sibling( elem, "nextSibling" );
3837	},
3838	prev: function( elem ) {
3839		return sibling( elem, "previousSibling" );
3840	},
3841	nextAll: function( elem ) {
3842		return dir( elem, "nextSibling" );
3843	},
3844	prevAll: function( elem ) {
3845		return dir( elem, "previousSibling" );
3846	},
3847	nextUntil: function( elem, _i, until ) {
3848		return dir( elem, "nextSibling", until );
3849	},
3850	prevUntil: function( elem, _i, until ) {
3851		return dir( elem, "previousSibling", until );
3852	},
3853	siblings: function( elem ) {
3854		return siblings( ( elem.parentNode || {} ).firstChild, elem );
3855	},
3856	children: function( elem ) {
3857		return siblings( elem.firstChild );
3858	},
3859	contents: function( elem ) {
3860		if ( elem.contentDocument != null &&
3861
3862			// Support: IE 11+
3863			// <object> elements with no `data` attribute has an object
3864			// `contentDocument` with a `null` prototype.
3865			getProto( elem.contentDocument ) ) {
3866
3867			return elem.contentDocument;
3868		}
3869
3870		// Support: IE 9 - 11 only, iOS 7 only, Android Browser <=4.3 only
3871		// Treat the template element as a regular one in browsers that
3872		// don't support it.
3873		if ( nodeName( elem, "template" ) ) {
3874			elem = elem.content || elem;
3875		}
3876
3877		return jQuery.merge( [], elem.childNodes );
3878	}
3879}, function( name, fn ) {
3880	jQuery.fn[ name ] = function( until, selector ) {
3881		var matched = jQuery.map( this, fn, until );
3882
3883		if ( name.slice( -5 ) !== "Until" ) {
3884			selector = until;
3885		}
3886
3887		if ( selector && typeof selector === "string" ) {
3888			matched = jQuery.filter( selector, matched );
3889		}
3890
3891		if ( this.length > 1 ) {
3892
3893			// Remove duplicates
3894			if ( !guaranteedUnique[ name ] ) {
3895				jQuery.uniqueSort( matched );
3896			}
3897
3898			// Reverse order for parents* and prev-derivatives
3899			if ( rparentsprev.test( name ) ) {
3900				matched.reverse();
3901			}
3902		}
3903
3904		return this.pushStack( matched );
3905	};
3906} );
3907var rnothtmlwhite = ( /[^\x20\t\r\n\f]+/g );
3908
3909
3910
3911// Convert String-formatted options into Object-formatted ones
3912function createOptions( options ) {
3913	var object = {};
3914	jQuery.each( options.match( rnothtmlwhite ) || [], function( _, flag ) {
3915		object[ flag ] = true;
3916	} );
3917	return object;
3918}
3919
3920/*
3921 * Create a callback list using the following parameters:
3922 *
3923 *	options: an optional list of space-separated options that will change how
3924 *			the callback list behaves or a more traditional option object
3925 *
3926 * By default a callback list will act like an event callback list and can be
3927 * "fired" multiple times.
3928 *
3929 * Possible options:
3930 *
3931 *	once:			will ensure the callback list can only be fired once (like a Deferred)
3932 *
3933 *	memory:			will keep track of previous values and will call any callback added
3934 *					after the list has been fired right away with the latest "memorized"
3935 *					values (like a Deferred)
3936 *
3937 *	unique:			will ensure a callback can only be added once (no duplicate in the list)
3938 *
3939 *	stopOnFalse:	interrupt callings when a callback returns false
3940 *
3941 */
3942jQuery.Callbacks = function( options ) {
3943
3944	// Convert options from String-formatted to Object-formatted if needed
3945	// (we check in cache first)
3946	options = typeof options === "string" ?
3947		createOptions( options ) :
3948		jQuery.extend( {}, options );
3949
3950	var // Flag to know if list is currently firing
3951		firing,
3952
3953		// Last fire value for non-forgettable lists
3954		memory,
3955
3956		// Flag to know if list was already fired
3957		fired,
3958
3959		// Flag to prevent firing
3960		locked,
3961
3962		// Actual callback list
3963		list = [],
3964
3965		// Queue of execution data for repeatable lists
3966		queue = [],
3967
3968		// Index of currently firing callback (modified by add/remove as needed)
3969		firingIndex = -1,
3970
3971		// Fire callbacks
3972		fire = function() {
3973
3974			// Enforce single-firing
3975			locked = locked || options.once;
3976
3977			// Execute callbacks for all pending executions,
3978			// respecting firingIndex overrides and runtime changes
3979			fired = firing = true;
3980			for ( ; queue.length; firingIndex = -1 ) {
3981				memory = queue.shift();
3982				while ( ++firingIndex < list.length ) {
3983
3984					// Run callback and check for early termination
3985					if ( list[ firingIndex ].apply( memory[ 0 ], memory[ 1 ] ) === false &&
3986						options.stopOnFalse ) {
3987
3988						// Jump to end and forget the data so .add doesn't re-fire
3989						firingIndex = list.length;
3990						memory = false;
vendor: 4,918 bytes, lines 3991-4200
3991					}
3992				}
3993			}
3994
3995			// Forget the data if we're done with it
3996			if ( !options.memory ) {
3997				memory = false;
3998			}
3999
4000			firing = false;
4001
4002			// Clean up if we're done firing for good
4003			if ( locked ) {
4004
4005				// Keep an empty list if we have data for future add calls
4006				if ( memory ) {
4007					list = [];
4008
4009				// Otherwise, this object is spent
4010				} else {
4011					list = "";
4012				}
4013			}
4014		},
4015
4016		// Actual Callbacks object
4017		self = {
4018
4019			// Add a callback or a collection of callbacks to the list
4020			add: function() {
4021				if ( list ) {
4022
4023					// If we have memory from a past run, we should fire after adding
4024					if ( memory && !firing ) {
4025						firingIndex = list.length - 1;
4026						queue.push( memory );
4027					}
4028
4029					( function add( args ) {
4030						jQuery.each( args, function( _, arg ) {
4031							if ( isFunction( arg ) ) {
4032								if ( !options.unique || !self.has( arg ) ) {
4033									list.push( arg );
4034								}
4035							} else if ( arg && arg.length && toType( arg ) !== "string" ) {
4036
4037								// Inspect recursively
4038								add( arg );
4039							}
4040						} );
4041					} )( arguments );
4042
4043					if ( memory && !firing ) {
4044						fire();
4045					}
4046				}
4047				return this;
4048			},
4049
4050			// Remove a callback from the list
4051			remove: function() {
4052				jQuery.each( arguments, function( _, arg ) {
4053					var index;
4054					while ( ( index = jQuery.inArray( arg, list, index ) ) > -1 ) {
4055						list.splice( index, 1 );
4056
4057						// Handle firing indexes
4058						if ( index <= firingIndex ) {
4059							firingIndex--;
4060						}
4061					}
4062				} );
4063				return this;
4064			},
4065
4066			// Check if a given callback is in the list.
4067			// If no argument is given, return whether or not list has callbacks attached.
4068			has: function( fn ) {
4069				return fn ?
4070					jQuery.inArray( fn, list ) > -1 :
4071					list.length > 0;
4072			},
4073
4074			// Remove all callbacks from the list
4075			empty: function() {
4076				if ( list ) {
4077					list = [];
4078				}
4079				return this;
4080			},
4081
4082			// Disable .fire and .add
4083			// Abort any current/pending executions
4084			// Clear all callbacks and values
4085			disable: function() {
4086				locked = queue = [];
4087				list = memory = "";
4088				return this;
4089			},
4090			disabled: function() {
4091				return !list;
4092			},
4093
4094			// Disable .fire
4095			// Also disable .add unless we have memory (since it would have no effect)
4096			// Abort any pending executions
4097			lock: function() {
4098				locked = queue = [];
4099				if ( !memory && !firing ) {
4100					list = memory = "";
4101				}
4102				return this;
4103			},
4104			locked: function() {
4105				return !!locked;
4106			},
4107
4108			// Call all callbacks with the given context and arguments
4109			fireWith: function( context, args ) {
4110				if ( !locked ) {
4111					args = args || [];
4112					args = [ context, args.slice ? args.slice() : args ];
4113					queue.push( args );
4114					if ( !firing ) {
4115						fire();
4116					}
4117				}
4118				return this;
4119			},
4120
4121			// Call all the callbacks with the given arguments
4122			fire: function() {
4123				self.fireWith( this, arguments );
4124				return this;
4125			},
4126
4127			// To know if the callbacks have already been called at least once
4128			fired: function() {
4129				return !!fired;
4130			}
4131		};
4132
4133	return self;
4134};
4135
4136
4137function Identity( v ) {
4138	return v;
4139}
4140function Thrower( ex ) {
4141	throw ex;
4142}
4143
4144function adoptValue( value, resolve, reject, noValue ) {
4145	var method;
4146
4147	try {
4148
4149		// Check for promise aspect first to privilege synchronous behavior
4150		if ( value && isFunction( ( method = value.promise ) ) ) {
4151			method.call( value ).done( resolve ).fail( reject );
4152
4153		// Other thenables
4154		} else if ( value && isFunction( ( method = value.then ) ) ) {
4155			method.call( value, resolve, reject );
4156
4157		// Other non-thenables
4158		} else {
4159
4160			// Control `resolve` arguments by letting Array#slice cast boolean `noValue` to integer:
4161			// * false: [ value ].slice( 0 ) => resolve( value )
4162			// * true: [ value ].slice( 1 ) => resolve()
4163			resolve.apply( undefined, [ value ].slice( noValue ) );
4164		}
4165
4166	// For Promises/A+, convert exceptions into rejections
4167	// Since jQuery.when doesn't unwrap thenables, we can skip the extra checks appearing in
4168	// Deferred#then to conditionally suppress rejection.
4169	} catch ( value ) {
4170
4171		// Support: Android 4.0 only
4172		// Strict mode functions invoked without .call/.apply get global-object context
4173		reject.apply( undefined, [ value ] );
4174	}
4175}
4176
4177jQuery.extend( {
4178
4179	Deferred: function( func ) {
4180		var tuples = [
4181
4182				// action, add listener, callbacks,
4183				// ... .then handlers, argument index, [final state]
4184				[ "notify", "progress", jQuery.Callbacks( "memory" ),
4185					jQuery.Callbacks( "memory" ), 2 ],
4186				[ "resolve", "done", jQuery.Callbacks( "once memory" ),
4187					jQuery.Callbacks( "once memory" ), 0, "resolved" ],
4188				[ "reject", "fail", jQuery.Callbacks( "once memory" ),
4189					jQuery.Callbacks( "once memory" ), 1, "rejected" ]
4190			],
4191			state = "pending",
4192			promise = {
4193				state: function() {
4194					return state;
4195				},
4196				always: function() {
4197					deferred.done( arguments ).fail( arguments );
4198					return this;
4199				},
4200				"catch": function( fn ) {
vendor: 70,494 bytes, lines 4201-6812
4201					return promise.then( null, fn );
4202				},
4203
4204				// Keep pipe for back-compat
4205				pipe: function( /* fnDone, fnFail, fnProgress */ ) {
4206					var fns = arguments;
4207
4208					return jQuery.Deferred( function( newDefer ) {
4209						jQuery.each( tuples, function( _i, tuple ) {
4210
4211							// Map tuples (progress, done, fail) to arguments (done, fail, progress)
4212							var fn = isFunction( fns[ tuple[ 4 ] ] ) && fns[ tuple[ 4 ] ];
4213
4214							// deferred.progress(function() { bind to newDefer or newDefer.notify })
4215							// deferred.done(function() { bind to newDefer or newDefer.resolve })
4216							// deferred.fail(function() { bind to newDefer or newDefer.reject })
4217							deferred[ tuple[ 1 ] ]( function() {
4218								var returned = fn && fn.apply( this, arguments );
4219								if ( returned && isFunction( returned.promise ) ) {
4220									returned.promise()
4221										.progress( newDefer.notify )
4222										.done( newDefer.resolve )
4223										.fail( newDefer.reject );
4224								} else {
4225									newDefer[ tuple[ 0 ] + "With" ](
4226										this,
4227										fn ? [ returned ] : arguments
4228									);
4229								}
4230							} );
4231						} );
4232						fns = null;
4233					} ).promise();
4234				},
4235				then: function( onFulfilled, onRejected, onProgress ) {
4236					var maxDepth = 0;
4237					function resolve( depth, deferred, handler, special ) {
4238						return function() {
4239							var that = this,
4240								args = arguments,
4241								mightThrow = function() {
4242									var returned, then;
4243
4244									// Support: Promises/A+ section 2.3.3.3.3
4245									// https://promisesaplus.com/#point-59
4246									// Ignore double-resolution attempts
4247									if ( depth < maxDepth ) {
4248										return;
4249									}
4250
4251									returned = handler.apply( that, args );
4252
4253									// Support: Promises/A+ section 2.3.1
4254									// https://promisesaplus.com/#point-48
4255									if ( returned === deferred.promise() ) {
4256										throw new TypeError( "Thenable self-resolution" );
4257									}
4258
4259									// Support: Promises/A+ sections 2.3.3.1, 3.5
4260									// https://promisesaplus.com/#point-54
4261									// https://promisesaplus.com/#point-75
4262									// Retrieve `then` only once
4263									then = returned &&
4264
4265										// Support: Promises/A+ section 2.3.4
4266										// https://promisesaplus.com/#point-64
4267										// Only check objects and functions for thenability
4268										( typeof returned === "object" ||
4269											typeof returned === "function" ) &&
4270										returned.then;
4271
4272									// Handle a returned thenable
4273									if ( isFunction( then ) ) {
4274
4275										// Special processors (notify) just wait for resolution
4276										if ( special ) {
4277											then.call(
4278												returned,
4279												resolve( maxDepth, deferred, Identity, special ),
4280												resolve( maxDepth, deferred, Thrower, special )
4281											);
4282
4283										// Normal processors (resolve) also hook into progress
4284										} else {
4285
4286											// ...and disregard older resolution values
4287											maxDepth++;
4288
4289											then.call(
4290												returned,
4291												resolve( maxDepth, deferred, Identity, special ),
4292												resolve( maxDepth, deferred, Thrower, special ),
4293												resolve( maxDepth, deferred, Identity,
4294													deferred.notifyWith )
4295											);
4296										}
4297
4298									// Handle all other returned values
4299									} else {
4300
4301										// Only substitute handlers pass on context
4302										// and multiple values (non-spec behavior)
4303										if ( handler !== Identity ) {
4304											that = undefined;
4305											args = [ returned ];
4306										}
4307
4308										// Process the value(s)
4309										// Default process is resolve
4310										( special || deferred.resolveWith )( that, args );
4311									}
4312								},
4313
4314								// Only normal processors (resolve) catch and reject exceptions
4315								process = special ?
4316									mightThrow :
4317									function() {
4318										try {
4319											mightThrow();
4320										} catch ( e ) {
4321
4322											if ( jQuery.Deferred.exceptionHook ) {
4323												jQuery.Deferred.exceptionHook( e,
4324													process.error );
4325											}
4326
4327											// Support: Promises/A+ section 2.3.3.3.4.1
4328											// https://promisesaplus.com/#point-61
4329											// Ignore post-resolution exceptions
4330											if ( depth + 1 >= maxDepth ) {
4331
4332												// Only substitute handlers pass on context
4333												// and multiple values (non-spec behavior)
4334												if ( handler !== Thrower ) {
4335													that = undefined;
4336													args = [ e ];
4337												}
4338
4339												deferred.rejectWith( that, args );
4340											}
4341										}
4342									};
4343
4344							// Support: Promises/A+ section 2.3.3.3.1
4345							// https://promisesaplus.com/#point-57
4346							// Re-resolve promises immediately to dodge false rejection from
4347							// subsequent errors
4348							if ( depth ) {
4349								process();
4350							} else {
4351
4352								// Call an optional hook to record the error, in case of exception
4353								// since it's otherwise lost when execution goes async
4354								if ( jQuery.Deferred.getErrorHook ) {
4355									process.error = jQuery.Deferred.getErrorHook();
4356
4357								// The deprecated alias of the above. While the name suggests
4358								// returning the stack, not an error instance, jQuery just passes
4359								// it directly to `console.warn` so both will work; an instance
4360								// just better cooperates with source maps.
4361								} else if ( jQuery.Deferred.getStackHook ) {
4362									process.error = jQuery.Deferred.getStackHook();
4363								}
4364								window.setTimeout( process );
4365							}
4366						};
4367					}
4368
4369					return jQuery.Deferred( function( newDefer ) {
4370
4371						// progress_handlers.add( ... )
4372						tuples[ 0 ][ 3 ].add(
4373							resolve(
4374								0,
4375								newDefer,
4376								isFunction( onProgress ) ?
4377									onProgress :
4378									Identity,
4379								newDefer.notifyWith
4380							)
4381						);
4382
4383						// fulfilled_handlers.add( ... )
4384						tuples[ 1 ][ 3 ].add(
4385							resolve(
4386								0,
4387								newDefer,
4388								isFunction( onFulfilled ) ?
4389									onFulfilled :
4390									Identity
4391							)
4392						);
4393
4394						// rejected_handlers.add( ... )
4395						tuples[ 2 ][ 3 ].add(
4396							resolve(
4397								0,
4398								newDefer,
4399								isFunction( onRejected ) ?
4400									onRejected :
4401									Thrower
4402							)
4403						);
4404					} ).promise();
4405				},
4406
4407				// Get a promise for this deferred
4408				// If obj is provided, the promise aspect is added to the object
4409				promise: function( obj ) {
4410					return obj != null ? jQuery.extend( obj, promise ) : promise;
4411				}
4412			},
4413			deferred = {};
4414
4415		// Add list-specific methods
4416		jQuery.each( tuples, function( i, tuple ) {
4417			var list = tuple[ 2 ],
4418				stateString = tuple[ 5 ];
4419
4420			// promise.progress = list.add
4421			// promise.done = list.add
4422			// promise.fail = list.add
4423			promise[ tuple[ 1 ] ] = list.add;
4424
4425			// Handle state
4426			if ( stateString ) {
4427				list.add(
4428					function() {
4429
4430						// state = "resolved" (i.e., fulfilled)
4431						// state = "rejected"
4432						state = stateString;
4433					},
4434
4435					// rejected_callbacks.disable
4436					// fulfilled_callbacks.disable
4437					tuples[ 3 - i ][ 2 ].disable,
4438
4439					// rejected_handlers.disable
4440					// fulfilled_handlers.disable
4441					tuples[ 3 - i ][ 3 ].disable,
4442
4443					// progress_callbacks.lock
4444					tuples[ 0 ][ 2 ].lock,
4445
4446					// progress_handlers.lock
4447					tuples[ 0 ][ 3 ].lock
4448				);
4449			}
4450
4451			// progress_handlers.fire
4452			// fulfilled_handlers.fire
4453			// rejected_handlers.fire
4454			list.add( tuple[ 3 ].fire );
4455
4456			// deferred.notify = function() { deferred.notifyWith(...) }
4457			// deferred.resolve = function() { deferred.resolveWith(...) }
4458			// deferred.reject = function() { deferred.rejectWith(...) }
4459			deferred[ tuple[ 0 ] ] = function() {
4460				deferred[ tuple[ 0 ] + "With" ]( this === deferred ? undefined : this, arguments );
4461				return this;
4462			};
4463
4464			// deferred.notifyWith = list.fireWith
4465			// deferred.resolveWith = list.fireWith
4466			// deferred.rejectWith = list.fireWith
4467			deferred[ tuple[ 0 ] + "With" ] = list.fireWith;
4468		} );
4469
4470		// Make the deferred a promise
4471		promise.promise( deferred );
4472
4473		// Call given func if any
4474		if ( func ) {
4475			func.call( deferred, deferred );
4476		}
4477
4478		// All done!
4479		return deferred;
4480	},
4481
4482	// Deferred helper
4483	when: function( singleValue ) {
4484		var
4485
4486			// count of uncompleted subordinates
4487			remaining = arguments.length,
4488
4489			// count of unprocessed arguments
4490			i = remaining,
4491
4492			// subordinate fulfillment data
4493			resolveContexts = Array( i ),
4494			resolveValues = slice.call( arguments ),
4495
4496			// the primary Deferred
4497			primary = jQuery.Deferred(),
4498
4499			// subordinate callback factory
4500			updateFunc = function( i ) {
4501				return function( value ) {
4502					resolveContexts[ i ] = this;
4503					resolveValues[ i ] = arguments.length > 1 ? slice.call( arguments ) : value;
4504					if ( !( --remaining ) ) {
4505						primary.resolveWith( resolveContexts, resolveValues );
4506					}
4507				};
4508			};
4509
4510		// Single- and empty arguments are adopted like Promise.resolve
4511		if ( remaining <= 1 ) {
4512			adoptValue( singleValue, primary.done( updateFunc( i ) ).resolve, primary.reject,
4513				!remaining );
4514
4515			// Use .then() to unwrap secondary thenables (cf. gh-3000)
4516			if ( primary.state() === "pending" ||
4517				isFunction( resolveValues[ i ] && resolveValues[ i ].then ) ) {
4518
4519				return primary.then();
4520			}
4521		}
4522
4523		// Multiple arguments are aggregated like Promise.all array elements
4524		while ( i-- ) {
4525			adoptValue( resolveValues[ i ], updateFunc( i ), primary.reject );
4526		}
4527
4528		return primary.promise();
4529	}
4530} );
4531
4532
4533// These usually indicate a programmer mistake during development,
4534// warn about them ASAP rather than swallowing them by default.
4535var rerrorNames = /^(Eval|Internal|Range|Reference|Syntax|Type|URI)Error$/;
4536
4537// If `jQuery.Deferred.getErrorHook` is defined, `asyncError` is an error
4538// captured before the async barrier to get the original error cause
4539// which may otherwise be hidden.
4540jQuery.Deferred.exceptionHook = function( error, asyncError ) {
4541
4542	// Support: IE 8 - 9 only
4543	// Console exists when dev tools are open, which can happen at any time
4544	if ( window.console && window.console.warn && error && rerrorNames.test( error.name ) ) {
4545		window.console.warn( "jQuery.Deferred exception: " + error.message,
4546			error.stack, asyncError );
4547	}
4548};
4549
4550
4551
4552
4553jQuery.readyException = function( error ) {
4554	window.setTimeout( function() {
4555		throw error;
4556	} );
4557};
4558
4559
4560
4561
4562// The deferred used on DOM ready
4563var readyList = jQuery.Deferred();
4564
4565jQuery.fn.ready = function( fn ) {
4566
4567	readyList
4568		.then( fn )
4569
4570		// Wrap jQuery.readyException in a function so that the lookup
4571		// happens at the time of error handling instead of callback
4572		// registration.
4573		.catch( function( error ) {
4574			jQuery.readyException( error );
4575		} );
4576
4577	return this;
4578};
4579
4580jQuery.extend( {
4581
4582	// Is the DOM ready to be used? Set to true once it occurs.
4583	isReady: false,
4584
4585	// A counter to track how many items to wait for before
4586	// the ready event fires. See trac-6781
4587	readyWait: 1,
4588
4589	// Handle when the DOM is ready
4590	ready: function( wait ) {
4591
4592		// Abort if there are pending holds or we're already ready
4593		if ( wait === true ? --jQuery.readyWait : jQuery.isReady ) {
4594			return;
4595		}
4596
4597		// Remember that the DOM is ready
4598		jQuery.isReady = true;
4599
4600		// If a normal DOM Ready event fired, decrement, and wait if need be
4601		if ( wait !== true && --jQuery.readyWait > 0 ) {
4602			return;
4603		}
4604
4605		// If there are functions bound, to execute
4606		readyList.resolveWith( document, [ jQuery ] );
4607	}
4608} );
4609
4610jQuery.ready.then = readyList.then;
4611
4612// The ready event handler and self cleanup method
4613function completed() {
4614	document.removeEventListener( "DOMContentLoaded", completed );
4615	window.removeEventListener( "load", completed );
4616	jQuery.ready();
4617}
4618
4619// Catch cases where $(document).ready() is called
4620// after the browser event has already occurred.
4621// Support: IE <=9 - 10 only
4622// Older IE sometimes signals "interactive" too soon
4623if ( document.readyState === "complete" ||
4624	( document.readyState !== "loading" && !document.documentElement.doScroll ) ) {
4625
4626	// Handle it asynchronously to allow scripts the opportunity to delay ready
4627	window.setTimeout( jQuery.ready );
4628
4629} else {
4630
4631	// Use the handy event callback
4632	document.addEventListener( "DOMContentLoaded", completed );
4633
4634	// A fallback to window.onload, that will always work
4635	window.addEventListener( "load", completed );
4636}
4637
4638
4639
4640
4641// Multifunctional method to get and set values of a collection
4642// The value/s can optionally be executed if it's a function
4643var access = function( elems, fn, key, value, chainable, emptyGet, raw ) {
4644	var i = 0,
4645		len = elems.length,
4646		bulk = key == null;
4647
4648	// Sets many values
4649	if ( toType( key ) === "object" ) {
4650		chainable = true;
4651		for ( i in key ) {
4652			access( elems, fn, i, key[ i ], true, emptyGet, raw );
4653		}
4654
4655	// Sets one value
4656	} else if ( value !== undefined ) {
4657		chainable = true;
4658
4659		if ( !isFunction( value ) ) {
4660			raw = true;
4661		}
4662
4663		if ( bulk ) {
4664
4665			// Bulk operations run against the entire set
4666			if ( raw ) {
4667				fn.call( elems, value );
4668				fn = null;
4669
4670			// ...except when executing function values
4671			} else {
4672				bulk = fn;
4673				fn = function( elem, _key, value ) {
4674					return bulk.call( jQuery( elem ), value );
4675				};
4676			}
4677		}
4678
4679		if ( fn ) {
4680			for ( ; i < len; i++ ) {
4681				fn(
4682					elems[ i ], key, raw ?
4683						value :
4684						value.call( elems[ i ], i, fn( elems[ i ], key ) )
4685				);
4686			}
4687		}
4688	}
4689
4690	if ( chainable ) {
4691		return elems;
4692	}
4693
4694	// Gets
4695	if ( bulk ) {
4696		return fn.call( elems );
4697	}
4698
4699	return len ? fn( elems[ 0 ], key ) : emptyGet;
4700};
4701
4702
4703// Matches dashed string for camelizing
4704var rmsPrefix = /^-ms-/,
4705	rdashAlpha = /-([a-z])/g;
4706
4707// Used by camelCase as callback to replace()
4708function fcamelCase( _all, letter ) {
4709	return letter.toUpperCase();
4710}
4711
4712// Convert dashed to camelCase; used by the css and data modules
4713// Support: IE <=9 - 11, Edge 12 - 15
4714// Microsoft forgot to hump their vendor prefix (trac-9572)
4715function camelCase( string ) {
4716	return string.replace( rmsPrefix, "ms-" ).replace( rdashAlpha, fcamelCase );
4717}
4718var acceptData = function( owner ) {
4719
4720	// Accepts only:
4721	//  - Node
4722	//    - Node.ELEMENT_NODE
4723	//    - Node.DOCUMENT_NODE
4724	//  - Object
4725	//    - Any
4726	return owner.nodeType === 1 || owner.nodeType === 9 || !( +owner.nodeType );
4727};
4728
4729
4730
4731
4732function Data() {
4733	this.expando = jQuery.expando + Data.uid++;
4734}
4735
4736Data.uid = 1;
4737
4738Data.prototype = {
4739
4740	cache: function( owner ) {
4741
4742		// Check if the owner object already has a cache
4743		var value = owner[ this.expando ];
4744
4745		// If not, create one
4746		if ( !value ) {
4747			value = {};
4748
4749			// We can accept data for non-element nodes in modern browsers,
4750			// but we should not, see trac-8335.
4751			// Always return an empty object.
4752			if ( acceptData( owner ) ) {
4753
4754				// If it is a node unlikely to be stringify-ed or looped over
4755				// use plain assignment
4756				if ( owner.nodeType ) {
4757					owner[ this.expando ] = value;
4758
4759				// Otherwise secure it in a non-enumerable property
4760				// configurable must be true to allow the property to be
4761				// deleted when data is removed
4762				} else {
4763					Object.defineProperty( owner, this.expando, {
4764						value: value,
4765						configurable: true
4766					} );
4767				}
4768			}
4769		}
4770
4771		return value;
4772	},
4773	set: function( owner, data, value ) {
4774		var prop,
4775			cache = this.cache( owner );
4776
4777		// Handle: [ owner, key, value ] args
4778		// Always use camelCase key (gh-2257)
4779		if ( typeof data === "string" ) {
4780			cache[ camelCase( data ) ] = value;
4781
4782		// Handle: [ owner, { properties } ] args
4783		} else {
4784
4785			// Copy the properties one-by-one to the cache object
4786			for ( prop in data ) {
4787				cache[ camelCase( prop ) ] = data[ prop ];
4788			}
4789		}
4790		return cache;
4791	},
4792	get: function( owner, key ) {
4793		return key === undefined ?
4794			this.cache( owner ) :
4795
4796			// Always use camelCase key (gh-2257)
4797			owner[ this.expando ] && owner[ this.expando ][ camelCase( key ) ];
4798	},
4799	access: function( owner, key, value ) {
4800
4801		// In cases where either:
4802		//
4803		//   1. No key was specified
4804		//   2. A string key was specified, but no value provided
4805		//
4806		// Take the "read" path and allow the get method to determine
4807		// which value to return, respectively either:
4808		//
4809		//   1. The entire cache object
4810		//   2. The data stored at the key
4811		//
4812		if ( key === undefined ||
4813				( ( key && typeof key === "string" ) && value === undefined ) ) {
4814
4815			return this.get( owner, key );
4816		}
4817
4818		// When the key is not a string, or both a key and value
4819		// are specified, set or extend (existing objects) with either:
4820		//
4821		//   1. An object of properties
4822		//   2. A key and value
4823		//
4824		this.set( owner, key, value );
4825
4826		// Since the "set" path can have two possible entry points
4827		// return the expected data based on which path was taken[*]
4828		return value !== undefined ? value : key;
4829	},
4830	remove: function( owner, key ) {
4831		var i,
4832			cache = owner[ this.expando ];
4833
4834		if ( cache === undefined ) {
4835			return;
4836		}
4837
4838		if ( key !== undefined ) {
4839
4840			// Support array or space separated string of keys
4841			if ( Array.isArray( key ) ) {
4842
4843				// If key is an array of keys...
4844				// We always set camelCase keys, so remove that.
4845				key = key.map( camelCase );
4846			} else {
4847				key = camelCase( key );
4848
4849				// If a key with the spaces exists, use it.
4850				// Otherwise, create an array by matching non-whitespace
4851				key = key in cache ?
4852					[ key ] :
4853					( key.match( rnothtmlwhite ) || [] );
4854			}
4855
4856			i = key.length;
4857
4858			while ( i-- ) {
4859				delete cache[ key[ i ] ];
4860			}
4861		}
4862
4863		// Remove the expando if there's no more data
4864		if ( key === undefined || jQuery.isEmptyObject( cache ) ) {
4865
4866			// Support: Chrome <=35 - 45
4867			// Webkit & Blink performance suffers when deleting properties
4868			// from DOM nodes, so set to undefined instead
4869			// https://bugs.chromium.org/p/chromium/issues/detail?id=378607 (bug restricted)
4870			if ( owner.nodeType ) {
4871				owner[ this.expando ] = undefined;
4872			} else {
4873				delete owner[ this.expando ];
4874			}
4875		}
4876	},
4877	hasData: function( owner ) {
4878		var cache = owner[ this.expando ];
4879		return cache !== undefined && !jQuery.isEmptyObject( cache );
4880	}
4881};
4882var dataPriv = new Data();
4883
4884var dataUser = new Data();
4885
4886
4887
4888//	Implementation Summary
4889//
4890//	1. Enforce API surface and semantic compatibility with 1.9.x branch
4891//	2. Improve the module's maintainability by reducing the storage
4892//		paths to a single mechanism.
4893//	3. Use the same single mechanism to support "private" and "user" data.
4894//	4. _Never_ expose "private" data to user code (TODO: Drop _data, _removeData)
4895//	5. Avoid exposing implementation details on user objects (eg. expando properties)
4896//	6. Provide a clear path for implementation upgrade to WeakMap in 2014
4897
4898var rbrace = /^(?:\{[\w\W]*\}|\[[\w\W]*\])$/,
4899	rmultiDash = /[A-Z]/g;
4900
4901function getData( data ) {
4902	if ( data === "true" ) {
4903		return true;
4904	}
4905
4906	if ( data === "false" ) {
4907		return false;
4908	}
4909
4910	if ( data === "null" ) {
4911		return null;
4912	}
4913
4914	// Only convert to a number if it doesn't change the string
4915	if ( data === +data + "" ) {
4916		return +data;
4917	}
4918
4919	if ( rbrace.test( data ) ) {
4920		return JSON.parse( data );
4921	}
4922
4923	return data;
4924}
4925
4926function dataAttr( elem, key, data ) {
4927	var name;
4928
4929	// If nothing was found internally, try to fetch any
4930	// data from the HTML5 data-* attribute
4931	if ( data === undefined && elem.nodeType === 1 ) {
4932		name = "data-" + key.replace( rmultiDash, "-$&" ).toLowerCase();
4933		data = elem.getAttribute( name );
4934
4935		if ( typeof data === "string" ) {
4936			try {
4937				data = getData( data );
4938			} catch ( e ) {}
4939
4940			// Make sure we set the data so it isn't changed later
4941			dataUser.set( elem, key, data );
4942		} else {
4943			data = undefined;
4944		}
4945	}
4946	return data;
4947}
4948
4949jQuery.extend( {
4950	hasData: function( elem ) {
4951		return dataUser.hasData( elem ) || dataPriv.hasData( elem );
4952	},
4953
4954	data: function( elem, name, data ) {
4955		return dataUser.access( elem, name, data );
4956	},
4957
4958	removeData: function( elem, name ) {
4959		dataUser.remove( elem, name );
4960	},
4961
4962	// TODO: Now that all calls to _data and _removeData have been replaced
4963	// with direct calls to dataPriv methods, these can be deprecated.
4964	_data: function( elem, name, data ) {
4965		return dataPriv.access( elem, name, data );
4966	},
4967
4968	_removeData: function( elem, name ) {
4969		dataPriv.remove( elem, name );
4970	}
4971} );
4972
4973jQuery.fn.extend( {
4974	data: function( key, value ) {
4975		var i, name, data,
4976			elem = this[ 0 ],
4977			attrs = elem && elem.attributes;
4978
4979		// Gets all values
4980		if ( key === undefined ) {
4981			if ( this.length ) {
4982				data = dataUser.get( elem );
4983
4984				if ( elem.nodeType === 1 && !dataPriv.get( elem, "hasDataAttrs" ) ) {
4985					i = attrs.length;
4986					while ( i-- ) {
4987
4988						// Support: IE 11 only
4989						// The attrs elements can be null (trac-14894)
4990						if ( attrs[ i ] ) {
4991							name = attrs[ i ].name;
4992							if ( name.indexOf( "data-" ) === 0 ) {
4993								name = camelCase( name.slice( 5 ) );
4994								dataAttr( elem, name, data[ name ] );
4995							}
4996						}
4997					}
4998					dataPriv.set( elem, "hasDataAttrs", true );
4999				}
5000			}
5001
5002			return data;
5003		}
5004
5005		// Sets multiple values
5006		if ( typeof key === "object" ) {
5007			return this.each( function() {
5008				dataUser.set( this, key );
5009			} );
5010		}
5011
5012		return access( this, function( value ) {
5013			var data;
5014
5015			// The calling jQuery object (element matches) is not empty
5016			// (and therefore has an element appears at this[ 0 ]) and the
5017			// `value` parameter was not undefined. An empty jQuery object
5018			// will result in `undefined` for elem = this[ 0 ] which will
5019			// throw an exception if an attempt to read a data cache is made.
5020			if ( elem && value === undefined ) {
5021
5022				// Attempt to get data from the cache
5023				// The key will always be camelCased in Data
5024				data = dataUser.get( elem, key );
5025				if ( data !== undefined ) {
5026					return data;
5027				}
5028
5029				// Attempt to "discover" the data in
5030				// HTML5 custom data-* attrs
5031				data = dataAttr( elem, key );
5032				if ( data !== undefined ) {
5033					return data;
5034				}
5035
5036				// We tried really hard, but the data doesn't exist.
5037				return;
5038			}
5039
5040			// Set the data...
5041			this.each( function() {
5042
5043				// We always store the camelCased key
5044				dataUser.set( this, key, value );
5045			} );
5046		}, null, value, arguments.length > 1, null, true );
5047	},
5048
5049	removeData: function( key ) {
5050		return this.each( function() {
5051			dataUser.remove( this, key );
5052		} );
5053	}
5054} );
5055
5056
5057jQuery.extend( {
5058	queue: function( elem, type, data ) {
5059		var queue;
5060
5061		if ( elem ) {
5062			type = ( type || "fx" ) + "queue";
5063			queue = dataPriv.get( elem, type );
5064
5065			// Speed up dequeue by getting out quickly if this is just a lookup
5066			if ( data ) {
5067				if ( !queue || Array.isArray( data ) ) {
5068					queue = dataPriv.access( elem, type, jQuery.makeArray( data ) );
5069				} else {
5070					queue.push( data );
5071				}
5072			}
5073			return queue || [];
5074		}
5075	},
5076
5077	dequeue: function( elem, type ) {
5078		type = type || "fx";
5079
5080		var queue = jQuery.queue( elem, type ),
5081			startLength = queue.length,
5082			fn = queue.shift(),
5083			hooks = jQuery._queueHooks( elem, type ),
5084			next = function() {
5085				jQuery.dequeue( elem, type );
5086			};
5087
5088		// If the fx queue is dequeued, always remove the progress sentinel
5089		if ( fn === "inprogress" ) {
5090			fn = queue.shift();
5091			startLength--;
5092		}
5093
5094		if ( fn ) {
5095
5096			// Add a progress sentinel to prevent the fx queue from being
5097			// automatically dequeued
5098			if ( type === "fx" ) {
5099				queue.unshift( "inprogress" );
5100			}
5101
5102			// Clear up the last queue stop function
5103			delete hooks.stop;
5104			fn.call( elem, next, hooks );
5105		}
5106
5107		if ( !startLength && hooks ) {
5108			hooks.empty.fire();
5109		}
5110	},
5111
5112	// Not public - generate a queueHooks object, or return the current one
5113	_queueHooks: function( elem, type ) {
5114		var key = type + "queueHooks";
5115		return dataPriv.get( elem, key ) || dataPriv.access( elem, key, {
5116			empty: jQuery.Callbacks( "once memory" ).add( function() {
5117				dataPriv.remove( elem, [ type + "queue", key ] );
5118			} )
5119		} );
5120	}
5121} );
5122
5123jQuery.fn.extend( {
5124	queue: function( type, data ) {
5125		var setter = 2;
5126
5127		if ( typeof type !== "string" ) {
5128			data = type;
5129			type = "fx";
5130			setter--;
5131		}
5132
5133		if ( arguments.length < setter ) {
5134			return jQuery.queue( this[ 0 ], type );
5135		}
5136
5137		return data === undefined ?
5138			this :
5139			this.each( function() {
5140				var queue = jQuery.queue( this, type, data );
5141
5142				// Ensure a hooks for this queue
5143				jQuery._queueHooks( this, type );
5144
5145				if ( type === "fx" && queue[ 0 ] !== "inprogress" ) {
5146					jQuery.dequeue( this, type );
5147				}
5148			} );
5149	},
5150	dequeue: function( type ) {
5151		return this.each( function() {
5152			jQuery.dequeue( this, type );
5153		} );
5154	},
5155	clearQueue: function( type ) {
5156		return this.queue( type || "fx", [] );
5157	},
5158
5159	// Get a promise resolved when queues of a certain type
5160	// are emptied (fx is the type by default)
5161	promise: function( type, obj ) {
5162		var tmp,
5163			count = 1,
5164			defer = jQuery.Deferred(),
5165			elements = this,
5166			i = this.length,
5167			resolve = function() {
5168				if ( !( --count ) ) {
5169					defer.resolveWith( elements, [ elements ] );
5170				}
5171			};
5172
5173		if ( typeof type !== "string" ) {
5174			obj = type;
5175			type = undefined;
5176		}
5177		type = type || "fx";
5178
5179		while ( i-- ) {
5180			tmp = dataPriv.get( elements[ i ], type + "queueHooks" );
5181			if ( tmp && tmp.empty ) {
5182				count++;
5183				tmp.empty.add( resolve );
5184			}
5185		}
5186		resolve();
5187		return defer.promise( obj );
5188	}
5189} );
5190var pnum = ( /[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/ ).source;
5191
5192var rcssNum = new RegExp( "^(?:([+-])=|)(" + pnum + ")([a-z%]*)$", "i" );
5193
5194
5195var cssExpand = [ "Top", "Right", "Bottom", "Left" ];
5196
5197var documentElement = document.documentElement;
5198
5199
5200
5201	var isAttached = function( elem ) {
5202			return jQuery.contains( elem.ownerDocument, elem );
5203		},
5204		composed = { composed: true };
5205
5206	// Support: IE 9 - 11+, Edge 12 - 18+, iOS 10.0 - 10.2 only
5207	// Check attachment across shadow DOM boundaries when possible (gh-3504)
5208	// Support: iOS 10.0-10.2 only
5209	// Early iOS 10 versions support `attachShadow` but not `getRootNode`,
5210	// leading to errors. We need to check for `getRootNode`.
5211	if ( documentElement.getRootNode ) {
5212		isAttached = function( elem ) {
5213			return jQuery.contains( elem.ownerDocument, elem ) ||
5214				elem.getRootNode( composed ) === elem.ownerDocument;
5215		};
5216	}
5217var isHiddenWithinTree = function( elem, el ) {
5218
5219		// isHiddenWithinTree might be called from jQuery#filter function;
5220		// in that case, element will be second argument
5221		elem = el || elem;
5222
5223		// Inline style trumps all
5224		return elem.style.display === "none" ||
5225			elem.style.display === "" &&
5226
5227			// Otherwise, check computed style
5228			// Support: Firefox <=43 - 45
5229			// Disconnected elements can have computed display: none, so first confirm that elem is
5230			// in the document.
5231			isAttached( elem ) &&
5232
5233			jQuery.css( elem, "display" ) === "none";
5234	};
5235
5236
5237
5238function adjustCSS( elem, prop, valueParts, tween ) {
5239	var adjusted, scale,
5240		maxIterations = 20,
5241		currentValue = tween ?
5242			function() {
5243				return tween.cur();
5244			} :
5245			function() {
5246				return jQuery.css( elem, prop, "" );
5247			},
5248		initial = currentValue(),
5249		unit = valueParts && valueParts[ 3 ] || ( jQuery.cssNumber[ prop ] ? "" : "px" ),
5250
5251		// Starting value computation is required for potential unit mismatches
5252		initialInUnit = elem.nodeType &&
5253			( jQuery.cssNumber[ prop ] || unit !== "px" && +initial ) &&
5254			rcssNum.exec( jQuery.css( elem, prop ) );
5255
5256	if ( initialInUnit && initialInUnit[ 3 ] !== unit ) {
5257
5258		// Support: Firefox <=54
5259		// Halve the iteration target value to prevent interference from CSS upper bounds (gh-2144)
5260		initial = initial / 2;
5261
5262		// Trust units reported by jQuery.css
5263		unit = unit || initialInUnit[ 3 ];
5264
5265		// Iteratively approximate from a nonzero starting point
5266		initialInUnit = +initial || 1;
5267
5268		while ( maxIterations-- ) {
5269
5270			// Evaluate and update our best guess (doubling guesses that zero out).
5271			// Finish if the scale equals or crosses 1 (making the old*new product non-positive).
5272			jQuery.style( elem, prop, initialInUnit + unit );
5273			if ( ( 1 - scale ) * ( 1 - ( scale = currentValue() / initial || 0.5 ) ) <= 0 ) {
5274				maxIterations = 0;
5275			}
5276			initialInUnit = initialInUnit / scale;
5277
5278		}
5279
5280		initialInUnit = initialInUnit * 2;
5281		jQuery.style( elem, prop, initialInUnit + unit );
5282
5283		// Make sure we update the tween properties later on
5284		valueParts = valueParts || [];
5285	}
5286
5287	if ( valueParts ) {
5288		initialInUnit = +initialInUnit || +initial || 0;
5289
5290		// Apply relative offset (+=/-=) if specified
5291		adjusted = valueParts[ 1 ] ?
5292			initialInUnit + ( valueParts[ 1 ] + 1 ) * valueParts[ 2 ] :
5293			+valueParts[ 2 ];
5294		if ( tween ) {
5295			tween.unit = unit;
5296			tween.start = initialInUnit;
5297			tween.end = adjusted;
5298		}
5299	}
5300	return adjusted;
5301}
5302
5303
5304var defaultDisplayMap = {};
5305
5306function getDefaultDisplay( elem ) {
5307	var temp,
5308		doc = elem.ownerDocument,
5309		nodeName = elem.nodeName,
5310		display = defaultDisplayMap[ nodeName ];
5311
5312	if ( display ) {
5313		return display;
5314	}
5315
5316	temp = doc.body.appendChild( doc.createElement( nodeName ) );
5317	display = jQuery.css( temp, "display" );
5318
5319	temp.parentNode.removeChild( temp );
5320
5321	if ( display === "none" ) {
5322		display = "block";
5323	}
5324	defaultDisplayMap[ nodeName ] = display;
5325
5326	return display;
5327}
5328
5329function showHide( elements, show ) {
5330	var display, elem,
5331		values = [],
5332		index = 0,
5333		length = elements.length;
5334
5335	// Determine new display value for elements that need to change
5336	for ( ; index < length; index++ ) {
5337		elem = elements[ index ];
5338		if ( !elem.style ) {
5339			continue;
5340		}
5341
5342		display = elem.style.display;
5343		if ( show ) {
5344
5345			// Since we force visibility upon cascade-hidden elements, an immediate (and slow)
5346			// check is required in this first loop unless we have a nonempty display value (either
5347			// inline or about-to-be-restored)
5348			if ( display === "none" ) {
5349				values[ index ] = dataPriv.get( elem, "display" ) || null;
5350				if ( !values[ index ] ) {
5351					elem.style.display = "";
5352				}
5353			}
5354			if ( elem.style.display === "" && isHiddenWithinTree( elem ) ) {
5355				values[ index ] = getDefaultDisplay( elem );
5356			}
5357		} else {
5358			if ( display !== "none" ) {
5359				values[ index ] = "none";
5360
5361				// Remember what we're overwriting
5362				dataPriv.set( elem, "display", display );
5363			}
5364		}
5365	}
5366
5367	// Set the display of the elements in a second loop to avoid constant reflow
5368	for ( index = 0; index < length; index++ ) {
5369		if ( values[ index ] != null ) {
5370			elements[ index ].style.display = values[ index ];
5371		}
5372	}
5373
5374	return elements;
5375}
5376
5377jQuery.fn.extend( {
5378	show: function() {
5379		return showHide( this, true );
5380	},
5381	hide: function() {
5382		return showHide( this );
5383	},
5384	toggle: function( state ) {
5385		if ( typeof state === "boolean" ) {
5386			return state ? this.show() : this.hide();
5387		}
5388
5389		return this.each( function() {
5390			if ( isHiddenWithinTree( this ) ) {
5391				jQuery( this ).show();
5392			} else {
5393				jQuery( this ).hide();
5394			}
5395		} );
5396	}
5397} );
5398var rcheckableType = ( /^(?:checkbox|radio)$/i );
5399
5400var rtagName = ( /<([a-z][^\/\0>\x20\t\r\n\f]*)/i );
5401
5402var rscriptType = ( /^$|^module$|\/(?:java|ecma)script/i );
5403
5404
5405
5406( function() {
5407	var fragment = document.createDocumentFragment(),
5408		div = fragment.appendChild( document.createElement( "div" ) ),
5409		input = document.createElement( "input" );
5410
5411	// Support: Android 4.0 - 4.3 only
5412	// Check state lost if the name is set (trac-11217)
5413	// Support: Windows Web Apps (WWA)
5414	// `name` and `type` must use .setAttribute for WWA (trac-14901)
5415	input.setAttribute( "type", "radio" );
5416	input.setAttribute( "checked", "checked" );
5417	input.setAttribute( "name", "t" );
5418
5419	div.appendChild( input );
5420
5421	// Support: Android <=4.1 only
5422	// Older WebKit doesn't clone checked state correctly in fragments
5423	support.checkClone = div.cloneNode( true ).cloneNode( true ).lastChild.checked;
5424
5425	// Support: IE <=11 only
5426	// Make sure textarea (and checkbox) defaultValue is properly cloned
5427	div.innerHTML = "<textarea>x</textarea>";
5428	support.noCloneChecked = !!div.cloneNode( true ).lastChild.defaultValue;
5429
5430	// Support: IE <=9 only
5431	// IE <=9 replaces <option> tags with their contents when inserted outside of
5432	// the select element.
5433	div.innerHTML = "<option></option>";
5434	support.option = !!div.lastChild;
5435} )();
5436
5437
5438// We have to close these tags to support XHTML (trac-13200)
5439var wrapMap = {
5440
5441	// XHTML parsers do not magically insert elements in the
5442	// same way that tag soup parsers do. So we cannot shorten
5443	// this by omitting <tbody> or other required elements.
5444	thead: [ 1, "<table>", "</table>" ],
5445	col: [ 2, "<table><colgroup>", "</colgroup></table>" ],
5446	tr: [ 2, "<table><tbody>", "</tbody></table>" ],
5447	td: [ 3, "<table><tbody><tr>", "</tr></tbody></table>" ],
5448
5449	_default: [ 0, "", "" ]
5450};
5451
5452wrapMap.tbody = wrapMap.tfoot = wrapMap.colgroup = wrapMap.caption = wrapMap.thead;
5453wrapMap.th = wrapMap.td;
5454
5455// Support: IE <=9 only
5456if ( !support.option ) {
5457	wrapMap.optgroup = wrapMap.option = [ 1, "<select multiple='multiple'>", "</select>" ];
5458}
5459
5460
5461function getAll( context, tag ) {
5462
5463	// Support: IE <=9 - 11 only
5464	// Use typeof to avoid zero-argument method invocation on host objects (trac-15151)
5465	var ret;
5466
5467	if ( typeof context.getElementsByTagName !== "undefined" ) {
5468		ret = context.getElementsByTagName( tag || "*" );
5469
5470	} else if ( typeof context.querySelectorAll !== "undefined" ) {
5471		ret = context.querySelectorAll( tag || "*" );
5472
5473	} else {
5474		ret = [];
5475	}
5476
5477	if ( tag === undefined || tag && nodeName( context, tag ) ) {
5478		return jQuery.merge( [ context ], ret );
5479	}
5480
5481	return ret;
5482}
5483
5484
5485// Mark scripts as having already been evaluated
5486function setGlobalEval( elems, refElements ) {
5487	var i = 0,
5488		l = elems.length;
5489
5490	for ( ; i < l; i++ ) {
5491		dataPriv.set(
5492			elems[ i ],
5493			"globalEval",
5494			!refElements || dataPriv.get( refElements[ i ], "globalEval" )
5495		);
5496	}
5497}
5498
5499
5500var rhtml = /<|&#?\w+;/;
5501
5502function buildFragment( elems, context, scripts, selection, ignored ) {
5503	var elem, tmp, tag, wrap, attached, j,
5504		fragment = context.createDocumentFragment(),
5505		nodes = [],
5506		i = 0,
5507		l = elems.length;
5508
5509	for ( ; i < l; i++ ) {
5510		elem = elems[ i ];
5511
5512		if ( elem || elem === 0 ) {
5513
5514			// Add nodes directly
5515			if ( toType( elem ) === "object" ) {
5516
5517				// Support: Android <=4.0 only, PhantomJS 1 only
5518				// push.apply(_, arraylike) throws on ancient WebKit
5519				jQuery.merge( nodes, elem.nodeType ? [ elem ] : elem );
5520
5521			// Convert non-html into a text node
5522			} else if ( !rhtml.test( elem ) ) {
5523				nodes.push( context.createTextNode( elem ) );
5524
5525			// Convert html into DOM nodes
5526			} else {
5527				tmp = tmp || fragment.appendChild( context.createElement( "div" ) );
5528
5529				// Deserialize a standard representation
5530				tag = ( rtagName.exec( elem ) || [ "", "" ] )[ 1 ].toLowerCase();
5531				wrap = wrapMap[ tag ] || wrapMap._default;
5532				tmp.innerHTML = wrap[ 1 ] + jQuery.htmlPrefilter( elem ) + wrap[ 2 ];
5533
5534				// Descend through wrappers to the right content
5535				j = wrap[ 0 ];
5536				while ( j-- ) {
5537					tmp = tmp.lastChild;
5538				}
5539
5540				// Support: Android <=4.0 only, PhantomJS 1 only
5541				// push.apply(_, arraylike) throws on ancient WebKit
5542				jQuery.merge( nodes, tmp.childNodes );
5543
5544				// Remember the top-level container
5545				tmp = fragment.firstChild;
5546
5547				// Ensure the created nodes are orphaned (trac-12392)
5548				tmp.textContent = "";
5549			}
5550		}
5551	}
5552
5553	// Remove wrapper from fragment
5554	fragment.textContent = "";
5555
5556	i = 0;
5557	while ( ( elem = nodes[ i++ ] ) ) {
5558
5559		// Skip elements already in the context collection (trac-4087)
5560		if ( selection && jQuery.inArray( elem, selection ) > -1 ) {
5561			if ( ignored ) {
5562				ignored.push( elem );
5563			}
5564			continue;
5565		}
5566
5567		attached = isAttached( elem );
5568
5569		// Append to fragment
5570		tmp = getAll( fragment.appendChild( elem ), "script" );
5571
5572		// Preserve script evaluation history
5573		if ( attached ) {
5574			setGlobalEval( tmp );
5575		}
5576
5577		// Capture executables
5578		if ( scripts ) {
5579			j = 0;
5580			while ( ( elem = tmp[ j++ ] ) ) {
5581				if ( rscriptType.test( elem.type || "" ) ) {
5582					scripts.push( elem );
5583				}
5584			}
5585		}
5586	}
5587
5588	return fragment;
5589}
5590
5591
5592var rtypenamespace = /^([^.]*)(?:\.(.+)|)/;
5593
5594function returnTrue() {
5595	return true;
5596}
5597
5598function returnFalse() {
5599	return false;
5600}
5601
5602function on( elem, types, selector, data, fn, one ) {
5603	var origFn, type;
5604
5605	// Types can be a map of types/handlers
5606	if ( typeof types === "object" ) {
5607
5608		// ( types-Object, selector, data )
5609		if ( typeof selector !== "string" ) {
5610
5611			// ( types-Object, data )
5612			data = data || selector;
5613			selector = undefined;
5614		}
5615		for ( type in types ) {
5616			on( elem, type, selector, data, types[ type ], one );
5617		}
5618		return elem;
5619	}
5620
5621	if ( data == null && fn == null ) {
5622
5623		// ( types, fn )
5624		fn = selector;
5625		data = selector = undefined;
5626	} else if ( fn == null ) {
5627		if ( typeof selector === "string" ) {
5628
5629			// ( types, selector, fn )
5630			fn = data;
5631			data = undefined;
5632		} else {
5633
5634			// ( types, data, fn )
5635			fn = data;
5636			data = selector;
5637			selector = undefined;
5638		}
5639	}
5640	if ( fn === false ) {
5641		fn = returnFalse;
5642	} else if ( !fn ) {
5643		return elem;
5644	}
5645
5646	if ( one === 1 ) {
5647		origFn = fn;
5648		fn = function( event ) {
5649
5650			// Can use an empty set, since event contains the info
5651			jQuery().off( event );
5652			return origFn.apply( this, arguments );
5653		};
5654
5655		// Use same guid so caller can remove using origFn
5656		fn.guid = origFn.guid || ( origFn.guid = jQuery.guid++ );
5657	}
5658	return elem.each( function() {
5659		jQuery.event.add( this, types, fn, data, selector );
5660	} );
5661}
5662
5663/*
5664 * Helper functions for managing events -- not part of the public interface.
5665 * Props to Dean Edwards' addEvent library for many of the ideas.
5666 */
5667jQuery.event = {
5668
5669	global: {},
5670
5671	add: function( elem, types, handler, data, selector ) {
5672
5673		var handleObjIn, eventHandle, tmp,
5674			events, t, handleObj,
5675			special, handlers, type, namespaces, origType,
5676			elemData = dataPriv.get( elem );
5677
5678		// Only attach events to objects that accept data
5679		if ( !acceptData( elem ) ) {
5680			return;
5681		}
5682
5683		// Caller can pass in an object of custom data in lieu of the handler
5684		if ( handler.handler ) {
5685			handleObjIn = handler;
5686			handler = handleObjIn.handler;
5687			selector = handleObjIn.selector;
5688		}
5689
5690		// Ensure that invalid selectors throw exceptions at attach time
5691		// Evaluate against documentElement in case elem is a non-element node (e.g., document)
5692		if ( selector ) {
5693			jQuery.find.matchesSelector( documentElement, selector );
5694		}
5695
5696		// Make sure that the handler has a unique ID, used to find/remove it later
5697		if ( !handler.guid ) {
5698			handler.guid = jQuery.guid++;
5699		}
5700
5701		// Init the element's event structure and main handler, if this is the first
5702		if ( !( events = elemData.events ) ) {
5703			events = elemData.events = Object.create( null );
5704		}
5705		if ( !( eventHandle = elemData.handle ) ) {
5706			eventHandle = elemData.handle = function( e ) {
5707
5708				// Discard the second event of a jQuery.event.trigger() and
5709				// when an event is called after a page has unloaded
5710				return typeof jQuery !== "undefined" && jQuery.event.triggered !== e.type ?
5711					jQuery.event.dispatch.apply( elem, arguments ) : undefined;
5712			};
5713		}
5714
5715		// Handle multiple events separated by a space
5716		types = ( types || "" ).match( rnothtmlwhite ) || [ "" ];
5717		t = types.length;
5718		while ( t-- ) {
5719			tmp = rtypenamespace.exec( types[ t ] ) || [];
5720			type = origType = tmp[ 1 ];
5721			namespaces = ( tmp[ 2 ] || "" ).split( "." ).sort();
5722
5723			// There *must* be a type, no attaching namespace-only handlers
5724			if ( !type ) {
5725				continue;
5726			}
5727
5728			// If event changes its type, use the special event handlers for the changed type
5729			special = jQuery.event.special[ type ] || {};
5730
5731			// If selector defined, determine special event api type, otherwise given type
5732			type = ( selector ? special.delegateType : special.bindType ) || type;
5733
5734			// Update special based on newly reset type
5735			special = jQuery.event.special[ type ] || {};
5736
5737			// handleObj is passed to all event handlers
5738			handleObj = jQuery.extend( {
5739				type: type,
5740				origType: origType,
5741				data: data,
5742				handler: handler,
5743				guid: handler.guid,
5744				selector: selector,
5745				needsContext: selector && jQuery.expr.match.needsContext.test( selector ),
5746				namespace: namespaces.join( "." )
5747			}, handleObjIn );
5748
5749			// Init the event handler queue if we're the first
5750			if ( !( handlers = events[ type ] ) ) {
5751				handlers = events[ type ] = [];
5752				handlers.delegateCount = 0;
5753
5754				// Only use addEventListener if the special events handler returns false
5755				if ( !special.setup ||
5756					special.setup.call( elem, data, namespaces, eventHandle ) === false ) {
5757
5758					if ( elem.addEventListener ) {
5759						elem.addEventListener( type, eventHandle );
5760					}
5761				}
5762			}
5763
5764			if ( special.add ) {
5765				special.add.call( elem, handleObj );
5766
5767				if ( !handleObj.handler.guid ) {
5768					handleObj.handler.guid = handler.guid;
5769				}
5770			}
5771
5772			// Add to the element's handler list, delegates in front
5773			if ( selector ) {
5774				handlers.splice( handlers.delegateCount++, 0, handleObj );
5775			} else {
5776				handlers.push( handleObj );
5777			}
5778
5779			// Keep track of which events have ever been used, for event optimization
5780			jQuery.event.global[ type ] = true;
5781		}
5782
5783	},
5784
5785	// Detach an event or set of events from an element
5786	remove: function( elem, types, handler, selector, mappedTypes ) {
5787
5788		var j, origCount, tmp,
5789			events, t, handleObj,
5790			special, handlers, type, namespaces, origType,
5791			elemData = dataPriv.hasData( elem ) && dataPriv.get( elem );
5792
5793		if ( !elemData || !( events = elemData.events ) ) {
5794			return;
5795		}
5796
5797		// Once for each type.namespace in types; type may be omitted
5798		types = ( types || "" ).match( rnothtmlwhite ) || [ "" ];
5799		t = types.length;
5800		while ( t-- ) {
5801			tmp = rtypenamespace.exec( types[ t ] ) || [];
5802			type = origType = tmp[ 1 ];
5803			namespaces = ( tmp[ 2 ] || "" ).split( "." ).sort();
5804
5805			// Unbind all events (on this namespace, if provided) for the element
5806			if ( !type ) {
5807				for ( type in events ) {
5808					jQuery.event.remove( elem, type + types[ t ], handler, selector, true );
5809				}
5810				continue;
5811			}
5812
5813			special = jQuery.event.special[ type ] || {};
5814			type = ( selector ? special.delegateType : special.bindType ) || type;
5815			handlers = events[ type ] || [];
5816			tmp = tmp[ 2 ] &&
5817				new RegExp( "(^|\\.)" + namespaces.join( "\\.(?:.*\\.|)" ) + "(\\.|$)" );
5818
5819			// Remove matching events
5820			origCount = j = handlers.length;
5821			while ( j-- ) {
5822				handleObj = handlers[ j ];
5823
5824				if ( ( mappedTypes || origType === handleObj.origType ) &&
5825					( !handler || handler.guid === handleObj.guid ) &&
5826					( !tmp || tmp.test( handleObj.namespace ) ) &&
5827					( !selector || selector === handleObj.selector ||
5828						selector === "**" && handleObj.selector ) ) {
5829					handlers.splice( j, 1 );
5830
5831					if ( handleObj.selector ) {
5832						handlers.delegateCount--;
5833					}
5834					if ( special.remove ) {
5835						special.remove.call( elem, handleObj );
5836					}
5837				}
5838			}
5839
5840			// Remove generic event handler if we removed something and no more handlers exist
5841			// (avoids potential for endless recursion during removal of special event handlers)
5842			if ( origCount && !handlers.length ) {
5843				if ( !special.teardown ||
5844					special.teardown.call( elem, namespaces, elemData.handle ) === false ) {
5845
5846					jQuery.removeEvent( elem, type, elemData.handle );
5847				}
5848
5849				delete events[ type ];
5850			}
5851		}
5852
5853		// Remove data and the expando if it's no longer used
5854		if ( jQuery.isEmptyObject( events ) ) {
5855			dataPriv.remove( elem, "handle events" );
5856		}
5857	},
5858
5859	dispatch: function( nativeEvent ) {
5860
5861		var i, j, ret, matched, handleObj, handlerQueue,
5862			args = new Array( arguments.length ),
5863
5864			// Make a writable jQuery.Event from the native event object
5865			event = jQuery.event.fix( nativeEvent ),
5866
5867			handlers = (
5868				dataPriv.get( this, "events" ) || Object.create( null )
5869			)[ event.type ] || [],
5870			special = jQuery.event.special[ event.type ] || {};
5871
5872		// Use the fix-ed jQuery.Event rather than the (read-only) native event
5873		args[ 0 ] = event;
5874
5875		for ( i = 1; i < arguments.length; i++ ) {
5876			args[ i ] = arguments[ i ];
5877		}
5878
5879		event.delegateTarget = this;
5880
5881		// Call the preDispatch hook for the mapped type, and let it bail if desired
5882		if ( special.preDispatch && special.preDispatch.call( this, event ) === false ) {
5883			return;
5884		}
5885
5886		// Determine handlers
5887		handlerQueue = jQuery.event.handlers.call( this, event, handlers );
5888
5889		// Run delegates first; they may want to stop propagation beneath us
5890		i = 0;
5891		while ( ( matched = handlerQueue[ i++ ] ) && !event.isPropagationStopped() ) {
5892			event.currentTarget = matched.elem;
5893
5894			j = 0;
5895			while ( ( handleObj = matched.handlers[ j++ ] ) &&
5896				!event.isImmediatePropagationStopped() ) {
5897
5898				// If the event is namespaced, then each handler is only invoked if it is
5899				// specially universal or its namespaces are a superset of the event's.
5900				if ( !event.rnamespace || handleObj.namespace === false ||
5901					event.rnamespace.test( handleObj.namespace ) ) {
5902
5903					event.handleObj = handleObj;
5904					event.data = handleObj.data;
5905
5906					ret = ( ( jQuery.event.special[ handleObj.origType ] || {} ).handle ||
5907						handleObj.handler ).apply( matched.elem, args );
5908
5909					if ( ret !== undefined ) {
5910						if ( ( event.result = ret ) === false ) {
5911							event.preventDefault();
5912							event.stopPropagation();
5913						}
5914					}
5915				}
5916			}
5917		}
5918
5919		// Call the postDispatch hook for the mapped type
5920		if ( special.postDispatch ) {
5921			special.postDispatch.call( this, event );
5922		}
5923
5924		return event.result;
5925	},
5926
5927	handlers: function( event, handlers ) {
5928		var i, handleObj, sel, matchedHandlers, matchedSelectors,
5929			handlerQueue = [],
5930			delegateCount = handlers.delegateCount,
5931			cur = event.target;
5932
5933		// Find delegate handlers
5934		if ( delegateCount &&
5935
5936			// Support: IE <=9
5937			// Black-hole SVG <use> instance trees (trac-13180)
5938			cur.nodeType &&
5939
5940			// Support: Firefox <=42
5941			// Suppress spec-violating clicks indicating a non-primary pointer button (trac-3861)
5942			// https://www.w3.org/TR/DOM-Level-3-Events/#event-type-click
5943			// Support: IE 11 only
5944			// ...but not arrow key "clicks" of radio inputs, which can have `button` -1 (gh-2343)
5945			!( event.type === "click" && event.button >= 1 ) ) {
5946
5947			for ( ; cur !== this; cur = cur.parentNode || this ) {
5948
5949				// Don't check non-elements (trac-13208)
5950				// Don't process clicks on disabled elements (trac-6911, trac-8165, trac-11382, trac-11764)
5951				if ( cur.nodeType === 1 && !( event.type === "click" && cur.disabled === true ) ) {
5952					matchedHandlers = [];
5953					matchedSelectors = {};
5954					for ( i = 0; i < delegateCount; i++ ) {
5955						handleObj = handlers[ i ];
5956
5957						// Don't conflict with Object.prototype properties (trac-13203)
5958						sel = handleObj.selector + " ";
5959
5960						if ( matchedSelectors[ sel ] === undefined ) {
5961							matchedSelectors[ sel ] = handleObj.needsContext ?
5962								jQuery( sel, this ).index( cur ) > -1 :
5963								jQuery.find( sel, this, null, [ cur ] ).length;
5964						}
5965						if ( matchedSelectors[ sel ] ) {
5966							matchedHandlers.push( handleObj );
5967						}
5968					}
5969					if ( matchedHandlers.length ) {
5970						handlerQueue.push( { elem: cur, handlers: matchedHandlers } );
5971					}
5972				}
5973			}
5974		}
5975
5976		// Add the remaining (directly-bound) handlers
5977		cur = this;
5978		if ( delegateCount < handlers.length ) {
5979			handlerQueue.push( { elem: cur, handlers: handlers.slice( delegateCount ) } );
5980		}
5981
5982		return handlerQueue;
5983	},
5984
5985	addProp: function( name, hook ) {
5986		Object.defineProperty( jQuery.Event.prototype, name, {
5987			enumerable: true,
5988			configurable: true,
5989
5990			get: isFunction( hook ) ?
5991				function() {
5992					if ( this.originalEvent ) {
5993						return hook( this.originalEvent );
5994					}
5995				} :
5996				function() {
5997					if ( this.originalEvent ) {
5998						return this.originalEvent[ name ];
5999					}
6000				},
6001
6002			set: function( value ) {
6003				Object.defineProperty( this, name, {
6004					enumerable: true,
6005					configurable: true,
6006					writable: true,
6007					value: value
6008				} );
6009			}
6010		} );
6011	},
6012
6013	fix: function( originalEvent ) {
6014		return originalEvent[ jQuery.expando ] ?
6015			originalEvent :
6016			new jQuery.Event( originalEvent );
6017	},
6018
6019	special: {
6020		load: {
6021
6022			// Prevent triggered image.load events from bubbling to window.load
6023			noBubble: true
6024		},
6025		click: {
6026
6027			// Utilize native event to ensure correct state for checkable inputs
6028			setup: function( data ) {
6029
6030				// For mutual compressibility with _default, replace `this` access with a local var.
6031				// `|| data` is dead code meant only to preserve the variable through minification.
6032				var el = this || data;
6033
6034				// Claim the first handler
6035				if ( rcheckableType.test( el.type ) &&
6036					el.click && nodeName( el, "input" ) ) {
6037
6038					// dataPriv.set( el, "click", ... )
6039					leverageNative( el, "click", true );
6040				}
6041
6042				// Return false to allow normal processing in the caller
6043				return false;
6044			},
6045			trigger: function( data ) {
6046
6047				// For mutual compressibility with _default, replace `this` access with a local var.
6048				// `|| data` is dead code meant only to preserve the variable through minification.
6049				var el = this || data;
6050
6051				// Force setup before triggering a click
6052				if ( rcheckableType.test( el.type ) &&
6053					el.click && nodeName( el, "input" ) ) {
6054
6055					leverageNative( el, "click" );
6056				}
6057
6058				// Return non-false to allow normal event-path propagation
6059				return true;
6060			},
6061
6062			// For cross-browser consistency, suppress native .click() on links
6063			// Also prevent it if we're currently inside a leveraged native-event stack
6064			_default: function( event ) {
6065				var target = event.target;
6066				return rcheckableType.test( target.type ) &&
6067					target.click && nodeName( target, "input" ) &&
6068					dataPriv.get( target, "click" ) ||
6069					nodeName( target, "a" );
6070			}
6071		},
6072
6073		beforeunload: {
6074			postDispatch: function( event ) {
6075
6076				// Support: Firefox 20+
6077				// Firefox doesn't alert if the returnValue field is not set.
6078				if ( event.result !== undefined && event.originalEvent ) {
6079					event.originalEvent.returnValue = event.result;
6080				}
6081			}
6082		}
6083	}
6084};
6085
6086// Ensure the presence of an event listener that handles manually-triggered
6087// synthetic events by interrupting progress until reinvoked in response to
6088// *native* events that it fires directly, ensuring that state changes have
6089// already occurred before other listeners are invoked.
6090function leverageNative( el, type, isSetup ) {
6091
6092	// Missing `isSetup` indicates a trigger call, which must force setup through jQuery.event.add
6093	if ( !isSetup ) {
6094		if ( dataPriv.get( el, type ) === undefined ) {
6095			jQuery.event.add( el, type, returnTrue );
6096		}
6097		return;
6098	}
6099
6100	// Register the controller as a special universal handler for all event namespaces
6101	dataPriv.set( el, type, false );
6102	jQuery.event.add( el, type, {
6103		namespace: false,
6104		handler: function( event ) {
6105			var result,
6106				saved = dataPriv.get( this, type );
6107
6108			if ( ( event.isTrigger & 1 ) && this[ type ] ) {
6109
6110				// Interrupt processing of the outer synthetic .trigger()ed event
6111				if ( !saved ) {
6112
6113					// Store arguments for use when handling the inner native event
6114					// There will always be at least one argument (an event object), so this array
6115					// will not be confused with a leftover capture object.
6116					saved = slice.call( arguments );
6117					dataPriv.set( this, type, saved );
6118
6119					// Trigger the native event and capture its result
6120					this[ type ]();
6121					result = dataPriv.get( this, type );
6122					dataPriv.set( this, type, false );
6123
6124					if ( saved !== result ) {
6125
6126						// Cancel the outer synthetic event
6127						event.stopImmediatePropagation();
6128						event.preventDefault();
6129
6130						return result;
6131					}
6132
6133				// If this is an inner synthetic event for an event with a bubbling surrogate
6134				// (focus or blur), assume that the surrogate already propagated from triggering
6135				// the native event and prevent that from happening again here.
6136				// This technically gets the ordering wrong w.r.t. to `.trigger()` (in which the
6137				// bubbling surrogate propagates *after* the non-bubbling base), but that seems
6138				// less bad than duplication.
6139				} else if ( ( jQuery.event.special[ type ] || {} ).delegateType ) {
6140					event.stopPropagation();
6141				}
6142
6143			// If this is a native event triggered above, everything is now in order
6144			// Fire an inner synthetic event with the original arguments
6145			} else if ( saved ) {
6146
6147				// ...and capture the result
6148				dataPriv.set( this, type, jQuery.event.trigger(
6149					saved[ 0 ],
6150					saved.slice( 1 ),
6151					this
6152				) );
6153
6154				// Abort handling of the native event by all jQuery handlers while allowing
6155				// native handlers on the same element to run. On target, this is achieved
6156				// by stopping immediate propagation just on the jQuery event. However,
6157				// the native event is re-wrapped by a jQuery one on each level of the
6158				// propagation so the only way to stop it for jQuery is to stop it for
6159				// everyone via native `stopPropagation()`. This is not a problem for
6160				// focus/blur which don't bubble, but it does also stop click on checkboxes
6161				// and radios. We accept this limitation.
6162				event.stopPropagation();
6163				event.isImmediatePropagationStopped = returnTrue;
6164			}
6165		}
6166	} );
6167}
6168
6169jQuery.removeEvent = function( elem, type, handle ) {
6170
6171	// This "if" is needed for plain objects
6172	if ( elem.removeEventListener ) {
6173		elem.removeEventListener( type, handle );
6174	}
6175};
6176
6177jQuery.Event = function( src, props ) {
6178
6179	// Allow instantiation without the 'new' keyword
6180	if ( !( this instanceof jQuery.Event ) ) {
6181		return new jQuery.Event( src, props );
6182	}
6183
6184	// Event object
6185	if ( src && src.type ) {
6186		this.originalEvent = src;
6187		this.type = src.type;
6188
6189		// Events bubbling up the document may have been marked as prevented
6190		// by a handler lower down the tree; reflect the correct value.
6191		this.isDefaultPrevented = src.defaultPrevented ||
6192				src.defaultPrevented === undefined &&
6193
6194				// Support: Android <=2.3 only
6195				src.returnValue === false ?
6196			returnTrue :
6197			returnFalse;
6198
6199		// Create target properties
6200		// Support: Safari <=6 - 7 only
6201		// Target should not be a text node (trac-504, trac-13143)
6202		this.target = ( src.target && src.target.nodeType === 3 ) ?
6203			src.target.parentNode :
6204			src.target;
6205
6206		this.currentTarget = src.currentTarget;
6207		this.relatedTarget = src.relatedTarget;
6208
6209	// Event type
6210	} else {
6211		this.type = src;
6212	}
6213
6214	// Put explicitly provided properties onto the event object
6215	if ( props ) {
6216		jQuery.extend( this, props );
6217	}
6218
6219	// Create a timestamp if incoming event doesn't have one
6220	this.timeStamp = src && src.timeStamp || Date.now();
6221
6222	// Mark it as fixed
6223	this[ jQuery.expando ] = true;
6224};
6225
6226// jQuery.Event is based on DOM3 Events as specified by the ECMAScript Language Binding
6227// https://www.w3.org/TR/2003/WD-DOM-Level-3-Events-20030331/ecma-script-binding.html
6228jQuery.Event.prototype = {
6229	constructor: jQuery.Event,
6230	isDefaultPrevented: returnFalse,
6231	isPropagationStopped: returnFalse,
6232	isImmediatePropagationStopped: returnFalse,
6233	isSimulated: false,
6234
6235	preventDefault: function() {
6236		var e = this.originalEvent;
6237
6238		this.isDefaultPrevented = returnTrue;
6239
6240		if ( e && !this.isSimulated ) {
6241			e.preventDefault();
6242		}
6243	},
6244	stopPropagation: function() {
6245		var e = this.originalEvent;
6246
6247		this.isPropagationStopped = returnTrue;
6248
6249		if ( e && !this.isSimulated ) {
6250			e.stopPropagation();
6251		}
6252	},
6253	stopImmediatePropagation: function() {
6254		var e = this.originalEvent;
6255
6256		this.isImmediatePropagationStopped = returnTrue;
6257
6258		if ( e && !this.isSimulated ) {
6259			e.stopImmediatePropagation();
6260		}
6261
6262		this.stopPropagation();
6263	}
6264};
6265
6266// Includes all common event props including KeyEvent and MouseEvent specific props
6267jQuery.each( {
6268	altKey: true,
6269	bubbles: true,
6270	cancelable: true,
6271	changedTouches: true,
6272	ctrlKey: true,
6273	detail: true,
6274	eventPhase: true,
6275	metaKey: true,
6276	pageX: true,
6277	pageY: true,
6278	shiftKey: true,
6279	view: true,
6280	"char": true,
6281	code: true,
6282	charCode: true,
6283	key: true,
6284	keyCode: true,
6285	button: true,
6286	buttons: true,
6287	clientX: true,
6288	clientY: true,
6289	offsetX: true,
6290	offsetY: true,
6291	pointerId: true,
6292	pointerType: true,
6293	screenX: true,
6294	screenY: true,
6295	targetTouches: true,
6296	toElement: true,
6297	touches: true,
6298	which: true
6299}, jQuery.event.addProp );
6300
6301jQuery.each( { focus: "focusin", blur: "focusout" }, function( type, delegateType ) {
6302
6303	function focusMappedHandler( nativeEvent ) {
6304		if ( document.documentMode ) {
6305
6306			// Support: IE 11+
6307			// Attach a single focusin/focusout handler on the document while someone wants
6308			// focus/blur. This is because the former are synchronous in IE while the latter
6309			// are async. In other browsers, all those handlers are invoked synchronously.
6310
6311			// `handle` from private data would already wrap the event, but we need
6312			// to change the `type` here.
6313			var handle = dataPriv.get( this, "handle" ),
6314				event = jQuery.event.fix( nativeEvent );
6315			event.type = nativeEvent.type === "focusin" ? "focus" : "blur";
6316			event.isSimulated = true;
6317
6318			// First, handle focusin/focusout
6319			handle( nativeEvent );
6320
6321			// ...then, handle focus/blur
6322			//
6323			// focus/blur don't bubble while focusin/focusout do; simulate the former by only
6324			// invoking the handler at the lower level.
6325			if ( event.target === event.currentTarget ) {
6326
6327				// The setup part calls `leverageNative`, which, in turn, calls
6328				// `jQuery.event.add`, so event handle will already have been set
6329				// by this point.
6330				handle( event );
6331			}
6332		} else {
6333
6334			// For non-IE browsers, attach a single capturing handler on the document
6335			// while someone wants focusin/focusout.
6336			jQuery.event.simulate( delegateType, nativeEvent.target,
6337				jQuery.event.fix( nativeEvent ) );
6338		}
6339	}
6340
6341	jQuery.event.special[ type ] = {
6342
6343		// Utilize native event if possible so blur/focus sequence is correct
6344		setup: function() {
6345
6346			var attaches;
6347
6348			// Claim the first handler
6349			// dataPriv.set( this, "focus", ... )
6350			// dataPriv.set( this, "blur", ... )
6351			leverageNative( this, type, true );
6352
6353			if ( document.documentMode ) {
6354
6355				// Support: IE 9 - 11+
6356				// We use the same native handler for focusin & focus (and focusout & blur)
6357				// so we need to coordinate setup & teardown parts between those events.
6358				// Use `delegateType` as the key as `type` is already used by `leverageNative`.
6359				attaches = dataPriv.get( this, delegateType );
6360				if ( !attaches ) {
6361					this.addEventListener( delegateType, focusMappedHandler );
6362				}
6363				dataPriv.set( this, delegateType, ( attaches || 0 ) + 1 );
6364			} else {
6365
6366				// Return false to allow normal processing in the caller
6367				return false;
6368			}
6369		},
6370		trigger: function() {
6371
6372			// Force setup before trigger
6373			leverageNative( this, type );
6374
6375			// Return non-false to allow normal event-path propagation
6376			return true;
6377		},
6378
6379		teardown: function() {
6380			var attaches;
6381
6382			if ( document.documentMode ) {
6383				attaches = dataPriv.get( this, delegateType ) - 1;
6384				if ( !attaches ) {
6385					this.removeEventListener( delegateType, focusMappedHandler );
6386					dataPriv.remove( this, delegateType );
6387				} else {
6388					dataPriv.set( this, delegateType, attaches );
6389				}
6390			} else {
6391
6392				// Return false to indicate standard teardown should be applied
6393				return false;
6394			}
6395		},
6396
6397		// Suppress native focus or blur if we're currently inside
6398		// a leveraged native-event stack
6399		_default: function( event ) {
6400			return dataPriv.get( event.target, type );
6401		},
6402
6403		delegateType: delegateType
6404	};
6405
6406	// Support: Firefox <=44
6407	// Firefox doesn't have focus(in | out) events
6408	// Related ticket - https://bugzilla.mozilla.org/show_bug.cgi?id=687787
6409	//
6410	// Support: Chrome <=48 - 49, Safari <=9.0 - 9.1
6411	// focus(in | out) events fire after focus & blur events,
6412	// which is spec violation - http://www.w3.org/TR/DOM-Level-3-Events/#events-focusevent-event-order
6413	// Related ticket - https://bugs.chromium.org/p/chromium/issues/detail?id=449857
6414	//
6415	// Support: IE 9 - 11+
6416	// To preserve relative focusin/focus & focusout/blur event order guaranteed on the 3.x branch,
6417	// attach a single handler for both events in IE.
6418	jQuery.event.special[ delegateType ] = {
6419		setup: function() {
6420
6421			// Handle: regular nodes (via `this.ownerDocument`), window
6422			// (via `this.document`) & document (via `this`).
6423			var doc = this.ownerDocument || this.document || this,
6424				dataHolder = document.documentMode ? this : doc,
6425				attaches = dataPriv.get( dataHolder, delegateType );
6426
6427			// Support: IE 9 - 11+
6428			// We use the same native handler for focusin & focus (and focusout & blur)
6429			// so we need to coordinate setup & teardown parts between those events.
6430			// Use `delegateType` as the key as `type` is already used by `leverageNative`.
6431			if ( !attaches ) {
6432				if ( document.documentMode ) {
6433					this.addEventListener( delegateType, focusMappedHandler );
6434				} else {
6435					doc.addEventListener( type, focusMappedHandler, true );
6436				}
6437			}
6438			dataPriv.set( dataHolder, delegateType, ( attaches || 0 ) + 1 );
6439		},
6440		teardown: function() {
6441			var doc = this.ownerDocument || this.document || this,
6442				dataHolder = document.documentMode ? this : doc,
6443				attaches = dataPriv.get( dataHolder, delegateType ) - 1;
6444
6445			if ( !attaches ) {
6446				if ( document.documentMode ) {
6447					this.removeEventListener( delegateType, focusMappedHandler );
6448				} else {
6449					doc.removeEventListener( type, focusMappedHandler, true );
6450				}
6451				dataPriv.remove( dataHolder, delegateType );
6452			} else {
6453				dataPriv.set( dataHolder, delegateType, attaches );
6454			}
6455		}
6456	};
6457} );
6458
6459// Create mouseenter/leave events using mouseover/out and event-time checks
6460// so that event delegation works in jQuery.
6461// Do the same for pointerenter/pointerleave and pointerover/pointerout
6462//
6463// Support: Safari 7 only
6464// Safari sends mouseenter too often; see:
6465// https://bugs.chromium.org/p/chromium/issues/detail?id=470258
6466// for the description of the bug (it existed in older Chrome versions as well).
6467jQuery.each( {
6468	mouseenter: "mouseover",
6469	mouseleave: "mouseout",
6470	pointerenter: "pointerover",
6471	pointerleave: "pointerout"
6472}, function( orig, fix ) {
6473	jQuery.event.special[ orig ] = {
6474		delegateType: fix,
6475		bindType: fix,
6476
6477		handle: function( event ) {
6478			var ret,
6479				target = this,
6480				related = event.relatedTarget,
6481				handleObj = event.handleObj;
6482
6483			// For mouseenter/leave call the handler if related is outside the target.
6484			// NB: No relatedTarget if the mouse left/entered the browser window
6485			if ( !related || ( related !== target && !jQuery.contains( target, related ) ) ) {
6486				event.type = handleObj.origType;
6487				ret = handleObj.handler.apply( this, arguments );
6488				event.type = fix;
6489			}
6490			return ret;
6491		}
6492	};
6493} );
6494
6495jQuery.fn.extend( {
6496
6497	on: function( types, selector, data, fn ) {
6498		return on( this, types, selector, data, fn );
6499	},
6500	one: function( types, selector, data, fn ) {
6501		return on( this, types, selector, data, fn, 1 );
6502	},
6503	off: function( types, selector, fn ) {
6504		var handleObj, type;
6505		if ( types && types.preventDefault && types.handleObj ) {
6506
6507			// ( event )  dispatched jQuery.Event
6508			handleObj = types.handleObj;
6509			jQuery( types.delegateTarget ).off(
6510				handleObj.namespace ?
6511					handleObj.origType + "." + handleObj.namespace :
6512					handleObj.origType,
6513				handleObj.selector,
6514				handleObj.handler
6515			);
6516			return this;
6517		}
6518		if ( typeof types === "object" ) {
6519
6520			// ( types-object [, selector] )
6521			for ( type in types ) {
6522				this.off( type, selector, types[ type ] );
6523			}
6524			return this;
6525		}
6526		if ( selector === false || typeof selector === "function" ) {
6527
6528			// ( types [, fn] )
6529			fn = selector;
6530			selector = undefined;
6531		}
6532		if ( fn === false ) {
6533			fn = returnFalse;
6534		}
6535		return this.each( function() {
6536			jQuery.event.remove( this, types, fn, selector );
6537		} );
6538	}
6539} );
6540
6541
6542var
6543
6544	// Support: IE <=10 - 11, Edge 12 - 13 only
6545	// In IE/Edge using regex groups here causes severe slowdowns.
6546	// See https://connect.microsoft.com/IE/feedback/details/1736512/
6547	rnoInnerhtml = /<script|<style|<link/i,
6548
6549	// checked="checked" or checked
6550	rchecked = /checked\s*(?:[^=]|=\s*.checked.)/i,
6551
6552	rcleanScript = /^\s*<!\[CDATA\[|\]\]>\s*$/g;
6553
6554// Prefer a tbody over its parent table for containing new rows
6555function manipulationTarget( elem, content ) {
6556	if ( nodeName( elem, "table" ) &&
6557		nodeName( content.nodeType !== 11 ? content : content.firstChild, "tr" ) ) {
6558
6559		return jQuery( elem ).children( "tbody" )[ 0 ] || elem;
6560	}
6561
6562	return elem;
6563}
6564
6565// Replace/restore the type attribute of script elements for safe DOM manipulation
6566function disableScript( elem ) {
6567	elem.type = ( elem.getAttribute( "type" ) !== null ) + "/" + elem.type;
6568	return elem;
6569}
6570function restoreScript( elem ) {
6571	if ( ( elem.type || "" ).slice( 0, 5 ) === "true/" ) {
6572		elem.type = elem.type.slice( 5 );
6573	} else {
6574		elem.removeAttribute( "type" );
6575	}
6576
6577	return elem;
6578}
6579
6580function cloneCopyEvent( src, dest ) {
6581	var i, l, type, pdataOld, udataOld, udataCur, events;
6582
6583	if ( dest.nodeType !== 1 ) {
6584		return;
6585	}
6586
6587	// 1. Copy private data: events, handlers, etc.
6588	if ( dataPriv.hasData( src ) ) {
6589		pdataOld = dataPriv.get( src );
6590		events = pdataOld.events;
6591
6592		if ( events ) {
6593			dataPriv.remove( dest, "handle events" );
6594
6595			for ( type in events ) {
6596				for ( i = 0, l = events[ type ].length; i < l; i++ ) {
6597					jQuery.event.add( dest, type, events[ type ][ i ] );
6598				}
6599			}
6600		}
6601	}
6602
6603	// 2. Copy user data
6604	if ( dataUser.hasData( src ) ) {
6605		udataOld = dataUser.access( src );
6606		udataCur = jQuery.extend( {}, udataOld );
6607
6608		dataUser.set( dest, udataCur );
6609	}
6610}
6611
6612// Fix IE bugs, see support tests
6613function fixInput( src, dest ) {
6614	var nodeName = dest.nodeName.toLowerCase();
6615
6616	// Fails to persist the checked state of a cloned checkbox or radio button.
6617	if ( nodeName === "input" && rcheckableType.test( src.type ) ) {
6618		dest.checked = src.checked;
6619
6620	// Fails to return the selected option to the default selected state when cloning options
6621	} else if ( nodeName === "input" || nodeName === "textarea" ) {
6622		dest.defaultValue = src.defaultValue;
6623	}
6624}
6625
6626function domManip( collection, args, callback, ignored ) {
6627
6628	// Flatten any nested arrays
6629	args = flat( args );
6630
6631	var fragment, first, scripts, hasScripts, node, doc,
6632		i = 0,
6633		l = collection.length,
6634		iNoClone = l - 1,
6635		value = args[ 0 ],
6636		valueIsFunction = isFunction( value );
6637
6638	// We can't cloneNode fragments that contain checked, in WebKit
6639	if ( valueIsFunction ||
6640			( l > 1 && typeof value === "string" &&
6641				!support.checkClone && rchecked.test( value ) ) ) {
6642		return collection.each( function( index ) {
6643			var self = collection.eq( index );
6644			if ( valueIsFunction ) {
6645				args[ 0 ] = value.call( this, index, self.html() );
6646			}
6647			domManip( self, args, callback, ignored );
6648		} );
6649	}
6650
6651	if ( l ) {
6652		fragment = buildFragment( args, collection[ 0 ].ownerDocument, false, collection, ignored );
6653		first = fragment.firstChild;
6654
6655		if ( fragment.childNodes.length === 1 ) {
6656			fragment = first;
6657		}
6658
6659		// Require either new content or an interest in ignored elements to invoke the callback
6660		if ( first || ignored ) {
6661			scripts = jQuery.map( getAll( fragment, "script" ), disableScript );
6662			hasScripts = scripts.length;
6663
6664			// Use the original fragment for the last item
6665			// instead of the first because it can end up
6666			// being emptied incorrectly in certain situations (trac-8070).
6667			for ( ; i < l; i++ ) {
6668				node = fragment;
6669
6670				if ( i !== iNoClone ) {
6671					node = jQuery.clone( node, true, true );
6672
6673					// Keep references to cloned scripts for later restoration
6674					if ( hasScripts ) {
6675
6676						// Support: Android <=4.0 only, PhantomJS 1 only
6677						// push.apply(_, arraylike) throws on ancient WebKit
6678						jQuery.merge( scripts, getAll( node, "script" ) );
6679					}
6680				}
6681
6682				callback.call( collection[ i ], node, i );
6683			}
6684
6685			if ( hasScripts ) {
6686				doc = scripts[ scripts.length - 1 ].ownerDocument;
6687
6688				// Re-enable scripts
6689				jQuery.map( scripts, restoreScript );
6690
6691				// Evaluate executable scripts on first document insertion
6692				for ( i = 0; i < hasScripts; i++ ) {
6693					node = scripts[ i ];
6694					if ( rscriptType.test( node.type || "" ) &&
6695						!dataPriv.access( node, "globalEval" ) &&
6696						jQuery.contains( doc, node ) ) {
6697
6698						if ( node.src && ( node.type || "" ).toLowerCase()  !== "module" ) {
6699
6700							// Optional AJAX dependency, but won't run scripts if not present
6701							if ( jQuery._evalUrl && !node.noModule ) {
6702								jQuery._evalUrl( node.src, {
6703									nonce: node.nonce || node.getAttribute( "nonce" )
6704								}, doc );
6705							}
6706						} else {
6707
6708							// Unwrap a CDATA section containing script contents. This shouldn't be
6709							// needed as in XML documents they're already not visible when
6710							// inspecting element contents and in HTML documents they have no
6711							// meaning but we're preserving that logic for backwards compatibility.
6712							// This will be removed completely in 4.0. See gh-4904.
6713							DOMEval( node.textContent.replace( rcleanScript, "" ), node, doc );
6714						}
6715					}
6716				}
6717			}
6718		}
6719	}
6720
6721	return collection;
6722}
6723
6724function remove( elem, selector, keepData ) {
6725	var node,
6726		nodes = selector ? jQuery.filter( selector, elem ) : elem,
6727		i = 0;
6728
6729	for ( ; ( node = nodes[ i ] ) != null; i++ ) {
6730		if ( !keepData && node.nodeType === 1 ) {
6731			jQuery.cleanData( getAll( node ) );
6732		}
6733
6734		if ( node.parentNode ) {
6735			if ( keepData && isAttached( node ) ) {
6736				setGlobalEval( getAll( node, "script" ) );
6737			}
6738			node.parentNode.removeChild( node );
6739		}
6740	}
6741
6742	return elem;
6743}
6744
6745jQuery.extend( {
6746	htmlPrefilter: function( html ) {
6747		return html;
6748	},
6749
6750	clone: function( elem, dataAndEvents, deepDataAndEvents ) {
6751		var i, l, srcElements, destElements,
6752			clone = elem.cloneNode( true ),
6753			inPage = isAttached( elem );
6754
6755		// Fix IE cloning issues
6756		if ( !support.noCloneChecked && ( elem.nodeType === 1 || elem.nodeType === 11 ) &&
6757				!jQuery.isXMLDoc( elem ) ) {
6758
6759			// We eschew jQuery#find here for performance reasons:
6760			// https://jsperf.com/getall-vs-sizzle/2
6761			destElements = getAll( clone );
6762			srcElements = getAll( elem );
6763
6764			for ( i = 0, l = srcElements.length; i < l; i++ ) {
6765				fixInput( srcElements[ i ], destElements[ i ] );
6766			}
6767		}
6768
6769		// Copy the events from the original to the clone
6770		if ( dataAndEvents ) {
6771			if ( deepDataAndEvents ) {
6772				srcElements = srcElements || getAll( elem );
6773				destElements = destElements || getAll( clone );
6774
6775				for ( i = 0, l = srcElements.length; i < l; i++ ) {
6776					cloneCopyEvent( srcElements[ i ], destElements[ i ] );
6777				}
6778			} else {
6779				cloneCopyEvent( elem, clone );
6780			}
6781		}
6782
6783		// Preserve script evaluation history
6784		destElements = getAll( clone, "script" );
6785		if ( destElements.length > 0 ) {
6786			setGlobalEval( destElements, !inPage && getAll( elem, "script" ) );
6787		}
6788
6789		// Return the cloned set
6790		return clone;
6791	},
6792
6793	cleanData: function( elems ) {
6794		var data, elem, type,
6795			special = jQuery.event.special,
6796			i = 0;
6797
6798		for ( ; ( elem = elems[ i ] ) !== undefined; i++ ) {
6799			if ( acceptData( elem ) ) {
6800				if ( ( data = elem[ dataPriv.expando ] ) ) {
6801					if ( data.events ) {
6802						for ( type in data.events ) {
6803							if ( special[ type ] ) {
6804								jQuery.event.remove( elem, type );
6805
6806							// This is a shortcut to avoid jQuery.event.remove's overhead
6807							} else {
6808								jQuery.removeEvent( elem, type, data.handle );
6809							}
6810						}
6811					}
6812
vendor: 1,119 bytes, lines 6813-6857
6813					// Support: Chrome <=35 - 45+
6814					// Assign undefined instead of using delete, see Data#remove
6815					elem[ dataPriv.expando ] = undefined;
6816				}
6817				if ( elem[ dataUser.expando ] ) {
6818
6819					// Support: Chrome <=35 - 45+
6820					// Assign undefined instead of using delete, see Data#remove
6821					elem[ dataUser.expando ] = undefined;
6822				}
6823			}
6824		}
6825	}
6826} );
6827
6828jQuery.fn.extend( {
6829	detach: function( selector ) {
6830		return remove( this, selector, true );
6831	},
6832
6833	remove: function( selector ) {
6834		return remove( this, selector );
6835	},
6836
6837	text: function( value ) {
6838		return access( this, function( value ) {
6839			return value === undefined ?
6840				jQuery.text( this ) :
6841				this.empty().each( function() {
6842					if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) {
6843						this.textContent = value;
6844					}
6845				} );
6846		}, null, value, arguments.length );
6847	},
6848
6849	append: function() {
6850		return domManip( this, arguments, function( elem ) {
6851			if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) {
6852				var target = manipulationTarget( this, elem );
6853				target.appendChild( elem );
6854			}
6855		} );
6856	},
6857
vendor: 58,446 bytes, lines 6858-9072
6858	prepend: function() {
6859		return domManip( this, arguments, function( elem ) {
6860			if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) {
6861				var target = manipulationTarget( this, elem );
6862				target.insertBefore( elem, target.firstChild );
6863			}
6864		} );
6865	},
6866
6867	before: function() {
6868		return domManip( this, arguments, function( elem ) {
6869			if ( this.parentNode ) {
6870				this.parentNode.insertBefore( elem, this );
6871			}
6872		} );
6873	},
6874
6875	after: function() {
6876		return domManip( this, arguments, function( elem ) {
6877			if ( this.parentNode ) {
6878				this.parentNode.insertBefore( elem, this.nextSibling );
6879			}
6880		} );
6881	},
6882
6883	empty: function() {
6884		var elem,
6885			i = 0;
6886
6887		for ( ; ( elem = this[ i ] ) != null; i++ ) {
6888			if ( elem.nodeType === 1 ) {
6889
6890				// Prevent memory leaks
6891				jQuery.cleanData( getAll( elem, false ) );
6892
6893				// Remove any remaining nodes
6894				elem.textContent = "";
6895			}
6896		}
6897
6898		return this;
6899	},
6900
6901	clone: function( dataAndEvents, deepDataAndEvents ) {
6902		dataAndEvents = dataAndEvents == null ? false : dataAndEvents;
6903		deepDataAndEvents = deepDataAndEvents == null ? dataAndEvents : deepDataAndEvents;
6904
6905		return this.map( function() {
6906			return jQuery.clone( this, dataAndEvents, deepDataAndEvents );
6907		} );
6908	},
6909
6910	html: function( value ) {
6911		return access( this, function( value ) {
6912			var elem = this[ 0 ] || {},
6913				i = 0,
6914				l = this.length;
6915
6916			if ( value === undefined && elem.nodeType === 1 ) {
6917				return elem.innerHTML;
6918			}
6919
6920			// See if we can take a shortcut and just use innerHTML
6921			if ( typeof value === "string" && !rnoInnerhtml.test( value ) &&
6922				!wrapMap[ ( rtagName.exec( value ) || [ "", "" ] )[ 1 ].toLowerCase() ] ) {
6923
6924				value = jQuery.htmlPrefilter( value );
6925
6926				try {
6927					for ( ; i < l; i++ ) {
6928						elem = this[ i ] || {};
6929
6930						// Remove element nodes and prevent memory leaks
6931						if ( elem.nodeType === 1 ) {
6932							jQuery.cleanData( getAll( elem, false ) );
6933							elem.innerHTML = value;
6934						}
6935					}
6936
6937					elem = 0;
6938
6939				// If using innerHTML throws an exception, use the fallback method
6940				} catch ( e ) {}
6941			}
6942
6943			if ( elem ) {
6944				this.empty().append( value );
6945			}
6946		}, null, value, arguments.length );
6947	},
6948
6949	replaceWith: function() {
6950		var ignored = [];
6951
6952		// Make the changes, replacing each non-ignored context element with the new content
6953		return domManip( this, arguments, function( elem ) {
6954			var parent = this.parentNode;
6955
6956			if ( jQuery.inArray( this, ignored ) < 0 ) {
6957				jQuery.cleanData( getAll( this ) );
6958				if ( parent ) {
6959					parent.replaceChild( elem, this );
6960				}
6961			}
6962
6963		// Force callback invocation
6964		}, ignored );
6965	}
6966} );
6967
6968jQuery.each( {
6969	appendTo: "append",
6970	prependTo: "prepend",
6971	insertBefore: "before",
6972	insertAfter: "after",
6973	replaceAll: "replaceWith"
6974}, function( name, original ) {
6975	jQuery.fn[ name ] = function( selector ) {
6976		var elems,
6977			ret = [],
6978			insert = jQuery( selector ),
6979			last = insert.length - 1,
6980			i = 0;
6981
6982		for ( ; i <= last; i++ ) {
6983			elems = i === last ? this : this.clone( true );
6984			jQuery( insert[ i ] )[ original ]( elems );
6985
6986			// Support: Android <=4.0 only, PhantomJS 1 only
6987			// .get() because push.apply(_, arraylike) throws on ancient WebKit
6988			push.apply( ret, elems.get() );
6989		}
6990
6991		return this.pushStack( ret );
6992	};
6993} );
6994var rnumnonpx = new RegExp( "^(" + pnum + ")(?!px)[a-z%]+$", "i" );
6995
6996var rcustomProp = /^--/;
6997
6998
6999var getStyles = function( elem ) {
7000
7001		// Support: IE <=11 only, Firefox <=30 (trac-15098, trac-14150)
7002		// IE throws on elements created in popups
7003		// FF meanwhile throws on frame elements through "defaultView.getComputedStyle"
7004		var view = elem.ownerDocument.defaultView;
7005
7006		if ( !view || !view.opener ) {
7007			view = window;
7008		}
7009
7010		return view.getComputedStyle( elem );
7011	};
7012
7013var swap = function( elem, options, callback ) {
7014	var ret, name,
7015		old = {};
7016
7017	// Remember the old values, and insert the new ones
7018	for ( name in options ) {
7019		old[ name ] = elem.style[ name ];
7020		elem.style[ name ] = options[ name ];
7021	}
7022
7023	ret = callback.call( elem );
7024
7025	// Revert the old values
7026	for ( name in options ) {
7027		elem.style[ name ] = old[ name ];
7028	}
7029
7030	return ret;
7031};
7032
7033
7034var rboxStyle = new RegExp( cssExpand.join( "|" ), "i" );
7035
7036
7037
7038( function() {
7039
7040	// Executing both pixelPosition & boxSizingReliable tests require only one layout
7041	// so they're executed at the same time to save the second computation.
7042	function computeStyleTests() {
7043
7044		// This is a singleton, we need to execute it only once
7045		if ( !div ) {
7046			return;
7047		}
7048
7049		container.style.cssText = "position:absolute;left:-11111px;width:60px;" +
7050			"margin-top:1px;padding:0;border:0";
7051		div.style.cssText =
7052			"position:relative;display:block;box-sizing:border-box;overflow:scroll;" +
7053			"margin:auto;border:1px;padding:1px;" +
7054			"width:60%;top:1%";
7055		documentElement.appendChild( container ).appendChild( div );
7056
7057		var divStyle = window.getComputedStyle( div );
7058		pixelPositionVal = divStyle.top !== "1%";
7059
7060		// Support: Android 4.0 - 4.3 only, Firefox <=3 - 44
7061		reliableMarginLeftVal = roundPixelMeasures( divStyle.marginLeft ) === 12;
7062
7063		// Support: Android 4.0 - 4.3 only, Safari <=9.1 - 10.1, iOS <=7.0 - 9.3
7064		// Some styles come back with percentage values, even though they shouldn't
7065		div.style.right = "60%";
7066		pixelBoxStylesVal = roundPixelMeasures( divStyle.right ) === 36;
7067
7068		// Support: IE 9 - 11 only
7069		// Detect misreporting of content dimensions for box-sizing:border-box elements
7070		boxSizingReliableVal = roundPixelMeasures( divStyle.width ) === 36;
7071
7072		// Support: IE 9 only
7073		// Detect overflow:scroll screwiness (gh-3699)
7074		// Support: Chrome <=64
7075		// Don't get tricked when zoom affects offsetWidth (gh-4029)
7076		div.style.position = "absolute";
7077		scrollboxSizeVal = roundPixelMeasures( div.offsetWidth / 3 ) === 12;
7078
7079		documentElement.removeChild( container );
7080
7081		// Nullify the div so it wouldn't be stored in the memory and
7082		// it will also be a sign that checks already performed
7083		div = null;
7084	}
7085
7086	function roundPixelMeasures( measure ) {
7087		return Math.round( parseFloat( measure ) );
7088	}
7089
7090	var pixelPositionVal, boxSizingReliableVal, scrollboxSizeVal, pixelBoxStylesVal,
7091		reliableTrDimensionsVal, reliableMarginLeftVal,
7092		container = document.createElement( "div" ),
7093		div = document.createElement( "div" );
7094
7095	// Finish early in limited (non-browser) environments
7096	if ( !div.style ) {
7097		return;
7098	}
7099
7100	// Support: IE <=9 - 11 only
7101	// Style of cloned element affects source element cloned (trac-8908)
7102	div.style.backgroundClip = "content-box";
7103	div.cloneNode( true ).style.backgroundClip = "";
7104	support.clearCloneStyle = div.style.backgroundClip === "content-box";
7105
7106	jQuery.extend( support, {
7107		boxSizingReliable: function() {
7108			computeStyleTests();
7109			return boxSizingReliableVal;
7110		},
7111		pixelBoxStyles: function() {
7112			computeStyleTests();
7113			return pixelBoxStylesVal;
7114		},
7115		pixelPosition: function() {
7116			computeStyleTests();
7117			return pixelPositionVal;
7118		},
7119		reliableMarginLeft: function() {
7120			computeStyleTests();
7121			return reliableMarginLeftVal;
7122		},
7123		scrollboxSize: function() {
7124			computeStyleTests();
7125			return scrollboxSizeVal;
7126		},
7127
7128		// Support: IE 9 - 11+, Edge 15 - 18+
7129		// IE/Edge misreport `getComputedStyle` of table rows with width/height
7130		// set in CSS while `offset*` properties report correct values.
7131		// Behavior in IE 9 is more subtle than in newer versions & it passes
7132		// some versions of this test; make sure not to make it pass there!
7133		//
7134		// Support: Firefox 70+
7135		// Only Firefox includes border widths
7136		// in computed dimensions. (gh-4529)
7137		reliableTrDimensions: function() {
7138			var table, tr, trChild, trStyle;
7139			if ( reliableTrDimensionsVal == null ) {
7140				table = document.createElement( "table" );
7141				tr = document.createElement( "tr" );
7142				trChild = document.createElement( "div" );
7143
7144				table.style.cssText = "position:absolute;left:-11111px;border-collapse:separate";
7145				tr.style.cssText = "box-sizing:content-box;border:1px solid";
7146
7147				// Support: Chrome 86+
7148				// Height set through cssText does not get applied.
7149				// Computed height then comes back as 0.
7150				tr.style.height = "1px";
7151				trChild.style.height = "9px";
7152
7153				// Support: Android 8 Chrome 86+
7154				// In our bodyBackground.html iframe,
7155				// display for all div elements is set to "inline",
7156				// which causes a problem only in Android 8 Chrome 86.
7157				// Ensuring the div is `display: block`
7158				// gets around this issue.
7159				trChild.style.display = "block";
7160
7161				documentElement
7162					.appendChild( table )
7163					.appendChild( tr )
7164					.appendChild( trChild );
7165
7166				trStyle = window.getComputedStyle( tr );
7167				reliableTrDimensionsVal = ( parseInt( trStyle.height, 10 ) +
7168					parseInt( trStyle.borderTopWidth, 10 ) +
7169					parseInt( trStyle.borderBottomWidth, 10 ) ) === tr.offsetHeight;
7170
7171				documentElement.removeChild( table );
7172			}
7173			return reliableTrDimensionsVal;
7174		}
7175	} );
7176} )();
7177
7178
7179function curCSS( elem, name, computed ) {
7180	var width, minWidth, maxWidth, ret,
7181		isCustomProp = rcustomProp.test( name ),
7182
7183		// Support: Firefox 51+
7184		// Retrieving style before computed somehow
7185		// fixes an issue with getting wrong values
7186		// on detached elements
7187		style = elem.style;
7188
7189	computed = computed || getStyles( elem );
7190
7191	// getPropertyValue is needed for:
7192	//   .css('filter') (IE 9 only, trac-12537)
7193	//   .css('--customProperty) (gh-3144)
7194	if ( computed ) {
7195
7196		// Support: IE <=9 - 11+
7197		// IE only supports `"float"` in `getPropertyValue`; in computed styles
7198		// it's only available as `"cssFloat"`. We no longer modify properties
7199		// sent to `.css()` apart from camelCasing, so we need to check both.
7200		// Normally, this would create difference in behavior: if
7201		// `getPropertyValue` returns an empty string, the value returned
7202		// by `.css()` would be `undefined`. This is usually the case for
7203		// disconnected elements. However, in IE even disconnected elements
7204		// with no styles return `"none"` for `getPropertyValue( "float" )`
7205		ret = computed.getPropertyValue( name ) || computed[ name ];
7206
7207		if ( isCustomProp && ret ) {
7208
7209			// Support: Firefox 105+, Chrome <=105+
7210			// Spec requires trimming whitespace for custom properties (gh-4926).
7211			// Firefox only trims leading whitespace. Chrome just collapses
7212			// both leading & trailing whitespace to a single space.
7213			//
7214			// Fall back to `undefined` if empty string returned.
7215			// This collapses a missing definition with property defined
7216			// and set to an empty string but there's no standard API
7217			// allowing us to differentiate them without a performance penalty
7218			// and returning `undefined` aligns with older jQuery.
7219			//
7220			// rtrimCSS treats U+000D CARRIAGE RETURN and U+000C FORM FEED
7221			// as whitespace while CSS does not, but this is not a problem
7222			// because CSS preprocessing replaces them with U+000A LINE FEED
7223			// (which *is* CSS whitespace)
7224			// https://www.w3.org/TR/css-syntax-3/#input-preprocessing
7225			ret = ret.replace( rtrimCSS, "$1" ) || undefined;
7226		}
7227
7228		if ( ret === "" && !isAttached( elem ) ) {
7229			ret = jQuery.style( elem, name );
7230		}
7231
7232		// A tribute to the "awesome hack by Dean Edwards"
7233		// Android Browser returns percentage for some values,
7234		// but width seems to be reliably pixels.
7235		// This is against the CSSOM draft spec:
7236		// https://drafts.csswg.org/cssom/#resolved-values
7237		if ( !support.pixelBoxStyles() && rnumnonpx.test( ret ) && rboxStyle.test( name ) ) {
7238
7239			// Remember the original values
7240			width = style.width;
7241			minWidth = style.minWidth;
7242			maxWidth = style.maxWidth;
7243
7244			// Put in the new values to get a computed value out
7245			style.minWidth = style.maxWidth = style.width = ret;
7246			ret = computed.width;
7247
7248			// Revert the changed values
7249			style.width = width;
7250			style.minWidth = minWidth;
7251			style.maxWidth = maxWidth;
7252		}
7253	}
7254
7255	return ret !== undefined ?
7256
7257		// Support: IE <=9 - 11 only
7258		// IE returns zIndex value as an integer.
7259		ret + "" :
7260		ret;
7261}
7262
7263
7264function addGetHookIf( conditionFn, hookFn ) {
7265
7266	// Define the hook, we'll check on the first run if it's really needed.
7267	return {
7268		get: function() {
7269			if ( conditionFn() ) {
7270
7271				// Hook not needed (or it's not possible to use it due
7272				// to missing dependency), remove it.
7273				delete this.get;
7274				return;
7275			}
7276
7277			// Hook needed; redefine it so that the support test is not executed again.
7278			return ( this.get = hookFn ).apply( this, arguments );
7279		}
7280	};
7281}
7282
7283
7284var cssPrefixes = [ "Webkit", "Moz", "ms" ],
7285	emptyStyle = document.createElement( "div" ).style,
7286	vendorProps = {};
7287
7288// Return a vendor-prefixed property or undefined
7289function vendorPropName( name ) {
7290
7291	// Check for vendor prefixed names
7292	var capName = name[ 0 ].toUpperCase() + name.slice( 1 ),
7293		i = cssPrefixes.length;
7294
7295	while ( i-- ) {
7296		name = cssPrefixes[ i ] + capName;
7297		if ( name in emptyStyle ) {
7298			return name;
7299		}
7300	}
7301}
7302
7303// Return a potentially-mapped jQuery.cssProps or vendor prefixed property
7304function finalPropName( name ) {
7305	var final = jQuery.cssProps[ name ] || vendorProps[ name ];
7306
7307	if ( final ) {
7308		return final;
7309	}
7310	if ( name in emptyStyle ) {
7311		return name;
7312	}
7313	return vendorProps[ name ] = vendorPropName( name ) || name;
7314}
7315
7316
7317var
7318
7319	// Swappable if display is none or starts with table
7320	// except "table", "table-cell", or "table-caption"
7321	// See here for display values: https://developer.mozilla.org/en-US/docs/CSS/display
7322	rdisplayswap = /^(none|table(?!-c[ea]).+)/,
7323	cssShow = { position: "absolute", visibility: "hidden", display: "block" },
7324	cssNormalTransform = {
7325		letterSpacing: "0",
7326		fontWeight: "400"
7327	};
7328
7329function setPositiveNumber( _elem, value, subtract ) {
7330
7331	// Any relative (+/-) values have already been
7332	// normalized at this point
7333	var matches = rcssNum.exec( value );
7334	return matches ?
7335
7336		// Guard against undefined "subtract", e.g., when used as in cssHooks
7337		Math.max( 0, matches[ 2 ] - ( subtract || 0 ) ) + ( matches[ 3 ] || "px" ) :
7338		value;
7339}
7340
7341function boxModelAdjustment( elem, dimension, box, isBorderBox, styles, computedVal ) {
7342	var i = dimension === "width" ? 1 : 0,
7343		extra = 0,
7344		delta = 0,
7345		marginDelta = 0;
7346
7347	// Adjustment may not be necessary
7348	if ( box === ( isBorderBox ? "border" : "content" ) ) {
7349		return 0;
7350	}
7351
7352	for ( ; i < 4; i += 2 ) {
7353
7354		// Both box models exclude margin
7355		// Count margin delta separately to only add it after scroll gutter adjustment.
7356		// This is needed to make negative margins work with `outerHeight( true )` (gh-3982).
7357		if ( box === "margin" ) {
7358			marginDelta += jQuery.css( elem, box + cssExpand[ i ], true, styles );
7359		}
7360
7361		// If we get here with a content-box, we're seeking "padding" or "border" or "margin"
7362		if ( !isBorderBox ) {
7363
7364			// Add padding
7365			delta += jQuery.css( elem, "padding" + cssExpand[ i ], true, styles );
7366
7367			// For "border" or "margin", add border
7368			if ( box !== "padding" ) {
7369				delta += jQuery.css( elem, "border" + cssExpand[ i ] + "Width", true, styles );
7370
7371			// But still keep track of it otherwise
7372			} else {
7373				extra += jQuery.css( elem, "border" + cssExpand[ i ] + "Width", true, styles );
7374			}
7375
7376		// If we get here with a border-box (content + padding + border), we're seeking "content" or
7377		// "padding" or "margin"
7378		} else {
7379
7380			// For "content", subtract padding
7381			if ( box === "content" ) {
7382				delta -= jQuery.css( elem, "padding" + cssExpand[ i ], true, styles );
7383			}
7384
7385			// For "content" or "padding", subtract border
7386			if ( box !== "margin" ) {
7387				delta -= jQuery.css( elem, "border" + cssExpand[ i ] + "Width", true, styles );
7388			}
7389		}
7390	}
7391
7392	// Account for positive content-box scroll gutter when requested by providing computedVal
7393	if ( !isBorderBox && computedVal >= 0 ) {
7394
7395		// offsetWidth/offsetHeight is a rounded sum of content, padding, scroll gutter, and border
7396		// Assuming integer scroll gutter, subtract the rest and round down
7397		delta += Math.max( 0, Math.ceil(
7398			elem[ "offset" + dimension[ 0 ].toUpperCase() + dimension.slice( 1 ) ] -
7399			computedVal -
7400			delta -
7401			extra -
7402			0.5
7403
7404		// If offsetWidth/offsetHeight is unknown, then we can't determine content-box scroll gutter
7405		// Use an explicit zero to avoid NaN (gh-3964)
7406		) ) || 0;
7407	}
7408
7409	return delta + marginDelta;
7410}
7411
7412function getWidthOrHeight( elem, dimension, extra ) {
7413
7414	// Start with computed style
7415	var styles = getStyles( elem ),
7416
7417		// To avoid forcing a reflow, only fetch boxSizing if we need it (gh-4322).
7418		// Fake content-box until we know it's needed to know the true value.
7419		boxSizingNeeded = !support.boxSizingReliable() || extra,
7420		isBorderBox = boxSizingNeeded &&
7421			jQuery.css( elem, "boxSizing", false, styles ) === "border-box",
7422		valueIsBorderBox = isBorderBox,
7423
7424		val = curCSS( elem, dimension, styles ),
7425		offsetProp = "offset" + dimension[ 0 ].toUpperCase() + dimension.slice( 1 );
7426
7427	// Support: Firefox <=54
7428	// Return a confounding non-pixel value or feign ignorance, as appropriate.
7429	if ( rnumnonpx.test( val ) ) {
7430		if ( !extra ) {
7431			return val;
7432		}
7433		val = "auto";
7434	}
7435
7436
7437	// Support: IE 9 - 11 only
7438	// Use offsetWidth/offsetHeight for when box sizing is unreliable.
7439	// In those cases, the computed value can be trusted to be border-box.
7440	if ( ( !support.boxSizingReliable() && isBorderBox ||
7441
7442		// Support: IE 10 - 11+, Edge 15 - 18+
7443		// IE/Edge misreport `getComputedStyle` of table rows with width/height
7444		// set in CSS while `offset*` properties report correct values.
7445		// Interestingly, in some cases IE 9 doesn't suffer from this issue.
7446		!support.reliableTrDimensions() && nodeName( elem, "tr" ) ||
7447
7448		// Fall back to offsetWidth/offsetHeight when value is "auto"
7449		// This happens for inline elements with no explicit setting (gh-3571)
7450		val === "auto" ||
7451
7452		// Support: Android <=4.1 - 4.3 only
7453		// Also use offsetWidth/offsetHeight for misreported inline dimensions (gh-3602)
7454		!parseFloat( val ) && jQuery.css( elem, "display", false, styles ) === "inline" ) &&
7455
7456		// Make sure the element is visible & connected
7457		elem.getClientRects().length ) {
7458
7459		isBorderBox = jQuery.css( elem, "boxSizing", false, styles ) === "border-box";
7460
7461		// Where available, offsetWidth/offsetHeight approximate border box dimensions.
7462		// Where not available (e.g., SVG), assume unreliable box-sizing and interpret the
7463		// retrieved value as a content box dimension.
7464		valueIsBorderBox = offsetProp in elem;
7465		if ( valueIsBorderBox ) {
7466			val = elem[ offsetProp ];
7467		}
7468	}
7469
7470	// Normalize "" and auto
7471	val = parseFloat( val ) || 0;
7472
7473	// Adjust for the element's box model
7474	return ( val +
7475		boxModelAdjustment(
7476			elem,
7477			dimension,
7478			extra || ( isBorderBox ? "border" : "content" ),
7479			valueIsBorderBox,
7480			styles,
7481
7482			// Provide the current computed size to request scroll gutter calculation (gh-3589)
7483			val
7484		)
7485	) + "px";
7486}
7487
7488jQuery.extend( {
7489
7490	// Add in style property hooks for overriding the default
7491	// behavior of getting and setting a style property
7492	cssHooks: {
7493		opacity: {
7494			get: function( elem, computed ) {
7495				if ( computed ) {
7496
7497					// We should always get a number back from opacity
7498					var ret = curCSS( elem, "opacity" );
7499					return ret === "" ? "1" : ret;
7500				}
7501			}
7502		}
7503	},
7504
7505	// Don't automatically add "px" to these possibly-unitless properties
7506	cssNumber: {
7507		animationIterationCount: true,
7508		aspectRatio: true,
7509		borderImageSlice: true,
7510		columnCount: true,
7511		flexGrow: true,
7512		flexShrink: true,
7513		fontWeight: true,
7514		gridArea: true,
7515		gridColumn: true,
7516		gridColumnEnd: true,
7517		gridColumnStart: true,
7518		gridRow: true,
7519		gridRowEnd: true,
7520		gridRowStart: true,
7521		lineHeight: true,
7522		opacity: true,
7523		order: true,
7524		orphans: true,
7525		scale: true,
7526		widows: true,
7527		zIndex: true,
7528		zoom: true,
7529
7530		// SVG-related
7531		fillOpacity: true,
7532		floodOpacity: true,
7533		stopOpacity: true,
7534		strokeMiterlimit: true,
7535		strokeOpacity: true
7536	},
7537
7538	// Add in properties whose names you wish to fix before
7539	// setting or getting the value
7540	cssProps: {},
7541
7542	// Get and set the style property on a DOM Node
7543	style: function( elem, name, value, extra ) {
7544
7545		// Don't set styles on text and comment nodes
7546		if ( !elem || elem.nodeType === 3 || elem.nodeType === 8 || !elem.style ) {
7547			return;
7548		}
7549
7550		// Make sure that we're working with the right name
7551		var ret, type, hooks,
7552			origName = camelCase( name ),
7553			isCustomProp = rcustomProp.test( name ),
7554			style = elem.style;
7555
7556		// Make sure that we're working with the right name. We don't
7557		// want to query the value if it is a CSS custom property
7558		// since they are user-defined.
7559		if ( !isCustomProp ) {
7560			name = finalPropName( origName );
7561		}
7562
7563		// Gets hook for the prefixed version, then unprefixed version
7564		hooks = jQuery.cssHooks[ name ] || jQuery.cssHooks[ origName ];
7565
7566		// Check if we're setting a value
7567		if ( value !== undefined ) {
7568			type = typeof value;
7569
7570			// Convert "+=" or "-=" to relative numbers (trac-7345)
7571			if ( type === "string" && ( ret = rcssNum.exec( value ) ) && ret[ 1 ] ) {
7572				value = adjustCSS( elem, name, ret );
7573
7574				// Fixes bug trac-9237
7575				type = "number";
7576			}
7577
7578			// Make sure that null and NaN values aren't set (trac-7116)
7579			if ( value == null || value !== value ) {
7580				return;
7581			}
7582
7583			// If a number was passed in, add the unit (except for certain CSS properties)
7584			// The isCustomProp check can be removed in jQuery 4.0 when we only auto-append
7585			// "px" to a few hardcoded values.
7586			if ( type === "number" && !isCustomProp ) {
7587				value += ret && ret[ 3 ] || ( jQuery.cssNumber[ origName ] ? "" : "px" );
7588			}
7589
7590			// background-* props affect original clone's values
7591			if ( !support.clearCloneStyle && value === "" && name.indexOf( "background" ) === 0 ) {
7592				style[ name ] = "inherit";
7593			}
7594
7595			// If a hook was provided, use that value, otherwise just set the specified value
7596			if ( !hooks || !( "set" in hooks ) ||
7597				( value = hooks.set( elem, value, extra ) ) !== undefined ) {
7598
7599				if ( isCustomProp ) {
7600					style.setProperty( name, value );
7601				} else {
7602					style[ name ] = value;
7603				}
7604			}
7605
7606		} else {
7607
7608			// If a hook was provided get the non-computed value from there
7609			if ( hooks && "get" in hooks &&
7610				( ret = hooks.get( elem, false, extra ) ) !== undefined ) {
7611
7612				return ret;
7613			}
7614
7615			// Otherwise just get the value from the style object
7616			return style[ name ];
7617		}
7618	},
7619
7620	css: function( elem, name, extra, styles ) {
7621		var val, num, hooks,
7622			origName = camelCase( name ),
7623			isCustomProp = rcustomProp.test( name );
7624
7625		// Make sure that we're working with the right name. We don't
7626		// want to modify the value if it is a CSS custom property
7627		// since they are user-defined.
7628		if ( !isCustomProp ) {
7629			name = finalPropName( origName );
7630		}
7631
7632		// Try prefixed name followed by the unprefixed name
7633		hooks = jQuery.cssHooks[ name ] || jQuery.cssHooks[ origName ];
7634
7635		// If a hook was provided get the computed value from there
7636		if ( hooks && "get" in hooks ) {
7637			val = hooks.get( elem, true, extra );
7638		}
7639
7640		// Otherwise, if a way to get the computed value exists, use that
7641		if ( val === undefined ) {
7642			val = curCSS( elem, name, styles );
7643		}
7644
7645		// Convert "normal" to computed value
7646		if ( val === "normal" && name in cssNormalTransform ) {
7647			val = cssNormalTransform[ name ];
7648		}
7649
7650		// Make numeric if forced or a qualifier was provided and val looks numeric
7651		if ( extra === "" || extra ) {
7652			num = parseFloat( val );
7653			return extra === true || isFinite( num ) ? num || 0 : val;
7654		}
7655
7656		return val;
7657	}
7658} );
7659
7660jQuery.each( [ "height", "width" ], function( _i, dimension ) {
7661	jQuery.cssHooks[ dimension ] = {
7662		get: function( elem, computed, extra ) {
7663			if ( computed ) {
7664
7665				// Certain elements can have dimension info if we invisibly show them
7666				// but it must have a current display style that would benefit
7667				return rdisplayswap.test( jQuery.css( elem, "display" ) ) &&
7668
7669					// Support: Safari 8+
7670					// Table columns in Safari have non-zero offsetWidth & zero
7671					// getBoundingClientRect().width unless display is changed.
7672					// Support: IE <=11 only
7673					// Running getBoundingClientRect on a disconnected node
7674					// in IE throws an error.
7675					( !elem.getClientRects().length || !elem.getBoundingClientRect().width ) ?
7676					swap( elem, cssShow, function() {
7677						return getWidthOrHeight( elem, dimension, extra );
7678					} ) :
7679					getWidthOrHeight( elem, dimension, extra );
7680			}
7681		},
7682
7683		set: function( elem, value, extra ) {
7684			var matches,
7685				styles = getStyles( elem ),
7686
7687				// Only read styles.position if the test has a chance to fail
7688				// to avoid forcing a reflow.
7689				scrollboxSizeBuggy = !support.scrollboxSize() &&
7690					styles.position === "absolute",
7691
7692				// To avoid forcing a reflow, only fetch boxSizing if we need it (gh-3991)
7693				boxSizingNeeded = scrollboxSizeBuggy || extra,
7694				isBorderBox = boxSizingNeeded &&
7695					jQuery.css( elem, "boxSizing", false, styles ) === "border-box",
7696				subtract = extra ?
7697					boxModelAdjustment(
7698						elem,
7699						dimension,
7700						extra,
7701						isBorderBox,
7702						styles
7703					) :
7704					0;
7705
7706			// Account for unreliable border-box dimensions by comparing offset* to computed and
7707			// faking a content-box to get border and padding (gh-3699)
7708			if ( isBorderBox && scrollboxSizeBuggy ) {
7709				subtract -= Math.ceil(
7710					elem[ "offset" + dimension[ 0 ].toUpperCase() + dimension.slice( 1 ) ] -
7711					parseFloat( styles[ dimension ] ) -
7712					boxModelAdjustment( elem, dimension, "border", false, styles ) -
7713					0.5
7714				);
7715			}
7716
7717			// Convert to pixels if value adjustment is needed
7718			if ( subtract && ( matches = rcssNum.exec( value ) ) &&
7719				( matches[ 3 ] || "px" ) !== "px" ) {
7720
7721				elem.style[ dimension ] = value;
7722				value = jQuery.css( elem, dimension );
7723			}
7724
7725			return setPositiveNumber( elem, value, subtract );
7726		}
7727	};
7728} );
7729
7730jQuery.cssHooks.marginLeft = addGetHookIf( support.reliableMarginLeft,
7731	function( elem, computed ) {
7732		if ( computed ) {
7733			return ( parseFloat( curCSS( elem, "marginLeft" ) ) ||
7734				elem.getBoundingClientRect().left -
7735					swap( elem, { marginLeft: 0 }, function() {
7736						return elem.getBoundingClientRect().left;
7737					} )
7738			) + "px";
7739		}
7740	}
7741);
7742
7743// These hooks are used by animate to expand properties
7744jQuery.each( {
7745	margin: "",
7746	padding: "",
7747	border: "Width"
7748}, function( prefix, suffix ) {
7749	jQuery.cssHooks[ prefix + suffix ] = {
7750		expand: function( value ) {
7751			var i = 0,
7752				expanded = {},
7753
7754				// Assumes a single number if not a string
7755				parts = typeof value === "string" ? value.split( " " ) : [ value ];
7756
7757			for ( ; i < 4; i++ ) {
7758				expanded[ prefix + cssExpand[ i ] + suffix ] =
7759					parts[ i ] || parts[ i - 2 ] || parts[ 0 ];
7760			}
7761
7762			return expanded;
7763		}
7764	};
7765
7766	if ( prefix !== "margin" ) {
7767		jQuery.cssHooks[ prefix + suffix ].set = setPositiveNumber;
7768	}
7769} );
7770
7771jQuery.fn.extend( {
7772	css: function( name, value ) {
7773		return access( this, function( elem, name, value ) {
7774			var styles, len,
7775				map = {},
7776				i = 0;
7777
7778			if ( Array.isArray( name ) ) {
7779				styles = getStyles( elem );
7780				len = name.length;
7781
7782				for ( ; i < len; i++ ) {
7783					map[ name[ i ] ] = jQuery.css( elem, name[ i ], false, styles );
7784				}
7785
7786				return map;
7787			}
7788
7789			return value !== undefined ?
7790				jQuery.style( elem, name, value ) :
7791				jQuery.css( elem, name );
7792		}, name, value, arguments.length > 1 );
7793	}
7794} );
7795
7796
7797function Tween( elem, options, prop, end, easing ) {
7798	return new Tween.prototype.init( elem, options, prop, end, easing );
7799}
7800jQuery.Tween = Tween;
7801
7802Tween.prototype = {
7803	constructor: Tween,
7804	init: function( elem, options, prop, end, easing, unit ) {
7805		this.elem = elem;
7806		this.prop = prop;
7807		this.easing = easing || jQuery.easing._default;
7808		this.options = options;
7809		this.start = this.now = this.cur();
7810		this.end = end;
7811		this.unit = unit || ( jQuery.cssNumber[ prop ] ? "" : "px" );
7812	},
7813	cur: function() {
7814		var hooks = Tween.propHooks[ this.prop ];
7815
7816		return hooks && hooks.get ?
7817			hooks.get( this ) :
7818			Tween.propHooks._default.get( this );
7819	},
7820	run: function( percent ) {
7821		var eased,
7822			hooks = Tween.propHooks[ this.prop ];
7823
7824		if ( this.options.duration ) {
7825			this.pos = eased = jQuery.easing[ this.easing ](
7826				percent, this.options.duration * percent, 0, 1, this.options.duration
7827			);
7828		} else {
7829			this.pos = eased = percent;
7830		}
7831		this.now = ( this.end - this.start ) * eased + this.start;
7832
7833		if ( this.options.step ) {
7834			this.options.step.call( this.elem, this.now, this );
7835		}
7836
7837		if ( hooks && hooks.set ) {
7838			hooks.set( this );
7839		} else {
7840			Tween.propHooks._default.set( this );
7841		}
7842		return this;
7843	}
7844};
7845
7846Tween.prototype.init.prototype = Tween.prototype;
7847
7848Tween.propHooks = {
7849	_default: {
7850		get: function( tween ) {
7851			var result;
7852
7853			// Use a property on the element directly when it is not a DOM element,
7854			// or when there is no matching style property that exists.
7855			if ( tween.elem.nodeType !== 1 ||
7856				tween.elem[ tween.prop ] != null && tween.elem.style[ tween.prop ] == null ) {
7857				return tween.elem[ tween.prop ];
7858			}
7859
7860			// Passing an empty string as a 3rd parameter to .css will automatically
7861			// attempt a parseFloat and fallback to a string if the parse fails.
7862			// Simple values such as "10px" are parsed to Float;
7863			// complex values such as "rotate(1rad)" are returned as-is.
7864			result = jQuery.css( tween.elem, tween.prop, "" );
7865
7866			// Empty strings, null, undefined and "auto" are converted to 0.
7867			return !result || result === "auto" ? 0 : result;
7868		},
7869		set: function( tween ) {
7870
7871			// Use step hook for back compat.
7872			// Use cssHook if its there.
7873			// Use .style if available and use plain properties where available.
7874			if ( jQuery.fx.step[ tween.prop ] ) {
7875				jQuery.fx.step[ tween.prop ]( tween );
7876			} else if ( tween.elem.nodeType === 1 && (
7877				jQuery.cssHooks[ tween.prop ] ||
7878					tween.elem.style[ finalPropName( tween.prop ) ] != null ) ) {
7879				jQuery.style( tween.elem, tween.prop, tween.now + tween.unit );
7880			} else {
7881				tween.elem[ tween.prop ] = tween.now;
7882			}
7883		}
7884	}
7885};
7886
7887// Support: IE <=9 only
7888// Panic based approach to setting things on disconnected nodes
7889Tween.propHooks.scrollTop = Tween.propHooks.scrollLeft = {
7890	set: function( tween ) {
7891		if ( tween.elem.nodeType && tween.elem.parentNode ) {
7892			tween.elem[ tween.prop ] = tween.now;
7893		}
7894	}
7895};
7896
7897jQuery.easing = {
7898	linear: function( p ) {
7899		return p;
7900	},
7901	swing: function( p ) {
7902		return 0.5 - Math.cos( p * Math.PI ) / 2;
7903	},
7904	_default: "swing"
7905};
7906
7907jQuery.fx = Tween.prototype.init;
7908
7909// Back compat <1.8 extension point
7910jQuery.fx.step = {};
7911
7912
7913
7914
7915var
7916	fxNow, inProgress,
7917	rfxtypes = /^(?:toggle|show|hide)$/,
7918	rrun = /queueHooks$/;
7919
7920function schedule() {
7921	if ( inProgress ) {
7922		if ( document.hidden === false && window.requestAnimationFrame ) {
7923			window.requestAnimationFrame( schedule );
7924		} else {
7925			window.setTimeout( schedule, jQuery.fx.interval );
7926		}
7927
7928		jQuery.fx.tick();
7929	}
7930}
7931
7932// Animations created synchronously will run synchronously
7933function createFxNow() {
7934	window.setTimeout( function() {
7935		fxNow = undefined;
7936	} );
7937	return ( fxNow = Date.now() );
7938}
7939
7940// Generate parameters to create a standard animation
7941function genFx( type, includeWidth ) {
7942	var which,
7943		i = 0,
7944		attrs = { height: type };
7945
7946	// If we include width, step value is 1 to do all cssExpand values,
7947	// otherwise step value is 2 to skip over Left and Right
7948	includeWidth = includeWidth ? 1 : 0;
7949	for ( ; i < 4; i += 2 - includeWidth ) {
7950		which = cssExpand[ i ];
7951		attrs[ "margin" + which ] = attrs[ "padding" + which ] = type;
7952	}
7953
7954	if ( includeWidth ) {
7955		attrs.opacity = attrs.width = type;
7956	}
7957
7958	return attrs;
7959}
7960
7961function createTween( value, prop, animation ) {
7962	var tween,
7963		collection = ( Animation.tweeners[ prop ] || [] ).concat( Animation.tweeners[ "*" ] ),
7964		index = 0,
7965		length = collection.length;
7966	for ( ; index < length; index++ ) {
7967		if ( ( tween = collection[ index ].call( animation, prop, value ) ) ) {
7968
7969			// We're done with this property
7970			return tween;
7971		}
7972	}
7973}
7974
7975function defaultPrefilter( elem, props, opts ) {
7976	var prop, value, toggle, hooks, oldfire, propTween, restoreDisplay, display,
7977		isBox = "width" in props || "height" in props,
7978		anim = this,
7979		orig = {},
7980		style = elem.style,
7981		hidden = elem.nodeType && isHiddenWithinTree( elem ),
7982		dataShow = dataPriv.get( elem, "fxshow" );
7983
7984	// Queue-skipping animations hijack the fx hooks
7985	if ( !opts.queue ) {
7986		hooks = jQuery._queueHooks( elem, "fx" );
7987		if ( hooks.unqueued == null ) {
7988			hooks.unqueued = 0;
7989			oldfire = hooks.empty.fire;
7990			hooks.empty.fire = function() {
7991				if ( !hooks.unqueued ) {
7992					oldfire();
7993				}
7994			};
7995		}
7996		hooks.unqueued++;
7997
7998		anim.always( function() {
7999
8000			// Ensure the complete handler is called before this completes
8001			anim.always( function() {
8002				hooks.unqueued--;
8003				if ( !jQuery.queue( elem, "fx" ).length ) {
8004					hooks.empty.fire();
8005				}
8006			} );
8007		} );
8008	}
8009
8010	// Detect show/hide animations
8011	for ( prop in props ) {
8012		value = props[ prop ];
8013		if ( rfxtypes.test( value ) ) {
8014			delete props[ prop ];
8015			toggle = toggle || value === "toggle";
8016			if ( value === ( hidden ? "hide" : "show" ) ) {
8017
8018				// Pretend to be hidden if this is a "show" and
8019				// there is still data from a stopped show/hide
8020				if ( value === "show" && dataShow && dataShow[ prop ] !== undefined ) {
8021					hidden = true;
8022
8023				// Ignore all other no-op show/hide data
8024				} else {
8025					continue;
8026				}
8027			}
8028			orig[ prop ] = dataShow && dataShow[ prop ] || jQuery.style( elem, prop );
8029		}
8030	}
8031
8032	// Bail out if this is a no-op like .hide().hide()
8033	propTween = !jQuery.isEmptyObject( props );
8034	if ( !propTween && jQuery.isEmptyObject( orig ) ) {
8035		return;
8036	}
8037
8038	// Restrict "overflow" and "display" styles during box animations
8039	if ( isBox && elem.nodeType === 1 ) {
8040
8041		// Support: IE <=9 - 11, Edge 12 - 15
8042		// Record all 3 overflow attributes because IE does not infer the shorthand
8043		// from identically-valued overflowX and overflowY and Edge just mirrors
8044		// the overflowX value there.
8045		opts.overflow = [ style.overflow, style.overflowX, style.overflowY ];
8046
8047		// Identify a display type, preferring old show/hide data over the CSS cascade
8048		restoreDisplay = dataShow && dataShow.display;
8049		if ( restoreDisplay == null ) {
8050			restoreDisplay = dataPriv.get( elem, "display" );
8051		}
8052		display = jQuery.css( elem, "display" );
8053		if ( display === "none" ) {
8054			if ( restoreDisplay ) {
8055				display = restoreDisplay;
8056			} else {
8057
8058				// Get nonempty value(s) by temporarily forcing visibility
8059				showHide( [ elem ], true );
8060				restoreDisplay = elem.style.display || restoreDisplay;
8061				display = jQuery.css( elem, "display" );
8062				showHide( [ elem ] );
8063			}
8064		}
8065
8066		// Animate inline elements as inline-block
8067		if ( display === "inline" || display === "inline-block" && restoreDisplay != null ) {
8068			if ( jQuery.css( elem, "float" ) === "none" ) {
8069
8070				// Restore the original display value at the end of pure show/hide animations
8071				if ( !propTween ) {
8072					anim.done( function() {
8073						style.display = restoreDisplay;
8074					} );
8075					if ( restoreDisplay == null ) {
8076						display = style.display;
8077						restoreDisplay = display === "none" ? "" : display;
8078					}
8079				}
8080				style.display = "inline-block";
8081			}
8082		}
8083	}
8084
8085	if ( opts.overflow ) {
8086		style.overflow = "hidden";
8087		anim.always( function() {
8088			style.overflow = opts.overflow[ 0 ];
8089			style.overflowX = opts.overflow[ 1 ];
8090			style.overflowY = opts.overflow[ 2 ];
8091		} );
8092	}
8093
8094	// Implement show/hide animations
8095	propTween = false;
8096	for ( prop in orig ) {
8097
8098		// General show/hide setup for this element animation
8099		if ( !propTween ) {
8100			if ( dataShow ) {
8101				if ( "hidden" in dataShow ) {
8102					hidden = dataShow.hidden;
8103				}
8104			} else {
8105				dataShow = dataPriv.access( elem, "fxshow", { display: restoreDisplay } );
8106			}
8107
8108			// Store hidden/visible for toggle so `.stop().toggle()` "reverses"
8109			if ( toggle ) {
8110				dataShow.hidden = !hidden;
8111			}
8112
8113			// Show elements before animating them
8114			if ( hidden ) {
8115				showHide( [ elem ], true );
8116			}
8117
8118			/* eslint-disable no-loop-func */
8119
8120			anim.done( function() {
8121
8122				/* eslint-enable no-loop-func */
8123
8124				// The final step of a "hide" animation is actually hiding the element
8125				if ( !hidden ) {
8126					showHide( [ elem ] );
8127				}
8128				dataPriv.remove( elem, "fxshow" );
8129				for ( prop in orig ) {
8130					jQuery.style( elem, prop, orig[ prop ] );
8131				}
8132			} );
8133		}
8134
8135		// Per-property setup
8136		propTween = createTween( hidden ? dataShow[ prop ] : 0, prop, anim );
8137		if ( !( prop in dataShow ) ) {
8138			dataShow[ prop ] = propTween.start;
8139			if ( hidden ) {
8140				propTween.end = propTween.start;
8141				propTween.start = 0;
8142			}
8143		}
8144	}
8145}
8146
8147function propFilter( props, specialEasing ) {
8148	var index, name, easing, value, hooks;
8149
8150	// camelCase, specialEasing and expand cssHook pass
8151	for ( index in props ) {
8152		name = camelCase( index );
8153		easing = specialEasing[ name ];
8154		value = props[ index ];
8155		if ( Array.isArray( value ) ) {
8156			easing = value[ 1 ];
8157			value = props[ index ] = value[ 0 ];
8158		}
8159
8160		if ( index !== name ) {
8161			props[ name ] = value;
8162			delete props[ index ];
8163		}
8164
8165		hooks = jQuery.cssHooks[ name ];
8166		if ( hooks && "expand" in hooks ) {
8167			value = hooks.expand( value );
8168			delete props[ name ];
8169
8170			// Not quite $.extend, this won't overwrite existing keys.
8171			// Reusing 'index' because we have the correct "name"
8172			for ( index in value ) {
8173				if ( !( index in props ) ) {
8174					props[ index ] = value[ index ];
8175					specialEasing[ index ] = easing;
8176				}
8177			}
8178		} else {
8179			specialEasing[ name ] = easing;
8180		}
8181	}
8182}
8183
8184function Animation( elem, properties, options ) {
8185	var result,
8186		stopped,
8187		index = 0,
8188		length = Animation.prefilters.length,
8189		deferred = jQuery.Deferred().always( function() {
8190
8191			// Don't match elem in the :animated selector
8192			delete tick.elem;
8193		} ),
8194		tick = function() {
8195			if ( stopped ) {
8196				return false;
8197			}
8198			var currentTime = fxNow || createFxNow(),
8199				remaining = Math.max( 0, animation.startTime + animation.duration - currentTime ),
8200
8201				// Support: Android 2.3 only
8202				// Archaic crash bug won't allow us to use `1 - ( 0.5 || 0 )` (trac-12497)
8203				temp = remaining / animation.duration || 0,
8204				percent = 1 - temp,
8205				index = 0,
8206				length = animation.tweens.length;
8207
8208			for ( ; index < length; index++ ) {
8209				animation.tweens[ index ].run( percent );
8210			}
8211
8212			deferred.notifyWith( elem, [ animation, percent, remaining ] );
8213
8214			// If there's more to do, yield
8215			if ( percent < 1 && length ) {
8216				return remaining;
8217			}
8218
8219			// If this was an empty animation, synthesize a final progress notification
8220			if ( !length ) {
8221				deferred.notifyWith( elem, [ animation, 1, 0 ] );
8222			}
8223
8224			// Resolve the animation and report its conclusion
8225			deferred.resolveWith( elem, [ animation ] );
8226			return false;
8227		},
8228		animation = deferred.promise( {
8229			elem: elem,
8230			props: jQuery.extend( {}, properties ),
8231			opts: jQuery.extend( true, {
8232				specialEasing: {},
8233				easing: jQuery.easing._default
8234			}, options ),
8235			originalProperties: properties,
8236			originalOptions: options,
8237			startTime: fxNow || createFxNow(),
8238			duration: options.duration,
8239			tweens: [],
8240			createTween: function( prop, end ) {
8241				var tween = jQuery.Tween( elem, animation.opts, prop, end,
8242					animation.opts.specialEasing[ prop ] || animation.opts.easing );
8243				animation.tweens.push( tween );
8244				return tween;
8245			},
8246			stop: function( gotoEnd ) {
8247				var index = 0,
8248
8249					// If we are going to the end, we want to run all the tweens
8250					// otherwise we skip this part
8251					length = gotoEnd ? animation.tweens.length : 0;
8252				if ( stopped ) {
8253					return this;
8254				}
8255				stopped = true;
8256				for ( ; index < length; index++ ) {
8257					animation.tweens[ index ].run( 1 );
8258				}
8259
8260				// Resolve when we played the last frame; otherwise, reject
8261				if ( gotoEnd ) {
8262					deferred.notifyWith( elem, [ animation, 1, 0 ] );
8263					deferred.resolveWith( elem, [ animation, gotoEnd ] );
8264				} else {
8265					deferred.rejectWith( elem, [ animation, gotoEnd ] );
8266				}
8267				return this;
8268			}
8269		} ),
8270		props = animation.props;
8271
8272	propFilter( props, animation.opts.specialEasing );
8273
8274	for ( ; index < length; index++ ) {
8275		result = Animation.prefilters[ index ].call( animation, elem, props, animation.opts );
8276		if ( result ) {
8277			if ( isFunction( result.stop ) ) {
8278				jQuery._queueHooks( animation.elem, animation.opts.queue ).stop =
8279					result.stop.bind( result );
8280			}
8281			return result;
8282		}
8283	}
8284
8285	jQuery.map( props, createTween, animation );
8286
8287	if ( isFunction( animation.opts.start ) ) {
8288		animation.opts.start.call( elem, animation );
8289	}
8290
8291	// Attach callbacks from options
8292	animation
8293		.progress( animation.opts.progress )
8294		.done( animation.opts.done, animation.opts.complete )
8295		.fail( animation.opts.fail )
8296		.always( animation.opts.always );
8297
8298	jQuery.fx.timer(
8299		jQuery.extend( tick, {
8300			elem: elem,
8301			anim: animation,
8302			queue: animation.opts.queue
8303		} )
8304	);
8305
8306	return animation;
8307}
8308
8309jQuery.Animation = jQuery.extend( Animation, {
8310
8311	tweeners: {
8312		"*": [ function( prop, value ) {
8313			var tween = this.createTween( prop, value );
8314			adjustCSS( tween.elem, prop, rcssNum.exec( value ), tween );
8315			return tween;
8316		} ]
8317	},
8318
8319	tweener: function( props, callback ) {
8320		if ( isFunction( props ) ) {
8321			callback = props;
8322			props = [ "*" ];
8323		} else {
8324			props = props.match( rnothtmlwhite );
8325		}
8326
8327		var prop,
8328			index = 0,
8329			length = props.length;
8330
8331		for ( ; index < length; index++ ) {
8332			prop = props[ index ];
8333			Animation.tweeners[ prop ] = Animation.tweeners[ prop ] || [];
8334			Animation.tweeners[ prop ].unshift( callback );
8335		}
8336	},
8337
8338	prefilters: [ defaultPrefilter ],
8339
8340	prefilter: function( callback, prepend ) {
8341		if ( prepend ) {
8342			Animation.prefilters.unshift( callback );
8343		} else {
8344			Animation.prefilters.push( callback );
8345		}
8346	}
8347} );
8348
8349jQuery.speed = function( speed, easing, fn ) {
8350	var opt = speed && typeof speed === "object" ? jQuery.extend( {}, speed ) : {
8351		complete: fn || !fn && easing ||
8352			isFunction( speed ) && speed,
8353		duration: speed,
8354		easing: fn && easing || easing && !isFunction( easing ) && easing
8355	};
8356
8357	// Go to the end state if fx are off
8358	if ( jQuery.fx.off ) {
8359		opt.duration = 0;
8360
8361	} else {
8362		if ( typeof opt.duration !== "number" ) {
8363			if ( opt.duration in jQuery.fx.speeds ) {
8364				opt.duration = jQuery.fx.speeds[ opt.duration ];
8365
8366			} else {
8367				opt.duration = jQuery.fx.speeds._default;
8368			}
8369		}
8370	}
8371
8372	// Normalize opt.queue - true/undefined/null -> "fx"
8373	if ( opt.queue == null || opt.queue === true ) {
8374		opt.queue = "fx";
8375	}
8376
8377	// Queueing
8378	opt.old = opt.complete;
8379
8380	opt.complete = function() {
8381		if ( isFunction( opt.old ) ) {
8382			opt.old.call( this );
8383		}
8384
8385		if ( opt.queue ) {
8386			jQuery.dequeue( this, opt.queue );
8387		}
8388	};
8389
8390	return opt;
8391};
8392
8393jQuery.fn.extend( {
8394	fadeTo: function( speed, to, easing, callback ) {
8395
8396		// Show any hidden elements after setting opacity to 0
8397		return this.filter( isHiddenWithinTree ).css( "opacity", 0 ).show()
8398
8399			// Animate to the value specified
8400			.end().animate( { opacity: to }, speed, easing, callback );
8401	},
8402	animate: function( prop, speed, easing, callback ) {
8403		var empty = jQuery.isEmptyObject( prop ),
8404			optall = jQuery.speed( speed, easing, callback ),
8405			doAnimation = function() {
8406
8407				// Operate on a copy of prop so per-property easing won't be lost
8408				var anim = Animation( this, jQuery.extend( {}, prop ), optall );
8409
8410				// Empty animations, or finishing resolves immediately
8411				if ( empty || dataPriv.get( this, "finish" ) ) {
8412					anim.stop( true );
8413				}
8414			};
8415
8416		doAnimation.finish = doAnimation;
8417
8418		return empty || optall.queue === false ?
8419			this.each( doAnimation ) :
8420			this.queue( optall.queue, doAnimation );
8421	},
8422	stop: function( type, clearQueue, gotoEnd ) {
8423		var stopQueue = function( hooks ) {
8424			var stop = hooks.stop;
8425			delete hooks.stop;
8426			stop( gotoEnd );
8427		};
8428
8429		if ( typeof type !== "string" ) {
8430			gotoEnd = clearQueue;
8431			clearQueue = type;
8432			type = undefined;
8433		}
8434		if ( clearQueue ) {
8435			this.queue( type || "fx", [] );
8436		}
8437
8438		return this.each( function() {
8439			var dequeue = true,
8440				index = type != null && type + "queueHooks",
8441				timers = jQuery.timers,
8442				data = dataPriv.get( this );
8443
8444			if ( index ) {
8445				if ( data[ index ] && data[ index ].stop ) {
8446					stopQueue( data[ index ] );
8447				}
8448			} else {
8449				for ( index in data ) {
8450					if ( data[ index ] && data[ index ].stop && rrun.test( index ) ) {
8451						stopQueue( data[ index ] );
8452					}
8453				}
8454			}
8455
8456			for ( index = timers.length; index--; ) {
8457				if ( timers[ index ].elem === this &&
8458					( type == null || timers[ index ].queue === type ) ) {
8459
8460					timers[ index ].anim.stop( gotoEnd );
8461					dequeue = false;
8462					timers.splice( index, 1 );
8463				}
8464			}
8465
8466			// Start the next in the queue if the last step wasn't forced.
8467			// Timers currently will call their complete callbacks, which
8468			// will dequeue but only if they were gotoEnd.
8469			if ( dequeue || !gotoEnd ) {
8470				jQuery.dequeue( this, type );
8471			}
8472		} );
8473	},
8474	finish: function( type ) {
8475		if ( type !== false ) {
8476			type = type || "fx";
8477		}
8478		return this.each( function() {
8479			var index,
8480				data = dataPriv.get( this ),
8481				queue = data[ type + "queue" ],
8482				hooks = data[ type + "queueHooks" ],
8483				timers = jQuery.timers,
8484				length = queue ? queue.length : 0;
8485
8486			// Enable finishing flag on private data
8487			data.finish = true;
8488
8489			// Empty the queue first
8490			jQuery.queue( this, type, [] );
8491
8492			if ( hooks && hooks.stop ) {
8493				hooks.stop.call( this, true );
8494			}
8495
8496			// Look for any active animations, and finish them
8497			for ( index = timers.length; index--; ) {
8498				if ( timers[ index ].elem === this && timers[ index ].queue === type ) {
8499					timers[ index ].anim.stop( true );
8500					timers.splice( index, 1 );
8501				}
8502			}
8503
8504			// Look for any animations in the old queue and finish them
8505			for ( index = 0; index < length; index++ ) {
8506				if ( queue[ index ] && queue[ index ].finish ) {
8507					queue[ index ].finish.call( this );
8508				}
8509			}
8510
8511			// Turn off finishing flag
8512			delete data.finish;
8513		} );
8514	}
8515} );
8516
8517jQuery.each( [ "toggle", "show", "hide" ], function( _i, name ) {
8518	var cssFn = jQuery.fn[ name ];
8519	jQuery.fn[ name ] = function( speed, easing, callback ) {
8520		return speed == null || typeof speed === "boolean" ?
8521			cssFn.apply( this, arguments ) :
8522			this.animate( genFx( name, true ), speed, easing, callback );
8523	};
8524} );
8525
8526// Generate shortcuts for custom animations
8527jQuery.each( {
8528	slideDown: genFx( "show" ),
8529	slideUp: genFx( "hide" ),
8530	slideToggle: genFx( "toggle" ),
8531	fadeIn: { opacity: "show" },
8532	fadeOut: { opacity: "hide" },
8533	fadeToggle: { opacity: "toggle" }
8534}, function( name, props ) {
8535	jQuery.fn[ name ] = function( speed, easing, callback ) {
8536		return this.animate( props, speed, easing, callback );
8537	};
8538} );
8539
8540jQuery.timers = [];
8541jQuery.fx.tick = function() {
8542	var timer,
8543		i = 0,
8544		timers = jQuery.timers;
8545
8546	fxNow = Date.now();
8547
8548	for ( ; i < timers.length; i++ ) {
8549		timer = timers[ i ];
8550
8551		// Run the timer and safely remove it when done (allowing for external removal)
8552		if ( !timer() && timers[ i ] === timer ) {
8553			timers.splice( i--, 1 );
8554		}
8555	}
8556
8557	if ( !timers.length ) {
8558		jQuery.fx.stop();
8559	}
8560	fxNow = undefined;
8561};
8562
8563jQuery.fx.timer = function( timer ) {
8564	jQuery.timers.push( timer );
8565	jQuery.fx.start();
8566};
8567
8568jQuery.fx.interval = 13;
8569jQuery.fx.start = function() {
8570	if ( inProgress ) {
8571		return;
8572	}
8573
8574	inProgress = true;
8575	schedule();
8576};
8577
8578jQuery.fx.stop = function() {
8579	inProgress = null;
8580};
8581
8582jQuery.fx.speeds = {
8583	slow: 600,
8584	fast: 200,
8585
8586	// Default speed
8587	_default: 400
8588};
8589
8590
8591// Based off of the plugin by Clint Helfers, with permission.
8592jQuery.fn.delay = function( time, type ) {
8593	time = jQuery.fx ? jQuery.fx.speeds[ time ] || time : time;
8594	type = type || "fx";
8595
8596	return this.queue( type, function( next, hooks ) {
8597		var timeout = window.setTimeout( next, time );
8598		hooks.stop = function() {
8599			window.clearTimeout( timeout );
8600		};
8601	} );
8602};
8603
8604
8605( function() {
8606	var input = document.createElement( "input" ),
8607		select = document.createElement( "select" ),
8608		opt = select.appendChild( document.createElement( "option" ) );
8609
8610	input.type = "checkbox";
8611
8612	// Support: Android <=4.3 only
8613	// Default value for a checkbox should be "on"
8614	support.checkOn = input.value !== "";
8615
8616	// Support: IE <=11 only
8617	// Must access selectedIndex to make default options select
8618	support.optSelected = opt.selected;
8619
8620	// Support: IE <=11 only
8621	// An input loses its value after becoming a radio
8622	input = document.createElement( "input" );
8623	input.value = "t";
8624	input.type = "radio";
8625	support.radioValue = input.value === "t";
8626} )();
8627
8628
8629var boolHook,
8630	attrHandle = jQuery.expr.attrHandle;
8631
8632jQuery.fn.extend( {
8633	attr: function( name, value ) {
8634		return access( this, jQuery.attr, name, value, arguments.length > 1 );
8635	},
8636
8637	removeAttr: function( name ) {
8638		return this.each( function() {
8639			jQuery.removeAttr( this, name );
8640		} );
8641	}
8642} );
8643
8644jQuery.extend( {
8645	attr: function( elem, name, value ) {
8646		var ret, hooks,
8647			nType = elem.nodeType;
8648
8649		// Don't get/set attributes on text, comment and attribute nodes
8650		if ( nType === 3 || nType === 8 || nType === 2 ) {
8651			return;
8652		}
8653
8654		// Fallback to prop when attributes are not supported
8655		if ( typeof elem.getAttribute === "undefined" ) {
8656			return jQuery.prop( elem, name, value );
8657		}
8658
8659		// Attribute hooks are determined by the lowercase version
8660		// Grab necessary hook if one is defined
8661		if ( nType !== 1 || !jQuery.isXMLDoc( elem ) ) {
8662			hooks = jQuery.attrHooks[ name.toLowerCase() ] ||
8663				( jQuery.expr.match.bool.test( name ) ? boolHook : undefined );
8664		}
8665
8666		if ( value !== undefined ) {
8667			if ( value === null ) {
8668				jQuery.removeAttr( elem, name );
8669				return;
8670			}
8671
8672			if ( hooks && "set" in hooks &&
8673				( ret = hooks.set( elem, value, name ) ) !== undefined ) {
8674				return ret;
8675			}
8676
8677			elem.setAttribute( name, value + "" );
8678			return value;
8679		}
8680
8681		if ( hooks && "get" in hooks && ( ret = hooks.get( elem, name ) ) !== null ) {
8682			return ret;
8683		}
8684
8685		ret = jQuery.find.attr( elem, name );
8686
8687		// Non-existent attributes return null, we normalize to undefined
8688		return ret == null ? undefined : ret;
8689	},
8690
8691	attrHooks: {
8692		type: {
8693			set: function( elem, value ) {
8694				if ( !support.radioValue && value === "radio" &&
8695					nodeName( elem, "input" ) ) {
8696					var val = elem.value;
8697					elem.setAttribute( "type", value );
8698					if ( val ) {
8699						elem.value = val;
8700					}
8701					return value;
8702				}
8703			}
8704		}
8705	},
8706
8707	removeAttr: function( elem, value ) {
8708		var name,
8709			i = 0,
8710
8711			// Attribute names can contain non-HTML whitespace characters
8712			// https://html.spec.whatwg.org/multipage/syntax.html#attributes-2
8713			attrNames = value && value.match( rnothtmlwhite );
8714
8715		if ( attrNames && elem.nodeType === 1 ) {
8716			while ( ( name = attrNames[ i++ ] ) ) {
8717				elem.removeAttribute( name );
8718			}
8719		}
8720	}
8721} );
8722
8723// Hooks for boolean attributes
8724boolHook = {
8725	set: function( elem, value, name ) {
8726		if ( value === false ) {
8727
8728			// Remove boolean attributes when set to false
8729			jQuery.removeAttr( elem, name );
8730		} else {
8731			elem.setAttribute( name, name );
8732		}
8733		return name;
8734	}
8735};
8736
8737jQuery.each( jQuery.expr.match.bool.source.match( /\w+/g ), function( _i, name ) {
8738	var getter = attrHandle[ name ] || jQuery.find.attr;
8739
8740	attrHandle[ name ] = function( elem, name, isXML ) {
8741		var ret, handle,
8742			lowercaseName = name.toLowerCase();
8743
8744		if ( !isXML ) {
8745
8746			// Avoid an infinite loop by temporarily removing this function from the getter
8747			handle = attrHandle[ lowercaseName ];
8748			attrHandle[ lowercaseName ] = ret;
8749			ret = getter( elem, name, isXML ) != null ?
8750				lowercaseName :
8751				null;
8752			attrHandle[ lowercaseName ] = handle;
8753		}
8754		return ret;
8755	};
8756} );
8757
8758
8759
8760
8761var rfocusable = /^(?:input|select|textarea|button)$/i,
8762	rclickable = /^(?:a|area)$/i;
8763
8764jQuery.fn.extend( {
8765	prop: function( name, value ) {
8766		return access( this, jQuery.prop, name, value, arguments.length > 1 );
8767	},
8768
8769	removeProp: function( name ) {
8770		return this.each( function() {
8771			delete this[ jQuery.propFix[ name ] || name ];
8772		} );
8773	}
8774} );
8775
8776jQuery.extend( {
8777	prop: function( elem, name, value ) {
8778		var ret, hooks,
8779			nType = elem.nodeType;
8780
8781		// Don't get/set properties on text, comment and attribute nodes
8782		if ( nType === 3 || nType === 8 || nType === 2 ) {
8783			return;
8784		}
8785
8786		if ( nType !== 1 || !jQuery.isXMLDoc( elem ) ) {
8787
8788			// Fix name and attach hooks
8789			name = jQuery.propFix[ name ] || name;
8790			hooks = jQuery.propHooks[ name ];
8791		}
8792
8793		if ( value !== undefined ) {
8794			if ( hooks && "set" in hooks &&
8795				( ret = hooks.set( elem, value, name ) ) !== undefined ) {
8796				return ret;
8797			}
8798
8799			return ( elem[ name ] = value );
8800		}
8801
8802		if ( hooks && "get" in hooks && ( ret = hooks.get( elem, name ) ) !== null ) {
8803			return ret;
8804		}
8805
8806		return elem[ name ];
8807	},
8808
8809	propHooks: {
8810		tabIndex: {
8811			get: function( elem ) {
8812
8813				// Support: IE <=9 - 11 only
8814				// elem.tabIndex doesn't always return the
8815				// correct value when it hasn't been explicitly set
8816				// Use proper attribute retrieval (trac-12072)
8817				var tabindex = jQuery.find.attr( elem, "tabindex" );
8818
8819				if ( tabindex ) {
8820					return parseInt( tabindex, 10 );
8821				}
8822
8823				if (
8824					rfocusable.test( elem.nodeName ) ||
8825					rclickable.test( elem.nodeName ) &&
8826					elem.href
8827				) {
8828					return 0;
8829				}
8830
8831				return -1;
8832			}
8833		}
8834	},
8835
8836	propFix: {
8837		"for": "htmlFor",
8838		"class": "className"
8839	}
8840} );
8841
8842// Support: IE <=11 only
8843// Accessing the selectedIndex property
8844// forces the browser to respect setting selected
8845// on the option
8846// The getter ensures a default option is selected
8847// when in an optgroup
8848// eslint rule "no-unused-expressions" is disabled for this code
8849// since it considers such accessions noop
8850if ( !support.optSelected ) {
8851	jQuery.propHooks.selected = {
8852		get: function( elem ) {
8853
8854			/* eslint no-unused-expressions: "off" */
8855
8856			var parent = elem.parentNode;
8857			if ( parent && parent.parentNode ) {
8858				parent.parentNode.selectedIndex;
8859			}
8860			return null;
8861		},
8862		set: function( elem ) {
8863
8864			/* eslint no-unused-expressions: "off" */
8865
8866			var parent = elem.parentNode;
8867			if ( parent ) {
8868				parent.selectedIndex;
8869
8870				if ( parent.parentNode ) {
8871					parent.parentNode.selectedIndex;
8872				}
8873			}
8874		}
8875	};
8876}
8877
8878jQuery.each( [
8879	"tabIndex",
8880	"readOnly",
8881	"maxLength",
8882	"cellSpacing",
8883	"cellPadding",
8884	"rowSpan",
8885	"colSpan",
8886	"useMap",
8887	"frameBorder",
8888	"contentEditable"
8889], function() {
8890	jQuery.propFix[ this.toLowerCase() ] = this;
8891} );
8892
8893
8894
8895
8896	// Strip and collapse whitespace according to HTML spec
8897	// https://infra.spec.whatwg.org/#strip-and-collapse-ascii-whitespace
8898	function stripAndCollapse( value ) {
8899		var tokens = value.match( rnothtmlwhite ) || [];
8900		return tokens.join( " " );
8901	}
8902
8903
8904function getClass( elem ) {
8905	return elem.getAttribute && elem.getAttribute( "class" ) || "";
8906}
8907
8908function classesToArray( value ) {
8909	if ( Array.isArray( value ) ) {
8910		return value;
8911	}
8912	if ( typeof value === "string" ) {
8913		return value.match( rnothtmlwhite ) || [];
8914	}
8915	return [];
8916}
8917
8918jQuery.fn.extend( {
8919	addClass: function( value ) {
8920		var classNames, cur, curValue, className, i, finalValue;
8921
8922		if ( isFunction( value ) ) {
8923			return this.each( function( j ) {
8924				jQuery( this ).addClass( value.call( this, j, getClass( this ) ) );
8925			} );
8926		}
8927
8928		classNames = classesToArray( value );
8929
8930		if ( classNames.length ) {
8931			return this.each( function() {
8932				curValue = getClass( this );
8933				cur = this.nodeType === 1 && ( " " + stripAndCollapse( curValue ) + " " );
8934
8935				if ( cur ) {
8936					for ( i = 0; i < classNames.length; i++ ) {
8937						className = classNames[ i ];
8938						if ( cur.indexOf( " " + className + " " ) < 0 ) {
8939							cur += className + " ";
8940						}
8941					}
8942
8943					// Only assign if different to avoid unneeded rendering.
8944					finalValue = stripAndCollapse( cur );
8945					if ( curValue !== finalValue ) {
8946						this.setAttribute( "class", finalValue );
8947					}
8948				}
8949			} );
8950		}
8951
8952		return this;
8953	},
8954
8955	removeClass: function( value ) {
8956		var classNames, cur, curValue, className, i, finalValue;
8957
8958		if ( isFunction( value ) ) {
8959			return this.each( function( j ) {
8960				jQuery( this ).removeClass( value.call( this, j, getClass( this ) ) );
8961			} );
8962		}
8963
8964		if ( !arguments.length ) {
8965			return this.attr( "class", "" );
8966		}
8967
8968		classNames = classesToArray( value );
8969
8970		if ( classNames.length ) {
8971			return this.each( function() {
8972				curValue = getClass( this );
8973
8974				// This expression is here for better compressibility (see addClass)
8975				cur = this.nodeType === 1 && ( " " + stripAndCollapse( curValue ) + " " );
8976
8977				if ( cur ) {
8978					for ( i = 0; i < classNames.length; i++ ) {
8979						className = classNames[ i ];
8980
8981						// Remove *all* instances
8982						while ( cur.indexOf( " " + className + " " ) > -1 ) {
8983							cur = cur.replace( " " + className + " ", " " );
8984						}
8985					}
8986
8987					// Only assign if different to avoid unneeded rendering.
8988					finalValue = stripAndCollapse( cur );
8989					if ( curValue !== finalValue ) {
8990						this.setAttribute( "class", finalValue );
8991					}
8992				}
8993			} );
8994		}
8995
8996		return this;
8997	},
8998
8999	toggleClass: function( value, stateVal ) {
9000		var classNames, className, i, self,
9001			type = typeof value,
9002			isValidValue = type === "string" || Array.isArray( value );
9003
9004		if ( isFunction( value ) ) {
9005			return this.each( function( i ) {
9006				jQuery( this ).toggleClass(
9007					value.call( this, i, getClass( this ), stateVal ),
9008					stateVal
9009				);
9010			} );
9011		}
9012
9013		if ( typeof stateVal === "boolean" && isValidValue ) {
9014			return stateVal ? this.addClass( value ) : this.removeClass( value );
9015		}
9016
9017		classNames = classesToArray( value );
9018
9019		return this.each( function() {
9020			if ( isValidValue ) {
9021
9022				// Toggle individual class names
9023				self = jQuery( this );
9024
9025				for ( i = 0; i < classNames.length; i++ ) {
9026					className = classNames[ i ];
9027
9028					// Check each className given, space separated list
9029					if ( self.hasClass( className ) ) {
9030						self.removeClass( className );
9031					} else {
9032						self.addClass( className );
9033					}
9034				}
9035
9036			// Toggle whole class name
9037			} else if ( value === undefined || type === "boolean" ) {
9038				className = getClass( this );
9039				if ( className ) {
9040
9041					// Store className if set
9042					dataPriv.set( this, "__className__", className );
9043				}
9044
9045				// If the element has a class name or if we're passed `false`,
9046				// then remove the whole classname (if there was one, the above saved it).
9047				// Otherwise bring back whatever was previously saved (if anything),
9048				// falling back to the empty string if nothing was stored.
9049				if ( this.setAttribute ) {
9050					this.setAttribute( "class",
9051						className || value === false ?
9052							"" :
9053							dataPriv.get( this, "__className__" ) || ""
9054					);
9055				}
9056			}
9057		} );
9058	},
9059
9060	hasClass: function( selector ) {
9061		var className, elem,
9062			i = 0;
9063
9064		className = " " + selector + " ";
9065		while ( ( elem = this[ i++ ] ) ) {
9066			if ( elem.nodeType === 1 &&
9067				( " " + stripAndCollapse( getClass( elem ) ) + " " ).indexOf( className ) > -1 ) {
9068				return true;
9069			}
9070		}
9071
9072		return false;
vendor: 1,385 bytes, lines 9073-9142
9073	}
9074} );
9075
9076
9077
9078
9079var rreturn = /\r/g;
9080
9081jQuery.fn.extend( {
9082	val: function( value ) {
9083		var hooks, ret, valueIsFunction,
9084			elem = this[ 0 ];
9085
9086		if ( !arguments.length ) {
9087			if ( elem ) {
9088				hooks = jQuery.valHooks[ elem.type ] ||
9089					jQuery.valHooks[ elem.nodeName.toLowerCase() ];
9090
9091				if ( hooks &&
9092					"get" in hooks &&
9093					( ret = hooks.get( elem, "value" ) ) !== undefined
9094				) {
9095					return ret;
9096				}
9097
9098				ret = elem.value;
9099
9100				// Handle most common string cases
9101				if ( typeof ret === "string" ) {
9102					return ret.replace( rreturn, "" );
9103				}
9104
9105				// Handle cases where value is null/undef or number
9106				return ret == null ? "" : ret;
9107			}
9108
9109			return;
9110		}
9111
9112		valueIsFunction = isFunction( value );
9113
9114		return this.each( function( i ) {
9115			var val;
9116
9117			if ( this.nodeType !== 1 ) {
9118				return;
9119			}
9120
9121			if ( valueIsFunction ) {
9122				val = value.call( this, i, jQuery( this ).val() );
9123			} else {
9124				val = value;
9125			}
9126
9127			// Treat null/undefined as ""; convert numbers to string
9128			if ( val == null ) {
9129				val = "";
9130
9131			} else if ( typeof val === "number" ) {
9132				val += "";
9133
9134			} else if ( Array.isArray( val ) ) {
9135				val = jQuery.map( val, function( value ) {
9136					return value == null ? "" : value + "";
9137				} );
9138			}
9139
9140			hooks = jQuery.valHooks[ this.type ] || jQuery.valHooks[ this.nodeName.toLowerCase() ];
9141
9142			// If set returns undefined, fall back to normal setting
vendor: 43,419 bytes, lines 9143-10832
9143			if ( !hooks || !( "set" in hooks ) || hooks.set( this, val, "value" ) === undefined ) {
9144				this.value = val;
9145			}
9146		} );
9147	}
9148} );
9149
9150jQuery.extend( {
9151	valHooks: {
9152		option: {
9153			get: function( elem ) {
9154
9155				var val = jQuery.find.attr( elem, "value" );
9156				return val != null ?
9157					val :
9158
9159					// Support: IE <=10 - 11 only
9160					// option.text throws exceptions (trac-14686, trac-14858)
9161					// Strip and collapse whitespace
9162					// https://html.spec.whatwg.org/#strip-and-collapse-whitespace
9163					stripAndCollapse( jQuery.text( elem ) );
9164			}
9165		},
9166		select: {
9167			get: function( elem ) {
9168				var value, option, i,
9169					options = elem.options,
9170					index = elem.selectedIndex,
9171					one = elem.type === "select-one",
9172					values = one ? null : [],
9173					max = one ? index + 1 : options.length;
9174
9175				if ( index < 0 ) {
9176					i = max;
9177
9178				} else {
9179					i = one ? index : 0;
9180				}
9181
9182				// Loop through all the selected options
9183				for ( ; i < max; i++ ) {
9184					option = options[ i ];
9185
9186					// Support: IE <=9 only
9187					// IE8-9 doesn't update selected after form reset (trac-2551)
9188					if ( ( option.selected || i === index ) &&
9189
9190							// Don't return options that are disabled or in a disabled optgroup
9191							!option.disabled &&
9192							( !option.parentNode.disabled ||
9193								!nodeName( option.parentNode, "optgroup" ) ) ) {
9194
9195						// Get the specific value for the option
9196						value = jQuery( option ).val();
9197
9198						// We don't need an array for one selects
9199						if ( one ) {
9200							return value;
9201						}
9202
9203						// Multi-Selects return an array
9204						values.push( value );
9205					}
9206				}
9207
9208				return values;
9209			},
9210
9211			set: function( elem, value ) {
9212				var optionSet, option,
9213					options = elem.options,
9214					values = jQuery.makeArray( value ),
9215					i = options.length;
9216
9217				while ( i-- ) {
9218					option = options[ i ];
9219
9220					/* eslint-disable no-cond-assign */
9221
9222					if ( option.selected =
9223						jQuery.inArray( jQuery.valHooks.option.get( option ), values ) > -1
9224					) {
9225						optionSet = true;
9226					}
9227
9228					/* eslint-enable no-cond-assign */
9229				}
9230
9231				// Force browsers to behave consistently when non-matching value is set
9232				if ( !optionSet ) {
9233					elem.selectedIndex = -1;
9234				}
9235				return values;
9236			}
9237		}
9238	}
9239} );
9240
9241// Radios and checkboxes getter/setter
9242jQuery.each( [ "radio", "checkbox" ], function() {
9243	jQuery.valHooks[ this ] = {
9244		set: function( elem, value ) {
9245			if ( Array.isArray( value ) ) {
9246				return ( elem.checked = jQuery.inArray( jQuery( elem ).val(), value ) > -1 );
9247			}
9248		}
9249	};
9250	if ( !support.checkOn ) {
9251		jQuery.valHooks[ this ].get = function( elem ) {
9252			return elem.getAttribute( "value" ) === null ? "on" : elem.value;
9253		};
9254	}
9255} );
9256
9257
9258
9259
9260// Return jQuery for attributes-only inclusion
9261var location = window.location;
9262
9263var nonce = { guid: Date.now() };
9264
9265var rquery = ( /\?/ );
9266
9267
9268
9269// Cross-browser xml parsing
9270jQuery.parseXML = function( data ) {
9271	var xml, parserErrorElem;
9272	if ( !data || typeof data !== "string" ) {
9273		return null;
9274	}
9275
9276	// Support: IE 9 - 11 only
9277	// IE throws on parseFromString with invalid input.
9278	try {
9279		xml = ( new window.DOMParser() ).parseFromString( data, "text/xml" );
9280	} catch ( e ) {}
9281
9282	parserErrorElem = xml && xml.getElementsByTagName( "parsererror" )[ 0 ];
9283	if ( !xml || parserErrorElem ) {
9284		jQuery.error( "Invalid XML: " + (
9285			parserErrorElem ?
9286				jQuery.map( parserErrorElem.childNodes, function( el ) {
9287					return el.textContent;
9288				} ).join( "\n" ) :
9289				data
9290		) );
9291	}
9292	return xml;
9293};
9294
9295
9296var rfocusMorph = /^(?:focusinfocus|focusoutblur)$/,
9297	stopPropagationCallback = function( e ) {
9298		e.stopPropagation();
9299	};
9300
9301jQuery.extend( jQuery.event, {
9302
9303	trigger: function( event, data, elem, onlyHandlers ) {
9304
9305		var i, cur, tmp, bubbleType, ontype, handle, special, lastElement,
9306			eventPath = [ elem || document ],
9307			type = hasOwn.call( event, "type" ) ? event.type : event,
9308			namespaces = hasOwn.call( event, "namespace" ) ? event.namespace.split( "." ) : [];
9309
9310		cur = lastElement = tmp = elem = elem || document;
9311
9312		// Don't do events on text and comment nodes
9313		if ( elem.nodeType === 3 || elem.nodeType === 8 ) {
9314			return;
9315		}
9316
9317		// focus/blur morphs to focusin/out; ensure we're not firing them right now
9318		if ( rfocusMorph.test( type + jQuery.event.triggered ) ) {
9319			return;
9320		}
9321
9322		if ( type.indexOf( "." ) > -1 ) {
9323
9324			// Namespaced trigger; create a regexp to match event type in handle()
9325			namespaces = type.split( "." );
9326			type = namespaces.shift();
9327			namespaces.sort();
9328		}
9329		ontype = type.indexOf( ":" ) < 0 && "on" + type;
9330
9331		// Caller can pass in a jQuery.Event object, Object, or just an event type string
9332		event = event[ jQuery.expando ] ?
9333			event :
9334			new jQuery.Event( type, typeof event === "object" && event );
9335
9336		// Trigger bitmask: & 1 for native handlers; & 2 for jQuery (always true)
9337		event.isTrigger = onlyHandlers ? 2 : 3;
9338		event.namespace = namespaces.join( "." );
9339		event.rnamespace = event.namespace ?
9340			new RegExp( "(^|\\.)" + namespaces.join( "\\.(?:.*\\.|)" ) + "(\\.|$)" ) :
9341			null;
9342
9343		// Clean up the event in case it is being reused
9344		event.result = undefined;
9345		if ( !event.target ) {
9346			event.target = elem;
9347		}
9348
9349		// Clone any incoming data and prepend the event, creating the handler arg list
9350		data = data == null ?
9351			[ event ] :
9352			jQuery.makeArray( data, [ event ] );
9353
9354		// Allow special events to draw outside the lines
9355		special = jQuery.event.special[ type ] || {};
9356		if ( !onlyHandlers && special.trigger && special.trigger.apply( elem, data ) === false ) {
9357			return;
9358		}
9359
9360		// Determine event propagation path in advance, per W3C events spec (trac-9951)
9361		// Bubble up to document, then to window; watch for a global ownerDocument var (trac-9724)
9362		if ( !onlyHandlers && !special.noBubble && !isWindow( elem ) ) {
9363
9364			bubbleType = special.delegateType || type;
9365			if ( !rfocusMorph.test( bubbleType + type ) ) {
9366				cur = cur.parentNode;
9367			}
9368			for ( ; cur; cur = cur.parentNode ) {
9369				eventPath.push( cur );
9370				tmp = cur;
9371			}
9372
9373			// Only add window if we got to document (e.g., not plain obj or detached DOM)
9374			if ( tmp === ( elem.ownerDocument || document ) ) {
9375				eventPath.push( tmp.defaultView || tmp.parentWindow || window );
9376			}
9377		}
9378
9379		// Fire handlers on the event path
9380		i = 0;
9381		while ( ( cur = eventPath[ i++ ] ) && !event.isPropagationStopped() ) {
9382			lastElement = cur;
9383			event.type = i > 1 ?
9384				bubbleType :
9385				special.bindType || type;
9386
9387			// jQuery handler
9388			handle = ( dataPriv.get( cur, "events" ) || Object.create( null ) )[ event.type ] &&
9389				dataPriv.get( cur, "handle" );
9390			if ( handle ) {
9391				handle.apply( cur, data );
9392			}
9393
9394			// Native handler
9395			handle = ontype && cur[ ontype ];
9396			if ( handle && handle.apply && acceptData( cur ) ) {
9397				event.result = handle.apply( cur, data );
9398				if ( event.result === false ) {
9399					event.preventDefault();
9400				}
9401			}
9402		}
9403		event.type = type;
9404
9405		// If nobody prevented the default action, do it now
9406		if ( !onlyHandlers && !event.isDefaultPrevented() ) {
9407
9408			if ( ( !special._default ||
9409				special._default.apply( eventPath.pop(), data ) === false ) &&
9410				acceptData( elem ) ) {
9411
9412				// Call a native DOM method on the target with the same name as the event.
9413				// Don't do default actions on window, that's where global variables be (trac-6170)
9414				if ( ontype && isFunction( elem[ type ] ) && !isWindow( elem ) ) {
9415
9416					// Don't re-trigger an onFOO event when we call its FOO() method
9417					tmp = elem[ ontype ];
9418
9419					if ( tmp ) {
9420						elem[ ontype ] = null;
9421					}
9422
9423					// Prevent re-triggering of the same event, since we already bubbled it above
9424					jQuery.event.triggered = type;
9425
9426					if ( event.isPropagationStopped() ) {
9427						lastElement.addEventListener( type, stopPropagationCallback );
9428					}
9429
9430					elem[ type ]();
9431
9432					if ( event.isPropagationStopped() ) {
9433						lastElement.removeEventListener( type, stopPropagationCallback );
9434					}
9435
9436					jQuery.event.triggered = undefined;
9437
9438					if ( tmp ) {
9439						elem[ ontype ] = tmp;
9440					}
9441				}
9442			}
9443		}
9444
9445		return event.result;
9446	},
9447
9448	// Piggyback on a donor event to simulate a different one
9449	// Used only for `focus(in | out)` events
9450	simulate: function( type, elem, event ) {
9451		var e = jQuery.extend(
9452			new jQuery.Event(),
9453			event,
9454			{
9455				type: type,
9456				isSimulated: true
9457			}
9458		);
9459
9460		jQuery.event.trigger( e, null, elem );
9461	}
9462
9463} );
9464
9465jQuery.fn.extend( {
9466
9467	trigger: function( type, data ) {
9468		return this.each( function() {
9469			jQuery.event.trigger( type, data, this );
9470		} );
9471	},
9472	triggerHandler: function( type, data ) {
9473		var elem = this[ 0 ];
9474		if ( elem ) {
9475			return jQuery.event.trigger( type, data, elem, true );
9476		}
9477	}
9478} );
9479
9480
9481var
9482	rbracket = /\[\]$/,
9483	rCRLF = /\r?\n/g,
9484	rsubmitterTypes = /^(?:submit|button|image|reset|file)$/i,
9485	rsubmittable = /^(?:input|select|textarea|keygen)/i;
9486
9487function buildParams( prefix, obj, traditional, add ) {
9488	var name;
9489
9490	if ( Array.isArray( obj ) ) {
9491
9492		// Serialize array item.
9493		jQuery.each( obj, function( i, v ) {
9494			if ( traditional || rbracket.test( prefix ) ) {
9495
9496				// Treat each array item as a scalar.
9497				add( prefix, v );
9498
9499			} else {
9500
9501				// Item is non-scalar (array or object), encode its numeric index.
9502				buildParams(
9503					prefix + "[" + ( typeof v === "object" && v != null ? i : "" ) + "]",
9504					v,
9505					traditional,
9506					add
9507				);
9508			}
9509		} );
9510
9511	} else if ( !traditional && toType( obj ) === "object" ) {
9512
9513		// Serialize object item.
9514		for ( name in obj ) {
9515			buildParams( prefix + "[" + name + "]", obj[ name ], traditional, add );
9516		}
9517
9518	} else {
9519
9520		// Serialize scalar item.
9521		add( prefix, obj );
9522	}
9523}
9524
9525// Serialize an array of form elements or a set of
9526// key/values into a query string
9527jQuery.param = function( a, traditional ) {
9528	var prefix,
9529		s = [],
9530		add = function( key, valueOrFunction ) {
9531
9532			// If value is a function, invoke it and use its return value
9533			var value = isFunction( valueOrFunction ) ?
9534				valueOrFunction() :
9535				valueOrFunction;
9536
9537			s[ s.length ] = encodeURIComponent( key ) + "=" +
9538				encodeURIComponent( value == null ? "" : value );
9539		};
9540
9541	if ( a == null ) {
9542		return "";
9543	}
9544
9545	// If an array was passed in, assume that it is an array of form elements.
9546	if ( Array.isArray( a ) || ( a.jquery && !jQuery.isPlainObject( a ) ) ) {
9547
9548		// Serialize the form elements
9549		jQuery.each( a, function() {
9550			add( this.name, this.value );
9551		} );
9552
9553	} else {
9554
9555		// If traditional, encode the "old" way (the way 1.3.2 or older
9556		// did it), otherwise encode params recursively.
9557		for ( prefix in a ) {
9558			buildParams( prefix, a[ prefix ], traditional, add );
9559		}
9560	}
9561
9562	// Return the resulting serialization
9563	return s.join( "&" );
9564};
9565
9566jQuery.fn.extend( {
9567	serialize: function() {
9568		return jQuery.param( this.serializeArray() );
9569	},
9570	serializeArray: function() {
9571		return this.map( function() {
9572
9573			// Can add propHook for "elements" to filter or add form elements
9574			var elements = jQuery.prop( this, "elements" );
9575			return elements ? jQuery.makeArray( elements ) : this;
9576		} ).filter( function() {
9577			var type = this.type;
9578
9579			// Use .is( ":disabled" ) so that fieldset[disabled] works
9580			return this.name && !jQuery( this ).is( ":disabled" ) &&
9581				rsubmittable.test( this.nodeName ) && !rsubmitterTypes.test( type ) &&
9582				( this.checked || !rcheckableType.test( type ) );
9583		} ).map( function( _i, elem ) {
9584			var val = jQuery( this ).val();
9585
9586			if ( val == null ) {
9587				return null;
9588			}
9589
9590			if ( Array.isArray( val ) ) {
9591				return jQuery.map( val, function( val ) {
9592					return { name: elem.name, value: val.replace( rCRLF, "\r\n" ) };
9593				} );
9594			}
9595
9596			return { name: elem.name, value: val.replace( rCRLF, "\r\n" ) };
9597		} ).get();
9598	}
9599} );
9600
9601
9602var
9603	r20 = /%20/g,
9604	rhash = /#.*$/,
9605	rantiCache = /([?&])_=[^&]*/,
9606	rheaders = /^(.*?):[ \t]*([^\r\n]*)$/mg,
9607
9608	// trac-7653, trac-8125, trac-8152: local protocol detection
9609	rlocalProtocol = /^(?:about|app|app-storage|.+-extension|file|res|widget):$/,
9610	rnoContent = /^(?:GET|HEAD)$/,
9611	rprotocol = /^\/\//,
9612
9613	/* Prefilters
9614	 * 1) They are useful to introduce custom dataTypes (see ajax/jsonp.js for an example)
9615	 * 2) These are called:
9616	 *    - BEFORE asking for a transport
9617	 *    - AFTER param serialization (s.data is a string if s.processData is true)
9618	 * 3) key is the dataType
9619	 * 4) the catchall symbol "*" can be used
9620	 * 5) execution will start with transport dataType and THEN continue down to "*" if needed
9621	 */
9622	prefilters = {},
9623
9624	/* Transports bindings
9625	 * 1) key is the dataType
9626	 * 2) the catchall symbol "*" can be used
9627	 * 3) selection will start with transport dataType and THEN go to "*" if needed
9628	 */
9629	transports = {},
9630
9631	// Avoid comment-prolog char sequence (trac-10098); must appease lint and evade compression
9632	allTypes = "*/".concat( "*" ),
9633
9634	// Anchor tag for parsing the document origin
9635	originAnchor = document.createElement( "a" );
9636
9637originAnchor.href = location.href;
9638
9639// Base "constructor" for jQuery.ajaxPrefilter and jQuery.ajaxTransport
9640function addToPrefiltersOrTransports( structure ) {
9641
9642	// dataTypeExpression is optional and defaults to "*"
9643	return function( dataTypeExpression, func ) {
9644
9645		if ( typeof dataTypeExpression !== "string" ) {
9646			func = dataTypeExpression;
9647			dataTypeExpression = "*";
9648		}
9649
9650		var dataType,
9651			i = 0,
9652			dataTypes = dataTypeExpression.toLowerCase().match( rnothtmlwhite ) || [];
9653
9654		if ( isFunction( func ) ) {
9655
9656			// For each dataType in the dataTypeExpression
9657			while ( ( dataType = dataTypes[ i++ ] ) ) {
9658
9659				// Prepend if requested
9660				if ( dataType[ 0 ] === "+" ) {
9661					dataType = dataType.slice( 1 ) || "*";
9662					( structure[ dataType ] = structure[ dataType ] || [] ).unshift( func );
9663
9664				// Otherwise append
9665				} else {
9666					( structure[ dataType ] = structure[ dataType ] || [] ).push( func );
9667				}
9668			}
9669		}
9670	};
9671}
9672
9673// Base inspection function for prefilters and transports
9674function inspectPrefiltersOrTransports( structure, options, originalOptions, jqXHR ) {
9675
9676	var inspected = {},
9677		seekingTransport = ( structure === transports );
9678
9679	function inspect( dataType ) {
9680		var selected;
9681		inspected[ dataType ] = true;
9682		jQuery.each( structure[ dataType ] || [], function( _, prefilterOrFactory ) {
9683			var dataTypeOrTransport = prefilterOrFactory( options, originalOptions, jqXHR );
9684			if ( typeof dataTypeOrTransport === "string" &&
9685				!seekingTransport && !inspected[ dataTypeOrTransport ] ) {
9686
9687				options.dataTypes.unshift( dataTypeOrTransport );
9688				inspect( dataTypeOrTransport );
9689				return false;
9690			} else if ( seekingTransport ) {
9691				return !( selected = dataTypeOrTransport );
9692			}
9693		} );
9694		return selected;
9695	}
9696
9697	return inspect( options.dataTypes[ 0 ] ) || !inspected[ "*" ] && inspect( "*" );
9698}
9699
9700// A special extend for ajax options
9701// that takes "flat" options (not to be deep extended)
9702// Fixes trac-9887
9703function ajaxExtend( target, src ) {
9704	var key, deep,
9705		flatOptions = jQuery.ajaxSettings.flatOptions || {};
9706
9707	for ( key in src ) {
9708		if ( src[ key ] !== undefined ) {
9709			( flatOptions[ key ] ? target : ( deep || ( deep = {} ) ) )[ key ] = src[ key ];
9710		}
9711	}
9712	if ( deep ) {
9713		jQuery.extend( true, target, deep );
9714	}
9715
9716	return target;
9717}
9718
9719/* Handles responses to an ajax request:
9720 * - finds the right dataType (mediates between content-type and expected dataType)
9721 * - returns the corresponding response
9722 */
9723function ajaxHandleResponses( s, jqXHR, responses ) {
9724
9725	var ct, type, finalDataType, firstDataType,
9726		contents = s.contents,
9727		dataTypes = s.dataTypes;
9728
9729	// Remove auto dataType and get content-type in the process
9730	while ( dataTypes[ 0 ] === "*" ) {
9731		dataTypes.shift();
9732		if ( ct === undefined ) {
9733			ct = s.mimeType || jqXHR.getResponseHeader( "Content-Type" );
9734		}
9735	}
9736
9737	// Check if we're dealing with a known content-type
9738	if ( ct ) {
9739		for ( type in contents ) {
9740			if ( contents[ type ] && contents[ type ].test( ct ) ) {
9741				dataTypes.unshift( type );
9742				break;
9743			}
9744		}
9745	}
9746
9747	// Check to see if we have a response for the expected dataType
9748	if ( dataTypes[ 0 ] in responses ) {
9749		finalDataType = dataTypes[ 0 ];
9750	} else {
9751
9752		// Try convertible dataTypes
9753		for ( type in responses ) {
9754			if ( !dataTypes[ 0 ] || s.converters[ type + " " + dataTypes[ 0 ] ] ) {
9755				finalDataType = type;
9756				break;
9757			}
9758			if ( !firstDataType ) {
9759				firstDataType = type;
9760			}
9761		}
9762
9763		// Or just use first one
9764		finalDataType = finalDataType || firstDataType;
9765	}
9766
9767	// If we found a dataType
9768	// We add the dataType to the list if needed
9769	// and return the corresponding response
9770	if ( finalDataType ) {
9771		if ( finalDataType !== dataTypes[ 0 ] ) {
9772			dataTypes.unshift( finalDataType );
9773		}
9774		return responses[ finalDataType ];
9775	}
9776}
9777
9778/* Chain conversions given the request and the original response
9779 * Also sets the responseXXX fields on the jqXHR instance
9780 */
9781function ajaxConvert( s, response, jqXHR, isSuccess ) {
9782	var conv2, current, conv, tmp, prev,
9783		converters = {},
9784
9785		// Work with a copy of dataTypes in case we need to modify it for conversion
9786		dataTypes = s.dataTypes.slice();
9787
9788	// Create converters map with lowercased keys
9789	if ( dataTypes[ 1 ] ) {
9790		for ( conv in s.converters ) {
9791			converters[ conv.toLowerCase() ] = s.converters[ conv ];
9792		}
9793	}
9794
9795	current = dataTypes.shift();
9796
9797	// Convert to each sequential dataType
9798	while ( current ) {
9799
9800		if ( s.responseFields[ current ] ) {
9801			jqXHR[ s.responseFields[ current ] ] = response;
9802		}
9803
9804		// Apply the dataFilter if provided
9805		if ( !prev && isSuccess && s.dataFilter ) {
9806			response = s.dataFilter( response, s.dataType );
9807		}
9808
9809		prev = current;
9810		current = dataTypes.shift();
9811
9812		if ( current ) {
9813
9814			// There's only work to do if current dataType is non-auto
9815			if ( current === "*" ) {
9816
9817				current = prev;
9818
9819			// Convert response if prev dataType is non-auto and differs from current
9820			} else if ( prev !== "*" && prev !== current ) {
9821
9822				// Seek a direct converter
9823				conv = converters[ prev + " " + current ] || converters[ "* " + current ];
9824
9825				// If none found, seek a pair
9826				if ( !conv ) {
9827					for ( conv2 in converters ) {
9828
9829						// If conv2 outputs current
9830						tmp = conv2.split( " " );
9831						if ( tmp[ 1 ] === current ) {
9832
9833							// If prev can be converted to accepted input
9834							conv = converters[ prev + " " + tmp[ 0 ] ] ||
9835								converters[ "* " + tmp[ 0 ] ];
9836							if ( conv ) {
9837
9838								// Condense equivalence converters
9839								if ( conv === true ) {
9840									conv = converters[ conv2 ];
9841
9842								// Otherwise, insert the intermediate dataType
9843								} else if ( converters[ conv2 ] !== true ) {
9844									current = tmp[ 0 ];
9845									dataTypes.unshift( tmp[ 1 ] );
9846								}
9847								break;
9848							}
9849						}
9850					}
9851				}
9852
9853				// Apply converter (if not an equivalence)
9854				if ( conv !== true ) {
9855
9856					// Unless errors are allowed to bubble, catch and return them
9857					if ( conv && s.throws ) {
9858						response = conv( response );
9859					} else {
9860						try {
9861							response = conv( response );
9862						} catch ( e ) {
9863							return {
9864								state: "parsererror",
9865								error: conv ? e : "No conversion from " + prev + " to " + current
9866							};
9867						}
9868					}
9869				}
9870			}
9871		}
9872	}
9873
9874	return { state: "success", data: response };
9875}
9876
9877jQuery.extend( {
9878
9879	// Counter for holding the number of active queries
9880	active: 0,
9881
9882	// Last-Modified header cache for next request
9883	lastModified: {},
9884	etag: {},
9885
9886	ajaxSettings: {
9887		url: location.href,
9888		type: "GET",
9889		isLocal: rlocalProtocol.test( location.protocol ),
9890		global: true,
9891		processData: true,
9892		async: true,
9893		contentType: "application/x-www-form-urlencoded; charset=UTF-8",
9894
9895		/*
9896		timeout: 0,
9897		data: null,
9898		dataType: null,
9899		username: null,
9900		password: null,
9901		cache: null,
9902		throws: false,
9903		traditional: false,
9904		headers: {},
9905		*/
9906
9907		accepts: {
9908			"*": allTypes,
9909			text: "text/plain",
9910			html: "text/html",
9911			xml: "application/xml, text/xml",
9912			json: "application/json, text/javascript"
9913		},
9914
9915		contents: {
9916			xml: /\bxml\b/,
9917			html: /\bhtml/,
9918			json: /\bjson\b/
9919		},
9920
9921		responseFields: {
9922			xml: "responseXML",
9923			text: "responseText",
9924			json: "responseJSON"
9925		},
9926
9927		// Data converters
9928		// Keys separate source (or catchall "*") and destination types with a single space
9929		converters: {
9930
9931			// Convert anything to text
9932			"* text": String,
9933
9934			// Text to html (true = no transformation)
9935			"text html": true,
9936
9937			// Evaluate text as a json expression
9938			"text json": JSON.parse,
9939
9940			// Parse text as xml
9941			"text xml": jQuery.parseXML
9942		},
9943
9944		// For options that shouldn't be deep extended:
9945		// you can add your own custom options here if
9946		// and when you create one that shouldn't be
9947		// deep extended (see ajaxExtend)
9948		flatOptions: {
9949			url: true,
9950			context: true
9951		}
9952	},
9953
9954	// Creates a full fledged settings object into target
9955	// with both ajaxSettings and settings fields.
9956	// If target is omitted, writes into ajaxSettings.
9957	ajaxSetup: function( target, settings ) {
9958		return settings ?
9959
9960			// Building a settings object
9961			ajaxExtend( ajaxExtend( target, jQuery.ajaxSettings ), settings ) :
9962
9963			// Extending ajaxSettings
9964			ajaxExtend( jQuery.ajaxSettings, target );
9965	},
9966
9967	ajaxPrefilter: addToPrefiltersOrTransports( prefilters ),
9968	ajaxTransport: addToPrefiltersOrTransports( transports ),
9969
9970	// Main method
9971	ajax: function( url, options ) {
9972
9973		// If url is an object, simulate pre-1.5 signature
9974		if ( typeof url === "object" ) {
9975			options = url;
9976			url = undefined;
9977		}
9978
9979		// Force options to be an object
9980		options = options || {};
9981
9982		var transport,
9983
9984			// URL without anti-cache param
9985			cacheURL,
9986
9987			// Response headers
9988			responseHeadersString,
9989			responseHeaders,
9990
9991			// timeout handle
9992			timeoutTimer,
9993
9994			// Url cleanup var
9995			urlAnchor,
9996
9997			// Request state (becomes false upon send and true upon completion)
9998			completed,
9999
10000			// To know if global events are to be dispatched
10001			fireGlobals,
10002
10003			// Loop variable
10004			i,
10005
10006			// uncached part of the url
10007			uncached,
10008
10009			// Create the final options object
10010			s = jQuery.ajaxSetup( {}, options ),
10011
10012			// Callbacks context
10013			callbackContext = s.context || s,
10014
10015			// Context for global events is callbackContext if it is a DOM node or jQuery collection
10016			globalEventContext = s.context &&
10017				( callbackContext.nodeType || callbackContext.jquery ) ?
10018				jQuery( callbackContext ) :
10019				jQuery.event,
10020
10021			// Deferreds
10022			deferred = jQuery.Deferred(),
10023			completeDeferred = jQuery.Callbacks( "once memory" ),
10024
10025			// Status-dependent callbacks
10026			statusCode = s.statusCode || {},
10027
10028			// Headers (they are sent all at once)
10029			requestHeaders = {},
10030			requestHeadersNames = {},
10031
10032			// Default abort message
10033			strAbort = "canceled",
10034
10035			// Fake xhr
10036			jqXHR = {
10037				readyState: 0,
10038
10039				// Builds headers hashtable if needed
10040				getResponseHeader: function( key ) {
10041					var match;
10042					if ( completed ) {
10043						if ( !responseHeaders ) {
10044							responseHeaders = {};
10045							while ( ( match = rheaders.exec( responseHeadersString ) ) ) {
10046								responseHeaders[ match[ 1 ].toLowerCase() + " " ] =
10047									( responseHeaders[ match[ 1 ].toLowerCase() + " " ] || [] )
10048										.concat( match[ 2 ] );
10049							}
10050						}
10051						match = responseHeaders[ key.toLowerCase() + " " ];
10052					}
10053					return match == null ? null : match.join( ", " );
10054				},
10055
10056				// Raw string
10057				getAllResponseHeaders: function() {
10058					return completed ? responseHeadersString : null;
10059				},
10060
10061				// Caches the header
10062				setRequestHeader: function( name, value ) {
10063					if ( completed == null ) {
10064						name = requestHeadersNames[ name.toLowerCase() ] =
10065							requestHeadersNames[ name.toLowerCase() ] || name;
10066						requestHeaders[ name ] = value;
10067					}
10068					return this;
10069				},
10070
10071				// Overrides response content-type header
10072				overrideMimeType: function( type ) {
10073					if ( completed == null ) {
10074						s.mimeType = type;
10075					}
10076					return this;
10077				},
10078
10079				// Status-dependent callbacks
10080				statusCode: function( map ) {
10081					var code;
10082					if ( map ) {
10083						if ( completed ) {
10084
10085							// Execute the appropriate callbacks
10086							jqXHR.always( map[ jqXHR.status ] );
10087						} else {
10088
10089							// Lazy-add the new callbacks in a way that preserves old ones
10090							for ( code in map ) {
10091								statusCode[ code ] = [ statusCode[ code ], map[ code ] ];
10092							}
10093						}
10094					}
10095					return this;
10096				},
10097
10098				// Cancel the request
10099				abort: function( statusText ) {
10100					var finalText = statusText || strAbort;
10101					if ( transport ) {
10102						transport.abort( finalText );
10103					}
10104					done( 0, finalText );
10105					return this;
10106				}
10107			};
10108
10109		// Attach deferreds
10110		deferred.promise( jqXHR );
10111
10112		// Add protocol if not provided (prefilters might expect it)
10113		// Handle falsy url in the settings object (trac-10093: consistency with old signature)
10114		// We also use the url parameter if available
10115		s.url = ( ( url || s.url || location.href ) + "" )
10116			.replace( rprotocol, location.protocol + "//" );
10117
10118		// Alias method option to type as per ticket trac-12004
10119		s.type = options.method || options.type || s.method || s.type;
10120
10121		// Extract dataTypes list
10122		s.dataTypes = ( s.dataType || "*" ).toLowerCase().match( rnothtmlwhite ) || [ "" ];
10123
10124		// A cross-domain request is in order when the origin doesn't match the current origin.
10125		if ( s.crossDomain == null ) {
10126			urlAnchor = document.createElement( "a" );
10127
10128			// Support: IE <=8 - 11, Edge 12 - 15
10129			// IE throws exception on accessing the href property if url is malformed,
10130			// e.g. http://example.com:80x/
10131			try {
10132				urlAnchor.href = s.url;
10133
10134				// Support: IE <=8 - 11 only
10135				// Anchor's host property isn't correctly set when s.url is relative
10136				urlAnchor.href = urlAnchor.href;
10137				s.crossDomain = originAnchor.protocol + "//" + originAnchor.host !==
10138					urlAnchor.protocol + "//" + urlAnchor.host;
10139			} catch ( e ) {
10140
10141				// If there is an error parsing the URL, assume it is crossDomain,
10142				// it can be rejected by the transport if it is invalid
10143				s.crossDomain = true;
10144			}
10145		}
10146
10147		// Convert data if not already a string
10148		if ( s.data && s.processData && typeof s.data !== "string" ) {
10149			s.data = jQuery.param( s.data, s.traditional );
10150		}
10151
10152		// Apply prefilters
10153		inspectPrefiltersOrTransports( prefilters, s, options, jqXHR );
10154
10155		// If request was aborted inside a prefilter, stop there
10156		if ( completed ) {
10157			return jqXHR;
10158		}
10159
10160		// We can fire global events as of now if asked to
10161		// Don't fire events if jQuery.event is undefined in an AMD-usage scenario (trac-15118)
10162		fireGlobals = jQuery.event && s.global;
10163
10164		// Watch for a new set of requests
10165		if ( fireGlobals && jQuery.active++ === 0 ) {
10166			jQuery.event.trigger( "ajaxStart" );
10167		}
10168
10169		// Uppercase the type
10170		s.type = s.type.toUpperCase();
10171
10172		// Determine if request has content
10173		s.hasContent = !rnoContent.test( s.type );
10174
10175		// Save the URL in case we're toying with the If-Modified-Since
10176		// and/or If-None-Match header later on
10177		// Remove hash to simplify url manipulation
10178		cacheURL = s.url.replace( rhash, "" );
10179
10180		// More options handling for requests with no content
10181		if ( !s.hasContent ) {
10182
10183			// Remember the hash so we can put it back
10184			uncached = s.url.slice( cacheURL.length );
10185
10186			// If data is available and should be processed, append data to url
10187			if ( s.data && ( s.processData || typeof s.data === "string" ) ) {
10188				cacheURL += ( rquery.test( cacheURL ) ? "&" : "?" ) + s.data;
10189
10190				// trac-9682: remove data so that it's not used in an eventual retry
10191				delete s.data;
10192			}
10193
10194			// Add or update anti-cache param if needed
10195			if ( s.cache === false ) {
10196				cacheURL = cacheURL.replace( rantiCache, "$1" );
10197				uncached = ( rquery.test( cacheURL ) ? "&" : "?" ) + "_=" + ( nonce.guid++ ) +
10198					uncached;
10199			}
10200
10201			// Put hash and anti-cache on the URL that will be requested (gh-1732)
10202			s.url = cacheURL + uncached;
10203
10204		// Change '%20' to '+' if this is encoded form body content (gh-2658)
10205		} else if ( s.data && s.processData &&
10206			( s.contentType || "" ).indexOf( "application/x-www-form-urlencoded" ) === 0 ) {
10207			s.data = s.data.replace( r20, "+" );
10208		}
10209
10210		// Set the If-Modified-Since and/or If-None-Match header, if in ifModified mode.
10211		if ( s.ifModified ) {
10212			if ( jQuery.lastModified[ cacheURL ] ) {
10213				jqXHR.setRequestHeader( "If-Modified-Since", jQuery.lastModified[ cacheURL ] );
10214			}
10215			if ( jQuery.etag[ cacheURL ] ) {
10216				jqXHR.setRequestHeader( "If-None-Match", jQuery.etag[ cacheURL ] );
10217			}
10218		}
10219
10220		// Set the correct header, if data is being sent
10221		if ( s.data && s.hasContent && s.contentType !== false || options.contentType ) {
10222			jqXHR.setRequestHeader( "Content-Type", s.contentType );
10223		}
10224
10225		// Set the Accepts header for the server, depending on the dataType
10226		jqXHR.setRequestHeader(
10227			"Accept",
10228			s.dataTypes[ 0 ] && s.accepts[ s.dataTypes[ 0 ] ] ?
10229				s.accepts[ s.dataTypes[ 0 ] ] +
10230					( s.dataTypes[ 0 ] !== "*" ? ", " + allTypes + "; q=0.01" : "" ) :
10231				s.accepts[ "*" ]
10232		);
10233
10234		// Check for headers option
10235		for ( i in s.headers ) {
10236			jqXHR.setRequestHeader( i, s.headers[ i ] );
10237		}
10238
10239		// Allow custom headers/mimetypes and early abort
10240		if ( s.beforeSend &&
10241			( s.beforeSend.call( callbackContext, jqXHR, s ) === false || completed ) ) {
10242
10243			// Abort if not done already and return
10244			return jqXHR.abort();
10245		}
10246
10247		// Aborting is no longer a cancellation
10248		strAbort = "abort";
10249
10250		// Install callbacks on deferreds
10251		completeDeferred.add( s.complete );
10252		jqXHR.done( s.success );
10253		jqXHR.fail( s.error );
10254
10255		// Get transport
10256		transport = inspectPrefiltersOrTransports( transports, s, options, jqXHR );
10257
10258		// If no transport, we auto-abort
10259		if ( !transport ) {
10260			done( -1, "No Transport" );
10261		} else {
10262			jqXHR.readyState = 1;
10263
10264			// Send global event
10265			if ( fireGlobals ) {
10266				globalEventContext.trigger( "ajaxSend", [ jqXHR, s ] );
10267			}
10268
10269			// If request was aborted inside ajaxSend, stop there
10270			if ( completed ) {
10271				return jqXHR;
10272			}
10273
10274			// Timeout
10275			if ( s.async && s.timeout > 0 ) {
10276				timeoutTimer = window.setTimeout( function() {
10277					jqXHR.abort( "timeout" );
10278				}, s.timeout );
10279			}
10280
10281			try {
10282				completed = false;
10283				transport.send( requestHeaders, done );
10284			} catch ( e ) {
10285
10286				// Rethrow post-completion exceptions
10287				if ( completed ) {
10288					throw e;
10289				}
10290
10291				// Propagate others as results
10292				done( -1, e );
10293			}
10294		}
10295
10296		// Callback for when everything is done
10297		function done( status, nativeStatusText, responses, headers ) {
10298			var isSuccess, success, error, response, modified,
10299				statusText = nativeStatusText;
10300
10301			// Ignore repeat invocations
10302			if ( completed ) {
10303				return;
10304			}
10305
10306			completed = true;
10307
10308			// Clear timeout if it exists
10309			if ( timeoutTimer ) {
10310				window.clearTimeout( timeoutTimer );
10311			}
10312
10313			// Dereference transport for early garbage collection
10314			// (no matter how long the jqXHR object will be used)
10315			transport = undefined;
10316
10317			// Cache response headers
10318			responseHeadersString = headers || "";
10319
10320			// Set readyState
10321			jqXHR.readyState = status > 0 ? 4 : 0;
10322
10323			// Determine if successful
10324			isSuccess = status >= 200 && status < 300 || status === 304;
10325
10326			// Get response data
10327			if ( responses ) {
10328				response = ajaxHandleResponses( s, jqXHR, responses );
10329			}
10330
10331			// Use a noop converter for missing script but not if jsonp
10332			if ( !isSuccess &&
10333				jQuery.inArray( "script", s.dataTypes ) > -1 &&
10334				jQuery.inArray( "json", s.dataTypes ) < 0 ) {
10335				s.converters[ "text script" ] = function() {};
10336			}
10337
10338			// Convert no matter what (that way responseXXX fields are always set)
10339			response = ajaxConvert( s, response, jqXHR, isSuccess );
10340
10341			// If successful, handle type chaining
10342			if ( isSuccess ) {
10343
10344				// Set the If-Modified-Since and/or If-None-Match header, if in ifModified mode.
10345				if ( s.ifModified ) {
10346					modified = jqXHR.getResponseHeader( "Last-Modified" );
10347					if ( modified ) {
10348						jQuery.lastModified[ cacheURL ] = modified;
10349					}
10350					modified = jqXHR.getResponseHeader( "etag" );
10351					if ( modified ) {
10352						jQuery.etag[ cacheURL ] = modified;
10353					}
10354				}
10355
10356				// if no content
10357				if ( status === 204 || s.type === "HEAD" ) {
10358					statusText = "nocontent";
10359
10360				// if not modified
10361				} else if ( status === 304 ) {
10362					statusText = "notmodified";
10363
10364				// If we have data, let's convert it
10365				} else {
10366					statusText = response.state;
10367					success = response.data;
10368					error = response.error;
10369					isSuccess = !error;
10370				}
10371			} else {
10372
10373				// Extract error from statusText and normalize for non-aborts
10374				error = statusText;
10375				if ( status || !statusText ) {
10376					statusText = "error";
10377					if ( status < 0 ) {
10378						status = 0;
10379					}
10380				}
10381			}
10382
10383			// Set data for the fake xhr object
10384			jqXHR.status = status;
10385			jqXHR.statusText = ( nativeStatusText || statusText ) + "";
10386
10387			// Success/Error
10388			if ( isSuccess ) {
10389				deferred.resolveWith( callbackContext, [ success, statusText, jqXHR ] );
10390			} else {
10391				deferred.rejectWith( callbackContext, [ jqXHR, statusText, error ] );
10392			}
10393
10394			// Status-dependent callbacks
10395			jqXHR.statusCode( statusCode );
10396			statusCode = undefined;
10397
10398			if ( fireGlobals ) {
10399				globalEventContext.trigger( isSuccess ? "ajaxSuccess" : "ajaxError",
10400					[ jqXHR, s, isSuccess ? success : error ] );
10401			}
10402
10403			// Complete
10404			completeDeferred.fireWith( callbackContext, [ jqXHR, statusText ] );
10405
10406			if ( fireGlobals ) {
10407				globalEventContext.trigger( "ajaxComplete", [ jqXHR, s ] );
10408
10409				// Handle the global AJAX counter
10410				if ( !( --jQuery.active ) ) {
10411					jQuery.event.trigger( "ajaxStop" );
10412				}
10413			}
10414		}
10415
10416		return jqXHR;
10417	},
10418
10419	getJSON: function( url, data, callback ) {
10420		return jQuery.get( url, data, callback, "json" );
10421	},
10422
10423	getScript: function( url, callback ) {
10424		return jQuery.get( url, undefined, callback, "script" );
10425	}
10426} );
10427
10428jQuery.each( [ "get", "post" ], function( _i, method ) {
10429	jQuery[ method ] = function( url, data, callback, type ) {
10430
10431		// Shift arguments if data argument was omitted
10432		if ( isFunction( data ) ) {
10433			type = type || callback;
10434			callback = data;
10435			data = undefined;
10436		}
10437
10438		// The url can be an options object (which then must have .url)
10439		return jQuery.ajax( jQuery.extend( {
10440			url: url,
10441			type: method,
10442			dataType: type,
10443			data: data,
10444			success: callback
10445		}, jQuery.isPlainObject( url ) && url ) );
10446	};
10447} );
10448
10449jQuery.ajaxPrefilter( function( s ) {
10450	var i;
10451	for ( i in s.headers ) {
10452		if ( i.toLowerCase() === "content-type" ) {
10453			s.contentType = s.headers[ i ] || "";
10454		}
10455	}
10456} );
10457
10458
10459jQuery._evalUrl = function( url, options, doc ) {
10460	return jQuery.ajax( {
10461		url: url,
10462
10463		// Make this explicit, since user can override this through ajaxSetup (trac-11264)
10464		type: "GET",
10465		dataType: "script",
10466		cache: true,
10467		async: false,
10468		global: false,
10469
10470		// Only evaluate the response if it is successful (gh-4126)
10471		// dataFilter is not invoked for failure responses, so using it instead
10472		// of the default converter is kludgy but it works.
10473		converters: {
10474			"text script": function() {}
10475		},
10476		dataFilter: function( response ) {
10477			jQuery.globalEval( response, options, doc );
10478		}
10479	} );
10480};
10481
10482
10483jQuery.fn.extend( {
10484	wrapAll: function( html ) {
10485		var wrap;
10486
10487		if ( this[ 0 ] ) {
10488			if ( isFunction( html ) ) {
10489				html = html.call( this[ 0 ] );
10490			}
10491
10492			// The elements to wrap the target around
10493			wrap = jQuery( html, this[ 0 ].ownerDocument ).eq( 0 ).clone( true );
10494
10495			if ( this[ 0 ].parentNode ) {
10496				wrap.insertBefore( this[ 0 ] );
10497			}
10498
10499			wrap.map( function() {
10500				var elem = this;
10501
10502				while ( elem.firstElementChild ) {
10503					elem = elem.firstElementChild;
10504				}
10505
10506				return elem;
10507			} ).append( this );
10508		}
10509
10510		return this;
10511	},
10512
10513	wrapInner: function( html ) {
10514		if ( isFunction( html ) ) {
10515			return this.each( function( i ) {
10516				jQuery( this ).wrapInner( html.call( this, i ) );
10517			} );
10518		}
10519
10520		return this.each( function() {
10521			var self = jQuery( this ),
10522				contents = self.contents();
10523
10524			if ( contents.length ) {
10525				contents.wrapAll( html );
10526
10527			} else {
10528				self.append( html );
10529			}
10530		} );
10531	},
10532
10533	wrap: function( html ) {
10534		var htmlIsFunction = isFunction( html );
10535
10536		return this.each( function( i ) {
10537			jQuery( this ).wrapAll( htmlIsFunction ? html.call( this, i ) : html );
10538		} );
10539	},
10540
10541	unwrap: function( selector ) {
10542		this.parent( selector ).not( "body" ).each( function() {
10543			jQuery( this ).replaceWith( this.childNodes );
10544		} );
10545		return this;
10546	}
10547} );
10548
10549
10550jQuery.expr.pseudos.hidden = function( elem ) {
10551	return !jQuery.expr.pseudos.visible( elem );
10552};
10553jQuery.expr.pseudos.visible = function( elem ) {
10554	return !!( elem.offsetWidth || elem.offsetHeight || elem.getClientRects().length );
10555};
10556
10557
10558
10559
10560jQuery.ajaxSettings.xhr = function() {
10561	try {
10562		return new window.XMLHttpRequest();
10563	} catch ( e ) {}
10564};
10565
10566var xhrSuccessStatus = {
10567
10568		// File protocol always yields status code 0, assume 200
10569		0: 200,
10570
10571		// Support: IE <=9 only
10572		// trac-1450: sometimes IE returns 1223 when it should be 204
10573		1223: 204
10574	},
10575	xhrSupported = jQuery.ajaxSettings.xhr();
10576
10577support.cors = !!xhrSupported && ( "withCredentials" in xhrSupported );
10578support.ajax = xhrSupported = !!xhrSupported;
10579
10580jQuery.ajaxTransport( function( options ) {
10581	var callback, errorCallback;
10582
10583	// Cross domain only allowed if supported through XMLHttpRequest
10584	if ( support.cors || xhrSupported && !options.crossDomain ) {
10585		return {
10586			send: function( headers, complete ) {
10587				var i,
10588					xhr = options.xhr();
10589
10590				xhr.open(
10591					options.type,
10592					options.url,
10593					options.async,
10594					options.username,
10595					options.password
10596				);
10597
10598				// Apply custom fields if provided
10599				if ( options.xhrFields ) {
10600					for ( i in options.xhrFields ) {
10601						xhr[ i ] = options.xhrFields[ i ];
10602					}
10603				}
10604
10605				// Override mime type if needed
10606				if ( options.mimeType && xhr.overrideMimeType ) {
10607					xhr.overrideMimeType( options.mimeType );
10608				}
10609
10610				// X-Requested-With header
10611				// For cross-domain requests, seeing as conditions for a preflight are
10612				// akin to a jigsaw puzzle, we simply never set it to be sure.
10613				// (it can always be set on a per-request basis or even using ajaxSetup)
10614				// For same-domain requests, won't change header if already provided.
10615				if ( !options.crossDomain && !headers[ "X-Requested-With" ] ) {
10616					headers[ "X-Requested-With" ] = "XMLHttpRequest";
10617				}
10618
10619				// Set headers
10620				for ( i in headers ) {
10621					xhr.setRequestHeader( i, headers[ i ] );
10622				}
10623
10624				// Callback
10625				callback = function( type ) {
10626					return function() {
10627						if ( callback ) {
10628							callback = errorCallback = xhr.onload =
10629								xhr.onerror = xhr.onabort = xhr.ontimeout =
10630									xhr.onreadystatechange = null;
10631
10632							if ( type === "abort" ) {
10633								xhr.abort();
10634							} else if ( type === "error" ) {
10635
10636								// Support: IE <=9 only
10637								// On a manual native abort, IE9 throws
10638								// errors on any property access that is not readyState
10639								if ( typeof xhr.status !== "number" ) {
10640									complete( 0, "error" );
10641								} else {
10642									complete(
10643
10644										// File: protocol always yields status 0; see trac-8605, trac-14207
10645										xhr.status,
10646										xhr.statusText
10647									);
10648								}
10649							} else {
10650								complete(
10651									xhrSuccessStatus[ xhr.status ] || xhr.status,
10652									xhr.statusText,
10653
10654									// Support: IE <=9 only
10655									// IE9 has no XHR2 but throws on binary (trac-11426)
10656									// For XHR2 non-text, let the caller handle it (gh-2498)
10657									( xhr.responseType || "text" ) !== "text"  ||
10658									typeof xhr.responseText !== "string" ?
10659										{ binary: xhr.response } :
10660										{ text: xhr.responseText },
10661									xhr.getAllResponseHeaders()
10662								);
10663							}
10664						}
10665					};
10666				};
10667
10668				// Listen to events
10669				xhr.onload = callback();
10670				errorCallback = xhr.onerror = xhr.ontimeout = callback( "error" );
10671
10672				// Support: IE 9 only
10673				// Use onreadystatechange to replace onabort
10674				// to handle uncaught aborts
10675				if ( xhr.onabort !== undefined ) {
10676					xhr.onabort = errorCallback;
10677				} else {
10678					xhr.onreadystatechange = function() {
10679
10680						// Check readyState before timeout as it changes
10681						if ( xhr.readyState === 4 ) {
10682
10683							// Allow onerror to be called first,
10684							// but that will not handle a native abort
10685							// Also, save errorCallback to a variable
10686							// as xhr.onerror cannot be accessed
10687							window.setTimeout( function() {
10688								if ( callback ) {
10689									errorCallback();
10690								}
10691							} );
10692						}
10693					};
10694				}
10695
10696				// Create the abort callback
10697				callback = callback( "abort" );
10698
10699				try {
10700
10701					// Do send the request (this may raise an exception)
10702					xhr.send( options.hasContent && options.data || null );
10703				} catch ( e ) {
10704
10705					// trac-14683: Only rethrow if this hasn't been notified as an error yet
10706					if ( callback ) {
10707						throw e;
10708					}
10709				}
10710			},
10711
10712			abort: function() {
10713				if ( callback ) {
10714					callback();
10715				}
10716			}
10717		};
10718	}
10719} );
10720
10721
10722
10723
10724// Prevent auto-execution of scripts when no explicit dataType was provided (See gh-2432)
10725jQuery.ajaxPrefilter( function( s ) {
10726	if ( s.crossDomain ) {
10727		s.contents.script = false;
10728	}
10729} );
10730
10731// Install script dataType
10732jQuery.ajaxSetup( {
10733	accepts: {
10734		script: "text/javascript, application/javascript, " +
10735			"application/ecmascript, application/x-ecmascript"
10736	},
10737	contents: {
10738		script: /\b(?:java|ecma)script\b/
10739	},
10740	converters: {
10741		"text script": function( text ) {
10742			jQuery.globalEval( text );
10743			return text;
10744		}
10745	}
10746} );
10747
10748// Handle cache's special case and crossDomain
10749jQuery.ajaxPrefilter( "script", function( s ) {
10750	if ( s.cache === undefined ) {
10751		s.cache = false;
10752	}
10753	if ( s.crossDomain ) {
10754		s.type = "GET";
10755	}
10756} );
10757
10758// Bind script tag hack transport
10759jQuery.ajaxTransport( "script", function( s ) {
10760
10761	// This transport only deals with cross domain or forced-by-attrs requests
10762	if ( s.crossDomain || s.scriptAttrs ) {
10763		var script, callback;
10764		return {
10765			send: function( _, complete ) {
10766				script = jQuery( "<script>" )
10767					.attr( s.scriptAttrs || {} )
10768					.prop( { charset: s.scriptCharset, src: s.url } )
10769					.on( "load error", callback = function( evt ) {
10770						script.remove();
10771						callback = null;
10772						if ( evt ) {
10773							complete( evt.type === "error" ? 404 : 200, evt.type );
10774						}
10775					} );
10776
10777				// Use native DOM manipulation to avoid our domManip AJAX trickery
10778				document.head.appendChild( script[ 0 ] );
10779			},
10780			abort: function() {
10781				if ( callback ) {
10782					callback();
10783				}
10784			}
10785		};
10786	}
10787} );
10788
10789
10790
10791
10792var oldCallbacks = [],
10793	rjsonp = /(=)\?(?=&|$)|\?\?/;
10794
10795// Default jsonp settings
10796jQuery.ajaxSetup( {
10797	jsonp: "callback",
10798	jsonpCallback: function() {
10799		var callback = oldCallbacks.pop() || ( jQuery.expando + "_" + ( nonce.guid++ ) );
10800		this[ callback ] = true;
10801		return callback;
10802	}
10803} );
10804
10805// Detect, normalize options and install callbacks for jsonp requests
10806jQuery.ajaxPrefilter( "json jsonp", function( s, originalSettings, jqXHR ) {
10807
10808	var callbackName, overwritten, responseContainer,
10809		jsonProp = s.jsonp !== false && ( rjsonp.test( s.url ) ?
10810			"url" :
10811			typeof s.data === "string" &&
10812				( s.contentType || "" )
10813					.indexOf( "application/x-www-form-urlencoded" ) === 0 &&
10814				rjsonp.test( s.data ) && "data"
10815		);
10816
10817	// Handle iff the expected data type is "jsonp" or we have a parameter to set
10818	if ( jsonProp || s.dataTypes[ 0 ] === "jsonp" ) {
10819
10820		// Get callback name, remembering preexisting value associated with it
10821		callbackName = s.jsonpCallback = isFunction( s.jsonpCallback ) ?
10822			s.jsonpCallback() :
10823			s.jsonpCallback;
10824
10825		// Insert callback into url or form data
10826		if ( jsonProp ) {
10827			s[ jsonProp ] = s[ jsonProp ].replace( rjsonp, "$1" + callbackName );
10828		} else if ( s.jsonp !== false ) {
10829			s.url += ( rquery.test( s.url ) ? "&" : "?" ) + s.jsonp + "=" + callbackName;
10830		}
10831
10832		// Use data converter to retrieve json after script execution
vendor: 2,987 bytes, lines 10833-10942
10833		s.converters[ "script json" ] = function() {
10834			if ( !responseContainer ) {
10835				jQuery.error( callbackName + " was not called" );
10836			}
10837			return responseContainer[ 0 ];
10838		};
10839
10840		// Force json dataType
10841		s.dataTypes[ 0 ] = "json";
10842
10843		// Install callback
10844		overwritten = window[ callbackName ];
10845		window[ callbackName ] = function() {
10846			responseContainer = arguments;
10847		};
10848
10849		// Clean-up function (fires after converters)
10850		jqXHR.always( function() {
10851
10852			// If previous value didn't exist - remove it
10853			if ( overwritten === undefined ) {
10854				jQuery( window ).removeProp( callbackName );
10855
10856			// Otherwise restore preexisting value
10857			} else {
10858				window[ callbackName ] = overwritten;
10859			}
10860
10861			// Save back as free
10862			if ( s[ callbackName ] ) {
10863
10864				// Make sure that re-using the options doesn't screw things around
10865				s.jsonpCallback = originalSettings.jsonpCallback;
10866
10867				// Save the callback name for future use
10868				oldCallbacks.push( callbackName );
10869			}
10870
10871			// Call if it was a function and we have a response
10872			if ( responseContainer && isFunction( overwritten ) ) {
10873				overwritten( responseContainer[ 0 ] );
10874			}
10875
10876			responseContainer = overwritten = undefined;
10877		} );
10878
10879		// Delegate to script
10880		return "script";
10881	}
10882} );
10883
10884
10885
10886
10887// Support: Safari 8 only
10888// In Safari 8 documents created via document.implementation.createHTMLDocument
10889// collapse sibling forms: the second one becomes a child of the first one.
10890// Because of that, this security measure has to be disabled in Safari 8.
10891// https://bugs.webkit.org/show_bug.cgi?id=137337
10892support.createHTMLDocument = ( function() {
10893	var body = document.implementation.createHTMLDocument( "" ).body;
10894	body.innerHTML = "<form></form><form></form>";
10895	return body.childNodes.length === 2;
10896} )();
10897
10898
10899// Argument "data" should be string of html
10900// context (optional): If specified, the fragment will be created in this context,
10901// defaults to document
10902// keepScripts (optional): If true, will include scripts passed in the html string
10903jQuery.parseHTML = function( data, context, keepScripts ) {
10904	if ( typeof data !== "string" ) {
10905		return [];
10906	}
10907	if ( typeof context === "boolean" ) {
10908		keepScripts = context;
10909		context = false;
10910	}
10911
10912	var base, parsed, scripts;
10913
10914	if ( !context ) {
10915
10916		// Stop scripts or inline event handlers from being executed immediately
10917		// by using document.implementation
10918		if ( support.createHTMLDocument ) {
10919			context = document.implementation.createHTMLDocument( "" );
10920
10921			// Set the base href for the created document
10922			// so any parsed elements with URLs
10923			// are based on the document's URL (gh-2965)
10924			base = context.createElement( "base" );
10925			base.href = document.location.href;
10926			context.head.appendChild( base );
10927		} else {
10928			context = document;
10929		}
10930	}
10931
10932	parsed = rsingleTag.exec( data );
10933	scripts = !keepScripts && [];
10934
10935	// Single tag
10936	if ( parsed ) {
10937		return [ context.createElement( parsed[ 1 ] ) ];
10938	}
10939
10940	parsed = buildFragment( [ data ], context, scripts );
10941
10942	if ( scripts && scripts.length ) {
vendor: 14,891 bytes, lines 10943-11479
10943		jQuery( scripts ).remove();
10944	}
10945
10946	return jQuery.merge( [], parsed.childNodes );
10947};
10948
10949
10950/**
10951 * Load a url into a page
10952 */
10953jQuery.fn.load = function( url, params, callback ) {
10954	var selector, type, response,
10955		self = this,
10956		off = url.indexOf( " " );
10957
10958	if ( off > -1 ) {
10959		selector = stripAndCollapse( url.slice( off ) );
10960		url = url.slice( 0, off );
10961	}
10962
10963	// If it's a function
10964	if ( isFunction( params ) ) {
10965
10966		// We assume that it's the callback
10967		callback = params;
10968		params = undefined;
10969
10970	// Otherwise, build a param string
10971	} else if ( params && typeof params === "object" ) {
10972		type = "POST";
10973	}
10974
10975	// If we have elements to modify, make the request
10976	if ( self.length > 0 ) {
10977		jQuery.ajax( {
10978			url: url,
10979
10980			// If "type" variable is undefined, then "GET" method will be used.
10981			// Make value of this field explicit since
10982			// user can override it through ajaxSetup method
10983			type: type || "GET",
10984			dataType: "html",
10985			data: params
10986		} ).done( function( responseText ) {
10987
10988			// Save response for use in complete callback
10989			response = arguments;
10990
10991			self.html( selector ?
10992
10993				// If a selector was specified, locate the right elements in a dummy div
10994				// Exclude scripts to avoid IE 'Permission Denied' errors
10995				jQuery( "<div>" ).append( jQuery.parseHTML( responseText ) ).find( selector ) :
10996
10997				// Otherwise use the full result
10998				responseText );
10999
11000		// If the request succeeds, this function gets "data", "status", "jqXHR"
11001		// but they are ignored because response was set above.
11002		// If it fails, this function gets "jqXHR", "status", "error"
11003		} ).always( callback && function( jqXHR, status ) {
11004			self.each( function() {
11005				callback.apply( this, response || [ jqXHR.responseText, status, jqXHR ] );
11006			} );
11007		} );
11008	}
11009
11010	return this;
11011};
11012
11013
11014
11015
11016jQuery.expr.pseudos.animated = function( elem ) {
11017	return jQuery.grep( jQuery.timers, function( fn ) {
11018		return elem === fn.elem;
11019	} ).length;
11020};
11021
11022
11023
11024
11025jQuery.offset = {
11026	setOffset: function( elem, options, i ) {
11027		var curPosition, curLeft, curCSSTop, curTop, curOffset, curCSSLeft, calculatePosition,
11028			position = jQuery.css( elem, "position" ),
11029			curElem = jQuery( elem ),
11030			props = {};
11031
11032		// Set position first, in-case top/left are set even on static elem
11033		if ( position === "static" ) {
11034			elem.style.position = "relative";
11035		}
11036
11037		curOffset = curElem.offset();
11038		curCSSTop = jQuery.css( elem, "top" );
11039		curCSSLeft = jQuery.css( elem, "left" );
11040		calculatePosition = ( position === "absolute" || position === "fixed" ) &&
11041			( curCSSTop + curCSSLeft ).indexOf( "auto" ) > -1;
11042
11043		// Need to be able to calculate position if either
11044		// top or left is auto and position is either absolute or fixed
11045		if ( calculatePosition ) {
11046			curPosition = curElem.position();
11047			curTop = curPosition.top;
11048			curLeft = curPosition.left;
11049
11050		} else {
11051			curTop = parseFloat( curCSSTop ) || 0;
11052			curLeft = parseFloat( curCSSLeft ) || 0;
11053		}
11054
11055		if ( isFunction( options ) ) {
11056
11057			// Use jQuery.extend here to allow modification of coordinates argument (gh-1848)
11058			options = options.call( elem, i, jQuery.extend( {}, curOffset ) );
11059		}
11060
11061		if ( options.top != null ) {
11062			props.top = ( options.top - curOffset.top ) + curTop;
11063		}
11064		if ( options.left != null ) {
11065			props.left = ( options.left - curOffset.left ) + curLeft;
11066		}
11067
11068		if ( "using" in options ) {
11069			options.using.call( elem, props );
11070
11071		} else {
11072			curElem.css( props );
11073		}
11074	}
11075};
11076
11077jQuery.fn.extend( {
11078
11079	// offset() relates an element's border box to the document origin
11080	offset: function( options ) {
11081
11082		// Preserve chaining for setter
11083		if ( arguments.length ) {
11084			return options === undefined ?
11085				this :
11086				this.each( function( i ) {
11087					jQuery.offset.setOffset( this, options, i );
11088				} );
11089		}
11090
11091		var rect, win,
11092			elem = this[ 0 ];
11093
11094		if ( !elem ) {
11095			return;
11096		}
11097
11098		// Return zeros for disconnected and hidden (display: none) elements (gh-2310)
11099		// Support: IE <=11 only
11100		// Running getBoundingClientRect on a
11101		// disconnected node in IE throws an error
11102		if ( !elem.getClientRects().length ) {
11103			return { top: 0, left: 0 };
11104		}
11105
11106		// Get document-relative position by adding viewport scroll to viewport-relative gBCR
11107		rect = elem.getBoundingClientRect();
11108		win = elem.ownerDocument.defaultView;
11109		return {
11110			top: rect.top + win.pageYOffset,
11111			left: rect.left + win.pageXOffset
11112		};
11113	},
11114
11115	// position() relates an element's margin box to its offset parent's padding box
11116	// This corresponds to the behavior of CSS absolute positioning
11117	position: function() {
11118		if ( !this[ 0 ] ) {
11119			return;
11120		}
11121
11122		var offsetParent, offset, doc,
11123			elem = this[ 0 ],
11124			parentOffset = { top: 0, left: 0 };
11125
11126		// position:fixed elements are offset from the viewport, which itself always has zero offset
11127		if ( jQuery.css( elem, "position" ) === "fixed" ) {
11128
11129			// Assume position:fixed implies availability of getBoundingClientRect
11130			offset = elem.getBoundingClientRect();
11131
11132		} else {
11133			offset = this.offset();
11134
11135			// Account for the *real* offset parent, which can be the document or its root element
11136			// when a statically positioned element is identified
11137			doc = elem.ownerDocument;
11138			offsetParent = elem.offsetParent || doc.documentElement;
11139			while ( offsetParent &&
11140				( offsetParent === doc.body || offsetParent === doc.documentElement ) &&
11141				jQuery.css( offsetParent, "position" ) === "static" ) {
11142
11143				offsetParent = offsetParent.parentNode;
11144			}
11145			if ( offsetParent && offsetParent !== elem && offsetParent.nodeType === 1 ) {
11146
11147				// Incorporate borders into its offset, since they are outside its content origin
11148				parentOffset = jQuery( offsetParent ).offset();
11149				parentOffset.top += jQuery.css( offsetParent, "borderTopWidth", true );
11150				parentOffset.left += jQuery.css( offsetParent, "borderLeftWidth", true );
11151			}
11152		}
11153
11154		// Subtract parent offsets and element margins
11155		return {
11156			top: offset.top - parentOffset.top - jQuery.css( elem, "marginTop", true ),
11157			left: offset.left - parentOffset.left - jQuery.css( elem, "marginLeft", true )
11158		};
11159	},
11160
11161	// This method will return documentElement in the following cases:
11162	// 1) For the element inside the iframe without offsetParent, this method will return
11163	//    documentElement of the parent window
11164	// 2) For the hidden or detached element
11165	// 3) For body or html element, i.e. in case of the html node - it will return itself
11166	//
11167	// but those exceptions were never presented as a real life use-cases
11168	// and might be considered as more preferable results.
11169	//
11170	// This logic, however, is not guaranteed and can change at any point in the future
11171	offsetParent: function() {
11172		return this.map( function() {
11173			var offsetParent = this.offsetParent;
11174
11175			while ( offsetParent && jQuery.css( offsetParent, "position" ) === "static" ) {
11176				offsetParent = offsetParent.offsetParent;
11177			}
11178
11179			return offsetParent || documentElement;
11180		} );
11181	}
11182} );
11183
11184// Create scrollLeft and scrollTop methods
11185jQuery.each( { scrollLeft: "pageXOffset", scrollTop: "pageYOffset" }, function( method, prop ) {
11186	var top = "pageYOffset" === prop;
11187
11188	jQuery.fn[ method ] = function( val ) {
11189		return access( this, function( elem, method, val ) {
11190
11191			// Coalesce documents and windows
11192			var win;
11193			if ( isWindow( elem ) ) {
11194				win = elem;
11195			} else if ( elem.nodeType === 9 ) {
11196				win = elem.defaultView;
11197			}
11198
11199			if ( val === undefined ) {
11200				return win ? win[ prop ] : elem[ method ];
11201			}
11202
11203			if ( win ) {
11204				win.scrollTo(
11205					!top ? val : win.pageXOffset,
11206					top ? val : win.pageYOffset
11207				);
11208
11209			} else {
11210				elem[ method ] = val;
11211			}
11212		}, method, val, arguments.length );
11213	};
11214} );
11215
11216// Support: Safari <=7 - 9.1, Chrome <=37 - 49
11217// Add the top/left cssHooks using jQuery.fn.position
11218// Webkit bug: https://bugs.webkit.org/show_bug.cgi?id=29084
11219// Blink bug: https://bugs.chromium.org/p/chromium/issues/detail?id=589347
11220// getComputedStyle returns percent when specified for top/left/bottom/right;
11221// rather than make the css module depend on the offset module, just check for it here
11222jQuery.each( [ "top", "left" ], function( _i, prop ) {
11223	jQuery.cssHooks[ prop ] = addGetHookIf( support.pixelPosition,
11224		function( elem, computed ) {
11225			if ( computed ) {
11226				computed = curCSS( elem, prop );
11227
11228				// If curCSS returns percentage, fallback to offset
11229				return rnumnonpx.test( computed ) ?
11230					jQuery( elem ).position()[ prop ] + "px" :
11231					computed;
11232			}
11233		}
11234	);
11235} );
11236
11237
11238// Create innerHeight, innerWidth, height, width, outerHeight and outerWidth methods
11239jQuery.each( { Height: "height", Width: "width" }, function( name, type ) {
11240	jQuery.each( {
11241		padding: "inner" + name,
11242		content: type,
11243		"": "outer" + name
11244	}, function( defaultExtra, funcName ) {
11245
11246		// Margin is only for outerHeight, outerWidth
11247		jQuery.fn[ funcName ] = function( margin, value ) {
11248			var chainable = arguments.length && ( defaultExtra || typeof margin !== "boolean" ),
11249				extra = defaultExtra || ( margin === true || value === true ? "margin" : "border" );
11250
11251			return access( this, function( elem, type, value ) {
11252				var doc;
11253
11254				if ( isWindow( elem ) ) {
11255
11256					// $( window ).outerWidth/Height return w/h including scrollbars (gh-1729)
11257					return funcName.indexOf( "outer" ) === 0 ?
11258						elem[ "inner" + name ] :
11259						elem.document.documentElement[ "client" + name ];
11260				}
11261
11262				// Get document width or height
11263				if ( elem.nodeType === 9 ) {
11264					doc = elem.documentElement;
11265
11266					// Either scroll[Width/Height] or offset[Width/Height] or client[Width/Height],
11267					// whichever is greatest
11268					return Math.max(
11269						elem.body[ "scroll" + name ], doc[ "scroll" + name ],
11270						elem.body[ "offset" + name ], doc[ "offset" + name ],
11271						doc[ "client" + name ]
11272					);
11273				}
11274
11275				return value === undefined ?
11276
11277					// Get width or height on the element, requesting but not forcing parseFloat
11278					jQuery.css( elem, type, extra ) :
11279
11280					// Set width or height on the element
11281					jQuery.style( elem, type, value, extra );
11282			}, type, chainable ? margin : undefined, chainable );
11283		};
11284	} );
11285} );
11286
11287
11288jQuery.each( [
11289	"ajaxStart",
11290	"ajaxStop",
11291	"ajaxComplete",
11292	"ajaxError",
11293	"ajaxSuccess",
11294	"ajaxSend"
11295], function( _i, type ) {
11296	jQuery.fn[ type ] = function( fn ) {
11297		return this.on( type, fn );
11298	};
11299} );
11300
11301
11302
11303
11304jQuery.fn.extend( {
11305
11306	bind: function( types, data, fn ) {
11307		return this.on( types, null, data, fn );
11308	},
11309	unbind: function( types, fn ) {
11310		return this.off( types, null, fn );
11311	},
11312
11313	delegate: function( selector, types, data, fn ) {
11314		return this.on( types, selector, data, fn );
11315	},
11316	undelegate: function( selector, types, fn ) {
11317
11318		// ( namespace ) or ( selector, types [, fn] )
11319		return arguments.length === 1 ?
11320			this.off( selector, "**" ) :
11321			this.off( types, selector || "**", fn );
11322	},
11323
11324	hover: function( fnOver, fnOut ) {
11325		return this
11326			.on( "mouseenter", fnOver )
11327			.on( "mouseleave", fnOut || fnOver );
11328	}
11329} );
11330
11331jQuery.each(
11332	( "blur focus focusin focusout resize scroll click dblclick " +
11333	"mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave " +
11334	"change select submit keydown keypress keyup contextmenu" ).split( " " ),
11335	function( _i, name ) {
11336
11337		// Handle event binding
11338		jQuery.fn[ name ] = function( data, fn ) {
11339			return arguments.length > 0 ?
11340				this.on( name, null, data, fn ) :
11341				this.trigger( name );
11342		};
11343	}
11344);
11345
11346
11347
11348
11349// Support: Android <=4.0 only
11350// Make sure we trim BOM and NBSP
11351// Require that the "whitespace run" starts from a non-whitespace
11352// to avoid O(N^2) behavior when the engine would try matching "\s+$" at each space position.
11353var rtrim = /^[\s\uFEFF\xA0]+|([^\s\uFEFF\xA0])[\s\uFEFF\xA0]+$/g;
11354
11355// Bind a function to a context, optionally partially applying any
11356// arguments.
11357// jQuery.proxy is deprecated to promote standards (specifically Function#bind)
11358// However, it is not slated for removal any time soon
11359jQuery.proxy = function( fn, context ) {
11360	var tmp, args, proxy;
11361
11362	if ( typeof context === "string" ) {
11363		tmp = fn[ context ];
11364		context = fn;
11365		fn = tmp;
11366	}
11367
11368	// Quick check to determine if target is callable, in the spec
11369	// this throws a TypeError, but we will just return undefined.
11370	if ( !isFunction( fn ) ) {
11371		return undefined;
11372	}
11373
11374	// Simulated bind
11375	args = slice.call( arguments, 2 );
11376	proxy = function() {
11377		return fn.apply( context || this, args.concat( slice.call( arguments ) ) );
11378	};
11379
11380	// Set the guid of unique handler to the same of original handler, so it can be removed
11381	proxy.guid = fn.guid = fn.guid || jQuery.guid++;
11382
11383	return proxy;
11384};
11385
11386jQuery.holdReady = function( hold ) {
11387	if ( hold ) {
11388		jQuery.readyWait++;
11389	} else {
11390		jQuery.ready( true );
11391	}
11392};
11393jQuery.isArray = Array.isArray;
11394jQuery.parseJSON = JSON.parse;
11395jQuery.nodeName = nodeName;
11396jQuery.isFunction = isFunction;
11397jQuery.isWindow = isWindow;
11398jQuery.camelCase = camelCase;
11399jQuery.type = toType;
11400
11401jQuery.now = Date.now;
11402
11403jQuery.isNumeric = function( obj ) {
11404
11405	// As of jQuery 3.0, isNumeric is limited to
11406	// strings and numbers (primitives or objects)
11407	// that can be coerced to finite numbers (gh-2662)
11408	var type = jQuery.type( obj );
11409	return ( type === "number" || type === "string" ) &&
11410
11411		// parseFloat NaNs numeric-cast false positives ("")
11412		// ...but misinterprets leading-number strings, particularly hex literals ("0x...")
11413		// subtraction forces infinities to NaN
11414		!isNaN( obj - parseFloat( obj ) );
11415};
11416
11417jQuery.trim = function( text ) {
11418	return text == null ?
11419		"" :
11420		( text + "" ).replace( rtrim, "$1" );
11421};
11422
11423
11424
11425// Register as a named AMD module, since jQuery can be concatenated with other
11426// files that may use define, but not via a proper concatenation script that
11427// understands anonymous AMD modules. A named AMD is safest and most robust
11428// way to register. Lowercase jquery is used because AMD module names are
11429// derived from file names, and jQuery is normally delivered in a lowercase
11430// file name. Do this after creating the global so that if an AMD module wants
11431// to call noConflict to hide this version of jQuery, it will work.
11432
11433// Note that for maximum portability, libraries that are not jQuery should
11434// declare themselves as anonymous modules, and avoid setting a global if an
11435// AMD loader is present. jQuery is a special case. For more information, see
11436// https://github.com/jrburke/requirejs/wiki/Updating-existing-libraries#wiki-anon
11437
11438if ( typeof define === "function" && define.amd ) {
11439	define( "jquery", [], function() {
11440		return jQuery;
11441	} );
11442}
11443
11444
11445
11446
11447var
11448
11449	// Map over jQuery in case of overwrite
11450	_jQuery = window.jQuery,
11451
11452	// Map over the $ in case of overwrite
11453	_$ = window.$;
11454
11455jQuery.noConflict = function( deep ) {
11456	if ( window.$ === jQuery ) {
11457		window.$ = _$;
11458	}
11459
11460	if ( deep && window.jQuery === jQuery ) {
11461		window.jQuery = _jQuery;
11462	}
11463
11464	return jQuery;
11465};
11466
11467// Expose jQuery and $ identifiers, even in AMD
11468// (trac-7102#comment:10, https://github.com/jquery/jquery/pull/557)
11469// and CommonJS for browser emulators (trac-13566)
11470if ( typeof noGlobal === "undefined" ) {
11471	window.jQuery = window.$ = jQuery;
11472}
11473
11474
11475
11476
11477return jQuery;
11478} );
11479/*
11479 jshint node: true */
11480
11481/**
11482 * Unobtrusive scripting adapter for jQuery
11483 * https://github.com/rails/jquery-ujs
11484 *
11485 * Requires jQuery 1.8.0 or later.
11486 *
11487 * Released under the MIT license
11488 *
11489 */
11490
11491(function() {
11492  'use strict';
11493
11494  var jqueryUjsInit = function($, undefined) {
11495
11496  // Cut down on the number of issues from people inadvertently including jquery_ujs twice
11497  // by detecting and raising an error when it happens.
11498  if ( $.rails !== undefined ) {
11499    $.error('jquery-ujs has already been loaded!');
11500  }
11501
11502  // Shorthand to make it a little easier to call public rails functions from within rails.js
11503  var rails;
11504  var $document = $(document);
11505
11506  $.rails = rails = {
11507    // Link elements bound by jquery-ujs
11508    linkClickSelector: 'a[data-confirm], a[data-method], a[data-remote]:not([disabled]), a[data-disable-with], a[data-disable]',
11509
11510    // Button elements bound by jquery-ujs
11511    buttonClickSelector: 'button[data-remote]:not([form]):not(form button), button[data-confirm]:not([form]):not(form button)',
11512
11513    // Select elements bound by jquery-ujs
11514    inputChangeSelector: 'select[data-remote], input[data-remote], textarea[data-remote]',
11515
11516    // Form elements bound by jquery-ujs
11517    formSubmitSelector: 'form:not([data-turbo=true])',
11518
11519    // Form input elements bound by jquery-ujs
11520    formInputClickSelector: 'form:not([data-turbo=true]) input[type=submit], form:not([data-turbo=true]) input[type=image], form:not([data-turbo=true]) button[type=submit], form:not([data-turbo=true]) button:not([type]), input[type=submit][form], input[type=image][form], button[type=submit][form], button[form]:not([type])',
11521
11522    // Form input elements disabled during form submission
11523    disableSelector: 'input[data-disable-with]:enabled, button[data-disable-with]:enabled, textarea[data-disable-with]:enabled, input[data-disable]:enabled, button[data-disable]:enabled, textarea[data-disable]:enabled',
11524
11525    // Form input elements re-enabled after form submission
11526    enableSelector: 'input[data-disable-with]:disabled, button[data-disable-with]:disabled, textarea[data-disable-with]:disabled, input[data-disable]:disabled, button[data-disable]:disabled, textarea[data-disable]:disabled',
11527
11528    // Form required input elements
11529    requiredInputSelector: 'input[name][required]:not([disabled]), textarea[name][required]:not([disabled])',
11530
11531    // Form file input elements
11532    fileInputSelector: 'input[name][type=file]:not([disabled])',
11533
11534    // Link onClick disable selector with possible reenable after remote submission
11535    linkDisableSelector: 'a[data-disable-with], a[data-disable]',
11536
11537    // Button onClick disable selector with possible reenable after remote submission
11538    buttonDisableSelector: 'button[data-remote][data-disable-with], button[data-remote][data-disable]',
11539
11540    // Up-to-date Cross-Site Request Forgery token
11541    csrfToken: function() {
11542     return $('meta[name=csrf-token]').attr('content');
11543    },
11544
11545    // URL param that must contain the CSRF token
11546    csrfParam: function() {
11547     return $('meta[name=csrf-param]').attr('content');
11548    },
11549
11550    // Make sure that every Ajax request sends the CSRF token
11551    CSRFProtection: function(xhr) {
11552      var token = rails.csrfToken();
11553      if (token) xhr.setRequestHeader('X-CSRF-Token', token);
11554    },
11555
11556    // Make sure that all forms have actual up-to-date tokens (cached forms contain old ones)
11557    refreshCSRFTokens: function(){
11558      $('form input[name="' + rails.csrfParam() + '"]').val(rails.csrfToken());
11559    },
11560
11561    // Triggers an event on an element and returns false if the event result is false
11562    fire: function(obj, name, data) {
11563      var event = $.Event(name);
11564      obj.trigger(event, data);
11565      return event.result !== false;
11566    },
11567
11568    // Default confirm dialog, may be overridden with custom confirm dialog in $.rails.confirm
11569    confirm: function(message) {
11570      return confirm(message);
11571    },
11572
11573    // Default ajax function, may be overridden with custom function in $.rails.ajax
11574    ajax: function(options) {
11575      return $.ajax(options);
11576    },
11577
11578    // Default way to get an element's href. May be overridden at $.rails.href.
11579    href: function(element) {
11580      return element[0].href;
11581    },
11582
11583    // Checks "data-remote" if true to handle the request through a XHR request.
11584    isRemote: function(element) {
11585      return element.data('remote') !== undefined && element.data('remote') !== false;
11586    },
11587
11588    // Submits "remote" forms and links with ajax
11589    handleRemote: function(element) {
11590      var method, url, data, withCredentials, dataType, options;
11591
11592      if (rails.fire(element, 'ajax:before')) {
11593        withCredentials = element.data('with-credentials') || null;
11594        dataType = element.data('type') || ($.ajaxSettings && $.ajaxSettings.dataType);
11595
11596        if (element.is('form')) {
11597          method = element.data('ujs:submit-button-formmethod') || element.attr('method');
11598          url = element.data('ujs:submit-button-formaction') || element.attr('action');
11599          data = $(element[0]).serializeArray();
11600          // memoized value from clicked submit button
11601          var button = element.data('ujs:submit-button');
11602          if (button) {
11603            data.push(button);
11604            element.data('ujs:submit-button', null);
11605          }
11606          element.data('ujs:submit-button-formmethod', null);
11607          element.data('ujs:submit-button-formaction', null);
11608        } else if (element.is(rails.inputChangeSelector)) {
11609          method = element.data('method');
11610          url = element.data('url');
11611          data = element.serialize();
11612          if (element.data('params')) data = data + '&' + element.data('params');
11613        } else if (element.is(rails.buttonClickSelector)) {
11614          method = element.data('method') || 'get';
11615          url = element.data('url');
11616          data = element.serialize();
11617          if (element.data('params')) data = data + '&' + element.data('params');
11618        } else {
11619          method = element.data('method');
11620          url = rails.href(element);
11621          data = element.data('params') || null;
11622        }
11623
11624        options = {
11625          type: method || 'GET', data: data, dataType: dataType,
11626          // stopping the "ajax:beforeSend" event will cancel the ajax request
11627          beforeSend: function(xhr, settings) {
11628            if (settings.dataType === undefined) {
11629              xhr.setRequestHeader('accept', '*/*;q=0.5, ' + settings.accepts.script);
11630            }
11631            if (rails.fire(element, 'ajax:beforeSend', [xhr, settings])) {
11632              element.trigger('ajax:send', xhr);
11633            } else {
11634              return false;
11635            }
11636          },
11637          success: function(data, status, xhr) {
11638            element.trigger('ajax:success', [data, status, xhr]);
11639          },
11640          complete: function(xhr, status) {
11641            element.trigger('ajax:complete', [xhr, status]);
11642          },
11643          error: function(xhr, status, error) {
11644            element.trigger('ajax:error', [xhr, status, error]);
11645          },
11646          crossDomain: rails.isCrossDomain(url)
11647        };
11648
11649        // There is no withCredentials for IE6-8 when
11650        // "Enable native XMLHTTP support" is disabled
11651        if (withCredentials) {
11652          options.xhrFields = {
11653            withCredentials: withCredentials
11654          };
11655        }
11656
11657        // Only pass url to `ajax` options if not blank
11658        if (url) { options.url = url; }
11659
11660        return rails.ajax(options);
11661      } else {
11662        return false;
11663      }
11664    },
11665
11666    // Determines if the request is a cross domain request.
11667    isCrossDomain: function(url) {
11668      var originAnchor = document.createElement('a');
11669      originAnchor.href = location.href;
11670      var urlAnchor = document.createElement('a');
11671
11672      try {
11673        urlAnchor.href = url;
11674        // This is a workaround to a IE bug.
11675        urlAnchor.href = urlAnchor.href;
11676
11677        // If URL protocol is false or is a string containing a single colon
11678        // *and* host are false, assume it is not a cross-domain request
11679        // (should only be the case for IE7 and IE compatibility mode).
11680        // Otherwise, evaluate protocol and host of the URL against the origin
11681        // protocol and host.
11682        return !(((!urlAnchor.protocol || urlAnchor.protocol === ':') && !urlAnchor.host) ||
11683          (originAnchor.protocol + '//' + originAnchor.host ===
11684            urlAnchor.protocol + '//' + urlAnchor.host));
11685      } catch (e) {
11686        // If there is an error parsing the URL, assume it is crossDomain.
11687        return true;
11688      }
11689    },
11690
11691    // Handles "data-method" on links such as:
11692    // <a href="/users/5" data-method="delete" rel="nofollow" data-confirm="Are you sure?">Delete</a>
11693    handleMethod: function(link) {
11694      var href = rails.href(link),
11695        method = link.data('method'),
11696        target = link.attr('target'),
11697        csrfToken = rails.csrfToken(),
11698        csrfParam = rails.csrfParam(),
11699        form = $('<form method="post" action="' + href + '"></form>'),
11700        metadataInput = '<input name="_method" value="' + method + '" type="hidden" />';
11701
11702      if (csrfParam !== undefined && csrfToken !== undefined && !rails.isCrossDomain(href)) {
11703        metadataInput += '<input name="' + csrfParam + '" value="' + csrfToken + '" type="hidden" />';
11704      }
11705
11706      if (target) { form.attr('target', target); }
11707
11708      form.hide().append(metadataInput).appendTo('body');
11709      form.submit();
11710    },
11711
11712    // Helper function that returns form elements that match the specified CSS selector
11713    // If form is actually a "form" element this will return associated elements outside the from that have
11714    // the html form attribute set
11715    formElements: function(form, selector) {
11716      return form.is('form') ? $(form[0].elements).filter(selector) : form.find(selector);
11717    },
11718
11719    /* Disables form elements:
11720      - Caches element value in 'ujs:enable-with' data store
11721      - Replaces element text with value of 'data-disable-with' attribute
11722      - Sets disabled property to true
11723    */
11724    disableFormElements: function(form) {
11725      rails.formElements(form, rails.disableSelector).each(function() {
11726        rails.disableFormElement($(this));
11727      });
11728    },
11729
11730    disableFormElement: function(element) {
11731      var method, replacement;
11732
11733      method = element.is('button') ? 'html' : 'val';
11734      replacement = element.data('disable-with');
11735
11736      if (replacement !== undefined) {
11737        element.data('ujs:enable-with', element[method]());
11738        element[method](replacement);
11739      }
11740
11741      element.prop('disabled', true);
11742      element.data('ujs:disabled', true);
11743    },
11744
11745    /* Re-enables disabled form elements:
11746      - Replaces element text with cached value from 'ujs:enable-with' data store (created in `disableFormElements`)
11747      - Sets disabled property to false
11748    */
11749    enableFormElements: function(form) {
11750      rails.formElements(form, rails.enableSelector).each(function() {
11751        rails.enableFormElement($(this));
11752      });
11753    },
11754
11755    enableFormElement: function(element) {
11756      var method = element.is('button') ? 'html' : 'val';
11757      if (element.data('ujs:enable-with') !== undefined) {
11758        element[method](element.data('ujs:enable-with'));
11759        element.removeData('ujs:enable-with'); // clean up cache
11760      }
11761      element.prop('disabled', false);
11762      element.removeData('ujs:disabled');
11763    },
11764
11765   /* For 'data-confirm' attribute:
11766      - Fires `confirm` event
11767      - Shows the confirmation dialog
11768      - Fires the `confirm:complete` event
11769
11770      Returns `true` if no function stops the chain and user chose yes; `false` otherwise.
11771      Attaching a handler to the element's `confirm` event that returns a `falsy` value cancels the confirmation dialog.
11772      Attaching a handler to the element's `confirm:complete` event that returns a `falsy` value makes this function
11773      return false. The `confirm:complete` event is fired whether or not the user answered true or false to the dialog.
11774   */
11775    allowAction: function(element) {
11776      var message = element.data('confirm'),
11777          answer = false, callback;
11778      if (!message) { return true; }
11779
11780      if (rails.fire(element, 'confirm')) {
11781        try {
11782          answer = rails.confirm(message);
11783        } catch (e) {
11784          (console.error || console.log).call(console, e.stack || e);
11785        }
11786        callback = rails.fire(element, 'confirm:complete', [answer]);
11787      }
11788      return answer && callback;
11789    },
11790
11791    // Helper function which checks for blank inputs in a form that match the specified CSS selector
11792    blankInputs: function(form, specifiedSelector, nonBlank) {
11793      var foundInputs = $(),
11794        input,
11795        valueToCheck,
11796        radiosForNameWithNoneSelected,
11797        radioName,
11798        selector = specifiedSelector || 'input,textarea',
11799        requiredInputs = form.find(selector),
11800        checkedRadioButtonNames = {};
11801
11802      requiredInputs.each(function() {
11803        input = $(this);
11804        if (input.is('input[type=radio]')) {
11805
11806          // Don't count unchecked required radio as blank if other radio with same name is checked,
11807          // regardless of whether same-name radio input has required attribute or not. The spec
11808          // states https://www.w3.org/TR/html5/forms.html#the-required-attribute
11809          radioName = input.attr('name');
11810
11811          // Skip if we've already seen the radio with this name.
11812          if (!checkedRadioButtonNames[radioName]) {
11813
11814            // If none checked
11815            if (form.find('input[type=radio]:checked[name="' + radioName + '"]').length === 0) {
11816              radiosForNameWithNoneSelected = form.find(
11817                'input[type=radio][name="' + radioName + '"]');
11818              foundInputs = foundInputs.add(radiosForNameWithNoneSelected);
11819            }
11820
11821            // We only need to check each name once.
11822            checkedRadioButtonNames[radioName] = radioName;
11823          }
11824        } else {
11825          valueToCheck = input.is('input[type=checkbox],input[type=radio]') ? input.is(':checked') : !!input.val();
11826          if (valueToCheck === nonBlank) {
11827            foundInputs = foundInputs.add(input);
11828          }
11829        }
11830      });
11831      return foundInputs.length ? foundInputs : false;
11832    },
11833
11834    // Helper function which checks for non-blank inputs in a form that match the specified CSS selector
11835    nonBlankInputs: function(form, specifiedSelector) {
11836      return rails.blankInputs(form, specifiedSelector, true); // true specifies nonBlank
11837    },
11838
11839    // Helper function, needed to provide consistent behavior in IE
11840    stopEverything: function(e) {
11841      $(e.target).trigger('ujs:everythingStopped');
11842      e.stopImmediatePropagation();
11843      return false;
11844    },
11845
11846    //  Replace element's html with the 'data-disable-with' after storing original html
11847    //  and prevent clicking on it
11848    disableElement: function(element) {
11849      var replacement = element.data('disable-with');
11850
11851      if (replacement !== undefined) {
11852        element.data('ujs:enable-with', element.html()); // store enabled state
11853        element.html(replacement);
11854      }
11855
11856      element.on('click.railsDisable', function(e) { // prevent further clicking
11857        return rails.stopEverything(e);
11858      });
11859      element.data('ujs:disabled', true);
11860    },
11861
11862    // Restore element to its original state which was disabled by 'disableElement' above
11863    enableElement: function(element) {
11864      if (element.data('ujs:enable-with') !== undefined) {
11865        element.html(element.data('ujs:enable-with')); // set to old enabled state
11866        element.removeData('ujs:enable-with'); // clean up cache
11867      }
11868      element.off('click.railsDisable'); // enable element
11869      element.removeData('ujs:disabled');
11870    }
11871  };
11872
11873  if (rails.fire($document, 'rails:attachBindings')) {
11874
11875    $.ajaxPrefilter(function(options, originalOptions, xhr){ if ( !options.crossDomain ) { rails.CSRFProtection(xhr); }});
11876
11877    // This event works the same as the load event, except that it fires every
11878    // time the page is loaded.
11879    //
11880    // See https://github.com/rails/jquery-ujs/issues/357
11881    // See https://developer.mozilla.org/en-US/docs/Using_Firefox_1.5_caching
11882    $(window).on('pageshow.rails', function () {
11883      $($.rails.enableSelector).each(function () {
11884        var element = $(this);
11885
11886        if (element.data('ujs:disabled')) {
11887          $.rails.enableFormElement(element);
11888        }
11889      });
11890
11891      $($.rails.linkDisableSelector).each(function () {
11892        var element = $(this);
11893
11894        if (element.data('ujs:disabled')) {
11895          $.rails.enableElement(element);
11896        }
11897      });
11898    });
11899
11900    $document.on('ajax:complete', rails.linkDisableSelector, function() {
11901        rails.enableElement($(this));
11902    });
11903
11904    $document.on('ajax:complete', rails.buttonDisableSelector, function() {
11905        rails.enableFormElement($(this));
11906    });
11907
11908    $document.on('click.rails', rails.linkClickSelector, function(e) {
11909      var link = $(this), method = link.data('method'), data = link.data('params'), metaClick = e.metaKey || e.ctrlKey;
11910      if (!rails.allowAction(link)) return rails.stopEverything(e);
11911
11912      if (!metaClick && link.is(rails.linkDisableSelector)) rails.disableElement(link);
11913
11914      if (rails.isRemote(link)) {
11915        if (metaClick && (!method || method === 'GET') && !data) { return true; }
11916
11917        var handleRemote = rails.handleRemote(link);
11918        // Response from rails.handleRemote() will either be false or a deferred object promise.
11919        if (handleRemote === false) {
11920          rails.enableElement(link);
11921        } else {
11922          handleRemote.fail( function() { rails.enableElement(link); } );
11923        }
11924        return false;
11925
11926      } else if (method) {
11927        rails.handleMethod(link);
11928        return false;
11929      }
11930    });
11931
11932    $document.on('click.rails', rails.buttonClickSelector, function(e) {
11933      var button = $(this);
11934
11935      if (!rails.allowAction(button) || !rails.isRemote(button)) return rails.stopEverything(e);
11936
11937      if (button.is(rails.buttonDisableSelector)) rails.disableFormElement(button);
11938
11939      var handleRemote = rails.handleRemote(button);
11940      // Response from rails.handleRemote() will either be false or a deferred object promise.
11941      if (handleRemote === false) {
11942        rails.enableFormElement(button);
11943      } else {
11944        handleRemote.fail( function() { rails.enableFormElement(button); } );
11945      }
11946      return false;
11947    });
11948
11949    $document.on('change.rails', rails.inputChangeSelector, function(e) {
11950      var link = $(this);
11951      if (!rails.allowAction(link) || !rails.isRemote(link)) return rails.stopEverything(e);
11952
11953      rails.handleRemote(link);
11954      return false;
11955    });
11956
11957    $document.on('submit.rails', rails.formSubmitSelector, function(e) {
11958      var form = $(this),
11959        remote = rails.isRemote(form),
11960        blankRequiredInputs,
11961        nonBlankFileInputs;
11962
11963      if (!rails.allowAction(form)) return rails.stopEverything(e);
11964
11965      // Skip other logic when required values are missing or file upload is present
11966      if (form.attr('novalidate') === undefined) {
11967        if (form.data('ujs:formnovalidate-button') === undefined) {
11968          blankRequiredInputs = rails.blankInputs(form, rails.requiredInputSelector, false);
11969          if (blankRequiredInputs && rails.fire(form, 'ajax:aborted:required', [blankRequiredInputs])) {
11970            return rails.stopEverything(e);
11971          }
11972        } else {
11973          // Clear the formnovalidate in case the next button click is not on a formnovalidate button
11974          // Not strictly necessary to do here, since it is also reset on each button click, but just to be certain
11975          form.data('ujs:formnovalidate-button', undefined);
11976        }
11977      }
11978
11979      if (remote) {
11980        nonBlankFileInputs = rails.nonBlankInputs(form, rails.fileInputSelector);
11981        if (nonBlankFileInputs) {
11982          // Slight timeout so that the submit button gets properly serialized
11983          // (make it easy for event handler to serialize form without disabled values)
11984          setTimeout(function(){ rails.disableFormElements(form); }, 13);
11985          var aborted = rails.fire(form, 'ajax:aborted:file', [nonBlankFileInputs]);
11986
11987          // Re-enable form elements if event bindings return false (canceling normal form submission)
11988          if (!aborted) { setTimeout(function(){ rails.enableFormElements(form); }, 13); }
11989
11990          return aborted;
11991        }
11992
11993        rails.handleRemote(form);
11994        return false;
11995
11996      } else {
11997        // Slight timeout so that the submit button gets properly serialized
11998        setTimeout(function(){ rails.disableFormElements(form); }, 13);
11999      }
12000    });
12001
12002    $document.on('click.rails', rails.formInputClickSelector, function(event) {
12003      var button = $(this);
12004
12005      if (!rails.allowAction(button)) return rails.stopEverything(event);
12006
12007      // Register the pressed submit button
12008      var name = button.attr('name'),
12009        data = name ? {name:name, value:button.val()} : null;
12010
12011      var form = button.closest('form');
12012      if (form.length === 0) {
12013        form = $('#' + button.attr('form'));
12014      }
12015      form.data('ujs:submit-button', data);
12016
12017      // Save attributes from button
12018      form.data('ujs:formnovalidate-button', button.attr('formnovalidate'));
12019      form.data('ujs:submit-button-formaction', button.attr('formaction'));
12020      form.data('ujs:submit-button-formmethod', button.attr('formmethod'));
12021    });
12022
12023    $document.on('ajax:send.rails', rails.formSubmitSelector, function(event) {
12024      if (this === event.target) rails.disableFormElements($(this));
12025    });
12026
12027    $document.on('ajax:complete.rails', rails.formSubmitSelector, function(event) {
12028      if (this === event.target) rails.enableFormElements($(this));
12029    });
12030
12031    $(function(){
12032      rails.refreshCSRFTokens();
12033    });
12034  }
12035
12036  };
12037
12038  if (window.jQuery) {
12039    jqueryUjsInit(jQuery);
12040  } else if (typeof exports === 'object' && typeof module === 'object') {
12041    module.exports = jqueryUjsInit;
12042  }
12043})();
vendor: 20,791 bytes, lines 12044-12047
12044/*! jQuery Validation Plugin - v1.19.4 - 5/19/2022
12045 * https://jqueryvalidation.org/
12046 * Copyright (c) 2022 Jörn Zaefferer; Licensed MIT */
12047!function(a){"function"==typeof define&&define.amd?define(["jquery"],a):"object"==typeof module&&module.exports?module.exports=a(require("jquery")):a(jQuery)}(function(a){a.extend(a.fn,{validate:function(b){if(!this.length)return void(b&&b.debug&&window.console&&console.warn("Nothing selected, can't validate, returning nothing."));var c=a.data(this[0],"validator");return c?c:(this.attr("novalidate","novalidate"),c=new a.validator(b,this[0]),a.data(this[0],"validator",c),c.settings.onsubmit&&(this.on("click.validate",":submit",function(b){c.submitButton=b.currentTarget,a(this).hasClass("cancel")&&(c.cancelSubmit=!0),void 0!==a(this).attr("formnovalidate")&&(c.cancelSubmit=!0)}),this.on("submit.validate",function(b){function d(){var d,e;return c.submitButton&&(c.settings.submitHandler||c.formSubmitted)&&(d=a("<input type='hidden'/>").attr("name",c.submitButton.name).val(a(c.submitButton).val()).appendTo(c.currentForm)),!(c.settings.submitHandler&&!c.settings.debug)||(e=c.settings.submitHandler.call(c,c.currentForm,b),d&&d.remove(),void 0!==e&&e)}return c.settings.debug&&b.preventDefault(),c.cancelSubmit?(c.cancelSubmit=!1,d()):c.form()?c.pendingRequest?(c.formSubmitted=!0,!1):d():(c.focusInvalid(),!1)})),c)},valid:function(){var b,c,d;return a(this[0]).is("form")?b=this.validate().form():(d=[],b=!0,c=a(this[0].form).validate(),this.each(function(){b=c.element(this)&&b,b||(d=d.concat(c.errorList))}),c.errorList=d),b},rules:function(b,c){var d,e,f,g,h,i,j=this[0],k="undefined"!=typeof this.attr("contenteditable")&&"false"!==this.attr("contenteditable");if(null!=j&&(!j.form&&k&&(j.form=this.closest("form")[0],j.name=this.attr("name")),null!=j.form)){if(b)switch(d=a.data(j.form,"validator").settings,e=d.rules,f=a.validator.staticRules(j),b){case"add":a.extend(f,a.validator.normalizeRule(c)),delete f.messages,e[j.name]=f,c.messages&&(d.messages[j.name]=a.extend(d.messages[j.name],c.messages));break;case"remove":return c?(i={},a.each(c.split(/\s/),function(a,b){i[b]=f[b],delete f[b]}),i):(delete e[j.name],f)}return g=a.validator.normalizeRules(a.extend({},a.validator.classRules(j),a.validator.attributeRules(j),a.validator.dataRules(j),a.validator.staticRules(j)),j),g.required&&(h=g.required,delete g.required,g=a.extend({required:h},g)),g.remote&&(h=g.remote,delete g.remote,g=a.extend(g,{remote:h})),g}}});var b=function(a){return a.replace(/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,"")};a.extend(a.expr.pseudos||a.expr[":"],{blank:function(c){return!b(""+a(c).val())},filled:function(c){var d=a(c).val();return null!==d&&!!b(""+d)},unchecked:function(b){return!a(b).prop("checked")}}),a.validator=function(b,c){this.settings=a.extend(!0,{},a.validator.defaults,b),this.currentForm=c,this.init()},a.validator.format=function(b,c){return 1===arguments.length?function(){var c=a.makeArray(arguments);return c.unshift(b),a.validator.format.apply(this,c)}:void 0===c?b:(arguments.length>2&&c.constructor!==Array&&(c=a.makeArray(arguments).slice(1)),c.constructor!==Array&&(c=[c]),a.each(c,function(a,c){b=b.replace(new RegExp("\\{"+a+"\\}","g"),function(){return c})}),b)},a.extend(a.validator,{defaults:{messages:{},groups:{},rules:{},errorClass:"error",pendingClass:"pending",validClass:"valid",errorElement:"label",focusCleanup:!1,focusInvalid:!0,errorContainer:a([]),errorLabelContainer:a([]),onsubmit:!0,ignore:":hidden",ignoreTitle:!1,onfocusin:function(a){this.lastActive=a,this.settings.focusCleanup&&(this.settings.unhighlight&&this.settings.unhighlight.call(this,a,this.settings.errorClass,this.settings.validClass),this.hideThese(this.errorsFor(a)))},onfocusout:function(a){this.checkable(a)||!(a.name in this.submitted)&&this.optional(a)||this.element(a)},onkeyup:function(b,c){var d=[16,17,18,20,35,36,37,38,39,40,45,144,225];9===c.which&&""===this.elementValue(b)||a.inArray(c.keyCode,d)!==-1||(b.name in this.submitted||b.name in this.invalid)&&this.element(b)},onclick:function(a){a.name in this.submitted?this.element(a):a.parentNode.name in this.submitted&&this.element(a.parentNode)},highlight:function(b,c,d){"radio"===b.type?this.findByName(b.name).addClass(c).removeClass(d):a(b).addClass(c).removeClass(d)},unhighlight:function(b,c,d){"radio"===b.type?this.findByName(b.name).removeClass(c).addClass(d):a(b).removeClass(c).addClass(d)}},setDefaults:function(b){a.extend(a.validator.defaults,b)},messages:{required:"This field is required.",remote:"Please fix this field.",email:"Please enter a valid email address.",url:"Please enter a valid URL.",date:"Please enter a valid date.",dateISO:"Please enter a valid date (ISO).",number:"Please enter a valid number.",digits:"Please enter only digits.",equalTo:"Please enter the same value again.",maxlength:a.validator.format("Please enter no more than {0} characters."),minlength:a.validator.format("Please enter at least {0} characters."),rangelength:a.validator.format("Please enter a value between {0} and {1} characters long."),range:a.validator.format("Please enter a value between {0} and {1}."),max:a.validator.format("Please enter a value less than or equal to {0}."),min:a.validator.format("Please enter a value greater than or equal to {0}."),step:a.validator.format("Please enter a multiple of {0}.")},autoCreateRanges:!1,prototype:{init:function(){function b(b){var c="undefined"!=typeof a(this).attr("contenteditable")&&"false"!==a(this).attr("contenteditable");if(!this.form&&c&&(this.form=a(this).closest("form")[0],this.name=a(this).attr("name")),d===this.form){var e=a.data(this.form,"validator"),f="on"+b.type.replace(/^validate/,""),g=e.settings;g[f]&&!a(this).is(g.ignore)&&g[f].call(e,this,b)}}this.labelContainer=a(this.settings.errorLabelContainer),this.errorContext=this.labelContainer.length&&this.labelContainer||a(this.currentForm),this.containers=a(this.settings.errorContainer).add(this.settings.errorLabelContainer),this.submitted={},this.valueCache={},this.pendingRequest=0,this.pending={},this.invalid={},this.reset();var c,d=this.currentForm,e=this.groups={};a.each(this.settings.groups,function(b,c){"string"==typeof c&&(c=c.split(/\s/)),a.each(c,function(a,c){e[c]=b})}),c=this.settings.rules,a.each(c,function(b,d){c[b]=a.validator.normalizeRule(d)}),a(this.currentForm).on("focusin.validate focusout.validate keyup.validate",":text, [type='password'], [type='file'], select, textarea, [type='number'], [type='search'], [type='tel'], [type='url'], [type='email'], [type='datetime'], [type='date'], [type='month'], [type='week'], [type='time'], [type='datetime-local'], [type='range'], [type='color'], [type='radio'], [type='checkbox'], [contenteditable], [type='button']",b).on("click.validate","select, option, [type='radio'], [type='checkbox']",b),this.settings.invalidHandler&&a(this.currentForm).on("invalid-form.validate",this.settings.invalidHandler)},form:function(){return this.checkForm(),a.extend(this.submitted,this.errorMap),this.invalid=a.extend({},this.errorMap),this.valid()||a(this.currentForm).triggerHandler("invalid-form",[this]),this.showErrors(),this.valid()},checkForm:function(){this.prepareForm();for(var a=0,b=this.currentElements=this.elements();b[a];a++)this.check(b[a]);return this.valid()},element:function(b){var c,d,e=this.clean(b),f=this.validationTargetFor(e),g=this,h=!0;return void 0===f?delete this.invalid[e.name]:(this.prepareElement(f),this.currentElements=a(f),d=this.groups[f.name],d&&a.each(this.groups,function(a,b){b===d&&a!==f.name&&(e=g.validationTargetFor(g.clean(g.findByName(a))),e&&e.name in g.invalid&&(g.currentElements.push(e),h=g.check(e)&&h))}),c=this.check(f)!==!1,h=h&&c,c?this.invalid[f.name]=!1:this.invalid[f.name]=!0,this.numberOfInvalids()||(this.toHide=this.toHide.add(this.containers)),this.showErrors(),a(b).attr("aria-invalid",!c)),h},showErrors:function(b){if(b){var c=this;a.extend(this.errorMap,b),this.errorList=a.map(this.errorMap,function(a,b){return{message:a,element:c.findByName(b)[0]}}),this.successList=a.grep(this.successList,function(a){return!(a.name in b)})}this.settings.showErrors?this.settings.showErrors.call(this,this.errorMap,this.errorList):this.defaultShowErrors()},resetForm:function(){a.fn.resetForm&&a(this.currentForm).resetForm(),this.invalid={},this.submitted={},this.prepareForm(),this.hideErrors();var b=this.elements().removeData("previousValue").removeAttr("aria-invalid");this.resetElements(b)},resetElements:function(a){var b;if(this.settings.unhighlight)for(b=0;a[b];b++)this.settings.unhighlight.call(this,a[b],this.settings.errorClass,""),this.findByName(a[b].name).removeClass(this.settings.validClass);else a.removeClass(this.settings.errorClass).removeClass(this.settings.validClass)},numberOfInvalids:function(){return this.objectLength(this.invalid)},objectLength:function(a){var b,c=0;for(b in a)void 0!==a[b]&&null!==a[b]&&a[b]!==!1&&c++;return c},hideErrors:function(){this.hideThese(this.toHide)},hideThese:function(a){a.not(this.containers).text(""),this.addWrapper(a).hide()},valid:function(){return 0===this.size()},size:function(){return this.errorList.length},focusInvalid:function(){if(this.settings.focusInvalid)try{a(this.findLastActive()||this.errorList.length&&this.errorList[0].element||[]).filter(":visible").trigger("focus").trigger("focusin")}catch(b){}},findLastActive:function(){var b=this.lastActive;return b&&1===a.grep(this.errorList,function(a){return a.element.name===b.name}).length&&b},elements:function(){var b=this,c={};return a(this.currentForm).find("input, select, textarea, [contenteditable]").not(":submit, :reset, :image, :disabled").not(this.settings.ignore).filter(function(){var d=this.name||a(this).attr("name"),e="undefined"!=typeof a(this).attr("contenteditable")&&"false"!==a(this).attr("contenteditable");return!d&&b.settings.debug&&window.console&&console.error("%o has no name assigned",this),e&&(this.form=a(this).closest("form")[0],this.name=d),this.form===b.currentForm&&(!(d in c||!b.objectLength(a(this).rules()))&&(c[d]=!0,!0))})},clean:function(b){return a(b)[0]},errors:function(){var b=this.settings.errorClass.split(" ").join(".");return a(this.settings.errorElement+"."+b,this.errorContext)},resetInternals:function(){this.successList=[],this.errorList=[],this.errorMap={},this.toShow=a([]),this.toHide=a([])},reset:function(){this.resetInternals(),this.currentElements=a([])},prepareForm:function(){this.reset(),this.toHide=this.errors().add(this.containers)},prepareElement:function(a){this.reset(),this.toHide=this.errorsFor(a)},elementValue:function(b){var c,d,e=a(b),f=b.type,g="undefined"!=typeof e.attr("contenteditable")&&"false"!==e.attr("contenteditable");return"radio"===f||"checkbox"===f?this.findByName(b.name).filter(":checked").val():"number"===f&&"undefined"!=typeof b.validity?b.validity.badInput?"NaN":e.val():(c=g?e.text():e.val(),"file"===f?"C:\\fakepath\\"===c.substr(0,12)?c.substr(12):(d=c.lastIndexOf("/"),d>=0?c.substr(d+1):(d=c.lastIndexOf("\\"),d>=0?c.substr(d+1):c)):"string"==typeof c?c.replace(/\r/g,""):c)},check:function(b){b=this.validationTargetFor(this.clean(b));var c,d,e,f,g=a(b).rules(),h=a.map(g,function(a,b){return b}).length,i=!1,j=this.elementValue(b);"function"==typeof g.normalizer?f=g.normalizer:"function"==typeof this.settings.normalizer&&(f=this.settings.normalizer),f&&(j=f.call(b,j),delete g.normalizer);for(d in g){e={method:d,parameters:g[d]};try{if(c=a.validator.methods[d].call(this,j,b,e.parameters),"dependency-mismatch"===c&&1===h){i=!0;continue}if(i=!1,"pending"===c)return void(this.toHide=this.toHide.not(this.errorsFor(b)));if(!c)return this.formatAndAdd(b,e),!1}catch(k){throw this.settings.debug&&window.console&&console.log("Exception occurred when checking element "+b.id+", check the '"+e.method+"' method.",k),k instanceof TypeError&&(k.message+=".  Exception occurred when checking element "+b.id+", check the '"+e.method+"' method."),k}}if(!i)return this.objectLength(g)&&this.successList.push(b),!0},customDataMessage:function(b,c){return a(b).data("msg"+c.charAt(0).toUpperCase()+c.substring(1).toLowerCase())||a(b).data("msg")},customMessage:function(a,b){var c=this.settings.messages[a];return c&&(c.constructor===String?c:c[b])},findDefined:function(){for(var a=0;a<arguments.length;a++)if(void 0!==arguments[a])return arguments[a]},defaultMessage:function(b,c){"string"==typeof c&&(c={method:c});var d=this.findDefined(this.customMessage(b.name,c.method),this.customDataMessage(b,c.method),!this.settings.ignoreTitle&&b.title||void 0,a.validator.messages[c.method],"<strong>Warning: No message defined for "+b.name+"</strong>"),e=/\$?\{(\d+)\}/g;return"function"==typeof d?d=d.call(this,c.parameters,b):e.test(d)&&(d=a.validator.format(d.replace(e,"{$1}"),c.parameters)),d},formatAndAdd:function(a,b){var c=this.defaultMessage(a,b);this.errorList.push({message:c,element:a,method:b.method}),this.errorMap[a.name]=c,this.submitted[a.name]=c},addWrapper:function(a){return this.settings.wrapper&&(a=a.add(a.parent(this.settings.wrapper))),a},defaultShowErrors:function(){var a,b,c;for(a=0;this.errorList[a];a++)c=this.errorList[a],this.settings.highlight&&this.settings.highlight.call(this,c.element,this.settings.errorClass,this.settings.validClass),this.showLabel(c.element,c.message);if(this.errorList.length&&(this.toShow=this.toShow.add(this.containers)),this.settings.success)for(a=0;this.successList[a];a++)this.showLabel(this.successList[a]);if(this.settings.unhighlight)for(a=0,b=this.validElements();b[a];a++)this.settings.unhighlight.call(this,b[a],this.settings.errorClass,this.settings.validClass);this.toHide=this.toHide.not(this.toShow),this.hideErrors(),this.addWrapper(this.toShow).show()},validElements:function(){return this.currentElements.not(this.invalidElements())},invalidElements:function(){return a(this.errorList).map(function(){return this.element})},showLabel:function(b,c){var d,e,f,g,h=this.errorsFor(b),i=this.idOrName(b),j=a(b).attr("aria-describedby");h.length?(h.removeClass(this.settings.validClass).addClass(this.settings.errorClass),h.html(c)):(h=a("<"+this.settings.errorElement+">").attr("id",i+"-error").addClass(this.settings.errorClass).html(c||""),d=h,this.settings.wrapper&&(d=h.hide().show().wrap("<"+this.settings.wrapper+"/>").parent()),this.labelContainer.length?this.labelContainer.append(d):this.settings.errorPlacement?this.settings.errorPlacement.call(this,d,a(b)):d.insertAfter(b),h.is("label")?h.attr("for",i):0===h.parents("label[for='"+this.escapeCssMeta(i)+"']").length&&(f=h.attr("id"),j?j.match(new RegExp("\\b"+this.escapeCssMeta(f)+"\\b"))||(j+=" "+f):j=f,a(b).attr("aria-describedby",j),e=this.groups[b.name],e&&(g=this,a.each(g.groups,function(b,c){c===e&&a("[name='"+g.escapeCssMeta(b)+"']",g.currentForm).attr("aria-describedby",h.attr("id"))})))),!c&&this.settings.success&&(h.text(""),"string"==typeof this.settings.success?h.addClass(this.settings.success):this.settings.success(h,b)),this.toShow=this.toShow.add(h)},errorsFor:function(b){var c=this.escapeCssMeta(this.idOrName(b)),d=a(b).attr("aria-describedby"),e="label[for='"+c+"'], label[for='"+c+"'] *";return d&&(e=e+", #"+this.escapeCssMeta(d).replace(/\s+/g,", #")),this.errors().filter(e)},escapeCssMeta:function(a){return void 0===a?"":a.replace(/([\\!"#$%&'()*+,./:;<=>?@\[\]^`{|}~])/g,"\\$1")},idOrName:function(a){return this.groups[a.name]||(this.checkable(a)?a.name:a.id||a.name)},validationTargetFor:function(b){return this.checkable(b)&&(b=this.findByName(b.name)),a(b).not(this.settings.ignore)[0]},checkable:function(a){return/radio|checkbox/i.test(a.type)},findByName:function(b){return a(this.currentForm).find("[name='"+this.escapeCssMeta(b)+"']")},getLength:function(b,c){switch(c.nodeName.toLowerCase()){case"select":return a("option:selected",c).length;case"input":if(this.checkable(c))return this.findByName(c.name).filter(":checked").length}return b.length},depend:function(a,b){return!this.dependTypes[typeof a]||this.dependTypes[typeof a](a,b)},dependTypes:{"boolean":function(a){return a},string:function(b,c){return!!a(b,c.form).length},"function":function(a,b){return a(b)}},optional:function(b){var c=this.elementValue(b);return!a.validator.methods.required.call(this,c,b)&&"dependency-mismatch"},startRequest:function(b){this.pending[b.name]||(this.pendingRequest++,a(b).addClass(this.settings.pendingClass),this.pending[b.name]=!0)},stopRequest:function(b,c){this.pendingRequest--,this.pendingRequest<0&&(this.pendingRequest=0),delete this.pending[b.name],a(b).removeClass(this.settings.pendingClass),c&&0===this.pendingRequest&&this.formSubmitted&&this.form()&&0===this.pendingRequest?(a(this.currentForm).submit(),this.submitButton&&a("input:hidden[name='"+this.submitButton.name+"']",this.currentForm).remove(),this.formSubmitted=!1):!c&&0===this.pendingRequest&&this.formSubmitted&&(a(this.currentForm).triggerHandler("invalid-form",[this]),this.formSubmitted=!1)},previousValue:function(b,c){return c="string"==typeof c&&c||"remote",a.data(b,"previousValue")||a.data(b,"previousValue",{old:null,valid:!0,message:this.defaultMessage(b,{method:c})})},destroy:function(){this.resetForm(),a(this.currentForm).off(".validate").removeData("validator").find(".validate-equalTo-blur").off(".validate-equalTo").removeClass("validate-equalTo-blur").find(".validate-lessThan-blur").off(".validate-lessThan").removeClass("validate-lessThan-blur").find(".validate-lessThanEqual-blur").off(".validate-lessThanEqual").removeClass("validate-lessThanEqual-blur").find(".validate-greaterThanEqual-blur").off(".validate-greaterThanEqual").removeClass("validate-greaterThanEqual-blur").find(".validate-greaterThan-blur").off(".validate-greaterThan").removeClass("validate-greaterThan-blur")}},classRuleSettings:{required:{required:!0},email:{email:!0},url:{url:!0},date:{date:!0},dateISO:{dateISO:!0},number:{number:!0},digits:{digits:!0},creditcard:{creditcard:!0}},addClassRules:function(b,c){b.constructor===String?this.classRuleSettings[b]=c:a.extend(this.classRuleSettings,b)},classRules:function(b){var c={},d=a(b).attr("class");return d&&a.each(d.split(" "),function(){this in a.validator.classRuleSettings&&a.extend(c,a.validator.classRuleSettings[this])}),c},normalizeAttributeRule:function(a,b,c,d){/min|max|step/.test(c)&&(null===b||/number|range|text/.test(b))&&(d=Number(d),isNaN(d)&&(d=void 0)),d||0===d?a[c]=d:b===c&&"range"!==b&&(a["date"===b?"dateISO":c]=!0)},attributeRules:function(b){var c,d,e={},f=a(b),g=b.getAttribute("type");for(c in a.validator.methods)"required"===c?(d=b.getAttribute(c),""===d&&(d=!0),d=!!d):d=f.attr(c),this.normalizeAttributeRule(e,g,c,d);return e.maxlength&&/-1|2147483647|524288/.test(e.maxlength)&&delete e.maxlength,e},dataRules:function(b){var c,d,e={},f=a(b),g=b.getAttribute("type");for(c in a.validator.methods)d=f.data("rule"+c.charAt(0).toUpperCase()+c.substring(1).toLowerCase()),""===d&&(d=!0),this.normalizeAttributeRule(e,g,c,d);return e},staticRules:function(b){var c={},d=a.data(b.form,"validator");return d.settings.rules&&(c=a.validator.normalizeRule(d.settings.rules[b.name])||{}),c},normalizeRules:function(b,c){return a.each(b,function(d,e){if(e===!1)return void delete b[d];if(e.param||e.depends){var f=!0;switch(typeof e.depends){case"string":f=!!a(e.depends,c.form).length;break;case"function":f=e.depends.call(c,c)}f?b[d]=void 0===e.param||e.param:(a.data(c.form,"validator").resetElements(a(c)),delete b[d])}}),a.each(b,function(a,d){b[a]="function"==typeof d&&"normalizer"!==a?d(c):d}),a.each(["minlength","maxlength"],function(){b[this]&&(b[this]=Number(b[this]))}),a.each(["rangelength","range"],function(){var a;b[this]&&(Array.isArray(b[this])?b[this]=[Number(b[this][0]),Number(b[this][1])]:"string"==typeof b[this]&&(a=b[this].replace(/[\[\]]/g,"").split(/[\s,]+/),b[this]=[Number(a[0]),Number(a[1])]))}),a.validator.autoCreateRanges&&(null!=b.min&&null!=b.max&&(b.range=[b.min,b.max],delete b.min,delete b.max),null!=b.minlength&&null!=b.maxlength&&(b.rangelength=[b.minlength,b.maxlength],delete b.minlength,delete b.maxlength)),b},normalizeRule:function(b){if("string"==typeof b){var c={};a.each(b.split(/\s/),function(){c[this]=!0}),b=c}return b},addMethod:function(b,c,d){a.validator.methods[b]=c,a.validator.messages[b]=void 0!==d?d:a.validator.messages[b],c.length<3&&a.validator.addClassRules(b,a.validator.normalizeRule(b))},methods:{required:function(b,c,d){if(!this.depend(d,c))return"dependency-mismatch";if("select"===c.nodeName.toLowerCase()){var e=a(c).val();return e&&e.length>0}return this.checkable(c)?this.getLength(b,c)>0:void 0!==b&&null!==b&&b.length>0},email:function(a,b){return this.optional(b)||/^[a-zA-Z0-9.!#$%&'*+\/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/.test(a)}
12047,url:function(a,b){return this.optional(b)||/^(?:(?:(?:https?|ftp):)?\/\/)(?:\S+(?::\S*)?@)?(?:(?!(?:10|127)(?:\.\d{1,3}){3})(?!(?:169\.254|192\.168)(?:\.\d{1,3}){2})(?!172\.(?:1[6-9]|2\d|3[0-1])(?:\.\d{1,3}){2})(?:[1-9]\d?|1\d\d|2[01]\d|22[0-3])(?:\.(?:1?\d{1,2}|2[0-4]\d|25[0-5])){2}(?:\.(?:[1-9]\d?|1\d\d|2[0-4]\d|25[0-4]))|(?:(?:[a-z0-9\u00a1-\uffff][a-z0-9\u00a1-\uffff_-]{0,62})?[a-z0-9\u00a1-\uffff]\.)+(?:[a-z\u00a1-\uffff]{2,}\.?))(?::\d{2,5})?(?:[/?#]\S*)?$/i.test(a)},date:function(){var a=!1;return function(b,c){return a||(a=!0,this.settings.debug&&window.console&&console.warn("The `date` method is deprecated and will be removed in version '2.0.0'.\nPlease don't use it, since it relies on the Date constructor, which\nbehaves very differently across browsers and locales. Use `dateISO`\ninstead or one of the locale specific methods in `localizations/`\nand `additional-methods.js`.")),this.optional(c)||!/Invalid|NaN/.test(new Date(b).toString())}}(),dateISO:function(a,b){return this.optional(b)||/^\d{4}[\/\-](0?[1-9]|1[012])[\/\-](0?[1-9]|[12][0-9]|3[01])$/.test(a)},number:function(a,b){return this.optional(b)||/^(?:-?\d+|-?\d{1,3}(?:,\d{3})+)?(?:\.\d+)?$/.test(a)},digits:function(a,b){return this.optional(b)||/^\d+$/.test(a)},minlength:function(a,b,c){var d=Array.isArray(a)?a.length:this.getLength(a,b);return this.optional(b)||d>=c},maxlength:function(a,b,c){var d=Array.isArray(a)?a.length:this.getLength(a,b);return this.optional(b)||d<=c},rangelength:function(a,b,c){var d=Array.isArray(a)?a.length:this.getLength(a,b);return this.optional(b)||d>=c[0]&&d<=c[1]},min:function(a,b,c){return this.optional(b)||a>=c},max:function(a,b,c){return this.optional(b)||a<=c},range:function(a,b,c){return this.optional(b)||a>=c[0]&&a<=c[1]},step:function(b,c,d){var e,f=a(c).attr("type"),g="Step attribute on input type "+f+" is not supported.",h=["text","number","range"],i=new RegExp("\\b"+f+"\\b"),j=f&&!i.test(h.join()),k=function(a){var b=(""+a).match(/(?:\.(\d+))?$/);return b&&b[1]?b[1].length:0},l=function(a){return Math.round(a*Math.pow(10,e))},m=!0;if(j)throw new Error(g);return e=k(d),(k(b)>e||l(b)%l(d)!==0)&&(m=!1),this.optional(c)||m},equalTo:function(b,c,d){var e=a(d);return this.settings.onfocusout&&e.not(".validate-equalTo-blur").length&&e.addClass("validate-equalTo-blur").on("blur.validate-equalTo",function(){a(c).valid()}),b===e.val()},remote:function(b,c,d,e){if(this.optional(c))return"dependency-mismatch";e="string"==typeof e&&e||"remote";var f,g,h,i=this.previousValue(c,e);return this.settings.messages[c.name]||(this.settings.messages[c.name]={}),i.originalMessage=i.originalMessage||this.settings.messages[c.name][e],this.settings.messages[c.name][e]=i.message,d="string"==typeof d&&{url:d}||d,h=a.param(a.extend({data:b},d.data)),i.old===h?i.valid:(i.old=h,f=this,this.startRequest(c),g={},g[c.name]=b,a.ajax(a.extend(!0,{mode:"abort",port:"validate"+c.name,dataType:"json",data:g,context:f.currentForm,success:function(a){var d,g,h,j=a===!0||"true"===a;f.settings.messages[c.name][e]=i.originalMessage,j?(h=f.formSubmitted,f.resetInternals(),f.toHide=f.errorsFor(c),f.formSubmitted=h,f.successList.push(c),f.invalid[c.name]=!1,f.showErrors()):(d={},g=a||f.defaultMessage(c,{method:e,parameters:b}),d[c.name]=i.message=g,f.invalid[c.name]=!0,f.showErrors(d)),i.valid=j,f.stopRequest(c,j)}},d)),"pending")}}});var c,d={};return a.ajaxPrefilter?a.ajaxPrefilter(function(a,b,c){var e=a.port;"abort"===a.mode&&(d[e]&&d[e].abort(),d[e]=c)}):(c=a.ajax,a.ajax=function(b){var e=("mode"in b?b:a.ajaxSettings).mode,f=("port"in b?b:a.ajaxSettings).port;return"abort"===e?(d[f]&&d[f].abort(),d[f]=c.apply(this,arguments),d[f]):c.apply(this,arguments)}),a});
12048/*! jQuery Validation Plugin - v1.19.5 - 7/1/2022
12049 * https://jqueryvalidation.org/
12050 * Copyright (c) 2022 Jörn Zaefferer; Licensed MIT */
12051!function(a){"function"==typeof define&&define.amd?define(["jquery","./jquery.validate.min"],a):"object"==typeof module&&module.exports?module.exports=a(require("jquery")):a(jQuery)}(function(a){return function(){function b(a){return a.replace(/<.[^<>]*?>/g," ").replace(/&nbsp;|&#160;/gi," ").replace(/[.(),;:!?%#$'\"_+=\/\-“”’]*/g,"")}a.validator.addMethod("maxWords",function(a,c,d){return this.optional(c)||b(a).match(/\b\w+\b/g).length<=d},a.validator.format("Please enter {0} words or less.")),a.validator.addMethod("minWords",function(a,c,d){return this.optional(c)||b(a).match(/\b\w+\b/g).length>=d},a.validator.format("Please enter at least {0} words.")),a.validator.addMethod("rangeWords",function(a,c,d){var e=b(a),f=/\b\w+\b/g;return this.optional(c)||e.match(f).length>=d[0]&&e.match(f).length<=d[1]},a.validator.format("Please enter between {0} and {1} words."))}(),a.validator.addMethod("abaRoutingNumber",function(a){var b=0,c=a.split(""),d=c.length;if(9!==d)return!1;for(var e=0;e<d;e+=3)b+=3*parseInt(c[e],10)+7*parseInt(c[e+1],10)+parseInt(c[e+2],10);return 0!==b&&b%10===0},"Please enter a valid routing number."),a.validator.addMethod("accept",function(b,c,d){var e,f,g,h="string"==typeof d?d.replace(/\s/g,""):"image/*",i=this.optional(c);if(i)return i;if("file"===a(c).attr("type")&&(h=h.replace(/[\-\[\]\/\{\}\(\)\+\?\.\\\^\$\|]/g,"\\$&").replace(/,/g,"|").replace(/\/\*/g,"/.*"),c.files&&c.files.length))for(g=new RegExp(".?("+h+")$","i"),e=0;e<c.files.length;e++)if(f=c.files[e],!f.type.match(g))return!1;return!0},a.validator.format("Please enter a value with a valid mimetype.")),a.validator.addMethod("alphanumeric",function(a,b){return this.optional(b)||/^\w+$/i.test(a)},"Letters, numbers, and underscores only please."),a.validator.addMethod("bankaccountNL",function(a,b){if(this.optional(b))return!0;
vendor: 1,119 bytes, line 12051
12051if(!/^[0-9]{9}|([0-9]{2} ){3}[0-9]{3}$/.test(a))return!1;var c,d,e,f=a.replace(/ /g,""),g=0,h=f.length;for(c=0;c<h;c++)d=h-c,e=f.substring(c,c+1),g+=d*e;return g%11===0},"Please specify a valid bank account number."),a.validator.addMethod("bankorgiroaccountNL",function(b,c){return this.optional(c)||a.validator.methods.bankaccountNL.call(this,b,c)||a.validator.methods.giroaccountNL.call(this,b,c)},"Please specify a valid bank or giro account number."),a.validator.addMethod("bic",function(a,b){return this.optional(b)||/^([A-Z]{6}[A-Z2-9][A-NP-Z1-9])(X{3}|[A-WY-Z0-9][A-Z0-9]{2})?$/.test(a.toUpperCase())},"Please specify a valid BIC code."),a.validator.addMethod("cifES",function(a,b){"use strict";function c(a){return a%2===0}if(this.optional(b))return!0;var d,e,f,g,h=new RegExp(/^([ABCDEFGHJKLMNPQRSUVW])(\d{7})([0-9A-J])$/gi),i=a.substring(0,1),j=a.substring(1,8),k=a.substring(8,9),l=0,m=0,n=0;if(9!==a.length||!h.test(a))return!1;for(d=0;d<j.length;d++)e=parseInt(j[d],10),c(d)?(e*=2,n+=e<10?e:e-9):m+=e;return l=m+n,f=(10-l.toString().substr(-1)).toString(),f=parseInt(f,10)>9?"0":f,g="JABCDEFGHI".substr(f,1
vendor: 18,499 bytes, line 12051
12051).toString(),i.match(/[ABEH]/)?k===f:i.match(/[KPQS]/)?k===g:k===f||k===g},"Please specify a valid CIF number."),a.validator.addMethod("cnhBR",function(a){if(a=a.replace(/([~!@#$%^&*()_+=`{}\[\]\-|\\:;'<>,.\/? ])+/g,""),11!==a.length)return!1;var b,c,d,e,f,g,h=0,i=0;if(b=a.charAt(0),new Array(12).join(b)===a)return!1;for(e=0,f=9,g=0;e<9;++e,--f)h+=+(a.charAt(e)*f);for(c=h%11,c>=10&&(c=0,i=2),h=0,e=0,f=1,g=0;e<9;++e,++f)h+=+(a.charAt(e)*f);return d=h%11,d>=10?d=0:d-=i,String(c).concat(d)===a.substr(-2)},"Please specify a valid CNH number."),a.validator.addMethod("cnpjBR",function(a,b){"use strict";if(this.optional(b))return!0;if(a=a.replace(/[^\d]+/g,""),14!==a.length)return!1;if("00000000000000"===a||"11111111111111"===a||"22222222222222"===a||"33333333333333"===a||"44444444444444"===a||"55555555555555"===a||"66666666666666"===a||"77777777777777"===a||"88888888888888"===a||"99999999999999"===a)return!1;for(var c=a.length-2,d=a.substring(0,c),e=a.substring(c),f=0,g=c-7,h=c;h>=1;h--)f+=d.charAt(c-h)*g--,g<2&&(g=9);var i=f%11<2?0:11-f%11;if(i!==parseInt(e.charAt(0),10))return!1;c+=1,d=a.substring(0,c),f=0,g=c-7;for(var j=c;j>=1;j--)f+=d.charAt(c-j)*g--,g<2&&(g=9);return i=f%11<2?0:11-f%11,i===parseInt(e.charAt(1),10)},"Please specify a CNPJ value number."),a.validator.addMethod("cpfBR",function(a,b){"use strict";if(this.optional(b))return!0;if(a=a.replace(/([~!@#$%^&*()_+=`{}\[\]\-|\\:;'<>,.\/? ])+/g,""),11!==a.length)return!1;var c,d,e,f,g=0;if(c=parseInt(a.substring(9,10),10),d=parseInt(a.substring(10,11),10),e=function(a,b){var c=10*a%11;return 10!==c&&11!==c||(c=0),c===b},""===a||"00000000000"===a||"11111111111"===a||"22222222222"===a||"33333333333"===a||"44444444444"===a||"55555555555"===a||"66666666666"===a||"77777777777"===a||"88888888888"===a||"99999999999"===a)return!1;for(f=1;f<=9;f++)g+=parseInt(a.substring(f-1,f),10)*(11-f);if(e(g,c)){for(g=0,f=1;f<=10;f++)g+=parseInt(a.substring(f-1,f),10)*(12-f);return e(g,d)}return!1},"Please specify a valid CPF number."),a.validator.addMethod("creditcard",function(a,b){if(this.optional(b))return"dependency-mismatch";if(/[^0-9 \-]+/.test(a))return!1;var c,d,e=0,f=0,g=!1;if(a=a.replace(/\D/g,""),a.length<13||a.length>19)return!1;for(c=a.length-1;c>=0;c--)d=a.charAt(c),f=parseInt(d,10),g&&(f*=2)>9&&(f-=9),e+=f,g=!g;return e%10===0},"Please enter a valid credit card number."),a.validator.addMethod("creditcardtypes",function(a,b,c){if(/[^0-9\-]+/.test(a))return!1;a=a.replace(/\D/g,"");var d=0;return c.mastercard&&(d|=1),c.visa&&(d|=2),c.amex&&(d|=4),c.dinersclub&&(d|=8),c.enroute&&(d|=16),c.discover&&(d|=32),c.jcb&&(d|=64),c.unknown&&(d|=128),c.all&&(d=255),1&d&&(/^(5[12345])/.test(a)||/^(2[234567])/.test(a))?16===a.length:2&d&&/^(4)/.test(a)?16===a.length:4&d&&/^(3[47])/.test(a)?15===a.length:8&d&&/^(3(0[012345]|[68]))/.test(a)?14===a.length:16&d&&/^(2(014|149))/.test(a)?15===a.length:32&d&&/^(6011)/.test(a)?16===a.length:64&d&&/^(3)/.test(a)?16===a.length:64&d&&/^(2131|1800)/.test(a)?15===a.length:!!(128&d)},"Please enter a valid credit card number."),a.validator.addMethod("currency",function(a,b,c){var d,e="string"==typeof c,f=e?c:c[0],g=!!e||c[1];return f=f.replace(/,/g,""),f=g?f+"]":f+"]?",d="^["+f+"([1-9]{1}[0-9]{0,2}(\\,[0-9]{3})*(\\.[0-9]{0,2})?|[1-9]{1}[0-9]{0,}(\\.[0-9]{0,2})?|0(\\.[0-9]{0,2})?|(\\.[0-9]{1,2})?)$",d=new RegExp(d),this.optional(b)||d.test(a)},"Please specify a valid currency."),a.validator.addMethod("dateFA",function(a,b){return this.optional(b)||/^[1-4]\d{3}\/((0?[1-6]\/((3[0-1])|([1-2][0-9])|(0?[1-9])))|((1[0-2]|(0?[7-9]))\/(30|([1-2][0-9])|(0?[1-9]))))$/.test(a)},a.validator.messages.date),a.validator.addMethod("dateITA",function(a,b){var c,d,e,f,g,h=!1,i=/^\d{1,2}\/\d{1,2}\/\d{4}$/;return i.test(a)?(c=a.split("/"),d=parseInt(c[0],10),e=parseInt(c[1],10),f=parseInt(c[2],10),g=new Date(Date.UTC(f,e-1,d,12,0,0,0)),h=g.getUTCFullYear()===f&&g.getUTCMonth()===e-1&&g.getUTCDate()===d):h=!1,this.optional(b)||h},a.validator.messages.date),a.validator.addMethod("dateNL",function(a,b){return this.optional(b)||/^(0?[1-9]|[12]\d|3[01])[\.\/\-](0?[1-9]|1[012])[\.\/\-]([12]\d)?(\d\d)$/.test(a)},a.validator.messages.date),a.validator.addMethod("extension",function(a,b,c){return c="string"==typeof c?c.replace(/,/g,"|"):"png|jpe?g|gif",this.optional(b)||a.match(new RegExp("\\.("+c+")$","i"))},a.validator.format("Please enter a value with a valid extension.")),a.validator.addMethod("giroaccountNL",function(a,b){return this.optional(b)||/^[0-9]{1,7}$/.test(a)},"Please specify a valid giro account number."),a.validator.addMethod("greaterThan",function(b,c,d){var e=a(d);return this.settings.onfocusout&&e.not(".validate-greaterThan-blur").length&&e.addClass("validate-greaterThan-blur").on("blur.validate-greaterThan",function(){a(c).valid()}),b>e.val()},"Please enter a greater value."),a.validator.addMethod("greaterThanEqual",function(b,c,d){var e=a(d);return this.settings.onfocusout&&e.not(".validate-greaterThanEqual-blur").length&&e.addClass("validate-greaterThanEqual-blur").on("blur.validate-greaterThanEqual",function(){a(c).valid()}),b>=e.val()},"Please enter a greater value."),a.validator.addMethod("iban",function(a,b){if(this.optional(b))return!0;var c,d,e,f,g,h,i,j,k,l=a.replace(/ /g,"").toUpperCase(),m="",n=!0,o="",p="",q=5;if(l.length<q)return!1;if(c=l.substring(0,2),h={AL:"\\d{8}[\\dA-Z]{16}",AD:"\\d{8}[\\dA-Z]{12}",AT:"\\d{16}",AZ:"[\\dA-Z]{4}\\d{20}",BE:"\\d{12}",BH:"[A-Z]{4}[\\dA-Z]{14}",BA:"\\d{16}",BR:"\\d{23}[A-Z][\\dA-Z]",BG:"[A-Z]{4}\\d{6}[\\dA-Z]{8}",CR:"\\d{17}",HR:"\\d{17}",CY:"\\d{8}[\\dA-Z]{16}",CZ:"\\d{20}",DK:"\\d{14}",DO:"[A-Z]{4}\\d{20}",EE:"\\d{16}",FO:"\\d{14}",FI:"\\d{14}",FR:"\\d{10}[\\dA-Z]{11}\\d{2}",GE:"[\\dA-Z]{2}\\d{16}",DE:"\\d{18}",GI:"[A-Z]{4}[\\dA-Z]{15}",GR:"\\d{7}[\\dA-Z]{16}",GL:"\\d{14}",GT:"[\\dA-Z]{4}[\\dA-Z]{20}",HU:"\\d{24}",IS:"\\d{22}",IE:"[\\dA-Z]{4}\\d{14}",IL:"\\d{19}",IT:"[A-Z]\\d{10}[\\dA-Z]{12}",KZ:"\\d{3}[\\dA-Z]{13}",KW:"[A-Z]{4}[\\dA-Z]{22}",LV:"[A-Z]{4}[\\dA-Z]{13}",LB:"\\d{4}[\\dA-Z]{20}",LI:"\\d{5}[\\dA-Z]{12}",LT:"\\d{16}",LU:"\\d{3}[\\dA-Z]{13}",MK:"\\d{3}[\\dA-Z]{10}\\d{2}",MT:"[A-Z]{4}\\d{5}[\\dA-Z]{18}",MR:"\\d{23}",MU:"[A-Z]{4}\\d{19}[A-Z]{3}",MC:"\\d{10}[\\dA-Z]{11}\\d{2}",MD:"[\\dA-Z]{2}\\d{18}",ME:"\\d{18}",NL:"[A-Z]{4}\\d{10}",NO:"\\d{11}",PK:"[\\dA-Z]{4}\\d{16}",PS:"[\\dA-Z]{4}\\d{21}",PL:"\\d{24}",PT:"\\d{21}",RO:"[A-Z]{4}[\\dA-Z]{16}",SM:"[A-Z]\\d{10}[\\dA-Z]{12}",SA:"\\d{2}[\\dA-Z]{18}",RS:"\\d{18}",SK:"\\d{20}",SI:"\\d{15}",ES:"\\d{20}",SE:"\\d{20}",CH:"\\d{5}[\\dA-Z]{12}",TN:"\\d{20}",TR:"\\d{5}[\\dA-Z]{17}",AE:"\\d{3}\\d{16}",GB:"[A-Z]{4}\\d{14}",VG:"[\\dA-Z]{4}\\d{16}"},g=h[c],"undefined"!=typeof g&&(i=new RegExp("^[A-Z]{2}\\d{2}"+g+"$",""),!i.test(l)))return!1;for(d=l.substring(4,l.length)+l.substring(0,4),j=0;j<d.length;j++)e=d.charAt(j),"0"!==e&&(n=!1),n||(m+="0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ".indexOf(e));for(k=0;k<m.length;k++)f=m.charAt(k),p=""+o+f,o=p%97;return 1===o},"Please specify a valid IBAN."),a.validator.addMethod("integer",function(a,b){return this.optional(b)||/^-?\d+$/.test(a)},"A positive or negative non-decimal number please."),a.validator.addMethod("ipv4",function(a,b){return this.optional(b)||/^(25[0-5]|2[0-4]\d|[01]?\d\d?)\.(25[0-5]|2[0-4]\d|[01]?\d\d?)\.(25[0-5]|2[0-4]\d|[01]?\d\d?)\.(25[0-5]|2[0-4]\d|[01]?\d\d?)$/i.test(a)},"Please enter a valid IP v4 address."),a.validator.addMethod("ipv6",function(a,b){return this.optional(b)||/^((([0-9A-Fa-f]{1,4}:){7}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){6}:[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){5}:([0-9A-Fa-f]{1,4}:)?[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){4}:([0-9A-Fa-f]{1,4}:){0,2}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){3}:([0-9A-Fa-f]{1,4}:){0,3}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){2}:([0-9A-Fa-f]{1,4}:){0,4}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){6}((\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b)\.){3}(\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b))|(([0-9A-Fa-f]{1,4}:){0,5}:((\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b)\.){3}(\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b))|(::([0-9A-Fa-f]{1,4}:){0,5}((\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b)\.){3}(\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b))|([0-9A-Fa-f]{1,4}::([0-9A-Fa-f]{1,4}:){0,5}[0-9A-Fa-f]{1,4})|(::([0-9A-Fa-f]{1,4}:){0,6}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){1,7}:))$/i.test(a)},"Please enter a valid IP v6 address."),a.validator.addMethod("lessThan",function(b,c,d){var e=a(d);return this.settings.onfocusout&&e.not(".validate-lessThan-blur").length&&e.addClass("validate-lessThan-blur").on("blur.validate-lessThan",function(){a(c).valid()}),b<e.val()},"Please enter a lesser value."),a.validator.addMethod("lessThanEqual",function(b,c,d){var e=a(d);return this.settings.onfocusout&&e.not(".validate-lessThanEqual-blur").length&&e.addClass("validate-lessThanEqual-blur").on("blur.validate-lessThanEqual",function(){a(c).valid()}),b<=e.val()},"Please enter a lesser value."),a.validator.addMethod("lettersonly",function(a,b){return this.optional(b)||/^[a-z]+$/i.test(a)},"Letters only please."),a.validator.addMethod("letterswithbasicpunc",function(a,b){return this.optional(b)||/^[a-z\-.,()'"\s]+$/i.test(a)},"Letters or punctuation only please."),a.validator.addMethod("maxfiles",function(b,c,d){return!!this.optional(c)||!("file"===a(c).attr("type")&&c.files&&c.files.length>d)},a.validator.format("Please select no more than {0} files.")),a.validator.addMethod("maxsize",function(b,c,d){if(this.optional(c))return!0;if("file"===a(c).attr("type")&&c.files&&c.files.length)for(var e=0;e<c.files.length;e++)if(c.files[e].size>d)return!1;return!0},a.validator.format("File size must not exceed {0} bytes each.")),a.validator.addMethod("maxsizetotal",function(b,c,d){if(this.optional(c))return!0;if("file"===a(c).attr("type")&&c.files&&c.files.length)for(var e=0,f=0;f<c.files.length;f++)if(e+=c.files[f].size,e>d)return!1;return!0},a.validator.format("Total size of all files must not exceed {0} bytes.")),a.validator.addMethod("mobileNL",function(a,b){return this.optional(b)||/^((\+|00(\s|\s?\-\s?)?)31(\s|\s?\-\s?)?(\(0\)[\-\s]?)?|0)6((\s|\s?\-\s?)?[0-9]){8}$/.test(a)},"Please specify a valid mobile number."),a.validator.addMethod("mobileRU",function(a,b){var c=a.replace(/\(|\)|\s+|-/g,"");return this.optional(b)||c.length>9&&/^((\+7|7|8)+([0-9]){10})$/.test(c)},"Please specify a valid mobile number."),a.validator.addMethod("mobileUK",function(a,b){return a=a.replace(/\(|\)|\s+|-/g,""),this.optional(b)||a.length>9&&a.match(/^(?:(?:(?:00\s?|\+)44\s?|0)7(?:[1345789]\d{2}|624)\s?\d{3}\s?\d{3})$/)},"Please specify a valid mobile number."),a.validator.addMethod("netmask",function(a,b){return this.optional(b)||/^(254|252|248|240|224|192|128)\.0\.0\.0|255\.(254|252|248|240|224|192|128|0)\.0\.0|255\.255\.(254|252|248|240|224|192|128|0)\.0|255\.255\.255\.(254|252|248|240|224|192|128|0)/i.test(a)},"Please enter a valid netmask."),a.validator.addMethod("nieES",function(a,b){"use strict";if(this.optional(b))return!0;var c,d=new RegExp(/^[MXYZ]{1}[0-9]{7,8}[TRWAGMYFPDXBNJZSQVHLCKET]{1}$/gi),e="TRWAGMYFPDXBNJZSQVHLCKET",f=a.substr(a.length-1).toUpperCase();return a=a.toString().toUpperCase(),!(a.length>10||a.length<9||!d.test(a))&&(a=a.replace(/^[X]/,"0").replace(/^[Y]/,"1").replace(/^[Z]/,"2"),c=9===a.length?a.substr(0,8):a.substr(0,9),e.charAt(parseInt(c,10)%23)===f)},"Please specify a valid NIE number."),a.validator.addMethod("nifES",function(a,b){"use strict";return!!this.optional(b)||(a=a.toUpperCase(),!!a.match("((^[A-Z]{1}[0-9]{7}[A-Z0-9]{1}$|^[T]{1}[A-Z0-9]{8}$)|^[0-9]{8}[A-Z]{1}$)")&&(/^[0-9]{8}[A-Z]{1}$/.test(a)?"TRWAGMYFPDXBNJZSQVHLCKE".charAt(a.substring(8,0)%23)===a.charAt(8):!!/^[KLM]{1}/.test(a)&&a[8]==="TRWAGMYFPDXBNJZSQVHLCKE".charAt(a.substring(8,1)%23)))},"Please specify a valid NIF number."),a.validator.addMethod("nipPL",function(a){"use strict";if(a=a.replace(/[^0-9]/g,""),10!==a.length)return!1;for(var b=[6,5,7,2,3,4,5,6,7],c=0,d=0;d<9;d++)c+=b[d]*a[d];var e=c%11,f=10===e?0:e;return f===parseInt(a[9],10)},"Please specify a valid NIP number."),a.validator.addMethod("nisBR",function(a){var b,c,d,e,f,g=0;if(a=a.replace(/([~!@#$%^&*()_+=`{}\[\]\-|\\:;'<>,.\/? ])+/g,""),11!==a.length)return!1;for(c=parseInt(a.substring(10,11),10),b=parseInt(a.substring(0,10),10),e=2;e<12;e++)f=e,10===e&&(f=2),11===e&&(f=3),g+=b%10*f,b=parseInt(b/10,10);return d=g%11,d=d>1?11-d:0,c===d},"Please specify a valid NIS/PIS number."),a.validator.addMethod("notEqualTo",function(b,c,d){return this.optional(c)||!a.validator.methods.equalTo.call(this,b,c,d)},"Please enter a different value, values must not be the same."),a.validator.addMethod("nowhitespace",function(a,b){return this.optional(b)||/^\S+$/i.test(a)},"No white space please."),a.validator.addMethod("pattern",function(a,b,c){return!!this.optional(b)||("string"==typeof c&&(c=new RegExp("^(?:"+c+")$")),c.test(a))},"Invalid format."),a.validator.addMethod("phoneNL",function(a,b){return this.optional(b)||/^((\+|00(\s|\s?\-\s?)?)31(\s|\s?\-\s?)?(\(0\)[\-\s]?)?|0)[1-9]((\s|\s?\-\s?)?[0-9]){8}$/.test(a)},"Please specify a valid phone number."),a.validator.addMethod("phonePL",function(a,b){a=a.replace(/\s+/g,"");var c=/^(?:(?:(?:\+|00)?48)|(?:\(\+?48\)))?(?:1[2-8]|2[2-69]|3[2-49]|4[1-68]|5[0-9]|6[0-35-9]|[7-8][1-9]|9[145])\d{7}$/;return this.optional(b)||c.test(a)},"Please specify a valid phone number."),a.validator.addMethod("phonesUK",function(a,b){return a=a.replace(/\(|\)|\s+|-/g,""),this.optional(b)||a.length>9&&a.match(/^(?:(?:(?:00\s?|\+)44\s?|0)(?:1\d{8,9}|[23]\d{9}|7(?:[1345789]\d{8}|624\d{6})))$/)},"Please specify a valid uk phone number."),a.validator.addMethod("phoneUK",function(a,b){return a=a.replace(/\(|\)|\s+|-/g,""),this.optional(b)||a.length>9&&a.match(/^(?:(?:(?:00\s?|\+)44\s?)|(?:\(?0))(?:\d{2}\)?\s?\d{4}\s?\d{4}|\d{3}\)?\s?\d{3}\s?\d{3,4}|\d{4}\)?\s?(?:\d{5}|\d{3}\s?\d{3})|\d{5}\)?\s?\d{4,5})$/)},"Please specify a valid phone number."),a.validator.addMethod("phoneUS",function(a,b){return a=a.replace(/\s+/g,""),this.optional(b)||a.length>9&&a.match(/^(\+?1-?)?(\([2-9]([02-9]\d|1[02-9])\)|[2-9]([02-9]\d|1[02-9]))-?[2-9]\d{2}-?\d{4}$/)},"Please specify a valid phone number."),a.validator.addMethod("postalcodeBR",function(a,b){return this.optional(b)||/^\d{2}.\d{3}-\d{3}?$|^\d{5}-?\d{3}?$/.test(a)},"Informe um CEP válido."),a.validator.addMethod("postalCodeCA",function(a,b){return this.optional(b)||/^[ABCEGHJKLMNPRSTVXY]\d[ABCEGHJKLMNPRSTVWXYZ] *\d[ABCEGHJKLMNPRSTVWXYZ]\d$/i.test(a)},"Please specify a valid postal code."),a.validator.addMethod("postalcodeIT",function(a,b){return this.optional(b)||/^\d{5}$/.test(a)},"Please specify a valid postal code."),a.validator.addMethod("postalcodeNL",function(a,b){return this.optional(b)||/^[1-9][0-9]{3}\s?[a-zA-Z]{2}$/.test(a)},"Please specify a valid postal code."),a.validator.addMethod("postcodeUK",function(a,b){return this.optional(b)||/^((([A-PR-UWYZ][0-9])|([A-PR-UWYZ][0-9][0-9])|([A-PR-UWYZ][A-HK-Y][0-9])|([A-PR-UWYZ][A-HK-Y][0-9][0-9])|([A-PR-UWYZ][0-9][A-HJKSTUW])|([A-PR-UWYZ][A-HK-Y][0-9][ABEHMNPRVWXY]))\s?([0-9][ABD-HJLNP-UW-Z]{2})|(GIR)\s?(0AA))$/i.test(a)},"Please specify a valid UK postcode."),a.validator.addMethod("require_from_group",function(b,c,d){var e=a(d[1],c.form),f=e.eq(0),g=f.data("valid_req_grp")?f.data("valid_req_grp"):a.extend({},this),h=e.filter(function(){return g.elementValue(this)}).length>=d[0];return f.data("valid_req_grp",g),a(c).data("being_validated")||(e.data("being_validated",!0),e.each(function(){g.element(this)}),e.data("being_validated",!1)),h},a.validator.format("Please fill at least {0} of these fields.")),a.validator.addMethod("skip_or_fill_minimum",function(b,c,d){var e=a(d[1],c.form),f=e.eq(0),g=f.data("valid_skip")?f.data("valid_skip"):a.extend({},this),h=e.filter(function(){return g.elementValue(this)}).length,i=0===h||h>=d[0];return f.data("valid_skip",g),a(c).data("being_validated")||(e.data("being_validated",!0),e.each(function(){g.element(this)}),e.data("being_validated",!1)),i},a.validator.format("Please either skip these fields or fill at least {0} of them.")),a.validator.addMethod("stateUS",function(a,b,c){var d,e="undefined"==typeof c,f=!e&&"undefined"!=typeof c.caseSensitive&&c.caseSensitive,g=!e&&"undefined"!=typeof c.includeTerritories&&c.includeTerritories,h=!e&&"undefined"!=typeof c.includeMilitary&&c.includeMilitary;return d=g||h?g&&h?"^(A[AEKLPRSZ]|C[AOT]|D[CE]|FL|G[AU]|HI|I[ADLN]|K[SY]|LA|M[ADEINOPST]|N[CDEHJMVY]|O[HKR]|P[AR]|RI|S[CD]|T[NX]|UT|V[AIT]|W[AIVY])$":g?"^(A[KLRSZ]|C[AOT]|D[CE]|FL|G[AU]|HI|I[ADLN]|K[SY]|LA|M[ADEINOPST]|N[CDEHJMVY]|O[HKR]|P[AR]|RI|S[CD]|T[NX]|UT|V[AIT]|W[AIVY])$":"^(A[AEKLPRZ]|C[AOT]|D[CE]|FL|GA|HI|I[ADLN]|K[SY]|LA|M[ADEINOST]|N[CDEHJMVY]|O[HKR]|PA|RI|S[CD]|T[NX]|UT|V[AT]|W[AIVY])$":"^(A[KLRZ]|C[AOT]|D[CE]|FL|GA|HI|I[ADLN]|K[SY]|LA|M[ADEINOST]|N[CDEHJMVY]|O[HKR]|PA|RI|S[CD]|T[NX]|UT|V[AT]|W[AIVY])$",d=f?new RegExp(d):new RegExp(d,"i"),this.optional(b)||d.test(a)},"Please specify a valid state."),a.validator.addMethod("strippedminlength",function(b,c,d){return a(b).text().length>=d},a.validator.format("Please enter at least {0} characters.")),a.validator.addMethod("time",function(a,b){return this.optional(b)||/^([01]\d|2[0-3]|[0-9])(:[0-5]\d){1,2}$/.test(a)},"Please enter a valid time, between 00:00 and 23:59."),a.validator.addMethod("time12h",function(a,b){return this.optional(b)||/^((0?[1-9]|1[012])(:[0-5]\d){1,2}(\ ?[AP]M))$/i.test(a)},"Please enter a valid time in 12-hour am/pm format."),a.validator.addMethod("url2",function(a,b){return this.optional(b)||/^(?:(?:(?:https?|ftp):)?\/\/)(?:(?:[^\]\[?\/<~#`!@$^&*()+=}|:";',>{ ]|%[0-9A-Fa-f]{2})+(?::(?:[^\]\[?\/<~#`!@$^&*()+=}|:";',>{ ]|%[0-9A-Fa-f]{2})*)?@)?(?:(?!(?:10|127)(?:\.\d{1,3}){3})(?!(?:169\.254|192\.168)(?:\.\d{1,3}){2})(?!172\.(?:1[6-9]|2\d|3[0-1])(?:\.\d{1,3}){2})(?:[1-9]\d?|1\d\d|2[01]\d|22[0-3])(?:\.(?:1?\d{1,2}|2[0-4]\d|25[0-5])){2}(?:\.(?:[1-9]\d?|1\d\d|2[0-4]\d|25[0-4]))|(?:(?:[a-z0-9\u00a1-\uffff][a-z0-9\u00a1-\uffff_-]{0,62})?[a-z0-9\u00a1-\uffff]\.)+(?:[a-z\u00a1-\uffff]{2,}\.?)|(?:(?:[a-z0-9\u00a1-\uffff][a-z0-9\u00a1-\uffff_-]{0,62})?[a-z0-9\u00a1-\uffff])|(?:(?:[a-z0-9\u00a1-\uffff][a-z0-9\u00a1-\uffff_-]{0,62}\.)))(?::\d{2,5})?(?:[/?#]\S*)?$/i.test(a)},a.validator.messages.url),a.validator.addMethod("vinUS",function(a){if(17!==a.length)return!1;var b,c,d,e,f,g,h=["A","B","C","D","E","F","G","H","J","K","L","M","N","P","R","S","T","U","V","W","X","Y","Z"],i=[1,2,3,4,5,6,7,8,1,2,3,4,5,7,9,2,3,4,5,6,7,8,9],j=[8,7,6,5,4,3,2,10,0,9,8,7,6,5,4,3,2],k=0;for(b=0;b<17;b++)
12051{if(e=j[b],d=a.slice(b,b+1),8===b&&(g=d),isNaN(d)){for(c=0;c<h.length;c++)if(d.toUpperCase()===h[c]){d=i[c],d*=e,isNaN(g)&&8===c&&(g=h[c]);break}}else d*=e;k+=d}return f=k%11,10===f&&(f="X"),f===g},"The specified vehicle identification number (VIN) is invalid."),a.validator.addMethod("zipcodeUS",function(a,b){return this.optional(b)||/^\d{5}(-\d{4})?$/.test(a)},"The specified US ZIP Code is invalid."),a.validator.addMethod("ziprange",function(a,b){return this.optional(b)||/^90[2-5]\d\{2\}-\d{4}$/.test(a)},"Your ZIP-code must be in the range 902xx-xxxx to 905xx-xxxx."),a});
12052/**
12053 * Minified by jsDelivr using UglifyJS v3.4.4.
12054 * Original file: /npm/[email protected]/src/js.cookie.js
12055 *
12056 * Do NOT use SRI with dynamically generated files! More information: https://www.jsdelivr.com/using-sri-with-dynamic-files
12057 */
12058!function(e){var n=!1;if("function"==typeof define&&define.amd&&(define(e),n=!0),"object"==typeof exports&&(module.exports=e(),n=!0),!n){var o=window.Cookies,t=window.Cookies=e();t.noConflict=function(){return window.Cookies=o,t}}}(function(){function g(){for(var e=0,n={};e<arguments.length;e++){var o=arguments[e];for(var t in o)n[t]=o[t]}return n}return function e(l){function C(e,n,o){var t;if("undefined"!=typeof document){if(1<arguments.length){if("number"==typeof(o=g({path:"/"},C.defaults,o)).expires){var r=new Date;r.setMilliseconds(r.getMilliseconds()+864e5*o.expires),o.expires=r}o.expires=o.expires?o.expires.toUTCString():"";try{t=JSON.stringify(n),/^[\{\[]/.test(t)&&(n=t)}catch(e){}n=l.write?l.write(n,e):encodeURIComponent(String(n)).replace(/%(23|24|26|2B|3A|3C|3E|3D|2F|3F|40|5B|5D|5E|60|7B|7D|7C)/g,decodeURIComponent),e=(e=(e=encodeURIComponent(String(e))).replace(/%(23|24|26|2B|5E|60|7C)/g,decodeURIComponent)).replace(/[\(\)]/g,escape);var i="";for(var c in o)o[c]&&(i+="; "+c,!0!==o[c]&&(i+="="+o[c]));return document.cookie=e+"="+n+i}e||(t={});for(var a=document.cookie?document.cookie.split("; "):[],s=/(%[0-9A-Z]{2})+/g,f=0;f<a.length;f++){var p=a[f].split("="),d=p.slice(1).join("=");this.json||'"'!==d.charAt(0)||(d=d.slice(1,-1));try{var u=p[0].replace(s,decodeURIComponent);if(d=l.read?l.read(d,u):l(d,u)||d.replace(s,decodeURIComponent),this.json)try{d=JSON.parse(d)}catch(e){}if(e===u){t=d;break}e||(t[u]=d)}catch(e){}}return t}}return(C.set=C).get=function(e){return C.call(C,e)},C.getJSON=function(){return C.apply({json:!0},[].slice.call(arguments))},C.defaults={},C.remove=function(e,n){C(e,"",g(n,{expires:-1}))},C.withConverter=e,C}(function(){})});
12059
12060/**
12061*  Ajax Autocomplete for jQuery, version 1.2.24
12062*  (c) 2014 Tomas Kirda
12063*
12064*  Ajax Autocomplete for jQuery is freely distributable under the terms of an MIT-style license.
12065*  For details, see the web site: https://github.com/devbridge/jQuery-Autocomplete
12066*/
12067!function(a){"use strict";"function"==typeof define&&define.amd?define(["jquery"],a):a("object"==typeof exports&&"function"==typeof require?require("jquery"):jQuery)}(function(a){"use strict";function b(c,d){var e=function(){},f=this,g={ajaxSettings:{},autoSelectFirst:!1,appendTo:document.body,serviceUrl:null,lookup:null,onSelect:null,width:"auto",minChars:1,maxHeight:300,deferRequestBy:0,params:{},formatResult:b.formatResult,delimiter:null,zIndex:9999,type:"GET",noCache:!1,onSearchStart:e,onSearchComplete:e,onSearchError:e,preserveInput:!1,containerClass:"autocomplete-suggestions",tabDisabled:!1,dataType:"text",currentRequest:null,triggerSelectOnValidInput:!0,preventBadQueries:!0,lookupFilter:function(a,b,c){return-1!==a.value.toLowerCase().indexOf(c)},paramName:"query",transformResult:function(b){return"string"==typeof b?a.parseJSON(b):b},showNoSuggestionNotice:!1,noSuggestionNotice:"No results",orientation:"bottom",forceFixPosition:!1};f.element=c,f.el=a(c),f.suggestions=[],f.badQueries=[],f.selectedIndex=-1,f.currentValue=f.element.value,f.intervalId=0,f.cachedResponse={},f.onChangeInterval=null,f.onChange=null,f.isLocal=!1,f.suggestionsContainer=null,f.noSuggestionsContainer=null,f.options=a.extend({},g,d),f.classes={selected:"autocomplete-selected",suggestion:"autocomplete-suggestion"},f.hint=null,f.hintValue="",f.selection=null,f.initialize(),f.setOptions(d)}var c=function(){return{escapeRegExChars:function(a){return a.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g,"\\$&")},createNode:function(a){var b=document.createElement("div");return b.className=a,b.style.position="absolute",b.style.display="none",b}}}(),d={ESC:27,TAB:9,RETURN:13,LEFT:37,UP:38,RIGHT:39,DOWN:40};b.utils=c,a.Autocomplete=b,b.formatResult=function(a,b){var d="("+c.escapeRegExChars(b)+")";return a.value.replace(new RegExp(d,"gi"),"<strong>$1</strong>").replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;").replace(/&lt;(\/?strong)&gt;/g,"<$1>")},b.prototype={killerFn:null,initialize:function(){var c,d=this,e="."+d.classes.suggestion,f=d.classes.selected,g=d.options;d.element.setAttribute("autocomplete","off"),d.killerFn=function(b){0===a(b.target).closest("."+d.options.containerClass).length&&(d.killSuggestions(),d.disableKillerFn())},d.noSuggestionsContainer=a('<div class="autocomplete-no-suggestion"></div>').html(this.options.noSuggestionNotice).get(0),d.suggestionsContainer=b.utils.createNode(g.containerClass),c=a(d.suggestionsContainer),c.appendTo(g.appendTo),"auto"!==g.width&&c.width(g.width),c.on("mouseover.autocomplete",e,function(){d.activate(a(this).data("index"))}
12067),c.on("mouseout.autocomplete",function(){d.selectedIndex=-1,c.children("."+f).removeClass(f)}),c.on("click.autocomplete",e,function(){d.select(a(this).data("index"))}),d.fixPositionCapture=function(){d.visible&&d.fixPosition()},a(window).on("resize.autocomplete",d.fixPositionCapture),d.el.on("keydown.autocomplete",function(a){d.onKeyPress(a)}),d.el.on("keyup.autocomplete",function(a){d.onKeyUp(a)}),d.el.on("blur.autocomplete",function(){d.onBlur()}),d.el.on("focus.autocomplete",function(){d.onFocus()}),d.el.on("change.autocomplete",function(a){d.onKeyUp(a)}),d.el.on("input.autocomplete",function(a){d.onKeyUp(a)})},onFocus:function(){var a=this;a.fixPosition(),0===a.options.minChars&&0===a.el.val().length&&a.onValueChange()},onBlur:function(){this.enableKillerFn()},abortAjax:function(){var a=this;a.currentRequest&&(a.currentRequest.abort(),a.currentRequest=null)},setOptions:function(b){var c=this,d=c.options;a.extend(d,b),c.isLocal=a.isArray(d.lookup),c.isLocal&&(d.lookup=c.verifySuggestionsFormat(d.lookup)),d.orientation=c.validateOrientation(d.orientation,"bottom"),a(c.suggestionsContainer).css({"max-height":d.maxHeight+"px",width:d.width+"px","z-index":d.zIndex})},clearCache:function(){this.cachedResponse={},this.badQueries=[]},clear:function(){this.clearCache(),this.currentValue="",this.suggestions=[]},disable:function(){var a=this;a.disabled=!0,clearInterval(a.onChangeInterval),a.abortAjax()},enable:function(){this.disabled=!1},fixPosition:function(){var b=this,c=a(b.suggestionsContainer),d=c.parent().get(0);if(d===document.body||b.options.forceFixPosition){var e=b.options.orientation,f=c.outerHeight(),g=b.el.outerHeight(),h=b.el.offset(),i={top:h.top,left:h.left};if("auto"===e){var j=a(window).height(),k=a(window).scrollTop(),l=-k+h.top-f,m=k+j-(h.top+g+f);e=Math.max(l,m)===l?"top":"bottom"}if("top"===e?i.top+=-f:i.top+=g,d!==document.body){var n,o=c.css("opacity");b.visible||c.css("opacity",0).show(),n=c.offsetParent().offset(),i.top-=n.top,i.left-=n.left,b.visible||c.css("opacity",o).hide()}"auto"===b.options.width&&(i.width=b.el.outerWidth()-2+"px"),c.css(i)}},enableKillerFn:function(){var b=this;a(document).on("click.autocomplete",b.killerFn)},disableKillerFn:function(){var b=this;a(document).off("click.autocomplete",b.killerFn)},killSuggestions:function(){var a=this;a.stopKillSuggestions(),a.intervalId=window.setInterval(function(){a.visible&&(a.el.val(a.currentValue),a.hide()),a.stopKillSuggestions()},50)},stopKillSuggestions:function(){window.clearInterval(this.intervalId)},isCursorAtEnd:function(){var a,b=this,c=b.el.val().length,d=b.element.selectionStart;return"number"==typeof d?d===c:document.selection?(a=document.selection.createRange(),a.moveStart("character",-c),c===a.text.length):!0},onKeyPress:function(a){var b=this;if(!b.disabled&&!b.visible&&a.which===d.DOWN&&b.currentValue)return void b.suggest();if(!b.disabled&&b.visible){switch(a.which){case d.ESC:b.el.val(b.currentValue),b.hide();break;case d.RIGHT:if(b.hint&&b.options.onHint&&b.isCursorAtEnd()){b.selectHint();break}return;case d.TAB:if(b.hint&&b.options.onHint)return void b.selectHint();if(-1===b.selectedIndex)return void b.hide();if(b.select(b.selectedIndex),b.options.tabDisabled===!1)return;break;case d.RETURN:if(-1===b.selectedIndex)return void b.hide();b.select(b.selectedIndex);break;case d.UP:b.moveUp();break;case d.DOWN:b.moveDown();break;default:return}a.stopImmediatePropagation(),a.preventDefault()}},onKeyUp:function(a){var b=this;if(!b.disabled){switch(a.which){case d.UP:case d.DOWN:return}clearInterval(b.onChangeInterval),b.currentValue!==b.el.val()&&(b.findBestHint(),b.options.deferRequestBy>0?b.onChangeInterval=setInterval(function(){b.onValueChange()},b.options.deferRequestBy):b.onValueChange())}},onValueChange:function(){var b=this,c=b.options,d=b.el.val(),e=b.getQuery(d);return b.selection&&b.currentValue!==e&&(b.selection=null,(c.onInvalidateSelection||a.noop).call(b.element)),clearInterval(b.onChangeInterval),b.currentValue=d,b.selectedIndex=-1,c.triggerSelectOnValidInput&&b.isExactMatch(e)?void b.select(0):void(e.length<c.minChars?b.hide():b.getSuggestions(e))},isExactMatch:function(a){var b=this.suggestions;return 1===b.length&&b[0].value.toLowerCase()===a.toLowerCase()},getQuery:function(b){var c,d=this.options.delimiter;return d?(c=b.split(d),a.trim(c[c.length-1])):b},getSuggestionsLocal:function(b){var c,d=this,e=d.options,f=b.toLowerCase(),g=e.lookupFilter,h=parseInt(e.lookupLimit,10);return c={suggestions:a.grep(e.lookup,function(a){return g(a,b,f)})},h&&c.suggestions.length>h&&(c.suggestions=c.suggestions.slice(0,h)),c},getSuggestions:function(b){var c,d,e,f,g=this,h=g.options,i=h.serviceUrl;if(h.params[h.paramName]=b,d=h.ignoreParams?null:h.params,h.onSearchStart.call(g.element,h.params)!==!1){if(a.isFunction(h.lookup))return void h.lookup(b,function(a){g.suggestions=a.suggestions,g.suggest(),h.onSearchComplete.call(g.element,b,a.suggestions)});g.isLocal?c=g.getSuggestionsLocal(b):(a.isFunction(i)&&(i=i.call(g.element,b)),e=i+"?"+a.param(d||{}),c=g.cachedResponse[e]),c&&a.isArray(c.suggestions)?(g.suggestions=c.suggestions,g.suggest(),h.onSearchComplete.call(g.element,b,c.suggestions)):g.isBadQuery(b)?h.onSearchComplete.call(g.element,b,[]):(g.abortAjax(),f={url:i,data:d,type:h.type,dataType:h.dataType},a.extend(f,h.ajaxSettings),g.currentRequest=a.ajax(f).done(function(a){var c;g.currentRequest=null,c=h.transformResult(a,b),g.processResponse(c,b,e),h.onSearchComplete.call(g.element,b,c.suggestions)}).fail(function(a,c,d){h.onSearchError.call(g.element,b,a,c,d)}))}}
12067,isBadQuery:function(a){if(!this.options.preventBadQueries)return!1;for(var b=this.badQueries,c=b.length;c--;)if(0===a.indexOf(b[c]))return!0;return!1},hide:function(){var b=this,c=a(b.suggestionsContainer);a.isFunction(b.options.onHide)&&b.visible&&b.options.onHide.call(b.element,c),b.visible=!1,b.selectedIndex=-1,clearInterval(b.onChangeInterval),a(b.suggestionsContainer).hide(),b.signalHint(null)},suggest:function(){if(0===this.suggestions.length)return void(this.options.showNoSuggestionNotice?this.noSuggestions():this.hide());var b,c=this,d=c.options,e=d.groupBy,f=d.formatResult,g=c.getQuery(c.currentValue),h=c.classes.suggestion,i=c.classes.selected,j=a(c.suggestionsContainer),k=a(c.noSuggestionsContainer),l=d.beforeRender,m="",n=function(a,c){var d=a.data[e];return b===d?"":(b=d,'<div class="autocomplete-group"><strong>'+b+"</strong></div>")};return d.triggerSelectOnValidInput&&c.isExactMatch(g)?void c.select(0):(a.each(c.suggestions,function(a,b){e&&(m+=n(b,g,a)),m+='<div class="'+h+'" data-index="'+a+'">'+f(b,g)+"</div>"}),this.adjustContainerWidth(),k.detach(),j.html(m),a.isFunction(l)&&l.call(c.element,j),c.fixPosition(),j.show(),d.autoSelectFirst&&(c.selectedIndex=0,j.scrollTop(0),j.children("."+h).first().addClass(i)),c.visible=!0,void c.findBestHint())},noSuggestions:function(){var b=this,c=a(b.suggestionsContainer),d=a(b.noSuggestionsContainer);this.adjustContainerWidth(),d.detach(),c.empty(),c.append(d),b.fixPosition(),c.show(),b.visible=!0},adjustContainerWidth:function(){var b,c=this,d=c.options,e=a(c.suggestionsContainer);"auto"===d.width&&(b=c.el.outerWidth()-2,e.width(b>0?b:300))},findBestHint:function(){var b=this,c=b.el.val().toLowerCase(),d=null;c&&(a.each(b.suggestions,function(a,b){var e=0===b.value.toLowerCase().indexOf(c);return e&&(d=b),!e}),b.signalHint(d))},signalHint:function(b){var c="",d=this;b&&(c=d.currentValue+b.value.substr(d.currentValue.length)),d.hintValue!==c&&(d.hintValue=c,d.hint=b,(this.options.onHint||a.noop)(c))},verifySuggestionsFormat:function(b){return b.length&&"string"==typeof b[0]?a.map(b,function(a){return{value:a,data:null}}):b},validateOrientation:function(b,c){return b=a.trim(b||"").toLowerCase(),-1===a.inArray(b,["auto","bottom","top"])&&(b=c),b},processResponse:function(a,b,c){var d=this,e=d.options;a.suggestions=d.verifySuggestionsFormat(a.suggestions),e.noCache||(d.cachedResponse[c]=a,e.preventBadQueries&&0===a.suggestions.length&&d.badQueries.push(b)),b===d.getQuery(d.currentValue)&&(d.suggestions=a.suggestions,d.suggest())},activate:function(b){var c,d=this,e=d.classes.selected,f=a(d.suggestionsContainer),g=f.find("."+d.classes.suggestion);return f.find("."+e).removeClass(e),d.selectedIndex=b,-1!==d.selectedIndex&&g.length>d.selectedIndex?(c=g.get(d.selectedIndex),a(c).addClass(e),c):null},selectHint:function(){var b=this,c=a.inArray(b.hint,b.suggestions);b.select(c)},select:function(a){var b=this;b.hide(),b.onSelect(a)},moveUp:function(){var b=this;if(-1!==b.selectedIndex)return 0===b.selectedIndex?(a(b.suggestionsContainer).children().first().removeClass(b.classes.selected),b.selectedIndex=-1,b.el.val(b.currentValue),void b.findBestHint()):void b.adjustScroll(b.selectedIndex-1)},moveDown:function(){var a=this;a.selectedIndex!==a.suggestions.length-1&&a.adjustScroll(a.selectedIndex+1)},adjustScroll:function(b){var c=this,d=c.activate(b);if(d){var e,f,g,h=a(d).outerHeight();e=d.offsetTop,f=a(c.suggestionsContainer).scrollTop(),g=f+c.options.maxHeight-h,f>e?a(c.suggestionsContainer).scrollTop(e):e>g&&a(c.suggestionsContainer).scrollTop(e-c.options.maxHeight+h),c.options.preserveInput||c.el.val(c.getValue(c.suggestions[b].value)),c.signalHint(null)}},onSelect:function(b){var c=this,d=c.options.onSelect,e=c.suggestions[b];c.currentValue=c.getValue(e.value),c.currentValue===c.el.val()||c.options.preserveInput||c.el.val(c.currentValue),c.signalHint(null),c.suggestions=[],c.selection=e,a.isFunction(d)&&d.call(c.element,e)},getValue:function(a){var b,c,d=this,e=d.options.delimiter;return e?(b=d.currentValue,c=b.split(e),1===c.length?a:b.substr(0,b.length-c[c.length-1].length)+a):a},dispose:function(){var b=this;b.el.off(".autocomplete").removeData("autocomplete"),b.disableKillerFn(),a(window).off("resize.autocomplete",b.fixPositionCapture),a(b.suggestionsContainer).remove()}},a.fn.autocomplete=a.fn.devbridgeAutocomplete=function(c,d){var e="autocomplete";return 0===arguments.length?this.first().data(e):this.each(function(){var f=a(this),g=f.data(e);"string"==typeof c?g&&"function"==typeof g[c]&&g[c](d):(g&&g.dispose&&g.dispose(),g=new b(this,c),f.data(e,g))})}});
12068/*!
12069 * numeral.js
12070 * version : 1.5.3
12071 * author : Adam Draper
12072 * license : MIT
12073 * http://adamwdraper.github.com/Numeral-js/
12074 */
12075(function(){function a(a){this._value=a}function b(a,b,c,d){var e,f,g=Math.pow(10,b);return f=(c(a*g)/g).toFixed(b),d&&(e=new RegExp("0{1,"+d+"}$"),f=f.replace(e,"")),f}function c(a,b,c){var d;return d=b.indexOf("$")>-1?e(a,b,c):b.indexOf("%")>-1?f(a,b,c):b.indexOf(":")>-1?g(a,b):i(a._value,b,c)}function d(a,b){var c,d,e,f,g,i=b,j=["KB","MB","GB","TB","PB","EB","ZB","YB"],k=!1;if(b.indexOf(":")>-1)a._value=h(b);else if(b===q)a._value=0;else{for("."!==o[p].delimiters.decimal&&(b=b.replace(/\./g,"").replace(o[p].delimiters.decimal,".")),c=new RegExp("[^a-zA-Z]"+o[p].abbreviations.thousand+"(?:\\)|(\\"+o[p].currency.symbol+")?(?:\\))?)?$"),d=new RegExp("[^a-zA-Z]"+o[p].abbreviations.million+"(?:\\)|(\\"+o[p].currency.symbol+")?(?:\\))?)?$"),e=new RegExp("[^a-zA-Z]"+o[p].abbreviations.billion+"(?:\\)|(\\"+o[p].currency.symbol+")?(?:\\))?)?$"),f=new RegExp("[^a-zA-Z]"+o[p].abbreviations.trillion+"(?:\\)|(\\"+o[p].currency.symbol+")?(?:\\))?)?$"),g=0;g<=j.length&&!(k=b.indexOf(j[g])>-1?Math.pow(1024,g+1):!1);g++);a._value=(k?k:1)*(i.match(c)?Math.pow(10,3):1)*(i.match(d)?Math.pow(10,6):1)*(i.match(e)?Math.pow(10,9):1)*(i.match(f)?Math.pow(10,12):1)*(b.indexOf("%")>-1?.01:1)*((b.split("-").length+Math.min(b.split("(").length-1,b.split(")").length-1))%2?1:-1)*Number(b.replace(/[^0-9\.]+/g,"")),a._value=k?Math.ceil(a._value):a._value}return a._value}function e(a,b,c){var d,e,f=b.indexOf("$"),g=b.indexOf("("),h=b.indexOf("-"),j="";return b.indexOf(" $")>-1?(j=" ",b=b.replace(" $","")):b.indexOf("$ ")>-1?(j=" ",b=b.replace("$ ","")):b=b.replace("$",""),e=i(a._value,b,c),1>=f?e.indexOf("(")>-1||e.indexOf("-")>-1?(e=e.split(""),d=1,(g>f||h>f)&&(d=0),e.splice(d,0,o[p].currency.symbol+j),e=e.join("")):e=o[p
12075].currency.symbol+j+e:e.indexOf(")")>-1?(e=e.split(""),e.splice(-1,0,j+o[p].currency.symbol),e=e.join("")):e=e+j+o[p].currency.symbol,e}function f(a,b,c){var d,e="",f=100*a._value;return b.indexOf(" %")>-1?(e=" ",b=b.replace(" %","")):b=b.replace("%",""),d=i(f,b,c),d.indexOf(")")>-1?(d=d.split(""),d.splice(-1,0,e+"%"),d=d.join("")):d=d+e+"%",d}function g(a){var b=Math.floor(a._value/60/60),c=Math.floor((a._value-60*b*60)/60),d=Math.round(a._value-60*b*60-60*c);return b+":"+(10>c?"0"+c:c)+":"+(10>d?"0"+d:d)}function h(a){var b=a.split(":"),c=0;return 3===b.length?(c+=60*Number(b[0])*60,c+=60*Number(b[1]),c+=Number(b[2])):2===b.length&&(c+=60*Number(b[0]),c+=Number(b[1])),Number(c)}function i(a,c,d){var e,f,g,h,i,j,k=!1,l=!1,m=!1,n="",r=!1,s=!1,t=!1,u=!1,v=!1,w="",x="",y=Math.abs(a),z=["B","KB","MB","GB","TB","PB","EB","ZB","YB"],A="",B=!1;if(0===a&&null!==q)return q;if(c.indexOf("(")>-1?(k=!0,c=c.slice(1,-1)):c.indexOf("+")>-1&&(l=!0,c=c.replace(/\+/g,"")),c.indexOf("a")>-1&&(r=c.indexOf("aK")>=0,s=c.indexOf("aM")>=0,t=c.indexOf("aB")>=0,u=c.indexOf("aT")>=0,v=r||s||t||u,c.indexOf(" a")>-1?(n=" ",c=c.replace(" a","")):c=c.replace("a",""),y>=Math.pow(10,12)&&!v||u?(n+=o[p].abbreviations.trillion,a/=Math.pow(10,12)):y<Math.pow(10,12)&&y>=Math.pow(10,9)&&!v||t?(n+=o[p].abbreviations.billion,a/=Math.pow(10,9)):y<Math.pow(10,9)&&y>=Math.pow(10,6)&&!v||s?(n+=o[p].abbreviations.million,a/=Math.pow(10,6)):(y<Math.pow(10,6)&&y>=Math.pow(10,3)&&!v||r)&&(n+=o[p].abbreviations.thousand,a/=Math.pow(10,3))),c.indexOf("b")>-1)for(c.indexOf(" b")>-1?(w=" ",c=c.replace(" b","")):c=c.replace("b",""),g=0;g<=z.length;g++)if(e=Math.pow(1024,g),f=Math.pow(1024,g+1),a>=e&&f>a){w+=z[g],e>0&&(a/=e);break}return c.indexOf("o")>-1&&(c.indexOf(" o")>-1?(x=" ",c=c.replace(" o","")):c=c.replace("o",""),x+=o[p].ordinal(a)),c.indexOf("[.]")>-1&&(m=!0,c=c.replace("[.]",".")),h=a.toString().split(".")[0],i=c.split(".")[1],j=c.indexOf(","),i?(i.indexOf("[")>-1?(i=i.replace("]",""),i=i.split("["),A=b(a,i[0].length+i[1].length,d,i[1].length)):A=b(a,i.length,d),h=A.split(".")[0],A=A.split(".")[1].length?o[p].delimiters.decimal+A.split(".")[1]:"",m&&0===Number(A.slice(1))&&(A="")):h=b(a,null,d),h.indexOf("-")>-1&&(h=h.slice(1),B=!0),j>-1&&(h=h.toString().replace(/(\d)(?=(\d{3})+(?!\d))/g,"$1"+o[p].delimiters.thousands)),0===c.indexOf(".")&&(h=""),(k&&B?"(":"")+(!k&&B?"-":"")+(!B&&l?"+":"")+h+A+(x?x:"")+(n?n:"")+(w?w:"")+(k&&B?")":"")}function j(a,b){o[a]=b}function k(a){var b=a.toString().split(".");return b.length<2?1:Math.pow(10,b[1].length)}function l(){var a=Array.prototype.slice.call(arguments);return a.reduce(function(a,b){var c=k(a),d=k(b);return c>d?c:d},-1/0)}var m,n="1.5.3",o={},p="en",q=null,r="0,0",s="undefined"!=typeof module&&module.exports;m=function(b){return m.isNumeral(b)?b=b.value():0===b||"undefined"==typeof b?b=0:Number(b)||(b=m.fn.unformat(b)),new a(Number(b))},m.version=n,m.isNumeral=function(b){return b instanceof a},m.language=function(a,b){if(!a)return p;if(a&&!b){if(!o[a])throw new Error("Unknown language : "+a);p=a}return(b||!o[a])&&j(a,b),m},m.languageData=function(a){if(!a)return o[p];if(!o[a])throw new Error("Unknown language : "+a);return o[a]},m.language("en",{delimiters:{thousands:",",decimal:"."},abbreviations:{thousand:"k",million:"m",billion:"b",trillion:"t"},ordinal:function(a){var b=a%10;return 1===~~(a%100/10)?"th":1===b?"st":2===b?"nd":3===b?"rd":"th"},currency:{symbol:"$"}}),m.zeroFormat=function(a){q="string"==typeof a?a:null},m.defaultFormat=function(a){r="string"==typeof a?a:"0.0"},"function"!=typeof Array.prototype.reduce&&(Array.prototype.reduce=function(a,b){"use strict";if(null===this||"undefined"==typeof this)throw new TypeError("Array.prototype.reduce called on null or undefined");if("function"!=typeof a)throw new TypeError(a+" is not a function");var c,d,e=this.length>>>0,f=!1;for(1<arguments.length&&(d=b,f=!0),c=0;e>c;++c)this.hasOwnProperty(c)&&(f?d=a(d,this[c],c,this):(d=this[c],f=!0));if(!f)throw new TypeError("Reduce of empty array with no initial value");return d}),m.fn=a.prototype={clone:function(){return m(this)},format:function(a,b){return c(this,a?a:r,void 0!==b?b:Math.round)},unformat:function(a){return"[object Number]"===Object.prototype.toString.call(a)?a:d(this,a?a:r)},value:function(){return this._value}
12075,valueOf:function(){return this._value},set:function(a){return this._value=Number(a),this},add:function(a){function b(a,b){return a+c*b}var c=l.call(null,this._value,a);return this._value=[this._value,a].reduce(b,0)/c,this},subtract:function(a){function b(a,b){return a-c*b}var c=l.call(null,this._value,a);return this._value=[a].reduce(b,this._value*c)/c,this},multiply:function(a){function b(a,b){var c=l(a,b);return a*c*b*c/(c*c)}return this._value=[this._value,a].reduce(b,1),this},divide:function(a){function b(a,b){var c=l(a,b);return a*c/(b*c)}return this._value=[this._value,a].reduce(b),this},difference:function(a){return Math.abs(m(this._value).subtract(a).value())}},s&&(module.exports=m),"undefined"==typeof ender&&(this.numeral=m),"function"==typeof define&&define.amd&&define([],function(){return m})}).call(this);
12076/*!
12077* jquery.inputmask.bundle.js
12078* https://github.com/RobinHerbots/jquery.inputmask
12079* Copyright (c) 2010 - 2016 Robin Herbots
12080* Licensed under the MIT license (http://www.opensource.org/licenses/mit-license.php)
12081* Version: 3.3.4-47
12082*/
12083!function(a){function b(c,d){return this instanceof b?(a.isPlainObject(c)?d=c:(d=d||{},d.alias=c),this.el=void 0,this.opts=a.extend(!0,{},this.defaults,d),this.noMasksCache=d&&void 0!==d.definitions,this.userOptions=d||{},this.events={},void e(this.opts.alias,d,this.opts)):new b(c,d)}function c(a){var b=document.createElement("input"),c="on"+a,d=c in b;return d||(b.setAttribute(c,"return;"),d="function"==typeof b[c]),b=null,d}function d(b,c){var d=b.getAttribute("type"),e="INPUT"===b.tagName&&a.inArray(d,c.supportsInputType)!==-1||b.isContentEditable||"TEXTAREA"===b.tagName;if(!e&&"INPUT"===b.tagName){var f=document.createElement("input");f.setAttribute("type",d),e="text"===f.type,f=null}return e}function e(b,c,d){var f=d.aliases[b];return f?(f.alias&&e(f.alias,void 0,d),a.extend(!0,d,f),a.extend(!0,d,c),!0):(null===d.mask&&(d.mask=b),!1)}function f(b,c,d){function f(a,c){c=void 0!==c?c:b.getAttribute("data-inputmask-"+a),null!==c&&("string"==typeof c&&(0===a.indexOf("on")?c=window[c]:"false"===c?c=!1:"true"===c&&(c=!0)),d[a]=c)}var g,h,i,j,k=b.getAttribute("data-inputmask");if(k&&""!==k&&(k=k.replace(new RegExp("'","g"),'"'),h=JSON.parse("{"+k+"}")),h){i=void 0;for(j in h)if("alias"===j.toLowerCase()){i=h[j];break}}f("alias",i),d.alias&&e(d.alias,d,c);for(g in c){if(h){i=void 0;for(j in h)if(j.toLowerCase()===g.toLowerCase()){i=h[j];break}}f(g,i)}return a.extend(!0,c,d),c}function g(c,d){function e(b){function d(a,b,c,d){this.matches=[],this.isGroup=a||!1,this.isOptional=b||!1,this.isQuantifier=c||!1,this.isAlternator=d||!1,this.quantifier={min:1,max:1}}function e(b,d,e){var f=c.definitions[d];e=void 0!==e?e:b.matches.length;var g=b.matches[e-1];if(f&&!r){f.placeholder=a.isFunction(f.placeholder)?f.placeholder(c):f.placeholder;for(var h=f.prevalidator,i=h?h.length:0,j=1;j<f.cardinality;j++){var k=i>=j?h[j-1]:[],l=k.validator,m=k.cardinality;b.matches.splice(e++,0,{fn:l?"string"==typeof l?new RegExp(l):new function(){this.test=l}:new RegExp("."),cardinality:m?m:1,optionality:b.isOptional,newBlockMarker:void 0===g||g.def!==(f.definitionSymbol||d),casing:f.casing,def:f.definitionSymbol||d,placeholder:f.placeholder,mask:d}),g=b.matches[e-1]}b.matches.splice(e++,0,{fn:f.validator?"string"==typeof f.validator?new RegExp(f.validator):new function(){this.test=f.validator}:new RegExp("."),cardinality:f.cardinality,optionality:b.isOptional,newBlockMarker:void 0===g||g.def!==(f.definitionSymbol||d),casing:f.casing,def:f.definitionSymbol||d,placeholder:f.placeholder,mask:d})}else b.matches.splice(e++,0,{fn:null,cardinality:0,optionality:b.isOptional,newBlockMarker:void 0===g||g.def!==d,casing:null,def:c.staticDefinitionSymbol||d,placeholder:void 0!==c.staticDefinitionSymbol?d:void 0,mask:d}),r=!1}function f(a,b){a.isGroup&&(a.isGroup=!1,e(a,c.groupmarker.start,0),b!==!0&&e(a,c.groupmarker.end))}function g(a,b,c,d){b.matches.length>0&&(void 0===d||d)&&(c=b.matches[b.matches.length-1],f(c)),e(b,a)}function h(){if(t.length>0){if(m=t[t.length-1],g(k,m,o,!m.isAlternator),m.isAlternator){n=t.pop();for(var a=0;a<n.matches.length;a++)n.matches[a].isGroup=!1;t.length>0?(m=t[t.length-1],m.matches.push(n)):s.matches.push(n)}}else g(k,s,o)}function i(a){function b(a){return a===c.optionalmarker.start?a=c.optionalmarker.end:a===c.optionalmarker.end?a=c.optionalmarker.start:a===c.groupmarker.start?a=c.groupmarker.end:a===c.groupmarker.end&&(a=c.groupmarker.start),a}a.matches=a.matches.reverse();for(var d in a.matches){var e=parseInt(d);if(a.matches[d].isQuantifier&&a.matches[e+1]&&a.matches[e+1].isGroup){var f=a.matches[d];a.matches.splice(d,1),a.matches.splice(e+1,0,f)}void 0!==a.matches[d].matches?a.matches[d]=i(a.matches[d]):a.matches[d]=b(a.matches[d])}return a}for(var j,k,l,m,n,o,p,q=/(?:[?*+]|\{[0-9\+\*]+(?:,[0-9\+\*]*)?\})|[^.?*+^${[]()|\\]+|./g,r=!1,s=new d,t=[],u=[];j=q.exec(b);)if(k=j[0],r)h();else switch(k.charAt(0)){case c.escapeChar:r=!0;break;case c.optionalmarker.end:case c.groupmarker.end:if(l=t.pop(),void 0!==l)if(t.length>0){if(m=t[t.length-1],m.matches.push(l),m.isAlternator){n=t.pop();for(var v=0;v<n.matches.length;v++)n.matches[v].isGroup=!1;t.length>0?(m=t[t.length-1],m.matches.push(n)):s.matches.push(n)}}else s.matches.push(l);else h();break;case c.optionalmarker.start:t.push(new d((!1),(!0)));break;case c.groupmarker.start:t.push(new d((!0)));break;case c.quantifiermarker.start:var w=new d((!1),(!1),(!0));k=k.replace(/[{}]/g,"");var x=k.split(","),y=isNaN(x[0])?x[0]:parseInt(x[0]),z=1===x.length?y:isNaN(x[1])?x[1]:parseInt(x[1]);if("*"!==z&&"+"!==z||(y="*"===z?0:1),w.quantifier={min:y,max:z},t.length>0){var A=t[t.length-1].matches;j=A.pop(),j.isGroup||(p=new d((!0)),p.matches.push(j),j=p),A.push(j),A.push(w)}else j=s.matches.pop(),j.isGroup||(p=new d((!0)),p.matches.push(j),j=p),s.matches.push(j),s.matches.push(w);break;case c.alternatormarker:t.length>0?(m=t[t.length-1],o=m.matches.pop()):o=s.matches.pop(),o.isAlternator?t.push(o):(n=new d((!1),(!1),(!1),(!0)),n.matches.push(o),t.push(n));break;default:h()}for(;t.length>0;
12083)l=t.pop(),f(l,!0),s.matches.push(l);return s.matches.length>0&&(o=s.matches[s.matches.length-1],f(o),u.push(s)),c.numericInput&&i(u[0]),u}function f(f,g){if(null!==f&&""!==f){if(1===f.length&&c.greedy===!1&&0!==c.repeat&&(c.placeholder=""),c.repeat>0||"*"===c.repeat||"+"===c.repeat){var h="*"===c.repeat?0:"+"===c.repeat?1:c.repeat;f=c.groupmarker.start+f+c.groupmarker.end+c.quantifiermarker.start+h+","+c.repeat+c.quantifiermarker.end}var i;return void 0===b.prototype.masksCache[f]||d===!0?(i={mask:f,maskToken:e(f),validPositions:{},_buffer:void 0,buffer:void 0,tests:{},metadata:g,maskLength:void 0},d!==!0&&(b.prototype.masksCache[c.numericInput?f.split("").reverse().join(""):f]=i,i=a.extend(!0,{},b.prototype.masksCache[c.numericInput?f.split("").reverse().join(""):f]))):i=a.extend(!0,{},b.prototype.masksCache[c.numericInput?f.split("").reverse().join(""):f]),i}}function g(a){return a=a.toString()}var h;if(a.isFunction(c.mask)&&(c.mask=c.mask(c)),a.isArray(c.mask)){if(c.mask.length>1){c.keepStatic=null===c.keepStatic||c.keepStatic;var i="(";return a.each(c.numericInput?c.mask.reverse():c.mask,function(b,c){i.length>1&&(i+=")|("),i+=g(void 0===c.mask||a.isFunction(c.mask)?c:c.mask)}),i+=")",f(i,c.mask)}c.mask=c.mask.pop()}return c.mask&&(h=void 0===c.mask.mask||a.isFunction(c.mask.mask)?f(g(c.mask),c.mask):f(g(c.mask.mask),c.mask)),h}function h(e,f,g){function i(a,b,c){b=b||0;var d,e,f,h=[],i=0,j=p();ja=void 0!==ha?ha.maxLength:void 0,ja===-1&&(ja=void 0);do a===!0&&n().validPositions[i]?(f=n().validPositions[i],e=f.match,d=f.locator.slice(),h.push(c===!0?f.input:I(i,e))):(f=s(i,d,i-1),e=f.match,d=f.locator.slice(),(g.jitMasking===!1||i<j||Number.isFinite(g.jitMasking)&&g.jitMasking>i)&&h.push(I(i,e))),i++;while((void 0===ja||i<ja)&&(null!==e.fn||""!==e.def)||b>i);return""===h[h.length-1]&&h.pop(),n().maskLength=i+1,h}function n(){return f}function o(a){var b=n();b.buffer=void 0,a!==!0&&(b._buffer=void 0,b.validPositions={},b.p=0)}function p(a,b,c){var d=-1,e=-1,f=c||n().validPositions;void 0===a&&(a=-1);for(var g in f){var h=parseInt(g);f[h]&&(b||null!==f[h].match.fn)&&(h<=a&&(d=h),h>=a&&(e=h))}return d!==-1&&a-d>1||e<a?d:e}function q(b,c,d,e){function f(a){var b=n().validPositions[a];if(void 0!==b&&null===b.match.fn){var c=n().validPositions[a-1],d=n().validPositions[a+1];return void 0!==c&&void 0!==d}return!1}var h,i=b,j=a.extend(!0,{},n().validPositions),k=!1;for(n().p=b,h=c-1;h>=i;h--)void 0!==n().validPositions[h]&&(d===!0||!f(h)&&g.canClearPosition(n(),h,p(),e,g)!==!1)&&delete n().validPositions[h];for(o(!0),h=i+1;h<=p();){for(;void 0!==n().validPositions[i];)i++;var l=n().validPositions[i];if(h<i&&(h=i+1),void 0===n().validPositions[h]&&D(h)||void 0!==l)h++;else{var m=s(h);k===!1&&j[i]&&j[i].match.def===m.match.def?(n().validPositions[i]=a.extend(!0,{},j[i]),n().validPositions[i].input=m.input,delete n().validPositions[h],h++):u(i,m.match.def)?C(i,m.input||I(h),!0)!==!1&&(delete n().validPositions[h],h++,k=!0):D(h)||(h++,i--),i++}}o(!0)}function r(a,b){for(var c,d=a,e=p(),f=n().validPositions[e]||w(0)[0],h=void 0!==f.alternation?f.locator[f.alternation].toString().split(","):[],i=0;i<d.length&&(c=d[i],!(c.match&&(g.greedy&&c.match.optionalQuantifier!==!0||(c.match.optionality===!1||c.match.newBlockMarker===!1)&&c.match.optionalQuantifier!==!0)&&(void 0===f.alternation||f.alternation!==c.alternation||void 0!==c.locator[f.alternation]&&B(c.locator[f.alternation].toString().split(","),h)))||b===!0&&(null!==c.match.fn||/[0-9a-bA-Z]/.test(c.match.def)));i++);return c}function s(a,b,c){return n().validPositions[a]||r(w(a,b?b.slice():b,c))}function t(a){return n().validPositions[a]?n().validPositions[a]:w(a)[0]}function u(a,b){for(var c=!1,d=w(a),e=0;e<d.length;e++)if(d[e].match&&d[e].match.def===b){c=!0;break}return c}function v(b,c){var d,e;return(n().tests[b]||n().validPositions[b])&&a.each(n().tests[b]||[n().validPositions[b]],function(a,b){var f=b.alternation?b.locator[b.alternation].toString().indexOf(c):-1;(void 0===e||f<e)&&f!==-1&&(d=b,e=f)}),d}function w(b,c,d){function e(c,d,f,h){function j(f,h,l){function q(b,c){var d=0===a.inArray(b,c.matches);return d||a.each(c.matches,function(a,e){if(e.isQuantifier===!0&&(d=q(b,c.matches[a-1]
12083)))return!1}),d}function r(a,b){var c=v(a,b);return c?c.locator.slice(c.alternation+1):void 0}function s(a,c){return null===a.match.fn&&null!==c.match.fn&&c.match.fn.test(a.match.def,n(),b,!1,g,!1)}if(k>1e4)throw"Inputmask: There is probably an error in your mask definition or in the code. Create an issue on github with an example of the mask you are using. "+n().mask;if(k===b&&void 0===f.matches)return m.push({match:f,locator:h.reverse(),cd:p}),!0;if(void 0!==f.matches){if(f.isGroup&&l!==f){if(f=j(c.matches[a.inArray(f,c.matches)+1],h))return!0}else if(f.isOptional){var t=f;if(f=e(f,d,h,l)){if(i=m[m.length-1].match,!q(i,t))return!0;o=!0,k=b}}else if(f.isAlternator){var u,w=f,x=[],y=m.slice(),z=h.length,A=d.length>0?d.shift():-1;if(A===-1||"string"==typeof A){var B,C=k,D=d.slice(),E=[];if("string"==typeof A)E=A.split(",");else for(B=0;B<w.matches.length;B++)E.push(B);for(var F=0;F<E.length;F++){if(B=parseInt(E[F]),m=[],d=r(k,B)||D.slice(),f=j(w.matches[B]||c.matches[B],[B].concat(h),l)||f,f!==!0&&void 0!==f&&E[E.length-1]<w.matches.length){var G=a.inArray(f,c.matches)+1;c.matches.length>G&&(f=j(c.matches[G],[G].concat(h.slice(1,h.length)),l),f&&(E.push(G.toString()),a.each(m,function(a,b){b.alternation=h.length-1})))}u=m.slice(),k=C,m=[];for(var H=0;H<u.length;H++){var I=u[H],J=!1;I.alternation=I.alternation||z;for(var K=0;K<x.length;K++){var L=x[K];if(("string"!=typeof A||a.inArray(I.locator[I.alternation].toString(),E)!==-1)&&(I.match.def===L.match.def||s(I,L))){J=I.match.mask===L.match.mask,L.locator[I.alternation].toString().indexOf(I.locator[I.alternation])===-1&&(L.locator[I.alternation]=L.locator[I.alternation]+","+I.locator[I.alternation],L.alternation=I.alternation,null==I.match.fn&&(L.na=L.na||I.locator[I.alternation].toString(),L.na.indexOf(I.locator[I.alternation])===-1&&(L.na=L.na+","+I.locator[I.alternation])));break}}J||x.push(I)}}"string"==typeof A&&(x=a.map(x,function(b,c){if(isFinite(c)){var d,e=b.alternation,f=b.locator[e].toString().split(",");b.locator[e]=void 0,b.alternation=void 0;for(var g=0;g<f.length;g++)d=a.inArray(f[g],E)!==-1,d&&(void 0!==b.locator[e]?(b.locator[e]+=",",b.locator[e]+=f[g]):b.locator[e]=parseInt(f[g]),b.alternation=e);if(void 0!==b.locator[e])return b}})),m=y.concat(x),k=b,o=m.length>0,d=D.slice()}else f=j(w.matches[A]||c.matches[A],[A].concat(h),l);if(f)return!0}else if(f.isQuantifier&&l!==c.matches[a.inArray(f,c.matches)-1])for(var M=f,N=d.length>0?d.shift():0;N<(isNaN(M.quantifier.max)?N+1:M.quantifier.max)&&k<=b;N++){var O=c.matches[a.inArray(M,c.matches)-1];if(f=j(O,[N].concat(h),O)){if(i=m[m.length-1].match,i.optionalQuantifier=N>M.quantifier.min-1,q(i,O)){if(N>M.quantifier.min-1){o=!0,k=b;break}return!0}return!0}}else if(f=e(f,d,h,l))return!0}else k++}for(var l=d.length>0?d.shift():0;l<c.matches.length;l++)if(c.matches[l].isQuantifier!==!0){var q=j(c.matches[l],[l].concat(f),h);if(q&&k===b)return q;if(k>b)break}}function f(b){var c=[];return a.isArray(b)||(b=[b]),b.length>0&&(void 0===b[0].alternation?(c=r(b.slice()).locator.slice(),0===c.length&&(c=b[0].locator.slice())):a.each(b,function(a,b){if(""!==b.def)if(0===c.length)c=b.locator.slice();else for(var d=0;d<c.length;d++)b.locator[d]&&c[d].toString().indexOf(b.locator[d])===-1&&(c[d]+=","+b.locator[d])})),c}function h(a){return g.keepStatic&&b>0&&a.length>1+(""===a[a.length-1].match.def?1:0)&&a[0].match.optionality!==!0&&a[0].match.optionalQuantifier!==!0&&null===a[0].match.fn&&!/[0-9a-bA-Z]/.test(a[0].match.def)?[r(a)]:a}var i,j=n().maskToken,k=c?d:0,l=c?c.slice():[0],m=[],o=!1,p=c?c.join(""):"";if(b>-1){if(void 0===c){for(var q,s=b-1;void 0===(q=n().validPositions[s]||n().tests[s])&&s>-1;)s--;void 0!==q&&s>-1&&(l=f(q),p=l.join(""),k=s)}if(n().tests[b]&&n().tests[b][0].cd===p)return h(n().tests[b]);for(var t=l.shift();t<j.length;t++){var u=e(j[t],l,[t]);if(u&&k===b||k>b)break}}return(0===m.length||o)&&m.push({match:{fn:null,cardinality:0,optionality:!0,casing:null,def:"",placeholder:""},locator:[],cd:p}),void 0!==c&&n().tests[b]?h(a.extend(!0,[],m)):(n().tests[b]=a.extend(!0,[],m),h(n().tests[b]))}function x(){return void 0===n()._buffer&&(n()._buffer=i(!1,1),void 0===n().buffer&&n()._buffer.slice()),n()._buffer}function y(a){return void 0!==n().buffer&&a!==!0||(n().buffer=i(!0,p(),!0)),n().buffer}function z(a,b,c){var d;if(a===!0)o(),a=0,b=c.length;else for(d=a;d<b;d++)delete n().validPositions[d];for(d=a;d<b;d++)o(!0),c[d]!==g.skipOptionalPartCharacter&&C(d,c[d],!0,!0)}function A(a,c,d){switch(g.casing||c.casing){case"upper":a=a.toUpperCase();break;case"lower":a=a.toLowerCase();break;case"title":var e=n().validPositions[d-1];a=0===d||e&&e.input===String.fromCharCode(b.keyCode.SPACE)?a.toUpperCase():a.toLowerCase()}return a}function B(b,c){for(var d=g.greedy?c:c.slice(0,1),e=!1,f=0;f<b.length;f++)if(a.inArray(b[f],d)!==-1){e=!0;break}return e}function C(c,d,e,f,h){function i(a){return ma?a.begin-a.end>1||a.begin-a.end===1&&g.insertMode:a.end-a.begin>1||a.end-a.begin===1&&g.insertMode}function j(b,d,e){var h=!1;return a.each(w(b),function(j,k){for(var l=k.match,r=d?1:0,s="",t=l.cardinality;t>r;t--)s+=G(b-(t-1));if(d&&(s+=d),y(!0),h=null!=l.fn?l.fn.test(s,n(),b,e,g,i(c)):(d===l.def||d===g.skipOptionalPartCharacter)&&""!==l.def&&{c:l.placeholder||l.def,pos:b},h!==!1){var u=void 0!==h.c?h.c:d;u=u===g.skipOptionalPartCharacter&&null===l.fn?l.placeholder||l.def:u;var v=b,w=y();if(void 0!==h.remove&&(a.isArray(h.remove)||(h.remove=[h.remove]),a.each(h.remove.sort(function(a,b){return b-
12083a}),function(a,b){q(b,b+1,!0)})),void 0!==h.insert&&(a.isArray(h.insert)||(h.insert=[h.insert]),a.each(h.insert.sort(function(a,b){return a-b}),function(a,b){C(b.pos,b.c,!0,f)})),h.refreshFromBuffer){var x=h.refreshFromBuffer;if(e=!0,z(x===!0?x:x.start,x.end,w),void 0===h.pos&&void 0===h.c)return h.pos=p(),!1;if(v=void 0!==h.pos?h.pos:b,v!==b)return h=a.extend(h,C(v,u,!0,f)),!1}else if(h!==!0&&void 0!==h.pos&&h.pos!==b&&(v=h.pos,z(b,v,y().slice()),v!==b))return h=a.extend(h,C(v,u,!0)),!1;return(h===!0||void 0!==h.pos||void 0!==h.c)&&(j>0&&o(!0),m(v,a.extend({},k,{input:A(u,l,v)}),f,i(c))||(h=!1),!1)}}),h}function k(b,c,d){var e,h,i,j,k,l,m,q,r=a.extend(!0,{},n().validPositions),s=!1,t=p();for(j=n().validPositions[t];t>=0;t--)if(i=n().validPositions[t],i&&void 0!==i.alternation){if(e=t,h=n().validPositions[e].alternation,j.locator[i.alternation]!==i.locator[i.alternation])break;j=i}if(void 0!==h){q=parseInt(e);var u=void 0!==j.locator[j.alternation||h]?j.locator[j.alternation||h]:m[0];u.length>0&&(u=u.split(",")[0]);var v=n().validPositions[q],x=n().validPositions[q-1];a.each(w(q,x?x.locator:void 0,q-1),function(e,i){m=i.locator[h]?i.locator[h].toString().split(","):[];for(var j=0;j<m.length;j++){var t=[],w=0,x=0,y=!1;if(u<m[j]&&(void 0===i.na||a.inArray(m[j],i.na.split(","))===-1)){n().validPositions[q]=a.extend(!0,{},i);var z=n().validPositions[q].locator;for(n().validPositions[q].locator[h]=parseInt(m[j]),null==i.match.fn?(v.input!==i.match.def&&(y=!0,v.generatedInput!==!0&&t.push(v.input)),x++,n().validPositions[q].generatedInput=!/[0-9a-bA-Z]/.test(i.match.def),n().validPositions[q].input=i.match.def):n().validPositions[q].input=v.input,k=q+1;k<p(void 0,!0)+1;k++)l=n().validPositions[k],l&&l.generatedInput!==!0&&/[0-9a-bA-Z]/.test(l.input)?t.push(l.input):k<b&&w++,delete n().validPositions[k];for(y&&t[0]===i.match.def&&t.shift(),o(!0),s=!0;t.length>0;){var A=t.shift();if(A!==g.skipOptionalPartCharacter&&!(s=C(p(void 0,!0)+1,A,!1,f,!0)))break}if(s){n().validPositions[q].locator=z;var B=p(b)+1;for(k=q+1;k<p()+1;k++)l=n().validPositions[k],(void 0===l||null==l.match.fn)&&k<b+(x-w)&&x++;b+=x-w,s=C(b>B?B:b,c,d,f,!0)}if(s)return!1;o(),n().validPositions=a.extend(!0,{},r)}}})}return s}function l(b,c){var d=n().validPositions[c];if(d)for(var e=d.locator,f=e.length,g=b;g<c;g++)if(void 0===n().validPositions[g]&&!D(g,!0)){var h=w(g),i=h[0],j=-1;a.each(h,function(a,b){for(var c=0;c<f&&(void 0!==b.locator[c]&&B(b.locator[c].toString().split(","),e[c].toString().split(",")));c++)j<c&&(j=c,i=b)}),m(g,a.extend({},i,{input:i.match.placeholder||i.match.def}),!0)}}function m(b,c,d,e){if(e||g.insertMode&&void 0!==n().validPositions[b]&&void 0===d){var f,h=a.extend(!0,{},n().validPositions),i=p(void 0,!0);for(f=b;f<=i;f++)delete n().validPositions[f];n().validPositions[b]=a.extend(!0,{},c);var j,k=!0,l=n().validPositions,m=!1,q=n().maskLength;for(f=j=b;f<=i;f++){var r=h[f];if(void 0!==r)for(var s=j;s<n().maskLength&&(null==r.match.fn&&l[f]&&(l[f].match.optionalQuantifier===!0||l[f].match.optionality===!0)||null!=r.match.fn);){if(s++,m===!1&&h[s]&&h[s].match.def===r.match.def)n().validPositions[s]=a.extend(!0,{},h[s]),n().validPositions[s].input=r.input,t(s),j=s,k=!0;else if(u(s,r.match.def)){var v=C(s,r.input,!0,!0);k=v!==!1,j=v.caret||v.insert?p():s,m=!0}else k=r.generatedInput===!0;if(n().maskLength<q&&(n().maskLength=q),k)break}if(!k)break}if(!k)return n().validPositions=a.extend(!0,{},h),o(!0),!1}else n().validPositions[b]=a.extend(!0,{},c);return o(!0),!0}function t(b){for(var c=b-1;c>-1&&!n().validPositions[c];c--);var d,e;for(c++;c<b;c++)void 0===n().validPositions[c]&&(g.jitMasking===!1||g.jitMasking>c)&&(e=w(c,s(c-1).locator,c-1).slice(),""===e[e.length-1].match.def&&e.pop(),d=r(e),d&&(d.match.def===g.radixPointDefinitionSymbol||!D(c,!0)||a.inArray(g.radixPoint,y())<c&&d.match.fn&&d.match.fn.test(I(c),n(),c,!1,g))&&(x=j(c,d.match.placeholder||(null==d.match.fn?d.match.def:""!==I(c)?I(c):y()[c]),!0),x!==!1&&(n().validPositions[x.pos||c].generatedInput=!0)))}e=e===!0;var v=c;void 0!==c.begin&&(v=ma&&!i(c)?c.end:c.begin);var x=!1,F=a.extend(!0,{},n().validPositions);if(t(v),i(c)&&(Q(void 0,b.keyCode.DELETE,c),v=n().p),v<n().maskLength&&(x=j(v,d,e),(!e||f===!0)&&x===!1)){var H=n().validPositions[v];if(!H||null!==H.match.fn||H.match.def!==d&&d!==g.skipOptionalPartCharacter){if((g.insertMode||void 0===n().validPositions[E(v)])&&!D(v,!0)){var J=w(v).slice();""===J[J.length-1].match.def&&J.pop();var K=r(J,!0);
12083K&&null===K.match.fn&&(K=K.match.placeholder||K.match.def,j(v,K,e),n().validPositions[v].generatedInput=!0);for(var L=v+1,M=E(v);L<=M;L++)if(x=j(L,d,e),x!==!1){l(v,void 0!==x.pos?x.pos:L),v=L;break}}}else x={caret:E(v)}}return x===!1&&g.keepStatic&&!e&&h!==!0&&(x=k(v,d,e)),x===!0&&(x={pos:v}),a.isFunction(g.postValidation)&&x!==!1&&!e&&f!==!0&&(x=!!g.postValidation(y(!0),x,g)&&x),void 0===x.pos&&(x.pos=v),x===!1&&(o(!0),n().validPositions=a.extend(!0,{},F)),x}function D(a,b){var c;if(b?(c=s(a).match,""===c.def&&(c=t(a).match)):c=t(a).match,null!=c.fn)return c.fn;if(b!==!0&&a>-1){var d=w(a);return d.length>1+(""===d[d.length-1].match.def?1:0)}return!1}function E(a,b){var c=n().maskLength;if(a>=c)return c;for(var d=a;++d<c&&(b===!0&&(t(d).match.newBlockMarker!==!0||!D(d))||b!==!0&&!D(d)););return d}function F(a,b){var c,d=a;if(d<=0)return 0;for(;--d>0&&(b===!0&&t(d).match.newBlockMarker!==!0||b!==!0&&!D(d)&&(c=w(d),c.length<2||2===c.length&&""===c[1].match.def)););return d}function G(a){return void 0===n().validPositions[a]?I(a):n().validPositions[a].input}function H(b,c,d,e,f){if(e&&a.isFunction(g.onBeforeWrite)){var h=g.onBeforeWrite(e,c,d,g);if(h){if(h.refreshFromBuffer){var i=h.refreshFromBuffer;z(i===!0?i:i.start,i.end,h.buffer||c),c=y(!0)}void 0!==d&&(d=void 0!==h.caret?h.caret:d)}}b.inputmask._valueSet(c.join("")),void 0===d||void 0!==e&&"blur"===e.type?ea(b,c,d):L(b,d),f===!0&&(oa=!0,a(b).trigger("input"))}function I(a,b){if(b=b||t(a).match,void 0!==b.placeholder)return b.placeholder;if(null===b.fn){if(a>-1&&void 0===n().validPositions[a]){var c,d=w(a),e=[];if(d.length>1+(""===d[d.length-1].match.def?1:0))for(var f=0;f<d.length;f++)if(d[f].match.optionality!==!0&&d[f].match.optionalQuantifier!==!0&&(null===d[f].match.fn||void 0===c||d[f].match.fn.test(c.match.def,n(),a,!0,g)!==!1)&&(e.push(d[f]),null===d[f].match.fn&&(c=d[f]),e.length>1&&/[0-9a-bA-Z]/.test(e[0].match.def)))return g.placeholder.charAt(a%g.placeholder.length)}return b.def}return g.placeholder.charAt(a%g.placeholder.length)}function J(c,d,e,f,h,i){function j(){var a=!1,b=x().slice(m,E(m)).join("").indexOf(l);if(b!==-1&&!D(m)){a=!0;for(var c=x().slice(m,m+b),d=0;d<c.length;d++)if(" "!==c[d]){a=!1;break}}return a}var k=f.slice(),l="",m=0,q=void 0;if(o(),n().p=E(-1),!e)if(g.autoUnmask!==!0){var r=x().slice(0,E(-1)).join(""),t=k.join("").match(new RegExp("^"+b.escapeRegex(r),"g"));t&&t.length>0&&(k.splice(0,t.length*r.length),m=E(m))}else m=E(m);if(a.each(k,function(b,d){if(void 0!==d){var f=new a.Event("keypress");f.which=d.charCodeAt(0),l+=d;var h=p(void 0,!0),i=n().validPositions[h],k=s(h+1,i?i.locator.slice():void 0,h);if(!j()||e||g.autoUnmask){var r=e?b:null==k.match.fn&&k.match.optionality&&h+1<n().p?h+1:n().p;q=S.call(c,f,!0,!1,e,r),m=r+1,l=""}else q=S.call(c,f,!0,!1,!0,h+1);if(!e&&a.isFunction(g.onBeforeWrite)&&(q=g.onBeforeWrite(f,y(),q.forwardPosition,g),q&&q.refreshFromBuffer)){var t=q.refreshFromBuffer;z(t===!0?t:t.start,t.end,q.buffer),o(!0),q.caret&&(n().p=q.caret)}}}),d){var u=void 0,v=p();document.activeElement===c&&(h||q)&&(u=L(c).begin,h&&q===!1&&(u=E(p(u))),q&&i!==!0&&(u<v+1||v===-1)&&(u=g.numericInput&&void 0===q.caret?F(q.forwardPosition):q.forwardPosition)),H(c,y(),u,h||new a.Event("checkval"))}}function K(b){if(b&&void 0===b.inputmask)return b.value;var c=[],d=n().validPositions;for(var e in d)d[e].match&&null!=d[e].match.fn&&c.push(d[e].input);var f=0===c.length?"":(ma?c.reverse():c).join("");if(a.isFunction(g.onUnMask)){var h=(ma?y().slice().reverse():y()).join("");f=g.onUnMask(h,f,g)||f}return f}function L(a,b,c,d){function e(a){if(d!==!0&&ma&&"number"==typeof a&&(!g.greedy||""!==g.placeholder)){var b=y().join("").length;a=b-a}return a}var f;if("number"!=typeof b)return a.setSelectionRange?(b=a.selectionStart,c=a.selectionEnd):window.getSelection?(f=window.getSelection().getRangeAt(0),f.commonAncestorContainer.parentNode!==a&&f.commonAncestorContainer!==a||(b=f.startOffset,c=f.endOffset)):document.selection&&document.selection.createRange&&(f=document.selection.createRange(),b=0-f.duplicate().moveStart("character",-a.inputmask._valueGet().length),c=b+f.text.length),{begin:e(b),end:e(c)};b=e(b),c=e(c),c="number"==typeof c?c:b;var h=parseInt(((a.ownerDocument.defaultView||window).getComputedStyle?(a.ownerDocument.defaultView||window).getComputedStyle(a,null):a.currentStyle).fontSize)*c;if(a.scrollLeft=h>a.scrollWidth?h:0,j||g.insertMode!==!1||b!==c||c++,a.setSelectionRange)a.selectionStart=b,a.selectionEnd=c;else if(window.getSelection){if(f=document.createRange(),void 0===a.firstChild||null===a.firstChild){var i=document.createTextNode("");a.appendChild(i)}f.setStart(a.firstChild,b<a.inputmask._valueGet().length?b:a.inputmask._valueGet().length),f.setEnd(a.firstChild,c<a.inputmask._valueGet().length?c:a.inputmask._valueGet().length),f.collapse(!0);var k=window.getSelection();k.removeAllRanges(),k.addRange(f)}else a.createTextRange&&(f=a.createTextRange(),f.collapse(!0),f.moveEnd("character",c),f.moveStart("character",b),f.select());ea(a,void 0,{begin:b,end:c})}function M(b){var c,d,e=y(),f=e.length,g=p(),h={},i=n().validPositions[g],j=void 0!==i?i.locator.slice():void 0;for(c=g+1;c<e.length;c++)d=s(c,j,c-1),j=d.locator.slice(),h[c]=a.extend(!0,{},d);var k=i&&void 0!==i.alternation?i.locator[i.alternation]:void 0;for(c=f-1;c>g&&(d=h[c],(d.match.optionality||d.match.optionalQuantifier||k&&(k!==h[c].locator[i.alternation]&&null!=d.match.fn||null===d.match.fn&&d.locator[i.alternation]&&B(d.locator[i.alternation].toString().split(","),k.toString().split(","))&&""!==w(c)[0].def))&&e[c]===I(c,d.match));c--)f--;return b?{l:f,def:h[f]?h[f].match:void 0}:f}function N(a){for(var b=M(),c=a.length-1;c>b&&!D(c);c--);return a.splice(b,c+1-b),a}function O(b){if(a.isFunction(g.isComplete))return g.isComplete(b,g);if("*"!==g.repeat){var c=!1,d=M(!0),e=F(d.l);if(void 0===d.def||d.def.newBlockMarker||d.def.optionality||d.def.optionalQuantifier){c=!0;for(var f=0;f<=e;f++){var h=s(f).match;if(null!==h.fn&&void 0===n().validPositions[f]&&h.optionality!==!0&&h.optionalQuantifier!==!0||null===h.fn&&b[f]!==I(f,h)){c=!1;break}}}return c}}function P(b){function c(b){if(a.valHooks&&(void 0===a.valHooks[b]||a.valHooks[b].inputmaskpatch!==!0)){var c=a.valHooks[b]&&a.valHooks[b].get?a.valHooks[b].get:function(a){return a.value},d=a.valHooks[b]&&a.valHooks[b].set?a.valHooks[b].set:function(a,b){return a.value=b,a};
12083a.valHooks[b]={get:function(a){if(a.inputmask){if(a.inputmask.opts.autoUnmask)return a.inputmask.unmaskedvalue();var b=c(a);return p(void 0,void 0,a.inputmask.maskset.validPositions)!==-1||g.nullable!==!0?b:""}return c(a)},set:function(b,c){var e,f=a(b);return e=d(b,c),b.inputmask&&f.trigger("setvalue"),e},inputmaskpatch:!0}}}function d(){return this.inputmask?this.inputmask.opts.autoUnmask?this.inputmask.unmaskedvalue():p()!==-1||g.nullable!==!0?document.activeElement===this&&g.clearMaskOnLostFocus?(ma?N(y().slice()).reverse():N(y().slice())).join(""):h.call(this):"":h.call(this)}function e(b){i.call(this,b),this.inputmask&&a(this).trigger("setvalue")}function f(b){ra.on(b,"mouseenter",function(b){var c=a(this),d=this,e=d.inputmask._valueGet();e!==y().join("")&&c.trigger("setvalue")})}var h,i;if(!b.inputmask.__valueGet){if(g.noValuePatching!==!0){if(Object.getOwnPropertyDescriptor){"function"!=typeof Object.getPrototypeOf&&(Object.getPrototypeOf="object"==typeof"test".__proto__?function(a){return a.__proto__}:function(a){return a.constructor.prototype});var j=Object.getPrototypeOf?Object.getOwnPropertyDescriptor(Object.getPrototypeOf(b),"value"):void 0;j&&j.get&&j.set?(h=j.get,i=j.set,Object.defineProperty(b,"value",{get:d,set:e,configurable:!0})):"INPUT"!==b.tagName&&(h=function(){return this.textContent},i=function(a){this.textContent=a},Object.defineProperty(b,"value",{get:d,set:e,configurable:!0}))}else document.__lookupGetter__&&b.__lookupGetter__("value")&&(h=b.__lookupGetter__("value"),i=b.__lookupSetter__("value"),b.__defineGetter__("value",d),b.__defineSetter__("value",e));b.inputmask.__valueGet=h,b.inputmask.__valueSet=i}b.inputmask._valueGet=function(a){return ma&&a!==!0?h.call(this.el).split("").reverse().join(""):h.call(this.el)},b.inputmask._valueSet=function(a,b){i.call(this.el,null===a||void 0===a?"":b!==!0&&ma?a.split("").reverse().join(""):a)},void 0===h&&(h=function(){return this.value},i=function(a){this.value=a},c(b.type),f(b))}}function Q(c,d,e,f){function h(){if(g.keepStatic){for(var b=[],d=p(-1,!0),e=a.extend(!0,{},n().validPositions),f=n().validPositions[d];d>=0;d--){var h=n().validPositions[d];if(h){if(h.generatedInput!==!0&&/[0-9a-bA-Z]/.test(h.input)&&b.push(h.input),delete n().validPositions[d],void 0!==h.alternation&&h.locator[h.alternation]!==f.locator[h.alternation])break;f=h}}if(d>-1)for(n().p=E(p(-1,!0));b.length>0;){var i=new a.Event("keypress");i.which=b.pop().charCodeAt(0),S.call(c,i,!0,!1,!1,n().p)}else n().validPositions=a.extend(!0,{},e)}}if((g.numericInput||ma)&&(d===b.keyCode.BACKSPACE?d=b.keyCode.DELETE:d===b.keyCode.DELETE&&(d=b.keyCode.BACKSPACE),ma)){var i=e.end;e.end=e.begin,e.begin=i}d===b.keyCode.BACKSPACE&&(e.end-e.begin<1||g.insertMode===!1)?(e.begin=F(e.begin),void 0===n().validPositions[e.begin]||n().validPositions[e.begin].input!==g.groupSeparator&&n().validPositions[e.begin].input!==g.radixPoint||e.begin--):d===b.keyCode.DELETE&&e.begin===e.end&&(e.end=D(e.end,!0)?e.end+1:E(e.end)+1,void 0===n().validPositions[e.begin]||n().validPositions[e.begin].input!==g.groupSeparator&&n().validPositions[e.begin].input!==g.radixPoint||e.end++),q(e.begin,e.end,!1,f),f!==!0&&h();var j=p(e.begin,!0);j<e.begin?n().p=E(j):f!==!0&&(n().p=e.begin)}function R(d){var e=this,f=a(e),h=d.keyCode,i=L(e);if(h===b.keyCode.BACKSPACE||h===b.keyCode.DELETE||l&&h===b.keyCode.BACKSPACE_SAFARI||d.ctrlKey&&h===b.keyCode.X&&!c("cut"))d.preventDefault(),Q(e,h,i),H(e,y(!0),n().p,d,e.inputmask._valueGet()!==y().join("")),e.inputmask._valueGet()===x().join("")?f.trigger("cleared"):O(y())===!0&&f.trigger("complete"),g.showTooltip&&(e.title=g.tooltip||n().mask);else if(h===b.keyCode.END||h===b.keyCode.PAGE_DOWN){d.preventDefault();var j=E(p());g.insertMode||j!==n().maskLength||d.shiftKey||j--,L(e,d.shiftKey?i.begin:j,j,!0)}else h===b.keyCode.HOME&&!d.shiftKey||h===b.keyCode.PAGE_UP?(d.preventDefault(),L(e,0,d.shiftKey?i.begin:0,!0)):(g.undoOnEscape&&h===b.keyCode.ESCAPE||90===h&&d.ctrlKey)&&d.altKey!==!0?(J(e,!0,!1,ga.split("")),f.trigger("click")):h!==b.keyCode.INSERT||d.shiftKey||d.ctrlKey?g.tabThrough===!0&&h===b.keyCode.TAB?(d.shift
12083Key===!0?(null===t(i.begin).match.fn&&(i.begin=E(i.begin)),i.end=F(i.begin,!0),i.begin=F(i.end,!0)):(i.begin=E(i.begin,!0),i.end=E(i.begin,!0),i.end<n().maskLength&&i.end--),i.begin<n().maskLength&&(d.preventDefault(),L(e,i.begin,i.end))):d.shiftKey||(g.insertMode===!1?h===b.keyCode.RIGHT?setTimeout(function(){var a=L(e);L(e,a.begin)},0):h===b.keyCode.LEFT&&setTimeout(function(){var a=L(e);L(e,ma?a.begin+1:a.begin-1)},0):setTimeout(function(){ea(e)},0)):(g.insertMode=!g.insertMode,L(e,g.insertMode||i.begin!==n().maskLength?i.begin:i.begin-1));g.onKeyDown.call(this,d,y(),L(e).begin,g),pa=a.inArray(h,g.ignorables)!==-1}function S(c,d,e,f,h){var i=this,j=a(i),k=c.which||c.charCode||c.keyCode;if(!(d===!0||c.ctrlKey&&c.altKey)&&(c.ctrlKey||c.metaKey||pa))return k===b.keyCode.ENTER&&ga!==y().join("")&&(ga=y().join(""),setTimeout(function(){j.trigger("change")},0)),!0;if(k){46===k&&c.shiftKey===!1&&","===g.radixPoint&&(k=44);var l,m=d?{begin:h,end:h}:L(i),p=String.fromCharCode(k);n().writeOutBuffer=!0;var q=C(m,p,f);if(q!==!1&&(o(!0),l=void 0!==q.caret?q.caret:d?q.pos+1:E(q.pos),n().p=l),e!==!1){var r=this;if(setTimeout(function(){g.onKeyValidation.call(r,k,q,g)},0),n().writeOutBuffer&&q!==!1){var s=y();H(i,s,g.numericInput&&void 0===q.caret?F(l):l,c,d!==!0),d!==!0&&setTimeout(function(){O(s)===!0&&j.trigger("complete")},0)}}if(g.showTooltip&&(i.title=g.tooltip||n().mask),c.preventDefault(),d)return q.forwardPosition=l,q}}function T(b){var c,d=this,e=b.originalEvent||b,f=a(d),h=d.inputmask._valueGet(!0),i=L(d);ma&&(c=i.end,i.end=i.begin,i.begin=c);var j=h.substr(0,i.begin),k=h.substr(i.end,h.length);if(j===(ma?x().reverse():x()).slice(0,i.begin).join("")&&(j=""),k===(ma?x().reverse():x()).slice(i.end).join("")&&(k=""),ma&&(c=j,j=k,k=c),window.clipboardData&&window.clipboardData.getData)h=j+window.clipboardData.getData("Text")+k;else{if(!e.clipboardData||!e.clipboardData.getData)return!0;h=j+e.clipboardData.getData("text/plain")+k}var l=h;if(a.isFunction(g.onBeforePaste)){if(l=g.onBeforePaste(h,g),l===!1)return b.preventDefault();l||(l=h)}return J(d,!1,!1,ma?l.split("").reverse():l.toString().split("")),H(d,y(),E(p()),b,ga!==y().join("")),O(y())===!0&&f.trigger("complete"),b.preventDefault()}function U(c){var d=this,e=d.inputmask._valueGet();if(y().join("")!==e){
12084var f=L(d);if(e=e.replace(new RegExp("("+b.escapeRegex(x().join(""))+")*"),""),k){var g=e.replace(y().join(""),"");if(1===g.length){var h=new a.Event("keypress");return h.which=g.charCodeAt(0),S.call(d,h,!0,!0,!1,n().validPositions[f.begin-1]?f.begin:f.begin-1),!1}}if(f.begin>e.length&&(L(d,e.length),f=L(d)),y().length-e.length!==1||e.charAt(f.begin)===y()[f.begin]||e.charAt(f.begin+1)===y()[f.begin]||D(f.begin)){for(var i=p()+1,j=y().slice(i).join("");null===e.match(b.escapeRegex(j)+"$");)j=j.slice(1);e=e.replace(j,""),e=e.split(""),J(d,!0,!1,e,c,f.begin<i),O(y())===!0&&a(d).trigger("complete")}else c.keyCode=b.keyCode.BACKSPACE,R.call(d,c);c.preventDefault()}}function V(b){var c=this,d=c.inputmask._valueGet();J(c,!0,!1,(a.isFunction(g.onBeforeMask)?g.onBeforeMask(d,g)||d:d).split("")),ga=y().join(""),(g.clearMaskOnLostFocus||g.clearIncomplete)&&c.inputmask._valueGet()===x().join("")&&c.inputmask._valueSet("")}function W(a){var b=this,c=b.inputmask._valueGet();g.showMaskOnFocus&&(!g.showMaskOnHover||g.showMaskOnHover&&""===c)?b.inputmask._valueGet()!==y().join("")&&H(b,y(),E(p())):qa===!1&&L(b,E(p())),g.positionCaretOnTab===!0&&setTimeout(function(){Y.apply(this,[a])},0),ga=y().join("")}function X(a){var b=this;if(qa=!1,g.clearMaskOnLostFocus&&document.activeElement!==b){var c=y().slice(),d=b.inputmask._valueGet();d!==b.getAttribute("placeholder")&&""!==d&&(p()===-1&&d===x().join("")?c=[]:N(c),H(b,c))}}function Y(b){function c(b){if(""!==g.radixPoint){var c=n().validPositions;if(void 0===c[b]||c[b].input===I(b)){if(b<E(-1))return!0;var d=a.inArray(g.radixPoint,y());if(d!==-1){for(var e in c)if(d<e&&c[e].input!==I(e))return!1;return!0}}}return!1}var d=this;setTimeout(function(){if(document.activeElement===d){var b=L(d);if(b.begin===b.end)switch(g.positionCaretOnClick){case"none":break;case"radixFocus":if(c(b.begin)){var e=a.inArray(g.radixPoint,y().join(""));L(d,g.numericInput?E(e):e);break}default:var f=b.begin,h=p(f,!0),i=E(h);if(f<i)L(d,D(f)||D(f-1)?f:E(f));else{var j=I(i);
12084(""!==j&&y()[i]!==j&&t(i).match.optionalQuantifier!==!0||!D(i,!0)&&t(i).match.def===j)&&(i=E(i)),L(d,i)}}}},0)}function Z(a){var b=this;setTimeout(function(){L(b,0,E(p()))},0)}function $(c){var d=this,e=a(d),f=L(d),h=c.originalEvent||c,i=window.clipboardData||h.clipboardData,j=ma?y().slice(f.end,f.begin):y().slice(f.begin,f.end);i.setData("text",ma?j.reverse().join(""):j.join("")),document.execCommand&&document.execCommand("copy"),Q(d,b.keyCode.DELETE,f),H(d,y(),n().p,c,ga!==y().join("")),d.inputmask._valueGet()===x().join("")&&e.trigger("cleared"),g.showTooltip&&(d.title=g.tooltip||n().mask)}function _(b){var c=a(this),d=this;if(d.inputmask){var e=d.inputmask._valueGet(),f=y().slice();ga!==f.join("")&&setTimeout(function(){c.trigger("change"),ga=f.join("")},0),""!==e&&(g.clearMaskOnLostFocus&&(p()===-1&&e===x().join("")?f=[]:N(f)),O(f)===!1&&(setTimeout(function(){c.trigger("incomplete")},0),g.clearIncomplete&&(o(),f=g.clearMaskOnLostFocus?[]:x().slice())),H(d,f,void 0,b))}}function aa(a){var b=this;qa=!0,document.activeElement!==b&&g.showMaskOnHover&&b.inputmask._valueGet()!==y().join("")&&H(b,y())}function ba(a){ga!==y().join("")&&ia.trigger("change"),g.clearMaskOnLostFocus&&p()===-1&&ha.inputmask._valueGet&&ha.inputmask._valueGet()===x().join("")&&ha.inputmask._valueSet(""),g.removeMaskOnSubmit&&(ha.inputmask._valueSet(ha.inputmask.unmaskedvalue(),!0),setTimeout(function(){H(ha,y())},0))}function ca(a){setTimeout(function(){ia.trigger("setvalue")},0)}function da(b){function c(a){var c,d=document.createElement("span");for(var e in f)isNaN(e)&&e.indexOf("font")!==-1&&(d.style[e]=f[e]);d.style.textTransform=f.textTransform,d.style.letterSpacing=f.letterSpacing,d.style.position="absolute",d.style.height="auto",d.style.width="auto",d.style.visibility="hidden",d.style.whiteSpace="nowrap",document.body.appendChild(d);var g,h=b.inputmask._valueGet(),i=0;for(c=0,g=h.length;c<=g;c++){if(d.innerHTML+=h.charAt(c)||"_",d.offsetWidth>=a){var j=a-i,k=d.offsetWidth-a;d.innerHTML=h.charAt(c),j-=d.offsetWidth/3,c=j<k?c-1:c;break}i=d.offsetWidth}return document.body.removeChild(d),c}function d(){ka.style.position="absolute",ka.style.top=e.top+"px",ka.style.left=e.left+"px",ka.style.width=parseInt(b.offsetWidth)-parseInt(f.paddingLeft)-parseInt(f.paddingRight)-parseInt(f.borderLeftWidth)-parseInt(f.borderRightWidth)+"px",ka.style.height=parseInt(b.offsetHeight)-parseInt(f.paddingTop)-parseInt(f.paddingBottom)-parseInt(f.borderTopWidth)-parseInt(f.borderBottomWidth)+"px",ka.style.lineHeight=ka.style.height,ka.style.zIndex=isNaN(f.zIndex)?-1:f.zIndex-1,ka.style.webkitAppearance="textfield",ka.style.mozAppearance="textfield",ka.style.Appearance="textfield"}var e=a(b).position(),f=(b.ownerDocument.defaultView||window).getComputedStyle(b,null);b.parentNode;ka=document.createElement("div"),document.body.appendChild(ka);for(var g in f)isNaN(g)&&"cssText"!==g&&g.indexOf("webkit")==-1&&(ka.style[g]=f[g]);b.style.backgroundColor="transparent",b.style.color="transparent",b.style.webkitAppearance="caret",b.style.mozAppearance="caret",b.style.Appearance="caret",d(),a(window).on("resize",function(c){e=a(b).position(),f=(b.ownerDocument.defaultView||window).getComputedStyle(b,null),d()}),a(b).on("click",function(a){return L(b,c(a.clientX)),Y.call(this,[a])})}function ea(a,b,c){function d(){f||null!==i.fn&&void 0!==j.input?f&&null!==i.fn&&void 0!==j.input&&(f=!1,e+="</span>"):(f=!0,e+="<span class='im-static''>")}if(void 0!==ka){b=b||y(),void 0===c?c=L(a):void 0===c.begin&&(c={begin:c,end:c});var e="",f=!1;if(""!=b){var h,i,j,k=0,l=p();do k===c.begin&&document.activeElement===a&&(e+="<span class='im-caret' style='border-right-width: 1px;border-right-style: solid;'></span>"),n().validPositions[k]?(j=n().validPositions[k],i=j.match,h=j.locator.slice(),d(),e+=j.input):(j=s(k,h,k-1),i=j.match,h=j.locator.slice(),(g.jitMasking===!1||k<l||Number.isFinite(g.jitMasking)&&g.jitMasking>k)&&(d(),e+=I(k,i))),k++;while((void 0===ja||k<ja)&&(null!==i.fn||""!==i.def)||l>k)}ka.innerHTML=e}}function fa(b){if(ha=b,ia=a(ha),g.showTooltip&&(ha.title=g.tooltip||n().mask),("rtl"===ha.dir||g.rightAlign)&&(ha.style.textAlign="right"),("rtl"===ha.dir||g.numericInput)&&(ha.dir="ltr",ha.removeAttribute("dir"),ha.inputmask.isRTL=!0,ma=!0),g.colorMask===!0&&da(ha),m&&(ha.hasOwnProperty("inputmode")&&(ha.inputmode=g.inputmode,ha.setAttribute("inputmode",g.inputmode)),"rtfm"===g.androidHack&&(g.colorMask!==!0&&da(ha),ha.type="password")),ra.off(ha),P(ha),d(ha,g)&&(ra.on(ha,"submit",ba),ra.on(ha,"reset",ca),ra.on(ha,"mouseenter",aa),ra.on(ha,"blur",_),ra.on(ha,"focus",W),ra.on(ha,"mouseleave",X),g.colorMask!==!0&&ra.on(ha,"click",Y),ra.on(ha,"dblclick",Z),ra.on(ha,"paste",T),ra.on(ha,"dragdrop",T),ra.on(ha,"drop",T),ra.on(ha,"cut",$),ra.on(ha,"complete",g.oncomplete),ra.on(ha,"incomplete",g.onincomplete),ra.on(ha,"cleared",g.oncleared),g.inputEventOnly!==!0&&(ra.on(ha,"keydown",R),ra.on(ha,"keypress",S)),ra.on(ha,"compositionstart",a.noop),ra.on(ha,"compositionupdate",a.noop),ra.on(ha,"compositionend",a.noop),ra.on(ha,"keyup",a.noop),ra.on(ha,"input",U)),ra.on(ha,"setvalue",V),x(),""!==ha.inputmask._valueGet()||g.clearMaskOnLostFocus===!1||document.activeElement===ha){var c=a.isFunction(g.onBeforeMask)?g.onBeforeMask(h
12084a.inputmask._valueGet(),g)||ha.inputmask._valueGet():ha.inputmask._valueGet();J(ha,!0,!1,c.split(""));var e=y().slice();ga=e.join(""),O(e)===!1&&g.clearIncomplete&&o(),g.clearMaskOnLostFocus&&document.activeElement!==ha&&(p()===-1?e=[]:N(e)),H(ha,e),document.activeElement===ha&&L(ha,E(p()))}}var ga,ha,ia,ja,ka,la,ma=!1,na=!1,oa=!1,pa=!1,qa=!0,ra={on:function(c,d,e){var f=function(c){if(void 0===this.inputmask&&"FORM"!==this.nodeName){var d=a.data(this,"_inputmask_opts");d?new b(d).mask(this):ra.off(this)}else{if("setvalue"===c.type||!(this.disabled||this.readOnly&&!("keydown"===c.type&&c.ctrlKey&&67===c.keyCode||g.tabThrough===!1&&c.keyCode===b.keyCode.TAB))){switch(c.type){case"input":if(oa===!0)return oa=!1,c.preventDefault();break;case"keydown":na=!1,oa=!1;break;case"keypress":if(na===!0)return c.preventDefault();na=!0;break;case"click":if(k||l){var f=this,h=arguments;return setTimeout(function(){e.apply(f,h)},0),!1}}var i=e.apply(this,arguments);return i===!1&&(c.preventDefault(),c.stopPropagation()),i}c.preventDefault()}};c.inputmask.events[d]=c.inputmask.events[d]||[],c.inputmask.events[d].push(f),a.inArray(d,["submit","reset"])!==-1?null!=c.form&&a(c.form).on(d,f):a(c).on(d,f)},off:function(b,c){if(b.inputmask&&b.inputmask.events){var d;c?(d=[],d[c]=b.inputmask.events[c]):d=b.inputmask.events,a.each(d,function(c,d){for(;d.length>0;){var e=d.pop();a.inArray(c,["submit","reset"])!==-1?null!=b.form&&a(b.form).off(c,e):a(b).off(c,e)}delete b.inputmask.events[c]})}}};if(void 0!==e)switch(e.action){case"isComplete":return ha=e.el,O(y());case"unmaskedvalue":return ha=e.el,void 0!==ha&&void 0!==ha.inputmask?(f=ha.inputmask.maskset,g=ha.inputmask.opts,ma=ha.inputmask.isRTL):(la=e.value,g.numericInput&&(ma=!0),la=(a.isFunction(g.onBeforeMask)?g.onBeforeMask(la,g)||la:la).split(""),J(void 0,!1,!1,ma?la.reverse():la),a.isFunction(g.onBeforeWrite)&&g.onBeforeWrite(void 0,y(),0,g)),K(ha);case"mask":ha=e.el,f=ha.inputmask.maskset,g=ha.inputmask.opts,ma=ha.inputmask.isRTL,fa(ha);break;case"format":return g.numericInput&&(ma=!0),la=(a.isFunction(g.onBeforeMask)?g.onBeforeMask(e.value,g)||e.value:e.value).split(""),J(void 0,!1,!1,ma?la.reverse():la),a.isFunction(g.onBeforeWrite)&&g.onBeforeWrite(void 0,y(),0,g),e.metadata?{value:ma?y().slice().reverse().join(""):y().join(""),metadata:h({action:"getmetadata"},f,g)}:ma?y().slice().reverse().join(""):y().join("");case"isValid":g.numericInput&&(ma=!0),e.value?(la=e.value.split(""),J(void 0,!1,!0,ma?la.reverse():la)):e.value=y().join("");for(var sa=y(),ta=M(),ua=sa.length-1;ua>ta&&!D(ua);ua--);return sa.splice(ta,ua+1-ta),O(sa)&&e.value===y().join("");case"getemptymask":return x().join("");case"remove":ha=e.el,ia=a(ha),f=ha.inputmask.maskset,g=ha.inputmask.opts,ha.inputmask._valueSet(K(ha)),ra.off(ha);var va;Object.getOwnPropertyDescriptor&&Object.getPrototypeOf?(va=Object.getOwnPropertyDescriptor(Object.getPrototypeOf(ha),"value"),va&&ha.inputmask.__valueGet&&Object.defineProperty(ha,"value",{get:ha.inputmask.__valueGet,set:ha.inputmask.__valueSet,configurable:!0})):document.__lookupGetter__&&ha.__lookupGetter__("value")&&ha.inputmask.__valueGet&&(ha.__defineGetter__("value",ha.inputmask.__valueGet),ha.__defineSetter__("value",ha.inputmask.__valueSet)),ha.inputmask=void 0;break;case"getmetadata":if(a.isArray(f.metadata)){for(var wa,xa=p(void 0,!0),ya=xa;ya>=0;ya--)if(n().validPositions[ya]&&void 0!==n().validPositions[ya].alternation){wa=n().validPositions[ya].alternation;break}return void 0!==wa?f.metadata[n().validPositions[ya].locator[wa]]:[]}return f.metadata}}b.prototype={defaults:{placeholder:"_",optionalmarker:{start:"[",end:"]"},quantifiermarker:{start:"{",end:"}"},groupmarker:{start:"(",end:")"},alternatormarker:"|",escapeChar:"\\",mask:null,oncomplete:a.noop,onincomplete:a.noop,oncleared:a.noop,repeat:0,greedy:!0,autoUnmask:!1,removeMaskOnSubmit:!1,clearMaskOnLostFocus:!0,insertMode:!0,clearIncomplete:!1,aliases:{},alias:null,onKeyDown:a.noop,onBeforeMask:null,onBeforePaste:function(b,c){return a.isFunction(c.onBeforeMask)?c.onBeforeMask(b,c):b},onBeforeWrite:null,onUnMask:null,showMaskOnFocus:!0,showMaskOnHover:!0,onKeyValidation:a.noop,skipOptionalPartCharacter:" ",showTooltip:!1,tooltip:void 0,numericInput:!1,rightAlign:!1,undoOnEscape:!0,radixPoint:"",radixPointDefinitionSymbol:void 0,groupSeparator:"",keepStatic:null,positionCaretOnTab:!0,tabThrough:!1,supportsInputType:["text","tel","password"],definitions:{9:{validator:"[0-9]",cardinality:1,definitionSymbol:"*"},a:{validator:"[A-Za-z\u0410-\u044f\u0401\u0451\xc0-\xff\xb5]",cardinality:1,definitionSymbol:"*"},"*":{validator:"[0-9A-Za-z\u0410-\u044f\u0401\u0451\xc0-\xff\xb5]",cardinality:1}},ignorables:[8,9,13,19,27,33,34,35,36,37,38,39,40,45,46,93,112,113,114,115,116,117,118,119,120,121,122,123],isComplete:null,canClearPosition:a.noop,postValidation:null,staticDefinitionSymbol:void 0,jitMasking:!1,nullable:!0,inputEventOnly:!1,noValuePatching:!1,positionCaretOnClick:"lvp",casing:null,inputmode:"verbatim",colorMask:!1,androidHack:!1},masksCache:{},mask:function(c){var d=this;return"string"==typeof c&&(c=document.getElementById(c)||document.querySelectorAll(c)),c=c.nodeName?[c]:c,a.each(c,function(c,e){var i=a.extend(!0,{},d.opts);f(e,i,a.extend(!0,{},d.userOptions));var j=g(i,d.noMasksCache);void 0!==j&&(void 0!==e.inputmask&&e.inputmask.remove(),e.inputmask=new b,e.inputmask.opts=i,e.inputmask.noMasksCache=d.noMasksCache,e.inputmask.userOptions=a.extend(!0,{},d.userOptions),e.inputmask.el=e,e.inputmask.maskset=j,e.inputmask.isRTL=!1,a.data(e,"_inputmask_opts",i),h({action:"mask",el:e}))}),c&&c[0]?c[0].inputmask||this:this},option:function(b,c){return"string"==typeof b?this.opts[b]:"object"==typeof b?(a.extend(this.userOptions,b),this.el&&c!==!0&&this.mask(this.el),this):void 0},unmaskedvalue:function(a){return h({action:"unmaskedvalue",el:this.el,value:a},this.el&&this.el.inputmask?this.el.inputmask.maskset:g(this.opts,this.noMasksCache),this.opts)},remove:function(){if(this.el)return h({action:"remove",el:this.el}),this.el.inputmask=void 0,this.el},getemptymask:function(){return h({action:"getemptymask"},this.maskset||g(this.opts,this.noMasksCache),this.opts)},hasMaskedValue:function(){return!this.opts.autoUnmask},isComplete:function(){return h({action:"isComplete",el:this.el},this.maskset||g(this.opts,this.noMasksCache),this.opts)},getmetadata:function(){return h({action:"getmetadata"},this.maskset||g(this.opts,this.noMasksCache),this.opts)},isValid:function(a){return h({action:"isValid",value:a},this.maskset||g(this.opts,this.noMasksCache),this.opts)},format:function(a,b){return h({action:"format",value:a,metadata:b},this.maskset||g(this.opts,this.noMasksCache),this.opts)}},b.extendDefaults=function(c){a.extend(!0,b.prototype.defaults,c)},b.extendDefinitions=function(c){a.extend(!0,b.prototype.defaults.definitions,c)},b.extendAliases=function(c){a.extend(!0,b.prototype.defaults.aliases,c)},b.format=function(a,c,d){return b(c).format(a,d)}
12084,b.unmask=function(a,c){return b(c).unmaskedvalue(a)},b.isValid=function(a,c){return b(c).isValid(a)},b.remove=function(b){a.each(b,function(a,b){b.inputmask&&b.inputmask.remove()})},b.escapeRegex=function(a){var b=["/",".","*","+","?","|","(",")","[","]","{","}","\\","$","^"];return a.replace(new RegExp("(\\"+b.join("|\\")+")","gim"),"\\$1")},b.keyCode={ALT:18,BACKSPACE:8,BACKSPACE_SAFARI:127,CAPS_LOCK:20,COMMA:188,COMMAND:91,COMMAND_LEFT:91,COMMAND_RIGHT:93,CONTROL:17,DELETE:46,DOWN:40,END:35,ENTER:13,ESCAPE:27,HOME:36,INSERT:45,LEFT:37,MENU:93,NUMPAD_ADD:107,NUMPAD_DECIMAL:110,NUMPAD_DIVIDE:111,NUMPAD_ENTER:108,NUMPAD_MULTIPLY:106,NUMPAD_SUBTRACT:109,PAGE_DOWN:34,PAGE_UP:33,PERIOD:190,RIGHT:39,SHIFT:16,SPACE:32,TAB:9,UP:38,WINDOWS:91,X:88};var i=navigator.userAgent,j=/mobile/i.test(i),k=/iemobile/i.test(i),l=/iphone/i.test(i)&&!k,m=/android/i.test(i)&&!k;return window.Inputmask=b,b}(jQuery),function(a,b){return void 0===a.fn.inputmask&&(a.fn.inputmask=function(c,d){var e,f=this[0];if(void 0===d&&(d={}),"string"==typeof c)switch(c){case"unmaskedvalue":return f&&f.inputmask?f.inputmask.unmaskedvalue():a(f).val();case"remove":return this.each(function(){this.inputmask&&this.inputmask.remove()});case"getemptymask":return f&&f.inputmask?f.inputmask.getemptymask():"";case"hasMaskedValue":return!(!f||!f.inputmask)&&f.inputmask.hasMaskedValue();case"isComplete":return!f||!f.inputmask||f.inputmask.isComplete();case"getmetadata":return f&&f.inputmask?f.inputmask.getmetadata():void 0;case"setvalue":a(f).val(d),f&&void 0===f.inputmask&&a(f).triggerHandler("setvalue");break;case"option":if("string"!=typeof d)return this.each(function(){if(void 0!==this.inputmask)return this.inputmask.option(d)});if(f&&void 0!==f.inputmask)return f.inputmask.option(d);break;default:return d.alias=c,e=new b(d),this.each(function(){e.mask(this)})}else{if("object"==typeof c)return e=new b(c),void 0===c.mask&&void 0===c.alias?this.each(function(){return void 0!==this.inputmask?this.inputmask.option(c):void e.mask(this)}):this.each(function(){e.mask(this)});if(void 0===c)return this.each(function(){e=new b(d),e.mask(this)})}}),a.fn.inputmask}(jQuery,Inputmask),function(a,b){return b.extendDefinitions({h:{validator:"[01][0-9]|2[0-3]",cardinality:2,prevalidator:[{validator:"[0-2]",cardinality:1}]},s:{validator:"[0-5][0-9]",cardinality:2,prevalidator:[{validator:"[0-5]",cardinality:1}]},d:{validator:"0[1-9]|[12][0-9]|3[01]",cardinality:2,prevalidator:[{validator:"[0-3]",cardinality:1}]},m:{validator:"0[1-9]|1[012]",cardinality:2,prevalidator:[{validator:"[01]",cardinality:1}]},y:{validator:"(19|20)\\d{2}",cardinality:4,prevalidator:[{validator:"[12]",cardinality:1},{validator:"(19|20)",cardinality:2},{validator:"(19|20)\\d",cardinality:3}]}}),b.extendAliases({"dd/mm/yyyy":{mask:"1/2/y",placeholder:"dd/mm/yyyy",regex:{val1pre:new RegExp("[0-3]"),val1:new RegExp("0[1-9]|[12][0-9]|3[01]"),val2pre:function(a){var c=b.escapeRegex.call(this,a);return new RegExp("((0[1-9]|[12][0-9]|3[01])"+c+"[01])")},val2:function(a){var c=b.escapeRegex.call(this,a);return new RegExp("((0[1-9]|[12][0-9])"+c+"(0[1-9]|1[012]))|(30"+c+"(0[13-9]|1[012]))|(31"+c+"(0[13578]|1[02]))")}},leapday:"29/02/",separator:"/",yearrange:{minyear:1900,maxyear:2099},isInYearRange:function(a,b,c){if(isNaN(a))return!1;var d=parseInt(a.concat(b.toString().slice(a.length))),e=parseInt(a.concat(c.toString().slice(a.length)));return!isNaN(d)&&(b<=d&&d<=c)||!isNaN(e)&&(b<=e&&e<=c)},determinebaseyear:function(a,b,c){var d=(new Date).getFullYear();if(a>d)return a;if(b<d){for(var e=b.toString().slice(0,2),f=b.toString().slice(2,4);b<e+c;)e--;var g=e+f;return a>g?a:g}if(a<=d&&d<=b){for(var h=d.toString().slice(0,2);b<h+c;)h--;var i=h+c;return i<a?a:i}return d},onKeyDown:function(c,d,e,f){var g=a(this);if(c.ctrlKey&&c.keyCode===b.keyCode.RIGHT){var h=new Date;g.val(h.getDate().toString()+(h.getMonth()+1).toString()+h.getFullYear().toString()),g.trigger("setvalue")}},getFrontValue:function(a,b,c){for(var d=0,e=0,f=0;f<a.length&&"2"!==a.charAt(f);f++){var g=c.definitions[a.charAt(f)];g?(d+=e,e=g.cardinality):e++}return b.join("").substr(d,e)},definitions:{1:{validator:function(a,b,c,d,e){var f=e.regex.val1.test(a);return d||f||a.charAt(1)!==e.separator&&"-./".indexOf(a.charAt(1))===-1||!(f=e.regex.val1.test("0"+a.charAt(0)))?f:(b.buffer[c-1]="0",{refreshFromBuffer:{start:c-1,end:c},pos:c,c:a.charAt(0)})}
12084,cardinality:2,prevalidator:[{validator:function(a,b,c,d,e){var f=a;isNaN(b.buffer[c+1])||(f+=b.buffer[c+1]);var g=1===f.length?e.regex.val1pre.test(f):e.regex.val1.test(f);if(!d&&!g){if(g=e.regex.val1.test(a+"0"))return b.buffer[c]=a,b.buffer[++c]="0",{pos:c,c:"0"};if(g=e.regex.val1.test("0"+a))return b.buffer[c]="0",c++,{pos:c}}return g},cardinality:1}]},2:{validator:function(a,b,c,d,e){var f=e.getFrontValue(b.mask,b.buffer,e);f.indexOf(e.placeholder[0])!==-1&&(f="01"+e.separator);var g=e.regex.val2(e.separator).test(f+a);if(!d&&!g&&(a.charAt(1)===e.separator||"-./".indexOf(a.charAt(1))!==-1)&&(g=e.regex.val2(e.separator).test(f+"0"+a.charAt(0))))return b.buffer[c-1]="0",{refreshFromBuffer:{start:c-1,end:c},pos:c,c:a.charAt(0)};if(e.mask.indexOf("2")===e.mask.length-1&&g){var h=b.buffer.join("").substr(4,4)+a;if(h!==e.leapday)return!0;var i=parseInt(b.buffer.join("").substr(0,4),10);return i%4===0&&(i%100!==0||i%400===0)}return g},cardinality:2,prevalidator:[{validator:function(a,b,c,d,e){isNaN(b.buffer[c+1])||(a+=b.buffer[c+1]);var f=e.getFrontValue(b.mask,b.buffer,e);f.indexOf(e.placeholder[0])!==-1&&(f="01"+e.separator);var g=1===a.length?e.regex.val2pre(e.separator).test(f+a):e.regex.val2(e.separator).test(f+a);return d||g||!(g=e.regex.val2(e.separator).test(f+"0"+a))?g:(b.buffer[c]="0",c++,{pos:c})},cardinality:1}]},y:{validator:function(a,b,c,d,e){if(e.isInYearRange(a,e.yearrange.minyear,e.yearrange.maxyear)){var f=b.buffer.join("").substr(0,6);if(f!==e.leapday)return!0;var g=parseInt(a,10);return g%4===0&&(g%100!==0||g%400===0)}return!1},cardinality:4,prevalidator:[{validator:function(a,b,c,d,e){var f=e.isInYearRange(a,e.yearrange.minyear,e.yearrange.maxyear);if(!d&&!f){var g=e.determinebaseyear(e.yearrange.minyear,e.yearrange.maxyear,a+"0").toString().slice(0,1);if(f=e.isInYearRange(g+a,e.yearrange.minyear,e.yearrange.maxyear))return b.buffer[c++]=g.charAt(0),{pos:c};if(g=e.determinebaseyear(e.yearrange.minyear,e.yearrange.maxyear,a+"0").toString().slice(0,2),f=e.isInYearRange(g+a,e.yearrange.minyear,e.yearrange.maxyear))return b.buffer[c++]=g.charAt(0),b.buffer[c++]=g.charAt(1),{pos:c}}return f},cardinality:1},{validator:function(a,b,c,d,e){var f=e.isInYearRange(a,e.yearrange.minyear,e.yearrange.maxyear);if(!d&&!f){var g=e.determinebaseyear(e.yearrange.minyear,e.yearrange.maxyear,a).toString().slice(0,2);if(f=e.isInYearRange(a[0]+g[1]+a[1],e.yearrange.minyear,e.yearrange.maxyear))return b.buffer[c++]=g.charAt(1),{pos:c};if(g=e.determinebaseyear(e.yearrange.minyear,e.yearrange.maxyear,a).toString().slice(0,2),e.isInYearRange(g+a,e.yearrange.minyear,e.yearrange.maxyear)){var h=b.buffer.join("").substr(0,6);if(h!==e.leapday)f=!0;else{var i=parseInt(a,10);f=i%4===0&&(i%100!==0||i%400===0)}}else f=!1;if(f)return b.buffer[c-1]=g.charAt(0),b.buffer[c++]=g.charAt(1),b.buffer[c++]=a.charAt(0),{refreshFromBuffer:{start:c-3,end:c},pos:c}}return f},cardinality:2},{validator:function(a,b,c,d,e){return e.isInYearRange(a,e.yearrange.minyear,e.yearrange.maxyear)},cardinality:3}]}},insertMode:!1,autoUnmask:!1},"mm/dd/yyyy":{placeholder:"mm/dd/yyyy",alias:"dd/mm/yyyy",regex:{val2pre:function(a){var c=b.escapeRegex.call(this,a);return new RegExp("((0[13-9]|1[012])"+c+"[0-3])|(02"+c+"[0-2])")},val2:function(a){var c=b.escapeRegex.call(this,a);return new RegExp("((0[1-9]|1[012])"+c+"(0[1-9]|[12][0-9]))|((0[13-9]|1[012])"+c+"30)|((0[13578]|1[02])"+c+"31)")},val1pre:new RegExp("[01]"),val1:new RegExp("0[1-9]|1[012]")},leapday:"02/29/",onKeyDown:function(c,d,e,f){var g=a(this);if(c.ctrlKey&&c.keyCode===b.keyCode.RIGHT){var h=new Date;g.val((h.getMonth()+1).toString()+h.getDate().toString()+h.getFullYear().toString()),g.trigger("setvalue")}}},"yyyy/mm/dd":{mask:"y/1/2",placeholder:"yyyy/mm/dd",alias:"mm/dd/yyyy",leapday:"/02/29",onKeyDown:function(c,d,e,f){var g=a(this);if(c.ctrlKey&&c.keyCode===b.keyCode.RIGHT){var h=new Date;g.val(h.getFullYear().toString()+(h.getMonth()+1).toString()+h.getDate().toString()),g.trigger("setvalue")}}},"dd.mm.yyyy":{mask:"1.2.y",placeholder:"dd.mm.yyyy",leapday:"29.02.",separator:".",alias:"dd/mm/yyyy"},"dd-mm-yyyy":{mask:"1-2-y",placeholder:"dd-mm-yyyy",leapday:"29-02-",separator:"-",alias:"dd/mm/yyyy"},"mm.dd.yyyy":{mask:"1.2.y",placeholder:"mm.dd.yyyy",leapday:"02.29.",separator:".",alias:"mm/dd/yyyy"},"mm-dd-yyyy":{mask:"1-2-y",placeholder:"mm-dd-yyyy",leapday:"02-29-",separator:"-",alias:"mm/dd/yyyy"},"yyyy.mm.dd":{mask:"y.1.2",placeholder:"yyyy.mm.dd",leapday:".02.29",separator:".",alias:"yyyy/mm/dd"},"yyyy-mm-dd":{mask:"y-1-2",placeholder:"yyyy-mm-dd",leapday:"-02-29",separator:"-",alias:"yyyy/mm/dd"},datetime:{mask:"1/2/y h:s",placeholder:"dd/mm/yyyy hh:mm",alias:"dd/mm/yyyy",regex:{hrspre:new RegExp("[012]"),hrs24:new RegExp("2[0-4]|1[3-9]"),hrs:new RegExp("[01][0-9]|2[0-4]"),ampm:new RegExp("^[a|p|A|P][m|M]"),mspre:new RegExp("[0-5]"),ms:new RegExp("[0-5][0-9]")},timeseparator:":",hourFormat:"24",definitions:{h:{validator:function(a,b,c,d,e){if("24"===e.hourFormat&&24===parseInt(a,10))return b.buffer[c-1]="0",b.buffer[c]="0",{refreshFromBuffer:{start:c-1,end:c},c:"0"};var f=e.regex.hrs.test(a);if(!d&&!f&&(a.charAt(1)===e.timeseparator||"-.:".indexOf(a.charAt(1))!==-1)&&(f=e.regex.hrs.test("0"+a.charAt(0))))return b.buffer[c-1]="0",b.buffer[c]=a.charAt(0),c++,{refreshFromBuffer:{start:c-2,end:c},pos:c,c:e.timeseparator};if(f&&"24"!==e.hourFormat&&e.regex.hrs24.test(a)){var g=parseInt(a,10);return 24===g?(b.buffer[c+5]="a",b.buffer[c+6]="m"):(b.buffer[c+5]="p",b.buffer[c+6]="m"),g-=12,g<10?(b.buffer[c]=g.toString(),b.buffer[c-1]="0"):(b.buffer[c]=g.toString().charAt(1),b.buffer[c-1]=g.toString().charAt(0)),{refreshFromBuffer:{start:c-1,end:c+6},c:b.buffer[c]}}return f}
12084,cardinality:2,prevalidator:[{validator:function(a,b,c,d,e){var f=e.regex.hrspre.test(a);return d||f||!(f=e.regex.hrs.test("0"+a))?f:(b.buffer[c]="0",c++,{pos:c})},cardinality:1}]},s:{validator:"[0-5][0-9]",cardinality:2,prevalidator:[{validator:function(a,b,c,d,e){var f=e.regex.mspre.test(a);return d||f||!(f=e.regex.ms.test("0"+a))?f:(b.buffer[c]="0",c++,{pos:c})},cardinality:1}]},t:{validator:function(a,b,c,d,e){return e.regex.ampm.test(a+"m")},casing:"lower",cardinality:1}},insertMode:!1,autoUnmask:!1},datetime12:{mask:"1/2/y h:s t\\m",placeholder:"dd/mm/yyyy hh:mm xm",alias:"datetime",hourFormat:"12"},"mm/dd/yyyy hh:mm xm":{mask:"1/2/y h:s t\\m",placeholder:"mm/dd/yyyy hh:mm xm",alias:"datetime12",regex:{val2pre:function(a){var c=b.escapeRegex.call(this,a);return new RegExp("((0[13-9]|1[012])"+c+"[0-3])|(02"+c+"[0-2])")},val2:function(a){var c=b.escapeRegex.call(this,a);return new RegExp("((0[1-9]|1[012])"+c+"(0[1-9]|[12][0-9]))|((0[13-9]|1[012])"+c+"30)|((0[13578]|1[02])"+c+"31)")},val1pre:new RegExp("[01]"),val1:new RegExp("0[1-9]|1[012]")},leapday:"02/29/",onKeyDown:function(c,d,e,f){var g=a(this);if(c.ctrlKey&&c.keyCode===b.keyCode.RIGHT){var h=new Date;g.val((h.getMonth()+1).toString()+h.getDate().toString()+h.getFullYear().toString()),g.trigger("setvalue")}}},"hh:mm t":{mask:"h:s t\\m",placeholder:"hh:mm xm",alias:"datetime",hourFormat:"12"},"h:s t":{mask:"h:s t\\m",placeholder:"hh:mm xm",alias:"datetime",hourFormat:"12"},"hh:mm:ss":{mask:"h:s:s",placeholder:"hh:mm:ss",alias:"datetime",autoUnmask:!1},"hh:mm":{mask:"h:s",placeholder:"hh:mm",alias:"datetime",autoUnmask:!1},date:{alias:"dd/mm/yyyy"},"mm/yyyy":{mask:"1/y",placeholder:"mm/yyyy",leapday:"donotuse",separator:"/",alias:"mm/dd/yyyy"},shamsi:{regex:{val2pre:function(a){var c=b.escapeRegex.call(this,a);return new RegExp("((0[1-9]|1[012])"+c+"[0-3])")},val2:function(a){var c=b.escapeRegex.call(this,a);return new RegExp("((0[1-9]|1[012])"+c+"(0[1-9]|[12][0-9]))|((0[1-9]|1[012])"+c+"30)|((0[1-6])"+c+"31)")},val1pre:new RegExp("[01]"),val1:new RegExp("0[1-9]|1[012]")},yearrange:{minyear:1300,maxyear:1499},mask:"y/1/2",leapday:"/12/30",placeholder:"yyyy/mm/dd",alias:"mm/dd/yyyy",clearIncomplete:!0}}),b}(jQuery,Inputmask),function(a,b){return b.extendDefinitions({A:{validator:"[A-Za-z\u0410-\u044f\u0401\u0451\xc0-\xff\xb5]",cardinality:1,casing:"upper"},"&":{validator:"[0-9A-Za-z\u0410-\u044f\u0401\u0451\xc0-\xff\xb5]",cardinality:1,casing:"upper"},"#":{validator:"[0-9A-Fa-f]",cardinality:1,casing:"upper"}}),b.extendAliases({url:{definitions:{i:{validator:".",cardinality:1}},mask:"(\\http://)|(\\http\\s://)|(ftp://)|(ftp\\s://)i{+}",insertMode:!1,autoUnmask:!1,inputmode:"url"},ip:{mask:"i[i[i]].i[i[i]].i[i[i]].i[i[i]]",definitions:{i:{validator:function(a,b,c,d,e){return c-1>-1&&"."!==b.buffer[c-1]?(a=b.buffer[c-1]+a,a=c-2>
12084-1&&"."!==b.buffer[c-2]?b.buffer[c-2]+a:"0"+a):a="00"+a,new RegExp("25[0-5]|2[0-4][0-9]|[01][0-9][0-9]").test(a)},cardinality:1}},onUnMask:function(a,b,c){return a},inputmode:"numeric"},email:{mask:"*{1,64}[.*{1,64}][.*{1,64}][.*{1,63}]@-{1,63}.-{1,63}[.-{1,63}][.-{1,63}]",greedy:!1,onBeforePaste:function(a,b){return a=a.toLowerCase(),a.replace("mailto:","")},definitions:{"*":{validator:"[0-9A-Za-z!#$%&'*+/=?^_`{|}~-]",cardinality:1,casing:"lower"},"-":{validator:"[0-9A-Za-z-]",cardinality:1,casing:"lower"}},onUnMask:function(a,b,c){return a},inputmode:"email"},mac:{mask:"##:##:##:##:##:##"},vin:{mask:"V{13}9{4}",definitions:{V:{validator:"[A-HJ-NPR-Za-hj-npr-z\\d]",cardinality:1,casing:"upper"}},clearIncomplete:!0,autoUnmask:!0}}),b}(jQuery,Inputmask),function(a,b){return b.extendAliases({numeric:{mask:function(a){function c(b){for(var c="",d=0;d<b.length;d++)c+=a.definitions[b.charAt(d)]||a.optionalmarker.start===b.charAt(d)||a.optionalmarker.end===b.charAt(d)||a.quantifiermarker.start===b.charAt(d)||a.quantifiermarker.end===b.charAt(d)||a.groupmarker.start===b.charAt(d)||a.groupmarker.end===b.charAt(d)||a.alternatormarker===b.charAt(d)?"\\"+b.charAt(d):b.charAt(d);return c}if(0!==a.repeat&&isNaN(a.integerDigits)&&(a.integerDigits=a.repeat),a.repeat=0,a.groupSeparator===a.radixPoint&&("."===a.radixPoint?a.groupSeparator=",":","===a.radixPoint?a.groupSeparator=".":a.groupSeparator="")," "===a.groupSeparator&&(a.skipOptionalPartCharacter=void 0),a.autoGroup=a.autoGroup&&""!==a.groupSeparator,a.autoGroup&&("string"==typeof a.groupSize&&isFinite(a.groupSize)&&(a.groupSize=parseInt(a.groupSize)),isFinite(a.integerDigits))){var d=Math.floor(a.integerDigits/a.groupSize),e=a.integerDigits%a.groupSize;a.integerDigits=parseInt(a.integerDigits)+(0===e?d-1:d),a.integerDigits<1&&(a.integerDigits="*")}a.placeholder.length>1&&(a.placeholder=a.placeholder.charAt(0)),"radixFocus"===a.positionCaretOnClick&&""===a.placeholder&&a.integerOptional===!1&&(a.positionCaretOnClick="lvp"),a.definitions[";"]=a.definitions["~"],a.definitions[";"].definitionSymbol="~",a.numericInput===!0&&(a.positionCaretOnClick="radixFocus"===a.positionCaretOnClick?"lvp":a.positionCaretOnClick,a.digitsOptional=!1,isNaN(a.digits)&&(a.digits=2),a.decimalProtect=!1);var f="[+]";if(f+=c(a.prefix),f+=a.integerOptional===!0?"~{1,"+a.integerDigits+"}":"~{"+a.integerDigits+"}",void 0!==a.digits){a.decimalProtect&&(a.radixPointDefinitionSymbol=":");var g=a.digits.toString().split(",");isFinite(g[0]&&g[1]&&isFinite(g[1]))?f+=(a.decimalProtect?":":a.radixPoint)+";{"+a.digits+"}":(isNaN(a.digits)||parseInt(a.digits)>0)&&(f+=a.digitsOptional?"["+(a.decimalProtect?":":a.radixPoint)+";{1,"+a.digits+"}]":(a.decimalProtect?":":a.radixPoint)+";{"+a.digits+"}")}return f+=c(a.suffix),f+="[-]",a.greedy=!1,null!==a.min&&(a.min=a.min.toString().replace(new RegExp(b.escapeRegex(a.groupSeparator),"g"),""),","===a.radixPoint&&(a.min=a.min.replace(a.radixPoint,"."))),null!==a.max&&(a.max=a.max.toString().replace(new RegExp(b.escapeRegex(a.groupSeparator),"g"),""),","===a.radixPoint&&(a.max=a.max.replace(a.radixPoint,"."))),f},placeholder:"",greedy:!1,digits:"*",digitsOptional:!0,radixPoint:".",positionCaretOnClick:"radixFocus",groupSize:3,groupSeparator:"",autoGroup:!1,allowPlus:!0,allowMinus:!0,negationSymbol:{front:"-",back:""},integerDigits:"+",integerOptional:!0,prefix:"",suffix:"",rightAlign:!0,decimalProtect:!0,min:null,max:null,step:1,insertMode:!0,autoUnmask:!1,unmaskAsNumber:!1,inputmode:"numeric",postFormat:function(c,d,e){e.numericInput===!0&&(c=c.reverse(),isFinite(d)&&(d=c.join("").length-d-1));var f,g;d=d>=c.length?c.length-1:d<0?0:d;var h=c[d],i=c.slice();h===e.groupSeparator&&(i.splice(d--,1),h=i[d]);var j=i.join("").match(new RegExp("^"+b.escapeRegex(e.negationSymbol.front)));j=null!==j&&1===j.length,d>(j?e.negationSymbol.front.length:0)+e.prefix.length&&d<i.length-e.suffix.length&&(i[d]="!");var k=i.join(""),l=k;if(j&&(k=k.replace(new RegExp("^"+b.escapeRegex(e.negationSymbol.front)),""),k=k.replace(new RegExp(b.escapeRegex(e.negationSymbol.back)+"$"),"")),k=k.replace(new RegExp(b.escapeRegex(e.suffix)+"$"),""),k=k.replace(new RegExp("^"+b.escapeRegex(e.prefix)),""),k.length>0&&e.autoGroup||k.indexOf(e.groupSeparator)!==-1){var m=b.escapeRegex(e.groupSeparator);k=k.replace(new RegExp(m,"g"),"");var n=k.split(h===e.radixPoint?"!":e.radixPoint);if(k=""===e.radixPoint?k:n[0],h!==e.negationSymbol.front&&(k=k.replace("!","?")),k.length>e.groupSize)for(var o=new RegExp("([-+]?[\\d?]+)([\\d?]{"+e.groupSize+"})");o.test(k)&&""!==e.groupSeparator;)k=k.replace(o,"$1"+e.groupSeparator+"$2"),k=k.replace(e.groupSeparator+e.groupSeparator,e.groupSeparator);k=k.replace("?","!"),""!==e.radixPoint&&n.length>1&&(k+=(h===e.radixPoint?"!":e.radixPoint)+n[1])}k=e.prefix+k+e.suffix,j&&(k=e.negationSymbol.front+k+e.negationSymbol.back);
12085var p=l!==k;if(p)for(c.length=k.length,f=0,g=k.length;f<g;f++)c[f]=k.charAt(f);var q=a.inArray("!",k);return q===-1&&(q=d),c[q]=h,q=e.numericInput&&isFinite(d)?c.join("").length-q-1:q,e.numericInput&&(c=c.reverse(),a.inArray(e.radixPoint,c)<q&&c.join("").length-e.suffix.length!==q&&(q-=1)),{pos:q,refreshFromBuffer:p,buffer:c,isNegative:j}},onBeforeWrite:function(c,d,e,f){var g;
12085if(c&&("blur"===c.type||"checkval"===c.type||"keydown"===c.type)){var h=f.numericInput?d.slice().reverse().join(""):d.join(""),i=h.replace(f.prefix,"");i=i.replace(f.suffix,""),i=i.replace(new RegExp(b.escapeRegex(f.groupSeparator),"g"),""),","===f.radixPoint&&(i=i.replace(f.radixPoint,"."));var j=i.match(new RegExp("[-"+b.escapeRegex(f.negationSymbol.front)+"]","g"));if(j=null!==j&&1===j.length,i=i.replace(new RegExp("[-"+b.escapeRegex(f.negationSymbol.front)+"]","g"),""),i=i.replace(new RegExp(b.escapeRegex(f.negationSymbol.back)+"$"),""),isNaN(f.placeholder)&&(i=i.replace(new RegExp(b.escapeRegex(f.placeholder),"g"),"")),i=i===f.negationSymbol.front?i+"0":i,""!==i&&isFinite(i)){var k=parseFloat(i),l=j?k*-1:k;if(null!==f.min&&isFinite(f.min)&&l<parseFloat(f.min)?(k=Math.abs(f.min),j=f.min<0,h=void 0):null!==f.max&&isFinite(f.max)&&l>parseFloat(f.max)&&(k=Math.abs(f.max),j=f.max<0,h=void 0),i=k.toString().replace(".",f.radixPoint).split(""),isFinite(f.digits)){var m=a.inArray(f.radixPoint,i),n=a.inArray(f.radixPoint,h);m===-1&&(i.push(f.radixPoint),m=i.length-1);for(var o=1;o<=f.digits;o++)f.digitsOptional||void 0!==i[m+o]&&i[m+o]!==f.placeholder.charAt(0)?n!==-1&&void 0!==h[n+o]&&(i[m+o]=i[m+o]||h[n+o]):i[m+o]="0";i[i.length-1]===f.radixPoint&&delete i[i.length-1]}if(k.toString()!==i&&k.toString()+"."!==i||j)return i=(f.prefix+i.join("")).split(""),!j||0===k&&"blur"===c.type||(i.unshift(f.negationSymbol.front),i.push(f.negationSymbol.back)),f.numericInput&&(i=i.reverse()),g=f.postFormat(i,f.numericInput?e:e-1,f),g.buffer&&(g.refreshFromBuffer=g.buffer.join("")!==d.join("")),g}}if(f.autoGroup)return g=f.postFormat(d,f.numericInput?e:e-1,f),g.caret=e<(g.isNegative?f.negationSymbol.front.length:0)+f.prefix.length||e>g.buffer.length-(g.isNegative?f.negationSymbol.back.length:0)?g.pos:g.pos+1,g},regex:{integerPart:function(a){return new RegExp("["+b.escapeRegex(a.negationSymbol.front)+"+]?\\d+")},integerNPart:function(a){return new RegExp("[\\d"+b.escapeRegex(a.groupSeparator)+b.escapeRegex(a.placeholder.charAt(0))+"]+")}},signHandler:function(a,b,c,d,e){if(!d&&e.allowMinus&&"-"===a||e.allowPlus&&"+"===a){var f=b.buffer.join("").match(e.regex.integerPart(e));if(f&&f[0].length>0)return b.buffer[f.index]===("-"===a?"+":e.negationSymbol.front)?"-"===a?""!==e.negationSymbol.back?{pos:0,c:e.negationSymbol.front,remove:0,caret:c,insert:{pos:b.buffer.length-1,c:e.negationSymbol.back}}:{pos:0,c:e.negationSymbol.front,remove:0,caret:c}:""!==e.negationSymbol.back?{pos:0,c:"+",remove:[0,b.buffer.length-1],caret:c}:{pos:0,c:"+",remove:0,caret:c}:b.buffer[0]===("-"===a?e.negationSymbol.front:"+")?"-"===a&&""!==e.negationSymbol.back?{remove:[0,b.buffer.length-1],caret:c-1}:{remove:0,caret:c-1}:"-"===a?""!==e.negationSymbol.back?{pos:0,c:e.negationSymbol.front,caret:c+1,insert:{pos:b.buffer.length,c:e.negationSymbol.back}}:{pos:0,c:e.negationSymbol.front,caret:c+1}:{pos:0,c:a,caret:c+1}}return!1},radixHandler:function(b,c,d,e,f){if(!e&&f.numericInput!==!0&&b===f.radixPoint&&void 0!==f.digits&&(isNaN(f.digits)||parseInt(f.digits)>0)){var g=a.inArray(f.radixPoint,c.buffer),h=c.buffer.join("").match(f.regex.integerPart(f));if(g!==-1&&c.validPositions[g])return c.validPositions[g-1]?{caret:g+1}:{pos:h.index,c:h[0],caret:g+1};if(!h||"0"===h[0]&&h.index+1!==d)return c.buffer[h?h.index:d]="0",{pos:(h?h.index:d)+1,c:f.radixPoint}}return!1},leadingZeroHandler:function(b,c,d,e,f,g){if(!e){var h=c.buffer.slice("");if(h.splice(0,f.prefix.length),h.splice(h.length-f.suffix.length,f.suffix.length),f.numericInput===!0){var h=h.reverse(),i=h[0];if("0"===i&&void 0===c.validPositions[d-1])return{pos:d,remove:h.length-1}}else{d-=f.prefix.length;var j=a.inArray(f.radixPoint,h),k=h.slice(0,j!==-1?j:void 0).join("").match(f.regex.integerNPart(f));if(k&&(j===-1||d<=j)){var l=j===-1?0:parseInt(h.slice(j+1).join(""));if(0===k[0].indexOf(""!==f.placeholder?f.placeholder.charAt(0):"0")&&(k.index+1===d||g!==!0&&0===l))return c.buffer.splice(k.index+f.prefix.length,1),{pos:k.index+f.prefix.length,remove:k.index+f.prefix.length};if("0"===b&&d<=k.index&&k[0]!==f.groupSeparator)return!1}}}return!0},definitions:{"~":{validator:function(c,d,e,f,g,h){var i=g.signHandler(c,d,e,f,g);if(!i&&(i=g.radixHandler(c,d,e,f,g),!i&&(i=f?new RegExp("[0-9"+b.escapeRegex(g.groupSeparator)+"]").test(c):new RegExp("[0-9]").test(c),i===!0&&(i=g.leadingZeroHandler(c,d,e,f,g,h),i===!0)))){var j=a.inArray(g.radixPoint,d.buffer);i=j!==-1&&(g.digitsOptional===!1||d.validPositions[e])&&g.numericInput!==!0&&e>j&&!f?{pos:e,remove:e}:{pos:e}}return i},cardinality:1},"+":{validator:function(a,b,c,d,e){var f=e.signHandler(a,b,c,d,e);return!f&&(d&&e.allowMinus&&a===e.negationSymbol.front||e.allowMinus&&"-"===a||e.allowPlus&&"+"===a)&&(f=!(!d&&"-"===a)||(""!==e.negationSymbol.back?{pos:c,c:"-"===a?e.negationSymbol.front:"+",caret:c+1,insert:{pos:b.buffer.length,c:e.negationSymbol.back}}:{pos:c,c:"-"===a?e.negationSymbol.front:"+",caret:c+1})),f},cardinality:1,placeholder:""},"-":{validator:function(a,b,c,d,e){var f=e.signHandler(a,b,c,d,e);return!f&&d&&e.allowMinus&&a===e.negationSymbol.back&&(f=!0),f},cardinality:1,placeholder:""},":":{validator:function(a,c,d,e,f){var g=f.signHandler(a,c,d,e,f);if(!g){var h="["+b.escapeRegex(f.radixPoint)+"]";g=new RegExp(h).test(a),g&&c.validPositions[d]&&c.validPositions[d].match.placeholder===f.radixPoint&&(g={caret:d+1})}return g?{c:f.radixPoint}:g},cardinality:1,placeholder:function(a){return a.radixPoint}}},onUnMask:function(a,c,d){if(""===c&&d.nullable===!0)return c;var e=a.replace(d.prefix,"");return e=e.replace(d.suffix,""),e=e.replace(new RegExp(b.escapeRegex(d.groupSeparator),"g"),""),d.unmaskAsNumber?(""!==d.radixPoint&&e.indexOf(d.radixPoint)!==-1&&(e=e.replace(b.escapeRegex.call(this,d.radixPoint),".")),Number(e)):e},isComplete:function(a,c){var d=a.join(""),e=a.slice();if(c.postFormat(e,0,c),e.join("")!==d)return!1;var f=d.replace(c.prefix,"");return f=f.replace(c.suffix,""),f=f.replace(new RegExp(b.escapeRegex(c.groupSeparator),"g"),""),","===c.radixPoint&&(f=f.replace(b.escapeRegex(c.radixPoint),".")),isFinite(f)},onBeforeMask:function(a,c){if(c.numericInput===!0&&(a=a.split("").reverse().join("")),""!==c.radixPoint&&isFinite(a))a=a.toString().replace(".",c.radixPoint);else{var d=a.match(/,/g),e=a.match(/\./g);e&&d?e.length>d.length?(a=a.replace(/\./g,""),a=a.replace(",",c.radixPoint)):d.length>e.length?(a=a.replace(/,/g,""),a=a.replace(".",c.radixPoint)):a=a.indexOf(".")<a.indexOf(",")?a.replace(/\./g,""):a=a.replace(/,/g,""):a=a.replace(new RegExp(b.escapeRegex(c.groupSeparator),"g"),"")}if(0===c.digits&&(a.indexOf(".")!==-1?a=a.substring(0,a.indexOf(".")):a.indexOf(",")!==-1&&(a=a.substring(0,a.indexOf(",")))),""!==c.radixPoint&&isFinite(c.digits)&&a.indexOf(c.radixPoint)!==-1){var f=a.split(c.radixPoint),g=f[1].match(new RegExp("\\d*"))[0];if(parseInt(c.digits)<g.toString().length){var h=Math.pow(10,parseInt(c.digits));a=a.replace(b.escapeRegex(c.radixPoint),"."),a=Math.round(parseFloat(a)*h)/h,a=a.toString().replace(".",c.radixPoint)}}return c.numericInput===!0&&(a=a.split("").reverse().join("")),a.toString()},canClearPosition:function(a,b,c,d,e){var f=a.validPositions[b].input,g=f!==e.radixPoint||null!==a.validPositions[b].match.fn&&e.decimalProtect===!1||isFinite(f)||b===c||f===e.groupSeparator||f===e.negationSymbol.front||f===e.negationSymbol.back;return g},onKeyDown:function(c,d,e,f){var g=a(this);if(c.ctrlKey)switch(c.keyCode){case b.keyCode.UP:g.val(parseFloat(this.inputmask.unmaskedvalue())+parseInt(f.step)),g.trigger("setvalue");break;case b.keyCode.DOWN:g.val(parseFloat(this.inputmask.unmaskedvalue())-parseInt(f.step)),g.trigger("setvalue")}}},currency:{prefix:"$ ",groupSeparator:",",alias:"numeric",placeholder:"0",autoGroup:!0,digits:2,digitsOptional:!1,clearMaskOnLostFocus:!1}
12085,decimal:{alias:"numeric"},integer:{alias:"numeric",digits:0,radixPoint:""},percentage:{alias:"numeric",digits:2,radixPoint:".",placeholder:"0",autoGroup:!1,min:0,max:100,suffix:" %",allowPlus:!1,allowMinus:!1}}),b}(jQuery,Inputmask),function(a,b){return b.extendAliases({abstractphone:{countrycode:"",phoneCodes:[],mask:function(a){a.definitions={"#":a.definitions[9]};var b=a.phoneCodes.sort(function(a,b){var c=(a.mask||a).replace(/#/g,"9").replace(/[\+\(\)#-]/g,""),d=(b.mask||b).replace(/#/g,"9").replace(/[\+\(\)#-]/g,""),e=(a.mask||a).split("#")[0],f=(b.mask||b).split("#")[0];return 0===f.indexOf(e)?-1:0===e.indexOf(f)?1:c.localeCompare(d)});return b},keepStatic:!0,onBeforeMask:function(a,b){var c=a.replace(/^0{1,2}/,"").replace(/[\s]/g,"");return(c.indexOf(b.countrycode)>1||c.indexOf(b.countrycode)===-1)&&(c="+"+b.countrycode+c),c},onUnMask:function(a,b,c){return b},inputmode:"tel"}}),b}(jQuery,Inputmask),function(a,b){return b.extendAliases({Regex:{mask:"r",greedy:!1,repeat:"*",regex:null,regexTokens:null,tokenizer:/\[\^?]?(?:[^\\\]]+|\\[\S\s]?)*]?|\\(?:0(?:[0-3][0-7]{0,2}|[4-7][0-7]?)?|[1-9][0-9]*|x[0-9A-Fa-f]{2}|u[0-9A-Fa-f]{4}|c[A-Za-z]|[\S\s]?)|\((?:\?[:=!]?)?|(?:[?*+]|\{[0-9]+(?:,[0-9]*)?\})\??|[^.?*+^${[()|\\]+|./g,quantifierFilter:/[0-9]+[^,]/,isComplete:function(a,b){return new RegExp(b.regex).test(a.join(""))},definitions:{r:{validator:function(b,c,d,e,f){function g(a,b){this.matches=[],this.isGroup=a||!1,this.isQuantifier=b||!1,this.quantifier={min:1,max:1},this.repeaterPart=void 0}function h(){var a,b,c=new g,d=[];for(f.regexTokens=[];a=f.tokenizer.exec(f.regex);)switch(b=a[0],b.charAt(0)){case"(":d.push(new g((!0)));break;case")":k=d.pop(),d.length>0?d[d.length-1].matches.push(k):c.matches.push(k);break;case"{":case"+":case"*":var e=new g((!1),(!0));b=b.replace(/[{}]/g,"");var h=b.split(","),i=isNaN(h[0])?h[0]:parseInt(h[0]),j=1===h.length?i:isNaN(h[1])?h[1]:parseInt(h[1]);if(e.quantifier={min:i,max:j},d.length>0){var l=d[d.length-1].matches;a=l.pop(),a.isGroup||(k=new g((!0)),k.matches.push(a),a=k),l.push(a),l.push(e)}else a=c.matches.pop(),a.isGroup||(k=new g((!0)),k.matches.push(a),a=k),c.matches.push(a),c.matches.push(e);break;default:d.length>0?d[d.length-1].matches.push(b):c.matches.push(b)}c.matches.length>0&&f.regexTokens.push(c)}function i(b,c){var d=!1;c&&(m+="(",o++);for(var e=0;e<b.matches.length;e++){var f=b.matches[e];if(f.isGroup===!0)d=i(f,!0);else if(f.isQuantifier===!0){var g=a.inArray(f,b.matches),h=b.matches[g-1],k=m;if(isNaN(f.quantifier.max)){for(;f.repeaterPart&&f.repeaterPart!==m&&f.repeaterPart.length>m.length&&!(d=i(h,!0)););d=d||i(h,!0),d&&(f.repeaterPart=m),m=k+f.quantifier.max}else{for(var l=0,n=f.quantifier.max-1;l<n&&!(d=i(h,!0));l++);m=k+"{"+f.quantifier.min+","+f.quantifier.max+"}"}}else if(void 0!==f.matches)for(var p=0;p<f.length&&!(d=i(f[p],c));p++);else{var q;if("["==f.charAt(0)){q=m,q+=f;for(var r=0;r<o;r++)q+=")";var s=new RegExp("^("+q+")$");d=s.test(j)}else for(var t=0,u=f.length;t<u;t++)if("\\"!==f.charAt(t)){q=m,q+=f.substr(0,t+1),q=q.replace(/\|$/,"");for(var r=0;r<o;r++)q+=")";var s=new RegExp("^("+q+")$");if(d=s.test(j))break}m+=f}if(d)break}return c&&(m+=")",o--),d}var j,k,l=c.buffer.slice(),m="",n=!1,o=0;null===f.regexTokens&&h(),l.splice(d,0,b),j=l.join("");for(var p=0;p<f.regexTokens.length;p++){var q=f.regexTokens[p];if(n=i(q,q.isGroup))break}return n},cardinality:1}}}}),b}(jQuery,Inputmask);
12086window.tinymce = window.tinymce || {
12087  base:   '/assets/tinymce',
12088  suffix: ''
12089};
12090// 4.3.3 (2016-01-14)
12091
12092/**
12093 * Compiled inline version. (Library mode)
12094 */
12095
12096/*jshint smarttabs:true, undef:true, latedef:true, curly:true, bitwise:true, camelcase:true */
12097/*globals $code */
12098
12099(function(exports, undefined) {
12100	"use strict";
12101
12102	var modules = {};
12103
12104	function require(ids, callback) {
12105		var module, defs = [];
12106
12107		for (var i = 0; i < ids.length; ++i) {
12108			module = modules[ids[i]] || resolve(ids[i]);
12109			if (!module) {
12110				throw 'module definition dependecy not found: ' + ids[i];
12111			}
12112
12113			defs.push(module);
12114		}
12115
12116		callback.apply(null, defs);
12117	}
12118
12119	function define(id, dependencies, definition) {
12120		if (typeof id !== 'string') {
12121			throw 'invalid module definition, module id must be defined and be a string';
12122		}
12123
12124		if (dependencies === undefined) {
12125			throw 'invalid module definition, dependencies must be specified';
12126		}
12127
12128		if (definition === undefined) {
12129			throw 'invalid module definition, definition function must be specified';
12130		}
12131
12132		require(dependencies, function() {
12133			modules[id] = definition.apply(null, arguments);
12134		});
12135	}
12136
12137	function defined(id) {
12138		return !!modules[id];
12139	}
12140
12141	function resolve(id) {
12142		var target = exports;
12143		var fragments = id.split(/[.\/]/);
12144
12145		for (var fi = 0; fi < fragments.length; ++fi) {
12146			if (!target[fragments[fi]]) {
12147				return;
12148			}
12149
12150			target = target[fragments[fi]];
12151		}
12152
12153		return target;
12154	}
12155
12156	function expose(ids) {
12157		var i, target, id, fragments, privateModules;
12158
12159		for (i = 0; i < ids.length; i++) {
12160			target = exports;
12161			id = ids[i];
12162			fragments = id.split(/[.\/]/);
12163
12164			for (var fi = 0; fi < fragments.length - 1; ++fi) {
12165				if (target[fragments[fi]] === undefined) {
12166					target[fragments[fi]] = {};
12167				}
12168
12169				target = target[fragments[fi]];
12170			}
12171
12172			target[fragments[fragments.length - 1]] = modules[id];
12173		}
12174		
12175		// Expose private modules for unit tests
12176		if (exports.AMDLC_TESTS) {
12177			privateModules = exports.privateModules || {};
12178
12179			for (id in modules) {
12180				privateModules[id] = modules[id];
12181			}
12182
12183			for (i = 0; i < ids.length; i++) {
12184				delete privateModules[ids[i]];
12185			}
12186
12187			exports.privateModules = privateModules;
12188		}
12189	}
12190
12191// Included from: js/tinymce/classes/geom/Rect.js
12192
12193/**
12194 * Rect.js
12195 *
12196 * Released under LGPL License.
12197 * Copyright (c) 1999-2015 Ephox Corp. All rights reserved
12198 *
12199 * License: http://www.tinymce.com/license
12200 * Contributing: http://www.tinymce.com/contributing
12201 */
12202
12203/**
12204 * Contains various tools for rect/position calculation.
12205 *
12206 * @class tinymce.geom.Rect
12207 */
12208define("tinymce/geom/Rect", [
12209], function() {
12210	"use strict";
12211
12212	var min = Math.min, max = Math.max, round = Math.round;
12213
12214	/**
12215	 * Returns the rect positioned based on the relative position name
12216	 * to the target rect.
12217	 *
12218	 * @method relativePosition
12219	 * @param {Rect} rect Source rect to modify into a new rect.
12220	 * @param {Rect} targetRect Rect to move relative to based on the rel option.
12221	 * @param {String} rel Relative position. For example: tr-bl.
12222	 */
12223	function relativePosition(rect, targetRect, rel) {
12224		var x, y, w, h, targetW, targetH;
12225
12226		x = targetRect.x;
12227		y = targetRect.y;
12228		w = rect.w;
12229		h = rect.h;
12230		targetW = targetRect.w;
12231		targetH = targetRect.h;
12232
12233		rel = (rel || '').split('');
12234
12235		if (rel[0] === 'b') {
12236			y += targetH;
12237		}
12238
12239		if (rel[1] === 'r') {
12240			x += targetW;
12241		}
12242
12243		if (rel[0] === 'c') {
12244			y += round(targetH / 2);
12245		}
12246
12247		if (rel[1] === 'c') {
12248			x += round(targetW / 2);
12249		}
12250
12251		if (rel[3] === 'b') {
12252			y -= h;
12253		}
12254
12255		if (rel[4] === 'r') {
12256			x -= w;
12257		}
12258
12259		if (rel[3] === 'c') {
12260			y -= round(h / 2);
12261		}
12262
12263		if (rel[4] === 'c') {
12264			x -= round(w / 2);
12265		}
12266
12267		return create(x, y, w, h);
12268	}
12269
12270	/**
12271	 * Tests various positions to get the most suitable one.
12272	 *
12273	 * @method findBestRelativePosition
12274	 * @param {Rect} rect Rect to use as source.
12275	 * @param {Rect} targetRect Rect to move relative to.
12276	 * @param {Rect} constrainRect Rect to constrain within.
12277	 * @param {Array} rels Array of relative positions to test against.
12278	 */
12279	function findBestRelativePosition(rect, targetRect, constrainRect, rels) {
12280		var pos, i;
12281
12282		for (i = 0; i < rels.length; i++) {
12283			pos = relativePosition(rect, targetRect, rels[i]);
12284
12285			if (pos.x >= constrainRect.x && pos.x + pos.w <= constrainRect.w + constrainRect.x &&
12286				pos.y >= constrainRect.y && pos.y + pos.h <= constrainRect.h + constrainRect.y) {
12287				return rels[i];
12288			}
12289		}
12290
12291		return null;
12292	}
12293
12294	/**
12295	 * Inflates the rect in all directions.
12296	 *
12297	 * @method inflate
12298	 * @param {Rect} rect Rect to expand.
12299	 * @param {Number} w Relative width to expand by.
12300	 * @param {Number} h Relative height to expand by.
12301	 * @return {Rect} New expanded rect.
12302	 */
12303	function inflate(rect, w, h) {
12304		return create(rect.x - w, rect.y - h, rect.w + w * 2, rect.h + h * 2);
12305	}
12306
12307	/**
12308	 * Returns the intersection of the specified rectangles.
12309	 *
12310	 * @method intersect
12311	 * @param {Rect} rect The first rectangle to compare.
12312	 * @param {Rect} cropRect The second rectangle to compare.
12313	 * @return {Rect} The intersection of the two rectangles or null if they don't intersect.
12314	 */
12315	function intersect(rect, cropRect) {
12316		var x1, y1, x2, y2;
12317
12318		x1 = max(rect.x, cropRect.x);
12319		y1 = max(rect.y, cropRect.y);
12320		x2 = min(rect.x + rect.w, cropRect.x + cropRect.w);
12321		y2 = min(rect.y + rect.h, cropRect.y + cropRect.h);
12322
12323		if (x2 - x1 < 0 || y2 - y1 < 0) {
12324			return null;
12325		}
12326
12327		return create(x1, y1, x2 - x1, y2 - y1);
12328	}
12329
12330	/**
12331	 * Returns a rect clamped within the specified clamp rect. This forces the
12332	 * rect to be inside the clamp rect.
12333	 *
12334	 * @method clamp
12335	 * @param {Rect} rect Rectangle to force within clamp rect.
12336	 * @param {Rect} clampRect Rectable to force within.
12337	 * @param {Boolean} fixedSize True/false if size should be fixed.
12338	 * @return {Rect} Clamped rect.
12339	 */
12340	function clamp(rect, clampRect, fixedSize) {
12341		var underflowX1, underflowY1, overflowX2, overflowY2,
12342			x1, y1, x2, y2, cx2, cy2;
12343
12344		x1 = rect.x;
12345		y1 = rect.y;
12346		x2 = rect.x + rect.w;
12347		y2 = rect.y + rect.h;
12348		cx2 = clampRect.x + clampRect.w;
12349		cy2 = clampRect.y + clampRect.h;
12350
12351		underflowX1 = max(0, clampRect.x - x1);
12352		underflowY1 = max(0, clampRect.y - y1);
12353		overflowX2 = max(0, x2 - cx2);
12354		overflowY2 = max(0, y2 - cy2);
12355
12356		x1 += underflowX1;
12357		y1 += underflowY1;
12358
12359		if (fixedSize) {
12360			x2 += underflowX1;
12361			y2 += underflowY1;
12362			x1 -= overflowX2;
12363			y1 -= overflowY2;
12364		}
12365
12366		x2 -= overflowX2;
12367		y2 -= overflowY2;
12368
12369		return create(x1, y1, x2 - x1, y2 - y1);
12370	}
12371
12372	/**
12373	 * Creates a new rectangle object.
12374	 *
12375	 * @method create
12376	 * @param {Number} x Rectangle x location.
12377	 * @param {Number} y Rectangle y location.
12378	 * @param {Number} w Rectangle width.
12379	 * @param {Number} h Rectangle height.
12380	 * @return {Rect} New rectangle object.
12381	 */
12382	function create(x, y, w, h) {
12383		return {x: x, y: y, w: w, h: h};
12384	}
12385
12386	/**
12387	 * Creates a new rectangle object form a clientRects object.
12388	 *
12389	 * @method fromClientRect
12390	 * @param {ClientRect} clientRect DOM ClientRect object.
12391	 * @return {Rect} New rectangle object.
12392	 */
12393	function fromClientRect(clientRect) {
12394		return create(clientRect.left, clientRect.top, clientRect.width, clientRect.height);
12395	}
12396
12397	return {
12398		inflate: inflate,
12399		relativePosition: relativePosition,
12400		findBestRelativePosition: findBestRelativePosition,
12401		intersect: intersect,
12402		clamp: clamp,
12403		create: create,
12404		fromClientRect: fromClientRect
12405	};
12406});
12407
12408// Included from: js/tinymce/classes/util/Promise.js
12409
12410/**
12411 * Promise.js
12412 *
12413 * Released under LGPL License.
12414 * Copyright (c) 1999-2015 Ephox Corp. All rights reserved
12415 *
12416 * Promise polyfill under MIT license: https://github.com/taylorhakes/promise-polyfill
12417 *
12418 * License: http://www.tinymce.com/license
12419 * Contributing: http://www.tinymce.com/contributing
12420 */
12421
12422/* eslint-disable */
12423/* jshint ignore:start */
12424
12425/**
12426 * Modifed to be a feature fill and wrapped as tinymce module.
12427 */
12428define("tinymce/util/Promise", [], function() {
12429	if (window.Promise) {
12430		return window.Promise;
12431	}
12432
12433	// Use polyfill for setImmediate for performance gains
12434	var asap = Promise.immediateFn || (typeof setImmediate === 'function' && setImmediate) ||
12435		function(fn) { setTimeout(fn, 1); };
12436
12437	// Polyfill for Function.prototype.bind
12438	function bind(fn, thisArg) {
12439		return function() {
12440			fn.apply(thisArg, arguments);
12441		};
12442	}
12443
12444	var isArray = Array.isArray || function(value) { return Object.prototype.toString.call(value) === "[object Array]"; };
12445
12446	function Promise(fn) {
12447		if (typeof this !== 'object') throw new TypeError('Promises must be constructed via new');
12448		if (typeof fn !== 'function') throw new TypeError('not a function');
12449		this._state = null;
12450		this._value = null;
12451		this._deferreds = [];
12452
12453		doResolve(fn, bind(resolve, this), bind(reject, this));
12454	}
12455
12456	function handle(deferred) {
12457		var me = this;
12458		if (this._state === null) {
12459			this._deferreds.push(deferred);
12460			return;
12461		}
12462		asap(function() {
12463			var cb = me._state ? deferred.onFulfilled : deferred.onRejected;
12464			if (cb === null) {
12465				(me._state ? deferred.resolve : deferred.reject)(me._value);
12466				return;
12467			}
12468			var ret;
12469			try {
12470				ret = cb(me._value);
12471			}
12472			catch (e) {
12473				deferred.reject(e);
12474				return;
12475			}
12476			deferred.resolve(ret);
12477		});
12478	}
12479
12480	function resolve(newValue) {
12481		try { //Promise Resolution Procedure: https://github.com/promises-aplus/promises-spec#the-promise-resolution-procedure
12482			if (newValue === this) throw new TypeError('A promise cannot be resolved with itself.');
12483			if (newValue && (typeof newValue === 'object' || typeof newValue === 'function')) {
12484				var then = newValue.then;
12485				if (typeof then === 'function') {
12486					doResolve(bind(then, newValue), bind(resolve, this), bind(reject, this));
12487					return;
12488				}
12489			}
12490			this._state = true;
12491			this._value = newValue;
12492			finale.call(this);
12493		} catch (e) { reject.call(this, e); }
12494	}
12495
12496	function reject(newValue) {
12497		this._state = false;
12498		this._value = newValue;
12499		finale.call(this);
12500	}
12501
12502	function finale() {
12503		for (var i = 0, len = this._deferreds.length; i < len; i++) {
12504			handle.call(this, this._deferreds[i]);
12505		}
12506		this._deferreds = null;
12507	}
12508
12509	function Handler(onFulfilled, onRejected, resolve, reject){
12510		this.onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : null;
12511		this.onRejected = typeof onRejected === 'function' ? onRejected : null;
12512		this.resolve = resolve;
12513		this.reject = reject;
12514	}
12515
12516	/**
12517	 * Take a potentially misbehaving resolver function and make sure
12518	 * onFulfilled and onRejected are only called once.
12519	 *
12520	 * Makes no guarantees about asynchrony.
12521	 */
12522	function doResolve(fn, onFulfilled, onRejected) {
12523		var done = false;
12524		try {
12525			fn(function (value) {
12526				if (done) return;
12527				done = true;
12528				onFulfilled(value);
12529			}, function (reason) {
12530				if (done) return;
12531				done = true;
12532				onRejected(reason);
12533			});
12534		} catch (ex) {
12535			if (done) return;
12536			done = true;
12537			onRejected(ex);
12538		}
12539	}
12540
12541	Promise.prototype['catch'] = function (onRejected) {
12542		return this.then(null, onRejected);
12543	};
12544
12545	Promise.prototype.then = function(onFulfilled, onRejected) {
12546		var me = this;
12547		return new Promise(function(resolve, reject) {
12548			handle.call(me, new Handler(onFulfilled, onRejected, resolve, reject));
12549		});
12550	};
12551
12552	Promise.all = function () {
12553		var args = Array.prototype.slice.call(arguments.length === 1 && isArray(arguments[0]) ? arguments[0] : arguments);
12554
12555		return new Promise(function (resolve, reject) {
12556			if (args.length === 0) return resolve([]);
12557			var remaining = args.length;
12558			function res(i, val) {
12559				try {
12560					if (val && (typeof val === 'object' || typeof val === 'function')) {
12561						var then = val.then;
12562						if (typeof then === 'function') {
12563							then.call(val, function (val) { res(i, val); }, reject);
12564							return;
12565						}
12566					}
12567					args[i] = val;
12568					if (--remaining === 0) {
12569						resolve(args);
12570					}
12571				} catch (ex) {
12572					reject(ex);
12573				}
12574			}
12575			for (var i = 0; i < args.length; i++) {
12576				res(i, args[i]);
12577			}
12578		});
12579	};
12580
12581	Promise.resolve = function (value) {
12582		if (value && typeof value === 'object' && value.constructor === Promise) {
12583			return value;
12584		}
12585
12586		return new Promise(function (resolve) {
12587			resolve(value);
12588		});
12589	};
12590
12591	Promise.reject = function (value) {
12592		return new Promise(function (resolve, reject) {
12593			reject(value);
12594		});
12595	};
12596
12597	Promise.race = function (values) {
12598		return new Promise(function (resolve, reject) {
12599			for(var i = 0, len = values.length; i < len; i++) {
12600				values[i].then(resolve, reject);
12601			}
12602		});
12603	};
12604
12605	return Promise;
12606});
12607
12608/* jshint ignore:end */
12609/* eslint-enable */
12610
12611// Included from: js/tinymce/classes/util/Delay.js
12612
12613/**
12614 * Delay.js
12615 *
12616 * Released under LGPL License.
12617 * Copyright (c) 1999-2015 Ephox Corp. All rights reserved
12618 *
12619 * License: http://www.tinymce.com/license
12620 * Contributing: http://www.tinymce.com/contributing
12621 */
12622
12623/**
12624 * Utility class for working with delayed actions like setTimeout.
12625 *
12626 * @class tinymce.util.Delay
12627 */
12628define("tinymce/util/Delay", [
12629	"tinymce/util/Promise"
12630], function(Promise) {
12631	var requestAnimationFramePromise;
12632
12633	function requestAnimationFrame(callback, element) {
12634		var i, requestAnimationFrameFunc = window.requestAnimationFrame, vendors = ['ms', 'moz', 'webkit'];
12635
12636		function featurefill(callback) {
12637			window.setTimeout(callback, 0);
12638		}
12639
12640		for (i = 0; i < vendors.length && !requestAnimationFrameFunc; i++) {
12641			requestAnimationFrameFunc = window[vendors[i] + 'RequestAnimationFrame'];
12642		}
12643
12644		if (!requestAnimationFrameFunc) {
12645			requestAnimationFrameFunc = featurefill;
12646		}
12647
12648		requestAnimationFrameFunc(callback, element);
12649	}
12650
12651	function wrappedSetTimeout(callback, time) {
12652		if (typeof time != 'number') {
12653			time = 0;
12654		}
12655
12656		return setTimeout(callback, time);
12657	}
12658
12659	function wrappedSetInterval(callback, time) {
12660		if (typeof time != 'number') {
12661			time = 0;
12662		}
12663
12664		return setInterval(callback, time);
12665	}
12666
12667	function wrappedClearTimeout(id) {
12668		return clearTimeout(id);
12669	}
12670
12671	function wrappedClearInterval(id) {
12672		return clearInterval(id);
12673	}
12674
12675	return {
12676		/**
12677		 * Requests an animation frame and fallbacks to a timeout on older browsers.
12678		 *
12679		 * @method requestAnimationFrame
12680		 * @param {function} callback Callback to execute when a new frame is available.
12681		 * @param {DOMElement} element Optional element to scope it to.
12682		 */
12683		requestAnimationFrame: function(callback, element) {
12684			if (requestAnimationFramePromise) {
12685				requestAnimationFramePromise.then(callback);
12686				return;
12687			}
12688
12689			requestAnimationFramePromise = new Promise(function(resolve) {
12690				if (!element) {
12691					element = document.body;
12692				}
12693
12694				requestAnimationFrame(resolve, element);
12695			}).then(callback);
12696		},
12697
12698		/**
12699		 * Sets a timer in ms and executes the specified callback when the timer runs out.
12700		 *
12701		 * @method setTimeout
12702		 * @param {function} callback Callback to execute when timer runs out.
12703		 * @param {Number} time Optional time to wait before the callback is executed, defaults to 0.
12704		 * @return {Number} Timeout id number.
12705		 */
12706		setTimeout: wrappedSetTimeout,
12707
12708		/**
12709		 * Sets an interval timer in ms and executes the specified callback at every interval of that time.
12710		 *
12711		 * @method setInterval
12712		 * @param {function} callback Callback to execute when interval time runs out.
12713		 * @param {Number} time Optional time to wait before the callback is executed, defaults to 0.
12714		 * @return {Number} Timeout id number.
12715		 */
12716		setInterval: wrappedSetInterval,
12717
12718		/**
12719		 * Sets an editor timeout it's similar to setTimeout except that it checks if the editor instance is
12720		 * still alive when the callback gets executed.
12721		 *
12722		 * @method setEditorTimeout
12723		 * @param {tinymce.Editor} editor Editor instance to check the removed state on.
12724		 * @param {function} callback Callback to execute when timer runs out.
12725		 * @param {Number} time Optional time to wait before the callback is executed, defaults to 0.
12726		 * @return {Number} Timeout id number.
12727		 */
12728		setEditorTimeout: function(editor, callback, time) {
12729			return wrappedSetTimeout(function() {
12730				if (!editor.removed) {
12731					callback();
12732				}
12733			}, time);
12734		},
12735
12736		/**
12737		 * Sets an interval timer it's similar to setInterval except that it checks if the editor instance is
12738		 * still alive when the callback gets executed.
12739		 *
12740		 * @method setEditorInterval
12741		 * @param {function} callback Callback to execute when interval time runs out.
12742		 * @param {Number} time Optional time to wait before the callback is executed, defaults to 0.
12743		 * @return {Number} Timeout id number.
12744		 */
12745		setEditorInterval: function(editor, callback, time) {
12746			var timer;
12747
12748			timer = wrappedSetInterval(function() {
12749				if (!editor.removed) {
12750					callback();
12751				} else {
12752					clearInterval(timer);
12753				}
12754			}, time);
12755
12756			return timer;
12757		},
12758
12759		/**
12760		 * Creates throttled callback function that only gets executed once within the specified time.
12761		 *
12762		 * @method throttle
12763		 * @param {function} callback Callback to execute when timer finishes.
12764		 * @param {Number} time Optional time to wait before the callback is executed, defaults to 0.
12765		 * @return {Function} Throttled function callback.
12766		 */
12767		throttle: function(callback, time) {
12768			var timer;
12769
12770			return function() {
12771				var args = arguments;
12772
12773				clearTimeout(timer);
12774
12775				timer = wrappedSetTimeout(function() {
12776					callback.apply(this, args);
12777				}, time);
12778			};
12779		},
12780
12781		/**
12782		 * Clears an interval timer so it won't execute.
12783		 *
12784		 * @method clearInterval
12785		 * @param {Number} Interval timer id number.
12786		 */
12787		clearInterval: wrappedClearInterval,
12788
12789		/**
12790		 * Clears an timeout timer so it won't execute.
12791		 *
12792		 * @method clearTimeout
12793		 * @param {Number} Timeout timer id number.
12794		 */
12795		clearTimeout: wrappedClearTimeout
12796	};
12797});
12798
12799// Included from: js/tinymce/classes/dom/EventUtils.js
12800
12801/**
12802 * EventUtils.js
12803 *
12804 * Released under LGPL License.
12805 * Copyright (c) 1999-2015 Ephox Corp. All rights reserved
12806 *
12807 * License: http://www.tinymce.com/license
12808 * Contributing: http://www.tinymce.com/contributing
12809 */
12810
12811/*jshint loopfunc:true*/
12812/*eslint no-loop-func:0 */
12813
12814/**
12815 * This class wraps the browsers native event logic with more convenient methods.
12816 *
12817 * @class tinymce.dom.EventUtils
12818 */
12819define("tinymce/dom/EventUtils", [
12820	"tinymce/util/Delay"
12821], function(Delay) {
12822	"use strict";
12823
12824	var eventExpandoPrefix = "mce-data-";
12825	var mouseEventRe = /^(?:mouse|contextmenu)|click/;
12826	var deprecated = {keyLocation: 1, layerX: 1, layerY: 1, returnValue: 1, webkitMovementX: 1, webkitMovementY: 1};
12827
12828	/**
12829	 * Binds a native event to a callback on the speified target.
12830	 */
12831	function addEvent(target, name, callback, capture) {
12832		if (target.addEventListener) {
12833			target.addEventListener(name, callback, capture || false);
12834		} else if (target.attachEvent) {
12835			target.attachEvent('on' + name, callback);
12836		}
12837	}
12838
12839	/**
12840	 * Unbinds a native event callback on the specified target.
12841	 */
12842	function removeEvent(target, name, callback, capture) {
12843		if (target.removeEventListener) {
12844			target.removeEventListener(name, callback, capture || false);
12845		} else if (target.detachEvent) {
12846			target.detachEvent('on' + name, callback);
12847		}
12848	}
12849
12850	/**
12851	 * Normalizes a native event object or just adds the event specific methods on a custom event.
12852	 */
12853	function fix(originalEvent, data) {
12854		var name, event = data || {}, undef;
12855
12856		// Dummy function that gets replaced on the delegation state functions
12857		function returnFalse() {
12858			return false;
12859		}
12860
12861		// Dummy function that gets replaced on the delegation state functions
12862		function returnTrue() {
12863			return true;
12864		}
12865
12866		// Copy all properties from the original event
12867		for (name in originalEvent) {
12868			// layerX/layerY is deprecated in Chrome and produces a warning
12869			if (!deprecated[name]) {
12870				event[name] = originalEvent[name];
12871			}
12872		}
12873
12874		// Normalize target IE uses srcElement
12875		if (!event.target) {
12876			event.target = event.srcElement || document;
12877		}
12878
12879		// Calculate pageX/Y if missing and clientX/Y available
12880		if (originalEvent && mouseEventRe.test(originalEvent.type) && originalEvent.pageX === undef && originalEvent.clientX !== undef) {
12881			var eventDoc = event.target.ownerDocument || document;
12882			var doc = eventDoc.documentElement;
12883			var body = eventDoc.body;
12884
12885			event.pageX = originalEvent.clientX + (doc && doc.scrollLeft || body && body.scrollLeft || 0) -
12886				(doc && doc.clientLeft || body && body.clientLeft || 0);
12887
12888			event.pageY = originalEvent.clientY + (doc && doc.scrollTop || body && body.scrollTop || 0) -
12889				(doc && doc.clientTop || body && body.clientTop || 0);
12890		}
12891
12892		// Add preventDefault method
12893		event.preventDefault = function() {
12894			event.isDefaultPrevented = returnTrue;
12895
12896			// Execute preventDefault on the original event object
12897			if (originalEvent) {
12898				if (originalEvent.preventDefault) {
12899					originalEvent.preventDefault();
12900				} else {
12901					originalEvent.returnValue = false; // IE
12902				}
12903			}
12904		};
12905
12906		// Add stopPropagation
12907		event.stopPropagation = function() {
12908			event.isPropagationStopped = returnTrue;
12909
12910			// Execute stopPropagation on the original event object
12911			if (originalEvent) {
12912				if (originalEvent.stopPropagation) {
12913					originalEvent.stopPropagation();
12914				} else {
12915					originalEvent.cancelBubble = true; // IE
12916				}
12917			}
12918		};
12919
12920		// Add stopImmediatePropagation
12921		event.stopImmediatePropagation = function() {
12922			event.isImmediatePropagationStopped = returnTrue;
12923			event.stopPropagation();
12924		};
12925
12926		// Add event delegation states
12927		if (!event.isDefaultPrevented) {
12928			event.isDefaultPrevented = returnFalse;
12929			event.isPropagationStopped = returnFalse;
12930			event.isImmediatePropagationStopped = returnFalse;
12931		}
12932
12933		// Add missing metaKey for IE 8
12934		if (typeof event.metaKey == 'undefined') {
12935			event.metaKey = false;
12936		}
12937
12938		return event;
12939	}
12940
12941	/**
12942	 * Bind a DOMContentLoaded event across browsers and executes the callback once the page DOM is initialized.
12943	 * It will also set/check the domLoaded state of the event_utils instance so ready isn't called multiple times.
12944	 */
12945	function bindOnReady(win, callback, eventUtils) {
12946		var doc = win.document, event = {type: 'ready'};
12947
12948		if (eventUtils.domLoaded) {
12949			callback(event);
12950			return;
12951		}
12952
12953		// Gets called when the DOM is ready
12954		function readyHandler() {
12955			if (!eventUtils.domLoaded) {
12956				eventUtils.domLoaded = true;
12957				callback(event);
12958			}
12959		}
12960
12961		function waitForDomLoaded() {
12962			// Check complete or interactive state if there is a body
12963			// element on some iframes IE 8 will produce a null body
12964			if (doc.readyState === "complete" || (doc.readyState === "interactive" && doc.body)) {
12965				removeEvent(doc, "readystatechange", waitForDomLoaded);
12966				readyHandler();
12967			}
12968		}
12969
12970		function tryScroll() {
12971			try {
12972				// If IE is used, use the trick by Diego Perini licensed under MIT by request to the author.
12973				// http://javascript.nwbox.com/IEContentLoaded/
12974				doc.documentElement.doScroll("left");
12975			} catch (ex) {
12976				Delay.setTimeout(tryScroll);
12977				return;
12978			}
12979
12980			readyHandler();
12981		}
12982
12983		// Use W3C method
12984		if (doc.addEventListener) {
12985			if (doc.readyState === "complete") {
12986				readyHandler();
12987			} else {
12988				addEvent(win, 'DOMContentLoaded', readyHandler);
12989			}
12990		} else {
12991			// Use IE method
12992			addEvent(doc, "readystatechange", waitForDomLoaded);
12993
12994			// Wait until we can scroll, when we can the DOM is initialized
12995			if (doc.documentElement.doScroll && win.self === win.top) {
12996				tryScroll();
12997			}
12998		}
12999
13000		// Fallback if any of the above methods should fail for some odd reason
13001		addEvent(win, 'load', readyHandler);
13002	}
13003
13004	/**
13005	 * This class enables you to bind/unbind native events to elements and normalize it's behavior across browsers.
13006	 */
13007	function EventUtils() {
13008		var self = this, events = {}
13008, count, expando, hasFocusIn, hasMouseEnterLeave, mouseEnterLeave;
13009
13010		expando = eventExpandoPrefix + (+new Date()).toString(32);
13011		hasMouseEnterLeave = "onmouseenter" in document.documentElement;
13012		hasFocusIn = "onfocusin" in document.documentElement;
13013		mouseEnterLeave = {mouseenter: 'mouseover', mouseleave: 'mouseout'};
13014		count = 1;
13015
13016		// State if the DOMContentLoaded was executed or not
13017		self.domLoaded = false;
13018		self.events = events;
13019
13020		/**
13021		 * Executes all event handler callbacks for a specific event.
13022		 *
13023		 * @private
13024		 * @param {Event} evt Event object.
13025		 * @param {String} id Expando id value to look for.
13026		 */
13027		function executeHandlers(evt, id) {
13028			var callbackList, i, l, callback, container = events[id];
13029
13030			callbackList = container && container[evt.type];
13031			if (callbackList) {
13032				for (i = 0, l = callbackList.length; i < l; i++) {
13033					callback = callbackList[i];
13034
13035					// Check if callback exists might be removed if a unbind is called inside the callback
13036					if (callback && callback.func.call(callback.scope, evt) === false) {
13037						evt.preventDefault();
13038					}
13039
13040					// Should we stop propagation to immediate listeners
13041					if (evt.isImmediatePropagationStopped()) {
13042						return;
13043					}
13044				}
13045			}
13046		}
13047
13048		/**
13049		 * Binds a callback to an event on the specified target.
13050		 *
13051		 * @method bind
13052		 * @param {Object} target Target node/window or custom object.
13053		 * @param {String} names Name of the event to bind.
13054		 * @param {function} callback Callback function to execute when the event occurs.
13055		 * @param {Object} scope Scope to call the callback function on, defaults to target.
13056		 * @return {function} Callback function that got bound.
13057		 */
13058		self.bind = function(target, names, callback, scope) {
13059			var id, callbackList, i, name, fakeName, nativeHandler, capture, win = window;
13060
13061			// Native event handler function patches the event and executes the callbacks for the expando
13062			function defaultNativeHandler(evt) {
13063				executeHandlers(fix(evt || win.event), id);
13064			}
13065
13066			// Don't bind to text nodes or comments
13067			if (!target || target.nodeType === 3 || target.nodeType === 8) {
13068				return;
13069			}
13070
13071			// Create or get events id for the target
13072			if (!target[expando]) {
13073				id = count++;
13074				target[expando] = id;
13075				events[id] = {};
13076			} else {
13077				id = target[expando];
13078			}
13079
13080			// Setup the specified scope or use the target as a default
13081			scope = scope || target;
13082
13083			// Split names and bind each event, enables you to bind multiple events with one call
13084			names = names.split(' ');
13085			i = names.length;
13086			while (i--) {
13087				name = names[i];
13088				nativeHandler = defaultNativeHandler;
13089				fakeName = capture = false;
13090
13091				// Use ready instead of DOMContentLoaded
13092				if (name === "DOMContentLoaded") {
13093					name = "ready";
13094				}
13095
13096				// DOM is already ready
13097				if (self.domLoaded && name === "ready" && target.readyState == 'complete') {
13098					callback.call(scope, fix({type: name}));
13099					continue;
13100				}
13101
13102				// Handle mouseenter/mouseleaver
13103				if (!hasMouseEnterLeave) {
13104					fakeName = mouseEnterLeave[name];
13105
13106					if (fakeName) {
13107						nativeHandler = function(evt) {
13108							var current, related;
13109
13110							current = evt.currentTarget;
13111							related = evt.relatedTarget;
13112
13113							// Check if related is inside the current target if it's not then the event should
13114							// be ignored since it's a mouseover/mouseout inside the element
13115							if (related && current.contains) {
13116								// Use contains for performance
13117								related = current.contains(related);
13118							} else {
13119								while (related && related !== current) {
13120									related = related.parentNode;
13121								}
13122							}
13123
13124							// Fire fake event
13125							if (!related) {
13126								evt = fix(evt || win.event);
13127								evt.type = evt.type === 'mouseout' ? 'mouseleave' : 'mouseenter';
13128								evt.target = current;
13129								executeHandlers(evt, id);
13130							}
13131						};
13132					}
13133				}
13134
13135				// Fake bubbling of focusin/focusout
13136				if (!hasFocusIn && (name === "focusin" || name === "focusout")) {
13137					capture = true;
13138					fakeName = name === "focusin" ? "focus" : "blur";
13139					nativeHandler = function(evt) {
13140						evt = fix(evt || win.event);
13141						evt.type = evt.type === 'focus' ? 'focusin' : 'focusout';
13142						executeHandlers(evt, id);
13143					};
13144				}
13145
13146				// Setup callback list and bind native event
13147				callbackList = events[id][name];
13148				if (!callbackList) {
13149					events[id][name] = callbackList = [{func: callback, scope: scope}];
13150					callbackList.fakeName = fakeName;
13151					callbackList.capture = capture;
13152					//callbackList.callback = callback;
13153
13154					// Add the nativeHandler to the callback list so that we can later unbind it
13155					callbackList.nativeHandler = nativeHandler;
13156
13157					// Check if the target has native events support
13158
13159					if (name === "ready") {
13160						bindOnReady(target, nativeHandler, self);
13161					} else {
13162						addEvent(target, fakeName || name, nativeHandler, capture);
13163					}
13164				} else {
13165					if (name === "ready" && self.domLoaded) {
13166						callback({type: name});
13167					} else {
13168						// If it already has an native handler then just push the callback
13169						callbackList.push({func: callback, scope: scope});
13170					}
13171				}
13172			}
13173
13174			target = callbackList = 0; // Clean memory for IE
13175
13176			return callback;
13177		};
13178
13179		/**
13180		 * Unbinds the specified event by name, name and callback or all events on the target.
13181		 *
13182		 * @method unbind
13183		 * @param {Object} target Target node/window or custom object.
13184		 * @param {String} names Optional event name to unbind.
13185		 * @param {function} callback Optional callback function to unbind.
13186		 * @return {EventUtils} Event utils instance.
13187		 */
13188		self.unbind = function(target, names, callback) {
13189			var id, callbackList, i, ci, name, eventMap;
13190
13191			// Don't bind to text nodes or comments
13192			if (!target || target.nodeType === 3 || target.nodeType === 8) {
13193				return self;
13194			}
13195
13196			// Unbind event or events if the target has the expando
13197			id = target[expando];
13198			if (id) {
13199				eventMap = events[id];
13200
13201				// Specific callback
13202				if (names) {
13203					names = names.split(' ');
13204					i = names.length;
13205					while (i--) {
13206						name = names[i];
13207						callbackList = eventMap[name];
13208
13209						// Unbind the event if it exists in the map
13210						if (callbackList) {
13211							// Remove specified callback
13212							if (callback) {
13213								ci = callbackList.length;
13214								while (ci--) {
13215									if (callbackList[ci].func === callback) {
13216										var nativeHandler = callbackList.nativeHandler;
13217										var fakeName = callbackList.fakeName, capture = callbackList.capture;
13218
13219										// Clone callbackList since unbind inside a callback would otherwise break the handlers loop
13220										callbackList = callbackList.slice(0, ci).concat(callbackList.slice(ci + 1));
13221										callbackList.nativeHandler = nativeHandler;
13222										callbackList.fakeName = fakeName;
13223										callbackList.capture = capture;
13224
13225										eventMap[name] = callbackList;
13226									}
13227								}
13228							}
13229
13230							// Remove all callbacks if there isn't a specified callback or there is no callbacks left
13231							if (!callback || callbackList.length === 0) {
13232								delete eventMap[name];
13233								removeEvent(target, callbackList.fakeName || name, callbackList.nativeHandler, callbackList.capture);
13234							}
13235						}
13236					}
13237				} else {
13238					// All events for a specific element
13239					for (name in eventMap) {
13240						callbackList = eventMap[name];
13241						removeEvent(target, callbackList.fakeName || name, callbackList.nativeHandler, callbackList.capture);
13242					}
13243
13244					eventMap = {};
13245				}
13246
13247				// Check if object is empty, if it isn't then we won't remove the expando map
13248				for (name in eventMap) {
13249					return self;
13250				}
13251
13252				// Delete event object
13253				delete events[id];
13254
13255				// Remove expando from target
13256				try {
13257					// IE will fail here since it can't delete properties from window
13258					delete target[expando];
13259				} catch (ex) {
13260					// IE will set it to null
13261					target[expando] = null;
13262				}
13263			}
13264
13265			return self;
13266		};
13267
13268		/**
13269		 * Fires the specified event on the specified target.
13270		 *
13271		 * @method fire
13272		 * @param {Object} target Target node/window or custom object.
13273		 * @param {String} name Event name to fire.
13274		 * @param {Object} args Optional arguments to send to the observers.
13275		 * @return {EventUtils} Event utils instance.
13276		 */
13277		self.fire = function(target, name, args) {
13278			var id;
13279
13280			// Don't bind to text nodes or comments
13281			if (!target || target.nodeType === 3 || target.nodeType === 8) {
13282				return self;
13283			}
13284
13285			// Build event object by patching the args
13286			args = fix(null, args);
13287			args.type = name;
13288			args.target = target;
13289
13290			do {
13291				// Found an expando that means there is listeners to execute
13292				id = target[expando];
13293				if (id) {
13294					executeHandlers(args, id);
13295				}
13296
13297				// Walk up the DOM
13298				target = target.parentNode || target.ownerDocument || target.defaultView || target.parentWindow;
13299			} while (target && !args.isPropagationStopped());
13300
13301			return self;
13302		};
13303
13304		/**
13305		 * Removes all bound event listeners for the specified target. This will also remove any bound
13306		 * listeners to child nodes within that target.
13307		 *
13308		 * @method clean
13309		 * @param {Object} target Target node/window object.
13310		 * @return {EventUtils} Event utils instance.
13311		 */
13312		self.clean = function(target) {
13313			var i, children, unbind = self.unbind;
13314
13315			// Don't bind to text nodes or comments
13316			if (!target || target.nodeType === 3 || target.nodeType === 8) {
13317				return self;
13318			}
13319
13320			// Unbind any element on the specified target
13321			if (target[expando]) {
13322				unbind(target);
13323			}
13324
13325			// Target doesn't have getElementsByTagName it's probably a window object then use it's document to find the children
13326			if (!target.getElementsByTagName) {
13327				target = target.document;
13328			}
13329
13330			// Remove events from each child element
13331			if (target && target.getElementsByTagName) {
13332				unbind(target);
13333
13334				children = target.getElementsByTagName('*');
13335				i = children.length;
13336				while (i--) {
13337					target = children[i];
13338
13339					if (target[expando]) {
13340						unbind(target);
13341					}
13342				}
13343			}
13344
13345			return self;
13346		};
13347
13348		/**
13349		 * Destroys the event object. Call this on IE to remove memory leaks.
13350		 */
13351		self.destroy = function() {
13352			events = {};
13353		};
13354
13355		// Legacy function for canceling events
13356		self.cancel = function(e) {
13357			if (e) {
13358				e.preventDefault();
13359				e.stopImmediatePropagation();
13360			}
13361
13362			return false;
13363		};
13364	}
13365
13366	EventUtils.Event = new EventUtils();
13367	EventUtils.Event.bind(window, 'ready', function() {});
13368
13369	return EventUtils;
13370});
13371
13372// Included from: js/tinymce/classes/dom/Sizzle.jQuery.js
13373
13374/**
13375 * Sizzle.jQuery.js
13376 *
13377 * Released under LGPL License.
13378 * Copyright (c) 1999-2015 Ephox Corp. All rights reserved
13379 *
13380 * License: http://www.tinymce.com/license
13381 * Contributing: http://www.tinymce.com/contributing
13382 */
13383
13384/*global jQuery:true */
13385
13386/*
13387 * Fake Sizzle using jQuery.
13388 */
13389define("tinymce/dom/Sizzle", [], function() {
13390	// Detect if jQuery is loaded
13391	if (!window.jQuery) {
13392		throw new Error("Load jQuery first");
13393	}
13394
13395	return jQuery.find;
13396});
13397
13398// Included from: js/tinymce/classes/Env.js
13399
13400/**
13401 * Env.js
13402 *
13403 * Released under LGPL License.
13404 * Copyright (c) 1999-2015 Ephox Corp. All rights reserved
13405 *
13406 * License: http://www.tinymce.com/license
13407 * Contributing: http://www.tinymce.com/contributing
13408 */
13409
13410/**
13411 * This class contains various environment constants like browser versions etc.
13412 * Normally you don't want to sniff specific browser versions but sometimes you have
13413 * to when it's impossible to feature detect. So use this with care.
13414 *
13415 * @class tinymce.Env
13416 * @static
13417 */
13418define("tinymce/Env", [], function() {
13419	var nav = navigator, userAgent = nav.userAgent;
13420	var opera, webkit, ie, ie11, ie12, gecko, mac, iDevice, android, fileApi, phone, tablet;
13421
13422	function matchMediaQuery(query) {
13423		return "matchMedia" in window ? matchMedia(query).matches : false;
13424	}
13425
13426	opera = window.opera && window.opera.buildNumber;
13427	android = /Android/.test(userAgent);
13428	webkit = /WebKit/.test(userAgent);
13429	ie = !webkit && !opera && (/MSIE/gi).test(userAgent) && (/Explorer/gi).test(nav.appName);
13430	ie = ie && /MSIE (\w+)\./.exec(userAgent)[1];
13431	ie11 = userAgent.indexOf('Trident/') != -1 && (userAgent.indexOf('rv:') != -1 || nav.appName.indexOf('Netscape') != -1) ? 11 : false;
13432	ie12 = (userAgent.indexOf('Edge/') != -1 && !ie && !ie11) ? 12 : false;
13433	ie = ie || ie11 || ie12;
13434	gecko = !webkit && !ie11 && /Gecko/.test(userAgent);
13435	mac = userAgent.indexOf('Mac') != -1;
13436	iDevice = /(iPad|iPhone)/.test(userAgent);
13437	fileApi = "FormData" in window && "FileReader" in window && "URL" in window && !!URL.createObjectURL;
13438	phone = matchMediaQuery("only screen and (max-device-width: 480px)") && (android || iDevice);
13439	tablet = matchMediaQuery("only screen and (min-width: 800px)") && (android || iDevice);
13440
13441	if (ie12) {
13442		webkit = false;
13443	}
13444
13445	// Is a iPad/iPhone and not on iOS5 sniff the WebKit version since older iOS WebKit versions
13446	// says it has contentEditable support but there is no visible caret.
13447	var contentEditable = !iDevice || fileApi || userAgent.match(/AppleWebKit\/(\d*)/)[1] >= 534;
13448
13449	return {
13450		/**
13451		 * Constant that is true if the browser is Opera.
13452		 *
13453		 * @property opera
13454		 * @type Boolean
13455		 * @final
13456		 */
13457		opera: opera,
13458
13459		/**
13460		 * Constant that is true if the browser is WebKit (Safari/Chrome).
13461		 *
13462		 * @property webKit
13463		 * @type Boolean
13464		 * @final
13465		 */
13466		webkit: webkit,
13467
13468		/**
13469		 * Constant that is more than zero if the browser is IE.
13470		 *
13471		 * @property ie
13472		 * @type Boolean
13473		 * @final
13474		 */
13475		ie: ie,
13476
13477		/**
13478		 * Constant that is true if the browser is Gecko.
13479		 *
13480		 * @property gecko
13481		 * @type Boolean
13482		 * @final
13483		 */
13484		gecko: gecko,
13485
13486		/**
13487		 * Constant that is true if the os is Mac OS.
13488		 *
13489		 * @property mac
13490		 * @type Boolean
13491		 * @final
13492		 */
13493		mac: mac,
13494
13495		/**
13496		 * Constant that is true if the os is iOS.
13497		 *
13498		 * @property iOS
13499		 * @type Boolean
13500		 * @final
13501		 */
13502		iOS: iDevice,
13503
13504		/**
13505		 * Constant that is true if the os is android.
13506		 *
13507		 * @property android
13508		 * @type Boolean
13509		 * @final
13510		 */
13511		android: android,
13512
13513		/**
13514		 * Constant that is true if the browser supports editing.
13515		 *
13516		 * @property contentEditable
13517		 * @type Boolean
13518		 * @final
13519		 */
13520		contentEditable: contentEditable,
13521
13522		/**
13523		 * Transparent image data url.
13524		 *
13525		 * @property transparentSrc
13526		 * @type Boolean
13527		 * @final
13528		 */
13529		transparentSrc: "data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7",
13530
13531		/**
13532		 * Returns true/false if the browser can or can't place the caret after a inline block like an image.
13533		 *
13534		 * @property noCaretAfter
13535		 * @type Boolean
13536		 * @final
13537		 */
13538		caretAfter: ie != 8,
13539
13540		/**
13541		 * Constant that is true if the browser supports native DOM Ranges. IE 9+.
13542		 *
13543		 * @property range
13544		 * @type Boolean
13545		 */
13546		range: window.getSelection && "Range" in window,
13547
13548		/**
13549		 * Returns the IE document mode for non IE browsers this will fake IE 10.
13550		 *
13551		 * @property documentMode
13552		 * @type Number
13553		 */
13554		documentMode: ie && !ie12 ? (document.documentMode || 7) : 10,
13555
13556		/**
13557		 * Constant that is true if the browser has a modern file api.
13558		 *
13559		 * @property fileApi
13560		 * @type Boolean
13561		 */
13562		fileApi: fileApi,
13563
13564		/**
13565		 * Constant that is true if the browser supports contentEditable=false regions.
13566		 *
13567		 * @property ceFalse
13568		 * @type Boolean
13569		 */
13570		ceFalse: (ie === false || ie > 8),
13571
13572		desktop: !phone && !tablet
13573	};
13574});
13575
13576// Included from: js/tinymce/classes/util/Arr.js
13577
13578/**
13579 * Arr.js
13580 *
13581 * Released under LGPL License.
13582 * Copyright (c) 1999-2015 Ephox Corp. All rights reserved
13583 *
13584 * License: http://www.tinymce.com/license
13585 * Contributing: http://www.tinymce.com/contributing
13586 */
13587
13588/**
13589 * Array utility class.
13590 *
13591 * @private
13592 * @class tinymce.util.Arr
13593 */
13594define("tinymce/util/Arr", [], function() {
13595	var isArray = Array.isArray || function(obj) {
13596		return Object.prototype.toString.call(obj) === "[object Array]";
13597	};
13598
13599	function toArray(obj) {
13600		var array = obj, i, l;
13601
13602		if (!isArray(obj)) {
13603			array = [];
13604			for (i = 0, l = obj.length; i < l; i++) {
13605				array[i] = obj[i];
13606			}
13607		}
13608
13609		return array;
13610	}
13611
13612	function each(o, cb, s) {
13613		var n, l;
13614
13615		if (!o) {
13616			return 0;
13617		}
13618
13619		s = s || o;
13620
13621		if (o.length !== undefined) {
13622			// Indexed arrays, needed for Safari
13623			for (n = 0, l = o.length; n < l; n++) {
13624				if (cb.call(s, o[n], n, o) === false) {
13625					return 0;
13626				}
13627			}
13628		} else {
13629			// Hashtables
13630			for (n in o) {
13631				if (o.hasOwnProperty(n)) {
13632					if (cb.call(s, o[n], n, o) === false) {
13633						return 0;
13634					}
13635				}
13636			}
13637		}
13638
13639		return 1;
13640	}
13641
13642	function map(array, callback) {
13643		var out = [];
13644
13645		each(array, function(item, index) {
13646			out.push(callback(item, index, array));
13647		});
13648
13649		return out;
13650	}
13651
13652	function filter(a, f) {
13653		var o = [];
13654
13655		each(a, function(v, index) {
13656			if (!f || f(v, index, a)) {
13657				o.push(v);
13658			}
13659		});
13660
13661		return o;
13662	}
13663
13664	function indexOf(a, v) {
13665		var i, l;
13666
13667		if (a) {
13668			for (i = 0, l = a.length; i < l; i++) {
13669				if (a[i] === v) {
13670					return i;
13671				}
13672			}
13673		}
13674
13675		return -1;
13676	}
13677
13678	function reduce(collection, iteratee, accumulator, thisArg) {
13679		var i = 0;
13680
13681		if (arguments.length < 3) {
13682			accumulator = collection[0];
13683		}
13684
13685		for (; i < collection.length; i++) {
13686			accumulator = iteratee.call(thisArg, accumulator, collection[i], i);
13687		}
13688
13689		return accumulator;
13690	}
13691
13692	function findIndex(array, predicate, thisArg) {
13693		var i, l;
13694
13695		for (i = 0, l = array.length; i < l; i++) {
13696			if (predicate.call(thisArg, array[i], i, array)) {
13697				return i;
13698			}
13699		}
13700
13701		return -1;
13702	}
13703
13704	function find(array, predicate, thisArg) {
13705		var idx = findIndex(array, predicate, thisArg);
13706
13707		if (idx !== -1) {
13708			return array[idx];
13709		}
13710
13711		return undefined;
13712	}
13713
13714	function last(collection) {
13715		return collection[collection.length - 1];
13716	}
13717
13718	return {
13719		isArray: isArray,
13720		toArray: toArray,
13721		each: each,
13722		map: map,
13723		filter: filter,
13724		indexOf: indexOf,
13725		reduce: reduce,
13726		findIndex: findIndex,
13727		find: find,
13728		last: last
13729	};
13730});
13731
13732// Included from: js/tinymce/classes/util/Tools.js
13733
13734/**
13735 * Tools.js
13736 *
13737 * Released under LGPL License.
13738 * Copyright (c) 1999-2015 Ephox Corp. All rights reserved
13739 *
13740 * License: http://www.tinymce.com/license
13741 * Contributing: http://www.tinymce.com/contributing
13742 */
13743
13744/**
13745 * This class contains various utlity functions. These are also exposed
13746 * directly on the tinymce namespace.
13747 *
13748 * @class tinymce.util.Tools
13749 */
13750define("tinymce/util/Tools", [
13751	"tinymce/Env",
13752	"tinymce/util/Arr"
13753], function(Env, Arr) {
13754	/**
13755	 * Removes whitespace from the beginning and end of a string.
13756	 *
13757	 * @method trim
13758	 * @param {String} s String to remove whitespace from.
13759	 * @return {String} New string with removed whitespace.
13760	 */
13761	var whiteSpaceRegExp = /^\s*|\s*$/g;
13762
13763	function trim(str) {
13764		return (str === null || str === undefined) ? '' : ("" + str).replace(whiteSpaceRegExp, '');
13765	}
13766
13767	/**
13768	 * Checks if a object is of a specific type for example an array.
13769	 *
13770	 * @method is
13771	 * @param {Object} obj Object to check type of.
13772	 * @param {string} type Optional type to check for.
13773	 * @return {Boolean} true/false if the object is of the specified type.
13774	 */
13775	function is(obj, type) {
13776		if (!type) {
13777			return obj !== undefined;
13778		}
13779
13780		if (type == 'array' && Arr.isArray(obj)) {
13781			return true;
13782		}
13783
13784		return typeof obj == type;
13785	}
13786
13787	/**
13788	 * Makes a name/object map out of an array with names.
13789	 *
13790	 * @method makeMap
13791	 * @param {Array/String} items Items to make map out of.
13792	 * @param {String} delim Optional delimiter to split string by.
13793	 * @param {Object} map Optional map to add items to.
13794	 * @return {Object} Name/value map of items.
13795	 */
13796	function makeMap(items, delim, map) {
13797		var i;
13798
13799		items = items || [];
13800		delim = delim || ',';
13801
13802		if (typeof items == "string") {
13803			items = items.split(delim);
13804		}
13805
13806		map = map || {};
13807
13808		i = items.length;
13809		while (i--) {
13810			map[items[i]] = {};
13811		}
13812
13813		return map;
13814	}
13815
13816	/**
13817	 * Creates a class, subclass or static singleton.
13818	 * More details on this method can be found in the Wiki.
13819	 *
13820	 * @method create
13821	 * @param {String} s Class name, inheritance and prefix.
13822	 * @param {Object} p Collection of methods to add to the class.
13823	 * @param {Object} root Optional root object defaults to the global window object.
13824	 * @example
13825	 * // Creates a basic class
13826	 * tinymce.create('tinymce.somepackage.SomeClass', {
13827	 *     SomeClass: function() {
13828	 *         // Class constructor
13829	 *     },
13830	 *
13831	 *     method: function() {
13832	 *         // Some method
13833	 *     }
13834	 * });
13835	 *
13836	 * // Creates a basic subclass class
13837	 * tinymce.create('tinymce.somepackage.SomeSubClass:tinymce.somepackage.SomeClass', {
13838	 *     SomeSubClass: function() {
13839	 *         // Class constructor
13840	 *         this.parent(); // Call parent constructor
13841	 *     },
13842	 *
13843	 *     method: function() {
13844	 *         // Some method
13845	 *         this.parent(); // Call parent method
13846	 *     },
13847	 *
13848	 *     'static': {
13849	 *         staticMethod: function() {
13850	 *             // Static method
13851	 *         }
13852	 *     }
13853	 * });
13854	 *
13855	 * // Creates a singleton/static class
13856	 * tinymce.create('static tinymce.somepackage.SomeSingletonClass', {
13857	 *     method: function() {
13858	 *         // Some method
13859	 *     }
13860	 * });
13861	 */
13862	function create(s, p, root) {
13863		var self = this, sp, ns, cn, scn, c, de = 0;
13864
13865		// Parse : <prefix> <class>:<super class>
13866		s = /^((static) )?([\w.]+)(:([\w.]+))?/.exec(s);
13867		cn = s[3].match(/(^|\.)(\w+)$/i)[2]; // Class name
13868
13869		// Create namespace for new class
13870		ns = self.createNS(s[3].replace(/\.\w+$/, ''), root);
13871
13872		// Class already exists
13873		if (ns[cn]) {
13874			return;
13875		}
13876
13877		// Make pure static class
13878		if (s[2] == 'static') {
13879			ns[cn] = p;
13880
13881			if (this.onCreate) {
13882				this.onCreate(s[2], s[3], ns[cn]);
13883			}
13884
13885			return;
13886		}
13887
13888		// Create default constructor
13889		if (!p[cn]) {
13890			p[cn] = function() {};
13891			de = 1;
13892		}
13893
13894		// Add constructor and methods
13895		ns[cn] = p[cn];
13896		self.extend(ns[cn].prototype, p);
13897
13898		// Extend
13899		if (s[5]) {
13900			sp = self.resolve(s[5]).prototype;
13901			scn = s[5].match(/\.(\w+)$/i)[1]; // Class name
13902
13903			// Extend constructor
13904			c = ns[cn];
13905			if (de) {
13906				// Add passthrough constructor
13907				ns[cn] = function() {
13908					return sp[scn].apply(this, arguments);
13909				};
13910			} else {
13911				// Add inherit constructor
13912				ns[cn] = function() {
13913					this.parent = sp[scn];
13914					return c.apply(this, arguments);
13915				};
13916			}
13917			ns[cn].prototype[cn] = ns[cn];
13918
13919			// Add super methods
13920			self.each(sp, function(f, n) {
13921				ns[cn].prototype[n] = sp[n];
13922			});
13923
13924			// Add overridden methods
13925			self.each(p, function(f, n) {
13926				// Extend methods if needed
13927				if (sp[n]) {
13928					ns[cn].prototype[n] = function() {
13929						this.parent = sp[n];
13930						return f.apply(this, arguments);
13931					};
13932				} else {
13933					if (n != cn) {
13934						ns[cn].prototype[n] = f;
13935					}
13936				}
13937			});
13938		}
13939
13940		// Add static methods
13941		/*jshint sub:true*/
13942		/*eslint dot-notation:0*/
13943		self.each(p['static'], function(f, n) {
13944			ns[cn][n] = f;
13945		});
13946	}
13947
13948	function extend(obj, ext) {
13949		var i, l, name, args = arguments, value;
13950
13951		for (i = 1, l = args.length; i < l; i++) {
13952			ext = args[i];
13953			for (name in ext) {
13954				if (ext.hasOwnProperty(name)) {
13955					value = ext[name];
13956
13957					if (value !== undefined) {
13958						obj[name] = value;
13959					}
13960				}
13961			}
13962		}
13963
13964		return obj;
13965	}
13966
13967	/**
13968	 * Executed the specified function for each item in a object tree.
13969	 *
13970	 * @method walk
13971	 * @param {Object} o Object tree to walk though.
13972	 * @param {function} f Function to call for each item.
13973	 * @param {String} n Optional name of collection inside the objects to walk for example childNodes.
13974	 * @param {String} s Optional scope to execute the function in.
13975	 */
13976	function walk(o, f, n, s) {
13977		s = s || this;
13978
13979		if (o) {
13980			if (n) {
13981				o = o[n];
13982			}
13983
13984			Arr.each(o, function(o, i) {
13985				if (f.call(s, o, i, n) === false) {
13986					return false;
13987				}
13988
13989				walk(o, f, n, s);
13990			});
13991		}
13992	}
13993
13994	/**
13995	 * Creates a namespace on a specific object.
13996	 *
13997	 * @method createNS
13998	 * @param {String} n Namespace to create for example a.b.c.d.
13999	 * @param {Object} o Optional object to add namespace to, defaults to window.
14000	 * @return {Object} New namespace object the last item in path.
14001	 * @example
14002	 * // Create some namespace
14003	 * tinymce.createNS('tinymce.somepackage.subpackage');
14004	 *
14005	 * // Add a singleton
14006	 * var tinymce.somepackage.subpackage.SomeSingleton = {
14007	 *     method: function() {
14008	 *         // Some method
14009	 *     }
14010	 * };
14011	 */
14012	function createNS(n, o) {
14013		var i, v;
14014
14015		o = o || window;
14016
14017		n = n.split('.');
14018		for (i = 0; i < n.length; i++) {
14019			v = n[i];
14020
14021			if (!o[v]) {
14022				o[v] = {};
14023			}
14024
14025			o = o[v];
14026		}
14027
14028		return o;
14029	}
14030
14031	/**
14032	 * Resolves a string and returns the object from a specific structure.
14033	 *
14034	 * @method resolve
14035	 * @param {String} n Path to resolve for example a.b.c.d.
14036	 * @param {Object} o Optional object to search though, defaults to window.
14037	 * @return {Object} Last object in path or null if it couldn't be resolved.
14038	 * @example
14039	 * // Resolve a path into an object reference
14040	 * var obj = tinymce.resolve('a.b.c.d');
14041	 */
14042	function resolve(n, o) {
14043		var i, l;
14044
14045		o = o || window;
14046
14047		n = n.split('.');
14048		for (i = 0, l = n.length; i < l; i++) {
14049			o = o[n[i]];
14050
14051			if (!o) {
14052				break;
14053			}
14054		}
14055
14056		return o;
14057	}
14058
14059	/**
14060	 * Splits a string but removes the whitespace before and after each value.
14061	 *
14062	 * @method explode
14063	 * @param {string} s String to split.
14064	 * @param {string} d Delimiter to split by.
14065	 * @example
14066	 * // Split a string into an array with a,b,c
14067	 * var arr = tinymce.explode('a, b,   c');
14068	 */
14069	function explode(s, d) {
14070		if (!s || is(s, 'array')) {
14071			return s;
14072		}
14073
14074		return Arr.map(s.split(d || ','), trim);
14075	}
14076
14077	function _addCacheSuffix(url) {
14078		var cacheSuffix = Env.cacheSuffix;
14079
14080		if (cacheSuffix) {
14081			url += (url.indexOf('?') === -1 ? '?' : '&') + cacheSuffix;
14082		}
14083
14084		return url;
14085	}
14086
14087	return {
14088		trim: trim,
14089
14090		/**
14091		 * Returns true/false if the object is an array or not.
14092		 *
14093		 * @method isArray
14094		 * @param {Object} obj Object to check.
14095		 * @return {boolean} true/false state if the object is an array or not.
14096		 */
14097		isArray: Arr.isArray,
14098
14099		is: is,
14100
14101		/**
14102		 * Converts the specified object into a real JavaScript array.
14103		 *
14104		 * @method toArray
14105		 * @param {Object} obj Object to convert into array.
14106		 * @return {Array} Array object based in input.
14107		 */
14108		toArray: Arr.toArray,
14109		makeMap: makeMap,
14110
14111		/**
14112		 * Performs an iteration of all items in a collection such as an object or array. This method will execure the
14113		 * callback function for each item in the collection, if the callback returns false the iteration will terminate.
14114		 * The callback has the following format: cb(value, key_or_index).
14115		 *
14116		 * @method each
14117		 * @param {Object} o Collection to iterate.
14118		 * @param {function} cb Callback function to execute for each item.
14119		 * @param {Object} s Optional scope to execute the callback in.
14120		 * @example
14121		 * // Iterate an array
14122		 * tinymce.each([1,2,3], function(v, i) {
14123		 *     console.debug("Value: " + v + ", Index: " + i);
14124		 * });
14125		 *
14126		 * // Iterate an object
14127		 * tinymce.each({a: 1, b: 2, c: 3], function(v, k) {
14128		 *     console.debug("Value: " + v + ", Key: " + k);
14129		 * });
14130		 */
14131		each: Arr.each,
14132
14133		/**
14134		 * Creates a new array by the return value of each iteration function call. This enables you to convert
14135		 * one array list into another.
14136		 *
14137		 * @method map
14138		 * @param {Array} array Array of items to iterate.
14139		 * @param {function} callback Function to call for each item. It's return value will be the new value.
14140		 * @return {Array} Array with new values based on function return values.
14141		 */
14142		map: Arr.map,
14143
14144		/**
14145		 * Filters out items from the input array by calling the specified function for each item.
14146		 * If the function returns false the item will be excluded if it returns true it will be included.
14147		 *
14148		 * @method grep
14149		 * @param {Array} a Array of items to loop though.
14150		 * @param {function} f Function to call for each item. Include/exclude depends on it's return value.
14151		 * @return {Array} New array with values imported and filtered based in input.
14152		 * @example
14153		 * // Filter out some items, this will return an array with 4 and 5
14154		 * var items = tinymce.grep([1,2,3,4,5], function(v) {return v > 3;});
14155		 */
14156		grep: Arr.filter,
14157
14158		/**
14159		 * Returns true/false if the object is an array or not.
14160		 *
14161		 * @method isArray
14162		 * @param {Object} obj Object to check.
14163		 * @return {boolean} true/false state if the object is an array or not.
14164		 */
14165		inArray: Arr.indexOf,
14166
14167		extend: extend,
14168		create: create,
14169		walk: walk,
14170		createNS: createNS,
14171		resolve: resolve,
14172		explode: explode,
14173		_addCacheSuffix: _addCacheSuffix
14174	};
14175});
14176
14177// Included from: js/tinymce/classes/dom/DomQuery.js
14178
14179/**
14180 * DomQuery.js
14181 *
14182 * Released under LGPL License.
14183 * Copyright (c) 1999-2015 Ephox Corp. All rights reserved
14184 *
14185 * License: http://www.tinymce.com/license
14186 * Contributing: http://www.tinymce.com/contributing
14187 */
14188
14189/**
14190 * This class mimics most of the jQuery API:
14191 *
14192 * This is whats currently implemented:
14193 * - Utility functions
14194 * - DOM traversial
14195 * - DOM manipulation
14196 * - Event binding
14197 *
14198 * This is not currently implemented:
14199 * - Dimension
14200 * - Ajax
14201 * - Animation
14202 * - Advanced chaining
14203 *
14204 * @example
14205 * var $ = tinymce.dom.DomQuery;
14206 * $('p').attr('attr', 'value').addClass('class');
14207 *
14208 * @class tinymce.dom.DomQuery
14209 */
14210define("tinymce/dom/DomQuery", [
14211	"tinymce/dom/EventUtils",
14212	"tinymce/dom/Sizzle",
14213	"tinymce/util/Tools",
14214	"tinymce/Env"
14215], function(EventUtils, Sizzle, Tools, Env) {
14216	var doc = document, push = Array.prototype.push, slice = Array.prototype.slice;
14217	var rquickExpr = /^(?:[^#<]*(<[\w\W]+>)[^>]*$|#([\w\-]*)$)/;
14218	var Event = EventUtils.Event, undef;
14219	var skipUniques = Tools.makeMap('children,contents,next,prev');
14220
14221	function isDefined(obj) {
14222		return typeof obj !== 'undefined';
14223	}
14224
14225	function isString(obj) {
14226		return typeof obj === 'string';
14227	}
14228
14229	function isWindow(obj) {
14230		return obj && obj == obj.window;
14231	}
14232
14233	function createFragment(html, fragDoc) {
14234		var frag, node, container;
14235
14236		fragDoc = fragDoc || doc;
14237		container = fragDoc.createElement('div');
14238		frag = fragDoc.createDocumentFragment();
14239		container.innerHTML = html;
14240
14241		while ((node = container.firstChild)) {
14242			frag.appendChild(node);
14243		}
14244
14245		return frag;
14246	}
14247
14248	function domManipulate(targetNodes, sourceItem, callback, reverse) {
14249		var i;
14250
14251		if (isString(sourceItem)) {
14252			sourceItem = createFragment(sourceItem, getElementDocument(targetNodes[0]));
14253		} else if (sourceItem.length && !sourceItem.nodeType) {
14254			sourceItem = DomQuery.makeArray(sourceItem);
14255
14256			if (reverse) {
14257				for (i = sourceItem.length - 1; i >= 0; i--) {
14258					domManipulate(targetNodes, sourceItem[i], callback, reverse);
14259				}
14260			} else {
14261				for (i = 0; i < sourceItem.length; i++) {
14262					domManipulate(targetNodes, sourceItem[i], callback, reverse);
14263				}
14264			}
14265
14266			return targetNodes;
14267		}
14268
14269		if (sourceItem.nodeType) {
14270			i = targetNodes.length;
14271			while (i--) {
14272				callback.call(targetNodes[i], sourceItem);
14273			}
14274		}
14275
14276		return targetNodes;
14277	}
14278
14279	function hasClass(node, className) {
14280		return node && className && (' ' + node.className + ' ').indexOf(' ' + className + ' ') !== -1;
14281	}
14282
14283	function wrap(elements, wrapper, all) {
14284		var lastParent, newWrapper;
14285
14286		wrapper = DomQuery(wrapper)[0];
14287
14288		elements.each(function() {
14289			var self = this;
14290
14291			if (!all || lastParent != self.parentNode) {
14292				lastParent = self.parentNode;
14293				newWrapper = wrapper.cloneNode(false);
14294				self.parentNode.insertBefore(newWrapper, self);
14295				newWrapper.appendChild(self);
14296			} else {
14297				newWrapper.appendChild(self);
14298			}
14299		});
14300
14301		return elements;
14302	}
14303
14304	var numericCssMap = Tools.makeMap('fillOpacity fontWeight lineHeight opacity orphans widows zIndex zoom', ' ');
14305	var booleanMap = Tools.makeMap('checked compact declare defer disabled ismap multiple nohref nosh
14305ade nowrap readonly selected', ' ');
14306	var propFix = {
14307		'for': 'htmlFor',
14308		'class': 'className',
14309		'readonly': 'readOnly'
14310	};
14311	var cssFix = {
14312		'float': 'cssFloat'
14313	};
14314
14315	var attrHooks = {}, cssHooks = {};
14316
14317	function DomQuery(selector, context) {
14318		/*eslint new-cap:0 */
14319		return new DomQuery.fn.init(selector, context);
14320	}
14321
14322	function inArray(item, array) {
14323		var i;
14324
14325		if (array.indexOf) {
14326			return array.indexOf(item);
14327		}
14328
14329		i = array.length;
14330		while (i--) {
14331			if (array[i] === item) {
14332				return i;
14333			}
14334		}
14335
14336		return -1;
14337	}
14338
14339	var whiteSpaceRegExp = /^\s*|\s*$/g;
14340
14341	function trim(str) {
14342		return (str === null || str === undef) ? '' : ("" + str).replace(whiteSpaceRegExp, '');
14343	}
14344
14345	function each(obj, callback) {
14346		var length, key, i, undef, value;
14347
14348		if (obj) {
14349			length = obj.length;
14350
14351			if (length === undef) {
14352				// Loop object items
14353				for (key in obj) {
14354					if (obj.hasOwnProperty(key)) {
14355						value = obj[key];
14356						if (callback.call(value, key, value) === false) {
14357							break;
14358						}
14359					}
14360				}
14361			} else {
14362				// Loop array items
14363				for (i = 0; i < length; i++) {
14364					value = obj[i];
14365					if (callback.call(value, i, value) === false) {
14366						break;
14367					}
14368				}
14369			}
14370		}
14371
14372		return obj;
14373	}
14374
14375	function grep(array, callback) {
14376		var out = [];
14377
14378		each(array, function(i, item) {
14379			if (callback(item, i)) {
14380				out.push(item);
14381			}
14382		});
14383
14384		return out;
14385	}
14386
14387	function getElementDocument(element) {
14388		if (!element) {
14389			return doc;
14390		}
14391
14392		if (element.nodeType == 9) {
14393			return element;
14394		}
14395
14396		return element.ownerDocument;
14397	}
14398
14399	DomQuery.fn = DomQuery.prototype = {
14400		constructor: DomQuery,
14401
14402		/**
14403		 * Selector for the current set.
14404		 *
14405		 * @property selector
14406		 * @type String
14407		 */
14408		selector: "",
14409
14410		/**
14411		 * Context used to create the set.
14412		 *
14413		 * @property context
14414		 * @type Element
14415		 */
14416		context: null,
14417
14418		/**
14419		 * Number of items in the current set.
14420		 *
14421		 * @property length
14422		 * @type Number
14423		 */
14424		length: 0,
14425
14426		/**
14427		 * Constructs a new DomQuery instance with the specified selector or context.
14428		 *
14429		 * @constructor
14430		 * @method init
14431		 * @param {String/Array/DomQuery} selector Optional CSS selector/Array or array like object or HTML string.
14432		 * @param {Document/Element} context Optional context to search in.
14433		 */
14434		init: function(selector, context) {
14435			var self = this, match, node;
14436
14437			if (!selector) {
14438				return self;
14439			}
14440
14441			if (selector.nodeType) {
14442				self.context = self[0] = selector;
14443				self.length = 1;
14444
14445				return self;
14446			}
14447
14448			if (context && context.nodeType) {
14449				self.context = context;
14450			} else {
14451				if (context) {
14452					return DomQuery(selector).attr(context);
14453				}
14454
14455				self.context = context = document;
14456			}
14457
14458			if (isString(selector)) {
14459				self.selector = selector;
14460
14461				if (selector.charAt(0) === "<" && selector.charAt(selector.length - 1) === ">" && selector.length >= 3) {
14462					match = [null, selector, null];
14463				} else {
14464					match = rquickExpr.exec(selector);
14465				}
14466
14467				if (match) {
14468					if (match[1]) {
14469						node = createFragment(selector, getElementDocument(context)).firstChild;
14470
14471						while (node) {
14472							push.call(self, node);
14473							node = node.nextSibling;
14474						}
14475					} else {
14476						node = getElementDocument(context).getElementById(match[2]);
14477
14478						if (!node) {
14479							return self;
14480						}
14481
14482						if (node.id !== match[2]) {
14483							return self.find(selector);
14484						}
14485
14486						self.length = 1;
14487						self[0] = node;
14488					}
14489				} else {
14490					return DomQuery(context).find(selector);
14491				}
14492			} else {
14493				this.add(selector, false);
14494			}
14495
14496			return self;
14497		},
14498
14499		/**
14500		 * Converts the current set to an array.
14501		 *
14502		 * @method toArray
14503		 * @return {Array} Array of all nodes in set.
14504		 */
14505		toArray: function() {
14506			return Tools.toArray(this);
14507		},
14508
14509		/**
14510		 * Adds new nodes to the set.
14511		 *
14512		 * @method add
14513		 * @param {Array/tinymce.dom.DomQuery} items Array of all nodes to add to set.
14514		 * @param {Boolean} sort Optional sort flag that enables sorting of elements.
14515		 * @return {tinymce.dom.DomQuery} New instance with nodes added.
14516		 */
14517		add: function(items, sort) {
14518			var self = this, nodes, i;
14519
14520			if (isString(items)) {
14521				return self.add(DomQuery(items));
14522			}
14523
14524			if (sort !== false) {
14525				nodes = DomQuery.unique(self.toArray().concat(DomQuery.makeArray(items)));
14526				self.length = nodes.length;
14527				for (i = 0; i < nodes.length; i++) {
14528					self[i] = nodes[i];
14529				}
14530			} else {
14531				push.apply(self, DomQuery.makeArray(items));
14532			}
14533
14534			return self;
14535		},
14536
14537		/**
14538		 * Sets/gets attributes on the elements in the current set.
14539		 *
14540		 * @method attr
14541		 * @param {String/Object} name Name of attribute to get or an object with attributes to set.
14542		 * @param {String} value Optional value to set.
14543		 * @return {tinymce.dom.DomQuery/String} Current set or the specified attribute when only the name is specified.
14544		 */
14545		attr: function(name, value) {
14546			var self = this, hook;
14547
14548			if (typeof name === "object") {
14549				each(name, function(name, value) {
14550					self.attr(name, value);
14551				});
14552			} else if (isDefined(value)) {
14553				this.each(function() {
14554					var hook;
14555
14556					if (this.nodeType === 1) {
14557						hook = attrHooks[name];
14558						if (hook && hook.set) {
14559							hook.set(this, value);
14560							return;
14561						}
14562
14563						if (value === null) {
14564							this.removeAttribute(name, 2);
14565						} else {
14566							this.setAttribute(name, value, 2);
14567						}
14568					}
14569				});
14570			} else {
14571				if (self[0] && self[0].nodeType === 1) {
14572					hook = attrHooks[name];
14573					if (hook && hook.get) {
14574						return hook.get(self[0], name);
14575					}
14576
14577					if (booleanMap[name]) {
14578						return self.prop(name) ? name : undef;
14579					}
14580
14581					value = self[0].getAttribute(name, 2);
14582
14583					if (value === null) {
14584						value = undef;
14585					}
14586				}
14587
14588				return value;
14589			}
14590
14591			return self;
14592		},
14593
14594		/**
14595		 * Removes attributse on the elements in the current set.
14596		 *
14597		 * @method removeAttr
14598		 * @param {String/Object}
14598 name Name of attribute to remove.
14599		 * @return {tinymce.dom.DomQuery/String} Current set.
14600		 */
14601		removeAttr: function(name) {
14602			return this.attr(name, null);
14603		},
14604
14605		/**
14606		 * Sets/gets properties on the elements in the current set.
14607		 *
14608		 * @method attr
14609		 * @param {String/Object} name Name of property to get or an object with properties to set.
14610		 * @param {String} value Optional value to set.
14611		 * @return {tinymce.dom.DomQuery/String} Current set or the specified property when only the name is specified.
14612		 */
14613		prop: function(name, value) {
14614			var self = this;
14615
14616			name = propFix[name] || name;
14617
14618			if (typeof name === "object") {
14619				each(name, function(name, value) {
14620					self.prop(name, value);
14621				});
14622			} else if (isDefined(value)) {
14623				this.each(function() {
14624					if (this.nodeType == 1) {
14625						this[name] = value;
14626					}
14627				});
14628			} else {
14629				if (self[0] && self[0].nodeType && name in self[0]) {
14630					return self[0][name];
14631				}
14632
14633				return value;
14634			}
14635
14636			return self;
14637		},
14638
14639		/**
14640		 * Sets/gets styles on the elements in the current set.
14641		 *
14642		 * @method css
14643		 * @param {String/Object} name Name of style to get or an object with styles to set.
14644		 * @param {String} value Optional value to set.
14645		 * @return {tinymce.dom.DomQuery/String} Current set or the specified style when only the name is specified.
14646		 */
14647		css: function(name, value) {
14648			var self = this, elm, hook;
14649
14650			function camel(name) {
14651				return name.replace(/-(\D)/g, function(a, b) {
14652					return b.toUpperCase();
14653				});
14654			}
14655
14656			function dashed(name) {
14657				return name.replace(/[A-Z]/g, function(a) {
14658					return '-' + a;
14659				});
14660			}
14661
14662			if (typeof name === "object") {
14663				each(name, function(name, value) {
14664					self.css(name, value);
14665				});
14666			} else {
14667				if (isDefined(value)) {
14668					name = camel(name);
14669
14670					// Default px suffix on these
14671					if (typeof value === 'number' && !numericCssMap[name]) {
14672						value += 'px';
14673					}
14674
14675					self.each(function() {
14676						var style = this.style;
14677
14678						hook = cssHooks[name];
14679						if (hook && hook.set) {
14680							hook.set(this, value);
14681							return;
14682						}
14683
14684						try {
14685							this.style[cssFix[name] || name] = value;
14686						} catch (ex) {
14687							// Ignore
14688						}
14689
14690						if (value === null || value === '') {
14691							if (style.removeProperty) {
14692								style.removeProperty(dashed(name));
14693							} else {
14694								style.removeAttribute(name);
14695							}
14696						}
14697					});
14698				} else {
14699					elm = self[0];
14700
14701					hook = cssHooks[name];
14702					if (hook && hook.get) {
14703						return hook.get(elm);
14704					}
14705
14706					if (elm.ownerDocument.defaultView) {
14707						try {
14708							return elm.ownerDocument.defaultView.getComputedStyle(elm, null).getPropertyValue(dashed(name));
14709						} catch (ex) {
14710							return undef;
14711						}
14712					} else if (elm.currentStyle) {
14713						return elm.currentStyle[camel(name)];
14714					}
14715				}
14716			}
14717
14718			return self;
14719		},
14720
14721		/**
14722		 * Removes all nodes in set from the document.
14723		 *
14724		 * @method remove
14725		 * @return {tinymce.dom.DomQuery} Current set with the removed nodes.
14726		 */
14727		remove: function() {
14728			var self = this, node, i = this.length;
14729
14730			while (i--) {
14731				node = self[i];
14732				Event.clean(node);
14733
14734				if (node.parentNode) {
14735					node.parentNode.removeChild(node);
14736				}
14737			}
14738
14739			return this;
14740		},
14741
14742		/**
14743		 * Empties all elements in set.
14744		 *
14745		 * @method empty
14746		 * @return {tinymce.dom.DomQuery} Current set with the empty nodes.
14747		 */
14748		empty: function() {
14749			var self = this, node, i = this.length;
14750
14751			while (i--) {
14752				node = self[i];
14753				while (node.firstChild) {
14754					node.removeChild(node.firstChild);
14755				}
14756			}
14757
14758			return this;
14759		},
14760
14761		/**
14762		 * Sets or gets the HTML of the current set or first set node.
14763		 *
14764		 * @method html
14765		 * @param {String} value Optional innerHTML value to set on each element.
14766		 * @return {tinymce.dom.DomQuery/String} Current set or the innerHTML of the first element.
14767		 */
14768		html: function(value) {
14769			var self = this, i;
14770
14771			if (isDefined(value)) {
14772				i = self.length;
14773
14774				try {
14775					while (i--) {
14776						self[i].innerHTML = value;
14777					}
14778				} catch (ex) {
14779					// Workaround for "Unknown runtime error" when DIV is added to P on IE
14780					DomQuery(self[i]).empty().append(value);
14781				}
14782
14783				return self;
14784			}
14785
14786			return self[0] ? self[0].innerHTML : '';
14787		},
14788
14789		/**
14790		 * Sets or gets the text of the current set or first set node.
14791		 *
14792		 * @method text
14793		 * @param {String} value Optional innerText value to set on each element.
14794		 * @return {tinymce.dom.DomQuery/String} Current set or the innerText of the first element.
14795		 */
14796		text: function(value) {
14797			var self = this, i;
14798
14799			if (isDefined(value)) {
14800				i = self.length;
14801				while (i--) {
14802					if ("innerText" in self[i]) {
14803						self[i].innerText = value;
14804					} else {
14805						self[0].textContent = value;
14806					}
14807				}
14808
14809				return self;
14810			}
14811
14812			return self[0] ? (self[0].innerText || self[0].textContent) : '';
14813		},
14814
14815		/**
14816		 * Appends the specified node/html or node set to the current set nodes.
14817		 *
14818		 * @method append
14819		 * @param {String/Element/Array/tinymce.dom.DomQuery} content Content to append to each element in set.
14820		 * @return {tinymce.dom.DomQuery} Current set.
14821		 */
14822		append: function() {
14823			return domManipulate(this, arguments, function(node) {
14824				if (this.nodeType === 1) {
14825					this.appendChild(node);
14826				}
14827			});
14828		},
14829
14830		/**
14831		 * Prepends the specified node/html or node set to the current set nodes.
14832		 *
14833		 * @method prepend
14834		 * @param {String/Element/Array/tinymce.dom.DomQuery}
14834 content Content to prepend to each element in set.
14835		 * @return {tinymce.dom.DomQuery} Current set.
14836		 */
14837		prepend: function() {
14838			return domManipulate(this, arguments, function(node) {
14839				if (this.nodeType === 1) {
14840					this.insertBefore(node, this.firstChild);
14841				}
14842			}, true);
14843		},
14844
14845		/**
14846		 * Adds the specified elements before current set nodes.
14847		 *
14848		 * @method before
14849		 * @param {String/Element/Array/tinymce.dom.DomQuery} content Content to add before to each element in set.
14850		 * @return {tinymce.dom.DomQuery} Current set.
14851		 */
14852		before: function() {
14853			var self = this;
14854
14855			if (self[0] && self[0].parentNode) {
14856				return domManipulate(self, arguments, function(node) {
14857					this.parentNode.insertBefore(node, this);
14858				});
14859			}
14860
14861			return self;
14862		},
14863
14864		/**
14865		 * Adds the specified elements after current set nodes.
14866		 *
14867		 * @method after
14868		 * @param {String/Element/Array/tinymce.dom.DomQuery} content Content to add after to each element in set.
14869		 * @return {tinymce.dom.DomQuery} Current set.
14870		 */
14871		after: function() {
14872			var self = this;
14873
14874			if (self[0] && self[0].parentNode) {
14875				return domManipulate(self, arguments, function(node) {
14876					this.parentNode.insertBefore(node, this.nextSibling);
14877				}, true);
14878			}
14879
14880			return self;
14881		},
14882
14883		/**
14884		 * Appends the specified set nodes to the specified selector/instance.
14885		 *
14886		 * @method appendTo
14887		 * @param {String/Element/Array/tinymce.dom.DomQuery} val Item to append the current set to.
14888		 * @return {tinymce.dom.DomQuery} Current set with the appended nodes.
14889		 */
14890		appendTo: function(val) {
14891			DomQuery(val).append(this);
14892
14893			return this;
14894		},
14895
14896		/**
14897		 * Prepends the specified set nodes to the specified selector/instance.
14898		 *
14899		 * @method prependTo
14900		 * @param {String/Element/Array/tinymce.dom.DomQuery} val Item to prepend the current set to.
14901		 * @return {tinymce.dom.DomQuery} Current set with the prepended nodes.
14902		 */
14903		prependTo: function(val) {
14904			DomQuery(val).prepend(this);
14905
14906			return this;
14907		},
14908
14909		/**
14910		 * Replaces the nodes in set with the specified content.
14911		 *
14912		 * @method replaceWith
14913		 * @param {String/Element/Array/tinymce.dom.DomQuery} content Content to replace nodes with.
14914		 * @return {tinymce.dom.DomQuery} Set with replaced nodes.
14915		 */
14916		replaceWith: function(content) {
14917			return this.before(content).remove();
14918		},
14919
14920		/**
14921		 * Wraps all elements in set with the specified wrapper.
14922		 *
14923		 * @method wrap
14924		 * @param {String/Element/Array/tinymce.dom.DomQuery} content Content to wrap nodes with.
14925		 * @return {tinymce.dom.DomQuery} Set with wrapped nodes.
14926		 */
14927		wrap: function(content) {
14928			return wrap(this, content);
14929		},
14930
14931		/**
14932		 * Wraps all nodes in set with the specified wrapper. If the nodes are siblings all of them
14933		 * will be wrapped in the same wrapper.
14934		 *
14935		 * @method wrapAll
14936		 * @param {String/Element/Array/tinymce.dom.DomQuery} content Content to wrap nodes with.
14937		 * @return {tinymce.dom.DomQuery} Set with wrapped nodes.
14938		 */
14939		wrapAll: function(content) {
14940			return wrap(this, content, true);
14941		},
14942
14943		/**
14944		 * Wraps all elements inner contents in set with the specified wrapper.
14945		 *
14946		 * @method wrapInner
14947		 * @param {String/Element/Array/tinymce.dom.DomQuery} content Content to wrap nodes with.
14948		 * @return {tinymce.dom.DomQuery} Set with wrapped nodes.
14949		 */
14950		wrapInner: function(content) {
14951			this.each(function() {
14952				DomQuery(this).contents().wrapAll(content);
14953			});
14954
14955			return this;
14956		},
14957
14958		/**
14959		 * Unwraps all elements by removing the parent element of each item in set.
14960		 *
14961		 * @method unwrap
14962		 * @return {tinymce.dom.DomQuery} Set with unwrapped nodes.
14963		 */
14964		unwrap: function() {
14965			return this.parent().each(function() {
14966				DomQuery(this).replaceWith(this.childNodes);
14967			});
14968		},
14969
14970		/**
14971		 * Clones all nodes in set.
14972		 *
14973		 * @method clone
14974		 * @return {tinymce.dom.DomQuery} Set with cloned nodes.
14975		 */
14976		clone: function() {
14977			var result = [];
14978
14979			this.each(function() {
14980				result.push(this.cloneNode(true));
14981			});
14982
14983			return DomQuery(result);
14984		},
14985
14986		/**
14987		 * Adds the specified class name to the current set elements.
14988		 *
14989		 * @method addClass
14990		 * @param {String} className Class name to add.
14991		 * @return {tinymce.dom.DomQuery} Current set.
14992		 */
14993		addClass: function(className) {
14994			return this.toggleClass(className, true);
14995		},
14996
14997		/**
14998		 * Removes the specified class name to the current set elements.
14999		 *
15000		 * @method removeClass
15001		 * @param {String} className Class name to remove.
15002		 * @return {tinymce.dom.DomQuery} Current set.
15003		 */
15004		removeClass: function(className) {
15005			return this.toggleClass(className, false);
15006		},
15007
15008		/**
15009		 * Toggles the specified class name on the current set elements.
15010		 *
15011		 * @method toggleClass
15012		 * @param {String} className Class name to add/remove.
15013		 * @param {Boolean} state Optional state to toggle on/off.
15014		 * @return {tinymce.dom.DomQuery} Current set.
15015		 */
15016		toggleClass: function(className, state) {
15017			var self = this;
15018
15019			// Functions are not supported
15020			if (typeof className != 'string') {
15021				return self;
15022			}
15023
15024			if (className.indexOf(' ') !== -1) {
15025				each(className.split(' '), function() {
15026					self.toggleClass(this, state);
15027				});
15028			} else {
15029				self.each(function(index, node) {
15030					var existingClassName, classState;
15031
15032					classState = hasClass(node, className);
15033					if (classState !== state) {
15034						existingClassName = node.className;
15035
15036						if (classState) {
15037							node.className = trim((" " + existingClassName + " ").replace(' ' + className + ' ', ' '));
15038						} else {
15039							node.className += existingClassName ? ' ' + className : className;
15040						}
15041					}
15042				});
15043			}
15044
15045			return self;
15046		},
15047
15048		/**
15049		 * Returns true/false if the first item in set has the specified class.
15050		 *
15051		 * @method hasClass
15052		 * @param {String} className Class name to check for.
15053		 * @return {Boolean} True/false if the set has the specified class.
15054		 */
15055		hasClass: function(className) {
15056			return hasClass(this[0], className);
15057		},
15058
15059		/**
15060		 * Executes the callback function for each item DomQuery collection. If you return false in the
15061		 * callback it will break the loop.
15062		 *
15063		 * @method each
15064		 * @param {function} callback Callback function to execute for each item.
15065		 * @return {tinymce.dom.DomQuery} Current set.
15066		 */
15067		each: function(callback) {
15068			return each(this, callback);
15069		},
15070
15071		/**
15072		 * Binds an event with callback function to the elements in set.
15073		 *
15074		 * @method on
15075		 * @param {String} name Name of the event to bind.
15076		 * @param {function} callback Callback function to execute when the event occurs.
15077		 * @return {tinymce.dom.DomQuery} Current set.
15078		 */
15079		on: function(name, callback) {
15080			return this.each(function() {
15081				Event.bind(this, name, callback);
15082			});
15083		},
15084
15085		/**
15086		 * Unbinds an event with callback function to the elements in set.
15087		 *
15088		 * @method off
15089		 * @param {String} name Optional name of the event to bind.
15090		 * @param {function} callback Optional callback function to execute when the event occurs.
15091		 * @return {tinymce.dom.DomQuery} Current set.
15092		 */
15093		off: function(name, callback) {
15094			return this.each(function() {
15095				Event.unbind(this, name, callback);
15096			});
15097		},
15098
15099		/**
15100		 * Triggers the specified event by name or event object.
15101		 *
15102		 * @method trigger
15103		 * @param {String/Object} name Name of the event to trigger or event object.
15104		 * @return {tinymce.dom.DomQuery} Current set.
15105		 */
15106		trigger: function(name) {
15107			return this.each(function() {
15108				if (typeof name == 'object') {
15109					Event.fire(this, name.type, name);
15110				} else {
15111					Event.fire(this, name);
15112				}
15113			});
15114		},
15115
15116		/**
15117		 * Shows all elements in set.
15118		 *
15119		 * @method show
15120		 * @return {tinymce.dom.DomQuery} Current set.
15121		 */
15122		show: function() {
15123			return this.css('display', '');
15124		},
15125
15126		/**
15127		 * Hides all elements in set.
15128		 *
15129		 * @method hide
15130		 * @return {tinymce.dom.DomQuery} Current set.
15131		 */
15132		hide: function() {
15133			return this.css('display', 'none');
15134		},
15135
15136		/**
15137		 * Slices the current set.
15138		 *
15139		 * @method slice
15140		 * @param {Number} start Start index to slice at.
15141		 * @param {Number} end Optional end index to end slice at.
15142		 * @return {tinymce.dom.DomQuery} Sliced set.
15143		 */
15144		slice: function() {
15145			return new DomQuery(slice.apply(this, arguments));
15146		},
15147
15148		/**
15149		 * Makes the set equal to the specified index.
15150		 *
15151		 * @method eq
15152		 * @param {Number} index Index to set it equal to.
15153		 * @return {tinymce.dom.DomQuery} Single item set.
15154		 */
15155		eq: function(index) {
15156			return index === -1 ? this.slice(index) : this.slice(index, +index + 1);
15157		},
15158
15159		/**
15160		 * Makes the set equal to first element in set.
15161		 *
15162		 * @method first
15163		 * @return {tinymce.dom.DomQuery} Single item set.
15164		 */
15165		first: function() {
15166			return this.eq(0);
15167		},
15168
15169		/**
15170		 * Makes the set equal to last element in set.
15171		 *
15172		 * @method last
15173		 * @return {tinymce.dom.DomQuery} Single item set.
15174		 */
15175		last: function() {
15176			return this.eq(-1);
15177		},
15178
15179		/**
15180		 * Finds elements by the specified selector for each element in set.
15181		 *
15182		 * @method find
15183		 * @param {String} selector Selector to find elements by.
15184		 * @return {tinymce.dom.DomQuery} Set with matches elements.
15185		 */
15186		find: function(selector) {
15187			var i, l, ret = [];
15188
15189			for (i = 0, l = this.length; i < l; i++) {
15190				DomQuery.find(selector, this[i], ret);
15191			}
15192
15193			return DomQuery(ret);
15194		},
15195
15196		/**
15197		 * Filters the current set with the specified selector.
15198		 *
15199		 * @method filter
15200		 * @param {String/function} selector Selector to filter elements by.
15201		 * @return {tinymce.dom.DomQuery} Set with filtered elements.
15202		 */
15203		filter: function(selector) {
15204			if (typeof selector == 'function') {
15205				return DomQuery(grep(this.toArray(), function(item, i) {
15206					return selector(i, item);
15207				}));
15208			}
15209
15210			return DomQuery(DomQuery.filter(selector, this.toArray()));
15211		},
15212
15213		/**
15214		 * Gets the current node or any parent matching the specified selector.
15215		 *
15216		 * @method closest
15217		 * @param {String/Element/tinymce.dom.DomQuery} selector Selector or element to find.
15218		 * @return {tinymce.dom.DomQuery} Set with closest elements.
15219		 */
15220		closest: function(selector) {
15221			var result = [];
15222
15223			if (selector instanceof DomQuery) {
15224				selector = selector[0];
15225			}
15226
15227			this.each(function(i, node) {
15228				while (node) {
15229					if (typeof selector == 'string' && DomQuery(node).is(selector)) {
15230						result.push(node);
15231						break;
15232					} else if (node == selector) {
15233						result.push(node);
15234						break;
15235					}
15236
15237					node = node.parentNode;
15238				}
15239			});
15240
15241			return DomQuery(result);
15242		},
15243
15244		/**
15245		 * Returns the offset of the first element in set or sets the top/left css properties of all elements in set.
15246		 *
15247		 * @method offset
15248		 * @param {Object} offset Optional offset object to set on each item.
15249		 * @return {Object/tinymce.dom.DomQuery} Returns the first element offset or the current set if you specified an offset.
15250		 */
15251		offset: function(offset) {
15252			var elm, doc, docElm;
15253			var x = 0, y = 0, pos;
15254
15255			if (!offset) {
15256				elm = this[0];
15257
15258				if (elm) {
15259					doc = elm.ownerDocument;
15260					docElm = doc.documentElement;
15261
15262					if (elm.getBoundingClientRect) {
15263						pos = elm.getBoundingClientRect();
15264						x = pos.left + (docElm.scrollLeft || doc.body.scrollLeft) - docElm.clientLeft;
15265						y = pos.top + (docElm.scrollTop || doc.body.scrollTop) - docElm.clientTop;
15266					}
15267				}
15268
15269				return {
15270					left: x,
15271					top: y
15272				};
15273			}
15274
15275			return this.css(offset);
15276		},
15277
15278		push: push,
15279		sort: [].sort,
15280		splice: [].splice
15281	};
15282
15283	// Static members
15284	Tools.extend(DomQuery, {
15285		/**
15286		 * Extends the specified object with one or more objects.
15287		 *
15288		 * @static
15289		 * @method extend
15290		 * @param {Object} target Target object to extend with new items.
15291		 * @param {Object..} object Object to extend the target with.
15292		 * @return {Object} Extended input object.
15293		 */
15294		extend: Tools.extend,
15295
15296		/**
15297		 * Creates an array out of an array like object.
15298		 *
15299		 * @static
15300		 * @method makeArray
15301		 * @param {Object} object Object to convert to array.
15302		 * @return {Array} Array produced from object.
15303		 */
15304		makeArray: function(object) {
15305			if (isWindow(object) || object.nodeType) {
15306				return [object];
15307			}
15308
15309			return Tools.toArray(object);
15310		},
15311
15312		/**
15313		 * Returns the index of the specified item inside the array.
15314		 *
15315		 * @static
15316		 * @method inArray
15317		 * @param {Object} item Item to look for.
15318		 * @param {Array} array Array to look for item in.
15319		 * @return {Number} Index of the item or -1.
15320		 */
15321		inArray: inArray,
15322
15323		/**
15324		 * Returns true/false if the specified object is an array or not.
15325		 *
15326		 * @static
15327		 * @method isArray
15328		 * @param {Object} array Object to check if it's an array or not.
15329		 * @return {Boolean} True/false if the object is an array.
15330		 */
15331		isArray: Tools.isArray,
15332
15333		/**
15334		 * Executes the callback function for each item in array/object. If you return false in the
15335		 * callback it will break the loop.
15336		 *
15337		 * @static
15338		 * @method each
15339		 * @param {Object} obj Object to iterate.
15340		 * @param {function} callback Callback function to execute for each item.
15341		 */
15342		each: each,
15343
15344		/**
15345		 * Removes whitespace from the beginning and end of a string.
15346		 *
15347		 * @static
15348		 * @method trim
15349		 * @param {String} str String to remove whitespace from.
15350		 * @return {String} New string with removed whitespace.
15351		 */
15352		trim: trim,
15353
15354		/**
15355		 * Filters out items from the input array by calling the specified function for each item.
15356		 * If the function returns false the item will be excluded if it returns true it will be included.
15357		 *
15358		 * @static
15359		 * @method grep
15360		 * @param {Array} array Array of items to loop though.
15361		 * @param {function} callback Function to call for each item. Include/exclude depends on it's return value.
15362		 * @return {Array} New array with values imported and filtered based in input.
15363		 * @example
15364		 * // Filter out some items, this will return an array with 4 and 5
15365		 * var items = DomQuery.grep([1, 2, 3, 4, 5], function(v) {return v > 3;});
15366		 */
15367		grep: grep,
15368
15369		// Sizzle
15370		find: Sizzle,
15371		expr: Sizzle.selectors,
15372		unique: Sizzle.uniqueSort,
15373		text: Sizzle.getText,
15374		contains: Sizzle.contains,
15375		filter: function(expr, elems, not) {
15376			var i = elems.length;
15377
15378			if (not) {
15379				expr = ":not(" + expr + ")";
15380			}
15381
15382			while (i--) {
15383				if (elems[i].nodeType != 1) {
15384					elems.splice(i, 1);
15385				}
15386			}
15387
15388			if (elems.length === 1) {
15389				elems = DomQuery.find.matchesSelector(elems[0], expr) ? [elems[0]] : [];
15390			} else {
15391				elems = DomQuery.find.matches(expr, elems);
15392			}
15393
15394			return elems;
15395		}
15396	});
15397
15398	function dir(el, prop, until) {
15399		var matched = [], cur = el[prop];
15400
15401		if (typeof until != 'string' && until instanceof DomQuery) {
15402			until = until[0];
15403		}
15404
15405		while (cur && cur.nodeType !== 9) {
15406			if (until !== undefined) {
15407				if (cur === until) {
15408					break;
15409				}
15410
15411				if (typeof until == 'string' && DomQuery(cur).is(until)) {
15412					break;
15413				}
15414			}
15415
15416			if (cur.nodeType === 1) {
15417				matched.push(cur);
15418			}
15419
15420			cur = cur[prop];
15421		}
15422
15423		return matched;
15424	}
15425
15426	function sibling(node, siblingName, nodeType, until) {
15427		var result = [];
15428
15429		if (until instanceof DomQuery) {
15430			until = until[0];
15431		}
15432
15433		for (; node; node = node[siblingName]) {
15434			if (nodeType && node.nodeType !== nodeType) {
15435				continue;
15436			}
15437
15438			if (until !== undefined) {
15439				if (node === until) {
15440					break;
15441				}
15442
15443				if (typeof until == 'string' && DomQuery(node).is(until)) {
15444					break;
15445				}
15446			}
15447
15448			result.push(node);
15449		}
15450
15451		return result;
15452	}
15453
15454	function firstSibling(node, siblingName, nodeType) {
15455		for (node = node[siblingName]; node; node = node[siblingName]) {
15456			if (node.nodeType == nodeType) {
15457				return node;
15458			}
15459		}
15460
15461		return null;
15462	}
15463
15464	each({
15465		/**
15466		 * Returns a new collection with the parent of each item in current collection matching the optional selector.
15467		 *
15468		 * @method parent
15469		 * @param {Element/tinymce.dom.DomQuery} node Node to match parents against.
15470		 * @return {tinymce.dom.DomQuery} New DomQuery instance with all matching parents.
15471		 */
15472		parent: function(node) {
15473			var parent = node.parentNode;
15474
15475			return parent && parent.nodeType !== 11 ? parent : null;
15476		},
15477
15478		/**
15479		 * Returns a new collection with the all the parents of each item in current collection matching the optional selector.
15480		 *
15481		 * @method parents
15482		 * @param {Element/tinymce.dom.DomQuery} node Node to match parents against.
15483		 * @return {tinymce.dom.DomQuery} New DomQuery instance with all matching parents.
15484		 */
15485		parents: function(node) {
15486			return dir(node, "parentNode");
15487		},
15488
15489		/**
15490		 * Returns a new collection with next sibling of each item in current collection matching the optional selector.
15491		 *
15492		 * @method next
15493		 * @param {Element/tinymce.dom.DomQuery} node Node to match the next element against.
15494		 * @return {tinymce.dom.DomQuery} New DomQuery instance with all matching elements.
15495		 */
15496		next: function(node) {
15497			return firstSibling(node, 'nextSibling', 1);
15498		},
15499
15500		/**
15501		 * Returns a new collection with previous sibling of each item in current collection matching the optional selector.
15502		 *
15503		 * @method prev
15504		 * @param {Element/tinymce.dom.DomQuery} node Node to match the previous element against.
15505		 * @return {tinymce.dom.DomQuery} New DomQuery instance with all matching elements.
15506		 */
15507		prev: function(node) {
15508			return firstSibling(node, 'previousSibling', 1);
15509		},
15510
15511		/**
15512		 * Returns all child elements matching the optional selector.
15513		 *
15514		 * @method children
15515		 * @param {Element/tinymce.dom.DomQuery} node Node to match the elements against.
15516		 * @return {tinymce.dom.DomQuery} New DomQuery instance with all matching elements.
15517		 */
15518		children: function(node) {
15519			return sibling(node.firstChild, 'nextSibling', 1);
15520		},
15521
15522		/**
15523		 * Returns all child nodes matching the optional selector.
15524		 *
15525		 * @method contents
15526		 * @param {Element/tinymce.dom.DomQuery} node Node to get the contents of.
15527		 * @return {tinymce.dom.DomQuery} New DomQuery instance with all matching elements.
15528		 */
15529		contents: function(node) {
15530			return Tools.toArray((node.nodeName === "iframe" ? node.contentDocument || node.contentWindow.document : node).childNodes);
15531		}
15532	}, function(name, fn) {
15533		DomQuery.fn[name] = function(selector) {
15534			var self = this, result = [];
15535
15536			self.each(function() {
15537				var nodes = fn.call(result, this, selector, result);
15538
15539				if (nodes) {
15540					if (DomQuery.isArray(nodes)) {
15541						result.push.apply(result, nodes);
15542					} else {
15543						result.push(nodes);
15544					}
15545				}
15546			});
15547
15548			// If traversing on multiple elements we might get the same elements twice
15549			if (this.length > 1) {
15550				if (!skipUniques[name]) {
15551					result = DomQuery.unique(result);
15552				}
15553
15554				if (name.indexOf('parents') === 0) {
15555					result = result.reverse();
15556				}
15557			}
15558
15559			result = DomQuery(result);
15560
15561			if (selector) {
15562				return result.filter(selector);
15563			}
15564
15565			return result;
15566		};
15567	});
15568
15569	each({
15570		/**
15571		 * Returns a new collection with the all the parents until the matching selector/element
15572		 * of each item in current collection matching the optional selector.
15573		 *
15574		 * @method parentsUntil
15575		 * @param {Element/tinymce.dom.DomQuery} node Node to find parent of.
15576		 * @param {String/Element/tinymce.dom.DomQuery} until Until the matching selector or element.
15577		 * @return {tinymce.dom.DomQuery} New DomQuery instance with all matching parents.
15578		 */
15579		parentsUntil: function(node, until) {
15580			return dir(node, "parentNode", until);
15581		},
15582
15583		/**
15584		 * Returns a new collection with all next siblings of each item in current collection matching the optional selector.
15585		 *
15586		 * @method nextUntil
15587		 * @param {Element/tinymce.dom.DomQuery} node Node to find next siblings on.
15588		 * @param {String/Element/tinymce.dom.DomQuery} until Until the matching selector or element.
15589		 * @return {tinymce.dom.DomQuery} New DomQuery instance with all matching elements.
15590		 */
15591		nextUntil: function(node, until) {
15592			return sibling(node, 'nextSibling', 1, until).slice(1);
15593		},
15594
15595		/**
15596		 * Returns a new collection with all previous siblings of each item in current collection matching the optional selector.
15597		 *
15598		 * @method prevUntil
15599		 * @param {Element/tinymce.dom.DomQuery} node Node to find previous siblings on.
15600		 * @param {String/Element/tinymce.dom.DomQuery} until Until the matching selector or element.
15601		 * @return {tinymce.dom.DomQuery} New DomQuery instance with all matching elements.
15602		 */
15603		prevUntil: function(node, until) {
15604			return sibling(node, 'previousSibling', 1, until).slice(1);
15605		}
15606	}, function(name, fn) {
15607		DomQuery.fn[name] = function(selector, filter) {
15608			var self = this, result = [];
15609
15610			self.each(function() {
15611				var nodes = fn.call(result, this, selector, result);
15612
15613				if (nodes) {
15614					if (DomQuery.isArray(nodes)) {
15615						result.push.apply(result, nodes);
15616					} else {
15617						result.push(nodes);
15618					}
15619				}
15620			});
15621
15622			// If traversing on multiple elements we might get the same elements twice
15623			if (this.length > 1) {
15624				result = DomQuery.unique(result);
15625
15626				if (name.indexOf('parents') === 0 || name === 'prevUntil') {
15627					result = result.reverse();
15628				}
15629			}
15630
15631			result = DomQuery(result);
15632
15633			if (filter) {
15634				return result.filter(filter);
15635			}
15636
15637			return result;
15638		};
15639	});
15640
15641	/**
15642	 * Returns true/false if the current set items matches the selector.
15643	 *
15644	 * @method is
15645	 * @param {String} selector Selector to match the elements against.
15646	 * @return {Boolean} True/false if the current set matches the selector.
15647	 */
15648	DomQuery.fn.is = function(selector) {
15649		return !!selector && this.filter(selector).length > 0;
15650	};
15651
15652	DomQuery.fn.init.prototype = DomQuery.fn;
15653
15654	DomQuery.overrideDefaults = function(callback) {
15655		var defaults;
15656
15657		function sub(selector, context) {
15658			defaults = defaults || callback();
15659
15660			if (arguments.length === 0) {
15661				selector = defaults.element;
15662			}
15663
15664			if (!context) {
15665				context = defaults.context;
15666			}
15667
15668			return new sub.fn.init(selector, context);
15669		}
15670
15671		DomQuery.extend(sub, this);
15672
15673		return sub;
15674	};
15675
15676	function appendHooks(targetHooks, prop, hooks) {
15677		each(hooks, function(name, func) {
15678			targetHooks[name] = targetHooks[name] || {};
15679			targetHooks[name][prop] = func;
15680		});
15681	}
15682
15683	if (Env.ie && Env.ie < 8) {
15684		appendHooks(attrHooks, 'get', {
15685			maxlength: function(elm) {
15686				var value = elm.maxLength;
15687
15688				if (value === 0x7fffffff) {
15689					return undef;
15690				}
15691
15692				return value;
15693			},
15694
15695			size: function(elm) {
15696				var value = elm.size;
15697
15698				if (value === 20) {
15699					return undef;
15700				}
15701
15702				return value;
15703			},
15704
15705			'class': function(elm) {
15706				return elm.className;
15707			},
15708
15709			style: function(elm) {
15710				var value = elm.style.cssText;
15711
15712				if (value.length === 0) {
15713					return undef;
15714				}
15715
15716				return value;
15717			}
15718		});
15719
15720		appendHooks(attrHooks, 'set', {
15721			'class': function(elm, value) {
15722				elm.className = value;
15723			},
15724
15725			style: function(elm, value) {
15726				elm.style.cssText = value;
15727			}
15728		});
15729	}
15730
15731	if (Env.ie && Env.ie < 9) {
15732		/*jshint sub:true */
15733		/*eslint dot-notation: 0*/
15734		cssFix['float'] = 'styleFloat';
15735
15736		appendHooks(cssHooks, 'set', {
15737			opacity: function(elm, value) {
15738				var style = elm.style;
15739
15740				if (value === null || value === '') {
15741					style.removeAttribute('filter');
15742				} else {
15743					style.zoom = 1;
15744					style.filter = 'alpha(opacity=' + (value * 100) + ')';
15745				}
15746			}
15747		});
15748	}
15749
15750	DomQuery.attrHooks = attrHooks;
15751	DomQuery.cssHooks = cssHooks;
15752
15753	return DomQuery;
15754});
15755
15756// Included from: js/tinymce/classes/html/Styles.js
15757
15758/**
15759 * Styles.js
15760 *
15761 * Released under LGPL License.
15762 * Copyright (c) 1999-2015 Ephox Corp. All rights reserved
15763 *
15764 * License: http://www.tinymce.com/license
15765 * Contributing: http://www.tinymce.com/contributing
15766 */
15767
15768/**
15769 * This class is used to parse CSS styles it also compresses styles to reduce the output size.
15770 *
15771 * @example
15772 * var Styles = new tinymce.html.Styles({
15773 *    url_converter: function(url) {
15774 *       return url;
15775 *    }
15776 * });
15777 *
15778 * styles = Styles.parse('border: 1px solid red');
15779 * styles.color = 'red';
15780 *
15781 * console.log(new tinymce.html.StyleSerializer().serialize(styles));
15782 *
15783 * @class tinymce.html.Styles
15784 * @version 3.4
15785 */
15786define("tinymce/html/Styles", [], function() {
15787	return function(settings, schema) {
15788		/*jshint maxlen:255 */
15789		/*eslint max-len:0 */
15790		var rgbRegExp = /rgb\s*\(\s*([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\s*\)/gi,
15791			urlOrStrRegExp = /(?:url(?:(?:\(\s*\"([^\"]+)\"\s*\))|(?:\(\s*\'([^\']+)\'\s*\))|(?:\(\s*([^)\s]+)\s*\))))|(?:\'([^\']+)\')|(?:\"([^\"]+)\")/gi,
15792			styleRegExp = /\s*([^:]+):\s*([^;]+);?/g,
15793			trimRightRegExp = /\s+$/,
15794			undef, i, encodingLookup = {}, encodingItems, validStyles, invalidStyles, invisibleChar = '\uFEFF';
15795
15796		settings = settings || {};
15797
15798		if (schema) {
15799			validStyles = schema.getValidStyles();
15800			invalidStyles = schema.getInvalidStyles();
15801		}
15802
15803		encodingItems = ('\\" \\\' \\; \\: ; : ' + invisibleChar).split(' ');
15804		for (i = 0; i < encodingItems.length; i++) {
15805			encodingLookup[encodingItems[i]] = invisibleChar + i;
15806			encodingLookup[invisibleChar + i] = encodingItems[i];
15807		}
15808
15809		function toHex(match, r, g, b) {
15810			function hex(val) {
15811				val = parseInt(val, 10).toString(16);
15812
15813				return val.length > 1 ? val : '0' + val; // 0 -> 00
15814			}
15815
15816			return '#' + hex(r) + hex(g) + hex(b);
15817		}
15818
15819		return {
15820			/**
15821			 * Parses the specified RGB color value and returns a hex version of that color.
15822			 *
15823			 * @method toHex
15824			 * @param {String} color RGB string value like rgb(1,2,3)
15825			 * @return {String} Hex version of that RGB value like #FF00FF.
15826			 */
15827			toHex: function(color) {
15828				return color.replace(rgbRegExp, toHex);
15829			},
15830
15831			/**
15832			 * Parses the specified style value into an object collection. This parser will also
15833			 * merge and remove any redundant items that browsers might have added. It will also convert non hex
15834			 * colors to hex values. Urls inside the styles will also be converted to absolute/relative based on settings.
15835			 *
15836			 * @method parse
15837			 * @param {String} css Style value to parse for example: border:1px solid red;.
15838			 * @return {Object} Object representation of that style like {border: '1px solid red'}
15839			 */
15840			parse: function(css) {
15841				var styles = {}, matches, name, value, isEncoded, urlConverter = settings.url_converter;
15842				var urlConverterScope = settings.url_converter_scope || this;
15843
15844				function compress(prefix, suffix, noJoin) {
15845					var top, right, bottom, left;
15846
15847					top = styles[prefix + '-top' + suffix];
15848					if (!top) {
15849						return;
15850					}
15851
15852					right = styles[prefix + '-right' + suffix];
15853					if (!right) {
15854						return;
15855					}
15856
15857					bottom = styles[prefix + '-bottom' + suffix];
15858					if (!bottom) {
15859						return;
15860					}
15861
15862					left = styles[prefix + '-left' + suffix];
15863					if (!left) {
15864						return;
15865					}
15866
15867					var box = [top, right, bottom, left];
15868					i = box.length - 1;
15869					while (i--) {
15870						if (box[i] !== box[i + 1]) {
15871							break;
15872						}
15873					}
15874
15875					if (i > -1 && noJoin) {
15876						return;
15877					}
15878
15879					styles[prefix + suffix] = i == -1 ? box[0] : box.join(' ');
15880					delete styles[prefix + '-top' + suffix];
15881					delete styles[prefix + '-right' + suffix];
15882					delete styles[prefix + '-bottom' + suffix];
15883					delete styles[prefix + '-left' + suffix];
15884				}
15885
15886				/**
15887				 * Checks if the specific style can be compressed in other words if all border-width are equal.
15888				 */
15889				function canCompress(key) {
15890					var value = styles[key], i;
15891
15892					if (!value) {
15893						return;
15894					}
15895
15896					value = value.split(' ');
15897					i = value.length;
15898					while (i--) {
15899						if (value[i] !== value[0]) {
15900							return false;
15901						}
15902					}
15903
15904					styles[key] = value[0];
15905
15906					return true;
15907				}
15908
15909				/**
15910				 * Compresses multiple styles into one style.
15911				 */
15912				function compress2(target, a, b, c) {
15913					if (!canCompress(a)) {
15914						return;
15915					}
15916
15917					if (!canCompress(b)) {
15918						return;
15919					}
15920
15921					if (!canCompress(c)) {
15922						return;
15923					}
15924
15925					// Compress
15926					styles[target] = styles[a] + ' ' + styles[b] + ' ' + styles[c];
15927					delete styles[a];
15928					delete styles[b];
15929					delete styles[c];
15930				}
15931
15932				// Encodes the specified string by replacing all \" \' ; : with _<num>
15933				function encode(str) {
15934					isEncoded = true;
15935
15936					return encodingLookup[str];
15937				}
15938
15939				// Decodes the specified string by replacing all _<num> with it's original value \" \' etc
15940				// It will also decode the \" \' if keep_slashes is set to fale or omitted
15941				function decode(str, keep_slashes) {
15942					if (isEncoded) {
15943						str = str.replace(/\uFEFF[0-9]/g, function(str) {
15944							return encodingLookup[str];
15945						});
15946					}
15947
15948					if (!keep_slashes) {
15949						str = str.replace(/\\([\'\";:])/g, "$1");
15950					}
15951
15952					return str;
15953				}
15954
15955				function processUrl(match, url, url2, url3, str, str2) {
15956					str = str || str2;
15957
15958					if (str) {
15959						str = decode(str);
15960
15961						// Force strings into single quote format
15962						return "'" + str.replace(/\'/g, "\\'") + "'";
15963					}
15964
15965					url = decode(url || url2 || url3);
15966
15967					if (!settings.allow_script_urls) {
15968						var scriptUrl = url.replace(/[\s\r\n]+/, '');
15969
15970						if (/(java|vb)script:/i.test(scriptUrl)) {
15971							return "";
15972						}
15973
15974						if (!settings.allow_svg_data_urls && /^data:image\/svg/i.test(scriptUrl)) {
15975							return "";
15976						}
15977					}
15978
15979					// Convert the URL to relative/absolute depending on config
15980					if (urlConverter) {
15981						url = urlConverter.call(urlConverterScope, url, 'style');
15982					}
15983
15984					// Output new URL format
15985					return "url('" + url.replace(/\'/g, "\\'") + "')";
15986				}
15987
15988				if (css) {
15989					css = css.replace(/[\u0000-\u001F]/g, '');
15990
15991					// Encode \" \' % and ; and : inside strings so they don't interfere with the style parsing
15992					css = css.replace(/\\[\"\';:\uFEFF]/g, encode).replace(/\"[^\"]+\"|\'[^\']+\'/g, function(str) {
15993						return str.replace(/[;:]/g, encode);
15994					});
15995
15996					// Parse styles
15997					while ((matches = styleRegExp.exec(css))) {
15998						name = matches[1].replace(trimRightRegExp, '').toLowerCase();
15999						value = matches[2].replace(trimRightRegExp, '');
16000
16001						// Decode escaped sequences like \65 -> e
16002						/*jshint loopfunc:true*/
16003						/*eslint no-loop-func:0 */
16004						value = value.replace(/\\[0-9a-f]+/g, function(e) {
16005							return String.fromCharCode(parseInt(e.substr(1), 16));
16006						});
16007
16008						if (name && value.length > 0) {
16009							// Don't allow behavior name or expression/comments within the values
16010							if (!settings.allow_script_urls && (name == "behavior" || /expression\s*\(|\/\*|\*\//.test(value))) {
16011								continue;
16012							}
16013
16014							// Opera will produce 700 instead of bold in their style values
16015							if (name === 'font-weight' && value === '700') {
16016								value = 'bold';
16017							} else if (name === 'color' || name === 'background-color') { // Lowercase colors like RED
16018								value = value.toLowerCase();
16019							}
16020
16021							// Convert RGB colors to HEX
16022							value = value.replace(rgbRegExp, toHex);
16023
16024							// Convert URLs and force them into url('value') format
16025							value = value.replace(urlOrStrRegExp, processUrl);
16026							styles[name] = isEncoded ? decode(value, true) : value;
16027						}
16028
16029						styleRegExp.lastIndex = matches.index + matches[0].length;
16030					}
16031					// Compress the styles to reduce it's size for example IE will expand styles
16032					compress("border", "", true);
16033					compress("border", "-width");
16034					compress("border", "-color");
16035					compress("border", "-style");
16036					compress("padding", "");
16037					compress("margin", "");
16038					compress2('border', 'border-width', 'border-style', 'border-color');
16039
16040					// Remove pointless border, IE produces these
16041					if (styles.border === 'medium none') {
16042						delete styles.border;
16043					}
16044
16045					// IE 11 will produce a border-image: none when getting the style attribute from <p style="border: 1px solid red"></p>
16046					// So let us assume it shouldn't be there
16047					if (styles['border-image'] === 'none') {
16048						delete styles['border-image'];
16049					}
16050				}
16051
16052				return styles;
16053			},
16054
16055			/**
16056			 * Serializes the specified style object into a string.
16057			 *
16058			 * @method serialize
16059			 * @param {Object} styles Object to serialize as string for example: {border: '1px solid red'}
16060			 * @param {String} elementName Optional element name, if specified only the styles that matches the schema will be serialized.
16061			 * @return {String} String representation of the style object for example: border: 1px solid red.
16062			 */
16063			serialize: function(styles, elementName) {
16064				var css = '', name, value;
16065
16066				function serializeStyles(name) {
16067					var styleList, i, l, value;
16068
16069					styleList = validStyles[name];
16070					if (styleList) {
16071						for (i = 0, l = styleList.length; i < l; i++) {
16072							name = styleList[i];
16073							value = styles[name];
16074
16075							if (value !== undef && value.length > 0) {
16076								css += (css.length > 0 ? ' ' : '') + name + ': ' + value + ';';
16077							}
16078						}
16079					}
16080				}
16081
16082				function isValid(name, elementName) {
16083					var styleMap;
16084
16085					styleMap = invalidStyles['*'];
16086					if (styleMap && styleMap[name]) {
16087						return false;
16088					}
16089
16090					styleMap = invalidStyles[elementName];
16091					if (styleMap && styleMap[name]) {
16092						return false;
16093					}
16094
16095					return true;
16096				}
16097
16098				// Serialize styles according to schema
16099				if (elementName && validStyles) {
16100					// Serialize global styles and element specific styles
16101					serializeStyles('*');
16102					serializeStyles(elementName);
16103				} else {
16104					// Output the styles in the order they are inside the object
16105					for (name in styles) {
16106						value = styles[name];
16107
16108						if (value !== undef && value.length > 0) {
16109							if (!invalidStyles || isValid(name, elementName)) {
16110								css += (css.length > 0 ? ' ' : '') + name + ': ' + value + ';';
16111							}
16112						}
16113					}
16114				}
16115
16116				return css;
16117			}
16118		};
16119	};
16120});
16121
16122// Included from: js/tinymce/classes/dom/TreeWalker.js
16123
16124/**
16125 * TreeWalker.js
16126 *
16127 * Released under LGPL License.
16128 * Copyright (c) 1999-2015 Ephox Corp. All rights reserved
16129 *
16130 * License: http://www.tinymce.com/license
16131 * Contributing: http://www.tinymce.com/contributing
16132 */
16133
16134/**
16135 * TreeWalker class enables you to walk the DOM in a linear manner.
16136 *
16137 * @class tinymce.dom.TreeWalker
16138 * @example
16139 * var walker = new tinymce.dom.TreeWalker(startNode);
16140 *
16141 * do {
16142 *     console.log(walker.current());
16143 * } while (walker.next());
16144 */
16145define("tinymce/dom/TreeWalker", [], function() {
16146	/**
16147	 * Constructs a new TreeWalker instance.
16148	 *
16149	 * @constructor
16150	 * @method TreeWalker
16151	 * @param {Node} startNode Node to start walking from.
16152	 * @param {node} rootNode Optional root node to never walk out of.
16153	 */
16154	return function(startNode, rootNode) {
16155		var node = startNode;
16156
16157		function findSibling(node, startName, siblingName, shallow) {
16158			var sibling, parent;
16159
16160			if (node) {
16161				// Walk into nodes if it has a start
16162				if (!shallow && node[startName]) {
16163					return node[startName];
16164				}
16165
16166				// Return the sibling if it has one
16167				if (node != rootNode) {
16168					sibling = node[siblingName];
16169					if (sibling) {
16170						return sibling;
16171					}
16172
16173					// Walk up the parents to look for siblings
16174					for (parent = node.parentNode; parent && parent != rootNode; parent = parent.parentNode) {
16175						sibling = parent[siblingName];
16176						if (sibling) {
16177							return sibling;
16178						}
16179					}
16180				}
16181			}
16182		}
16183
16184		/**
16185		 * Returns the current node.
16186		 *
16187		 * @method current
16188		 * @return {Node} Current node where the walker is.
16189		 */
16190		this.current = function() {
16191			return node;
16192		};
16193
16194		/**
16195		 * Walks to the next node in tree.
16196		 *
16197		 * @method next
16198		 * @return {Node} Current node where the walker is after moving to the next node.
16199		 */
16200		this.next = function(shallow) {
16201			node = findSibling(node, 'firstChild', 'nextSibling', shallow);
16202			return node;
16203		};
16204
16205		/**
16206		 * Walks to the previous node in tree.
16207		 *
16208		 * @method prev
16209		 * @return {Node} Current node where the walker is after moving to the previous node.
16210		 */
16211		this.prev = function(shallow) {
16212			node = findSibling(node, 'lastChild', 'previousSibling', shallow);
16213			return node;
16214		};
16215	};
16216});
16217
16218// Included from: js/tinymce/classes/dom/Range.js
16219
16220/**
16221 * Range.js
16222 *
16223 * Released under LGPL License.
16224 * Copyright (c) 1999-2015 Ephox Corp. All rights reserved
16225 *
16226 * License: http://www.tinymce.com/license
16227 * Contributing: http://www.tinymce.com/contributing
16228 */
16229
16230/**
16231 * Old IE Range.
16232 *
16233 * @private
16234 * @class tinymce.dom.Range
16235 */
16236define("tinymce/dom/Range", [
16237	"tinymce/util/Tools"
16238], function(Tools) {
16239	// Range constructor
16240	function Range(dom) {
16241		var self = this,
16242			doc = dom.doc,
16243			EXTRACT = 0,
16244			CLONE = 1,
16245			DELETE = 2,
16246			TRUE = true,
16247			FALSE = false,
16248			START_OFFSET = 'startOffset',
16249			START_CONTAINER = 'startContainer',
16250			END_CONTAINER = 'endContainer',
16251			END_OFFSET = 'endOffset',
16252			extend = Tools.extend,
16253			nodeIndex = dom.nodeIndex;
16254
16255		function createDocumentFragment() {
16256			return doc.createDocumentFragment();
16257		}
16258
16259		function setStart(n, o) {
16260			_setEndPoint(TRUE, n, o);
16261		}
16262
16263		function setEnd(n, o) {
16264			_setEndPoint(FALSE, n, o);
16265		}
16266
16267		function setStartBefore(n) {
16268			setStart(n.parentNode, nodeIndex(n));
16269		}
16270
16271		function setStartAfter(n) {
16272			setStart(n.parentNode, nodeIndex(n) + 1);
16273		}
16274
16275		function setEndBefore(n) {
16276			setEnd(n.parentNode, nodeIndex(n));
16277		}
16278
16279		function setEndAfter(n) {
16280			setEnd(n.parentNode, nodeIndex(n) + 1);
16281		}
16282
16283		function collapse(ts) {
16284			if (ts) {
16285				self[END_CONTAINER] = self[START_CONTAINER];
16286				self[END_OFFSET] = self[START_OFFSET];
16287			} else {
16288				self[START_CONTAINER] = self[END_CONTAINER];
16289				self[START_OFFSET] = self[END_OFFSET];
16290			}
16291
16292			self.collapsed = TRUE;
16293		}
16294
16295		function selectNode(n) {
16296			setStartBefore(n);
16297			setEndAfter(n);
16298		}
16299
16300		function selectNodeContents(n) {
16301			setStart(n, 0);
16302			setEnd(n, n.nodeType === 1 ? n.childNodes.length : n.nodeValue.length);
16303		}
16304
16305		function compareBoundaryPoints(h, r) {
16306			var sc = self[START_CONTAINER], so = self[START_OFFSET], ec = self[END_CONTAINER], eo = self[END_OFFSET],
16307			rsc = r.startContainer, rso = r.startOffset, rec = r.endContainer, reo = r.endOffset;
16308
16309			// Check START_TO_START
16310			if (h === 0) {
16311				return _compareBoundaryPoints(sc, so, rsc, rso);
16312			}
16313
16314			// Check START_TO_END
16315			if (h === 1) {
16316				return _compareBoundaryPoints(ec, eo, rsc, rso);
16317			}
16318
16319			// Check END_TO_END
16320			if (h === 2) {
16321				return _compareBoundaryPoints(ec, eo, rec, reo);
16322			}
16323
16324			// Check END_TO_START
16325			if (h === 3) {
16326				return _compareBoundaryPoints(sc, so, rec, reo);
16327			}
16328		}
16329
16330		function deleteContents() {
16331			_traverse(DELETE);
16332		}
16333
16334		function extractContents() {
16335			return _traverse(EXTRACT);
16336		}
16337
16338		function cloneContents() {
16339			return _traverse(CLONE);
16340		}
16341
16342		function insertNode(n) {
16343			var startContainer = this[START_CONTAINER],
16344				startOffset = this[START_OFFSET], nn, o;
16345
16346			// Node is TEXT_NODE or CDATA
16347			if ((startContainer.nodeType === 3 || startContainer.nodeType === 4) && startContainer.nodeValue) {
16348				if (!startOffset) {
16349					// At the start of text
16350					startContainer.parentNode.insertBefore(n, startContainer);
16351				} else if (startOffset >= startContainer.nodeValue.length) {
16352					// At the end of text
16353					dom.insertAfter(n, startContainer);
16354				} else {
16355					// Middle, need to split
16356					nn = startContainer.splitText(startOffset);
16357					startContainer.parentNode.insertBefore(n, nn);
16358				}
16359			} else {
16360				// Insert element node
16361				if (startContainer.childNodes.length > 0) {
16362					o = startContainer.childNodes[startOffset];
16363				}
16364
16365				if (o) {
16366					startContainer.insertBefore(n, o);
16367				} else {
16368					if (startContainer.nodeType == 3) {
16369						dom.insertAfter(n, startContainer);
16370					} else {
16371						startContainer.appendChild(n);
16372					}
16373				}
16374			}
16375		}
16376
16377		function surroundContents(n) {
16378			var f = self.extractContents();
16379
16380			self.insertNode(n);
16381			n.appendChild(f);
16382			self.selectNode(n);
16383		}
16384
16385		function cloneRange() {
16386			return extend(new Range(dom), {
16387				startContainer: self[START_CONTAINER],
16388				startOffset: self[START_OFFSET],
16389				endContainer: self[END_CONTAINER],
16390				endOffset: self[END_OFFSET],
16391				collapsed: self.collapsed,
16392				commonAncestorContainer: self.commonAncestorContainer
16393			});
16394		}
16395
16396		// Private methods
16397
16398		function _getSelectedNode(container, offset) {
16399			var child;
16400
16401			// TEXT_NODE
16402			if (container.nodeType == 3) {
16403				return container;
16404			}
16405
16406			if (offset < 0) {
16407				return container;
16408			}
16409
16410			child = container.firstChild;
16411			while (child && offset > 0) {
16412				--offset;
16413				child = child.nextSibling;
16414			}
16415
16416			if (child) {
16417				return child;
16418			}
16419
16420			return container;
16421		}
16422
16423		function _isCollapsed() {
16424			return (self[START_CONTAINER] == self[END_CONTAINER] && self[START_OFFSET] == self[END_OFFSET]);
16425		}
16426
16427		function _compareBoundaryPoints(containerA, offsetA, containerB, offsetB) {
16428			var c, offsetC, n, cmnRoot, childA, childB;
16429
16430			// In the first case the boundary-points have the same container. A is before B
16431			// if its offset is less than the offset of B, A is equal to B if its offset is
16432			// equal to the offset of B, and A is after B if its offset is greater than the
16433			// offset of B.
16434			if (containerA == containerB) {
16435				if (offsetA == offsetB) {
16436					return 0; // equal
16437				}
16438
16439				if (offsetA < offsetB) {
16440					return -1; // before
16441				}
16442
16443				return 1; // after
16444			}
16445
16446			// In the second case a child node C of the container of A is an ancestor
16447			// container of B. In this case, A is before B if the offset of A is less than or
16448			// equal to the index of the child node C and A is after B otherwise.
16449			c = containerB;
16450			while (c && c.parentNode != containerA) {
16451				c = c.parentNode;
16452			}
16453
16454			if (c) {
16455				offsetC = 0;
16456				n = containerA.firstChild;
16457
16458				while (n != c && offsetC < offsetA) {
16459					offsetC++;
16460					n = n.nextSibling;
16461				}
16462
16463				if (offsetA <= offsetC) {
16464					return -1; // before
16465				}
16466
16467				return 1; // after
16468			}
16469
16470			// In the third case a child node C of the container of B is an ancestor container
16471			// of A. In this case, A is before B if the index of the child node C is less than
16472			// the offset of B and A is after B otherwise.
16473			c = containerA;
16474			while (c && c.parentNode != containerB) {
16475				c = c.parentNode;
16476			}
16477
16478			if (c) {
16479				offsetC = 0;
16480				n = containerB.firstChild;
16481
16482				while (n != c && offsetC < offsetB) {
16483					offsetC++;
16484					n = n.nextSibling;
16485				}
16486
16487				if (offsetC < offsetB) {
16488					return -1; // before
16489				}
16490
16491				return 1; // after
16492			}
16493
16494			// In the fourth case, none of three other cases hold: the containers of A and B
16495			// are siblings or descendants of sibling nodes. In this case, A is before B if
16496			// the container of A is before the container of B in a pre-order traversal of the
16497			// Ranges' context tree and A is after B otherwise.
16498			cmnRoot = dom.findCommonAncestor(containerA, containerB);
16499			childA = containerA;
16500
16501			while (childA && childA.parentNode != cmnRoot) {
16502				childA = childA.parentNode;
16503			}
16504
16505			if (!childA) {
16506				childA = cmnRoot;
16507			}
16508
16509			childB = containerB;
16510			while (childB && childB.parentNode != cmnRoot) {
16511				childB = childB.parentNode;
16512			}
16513
16514			if (!childB) {
16515				childB = cmnRoot;
16516			}
16517
16518			if (childA == childB) {
16519				return 0; // equal
16520			}
16521
16522			n = cmnRoot.firstChild;
16523			while (n) {
16524				if (n == childA) {
16525					return -1; // before
16526				}
16527
16528				if (n == childB) {
16529					return 1; // after
16530				}
16531
16532				n = n.nextSibling;
16533			}
16534		}
16535
16536		function _setEndPoint(st, n, o) {
16537			var ec, sc;
16538
16539			if (st) {
16540				self[START_CONTAINER] = n;
16541				self[START_OFFSET] = o;
16542			} else {
16543				self[END_CONTAINER] = n;
16544				self[END_OFFSET] = o;
16545			}
16546
16547			// If one boundary-point of a Range is set to have a root container
16548			// other than the current one for the Range, the Range is collapsed to
16549			// the new position. This enforces the restriction that both boundary-
16550			// points of a Range must have the same root container.
16551			ec = self[END_CONTAINER];
16552			while (ec.parentNode) {
16553				ec = ec.parentNode;
16554			}
16555
16556			sc = self[START_CONTAINER];
16557			while (sc.parentNode) {
16558				sc = sc.parentNode;
16559			}
16560
16561			if (sc == ec) {
16562				// The start position of a Range is guaranteed to never be after the
16563				// end position. To enforce this restriction, if the start is set to
16564				// be at a position after the end, the Range is collapsed to that
16565				// position.
16566				if (_compareBoundaryPoints(self[START_CONTAINER], self[START_OFFSET], self[END_CONTAINER], self[END_OFFSET]) > 0) {
16567					self.collapse(st);
16568				}
16569			} else {
16570				self.collapse(st);
16571			}
16572
16573			self.collapsed = _isCollapsed();
16574			self.commonAncestorContainer = dom.findCommonAncestor(self[START_CONTAINER], self[END_CONTAINER]);
16575		}
16576
16577		function _traverse(how) {
16578			var c, endContainerDepth = 0, startContainerDepth = 0, p, depthDiff, startNode, endNode, sp, ep;
16579
16580			if (self[START_CONTAINER] == self[END_CONTAINER]) {
16581				return _traverseSameContainer(how);
16582			}
16583
16584			for (c = self[END_CONTAINER], p = c.parentNode; p; c = p, p = p.parentNode) {
16585				if (p == self[START_CONTAINER]) {
16586					return _traverseCommonStartContainer(c, how);
16587				}
16588
16589				++endContainerDepth;
16590			}
16591
16592			for (c = self[START_CONTAINER], p = c.parentNode; p; c = p, p = p.parentNode) {
16593				if (p == self[END_CONTAINER]) {
16594					return _traverseCommonEndContainer(c, how);
16595				}
16596
16597				++startContainerDepth;
16598			}
16599
16600			depthDiff = startContainerDepth - endContainerDepth;
16601
16602			startNode = self[START_CONTAINER];
16603			while (depthDiff > 0) {
16604				startNode = startNode.parentNode;
16605				depthDiff--;
16606			}
16607
16608			endNode = self[END_CONTAINER];
16609			while (depthDiff < 0) {
16610				endNode = endNode.parentNode;
16611				depthDiff++;
16612			}
16613
16614			// ascend the ancestor hierarchy until we have a common parent.
16615			for (sp = startNode.parentNode, ep = endNode.parentNode; sp != ep; sp = sp.parentNode, ep = ep.parentNode) {
16616				startNode = sp;
16617				endNode = ep;
16618			}
16619
16620			return _traverseCommonAncestors(startNode, endNode, how);
16621		}
16622
16623		function _traverseSameContainer(how) {
16624			var frag, s, sub, n, cnt, sibling, xferNode, start, len;
16625
16626			if (how != DELETE) {
16627				frag = createDocumentFragment();
16628			}
16629
16630			// If selection is empty, just return the fragment
16631			if (self[START_OFFSET] == self[END_OFFSET]) {
16632				return frag;
16633			}
16634
16635			// Text node needs special case handling
16636			if (self[START_CONTAINER].nodeType == 3) { // TEXT_NODE
16637				// get the substring
16638				s = self[START_CONTAINER].nodeValue;
16639				sub = s.substring(self[START_OFFSET], self[END_OFFSET]);
16640
16641				// set the original text node to its new value
16642				if (how != CLONE) {
16643					n = self[START_CONTAINER];
16644					start = self[START_OFFSET];
16645					len = self[END_OFFSET] - self[START_OFFSET];
16646
16647					if (start === 0 && len >= n.nodeValue.length - 1) {
16648						n.parentNode.removeChild(n);
16649					} else {
16650						n.deleteData(start, len);
16651					}
16652
16653					// Nothing is partially selected, so collapse to start point
16654					self.collapse(TRUE);
16655				}
16656
16657				if (how == DELETE) {
16658					return;
16659				}
16660
16661				if (sub.length > 0) {
16662					frag.appendChild(doc.createTextNode(sub));
16663				}
16664
16665				return frag;
16666			}
16667
16668			// Copy nodes between the start/end offsets.
16669			n = _getSelectedNode(self[START_CONTAINER], self[START_OFFSET]);
16670			cnt = self[END_OFFSET] - self[START_OFFSET];
16671
16672			while (n && cnt > 0) {
16673				sibling = n.nextSibling;
16674				xferNode = _traverseFullySelected(n, how);
16675
16676				if (frag) {
16677					frag.appendChild(xferNode);
16678				}
16679
16680				--cnt;
16681				n = sibling;
16682			}
16683
16684			// Nothing is partially selected, so collapse to start point
16685			if (how != CLONE) {
16686				self.collapse(TRUE);
16687			}
16688
16689			return frag;
16690		}
16691
16692		function _traverseCommonStartContainer(endAncestor, how) {
16693			var frag, n, endIdx, cnt, sibling, xferNode;
16694
16695			if (how != DELETE) {
16696				frag = createDocumentFragment();
16697			}
16698
16699			n = _traverseRightBoundary(endAncestor, how);
16700
16701			if (frag) {
16702				frag.appendChild(n);
16703			}
16704
16705			endIdx = nodeIndex(endAncestor);
16706			cnt = endIdx - self[START_OFFSET];
16707
16708			if (cnt <= 0) {
16709				// Collapse to just before the endAncestor, which
16710				// is partially selected.
16711				if (how != CLONE) {
16712					self.setEndBefore(endAncestor);
16713					self.collapse(FALSE);
16714				}
16715
16716				return frag;
16717			}
16718
16719			n = endAncestor.previousSibling;
16720			while (cnt > 0) {
16721				sibling = n.previousSibling;
16722				xferNode = _traverseFullySelected(n, how);
16723
16724				if (frag) {
16725					frag.insertBefore(xferNode, frag.firstChild);
16726				}
16727
16728				--cnt;
16729				n = sibling;
16730			}
16731
16732			// Collapse to just before the endAncestor, which
16733			// is partially selected.
16734			if (how != CLONE) {
16735				self.setEndBefore(endAncestor);
16736				self.collapse(FALSE);
16737			}
16738
16739			return frag;
16740		}
16741
16742		function _traverseCommonEndContainer(startAncestor, how) {
16743			var frag, startIdx, n, cnt, sibling, xferNode;
16744
16745			if (how != DELETE) {
16746				frag = createDocumentFragment();
16747			}
16748
16749			n = _traverseLeftBoundary(startAncestor, how);
16750			if (frag) {
16751				frag.appendChild(n);
16752			}
16753
16754			startIdx = nodeIndex(startAncestor);
16755			++startIdx; // Because we already traversed it
16756
16757			cnt = self[END_OFFSET] - startIdx;
16758			n = startAncestor.nextSibling;
16759			while (n && cnt > 0) {
16760				sibling = n.nextSibling;
16761				xferNode = _traverseFullySelected(n, how);
16762
16763				if (frag) {
16764					frag.appendChild(xferNode);
16765				}
16766
16767				--cnt;
16768				n = sibling;
16769			}
16770
16771			if (how != CLONE) {
16772				self.setStartAfter(startAncestor);
16773				self.collapse(TRUE);
16774			}
16775
16776			return frag;
16777		}
16778
16779		function _traverseCommonAncestors(startAncestor, endAncestor, how) {
16780			var n, frag, startOffset, endOffset, cnt, sibling, nextSibling;
16781
16782			if (how != DELETE) {
16783				frag = createDocumentFragment();
16784			}
16785
16786			n = _traverseLeftBoundary(startAncestor, how);
16787			if (frag) {
16788				frag.appendChild(n);
16789			}
16790
16791			startOffset = nodeIndex(startAncestor);
16792			endOffset = nodeIndex(endAncestor);
16793			++startOffset;
16794
16795			cnt = endOffset - startOffset;
16796			sibling = startAncestor.nextSibling;
16797
16798			while (cnt > 0) {
16799				nextSibling = sibling.nextSibling;
16800				n = _traverseFullySelected(sibling, how);
16801
16802				if (frag) {
16803					frag.appendChild(n);
16804				}
16805
16806				sibling = nextSibling;
16807				--cnt;
16808			}
16809
16810			n = _traverseRightBoundary(endAncestor, how);
16811
16812			if (frag) {
16813				frag.appendChild(n);
16814			}
16815
16816			if (how != CLONE) {
16817				self.setStartAfter(startAncestor);
16818				self.collapse(TRUE);
16819			}
16820
16821			return frag;
16822		}
16823
16824		function _traverseRightBoundary(root, how) {
16825			var next = _getSelectedNode(self[END_CONTAINER], self[END_OFFSET] - 1), parent, clonedParent;
16826			var prevSibling, clonedChild, clonedGrandParent, isFullySelected = next != self[END_CONTAINER];
16827
16828			if (next == root) {
16829				return _traverseNode(next, isFullySelected, FALSE, how);
16830			}
16831
16832			parent = next.parentNode;
16833			clonedParent = _traverseNode(parent, FALSE, FALSE, how);
16834
16835			while (parent) {
16836				while (next) {
16837					prevSibling = next.previousSibling;
16838					clonedChild = _traverseNode(next, isFullySelected, FALSE, how);
16839
16840					if (how != DELETE) {
16841						clonedParent.insertBefore(clonedChild, clonedParent.firstChild);
16842					}
16843
16844					isFullySelected = TRUE;
16845					next = prevSibling;
16846				}
16847
16848				if (parent == root) {
16849					return clonedParent;
16850				}
16851
16852				next = parent.previousSibling;
16853				parent = parent.parentNode;
16854
16855				clonedGrandParent = _traverseNode(parent, FALSE, FALSE, how);
16856
16857				if (how != DELETE) {
16858					clonedGrandParent.appendChild(clonedParent);
16859				}
16860
16861				clonedParent = clonedGrandParent;
16862			}
16863		}
16864
16865		function _traverseLeftBoundary(root, how) {
16866			var next = _getSelectedNode(self[START_CONTAINER], self[START_OFFSET]), isFullySelected = next != self[START_CONTAINER];
16867			var parent, clonedParent, nextSibling, clonedChild, clonedGrandParent;
16868
16869			if (next == root) {
16870				return _traverseNode(next, isFullySelected, TRUE, how);
16871			}
16872
16873			parent = next.parentNode;
16874			clonedParent = _traverseNode(parent, FALSE, TRUE, how);
16875
16876			while (parent) {
16877				while (next) {
16878					nextSibling = next.nextSibling;
16879					clonedChild = _traverseNode(next, isFullySelected, TRUE, how);
16880
16881					if (how != DELETE) {
16882						clonedParent.appendChild(clonedChild);
16883					}
16884
16885					isFullySelected = TRUE;
16886					next = nextSibling;
16887				}
16888
16889				if (parent == root) {
16890					return clonedParent;
16891				}
16892
16893				next = parent.nextSibling;
16894				parent = parent.parentNode;
16895
16896				clonedGrandParent = _traverseNode(parent, FALSE, TRUE, how);
16897
16898				if (how != DELETE) {
16899					clonedGrandParent.appendChild(clonedParent);
16900				}
16901
16902				clonedParent = clonedGrandParent;
16903			}
16904		}
16905
16906		function _traverseNode(n, isFullySelected, isLeft, how) {
16907			var txtValue, newNodeValue, oldNodeValue, offset, newNode;
16908
16909			if (isFullySelected) {
16910				return _traverseFullySelected(n, how);
16911			}
16912
16913			// TEXT_NODE
16914			if (n.nodeType == 3) {
16915				txtValue = n.nodeValue;
16916
16917				if (isLeft) {
16918					offset = self[START_OFFSET];
16919					newNodeValue = txtValue.substring(offset);
16920					oldNodeValue = txtValue.substring(0, offset);
16921				} else {
16922					offset = self[END_OFFSET];
16923					newNodeValue = txtValue.substring(0, offset);
16924					oldNodeValue = txtValue.substring(offset);
16925				}
16926
16927				if (how != CLONE) {
16928					n.nodeValue = oldNodeValue;
16929				}
16930
16931				if (how == DELETE) {
16932					return;
16933				}
16934
16935				newNode = dom.clone(n, FALSE);
16936				newNode.nodeValue = newNodeValue;
16937
16938				return newNode;
16939			}
16940
16941			if (how == DELETE) {
16942				return;
16943			}
16944
16945			return dom.clone(n, FALSE);
16946		}
16947
16948		function _traverseFullySelected(n, how) {
16949			if (how != DELETE) {
16950				return how == CLONE ? dom.clone(n, TRUE) : n;
16951			}
16952
16953			n.parentNode.removeChild(n);
16954		}
16955
16956		function toStringIE() {
16957			return dom.create('body', null, cloneContents()).outerText;
16958		}
16959
16960		extend(self, {
16961			// Initial states
16962			startContainer: doc,
16963			startOffset: 0,
16964			endContainer: doc,
16965			endOffset: 0,
16966			collapsed: TRUE,
16967			commonAncestorContainer: doc,
16968
16969			// Range constants
16970			START_TO_START: 0,
16971			START_TO_END: 1,
16972			END_TO_END: 2,
16973			END_TO_START: 3,
16974
16975			// Public methods
16976			setStart: setStart,
16977			setEnd: setEnd,
16978			setStartBefore: setStartBefore,
16979			setStartAfter: setStartAfter,
16980			setEndBefore: setEndBefore,
16981			setEndAfter: setEndAfter,
16982			collapse: collapse,
16983			selectNode: selectNode,
16984			selectNodeContents: selectNodeContents,
16985			compareBoundaryPoints: compareBoundaryPoints,
16986			deleteContents: deleteContents,
16987			extractContents: extractContents,
16988			cloneContents: cloneContents,
16989			insertNode: insertNode,
16990			surroundContents: surroundContents,
16991			cloneRange: cloneRange,
16992			toStringIE: toStringIE
16993		});
16994
16995		return self;
16996	}
16997
16998	// Older IE versions doesn't let you override toString by it's constructor so we have to stick it in the prototype
16999	Range.prototype.toString = function() {
17000		return this.toStringIE();
17001	};
17002
17003	return Range;
17004});
17005
17006// Included from: js/tinymce/classes/html/Entities.js
17007
17008/**
17009 * Entities.js
17010 *
17011 * Released under LGPL License.
17012 * Copyright (c) 1999-2015 Ephox Corp. All rights reserved
17013 *
17014 * License: http://www.tinymce.com/license
17015 * Contributing: http://www.tinymce.com/contributing
17016 */
17017
17018/*jshint bitwise:false */
17019/*eslint no-bitwise:0 */
17020
17021/**
17022 * Entity encoder class.
17023 *
17024 * @class tinymce.html.Entities
17025 * @static
17026 * @version 3.4
17027 */
17028define("tinymce/html/Entities", [
17029	"tinymce/util/Tools"
17030], function(Tools) {
17031	var makeMap = Tools.makeMap;
17032
17033	var namedEntities, baseEntities, reverseEntities,
17034		attrsCharsRegExp = /[&<>\"\u0060\u007E-\uD7FF\uE000-\uFFEF]|[\uD800-\uDBFF][\uDC00-\uDFFF]/g,
17035		textCharsRegExp = /[<>&\u007E-\uD7FF\uE000-\uFFEF]|[\uD800-\uDBFF][\uDC00-\uDFFF]/g,
17036		rawCharsRegExp = /[<>&\"\']/g,
17037		entityRegExp = /&#([a-z0-9]+);?|&([a-z0-9]+);/gi,
17038		asciiMap = {
17039			128: "\u20AC", 130: "\u201A", 131: "\u0192", 132: "\u201E", 133: "\u2026", 134: "\u2020",
17040			135: "\u2021", 136: "\u02C6", 137: "\u2030", 138: "\u0160", 139: "\u2039", 140: "\u0152",
17041			142: "\u017D", 145: "\u2018", 146: "\u2019", 147: "\u201C", 148: "\u201D", 149: "\u2022",
17042			150: "\u2013", 151: "\u2014", 152: "\u02DC", 153: "\u2122", 154: "\u0161", 155: "\u203A",
17043			156: "\u0153", 158: "\u017E", 159: "\u0178"
17044		};
17045
17046	// Raw entities
17047	baseEntities = {
17048		'\"': '&quot;', // Needs to be escaped since the YUI compressor would otherwise break the code
17049		"'": '&#39;',
17050		'<': '&lt;',
17051		'>': '&gt;',
17052		'&': '&amp;',
17053		'\u0060': '&#96;'
17054	};
17055
17056	// Reverse lookup table for raw entities
17057	reverseEntities = {
17058		'&lt;': '<',
17059		'&gt;': '>',
17060		'&amp;': '&',
17061		'&quot;': '"',
17062		'&apos;': "'"
17063	};
17064
17065	// Decodes text by using the browser
17066	function nativeDecode(text) {
17067		var elm;
17068
17069		elm = document.createElement("div");
17070		elm.innerHTML = text;
17071
17072		return elm.textContent || elm.innerText || text;
17073	}
17074
17075	// Build a two way lookup table for the entities
17076	function buildEntitiesLookup(items, radix) {
17077		var i, chr, entity, lookup = {};
17078
17079		if (items) {
17080			items = items.split(',');
17081			radix = radix || 10;
17082
17083			// Build entities lookup table
17084			for (i = 0; i < items.length; i += 2) {
17085				chr = String.fromCharCode(parseInt(items[i], radix));
17086
17087				// Only add non base entities
17088				if (!baseEntities[chr]) {
17089					entity = '&' + items[i + 1] + ';';
17090					lookup[chr] = entity;
17091					lookup[entity] = chr;
17092				}
17093			}
17094
17095			return lookup;
17096		}
17097	}
17098
17099	// Unpack entities lookup where the numbers are in radix 32 to reduce the size
17100	namedEntities = buildEntitiesLookup(
17101		'50,nbsp,51,iexcl,52,cent,53,pound,54,curren,55,yen,56,brvbar,57,sect,58,uml,59,copy,' +
17102		'5a,ordf,5b,laquo,5c,not,5d,shy,5e,reg,5f,macr,5g,deg,5h,plusmn,5i,sup2,5j,sup3,5k,acute,' +
17103		'5l,micro,5m,para,5n,middot,5o,cedil,5p,sup1,5q,ordm,5r,raquo,5s,frac14,5t,frac12,5u,frac34,' +
17104		'5v,iquest,60,Agrave,61,Aacute,62,Acirc,63,Atilde,64,Auml,65,Aring,66,AElig,67,Ccedil,' +
17105		'68,Egrave,69,Eacute,6a,Ecirc,6b,Euml,6c,Igrave,6d,Iacute,6e,Icirc,6f,Iuml,6g,ETH,6h,Ntilde,' +
17106		'6i,Ograve,6j,Oacute,6k,Ocirc,6l,Otilde,6m,Ouml,6n,times,6o,Oslash,6p,Ugrave,6q,Uacute,' +
17107		'6r,Ucirc,6s,Uuml,6t,Yacute,6u,THORN,6v,szlig,70,agrave,71,aacute,72,acirc,73,atilde,74,auml,' +
17108		'75,aring,76,aelig,77,ccedil,78,egrave,79,eacute,7a,ecirc,7b,euml,7c,igrave,7d,iacute,7e,icirc,' +
17109		'7f,iuml,7g,eth,7h,ntilde,7i,ograve,7j,oacute,7k,ocirc,7l,otilde,7m,ouml,7n,divide,7o,oslash,' +
17110		'7p,ugrave,7q,uacute,7r,ucirc,7s,uuml,7t,yacute,7u,thorn,7v,yuml,ci,fnof,sh,Alpha,si,Beta,' +
17111		'sj,Gamma,sk,Delta,sl,Epsilon,sm,Zeta,sn,Eta,so,Theta,sp,Iota,sq,Kappa,sr,Lambda,ss,Mu,' +
17112		'st,Nu,su,Xi,sv,Omicron,t0,Pi,t1,Rho,t3,Sigma,t4,Tau,t5,Upsilon,t6,Phi,t7,Chi,t8,Psi,' +
17113		't9,Omega,th,alpha,ti,beta,tj,gamma,tk,delta,tl,epsilon,tm,zeta,tn,eta,to,theta,tp,iota,' +
17114		'tq,kappa,tr,lambda,ts,mu,tt,nu,tu,xi,tv,omicron,u0,pi,u1,rho,u2,sigmaf,u3,sigma,u4,tau,' +
17115		'u5,upsilon,u6,phi,u7,chi,u8,psi,u9,omega,uh,thetasym,ui,upsih,um,piv,812,bull,816,hellip,' +
17116		'81i,prime,81j,Prime,81u,oline,824,frasl,88o,weierp,88h,image,88s,real,892,trade,89l,alefsym,' +
17117		'8cg,larr,8ch,uarr,8ci,rarr,8cj,darr,8ck,harr,8dl,crarr,8eg,lArr,8eh,uArr,8ei,rArr,8ej,dArr,' +
17118		'8ek,hArr,8g0,forall,8g2,part,8g3,exist,8g5,empty,8g7,nabla,8g8,isin,8g9,notin,8gb,ni,8gf,prod,' +
17119		'8gh,sum,8gi,minus,8gn,lowast,8gq,radic,8gt,prop,8gu,infin,8h0,ang,8h7,and,8h8,or,8h9,cap,8ha,cup,' +
17120		'8hb,int,8hk,there4,8hs,sim,8i5,cong,8i8,asymp,8j0,ne,8j1,equiv,8j4,le,8j5,ge,8k2,sub,8k3,sup,8
17120k4,' +
17121		'nsub,8k6,sube,8k7,supe,8kl,oplus,8kn,otimes,8l5,perp,8m5,sdot,8o8,lceil,8o9,rceil,8oa,lfloor,8ob,' +
17122		'rfloor,8p9,lang,8pa,rang,9ea,loz,9j0,spades,9j3,clubs,9j5,hearts,9j6,diams,ai,OElig,aj,oelig,b0,' +
17123		'Scaron,b1,scaron,bo,Yuml,m6,circ,ms,tilde,802,ensp,803,emsp,809,thinsp,80c,zwnj,80d,zwj,80e,lrm,' +
17124		'80f,rlm,80j,ndash,80k,mdash,80o,lsquo,80p,rsquo,80q,sbquo,80s,ldquo,80t,rdquo,80u,bdquo,810,dagger,' +
17125		'811,Dagger,81g,permil,81p,lsaquo,81q,rsaquo,85c,euro', 32);
17126
17127	var Entities = {
17128		/**
17129		 * Encodes the specified string using raw entities. This means only the required XML base entities will be encoded.
17130		 *
17131		 * @method encodeRaw
17132		 * @param {String} text Text to encode.
17133		 * @param {Boolean} attr Optional flag to specify if the text is attribute contents.
17134		 * @return {String} Entity encoded text.
17135		 */
17136		encodeRaw: function(text, attr) {
17137			return text.replace(attr ? attrsCharsRegExp : textCharsRegExp, function(chr) {
17138				return baseEntities[chr] || chr;
17139			});
17140		},
17141
17142		/**
17143		 * Encoded the specified text with both the attributes and text entities. This function will produce larger text contents
17144		 * since it doesn't know if the context is within a attribute or text node. This was added for compatibility
17145		 * and is exposed as the DOMUtils.encode function.
17146		 *
17147		 * @method encodeAllRaw
17148		 * @param {String} text Text to encode.
17149		 * @return {String} Entity encoded text.
17150		 */
17151		encodeAllRaw: function(text) {
17152			return ('' + text).replace(rawCharsRegExp, function(chr) {
17153				return baseEntities[chr] || chr;
17154			});
17155		},
17156
17157		/**
17158		 * Encodes the specified string using numeric entities. The core entities will be
17159		 * encoded as named ones but all non lower ascii characters will be encoded into numeric entities.
17160		 *
17161		 * @method encodeNumeric
17162		 * @param {String} text Text to encode.
17163		 * @param {Boolean} attr Optional flag to specify if the text is attribute contents.
17164		 * @return {String} Entity encoded text.
17165		 */
17166		encodeNumeric: function(text, attr) {
17167			return text.replace(attr ? attrsCharsRegExp : textCharsRegExp, function(chr) {
17168				// Multi byte sequence convert it to a single entity
17169				if (chr.length > 1) {
17170					return '&#' + (((chr.charCodeAt(0) - 0xD800) * 0x400) + (chr.charCodeAt(1) - 0xDC00) + 0x10000) + ';';
17171				}
17172
17173				return baseEntities[chr] || '&#' + chr.charCodeAt(0) + ';';
17174			});
17175		},
17176
17177		/**
17178		 * Encodes the specified string using named entities. The core entities will be encoded
17179		 * as named ones but all non lower ascii characters will be encoded into named entities.
17180		 *
17181		 * @method encodeNamed
17182		 * @param {String} text Text to encode.
17183		 * @param {Boolean} attr Optional flag to specify if the text is attribute contents.
17184		 * @param {Object} entities Optional parameter with entities to use.
17185		 * @return {String} Entity encoded text.
17186		 */
17187		encodeNamed: function(text, attr, entities) {
17188			entities = entities || namedEntities;
17189
17190			return text.replace(attr ? attrsCharsRegExp : textCharsRegExp, function(chr) {
17191				return baseEntities[chr] || entities[chr] || chr;
17192			});
17193		},
17194
17195		/**
17196		 * Returns an encode function based on the name(s) and it's optional entities.
17197		 *
17198		 * @method getEncodeFunc
17199		 * @param {String} name Comma separated list of encoders for example named,numeric.
17200		 * @param {String} entities Optional parameter with entities to use instead of the built in set.
17201		 * @return {function} Encode function to be used.
17202		 */
17203		getEncodeFunc: function(name, entities) {
17204			entities = buildEntitiesLookup(entities) || namedEntities;
17205
17206			function encodeNamedAndNumeric(text, attr) {
17207				return text.replace(attr ? attrsCharsRegExp : textCharsRegExp, function(chr) {
17208					return baseEntities[chr] || entities[chr] || '&#' + chr.charCodeAt(0) + ';' || chr;
17209				});
17210			}
17211
17212			function encodeCustomNamed(text, attr) {
17213				return Entities.encodeNamed(text, attr, entities);
17214			}
17215
17216			// Replace + with , to be compatible with previous TinyMCE versions
17217			name = makeMap(name.replace(/\+/g, ','));
17218
17219			// Named and numeric encoder
17220			if (name.named && name.numeric) {
17221				return encodeNamedAndNumeric;
17222			}
17223
17224			// Named encoder
17225			if (name.named) {
17226				// Custom names
17227				if (entities) {
17228					return encodeCustomNamed;
17229				}
17230
17231				return Entities.encodeNamed;
17232			}
17233
17234			// Numeric
17235			if (name.numeric) {
17236				return Entities.encodeNumeric;
17237			}
17238
17239			// Raw encoder
17240			return Entities.encodeRaw;
17241		},
17242
17243		/**
17244		 * Decodes the specified string, this will replace entities with raw UTF characters.
17245		 *
17246		 * @method decode
17247		 * @param {String} text Text to entity decode.
17248		 * @return {String} Entity decoded string.
17249		 */
17250		decode: function(text) {
17251			return text.replace(entityRegExp, function(all, numeric) {
17252				if (numeric) {
17253					if (numeric.charAt(0).toLowerCase() === 'x') {
17254						numeric = parseInt(numeric.substr(1), 16);
17255					} else {
17256						numeric = parseInt(numeric, 10);
17257					}
17258
17259					// Support upper UTF
17260					if (numeric > 0xFFFF) {
17261						numeric -= 0x10000;
17262
17263						return String.fromCharCode(0xD800 + (numeric >> 10), 0xDC00 + (numeric & 0x3FF));
17264					}
17265
17266					return asciiMap[numeric] || String.fromCharCode(numeric);
17267				}
17268
17269				return reverseEntities[all] || namedEntities[all] || nativeDecode(all);
17270			});
17271		}
17272	};
17273
17274	return Entities;
17275});
17276
17277// Included from: js/tinymce/classes/dom/StyleSheetLoader.js
17278
17279/**
17280 * StyleSheetLoader.js
17281 *
17282 * Released under LGPL License.
17283 * Copyright (c) 1999-2015 Ephox Corp. All rights reserved
17284 *
17285 * License: http://www.tinymce.com/license
17286 * Contributing: http://www.tinymce.com/contributing
17287 */
17288
17289/**
17290 * This class handles loading of external stylesheets and fires events when these are loaded.
17291 *
17292 * @class tinymce.dom.StyleSheetLoader
17293 * @private
17294 */
17295define("tinymce/dom/StyleSheetLoader", [
17296	"tinymce/util/Tools",
17297	"tinymce/util/Delay"
17298], function(Tools, Delay) {
17299	"use strict";
17300
17301	return function(document, settings) {
17302		var idCount = 0, loadedStates = {}, maxLoadTime;
17303
17304		settings = settings || {};
17305		maxLoadTime = settings.maxLoadTime || 5000;
17306
17307		function appendToHead(node) {
17308			document.getElementsByTagName('head')[0].appendChild(node);
17309		}
17310
17311		/**
17312		 * Loads the specified css style sheet file and call the loadedCallback once it's finished loading.
17313		 *
17314		 * @method load
17315		 * @param {String} url Url to be loaded.
17316		 * @param {Function} loadedCallback Callback to be executed when loaded.
17317		 * @param {Function} errorCallback Callback to be executed when failed loading.
17318		 */
17319		function load(url, loadedCallback, errorCallback) {
17320			var link, style, startTime, state;
17321
17322			function passed() {
17323				var callbacks = state.passed, i = callbacks.length;
17324
17325				while (i--) {
17326					callbacks[i]();
17327				}
17328
17329				state.status = 2;
17330				state.passed = [];
17331				state.failed = [];
17332			}
17333
17334			function failed() {
17335				var callbacks = state.failed, i = callbacks.length;
17336
17337				while (i--) {
17338					callbacks[i]();
17339				}
17340
17341				state.status = 3;
17342				state.passed = [];
17343				state.failed = [];
17344			}
17345
17346			// Sniffs for older WebKit versions that have the link.onload but a broken one
17347			function isOldWebKit() {
17348				var webKitChunks = navigator.userAgent.match(/WebKit\/(\d*)/);
17349				return !!(webKitChunks && webKitChunks[1] < 536);
17350			}
17351
17352			// Calls the waitCallback until the test returns true or the timeout occurs
17353			function wait(testCallback, waitCallback) {
17354				if (!testCallback()) {
17355					// Wait for timeout
17356					if ((new Date().getTime()) - startTime < maxLoadTime) {
17357						Delay.setTimeout(waitCallback);
17358					} else {
17359						failed();
17360					}
17361				}
17362			}
17363
17364			// Workaround for WebKit that doesn't properly support the onload event for link elements
17365			// Or WebKit that fires the onload event before the StyleSheet is added to the document
17366			function waitForWebKitLinkLoaded() {
17367				wait(function() {
17368					var styleSheets = document.styleSheets, styleSheet, i = styleSheets.length, owner;
17369
17370					while (i--) {
17371						styleSheet = styleSheets[i];
17372						owner = styleSheet.ownerNode ? styleSheet.ownerNode : styleSheet.owningElement;
17373						if (owner && owner.id === link.id) {
17374							passed();
17375							return true;
17376						}
17377					}
17378				}, waitForWebKitLinkLoaded);
17379			}
17380
17381			// Workaround for older Geckos that doesn't have any onload event for StyleSheets
17382			function waitForGeckoLinkLoaded() {
17383				wait(function() {
17384					try {
17385						// Accessing the cssRules will throw an exception until the CSS file is loaded
17386						var cssRules = style.sheet.cssRules;
17387						passed();
17388						return !!cssRules;
17389					} catch (ex) {
17390						// Ignore
17391					}
17392				}, waitForGeckoLinkLoaded);
17393			}
17394
17395			url = Tools._addCacheSuffix(url);
17396
17397			if (!loadedStates[url]) {
17398				state = {
17399					passed: [],
17400					failed: []
17401				};
17402
17403				loadedStates[url] = state;
17404			} else {
17405				state = loadedStates[url];
17406			}
17407
17408			if (loadedCallback) {
17409				state.passed.push(loadedCallback);
17410			}
17411
17412			if (errorCallback) {
17413				state.failed.push(errorCallback);
17414			}
17415
17416			// Is loading wait for it to pass
17417			if (state.status == 1) {
17418				return;
17419			}
17420
17421			// Has finished loading and was success
17422			if (state.status == 2) {
17423				passed();
17424				return;
17425			}
17426
17427			// Has finished loading and was a failure
17428			if (state.status == 3) {
17429				failed();
17430				return;
17431			}
17432
17433			// Start loading
17434			state.status = 1;
17435			link = document.createElement('link');
17436			link.rel = 'stylesheet';
17437			link.type = 'text/css';
17438			link.id = 'u' + (idCount++);
17439			link.async = false;
17440			link.defer = false;
17441			startTime = new Date().getTime();
17442
17443			// Feature detect onload on link element and sniff older webkits since it has an broken onload event
17444			if ("onload" in link && !isOldWebKit()) {
17445				link.onload = waitForWebKitLinkLoaded;
17446				link.onerror = failed;
17447			} else {
17448				// Sniff for old Firefox that doesn't support the onload event on link elements
17449				// TODO: Remove this in the future when everyone uses modern browsers
17450				if (navigator.userAgent.indexOf("Firefox") > 0) {
17451					style = document.createElement('style');
17452					style.textContent = '@import "' + url + '"';
17453					waitForGeckoLinkLoaded();
17454					appendToHead(style);
17455					return;
17456				}
17457
17458				// Use the id owner on older webkits
17459				waitForWebKitLinkLoaded();
17460			}
17461
17462			appendToHead(link);
17463			link.href = url;
17464		}
17465
17466		this.load = load;
17467	};
17468});
17469
17470// Included from: js/tinymce/classes/dom/DOMUtils.js
17471
17472/**
17473 * DOMUtils.js
17474 *
17475 * Released under LGPL License.
17476 * Copyright (c) 1999-2015 Ephox Corp. All rights reserved
17477 *
17478 * License: http://www.tinymce.com/license
17479 * Contributing: http://www.tinymce.com/contributing
17480 */
17481
17482/**
17483 * Utility class for various DOM manipulation and retrieval functions.
17484 *
17485 * @class tinymce.dom.DOMUtils
17486 * @example
17487 * // Add a class to an element by id in the page
17488 * tinymce.DOM.addClass('someid', 'someclass');
17489 *
17490 * // Add a class to an element by id inside the editor
17491 * tinymce.activeEditor.dom.addClass('someid', 'someclass');
17492 */
17493define("tinymce/dom/DOMUtils", [
17494	"tinymce/dom/Sizzle",
17495	"tinymce/dom/DomQuery",
17496	"tinymce/html/Styles",
17497	"tinymce/dom/EventUtils",
17498	"tinymce/dom/TreeWalker",
17499	"tinymce/dom/Range",
17500	"tinymce/html/Entities",
17501	"tinymce/Env",
17502	"tinymce/util/Tools",
17503	"tinymce/dom/StyleSheetLoader"
17504], function(Sizzle, $, Styles, EventUtils, TreeWalker, Range, Entities, Env, Tools, StyleSheetLoader) {
17505	// Shorten names
17506	var each = Tools.each, is = Tools.is, grep = Tools.grep, trim = Tools.trim;
17507	var isIE = Env.ie;
17508	var simpleSelectorRe = /^([a-z0-9],?)+$/i;
17509	var whiteSpaceRegExp = /^[ \t\r\n]*$/;
17510
17511	function setupAttrHooks(domUtils, settings) {
17512		var attrHooks = {}, keepValues = settings.keep_values, keepUrlHook;
17513
17514		keepUrlHook = {
17515			set: function($elm, value, name) {
17516				if (settings.url_converter) {
17517					value = settings.url_converter.call(settings.url_converter_scope || domUtils, value, name, $elm[0]);
17518				}
17519
17520				$elm.attr('data-mce-' + name, value).attr(name, value);
17521			},
17522
17523			get: function($elm, name) {
17524				return $elm.attr('data-mce-' + name) || $elm.attr(name);
17525			}
17526		};
17527
17528		attrHooks = {
17529			style: {
17530				set: function($elm, value) {
17531					if (value !== null && typeof value === 'object') {
17532						$elm.css(value);
17533						return;
17534					}
17535
17536					if (keepValues) {
17537						$elm.attr('data-mce-style', value);
17538					}
17539
17540					$elm.attr('style', value);
17541				},
17542
17543				get: function($elm) {
17544					var value = $elm.attr('data-mce-style') || $elm.attr('style');
17545
17546					value = domUtils.serializeStyle(domUtils.parseStyle(value), $elm[0].nodeName);
17547
17548					return value;
17549				}
17550			}
17551		};
17552
17553		if (keepValues) {
17554			attrHooks.href = attrHooks.src = keepUrlHook;
17555		}
17556
17557		return attrHooks;
17558	}
17559
17560	function updateInternalStyleAttr(domUtils, $elm) {
17561		var value = $elm.attr('style');
17562
17563		value = domUtils.serializeStyle(domUtils.parseStyle(value), $elm[0].nodeName);
17564
17565		if (!value) {
17566			value = null;
17567		}
17568
17569		$elm.attr('data-mce-style', value);
17570	}
17571
17572	function nodeIndex(node, normalized) {
17573		var idx = 0, lastNodeType, nodeType;
17574
17575		if (node) {
17576			for (lastNodeType = node.nodeType, node = node.previousSibling; node; node = node.previousSibling) {
17577				nodeType = node.nodeType;
17578
17579				// Normalize text nodes
17580				if (normalized && nodeType == 3) {
17581					if (nodeType == lastNodeType || !node.nodeValue.length) {
17582						continue;
17583					}
17584				}
17585				idx++;
17586				lastNodeType = nodeType;
17587			}
17588		}
17589
17590		return idx;
17591	}
17592
17593	/**
17594	 * Constructs a new DOMUtils instance. Consult the Wiki for more details on settings etc for this class.
17595	 *
17596	 * @constructor
17597	 * @method DOMUtils
17598	 * @param {Document} doc Document reference to bind the utility class to.
17599	 * @param {settings} settings Optional settings collection.
17600	 */
17601	function DOMUtils(doc, settings) {
17602		var self = this, blockElementsMap;
17603
17604		self.doc = doc;
17605		self.win = window;
17606		self.files = {};
17607		self.counter = 0;
17608		self.stdMode = !isIE || doc.documentMode >= 8;
17609		self.boxModel = !isIE || doc.compatMode == "CSS1Compat" || self.stdMode;
17610		self.styleSheetLoader = new StyleSheetLoader(doc);
17611		self.boundEvents = [];
17612		self.settings = settings = settings || {};
17613		self.schema = settings.schema;
17614		self.styles = new Styles({
17615			url_converter: settings.url_converter,
17616			url_converter_scope: settings.url_converter_scope
17617		}, settings.schema);
17618
17619		self.fixDoc(doc);
17620		self.events = settings.ownEvents ? new EventUtils(settings.proxy) : EventUtils.Event;
17621		self.attrHooks = setupAttrHooks(self, settings);
17622		blockElementsMap = settings.schema ? settings.schema.getBlockElements() : {};
17623		self.$ = $.overrideDefaults(function() {
17624			return {
17625				context: doc,
17626				element: self.getRoot()
17627			};
17628		});
17629
17630		/**
17631		 * Returns true/false if the specified element is a block element or not.
17632		 *
17633		 * @method isBlock
17634		 * @param {Node/String} node Element/Node to check.
17635		 * @return {Boolean} True/False state if the node is a block element or not.
17636		 */
17637		self.isBlock = function(node) {
17638			// Fix for #5446
17639			if (!node) {
17640				return false;
17641			}
17642
17643			// This function is called in module pattern style since it might be executed with the wrong this scope
17644			var type = node.nodeType;
17645
17646			// If it's a node then check the type and use the nodeName
17647			if (type) {
17648				return !!(type === 1 && blockElementsMap[node.nodeName]);
17649			}
17650
17651			return !!blockElementsMap[node];
17652		};
17653	}
17654
17655	DOMUtils.prototype = {
17656		$$: function(elm) {
17657			if (typeof elm == 'string') {
17658				elm = this.get(elm);
17659			}
17660
17661			return this.$(elm);
17662		},
17663
17664		root: null,
17665
17666		fixDoc: function(doc) {
17667			var settings = this.settings, name;
17668
17669			if (isIE && settings.schema) {
17670				// Add missing HTML 4/5 elements to IE
17671				('abbr article aside audio canvas ' +
17672				'details figcaption figure footer ' +
17673				'header hgroup mark menu meter nav ' +
17674				'output progress section summary ' +
17675				'time video').replace(/\w+/g, function(name) {
17676					doc.createElement(name);
17677				});
17678
17679				// Create all custom elements
17680				for (name in settings.schema.getCustomElements()) {
17681					doc.createElement(name);
17682				}
17683			}
17684		},
17685
17686		clone: function(node, deep) {
17687			var self = this, clone, doc;
17688
17689			// TODO: Add feature detection here in the future
17690			if (!isIE || node.nodeType !== 1 || deep) {
17691				return node.cloneNode(deep);
17692			}
17693
17694			doc = self.doc;
17695
17696			// Make a HTML5 safe shallow copy
17697			if (!deep) {
17698				clone = doc.createElement(node.nodeName);
17699
17700				// Copy attribs
17701				each(self.getAttribs(node), function(attr) {
17702					self.setAttrib(clone, attr.nodeName, self.getAttrib(node, attr.nodeName));
17703				});
17704
17705				return clone;
17706			}
17707
17708			return clone.firstChild;
17709		},
17710
17711		/**
17712		 * Returns the root node of the document. This is normally the body but might be a DIV. Parents like getParent will not
17713		 * go above the point of this root node.
17714		 *
17715		 * @method getRoot
17716		 * @return {Element} Root element for the utility class.
17717		 */
17718		getRoot: function() {
17719			var self = this;
17720
17721			return self.settings.root_element || self.doc.body;
17722		},
17723
17724		/**
17725		 * Returns the viewport of the window.
17726		 *
17727		 * @method getViewPort
17728		 * @param {Window} win Optional window to get viewport of.
17729		 * @return {Object} Viewport object with fields x, y, w and h.
17730		 */
17731		getViewPort: function(win) {
17732			var doc, rootElm;
17733
17734			win = !win ? this.win : win;
17735			doc = win.document;
17736			rootElm = this.boxModel ? doc.documentElement : doc.body;
17737
17738			// Returns viewport size excluding scrollbars
17739			return {
17740				x: win.pageXOffset || rootElm.scrollLeft,
17741				y: win.pageYOffset || rootElm.scrollTop,
17742				w: win.innerWidth || rootElm.clientWidth,
17743				h: win.innerHeight || rootElm.clientHeight
17744			};
17745		},
17746
17747		/**
17748		 * Returns the rectangle for a specific element.
17749		 *
17750		 * @method getRect
17751		 * @param {Element/String} elm Element object or element ID to get rectangle from.
17752		 * @return {object} Rectangle for specified element object with x, y, w, h fields.
17753		 */
17754		getRect: function(elm) {
17755			var self = this, pos, size;
17756
17757			elm = self.get(elm);
17758			pos = self.getPos(elm);
17759			size = self.getSize(elm);
17760
17761			return {
17762				x: pos.x, y: pos.y,
17763				w: size.w, h: size.h
17764			};
17765		},
17766
17767		/**
17768		 * Returns the size dimensions of the specified element.
17769		 *
17770		 * @method getSize
17771		 * @param {Element/String} elm Element object or element ID to get rectangle from.
17772		 * @return {object} Rectangle for specified element object with w, h fields.
17773		 */
17774		getSize: function(elm) {
17775			var self = this, w, h;
17776
17777			elm = self.get(elm);
17778			w = self.getStyle(elm, 'width');
17779			h = self.getStyle(elm, 'height');
17780
17781			// Non pixel value, then force offset/clientWidth
17782			if (w.indexOf('px') === -1) {
17783				w = 0;
17784			}
17785
17786			// Non pixel value, then force offset/clientWidth
17787			if (h.indexOf('px') === -1) {
17788				h = 0;
17789			}
17790
17791			return {
17792				w: parseInt(w, 10) || elm.offsetWidth || elm.clientWidth,
17793				h: parseInt(h, 10) || elm.offsetHeight || elm.clientHeight
17794			};
17795		},
17796
17797		/**
17798		 * Returns a node by the specified selector function. This function will
17799		 * loop through all parent nodes and call the specified function for each node.
17800		 * If the function then returns true indicating that it has found what it was looking for, the loop execution will then end
17801		 * and the node it found will be returned.
17802		 *
17803		 * @method getParent
17804		 * @param {Node/String}
17804 node DOM node to search parents on or ID string.
17805		 * @param {function} selector Selection function or CSS selector to execute on each node.
17806		 * @param {Node} root Optional root element, never go below this point.
17807		 * @return {Node} DOM Node or null if it wasn't found.
17808		 */
17809		getParent: function(node, selector, root) {
17810			return this.getParents(node, selector, root, false);
17811		},
17812
17813		/**
17814		 * Returns a node list of all parents matching the specified selector function or pattern.
17815		 * If the function then returns true indicating that it has found what it was looking for and that node will be collected.
17816		 *
17817		 * @method getParents
17818		 * @param {Node/String} node DOM node to search parents on or ID string.
17819		 * @param {function} selector Selection function to execute on each node or CSS pattern.
17820		 * @param {Node} root Optional root element, never go below this point.
17821		 * @return {Array} Array of nodes or null if it wasn't found.
17822		 */
17823		getParents: function(node, selector, root, collect) {
17824			var self = this, selectorVal, result = [];
17825
17826			node = self.get(node);
17827			collect = collect === undefined;
17828
17829			// Default root on inline mode
17830			root = root || (self.getRoot().nodeName != 'BODY' ? self.getRoot().parentNode : null);
17831
17832			// Wrap node name as func
17833			if (is(selector, 'string')) {
17834				selectorVal = selector;
17835
17836				if (selector === '*') {
17837					selector = function(node) {
17838						return node.nodeType == 1;
17839					};
17840				} else {
17841					selector = function(node) {
17842						return self.is(node, selectorVal);
17843					};
17844				}
17845			}
17846
17847			while (node) {
17848				if (node == root || !node.nodeType || node.nodeType === 9) {
17849					break;
17850				}
17851
17852				if (!selector || selector(node)) {
17853					if (collect) {
17854						result.push(node);
17855					} else {
17856						return node;
17857					}
17858				}
17859
17860				node = node.parentNode;
17861			}
17862
17863			return collect ? result : null;
17864		},
17865
17866		/**
17867		 * Returns the specified element by ID or the input element if it isn't a string.
17868		 *
17869		 * @method get
17870		 * @param {String/Element} n Element id to look for or element to just pass though.
17871		 * @return {Element} Element matching the specified id or null if it wasn't found.
17872		 */
17873		get: function(elm) {
17874			var name;
17875
17876			if (elm && this.doc && typeof elm == 'string') {
17877				name = elm;
17878				elm = this.doc.getElementById(elm);
17879
17880				// IE and Opera returns meta elements when they match the specified input ID, but getElementsByName seems to do the trick
17881				if (elm && elm.id !== name) {
17882					return this.doc.getElementsByName(name)[1];
17883				}
17884			}
17885
17886			return elm;
17887		},
17888
17889		/**
17890		 * Returns the next node that matches selector or function
17891		 *
17892		 * @method getNext
17893		 * @param {Node} node Node to find siblings from.
17894		 * @param {String/function} selector Selector CSS expression or function.
17895		 * @return {Node} Next node item matching the selector or null if it wasn't found.
17896		 */
17897		getNext: function(node, selector) {
17898			return this._findSib(node, selector, 'nextSibling');
17899		},
17900
17901		/**
17902		 * Returns the previous node that matches selector or function
17903		 *
17904		 * @method getPrev
17905		 * @param {Node} node Node to find siblings from.
17906		 * @param {String/function} selector Selector CSS expression or function.
17907		 * @return {Node} Previous node item matching the selector or null if it wasn't found.
17908		 */
17909		getPrev: function(node, selector) {
17910			return this._findSib(node, selector, 'previousSibling');
17911		},
17912
17913		// #ifndef jquery
17914
17915		/**
17916		 * Selects specific elements by a CSS level 3 pattern. For example "div#a1 p.test".
17917		 * This function is optimized for the most common patterns needed in TinyMCE but it also performs well enough
17918		 * on more complex patterns.
17919		 *
17920		 * @method select
17921		 * @param {String} selector CSS level 3 pattern to select/find elements by.
17922		 * @param {Object} scope Optional root element/scope element to search in.
17923		 * @return {Array} Array with all matched elements.
17924		 * @example
17925		 * // Adds a class to all paragraphs in the currently active editor
17926		 * tinymce.activeEditor.dom.addClass(tinymce.activeEditor.dom.select('p'), 'someclass');
17927		 *
17928		 * // Adds a class to all spans that have the test class in the currently active editor
17929		 * tinymce.activeEditor.dom.addClass(tinymce.activeEditor.dom.select('span.test'), 'someclass')
17930		 */
17931		select: function(selector, scope) {
17932			var self = this;
17933
17934			/*eslint new-cap:0 */
17935			return Sizzle(selector, self.get(scope) || self.settings.root_element || self.doc, []);
17936		},
17937
17938		/**
17939		 * Returns true/false if the specified element matches the specified css pattern.
17940		 *
17941		 * @method is
17942		 * @param {Node/NodeList} elm DOM node to match or an array of nodes to match.
17943		 * @param {String} selector CSS pattern to match the element against.
17944		 */
17945		is: function(elm, selector) {
17946			var i;
17947
17948			// If it isn't an array then try to do some simple selectors instead of Sizzle for to boost performance
17949			if (elm.length === undefined) {
17950				// Simple all selector
17951				if (selector === '*') {
17952					return elm.nodeType == 1;
17953				}
17954
17955				// Simple selector just elements
17956				if (simpleSelectorRe.test(selector)) {
17957					selector = selector.toLowerCase().split(/,/);
17958					elm = elm.nodeName.toLowerCase();
17959
17960					for (i = selector.length - 1; i >= 0; i--) {
17961						if (selector[i] == elm) {
17962							return true;
17963						}
17964					}
17965
17966					return false;
17967				}
17968			}
17969
17970			// Is non element
17971			if (elm.nodeType && elm.nodeType != 1) {
17972				return false;
17973			}
17974
17975			var elms = elm.nodeType ? [elm] : elm;
17976
17977			/*eslint new-cap:0 */
17978			return Sizzle(selector, elms[0].ownerDocument || elms[0], null, elms).length > 0;
17979		},
17980
17981		// #endif
17982
17983		/**
17984		 * Adds the specified element to another element or elements.
17985		 *
17986		 * @method add
17987		 * @param {String/Element/Array} parentElm Element id string, DOM node element or array of ids or elements to add to.
17988		 * @param {String/Element} name Name of new element to add or existing element to add.
17989		 * @param {Object} attrs Optional object collection with arguments to add to the new element(s).
17990		 * @param {String} html Optional inner HTML contents to add for each element.
17991		 * @param {Boolean} create Optional flag if the element should be created or added.
17992		 * @return {Element/Array} Element that got created, or an array of created elements if multiple input elements
17993		 * were passed in.
17994		 * @example
17995		 * // Adds a new paragraph to the end of the active editor
17996		 * tinymce.activeEditor.dom.add(tinymce.activeEditor.getBody(), 'p', {title: 'my title'}, 'Some content');
17997		 */
17998		add: function(parentElm, name, attrs, html, create) {
17999			var self = this;
18000
18001			return this.run(parentElm, function(parentElm) {
18002				var newElm;
18003
18004				newElm = is(name, 'string') ? self.doc.createElement(name) : name;
18005				self.setAttribs(newElm, attrs);
18006
18007				if (html) {
18008					if (html.nodeType) {
18009						newElm.appendChild(html);
18010					} else {
18011						self.setHTML(newElm, html);
18012					}
18013				}
18014
18015				return !create ? parentElm.appendChild(newElm) : newElm;
18016			});
18017		},
18018
18019		/**
18020		 * Creates a new element.
18021		 *
18022		 * @method create
18023		 * @param {String} name Name of new element.
18024		 * @param {Object} attrs Optional object name/value collection with element attributes.
18025		 * @param {String} html Optional HTML string to set as inner HTML of the element.
18026		 * @return {Element} HTML DOM node element that got created.
18027		 * @example
18028		 * // Adds an element where the caret/selection is in the active editor
18029		 * var el = tinymce.activeEditor.dom.create('div', {id: 'test', 'class': 'myclass'}, 'some content');
18030		 * tinymce.activeEditor.selection.setNode(el);
18031		 */
18032		create: function(name, attrs, html) {
18033			return this.add(this.doc.createElement(name), name, attrs, html, 1);
18034		},
18035
18036		/**
18037		 * Creates HTML string for element. The element will be closed unless an empty inner HTML string is passed in.
18038		 *
18039		 * @method createHTML
18040		 * @param {String} name Name of new element.
18041		 * @param {Object} attrs Optional object name/value collection with element attributes.
18042		 * @param {String} html Optional HTML string to set as inner HTML of the element.
18043		 * @return {String} String with new HTML element, for example: <a href="#">test</a>.
18044		 * @example
18045		 * // Creates a html chunk and inserts it at the current selection/caret location
18046		 * tinymce.activeEditor.selection.setContent(tinymce.activeEditor.dom.createHTML('a', {href: 'test.html'}, 'some line'));
18047		 */
18048		createHTML: function(name, attrs, html) {
18049			var outHtml = '', key;
18050
18051			outHtml += '<' + name;
18052
18053			for (key in attrs) {
18054				if (attrs.hasOwnProperty(key) && attrs[key] !== null && typeof attrs[key] != 'undefined') {
18055					outHtml += ' ' + key + '="' + this.encode(attrs[key]) + '"';
18056				}
18057			}
18058
18059			// A call to tinymce.is doesn't work for some odd reason on IE9 possible bug inside their JS runtime
18060			if (typeof html != "undefined") {
18061				return outHtml + '>' + html + '</' + name + '>';
18062			}
18063
18064			return outHtml + ' />';
18065		},
18066
18067		/**
18068		 * Creates a document fragment out of the specified HTML string.
18069		 *
18070		 * @method createFragment
18071		 * @param {String} html Html string to create fragment from.
18072		 * @return {DocumentFragment} Document fragment node.
18073		 */
18074		createFragment: function(html) {
18075			var frag, node, doc = this.doc, container;
18076
18077			container = doc.createElement("div");
18078			frag = doc.createDocumentFragment();
18079
18080			if (html) {
18081				container.innerHTML = html;
18082			}
18083
18084			while ((node = container.firstChild)) {
18085				frag.appendChild(node);
18086			}
18087
18088			return frag;
18089		},
18090
18091		/**
18092		 * Removes/deletes the specified element(s) from the DOM.
18093		 *
18094		 * @method remove
18095		 * @param {String/Element/Array} node ID of element or DOM element object or array containing multiple elements/ids.
18096		 * @param {Boolean} keepChildren Optional state to keep children or not. If set to true all children will be
18097		 * placed at the location of the removed element.
18098		 * @return {Element/Array} HTML DOM element that got removed, or an array of removed elements if multiple input elements
18099		 * were passed in.
18100		 * @example
18101		 * // Removes all paragraphs in the active editor
18102		 * tinymce.activeEditor.dom.remove(tinymce.activeEditor.dom.select('p'));
18103		 *
18104		 * // Removes an element by id in the document
18105		 * tinymce.DOM.remove('mydiv');
18106		 */
18107		remove: function(node, keepChildren) {
18108			node = this.$$(node);
18109
18110			if (keepChildren) {
18111				node.each(function() {
18112					var child;
18113
18114					while ((child = this.firstChild)) {
18115						if (child.nodeType == 3 && child.data.length === 0) {
18116							this.removeChild(child);
18117						} else {
18118							this.parentNode.insertBefore(child, this);
18119						}
18120					}
18121				}).remove();
18122			} else {
18123				node.remove();
18124			}
18125
18126			return node.length > 1 ? node.toArray() : node[0];
18127		},
18128
18129		/**
18130		 * Sets the CSS style value on a HTML element. The name can be a camelcase string
18131		 * or the CSS style name like background-color.
18132		 *
18133		 * @method setStyle
18134		 * @param {String/Element/Array} elm HTML element/Array of elements to set CSS style value on.
18135		 * @param {String} name Name of the style value to set.
18136		 * @param {String} value Value to set on the style.
18137		 * @example
18138		 * // Sets a style value on all paragraphs in the currently active editor
18139		 * tinymce.activeEditor.dom.setStyle(tinymce.activeEditor.dom.select('p'), 'background-color', 'red');
18140		 *
18141		 * // Sets a style value to an element by id in the current document
18142		 * tinymce.DOM.setStyle('mydiv', 'background-color', 'red');
18143		 */
18144		setStyle: function(elm, name, value) {
18145			elm = this.$$(elm).css(name, value);
18146
18147			if (this.settings.update_styles) {
18148				updateInternalStyleAttr(this, elm);
18149			}
18150		},
18151
18152		/**
18153		 * Returns the current style or runtime/computed value of an element.
18154		 *
18155		 * @method getStyle
18156		 * @param {String/Element} elm HTML element or element id string to get style from.
18157		 * @param {String} name Style name to return.
18158		 * @param {Boolean} computed Computed style.
18159		 * @return {String} Current style or computed style value of an element.
18160		 */
18161		getStyle: function(elm, name, computed) {
18162			elm = this.$$(elm);
18163
18164			if (computed) {
18165				return elm.css(name);
18166			}
18167
18168			// Camelcase it, if needed
18169			name = name.replace(/-(\D)/g, function(a, b) {
18170				return b.toUpperCase();
18171			});
18172
18173			if (name == 'float') {
18174				name = Env.ie && Env.ie < 12 ? 'styleFloat' : 'cssFloat';
18175			}
18176
18177			return elm[0] && elm[0].style ? elm[0].style[name] : undefined;
18178		},
18179
18180		/**
18181		 * Sets multiple styles on the specified element(s).
18182		 *
18183		 * @method setStyles
18184		 * @param {Element/String/Array} elm DOM element, element id string or array of elements/ids to set styles on.
18185		 * @param {Object} styles Name/Value collection of style items to add to the element(s).
18186		 * @example
18187		 * // Sets styles on all paragraphs in the currently active editor
18188		 * tinymce.activeEditor.dom.setStyles(tinymce.activeEditor.dom.select('p'), {'background-color': 'red', 'color': 'green'});
18189		 *
18190		 * // Sets styles to an element by id in the current document
18191		 * tinymce.DOM.setStyles('mydiv', {'background-color': 'red', 'color': 'green'});
18192		 */
18193		setStyles: function(elm, styles) {
18194			elm = this.$$(elm).css(styles);
18195
18196			if (this.settings.update_styles) {
18197				updateInternalStyleAttr(this, elm);
18198			}
18199		},
18200
18201		/**
18202		 * Removes all attributes from an element or elements.
18203		 *
18204		 * @method removeAllAttribs
18205		 * @param {Element/String/Array} e DOM element, element id string or array of elements/ids to remove attributes from.
18206		 */
18207		removeAllAttribs: function(e) {
18208			return this.run(e, function(e) {
18209				var i, attrs = e.attributes;
18210				for (i = attrs.length - 1; i >= 0; i--) {
18211					e.removeAttributeNode(attrs.item(i));
18212				}
18213			});
18214		},
18215
18216		/**
18217		 * Sets the specified attribute of an element or elements.
18218		 *
18219		 * @method setAttrib
18220		 * @param {Element/String/Array} elm DOM element, element id string or array of elements/ids to set attribute on.
18221		 * @param {String} name Name of attribute to set.
18222		 * @param {String} value Value to set on the attribute - if this value is falsy like null, 0 or '' it will remove
18223		 * the attribute instead.
18224		 * @example
18225		 * // Sets class attribute on all paragraphs in the active editor
18226		 * tinymce.activeEditor.dom.setAttrib(tinymce.activeEditor.dom.select('p'), 'class', 'myclass');
18227		 *
18228		 * // Sets class attribute on a specific element in the current page
18229		 * tinymce.dom.setAttrib('mydiv', 'class', 'myclass');
18230		 */
18231		setAttrib: function(elm, name, value) {
18232			var self = this, originalValue, hook, settings = self.settings;
18233
18234			if (value === '') {
18235				value = null;
18236			}
18237
18238			elm = self.$$(elm);
18239			originalValue = elm.attr(name);
18240
18241			if (!elm.length) {
18242				return;
18243			}
18244
18245			hook = self.attrHooks[name];
18246			if (hook && hook.set) {
18247				hook.set(elm, value, name);
18248			} else {
18249				elm.attr(name, value);
18250			}
18251
18252			if (originalValue != value && settings.onSetAttrib) {
18253				settings.onSetAttrib({
18254					attrElm: elm,
18255					attrName: name,
18256					attrValue: value
18257				});
18258			}
18259		},
18260
18261		/**
18262		 * Sets two or more specified attributes of an element or elements.
18263		 *
18264		 * @method setAttribs
18265		 * @param {Element/String/Array} elm DOM element, element id string or array of elements/ids to set attributes on.
18266		 * @param {Object} attrs Name/Value collection of attribute items to add to the element(s).
18267		 * @example
18268		 * // Sets class and title attributes on all paragraphs in the active editor
18269		 * tinymce.activeEditor.dom.setAttribs(tinymce.activeEditor.dom.select('p'), {'class': 'myclass', title: 'some title'});
18270		 *
18271		 * // Sets class and title attributes on a specific element in the current page
18272		 * tinymce.DOM.setAttribs('mydiv', {'class': 'myclass', title: 'some title'});
18273		 */
18274		setAttribs: function(elm, attrs) {
18275			var self = this;
18276
18277			self.$$(elm).each(function(i, node) {
18278				each(attrs, function(value, name) {
18279					self.setAttrib(node, name, value);
18280				});
18281			});
18282		},
18283
18284		/**
18285		 * Returns the specified attribute by name.
18286		 *
18287		 * @method getAttrib
18288		 * @param {String/Element} elm Element string id or DOM element to get attribute from.
18289		 * @param {String} name Name of attribute to get.
18290		 * @param {String} defaultVal Optional default value to return if the attribute didn't exist.
18291		 * @return {String} Attribute value string, default value or null if the attribute wasn't found.
18292		 */
18293		getAttrib: function(elm, name, defaultVal) {
18294			var self = this, hook, value;
18295
18296			elm = self.$$(elm);
18297
18298			if (elm.length) {
18299				hook = self.attrHooks[name];
18300
18301				if (hook && hook.get) {
18302					value = hook.get(elm, name);
18303				} else {
18304					value = elm.attr(name);
18305				}
18306			}
18307
18308			if (typeof value == 'undefined') {
18309				value = defaultVal || '';
18310			}
18311
18312			return value;
18313		},
18314
18315		/**
18316		 * Returns the absolute x, y position of a node. The position will be returned in an object with x, y fields.
18317		 *
18318		 * @method getPos
18319		 * @param {Element/String} elm HTML element or element id to get x, y position from.
18320		 * @param {Element} rootElm Optional root element to stop calculations at.
18321		 * @return {object} Absolute position of the specified element object with x, y fields.
18322		 */
18323		getPos: function(elm, rootElm) {
18324			var self = this, x = 0, y = 0, offsetParent, doc = self.doc, body = doc.body, pos;
18325
18326			elm = self.get(elm);
18327			rootElm = rootElm || body;
18328
18329			if (elm) {
18330				// Use getBoundingClientRect if it exists since it's faster than looping offset nodes
18331				// Fallback to offsetParent calculations if the body isn't static better since it stops at the body root
18332				if (rootElm === body && elm.getBoundingClientRect && $(body).css('position') === 'static') {
18333					pos = elm.getBoundingClientRect();
18334					rootElm = self.boxModel ? doc.documentElement : body;
18335
18336					// Add scroll offsets from documentElement or body since IE with the wrong box model will use d.body and so do WebKit
18337					// Also remove the body/documentelement clientTop/clientLeft on IE 6, 7 since they offset the position
18338					x = pos.left + (doc.documentElement.scrollLeft || body.scrollLeft) - rootElm.clientLeft;
18339					y = pos.top + (doc.documentElement.scrollTop || body.scrollTop) - rootElm.clientTop;
18340
18341					return {x: x, y: y};
18342				}
18343
18344				offsetParent = elm;
18345				while (offsetParent && offsetParent != rootElm && offsetParent.nodeType) {
18346					x += offsetParent.offsetLeft || 0;
18347					y += offsetParent.offsetTop || 0;
18348					offsetParent = offsetParent.offsetParent;
18349				}
18350
18351				offsetParent = elm.parentNode;
18352				while (offsetParent && offsetParent != rootElm && offsetParent.nodeType) {
18353					x -= offsetParent.scrollLeft || 0;
18354					y -= offsetParent.scrollTop || 0;
18355					offsetParent = offsetParent.parentNode;
18356				}
18357			}
18358
18359			return {x: x, y: y};
18360		},
18361
18362		/**
18363		 * Parses the specified style value into an object collection. This parser will also
18364		 * merge and remove any redundant items that browsers might have added. It will also convert non-hex
18365		 * colors to hex values. Urls inside the styles will also be converted to absolute/relative based on settings.
18366		 *
18367		 * @method parseStyle
18368		 * @param {String} cssText Style value to parse, for example: border:1px solid red;.
18369		 * @return {Object} Object representation of that style, for example: {border: '1px solid red'}
18370		 */
18371		parseStyle: function(cssText) {
18372			return this.styles.parse(cssText);
18373		},
18374
18375		/**
18376		 * Serializes the specified style object into a string.
18377		 *
18378		 * @method serializeStyle
18379		 * @param {Object} styles Object to serialize as string, for example: {border: '1px solid red'}
18380		 * @param {String} name Optional element name.
18381		 * @return {String} String representation of the style object, for example: border: 1px solid red.
18382		 */
18383		serializeStyle: function(styles, name) {
18384			return this.styles.serialize(styles, name);
18385		},
18386
18387		/**
18388		 * Adds a style element at the top of the document with the specified cssText content.
18389		 *
18390		 * @method addStyle
18391		 * @param {String} cssText CSS Text style to add to top of head of document.
18392		 */
18393		addStyle: function(cssText) {
18394			var self = this, doc = self.doc, head, styleElm;
18395
18396			// Prevent inline from loading the same styles twice
18397			if (self !== DOMUtils.DOM && doc === document) {
18398				var addedStyles = DOMUtils.DOM.addedStyles;
18399
18400				addedStyles = addedStyles || [];
18401				if (addedStyles[cssText]) {
18402					return;
18403				}
18404
18405				addedStyles[cssText] = true;
18406				DOMUtils.DOM.addedStyles = addedStyles;
18407			}
18408
18409			// Create style element if needed
18410			styleElm = doc.getElementById('mceDefaultStyles');
18411			if (!styleElm) {
18412				styleElm = doc.createElement('style');
18413				styleElm.id = 'mceDefaultStyles';
18414				styleElm.type = 'text/css';
18415
18416				head = doc.getElementsByTagName('head')[0];
18417				if (head.firstChild) {
18418					head.insertBefore(styleElm, head.firstChild);
18419				} else {
18420					head.appendChild(styleElm);
18421				}
18422			}
18423
18424			// Append style data to old or new style element
18425			if (styleElm.styleSheet) {
18426				styleElm.styleSheet.cssText += cssText;
18427			} else {
18428				styleElm.appendChild(doc.createTextNode(cssText));
18429			}
18430		},
18431
18432		/**
18433		 * Imports/loads the specified CSS file into the document bound to the class.
18434		 *
18435		 * @method loadCSS
18436		 * @param {String} url URL to CSS file to load.
18437		 * @example
18438		 * // Loads a CSS file dynamically into the current document
18439		 * tinymce.DOM.loadCSS('somepath/some.css');
18440		 *
18441		 * // Loads a CSS file into the currently active editor instance
18442		 * tinymce.activeEditor.dom.loadCSS('somepath/some.css');
18443		 *
18444		 * // Loads a CSS file into an editor instance by id
18445		 * tinymce.get('someid').dom.loadCSS('somepath/some.css');
18446		 *
18447		 * // Loads multiple CSS files into the current document
18448		 * tinymce.DOM.loadCSS('somepath/some.css,somepath/someother.css');
18449		 */
18450		loadCSS: function(url) {
18451			var self = this, doc = self.doc, head;
18452
18453			// Prevent inline from loading the same CSS file twice
18454			if (self !== DOMUtils.DOM && doc === document) {
18455				DOMUtils.DOM.loadCSS(url);
18456				return;
18457			}
18458
18459			if (!url) {
18460				url = '';
18461			}
18462
18463			head = doc.getElementsByTagName('head')[0];
18464
18465			each(url.split(','), function(url) {
18466				var link;
18467
18468				url = Tools._addCacheSuffix(url);
18469
18470				if (self.files[url]) {
18471					return;
18472				}
18473
18474				self.files[url] = true;
18475				link = self.create('link', {rel: 'stylesheet', href: url});
18476
18477				// IE 8 has a bug where dynamically loading stylesheets would produce a 1 item remaining bug
18478				// This fix seems to resolve that issue by recalcing the document once a stylesheet finishes loading
18479				// It's ugly but it seems to work fine.
18480				if (isIE && doc.documentMode && doc.recalc) {
18481					link.onload = function() {
18482						if (doc.recalc) {
18483							doc.recalc();
18484						}
18485
18486						link.onload = null;
18487					};
18488				}
18489
18490				head.appendChild(link);
18491			});
18492		},
18493
18494		/**
18495		 * Adds a class to the specified element or elements.
18496		 *
18497		 * @method addClass
18498		 * @param {String/Element/Array} elm Element ID string or DOM element or array with elements or IDs.
18499		 * @param {String} cls Class name to add to each element.
18500		 * @return {String/Array}
18500 String with new class value or array with new class values for all elements.
18501		 * @example
18502		 * // Adds a class to all paragraphs in the active editor
18503		 * tinymce.activeEditor.dom.addClass(tinymce.activeEditor.dom.select('p'), 'myclass');
18504		 *
18505		 * // Adds a class to a specific element in the current page
18506		 * tinymce.DOM.addClass('mydiv', 'myclass');
18507		 */
18508		addClass: function(elm, cls) {
18509			this.$$(elm).addClass(cls);
18510		},
18511
18512		/**
18513		 * Removes a class from the specified element or elements.
18514		 *
18515		 * @method removeClass
18516		 * @param {String/Element/Array} elm Element ID string or DOM element or array with elements or IDs.
18517		 * @param {String} cls Class name to remove from each element.
18518		 * @return {String/Array} String of remaining class name(s), or an array of strings if multiple input elements
18519		 * were passed in.
18520		 * @example
18521		 * // Removes a class from all paragraphs in the active editor
18522		 * tinymce.activeEditor.dom.removeClass(tinymce.activeEditor.dom.select('p'), 'myclass');
18523		 *
18524		 * // Removes a class from a specific element in the current page
18525		 * tinymce.DOM.removeClass('mydiv', 'myclass');
18526		 */
18527		removeClass: function(elm, cls) {
18528			this.toggleClass(elm, cls, false);
18529		},
18530
18531		/**
18532		 * Returns true if the specified element has the specified class.
18533		 *
18534		 * @method hasClass
18535		 * @param {String/Element} elm HTML element or element id string to check CSS class on.
18536		 * @param {String} cls CSS class to check for.
18537		 * @return {Boolean} true/false if the specified element has the specified class.
18538		 */
18539		hasClass: function(elm, cls) {
18540			return this.$$(elm).hasClass(cls);
18541		},
18542
18543		/**
18544		 * Toggles the specified class on/off.
18545		 *
18546		 * @method toggleClass
18547		 * @param {Element} elm Element to toggle class on.
18548		 * @param {[type]} cls Class to toggle on/off.
18549		 * @param {[type]} state Optional state to set.
18550		 */
18551		toggleClass: function(elm, cls, state) {
18552			this.$$(elm).toggleClass(cls, state).each(function() {
18553				if (this.className === '') {
18554					$(this).attr('class', null);
18555				}
18556			});
18557		},
18558
18559		/**
18560		 * Shows the specified element(s) by ID by setting the "display" style.
18561		 *
18562		 * @method show
18563		 * @param {String/Element/Array} elm ID of DOM element or DOM element or array with elements or IDs to show.
18564		 */
18565		show: function(elm) {
18566			this.$$(elm).show();
18567		},
18568
18569		/**
18570		 * Hides the specified element(s) by ID by setting the "display" style.
18571		 *
18572		 * @method hide
18573		 * @param {String/Element/Array} elm ID of DOM element or DOM element or array with elements or IDs to hide.
18574		 * @example
18575		 * // Hides an element by id in the document
18576		 * tinymce.DOM.hide('myid');
18577		 */
18578		hide: function(elm) {
18579			this.$$(elm).hide();
18580		},
18581
18582		/**
18583		 * Returns true/false if the element is hidden or not by checking the "display" style.
18584		 *
18585		 * @method isHidden
18586		 * @param {String/Element} elm Id or element to check display state on.
18587		 * @return {Boolean} true/false if the element is hidden or not.
18588		 */
18589		isHidden: function(elm) {
18590			return this.$$(elm).css('display') == 'none';
18591		},
18592
18593		/**
18594		 * Returns a unique id. This can be useful when generating elements on the fly.
18595		 * This method will not check if the element already exists.
18596		 *
18597		 * @method uniqueId
18598		 * @param {String} prefix Optional prefix to add in front of all ids - defaults to "mce_".
18599		 * @return {String} Unique id.
18600		 */
18601		uniqueId: function(prefix) {
18602			return (!prefix ? 'mce_' : prefix) + (this.counter++);
18603		},
18604
18605		/**
18606		 * Sets the specified HTML content inside the element or elements. The HTML will first be processed. This means
18607		 * URLs will get converted, hex color values fixed etc. Check processHTML for details.
18608		 *
18609		 * @method setHTML
18610		 * @param {Element/String/Array} elm DOM element, element id string or array of elements/ids to set HTML inside of.
18611		 * @param {String} html HTML content to set as inner HTML of the element.
18612		 * @example
18613		 * // Sets the inner HTML of all paragraphs in the active editor
18614		 * tinymce.activeEditor.dom.setHTML(tinymce.activeEditor.dom.select('p'), 'some inner html');
18615		 *
18616		 * // Sets the inner HTML of an element by id in the document
18617		 * tinymce.DOM.setHTML('mydiv', 'some inner html');
18618		 */
18619		setHTML: function(elm, html) {
18620			elm = this.$$(elm);
18621
18622			if (isIE) {
18623				elm.each(function(i, target) {
18624					if (target.canHaveHTML === false) {
18625						return;
18626					}
18627
18628					// Remove all child nodes, IE keeps empty text nodes in DOM
18629					while (target.firstChild) {
18630						target.removeChild(target.firstChild);
18631					}
18632
18633					try {
18634						// IE will remove comments from the beginning
18635						// unless you padd the contents with something
18636						target.innerHTML = '<br>' + html;
18637						target.removeChild(target.firstChild);
18638					} catch (ex) {
18639						// IE sometimes produces an unknown runtime error on innerHTML if it's a div inside a p
18640						$('<div>').html('<br>' + html).contents().slice(1).appendTo(target);
18641					}
18642
18643					return html;
18644				});
18645			} else {
18646				elm.html(html);
18647			}
18648		},
18649
18650		/**
18651		 * Returns the outer HTML of an element.
18652		 *
18653		 * @method getOuterHTML
18654		 * @param {String/Element} elm Element ID or element object to get outer HTML from.
18655		 * @return {String} Outer HTML string.
18656		 * @example
18657		 * tinymce.DOM.getOuterHTML(editorElement);
18658		 * tinymce.activeEditor.getOuterHTML(tinymce.activeEditor.getBody());
18659		 */
18660		getOuterHTML: function(elm) {
18661			elm = this.get(elm);
18662
18663			// Older FF doesn't have outerHTML 3.6 is still used by some orgaizations
18664			return elm.nodeType == 1 && "outerHTML" in elm ? elm.outerHTML : $('<div>').append($(elm).clone()).html();
18665		},
18666
18667		/**
18668		 * Sets the specified outer HTML on an element or elements.
18669		 *
18670		 * @method setOuterHTML
18671		 * @param {Element/String/Array} elm DOM element, element id string or array of elements/ids to set outer HTML on.
18672		 * @param {Object} html HTML code to set as outer value for the element.
18673		 * @example
18674		 * // Sets the outer HTML of all paragraphs in the active editor
18675		 * tinymce.activeEditor.dom.setOuterHTML(tinymce.activeEditor.dom.select('p'), '<div>some html</div>');
18676		 *
18677		 * // Sets the outer HTML of an element by id in the document
18678		 * tinymce.DOM.setOuterHTML('mydiv', '<div>some html</div>');
18679		 */
18680		setOuterHTML: function(elm, html) {
18681			var self = this;
18682
18683			self.$$(elm).each(function() {
18684				try {
18685					// Older FF doesn't have outerHTML 3.6 is still used by some organizations
18686					if ("outerHTML" in this) {
18687						this.outerHTML = html;
18688						return;
18689					}
18690				} catch (ex) {
18691					// Ignore
18692				}
18693
18694				// OuterHTML for IE it sometimes produces an "unknown runtime error"
18695				self.remove($(this).html(html), true);
18696			});
18697		},
18698
18699		/**
18700		 * Entity decodes a string. This method decodes any HTML entities, such as &aring;.
18701		 *
18702		 * @method decode
18703		 * @param {String} s String to decode entities on.
18704		 * @return {String} Entity decoded string.
18705		 */
18706		decode: Entities.decode,
18707
18708		/**
18709		 * Entity encodes a string. This method encodes the most common entities, such as <>"&.
18710		 *
18711		 * @method encode
18712		 * @param {String} text String to encode with entities.
18713		 * @return {String} Entity encoded string.
18714		 */
18715		encode: Entities.encodeAllRaw,
18716
18717		/**
18718		 * Inserts an element after the reference element.
18719		 *
18720		 * @method insertAfter
18721		 * @param {Element} node Element to insert after the reference.
18722		 * @param {Element/String/Array} referenceNode Reference element, element id or array of elements to insert after.
18723		 * @return {Element/Array} Element that got added or an array with elements.
18724		 */
18725		insertAfter: function(node, referenceNode) {
18726			referenceNode = this.get(referenceNode);
18727
18728			return this.run(node, function(node) {
18729				var parent, nextSibling;
18730
18731				parent = referenceNode.parentNode;
18732				nextSibling = referenceNode.nextSibling;
18733
18734				if (nextSibling) {
18735					parent.insertBefore(node, nextSibling);
18736				} else {
18737					parent.appendChild(node);
18738				}
18739
18740				return node;
18741			});
18742		},
18743
18744		/**
18745		 * Replaces the specified element or elements with the new element specified. The new element will
18746		 * be cloned if multiple input elements are passed in.
18747		 *
18748		 * @method replace
18749		 * @param {Element} newElm New element to replace old ones with.
18750		 * @param {Element/String/Array} oldElm Element DOM node, element id or array of elements or ids to replace.
18751		 * @param {Boolean} keepChildren Optional keep children state, if set to true child nodes from the old object will be added
18752		 * to new ones.
18753		 */
18754		replace: function(newElm, oldElm, keepChildren) {
18755			var self = this;
18756
18757			return self.run(oldElm, function(oldElm) {
18758				if (is(oldElm, 'array')) {
18759					newElm = newElm.cloneNode(true);
18760				}
18761
18762				if (keepChildren) {
18763					each(grep(oldElm.childNodes), function(node) {
18764						newElm.appendChild(node);
18765					});
18766				}
18767
18768				return oldElm.parentNode.replaceChild(newElm, oldElm);
18769			});
18770		},
18771
18772		/**
18773		 * Renames the specified element and keeps its attributes and children.
18774		 *
18775		 * @method rename
18776		 * @param {Element} elm Element to rename.
18777		 * @param {String} name Name of the new element.
18778		 * @return {Element} New element or the old element if it needed renaming.
18779		 */
18780		rename: function(elm, name) {
18781			var self = this, newElm;
18782
18783			if (elm.nodeName != name.toUpperCase()) {
18784				// Rename block element
18785				newElm = self.create(name);
18786
18787				// Copy attribs to new block
18788				each(self.getAttribs(elm), function(attrNode) {
18789					self.setAttrib(newElm, attrNode.nodeName, self.getAttrib(elm, attrNode.nodeName));
18790				});
18791
18792				// Replace block
18793				self.replace(newElm, elm, 1);
18794			}
18795
18796			return newElm || elm;
18797		},
18798
18799		/**
18800		 * Find the common ancestor of two elements. This is a shorter method than using the DOM Range logic.
18801		 *
18802		 * @method findCommonAncestor
18803		 * @param {Element} a Element to find common ancestor of.
18804		 * @param {Element} b Element to find common ancestor of.
18805		 * @return {Element} Common ancestor element of the two input elements.
18806		 */
18807		findCommonAncestor: function(a, b) {
18808			var ps = a, pe;
18809
18810			while (ps) {
18811				pe = b;
18812
18813				while (pe && ps != pe) {
18814					pe = pe.parentNode;
18815				}
18816
18817				if (ps == pe) {
18818					break;
18819				}
18820
18821				ps = ps.parentNode;
18822			}
18823
18824			if (!ps && a.ownerDocument) {
18825				return a.ownerDocument.documentElement;
18826			}
18827
18828			return ps;
18829		},
18830
18831		/**
18832		 * Parses the specified RGB color value and returns a hex version of that color.
18833		 *
18834		 * @method toHex
18835		 * @param {String} rgbVal RGB string value like rgb(1,2,3)
18836		 * @return {String} Hex version of that RGB value like #FF00FF.
18837		 */
18838		toHex: function(rgbVal) {
18839			return this.styles.toHex(Tools.trim(rgbVal));
18840		},
18841
18842		/**
18843		 * Executes the specified function on the element by id or dom element node or array of elements/id.
18844		 *
18845		 * @method run
18846		 * @param {String/Element/Array} elm ID or DOM element object or array with ids or elements.
18847		 * @param {function}
18847 func Function to execute for each item.
18848		 * @param {Object} scope Optional scope to execute the function in.
18849		 * @return {Object/Array} Single object, or an array of objects if multiple input elements were passed in.
18850		 */
18851		run: function(elm, func, scope) {
18852			var self = this, result;
18853
18854			if (typeof elm === 'string') {
18855				elm = self.get(elm);
18856			}
18857
18858			if (!elm) {
18859				return false;
18860			}
18861
18862			scope = scope || this;
18863			if (!elm.nodeType && (elm.length || elm.length === 0)) {
18864				result = [];
18865
18866				each(elm, function(elm, i) {
18867					if (elm) {
18868						if (typeof elm == 'string') {
18869							elm = self.get(elm);
18870						}
18871
18872						result.push(func.call(scope, elm, i));
18873					}
18874				});
18875
18876				return result;
18877			}
18878
18879			return func.call(scope, elm);
18880		},
18881
18882		/**
18883		 * Returns a NodeList with attributes for the element.
18884		 *
18885		 * @method getAttribs
18886		 * @param {HTMLElement/string} elm Element node or string id to get attributes from.
18887		 * @return {NodeList} NodeList with attributes.
18888		 */
18889		getAttribs: function(elm) {
18890			var attrs;
18891
18892			elm = this.get(elm);
18893
18894			if (!elm) {
18895				return [];
18896			}
18897
18898			if (isIE) {
18899				attrs = [];
18900
18901				// Object will throw exception in IE
18902				if (elm.nodeName == 'OBJECT') {
18903					return elm.attributes;
18904				}
18905
18906				// IE doesn't keep the selected attribute if you clone option elements
18907				if (elm.nodeName === 'OPTION' && this.getAttrib(elm, 'selected')) {
18908					attrs.push({specified: 1, nodeName: 'selected'});
18909				}
18910
18911				// It's crazy that this is faster in IE but it's because it returns all attributes all the time
18912				var attrRegExp = /<\/?[\w:\-]+ ?|=[\"][^\"]+\"|=\'[^\']+\'|=[\w\-]+|>/gi;
18913				elm.cloneNode(false).outerHTML.replace(attrRegExp, '').replace(/[\w:\-]+/gi, function(a) {
18914					attrs.push({specified: 1, nodeName: a});
18915				});
18916
18917				return attrs;
18918			}
18919
18920			return elm.attributes;
18921		},
18922
18923		/**
18924		 * Returns true/false if the specified node is to be considered empty or not.
18925		 *
18926		 * @example
18927		 * tinymce.DOM.isEmpty(node, {img: true});
18928		 * @method isEmpty
18929		 * @param {Object} elements Optional name/value object with elements that are automatically treated as non-empty elements.
18930		 * @return {Boolean} true/false if the node is empty or not.
18931		 */
18932		isEmpty: function(node, elements) {
18933			var self = this, i, attributes, type, walker, name, brCount = 0;
18934
18935			node = node.firstChild;
18936			if (node) {
18937				walker = new TreeWalker(node, node.parentNode);
18938				elements = elements || (self.schema ? self.schema.getNonEmptyElements() : null);
18939
18940				do {
18941					type = node.nodeType;
18942
18943					if (type === 1) {
18944						// Ignore bogus elements
18945						if (node.getAttribute('data-mce-bogus')) {
18946							continue;
18947						}
18948
18949						// Keep empty elements like <img />
18950						name = node.nodeName.toLowerCase();
18951						if (elements && elements[name]) {
18952							// Ignore single BR elements in blocks like <p><br /></p> or <p><span><br /></span></p>
18953							if (name === 'br') {
18954								brCount++;
18955								continue;
18956							}
18957
18958							return false;
18959						}
18960
18961						// Keep elements with data-bookmark attributes or name attribute like <a name="1"></a>
18962						attributes = self.getAttribs(node);
18963						i = attributes.length;
18964						while (i--) {
18965							name = attributes[i].nodeName;
18966							if (name === "name" || name === 'data-mce-bookmark') {
18967								return false;
18968							}
18969						}
18970					}
18971
18972					// Keep comment nodes
18973					if (type == 8) {
18974						return false;
18975					}
18976
18977					// Keep non whitespace text nodes
18978					if ((type === 3 && !whiteSpaceRegExp.test(node.nodeValue))) {
18979						return false;
18980					}
18981				} while ((node = walker.next()));
18982			}
18983
18984			return brCount <= 1;
18985		},
18986
18987		/**
18988		 * Creates a new DOM Range object. This will use the native DOM Range API if it's
18989		 * available. If it's not, it will fall back to the custom TinyMCE implementation.
18990		 *
18991		 * @method createRng
18992		 * @return {DOMRange} DOM Range object.
18993		 * @example
18994		 * var rng = tinymce.DOM.createRng();
18995		 * alert(rng.startContainer + "," + rng.startOffset);
18996		 */
18997		createRng: function() {
18998			var doc = this.doc;
18999
19000			return doc.createRange ? doc.createRange() : new Range(this);
19001		},
19002
19003		/**
19004		 * Returns the index of the specified node within its parent.
19005		 *
19006		 * @method nodeIndex
19007		 * @param {Node} node Node to look for.
19008		 * @param {boolean} normalized Optional true/false state if the index is what it would be after a normalization.
19009		 * @return {Number} Index of the specified node.
19010		 */
19011		nodeIndex: nodeIndex,
19012
19013		/**
19014		 * Splits an element into two new elements and places the specified split
19015		 * element or elements between the new ones. For example splitting the paragraph at the bold element in
19016		 * this example <p>abc<b>abc</b>123</p> would produce <p>abc</p><b>abc</b><p>123</p>.
19017		 *
19018		 * @method split
19019		 * @param {Element} parentElm Parent element to split.
19020		 * @param {Element} splitElm Element to split at.
19021		 * @param {Element} replacementElm Optional replacement element to replace the split element with.
19022		 * @return {Element} Returns the split element or the replacement element if that is specified.
19023		 */
19024		split: function(parentElm, splitElm, replacementElm) {
19025			var self = this, r = self.createRng(), bef, aft, pa;
19026
19027			// W3C valid browsers tend to leave empty nodes to the left/right side of the contents - this makes sense
19028			// but we don't want that in our code since it serves no purpose for the end user
19029			// For example splitting this html at the bold element:
19030			//   <p>text 1<span><b>CHOP</b></span>text 2</p>
19031			// would produce:
19032			//   <p>text 1<span></span></p><b>CHOP</b><p><span></span>text 2</p>
19033			// this function will then trim off empty edges and produce:
19034			//   <p>text 1</p><b>CHOP</b><p>text 2</p>
19035			function trimNode(node) {
19036				var i, children = node.childNodes, type = node.nodeType;
19037
19038				function surroundedBySpans(node) {
19039					var previousIsSpan = node.previousSibling && node.previousSibling.nodeName == 'SPAN';
19040					var nextIsSpan = node.nextSibling && node.nextSibling.nodeName == 'SPAN';
19041					return previousIsSpan && nextIsSpan;
19042				}
19043
19044				if (type == 1 && node.getAttribute('data-mce-type') == 'bookmark') {
19045					return;
19046				}
19047
19048				for (i = children.length - 1; i >= 0; i--) {
19049					trimNode(children[i]);
19050				}
19051
19052				if (type != 9) {
19053					// Keep non whitespace text nodes
19054					if (type == 3 && node.nodeValue.length > 0) {
19055						// If parent element isn't a block or there isn't any useful contents for example "<p>   </p>"
19056						// Also keep text nodes with only spaces if surrounded by spans.
19057						// eg. "<p><span>a</span> <span>b</span></p>" should keep space between a and b
19058						var trimmedLength = trim(node.nodeValue).length;
19059						if (!self.isBlock(node.parentNode) || trimmedLength > 0 || trimmedLength === 0 && surroundedBySpans(node)) {
19060							return;
19061						}
19062					} else if (type == 1) {
19063						// If the only child is a bookmark then move it up
19064						children = node.childNodes;
19065
19066						// TODO fix this complex if
19067						if (children.length == 1 && children[0] && children[0].nodeType == 1 &&
19068							children[0].getAttribute('data-mce-type') == 'bookmark') {
19069							node.parentNode.insertBefore(children[0], node);
19070						}
19071
19072						// Keep non empty elements or img, hr etc
19073						if (children.length || /^(br|hr|input|img)$/i.test(node.nodeName)) {
19074							return;
19075						}
19076					}
19077
19078					self.remove(node);
19079				}
19080
19081				return node;
19082			}
19083
19084			if (parentElm && splitElm) {
19085				// Get before chunk
19086				r.setStart(parentElm.parentNode, self.nodeIndex(parentElm));
19087				r.setEnd(splitElm.parentNode, self.nodeIndex(splitElm));
19088				bef = r.extractContents();
19089
19090				// Get after chunk
19091				r = self.createRng();
19092				r.setStart(splitElm.parentNode, self.nodeIndex(splitElm) + 1);
19093				r.setEnd(parentElm.parentNode, self.nodeIndex(parentElm) + 1);
19094				aft = r.extractContents();
19095
19096				// Insert before chunk
19097				pa = parentElm.parentNode;
19098				pa.insertBefore(trimNode(bef), parentElm);
19099
19100				// Insert middle chunk
19101				if (replacementElm) {
19102					pa.insertBefore(replacementElm, parentElm);
19103					//pa.replaceChild(replacementElm, splitElm);
19104				} else {
19105					pa.insertBefore(splitElm, parentElm);
19106				}
19107
19108				// Insert after chunk
19109				pa.insertBefore(trimNode(aft), parentElm);
19110				self.remove(parentElm);
19111
19112				return replacementElm || splitElm;
19113			}
19114		},
19115
19116		/**
19117		 * Adds an event handler to the specified object.
19118		 *
19119		 * @method bind
19120		 * @param {Element/Document/Window/Array} target Target element to bind events to.
19121		 * handler to or an array of elements/ids/documents.
19122		 * @param {String} name Name of event handler to add, for example: click.
19123		 * @param {function} func Function to execute when the event occurs.
19124		 * @param {Object} scope Optional scope to execute the function in.
19125		 * @return {function} Function callback handler the same as the one passed in.
19126		 */
19127		bind: function(target, name, func, scope) {
19128			var self = this;
19129
19130			if (Tools.isArray(target)) {
19131				var i = target.length;
19132
19133				while (i--) {
19134					target[i] = self.bind(target[i], name, func, scope);
19135				}
19136
19137				return target;
19138			}
19139
19140			// Collect all window/document events bound by editor instance
19141			if (self.settings.collect && (target === self.doc || target === self.win)) {
19142				self.boundEvents.push([target, name, func, scope]);
19143			}
19144
19145			return self.events.bind(target, name, func, scope || self);
19146		},
19147
19148		/**
19149		 * Removes the specified event handler by name and function from an element or collection of elements.
19150		 *
19151		 * @method unbind
19152		 * @param {Element/Document/Window/Array} target Target element to unbind events on.
19153		 * @param {String} name Event handler name, for example: "click"
19154		 * @param {function}
19154 func Function to remove.
19155		 * @return {bool/Array} Bool state of true if the handler was removed, or an array of states if multiple input elements
19156		 * were passed in.
19157		 */
19158		unbind: function(target, name, func) {
19159			var self = this, i;
19160
19161			if (Tools.isArray(target)) {
19162				i = target.length;
19163
19164				while (i--) {
19165					target[i] = self.unbind(target[i], name, func);
19166				}
19167
19168				return target;
19169			}
19170
19171			// Remove any bound events matching the input
19172			if (self.boundEvents && (target === self.doc || target === self.win)) {
19173				i = self.boundEvents.length;
19174
19175				while (i--) {
19176					var item = self.boundEvents[i];
19177
19178					if (target == item[0] && (!name || name == item[1]) && (!func || func == item[2])) {
19179						this.events.unbind(item[0], item[1], item[2]);
19180					}
19181				}
19182			}
19183
19184			return this.events.unbind(target, name, func);
19185		},
19186
19187		/**
19188		 * Fires the specified event name with object on target.
19189		 *
19190		 * @method fire
19191		 * @param {Node/Document/Window} target Target element or object to fire event on.
19192		 * @param {String} name Name of the event to fire.
19193		 * @param {Object} evt Event object to send.
19194		 * @return {Event} Event object.
19195		 */
19196		fire: function(target, name, evt) {
19197			return this.events.fire(target, name, evt);
19198		},
19199
19200		// Returns the content editable state of a node
19201		getContentEditable: function(node) {
19202			var contentEditable;
19203
19204			// Check type
19205			if (!node || node.nodeType != 1) {
19206				return null;
19207			}
19208
19209			// Check for fake content editable
19210			contentEditable = node.getAttribute("data-mce-contenteditable");
19211			if (contentEditable && contentEditable !== "inherit") {
19212				return contentEditable;
19213			}
19214
19215			// Check for real content editable
19216			return node.contentEditable !== "inherit" ? node.contentEditable : null;
19217		},
19218
19219		getContentEditableParent: function(node) {
19220			var root = this.getRoot(), state = null;
19221
19222			for (; node && node !== root; node = node.parentNode) {
19223				state = this.getContentEditable(node);
19224
19225				if (state !== null) {
19226					break;
19227				}
19228			}
19229
19230			return state;
19231		},
19232
19233		/**
19234		 * Destroys all internal references to the DOM to solve IE leak issues.
19235		 *
19236		 * @method destroy
19237		 */
19238		destroy: function() {
19239			var self = this;
19240
19241			// Unbind all events bound to window/document by editor instance
19242			if (self.boundEvents) {
19243				var i = self.boundEvents.length;
19244
19245				while (i--) {
19246					var item = self.boundEvents[i];
19247					this.events.unbind(item[0], item[1], item[2]);
19248				}
19249
19250				self.boundEvents = null;
19251			}
19252
19253			// Restore sizzle document to window.document
19254			// Since the current document might be removed producing "Permission denied" on IE see #6325
19255			if (Sizzle.setDocument) {
19256				Sizzle.setDocument();
19257			}
19258
19259			self.win = self.doc = self.root = self.events = self.frag = null;
19260		},
19261
19262		isChildOf: function(node, parent) {
19263			while (node) {
19264				if (parent === node) {
19265					return true;
19266				}
19267
19268				node = node.parentNode;
19269			}
19270
19271			return false;
19272		},
19273
19274		// #ifdef debug
19275
19276		dumpRng: function(r) {
19277			return (
19278				'startContainer: ' + r.startContainer.nodeName +
19279				', startOffset: ' + r.startOffset +
19280				', endContainer: ' + r.endContainer.nodeName +
19281				', endOffset: ' + r.endOffset
19282			);
19283		},
19284
19285		// #endif
19286
19287		_findSib: function(node, selector, name) {
19288			var self = this, func = selector;
19289
19290			if (node) {
19291				// If expression make a function of it using is
19292				if (typeof func == 'string') {
19293					func = function(node) {
19294						return self.is(node, selector);
19295					};
19296				}
19297
19298				// Loop all siblings
19299				for (node = node[name]; node; node = node[name]) {
19300					if (func(node)) {
19301						return node;
19302					}
19303				}
19304			}
19305
19306			return null;
19307		}
19308	};
19309
19310	/**
19311	 * Instance of DOMUtils for the current document.
19312	 *
19313	 * @static
19314	 * @property DOM
19315	 * @type tinymce.dom.DOMUtils
19316	 * @example
19317	 * // Example of how to add a class to some element by id
19318	 * tinymce.DOM.addClass('someid', 'someclass');
19319	 */
19320	DOMUtils.DOM = new DOMUtils(document);
19321	DOMUtils.nodeIndex = nodeIndex;
19322
19323	return DOMUtils;
19324});
19325
19326// Included from: js/tinymce/classes/dom/ScriptLoader.js
19327
19328/**
19329 * ScriptLoader.js
19330 *
19331 * Released under LGPL License.
19332 * Copyright (c) 1999-2015 Ephox Corp. All rights reserved
19333 *
19334 * License: http://www.tinymce.com/license
19335 * Contributing: http://www.tinymce.com/contributing
19336 */
19337
19338/*globals console*/
19339
19340/**
19341 * This class handles asynchronous/synchronous loading of JavaScript files it will execute callbacks
19342 * when various items gets loaded. This class is useful to load external JavaScript files.
19343 *
19344 * @class tinymce.dom.ScriptLoader
19345 * @example
19346 * // Load a script from a specific URL using the global script loader
19347 * tinymce.ScriptLoader.load('somescript.js');
19348 *
19349 * // Load a script using a unique instance of the script loader
19350 * var scriptLoader = new tinymce.dom.ScriptLoader();
19351 *
19352 * scriptLoader.load('somescript.js');
19353 *
19354 * // Load multiple scripts
19355 * var scriptLoader = new tinymce.dom.ScriptLoader();
19356 *
19357 * scriptLoader.add('somescript1.js');
19358 * scriptLoader.add('somescript2.js');
19359 * scriptLoader.add('somescript3.js');
19360 *
19361 * scriptLoader.loadQueue(function() {
19362 *    alert('All scripts are now loaded.');
19363 * });
19364 */
19365define("tinymce/dom/ScriptLoader", [
19366	"tinymce/dom/DOMUtils",
19367	"tinymce/util/Tools"
19368], function(DOMUtils, Tools) {
19369	var DOM = DOMUtils.DOM;
19370	var each = Tools.each, grep = Tools.grep;
19371
19372	function ScriptLoader() {
19373		var QUEUED = 0,
19374			LOADING = 1,
19375			LOADED = 2,
19376			states = {},
19377			queue = [],
19378			scriptLoadedCallbacks = {},
19379			queueLoadedCallbacks = [],
19380			loading = 0,
19381			undef;
19382
19383		/**
19384		 * Loads a specific script directly without adding it to the load queue.
19385		 *
19386		 * @method load
19387		 * @param {String} url Absolute URL to script to add.
19388		 * @param {function} callback Optional callback function to execute ones this script gets loaded.
19389		 */
19390		function loadScript(url, callback) {
19391			var dom = DOM, elm, id;
19392
19393			// Execute callback when script is loaded
19394			function done() {
19395				dom.remove(id);
19396
19397				if (elm) {
19398					elm.onreadystatechange = elm.onload = elm = null;
19399				}
19400
19401				callback();
19402			}
19403
19404			function error() {
19405				/*eslint no-console:0 */
19406
19407				// Report the error so it's easier for people to spot loading errors
19408				if (typeof console !== "undefined" && console.log) {
19409					console.log("Failed to load: " + url);
19410				}
19411
19412				// We can't mark it as done if there is a load error since
19413				// A) We don't want to produce 404 errors on the server and
19414				// B) the onerror event won't fire on all browsers.
19415				// done();
19416			}
19417
19418			id = dom.uniqueId();
19419
19420			// Create new script element
19421			elm = document.createElement('script');
19422			elm.id = id;
19423			elm.type = 'text/javascript';
19424			elm.src = Tools._addCacheSuffix(url);
19425
19426			// Seems that onreadystatechange works better on IE 10 onload seems to fire incorrectly
19427			if ("onreadystatechange" in elm) {
19428				elm.onreadystatechange = function() {
19429					if (/loaded|complete/.test(elm.readyState)) {
19430						done();
19431					}
19432				};
19433			} else {
19434				elm.onload = done;
19435			}
19436
19437			// Add onerror event will get fired on some browsers but not all of them
19438			elm.onerror = error;
19439
19440			// Add script to document
19441			(document.getElementsByTagName('head')[0] || document.body).appendChild(elm);
19442		}
19443
19444		/**
19445		 * Returns true/false if a script has been loaded or not.
19446		 *
19447		 * @method isDone
19448		 * @param {String} url URL to check for.
19449		 * @return {Boolean} true/false if the URL is loaded.
19450		 */
19451		this.isDone = function(url) {
19452			return states[url] == LOADED;
19453		};
19454
19455		/**
19456		 * Marks a specific script to be loaded. This can be useful if a script got loaded outside
19457		 * the script loader or to skip it from loading some script.
19458		 *
19459		 * @method markDone
19460		 * @param {string} url Absolute URL to the script to mark as loaded.
19461		 */
19462		this.markDone = function(url) {
19463			states[url] = LOADED;
19464		};
19465
19466		/**
19467		 * Adds a specific script to the load queue of the script loader.
19468		 *
19469		 * @method add
19470		 * @param {String} url Absolute URL to script to add.
19471		 * @param {function} callback Optional callback function to execute ones this script gets loaded.
19472		 * @param {Object} scope Optional scope to execute callback in.
19473		 */
19474		this.add = this.load = function(url, callback, scope) {
19475			var state = states[url];
19476
19477			// Add url to load queue
19478			if (state == undef) {
19479				queue.push(url);
19480				states[url] = QUEUED;
19481			}
19482
19483			if (callback) {
19484				// Store away callback for later execution
19485				if (!scriptLoadedCallbacks[url]) {
19486					scriptLoadedCallbacks[url] = [];
19487				}
19488
19489				scriptLoadedCallbacks[url].push({
19490					func: callback,
19491					scope: scope || this
19492				});
19493			}
19494		};
19495
19496		/**
19497		 * Starts the loading of the queue.
19498		 *
19499		 * @method loadQueue
19500		 * @param {function} callback Optional callback to execute when all queued items are loaded.
19501		 * @param {Object} scope Optional scope to execute the callback in.
19502		 */
19503		this.loadQueue = function(callback, scope) {
19504			this.loadScripts(queue, callback, scope);
19505		};
19506
19507		/**
19508		 * Loads the specified queue of files and executes the callback ones they are loaded.
19509		 * This method is generally not used outside this class but it might be useful in some scenarios.
19510		 *
19511		 * @method loadScripts
19512		 * @param {Array} scripts Array of queue items to load.
19513		 * @param {function} callback Optional callback to execute ones all items are loaded.
19514		 * @param {Object} scope Optional scope to execute callback in.
19515		 */
19516		this.loadScripts = function(scripts, callback, scope) {
19517			var loadScripts;
19518
19519			function execScriptLoadedCallbacks(url) {
19520				// Execute URL callback functions
19521				each(scriptLoadedCallbacks[url], function(callback) {
19522					callback.func.call(callback.scope);
19523				});
19524
19525				scriptLoadedCallbacks[url] = undef;
19526			}
19527
19528			queueLoadedCallbacks.push({
19529				func: callback,
19530				scope: scope || this
19531			});
19532
19533			loadScripts = function() {
19534				var loadingScripts = grep(scripts);
19535
19536				// Current scripts has been handled
19537				scripts.length = 0;
19538
19539				// Load scripts that needs to be loaded
19540				each(loadingScripts, function(url) {
19541					// Script is already loaded then execute script callbacks directly
19542					if (states[url] == LOADED) {
19543						execScriptLoadedCallbacks(url);
19544						return;
19545					}
19546
19547					// Is script not loading then start loading it
19548					if (states[url] != LOADING) {
19549						states[url] = LOADING;
19550						loading++;
19551
19552						loadScript(url, function() {
19553							states[url] = LOADED;
19554							loading--;
19555
19556							execScriptLoadedCallbacks(url);
19557
19558							// Load more scripts if they where added by the recently loaded script
19559							loadScripts();
19560						});
19561					}
19562				});
19563
19564				// No scripts are currently loading then execute all pending queue loaded callbacks
19565				if (!loading) {
19566					each(queueLoadedCallbacks, function(callback) {
19567						callback.func.call(callback.scope);
19568					});
19569
19570					queueLoadedCallbacks.length = 0;
19571				}
19572			};
19573
19574			loadScripts();
19575		};
19576	}
19577
19578	ScriptLoader.ScriptLoader = new ScriptLoader();
19579
19580	return ScriptLoader;
19581});
19582
19583// Included from: js/tinymce/classes/AddOnManager.js
19584
19585/**
19586 * AddOnManager.js
19587 *
19588 * Released under LGPL License.
19589 * Copyright (c) 1999-2015 Ephox Corp. All rights reserved
19590 *
19591 * License: http://www.tinymce.com/license
19592 * Contributing: http://www.tinymce.com/contributing
19593 */
19594
19595/**
19596 * This class handles the loading of themes/plugins or other add-ons and their language packs.
19597 *
19598 * @class tinymce.AddOnManager
19599 */
19600define("tinymce/AddOnManager", [
19601	"tinymce/dom/ScriptLoader",
19602	"tinymce/util/Tools"
19603], function(ScriptLoader, Tools) {
19604	var each = Tools.each;
19605
19606	function AddOnManager() {
19607		var self = this;
19608
19609		self.items = [];
19610		self.urls = {};
19611		self.lookup = {};
19612	}
19613
19614	AddOnManager.prototype = {
19615		/**
19616		 * Returns the specified add on by the short name.
19617		 *
19618		 * @method get
19619		 * @param {String} name Add-on to look for.
19620		 * @return {tinymce.Theme/tinymce.Plugin}
19620 Theme or plugin add-on instance or undefined.
19621		 */
19622		get: function(name) {
19623			if (this.lookup[name]) {
19624				return this.lookup[name].instance;
19625			}
19626
19627			return undefined;
19628		},
19629
19630		dependencies: function(name) {
19631			var result;
19632
19633			if (this.lookup[name]) {
19634				result = this.lookup[name].dependencies;
19635			}
19636
19637			return result || [];
19638		},
19639
19640		/**
19641		 * Loads a language pack for the specified add-on.
19642		 *
19643		 * @method requireLangPack
19644		 * @param {String} name Short name of the add-on.
19645		 * @param {String} languages Optional comma or space separated list of languages to check if it matches the name.
19646		 */
19647		requireLangPack: function(name, languages) {
19648			var language = AddOnManager.language;
19649
19650			if (language && AddOnManager.languageLoad !== false) {
19651				if (languages) {
19652					languages = ',' + languages + ',';
19653
19654					// Load short form sv.js or long form sv_SE.js
19655					if (languages.indexOf(',' + language.substr(0, 2) + ',') != -1) {
19656						language = language.substr(0, 2);
19657					} else if (languages.indexOf(',' + language + ',') == -1) {
19658						return;
19659					}
19660				}
19661
19662				ScriptLoader.ScriptLoader.add(this.urls[name] + '/langs/' + language + '.js');
19663			}
19664		},
19665
19666		/**
19667		 * Adds a instance of the add-on by it's short name.
19668		 *
19669		 * @method add
19670		 * @param {String} id Short name/id for the add-on.
19671		 * @param {tinymce.Theme/tinymce.Plugin} addOn Theme or plugin to add.
19672		 * @return {tinymce.Theme/tinymce.Plugin} The same theme or plugin instance that got passed in.
19673		 * @example
19674		 * // Create a simple plugin
19675		 * tinymce.create('tinymce.plugins.TestPlugin', {
19676		 *   TestPlugin: function(ed, url) {
19677		 *   ed.on('click', function(e) {
19678		 *      ed.windowManager.alert('Hello World!');
19679		 *   });
19680		 *   }
19681		 * });
19682		 *
19683		 * // Register plugin using the add method
19684		 * tinymce.PluginManager.add('test', tinymce.plugins.TestPlugin);
19685		 *
19686		 * // Initialize TinyMCE
19687		 * tinymce.init({
19688		 *  ...
19689		 *  plugins: '-test' // Init the plugin but don't try to load it
19690		 * });
19691		 */
19692		add: function(id, addOn, dependencies) {
19693			this.items.push(addOn);
19694			this.lookup[id] = {instance: addOn, dependencies: dependencies};
19695
19696			return addOn;
19697		},
19698
19699		createUrl: function(baseUrl, dep) {
19700			if (typeof dep === "object") {
19701				return dep;
19702			}
19703
19704			return {prefix: baseUrl.prefix, resource: dep, suffix: baseUrl.suffix};
19705		},
19706
19707		/**
19708		 * Add a set of components that will make up the add-on. Using the url of the add-on name as the base url.
19709		 * This should be used in development mode.  A new compressor/javascript munger process will ensure that the
19710		 * components are put together into the plugin.js file and compressed correctly.
19711		 *
19712		 * @method addComponents
19713		 * @param {String} pluginName name of the plugin to load scripts from (will be used to get the base url for the plugins).
19714		 * @param {Array} scripts Array containing the names of the scripts to load.
19715		 */
19716		addComponents: function(pluginName, scripts) {
19717			var pluginUrl = this.urls[pluginName];
19718
19719			each(scripts, function(script) {
19720				ScriptLoader.ScriptLoader.add(pluginUrl + "/" + script);
19721			});
19722		},
19723
19724		/**
19725		 * Loads an add-on from a specific url.
19726		 *
19727		 * @method load
19728		 * @param {String} name Short name of the add-on that gets loaded.
19729		 * @param {String} addOnUrl URL to the add-on that will get loaded.
19730		 * @param {function} callback Optional callback to execute ones the add-on is loaded.
19731		 * @param {Object} scope Optional scope to execute the callback in.
19732		 * @example
19733		 * // Loads a plugin from an external URL
19734		 * tinymce.PluginManager.load('myplugin', '/some/dir/someplugin/plugin.js');
19735		 *
19736		 * // Initialize TinyMCE
19737		 * tinymce.init({
19738		 *  ...
19739		 *  plugins: '-myplugin' // Don't try to load it again
19740		 * });
19741		 */
19742		load: function(name, addOnUrl, callback, scope) {
19743			var self = this, url = addOnUrl;
19744
19745			function loadDependencies() {
19746				var dependencies = self.dependencies(name);
19747
19748				each(dependencies, function(dep) {
19749					var newUrl = self.createUrl(addOnUrl, dep);
19750
19751					self.load(newUrl.resource, newUrl, undefined, undefined);
19752				});
19753
19754				if (callback) {
19755					if (scope) {
19756						callback.call(scope);
19757					} else {
19758						callback.call(ScriptLoader);
19759					}
19760				}
19761			}
19762
19763			if (self.urls[name]) {
19764				return;
19765			}
19766
19767			if (typeof addOnUrl === "object") {
19768				url = addOnUrl.prefix + addOnUrl.resource + addOnUrl.suffix;
19769			}
19770
19771			if (url.indexOf('/') !== 0 && url.indexOf('://') == -1) {
19772				url = AddOnManager.baseURL + '/' + url;
19773			}
19774
19775			self.urls[name] = url.substring(0, url.lastIndexOf('/'));
19776
19777			if (self.lookup[name]) {
19778				loadDependencies();
19779			} else {
19780				ScriptLoader.ScriptLoader.add(url, loadDependencies, scope);
19781			}
19782		}
19783	};
19784
19785	AddOnManager.PluginManager = new AddOnManager();
19786	AddOnManager.ThemeManager = new AddOnManager();
19787
19788	return AddOnManager;
19789});
19790
19791/**
19792 * TinyMCE theme class.
19793 *
19794 * @class tinymce.Theme
19795 */
19796
19797/**
19798 * This method is responsible for rendering/generating the overall user interface with toolbars, buttons, iframe containers etc.
19799 *
19800 * @method renderUI
19801 * @param {Object} obj Object parameter containing the targetNode DOM node that will be replaced visually with an editor instance.
19802 * @return {Object}
19802 an object with items like iframeContainer, editorContainer, sizeContainer, deltaWidth, deltaHeight.
19803 */
19804
19805/**
19806 * Plugin base class, this is a pseudo class that describes how a plugin is to be created for TinyMCE. The methods below are all optional.
19807 *
19808 * @class tinymce.Plugin
19809 * @example
19810 * tinymce.PluginManager.add('example', function(editor, url) {
19811 *     // Add a button that opens a window
19812 *     editor.addButton('example', {
19813 *         text: 'My button',
19814 *         icon: false,
19815 *         onclick: function() {
19816 *             // Open window
19817 *             editor.windowManager.open({
19818 *                 title: 'Example plugin',
19819 *                 body: [
19820 *                     {type: 'textbox', name: 'title', label: 'Title'}
19821 *                 ],
19822 *                 onsubmit: function(e) {
19823 *                     // Insert content when the window form is submitted
19824 *                     editor.insertContent('Title: ' + e.data.title);
19825 *                 }
19826 *             });
19827 *         }
19828 *     });
19829 *
19830 *     // Adds a menu item to the tools menu
19831 *     editor.addMenuItem('example', {
19832 *         text: 'Example plugin',
19833 *         context: 'tools',
19834 *         onclick: function() {
19835 *             // Open window with a specific url
19836 *             editor.windowManager.open({
19837 *                 title: 'TinyMCE site',
19838 *                 url: 'http://www.tinymce.com',
19839 *                 width: 800,
19840 *                 height: 600,
19841 *                 buttons: [{
19842 *                     text: 'Close',
19843 *                     onclick: 'close'
19844 *                 }]
19845 *             });
19846 *         }
19847 *     });
19848 * });
19849 */
19850
19851// Included from: js/tinymce/classes/dom/NodeType.js
19852
19853/**
19854 * NodeType.js
19855 *
19856 * Released under LGPL License.
19857 * Copyright (c) 1999-2015 Ephox Corp. All rights reserved
19858 *
19859 * License: http://www.tinymce.com/license
19860 * Contributing: http://www.tinymce.com/contributing
19861 */
19862
19863/**
19864 * Contains various node validation functions.
19865 *
19866 * @private
19867 * @class tinymce.dom.NodeType
19868 */
19869define("tinymce/dom/NodeType", [], function() {
19870	function isNodeType(type) {
19871		return function(node) {
19872			return !!node && node.nodeType == type;
19873		};
19874	}
19875
19876	var isElement = isNodeType(1);
19877
19878	function matchNodeNames(names) {
19879		names = names.toLowerCase().split(' ');
19880
19881		return function(node) {
19882			var i, name;
19883
19884			if (node && node.nodeType) {
19885				name = node.nodeName.toLowerCase();
19886
19887				for (i = 0; i < names.length; i++) {
19888					if (name === names[i]) {
19889						return true;
19890					}
19891				}
19892			}
19893
19894			return false;
19895		};
19896	}
19897
19898	function matchStyleValues(name, values) {
19899		values = values.toLowerCase().split(' ');
19900
19901		return function(node) {
19902			var i, cssValue;
19903
19904			if (isElement(node)) {
19905				for (i = 0; i < values.length; i++) {
19906					cssValue = getComputedStyle(node, null).getPropertyValue(name);
19907					if (cssValue === values[i]) {
19908						return true;
19909					}
19910				}
19911			}
19912
19913			return false;
19914		};
19915	}
19916
19917	function hasPropValue(propName, propValue) {
19918		return function(node) {
19919			return isElement(node) && node[propName] === propValue;
19920		};
19921	}
19922
19923	function hasAttributeValue(attrName, attrValue) {
19924		return function(node) {
19925			return isElement(node) && node.getAttribute(attrName) === attrValue;
19926		};
19927	}
19928
19929	function isBogus(node) {
19930		return isElement(node) && node.hasAttribute('data-mce-bogus');
19931	}
19932
19933	function hasContentEditableState(value) {
19934		return function(node) {
19935			if (isElement(node)) {
19936				if (node.contentEditable === value) {
19937					return true;
19938				}
19939
19940				if (node.getAttribute('data-mce-contenteditable') === value) {
19941					return true;
19942				}
19943			}
19944
19945			return false;
19946		};
19947	}
19948
19949	return {
19950		isText: isNodeType(3),
19951		isElement: isElement,
19952		isComment: isNodeType(8),
19953		isBr: matchNodeNames('br'),
19954		isContentEditableTrue: hasContentEditableState('true'),
19955		isContentEditableFalse: hasContentEditableState('false'),
19956		matchNodeNames: matchNodeNames,
19957		hasPropValue: hasPropValue,
19958		hasAttributeValue: hasAttributeValue,
19959		matchStyleValues: matchStyleValues,
19960		isBogus: isBogus
19961	};
19962});
19963
19964// Included from: js/tinymce/classes/text/Zwsp.js
19965
19966/**
19967 * Zwsp.js
19968 *
19969 * Released under LGPL License.
19970 * Copyright (c) 1999-2015 Ephox Corp. All rights reserved
19971 *
19972 * License: http://www.tinymce.com/license
19973 * Contributing: http://www.tinymce.com/contributing
19974 */
19975
19976/**
19977 * ....
19978 *
19979 * @private
19980 * @class tinymce.text.Zwsp
19981 * @example
19982 * var isZwsp = Zwsp.isZwsp('\u200b');
19983 * var abc = Zwsp.trim('a\u200bc');
19984 */
19985define("tinymce/text/Zwsp", [], function() {
19986	var ZWSP = '\u200b';
19987
19988	function isZwsp(chr) {
19989		return chr == ZWSP;
19990	}
19991
19992	function trim(str) {
19993		return str.replace(new RegExp(ZWSP, 'g'), '');
19994	}
19995
19996	return {
19997		isZwsp: isZwsp,
19998		ZWSP: ZWSP,
19999		trim: trim
20000	};
20001});
20002
20003// Included from: js/tinymce/classes/caret/CaretContainer.js
20004
20005/**
20006 * CaretContainer.js
20007 *
20008 * Released under LGPL License.
20009 * Copyright (c) 1999-2015 Ephox Corp. All rights reserved
20010 *
20011 * License: http://www.tinymce.com/license
20012 * Contributing: http://www.tinymce.com/contributing
20013 */
20014
20015/**
20016 * This module handles caret containers. A caret container is a 
20016node that
20017 * holds the caret for positional purposes.
20018 *
20019 * @private
20020 * @class tinymce.caret.CaretContainer
20021 */
20022define("tinymce/caret/CaretContainer", [
20023	"tinymce/dom/NodeType",
20024	"tinymce/text/Zwsp"
20025], function(NodeType, Zwsp) {
20026	var isElement = NodeType.isElement,
20027		isText = NodeType.isText;
20028
20029	function isCaretContainerBlock(node) {
20030		if (isText(node)) {
20031			node = node.parentNode;
20032		}
20033
20034		return isElement(node) && node.hasAttribute('data-mce-caret');
20035	}
20036
20037	function isCaretContainerInline(node) {
20038		return isText(node) && Zwsp.isZwsp(node.data);
20039	}
20040
20041	function isCaretContainer(node) {
20042		return isCaretContainerBlock(node) || isCaretContainerInline(node);
20043	}
20044
20045	function insertInline(node, before) {
20046		var doc, sibling, textNode, parentNode;
20047
20048		doc = node.ownerDocument;
20049		textNode = doc.createTextNode(Zwsp.ZWSP);
20050		parentNode = node.parentNode;
20051
20052		if (!before) {
20053			sibling = node.nextSibling;
20054			if (isText(sibling)) {
20055				if (isCaretContainer(sibling)) {
20056					return sibling;
20057				}
20058
20059				if (startsWithCaretContainer(sibling)) {
20060					sibling.splitText(1);
20061					return sibling;
20062				}
20063			}
20064
20065			if (node.nextSibling) {
20066				parentNode.insertBefore(textNode, node.nextSibling);
20067			} else {
20068				parentNode.appendChild(textNode);
20069			}
20070		} else {
20071			sibling = node.previousSibling;
20072			if (isText(sibling)) {
20073				if (isCaretContainer(sibling)) {
20074					return sibling;
20075				}
20076
20077				if (endsWithCaretContainer(sibling)) {
20078					return sibling.splitText(sibling.data.length - 1);
20079				}
20080			}
20081
20082			parentNode.insertBefore(textNode, node);
20083		}
20084
20085		return textNode;
20086	}
20087
20088	function insertBlock(blockName, node, before) {
20089		var doc, blockNode, parentNode;
20090
20091		doc = node.ownerDocument;
20092		blockNode = doc.createElement(blockName);
20093		blockNode.setAttribute('data-mce-caret', before ? 'before' : 'after');
20094		blockNode.setAttribute('data-mce-bogus', 'all');
20095		blockNode.appendChild(doc.createTextNode('\u00a0'));
20096		parentNode = node.parentNode;
20097
20098		if (!before) {
20099			if (node.nextSibling) {
20100				parentNode.insertBefore(blockNode, node.nextSibling);
20101			} else {
20102				parentNode.appendChild(blockNode);
20103			}
20104		} else {
20105			parentNode.insertBefore(blockNode, node);
20106		}
20107
20108		return blockNode;
20109	}
20110
20111	function remove(caretContainerNode) {
20112		var text;
20113
20114		if (isElement(caretContainerNode) && isCaretContainer(caretContainerNode)) {
20115			if (caretContainerNode.innerHTML != '&nbsp;') {
20116				caretContainerNode.removeAttribute('data-mce-caret');
20117			} else {
20118				if (caretContainerNode.parentNode) {
20119					caretContainerNode.parentNode.removeChild(caretContainerNode);
20120				}
20121			}
20122		}
20123
20124		if (isText(caretContainerNode)) {
20125			text = Zwsp.trim(caretContainerNode.data);
20126
20127			if (text.length === 0) {
20128				if (caretContainerNode.parentNode) {
20129					caretContainerNode.parentNode.removeChild(caretContainerNode);
20130				}
20131			}
20132
20133			caretContainerNode.nodeValue = text;
20134		}
20135	}
20136
20137	function startsWithCaretContainer(node) {
20138		return isText(node) && node.data[0] == Zwsp.ZWSP;
20139	}
20140
20141	function endsWithCaretContainer(node) {
20142		return isText(node) && node.data[node.data.length - 1] == Zwsp.ZWSP;
20143	}
20144
20145	return {
20146		isCaretContainer: isCaretContainer,
20147		isCaretContainerBlock: isCaretContainerBlock,
20148		isCaretContainerInline: isCaretContainerInline,
20149		insertInline: insertInline,
20150		insertBlock: insertBlock,
20151		remove: remove,
20152		startsWithCaretContainer: startsWithCaretContainer,
20153		endsWithCaretContainer: endsWithCaretContainer
20154	};
20155});
20156
20157// Included from: js/tinymce/classes/dom/RangeUtils.js
20158
20159/**
20160 * RangeUtils.js
20161 *
20162 * Released under LGPL License.
20163 * Copyright (c) 1999-2015 Ephox Corp. All rights reserved
20164 *
20165 * License: http://www.tinymce.com/license
20166 * Contributing: http://www.tinymce.com/contributing
20167 */
20168
20169/**
20170 * This class contains a few utility methods for ranges.
20171 *
20172 * @class tinymce.dom.RangeUtils
20173 */
20174define("tinymce/dom/RangeUtils", [
20175	"tinymce/util/Tools",
20176	"tinymce/dom/TreeWalker",
20177	"tinymce/dom/NodeType",
20178	"tinymce/caret/CaretContainer"
20179], function(Tools, TreeWalker, NodeType, CaretContainer) {
20180	var each = Tools.each,
20181		isContentEditableFalse = NodeType.isContentEditableFalse,
20182		isCaretContainer = CaretContainer.isCaretContainer;
20183
20184	function getEndChild(container, index) {
20185		var childNodes = container.childNodes;
20186
20187		index--;
20188
20189		if (index > childNodes.length - 1) {
20190			index = childNodes.length - 1;
20191		} else if (index < 0) {
20192			index = 0;
20193		}
20194
20195		return childNodes[index] || container;
20196	}
20197
20198	function RangeUtils(dom) {
20199		/**
20200		 * Walks the specified range like object and executes the callback for each sibling collection it finds.
20201		 *
20202		 * @private
20203		 * @method walk
20204		 * @param {Object} rng Range like object.
20205		 * @param {function} callback Callback function to execute for each sibling collection.
20206		 */
20207		this.walk = function(rng, callback) {
20208			var startContainer = rng.startContainer,
20209				startOffset = rng.startOffset,
20210				endContainer = rng.endContainer,
20211				endOffset = rng.endOffset,
20212				ancestor, startPoint,
20213				endPoint, node, parent, siblings, nodes;
20214
20215			// Handle table cell selection the table plugin enables
20216			// you to fake select table cells and perform formatting actions on them
20217			nodes = dom.select('td.mce-item-selected,th.mce-item-selected');
20218			if (nodes.length > 0) {
20219				each(nodes, function(node) {
20220					callback([node]);
20221				});
20222
20223				return;
20224			}
20225
20226			/**
20227			 * Excludes start/end text node if they are out side the range
20228			 *
20229			 * @private
20230			 * @param {Array} nodes Nodes to exclude items from.
20231			 * @return {Array} Array with nodes excluding the start/end container if needed.
20232			 */
20233			function exclude(nodes) {
20234				var node;
20235
20236				// First node is excluded
20237				node = nodes[0];
20238				if (node.nodeType === 3 && node === startContainer && startOffset >= node.nodeValue.length) {
20239					nodes.splice(0, 1);
20240				}
20241
20242				// Last node is excluded
20243				node = nodes[nodes.length - 1];
20244				if (endOffset === 0 && nodes.length > 0 && node === endContainer && node.nodeType === 3) {
20245					nodes.splice(nodes.length - 1, 1);
20246				}
20247
20248				return nodes;
20249			}
20250
20251			/**
20252			 * Collects siblings
20253			 *
20254			 * @private
20255			 * @param {Node} node Node to collect siblings from.
20256			 * @param {String} name Name of the sibling to check for.
20257			 * @param {Node} end_node
20258			 * @return {Array} Array of collected siblings.
20259			 */
20260			function collectSiblings(node, name, end_node) {
20261				var siblings = [];
20262
20263				for (; node && node != end_node; node = node[name]) {
20264					siblings.push(node);
20265				}
20266
20267				return siblings;
20268			}
20269
20270			/**
20271			 * Find an end point this is the node just before the common ancestor root.
20272			 *
20273			 * @private
20274			 * @param {Node} node Node to start at.
20275			 * @param {Node} root Root/ancestor element to stop just before.
20276			 * @return {Node} Node just before the root element.
20277			 */
20278			function findEndPoint(node, root) {
20279				do {
20280					if (node.parentNode == root) {
20281						return node;
20282					}
20283
20284					node = node.parentNode;
20285				} while (node);
20286			}
20287
20288			function walkBoundary(start_node, end_node, next) {
20289				var siblingName = next ? 'nextSibling' : 'previousSibling';
20290
20291				for (node = start_node, parent = node.parentNode; node && node != end_node; node = parent) {
20292					parent = node.parentNode;
20293					siblings = collectSiblings(node == start_node ? node : node[siblingName], siblingName);
20294
20295					if (siblings.length) {
20296						if (!next) {
20297							siblings.reverse();
20298						}
20299
20300						callback(exclude(siblings));
20301					}
20302				}
20303			}
20304
20305			// If index based start position then resolve it
20306			if (startContainer.nodeType == 1 && startContainer.hasChildNodes()) {
20307				startContainer = startContainer.childNodes[startOffset];
20308			}
20309
20310			// If index based end position then resolve it
20311			if (endContainer.nodeType == 1 && endContainer.hasChildNodes()) {
20312				endContainer = getEndChild(endContainer, endOffset);
20313			}
20314
20315			// Same container
20316			if (startContainer == endContainer) {
20317				return callback(exclude([startContainer]));
20318			}
20319
20320			// Find common ancestor and end points
20321			ancestor = dom.findCommonAncestor(startContainer, endContainer);
20322
20323			// Process left side
20324			for (node = startContainer; node; node = node.parentNode) {
20325				if (node === endContainer) {
20326					return walkBoundary(startContainer, ancestor, true);
20327				}
20328
20329				if (node === ancestor) {
20330					break;
20331				}
20332			}
20333
20334			// Process right side
20335			for (node = endContainer; node; node = node.parentNode) {
20336				if (node === startContainer) {
20337					return walkBoundary(endContainer, ancestor);
20338				}
20339
20340				if (node === ancestor) {
20341					break;
20342				}
20343			}
20344
20345			// Find start/end point
20346			startPoint = findEndPoint(startContainer, ancestor) || startContainer;
20347			endPoint = findEndPoint(endContainer, ancestor) || endContainer;
20348
20349			// Walk left leaf
20350			walkBoundary(startContainer, startPoint, true);
20351
20352			// Walk the middle from start to end point
20353			siblings = collectSiblings(
20354				startPoint == startContainer ? startPoint : startPoint.nextSibling,
20355				'nextSibling',
20356				endPoint == endContainer ? endPoint.nextSibling : endPoint
20357			);
20358
20359			if (siblings.length) {
20360				callback(exclude(siblings));
20361			}
20362
20363			// Walk right leaf
20364			walkBoundary(endContainer, endPoint);
20365		};
20366
20367		/**
20368		 * Splits the specified range at it's start/end points.
20369		 *
20370		 * @private
20371		 * @param {Range/RangeObject} rng Range to split.
20372		 * @return {Object} Range position object.
20373		 */
20374		this.split = function(rng) {
20375			var startContainer = rng.startContainer,
20376				startOffset = rng.startOffset,
20377				endContainer = rng.endContainer,
20378				endOffset = rng.endOffset;
20379
20380			function splitText(node, offset) {
20381				return node.splitText(offset);
20382			}
20383
20384			// Handle single text node
20385			if (startContainer == endContainer && startContainer.nodeType == 3) {
20386				if (startOffset > 0 && startOffset < startContainer.nodeValue.length) {
20387					endContainer = splitText(startContainer, startOffset);
20388					startContainer = endContainer.previousSibling;
20389
20390					if (endOffset > startOffset) {
20391						endOffset = endOffset - startOffset;
20392						startContainer = endContainer = splitText(endContainer, endOffset).previousSibling;
20393						endOffset = endContainer.nodeValue.length;
20394						startOffset = 0;
20395					} else {
20396						endOffset = 0;
20397					}
20398				}
20399			} else {
20400				// Split startContainer text node if needed
20401				if (startContainer.nodeType == 3 && startOffset > 0 && startOffset < startContainer.nodeValue.length) {
20402					startContainer = splitText(startContainer, startOffset);
20403					startOffset = 0;
20404				}
20405
20406				// Split endContainer text node if needed
20407				if (endContainer.nodeType == 3 && endOffset > 0 && endOffset < endContainer.nodeValue.length) {
20408					endContainer = splitText(endContainer, endOffset).previousSibling;
20409					endOffset = endContainer.nodeValue.length;
20410				}
20411			}
20412
20413			return {
20414				startContainer: startContainer,
20415				startOffset: startOffset,
20416				endContainer: endContainer,
20417				endOffset: endOffset
20418			};
20419		};
20420
20421		/**
20422		 * Normalizes the specified range by finding the closest best suitable caret location.
20423		 *
20424		 * @private
20425		 * @param {Range} rng Range to normalize.
20426		 * @return {Boolean} True/false if the specified range was normalized or not.
20427		 */
20428		this.normalize = function(rng) {
20429			var normalized, collapsed;
20430
20431			function normalizeEndPoint(start) {
20432				var container, offset, walker, body = dom.getRoot(), node, nonEmptyElementsMap;
20433				var directionLeft, isAfterNode;
20434
20435				function isTableCell(node) {
20436					return node && /^(TD|TH|CAPTION)$/.test(node.nodeName);
20437				}
20438
20439				function hasBrBeforeAfter(node, left) {
20440					var walker = new TreeWalker(node, dom.getParent(node.parentNode, dom.isBlock) || body);
20441
20442					while ((node = walker[left ? 'prev' : 'next']())) {
20443						if (node.nodeName === "BR") {
20444							return true;
20445						}
20446					}
20447				}
20448
20449				function hasContentEditableFalseParent(node) {
20450					while (node && node != body) {
20451						if (isContentEditableFalse(node)) {
20452							return true;
20453						}
20454
20455						node = node.parentNode;
20456					}
20457
20458					return false;
20459				}
20460
20461				function isPrevNode(node, name) {
20462					return node.previousSibling && node.previousSibling.nodeName == name;
20463				}
20464
20465				// Walks the dom left/right to find a suitable text node to move the endpoint into
20466				// It will only walk within the current parent block or body and will stop if it hits a block or a BR/IMG
20467				function findTextNodeRelative(left, startNode) {
20468					var walker, lastInlineElement, parentBlockContainer;
20469
20470					startNode = startNode || container;
20471					parentBlockContainer = dom.getParent(startNode.parentNode, dom.isBlock) || body;
20472
20473					// Lean left before the BR element if it's the only BR within a block element. Gecko bug: #6680
20474					// This: <p><br>|</p> becomes <p>|<br></p>
20475					if (left && startNode.nodeName == 'BR' && isAfterNode && dom.isEmpty(parentBlockContainer)) {
20476						container = startNode.parentNode;
20477						offset = dom.nodeIndex(startNode);
20478						normalized = true;
20479						return;
20480					}
20481
20482					// Walk left until we hit a text node we can move to or a block/br/img
20483					walker = new TreeWalker(startNode, parentBlockContainer);
20484					while ((node = walker[left ? 'prev' : 'next']())) {
20485						// Break if we hit a non content editable node
20486						if (dom.getContentEditableParent(node) === "false" || isCaretContainer(node)) {
20487							return;
20488						}
20489
20490						// Found text node that has a length
20491						if (node.nodeType === 3 && node.nodeValue.length > 0) {
20492							container = node;
20493							offset = left ? node.nodeValue.length : 0;
20494							normalized = true;
20495							return;
20496						}
20497
20498						// Break if we find a block or a BR/IMG/INPUT etc
20499						if (dom.isBlock(node) || nonEmptyElementsMap[node.nodeName.toLowerCase()]) {
20500							return;
20501						}
20502
20503						lastInlineElement = node;
20504					}
20505
20506					// Only fetch the last inline element when in caret mode for now
20507					if (collapsed && lastInlineElement) {
20508						container = lastInlineElement;
20509						normalized = true;
20510						offset = 0;
20511					}
20512				}
20513
20514				container = rng[(start ? 'start' : 'end') + 'Container'];
20515				offset = rng[(start ? 'start' : 'end') + 'Offset'];
20516				isAfterNode = container.nodeType == 1 && offset === container.childNodes.length;
20517				nonEmptyElementsMap = dom.schema.getNonEmptyElements();
20518				directionLeft = start;
20519
20520				if (isCaretContainer(container)) {
20521					return;
20522				}
20523
20524				if (container.nodeType == 1 && offset > container.childNodes.length - 1) {
20525					directionLeft = false;
20526				}
20527
20528				// If the container is a document move it to the body element
20529				if (container.nodeType === 9) {
20530					container = dom.getRoot();
20531					offset = 0;
20532				}
20533
20534				// If the container is body try move it into the closest text node or position
20535				if (container === body) {
20536					// If start is before/after a image, table etc
20537					if (directionLeft) {
20538						node = container.childNodes[offset > 0 ? offset - 1 : 0];
20539						if (node) {
20540							if (isCaretContainer(node)) {
20541								return;
20542							}
20543
20544							if (nonEmptyElementsMap[node.nodeName] || node.nodeName == "TABLE") {
20545								return;
20546							}
20547						}
20548					}
20549
20550					// Resolve the index
20551					if (container.hasChildNodes()) {
20552						offset = Math.min(!directionLeft && offset > 0 ? offset - 1 : offset, container.childNodes.length - 1);
20553						container = container.childNodes[offset];
20554						offset = 0;
20555
20556						if (hasContentEditableFalseParent(container) || isCaretContainer(container)) {
20557							return;
20558						}
20559
20560						// Don't walk into elements that doesn't have any child nodes like a IMG
20561						if (container.hasChildNodes() && !/TABLE/.test(container.nodeName)) {
20562							// Walk the DOM to find a text node to place the caret at or a BR
20563							node = container;
20564							walker = new TreeWalker(container, body);
20565
20566							do {
20567								if (isContentEditableFalse(node) || isCaretContainer(node)) {
20568									normalized = false;
20569									break;
20570								}
20571
20572								// Found a text node use that position
20573								if (node.nodeType === 3 && node.nodeValue.length > 0) {
20574									offset = directionLeft ? 0 : node.nodeValue.length;
20575									container = node;
20576									normalized = true;
20577									break;
20578								}
20579
20580								// Found a BR/IMG element that we can place the caret before
20581								if (nonEmptyElementsMap[node.nodeName.toLowerCase()] && !isTableCell(node)) {
20582									offset = dom.nodeIndex(node);
20583									container = node.parentNode;
20584
20585									// Put caret after image when moving the end point
20586									if (node.nodeName == "IMG" && !directionLeft) {
20587										offset++;
20588									}
20589
20590									normalized = true;
20591									break;
20592								}
20593							} while ((node = (directionLeft ? walker.next() : walker.prev())));
20594						}
20595					}
20596				}
20597
20598				// Lean the caret to the left if possible
20599				if (collapsed) {
20600					// So this: <b>x</b><i>|x</i>
20601					// Becomes: <b>x|</b><i>x</i>
20602					// Seems that only gecko has issues with this
20603					if (container.nodeType === 3 && offset === 0) {
20604						findTextNodeRelative(true);
20605					}
20606
20607					// Lean left into empty inline elements when the caret is before a BR
20608					// So this: <i><b></b><i>|<br></i>
20609					// Becomes: <i><b>|</b><i><br></i>
20610					// Seems that only gecko has issues with this.
20611					// Special edge case for <p><a>x</a>|<br></p> since we don't want <p><a>x|</a><br></p>
20612					if (container.nodeType === 1) {
20613						node = container.childNodes[offset];
20614
20615						// Offset is after the containers last child
20616						// then use the previous child for normalization
20617						if (!node) {
20618							node = container.childNodes[offset - 1];
20619						}
20620
20621						if (node && node.nodeName === 'BR' && !isPrevNode(node, 'A') &&
20622							!hasBrBeforeAfter(node) && !hasBrBeforeAfter(node, true)) {
20623							findTextNodeRelative(true, node);
20624						}
20625					}
20626				}
20627
20628				// Lean the start of the selection right if possible
20629				// So this: x[<b>x]</b>
20630				// Becomes: x<b>[x]</b>
20631				if (directionLeft && !collapsed && container.nodeType === 3 && offset === container.nodeValue.length) {
20632					findTextNodeRelative(false);
20633				}
20634
20635				// Set endpoint if it was normalized
20636				if (normalized) {
20637					rng['set' + (start ? 'Start' : 'End')](container, offset);
20638				}
20639			}
20640
20641			collapsed = rng.collapsed;
20642
20643			normalizeEndPoint(true);
20644
20645			if (!collapsed) {
20646				normalizeEndPoint();
20647			}
20648
20649			// If it was collapsed then make sure it still is
20650			if (normalized && collapsed) {
20651				rng.collapse(true);
20652			}
20653
20654			return normalized;
20655		};
20656	}
20657
20658	/**
20659	 * Compares two ranges and checks if they are equal.
20660	 *
20661	 * @static
20662	 * @method compareRanges
20663	 * @param {DOMRange} rng1 First range to compare.
20664	 * @param {DOMRange} rng2 First range to compare.
20665	 * @return {Boolean} true/false if the ranges are equal.
20666	 */
20667	RangeUtils.compareRanges = function(rng1, rng2) {
20668		if (rng1 && rng2) {
20669			// Compare native IE ranges
20670			if (rng1.item || rng1.duplicate) {
20671				// Both are control ranges and the selected element matches
20672				if (rng1.item && rng2.item && rng1.item(0) === rng2.item(0)) {
20673					return true;
20674				}
20675
20676				// Both are text ranges and the range matches
20677				if (rng1.isEqual && rng2.isEqual && rng2.isEqual(rng1)) {
20678					return true;
20679				}
20680			} else {
20681				// Compare w3c ranges
20682				return rng1.startContainer == rng2.startContainer && rng1.startOffset == rng2.startOffset;
20683			}
20684		}
20685
20686		return false;
20687	};
20688
20689	/**
20690	 * Finds the closest selection rect tries to get the range from that.
20691	 */
20692	function findClosestIeRange(clientX, clientY, doc) {
20693		var element, rng, rects;
20694
20695		element = doc.elementFromPoint(clientX, clientY);
20696		rng = doc.body.createTextRange();
20697
20698		if (element.tagName == 'HTML') {
20699			element = doc.body;
20700		}
20701
20702		rng.moveToElementText(element);
20703		rects = Tools.toArray(rng.getClientRects());
20704
20705		rects = rects.sort(function(a, b) {
20706			a = Math.abs(Math.max(a.top - clientY, a.bottom - clientY));
20707			b = Math.abs(Math.max(b.top - clientY, b.bottom - clientY));
20708
20709			return a - b;
20710		});
20711
20712		if (rects.length > 0) {
20713			clientY = (rects[0].bottom + rects[0].top) / 2;
20714
20715			try {
20716				rng.moveToPoint(clientX, clientY);
20717				rng.collapse(true);
20718
20719				return rng;
20720			} catch (ex) {
20721				// At least we tried
20722			}
20723		}
20724
20725		return null;
20726	}
20727
20728	/**
20729	 * Gets the caret range for the given x/y location.
20730	 *
20731	 * @static
20732	 * @method getCaretRangeFromPoint
20733	 * @param {Number} clientX X coordinate for range
20734	 * @param {Number} clientY Y coordinate for range
20735	 * @param {Document} doc Document that x/y are relative to
20736	 * @returns {Range} caret range
20737	 */
20738	RangeUtils.getCaretRangeFromPoint = function(clientX, clientY, doc) {
20739		var rng, point;
20740
20741		if (doc.caretPositionFromPoint) {
20742			point = doc.caretPositionFromPoint(clientX, clientY);
20743			rng = doc.createRange();
20744			rng.setStart(point.offsetNode, point.offset);
20745			rng.collapse(true);
20746		} else if (doc.caretRangeFromPoint) {
20747			rng = doc.caretRangeFromPoint(clientX, clientY);
20748		} else if (doc.body.createTextRange) {
20749			rng = doc.body.createTextRange();
20750
20751			try {
20752				rng.moveToPoint(clientX, clientY);
20753				rng.collapse(true);
20754			} catch (ex) {
20755				rng = findClosestIeRange(clientX, clientY, doc);
20756			}
20757		}
20758
20759		return rng;
20760	};
20761
20762	RangeUtils.getSelectedNode = function(range) {
20763		var startContainer = range.startContainer,
20764			startOffset = range.startOffset;
20765
20766		if (startContainer.hasChildNodes() && range.endOffset == startOffset + 1) {
20767			return startContainer.childNodes[startOffset];
20768		}
20769
20770		return null;
20771	};
20772
20773	RangeUtils.getNode = function(container, offset) {
20774		if (container.nodeType == 1 && container.hasChildNodes()) {
20775			if (offset >= container.childNodes.length) {
20776				offset = container.childNodes.length - 1;
20777			}
20778
20779			container = container.childNodes[offset];
20780		}
20781
20782		return container;
20783	};
20784
20785	return RangeUtils;
20786});
20787
20788// Included from: js/tinymce/classes/NodeChange.js
20789
20790/**
20791 * NodeChange.js
20792 *
20793 * Released under LGPL License.
20794 * Copyright (c) 1999-2015 Ephox Corp. All rights reserved
20795 *
20796 * License: http://www.tinymce.com/license
20797 * Contributing: http://www.tinymce.com/contributing
20798 */
20799
20800/**
20801 * This class handles the nodechange event dispatching both manual and through selection change events.
20802 *
20803 * @class tinymce.NodeChange
20804 * @private
20805 */
20806define("tinymce/NodeChange", [
20807	"tinymce/dom/RangeUtils",
20808	"tinymce/Env",
20809	"tinymce/util/Delay"
20810], function(RangeUtils, Env, Delay) {
20811	return function(editor) {
20812		var lastRng, lastPath = [];
20813
20814		/**
20815		 * Returns true/false if the current element path has been changed or not.
20816		 *
20817		 * @private
20818		 * @return {Boolean} True if the element path is the same false if it's not.
20819		 */
20820		function isSameElementPath(startElm) {
20821			var i, currentPath;
20822
20823			currentPath = editor.$(startElm).parentsUntil(editor.getBody()).add(startElm);
20824			if (currentPath.length === lastPath.length) {
20825				for (i = currentPath.length; i >= 0; i--) {
20826					if (currentPath[i] !== lastPath[i]) {
20827						break;
20828					}
20829				}
20830
20831				if (i === -1) {
20832					lastPath = currentPath;
20833					return true;
20834				}
20835			}
20836
20837			lastPath = currentPath;
20838
20839			return false;
20840		}
20841
20842		// Gecko doesn't support the "selectionchange" event
20843		if (!('onselectionchange' in editor.getDoc())) {
20844			editor.on('NodeChange Click MouseUp KeyUp Focus', function(e) {
20845				var nativeRng, fakeRng;
20846
20847				// Since DOM Ranges mutate on modification
20848				// of the DOM we need to clone it's contents
20849				nativeRng = editor.selection.getRng();
20850				fakeRng = {
20851					startContainer: nativeRng.startContainer,
20852					startOffset: nativeRng.startOffset,
20853					endContainer: nativeRng.endContainer,
20854					endOffset: nativeRng.endOffset
20855				};
20856
20857				// Always treat nodechange as a selectionchange since applying
20858				// formatting to the current range wouldn't update the range but it's parent
20859				if (e.type == 'nodechange' || !RangeUtils.compareRanges(fakeRng, lastRng)) {
20860					editor.fire('SelectionChange');
20861				}
20862
20863				lastRng = fakeRng;
20864			});
20865		}
20866
20867		// IE has a bug where it fires a selectionchange on right click that has a range at the start of the body
20868		// When the contextmenu event fires the selection is located at the right location
20869		editor.on('contextmenu', function() {
20870			editor.fire('SelectionChange');
20871		});
20872
20873		// Selection change is delayed ~200ms on IE when you click inside the current range
20874		editor.on('SelectionChange', function() {
20875			var startElm = editor.selection.getStart(true);
20876
20877			// IE 8 will fire a selectionchange event with an incorrect selection
20878			// when focusing out of table cells. Click inside cell -> toolbar = Invalid SelectionChange event
20879			if (!Env.range && editor.selection.isCollapsed()) {
20880				return;
20881			}
20882
20883			if (!isSameElementPath(startElm) && editor.dom.isChildOf(startElm, editor.getBody())) {
20884				editor.nodeChanged({selectionChange: true});
20885			}
20886		});
20887
20888		// Fire an extra nodeChange on mouseup for compatibility reasons
20889		editor.on('MouseUp', function(e) {
20890			if (!e.isDefaultPrevented()) {
20891				// Delay nodeChanged call for WebKit edge case issue where the range
20892				// isn't updated until after you click outside a selected image
20893				if (editor.selection.getNode().nodeName == 'IMG') {
20894					Delay.setEditorTimeout(editor, function() {
20895						editor.nodeChanged();
20896					});
20897				} else {
20898					editor.nodeChanged();
20899				}
20900			}
20901		});
20902
20903		/**
20904		 * Dispatches out a onNodeChange event to all observers. This method should be called when you
20905		 * need to update the UI states or element path etc.
20906		 *
20907		 * @method nodeChanged
20908		 * @param {Object} args Optional args to pass to NodeChange event handlers.
20909		 */
20910		this.nodeChanged = function(args) {
20911			var selection = editor.selection, node, parents, root;
20912
20913			// Fix for bug #1896577 it seems that this can not be fired while the editor is loading
20914			if (editor.initialized && selection && !editor.settings.disable_nodechange && !editor.readonly) {
20915				// Get start node
20916				root = editor.getBody();
20917				node = selection.getStart() || root;
20918				node = node.ownerDocument != editor.getDoc() ? editor.getBody() : node;
20919
20920				// Edge case for <p>|<img></p>
20921				if (node.nodeName == 'IMG' && selection.isCollapsed()) {
20922					node = node.parentNode;
20923				}
20924
20925				// Get parents and add them to object
20926				parents = [];
20927				editor.dom.getParent(node, function(node) {
20928					if (node === root) {
20929						return true;
20930					}
20931
20932					parents.push(node);
20933				});
20934
20935				args = args || {};
20936				args.element = node;
20937				args.parents = parents;
20938
20939				editor.fire('NodeChange', args);
20940			}
20941		};
20942	};
20943});
20944
20945// Included from: js/tinymce/classes/html/Node.js
20946
20947/**
20948 * Node.js
20949 *
20950 * Released under LGPL License.
20951 * Copyright (c) 1999-2015 Ephox Corp. All rights reserved
20952 *
20953 * License: http://www.tinymce.com/license
20954 * Contributing: http://www.tinymce.com/contributing
20955 */
20956
20957/**
20958 * This class is a minimalistic implementation of a DOM like node used by the DomParser class.
20959 *
20960 * @example
20961 * var node = new tinymce.html.Node('strong', 1);
20962 * someRoot.append(node);
20963 *
20964 * @class tinymce.html.Node
20965 * @version 3.4
20966 */
20967define("tinymce/html/Node", [], function() {
20968	var whiteSpaceRegExp = /^[ \t\r\n]*$/, typeLookup = {
20969		'#text': 3,
20970		'#comment': 8,
20971		'#cdata': 4,
20972		'#pi': 7,
20973		'#doctype': 10,
20974		'#document-fragment': 11
20975	};
20976
20977	// Walks the tree left/right
20978	function walk(node, root_node, prev) {
20979		var sibling, parent, startName = prev ? 'lastChild' : 'firstChild', siblingName = prev ? 'prev' : 'next';
20980
20981		// Walk into nodes if it has a start
20982		if (node[startName]) {
20983			return node[startName];
20984		}
20985
20986		// Return the sibling if it has one
20987		if (node !== root_node) {
20988			sibling = node[siblingName];
20989
20990			if (sibling) {
20991				return sibling;
20992			}
20993
20994			// Walk up the parents to look for siblings
20995			for (parent = node.parent; parent && parent !== root_node; parent = parent.parent) {
20996				sibling = parent[siblingName];
20997
20998				if (sibling) {
20999					return sibling;
21000				}
21001			}
21002		}
21003	}
21004
21005	/**
21006	 * Constructs a new Node instance.
21007	 *
21008	 * @constructor
21009	 * @method Node
21010	 * @param {String} name Name of the node type.
21011	 * @param {Number} type Numeric type representing the node.
21012	 */
21013	function Node(name, type) {
21014		this.name = name;
21015		this.type = type;
21016
21017		if (type === 1) {
21018			this.attributes = [];
21019			this.attributes.map = {};
21020		}
21021	}
21022
21023	Node.prototype = {
21024		/**
21025		 * Replaces the current node with the specified one.
21026		 *
21027		 * @example
21028		 * someNode.replace(someNewNode);
21029		 *
21030		 * @method replace
21031		 * @param {tinymce.html.Node} node Node to replace the current node with.
21032		 * @return {tinymce.html.Node} The old node that got replaced.
21033		 */
21034		replace: function(node) {
21035			var self = this;
21036
21037			if (node.parent) {
21038				node.remove();
21039			}
21040
21041			self.insert(node, self);
21042			self.remove();
21043
21044			return self;
21045		},
21046
21047		/**
21048		 * Gets/sets or removes an attribute by name.
21049		 *
21050		 * @example
21051		 * someNode.attr("name", "value"); // Sets an attribute
21052		 * console.log(someNode.attr("name")); // Gets an attribute
21053		 * someNode.attr("name", null); // Removes an attribute
21054		 *
21055		 * @method attr
21056		 * @param {String} name Attribute name to set or get.
21057		 * @param {String} value Optional value to set.
21058		 * @return {String/tinymce.html.Node} String or undefined on a get operation or the current node on a set operation.
21059		 */
21060		attr: function(name, value) {
21061			var self = this, attrs, i, undef;
21062
21063			if (typeof name !== "string") {
21064				for (i in name) {
21065					self.attr(i, name[i]);
21066				}
21067
21068				return self;
21069			}
21070
21071			if ((attrs = self.attributes)) {
21072				if (value !== undef) {
21073					// Remove attribute
21074					if (value === null) {
21075						if (name in attrs.map) {
21076							delete attrs.map[name];
21077
21078							i = attrs.length;
21079							while (i--) {
21080								if (attrs[i].name === name) {
21081									attrs = attrs.splice(i, 1);
21082									return self;
21083								}
21084							}
21085						}
21086
21087						return self;
21088					}
21089
21090					// Set attribute
21091					if (name in attrs.map) {
21092						// Set attribute
21093						i = attrs.length;
21094						while (i--) {
21095							if (attrs[i].name === name) {
21096								attrs[i].value = value;
21097								break;
21098							}
21099						}
21100					} else {
21101						attrs.push({name: name, value: value});
21102					}
21103
21104					attrs.map[name] = value;
21105
21106					return self;
21107				}
21108
21109				return attrs.map[name];
21110			}
21111		},
21112
21113		/**
21114		 * Does a shallow clones the node into a new node. It will also exclude id attributes since
21115		 * there should only be one id per document.
21116		 *
21117		 * @example
21118		 * var clonedNode = node.clone();
21119		 *
21120		 * @method clone
21121		 * @return {tinymce.html.Node} New copy of the original node.
21122		 */
21123		clone: function() {
21124			var self = this, clone = new Node(self.name, self.type), i, l, selfAttrs, selfAttr, cloneAttrs;
21125
21126			// Clone element attributes
21127			if ((selfAttrs = self.attributes)) {
21128				cloneAttrs = [];
21129				cloneAttrs.map = {};
21130
21131				for (i = 0, l = selfAttrs.length; i < l; i++) {
21132					selfAttr = selfAttrs[i];
21133
21134					// Clone everything except id
21135					if (selfAttr.name !== 'id') {
21136						cloneAttrs[cloneAttrs.length] = {name: selfAttr.name, value: selfAttr.value};
21137						cloneAttrs.map[selfAttr.name] = selfAttr.value;
21138					}
21139				}
21140
21141				clone.attributes = cloneAttrs;
21142			}
21143
21144			clone.value = self.value;
21145			clone.shortEnded = self.shortEnded;
21146
21147			return clone;
21148		},
21149
21150		/**
21151		 * Wraps the node in in another node.
21152		 *
21153		 * @example
21154		 * node.wrap(wrapperNode);
21155		 *
21156		 * @method wrap
21157		 */
21158		wrap: function(wrapper) {
21159			var self = this;
21160
21161			self.parent.insert(wrapper, self);
21162			wrapper.append(self);
21163
21164			return self;
21165		},
21166
21167		/**
21168		 * Unwraps the node in other words it removes the node but keeps the children.
21169		 *
21170		 * @example
21171		 * node.unwrap();
21172		 *
21173		 * @method unwrap
21174		 */
21175		unwrap: function() {
21176			var self = this, node, next;
21177
21178			for (node = self.firstChild; node;) {
21179				next = node.next;
21180				self.insert(node, self, true);
21181				node = next;
21182			}
21183
21184			self.remove();
21185		},
21186
21187		/**
21188		 * Removes the node from it's parent.
21189		 *
21190		 * @example
21191		 * node.remove();
21192		 *
21193		 * @method remove
21194		 * @return {tinymce.html.Node} Current node that got removed.
21195		 */
21196		remove: function() {
21197			var self = this, parent = self.parent, next = self.next, prev = self.prev;
21198
21199			if (parent) {
21200				if (parent.firstChild === self) {
21201					parent.firstChild = next;
21202
21203					if (next) {
21204						next.prev = null;
21205					}
21206				} else {
21207					prev.next = next;
21208				}
21209
21210				if (parent.lastChild === self) {
21211					parent.lastChild = prev;
21212
21213					if (prev) {
21214						prev.next = null;
21215					}
21216				} else {
21217					next.prev = prev;
21218				}
21219
21220				self.parent = self.next = self.prev = null;
21221			}
21222
21223			return self;
21224		},
21225
21226		/**
21227		 * Appends a new node as a child of the current node.
21228		 *
21229		 * @example
21230		 * node.append(someNode);
21231		 *
21232		 * @method append
21233		 * @param {tinymce.html.Node} node Node to append as a child of the current one.
21234		 * @return {tinymce.html.Node} The node that got appended.
21235		 */
21236		append: function(node) {
21237			var self = this, last;
21238
21239			if (node.parent) {
21240				node.remove();
21241			}
21242
21243			last = self.lastChild;
21244			if (last) {
21245				last.next = node;
21246				node.prev = last;
21247				self.lastChild = node;
21248			} else {
21249				self.lastChild = self.firstChild = node;
21250			}
21251
21252			node.parent = self;
21253
21254			return node;
21255		},
21256
21257		/**
21258		 * Inserts a node at a specific position as a child of the current node.
21259		 *
21260		 * @example
21261		 * parentNode.insert(newChildNode, oldChildNode);
21262		 *
21263		 * @method insert
21264		 * @param {tinymce.html.Node} node Node to insert as a child of the current node.
21265		 * @param {tinymce.html.Node} ref_node Reference node to set node before/after.
21266		 * @param {Boolean} before Optional state to insert the node before the reference node.
21267		 * @return {tinymce.html.Node} The node that got inserted.
21268		 */
21269		insert: function(node, ref_node, before) {
21270			var parent;
21271
21272			if (node.parent) {
21273				node.remove();
21274			}
21275
21276			parent = ref_node.parent || this;
21277
21278			if (before) {
21279				if (ref_node === parent.firstChild) {
21280					parent.firstChild = node;
21281				} else {
21282					ref_node.prev.next = node;
21283				}
21284
21285				node.prev = ref_node.prev;
21286				node.next = ref_node;
21287				ref_node.prev = node;
21288			} else {
21289				if (ref_node === parent.lastChild) {
21290					parent.lastChild = node;
21291				} else {
21292					ref_node.next.prev = node;
21293				}
21294
21295				node.next = ref_node.next;
21296				node.prev = ref_node;
21297				ref_node.next = node;
21298			}
21299
21300			node.parent = parent;
21301
21302			return node;
21303		},
21304
21305		/**
21306		 * Get all children by name.
21307		 *
21308		 * @method getAll
21309		 * @param {String} name Name of the child nodes to collect.
21310		 * @return {Array} Array with child nodes matchin the specified name.
21311		 */
21312		getAll: function(name) {
21313			var self = this, node, collection = [];
21314
21315			for (node = self.firstChild; node; node = walk(node, self)) {
21316				if (node.name === name) {
21317					collection.push(node);
21318				}
21319			}
21320
21321			return collection;
21322		},
21323
21324		/**
21325		 * Removes all children of the current node.
21326		 *
21327		 * @method empty
21328		 * @return {tinymce.html.Node} The current node that got cleared.
21329		 */
21330		empty: function() {
21331			var self = this, nodes, i, node;
21332
21333			// Remove all children
21334			if (self.firstChild) {
21335				nodes = [];
21336
21337				// Collect the children
21338				for (node = self.firstChild; node; node = walk(node, self)) {
21339					nodes.push(node);
21340				}
21341
21342				// Remove the children
21343				i = nodes.length;
21344				while (i--) {
21345					node = nodes[i];
21346					node.parent = node.firstChild = node.lastChild = node.next = node.prev = null;
21347				}
21348			}
21349
21350			self.firstChild = self.lastChild = null;
21351
21352			return self;
21353		},
21354
21355		/**
21356		 * Returns true/false if the node is to be considered empty or not.
21357		 *
21358		 * @example
21359		 * node.isEmpty({img: true});
21360		 * @method isEmpty
21361		 * @param {Object} elements Name/value object with elements that are automatically treated as non empty elements.
21362		 * @return {Boolean} true/false if the node is empty or not.
21363		 */
21364		isEmpty: function(elements) {
21365			var self = this, node = self.firstChild, i, name;
21366
21367			if (node) {
21368				do {
21369					if (node.type === 1) {
21370						// Ignore bogus elements
21371						if (node.attributes.map['data-mce-bogus']) {
21372							continue;
21373						}
21374
21375						// Keep empty elements like <img />
21376						if (elements[node.name]) {
21377							return false;
21378						}
21379
21380						// Keep bookmark nodes and name attribute like <a name="1"></a>
21381						i = node.attributes.length;
21382						while (i--) {
21383							name = node.attributes[i].name;
21384							if (name === "name" || name.indexOf('data-mce-bookmark') === 0) {
21385								return false;
21386							}
21387						}
21388					}
21389
21390					// Keep comments
21391					if (node.type === 8) {
21392						return false;
21393					}
21394
21395					// Keep non whitespace text nodes
21396					if ((node.type === 3 && !whiteSpaceRegExp.test(node.value))) {
21397						return false;
21398					}
21399				} while ((node = walk(node, self)));
21400			}
21401
21402			return true;
21403		},
21404
21405		/**
21406		 * Walks to the next or previous node and returns that node or null if it wasn't found.
21407		 *
21408		 * @method walk
21409		 * @param {Boolean} prev Optional previous node state defaults to false.
21410		 * @return {tinymce.html.Node} Node that is next to or previous of the current node.
21411		 */
21412		walk: function(prev) {
21413			return walk(this, null, prev);
21414		}
21415	};
21416
21417	/**
21418	 * Creates a node of a specific type.
21419	 *
21420	 * @static
21421	 * @method create
21422	 * @param {String} name Name of the node type to create for example "b" or "#text".
21423	 * @param {Object} attrs Name/value collection of attributes that will be applied to elements.
21424	 */
21425	Node.create = function(name, attrs) {
21426		var node, attrName;
21427
21428		// Create node
21429		node = new Node(name, typeLookup[name] || 1);
21430
21431		// Add attributes if needed
21432		if (attrs) {
21433			for (attrName in attrs) {
21434				node.attr(attrName, attrs[attrName]);
21435			}
21436		}
21437
21438		return node;
21439	};
21440
21441	return Node;
21442});
21443
21444// Included from: js/tinymce/classes/html/Schema.js
21445
21446/**
21447 * Schema.js
21448 *
21449 * Released under LGPL License.
21450 * Copyright (c) 1999-2015 Ephox Corp. All rights reserved
21451 *
21452 * License: http://www.tinymce.com/license
21453 * Contributing: http://www.tinymce.com/contributing
21454 */
21455
21456/**
21457 * Schema validator class.
21458 *
21459 * @class tinymce.html.Schema
21460 * @example
21461 *  if (tinymce.activeEditor.schema.isValidChild('p', 'span'))
21462 *    alert('span is valid child of p.');
21463 *
21464 *  if (tinymce.activeEditor.schema.getElementRule('p'))
21465 *    alert('P is a valid element.');
21466 *
21467 * @class tinymce.html.Schema
21468 * @version 3.4
21469 */
21470define("tinymce/html/Schema", [
21471	"tinymce/util/Tools"
21472], function(Tools) {
21473	var mapCache = {}, dummyObj = {};
21474	var makeMap = Tools.makeMap, each = Tools.each, extend = Tools.extend, explode = Tools.explode, inArray = Tools.inArray;
21475
21476	function split(items, delim) {
21477		return items ? items.split(delim || ' ') : [];
21478	}
21479
21480	/**
21481	 * Builds a schema lookup table
21482	 *
21483	 * @private
21484	 * @param {String} type html4, html5 or html5-strict schema type.
21485	 * @return {Object} Schema lookup table.
21486	 */
21487	function compileSchema(type) {
21488		var schema = {}, globalAttributes, blockContent;
21489		var phrasingContent, flowContent, html4BlockContent, html4PhrasingContent;
21490
21491		function add(name, attributes, children) {
21492			var ni, i, attributesOrder, args = arguments;
21493
21494			function arrayToMap(array, obj) {
21495				var map = {}, i, l;
21496
21497				for (i = 0, l = array.length; i < l; i++) {
21498					map[array[i]] = obj || {};
21499				}
21500
21501				return map;
21502			}
21503
21504			children = children || [];
21505			attributes = attributes || "";
21506
21507			if (typeof children === "string") {
21508				children = split(children);
21509			}
21510
21511			// Split string children
21512			for (i = 3; i < args.length; i++) {
21513				if (typeof args[i] === "string") {
21514					args[i] = split(args[i]);
21515				}
21516
21517				children.push.apply(children, args[i]);
21518			}
21519
21520			name = split(name);
21521			ni = name.length;
21522			while (ni--) {
21523				attributesOrder = [].concat(globalAttributes, split(attributes));
21524				schema[name[ni]] = {
21525					attributes: arrayToMap(attributesOrder),
21526					attributesOrder: attributesOrder,
21527					children: arrayToMap(children, dummyObj)
21528				};
21529			}
21530		}
21531
21532		function addAttrs(name, attributes) {
21533			var ni, schemaItem, i, l;
21534
21535			name = split(name);
21536			ni = name.length;
21537			attributes = split(attributes);
21538			while (ni--) {
21539				schemaItem = schema[name[ni]];
21540				for (i = 0, l = attributes.length; i < l; i++) {
21541					schemaItem.attributes[attributes[i]] = {};
21542					schemaItem.attributesOrder.push(attributes[i]);
21543				}
21544			}
21545		}
21546
21547		// Use cached schema
21548		if (mapCache[type]) {
21549			return mapCache[type];
21550		}
21551
21552		// Attributes present on all elements
21553		globalAttributes = split("id accesskey class dir lang style tabindex title");
21554
21555		// Event attributes can be opt-in/opt-out
21556		/*eventAttributes = split("onabort onblur oncancel oncanplay oncanplaythrough onchange onclick onclose oncontextmenu oncuechange " +
21557				"ondblclick ondrag ondragend ondragenter ondragleave ondragover ondragstart ondrop ondurationchange onemptied onended " +
21558				"onerror onfocus oninput oninvalid onkeydown onkeypress onkeyup onload onloadeddata onloadedmetadata onloadstart " +
21559				"onmousedown onmousemove onmouseout onmouseover onmouseup onmousewheel onpause onplay onplaying onprogress onratechange " +
21560				"onreset onscroll onseeked onseeking onseeking onselect onshow onstalled onsubmit onsuspend ontimeupdate onvolumechange " +
21561				"onwaiting"
21562		);*/
21563
21564		// Block content elements
21565		blockContent = split(
21566			"address blockquote div dl fieldset form h1 h2 h3 h4 h5 h6 hr menu ol p pre table ul"
21567		);
21568
21569		// Phrasing content elements from the HTML5 spec (inline)
21570		phrasingContent = split(
21571			"a abbr b bdo br button cite code del dfn em embed i iframe img input ins kbd " +
21572			"label map noscript object q s samp script select small span strong sub sup " +
21573			"textarea u var #text #comment"
21574		);
21575
21576		// Add HTML5 items to globalAttributes, blockContent, phrasingContent
21577		if (type != "html4") {
21578			globalAttributes.push.apply(globalAttributes, split("contenteditable contextmenu draggable dropzone " +
21579				"hidden spellcheck translate"));
21580			blockContent.push.apply(blockContent, split("article aside details dialog figure header footer hgroup section nav"));
21581			phrasingContent.push.apply(phrasingContent, split("audio canvas command datalist mark meter output picture " +
21582				"progress time wbr video ruby bdi keygen"));
21583		}
21584
21585		// Add HTML4 elements unless it's html5-strict
21586		if (type != "html5-strict") {
21587			globalAttributes.push("xml:lang");
21588
21589			html4PhrasingContent = split("acronym applet basefont big font strike tt");
21590			phrasingContent.push.apply(phrasingContent, html4PhrasingContent);
21591
21592			each(html4PhrasingContent, function(name) {
21593				add(name, "", phrasingContent);
21594			});
21595
21596			html4BlockContent = split("center dir isindex noframes");
21597			blockContent.push.apply(blockContent, html4BlockContent);
21598
21599			// Flow content elements from the HTML5 spec (block+inline)
21600			flowContent = [].concat(blockContent, phrasingContent);
21601
21602			each(html4BlockContent, function(name) {
21603				add(name, "", flowContent);
21604			});
21605		}
21606
21607		// Flow content elements from the HTML5 spec (block+inline)
21608		flowContent = flowContent || [].concat(blockContent, phrasingContent);
21609
21610		// HTML4 base schema TODO: Move HTML5 specific attributes to HTML5 specific if statement
21611		// Schema items <element name>, <specific attributes>, <children ..>
21612		add("html", "manifest", "head body");
21613		add("head", "", "base command link meta noscript script style title");
21614		add("title hr noscript br");
21615		add("base", "href target");
21616		add("link", "href rel media hreflang type sizes hreflang");
21617		add("meta", "name http-equiv content charset");
21618		add("style", "media type scoped");
21619		add("script", "src async defer type charset");
21620		add("body", "onafterprint onbeforeprint onbeforeunload onblur onerror onfocus " +
21621				"onhashchange onload onmessage onoffline ononline onpagehide onpageshow " +
21622				"onpopstate onresize onscroll onstorage onunload", flowContent);
21623		add("address dt dd div caption", "", flowContent);
21624		add("h1 h2 h3 h4 h5 h6 pre p abbr code var samp kbd sub sup i b u bdo span legend em strong small s cite dfn", "", phrasingContent);
21625		add("blockquote", "cite", flowContent);
21626		add("ol", "reversed start type", "li");
21627		add("ul", "", "li");
21628		add("li", "value", flowContent);
21629		add("dl", "", "dt dd");
21630		add("a", "href target rel media hreflang type", phrasingContent);
21631		add("q", "cite", phrasingContent);
21632		add("ins del", "cite datetime", flowContent);
21633		add("img", "src sizes srcset alt usemap ismap width height");
21634		add("iframe", "src name width height", flowContent);
21635		add("embed", "src type width height");
21636		add("object", "data type typemustmatch name usemap form width height", flowContent, "param");
21637		add("param", "name value");
21638		add("map", "name", flowContent, "area");
21639		add("area", "alt coords shape href target rel media hreflang type");
21640		add("table", "border", "caption colgroup thead tfoot tbody tr" + (type == "html4" ? " col" : ""));
21641		add("colgroup", "span", "col");
21642		add("col", "span");
21643		add("tbody thead tfoot", "", "tr");
21644		add("tr", "", "td th");
21645		add("td", "colspan rowspan headers", flowContent);
21646		add("th", "colspan rowspan headers scope abbr", flowContent);
21647		add("form", "accept-charset action autocomplete enctype method name novalidate target", flowContent);
21648		add("fieldset", "disabled form name", flowContent, "legend");
21649		add("label", "form for", phrasingContent);
21650		add("input", "accept alt autocomplete checked dirname disabled form formaction formenctype formmethod formnovalidate " +
21651				"formtarget height list max maxlength min multiple name pattern readonly required size src step type value width"
21652		);
21653		add("button", "disabled form formaction formenctype formmethod formnovalidate formtarget name type value",
21654			type == "html4" ? flowContent : phrasingContent);
21655		add("select", "disabled form multiple name required size", "option optgroup");
21656		add("optgroup", "disabled label", "option");
21657		add("option", "disabled label selected value");
21658		add("textarea", "cols dirname disabled form maxlength name readonly required rows wrap");
21659		add("menu", "type label", flowContent, "li");
21660		add("noscript", "", flowContent);
21661
21662		// Extend with HTML5 elements
21663		if (type != "html4") {
21664			add("wbr");
21665			add("ruby", "", phrasingContent, "rt rp");
21666			add("figcaption", "", flowContent);
21667			add("mark rt rp summary bdi", "", phrasingContent);
21668			add("canvas", "width height", flowContent);
21669			add("video", "src crossorigin poster preload autoplay mediagroup loop " +
21670				"muted controls width height buffered", flowContent, "track source");
21671			add("audio", "src crossorigin preload autoplay mediagroup loop muted controls buffered volume", flowContent, "track source");
21672			add("picture", "", "img source");
21673			add("source", "src srcset type media sizes");
21674			add("track", "kind src srclang label default");
21675			add("datalist", "", phrasingContent, "option");
21676			add("article section nav aside header footer", "", flowContent);
21677			add("hgroup", "", "h1 h2 h3 h4 h5 h6");
21678			add("figure", "", flowContent, "figcaption");
21679			add("time", "datetime", phrasingContent);
21680			add("dialog", "open", flowContent);
21681			add("command", "type label icon disabled checked radiogroup command");
21682			add("output", "for form name", phrasingContent);
21683			add("progress", "value max", phrasingContent);
21684			add("meter", "value min max low high optimum", phrasingContent);
21685			add("details", "open", flowContent, "summary");
21686			add("keygen", "autofocus challenge disabled form keytype name");
21687		}
21688
21689		// Extend with HTML4 attributes unless it's html5-strict
21690		if (type != "html5-strict") {
21691			addAttrs("script", "language xml:space");
21692			addAttrs("style", "xml:space");
21693			addAttrs("object", "declare classid code codebase codetype archive standby align border hspace vspace");
21694			addAttrs("embed", "align name hspace vspace");
21695			addAttrs("param", "valuetype type");
21696			addAttrs("a", "charset name rev shape coords");
21697			addAttrs("br", "clear");
21698			addAttrs("applet", "codebase archive code object alt name width height align hspace vspace");
21699			addAttrs("img", "name longdesc align border hspace vspace");
21700			addAttrs("iframe", "longdesc frameborder marginwidth marginheight scrolling align");
21701			addAttrs("font basefont", "size color face");
21702			addAttrs("input", "usemap align");
21703			addAttrs("select", "onchange");
21704			addAttrs("textarea");
21705			addAttrs("h1 h2 h3 h4 h5 h6 div p legend caption", "align");
21706			addAttrs("ul", "type compact");
21707			addAttrs("li", "type");
21708			addAttrs("ol dl menu dir", "compact");
21709			addAttrs("pre", "width xml:space");
21710			addAttrs("hr", "align noshade size width");
21711			addAttrs("isindex", "prompt");
21712			addAttrs("table", "summary width frame rules cellspacing cellpadding align bgcolor");
21713			addAttrs("col", "width align char charoff valign");
21714			addAttrs("colgroup", "width align char charoff valign");
21715			addAttrs("thead", "align char charoff valign");
21716			addAttrs("tr", "align char charoff valign bgcolor");
21717			addAttrs("th", "axis align char charoff valign nowrap bgcolor width height");
21718			addAttrs("form", "accept");
21719			addAttrs("td", "abbr axis scope align char charoff valign nowrap bgcolor width height");
21720			addAttrs("tfoot", "align char charoff valign");
21721			addAttrs("tbody", "align char charoff valign");
21722			addAttrs("area", "nohref");
21723			addAttrs("body", "background bgcolor text link vlink alink");
21724		}
21725
21726		// Extend with HTML5 attributes unless it's html4
21727		if (type != "html4") {
21728			addAttrs("input button select textarea", "autofocus");
21729			addAttrs("input textarea", "placeholder");
21730			addAttrs("a", "download");
21731			addAttrs("link script img", "crossorigin");
21732			addAttrs("iframe", "sandbox seamless allowfullscreen"); // Excluded: srcdoc
21733		}
21734
21735		// Special: iframe, ruby, video, audio, label
21736
21737		// Delete children of the same name from it's parent
21738		// For example: form can't have a child of the name form
21739		each(split('a form meter progress dfn'), function(name) {
21740			if (schema[name]) {
21741				delete schema[name].children[name];
21742			}
21743		});
21744
21745		// Delete header, footer, sectioning and heading content descendants
21746		/*each('dt th address', function(name) {
21747			delete schema[name].children[name];
21748		});*/
21749
21750		// Caption can't have tables
21751		delete schema.caption.children.table;
21752
21753		// Delete scripts by default due to possible XSS
21754		delete schema.script;
21755
21756		// TODO: LI:s can only have value if parent is OL
21757
21758		// TODO: Handle transparent elements
21759		// a ins del canvas map
21760
21761		mapCache[type] = schema;
21762
21763		return schema;
21764	}
21765
21766	function compileElementMap(value, mode) {
21767		var styles;
21768
21769		if (value) {
21770			styles = {};
21771
21772			if (typeof value == 'string') {
21773				value = {
21774					'*': value
21775				};
21776			}
21777
21778			// Convert styles into a rule list
21779			each(value, function(value, key) {
21780				styles[key] = styles[key.toUpperCase()] = mode == 'map' ? makeMap(value, /[, ]/) : explode(value, /[, ]/);
21781			});
21782		}
21783
21784		return styles;
21785	}
21786
21787	/**
21788	 * Constructs a new Schema instance.
21789	 *
21790	 * @constructor
21791	 * @method Schema
21792	 * @param {Object} settings Name/value settings object.
21793	 */
21794	return function(settings) {
21795		var self = this, elements = {}, children = {}, patternElements = [], validStyles, invalidStyles, schemaItems;
21796		var whiteSpaceElementsMap, selfClosingElementsMap, shortEndedElementsMap, boolAttrMap, validClasses;
21797		var blockElementsMap, nonEmptyElementsMap, moveCaretBeforeOnEnterElementsMap, textBlockElementsMap, textInlineElementsMap;
21798		var customElementsMap = {}, specialElements = {};
21799
21800		// Creates an lookup table map object for the specified option or the default value
21801		function createLookupTable(option, default_value, extendWith) {
21802			var value = settings[option];
21803
21804			if (!value) {
21805				// Get cached default map or make it if needed
21806				value = mapCache[option];
21807
21808				if (!value) {
21809					value = makeMap(default_value, ' ', makeMap(default_value.toUpperCase(), ' '));
21810					value = extend(value, extendWith);
21811
21812					mapCache[option] = value;
21813				}
21814			} else {
21815				// Create custom map
21816				value = makeMap(value, /[, ]/, makeMap(value.toUpperCase(), /[, ]/));
21817			}
21818
21819			return value;
21820		}
21821
21822		settings = settings || {};
21823		schemaItems = compileSchema(settings.schema);
21824
21825		// Allow all elements and attributes if verify_html is set to false
21826		if (settings.verify_html === false) {
21827			settings.valid_elements = '*[*]';
21828		}
21829
21830		validStyles = compileElementMap(settings.valid_styles);
21831		invalidStyles = compileElementMap(settings.invalid_styles, 'map');
21832		validClasses = compileElementMap(settings.valid_classes, 'map');
21833
21834		// Setup map objects
21835		whiteSpaceElementsMap = createLookupTable('whitespace_elements', 'pre script noscript style textarea video audio iframe object');
21836		selfClosingElementsMap = createLookupTable('self_closing_elements', 'colgroup dd dt li option p td tfoot th thead tr');
21837		shortEndedElementsMap = createLookupTable('short_ended_elements', 'area base basefont br col frame hr img input isindex link ' +
21838			'meta param embed source wbr track');
21839		boolAttrMap = createLookupTable('boolean_attributes', 'checked compact declare defer disabled ismap multiple nohref noresize ' +
21840			'noshade nowrap readonly selected autoplay loop controls');
21841		nonEmptyElementsMap = createLookupTable('non_empty_elements', 'td th iframe video audio object script', shortEndedElementsMap);
21842		moveCaretBeforeOnEnterElementsMap = createLookupTable('move_caret_before_on_enter_elements', 'table', nonEmptyElementsMap);
21843		textBlockElementsMap = createLookupTable('text_block_elements', 'h1 h2 h3 h4 h5 h6 p div address pre form ' +
21844						'blockquote center dir fieldset header footer article section hgroup aside nav figure');
21845		blockElementsMap = createLookupTable('block_elements', 'hr table tbody thead tfoot ' +
21846						'th tr td li ol ul caption dl dt dd noscript menu isindex option ' +
21847						'datalist select optgroup figcaption', textBlockElementsMap);
21848		textInlineElementsMap = createLookupTable('text_inline_elements', 'span strong b em i font strike u var cite ' +
21849										'dfn code mark q sup sub samp');
21850
21851		each((settings.special || 'script noscript style textarea').split(' '), function(name) {
21852			specialElements[name] = new RegExp('<\/' + name + '[^>]*>', 'gi');
21853		});
21854
21855		// Converts a wildcard expression string to a regexp for example *a will become /.*a/.
21856		function patternToRegExp(str) {
21857			return new RegExp('^' + str.replace(/([?+*])/g, '.$1') + '$');
21858		}
21859
21860		// Parses the specified valid_elements string and adds to the current rules
21861		// This function is a bit hard to read since it's heavily optimized for speed
21862		function addValidElements(validElements) {
21863			var ei, el, ai, al, matches, element, attr, attrData, elementName, attrName, attrType, attributes, attributesOrder,
21864				prefix, outputName, globalAttributes, globalAttributesOrder, key, value,
21865				elementRuleRegExp = /^([#+\-])?([^\[!\/]+)(?:\/([^\[!]+))?(?:(!?)\[([^\]]+)\])?$/,
21866				attrRuleRegExp = /^([!\-])?(\w+::\w+|[^=:<]+)?(?:([=:<])(.*))?$/,
21867				hasPatternsRegExp = /[*?+]/;
21868
21869			if (validElements) {
21870				// Split valid elements into an array with rules
21871				validElements = split(validElements, ',');
21872
21873				if (elements['@']) {
21874					globalAttributes = elements['@'].attributes;
21875					globalAttributesOrder = elements['@'].attributesOrder;
21876				}
21877
21878				// Loop all rules
21879				for (ei = 0, el = validElements.length; ei < el; ei++) {
21880					// Parse element rule
21881					matches = elementRuleRegExp.exec(validElements[ei]);
21882					if (matches) {
21883						// Setup local names for matches
21884						prefix = matches[1];
21885						elementName = matches[2];
21886						outputName = matches[3];
21887						attrData = matches[5];
21888
21889						// Create new attributes and attributesOrder
21890						attributes = {};
21891						attributesOrder = [];
21892
21893						// Create the new element
21894						element = {
21895							attributes: attributes,
21896							attributesOrder: attributesOrder
21897						};
21898
21899						// Padd empty elements prefix
21900						if (prefix === '#') {
21901							element.paddEmpty = true;
21902						}
21903
21904						// Remove empty elements prefix
21905						if (prefix === '-') {
21906							element.removeEmpty = true;
21907						}
21908
21909						if (matches[4] === '!') {
21910							element.removeEmptyAttrs = true;
21911						}
21912
21913						// Copy attributes from global rule into current rule
21914						if (globalAttributes) {
21915							for (key in globalAttributes) {
21916								attributes[key] = globalAttributes[key];
21917							}
21918
21919							attributesOrder.push.apply(attributesOrder, globalAttributesOrder);
21920						}
21921
21922						// Attributes defined
21923						if (attrData) {
21924							attrData = split(attrData, '|');
21925							for (ai = 0, al = attrData.length; ai < al; ai++) {
21926								matches = attrRuleRegExp.exec(attrData[ai]);
21927								if (matches) {
21928									attr = {};
21929									attrType = matches[1];
21930									attrName = matches[2].replace(/::/g, ':');
21931									prefix = matches[3];
21932									value = matches[4];
21933
21934									// Required
21935									if (attrType === '!') {
21936										element.attributesRequired = element.attributesRequired || [];
21937										element.attributesRequired.push(attrName);
21938										attr.required = true;
21939									}
21940
21941									// Denied from global
21942									if (attrType === '-') {
21943										delete attributes[attrName];
21944										attributesOrder.splice(inArray(attributesOrder, attrName), 1);
21945										continue;
21946									}
21947
21948									// Default value
21949									if (prefix) {
21950										// Default value
21951										if (prefix === '=') {
21952											element.attributesDefault = element.attributesDefault || [];
21953											element.attributesDefault.push({name: attrName, value: value});
21954											attr.defaultValue = value;
21955										}
21956
21957										// Forced value
21958										if (prefix === ':') {
21959											element.attributesForced = element.attributesForced || [];
21960											element.attributesForced.push({name: attrName, value: value});
21961											attr.forcedValue = value;
21962										}
21963
21964										// Required values
21965										if (prefix === '<') {
21966											attr.validValues = makeMap(value, '?');
21967										}
21968									}
21969
21970									// Check for attribute patterns
21971									if (hasPatternsRegExp.test(attrName)) {
21972										element.attributePatterns = element.attributePatterns || [];
21973										attr.pattern = patternToRegExp(attrName);
21974										element.attributePatterns.push(attr);
21975									} else {
21976										// Add attribute to order list if it doesn't already exist
21977										if (!attributes[attrName]) {
21978											attributesOrder.push(attrName);
21979										}
21980
21981										attributes[attrName] = attr;
21982									}
21983								}
21984							}
21985						}
21986
21987						// Global rule, store away these for later usage
21988						if (!globalAttributes && elementName == '@') {
21989							globalAttributes = attributes;
21990							globalAttributesOrder = attributesOrder;
21991						}
21992
21993						// Handle substitute elements such as b/strong
21994						if (outputName) {
21995							element.outputName = elementName;
21996							elements[outputName] = element;
21997						}
21998
21999						// Add pattern or exact element
22000						if (hasPatternsRegExp.test(elementName)) {
22001							element.pattern = patternToRegExp(elementName);
22002							patternElements.push(element);
22003						} else {
22004							elements[elementName] = element;
22005						}
22006					}
22007				}
22008			}
22009		}
22010
22011		function setValidElements(validElements) {
22012			elements = {};
22013			patternElements = [];
22014
22015			addValidElements(validElements);
22016
22017			each(schemaItems, function(element, name) {
22018				children[name] = element.children;
22019			});
22020		}
22021
22022		// Adds custom non HTML elements to the schema
22023		function addCustomElements(customElements) {
22024			var customElementRegExp = /^(~)?(.+)$/;
22025
22026			if (customElements) {
22027				// Flush cached items since we are altering the default maps
22028				mapCache.text_block_elements = mapCache.block_elements = null;
22029
22030				each(split(customElements, ','), function(rule) {
22031					var matches = customElementRegExp.exec(rule),
22032						inline = matches[1] === '~',
22033						cloneName = inline ? 'span' : 'div',
22034						name = matches[2];
22035
22036					children[name] = children[cloneName];
22037					customElementsMap[name] = cloneName;
22038
22039					// If it's not marked as inline then add it to valid block elements
22040					if (!inline) {
22041						blockElementsMap[name.toUpperCase()] = {};
22042						blockElementsMap[name] = {};
22043					}
22044
22045					// Add elements clone if needed
22046					if (!elements[name]) {
22047						var customRule = elements[cloneName];
22048
22049						customRule = extend({}, customRule);
22050						delete customRule.removeEmptyAttrs;
22051						delete customRule.removeEmpty;
22052
22053						elements[name] = customRule;
22054					}
22055
22056					// Add custom elements at span/div positions
22057					each(children, function(element, elmName) {
22058						if (element[cloneName]) {
22059							children[elmName] = element = extend({}, children[elmName]);
22060							element[name] = element[cloneName];
22061						}
22062					});
22063				});
22064			}
22065		}
22066
22067		// Adds valid children to the schema object
22068		function addValidChildren(validChildren) {
22069			var childRuleRegExp = /^([+\-]?)(\w+)\[([^\]]+)\]$/;
22070
22071			// Invalidate the schema cache if the schema is mutated
22072			mapCache[settings.schema] = null;
22073
22074			if (validChildren) {
22075				each(split(validChildren, ','), function(rule) {
22076					var matches = childRuleRegExp.exec(rule), parent, prefix;
22077
22078					if (matches) {
22079						prefix = matches[1];
22080
22081						// Add/remove items from default
22082						if (prefix) {
22083							parent = children[matches[2]];
22084						} else {
22085							parent = children[matches[2]] = {'#comment': {}};
22086						}
22087
22088						parent = children[matches[2]];
22089
22090						each(split(matches[3], '|'), function(child) {
22091							if (prefix === '-') {
22092								delete parent[child];
22093							} else {
22094								parent[child] = {};
22095							}
22096						});
22097					}
22098				});
22099			}
22100		}
22101
22102		function getElementRule(name) {
22103			var element = elements[name], i;
22104
22105			// Exact match found
22106			if (element) {
22107				return element;
22108			}
22109
22110			// No exact match then try the patterns
22111			i = patternElements.length;
22112			while (i--) {
22113				element = patternElements[i];
22114
22115				if (element.pattern.test(name)) {
22116					return element;
22117				}
22118			}
22119		}
22120
22121		if (!settings.valid_elements) {
22122			// No valid elements defined then clone the elements from the schema spec
22123			each(schemaItems, function(element, name) {
22124				elements[name] = {
22125					attributes: element.attributes,
22126					attributesOrder: element.attributesOrder
22127				};
22128
22129				children[name] = element.children;
22130			});
22131
22132			// Switch these on HTML4
22133			if (settings.schema != "html5") {
22134				each(split('strong/b em/i'), function(item) {
22135					item = split(item, '/');
22136					elements[item[1]].outputName = item[0];
22137				});
22138			}
22139
22140			// Add default alt attribute for images
22141			elements.img.attributesDefault = [{name: 'alt', value: ''}];
22142
22143			// Remove these if they are empty by default
22144			each(split('ol ul sub sup blockquote span font a table tbody tr strong em b i'), function(name) {
22145				if (elements[name]) {
22146					elements[name].removeEmpty = true;
22147				}
22148			});
22149
22150			// Padd these by default
22151			each(split('p h1 h2 h3 h4 h5 h6 th td pre div address caption'), function(name) {
22152				elements[name].paddEmpty = true;
22153			});
22154
22155			// Remove these if they have no attributes
22156			each(split('span'), function(name) {
22157				elements[name].removeEmptyAttrs = true;
22158			});
22159
22160			// Remove these by default
22161			// TODO: Reenable in 4.1
22162			/*each(split('script style'), function(name) {
22163				delete elements[name];
22164			});*/
22165		} else {
22166			setValidElements(settings.valid_elements);
22167		}
22168
22169		addCustomElements(settings.custom_elements);
22170		addValidChildren(settings.valid_children);
22171		addValidElements(settings.extended_valid_elements);
22172
22173		// Todo: Remove this when we fix list handling to be valid
22174		addValidChildren('+ol[ul|ol],+ul[ul|ol]');
22175
22176		// Delete invalid elements
22177		if (settings.invalid_elements) {
22178			each(explode(settings.invalid_elements), function(item) {
22179				if (elements[item]) {
22180					delete elements[item];
22181				}
22182			});
22183		}
22184
22185		// If the user didn't allow span only allow internal spans
22186		if (!getElementRule('span')) {
22187			addValidElements('span[!data-mce-type|*]');
22188		}
22189
22190		/**
22191		 * Name/value map object with valid parents and children to those parents.
22192		 *
22193		 * @example
22194		 * children = {
22195		 *    div:{p:{}, h1:{}}
22196		 * };
22197		 * @field children
22198		 * @type Object
22199		 */
22200		self.children = children;
22201
22202		/**
22203		 * Name/value map object with valid styles for each element.
22204		 *
22205		 * @method getValidStyles
22206		 * @type Object
22207		 */
22208		self.getValidStyles = function() {
22209			return validStyles;
22210		};
22211
22212		/**
22213		 * Name/value map object with valid styles for each element.
22214		 *
22215		 * @method getInvalidStyles
22216		 * @type Object
22217		 */
22218		self.getInvalidStyles = function() {
22219			return invalidStyles;
22220		};
22221
22222		/**
22223		 * Name/value map object with valid classes for each element.
22224		 *
22225		 * @method getValidClasses
22226		 * @type Object
22227		 */
22228		self.getValidClasses = function() {
22229			return validClasses;
22230		};
22231
22232		/**
22233		 * Returns a map with boolean attributes.
22234		 *
22235		 * @method getBoolAttrs
22236		 * @return {Object} Name/value lookup map for boolean attributes.
22237		 */
22238		self.getBoolAttrs = function() {
22239			return boolAttrMap;
22240		};
22241
22242		/**
22243		 * Returns a map with block elements.
22244		 *
22245		 * @method getBlockElements
22246		 * @return {Object} Name/value lookup map for block elements.
22247		 */
22248		self.getBlockElements = function() {
22249			return blockElementsMap;
22250		};
22251
22252		/**
22253		 * Returns a map with text block elements. Such as: p,h1-h6,div,address
22254		 *
22255		 * @method getTextBlockElements
22256		 * @return {Object} Name/value lookup map for block elements.
22257		 */
22258		self.getTextBlockElements = function() {
22259			return textBlockElementsMap;
22260		};
22261
22262		/**
22263		 * Returns a map of inline text format nodes for example strong/span or ins.
22264		 *
22265		 * @method getTextInlineElements
22266		 * @return {Object} Name/value lookup map for text format elements.
22267		 */
22268		self.getTextInlineElements = function() {
22269			return textInlineElementsMap;
22270		};
22271
22272		/**
22273		 * Returns a map with short ended elements such as BR or IMG.
22274		 *
22275		 * @method getShortEndedElements
22276		 * @return {Object} Name/value lookup map for short ended elements.
22277		 */
22278		self.getShortEndedElements = function() {
22279			return shortEndedElementsMap;
22280		};
22281
22282		/**
22283		 * Returns a map with self closing tags such as <li>.
22284		 *
22285		 * @method getSelfClosingElements
22286		 * @return {Object} Name/value lookup map for self closing tags elements.
22287		 */
22288		self.getSelfClosingElements = function() {
22289			return selfClosingElementsMap;
22290		};
22291
22292		/**
22293		 * Returns a map with elements that should be treated as contents regardless if it has text
22294		 * content in them or not such as TD, VIDEO or IMG.
22295		 *
22296		 * @method getNonEmptyElements
22297		 * @return {Object} Name/value lookup map for non empty elements.
22298		 */
22299		self.getNonEmptyElements = function() {
22300			return nonEmptyElementsMap;
22301		};
22302
22303		/**
22304		 * Returns a map with elements that the caret should be moved in front of after enter is
22305		 * pressed
22306		 *
22307		 * @method getMoveCaretBeforeOnEnterElements
22308		 * @return {Object} Name/value lookup map for elements to place the caret in front of.
22309		 */
22310		self.getMoveCaretBeforeOnEnterElements = function() {
22311			return moveCaretBeforeOnEnterElementsMap;
22312		};
22313
22314		/**
22315		 * Returns a map with elements where white space is to be preserved like PRE or SCRIPT.
22316		 *
22317		 * @method getWhiteSpaceElements
22318		 * @return {Object} Name/value lookup map for white space elements.
22319		 */
22320		self.getWhiteSpaceElements = function() {
22321			return whiteSpaceElementsMap;
22322		};
22323
22324		/**
22325		 * Returns a map with special elements. These are elements that needs to be parsed
22326		 * in a special way such as script, style, textarea etc. The map object values
22327		 * are regexps used to find the end of the element.
22328		 *
22329		 * @method getSpecialElements
22330		 * @return {Object} Name/value lookup map for special elements.
22331		 */
22332		self.getSpecialElements = function() {
22333			return specialElements;
22334		};
22335
22336		/**
22337		 * Returns true/false if the specified element and it's child is valid or not
22338		 * according to the schema.
22339		 *
22340		 * @method isValidChild
22341		 * @param {String} name Element name to check for.
22342		 * @param {String} child Element child to verify.
22343		 * @return {Boolean} True/false if the element is a valid child of the specified parent.
22344		 */
22345		self.isValidChild = function(name, child) {
22346			var parent = children[name];
22347
22348			return !!(parent && parent[child]);
22349		};
22350
22351		/**
22352		 * Returns true/false if the specified element name and optional attribute is
22353		 * valid according to the schema.
22354		 *
22355		 * @method isValid
22356		 * @param {String} name Name of element to check.
22357		 * @param {String} attr Optional attribute name to check for.
22358		 * @return {Boolean} True/false if the element and attribute is valid.
22359		 */
22360		self.isValid = function(name, attr) {
22361			var attrPatterns, i, rule = getElementRule(name);
22362
22363			// Check if it's a valid element
22364			if (rule) {
22365				if (attr) {
22366					// Check if attribute name exists
22367					if (rule.attributes[attr]) {
22368						return true;
22369					}
22370
22371					// Check if attribute matches a regexp pattern
22372					attrPatterns = rule.attributePatterns;
22373					if (attrPatterns) {
22374						i = attrPatterns.length;
22375						while (i--) {
22376							if (attrPatterns[i].pattern.test(name)) {
22377								return true;
22378							}
22379						}
22380					}
22381				} else {
22382					return true;
22383				}
22384			}
22385
22386			// No match
22387			return false;
22388		};
22389
22390		/**
22391		 * Returns true/false if the specified element is valid or not
22392		 * according to the schema.
22393		 *
22394		 * @method getElementRule
22395		 * @param {String} name Element name to check for.
22396		 * @return {Object} Element object or undefined if the element isn't valid.
22397		 */
22398		self.getElementRule = getElementRule;
22399
22400		/**
22401		 * Returns an map object of all custom elements.
22402		 *
22403		 * @method getCustomElements
22404		 * @return {Object} Name/value map object of all custom elements.
22405		 */
22406		self.getCustomElements = function() {
22407			return customElementsMap;
22408		};
22409
22410		/**
22411		 * Parses a valid elements string and adds it to the schema. The valid elements
22412		 * format is for example "element[attr=default|otherattr]".
22413		 * Existing rules will be replaced with the ones specified, so this extends the schema.
22414		 *
22415		 * @method addValidElements
22416		 * @param {String} valid_elements String in the valid elements format to be parsed.
22417		 */
22418		self.addValidElements = addValidElements;
22419
22420		/**
22421		 * Parses a valid elements string and sets it to the schema. The valid elements
22422		 * format is for example "element[attr=default|otherattr]".
22423		 * Existing rules will be replaced with the ones specified, so this extends the schema.
22424		 *
22425		 * @method setValidElements
22426		 * @param {String} valid_elements String in the valid elements format to be parsed.
22427		 */
22428		self.setValidElements = setValidElements;
22429
22430		/**
22431		 * Adds custom non HTML elements to the schema.
22432		 *
22433		 * @method addCustomElements
22434		 * @param {String} custom_elements Comma separated list of custom elements to add.
22435		 */
22436		self.addCustomElements = addCustomElements;
22437
22438		/**
22439		 * Parses a valid children string and adds them to the schema structure. The valid children
22440		 * format is for example: "element[child1|child2]".
22441		 *
22442		 * @method addValidChildren
22443		 * @param {String} valid_children Valid children elements string to parse
22444		 */
22445		self.addValidChildren = addValidChildren;
22446
22447		self.elements = elements;
22448	};
22449});
22450
22451// Included from: js/tinymce/classes/html/SaxParser.js
22452
22453/**
22454 * SaxParser.js
22455 *
22456 * Released under LGPL License.
22457 * Copyright (c) 1999-2015 Ephox Corp. All rights reserved
22458 *
22459 * License: http://www.tinymce.com/license
22460 * Contributing: http://www.tinymce.com/contributing
22461 */
22462
22463/*eslint max-depth:[2, 9] */
22464
22465/**
22466 * This class parses HTML code using pure JavaScript and executes various events for each item it finds. It will
22467 * always execute the events in the right order for tag soup code like <b><p></b></p>. It will also remove elements
22468 * and attributes that doesn't fit the schema if the validate setting is enabled.
22469 *
22470 * @example
22471 * var parser = new tinymce.html.SaxParser({
22472 *     validate: true,
22473 *
22474 *     comment: function(text) {
22475 *         console.log('Comment:', text);
22476 *     },
22477 *
22478 *     cdata: function(text) {
22479 *         console.log('CDATA:', text);
22480 *     },
22481 *
22482 *     text: function(text, raw) {
22483 *         console.log('Text:', text, 'Raw:', raw);
22484 *     },
22485 *
22486 *     start: function(name, attrs, empty) {
22487 *         console.log('Start:', name, attrs, empty);
22488 *     },
22489 *
22490 *     end: function(name) {
22491 *         console.log('End:', name);
22492 *     },
22493 *
22494 *     pi: function(name, text) {
22495 *         console.log('PI:', name, text);
22496 *     },
22497 *
22498 *     doctype: function(text) {
22499 *         console.log('DocType:', text);
22500 *     }
22501 * }, schema);
22502 * @class tinymce.html.SaxParser
22503 * @version 3.4
22504 */
22505define("tinymce/html/SaxParser", [
22506	"tinymce/html/Schema",
22507	"tinymce/html/Entities",
22508	"tinymce/util/Tools"
22509], function(Schema, Entities, Tools) {
22510	var each = Tools.each;
22511
22512	/**
22513	 * Returns the index of the end tag for a specific start tag. This can be
22514	 * used to skip all children of a parent element from being processed.
22515	 *
22516	 * @private
22517	 * @method findEndTag
22518	 * @param {tinymce.html.Schema} schema Schema instance to use to match short ended elements.
22519	 * @param {String} html HTML string to find the end tag in.
22520	 * @param {Number} startIndex Indext to start searching at should be after the start tag.
22521	 * @return {Number} Index of the end tag.
22522	 */
22523	function findEndTag(schema, html, startIndex) {
22524		var count = 1, index, matches, tokenRegExp, shortEndedElements;
22525
22526		shortEndedElements = schema.getShortEndedElements();
22527		tokenRegExp = /<([!?\/])?([A-Za-z0-9\-_\:\.]+)((?:\s+[^"\'>]+(?:(?:"[^"]*")|(?:\'[^\']*\')|[^>]*))*|\/|\s+)>/g;
22528		tokenRegExp.lastIndex = index = startIndex;
22529
22530		while ((matches = tokenRegExp.exec(html))) {
22531			index = tokenRegExp.lastIndex;
22532
22533			if (matches[1] === '/') { // End element
22534				count--;
22535			} else if (!matches[1]) { // Start element
22536				if (matches[2] in shortEndedElements) {
22537					continue;
22538				}
22539
22540				count++;
22541			}
22542
22543			if (count === 0) {
22544				break;
22545			}
22546		}
22547
22548		return index;
22549	}
22550
22551	/**
22552	 * Constructs a new SaxParser instance.
22553	 *
22554	 * @constructor
22555	 * @method SaxParser
22556	 * @param {Object}
22556 settings Name/value collection of settings. comment, cdata, text, start and end are callbacks.
22557	 * @param {tinymce.html.Schema} schema HTML Schema class to use when parsing.
22558	 */
22559	function SaxParser(settings, schema) {
22560		var self = this;
22561
22562		function noop() {}
22563
22564		settings = settings || {};
22565		self.schema = schema = schema || new Schema();
22566
22567		if (settings.fix_self_closing !== false) {
22568			settings.fix_self_closing = true;
22569		}
22570
22571		// Add handler functions from settings and setup default handlers
22572		each('comment cdata text start end pi doctype'.split(' '), function(name) {
22573			if (name) {
22574				self[name] = settings[name] || noop;
22575			}
22576		});
22577
22578		/**
22579		 * Parses the specified HTML string and executes the callbacks for each item it finds.
22580		 *
22581		 * @example
22582		 * new SaxParser({...}).parse('<b>text</b>');
22583		 * @method parse
22584		 * @param {String} html Html string to sax parse.
22585		 */
22586		self.parse = function(html) {
22587			var self = this, matches, index = 0, value, endRegExp, stack = [], attrList, i, text, name;
22588			var isInternalElement, removeInternalElements, shortEndedElements, fillAttrsMap, isShortEnded;
22589			var validate, elementRule, isValidElement, attr, attribsValue, validAttributesMap, validAttributePatterns;
22590			var attributesRequired, attributesDefault, attributesForced;
22591			var anyAttributesRequired, selfClosing, tokenRegExp, attrRegExp, specialElements, attrValue, idCount = 0;
22592			var decode = Entities.decode, fixSelfClosing, filteredUrlAttrs = Tools.makeMap('src,href,data,background,formaction,poster');
22593			var scriptUriRegExp = /((java|vb)script|mhtml):/i, dataUriRegExp = /^data:/i;
22594
22595			function processEndTag(name) {
22596				var pos, i;
22597
22598				// Find position of parent of the same type
22599				pos = stack.length;
22600				while (pos--) {
22601					if (stack[pos].name === name) {
22602						break;
22603					}
22604				}
22605
22606				// Found parent
22607				if (pos >= 0) {
22608					// Close all the open elements
22609					for (i = stack.length - 1; i >= pos; i--) {
22610						name = stack[i];
22611
22612						if (name.valid) {
22613							self.end(name.name);
22614						}
22615					}
22616
22617					// Remove the open elements from the stack
22618					stack.length = pos;
22619				}
22620			}
22621
22622			function parseAttribute(match, name, value, val2, val3) {
22623				var attrRule, i, trimRegExp = /[\s\u0000-\u001F]+/g;
22624
22625				name = name.toLowerCase();
22626				value = name in fillAttrsMap ? name : decode(value || val2 || val3 || ''); // Handle boolean attribute than value attribute
22627
22628				// Validate name and value pass through all data- attributes
22629				if (validate && !isInternalElement && name.indexOf('data-') !== 0) {
22630					attrRule = validAttributesMap[name];
22631
22632					// Find rule by pattern matching
22633					if (!attrRule && validAttributePatterns) {
22634						i = validAttributePatterns.length;
22635						while (i--) {
22636							attrRule = validAttributePatterns[i];
22637							if (attrRule.pattern.test(name)) {
22638								break;
22639							}
22640						}
22641
22642						// No rule matched
22643						if (i === -1) {
22644							attrRule = null;
22645						}
22646					}
22647
22648					// No attribute rule found
22649					if (!attrRule) {
22650						return;
22651					}
22652
22653					// Validate value
22654					if (attrRule.validValues && !(value in attrRule.validValues)) {
22655						return;
22656					}
22657				}
22658
22659				// Block any javascript: urls or non image data uris
22660				if (filteredUrlAttrs[name] && !settings.allow_script_urls) {
22661					var uri = value.replace(trimRegExp, '');
22662
22663					try {
22664						// Might throw malformed URI sequence
22665						uri = decodeURIComponent(uri);
22666					} catch (ex) {
22667						// Fallback to non UTF-8 decoder
22668						uri = unescape(uri);
22669					}
22670
22671					if (scriptUriRegExp.test(uri)) {
22672						return;
22673					}
22674
22675					if (!settings.allow_html_data_urls && dataUriRegExp.test(uri) && !/^data:image\//i.test(uri)) {
22676						return;
22677					}
22678				}
22679
22680				// Add attribute to list and map
22681				attrList.map[name] = value;
22682				attrList.push({
22683					name: name,
22684					value: value
22685				});
22686			}
22687
22688			// Precompile RegExps and map objects
22689			tokenRegExp = new RegExp('<(?:' +
22690				'(?:!--([\\w\\W]*?)-->)|' + // Comment
22691				'(?:!\\[CDATA\\[([\\w\\W]*?)\\]\\]>)|' + // CDATA
22692				'(?:!DOCTYPE([\\w\\W]*?)>)|' + // DOCTYPE
22693				'(?:\\?([^\\s\\/<>]+) ?([\\w\\W]*?)[?/]>)|' + // PI
22694				'(?:\\/([^>]+)>)|' + // End element
22695				'(?:([A-Za-z0-9\\-_\\:\\.]+)((?:\\s+[^"\'>]+(?:(?:"[^"]*")|(?:\'[^\']*\')|[^>]*))*|\\/|\\s+)>)' + // Start element
22696			')', 'g');
22697
22698			attrRegExp = /([\w:\-]+)(?:\s*=\s*(?:(?:\"((?:[^\"])*)\")|(?:\'((?:[^\'])*)\')|([^>\s]+)))?/g;
22699
22700			// Setup lookup tables for empty elements and boolean attributes
22701			shortEndedElements = schema.getShortEndedElements();
22702			selfClosing = settings.self_closing_elements || schema.getSelfClosingElements();
22703			fillAttrsMap = schema.getBoolAttrs();
22704			validate = settings.validate;
22705			removeInternalElements = settings.remove_internals;
22706			fixSelfClosing = settings.fix_self_closing;
22707			specialElements = schema.getSpecialElements();
22708
22709			while ((matches = tokenRegExp.exec(html))) {
22710				// Text
22711				if (index < matches.index) {
22712					self.text(decode(html.substr(index, matches.index - index)));
22713				}
22714
22715				if ((value = matches[6])) { // End element
22716					value = value.toLowerCase();
22717
22718					// IE will add a ":" in front of elements it doesn't understand like custom elements or HTML5 elements
22719					if (value.charAt(0) === ':') {
22720						value = value.substr(1);
22721					}
22722
22723					processEndTag(value);
22724				} else if ((value = matches[7])) { // Start element
22725					value = value.toLowerCase();
22726
22727					// IE will add a ":" in front of elements it doesn't understand like custom elements or HTML5 elements
22728					if (value.charAt(0) === ':') {
22729						value = value.substr(1);
22730					}
22731
22732					isShortEnded = value in shortEndedElements;
22733
22734					// Is self closing tag for example an <li> after an open <li>
22735					if (fixSelfClosing && selfClosing[value] && stack.length > 0 && stack[stack.length - 1].name === value) {
22736						processEndTag(value);
22737					}
22738
22739					// Validate element
22740					if (!validate || (elementRule = schema.getElementRule(value))) {
22741						isValidElement = true;
22742
22743						// Grab attributes map and patters when validation is enabled
22744						if (validate) {
22745							validAttributesMap = elementRule.attributes;
22746							validAttributePatterns = elementRule.attributePatterns;
22747						}
22748
22749						// Parse attributes
22750						if ((attribsValue = matches[8])) {
22751							isInternalElement = attribsValue.indexOf('data-mce-type') !== -1; // Check if the element is an internal element
22752
22753							// If the element has internal attributes then remove it if we are told to do so
22754							if (isInternalElement && removeInternalElements) {
22755								isValidElement = false;
22756							}
22757
22758							attrList = [];
22759							attrList.map = {};
22760
22761							attribsValue.replace(attrRegExp, parseAttribute);
22762						} else {
22763							attrList = [];
22764							attrList.map = {};
22765						}
22766
22767						// Process attributes if validation is enabled
22768						if (validate && !isInternalElement) {
22769							attributesRequired = elementRule.attributesRequired;
22770							attributesDefault = elementRule.attributesDefault;
22771							attributesForced = elementRule.attributesForced;
22772							anyAttributesRequired = elementRule.removeEmptyAttrs;
22773
22774							// Check if any attribute exists
22775							if (anyAttributesRequired && !attrList.length) {
22776								isValidElement = false;
22777							}
22778
22779							// Handle forced attributes
22780							if (attributesForced) {
22781								i = attributesForced.length;
22782								while (i--) {
22783									attr = attributesForced[i];
22784									name = attr.name;
22785									attrValue = attr.value;
22786
22787									if (attrValue === '{$uid}') {
22788										attrValue = 'mce_' + idCount++;
22789									}
22790
22791									attrList.map[name] = attrValue;
22792									attrList.push({name: name, value: attrValue});
22793								}
22794							}
22795
22796							// Handle default attributes
22797							if (attributesDefault) {
22798								i = attributesDefault.length;
22799								while (i--) {
22800									attr = attributesDefault[i];
22801									name = attr.name;
22802
22803									if (!(name in attrList.map)) {
22804										attrValue = attr.value;
22805
22806										if (attrValue === '{$uid}') {
22807											attrValue = 'mce_' + idCount++;
22808										}
22809
22810										attrList.map[name] = attrValue;
22811										attrList.push({name: name, value: attrValue});
22812									}
22813								}
22814							}
22815
22816							// Handle required attributes
22817							if (attributesRequired) {
22818								i = attributesRequired.length;
22819								while (i--) {
22820									if (attributesRequired[i] in attrList.map) {
22821										break;
22822									}
22823								}
22824
22825								// None of the required attributes where found
22826								if (i === -1) {
22827									isValidElement = false;
22828								}
22829							}
22830
22831							// Invalidate element if it's marked as bogus
22832							if ((attr = attrList.map['data-mce-bogus'])) {
22833								if (attr === 'all') {
22834									index = findEndTag(schema, html, tokenRegExp.lastIndex);
22835									tokenRegExp.lastIndex = index;
22836									continue;
22837								}
22838
22839								isValidElement = false;
22840							}
22841						}
22842
22843						if (isValidElement) {
22844							self.start(value, attrList, isShortEnded);
22845						}
22846					} else {
22847						isValidElement = false;
22848					}
22849
22850					// Treat script, noscript and style a bit different since they may include code that looks like elements
22851					if ((endRegExp = specialElements[value])) {
22852						endRegExp.lastIndex = index = matches.index + matches[0].length;
22853
22854						if ((matches = endRegExp.exec(html))) {
22855							if (isValidElement) {
22856								text = html.substr(index, matches.index - index);
22857							}
22858
22859							index = matches.index + matches[0].length;
22860						} else {
22861							text = html.substr(index);
22862							index = html.length;
22863						}
22864
22865						if (isValidElement) {
22866							if (text.length > 0) {
22867								self.text(text, true);
22868							}
22869
22870							self.end(value);
22871						}
22872
22873						tokenRegExp.lastIndex = index;
22874						continue;
22875					}
22876
22877					// Push value on to stack
22878					if (!isShortEnded) {
22879						if (!attribsValue || attribsValue.indexOf('/') != attribsValue.length - 1) {
22880							stack.push({name: value, valid: isValidElement});
22881						} else if (isValidElement) {
22882							self.end(value);
22883						}
22884					}
22885				} else if ((value = matches[1])) { // Comment
22886					// Padd comment value to avoid browsers from parsing invalid comments as HTML
22887					if (value.charAt(0) === '>') {
22888						value = ' ' + value;
22889					}
22890
22891					if (!settings.allow_conditional_comments && value.substr(0, 3) === '[if') {
22892						value = ' ' + value;
22893					}
22894
22895					self.comment(value);
22896				} else if ((value = matches[2])) { // CDATA
22897					self.cdata(value);
22898				} else if ((value = matches[3])) { // DOCTYPE
22899					self.doctype(value);
22900				} else if ((value = matches[4])) { // PI
22901					self.pi(value, matches[5]);
22902				}
22903
22904				index = matches.index + matches[0].length;
22905			}
22906
22907			// Text
22908			if (index < html.length) {
22909				self.text(decode(html.substr(index)));
22910			}
22911
22912			// Close any open elements
22913			for (i = stack.length - 1; i >= 0; i--) {
22914				value = stack[i];
22915
22916				if (value.valid) {
22917					self.end(value.name);
22918				}
22919			}
22920		};
22921	}
22922
22923	SaxParser.findEndTag = findEndTag;
22924
22925	return SaxParser;
22926});
22927
22928// Included from: js/tinymce/classes/html/DomParser.js
22929
22930/**
22931 * DomParser.js
22932 *
22933 * Released under LGPL License.
22934 * Copyright (c) 1999-2015 Ephox Corp. All rights reserved
22935 *
22936 * License: http://www.tinymce.com/license
22937 * Contributing: http://www.tinymce.com/contributing
22938 */
22939
22940/**
22941 * This class parses HTML code into a DOM like structure of nodes it will remove redundant whitespace and make
22942 * sure that the node tree is valid according to the specified schema.
22943 * So for example: <p>a<p>b</p>c</p> will become <p>a</p><p>b</p><p>c</p>
22944 *
22945 * @example
22946 * var parser = new tinymce.html.DomParser({validate: true}, schema);
22947 * var rootNode = parser.parse('<h1>content</h1>');
22948 *
22949 * @class tinymce.html.DomParser
22950 * @version 3.4
22951 */
22952define("tinymce/html/DomParser", [
22953	"tinymce/html/Node",
22954	"tinymce/html/Schema",
22955	"tinymce/html/SaxParser",
22956	"tinymce/util/Tools"
22957], function(Node, Schema, SaxParser, Tools) {
22958	var makeMap = Tools.makeMap, each = Tools.each, explode = Tools.explode, extend = Tools.extend;
22959
22960	/**
22961	 * Constructs a new DomParser instance.
22962	 *
22963	 * @constructor
22964	 * @method DomParser
22965	 * @param {Object} settings Name/value collection of settings. comment, cdata, text, start and end are callbacks.
22966	 * @param {tinymce.html.Schema} schema HTML Schema class to use when parsing.
22967	 */
22968	return function(settings, schema) {
22969		var self = this, nodeFilters = {}, attributeFilters = [], matchedNodes = {}, matchedAttributes = {};
22970
22971		settings = settings || {};
22972		settings.validate = "validate" in settings ? settings.validate : true;
22973		settings.root_name = settings.root_name || 'body';
22974		self.schema = schema = schema || new Schema();
22975
22976		function fixInvalidChildren(nodes) {
22977			var ni, node, parent, parents, newParent, currentNode, tempNode, childNode, i;
22978			var nonEmptyElements, nonSplitableElements, textBlockElements, specialElements, sibling, nextNode;
22979
22980			nonSplitableElements = makeMap('tr,td,th,tbody,thead,tfoot,table');
22981			nonEmptyElements = schema.getNonEmptyElements();
22982			textBlockElements = schema.getTextBlockElements();
22983			specialElements = schema.getSpecialElements();
22984
22985			for (ni = 0; ni < nodes.length; ni++) {
22986				node = nodes[ni];
22987
22988				// Already removed or fixed
22989				if (!node.parent || node.fixed) {
22990					continue;
22991				}
22992
22993				// If the invalid element is a text block and the text block is within a parent LI element
22994				// Then unwrap the first text block and convert other sibling text blocks to LI elements similar to Word/Open Office
22995				if (textBlockElements[node.name] && node.parent.name == 'li') {
22996					// Move sibling text blocks after LI element
22997					sibling = node.next;
22998					while (sibling) {
22999						if (textBlockElements[sibling.name]) {
23000							sibling.name = 'li';
23001							sibling.fixed = true;
23002							node.parent.insert(sibling, node.parent);
23003						} else {
23004							break;
23005						}
23006
23007						sibling = sibling.next;
23008					}
23009
23010					// Unwrap current text block
23011					node.unwrap(node);
23012					continue;
23013				}
23014
23015				// Get list of all parent nodes until we find a valid parent to stick the child into
23016				parents = [node];
23017				for (parent = node.parent; parent && !schema.isValidChild(parent.name, node.name) &&
23018					!nonSplitableElements[parent.name]; parent = parent.parent) {
23019					parents.push(parent);
23020				}
23021
23022				// Found a suitable parent
23023				if (parent && parents.length > 1) {
23024					// Reverse the array since it makes looping easier
23025					parents.reverse();
23026
23027					// Clone the related parent and insert that after the moved node
23028					newParent = currentNode = self.filterNode(parents[0].clone());
23029
23030					// Start cloning and moving children on the left side of the target node
23031					for (i = 0; i < parents.length - 1; i++) {
23032						if (schema.isValidChild(currentNode.name, parents[i].name)) {
23033							tempNode = self.filterNode(parents[i].clone());
23034							currentNode.append(tempNode);
23035						} else {
23036							tempNode = currentNode;
23037						}
23038
23039						for (childNode = parents[i].firstChild; childNode && childNode != parents[i + 1];) {
23040							nextNode = childNode.next;
23041							tempNode.append(childNode);
23042							childNode = nextNode;
23043						}
23044
23045						currentNode = tempNode;
23046					}
23047
23048					if (!newParent.isEmpty(nonEmptyElements)) {
23049						parent.insert(newParent, parents[0], true);
23050						parent.insert(node, newParent);
23051					} else {
23052						parent.insert(node, parents[0], true);
23053					}
23054
23055					// Check if the element is empty by looking through it's contents and special treatment for <p><br /></p>
23056					parent = parents[0];
23057					if (parent.isEmpty(nonEmptyElements) || parent.firstChild === parent.lastChild && parent.firstChild.name === 'br') {
23058						parent.empty().remove();
23059					}
23060				} else if (node.parent) {
23061					// If it's an LI try to find a UL/OL for it or wrap it
23062					if (node.name === 'li') {
23063						sibling = node.prev;
23064						if (sibling && (sibling.name === 'ul' || sibling.name === 'ul')) {
23065							sibling.append(node);
23066							continue;
23067						}
23068
23069						sibling = node.next;
23070						if (sibling && (sibling.name === 'ul' || sibling.name === 'ul')) {
23071							sibling.insert(node, sibling.firstChild, true);
23072							continue;
23073						}
23074
23075						node.wrap(self.filterNode(new Node('ul', 1)));
23076						continue;
23077					}
23078
23079					// Try wrapping the element in a DIV
23080					if (schema.isValidChild(node.parent.name, 'div') && schema.isValidChild('div', node.name)) {
23081						node.wrap(self.filterNode(new Node('div', 1)));
23082					} else {
23083						// We failed wrapping it, then remove or unwrap it
23084						if (specialElements[node.name]) {
23085							node.empty().remove();
23086						} else {
23087							node.unwrap();
23088						}
23089					}
23090				}
23091			}
23092		}
23093
23094		/**
23095		 * Runs the specified node though the element and attributes filters.
23096		 *
23097		 * @method filterNode
23098		 * @param {tinymce.html.Node} Node the node to run filters on.
23099		 * @return {tinymce.html.Node} The passed in node.
23100		 */
23101		self.filterNode = function(node) {
23102			var i, name, list;
23103
23104			// Run element filters
23105			if (name in nodeFilters) {
23106				list = matchedNodes[name];
23107
23108				if (list) {
23109					list.push(node);
23110				} else {
23111					matchedNodes[name] = [node];
23112				}
23113			}
23114
23115			// Run attribute filters
23116			i = attributeFilters.length;
23117			while (i--) {
23118				name = attributeFilters[i].name;
23119
23120				if (name in node.attributes.map) {
23121					list = matchedAttributes[name];
23122
23123					if (list) {
23124						list.push(node);
23125					} else {
23126						matchedAttributes[name] = [node];
23127					}
23128				}
23129			}
23130
23131			return node;
23132		};
23133
23134		/**
23135		 * Adds a node filter function to the parser, the parser will collect the specified nodes by name
23136		 * and then execute the callback ones it has finished parsing the document.
23137		 *
23138		 * @example
23139		 * parser.addNodeFilter('p,h1', function(nodes, name) {
23140		 *		for (var i = 0; i < nodes.length; i++) {
23141		 *			console.log(nodes[i].name);
23142		 *		}
23143		 * });
23144		 * @method addNodeFilter
23145		 * @method {String} name Comma separated list of nodes to collect.
23146		 * @param {function} callback Callback function to execute once it has collected nodes.
23147		 */
23148		self.addNodeFilter = function(name, callback) {
23149			each(explode(name), function(name) {
23150				var list = nodeFilters[name];
23151
23152				if (!list) {
23153					nodeFilters[name] = list = [];
23154				}
23155
23156				list.push(callback);
23157			});
23158		};
23159
23160		/**
23161		 * Adds a attribute filter function to the parser, the parser will collect nodes that has the specified attributes
23162		 * and then execute the callback ones it has finished parsing the document.
23163		 *
23164		 * @example
23165		 * parser.addAttributeFilter('src,href', function(nodes, name) {
23166		 *		for (var i = 0; i < nodes.length; i++) {
23167		 *			console.log(nodes[i].name);
23168		 *		}
23169		 * });
23170		 * @method addAttributeFilter
23171		 * @method {String} name Comma separated list of nodes to collect.
23172		 * @param {function} callback Callback function to execute once it has collected nodes.
23173		 */
23174		self.addAttributeFilter = function(name, callback) {
23175			each(explode(name), function(name) {
23176				var i;
23177
23178				for (i = 0; i < attributeFilters.length; i++) {
23179					if (attributeFilters[i].name === name) {
23180						attributeFilters[i].callbacks.push(callback);
23181						return;
23182					}
23183				}
23184
23185				attributeFilters.push({name: name, callbacks: [callback]});
23186			});
23187		};
23188
23189		/**
23190		 * Parses the specified HTML string into a DOM like node tree and returns the result.
23191		 *
23192		 * @example
23193		 * var rootNode = new DomParser({...}).parse('<b>text</b>');
23194		 * @method parse
23195		 * @param {String} html Html string to sax parse.
23196		 * @param {Object} args Optional args object that gets passed to all filter functions.
23197		 * @return {tinymce.html.Node} Root node containing the tree.
23198		 */
23199		self.parse = function(html, args) {
23200			var parser, rootNode, node, nodes, i, l, fi, fl, list, name, validate;
23201			var blockElements, startWhiteSpaceRegExp, invalidChildren = [], isInWhiteSpacePreservedElement;
23202			var endWhiteSpaceRegExp, allWhiteSpaceRegExp, isAllWhiteSpaceRegExp, whiteSpaceElements;
23203			var children, nonEmptyElements, rootBlockName;
23204
23205			args = args || {};
23206			matchedNodes = {};
23207			matchedAttributes = {};
23208			blockElements = extend(makeMap('script,style,head,html,body,title,meta,param'), schema.getBlockElements());
23209			nonEmptyElements = schema.getNonEmptyElements();
23210			children = schema.children;
23211			validate = settings.validate;
23212			rootBlockName = "forced_root_block" in args ? args.forced_root_block : settings.forced_root_block;
23213
23214			whiteSpaceElements = schema.getWhiteSpaceElements();
23215			startWhiteSpaceRegExp = /^[ \t\r\n]+/;
23216			endWhiteSpaceRegExp = /[ \t\r\n]+$/;
23217			allWhiteSpaceRegExp = /[ \t\r\n]+/g;
23218			isAllWhiteSpaceRegExp = /^[ \t\r\n]+$/;
23219
23220			function addRootBlocks() {
23221				var node = rootNode.firstChild, next, rootBlockNode;
23222
23223				// Removes whitespace at beginning and end of block so:
23224				// <p> x </p> -> <p>x</p>
23225				function trim(rootBlockNode) {
23226					if (rootBlockNode) {
23227						node = rootBlockNode.firstChild;
23228						if (node && node.type == 3) {
23229							node.value = node.value.replace(startWhiteSpaceRegExp, '');
23230						}
23231
23232						node = rootBlockNode.lastChild;
23233						if (node && node.type == 3) {
23234							node.value = node.value.replace(endWhiteSpaceRegExp, '');
23235						}
23236					}
23237				}
23238
23239				// Check if rootBlock is valid within rootNode for example if P is valid in H1 if H1 is the contentEditabe root
23240				if (!schema.isValidChild(rootNode.name, rootBlockName.toLowerCase())) {
23241					return;
23242				}
23243
23244				while (node) {
23245					next = node.next;
23246
23247					if (node.type == 3 || (node.type == 1 && node.name !== 'p' &&
23248						!blockElements[node.name] && !node.attr('data-mce-type'))) {
23249						if (!rootBlockNode) {
23250							// Create a new root block element
23251							rootBlockNode = createNode(rootBlockName, 1);
23252							rootBlockNode.attr(settings.forced_root_block_attrs);
23253							rootNode.insert(rootBlockNode, node);
23254							rootBlockNode.append(node);
23255						} else {
23256							rootBlockNode.append(node);
23257						}
23258					} else {
23259						trim(rootBlockNode);
23260						rootBlockNode = null;
23261					}
23262
23263					node = next;
23264				}
23265
23266				trim(rootBlockNode);
23267			}
23268
23269			function createNode(name, type) {
23270				var node = new Node(name, type), list;
23271
23272				if (name in nodeFilters) {
23273					list = matchedNodes[name];
23274
23275					if (list) {
23276						list.push(node);
23277					} else {
23278						matchedNodes[name] = [node];
23279					}
23280				}
23281
23282				return node;
23283			}
23284
23285			function removeWhitespaceBefore(node) {
23286				var textNode, textNodeNext, textVal, sibling, blockElements = schema.getBlockElements();
23287
23288				for (textNode = node.prev; textNode && textNode.type === 3;) {
23289					textVal = textNode.value.replace(endWhiteSpaceRegExp, '');
23290
23291					// Found a text node with non whitespace then trim that and break
23292					if (textVal.length > 0) {
23293						textNode.value = textVal;
23294						return;
23295					}
23296
23297					textNodeNext = textNode.next;
23298
23299					// Fix for bug #7543 where bogus nodes would produce empty
23300					// text nodes and these would be removed if a nested list was before it
23301					if (textNodeNext) {
23302						if (textNodeNext.type == 3 && textNodeNext.value.length) {
23303							textNode = textNode.prev;
23304							continue;
23305						}
23306
23307						if (!blockElements[textNodeNext.name] && textNodeNext.name != 'script' && textNodeNext.name != 'style') {
23308							textNode = textNode.prev;
23309							continue;
23310						}
23311					}
23312
23313					sibling = textNode.prev;
23314					textNode.remove();
23315					textNode = sibling;
23316				}
23317			}
23318
23319			function cloneAndExcludeBlocks(input) {
23320				var name, output = {};
23321
23322				for (name in input) {
23323					if (name !== 'li' && name != 'p') {
23324						output[name] = input[name];
23325					}
23326				}
23327
23328				return output;
23329			}
23330
23331			parser = new SaxParser({
23332				validate: validate,
23333				allow_script_urls: settings.allow_script_urls,
23334				allow_conditional_comments: settings.allow_conditional_comments,
23335
23336				// Exclude P and LI from DOM parsing since it's treated better by the DOM parser
23337				self_closing_elements: cloneAndExcludeBlocks(schema.getSelfClosingElements()),
23338
23339				cdata: function(text) {
23340					node.append(createNode('#cdata', 4)).value = text;
23341				},
23342
23343				text: function(text, raw) {
23344					var textNode;
23345
23346					// Trim all redundant whitespace on non white space elements
23347					if (!isInWhiteSpacePreservedElement) {
23348						text = text.replace(allWhiteSpaceRegExp, ' ');
23349
23350						if (node.lastChild && blockElements[node.lastChild.name]) {
23351							text = text.replace(startWhiteSpaceRegExp, '');
23352						}
23353					}
23354
23355					// Do we need to create the node
23356					if (text.length !== 0) {
23357						textNode = createNode('#text', 3);
23358						textNode.raw = !!raw;
23359						node.append(textNode).value = text;
23360					}
23361				},
23362
23363				comment: function(text) {
23364					node.append(createNode('#comment', 8)).value = text;
23365				},
23366
23367				pi: function(name, text) {
23368					node.append(createNode(name, 7)).value = text;
23369					removeWhitespaceBefore(node);
23370				},
23371
23372				doctype: function(text) {
23373					var newNode;
23374
23375					newNode = node.append(createNode('#doctype', 10));
23376					newNode.value = text;
23377					removeWhitespaceBefore(node);
23378				},
23379
23380				start: function(name, attrs, empty) {
23381					var newNode, attrFiltersLen, elementRule, attrName, parent;
23382
23383					elementRule = validate ? schema.getElementRule(name) : {};
23384					if (elementRule) {
23385						newNode = createNode(elementRule.outputName || name, 1);
23386						newNode.attributes = attrs;
23387						newNode.shortEnded = empty;
23388
23389						node.append(newNode);
23390
23391						// Check if node is valid child of the parent node is the child is
23392						// unknown we don't collect it since it's probably a custom element
23393						parent = children[node.name];
23394						if (parent && children[newNode.name] && !parent[newNode.name]) {
23395							invalidChildren.push(newNode);
23396						}
23397
23398						attrFiltersLen = attributeFilters.length;
23399						while (attrFiltersLen--) {
23400							attrName = attributeFilters[attrFiltersLen].name;
23401
23402							if (attrName in attrs.map) {
23403								list = matchedAttributes[attrName];
23404
23405								if (list) {
23406									list.push(newNode);
23407								} else {
23408									matchedAttributes[attrName] = [newNode];
23409								}
23410							}
23411						}
23412
23413						// Trim whitespace before block
23414						if (blockElements[name]) {
23415							removeWhitespaceBefore(newNode);
23416						}
23417
23418						// Change current node if the element wasn't empty i.e not <br /> or <img />
23419						if (!empty) {
23420							node = newNode;
23421						}
23422
23423						// Check if we are inside a whitespace preserved element
23424						if (!isInWhiteSpacePreservedElement && whiteSpaceElements[name]) {
23425							isInWhiteSpacePreservedElement = true;
23426						}
23427					}
23428				},
23429
23430				end: function(name) {
23431					var textNode, elementRule, text, sibling, tempNode;
23432
23433					elementRule = validate ? schema.getElementRule(name) : {};
23434					if (elementRule) {
23435						if (blockElements[name]) {
23436							if (!isInWhiteSpacePreservedElement) {
23437								// Trim whitespace of the first node in a block
23438								textNode = node.firstChild;
23439								if (textNode && textNode.type === 3) {
23440									text = textNode.value.replace(startWhiteSpaceRegExp, '');
23441
23442									// Any characters left after trim or should we remove it
23443									if (text.length > 0) {
23444										textNode.value = text;
23445										textNode = textNode.next;
23446									} else {
23447										sibling = textNode.next;
23448										textNode.remove();
23449										textNode = sibling;
23450
23451										// Remove any pure whitespace siblings
23452										while (textNode && textNode.type === 3) {
23453											text = textNode.value;
23454											sibling = textNode.next;
23455
23456											if (text.length === 0 || isAllWhiteSpaceRegExp.test(text)) {
23457												textNode.remove();
23458												textNode = sibling;
23459											}
23460
23461											textNode = sibling;
23462										}
23463									}
23464								}
23465
23466								// Trim whitespace of the last node in a block
23467								textNode = node.lastChild;
23468								if (textNode && textNode.type === 3) {
23469									text = textNode.value.replace(endWhiteSpaceRegExp, '');
23470
23471									// Any characters left after trim or should we remove it
23472									if (text.length > 0) {
23473										textNode.value = text;
23474										textNode = textNode.prev;
23475									} else {
23476										sibling = textNode.prev;
23477										textNode.remove();
23478										textNode = sibling;
23479
23480										// Remove any pure whitespace siblings
23481										while (textNode && textNode.type === 3) {
23482											text = textNode.value;
23483											sibling = textNode.prev;
23484
23485											if (text.length === 0 || isAllWhiteSpaceRegExp.test(text)) {
23486												textNode.remove();
23487												textNode = sibling;
23488											}
23489
23490											textNode = sibling;
23491										}
23492									}
23493								}
23494							}
23495
23496							// Trim start white space
23497							// Removed due to: #5424
23498							/*textNode = node.prev;
23499							if (textNode && textNode.type === 3) {
23500								text = textNode.value.replace(startWhiteSpaceRegExp, '');
23501
23502								if (text.length > 0)
23503									textNode.value = text;
23504								else
23505									textNode.remove();
23506							}*/
23507						}
23508
23509						// Check if we exited a whitespace preserved element
23510						if (isInWhiteSpacePreservedElement && whiteSpaceElements[name]) {
23511							isInWhiteSpacePreservedElement = false;
23512						}
23513
23514						// Handle empty nodes
23515						if (elementRule.removeEmpty || elementRule.paddEmpty) {
23516							if (node.isEmpty(nonEmptyElements)) {
23517								if (elementRule.paddEmpty) {
23518									node.empty().append(new Node('#text', '3')).value = '\u00a0';
23519								} else {
23520									// Leave nodes that have a name like <a name="name">
23521									if (!node.attributes.map.name && !node.attributes.map.id) {
23522										tempNode = node.parent;
23523
23524										if (blockElements[node.name]) {
23525											node.empty().remove();
23526										} else {
23527											node.unwrap();
23528										}
23529
23530										node = tempNode;
23531										return;
23532									}
23533								}
23534							}
23535						}
23536
23537						node = node.parent;
23538					}
23539				}
23540			}, schema);
23541
23542			rootNode = node = new Node(args.context || settings.root_name, 11);
23543
23544			parser.parse(html);
23545
23546			// Fix invalid children or report invalid children in a contextual parsing
23547			if (validate && invalidChildren.length) {
23548				if (!args.context) {
23549					fixInvalidChildren(invalidChildren);
23550				} else {
23551					args.invalid = true;
23552				}
23553			}
23554
23555			// Wrap nodes in the root into block elements if the root is body
23556			if (rootBlockName && (rootNode.name == 'body' || args.isRootContent)) {
23557				addRootBlocks();
23558			}
23559
23560			// Run filters only when the contents is valid
23561			if (!args.invalid) {
23562				// Run node filters
23563				for (name in matchedNodes) {
23564					list = nodeFilters[name];
23565					nodes = matchedNodes[name];
23566
23567					// Remove already removed children
23568					fi = nodes.length;
23569					while (fi--) {
23570						if (!nodes[fi].parent) {
23571							nodes.splice(fi, 1);
23572						}
23573					}
23574
23575					for (i = 0, l = list.length; i < l; i++) {
23576						list[i](nodes, name, args);
23577					}
23578				}
23579
23580				// Run attribute filters
23581				for (i = 0, l = attributeFilters.length; i < l; i++) {
23582					list = attributeFilters[i];
23583
23584					if (list.name in matchedAttributes) {
23585						nodes = matchedAttributes[list.name];
23586
23587						// Remove already removed children
23588						fi = nodes.length;
23589						while (fi--) {
23590							if (!nodes[fi].parent) {
23591								nodes.splice(fi, 1);
23592							}
23593						}
23594
23595						for (fi = 0, fl = list.callbacks.length; fi < fl; fi++) {
23596							list.callbacks[fi](nodes, list.name, args);
23597						}
23598					}
23599				}
23600			}
23601
23602			return rootNode;
23603		};
23604
23605		// Remove <br> at end of block elements Gecko and WebKit injects BR elements to
23606		// make it possible to place the caret inside empty blocks. This logic tries to remove
23607		// these elements and keep br elements that where intended to be there intact
23608		if (settings.remove_trailing_brs) {
23609			self.addNodeFilter('br', function(nodes) {
23610				var i, l = nodes.length, node, blockElements = extend({}, schema.getBlockElements());
23611				var nonEmptyElements = schema.getNonEmptyElements(), parent, lastParent, prev, prevName;
23612				var elementRule, textNode;
23613
23614				// Remove brs from body element as well
23615				blockElements.body = 1;
23616
23617				// Must loop forwards since it will otherwise remove all brs in <p>a<br><br><br></p>
23618				for (i = 0; i < l; i++) {
23619					node = nodes[i];
23620					parent = node.parent;
23621
23622					if (blockElements[node.parent.name] && node === parent.lastChild) {
23623						// Loop all nodes to the left of the current node and check for other BR elements
23624						// excluding bookmarks since they are invisible
23625						prev = node.prev;
23626						while (prev) {
23627							prevName = prev.name;
23628
23629							// Ignore bookmarks
23630							if (prevName !== "span" || prev.attr('data-mce-type') !== 'bookmark') {
23631								// Found a non BR element
23632								if (prevName !== "br") {
23633									break;
23634								}
23635
23636								// Found another br it's a <br><br> structure then don't remove anything
23637								if (prevName === 'br') {
23638									node = null;
23639									break;
23640								}
23641							}
23642
23643							prev = prev.prev;
23644						}
23645
23646						if (node) {
23647							node.remove();
23648
23649							// Is the parent to be considered empty after we removed the BR
23650							if (parent.isEmpty(nonEmptyElements)) {
23651								elementRule = schema.getElementRule(parent.name);
23652
23653								// Remove or padd the element depending on schema rule
23654								if (elementRule) {
23655									if (elementRule.removeEmpty) {
23656										parent.remove();
23657									} else if (elementRule.paddEmpty) {
23658										parent.empty().append(new Node('#text', 3)).value = '\u00a0';
23659									}
23660								}
23661							}
23662						}
23663					} else {
23664						// Replaces BR elements inside inline elements like <p><b><i><br></i></b></p>
23665						// so they become <p><b><i>&nbsp;</i></b></p>
23666						lastParent = node;
23667						while (parent && parent.firstChild === lastParent && parent.lastChild === lastParent) {
23668							lastParent = parent;
23669
23670							if (blockElements[parent.name]) {
23671								break;
23672							}
23673
23674							parent = parent.parent;
23675						}
23676
23677						if (lastParent === parent) {
23678							textNode = new Node('#text', 3);
23679							textNode.value = '\u00a0';
23680							node.replace(textNode);
23681						}
23682					}
23683				}
23684			});
23685		}
23686
23687		// Force anchor names closed, unless the setting "allow_html_in_named_anchor" is explicitly included.
23688		if (!settings.allow_html_in_named_anchor) {
23689			self.addAttributeFilter('id,name', function(nodes) {
23690				var i = nodes.length, sibling, prevSibling, parent, node;
23691
23692				while (i--) {
23693					node = nodes[i];
23694					if (node.name === 'a' && node.firstChild && !node.attr('href')) {
23695						parent = node.parent;
23696
23697						// Move children after current node
23698						sibling = node.lastChild;
23699						do {
23700							prevSibling = sibling.prev;
23701							parent.insert(sibling, node);
23702							sibling = prevSibling;
23703						} while (sibling);
23704					}
23705				}
23706			});
23707		}
23708
23709		if (settings.validate && schema.getValidClasses()) {
23710			self.addAttributeFilter('class', function(nodes) {
23711				var i = nodes.length, node, classList, ci, className, classValue;
23712				var validClasses = schema.getValidClasses(), validClassesMap, valid;
23713
23714				while (i--) {
23715					node = nodes[i];
23716					classList = node.attr('class').split(' ');
23717					classValue = '';
23718
23719					for (ci = 0; ci < classList.length; ci++) {
23720						className = classList[ci];
23721						valid = false;
23722
23723						validClassesMap = validClasses['*'];
23724						if (validClassesMap && validClassesMap[className]) {
23725							valid = true;
23726						}
23727
23728						validClassesMap = validClasses[node.name];
23729						if (!valid && validClassesMap && validClassesMap[className]) {
23730							valid = true;
23731						}
23732
23733						if (valid) {
23734							if (classValue) {
23735								classValue += ' ';
23736							}
23737
23738							classValue += className;
23739						}
23740					}
23741
23742					if (!classValue.length) {
23743						classValue = null;
23744					}
23745
23746					node.attr('class', classValue);
23747				}
23748			});
23749		}
23750	};
23751});
23752
23753// Included from: js/tinymce/classes/html/Writer.js
23754
23755/**
23756 * Writer.js
23757 *
23758 * Released under LGPL License.
23759 * Copyright (c) 1999-2015 Ephox Corp. All rights reserved
23760 *
23761 * License: http://www.tinymce.com/license
23762 * Contributing: http://www.tinymce.com/contributing
23763 */
23764
23765/**
23766 * This class is used to write HTML tags out it can be used with the Serializer or the SaxParser.
23767 *
23768 * @class tinymce.html.Writer
23769 * @example
23770 * var writer = new tinymce.html.Writer({indent: true});
23771 * var parser = new tinymce.html.SaxParser(writer).parse('<p><br></p>');
23772 * console.log(writer.getContent());
23773 *
23774 * @class tinymce.html.Writer
23775 * @version 3.4
23776 */
23777define("tinymce/html/Writer", [
23778	"tinymce/html/Entities",
23779	"tinymce/util/Tools"
23780], function(Entities, Tools) {
23781	var makeMap = Tools.makeMap;
23782
23783	/**
23784	 * Constructs a new Writer instance.
23785	 *
23786	 * @constructor
23787	 * @method Writer
23788	 * @param {Object} settings Name/value settings object.
23789	 */
23790	return function(settings) {
23791		var html = [], indent, indentBefore, indentAfter, encode, htmlOutput;
23792
23793		settings = settings || {};
23794		indent = settings.indent;
23795		indentBefore = makeMap(settings.indent_before || '');
23796		indentAfter = makeMap(settings.indent_after || '');
23797		encode = Entities.getEncodeFunc(settings.entity_encoding || 'raw', settings.entities);
23798		htmlOutput = settings.element_format == "html";
23799
23800		return {
23801			/**
23802			 * Writes the a start element such as <p id="a">.
23803			 *
23804			 * @method start
23805			 * @param {String} name Name of the element.
23806			 * @param {Array} attrs Optional attribute array or undefined if it hasn't any.
23807			 * @param {Boolean} empty Optional empty state if the tag should end like <br />.
23808			 */
23809			start: function(name, attrs, empty) {
23810				var i, l, attr, value;
23811
23812				if (indent && indentBefore[name] && html.length > 0) {
23813					value = html[html.length - 1];
23814
23815					if (value.length > 0 && value !== '\n') {
23816						html.push('\n');
23817					}
23818				}
23819
23820				html.push('<', name);
23821
23822				if (attrs) {
23823					for (i = 0, l = attrs.length; i < l; i++) {
23824						attr = attrs[i];
23825						html.push(' ', attr.name, '="', encode(attr.value, true), '"');
23826					}
23827				}
23828
23829				if (!empty || htmlOutput) {
23830					html[html.length] = '>';
23831				} else {
23832					html[html.length] = ' />';
23833				}
23834
23835				if (empty && indent && indentAfter[name] && html.length > 0) {
23836					value = html[html.length - 1];
23837
23838					if (value.length > 0 && value !== '\n') {
23839						html.push('\n');
23840					}
23841				}
23842			},
23843
23844			/**
23845			 * Writes the a end element such as </p>.
23846			 *
23847			 * @method end
23848			 * @param {String} name Name of the element.
23849			 */
23850			end: function(name) {
23851				var value;
23852
23853				/*if (indent && indentBefore[name] && html.length > 0) {
23854					value = html[html.length - 1];
23855
23856					if (value.length > 0 && value !== '\n')
23857						html.push('\n');
23858				}*/
23859
23860				html.push('</', name, '>');
23861
23862				if (indent && indentAfter[name] && html.length > 0) {
23863					value = html[html.length - 1];
23864
23865					if (value.length > 0 && value !== '\n') {
23866						html.push('\n');
23867					}
23868				}
23869			},
23870
23871			/**
23872			 * Writes a text node.
23873			 *
23874			 * @method text
23875			 * @param {String} text String to write out.
23876			 * @param {Boolean} raw Optional raw state if true the contents wont get encoded.
23877			 */
23878			text: function(text, raw) {
23879				if (text.length > 0) {
23880					html[html.length] = raw ? text : encode(text);
23881				}
23882			},
23883
23884			/**
23885			 * Writes a cdata node such as <![CDATA[data]]>.
23886			 *
23887			 * @method cdata
23888			 * @param {String} text String to write out inside the cdata.
23889			 */
23890			cdata: function(text) {
23891				html.push('<![CDATA[', text, ']]>');
23892			},
23893
23894			/**
23895			 * Writes a comment node such as <!-- Comment -->.
23896			 *
23897			 * @method cdata
23898			 * @param {String} text String to write out inside the comment.
23899			 */
23900			comment: function(text) {
23901				html.push('<!--', text, '-->');
23902			},
23903
23904			/**
23905			 * Writes a PI node such as <?xml attr="value" ?>.
23906			 *
23907			 * @method pi
23908			 * @param {String} name Name of the pi.
23909			 * @param {String} text String to write out inside the pi.
23910			 */
23911			pi: function(name, text) {
23912				if (text) {
23913					html.push('<?', name, ' ', encode(text), '?>');
23914				} else {
23915					html.push('<?', name, '?>');
23916				}
23917
23918				if (indent) {
23919					html.push('\n');
23920				}
23921			},
23922
23923			/**
23924			 * Writes a doctype node such as <!DOCTYPE data>.
23925			 *
23926			 * @method doctype
23927			 * @param {String} text String to write out inside the doctype.
23928			 */
23929			doctype: function(text) {
23930				html.push('<!DOCTYPE', text, '>', indent ? '\n' : '');
23931			},
23932
23933			/**
23934			 * Resets the internal buffer if one wants to reuse the writer.
23935			 *
23936			 * @method reset
23937			 */
23938			reset: function() {
23939				html.length = 0;
23940			},
23941
23942			/**
23943			 * Returns the contents that got serialized.
23944			 *
23945			 * @method getContent
23946			 * @return {String} HTML contents that got written down.
23947			 */
23948			getContent: function() {
23949				return html.join('').replace(/\n$/, '');
23950			}
23951		};
23952	};
23953});
23954
23955// Included from: js/tinymce/classes/html/Serializer.js
23956
23957/**
23958 * Serializer.js
23959 *
23960 * Released under LGPL License.
23961 * Copyright (c) 1999-2015 Ephox Corp. All rights reserved
23962 *
23963 * License: http://www.tinymce.com/license
23964 * Contributing: http://www.tinymce.com/contributing
23965 */
23966
23967/**
23968 * This class is used to serialize down the DOM tree into a string using a Writer instance.
23969 *
23970 *
23971 * @example
23972 * new tinymce.html.Serializer().serialize(new tinymce.html.DomParser().parse('<p>text</p>'));
23973 * @class tinymce.html.Serializer
23974 * @version 3.4
23975 */
23976define("tinymce/html/Serializer", [
23977	"tinymce/html/Writer",
23978	"tinymce/html/Schema"
23979], function(Writer, Schema) {
23980	/**
23981	 * Constructs a new Serializer instance.
23982	 *
23983	 * @constructor
23984	 * @method Serializer
23985	 * @param {Object} settings Name/value settings object.
23986	 * @param {tinymce.html.Schema} schema Schema instance to use.
23987	 */
23988	return function(settings, schema) {
23989		var self = this, writer = new Writer(settings);
23990
23991		settings = settings || {};
23992		settings.validate = "validate" in settings ? settings.validate : true;
23993
23994		self.schema = schema = schema || new Schema();
23995		self.writer = writer;
23996
23997		/**
23998		 * Serializes the specified node into a string.
23999		 *
24000		 * @example
24001		 * new tinymce.html.Serializer().serialize(new tinymce.html.DomParser().parse('<p>text</p>'));
24002		 * @method serialize
24003		 * @param {tinymce.html.Node} node Node instance to serialize.
24004		 * @return {String} String with HTML based on DOM tree.
24005		 */
24006		self.serialize = function(node) {
24007			var handlers, validate;
24008
24009			validate = settings.validate;
24010
24011			handlers = {
24012				// #text
24013				3: function(node) {
24014					writer.text(node.value, node.raw);
24015				},
24016
24017				// #comment
24018				8: function(node) {
24019					writer.comment(node.value);
24020				},
24021
24022				// Processing instruction
24023				7: function(node) {
24024					writer.pi(node.name, node.value);
24025				},
24026
24027				// Doctype
24028				10: function(node) {
24029					writer.doctype(node.value);
24030				},
24031
24032				// CDATA
24033				4: function(node) {
24034					writer.cdata(node.value);
24035				},
24036
24037				// Document fragment
24038				11: function(node) {
24039					if ((node = node.firstChild)) {
24040						do {
24041							walk(node);
24042						} while ((node = node.next));
24043					}
24044				}
24045			};
24046
24047			writer.reset();
24048
24049			function walk(node) {
24050				var handler = handlers[node.type], name, isEmpty, attrs, attrName, attrValue, sortedAttrs, i, l, elementRule;
24051
24052				if (!handler) {
24053					name = node.name;
24054					isEmpty = node.shortEnded;
24055					attrs = node.attributes;
24056
24057					// Sort attributes
24058					if (validate && attrs && attrs.length > 1) {
24059						sortedAttrs = [];
24060						sortedAttrs.map = {};
24061
24062						elementRule = schema.getElementRule(node.name);
24063						if (elementRule) {
24064							for (i = 0, l = elementRule.attributesOrder.length; i < l; i++) {
24065								attrName = elementRule.attributesOrder[i];
24066
24067								if (attrName in attrs.map) {
24068									attrValue = attrs.map[attrName];
24069									sortedAttrs.map[attrName] = attrValue;
24070									sortedAttrs.push({name: attrName, value: attrValue});
24071								}
24072							}
24073
24074							for (i = 0, l = attrs.length; i < l; i++) {
24075								attrName = attrs[i].name;
24076
24077								if (!(attrName in sortedAttrs.map)) {
24078									attrValue = attrs.map[attrName];
24079									sortedAttrs.map[attrName] = attrValue;
24080									sortedAttrs.push({name: attrName, value: attrValue});
24081								}
24082							}
24083
24084							attrs = sortedAttrs;
24085						}
24086					}
24087
24088					writer.start(node.name, attrs, isEmpty);
24089
24090					if (!isEmpty) {
24091						if ((node = node.firstChild)) {
24092							do {
24093								walk(node);
24094							} while ((node = node.next));
24095						}
24096
24097						writer.end(name);
24098					}
24099				} else {
24100					handler(node);
24101				}
24102			}
24103
24104			// Serialize element and treat all non elements as fragments
24105			if (node.type == 1 && !settings.inner) {
24106				walk(node);
24107			} else {
24108				handlers[11](node);
24109			}
24110
24111			return writer.getContent();
24112		};
24113	};
24114});
24115
24116// Included from: js/tinymce/classes/dom/Serializer.js
24117
24118/**
24119 * Serializer.js
24120 *
24121 * Released under LGPL License.
24122 * Copyright (c) 1999-2015 Ephox Corp. All rights reserved
24123 *
24124 * License: http://www.tinymce.com/license
24125 * Contributing: http://www.tinymce.com/contributing
24126 */
24127
24128/**
24129 * This class is used to serialize DOM trees into a string. Consult the TinyMCE Wiki API for
24130 * more details and examples on how to use this class.
24131 *
24132 * @class tinymce.dom.Serializer
24133 */
24134define("tinymce/dom/Serializer", [
24135	"tinymce/dom/DOMUtils",
24136	"tinymce/html/DomParser",
24137	"tinymce/html/SaxParser",
24138	"tinymce/html/Entities",
24139	"tinymce/html/Serializer",
24140	"tinymce/html/Node",
24141	"tinymce/html/Schema",
24142	"tinymce/Env",
24143	"tinymce/util/Tools",
24144	"tinymce/text/Zwsp"
24145], function(DOMUtils, DomParser, SaxParser, Entities, Serializer, Node, Schema, Env, Tools, Zwsp) {
24146	var each = Tools.each, trim = Tools.trim;
24147	var DOM = DOMUtils.DOM;
24148	var trimContentRegExp = new RegExp([
24149		'<span[^>]+data-mce-bogus[^>]+>[\u200B\uFEFF]+<\\/span>', // Trim bogus spans like caret containers
24150		'\\s?data-mce-selected="[^"]+"' // Trim temporaty data-mce prefixed attributes like data-mce-selected
24151	].join('|'), 'gi');
24152
24153	/**
24154	 * IE 11 has a fantastic bug where it will produce two trailing BR elements to iframe bodies when
24155	 * the iframe is hidden by display: none on a parent container. The DOM is actually out of sync
24156	 * with innerHTML in this case. It's like IE adds shadow DOM BR elements that appears on innerHTML
24157	 * but not as the lastChild of the body. So this fix simply removes the last two
24158	 * BR elements at the end of the document.
24159	 *
24160	 * Example of what happens: <body>text</body> becomes <body>text<br><br></body>
24161	 */
24162	function trimTrailingBr(rootNode) {
24163		var brNode1, brNode2;
24164
24165		function isBr(node) {
24166			return node && node.name === 'br';
24167		}
24168
24169		brNode1 = rootNode.lastChild;
24170		if (isBr(brNode1)) {
24171			brNode2 = brNode1.prev;
24172
24173			if (isBr(brNode2)) {
24174				brNode1.remove();
24175				brNode2.remove();
24176			}
24177		}
24178	}
24179
24180	/**
24181	 * Constructs a new DOM serializer class.
24182	 *
24183	 * @constructor
24184	 * @method Serializer
24185	 * @param {Object} settings Serializer settings object.
24186	 * @param {tinymce.Editor} editor Optional editor to bind events to and get schema/dom from.
24187	 */
24188	return function(settings, editor) {
24189		var dom, schema, htmlParser;
24190
24191		if (editor) {
24192			dom = editor.dom;
24193			schema = editor.schema;
24194		}
24195
24196		/**
24197		 * Returns a trimmed version of the editor contents to be used for the undo level. This
24198		 * will remove any data-mce-bogus="all" marked elements since these are used for UI it will also
24199		 * remove the data-mce-selected attributes used for selection of objects and caret containers.
24200		 * It will keep all data-mce-bogus="1" elements since these can be used to place the caret etc and will
24201		 * be removed by the serialization logic when you save.
24202		 *
24203		 * @private
24204		 * @return {String} HTML contents of the editor excluding some internal bogus elements.
24205		 */
24206		function getTrimmedContent() {
24207			var content = editor.getBody().innerHTML;
24208			var bogusAllRegExp = /<(\w+) [^>]*data-mce-bogus="all"[^>]*>/g;
24209			var endTagIndex, index, matchLength, matches, shortEndedElements, schema = editor.schema;
24210
24211			content = Zwsp.trim(content.replace(trimContentRegExp, ''));
24212			shortEndedElements = schema.getShortEndedElements();
24213
24214			// Remove all bogus elements marked with "all"
24215			while ((matches = bogusAllRegExp.exec(content))) {
24216				index = bogusAllRegExp.lastIndex;
24217				matchLength = matches[0].length;
24218
24219				if (shortEndedElements[matches[1]]) {
24220					endTagIndex = index;
24221				} else {
24222					endTagIndex = SaxParser.findEndTag(schema, content, index);
24223				}
24224
24225				content = content.substring(0, index - matchLength) + content.substring(endTagIndex);
24226				bogusAllRegExp.lastIndex = index - matchLength;
24227			}
24228
24229			return trim(content);
24230		}
24231
24232		// Default DOM and Schema if they are undefined
24233		dom = dom || DOM;
24234		schema = schema || new Schema(settings);
24235		settings.entity_encoding = settings.entity_encoding || 'named';
24236		settings.remove_trailing_brs = "remove_trailing_brs" in settings ? settings.remove_trailing_brs : true;
24237
24238		htmlParser = new DomParser(settings, schema);
24239
24240		// Convert tabindex back to elements when serializing contents
24241		htmlParser.addAttributeFilter('data-mce-tabindex', function(nodes, name) {
24242			var i = nodes.length, node;
24243
24244			while (i--) {
24245				node = nodes[i];
24246				node.attr('tabindex', node.attributes.map['data-mce-tabindex']);
24247				node.attr(name, null);
24248			}
24249		});
24250
24251		// Convert move data-mce-src, data-mce-href and data-mce-style into nodes or process them if needed
24252		htmlParser.addAttributeFilter('src,href,style', function(nodes, name) {
24253			var i = nodes.length, node, value, internalName = 'data-mce-' + name;
24254			var urlConverter = settings.url_converter, urlConverterScope = settings.url_converter_scope, undef;
24255
24256			while (i--) {
24257				node = nodes[i];
24258
24259				value = node.attributes.map[internalName];
24260				if (value !== undef) {
24261					// Set external name to internal value and remove internal
24262					node.attr(name, value.length > 0 ? value : null);
24263					node.attr(internalName, null);
24264				} else {
24265					// No internal attribute found then convert the value we have in the DOM
24266					value = node.attributes.map[name];
24267
24268					if (name === "style") {
24269						value = dom.serializeStyle(dom.parseStyle(value), node.name);
24270					} else if (urlConverter) {
24271						value = urlConverter.call(urlConverterScope, value, name, node.name);
24272					}
24273
24274					node.attr(name, value.length > 0 ? value : null);
24275				}
24276			}
24277		});
24278
24279		// Remove internal classes mceItem<..> or mceSelected
24280		htmlParser.addAttributeFilter('class', function(nodes) {
24281			var i = nodes.length, node, value;
24282
24283			while (i--) {
24284				node = nodes[i];
24285				value = node.attr('class');
24286
24287				if (value) {
24288					value = node.attr('class').replace(/(?:^|\s)mce-item-\w+(?!\S)/g, '');
24289					node.attr('class', value.length > 0 ? value : null);
24290				}
24291			}
24292		});
24293
24294		// Remove bookmark elements
24295		htmlParser.addAttributeFilter('data-mce-type', function(nodes, name, args) {
24296			var i = nodes.length, node;
24297
24298			while (i--) {
24299				node = nodes[i];
24300
24301				if (node.attributes.map['data-mce-type'] === 'bookmark' && !args.cleanup) {
24302					node.remove();
24303				}
24304			}
24305		});
24306
24307		htmlParser.addNodeFilter('noscript', function(nodes) {
24308			var i = nodes.length, node;
24309
24310			while (i--) {
24311				node = nodes[i].firstChild;
24312
24313				if (node) {
24314					node.value = Entities.decode(node.value);
24315				}
24316			}
24317		});
24318
24319		// Force script into CDATA sections and remove the mce- prefix also add comments around styles
24320		htmlParser.addNodeFilter('script,style', function(nodes, name) {
24321			var i = nodes.length, node, value, type;
24322
24323			function trim(value) {
24324				/*jshint maxlen:255 */
24325				/*eslint max-len:0 */
24326				return value.replace(/(<!--\[CDATA\[|\]\]-->)/g, '\n')
24327						.replace(/^[\r\n]*|[\r\n]*$/g, '')
24328						.replace(/^\s*((<!--)?(\s*\/\/)?\s*<!\[CDATA\[|(<!--\s*)?\/\*\s*<!\[CDATA\[\s*\*\/|(\/\/)?\s*<!--|\/\*\s*<!--\s*\*\/)\s*[\r\n]*/gi, '')
24329						.replace(/\s*(\/\*\s*\]\]>\s*\*\/(-->)?|\s*\/\/\s*\]\]>(-->)?|\/\/\s*(-->)?|\]\]>|\/\*\s*-->\s*\*\/|\s*-->\s*)\s*$/g, '');
24330			}
24331
24332			while (i--) {
24333				node = nodes[i];
24334				value = node.firstChild ? node.firstChild.value : '';
24335
24336				if (name === "script") {
24337					// Remove mce- prefix from script elements and remove default type since the user specified
24338					// a script element without type attribute
24339					type = node.attr('type');
24340					if (type) {
24341						node.attr('type', type == 'mce-no/type' ? null : type.replace(/^mce\-/, ''));
24342					}
24343
24344					if (value.length > 0) {
24345						node.firstChild.value = '// <![CDATA[\n' + trim(value) + '\n// ]]>';
24346					}
24347				} else {
24348					if (value.length > 0) {
24349						node.firstChild.value = '<!--\n' + trim(value) + '\n-->';
24350					}
24351				}
24352			}
24353		});
24354
24355		// Convert comments to cdata and handle protected comments
24356		htmlParser.addNodeFilter('#comment', function(nodes) {
24357			var i = nodes.length, node;
24358
24359			while (i--) {
24360				node = nodes[i];
24361
24362				if (node.value.indexOf('[CDATA[') === 0) {
24363					node.name = '#cdata';
24364					node.type = 4;
24365					node.value = node.value.replace(/^\[CDATA\[|\]\]$/g, '');
24366				} else if (node.value.indexOf('mce:protected ') === 0) {
24367					node.name = "#text";
24368					node.type = 3;
24369					node.raw = true;
24370					node.value = unescape(node.value).substr(14);
24371				}
24372			}
24373		});
24374
24375		htmlParser.addNodeFilter('xml:namespace,input', function(nodes, name) {
24376			var i = nodes.length, node;
24377
24378			while (i--) {
24379				node = nodes[i];
24380				if (node.type === 7) {
24381					node.remove();
24382				} else if (node.type === 1) {
24383					if (name === "input" && !("type" in node.attributes.map)) {
24384						node.attr('type', 'text');
24385					}
24386				}
24387			}
24388		});
24389
24390		// Fix list elements, TODO: Replace this later
24391		if (settings.fix_list_elements) {
24392			htmlParser.addNodeFilter('ul,ol', function(nodes) {
24393				var i = nodes.length, node, parentNode;
24394
24395				while (i--) {
24396					node = nodes[i];
24397					parentNode = node.parent;
24398
24399					if (parentNode.name === 'ul' || parentNode.name === 'ol') {
24400						if (node.prev && node.prev.name === 'li') {
24401							node.prev.append(node);
24402						}
24403					}
24404				}
24405			});
24406		}
24407
24408		// Remove internal data attributes
24409		htmlParser.addAttributeFilter(
24410			'data-mce-src,data-mce-href,data-mce-style,' +
24411			'data-mce-selected,data-mce-expando,' +
24412			'data-mce-type,data-mce-resize',
24413
24414			function(nodes, name) {
24415				var i = nodes.length;
24416
24417				while (i--) {
24418					nodes[i].attr(name, null);
24419				}
24420			}
24421		);
24422
24423		// Return public methods
24424		return {
24425			/**
24426			 * Schema instance that was used to when the Serializer was constructed.
24427			 *
24428			 * @field {tinymce.html.Schema} schema
24429			 */
24430			schema: schema,
24431
24432			/**
24433			 * Adds a node filter function to the parser used by the serializer, the parser will c
24433ollect the specified nodes by name
24434			 * and then execute the callback ones it has finished parsing the document.
24435			 *
24436			 * @example
24437			 * parser.addNodeFilter('p,h1', function(nodes, name) {
24438			 *		for (var i = 0; i < nodes.length; i++) {
24439			 *			console.log(nodes[i].name);
24440			 *		}
24441			 * });
24442			 * @method addNodeFilter
24443			 * @method {String} name Comma separated list of nodes to collect.
24444			 * @param {function} callback Callback function to execute once it has collected nodes.
24445			 */
24446			addNodeFilter: htmlParser.addNodeFilter,
24447
24448			/**
24449			 * Adds a attribute filter function to the parser used by the serializer, the parser will
24450			 * collect nodes that has the specified attributes
24451			 * and then execute the callback ones it has finished parsing the document.
24452			 *
24453			 * @example
24454			 * parser.addAttributeFilter('src,href', function(nodes, name) {
24455			 *		for (var i = 0; i < nodes.length; i++) {
24456			 *			console.log(nodes[i].name);
24457			 *		}
24458			 * });
24459			 * @method addAttributeFilter
24460			 * @method {String} name Comma separated list of nodes to collect.
24461			 * @param {function} callback Callback function to execute once it has collected nodes.
24462			 */
24463			addAttributeFilter: htmlParser.addAttributeFilter,
24464
24465			/**
24466			 * Serializes the specified browser DOM node into a HTML string.
24467			 *
24468			 * @method serialize
24469			 * @param {DOMNode} node DOM node to serialize.
24470			 * @param {Object} args Arguments option that gets passed to event handlers.
24471			 */
24472			serialize: function(node, args) {
24473				var self = this, impl, doc, oldDoc, htmlSerializer, content, rootNode;
24474
24475				// Explorer won't clone contents of script and style and the
24476				// selected index of select elements are cleared on a clone operation.
24477				if (Env.ie && dom.select('script,style,select,map').length > 0) {
24478					content = node.innerHTML;
24479					node = node.cloneNode(false);
24480					dom.setHTML(node, content);
24481				} else {
24482					node = node.cloneNode(true);
24483				}
24484
24485				// Nodes needs to be attached to something in WebKit/Opera
24486				// This fix will make DOM ranges and make Sizzle happy!
24487				impl = node.ownerDocument.implementation;
24488				if (impl.createHTMLDocument) {
24489					// Create an empty HTML document
24490					doc = impl.createHTMLDocument("");
24491
24492					// Add the element or it's children if it's a body element to the new document
24493					each(node.nodeName == 'BODY' ? node.childNodes : [node], function(node) {
24494						doc.body.appendChild(doc.importNode(node, true));
24495					});
24496
24497					// Grab first child or body element for serialization
24498					if (node.nodeName != 'BODY') {
24499						node = doc.body.firstChild;
24500					} else {
24501						node = doc.body;
24502					}
24503
24504					// set the new document in DOMUtils so createElement etc works
24505					oldDoc = dom.doc;
24506					dom.doc = doc;
24507				}
24508
24509				args = args || {};
24510				args.format = args.format || 'html';
24511
24512				// Don't wrap content if we want selected html
24513				if (args.selection) {
24514					args.forced_root_block = '';
24515				}
24516
24517				// Pre process
24518				if (!args.no_events) {
24519					args.node = node;
24520					self.onPreProcess(args);
24521				}
24522
24523				// Parse HTML
24524				rootNode = htmlParser.parse(trim(args.getInner ? node.innerHTML : dom.getOuterHTML(node)), args);
24525				trimTrailingBr(rootNode);
24526
24527				// Serialize HTML
24528				htmlSerializer = new Serializer(settings, schema);
24529				args.content = htmlSerializer.serialize(rootNode);
24530
24531				// Replace all BOM characters for now until we can find a better solution
24532				if (!args.cleanup) {
24533					args.content = Zwsp.trim(args.content);
24534					args.content = args.content.replace(/\uFEFF/g, '');
24535				}
24536
24537				// Post process
24538				if (!args.no_events) {
24539					self.onPostProcess(args);
24540				}
24541
24542				// Restore the old document if it was changed
24543				if (oldDoc) {
24544					dom.doc = oldDoc;
24545				}
24546
24547				args.node = null;
24548
24549				return args.content;
24550			},
24551
24552			/**
24553			 * Adds valid elements rules to the serializers schema instance this enables you to specify things
24554			 * like what elements should be outputted and what attributes specific elements might have.
24555			 * Consult the Wiki for more details on this format.
24556			 *
24557			 * @method addRules
24558			 * @param {String} rules Valid elements rules string to add to schema.
24559			 */
24560			addRules: function(rules) {
24561				schema.addValidElements(rules);
24562			},
24563
24564			/**
24565			 * Sets the valid elements rules to the serializers schema instance this enables you to specify things
24566			 * like what elements should be outputted and what attributes specific elements might have.
24567			 * Consult the Wiki for more details on this format.
24568			 *
24569			 * @method setRules
24570			 * @param {String} rules Valid elements rules string.
24571			 */
24572			setRules: function(rules) {
24573				schema.setValidElements(rules);
24574			},
24575
24576			onPreProcess: function(args) {
24577				if (editor) {
24578					editor.fire('PreProcess', args);
24579				}
24580			},
24581
24582			onPostProcess: function(args) {
24583				if (editor) {
24584					editor.fire('PostProcess', args);
24585				}
24586			},
24587
24588			getTrimmedContent: getTrimmedContent
24589		};
24590	};
24591});
24592
24593// Included from: js/tinymce/classes/dom/TridentSelection.js
24594
24595/**
24596 * TridentSelection.js
24597 *
24598 * Released under LGPL License.
24599 * Copyright (c) 1999-2015 Ephox Corp. All rights reserved
24600 *
24601 * License: http://www.tinymce.com/license
24602 * Contributing: http://www.tinymce.com/contributing
24603 */
24604
24605/**
24606 * Selection class for old explorer versions. This one fakes the
24607 * native selection object available on modern browsers.
24608 *
24609 * @private
24610 * @class tinymce.dom.TridentSelection
24611 */
24612define("tinymce/dom/TridentSelection", [], function() {
24613	function Selection(selection) {
24614		var self = this, dom = selection.dom, FALSE = false;
24615
24616		function getPosition(rng, start) {
24617			var checkRng, startIndex = 0, endIndex, inside,
24618				children, child, offset, index, position = -1, parent;
24619
24620			// Setup test range, collapse it and get the parent
24621			checkRng = rng.duplicate();
24622			checkRng.collapse(start);
24623			parent = checkRng.parentElement();
24624
24625			// Check if the selection is within the right document
24626			if (parent.ownerDocument !== selection.dom.doc) {
24627				return;
24628			}
24629
24630			// IE will report non editable elements as it's parent so look for an editable one
24631			while (parent.contentEditable === "false") {
24632				parent = parent.parentNode;
24633			}
24634
24635			// If parent doesn't have any children then return that we are inside the element
24636			if (!parent.hasChildNodes()) {
24637				return {node: parent, inside: 1};
24638			}
24639
24640			// Setup node list and endIndex
24641			children = parent.children;
24642			endIndex = children.length - 1;
24643
24644			// Perform a binary search for the position
24645			while (startIndex <= endIndex) {
24646				index = Math.floor((startIndex + endIndex) / 2);
24647
24648				// Move selection to node and compare the ranges
24649				child = children[index];
24650				checkRng.moveToElementText(child);
24651				position = checkRng.compareEndPoints(start ? 'StartToStart' : 'EndToEnd', rng);
24652
24653				// Before/after or an exact match
24654				if (position > 0) {
24655					endIndex = index - 1;
24656				} else if (position < 0) {
24657					startIndex = index + 1;
24658				} else {
24659					return {node: child};
24660				}
24661			}
24662
24663			// Check if child position is before or we didn't find a position
24664			if (position < 0) {
24665				// No element child was found use the parent element and the offset inside that
24666				if (!child) {
24667					checkRng.moveToElementText(parent);
24668					checkRng.collapse(true);
24669					child = parent;
24670					inside = true;
24671				} else {
24672					checkRng.collapse(false);
24673				}
24674
24675				// Walk character by character in text node until we hit the selected range endpoint,
24676				// hit the end of document or parent isn't the right one
24677				// We need to walk char by char since rng.text or rng.htmlText will trim line endings
24678				offset = 0;
24679				while (checkRng.compareEndPoints(start ? 'StartToStart' : 'StartToEnd', rng) !== 0) {
24680					if (checkRng.move('character', 1) === 0 || parent != checkRng.parentElement()) {
24681						break;
24682					}
24683
24684					offset++;
24685				}
24686			} else {
24687				// Child position is after the selection endpoint
24688				checkRng.collapse(true);
24689
24690				// Walk character by character in text node until we hit the selected range endpoint, hit
24691				// the end of document or parent isn't the right one
24692				offset = 0;
24693				while (checkRng.compareEndPoints(start ? 'StartToStart' : 'StartToEnd', rng) !== 0) {
24694					if (checkRng.move('character', -1) === 0 || parent != checkRng.parentElement()) {
24695						break;
24696					}
24697
24698					offset++;
24699				}
24700			}
24701
24702			return {node: child, position: position, offset: offset, inside: inside};
24703		}
24704
24705		// Returns a W3C DOM compatible range object by using the IE Range API
24706		function getRange() {
24707			var ieRange = selection.getRng(), domRange = dom.createRng(), element, collapsed, tmpRange, element2, bookmark;
24708
24709			// If selection is outside the current document just return an empty range
24710			element = ieRange.item ? ieRange.item(0) : ieRange.parentElement();
24711			if (element.ownerDocument != dom.doc) {
24712				return domRange;
24713			}
24714
24715			collapsed = selection.isCollapsed();
24716
24717			// Handle control selection
24718			if (ieRange.item) {
24719				domRange.setStart(element.parentNode, dom.nodeIndex(element));
24720				domRange.setEnd(domRange.startContainer, domRange.startOffset + 1);
24721
24722				return domRange;
24723			}
24724
24725			function findEndPoint(start) {
24726				var endPoint = getPosition(ieRange, start), container, offset, textNodeOffset = 0, sibling, undef, nodeValue;
24727
24728				container = endPoint.node;
24729				offset = endPoint.offset;
24730
24731				if (endPoint.inside && !container.hasChildNodes()) {
24732					domRange[start ? 'setStart' : 'setEnd'](container, 0);
24733					return;
24734				}
24735
24736				if (offset === undef) {
24737					domRange[start ? 'setStartBefore' : 'setEndAfter'](container);
24738					return;
24739				}
24740
24741				if (endPoint.position < 0) {
24742					sibling = endPoint.inside ? container.firstChild : container.nextSibling;
24743
24744					if (!sibling) {
24745						domRange[start ? 'setStartAfter' : 'setEndAfter'](container);
24746						return;
24747					}
24748
24749					if (!offset) {
24750						if (sibling.nodeType == 3) {
24751							domRange[start ? 'setStart' : 'setEnd'](sibling, 0);
24752						} else {
24753							domRange[start ? 'setStartBefore' : 'setEndBefore'](sibling);
24754						}
24755
24756						return;
24757					}
24758
24759					// Find the text node and offset
24760					while (sibling) {
24761						if (sibling.nodeType == 3) {
24762							nodeValue = sibling.nodeValue;
24763							textNodeOffset += nodeValue.length;
24764
24765							// We are at or passed the position we where looking for
24766							if (textNodeOffset >= offset) {
24767								container = sibling;
24768								textNodeOffset -= offset;
24769								textNodeOffset = nodeValue.length - textNodeOffset;
24770								break;
24771							}
24772						}
24773
24774						sibling = sibling.nextSibling;
24775					}
24776				} else {
24777					// Find the text node and offset
24778					sibling = container.previousSibling;
24779
24780					if (!sibling) {
24781						return domRange[start ? 'setStartBefore' : 'setEndBefore'](container);
24782					}
24783
24784					// If there isn't any text to loop then use the first position
24785					if (!offset) {
24786						if (container.nodeType == 3) {
24787							domRange[start ? 'setStart' : 'setEnd'](sibling, container.nodeValue.length);
24788						} else {
24789							domRange[start ? 'setStartAfter' : 'setEndAfter'](sibling);
24790						}
24791
24792						return;
24793					}
24794
24795					while (sibling) {
24796						if (sibling.nodeType == 3) {
24797							textNodeOffset += sibling.nodeValue.length;
24798
24799							// We are at or passed the position we where looking for
24800							if (textNodeOffset >= offset) {
24801								container = sibling;
24802								textNodeOffset -= offset;
24803								break;
24804							}
24805						}
24806
24807						sibling = sibling.previousSibling;
24808					}
24809				}
24810
24811				domRange[start ? 'setStart' : 'setEnd'](container, textNodeOffset);
24812			}
24813
24814			try {
24815				// Find start point
24816				findEndPoint(true);
24817
24818				// Find end point if needed
24819				if (!collapsed) {
24820					findEndPoint();
24821				}
24822			} catch (ex) {
24823				// IE has a nasty bug where text nodes might throw "invalid argument" when you
24824				// access the nodeValue or other properties of text nodes. This seems to happen when
24825				// text nodes are split into two nodes by a delete/backspace call.
24826				// So let us detect and try to fix it.
24827				if (ex.number == -2147024809) {
24828					// Get the current selection
24829					bookmark = self.getBookmark(2);
24830
24831					// Get start element
24832					tmpRange = ieRange.duplicate();
24833					tmpRange.collapse(true);
24834					element = tmpRange.parentElement();
24835
24836					// Get end element
24837					if (!collapsed) {
24838						tmpRange = ieRange.duplicate();
24839						tmpRange.collapse(false);
24840						element2 = tmpRange.parentElement();
24841						element2.innerHTML = element2.innerHTML;
24842					}
24843
24844					// Remove the broken elements
24845					element.innerHTML = element.innerHTML;
24846
24847					// Restore the selection
24848					self.moveToBookmark(bookmark);
24849
24850					// Since the range has moved we need to re-get it
24851					ieRange = selection.getRng();
24852
24853					// Find start point
24854					findEndPoint(true);
24855
24856					// Find end point if needed
24857					if (!collapsed) {
24858						findEndPoint();
24859					}
24860				} else {
24861					throw ex; // Throw other errors
24862				}
24863			}
24864
24865			return domRange;
24866		}
24867
24868		this.getBookmark = function(type) {
24869			var rng = selection.getRng(), bookmark = {};
24870
24871			function getIndexes(node) {
24872				var parent, root, children, i, indexes = [];
24873
24874				parent = node.parentNode;
24875				root = dom.getRoot().parentNode;
24876
24877				while (parent != root && parent.nodeType !== 9) {
24878					children = parent.children;
24879
24880					i = children.length;
24881					while (i--) {
24882						if (node === children[i]) {
24883							indexes.push(i);
24884							break;
24885						}
24886					}
24887
24888					node = parent;
24889					parent = parent.parentNode;
24890				}
24891
24892				return indexes;
24893			}
24894
24895			function getBookmarkEndPoint(start) {
24896				var position;
24897
24898				position = getPosition(rng, start);
24899				if (position) {
24900					return {
24901						position: position.position,
24902						offset: position.offset,
24903						indexes: getIndexes(position.node),
24904						inside: position.inside
24905					};
24906				}
24907			}
24908
24909			// Non ubstructive bookmark
24910			if (type === 2) {
24911				// Handle text selection
24912				if (!rng.item) {
24913					bookmark.start = getBookmarkEndPoint(true);
24914
24915					if (!selection.isCollapsed()) {
24916						bookmark.end = getBookmarkEndPoint();
24917					}
24918				} else {
24919					bookmark.start = {ctrl: true, indexes: getIndexes(rng.item(0))};
24920				}
24921			}
24922
24923			return bookmark;
24924		};
24925
24926		this.moveToBookmark = function(bookmark) {
24927			var rng, body = dom.doc.body;
24928
24929			function resolveIndexes(indexes) {
24930				var node, i, idx, children;
24931
24932				node = dom.getRoot();
24933				for (i = indexes.length - 1; i >= 0; i--) {
24934					children = node.children;
24935					idx = indexes[i];
24936
24937					if (idx <= children.length - 1) {
24938						node = children[idx];
24939					}
24940				}
24941
24942				return node;
24943			}
24944
24945			function setBookmarkEndPoint(start) {
24946				var endPoint = bookmark[start ? 'start' : 'end'], moveLeft, moveRng, undef, offset;
24947
24948				if (endPoint) {
24949					moveLeft = endPoint.position > 0;
24950
24951					moveRng = body.createTextRange();
24952					moveRng.moveToElementText(resolveIndexes(endPoint.indexes));
24953
24954					offset = endPoint.offset;
24955					if (offset !== undef) {
24956						moveRng.collapse(endPoint.inside || moveLeft);
24957						moveRng.moveStart('character', moveLeft ? -offset : offset);
24958					} else {
24959						moveRng.collapse(start);
24960					}
24961
24962					rng.setEndPoint(start ? 'StartToStart' : 'EndToStart', moveRng);
24963
24964					if (start) {
24965						rng.collapse(true);
24966					}
24967				}
24968			}
24969
24970			if (bookmark.start) {
24971				if (bookmark.start.ctrl) {
24972					rng = body.createControlRange();
24973					rng.addElement(resolveIndexes(bookmark.start.indexes));
24974					rng.select();
24975				} else {
24976					rng = body.createTextRange();
24977					setBookmarkEndPoint(true);
24978					setBookmarkEndPoint();
24979					rng.select();
24980				}
24981			}
24982		};
24983
24984		this.addRange = function(rng) {
24985			var ieRng, ctrlRng, startContainer, startOffset, endContainer, endOffset, sibling,
24986				doc = selection.dom.doc, body = doc.body, nativeRng, ctrlElm;
24987
24988			function setEndPoint(start) {
24989				var container, offset, marker, tmpRng, nodes;
24990
24991				marker = dom.create('a');
24992				container = start ? startContainer : endContainer;
24993				offset = start ? startOffset : endOffset;
24994				tmpRng = ieRng.duplicate();
24995
24996				if (container == doc || container == doc.documentElement) {
24997					container = body;
24998					offset = 0;
24999				}
25000
25001				if (container.nodeType == 3) {
25002					container.parentNode.insertBefore(marker, container);
25003					tmpRng.moveToElementText(marker);
25004					tmpRng.moveStart('character', offset);
25005					dom.remove(marker);
25006					ieRng.setEndPoint(start ? 'StartToStart' : 'EndToEnd', tmpRng);
25007				} else {
25008					nodes = container.childNodes;
25009
25010					if (nodes.length) {
25011						if (offset >= nodes.length) {
25012							dom.insertAfter(marker, nodes[nodes.length - 1]);
25013						} else {
25014							container.insertBefore(marker, nodes[offset]);
25015						}
25016
25017						tmpRng.moveToElementText(marker);
25018					} else if (container.canHaveHTML) {
25019						// Empty node selection for example <div>|</div>
25020						// Setting innerHTML with a span marker then remove that marker seems to keep empty block elements open
25021						container.innerHTML = '<span>&#xFEFF;</span>';
25022						marker = container.firstChild;
25023						tmpRng.moveToElementText(marker);
25024						tmpRng.collapse(FALSE); // Collapse false works better than true for some odd reason
25025					}
25026
25027					ieRng.setEndPoint(start ? 'StartToStart' : 'EndToEnd', tmpRng);
25028					dom.remove(marker);
25029				}
25030			}
25031
25032			// Setup some shorter versions
25033			startContainer = rng.startContainer;
25034			startOffset = rng.startOffset;
25035			endContainer = rng.endContainer;
25036			endOffset = rng.endOffset;
25037			ieRng = body.createTextRange();
25038
25039			// If single element selection then try making a control selection out of it
25040			if (startContainer == endContainer && startContainer.nodeType == 1) {
25041				// Trick to place the caret inside an empty block element like <p></p>
25042				if (startOffset == endOffset && !startContainer.hasChildNodes()) {
25043					if (startContainer.canHaveHTML) {
25044						// Check if previous sibling is an empty block if it is then we need to render it
25045						// IE would otherwise move the caret into the sibling instead of the empty startContainer see: #5236
25046						// Example this: <p></p><p>|</p> would become this: <p>|</p><p></p>
25047						sibling = startContainer.previousSibling;
25048						if (sibling && !sibling.hasChildNodes() && dom.isBlock(sibling)) {
25049							sibling.innerHTML = '&#xFEFF;';
25050						} else {
25051							sibling = null;
25052						}
25053
25054						startContainer.innerHTML = '<span>&#xFEFF;</span><span>&#xFEFF;</span>';
25055						ieRng.moveToElementText(startContainer.lastChild);
25056						ieRng.select();
25057						dom.doc.selection.clear();
25058						startContainer.innerHTML = '';
25059
25060						if (sibling) {
25061							sibling.innerHTML = '';
25062						}
25063						return;
25064					}
25065
25066					startOffset = dom.nodeIndex(startContainer);
25067					startContainer = startContainer.parentNode;
25068				}
25069
25070				if (startOffset == endOffset - 1) {
25071					try {
25072						ctrlElm = startContainer.childNodes[startOffset];
25073						ctrlRng = body.createControlRange();
25074						ctrlRng.addElement(ctrlElm);
25075						ctrlRng.select();
25076
25077						// Check if the range produced is on the correct element and is a control range
25078						// On IE 8 it will select the parent contentEditable container if you select an inner element see: #5398
25079						nativeRng = selection.getRng();
25080						if (nativeRng.item && ctrlElm === nativeRng.item(0)) {
25081							return;
25082						}
25083					} catch (ex) {
25084						// Ignore
25085					}
25086				}
25087			}
25088
25089			// Set start/end point of selection
25090			setEndPoint(true);
25091			setEndPoint();
25092
25093			// Select the new range and scroll it into view
25094			ieRng.select();
25095		};
25096
25097		// Expose range method
25098		this.getRangeAt = getRange;
25099	}
25100
25101	return Selection;
25102});
25103
25104// Included from: js/tinymce/classes/util/VK.js
25105
25106/**
25107 * VK.js
25108 *
25109 * Released under LGPL License.
25110 * Copyright (c) 1999-2015 Ephox Corp. All rights reserved
25111 *
25112 * License: http://www.tinymce.com/license
25113 * Contributing: http://www.tinymce.com/contributing
25114 */
25115
25116/**
25117 * This file exposes a set of the common KeyCodes for use. Please grow it as needed.
25118 */
25119define("tinymce/util/VK", [
25120	"tinymce/Env"
25121], function(Env) {
25122	return {
25123		BACKSPACE: 8,
25124		DELETE: 46,
25125		DOWN: 40,
25126		ENTER: 13,
25127		LEFT: 37,
25128		RIGHT: 39,
25129		SPACEBAR: 32,
25130		TAB: 9,
25131		UP: 38,
25132
25133		modifierPressed: function(e) {
25134			return e.shiftKey || e.ctrlKey || e.altKey || this.metaKeyPressed(e);
25135		},
25136
25137		metaKeyPressed: function(e) {
25138			// Check if ctrl or meta key is pressed. Edge case for AltGr on Windows where it produces ctrlKey+altKey states
25139			return (Env.mac ? e.metaKey : e.ctrlKey && !e.altKey);
25140		}
25141	};
25142});
25143
25144// Included from: js/tinymce/classes/dom/ControlSelection.js
25145
25146/**
25147 * ControlSelection.js
25148 *
25149 * Released under LGPL License.
25150 * Copyright (c) 1999-2015 Ephox Corp. All rights reserved
25151 *
25152 * License: http://www.tinymce.com/license
25153 * Contributing: http://www.tinymce.com/contributing
25154 */
25155
25156/**
25157 * This class handles control selection of elements. Controls are elements
25158 * that can be resized and needs to be selected as a whole. It adds custom resize handles
25159 * to all browser engines that support properly disabling the built in resize logic.
25160 *
25161 * @class tinymce.dom.ControlSelection
25162 */
25163define("tinymce/dom/ControlSelection", [
25164	"tinymce/util/VK",
25165	"tinymce/util/Tools",
25166	"tinymce/util/Delay",
25167	"tinymce/Env",
25168	"tinymce/dom/NodeType"
25169], function(VK, Tools, Delay, Env, NodeType) {
25170	var isContentEditableFalse = NodeType.isContentEditableFalse;
25171
25172	return function(selection, editor) {
25173		var dom = editor.dom, each = Tools.each;
25174		var selectedElm, selectedElmGhost, resizeHelper, resizeHandles, selectedHandle, lastMouseDownEvent;
25175		var startX, startY, selectedElmX, selectedElmY, startW, startH, ratio, resizeStarted;
25176		var width, height, editableDoc = editor.getDoc(), rootDocument = document, isIE = Env.ie && Env.ie < 11;
25177		var abs = Math.abs, round = Math.round, rootElement = editor.getBody(), startScrollWidth, startScrollHeight;
25178
25179		// Details about each resize handle how to scale etc
25180		resizeHandles = {
25181			// Name: x multiplier, y multiplier, delta size x, delta size y
25182			/*n: [0.5, 0, 0, -1],
25183			e: [1, 0.5, 1, 0],
25184			s: [0.5, 1, 0, 1],
25185			w: [0, 0.5, -1, 0],*/
25186			nw: [0, 0, -1, -1],
25187			ne: [1, 0, 1, -1],
25188			se: [1, 1, 1, 1],
25189			sw: [0, 1, -1, 1]
25190		};
25191
25192		// Add CSS for resize handles, cloned element and selected
25193		var rootClass = '.mce-content-body';
25194		editor.contentStyles.push(
25195			rootClass + ' div.mce-resizehandle {' +
25196				'position: absolute;' +
25197				'border: 1px solid black;' +
25198				'background: #FFF;' +
25199				'width: 7px;' +
25200				'height: 7px;' +
25201				'z-index: 10000' +
25202			'}' +
25203			rootClass + ' .mce-resizehandle:hover {' +
25204				'background: #000' +
25205			'}' +
25206			rootClass + ' *[data-mce-selected] {' +
25207				'outline: 1px solid black;' +
25208				'resize: none' + // Have been talks about implementing this in browsers
25209			'}' +
25210			rootClass + ' .mce-clonedresizable {' +
25211				'position: absolute;' +
25212				(Env.gecko ? '' : 'outline: 1px dashed black;') + // Gecko produces trails while resizing
25213				'opacity: .5;' +
25214				'filter: alpha(opacity=50);' +
25215				'z-index: 10000' +
25216			'}' +
25217			rootClass + ' .mce-resize-helper {' +
25218				'background: #555;' +
25219				'background: rgba(0,0,0,0.75);' +
25220				'border-radius: 3px;' +
25221				'border: 1px;' +
25222				'color: white;' +
25223				'display: none;' +
25224				'font-family: sans-serif;' +
25225				'font-size: 12px;' +
25226				'white-space: nowrap;' +
25227				'line-height: 14px;' +
25228				'margin: 5px 10px;' +
25229				'padding: 5px;' +
25230				'position: absolute;' +
25231				'z-index: 10001' +
25232			'}'
25233		);
25234
25235		function isResizable(elm) {
25236			var selector = editor.settings.object_resizing;
25237
25238			if (selector === false || Env.iOS) {
25239				return false;
25240			}
25241
25242			if (typeof selector != 'string') {
25243				selector = 'table,img,div';
25244			}
25245
25246			if (elm.getAttribute('data-mce-resize') === 'false') {
25247				return false;
25248			}
25249
25250			if (elm == editor.getBody()) {
25251				return false;
25252			}
25253
25254			return editor.dom.is(elm, selector);
25255		}
25256
25257		function resizeGhostElement(e) {
25258			var deltaX, deltaY, proportional;
25259			var resizeHelperX, resizeHelperY;
25260
25261			// Calc new width/height
25262			deltaX = e.screenX - startX;
25263			deltaY = e.screenY - startY;
25264
25265			// Calc new size
25266			width = deltaX * selectedHandle[2] + startW;
25267			height = deltaY * selectedHandle[3] + startH;
25268
25269			// Never scale down lower than 5 pixels
25270			width = width < 5 ? 5 : width;
25271			height = height < 5 ? 5 : height;
25272
25273			if (selectedElm.nodeName == "IMG" && editor.settings.resize_img_proportional !== false) {
25274				proportional = !VK.modifierPressed(e);
25275			} else {
25276				proportional = VK.modifierPressed(e) || (selectedElm.nodeName == "IMG" && selectedHandle[2] * selectedHandle[3] !== 0);
25277			}
25278
25279			// Constrain proportions
25280			if (proportional) {
25281				if (abs(deltaX) > abs(deltaY)) {
25282					height = round(width * ratio);
25283					width = round(height / ratio);
25284				} else {
25285					width = round(height / ratio);
25286					height = round(width * ratio);
25287				}
25288			}
25289
25290			// Update ghost size
25291			dom.setStyles(selectedElmGhost, {
25292				width: width,
25293				height: height
25294			});
25295
25296			// Update resize helper position
25297			resizeHelperX = selectedHandle.startPos.x + deltaX;
25298			resizeHelperY = selectedHandle.startPos.y + deltaY;
25299			resizeHelperX = resizeHelperX > 0 ? resizeHelperX : 0;
25300			resizeHelperY = resizeHelperY > 0 ? resizeHelperY : 0;
25301
25302			dom.setStyles(resizeHelper, {
25303				left: resizeHelperX,
25304				top: resizeHelperY,
25305				display: 'block'
25306			});
25307
25308			resizeHelper.innerHTML = width + ' &times; ' + height;
25309
25310			// Update ghost X position if needed
25311			if (selectedHandle[2] < 0 && selectedElmGhost.clientWidth <= width) {
25312				dom.setStyle(selectedElmGhost, 'left', selectedElmX + (startW - width));
25313			}
25314
25315			// Update ghost Y position if needed
25316			if (selectedHandle[3] < 0 && selectedElmGhost.clientHeight <= height) {
25317				dom.setStyle(selectedElmGhost, 'top', selectedElmY + (startH - height));
25318			}
25319
25320			// Calculate how must overflow we got
25321			deltaX = rootElement.scrollWidth - startScrollWidth;
25322			deltaY = rootElement.scrollHeight - startScrollHeight;
25323
25324			// Re-position the resize helper based on the overflow
25325			if (deltaX + deltaY !== 0) {
25326				dom.setStyles(resizeHelper, {
25327					left: resizeHelperX - deltaX,
25328					top: resizeHelperY - deltaY
25329				});
25330			}
25331
25332			if (!resizeStarted) {
25333				editor.fire('ObjectResizeStart', {target: selectedElm, width: startW, height: startH});
25334				resizeStarted = true;
25335			}
25336		}
25337
25338		function endGhostResize() {
25339			resizeStarted = false;
25340
25341			function setSizeProp(name, value) {
25342				if (value) {
25343					// Resize by using style or attribute
25344					if (selectedElm.style[name] || !editor.schema.isValid(selectedElm.nodeName.toLowerCase(), name)) {
25345						dom.setStyle(selectedElm, name, value);
25346					} else {
25347						dom.setAttrib(selectedElm, name, value);
25348					}
25349				}
25350			}
25351
25352			// Set width/height properties
25353			setSizeProp('width', width);
25354			setSizeProp('height', height);
25355
25356			dom.unbind(editableDoc, 'mousemove', resizeGhostElement);
25357			dom.unbind(editableDoc, 'mouseup', endGhostResize);
25358
25359			if (rootDocument != editableDoc) {
25360				dom.unbind(rootDocument, 'mousemove', resizeGhostElement);
25361				dom.unbind(rootDocument, 'mouseup', endGhostResize);
25362			}
25363
25364			// Remove ghost/helper and update resize handle positions
25365			dom.remove(selectedElmGhost);
25366			dom.remove(resizeHelper);
25367
25368			if (!isIE || selectedElm.nodeName == "TABLE") {
25369				showResizeRect(selectedElm);
25370			}
25371
25372			editor.fire('ObjectResized', {target: selectedElm, width: width, height: height});
25373			dom.setAttrib(selectedElm, 'style', dom.getAttrib(selectedElm, 'style'));
25374			editor.nodeChanged();
25375		}
25376
25377		function showResizeRect(targetElm, mouseDownHandleName, mouseDownEvent) {
25378			var position, targetWidth, targetHeight, e, rect;
25379
25380			hideResizeRect();
25381			unbindResizeHandleEvents();
25382
25383			// Get position and size of target
25384			position = dom.getPos(targetElm, rootElement);
25385			selectedElmX = position.x;
25386			selectedElmY = position.y;
25387			rect = targetElm.getBoundingClientRect(); // Fix for Gecko offsetHeight for table with caption
25388			targetWidth = rect.width || (rect.right - rect.left);
25389			targetHeight = rect.height || (rect.bottom - rect.top);
25390
25391			// Reset width/height if user selects a new image/table
25392			if (selectedElm != targetElm) {
25393				detachResizeStartListener();
25394				selectedElm = targetElm;
25395				width = height = 0;
25396			}
25397
25398			// Makes it possible to disable resizing
25399			e = editor.fire('ObjectSelected', {target: targetElm});
25400
25401			if (isResizable(targetElm) && !e.isDefaultPrevented()) {
25402				each(resizeHandles, function(handle, name) {
25403					var handleElm;
25404
25405					function startDrag(e) {
25406						startX = e.screenX;
25407						startY = e.screenY;
25408						startW = selectedElm.clientWidth;
25409						startH = selectedElm.clientHeight;
25410						ratio = startH / startW;
25411						selectedHandle = handle;
25412
25413						handle.startPos = {
25414							x: targetWidth * handle[0] + selectedElmX,
25415							y: targetHeight * handle[1] + selectedElmY
25416						};
25417
25418						startScrollWidth = rootElement.scrollWidth;
25419						startScrollHeight = rootElement.scrollHeight;
25420
25421						selectedElmGhost = selectedElm.cloneNode(true);
25422						dom.addClass(selectedElmGhost, 'mce-clonedresizable');
25423						dom.setAttrib(selectedElmGhost, 'data-mce-bogus', 'all');
25424						selectedElmGhost.contentEditable = false; // Hides IE move layer cursor
25425						selectedElmGhost.unSelectabe = true;
25426						dom.setStyles(selectedElmGhost, {
25427							left: selectedElmX,
25428							top: selectedElmY,
25429							margin: 0
25430						});
25431
25432						selectedElmGhost.removeAttribute('data-mce-selected');
25433						rootElement.appendChild(selectedElmGhost);
25434
25435						dom.bind(editableDoc, 'mousemove', resizeGhostElement);
25436						dom.bind(editableDoc, 'mouseup', endGhostResize);
25437
25438						if (rootDocument != editableDoc) {
25439							dom.bind(rootDocument, 'mousemove', resizeGhostElement);
25440							dom.bind(rootDocument, 'mouseup', endGhostResize);
25441						}
25442
25443						resizeHelper = dom.add(rootElement, 'div', {
25444							'class': 'mce-resize-helper',
25445							'data-mce-bogus': 'all'
25446						}, startW + ' &times; ' + startH);
25447					}
25448
25449					if (mouseDownHandleName) {
25450						// Drag started by IE native resizestart
25451						if (name == mouseDownHandleName) {
25452							startDrag(mouseDownEvent);
25453						}
25454
25455						return;
25456					}
25457
25458					// Get existing or render resize handle
25459					handleElm = dom.get('mceResizeHandle' + name);
25460					if (handleElm) {
25461						dom.remove(handleElm);
25462					}
25463
25464					handleElm = dom.add(rootElement, 'div', {
25465						id: 'mceResizeHandle' + name,
25466						'data-mce-bogus': 'all',
25467						'class': 'mce-resizehandle',
25468						unselectable: true,
25469						style: 'cursor:' + name + '-resize; margin:0; padding:0'
25470					});
25471
25472					// Hides IE move layer cursor
25473					// If we set it on Chrome we get this wounderful bug: #6725
25474					if (Env.ie) {
25475						handleElm.contentEditable = false;
25476					}
25477
25478					dom.bind(handleElm, 'mousedown', function(e) {
25479						e.stopImmediatePropagation();
25480						e.preventDefault();
25481						startDrag(e);
25482					});
25483
25484					handle.elm = handleElm;
25485
25486					// Position element
25487					dom.setStyles(handleElm, {
25488						left: (targetWidth * handle[0] + selectedElmX) - (handleElm.offsetWidth / 2),
25489						top: (targetHeight * handle[1] + selectedElmY) - (handleElm.offsetHeight / 2)
25490					});
25491				});
25492			} else {
25493				hideResizeRect();
25494			}
25495
25496			selectedElm.setAttribute('data-mce-selected', '1');
25497		}
25498
25499		function hideResizeRect() {
25500			var name, handleElm;
25501
25502			unbindResizeHandleEvents();
25503
25504			if (selectedElm) {
25505				selectedElm.removeAttribute('data-mce-selected');
25506			}
25507
25508			for (name in resizeHandles) {
25509				handleElm = dom.get('mceResizeHandle' + name);
25510				if (handleElm) {
25511					dom.unbind(handleElm);
25512					dom.remove(handleElm);
25513				}
25514			}
25515		}
25516
25517		function updateResizeRect(e) {
25518			var startElm, controlElm;
25519
25520			function isChildOrEqual(node, parent) {
25521				if (node) {
25522					do {
25523						if (node === parent) {
25524							return true;
25525						}
25526					} while ((node = node.parentNode));
25527				}
25528			}
25529
25530			// Ignore all events while resizing or if the editor instance was removed
25531			if (resizeStarted || editor.removed) {
25532				return;
25533			}
25534
25535			// Remove data-mce-selected from all elements since they might have been copied using Ctrl+c/v
25536			each(dom.select('img[data-mce-selected],hr[data-mce-selected]'), function(img) {
25537				img.removeAttribute('data-mce-selected');
25538			});
25539
25540			controlElm = e.type == 'mousedown' ? e.target : selection.getNode();
25541			controlElm = dom.$(controlElm).closest(isIE ? 'table' : 'table,img,hr')[0];
25542
25543			if (isChildOrEqual(controlElm, rootElement)) {
25544				disableGeckoResize();
25545				startElm = selection.getStart(true);
25546
25547				if (isChildOrEqual(startElm, controlElm) && isChildOrEqual(selection.getEnd(true), controlElm)) {
25548					if (!isIE || (controlElm != startElm && startElm.nodeName !== 'IMG')) {
25549						showResizeRect(controlElm);
25550						return;
25551					}
25552				}
25553			}
25554
25555			hideResizeRect();
25556		}
25557
25558		function attachEvent(elm, name, func) {
25559			if (elm && elm.attachEvent) {
25560				elm.attachEvent('on' + name, func);
25561			}
25562		}
25563
25564		function detachEvent(elm, name, func) {
25565			if (elm && elm.detachEvent) {
25566				elm.detachEvent('on' + name, func);
25567			}
25568		}
25569
25570		function resizeNativeStart(e) {
25571			var target = e.srcElement, pos, name, corner, cornerX, cornerY, relativeX, relativeY;
25572
25573			pos = target.getBoundingClientRect();
25574			relativeX = lastMouseDownEvent.clientX - pos.left;
25575			relativeY = lastMouseDownEvent.clientY - pos.top;
25576
25577			// Figure out what corner we are draging on
25578			for (name in resizeHandles) {
25579				corner = resizeHandles[name];
25580
25581				cornerX = target.offsetWidth * corner[0];
25582				cornerY = target.offsetHeight * corner[1];
25583
25584				if (abs(cornerX - relativeX) < 8 && abs(cornerY - relativeY) < 8) {
25585					selectedHandle = corner;
25586					break;
25587				}
25588			}
25589
25590			// Remove native selection and let the magic begin
25591			resizeStarted = true;
25592			editor.fire('ObjectResizeStart', {
25593				target: selectedElm,
25594				width: selectedElm.clientWidth,
25595				height: selectedElm.clientHeight
25596			});
25597			editor.getDoc().selection.empty();
25598			showResizeRect(target, name, lastMouseDownEvent);
25599		}
25600
25601		function preventDefault(e) {
25602			if (e.preventDefault) {
25603				e.preventDefault();
25604			} else {
25605				e.returnValue = false; // IE
25606			}
25607		}
25608
25609		function nativeControlSelect(e) {
25610			var target = e.srcElement;
25611
25612			if (isContentEditableFalse(target)) {
25613				preventDefault(e);
25614				return;
25615			}
25616
25617			if (target != selectedElm) {
25618				editor.fire('ObjectSelected', {target: target});
25619				detachResizeStartListener();
25620
25621				if (target.id.indexOf('mceResizeHandle') === 0) {
25622					e.returnValue = false;
25623					return;
25624				}
25625
25626				if (target.nodeName == 'IMG' || target.nodeName == 'TABLE') {
25627					hideResizeRect();
25628					selectedElm = target;
25629					attachEvent(target, 'resizestart', resizeNativeStart);
25630				}
25631			}
25632		}
25633
25634		function detachResizeStartListener() {
25635			detachEvent(selectedElm, 'resizestart', resizeNativeStart);
25636		}
25637
25638		function unbindResizeHandleEvents() {
25639			for (var name in resizeHandles) {
25640				var handle = resizeHandles[name];
25641
25642				if (handle.elm) {
25643					dom.unbind(handle.elm);
25644					delete handle.elm;
25645				}
25646			}
25647		}
25648
25649		function disableGeckoResize() {
25650			try {
25651				// Disable object resizing on Gecko
25652				editor.getDoc().execCommand('enableObjectResizing', false, false);
25653			} catch (ex) {
25654				// Ignore
25655			}
25656		}
25657
25658		function controlSelect(elm) {
25659			var ctrlRng;
25660
25661			if (!isIE) {
25662				return;
25663			}
25664
25665			ctrlRng = editableDoc.body.createControlRange();
25666
25667			try {
25668				ctrlRng.addElement(elm);
25669				ctrlRng.select();
25670				return true;
25671			} catch (ex) {
25672				// Ignore since the element can't be control selected for example a P tag
25673			}
25674		}
25675
25676		editor.on('init', function() {
25677			if (isIE) {
25678				// Hide the resize rect on resize and reselect the image
25679				editor.on('ObjectResized', function(e) {
25680					if (e.target.nodeName != 'TABLE') {
25681						hideResizeRect();
25682						controlSelect(e.target);
25683					}
25684				});
25685
25686				attachEvent(rootElement, 'controlselect', nativeControlSelect);
25687
25688				editor.on('mousedown', function(e) {
25689					lastMouseDownEvent = e;
25690				});
25691			} else {
25692				disableGeckoResize();
25693
25694				// Sniff sniff, hard to feature detect this stuff
25695				if (Env.ie >= 11) {
25696					// Needs to be mousedown for drag/drop to work on IE 11
25697					// Needs to be click on Edge to properly select images
25698					editor.on('mousedown click', function(e) {
25699						var nodeName = e.target.nodeName;
25700
25701						if (!resizeStarted && /^(TABLE|IMG|HR)$/.test(nodeName)) {
25702							editor.selection.select(e.target, nodeName == 'TABLE');
25703
25704							// Only fire once since nodeChange is expensive
25705							if (e.type == 'mousedown') {
25706								editor.nodeChanged();
25707							}
25708						}
25709					});
25710
25711					editor.dom.bind(rootElement, 'mscontrolselect', function(e) {
25712						function delayedSelect(node) {
25713							Delay.setEditorTimeout(editor, function() {
25714								editor.selection.select(node);
25715							});
25716						}
25717
25718						if (isContentEditableFalse(e.target)) {
25719							e.preventDefault();
25720							delayedSelect(e.target);
25721							return;
25722						}
25723
25724						if (/^(TABLE|IMG|HR)$/.test(e.target.nodeName)) {
25725							e.preventDefault();
25726
25727							// This moves the selection from being a control selection to a text like selection like in WebKit #6753
25728							// TODO: Fix this the day IE works like other browsers without this nasty native ugly control selections.
25729							if (e.target.tagName == 'IMG') {
25730								delayedSelect(e.target);
25731							}
25732						}
25733					});
25734				}
25735			}
25736
25737			var throttledUpdateResizeRect = Delay.throttle(updateResizeRect);
25738
25739			editor.on('nodechange ResizeEditor ResizeWindow drop', throttledUpdateResizeRect);
25740
25741			// Update resize rect while typing in a table
25742			editor.on('keydown keyup', function(e) {
25743				if (selectedElm && selectedElm.nodeName == "TABLE") {
25744					throttledUpdateResizeRect(e);
25745				}
25746			});
25747
25748			editor.on('hide blur', hideResizeRect);
25749
25750			// Hide rect on focusout since it would float on top of windows otherwise
25751			//editor.on('focusout', hideResizeRect);
25752		});
25753
25754		editor.on('remove', unbindResizeHandleEvents);
25755
25756		function destroy() {
25757			selectedElm = selectedElmGhost = null;
25758
25759			if (isIE) {
25760				detachResizeStartListener();
25761				detachEvent(rootElement, 'controlselect', nativeControlSelect);
25762			}
25763		}
25764
25765		return {
25766			isResizable: isResizable,
25767			showResizeRect: showResizeRect,
25768			hideResizeRect: hideResizeRect,
25769			updateResizeRect: updateResizeRect,
25770			controlSelect: controlSelect,
25771			destroy: destroy
25772		};
25773	};
25774});
25775
25776// Included from: js/tinymce/classes/util/Fun.js
25777
25778/**
25779 * Fun.js
25780 *
25781 * Released under LGPL License.
25782 * Copyright (c) 1999-2015 Ephox Corp. All rights reserved
25783 *
25784 * License: http://www.tinymce.com/license
25785 * Contributing: http://www.tinymce.com/contributing
25786 */
25787
25788/**
25789 * Functional utility class.
25790 *
25791 * @private
25792 * @class tinymce.util.Fun
25793 */
25794define("tinymce/util/Fun", [], function() {
25795	var slice = [].slice;
25796
25797	function constant(value) {
25798		return function() {
25799			return value;
25800		};
25801	}
25802
25803	function negate(predicate) {
25804		return function(x) {
25805			return !predicate(x);
25806		};
25807	}
25808
25809	function compose(f, g) {
25810		return function(x) {
25811			return f(g(x));
25812		};
25813	}
25814
25815	function or() {
25816		var args = slice.call(arguments);
25817
25818		return function(x) {
25819			for (var i = 0; i < args.length; i++) {
25820				if (args[i](x)) {
25821					return true;
25822				}
25823			}
25824
25825			return false;
25826		};
25827	}
25828
25829	function and() {
25830		var args = slice.call(arguments);
25831
25832		return function(x) {
25833			for (var i = 0; i < args.length; i++) {
25834				if (!args[i](x)) {
25835					return false;
25836				}
25837			}
25838
25839			return true;
25840		};
25841	}
25842
25843	function curry(fn) {
25844		var args = slice.call(arguments);
25845
25846		if (args.length - 1 >= fn.length) {
25847			return fn.apply(this, args.slice(1));
25848		}
25849
25850		return function() {
25851			var tempArgs = args.concat([].slice.call(arguments));
25852			return curry.apply(this, tempArgs);
25853		};
25854	}
25855
25856	return {
25857		constant: constant,
25858		negate: negate,
25859		and: and,
25860		or: or,
25861		curry: curry,
25862		compose: compose
25863	};
25864});
25865
25866// Included from: js/tinymce/classes/caret/CaretCandidate.js
25867
25868/**
25869 * CaretCandidate.js
25870 *
25871 * Released under LGPL License.
25872 * Copyright (c) 1999-2015 Ephox Corp. All rights reserved
25873 *
25874 * License: http://www.tinymce.com/license
25875 * Contributing: http://www.tinymce.com/contributing
25876 */
25877
25878/**
25879 * This module contains logic for handling caret candidates. A caret candidate is
25880 * for example text nodes, images, input elements, cE=false elements etc.
25881 *
25882 * @private
25883 * @class tinymce.caret.CaretCandidate
25884 */
25885define("tinymce/caret/CaretCandidate", [
25886	"tinymce/dom/NodeType",
25887	"tinymce/util/Arr",
25888	"tinymce/caret/CaretContainer"
25889], function(NodeType, Arr, CaretContainer) {
25890	var isContentEditableTrue = NodeType.isContentEditableTrue,
25891		isContentEditableFalse = NodeType.isContentEditableFalse,
25892		isBr = NodeType.isBr,
25893		isText = NodeType.isText,
25894		isInvalidTextElement = NodeType.matchNodeNames('script style textarea'),
25895		isAtomicInline = NodeType.matchNodeNames('img input textarea hr iframe video audio object'),
25896		isTable = NodeType.matchNodeNames('table'),
25897		isCaretContainer = CaretContainer.isCaretContainer;
25898
25899	function isCaretCandidate(node) {
25900		if (isCaretContainer(node)) {
25901			return false;
25902		}
25903
25904		if (isText(node)) {
25905			if (isInvalidTextElement(node.parentNode)) {
25906				return false;
25907			}
25908
25909			return true;
25910		}
25911
25912		return isAtomicInline(node) || isBr(node) || isTable(node) || isContentEditableFalse(node);
25913	}
25914
25915	function isInEditable(node, rootNode) {
25916		for (node = node.parentNode; node && node != rootNode; node = node.parentNode) {
25917			if (isContentEditableFalse(node)) {
25918				return false;
25919			}
25920
25921			if (isContentEditableTrue(node)) {
25922				return true;
25923			}
25924		}
25925
25926		return true;
25927	}
25928
25929	function isAtomicContentEditableFalse(node) {
25930		if (!isContentEditableFalse(node)) {
25931			return false;
25932		}
25933
25934		return Arr.reduce(node.getElementsByTagName('*'), function(result, elm) {
25935			return result || isContentEditableTrue(elm);
25936		}, false) !== true;
25937	}
25938
25939	function isAtomic(node) {
25940		return isAtomicInline(node) || isAtomicContentEditableFalse(node);
25941	}
25942
25943	function isEditableCaretCandidate(node, rootNode) {
25944		return isCaretCandidate(node) && isInEditable(node, rootNode);
25945	}
25946
25947	return {
25948		isCaretCandidate: isCaretCandidate,
25949		isInEditable: isInEditable,
25950		isAtomic: isAtomic,
25951		isEditableCaretCandidate: isEditableCaretCandidate
25952	};
25953});
25954
25955// Included from: js/tinymce/classes/geom/ClientRect.js
25956
25957/**
25958 * ClientRect.js
25959 *
25960 * Released under LGPL License.
25961 * Copyright (c) 1999-2015 Ephox Corp. All rights reserved
25962 *
25963 * License: http://www.tinymce.com/license
25964 * Contributing: http://www.tinymce.com/contributing
25965 */
25966
25967/**
25968 * Utility functions for working with client rects.
25969 *
25970 * @private
25971 * @class tinymce.geom.ClientRect
25972 */
25973define("tinymce/geom/ClientRect", [], function() {
25974	var round = Math.round;
25975
25976	function clone(rect) {
25977		if (!rect) {
25978			return {left: 0, top: 0, bottom: 0, right: 0, width: 0, height: 0};
25979		}
25980
25981		return {
25982			left: round(rect.left),
25983			top: round(rect.top),
25984			bottom: round(rect.bottom),
25985			right: round(rect.right),
25986			width: round(rect.width),
25987			height: round(rect.height)
25988		};
25989	}
25990
25991	function collapse(clientRect, toStart) {
25992		clientRect = clone(clientRect);
25993
25994		if (toStart) {
25995			clientRect.right = clientRect.left;
25996		} else {
25997			clientRect.left = clientRect.left + clientRect.width;
25998			clientRect.right = clientRect.left;
25999		}
26000
26001		clientRect.width = 0;
26002
26003		return clientRect;
26004	}
26005
26006	function isEqual(rect1, rect2) {
26007		return (
26008			rect1.left === rect2.left &&
26009			rect1.top === rect2.top &&
26010			rect1.bottom === rect2.bottom &&
26011			rect1.right === rect2.right
26012		);
26013	}
26014
26015	function isValidOverflow(overflowY, clientRect1, clientRect2) {
26016		return overflowY >= 0 && overflowY <= Math.min(clientRect1.height, clientRect2.height) / 2;
26017
26018	}
26019
26020	function isAbove(clientRect1, clientRect2) {
26021		if (clientRect1.bottom < clientRect2.top) {
26022			return true;
26023		}
26024
26025		if (clientRect1.top > clientRect2.bottom) {
26026			return false;
26027		}
26028
26029		return isValidOverflow(clientRect2.top - clientRect1.bottom, clientRect1, clientRect2);
26030	}
26031
26032	function isBelow(clientRect1, clientRect2) {
26033		if (clientRect1.top > clientRect2.bottom) {
26034			return true;
26035		}
26036
26037		if (clientRect1.bottom < clientRect2.top) {
26038			return false;
26039		}
26040
26041		return isValidOverflow(clientRect2.bottom - clientRect1.top, clientRect1, clientRect2);
26042	}
26043
26044	function isLeft(clientRect1, clientRect2) {
26045		return clientRect1.left < clientRect2.left;
26046	}
26047
26048	function isRight(clientRect1, clientRect2) {
26049		return clientRect1.right > clientRect2.right;
26050	}
26051
26052	function compare(clientRect1, clientRect2) {
26053		if (isAbove(clientRect1, clientRect2)) {
26054			return -1;
26055		}
26056
26057		if (isBelow(clientRect1, clientRect2)) {
26058			return 1;
26059		}
26060
26061		if (isLeft(clientRect1, clientRect2)) {
26062			return -1;
26063		}
26064
26065		if (isRight(clientRect1, clientRect2)) {
26066			return 1;
26067		}
26068
26069		return 0;
26070	}
26071
26072	return {
26073		clone: clone,
26074		collapse: collapse,
26075		isEqual: isEqual,
26076		isAbove: isAbove,
26077		isBelow: isBelow,
26078		isLeft: isLeft,
26079		isRight: isRight,
26080		compare: compare
26081	};
26082});
26083
26084// Included from: js/tinymce/classes/text/ExtendingChar.js
26085
26086/**
26087 * ExtendingChar.js
26088 *
26089 * Released under LGPL License.
26090 * Copyright (c) 1999-2015 Ephox Corp. All rights reserved
26091 *
26092 * License: http://www.tinymce.com/license
26093 * Contributing: http://www.tinymce.com/contributing
26094 */
26095
26096/**
26097 * This class contains logic for detecting extending characters.
26098 *
26099 * @private
26100 * @class tinymce.text.ExtendingChar
26101 * @example
26102 * var isExtending = ExtendingChar.isExtendingChar('a');
26103 */
26104define("tinymce/text/ExtendingChar", [], function() {
26105	// Generated from: http://www.unicode.org/Public/UNIDATA/DerivedCoreProperties.txt
26106	// Only includes the characters in that fit into UCS-2 16 bit
26107	var extendingChars = new RegExp(
26108		"[\u0300-\u036F\u0483-\u0487\u0488-\u0489\u0591-\u05BD\u05BF\u05C1-\u05C2\u05C4-\u05C5\u05C7\u0610-\u061A" +
26109		"\u064B-\u065F\u0670\u06D6-\u06DC\u06DF-\u06E4\u06E7-\u06E8\u06EA-\u06ED\u0711\u0730-\u074A\u07A6-\u07B0" +
26110		"\u07EB-\u07F3\u0816-\u0819\u081B-\u0823\u0825-\u0827\u0829-\u082D\u0859-\u085B\u08E3-\u0902\u093A\u093C" +
26111		"\u0941-\u0948\u094D\u0951-\u0957\u0962-\u0963\u0981\u09BC\u09BE\u09C1-\u09C4\u09CD\u09D7\u09E2-\u09E3" +
26112		"\u0A01-\u0A02\u0A3C\u0A41-\u0A42\u0A47-\u0A48\u0A4B-\u0A4D\u0A51\u0A70-\u0A71\u0A75\u0A81-\u0A82\u0ABC" +
26113		"\u0AC1-\u0AC5\u0AC7-\u0AC8\u0ACD\u0AE2-\u0AE3\u0B01\u0B3C\u0B3E\u0B3F\u0B41-\u0B44\u0B4D\u0B56\u0B57" +
26114		"\u0B62-\u0B63\u0B82\u0BBE\u0BC0\u0BCD\u0BD7\u0C00\u0C3E-\u0C40\u0C46-\u0C48\u0C4A-\u0C4D\u0C55-\u0C56" +
26115		"\u0C62-\u0C63\u0C81\u0CBC\u0CBF\u0CC2\u0CC6\u0CCC-\u0CCD\u0CD5-\u0CD6\u0CE2-\u0CE3\u0D01\u0D3E\u0D41-\u0D44" +
26116		"\u0D4D\u0D57\u0D62-\u0D63\u0DCA\u0DCF\u0DD2-\u0DD4\u0DD6\u0DDF\u0E31\u0E34-\u0E3A\u0E47-\u0E4E\u0EB1\u0EB4-\u0EB9" +
26117		"\u0EBB-\u0EBC\u0EC8-\u0ECD\u0F18-\u0F19\u0F35\u0F37\u0F39\u0F71-\u0F7E\u0F80-\u0F84\u0F86-\u0F87\u0F8D-\u0F97" +
26118		"\u0F99-\u0FBC\u0FC6\u102D-\u1030\u1032-\u1037\u1039-\u103A\u103D-\u103E\u1058-\u1059\u105E-\u1060\u1071-\u1074" +
26119		"\u1082\u1085-\u1086\u108D\u109D\u135D-\u135F\u1712-\u1714\u1732-\u1734\u1752-\u1753\u1772-\u1773\u17B4-\u17B5" +
26120		"\u17B7-\u17BD\u17C6\u17C9-\u17D3\u17DD\u180B-\u180D\u18A9\u1920-\u1922\u1927-\u1928\u1932\u1939-\u193B\u1A17-\u1A18" +
26121		"\u1A1B\u1A56\u1A58-\u1A5E\u1A60\u1A62\u1A65-\u1A6C\u1A73-\u1A7C\u1A7F\u1AB0-\u1ABD\u1ABE\u1B00-\u1B03\u1B34" +
26122		"\u1B36-\u1B3A\u1B3C\u1B42\u1B6B-\u1B73\u1B80-\u1B81\u1BA2-\u1BA5\u1BA8-\u1BA9\u1BAB-\u1BAD\u1BE6\u1BE8-\u1BE9" +
26123		"\u1BED\u1BEF-\u1BF1\u1C2C-\u1C33\u1C36-\u1C37\u1CD0-\u1CD2\u1CD4-\u1CE0\u1CE2-\u1CE8\u1CED\u1CF4\u1CF8-\u1CF9" +
26124		"\u1DC0-\u1DF5\u1DFC-\u1DFF\u200C-\u200D\u20D0-\u20DC\u20DD-\u20E0\u20E1\u20E2-\u20E4\u20E5-\u20F0\u2CEF-\u2CF1" +
26125		"\u2D7F\u2DE0-\u2DFF\u302A-\u302D\u302E-\u302F\u3099-\u309A\uA66F\uA670-\uA672\uA674-\uA67D\uA69E-\uA69F\uA6F0-\uA6F1" +
26126		"\uA802\uA806\uA80B\uA825-\uA826\uA8C4\uA8E0-\uA8F1\uA926-\uA92D\uA947-\uA951\uA980-\uA982\uA9B3\uA9B6-\uA9B9\uA9BC" +
26127		"\uA9E5\uAA29-\uAA2E\uAA31-\uAA32\uAA35-\uAA36\uAA43\uAA4C\uAA7C\uAAB0\uAAB2-\uAAB4\uAAB7-\uAAB8\uAABE-\uAABF\uAAC1" +
26128		"\uAAEC-\uAAED\uAAF6\uABE5\uABE8\uABED\uFB1E\uFE00-\uFE0F\uFE20-\uFE2F\uFF9E-\uFF9F]"
26129	);
26130
26131	function isExtendingChar(ch) {
26132		return typeof ch == "string" && ch.charCodeAt(0) >= 768 && extendingChars.test(ch);
26133	}
26134
26135	return {
26136		isExtendingChar: isExtendingChar
26137	};
26138});
26139
26140// Included from: js/tinymce/classes/caret/CaretPosition.js
26141
26142/**
26143 * CaretPosition.js
26144 *
26145 * Released under LGPL License.
26146 * Copyright (c) 1999-2015 Ephox Corp. All rights reserved
26147 *
26148 * License: http://www.tinymce.com/license
26149 * Contributing: http://www.tinymce.com/contributing
26150 */
26151
26152/**
26153 * This module contains logic for creating caret positions within a document a caretposition
26154 * is similar to a DOMRange object but it doesn't have two endpoints and is also more lightweight
26155 * since it's now updated live when the DOM changes.
26156 *
26157 * @private
26158 * @class tinymce.caret.CaretPosition
26159 * @example
26160 * var caretPos1 = new CaretPosition(container, offset);
26161 * var caretPos2 = CaretPosition.fromRangeStart(someRange);
26162 */
26163define("tinymce/caret/CaretPosition", [
26164	"tinymce/util/Fun",
26165	"tinymce/dom/NodeType",
26166	"tinymce/dom/DOMUtils",
26167	"tinymce/dom/RangeUtils",
26168	"tinymce/caret/CaretCandidate",
26169	"tinymce/geom/ClientRect",
26170	"tinymce/text/ExtendingChar"
26171], function(Fun, NodeType, DOMUtils, RangeUtils, CaretCandidate, ClientRect, ExtendingChar) {
26172	var isElement = NodeType.isElement,
26173		isCaretCandidate = CaretCandidate.isCaretCandidate,
26174		isBlock = NodeType.matchStyleValues('display', 'block table'),
26175		isFloated = NodeType.matchStyleValues('float', 'left right'),
26176		isValidElementCaretCandidate = Fun.and(isElement, isCaretCandidate, Fun.negate(isFloated)),
26177		isNotPre = Fun.negate(NodeType.matchStyleValues('white-space', 'pre pre-line pre-wrap')),
26178		isText = NodeType.isText,
26179		isBr = NodeType.isBr,
26180		nodeIndex = DOMUtils.nodeIndex,
26181		resolveIndex = RangeUtils.getNode;
26182
26183	function isWhiteSpace(chr) {
26184		return chr && /[\r\n\t ]/.test(chr);
26185	}
26186
26187	function isHiddenWhiteSpaceRange(range) {
26188		var container = range.startContainer,
26189			offset = range.startOffset,
26190			text;
26191
26192		if (isWhiteSpace(range.toString()) && isNotPre(container.parentNode)) {
26193			text = container.data;
26194
26195			if (isWhiteSpace(text[offset - 1]) || isWhiteSpace(text[offset + 1])) {
26196				return true;
26197			}
26198		}
26199
26200		return false;
26201	}
26202
26203	function getCaretPositionClientRects(caretPosition) {
26204		var clientRects = [], beforeNode, node;
26205
26206		// Hack for older WebKit versions that doesn't
26207		// support getBoundingClientRect on BR elements
26208		function getBrClientRect(brNode) {
26209			var doc = brNode.ownerDocument,
26210				rng = doc.createRange(),
26211				nbsp = doc.createTextNode('\u00a0'),
26212				parentNode = brNode.parentNode,
26213				clientRect;
26214
26215			parentNode.insertBefore(nbsp, brNode);
26216			rng.setStart(nbsp, 0);
26217			rng.setEnd(nbsp, 1);
26218			clientRect = ClientRect.clone(rng.getBoundingClientRect());
26219			parentNode.removeChild(nbsp);
26220
26221			return clientRect;
26222		}
26223
26224		function getBoundingClientRect(item) {
26225			var clientRect, clientRects;
26226
26227			clientRects = item.getClientRects();
26228			if (clientRects.length > 0) {
26229				clientRect = ClientRect.clone(clientRects[0]);
26230			} else {
26231				clientRect = ClientRect.clone(item.getBoundingClientRect());
26232			}
26233
26234			if (isBr(item) && clientRect.left === 0) {
26235				return getBrClientRect(item);
26236			}
26237
26238			return clientRect;
26239		}
26240
26241		function collapseAndInflateWidth(clientRect, toStart) {
26242			clientRect = ClientRect.collapse(clientRect, toStart);
26243			clientRect.width = 1;
26244			clientRect.right = clientRect.left + 1;
26245
26246			return clientRect;
26247		}
26248
26249		function addUniqueAndValidRect(clientRect) {
26250			if (clientRect.height === 0) {
26251				return;
26252			}
26253
26254			if (clientRects.length > 0) {
26255				if (ClientRect.isEqual(clientRect, clientRects[clientRects.length - 1])) {
26256					return;
26257				}
26258			}
26259
26260			clientRects.push(clientRect);
26261		}
26262
26263		function addCharacterOffset(container, offset) {
26264			var range = container.ownerDocument.createRange();
26265
26266			if (offset < container.data.length) {
26267				if (ExtendingChar.isExtendingChar(container.data[offset])) {
26268					return clientRects;
26269				}
26270			}
26271
26272			if (offset > 0) {
26273				range.setStart(container, offset - 1);
26274				range.setEnd(container, offset);
26275
26276				if (!isHiddenWhiteSpaceRange(range)) {
26277					addUniqueAndValidRect(collapseAndInflateWidth(getBoundingClientRect(range), false));
26278				}
26279			}
26280
26281			if (offset < container.data.length) {
26282				range.setStart(container, offset);
26283				range.setEnd(container, offset + 1);
26284
26285				if (!isHiddenWhiteSpaceRange(range)) {
26286					addUniqueAndValidRect(collapseAndInflateWidth(getBoundingClientRect(range), true));
26287				}
26288			}
26289		}
26290
26291		if (isText(caretPosition.container())) {
26292			addCharacterOffset(caretPosition.container(), caretPosition.offset());
26293			return clientRects;
26294		}
26295
26296		if (isElement(caretPosition.container())) {
26297			if (caretPosition.isAtEnd()) {
26298				node = resolveIndex(caretPosition.container(), caretPosition.offset());
26299				if (isText(node)) {
26300					addCharacterOffset(node, node.data.length);
26301				}
26302
26303				if (isValidElementCaretCandidate(node) && !isBr(node)) {
26304					addUniqueAndValidRect(collapseAndInflateWidth(getBoundingClientRect(node), false));
26305				}
26306			} else {
26307				node = resolveIndex(caretPosition.container(), caretPosition.offset());
26308				if (isText(node)) {
26309					addCharacterOffset(node, 0);
26310				}
26311
26312				if (isValidElementCaretCandidate(node) && caretPosition.isAtEnd()) {
26313					addUniqueAndValidRect(collapseAndInflateWidth(getBoundingClientRect(node), false));
26314					return clientRects;
26315				}
26316
26317				beforeNode = resolveIndex(caretPosition.container(), caretPosition.offset() - 1);
26318				if (isValidElementCaretCandidate(beforeNode) && !isBr(beforeNode)) {
26319					if (isBlock(beforeNode) || isBlock(node) || !isValidElementCaretCandidate(node)) {
26320						addUniqueAndValidRect(collapseAndInflateWidth(getBoundingClientRect(beforeNode), false));
26321					}
26322				}
26323
26324				if (isValidElementCaretCandidate(node)) {
26325					addUniqueAndValidRect(collapseAndInflateWidth(getBoundingClientRect(node), true));
26326				}
26327			}
26328		}
26329
26330		return clientRects;
26331	}
26332
26333	/**
26334	 * Represents a location within the document by a container and an offset.
26335	 *
26336	 * @constructor
26337	 * @param {Node} container Container node.
26338	 * @param {Number} offset Offset within that container node.
26339	 * @param {Array} clientRects Optional client rects array for the position.
26340	 */
26341	function CaretPosition(container, offset, clientRects) {
26342		function isAtStart() {
26343			if (isText(container)) {
26344				return offset === 0;
26345			}
26346
26347			return offset === 0;
26348		}
26349
26350		function isAtEnd() {
26351			if (isText(container)) {
26352				return offset >= container.data.length;
26353			}
26354
26355			return offset >= container.childNodes.length;
26356		}
26357
26358		function toRange() {
26359			var range;
26360
26361			range = container.ownerDocument.createRange();
26362			range.setStart(container, offset);
26363			range.setEnd(container, offset);
26364
26365			return range;
26366		}
26367
26368		function getClientRects() {
26369			if (!clientRects) {
26370				clientRects = getCaretPositionClientRects(new CaretPosition(container, offset));
26371			}
26372
26373			return clientRects;
26374		}
26375
26376		function isVisible() {
26377			return getClientRects().length > 0;
26378		}
26379
26380		function isEqual(caretPosition) {
26381			return caretPosition && container === caretPosition.container() && offset === caretPosition.offset();
26382		}
26383
26384		function getNode(before) {
26385			return resolveIndex(container, before ? offset - 1 : offset);
26386		}
26387
26388		return {
26389			/**
26390			 * Returns the container node.
26391			 *
26392			 * @method container
26393			 * @return {Node} Container node.
26394			 */
26395			container: Fun.constant(container),
26396
26397			/**
26398			 * Returns the offset within the container node.
26399			 *
26400			 * @method offset
26401			 * @return {Number} Offset within the container node.
26402			 */
26403			offset: Fun.constant(offset),
26404
26405			/**
26406			 * Returns a range out of a the caret position.
26407			 *
26408			 * @method toRange
26409			 * @return {DOMRange} range for the caret position.
26410			 */
26411			toRange: toRange,
26412
26413			/**
26414			 * Returns the client rects for the caret position. Might be multiple rects between
26415			 * block elements.
26416			 *
26417			 * @method getClientRects
26418			 * @return {Array} Array of client rects.
26419			 */
26420			getClientRects: getClientRects,
26421
26422			/**
26423			 * Returns true if the caret location is visible/displayed on screen.
26424			 *
26425			 * @method isVisible
26426			 * @return {Boolean} true/false if the position is visible or not.
26427			 */
26428			isVisible: isVisible,
26429
26430			/**
26431			 * Returns true if the caret location is at the beginning of text node or container.
26432			 *
26433			 * @method isVisible
26434			 * @return {Boolean} true/false if the position is at the beginning.
26435			 */
26436			isAtStart: isAtStart,
26437
26438			/**
26439			 * Returns true if the caret location is at the end of text node or container.
26440			 *
26441			 * @method isVisible
26442			 * @return {Boolean} true/false if the position is at the end.
26443			 */
26444			isAtEnd: isAtEnd,
26445
26446			/**
26447			 * Compares the caret position to another caret position. This will only compare the
26448			 * container and offset not it's visual position.
26449			 *
26450			 * @method isEqual
26451			 * @param {tinymce.caret.CaretPosition} caretPosition Caret position to compare with.
26452			 * @return {Boolean} true if the caret positions are equal.
26453			 */
26454			isEqual: isEqual,
26455
26456			/**
26457			 * Returns the closest resolved node from a node index. That means if you have an offset after the
26458			 * last node in a container it will return that last node.
26459			 *
26460			 * @method getNode
26461			 * @return {Node} Node that is closest to the index.
26462			 */
26463			getNode: getNode
26464		};
26465	}
26466
26467	/**
26468	 * Creates a caret position from the start of a range.
26469	 *
26470	 * @method fromRangeStart
26471	 * @param {DOMRange} range DOM Range to create caret position from.
26472	 * @return {tinymce.caret.CaretPosition} Caret position from the start of DOM range.
26473	 */
26474	CaretPosition.fromRangeStart = function(range) {
26475		return new CaretPosition(range.startContainer, range.startOffset);
26476	};
26477
26478	/**
26479	 * Creates a caret position from the end of a range.
26480	 *
26481	 * @method fromRangeEnd
26482	 * @param {DOMRange} range DOM Range to create caret position from.
26483	 * @return {tinymce.caret.CaretPosition} Caret position from the end of DOM range.
26484	 */
26485	CaretPosition.fromRangeEnd = function(range) {
26486		return new CaretPosition(range.endContainer, range.endOffset);
26487	};
26488
26489	/**
26490	 * Creates a caret position from a node and places the offset after it.
26491	 *
26492	 * @method after
26493	 * @param {Node} node Node to get caret position from.
26494	 * @return {tinymce.caret.CaretPosition} Caret position from the node.
26495	 */
26496	CaretPosition.after = function(node) {
26497		return new CaretPosition(node.parentNode, nodeIndex(node) + 1);
26498	};
26499
26500	/**
26501	 * Creates a caret position from a node and places the offset before it.
26502	 *
26503	 * @method before
26504	 * @param {Node} node Node to get caret position from.
26505	 * @return {tinymce.caret.CaretPosition} Caret position from the node.
26506	 */
26507	CaretPosition.before = function(node) {
26508		return new CaretPosition(node.parentNode, nodeIndex(node));
26509	};
26510
26511	return CaretPosition;
26512});
26513
26514// Included from: js/tinymce/classes/caret/CaretBookmark.js
26515
26516/**
26517 * CaretBookmark.js
26518 *
26519 * Released under LGPL License.
26520 * Copyright (c) 1999-2015 Ephox Corp. All rights reserved
26521 *
26522 * License: http://www.tinymce.com/license
26523 * Contributing: http://www.tinymce.com/contributing
26524 */
26525
26526/**
26527 * This module creates or resolves xpath like string representation of a CaretPositions.
26528 *
26529 * The format is a / separated list of chunks with:
26530 * <element|text()>[index|after|before]
26531 *
26532 * For example:
26533 *  p[0]/b[0]/text()[0],1 = <p><b>a|c</b></p>
26534 *  p[0]/img[0],before = <p>|<img></p>
26535 *  p[0]/img[0],after = <p><img>|</p>
26536 *
26537 * @private
26538 * @static
26539 * @class tinymce.caret.CaretBookmark
26540 * @example
26541 * var bookmark = CaretBookmark.create(rootElm, CaretPosition.before(rootElm.firstChild));
26542 * var caretPosition = CaretBookmark.resolve(bookmark);
26543 */
26544define('tinymce/caret/CaretBookmark', [
26545	'tinymce/dom/NodeType',
26546	'tinymce/dom/DOMUtils',
26547	'tinymce/util/Fun',
26548	'tinymce/util/Arr',
26549	'tinymce/caret/CaretPosition'
26550], function(NodeType, DomUtils, Fun, Arr, CaretPosition) {
26551	var isText = NodeType.isText,
26552		isBogus = NodeType.isBogus,
26553		nodeIndex = DomUtils.nodeIndex;
26554
26555	function normalizedParent(node) {
26556		var parentNode = node.parentNode;
26557
26558		if (isBogus(parentNode)) {
26559			return normalizedParent(parentNode);
26560		}
26561
26562		return parentNode;
26563	}
26564
26565	function getChildNodes(node) {
26566		if (!node) {
26567			return [];
26568		}
26569
26570		return Arr.reduce(node.childNodes, function(result, node) {
26571			if (isBogus(node) && node.nodeName != 'BR') {
26572				result = result.concat(getChildNodes(node));
26573			} else {
26574				result.push(node);
26575			}
26576
26577			return result;
26578		}, []);
26579	}
26580
26581	function normalizedTextOffset(textNode, offset) {
26582		while ((textNode = textNode.previousSibling)) {
26583			if (!isText(textNode)) {
26584				break;
26585			}
26586
26587			offset += textNode.data.length;
26588		}
26589
26590		return offset;
26591	}
26592
26593	function equal(targetValue) {
26594		return function(value) {
26595			return targetValue === value;
26596		};
26597	}
26598
26599	function normalizedNodeIndex(node) {
26600		var nodes, index, numTextFragments;
26601
26602		nodes = getChildNodes(normalizedParent(node));
26603		index = Arr.findIndex(nodes, equal(node), node);
26604		nodes = nodes.slice(0, index + 1);
26605		numTextFragments = Arr.reduce(nodes, function(result, node, i) {
26606			if (isText(node) && isText(nodes[i - 1])) {
26607				result++;
26608			}
26609
26610			return result;
26611		}, 0);
26612
26613		nodes = Arr.filter(nodes, NodeType.matchNodeNames(node.nodeName));
26614		index = Arr.findIndex(nodes, equal(node), node);
26615
26616		return index - numTextFragments;
26617	}
26618
26619	function createPathItem(node) {
26620		var name;
26621
26622		if (isText(node)) {
26623			name = 'text()';
26624		} else {
26625			name = node.nodeName.toLowerCase();
26626		}
26627
26628		return name + '[' + normalizedNodeIndex(node) + ']';
26629	}
26630
26631	function parentsUntil(rootNode, node, predicate) {
26632		var parents = [];
26633
26634		for (node = node.parentNode; node != rootNode; node = node.parentNode) {
26635			if (predicate && predicate(node)) {
26636				break;
26637			}
26638
26639			parents.push(node);
26640		}
26641
26642		return parents;
26643	}
26644
26645	function create(rootNode, caretPosition) {
26646		var container, offset, path = [],
26647			outputOffset, childNodes, parents;
26648
26649		container = caretPosition.container();
26650		offset = caretPosition.offset();
26651
26652		if (isText(container)) {
26653			outputOffset = normalizedTextOffset(container, offset);
26654		} else {
26655			childNodes = container.childNodes;
26656			if (offset >= childNodes.length) {
26657				outputOffset = 'after';
26658				offset = childNodes.length - 1;
26659			} else {
26660				outputOffset = 'before';
26661			}
26662
26663			container = childNodes[offset];
26664		}
26665
26666		path.push(createPathItem(container));
26667		parents = parentsUntil(rootNode, container);
26668		parents = Arr.filter(parents, Fun.negate(NodeType.isBogus));
26669		path = path.concat(Arr.map(parents, function(node) {
26670			return createPathItem(node);
26671		}));
26672
26673		return path.reverse().join('/') + ',' + outputOffset;
26674	}
26675
26676	function resolvePathItem(node, name, index) {
26677		var nodes = getChildNodes(node);
26678
26679		nodes = Arr.filter(nodes, function(node, index) {
26680			return !isText(node) || !isText(nodes[index - 1]);
26681		});
26682
26683		nodes = Arr.filter(nodes, NodeType.matchNodeNames(name));
26684		return nodes[index];
26685	}
26686
26687	function findTextPosition(container, offset) {
26688		var node = container, targetOffset = 0, dataLen;
26689
26690		while (isText(node)) {
26691			dataLen = node.data.length;
26692
26693			if (offset >= targetOffset && offset <= targetOffset + dataLen) {
26694				container = node;
26695				offset = offset - targetOffset;
26696				break;
26697			}
26698
26699			if (!isText(node.nextSibling)) {
26700				container = node;
26701				offset = dataLen;
26702				break;
26703			}
26704
26705			targetOffset += dataLen;
26706			node = node.nextSibling;
26707		}
26708
26709		if (offset > container.data.length) {
26710			offset = container.data.length;
26711		}
26712
26713		return new CaretPosition(container, offset);
26714	}
26715
26716	function resolve(rootNode, path) {
26717		var parts, container, offset;
26718
26719		if (!path) {
26720			return null;
26721		}
26722
26723		parts = path.split(',');
26724		path = parts[0].split('/');
26725		offset = parts.length > 1 ? parts[1] : 'before';
26726
26727		container = Arr.reduce(path, function(result, value) {
26728			value = /([\w\-\(\)]+)\[([0-9]+)\]/.exec(value);
26729			if (!value) {
26730				return null;
26731			}
26732
26733			if (value[1] === 'text()') {
26734				value[1] = '#text';
26735			}
26736
26737			return resolvePathItem(result, value[1], parseInt(value[2], 10));
26738		}, rootNode);
26739
26740		if (!container) {
26741			return null;
26742		}
26743
26744		if (!isText(container)) {
26745			if (offset === 'after') {
26746				offset = nodeIndex(container) + 1;
26747			} else {
26748				offset = nodeIndex(container);
26749			}
26750
26751			return new CaretPosition(container.parentNode, offset);
26752		}
26753
26754		return findTextPosition(container, parseInt(offset, 10));
26755	}
26756
26757	return {
26758		/**
26759		 * Create a xpath bookmark location for the specified caret position.
26760		 *
26761		 * @method create
26762		 * @param {Node} rootNode Root node to create bookmark within.
26763		 * @param {tinymce.caret.CaretPosition} caretPosition Caret position within the root node.
26764		 * @return {String} String xpath like location of caret position.
26765		 */
26766		create: create,
26767
26768		/**
26769		 * Resolves a xpath like bookmark location to the a caret position.
26770		 *
26771		 * @method resolve
26772		 * @param {Node} rootNode Root node to resolve xpath bookmark within.
26773		 * @param {String} bookmark Bookmark string to resolve.
26774		 * @return {tinymce.caret.CaretPosition} Caret position resolved from xpath like bookmark.
26775		 */
26776		resolve: resolve
26777	};
26778});
26779
26780// Included from: js/tinymce/classes/dom/BookmarkManager.js
26781
26782/**
26783 * BookmarkManager.js
26784 *
26785 * Released under LGPL License.
26786 * Copyright (c) 1999-2015 Ephox Corp. All rights reserved
26787 *
26788 * License: http://www.tinymce.com/license
26789 * Contributing: http://www.tinymce.com/contributing
26790 */
26791
26792/**
26793 * This class handles selection bookmarks.
26794 *
26795 * @class tinymce.dom.BookmarkManager
26796 */
26797define("tinymce/dom/BookmarkManager", [
26798	"tinymce/Env",
26799	"tinymce/util/Tools",
26800	"tinymce/caret/CaretContainer",
26801	"tinymce/caret/CaretBookmark",
26802	"tinymce/caret/CaretPosition",
26803	"tinymce/dom/NodeType"
26804], function(Env, Tools, CaretContainer, CaretBookmark, CaretPosition, NodeType) {
26805	var isContentEditableFalse = NodeType.isContentEditableFalse;
26806
26807	/**
26808	 * Constructs a new BookmarkManager instance for a specific selection instance.
26809	 *
26810	 * @constructor
26811	 * @method BookmarkManager
26812	 * @param {tinymce.dom.Selection} selection Selection instance to handle bookmarks for.
26813	 */
26814	function BookmarkManager(selection) {
26815		var dom = selection.dom;
26816
26817		/**
26818		 * Returns a bookmark location for the current selection. This bookmark object
26819		 * can then be used to restore the selection after some content modification to the document.
26820		 *
26821		 * @method getBookmark
26822		 * @param {Number} type Optional state if the bookmark should be simple or not. Default is complex.
26823		 * @param {Boolean} normalized Optional state that enables you to get a position that it would be after normalization.
26824		 * @return {Object} Bookmark object, use moveToBookmark with this object to restore the selection.
26825		 * @example
26826		 * // Stores a bookmark of the current selection
26827		 * var bm = tinymce.activeEditor.selection.getBookmark();
26828		 *
26829		 * tinymce.activeEditor.setContent(tinymce.activeEditor.getContent() + 'Some new content');
26830		 *
26831		 * // Restore the selection bookmark
26832		 * tinymce.activeEditor.selection.moveToBookmark(bm);
26833		 */
26834		this.getBookmark = function(type, normalized) {
26835			var rng, rng2, id, collapsed, name, element, chr = '&#xFEFF;', styles;
26836
26837			function findIndex(name, element) {
26838				var count = 0;
26839
26840				Tools.each(dom.select(name), function(node) {
26841					if (node.getAttribute('data-mce-bogus') === 'all') {
26842						return;
26843					}
26844
26845					if (node == element) {
26846						return false;
26847					}
26848
26849					count++;
26850				});
26851
26852				return count;
26853			}
26854
26855			function normalizeTableCellSelection(rng) {
26856				function moveEndPoint(start) {
26857					var container, offset, childNodes, prefix = start ? 'start' : 'end';
26858
26859					container = rng[prefix + 'Container'];
26860					offset = rng[prefix + 'Offset'];
26861
26862					if (container.nodeType == 1 && container.nodeName == "TR") {
26863						childNodes = container.childNodes;
26864						container = childNodes[Math.min(start ? offset : offset - 1, childNodes.length - 1)];
26865						if (container) {
26866							offset = start ? 0 : container.childNodes.length;
26867							rng['set' + (start ? 'Start' : 'End')](container, offset);
26868						}
26869					}
26870				}
26871
26872				moveEndPoint(true);
26873				moveEndPoint();
26874
26875				return rng;
26876			}
26877
26878			function getLocation(rng) {
26879				var root = dom.getRoot(), bookmark = {};
26880
26881				function getPoint(rng, start) {
26882					var container = rng[start ? 'startContainer' : 'endContainer'],
26883						offset = rng[start ? 'startOffset' : 'endOffset'], point = [], node, childNodes, after = 0;
26884
26885					if (container.nodeType == 3) {
26886						if (normalized) {
26887							for (node = container.previousSibling; node && node.nodeType == 3; node = node.previousSibling) {
26888								offset += node.nodeValue.length;
26889							}
26890						}
26891
26892						point.push(offset);
26893					} else {
26894						childNodes = container.childNodes;
26895
26896						if (offset >= childNodes.length && childNodes.length) {
26897							after = 1;
26898							offset = Math.max(0, childNodes.length - 1);
26899						}
26900
26901						point.push(dom.nodeIndex(childNodes[offset], normalized) + after);
26902					}
26903
26904					for (; container && container != root; container = container.parentNode) {
26905						point.push(dom.nodeIndex(container, normalized));
26906					}
26907
26908					return point;
26909				}
26910
26911				bookmark.start = getPoint(rng, true);
26912
26913				if (!selection.isCollapsed()) {
26914					bookmark.end = getPoint(rng);
26915				}
26916
26917				return bookmark;
26918			}
26919
26920			function findAdjacentContentEditableFalseElm(rng) {
26921				function findSibling(node) {
26922					var sibling;
26923
26924					if (CaretContainer.isCaretContainer(node)) {
26925						if (NodeType.isText(node) && CaretContainer.isCaretContainerBlock(node)) {
26926							node = node.parentNode;
26927						}
26928
26929						sibling = node.previousSibling;
26930						if (isContentEditableFalse(sibling)) {
26931							return sibling;
26932						}
26933
26934						sibling = node.nextSibling;
26935						if (isContentEditableFalse(sibling)) {
26936							return sibling;
26937						}
26938					}
26939				}
26940
26941				return findSibling(rng.startContainer) || findSibling(rng.endContainer);
26942			}
26943
26944			if (type == 2) {
26945				element = selection.getNode();
26946				name = element ? element.nodeName : null;
26947				rng = selection.getRng();
26948
26949				if (isContentEditableFalse(element) || name == 'IMG') {
26950					return {name: name, index: findIndex(name, element)};
26951				}
26952
26953				if (selection.tridentSel) {
26954					return selection.tridentSel.getBookmark(type);
26955				}
26956
26957				element = findAdjacentContentEditableFalseElm(rng);
26958				if (element) {
26959					name = element.tagName;
26960					return {name: name, index: findIndex(name, element)};
26961				}
26962
26963				return getLocation(rng);
26964			}
26965
26966			if (type == 3) {
26967				rng = selection.getRng();
26968
26969				return {
26970					start: CaretBookmark.create(dom.getRoot(), CaretPosition.fromRangeStart(rng)),
26971					end: CaretBookmark.create(dom.getRoot(), CaretPosition.fromRangeEnd(rng))
26972				};
26973			}
26974
26975			// Handle simple range
26976			if (type) {
26977				return {rng: selection.getRng()};
26978			}
26979
26980			rng = selection.getRng();
26981			id = dom.uniqueId();
26982			collapsed = selection.isCollapsed();
26983			styles = 'overflow:hidden;line-height:0px';
26984
26985			// Explorer method
26986			if (rng.duplicate || rng.item) {
26987				// Text selection
26988				if (!rng.item) {
26989					rng2 = rng.duplicate();
26990
26991					try {
26992						// Insert start marker
26993						rng.collapse();
26994						rng.pasteHTML('<span data-mce-type="bookmark" id="' + id + '_start" style="' + styles + '">' + chr + '</span>');
26995
26996						// Insert end marker
26997						if (!collapsed) {
26998							rng2.collapse(false);
26999
27000							// Detect the empty space after block elements in IE and move the
27001							// end back one character <p></p>] becomes <p>]</p>
27002							rng.moveToElementText(rng2.parentElement());
27003							if (rng.compareEndPoints('StartToEnd', rng2) === 0) {
27004								rng2.move('character', -1);
27005							}
27006
27007							rng2.pasteHTML('<span data-mce-type="bookmark" id="' + id + '_end" style="' + styles + '">' + chr + '</span>');
27008						}
27009					} catch (ex) {
27010						// IE might throw unspecified error so lets ignore it
27011						return null;
27012					}
27013				} else {
27014					// Control selection
27015					element = rng.item(0);
27016					name = element.nodeName;
27017
27018					return {name: name, index: findIndex(name, element)};
27019				}
27020			} else {
27021				element = selection.getNode();
27022				name = element.nodeName;
27023				if (name == 'IMG') {
27024					return {name: name, index: findIndex(name, element)};
27025				}
27026
27027				// W3C method
27028				rng2 = normalizeTableCellSelection(rng.cloneRange());
27029
27030				// Insert end marker
27031				if (!collapsed) {
27032					rng2.collapse(false);
27033					rng2.insertNode(dom.create('span', {'data-mce-type': "bookmark", id: id + '_end', style: styles}, chr));
27034				}
27035
27036				rng = normalizeTableCellSelection(rng);
27037				rng.collapse(true);
27038				rng.insertNode(dom.create('span', {'data-mce-type': "bookmark", id: id + '_start', style: styles}, chr));
27039			}
27040
27041			selection.moveToBookmark({id: id, keep: 1});
27042
27043			return {id: id};
27044		};
27045
27046		/**
27047		 * Restores the selection to the specified bookmark.
27048		 *
27049		 * @method moveToBookmark
27050		 * @param {Object} bookmark Bookmark to restore selection from.
27051		 * @return {Boolean} true/false if it was successful or not.
27052		 * @example
27053		 * // Stores a bookmark of the current selection
27054		 * var bm = tinymce.activeEditor.selection.getBookmark();
27055		 *
27056		 * tinymce.activeEditor.setContent(tinymce.activeEditor.getContent() + 'Some new content');
27057		 *
27058		 * // Restore the selection bookmark
27059		 * tinymce.activeEditor.selection.moveToBookmark(bm);
27060		 */
27061		this.moveToBookmark = function(bookmark) {
27062			var rng, root, startContainer, endContainer, startOffset, endOffset;
27063
27064			function setEndPoint(start) {
27065				var point = bookmark[start ? 'start' : 'end'], i, node, offset, children;
27066
27067				if (point) {
27068					offset = point[0];
27069
27070					// Find container node
27071					for (node = root, i = point.length - 1; i >= 1; i--) {
27072						children = node.childNodes;
27073
27074						if (point[i] > children.length - 1) {
27075							return;
27076						}
27077
27078						node = children[point[i]];
27079					}
27080
27081					// Move text offset to best suitable location
27082					if (node.nodeType === 3) {
27083						offset = Math.min(point[0], node.nodeValue.length);
27084					}
27085
27086					// Move element offset to best suitable location
27087					if (node.nodeType === 1) {
27088						offset = Math.min(point[0], node.childNodes.length);
27089					}
27090
27091					// Set offset within container node
27092					if (start) {
27093						rng.setStart(node, offset);
27094					} else {
27095						rng.setEnd(node, offset);
27096					}
27097				}
27098
27099				return true;
27100			}
27101
27102			function restoreEndPoint(suffix) {
27103				var marker = dom.get(bookmark.id + '_' + suffix), node, idx, next, prev, keep = bookmark.keep;
27104
27105				if (marker) {
27106					node = marker.parentNode;
27107
27108					if (suffix == 'start') {
27109						if (!keep) {
27110							idx = dom.nodeIndex(marker);
27111						} else {
27112							node = marker.firstChild;
27113							idx = 1;
27114						}
27115
27116						startContainer = endContainer = node;
27117						startOffset = endOffset = idx;
27118					} else {
27119						if (!keep) {
27120							idx = dom.nodeIndex(marker);
27121						} else {
27122							node = marker.firstChild;
27123							idx = 1;
27124						}
27125
27126						endContainer = node;
27127						endOffset = idx;
27128					}
27129
27130					if (!keep) {
27131						prev = marker.previousSibling;
27132						next = marker.nextSibling;
27133
27134						// Remove all marker text nodes
27135						Tools.each(Tools.grep(marker.childNodes), function(node) {
27136							if (node.nodeType == 3) {
27137								node.nodeValue = node.nodeValue.replace(/\uFEFF/g, '');
27138							}
27139						});
27140
27141						// Remove marker but keep children if for example contents where inserted into the marker
27142						// Also remove duplicated instances of the marker for example by a
27143						// split operation or by WebKit auto split on paste feature
27144						while ((marker = dom.get(bookmark.id + '_' + suffix))) {
27145							dom.remove(marker, 1);
27146						}
27147
27148						// If siblings are text nodes then merge them unless it's Opera since it some how removes the node
27149						// and we are sniffing since adding a lot of detection code for a browser with 3% of the market
27150						// isn't worth the effort. Sorry, Opera but it's just a fact
27151						if (prev && next && prev.nodeType == next.nodeType && prev.nodeType == 3 && !Env.opera) {
27152							idx = prev.nodeValue.length;
27153							prev.appendData(next.nodeValue);
27154							dom.remove(next);
27155
27156							if (suffix == 'start') {
27157								startContainer = endContainer = prev;
27158								startOffset = endOffset = idx;
27159							} else {
27160								endContainer = prev;
27161								endOffset = idx;
27162							}
27163						}
27164					}
27165				}
27166			}
27167
27168			function addBogus(node) {
27169				// Adds a bogus BR element for empty block elements
27170				if (dom.isBlock(node) && !node.innerHTML && !Env.ie) {
27171					node.innerHTML = '<br data-mce-bogus="1" />';
27172				}
27173
27174				return node;
27175			}
27176
27177			function resolveCaretPositionBookmark() {
27178				var rng, pos;
27179
27180				rng = dom.createRng();
27181				pos = CaretBookmark.resolve(dom.getRoot(), bookmark.start);
27182				rng.setStart(pos.container(), pos.offset());
27183
27184				pos = CaretBookmark.resolve(dom.getRoot(), bookmark.end);
27185				rng.setEnd(pos.container(), pos.offset());
27186
27187				return rng;
27188			}
27189
27190			if (bookmark) {
27191				if (Tools.isArray(bookmark.start)) {
27192					rng = dom.createRng();
27193					root = dom.getRoot();
27194
27195					if (selection.tridentSel) {
27196						return selection.tridentSel.moveToBookmark(bookmark);
27197					}
27198
27199					if (setEndPoint(true) && setEndPoint()) {
27200						selection.setRng(rng);
27201					}
27202				} else if (typeof bookmark.start == 'string') {
27203					selection.setRng(resolveCaretPositionBookmark(bookmark));
27204				} else if (bookmark.id) {
27205					// Restore start/end points
27206					restoreEndPoint('start');
27207					restoreEndPoint('end');
27208
27209					if (startContainer) {
27210						rng = dom.createRng();
27211						rng.setStart(addBogus(startContainer), startOffset);
27212						rng.setEnd(addBogus(endContainer), endOffset);
27213						selection.setRng(rng);
27214					}
27215				} else if (bookmark.name) {
27216					selection.select(dom.select(bookmark.name)[bookmark.index]);
27217				} else if (bookmark.rng) {
27218					selection.setRng(bookmark.rng);
27219				}
27220			}
27221		};
27222	}
27223
27224	/**
27225	 * Returns true/false if the specified node is a bookmark node or not.
27226	 *
27227	 * @static
27228	 * @method isBookmarkNode
27229	 * @param {DOMNode} node DOM Node to check if it's a bookmark node or not.
27230	 * @return {Boolean} true/false if the node is a bookmark node or not.
27231	 */
27232	BookmarkManager.isBookmarkNode = function(node) {
27233		return node && node.tagName === 'SPAN' && node.getAttribute('data-mce-type') === 'bookmark';
27234	};
27235
27236	return BookmarkManager;
27237});
27238
27239// Included from: js/tinymce/classes/dom/Selection.js
27240
27241/**
27242 * Selection.js
27243 *
27244 * Released under LGPL License.
27245 * Copyright (c) 1999-2015 Ephox Corp. All rights reserved
27246 *
27247 * License: http://www.tinymce.com/license
27248 * Contributing: http://www.tinymce.com/contributing
27249 */
27250
27251/**
27252 * This class handles text and control selection it's an crossbrowser utility class.
27253 * Consult the TinyMCE Wiki API for more details and examples on how to use this class.
27254 *
27255 * @class tinymce.dom.Selection
27256 * @example
27257 * // Getting the currently selected node for the active editor
27258 * alert(tinymce.activeEditor.selection.getNode().nodeName);
27259 */
27260define("tinymce/dom/Selection", [
27261	"tinymce/dom/TreeWalker",
27262	"tinymce/dom/TridentSelection",
27263	"tinymce/dom/ControlSelection",
27264	"tinymce/dom/RangeUtils",
27265	"tinymce/dom/BookmarkManager",
27266	"tinymce/dom/NodeType",
27267	"tinymce/Env",
27268	"tinymce/util/Tools"
27269], function(TreeWalker, TridentSelection, ControlSelection, RangeUtils, BookmarkManager, NodeType, Env, Tools) {
27270	var each = Tools.each, trim = Tools.trim;
27271	var isIE = Env.ie;
27272
27273	/**
27274	 * Constructs a new selection instance.
27275	 *
27276	 * @constructor
27277	 * @method Selection
27278	 * @param {tinymce.dom.DOMUtils} dom DOMUtils object reference.
27279	 * @param {Window} win Window to bind the selection object to.
27280	 * @param {tinymce.Editor} editor Editor instance of the selection.
27281	 * @param {tinymce.dom.Serializer} serializer DOM serialization class to use for getContent.
27282	 */
27283	function Selection(dom, win, serializer, editor) {
27284		var self = this;
27285
27286		self.dom = dom;
27287		self.win = win;
27288		self.serializer = serializer;
27289		self.editor = editor;
27290		self.bookmarkManager = new BookmarkManager(self);
27291		self.controlSelection = new ControlSelection(self, editor);
27292
27293		// No W3C Range support
27294		if (!self.win.getSelection) {
27295			self.tridentSel = new TridentSelection(self);
27296		}
27297	}
27298
27299	Selection.prototype = {
27300		/**
27301		 * Move the selection cursor range to the specified node and offset.
27302		 * If there is no node specified it will move it to the first suitable location within the body.
27303		 *
27304		 * @method setCursorLocation
27305		 * @param {Node} node Optional node to put the cursor in.
27306		 * @param {Number} offset Optional offset from the start of the node to put the cursor at.
27307		 */
27308		setCursorLocation: function(node, offset) {
27309			var self = this, rng = self.dom.createRng();
27310
27311			if (!node) {
27312				self._moveEndPoint(rng, self.editor.getBody(), true);
27313				self.setRng(rng);
27314			} else {
27315				rng.setStart(node, offset);
27316				rng.setEnd(node, offset);
27317				self.setRng(rng);
27318				self.collapse(false);
27319			}
27320		},
27321
27322		/**
27323		 * Returns the selected contents using the DOM serializer passed in to this class.
27324		 *
27325		 * @method getContent
27326		 * @param {Object} args Optional settings class with for example output format text or html.
27327		 * @return {String} Selected contents in for example HTML format.
27328		 * @example
27329		 * // Alerts the currently selected contents
27330		 * alert(tinymce.activeEditor.selection.getContent());
27331		 *
27332		 * // Alerts the currently selected contents as plain text
27333		 * alert(tinymce.activeEditor.selection.getContent({format: 'text'}));
27334		 */
27335		getContent: function(args) {
27336			var self = this, rng = self.getRng(), tmpElm = self.dom.create("body");
27337			var se = self.getSel(), whiteSpaceBefore, whiteSpaceAfter, fragment;
27338
27339			args = args || {};
27340			whiteSpaceBefore = whiteSpaceAfter = '';
27341			args.get = true;
27342			args.format = args.format || 'html';
27343			args.selection = true;
27344			self.editor.fire('BeforeGetContent', args);
27345
27346			if (args.format == 'text') {
27347				return self.isCollapsed() ? '' : (rng.text || (se.toString ? se.toString() : ''));
27348			}
27349
27350			if (rng.cloneContents) {
27351				fragment = rng.cloneContents();
27352
27353				if (fragment) {
27354					tmpElm.appendChild(fragment);
27355				}
27356			} else if (rng.item !== undefined || rng.htmlText !== undefined) {
27357				// IE will produce invalid markup if elements are present that
27358				// it doesn't understand like custom elements or HTML5 elements.
27359				// Adding a BR in front of the contents and then remoiving it seems to fix it though.
27360				tmpElm.innerHTML = '<br>' + (rng.item ? rng.item(0).outerHTML : rng.htmlText);
27361				tmpElm.removeChild(tmpElm.firstChild);
27362			} else {
27363				tmpElm.innerHTML = rng.toString();
27364			}
27365
27366			// Keep whitespace before and after
27367			if (/^\s/.test(tmpElm.innerHTML)) {
27368				whiteSpaceBefore = ' ';
27369			}
27370
27371			if (/\s+$/.test(tmpElm.innerHTML)) {
27372				whiteSpaceAfter = ' ';
27373			}
27374
27375			args.getInner = true;
27376
27377			args.content = self.isCollapsed() ? '' : whiteSpaceBefore + self.serializer.serialize(tmpElm, args) + whiteSpaceAfter;
27378			self.editor.fire('GetContent', args);
27379
27380			return args.content;
27381		},
27382
27383		/**
27384		 * Sets the current selection to the specified content. If any contents is selected it will be replaced
27385		 * with the contents passed in to this function. If there is no selection the contents will be inserted
27386		 * where the caret is placed in the editor/page.
27387		 *
27388		 * @method setContent
27389		 * @param {String} content HTML contents to set could also be other formats depending on settings.
27390		 * @param {Object} args Optional settings object with for example data format.
27391		 * @example
27392		 * // Inserts some HTML contents at the current selection
27393		 * tinymce.activeEditor.selection.setContent('<strong>Some contents</strong>');
27394		 */
27395		setContent: function(content, args) {
27396			var self = this, rng = self.getRng(), caretNode, doc = self.win.document, frag, temp;
27397
27398			args = args || {format: 'html'};
27399			args.set = true;
27400			args.selection = true;
27401			args.content = content;
27402
27403			// Dispatch before set content event
27404			if (!args.no_events) {
27405				self.editor.fire('BeforeSetContent', args);
27406			}
27407
27408			content = args.content;
27409
27410			if (rng.insertNode) {
27411				// Make caret marker since insertNode places the caret in the beginning of text after insert
27412				content += '<span id="__caret">_</span>';
27413
27414				// Delete and insert new node
27415				if (rng.startContainer == doc && rng.endContainer == doc) {
27416					// WebKit will fail if the body is empty since the range is then invalid and it can't insert contents
27417					doc.body.innerHTML = content;
27418				} else {
27419					rng.deleteContents();
27420
27421					if (doc.body.childNodes.length === 0) {
27422						doc.body.innerHTML = content;
27423					} else {
27424						// createContextualFragment doesn't exists in IE 9 DOMRanges
27425						if (rng.createContextualFragment) {
27426							rng.insertNode(rng.createContextualFragment(content));
27427						} else {
27428							// Fake createContextualFragment call in IE 9
27429							frag = doc.createDocumentFragment();
27430							temp = doc.createElement('div');
27431
27432							frag.appendChild(temp);
27433							temp.outerHTML = content;
27434
27435							rng.insertNode(frag);
27436						}
27437					}
27438				}
27439
27440				// Move to caret marker
27441				caretNode = self.dom.get('__caret');
27442
27443				// Make sure we wrap it compleatly, Opera fails with a simple select call
27444				rng = doc.createRange();
27445				rng.setStartBefore(caretNode);
27446				rng.setEndBefore(caretNode);
27447				self.setRng(rng);
27448
27449				// Remove the caret position
27450				self.dom.remove('__caret');
27451
27452				try {
27453					self.setRng(rng);
27454				} catch (ex) {
27455					// Might fail on Opera for some odd reason
27456				}
27457			} else {
27458				if (rng.item) {
27459					// Delete content and get caret text selection
27460					doc.execCommand('Delete', false, null);
27461					rng = self.getRng();
27462				}
27463
27464				// Explorer removes spaces from the beginning of pasted contents
27465				if (/^\s+/.test(content)) {
27466					rng.pasteHTML('<span id="__mce_tmp">_</span>' + content);
27467					self.dom.remove('__mce_tmp');
27468				} else {
27469					rng.pasteHTML(content);
27470				}
27471			}
27472
27473			// Dispatch set content event
27474			if (!args.no_events) {
27475				self.editor.fire('SetContent', args);
27476			}
27477		},
27478
27479		/**
27480		 * Returns the start element of a selection range. If the start is in a text
27481		 * node the parent element will be returned.
27482		 *
27483		 * @method getStart
27484		 * @param {Boolean} real Optional state to get the real parent when the selection is collapsed not the closest element.
27485		 * @return {Element} Start element of selection range.
27486		 */
27487		getStart: function(real) {
27488			var self = this, rng = self.getRng(), startElement, parentElement, checkRng, node;
27489
27490			if (rng.duplicate || rng.item) {
27491				// Control selection, return first item
27492				if (rng.item) {
27493					return rng.item(0);
27494				}
27495
27496				// Get start element
27497				checkRng = rng.duplicate();
27498				checkRng.collapse(1);
27499				startElement = checkRng.parentElement();
27500				if (startElement.ownerDocument !== self.dom.doc) {
27501					startElement = self.dom.getRoot();
27502				}
27503
27504				// Check if range parent is inside the start element, then return the inner parent element
27505				// This will fix issues when a single element is selected, IE would otherwise return the wrong start element
27506				parentElement = node = rng.parentElement();
27507				while ((node = node.parentNode)) {
27508					if (node == startElement) {
27509						startElement = parentElement;
27510						break;
27511					}
27512				}
27513
27514				return startElement;
27515			}
27516
27517			startElement = rng.startContainer;
27518
27519			if (startElement.nodeType == 1 && startElement.hasChildNodes()) {
27520				if (!real || !rng.collapsed) {
27521					startElement = startElement.childNodes[Math.min(startElement.childNodes.length - 1, rng.startOffset)];
27522				}
27523			}
27524
27525			if (startElement && startElement.nodeType == 3) {
27526				return startElement.parentNode;
27527			}
27528
27529			return startElement;
27530		},
27531
27532		/**
27533		 * Returns the end element of a selection range. If the end is in a text
27534		 * node the parent element will be returned.
27535		 *
27536		 * @method getEnd
27537		 * @param {Boolean} real Optional state to get the real parent when the selection is collapsed not the closest element.
27538		 * @return {Element} End element of selection range.
27539		 */
27540		getEnd: function(real) {
27541			var self = this, rng = self.getRng(), endElement, endOffset;
27542
27543			if (rng.duplicate || rng.item) {
27544				if (rng.item) {
27545					return rng.item(0);
27546				}
27547
27548				rng = rng.duplicate();
27549				rng.collapse(0);
27550				endElement = rng.parentElement();
27551				if (endElement.ownerDocument !== self.dom.doc) {
27552					endElement = self.dom.getRoot();
27553				}
27554
27555				if (endElement && endElement.nodeName == 'BODY') {
27556					return endElement.lastChild || endElement;
27557				}
27558
27559				return endElement;
27560			}
27561
27562			endElement = rng.endContainer;
27563			endOffset = rng.endOffset;
27564
27565			if (endElement.nodeType == 1 && endElement.hasChildNodes()) {
27566				if (!real || !rng.collapsed) {
27567					endElement = endElement.childNodes[endOffset > 0 ? endOffset - 1 : endOffset];
27568				}
27569			}
27570
27571			if (endElement && endElement.nodeType == 3) {
27572				return endElement.parentNode;
27573			}
27574
27575			return endElement;
27576		},
27577
27578		/**
27579		 * Returns a bookmark location for the current selection. This bookmark object
27580		 * can then be used to restore the selection after some content modification to the document.
27581		 *
27582		 * @method getBookmark
27583		 * @param {Number} type Optional state if the bookmark should be simple or not. Default is complex.
27584		 * @param {Boolean} normalized Optional state that enables you to get a position that it would be after normalization.
27585		 * @return {Object} Bookmark object, use moveToBookmark with this object to restore the selection.
27586		 * @example
27587		 * // Stores a bookmark of the current selection
27588		 * var bm = tinymce.activeEditor.selection.getBookmark();
27589		 *
27590		 * tinymce.activeEditor.setContent(tinymce.activeEditor.getContent() + 'Some new content');
27591		 *
27592		 * // Restore the selection bookmark
27593		 * tinymce.activeEditor.selection.moveToBookmark(bm);
27594		 */
27595		getBookmark: function(type, normalized) {
27596			return this.bookmarkManager.getBookmark(type, normalized);
27597		},
27598
27599		/**
27600		 * Restores the selection to the specified bookmark.
27601		 *
27602		 * @method moveToBookmark
27603		 * @param {Object} bookmark Bookmark to restore selection from.
27604		 * @return {Boolean} true/false if it was successful or not.
27605		 * @example
27606		 * // Stores a bookmark of the current selection
27607		 * var bm = tinymce.activeEditor.selection.getBookmark();
27608		 *
27609		 * tinymce.activeEditor.setContent(tinymce.activeEditor.getContent() + 'Some new content');
27610		 *
27611		 * // Restore the selection bookmark
27612		 * tinymce.activeEditor.selection.moveToBookmark(bm);
27613		 */
27614		moveToBookmark: function(bookmark) {
27615			return this.bookmarkManager.moveToBookmark(bookmark);
27616		},
27617
27618		/**
27619		 * Selects the specified element. This will place the start and end of the selection range around the element.
27620		 *
27621		 * @method select
27622		 * @param {Element} node HTML DOM element to select.
27623		 * @param {Boolean} content Optional bool state if the contents should be selected or not on non IE browser.
27624		 * @return {Element} Selected element the same element as the one that got passed in.
27625		 * @example
27626		 * // Select the first paragraph in the active editor
27627		 * tinymce.activeEditor.selection.select(tinymce.activeEditor.dom.select('p')[0]);
27628		 */
27629		select: function(node, content) {
27630			var self = this, dom = self.dom, rng = dom.createRng(), idx;
27631
27632			// Clear stored range set by FocusManager
27633			self.lastFocusBookmark = null;
27634
27635			if (node) {
27636				if (!content && self.controlSelection.controlSelect(node)) {
27637					return;
27638				}
27639
27640				idx = dom.nodeIndex(node);
27641				rng.setStart(node.parentNode, idx);
27642				rng.setEnd(node.parentNode, idx + 1);
27643
27644				// Find first/last text node or BR element
27645				if (content) {
27646					self._moveEndPoint(rng, node, true);
27647					self._moveEndPoint(rng, node);
27648				}
27649
27650				self.setRng(rng);
27651			}
27652
27653			return node;
27654		},
27655
27656		/**
27657		 * Returns true/false if the selection range is collapsed or not. Collapsed means if it's a caret or a larger selection.
27658		 *
27659		 * @method isCollapsed
27660		 * @return {Boolean} true/false state if the selection range is collapsed or not.
27661		 * Collapsed means if it's a caret or a larger selection.
27662		 */
27663		isCollapsed: function() {
27664			var self = this, rng = self.getRng(), sel = self.getSel();
27665
27666			if (!rng || rng.item) {
27667				return false;
27668			}
27669
27670			if (rng.compareEndPoints) {
27671				return rng.compareEndPoints('StartToEnd', rng) === 0;
27672			}
27673
27674			return !sel || rng.collapsed;
27675		},
27676
27677		/**
27678		 * Collapse the selection to start or end of range.
27679		 *
27680		 * @method collapse
27681		 * @param {Boolean} toStart Optional boolean state if to collapse to end or not. Defaults to false.
27682		 */
27683		collapse: function(toStart) {
27684			var self = this, rng = self.getRng(), node;
27685
27686			// Control range on IE
27687			if (rng.item) {
27688				node = rng.item(0);
27689				rng = self.win.document.body.createTextRange();
27690				rng.moveToElementText(node);
27691			}
27692
27693			rng.collapse(!!toStart);
27694			self.setRng(rng);
27695		},
27696
27697		/**
27698		 * Returns the browsers internal selection object.
27699		 *
27700		 * @method getSel
27701		 * @return {Selection} Internal browser selection object.
27702		 */
27703		getSel: function() {
27704			var win = this.win;
27705
27706			return win.getSelection ? win.getSelection() : win.document.selection;
27707		},
27708
27709		/**
27710		 * Returns the browsers internal range object.
27711		 *
27712		 * @method getRng
27713		 * @param {Boolean} w3c Forces a compatible W3C range on IE.
27714		 * @return {Range} Internal browser range object.
27715		 * @see http://www.quirksmode.org/dom/range_intro.html
27716		 * @see http://www.dotvoid.com/2001/03/using-the-range-object-in-mozilla/
27717		 */
27718		getRng: function(w3c) {
27719			var self = this, selection, rng, elm, doc, ieRng, evt;
27720
27721			function tryCompareBoundaryPoints(how, sourceRange, destinationRange) {
27722				try {
27723					return sourceRange.compareBoundaryPoints(how, destinationRange);
27724				} catch (ex) {
27725					// Gecko throws wrong document exception if the range points
27726					// to nodes that where removed from the dom #6690
27727					// Browsers should mutate existing DOMRange instances so that they always point
27728					// to something in the document this is not the case in Gecko works fine in IE/WebKit/Blink
27729					// For performance reasons just return -1
27730					return -1;
27731				}
27732			}
27733
27734			if (!self.win) {
27735				return null;
27736			}
27737
27738			doc = self.win.document;
27739
27740			// Use last rng passed from FocusManager if it's available this enables
27741			// calls to editor.selection.getStart() to work when caret focus is lost on IE
27742			if (!w3c && self.lastFocusBookmark) {
27743				var bookmark = self.lastFocusBookmark;
27744
27745				// Convert bookmark to range IE 11 fix
27746				if (bookmark.startContainer) {
27747					rng = doc.createRange();
27748					rng.setStart(bookmark.startContainer, bookmark.startOffset);
27749					rng.setEnd(bookmark.endContainer, bookmark.endOffset);
27750				} else {
27751					rng = bookmark;
27752				}
27753
27754				return rng;
27755			}
27756
27757			// Found tridentSel object then we need to use that one
27758			if (w3c && self.tridentSel) {
27759				return self.tridentSel.getRangeAt(0);
27760			}
27761
27762			try {
27763				if ((selection = self.getSel())) {
27764					if (selection.rangeCount > 0) {
27765						rng = selection.getRangeAt(0);
27766					} else {
27767						rng = selection.createRange ? selection.createRange() : doc.createRange();
27768					}
27769				}
27770			} catch (ex) {
27771				// IE throws unspecified error here if TinyMCE is placed in a frame/iframe
27772			}
27773
27774			evt = self.editor.fire('GetSelectionRange', {range: rng});
27775			if (evt.range !== rng) {
27776				return evt.range;
27777			}
27778
27779			// We have W3C ranges and it's IE then fake control selection since IE9 doesn't handle that correctly yet
27780			// IE 11 doesn't support the selection object so we check for that as well
27781			if (isIE && rng && rng.setStart && doc.selection) {
27782				try {
27783					// IE will sometimes throw an exception here
27784					ieRng = doc.selection.createRange();
27785				} catch (ex) {
27786					// Ignore
27787				}
27788
27789				if (ieRng && ieRng.item) {
27790					elm = ieRng.item(0);
27791					rng = doc.createRange();
27792					rng.setStartBefore(elm);
27793					rng.setEndAfter(elm);
27794				}
27795			}
27796
27797			// No range found then create an empty one
27798			// This can occur when the editor is placed in a hidden container element on Gecko
27799			// Or on IE when there was an exception
27800			if (!rng) {
27801				rng = doc.createRange ? doc.createRange() : doc.body.createTextRange();
27802			}
27803
27804			// If range is at start of document then move it to start of body
27805			if (rng.setStart && rng.startContainer.nodeType === 9 && rng.collapsed) {
27806				elm = self.dom.getRoot();
27807				rng.setStart(elm, 0);
27808				rng.setEnd(elm, 0);
27809			}
27810
27811			if (self.selectedRange && self.explicitRange) {
27812				if (tryCompareBoundaryPoints(rng.START_TO_START, rng, self.selectedRange) === 0 &&
27813					tryCompareBoundaryPoints(rng.END_TO_END, rng, self.selectedRange) === 0) {
27814					// Safari, Opera and Chrome only ever select text which causes the range to change.
27815					// This lets us use the originally set range if the selection hasn't been changed by the user.
27816					rng = self.explicitRange;
27817				} else {
27818					self.selectedRange = null;
27819					self.explicitRange = null;
27820				}
27821			}
27822
27823			return rng;
27824		},
27825
27826		/**
27827		 * Changes the selection to the specified DOM range.
27828		 *
27829		 * @method setRng
27830		 * @param {Range} rng Range to select.
27831		 * @param {Boolean} forward Optional boolean if the selection is forwards or backwards.
27832		 */
27833		setRng: function(rng, forward) {
27834			var self = this, sel, node, evt;
27835
27836			if (!rng) {
27837				return;
27838			}
27839
27840			// Is IE specific range
27841			if (rng.select) {
27842				self.explicitRange = null;
27843
27844				try {
27845					rng.select();
27846				} catch (ex) {
27847					// Needed for some odd IE bug #1843306
27848				}
27849
27850				return;
27851			}
27852
27853			if (!self.tridentSel) {
27854				sel = self.getSel();
27855
27856				evt = self.editor.fire('SetSelectionRange', {range: rng});
27857				rng = evt.range;
27858
27859				if (sel) {
27860					self.explicitRange = rng;
27861
27862					try {
27863						sel.removeAllRanges();
27864						sel.addRange(rng);
27865					} catch (ex) {
27866						// IE might throw errors here if the editor is within a hidden container and selection is changed
27867					}
27868
27869					// Forward is set to false and we have an extend function
27870					if (forward === false && sel.extend) {
27871						sel.collapse(rng.endContainer, rng.endOffset);
27872						sel.extend(rng.startContainer, rng.startOffset);
27873					}
27874
27875					// adding range isn't always successful so we need to check range count otherwise an exception can occur
27876					self.selectedRange = sel.rangeCount > 0 ? sel.getRangeAt(0) : null;
27877				}
27878
27879				// WebKit egde case selecting images works better using setBaseAndExtent
27880				if (!rng.collapsed && rng.startContainer == rng.endContainer && sel.setBaseAndExtent && !Env.ie) {
27881					if (rng.endOffset - rng.startOffset < 2) {
27882						if (rng.startContainer.hasChildNodes()) {
27883							node = rng.startContainer.childNodes[rng.startOffset];
27884							if (node && node.tagName == 'IMG') {
27885								self.getSel().setBaseAndExtent(node, 0, node, 1);
27886							}
27887						}
27888					}
27889				}
27890			} else {
27891				// Is W3C Range fake range on IE
27892				if (rng.cloneRange) {
27893					try {
27894						self.tridentSel.addRange(rng);
27895					} catch (ex) {
27896						//IE9 throws an error here if called before selection is placed in the editor
27897					}
27898				}
27899			}
27900		},
27901
27902		/**
27903		 * Sets the current selection to the specified DOM element.
27904		 *
27905		 * @method setNode
27906		 * @param {Element} elm Element to set as the contents of the selection.
27907		 * @return {Element} Returns the element that got passed in.
27908		 * @example
27909		 * // Inserts a DOM node at current selection/caret location
27910		 * tinymce.activeEditor.selection.setNode(tinymce.activeEditor.dom.create('img', {src: 'some.gif', title: 'some title'}));
27911		 */
27912		setNode: function(elm) {
27913			var self = this;
27914
27915			self.setContent(self.dom.getOuterHTML(elm));
27916
27917			return elm;
27918		},
27919
27920		/**
27921		 * Returns the currently selected element or the common ancestor element for both start and end of the selection.
27922		 *
27923		 * @method getNode
27924		 * @return {Element} Currently selected element or common ancestor element.
27925		 * @example
27926		 * // Alerts the currently selected elements node name
27927		 * alert(tinymce.activeEditor.selection.getNode().nodeName);
27928		 */
27929		getNode: function() {
27930			var self = this, rng = self.getRng(), elm;
27931			var startContainer = rng.startContainer, endContainer = rng.endContainer;
27932			var startOffset = rng.startOffset, endOffset = rng.endOffset, root = self.dom.getRoot();
27933
27934			function skipEmptyTextNodes(node, forwards) {
27935				var orig = node;
27936
27937				while (node && node.nodeType === 3 && node.length === 0) {
27938					node = forwards ? node.nextSibling : node.previousSibling;
27939				}
27940
27941				return node || orig;
27942			}
27943
27944			// Range maybe lost after the editor is made visible again
27945			if (!rng) {
27946				return root;
27947			}
27948
27949			if (rng.setStart) {
27950				elm = rng.commonAncestorContainer;
27951
27952				// Handle selection a image or other control like element such as anchors
27953				if (!rng.collapsed) {
27954					if (startContainer == endContainer) {
27955						if (endOffset - startOffset < 2) {
27956							if (startContainer.hasChildNodes()) {
27957								elm = startContainer.childNodes[startOffset];
27958							}
27959						}
27960					}
27961
27962					// If the anchor node is a element instead of a text node then return this element
27963					//if (tinymce.isWebKit && sel.anchorNode && sel.anchorNode.nodeType == 1)
27964					//	return sel.anchorNode.childNodes[sel.anchorOffset];
27965
27966					// Handle cases where the selection is immediately wrapped around a node and return that node instead of it's parent.
27967					// This happens when you double click an underlined word in FireFox.
27968					if (startContainer.nodeType === 3 && endContainer.nodeType === 3) {
27969						if (startContainer.length === startOffset) {
27970							startContainer = skipEmptyTextNodes(startContainer.nextSibling, true);
27971						} else {
27972							startContainer = startContainer.parentNode;
27973						}
27974
27975						if (endOffset === 0) {
27976							endContainer = skipEmptyTextNodes(endContainer.previousSibling, false);
27977						} else {
27978							endContainer = endContainer.parentNode;
27979						}
27980
27981						if (startContainer && startContainer === endContainer) {
27982							return startContainer;
27983						}
27984					}
27985				}
27986
27987				if (elm && elm.nodeType == 3) {
27988					return elm.parentNode;
27989				}
27990
27991				return elm;
27992			}
27993
27994			elm = rng.item ? rng.item(0) : rng.parentElement();
27995
27996			// IE 7 might return elements outside the iframe
27997			if (elm.ownerDocument !== self.win.document) {
27998				elm = root;
27999			}
28000
28001			return elm;
28002		},
28003
28004		getSelectedBlocks: function(startElm, endElm) {
28005			var self = this, dom = self.dom, node, root, selectedBlocks = [];
28006
28007			root = dom.getRoot();
28008			startElm = dom.getParent(startElm || self.getStart(), dom.isBlock);
28009			endElm = dom.getParent(endElm || self.getEnd(), dom.isBlock);
28010
28011			if (startElm && startElm != root) {
28012				selectedBlocks.push(startElm);
28013			}
28014
28015			if (startElm && endElm && startElm != endElm) {
28016				node = startElm;
28017
28018				var walker = new TreeWalker(startElm, root);
28019				while ((node = walker.next()) && node != endElm) {
28020					if (dom.isBlock(node)) {
28021						selectedBlocks.push(node);
28022					}
28023				}
28024			}
28025
28026			if (endElm && startElm != endElm && endElm != root) {
28027				selectedBlocks.push(endElm);
28028			}
28029
28030			return selectedBlocks;
28031		},
28032
28033		isForward: function() {
28034			var dom = this.dom, sel = this.getSel(), anchorRange, focusRange;
28035
28036			// No support for selection direction then always return true
28037			if (!sel || !sel.anchorNode || !sel.focusNode) {
28038				return true;
28039			}
28040
28041			anchorRange = dom.createRng();
28042			anchorRange.setStart(sel.anchorNode, sel.anchorOffset);
28043			anchorRange.collapse(true);
28044
28045			focusRange = dom.createRng();
28046			focusRange.setStart(sel.focusNode, sel.focusOffset);
28047			focusRange.collapse(true);
28048
28049			return anchorRange.compareBoundaryPoints(anchorRange.START_TO_START, focusRange) <= 0;
28050		},
28051
28052		normalize: function() {
28053			var self = this, rng = self.getRng();
28054
28055			if (Env.range && new RangeUtils(self.dom).normalize(rng)) {
28056				self.setRng(rng, self.isForward());
28057			}
28058
28059			return rng;
28060		},
28061
28062		/**
28063		 * Executes callback when the current selection starts/stops matching the specified selector. The current
28064		 * state will be passed to the callback as it's first argument.
28065		 *
28066		 * @method selectorChanged
28067		 * @param {String} selector CSS selector to check for.
28068		 * @param {function} callback Callback with state and args when the selector is matches or not.
28069		 */
28070		selectorChanged: function(selector, callback) {
28071			var self = this, currentSelectors;
28072
28073			if (!self.selectorChangedData) {
28074				self.selectorChangedData = {};
28075				currentSelectors = {};
28076
28077				self.editor.on('NodeChange', function(e) {
28078					var node = e.element, dom = self.dom, parents = dom.getParents(node, null, dom.getRoot()), matchedSelectors = {};
28079
28080					// Check for new matching selectors
28081					each(self.selectorChangedData, function(callbacks, selector) {
28082						each(parents, function(node) {
28083							if (dom.is(node, selector)) {
28084								if (!currentSelectors[selector]) {
28085									// Execute callbacks
28086									each(callbacks, function(callback) {
28087										callback(true, {node: node, selector: selector, parents: parents});
28088									});
28089
28090									currentSelectors[selector] = callbacks;
28091								}
28092
28093								matchedSelectors[selector] = callbacks;
28094								return false;
28095							}
28096						});
28097					});
28098
28099					// Check if current selectors still match
28100					each(currentSelectors, function(callbacks, selector) {
28101						if (!matchedSelectors[selector]) {
28102							delete currentSelectors[selector];
28103
28104							each(callbacks, function(callback) {
28105								callback(false, {node: node, selector: selector, parents: parents});
28106							});
28107						}
28108					});
28109				});
28110			}
28111
28112			// Add selector listeners
28113			if (!self.selectorChangedData[selector]) {
28114				self.selectorChangedData[selector] = [];
28115			}
28116
28117			self.selectorChangedData[selector].push(callback);
28118
28119			return self;
28120		},
28121
28122		getScrollContainer: function() {
28123			var scrollContainer, node = this.dom.getRoot();
28124
28125			while (node && node.nodeName != 'BODY') {
28126				if (node.scrollHeight > node.clientHeight) {
28127					scrollContainer = node;
28128					break;
28129				}
28130
28131				node = node.parentNode;
28132			}
28133
28134			return scrollContainer;
28135		},
28136
28137		scrollIntoView: function(elm, alignToTop) {
28138			var y, viewPort, self = this, dom = self.dom, root = dom.getRoot(), viewPortY, viewPortH, offsetY = 0;
28139
28140			function getPos(elm) {
28141				var x = 0, y = 0;
28142
28143				var offsetParent = elm;
28144				while (offsetParent && offsetParent.nodeType) {
28145					x += offsetParent.offsetLeft || 0;
28146					y += offsetParent.offsetTop || 0;
28147					offsetParent = offsetParent.offsetParent;
28148				}
28149
28150				return {x: x, y: y};
28151			}
28152
28153			if (!NodeType.isElement(elm)) {
28154				return;
28155			}
28156
28157			if (alignToTop === false) {
28158				offsetY = elm.offsetHeight;
28159			}
28160
28161			if (root.nodeName != 'BODY') {
28162				var scrollContainer = self.getScrollContainer();
28163				if (scrollContainer) {
28164					y = getPos(elm).y - getPos(scrollContainer).y + offsetY;
28165					viewPortH = scrollContainer.clientHeight;
28166					viewPortY = scrollContainer.scrollTop;
28167					if (y < viewPortY || y + 25 > viewPortY + viewPortH) {
28168						scrollContainer.scrollTop = y < viewPortY ? y : y - viewPortH + 25;
28169					}
28170
28171					return;
28172				}
28173			}
28174
28175			viewPort = dom.getViewPort(self.editor.getWin());
28176			y = dom.getPos(elm).y + offsetY;
28177			viewPortY = viewPort.y;
28178			viewPortH = viewPort.h;
28179			if (y < viewPort.y || y + 25 > viewPortY + viewPortH) {
28180				self.editor.getWin().scrollTo(0, y < viewPortY ? y : y - viewPortH + 25);
28181			}
28182		},
28183
28184		placeCaretAt: function(clientX, clientY) {
28185			this.setRng(RangeUtils.getCaretRangeFromPoint(clientX, clientY, this.editor.getDoc()));
28186		},
28187
28188		_moveEndPoint: function(rng, node, start) {
28189			var root = node, walker = new TreeWalker(node, root);
28190			var nonEmptyElementsMap = this.dom.schema.getNonEmptyElements();
28191
28192			do {
28193				// Text node
28194				if (node.nodeType == 3 && trim(node.nodeValue).length !== 0) {
28195					if (start) {
28196						rng.setStart(node, 0);
28197					} else {
28198						rng.setEnd(node, node.nodeValue.length);
28199					}
28200
28201					return;
28202				}
28203
28204				// BR/IMG/INPUT elements but not table cells
28205				if (nonEmptyElementsMap[node.nodeName] && !/^(TD|TH)$/.test(node.nodeName)) {
28206					if (start) {
28207						rng.setStartBefore(node);
28208					} else {
28209						if (node.nodeName == 'BR') {
28210							rng.setEndBefore(node);
28211						} else {
28212							rng.setEndAfter(node);
28213						}
28214					}
28215
28216					return;
28217				}
28218
28219				// Found empty text block old IE can place the selection inside those
28220				if (Env.ie && Env.ie < 11 && this.dom.isBlock(node) && this.dom.isEmpty(node)) {
28221					if (start) {
28222						rng.setStart(node, 0);
28223					} else {
28224						rng.setEnd(node, 0);
28225					}
28226
28227					return;
28228				}
28229			} while ((node = (start ? walker.next() : walker.prev())));
28230
28231			// Failed to find any text node or other suitable location then move to the root of body
28232			if (root.nodeName == 'BODY') {
28233				if (start) {
28234					rng.setStart(root, 0);
28235				} else {
28236					rng.setEnd(root, root.childNodes.length);
28237				}
28238			}
28239		},
28240
28241		destroy: function() {
28242			this.win = null;
28243			this.controlSelection.destroy();
28244		}
28245	};
28246
28247	return Selection;
28248});
28249
28250// Included from: js/tinymce/classes/dom/ElementUtils.js
28251
28252/**
28253 * ElementUtils.js
28254 *
28255 * Released under LGPL License.
28256 * Copyright (c) 1999-2015 Ephox Corp. All rights reserved
28257 *
28258 * License: http://www.tinymce.com/license
28259 * Contributing: http://www.tinymce.com/contributing
28260 */
28261
28262/**
28263 * Utility class for various element specific functions.
28264 *
28265 * @private
28266 * @class tinymce.dom.ElementUtils
28267 */
28268define("tinymce/dom/ElementUtils", [
28269	"tinymce/dom/BookmarkManager",
28270	"tinymce/util/Tools"
28271], function(BookmarkManager, Tools) {
28272	var each = Tools.each;
28273
28274	function ElementUtils(dom) {
28275		/**
28276		 * Compares two nodes and checks if it's attributes and styles matches.
28277		 * This doesn't compare classes as items since their order is significant.
28278		 *
28279		 * @method compare
28280		 * @param {Node} node1 First node to compare with.
28281		 * @param {Node} node2 Second node to compare with.
28282		 * @return {boolean} True/false if the nodes are the same or not.
28283		 */
28284		this.compare = function(node1, node2) {
28285			// Not the same name
28286			if (node1.nodeName != node2.nodeName) {
28287				return false;
28288			}
28289
28290			/**
28291			 * Returns all the nodes attributes excluding internal ones, styles and classes.
28292			 *
28293			 * @private
28294			 * @param {Node} node Node to get attributes from.
28295			 * @return {Object} Name/value object with attributes and attribute values.
28296			 */
28297			function getAttribs(node) {
28298				var attribs = {};
28299
28300				each(dom.getAttribs(node), function(attr) {
28301					var name = attr.nodeName.toLowerCase();
28302
28303					// Don't compare internal attributes or style
28304					if (name.indexOf('_') !== 0 && name !== 'style' && name !== 'data-mce-style') {
28305						attribs[name] = dom.getAttrib(node, name);
28306					}
28307				});
28308
28309				return attribs;
28310			}
28311
28312			/**
28313			 * Compares two objects checks if it's key + value exists in the other one.
28314			 *
28315			 * @private
28316			 * @param {Object} obj1 First object to compare.
28317			 * @param {Object} obj2 Second object to compare.
28318			 * @return {boolean} True/false if the objects matches or not.
28319			 */
28320			function compareObjects(obj1, obj2) {
28321				var value, name;
28322
28323				for (name in obj1) {
28324					// Obj1 has item obj2 doesn't have
28325					if (obj1.hasOwnProperty(name)) {
28326						value = obj2[name];
28327
28328						// Obj2 doesn't have obj1 item
28329						if (typeof value == "undefined") {
28330							return false;
28331						}
28332
28333						// Obj2 item has a different value
28334						if (obj1[name] != value) {
28335							return false;
28336						}
28337
28338						// Delete similar value
28339						delete obj2[name];
28340					}
28341				}
28342
28343				// Check if obj 2 has something obj 1 doesn't have
28344				for (name in obj2) {
28345					// Obj2 has item obj1 doesn't have
28346					if (obj2.hasOwnProperty(name)) {
28347						return false;
28348					}
28349				}
28350
28351				return true;
28352			}
28353
28354			// Attribs are not the same
28355			if (!compareObjects(getAttribs(node1), getAttribs(node2))) {
28356				return false;
28357			}
28358
28359			// Styles are not the same
28360			if (!compareObjects(dom.parseStyle(dom.getAttrib(node1, 'style')), dom.parseStyle(dom.getAttrib(node2, 'style')))) {
28361				return false;
28362			}
28363
28364			return !BookmarkManager.isBookmarkNode(node1) && !BookmarkManager.isBookmarkNode(node2);
28365		};
28366	}
28367
28368	return ElementUtils;
28369});
28370
28371// Included from: js/tinymce/classes/fmt/Preview.js
28372
28373/**
28374 * Preview.js
28375 *
28376 * Released under LGPL License.
28377 * Copyright (c) 1999-2015 Ephox Corp. All rights reserved
28378 *
28379 * License: http://www.tinymce.com/license
28380 * Contributing: http://www.tinymce.com/contributing
28381 */
28382
28383/**
28384 * Internal class for generating previews styles for formats.
28385 *
28386 * Example:
28387 *  Preview.getCssText(editor, 'bold');
28388 *
28389 * @private
28390 * @class tinymce.fmt.Preview
28391 */
28392define("tinymce/fmt/Preview", [
28393	"tinymce/util/Tools"
28394], function(Tools) {
28395	var each = Tools.each;
28396
28397	function getCssText(editor, format) {
28398		var name, previewElm, dom = editor.dom;
28399		var previewCss = '', parentFontSize, previewStyles;
28400
28401		previewStyles = editor.settings.preview_styles;
28402
28403		// No preview forced
28404		if (previewStyles === false) {
28405			return '';
28406		}
28407
28408		// Default preview
28409		if (!previewStyles) {
28410			previewStyles = 'font-family font-size font-weight font-style text-decoration ' +
28411				'text-transform color background-color border border-radius outline text-shadow';
28412		}
28413
28414		// Removes any variables since these can't be previewed
28415		function removeVars(val) {
28416			return val.replace(/%(\w+)/g, '');
28417		}
28418
28419		// Create block/inline element to use for preview
28420		if (typeof format == "string") {
28421			format = editor.formatter.get(format);
28422			if (!format) {
28423				return;
28424			}
28425
28426			format = format[0];
28427		}
28428
28429		name = format.block || format.inline || 'span';
28430		previewElm = dom.create(name);
28431
28432		// Add format styles to preview element
28433		each(format.styles, function(value, name) {
28434			value = removeVars(value);
28435
28436			if (value) {
28437				dom.setStyle(previewElm, name, value);
28438			}
28439		});
28440
28441		// Add attributes to preview element
28442		each(format.attributes, function(value, name) {
28443			value = removeVars(value);
28444
28445			if (value) {
28446				dom.setAttrib(previewElm, name, value);
28447			}
28448		});
28449
28450		// Add classes to preview element
28451		each(format.classes, function(value) {
28452			value = removeVars(value);
28453
28454			if (!dom.hasClass(previewElm, value)) {
28455				dom.addClass(previewElm, value);
28456			}
28457		});
28458
28459		editor.fire('PreviewFormats');
28460
28461		// Add the previewElm outside the visual area
28462		dom.setStyles(previewElm, {position: 'absolute', left: -0xFFFF});
28463		editor.getBody().appendChild(previewElm);
28464
28465		// Get parent container font size so we can compute px values out of em/% for older IE:s
28466		parentFontSize = dom.getStyle(editor.getBody(), 'fontSize', true);
28467		parentFontSize = /px$/.test(parentFontSize) ? parseInt(parentFontSize, 10) : 0;
28468
28469		each(previewStyles.split(' '), function(name) {
28470			var value = dom.getStyle(previewElm, name, true);
28471
28472			// If background is transparent then check if the body has a background color we can use
28473			if (name == 'background-color' && /transparent|rgba\s*\([^)]+,\s*0\)/.test(value)) {
28474				value = dom.getStyle(editor.getBody(), name, true);
28475
28476				// Ignore white since it's the default color, not the nicest fix
28477				// TODO: Fix this by detecting runtime style
28478				if (dom.toHex(value).toLowerCase() == '#ffffff') {
28479					return;
28480				}
28481			}
28482
28483			if (name == 'color') {
28484				// Ignore black since it's the default color, not the nicest fix
28485				// TODO: Fix this by detecting runtime style
28486				if (dom.toHex(value).toLowerCase() == '#000000') {
28487					return;
28488				}
28489			}
28490
28491			// Old IE won't calculate the font size so we need to do that manually
28492			if (name == 'font-size') {
28493				if (/em|%$/.test(value)) {
28494					if (parentFontSize === 0) {
28495						return;
28496					}
28497
28498					// Convert font size from em/% to px
28499					value = parseFloat(value, 10) / (/%$/.test(value) ? 100 : 1);
28500					value = (value * parentFontSize) + 'px';
28501				}
28502			}
28503
28504			if (name == "border" && value) {
28505				previewCss += 'padding:0 2px;';
28506			}
28507
28508			previewCss += name + ':' + value + ';';
28509		});
28510
28511		editor.fire('AfterPreviewFormats');
28512
28513		//previewCss += 'line-height:normal';
28514
28515		dom.remove(previewElm);
28516
28517		return previewCss;
28518	}
28519
28520	return {
28521		getCssText: getCssText
28522	};
28523});
28524
28525// Included from: js/tinymce/classes/Formatter.js
28526
28527/**
28528 * Formatter.js
28529 *
28530 * Released under LGPL License.
28531 * Copyright (c) 1999-2015 Ephox Corp. All rights reserved
28532 *
28533 * License: http://www.tinymce.com/license
28534 * Contributing: http://www.tinymce.com/contributing
28535 */
28536
28537/**
28538 * Text formatter engine class. This class is used to apply formats like bold, italic, font size
28539 * etc to the current selection or specific nodes. This engine was built to replace the browser's
28540 * default formatting logic for execCommand due to its inconsistent and buggy behavior.
28541 *
28542 * @class tinymce.Formatter
28543 * @example
28544 *  tinymce.activeEditor.formatter.register('mycustomformat', {
28545 *    inline: 'span',
28546 *    styles: {color: '#ff0000'}
28547 *  });
28548 *
28549 *  tinymce.activeEditor.formatter.apply('mycustomformat');
28550 */
28551define("tinymce/Formatter", [
28552	"tinymce/dom/TreeWalker",
28553	"tinymce/dom/RangeUtils",
28554	"tinymce/dom/BookmarkManager",
28555	"tinymce/dom/ElementUtils",
28556	"tinymce/util/Tools",
28557	"tinymce/fmt/Preview"
28558], function(TreeWalker, RangeUtils, BookmarkManager, ElementUtils, Tools, Preview) {
28559	/**
28560	 * Constructs a new formatter instance.
28561	 *
28562	 * @constructor Formatter
28563	 * @param {tinymce.Editor} ed Editor instance to construct the formatter engine to.
28564	 */
28565	return function(ed) {
28566		var formats = {},
28567			dom = ed.dom,
28568			selection = ed.selection,
28569			rangeUtils = new RangeUtils(dom),
28570			isValid = ed.schema.isValidChild,
28571			isBlock = dom.isBlock,
28572			forcedRootBlock = ed.settings.forced_root_block,
28573			nodeIndex = dom.nodeIndex,
28574			INVISIBLE_CHAR = '\uFEFF',
28575			MCE_ATTR_RE = /^(src|href|style)$/,
28576			FALSE = false,
28577			TRUE = true,
28578			formatChangeData,
28579			undef,
28580			getContentEditable = dom.getContentEditable,
28581			disableCaretContainer,
28582			markCaretContainersBogus,
28583			isBookmarkNode = BookmarkManager.isBookmarkNode;
28584
28585		var each = Tools.each,
28586			grep = Tools.grep,
28587			walk = Tools.walk,
28588			extend = Tools.extend;
28589
28590		function isTextBlock(name) {
28591			if (name.nodeType) {
28592				name = name.nodeName;
28593			}
28594
28595			return !!ed.schema.getTextBlockElements()[name.toLowerCase()];
28596		}
28597
28598		function isTableCell(node) {
28599			return /^(TH|TD)$/.test(node.nodeName);
28600		}
28601
28602		function isInlineBlock(node) {
28603			return node && /^(IMG)$/.test(node.nodeName);
28604		}
28605
28606		function getParents(node, selector) {
28607			return dom.getParents(node, selector, dom.getRoot());
28608		}
28609
28610		function isCaretNode(node) {
28611			return node.nodeType === 1 && node.id === '_mce_caret';
28612		}
28613
28614		function defaultFormats() {
28615			register({
28616				valigntop: [
28617					{selector: 'td,th', styles: {'verticalAlign': 'top'}}
28618				],
28619
28620				valignmiddle: [
28621					{selector: 'td,th', styles: {'verticalAlign': 'middle'}}
28622				],
28623
28624				valignbottom: [
28625					{selector: 'td,th', styles: {'verticalAlign': 'bottom'}}
28626				],
28627
28628				alignleft: [
28629					{selector: 'figure.image', collapsed: false, classes: 'align-left', ceFalseOverride: true},
28630					{selector: 'figure,p,h1,h2,h3,h4,h5,h6,td,th,tr,div,ul,ol,li', styles: {textAlign: 'left'}, defaultBlock: 'div'},
28631					{selector: 'img,table', collapsed: false, styles: {'float': 'left'}}
28632				],
28633
28634				aligncenter: [
28635					{selector: 'figure,p,h1,h2,h3,h4,h5,h6,td,th,tr,div,ul,ol,li', styles: {textAlign: 'center'}, defaultBlock: 'div'},
28636					{selector: 'figure.image', collapsed: false, classes: 'align-center', ceFalseOverride: true},
28637					{selector: 'img', collapsed: false, styles: {display: 'block', marginLeft: 'auto', marginRight: 'auto'}},
28638					{selector: 'table', collapsed: false, styles: {marginLeft: 'auto', marginRight: 'auto'}}
28639				],
28640
28641				alignright: [
28642					{selector: 'figure.image', collapsed: false, classes: 'align-right', ceFalseOverride: true},
28643					{selector: 'figure,p,h1,h2,h3,h4,h5,h6,td,th,tr,div,ul,ol,li', styles: {textAlign: 'right'}, defaultBlock: 'div'},
28644					{selector: 'img,table', collapsed: false, styles: {'float': 'right'}}
28645				],
28646
28647				alignjustify: [
28648					{selector: 'figure,p,h1,h2,h3,h4,h5,h6,td,th,tr,div,ul,ol,li', styles: {textAlign: 'justify'}, defaultBlock: 'div'}
28649				],
28650
28651				bold: [
28652					{inline: 'strong', remove: 'all'},
28653					{inline: 'span', styles: {fontWeight: 'bold'}},
28654					{inline: 'b', remove: 'all'}
28655				],
28656
28657				italic: [
28658					{inline: 'em', remove: 'all'},
28659					{inline: 'span', styles: {fontStyle: 'italic'}},
28660					{inline: 'i', remove: 'all'}
28661				],
28662
28663				underline: [
28664					{inline: 'span', styles: {textDecoration: 'underline'}, exact: true},
28665					{inline: 'u', remove: 'all'}
28666				],
28667
28668				strikethrough: [
28669					{inline: 'span', styles: {textDecoration: 'line-through'}, exact: true},
28670					{inline: 'strike', remove: 'all'}
28671				],
28672
28673				forecolor: {inline: 'span', styles: {color: '%value'}, links: true, remove_similar: true},
28674				hilitecolor: {inline: 'span', styles: {backgroundColor: '%value'}, links: true, remove_similar: true},
28675				fontname: {inline: 'span', styles: {fontFamily: '%value'}},
28676				fontsize: {inline: 'span', styles: {fontSize: '%value'}},
28677				fontsize_class: {inline: 'span', attributes: {'class': '%value'}},
28678				blockquote: {block: 'blockquote', wrapper: 1, remove: 'all'},
28679				subscript: {inline: 'sub'},
28680				superscript: {inline: 'sup'},
28681				code: {inline: 'code'},
28682
28683				link: {inline: 'a', selector: 'a', remove: 'all', split: true, deep: true,
28684					onmatch: function() {
28685						return true;
28686					},
28687
28688					onformat: function(elm, fmt, vars) {
28689						each(vars, function(value, key) {
28690							dom.setAttrib(elm, key, value);
28691						});
28692					}
28693				},
28694
28695				removeformat: [
28696					{
28697						selector: 'b,strong,em,i,font,u,strike,sub,sup,dfn,code,samp,kbd,var,cite,mark,q,del,ins',
28698						remove: 'all',
28699						split: true,
28700						expand: false,
28701						block_expand: true,
28702						deep: true
28703					},
28704					{selector: 'span', attributes: ['style', 'class'], remove: 'empty', split: true, expand: false, deep: true},
28705					{selector: '*', attributes: ['style', 'class'], split: false, expand: false, deep: true}
28706				]
28707			});
28708
28709			// Register default block formats
28710			each('p h1 h2 h3 h4 h5 h6 div address pre div dt dd samp'.split(/\s/), function(name) {
28711				register(name, {block: name, remove: 'all'});
28712			});
28713
28714			// Register user defined formats
28715			register(ed.settings.formats);
28716		}
28717
28718		function addKeyboardShortcuts() {
28719			// Add some inline shortcuts
28720			ed.addShortcut('meta+b', 'bold_desc', 'Bold');
28721			ed.addShortcut('meta+i', 'italic_desc', 'Italic');
28722			ed.addShortcut('meta+u', 'underline_desc', 'Underline');
28723
28724			// BlockFormat shortcuts keys
28725			for (var i = 1; i <= 6; i++) {
28726				ed.addShortcut('access+' + i, '', ['FormatBlock', false, 'h' + i]);
28727			}
28728
28729			ed.addShortcut('access+7', '', ['FormatBlock', false, 'p']);
28730			ed.addShortcut('access+8', '', ['FormatBlock', false, 'div']);
28731			ed.addShortcut('access+9', '', ['FormatBlock', false, 'address']);
28732		}
28733
28734		// Public functions
28735
28736		/**
28737		 * Returns the format by name or all formats if no name is specified.
28738		 *
28739		 * @method get
28740		 * @param {String} name Optional name to retrieve by.
28741		 * @return {Array/Object} Array/Object with all registered formats or a specific format.
28742		 */
28743		function get(name) {
28744			return name ? formats[name] : formats;
28745		}
28746
28747		/**
28748		 * Registers a specific format by name.
28749		 *
28750		 * @method register
28751		 * @param {Object/String} name Name of the format for example "bold".
28752		 * @param {Object/Array} format Optional format object or array of format variants
28753		 * can only be omitted if the first arg is an object.
28754		 */
28755		function register(name, format) {
28756			if (name) {
28757				if (typeof name !== 'string') {
28758					each(name, function(format, name) {
28759						register(name, format);
28760					});
28761				} else {
28762					// Force format into array and add it to internal collection
28763					format = format.length ? format : [format];
28764
28765					each(format, function(format) {
28766						// Set deep to false by default on selector formats this to avoid removing
28767						// alignment on images inside paragraphs when alignment is changed on paragraphs
28768						if (format.deep === undef) {
28769							format.deep = !format.selector;
28770						}
28771
28772						// Default to true
28773						if (format.split === undef) {
28774							format.split = !format.selector || format.inline;
28775						}
28776
28777						// Default to true
28778						if (format.remove === undef && format.selector && !format.inline) {
28779							format.remove = 'none';
28780						}
28781
28782						// Mark format as a mixed format inline + block level
28783						if (format.selector && format.inline) {
28784							format.mixed = true;
28785							format.block_expand = true;
28786						}
28787
28788						// Split classes if needed
28789						if (typeof format.classes === 'string') {
28790							format.classes = format.classes.split(/\s+/);
28791						}
28792					});
28793
28794					formats[name] = format;
28795				}
28796			}
28797		}
28798
28799		/**
28800		 * Unregister a specific format by name.
28801		 *
28802		 * @method unregister
28803		 * @param {String} name Name of the format for example "bold".
28804		 */
28805		function unregister(name) {
28806			if (name && formats[name]) {
28807				delete formats[name];
28808			}
28809
28810			return formats;
28811		}
28812
28813		function getTextDecoration(node) {
28814			var decoration;
28815
28816			ed.dom.getParent(node, function(n) {
28817				decoration = ed.dom.getStyle(n, 'text-decoration');
28818				return decoration && decoration !== 'none';
28819			});
28820
28821			return decoration;
28822		}
28823
28824		function processUnderlineAndColor(node) {
28825			var textDecoration;
28826			if (node.nodeType === 1 && node.parentNode && node.parentNode.nodeType === 1) {
28827				textDecoration = getTextDecoration(node.parentNode);
28828				if (ed.dom.getStyle(node, 'color') && textDecoration) {
28829					ed.dom.setStyle(node, 'text-decoration', textDecoration);
28830				} else if (ed.dom.getStyle(node, 'text-decoration') === textDecoration) {
28831					ed.dom.setStyle(node, 'text-decoration', null);
28832				}
28833			}
28834		}
28835
28836		/**
28837		 * Applies the specified format to the current selection or specified node.
28838		 *
28839		 * @method apply
28840		 * @param {String} name Name of format to apply.
28841		 * @param {Object} vars Optional list of variables to replace within format before applying it.
28842		 * @param {Node} node Optional node to apply the format to defaults to current selection.
28843		 */
28844		function apply(name, vars, node) {
28845			var formatList = get(name), format = formatList[0], bookmark, rng, isCollapsed = !node && selection.isCollapsed();
28846
28847			function setElementFormat(elm, fmt) {
28848				fmt = fmt || format;
28849
28850				if (elm) {
28851					if (fmt.onformat) {
28852						fmt.onformat(elm, fmt, vars, node);
28853					}
28854
28855					each(fmt.styles, function(value, name) {
28856						dom.setStyle(elm, name, replaceVars(value, vars));
28857					});
28858
28859					// Needed for the WebKit span spam bug
28860					// TODO: Remove this once WebKit/Blink fixes this
28861					if (fmt.styles) {
28862						var styleVal = dom.getAttrib(elm, 'style');
28863
28864						if (styleVal) {
28865							elm.setAttribute('data-mce-style', styleVal);
28866						}
28867					}
28868
28869					each(fmt.attributes, function(value, name) {
28870						dom.setAttrib(elm, name, replaceVars(value, vars));
28871					});
28872
28873					each(fmt.classes, function(value) {
28874						value = replaceVars(value, vars);
28875
28876						if (!dom.hasClass(elm, value)) {
28877							dom.addClass(elm, value);
28878						}
28879					});
28880				}
28881			}
28882
28883			function adjustSelectionToVisibleSelection() {
28884				function findSelectionEnd(start, end) {
28885					var walker = new TreeWalker(end);
28886					for (node = walker.current(); node; node = walker.prev()) {
28887						if (node.childNodes.length > 1 || node == start || node.tagName == 'BR') {
28888							return node;
28889						}
28890					}
28891				}
28892
28893				// Adjust selection so that a end container with a end offset of zero is not included in the selection
28894				// as this isn't visible to the user.
28895				var rng = ed.selection.getRng();
28896				var start = rng.startContainer;
28897				var end = rng.endContainer;
28898
28899				if (start != end && rng.endOffset === 0) {
28900					var newEnd = findSelectionEnd(start, end);
28901					var endOffset = newEnd.nodeType == 3 ? newEnd.length : newEnd.childNodes.length;
28902
28903					rng.setEnd(newEnd, endOffset);
28904				}
28905
28906				return rng;
28907			}
28908
28909			function applyRngStyle(rng, bookmark, node_specific) {
28910				var newWrappers = [], wrapName, wrapElm, contentEditable = true;
28911
28912				// Setup wrapper element
28913				wrapName = format.inline || format.block;
28914				wrapElm = dom.create(wrapName);
28915				setElementFormat(wrapElm);
28916
28917				rangeUtils.walk(rng, function(nodes) {
28918					var currentWrapElm;
28919
28920					/**
28921					 * Process a list of nodes wrap them.
28922					 */
28923					function process(node) {
28924						var nodeName, parentName, found, hasContentEditableState, lastContentEditable;
28925
28926						lastContentEditable = contentEditable;
28927						nodeName = node.nodeName.toLowerCase();
28928						parentName = node.parentNode.nodeName.toLowerCase();
28929
28930						// Node has a contentEditable value
28931						if (node.nodeType === 1 && getContentEditable(node)) {
28932							lastContentEditable = contentEditable;
28933							contentEditable = getContentEditable(node) === "true";
28934							hasContentEditableState = true; // We don't want to wrap the container only it's children
28935						}
28936
28937						// Stop wrapping on br elements
28938						if (isEq(nodeName, 'br')) {
28939							currentWrapElm = 0;
28940
28941							// Remove any br elements when we wrap things
28942							if (format.block) {
28943								dom.remove(node);
28944							}
28945
28946							return;
28947						}
28948
28949						// If node is wrapper type
28950						if (format.wrapper && matchNode(node, name, vars)) {
28951							currentWrapElm = 0;
28952							return;
28953						}
28954
28955						// Can we rename the block
28956						// TODO: Break this if up, too complex
28957						if (contentEditable && !hasContentEditableState && format.block &&
28958							!format.wrapper && isTextBlock(nodeName) && isValid(parentName, wrapName)) {
28959							node = dom.rename(node, wrapName);
28960							setElementFormat(node);
28961							newWrappers.push(node);
28962							currentWrapElm = 0;
28963							return;
28964						}
28965
28966						// Handle selector patterns
28967						if (format.selector) {
28968							// Look for matching formats
28969							each(formatList, function(format) {
28970								// Check collapsed state if it exists
28971								if ('collapsed' in format && format.collapsed !== isCollapsed) {
28972									return;
28973								}
28974
28975								if (dom.is(node, format.selector) && !isCaretNode(node)) {
28976									setElementFormat(node, format);
28977									found = true;
28978									return false;
28979								}
28980							});
28981
28982							// Continue processing if a selector match wasn't found and a inline element is defined
28983							if (!format.inline || found) {
28984								currentWrapElm = 0;
28985								return;
28986							}
28987						}
28988
28989						// Is it valid to wrap this item
28990						// TODO: Break this if up, too complex
28991						if (contentEditable && !hasContentEditableState && isValid(wrapName, nodeName) && isValid(parentName, wrapName) &&
28992								!(!node_specific && node.nodeType === 3 &&
28993								node.nodeValue.length === 1 &&
28994								node.nodeValue.charCodeAt(0) === 65279) &&
28995								!isCaretNode(node) &&
28996								(!format.inline || !isBlock(node))) {
28997							// Start wrapping
28998							if (!currentWrapElm) {
28999								// Wrap the node
29000								currentWrapElm = dom.clone(wrapElm, FALSE);
29001								node.parentNode.insertBefore(currentWrapElm, node);
29002								newWrappers.push(currentWrapElm);
29003							}
29004
29005							currentWrapElm.appendChild(node);
29006						} else {
29007							// Start a new wrapper for possible children
29008							currentWrapElm = 0;
29009
29010							each(grep(node.childNodes), process);
29011
29012							if (hasContentEditableState) {
29013								contentEditable = lastContentEditable; // Restore last contentEditable state from stack
29014							}
29015
29016							// End the last wrapper
29017							currentWrapElm = 0;
29018						}
29019					}
29020
29021					// Process siblings from range
29022					each(nodes, process);
29023				});
29024
29025				// Apply formats to links as well to get the color of the underline to change as well
29026				if (format.links === true) {
29027					each(newWrappers, function(node) {
29028						function process(node) {
29029							if (node.nodeName === 'A') {
29030								setElementFormat(node, format);
29031							}
29032
29033							each(grep(node.childNodes), process);
29034						}
29035
29036						process(node);
29037					});
29038				}
29039
29040				// Cleanup
29041				each(newWrappers, function(node) {
29042					var childCount;
29043
29044					function getChildCount(node) {
29045						var count = 0;
29046
29047						each(node.childNodes, function(node) {
29048							if (!isWhiteSpaceNode(node) && !isBookmarkNode(node)) {
29049								count++;
29050							}
29051						});
29052
29053						return count;
29054					}
29055
29056					function mergeStyles(node) {
29057						var child, clone;
29058
29059						each(node.childNodes, function(node) {
29060							if (node.nodeType == 1 && !isBookmarkNode(node) && !isCaretNode(node)) {
29061								child = node;
29062								return FALSE; // break loop
29063							}
29064						});
29065
29066						// If child was found and of the same type as the current node
29067						if (child && !isBookmarkNode(child) && matchName(child, format)) {
29068							clone = dom.clone(child, FALSE);
29069							setElementFormat(clone);
29070
29071							dom.replace(clone, node, TRUE);
29072							dom.remove(child, 1);
29073						}
29074
29075						return clone || node;
29076					}
29077
29078					childCount = getChildCount(node);
29079
29080					// Remove empty nodes but only if there is multiple wrappers and they are not block
29081					// elements so never remove single <h1></h1> since that would remove the
29082					// current empty block element where the caret is at
29083					if ((newWrappers.length > 1 || !isBlock(node)) && childCount === 0) {
29084						dom.remove(node, 1);
29085						return;
29086					}
29087
29088					if (format.inline || format.wrapper) {
29089						// Merges the current node with it's children of similar type to reduce the number of elements
29090						if (!format.exact && childCount === 1) {
29091							node = mergeStyles(node);
29092						}
29093
29094						// Remove/merge children
29095						each(formatList, function(format) {
29096							// Merge all children of similar type will move styles from child to parent
29097							// this: <span style="color:red"><b><span style="color:red; font-size:10px">text</span></b></span>
29098							// will become: <span style="color:red"><b><span style="font-size:10px">text</span></b></span>
29099							each(dom.select(format.inline, node), function(child) {
29100								if (isBookmarkNode(child)) {
29101									return;
29102								}
29103
29104								removeFormat(format, vars, child, format.exact ? child : null);
29105							});
29106						});
29107
29108						// Remove child if direct parent is of same type
29109						if (matchNode(node.parentNode, name, vars)) {
29110							dom.remove(node, 1);
29111							node = 0;
29112							return TRUE;
29113						}
29114
29115						// Look for parent with similar style format
29116						if (format.merge_with_parents) {
29117							dom.getParent(node.parentNode, function(parent) {
29118								if (matchNode(parent, name, vars)) {
29119									dom.remove(node, 1);
29120									node = 0;
29121									return TRUE;
29122								}
29123							});
29124						}
29125
29126						// Merge next and previous siblings if they are similar <b>text</b><b>text</b> becomes <b>texttext</b>
29127						if (node && format.merge_siblings !== false) {
29128							node = mergeSiblings(getNonWhiteSpaceSibling(node), node);
29129							node = mergeSiblings(node, getNonWhiteSpaceSibling(node, TRUE));
29130						}
29131					}
29132				});
29133			}
29134
29135			if (getContentEditable(selection.getNode()) === "false") {
29136				node = selection.getNode();
29137				for (var i = 0, l = formatList.length; i < l; i++) {
29138					if (formatList[i].ceFalseOverride && dom.is(node, formatList[i].selector)) {
29139						setElementFormat(node, formatList[i]);
29140						return;
29141					}
29142				}
29143
29144				return;
29145			}
29146
29147			if (format) {
29148				if (node) {
29149					if (node.nodeType) {
29150						rng = dom.createRng();
29151						rng.setStartBefore(node);
29152						rng.setEndAfter(node);
29153						applyRngStyle(expandRng(rng, formatList), null, true);
29154					} else {
29155						applyRngStyle(node, null, true);
29156					}
29157				} else {
29158					if (!isCollapsed || !format.inline || dom.select('td.mce-item-selected,th.mce-item-selected').length) {
29159						// Obtain selection node before selection is unselected by applyRngStyle()
29160						var curSelNode = ed.selection.getNode();
29161
29162						// If the formats have a default block and we can't find a parent block then
29163						// start wrapping it with a DIV this is for forced_root_blocks: false
29164						// It's kind of a hack but people should be using the default block type P since all desktop editors work that way
29165						if (!forcedRootBlock && formatList[0].defaultBlock && !dom.getParent(curSelNode, dom.isBlock)) {
29166							apply(formatList[0].defaultBlock);
29167						}
29168
29169						// Apply formatting to selection
29170						ed.selection.setRng(adjustSelectionToVisibleSelection());
29171						bookmark = selection.getBookmark();
29172						applyRngStyle(expandRng(selection.getRng(TRUE), formatList), bookmark);
29173
29174						// Colored nodes should be underlined so that the color of the underline matches the text color.
29175						if (format.styles && (format.styles.color || format.styles.textDecoration)) {
29176							walk(curSelNode, processUnderlineAndColor, 'childNodes');
29177							processUnderlineAndColor(curSelNode);
29178						}
29179
29180						selection.moveToBookmark(bookmark);
29181						moveStart(selection.getRng(TRUE));
29182						ed.nodeChanged();
29183					} else {
29184						performCaretAction('apply', name, vars);
29185					}
29186				}
29187			}
29188		}
29189
29190		/**
29191		 * Removes the specified format from the current selection or specified node.
29192		 *
29193		 * @method remove
29194		 * @param {String} name Name of format to remove.
29195		 * @param {Object} vars Optional list of variables to replace within format before removing it.
29196		 * @param {Node/Range} node Optional node or DOM range to remove the format from defaults to current selection.
29197		 */
29198		function remove(name, vars, node, similar) {
29199			var formatList = get(name), format = formatList[0], bookmark, rng, contentEditable = true;
29200
29201			// Merges the styles for each node
29202			function process(node) {
29203				var children, i, l, lastContentEditable, hasContentEditableState;
29204
29205				// Node has a contentEditable value
29206				if (node.nodeType === 1 && getContentEditable(node)) {
29207					lastContentEditable = contentEditable;
29208					contentEditable = getContentEditable(node) === "true";
29209					hasContentEditableState = true; // We don't want to wrap the container only it's children
29210				}
29211
29212				// Grab the children first since the nodelist might be changed
29213				children = grep(node.childNodes);
29214
29215				// Process current node
29216				if (contentEditable && !hasContentEditableState) {
29217					for (i = 0, l = formatList.length; i < l; i++) {
29218						if (removeFormat(formatList[i], vars, node, node)) {
29219							break;
29220						}
29221					}
29222				}
29223
29224				// Process the children
29225				if (format.deep) {
29226					if (children.length) {
29227						for (i = 0, l = children.length; i < l; i++) {
29228							process(children[i]);
29229						}
29230
29231						if (hasContentEditableState) {
29232							contentEditable = lastContentEditable; // Restore last contentEditable state from stack
29233						}
29234					}
29235				}
29236			}
29237
29238			function findFormatRoot(container) {
29239				var formatRoot;
29240
29241				// Find format root
29242				each(getParents(container.parentNode).reverse(), function(parent) {
29243					var format;
29244
29245					// Find format root element
29246					if (!formatRoot && parent.id != '_start' && parent.id != '_end') {
29247						// Is the node matching the format we are looking for
29248						format = matchNode(parent, name, vars, similar);
29249						if (format && format.split !== false) {
29250							formatRoot = parent;
29251						}
29252					}
29253				});
29254
29255				return formatRoot;
29256			}
29257
29258			function wrapAndSplit(formatRoot, container, target, split) {
29259				var parent, clone, lastClone, firstClone, i, formatRootParent;
29260
29261				// Format root found then clone formats and split it
29262				if (formatRoot) {
29263					formatRootParent = formatRoot.parentNode;
29264
29265					for (parent = container.parentNode; parent && parent != formatRootParent; parent = parent.parentNode) {
29266						clone = dom.clone(parent, FALSE);
29267
29268						for (i = 0; i < formatList.length; i++) {
29269							if (removeFormat(formatList[i], vars, clone, clone)) {
29270								clone = 0;
29271								break;
29272							}
29273						}
29274
29275						// Build wrapper node
29276						if (clone) {
29277							if (lastClone) {
29278								clone.appendChild(lastClone);
29279							}
29280
29281							if (!firstClone) {
29282								firstClone = clone;
29283							}
29284
29285							lastClone = clone;
29286						}
29287					}
29288
29289					// Never split block elements if the format is mixed
29290					if (split && (!format.mixed || !isBlock(formatRoot))) {
29291						container = dom.split(formatRoot, container);
29292					}
29293
29294					// Wrap container in cloned formats
29295					if (lastClone) {
29296						target.parentNode.insertBefore(lastClone, target);
29297						firstClone.appendChild(target);
29298					}
29299				}
29300
29301				return container;
29302			}
29303
29304			function splitToFormatRoot(container) {
29305				return wrapAndSplit(findFormatRoot(container), container, container, true);
29306			}
29307
29308			function unwrap(start) {
29309				var node = dom.get(start ? '_start' : '_end'),
29310					out = node[start ? 'firstChild' : 'lastChild'];
29311
29312				// If the end is placed within the start the result will be removed
29313				// So this checks if the out node is a bookmark node if it is it
29314				// checks for another more suitable node
29315				if (isBookmarkNode(out)) {
29316					out = out[start ? 'firstChild' : 'lastChild'];
29317				}
29318
29319				// Since dom.remove removes empty text nodes then we need to try to find a better node
29320				if (out.nodeType == 3 && out.data.length === 0) {
29321					out = start ? node.previousSibling || node.nextSibling : node.nextSibling || node.previousSibling;
29322				}
29323
29324				dom.remove(node, true);
29325
29326				return out;
29327			}
29328
29329			function removeRngStyle(rng) {
29330				var startContainer, endContainer;
29331				var commonAncestorContainer = rng.commonAncestorContainer;
29332
29333				rng = expandRng(rng, formatList, TRUE);
29334
29335				if (format.split) {
29336					startContainer = getContainer(rng, TRUE);
29337					endContainer = getContainer(rng);
29338
29339					if (startContainer != endContainer) {
29340						// WebKit will render the table incorrectly if we wrap a TH or TD in a SPAN
29341						// so let's see if we can use the first child instead
29342						// This will happen if you triple click a table cell and use remove formatting
29343						if (/^(TR|TH|TD)$/.test(startContainer.nodeName) && startContainer.firstChild) {
29344							if (startContainer.nodeName == "TR") {
29345								startContainer = startContainer.firstChild.firstChild || startContainer;
29346							} else {
29347								startContainer = startContainer.firstChild || startContainer;
29348							}
29349						}
29350
29351						// Try to adjust endContainer as well if cells on the same row were selected - bug #6410
29352						if (commonAncestorContainer &&
29353							/^T(HEAD|BODY|FOOT|R)$/.test(commonAncestorContainer.nodeName) &&
29354							isTableCell(endContainer) && endContainer.firstChild) {
29355							endContainer = endContainer.firstChild || endContainer;
29356						}
29357
29358						if (dom.isChildOf(startContainer, endContainer) && !isBlock(endContainer) &&
29359							!isTableCell(startContainer) && !isTableCell(endContainer)) {
29360							startContainer = wrap(startContainer, 'span', {id: '_start', 'data-mce-type': 'bookmark'});
29361							splitToFormatRoot(startContainer);
29362							startContainer = unwrap(TRUE);
29363							return;
29364						}
29365
29366						// Wrap start/end nodes in span element since these might be cloned/moved
29367						startContainer = wrap(startContainer, 'span', {id: '_start', 'data-mce-type': 'bookmark'});
29368						endContainer = wrap(endContainer, 'span', {id: '_end', 'data-mce-type': 'bookmark'});
29369
29370						// Split start/end
29371						splitToFormatRoot(startContainer);
29372						splitToFormatRoot(endContainer);
29373
29374						// Unwrap start/end to get real elements again
29375						startContainer = unwrap(TRUE);
29376						endContainer = unwrap();
29377					} else {
29378						startContainer = endContainer = splitToFormatRoot(startContainer);
29379					}
29380
29381					// Update range positions since they might have changed after the split operations
29382					rng.startContainer = startContainer.parentNode ? startContainer.parentNode : startContainer;
29383					rng.startOffset = nodeIndex(startContainer);
29384					rng.endContainer = endContainer.parentNode ? endContainer.parentNode : endContainer;
29385					rng.endOffset = nodeIndex(endContainer) + 1;
29386				}
29387
29388				// Remove items between start/end
29389				rangeUtils.walk(rng, function(nodes) {
29390					each(nodes, function(node) {
29391						process(node);
29392
29393						// Remove parent span if it only contains text-decoration: underline, yet a parent node is also underlined.
29394						if (node.nodeType === 1 && ed.dom.getStyle(node, 'text-decoration') === 'underline' &&
29395							node.parentNode && getTextDecoration(node.parentNode) === 'underline') {
29396							removeFormat({
29397								'deep': false,
29398								'exact': true,
29399								'inline': 'span',
29400								'styles': {
29401									'textDecoration': 'underline'
29402								}
29403							}, null, node);
29404						}
29405					});
29406				});
29407			}
29408
29409			// Handle node
29410			if (node) {
29411				if (node.nodeType) {
29412					rng = dom.createRng();
29413					rng.setStartBefore(node);
29414					rng.setEndAfter(node);
29415					removeRngStyle(rng);
29416				} else {
29417					removeRngStyle(node);
29418				}
29419
29420				return;
29421			}
29422
29423			if (getContentEditable(selection.getNode()) === "false") {
29424				node = selection.getNode();
29425				for (var i = 0, l = formatList.length; i < l; i++) {
29426					if (formatList[i].ceFalseOverride) {
29427						if (removeFormat(formatList[i], vars, node, node)) {
29428							break;
29429						}
29430					}
29431				}
29432
29433				return;
29434			}
29435
29436			if (!selection.isCollapsed() || !format.inline || dom.select('td.mce
29436-item-selected,th.mce-item-selected').length) {
29437				bookmark = selection.getBookmark();
29438				removeRngStyle(selection.getRng(TRUE));
29439				selection.moveToBookmark(bookmark);
29440
29441				// Check if start element still has formatting then we are at: "<b>text|</b>text"
29442				// and need to move the start into the next text node
29443				if (format.inline && match(name, vars, selection.getStart())) {
29444					moveStart(selection.getRng(true));
29445				}
29446
29447				ed.nodeChanged();
29448			} else {
29449				performCaretAction('remove', name, vars, similar);
29450			}
29451		}
29452
29453		/**
29454		 * Toggles the specified format on/off.
29455		 *
29456		 * @method toggle
29457		 * @param {String} name Name of format to apply/remove.
29458		 * @param {Object} vars Optional list of variables to replace within format before applying/removing it.
29459		 * @param {Node} node Optional node to apply the format to or remove from. Defaults to current selection.
29460		 */
29461		function toggle(name, vars, node) {
29462			var fmt = get(name);
29463
29464			if (match(name, vars, node) && (!('toggle' in fmt[0]) || fmt[0].toggle)) {
29465				remove(name, vars, node);
29466			} else {
29467				apply(name, vars, node);
29468			}
29469		}
29470
29471		/**
29472		 * Return true/false if the specified node has the specified format.
29473		 *
29474		 * @method matchNode
29475		 * @param {Node} node Node to check the format on.
29476		 * @param {String} name Format name to check.
29477		 * @param {Object} vars Optional list of variables to replace before checking it.
29478		 * @param {Boolean} similar Match format that has similar properties.
29479		 * @return {Object} Returns the format object it matches or undefined if it doesn't match.
29480		 */
29481		function matchNode(node, name, vars, similar) {
29482			var formatList = get(name), format, i, classes;
29483
29484			function matchItems(node, format, item_name) {
29485				var key, value, items = format[item_name], i;
29486
29487				// Custom match
29488				if (format.onmatch) {
29489					return format.onmatch(node, format, item_name);
29490				}
29491
29492				// Check all items
29493				if (items) {
29494					// Non indexed object
29495					if (items.length === undef) {
29496						for (key in items) {
29497							if (items.hasOwnProperty(key)) {
29498								if (item_name === 'attributes') {
29499									value = dom.getAttrib(node, key);
29500								} else {
29501									value = getStyle(node, key);
29502								}
29503
29504								if (similar && !value && !format.exact) {
29505									return;
29506								}
29507
29508								if ((!similar || format.exact) && !isEq(value, normalizeStyleValue(replaceVars(items[key], vars), key))) {
29509									return;
29510								}
29511							}
29512						}
29513					} else {
29514						// Only one match needed for indexed arrays
29515						for (i = 0; i < items.length; i++) {
29516							if (item_name === 'attributes' ? dom.getAttrib(node, items[i]) : getStyle(node, items[i])) {
29517								return format;
29518							}
29519						}
29520					}
29521				}
29522
29523				return format;
29524			}
29525
29526			if (formatList && node) {
29527				// Check each format in list
29528				for (i = 0; i < formatList.length; i++) {
29529					format = formatList[i];
29530
29531					// Name name, attributes, styles and classes
29532					if (matchName(node, format) && matchItems(node, format, 'attributes') && matchItems(node, format, 'styles')) {
29533						// Match classes
29534						if ((classes = format.classes)) {
29535							for (i = 0; i < classes.length; i++) {
29536								if (!dom.hasClass(node, classes[i])) {
29537									return;
29538								}
29539							}
29540						}
29541
29542						return format;
29543					}
29544				}
29545			}
29546		}
29547
29548		/**
29549		 * Matches the current selection or specified node against the specified format name.
29550		 *
29551		 * @method match
29552		 * @param {String} name Name of format to match.
29553		 * @param {Object} vars Optional list of variables to replace before checking it.
29554		 * @param {Node} node Optional node to check.
29555		 * @return {boolean} true/false if the specified selection/node matches the format.
29556		 */
29557		function match(name, vars, node) {
29558			var startNode;
29559
29560			function matchParents(node) {
29561				var root = dom.getRoot();
29562
29563				if (node === root) {
29564					return false;
29565				}
29566
29567				// Find first node with similar format settings
29568				node = dom.getParent(node, function(node) {
29569					return node.parentNode === root || !!matchNode(node, name, vars, true);
29570				});
29571
29572				// Do an exact check on the similar format element
29573				return matchNode(node, name, vars);
29574			}
29575
29576			// Check specified node
29577			if (node) {
29578				return matchParents(node);
29579			}
29580
29581			// Check selected node
29582			node = selection.getNode();
29583			if (matchParents(node)) {
29584				return TRUE;
29585			}
29586
29587			// Check start node if it's different
29588			startNode = selection.getStart();
29589			if (startNode != node) {
29590				if (matchParents(startNode)) {
29591					return TRUE;
29592				}
29593			}
29594
29595			return FALSE;
29596		}
29597
29598		/**
29599		 * Matches the current selection against the array of formats and returns a new array with matching formats.
29600		 *
29601		 * @method matchAll
29602		 * @param {Array} names Name of format to match.
29603		 * @param {Object} vars Optional list of variables to replace before checking it.
29604		 * @return {Array} Array with matched formats.
29605		 */
29606		function matchAll(names, vars) {
29607			var startElement, matchedFormatNames = [], checkedMap = {};
29608
29609			// Check start of selection for formats
29610			startElement = selection.getStart();
29611			dom.getParent(startElement, function(node) {
29612				var i, name;
29613
29614				for (i = 0; i < names.length; i++) {
29615					name = names[i];
29616
29617					if (!checkedMap[name] && matchNode(node, name, vars)) {
29618						checkedMap[name] = true;
29619						matchedFormatNames.push(name);
29620					}
29621				}
29622			}, dom.getRoot());
29623
29624			return matchedFormatNames;
29625		}
29626
29627		/**
29628		 * Returns true/false if the specified format can be applied to the current selection or not. It
29629		 * will currently only check the state for selector formats, it returns true on all other format types.
29630		 *
29631		 * @method canApply
29632		 * @param {String} name Name of format to check.
29633		 * @return {boolean} true/false if the specified format can be applied to the current selection/node.
29634		 */
29635		function canApply(name) {
29636			var formatList = get(name), startNode, parents, i, x, selector;
29637
29638			if (formatList) {
29639				startNode = selection.getStart();
29640				parents = getParents(startNode);
29641
29642				for (x = formatList.length - 1; x >= 0; x--) {
29643					selector = formatList[x].selector;
29644
29645					// Format is not selector based then always return TRUE
29646					// Is it has a defaultBlock then it's likely it can be applied for example align on a non block element line
29647					if (!selector || formatList[x].defaultBlock) {
29648						return TRUE;
29649					}
29650
29651					for (i = parents.length - 1; i >= 0; i--) {
29652						if (dom.is(parents[i], selector)) {
29653							return TRUE;
29654						}
29655					}
29656				}
29657			}
29658
29659			return FALSE;
29660		}
29661
29662		/**
29663		 * Executes the specified callback when the current selection matches the formats or not.
29664		 *
29665		 * @method formatChanged
29666		 * @param {String} formats Comma separated list of formats to check for.
29667		 * @param {function} callback Callback with state and args when the format is changed/toggled on/off.
29668		 * @param {Boolean} similar True/false state if the match should handle similar or exact formats.
29669		 */
29670		function formatChanged(formats, callback, similar) {
29671			var currentFormats;
29672
29673			// Setup format node change logic
29674			if (!formatChangeData) {
29675				formatChangeData = {};
29676				currentFormats = {};
29677
29678				ed.on('NodeChange', function(e) {
29679					var parents = getParents(e.element), matchedFormats = {};
29680
29681					// Ignore bogus nodes like the <a> tag created by moveStart()
29682					parents = Tools.grep(parents, function(node) {
29683						return node.nodeType == 1 && !node.getAttribute('data-mce-bogus');
29684					});
29685
29686					// Check for new formats
29687					each(formatChangeData, function(callbacks, format) {
29688						each(parents, function(node) {
29689							if (matchNode(node, format, {}, callbacks.similar)) {
29690								if (!currentFormats[format]) {
29691									// Execute callbacks
29692									each(callbacks, function(callback) {
29693										callback(true, {node: node, format: format, parents: parents});
29694									});
29695
29696									currentFormats[format] = callbacks;
29697								}
29698
29699								matchedFormats[format] = callbacks;
29700								return false;
29701							}
29702						});
29703					});
29704
29705					// Check if current formats still match
29706					each(currentFormats, function(callbacks, format) {
29707						if (!matchedFormats[format]) {
29708							delete currentFormats[format];
29709
29710							each(callbacks, function(callback) {
29711								callback(false, {node: e.element, format: format, parents: parents});
29712							});
29713						}
29714					});
29715				});
29716			}
29717
29718			// Add format listeners
29719			each(formats.split(','), function(format) {
29720				if (!formatChangeData[format]) {
29721					formatChangeData[format] = [];
29722					formatChangeData[format].similar = similar;
29723				}
29724
29725				formatChangeData[format].push(callback);
29726			});
29727
29728			return this;
29729		}
29730
29731		/**
29732		 * Returns a preview css text for the specified format.
29733		 *
29734		 * @method getCssText
29735		 * @param {String/Object} format Format to generate preview css text for.
29736		 * @return {String} Css text for the specified format.
29737		 * @example
29738		 * var cssText1 = editor.formatter.getCssText('bold');
29739		 * var cssText2 = editor.formatter.getCssText({inline: 'b'});
29740		 */
29741		function getCssText(format) {
29742			return Preview.getCssText(ed, format);
29743		}
29744
29745		// Expose to public
29746		extend(this, {
29747			get: get,
29748			register: register,
29749			unregister: unregister,
29750			apply: apply,
29751			remove: remove,
29752			toggle: toggle,
29753			match: match,
29754			matchAll: matchAll,
29755			matchNode: matchNode,
29756			canApply: canApply,
29757			formatChanged: formatChanged,
29758			getCssText: getCssText
29759		});
29760
29761		// Initialize
29762		defaultFormats();
29763		addKeyboardShortcuts();
29764		ed.on('BeforeGetContent', function(e) {
29765			if (markCaretContainersBogus && e.format != 'raw') {
29766				markCaretContainersBogus();
29767			}
29768		});
29769		ed.on('mouseup keydown', function(e) {
29770			if (disableCaretContainer) {
29771				disableCaretContainer(e);
29772			}
29773		});
29774
29775		// Private functions
29776
29777		/**
29778		 * Checks if the specified nodes name matches the format inline/block or selector.
29779		 *
29780		 * @private
29781		 * @param {Node} node Node to match against the specified format.
29782		 * @param {Object} format Format object o match with.
29783		 * @return {boolean} true/false if the format matches.
29784		 */
29785		function matchName(node, format) {
29786			// Check for inline match
29787			if (isEq(node, format.inline)) {
29788				return TRUE;
29789			}
29790
29791			// Check for block match
29792			if (isEq(node, format.block)) {
29793				return TRUE;
29794			}
29795
29796			// Check for selector match
29797			if (format.selector) {
29798				return node.nodeType == 1 && dom.is(node, format.selector);
29799			}
29800		}
29801
29802		/**
29803		 * Compares two string/nodes regardless of their case.
29804		 *
29805		 * @private
29806		 * @param {String/Node} str1 Node or string to compare.
29807		 * @param {String/Node} str2 Node or string to compare.
29808		 * @return {boolean} True/false if they match.
29809		 */
29810		function isEq(str1, str2) {
29811			str1 = str1 || '';
29812			str2 = str2 || '';
29813
29814			str1 = '' + (str1.nodeName || str1);
29815			str2 = '' + (str2.nodeName || str2);
29816
29817			return str1.toLowerCase() == str2.toLowerCase();
29818		}
29819
29820		/**
29821		 * Returns the style by name on the specified node. This method modifies the style
29822		 * contents to make it more easy to match. This will resolve a few browser issues.
29823		 *
29824		 * @private
29825		 * @param {Node} node to get style from.
29826		 * @param {String} name Style name to get.
29827		 * @return {String} Style item value.
29828		 */
29829		function getStyle(node, name) {
29830			return normalizeStyleValue(dom.getStyle(node, name), name);
29831		}
29832
29833		/**
29834		 * Normalize style value by name. This method modifies the style contents
29835		 * to make it more easy to match. This will resolve a few browser issues.
29836		 *
29837		 * @private
29838		 * @param {String} value Value to get style from.
29839		 * @param {String} name Style name to get.
29840		 * @return {String} Style item value.
29841		 */
29842		function normalizeStyleValue(value, name) {
29843			// Force the format to hex
29844			if (name == 'color' || name == 'backgroundColor') {
29845				value = dom.toHex(value);
29846			}
29847
29848			// Opera will return bold as 700
29849			if (name == 'fontWeight' && value == 700) {
29850				value = 'bold';
29851			}
29852
29853			// Normalize fontFamily so "'Font name', Font" becomes: "Font name,Font"
29854			if (name == 'fontFamily') {
29855				value = value.replace(/[\'\"]/g, '').replace(/,\s+/g, ',');
29856			}
29857
29858			return '' + value;
29859		}
29860
29861		/**
29862		 * Replaces variables in the value. The variable format is %var.
29863		 *
29864		 * @private
29865		 * @param {String} value Value to replace variables in.
29866		 * @param {Object} vars Name/value array with variables to replace.
29867		 * @return {String} New value with replaced variables.
29868		 */
29869		function replaceVars(value, vars) {
29870			if (typeof value != "string") {
29871				value = value(vars);
29872			} else if (vars) {
29873				value = value.replace(/%(\w+)/g, function(str, name) {
29874					return vars[name] || str;
29875				});
29876			}
29877
29878			return value;
29879		}
29880
29881		function isWhiteSpaceNode(node) {
29882			return node && node.nodeType === 3 && /^([\t \r\n]+|)$/.test(node.nodeValue);
29883		}
29884
29885		function wrap(node, name, attrs) {
29886			var wrapper = dom.create(name, attrs);
29887
29888			node.parentNode.insertBefore(wrapper, node);
29889			wrapper.appendChild(node);
29890
29891			return wrapper;
29892		}
29893
29894		/**
29895		 * Expands the specified range like object to depending on format.
29896		 *
29897		 * For example on block formats it will move the start/end position
29898		 * to the beginning of the current block.
29899		 *
29900		 * @private
29901		 * @param {Object} rng Range like object.
29902		 * @param {Array} format Array with formats to expand by.
29903		 * @param {Boolean} remove
29904		 * @return {Object} Expanded range like object.
29905		 */
29906		function expandRng(rng, format, remove) {
29907			var lastIdx, leaf, endPoint,
29908				startContainer = rng.startContainer,
29909				startOffset = rng.startOffset,
29910				endContainer = rng.endContainer,
29911				endOffset = rng.endOffset;
29912
29913			// This function walks up the tree if there is no siblings before/after the node
29914			function findParentContainer(start) {
29915				var container, parent, sibling, siblingName, root;
29916
29917				container = parent = start ? startContainer : endContainer;
29918				siblingName = start ? 'previousSibling' : 'nextSibling';
29919				root = dom.getRoot();
29920
29921				function isBogusBr(node) {
29922					return node.nodeName == "BR" && node.getAttribute('data-mce-bogus') && !node.nextSibling;
29923				}
29924
29925				// If it's a text node and the offset is inside the text
29926				if (container.nodeType == 3 && !isWhiteSpaceNode(container)) {
29927					if (start ? startOffset > 0 : endOffset < container.nodeValue.length) {
29928						return container;
29929					}
29930				}
29931
29932				/*eslint no-constant-condition:0 */
29933				while (true) {
29934					// Stop expanding on block elements
29935					if (!format[0].block_expand && isBlock(parent)) {
29936						return parent;
29937					}
29938
29939					// Walk left/right
29940					for (sibling = parent[siblingName]; sibling; sibling = sibling[siblingName]) {
29941						if (!isBookmarkNode(sibling) && !isWhiteSpaceNode(sibling) && !isBogusBr(sibling)) {
29942							return parent;
29943						}
29944					}
29945
29946					// Check if we can move up are we at root level or body level
29947					if (parent == root || parent.parentNode == root) {
29948						container = parent;
29949						break;
29950					}
29951
29952					parent = parent.parentNode;
29953				}
29954
29955				return container;
29956			}
29957
29958			// This function walks down the tree to find the leaf at the selection.
29959			// The offset is also returned as if node initially a leaf, the offset may be in the middle of the text node.
29960			function findLeaf(node, offset) {
29961				if (offset === undef) {
29962					offset = node.nodeType === 3 ? node.length : node.childNodes.length;
29963				}
29964
29965				while (node && node.hasChildNodes()) {
29966					node = node.childNodes[offset];
29967					if (node) {
29968						offset = node.nodeType === 3 ? node.length : node.childNodes.length;
29969					}
29970				}
29971				return {node: node, offset: offset};
29972			}
29973
29974			// If index based start position then resolve it
29975			if (startContainer.nodeType == 1 && startContainer.hasChildNodes()) {
29976				lastIdx = startContainer.childNodes.length - 1;
29977				startContainer = startContainer.childNodes[startOffset > lastIdx ? lastIdx : startOffset];
29978
29979				if (startContainer.nodeType == 3) {
29980					startOffset = 0;
29981				}
29982			}
29983
29984			// If index based end position then resolve it
29985			if (endContainer.nodeType == 1 && endContainer.hasChildNodes()) {
29986				lastIdx = endContainer.childNodes.length - 1;
29987				endContainer = endContainer.childNodes[endOffset > lastIdx ? lastIdx : endOffset - 1];
29988
29989				if (endContainer.nodeType == 3) {
29990					endOffset = endContainer.nodeValue.length;
29991				}
29992			}
29993
29994			// Expands the node to the closes contentEditable false element if it exists
29995			function findParentContentEditable(node) {
29996				var parent = node;
29997
29998				while (parent) {
29999					if (parent.nodeType === 1 && getContentEditable(parent)) {
30000						return getContentEditable(parent) === "false" ? parent : node;
30001					}
30002
30003					parent = parent.parentNode;
30004				}
30005
30006				return node;
30007			}
30008
30009			function findWordEndPoint(container, offset, start) {
30010				var walker, node, pos, lastTextNode;
30011
30012				function findSpace(node, offset) {
30013					var pos, pos2, str = node.nodeValue;
30014
30015					if (typeof offset == "undefined") {
30016						offset = start ? str.length : 0;
30017					}
30018
30019					if (start) {
30020						pos = str.lastIndexOf(' ', offset);
30021						pos2 = str.lastIndexOf('\u00a0', offset);
30022						pos = pos > pos2 ? pos : pos2;
30023
30024						// Include the space on remove to avoid tag soup
30025						if (pos !== -1 && !remove) {
30026							pos++;
30027						}
30028					} else {
30029						pos = str.indexOf(' ', offset);
30030						pos2 = str.indexOf('\u00a0', offset);
30031						pos = pos !== -1 && (pos2 === -1 || pos < pos2) ? pos : pos2;
30032					}
30033
30034					return pos;
30035				}
30036
30037				if (container.nodeType === 3) {
30038					pos = findSpace(container, offset);
30039
30040					if (pos !== -1) {
30041						return {container: container, offset: pos};
30042					}
30043
30044					lastTextNode = container;
30045				}
30046
30047				// Walk the nodes inside the block
30048				walker = new TreeWalker(container, dom.getParent(container, isBlock) || ed.getBody());
30049				while ((node = walker[start ? 'prev' : 'next']())) {
30050					if (node.nodeType === 3) {
30051						lastTextNode = node;
30052						pos = findSpace(node);
30053
30054						if (pos !== -1) {
30055							return {container: node, offset: pos};
30056						}
30057					} else if (isBlock(node)) {
30058						break;
30059					}
30060				}
30061
30062				if (lastTextNode) {
30063					if (start) {
30064						offset = 0;
30065					} else {
30066						offset = lastTextNode.length;
30067					}
30068
30069					return {container: lastTextNode, offset: offset};
30070				}
30071			}
30072
30073			function findSelectorEndPoint(container, sibling_name) {
30074				var parents, i, y, curFormat;
30075
30076				if (container.nodeType == 3 && container.nodeValue.length === 0 && container[sibling_name]) {
30077					container = container[sibling_name];
30078				}
30079
30080				parents = getParents(container);
30081				for (i = 0; i < parents.length; i++) {
30082					for (y = 0; y < format.length; y++) {
30083						curFormat = format[y];
30084
30085						// If collapsed state is set then skip formats that doesn't match that
30086						if ("collapsed" in curFormat && curFormat.collapsed !== rng.collapsed) {
30087							continue;
30088						}
30089
30090						if (dom.is(parents[i], curFormat.selector)) {
30091							return parents[i];
30092						}
30093					}
30094				}
30095
30096				return container;
30097			}
30098
30099			function findBlockEndPoint(container, sibling_name) {
30100				var node, root = dom.getRoot();
30101
30102				// Expand to block of similar type
30103				if (!format[0].wrapper) {
30104					node = dom.getParent(container, format[0].block, root);
30105				}
30106
30107				// Expand to first wrappable block element or any block element
30108				if (!node) {
30109					node = dom.getParent(container.nodeType == 3 ? container.parentNode : container, function(node) {
30110						// Fixes #6183 where it would expand to editable parent element in inline mode
30111						return node != root && isTextBlock(node);
30112					});
30113				}
30114
30115				// Exclude inner lists from wrapping
30116				if (node && format[0].wrapper) {
30117					node = getParents(node, 'ul,ol').reverse()[0] || node;
30118				}
30119
30120				// Didn't find a block element look for first/last wrappable element
30121				if (!node) {
30122					node = container;
30123
30124					while (node[sibling_name] && !isBlock(node[sibling_name])) {
30125						node = node[sibling_name];
30126
30127						// Break on BR but include it will be removed later on
30128						// we can't remove it now since we need to check if it can be wrapped
30129						if (isEq(node, 'br')) {
30130							break;
30131						}
30132					}
30133				}
30134
30135				return node || container;
30136			}
30137
30138			// Expand to closest contentEditable element
30139			startContainer = findParentContentEditable(startContainer);
30140			endContainer = findParentContentEditable(endContainer);
30141
30142			// Exclude bookmark nodes if possible
30143			if (isBookmarkNode(startContainer.parentNode) || isBookmarkNode(startContainer)) {
30144				startContainer = isBookmarkNode(startContainer) ? startContainer : startContainer.parentNode;
30145				startContainer = startContainer.nextSibling || startContainer;
30146
30147				if (startContainer.nodeType == 3) {
30148					startOffset = 0;
30149				}
30150			}
30151
30152			if (isBookmarkNode(endContainer.parentNode) || isBookmarkNode(endContainer)) {
30153				endContainer = isBookmarkNode(endContainer) ? endContainer : endContainer.parentNode;
30154				endContainer = endContainer.previousSibling || endContainer;
30155
30156				if (endContainer.nodeType == 3) {
30157					endOffset = endContainer.length;
30158				}
30159			}
30160
30161			if (format[0].inline) {
30162				if (rng.collapsed) {
30163					// Expand left to closest word boundary
30164					endPoint = findWordEndPoint(startContainer, startOffset, true);
30165					if (endPoint) {
30166						startContainer = endPoint.container;
30167						startOffset = endPoint.offset;
30168					}
30169
30170					// Expand right to closest word boundary
30171					endPoint = findWordEndPoint(endContainer, endOffset);
30172					if (endPoint) {
30173						endContainer = endPoint.container;
30174						endOffset = endPoint.offset;
30175					}
30176				}
30177
30178				// Avoid applying formatting to a trailing space.
30179				leaf = findLeaf(endContainer, endOffset);
30180				if (leaf.node) {
30181					while (leaf.node && leaf.offset === 0 && leaf.node.previousSibling) {
30182						leaf = findLeaf(leaf.node.previousSibling);
30183					}
30184
30185					if (leaf.node && leaf.offset > 0 && leaf.node.nodeType === 3 &&
30186							leaf.node.nodeValue.charAt(leaf.offset - 1) === ' ') {
30187
30188						if (leaf.offset > 1) {
30189							endContainer = leaf.node;
30190							endContainer.splitText(leaf.offset - 1);
30191						}
30192					}
30193				}
30194			}
30195
30196			// Move start/end point up the tree if the leaves are sharp and if we are in different containers
30197			// Example * becomes !: !<p><b><i>*text</i><i>text*</i></b></p>!
30198			// This will reduce the number of wrapper elements that needs to be created
30199			// Move start point up the tree
30200			if (format[0].inline || format[0].block_expand) {
30201				if (!format[0].inline || (startContainer.nodeType != 3 || startOffset === 0)) {
30202					startContainer = findParentContainer(true);
30203				}
30204
30205				if (!format[0].inline || (endContainer.nodeType != 3 || endOffset === endContainer.nodeValue.length)) {
30206					endContainer = findParentContainer();
30207				}
30208			}
30209
30210			// Expand start/end container to matching selector
30211			if (format[0].selector && format[0].expand !== FALSE && !format[0].inline) {
30212				// Find new startContainer/endContainer if there is better one
30213				startContainer = findSelectorEndPoint(startContainer, 'previousSibling');
30214				endContainer = findSelectorEndPoint(endContainer, 'nextSibling');
30215			}
30216
30217			// Expand start/end container to matching block element or text node
30218			if (format[0].block || format[0].selector) {
30219				// Find new startContainer/endContainer if there is better one
30220				startContainer = findBlockEndPoint(startContainer, 'previousSibling');
30221				endContainer = findBlockEndPoint(endContainer, 'nextSibling');
30222
30223				// Non block element then try to expand up the leaf
30224				if (format[0].block) {
30225					if (!isBlock(startContainer)) {
30226						startContainer = findParentContainer(true);
30227					}
30228
30229					if (!isBlock(endContainer)) {
30230						endContainer = findParentContainer();
30231					}
30232				}
30233			}
30234
30235			// Setup index for startContainer
30236			if (startContainer.nodeType == 1) {
30237				startOffset = nodeIndex(startContainer);
30238				startContainer = startContainer.parentNode;
30239			}
30240
30241			// Setup index for endContainer
30242			if (endContainer.nodeType == 1) {
30243				endOffset = nodeIndex(endContainer) + 1;
30244				endContainer = endContainer.parentNode;
30245			}
30246
30247			// Return new range like object
30248			return {
30249				startContainer: startContainer,
30250				startOffset: startOffset,
30251				endContainer: endContainer,
30252				endOffset: endOffset
30253			};
30254		}
30255
30256		function isColorFormatAndAnchor(node, format) {
30257			return format.links && node.tagName == 'A';
30258		}
30259
30260		/**
30261		 * Removes the specified format for the specified node. It will also remove the node if it doesn't have
30262		 * any attributes if the format specifies it to do so.
30263		 *
30264		 * @private
30265		 * @param {Object} format Format object with items to remove from node.
30266		 * @param {Object} vars Name/value object with variables to apply to format.
30267		 * @param {Node} node Node to remove the format styles on.
30268		 * @param {Node} compare_node Optional compare node, if specified the styles will be compared to that node.
30269		 * @return {Boolean} True/false if the node was removed or not.
30270		 */
30271		function removeFormat(format, vars, node, compare_node) {
30272			var i, attrs, stylesModified;
30273
30274			// Check if node matches format
30275			if (!matchName(node, format) && !isColorFormatAndAnchor(node, format)) {
30276				return FALSE;
30277			}
30278
30279			// Should we compare with format attribs and styles
30280			if (format.remove != 'all') {
30281				// Remove styles
30282				each(format.styles, function(value, name) {
30283					value = normalizeStyleValue(replaceVars(value, vars), name);
30284
30285					// Indexed array
30286					if (typeof name === 'number') {
30287						name = value;
30288						compare_node = 0;
30289					}
30290
30291					if (format.remove_similar || (!compare_node || isEq(getStyle(compare_node, name), value))) {
30292						dom.setStyle(node, name, '');
30293					}
30294
30295					stylesModified = 1;
30296				});
30297
30298				// Remove style attribute if it's empty
30299				if (stylesModified && dom.getAttrib(node, 'style') === '') {
30300					node.removeAttribute('style');
30301					node.removeAttribute('data-mce-style');
30302				}
30303
30304				// Remove attributes
30305				each(format.attributes, function(value, name) {
30306					var valueOut;
30307
30308					value = replaceVars(value, vars);
30309
30310					// Indexed array
30311					if (typeof name === 'number') {
30312						name = value;
30313						compare_node = 0;
30314					}
30315
30316					if (!compare_node || isEq(dom.getAttrib(compare_node, name), value)) {
30317						// Keep internal classes
30318						if (name == 'class') {
30319							value = dom.getAttrib(node, name);
30320							if (value) {
30321								// Build new class value where everything is removed except the internal prefixed classes
30322								valueOut = '';
30323								each(value.split(/\s+/), function(cls) {
30324									if (/mce\-\w+/.test(cls)) {
30325										valueOut += (valueOut ? ' ' : '') + cls;
30326									}
30327								});
30328
30329								// We got some internal classes left
30330								if (valueOut) {
30331									dom.setAttrib(node, name, valueOut);
30332									return;
30333								}
30334							}
30335						}
30336
30337						// IE6 has a bug where the attribute doesn't get removed correctly
30338						if (name == "class") {
30339							node.removeAttribute('className');
30340						}
30341
30342						// Remove mce prefixed attributes
30343						if (MCE_ATTR_RE.test(name)) {
30344							node.removeAttribute('data-mce-' + name);
30345						}
30346
30347						node.removeAttribute(name);
30348					}
30349				});
30350
30351				// Remove classes
30352				each(format.classes, function(value) {
30353					value = replaceVars(value, vars);
30354
30355					if (!compare_node || dom.hasClass(compare_node, value)) {
30356						dom.removeClass(node, value);
30357					}
30358				});
30359
30360				// Check for non internal attributes
30361				attrs = dom.getAttribs(node);
30362				for (i = 0; i < attrs.length; i++) {
30363					if (attrs[i].nodeName.indexOf('_') !== 0) {
30364						return FALSE;
30365					}
30366				}
30367			}
30368
30369			// Remove the inline child if it's empty for example <b> or <span>
30370			if (format.remove != 'none') {
30371				removeNode(node, format);
30372				return TRUE;
30373			}
30374		}
30375
30376		/**
30377		 * Removes the node and wrap it's children in paragraphs before doing so or
30378		 * appends BR elements to the beginning/end of the block element if forcedRootBlocks is disabled.
30379		 *
30380		 * If the div in the node below gets removed:
30381		 *  text<div>text</div>text
30382		 *
30383		 * Output becomes:
30384		 *  text<div><br />text<br /></div>text
30385		 *
30386		 * So when the div is removed the result is:
30387		 *  text<br />text<br />text
30388		 *
30389		 * @private
30390		 * @param {Node} node Node to remove + apply BR/P elements to.
30391		 * @param {Object} format Format rule.
30392		 * @return {Node} Input node.
30393		 */
30394		function removeNode(node, format) {
30395			var parentNode = node.parentNode, rootBlockElm;
30396
30397			function find(node, next, inc) {
30398				node = getNonWhiteSpaceSibling(node, next, inc);
30399
30400				return !node || (node.nodeName == 'BR' || isBlock(node));
30401			}
30402
30403			if (format.block) {
30404				if (!forcedRootBlock) {
30405					// Append BR elements if needed before we remove the block
30406					if (isBlock(node) && !isBlock(parentNode)) {
30407						if (!find(node, FALSE) && !find(node.firstChild, TRUE, 1)) {
30408							node.insertBefore(dom.create('br'), node.firstChild);
30409						}
30410
30411						if (!find(node, TRUE) && !find(node.lastChild, FALSE, 1)) {
30412							node.appendChild(dom.create('br'));
30413						}
30414					}
30415				} else {
30416					// Wrap the block in a forcedRootBlock if we are at the root of document
30417					if (parentNode == dom.getRoot()) {
30418						if (!format.list_block || !isEq(node, format.list_block)) {
30419							each(grep(node.childNodes), function(node) {
30420								if (isValid(forcedRootBlock, node.nodeName.toLowerCase())) {
30421									if (!rootBlockElm) {
30422										rootBlockElm = wrap(node, forcedRootBlock);
30423										dom.setAttribs(rootBlockElm, ed.settings.forced_root_block_attrs);
30424									} else {
30425										rootBlockElm.appendChild(node);
30426									}
30427								} else {
30428									rootBlockElm = 0;
30429								}
30430							});
30431						}
30432					}
30433				}
30434			}
30435
30436			// Never remove nodes that isn't the specified inline element if a selector is specified too
30437			if (format.selector && format.inline && !isEq(format.inline, node)) {
30438				return;
30439			}
30440
30441			dom.remove(node, 1);
30442		}
30443
30444		/**
30445		 * Returns the next/previous non whitespace node.
30446		 *
30447		 * @private
30448		 * @param {Node} node Node to start at.
30449		 * @param {boolean} next (Optional) Include next or previous node defaults to previous.
30450		 * @param {boolean} inc (Optional) Include the current node in checking. Defaults to false.
30451		 * @return {Node} Next or previous node or undefined if it wasn't found.
30452		 */
30453		function getNonWhiteSpaceSibling(node, next, inc) {
30454			if (node) {
30455				next = next ? 'nextSibling' : 'previousSibling';
30456
30457				for (node = inc ? node : node[next]; node; node = node[next]) {
30458					if (node.nodeType == 1 || !isWhiteSpaceNode(node)) {
30459						return node;
30460					}
30461				}
30462			}
30463		}
30464
30465		/**
30466		 * Merges the next/previous sibling element if they match.
30467		 *
30468		 * @private
30469		 * @param {Node} prev Previous node to compare/merge.
30470		 * @param {Node} next Next node to compare/merge.
30471		 * @return {Node} Next node if we didn't merge and prev node if we did.
30472		 */
30473		function mergeSiblings(prev, next) {
30474			var sibling, tmpSibling, elementUtils = new ElementUtils(dom);
30475
30476			function findElementSibling(node, sibling_name) {
30477				for (sibling = node; sibling; sibling = sibling[sibling_name]) {
30478					if (sibling.nodeType == 3 && sibling.nodeValue.length !== 0) {
30479						return node;
30480					}
30481
30482					if (sibling.nodeType == 1 && !isBookmarkNode(sibling)) {
30483						return sibling;
30484					}
30485				}
30486
30487				return node;
30488			}
30489
30490			// Check if next/prev exists and that they are elements
30491			if (prev && next) {
30492				// If previous sibling is empty then jump over it
30493				prev = findElementSibling(prev, 'previousSibling');
30494				next = findElementSibling(next, 'nextSibling');
30495
30496				// Compare next and previous nodes
30497				if (elementUtils.compare(prev, next)) {
30498					// Append nodes between
30499					for (sibling = prev.nextSibling; sibling && sibling != next;) {
30500						tmpSibling = sibling;
30501						sibling = sibling.nextSibling;
30502						prev.appendChild(tmpSibling);
30503					}
30504
30505					// Remove next node
30506					dom.remove(next);
30507
30508					// Move children into prev node
30509					each(grep(next.childNodes), function(node) {
30510						prev.appendChild(node);
30511					});
30512
30513					return prev;
30514				}
30515			}
30516
30517			return next;
30518		}
30519
30520		function getContainer(rng, start) {
30521			var container, offset, lastIdx;
30522
30523			container = rng[start ? 'startContainer' : 'endContainer'];
30524			offset = rng[start ? 'startOffset' : 'endOffset'];
30525
30526			if (container.nodeType == 1) {
30527				lastIdx = container.childNodes.length - 1;
30528
30529				if (!start && offset) {
30530					offset--;
30531				}
30532
30533				container = container.childNodes[offset > lastIdx ? lastIdx : offset];
30534			}
30535
30536			// If start text node is excluded then walk to the next node
30537			if (container.nodeType === 3 && start && offset >= container.nodeValue.length) {
30538				container = new TreeWalker(container, ed.getBody()).next() || container;
30539			}
30540
30541			// If end text node is excluded then walk to the previous node
30542			if (container.nodeType === 3 && !start && offset === 0) {
30543				container = new TreeWalker(container, ed.getBody()).prev() || container;
30544			}
30545
30546			return container;
30547		}
30548
30549		function performCaretAction(type, name, vars, similar) {
30550			var caretContainerId = '_mce_caret', debug = ed.settings.caret_debug;
30551
30552			// Creates a caret container bogus element
30553			function createCaretContainer(fill) {
30554				var caretContainer = dom.create('span', {id: caretContainerId, 'data-mce-bogus': true, style: debug ? 'color:red' : ''});
30555
30556				if (fill) {
30557					caretContainer.appendChild(ed.getDoc().createTextNode(INVISIBLE_CHAR));
30558				}
30559
30560				return caretContainer;
30561			}
30562
30563			function isCaretContainerEmpty(node, nodes) {
30564				while (node) {
30565					if ((node.nodeType === 3 && node.nodeValue !== INVISIBLE_CHAR) || node.childNodes.length > 1) {
30566						return false;
30567					}
30568
30569					// Collect nodes
30570					if (nodes && node.nodeType === 1) {
30571						nodes.push(node);
30572					}
30573
30574					node = node.firstChild;
30575				}
30576
30577				return true;
30578			}
30579
30580			// Returns any parent caret container element
30581			function getParentCaretContainer(node) {
30582				while (node) {
30583					if (node.id === caretContainerId) {
30584						return node;
30585					}
30586
30587					node = node.parentNode;
30588				}
30589			}
30590
30591			// Finds the first text node in the specified node
30592			function findFirstTextNode(node) {
30593				var walker;
30594
30595				if (node) {
30596					walker = new TreeWalker(node, node);
30597
30598					for (node = walker.current(); node; node = walker.next()) {
30599						if (node.nodeType === 3) {
30600							return node;
30601						}
30602					}
30603				}
30604			}
30605
30606			// Removes the caret container for the specified node or all on the current document
30607			function removeCaretContainer(node, move_caret) {
30608				var child, rng;
30609
30610				if (!node) {
30611					node = getParentCaretContainer(selection.getStart());
30612
30613					if (!node) {
30614						while ((node = dom.get(caretContainerId))) {
30615							removeCaretContainer(node, false);
30616						}
30617					}
30618				} else {
30619					rng = selection.getRng(true);
30620
30621					if (isCaretContainerEmpty(node)) {
30622						if (move_caret !== false) {
30623							rng.setStartBefore(node);
30624							rng.setEndBefore(node);
30625						}
30626
30627						dom.remove(node);
30628					} else {
30629						child = findFirstTextNode(node);
30630
30631						if (child.nodeValue.charAt(0) === INVISIBLE_CHAR) {
30632							child.deleteData(0, 1);
30633
30634							// Fix for bug #6976
30635							if (rng.startContainer == child && rng.startOffset > 0) {
30636								rng.setStart(child, rng.startOffset - 1);
30637							}
30638
30639							if (rng.endContainer == child && rng.endOffset > 0) {
30640								rng.setEnd(child, rng.endOffset - 1);
30641							}
30642						}
30643
30644						dom.remove(node, 1);
30645					}
30646
30647					selection.setRng(rng);
30648				}
30649			}
30650
30651			// Applies formatting to the caret position
30652			function applyCaretFormat() {
30653				var rng, caretContainer, textNode, offset, bookmark, container, text;
30654
30655				rng = selection.getRng(true);
30656				offset = rng.startOffset;
30657				container = rng.startContainer;
30658				text = container.nodeValue;
30659
30660				caretContainer = getParentCaretContainer(selection.getStart());
30661				if (caretContainer) {
30662					textNode = findFirstTextNode(caretContainer);
30663				}
30664
30665				// Expand to word is caret is in the middle of a text node and the char before/after is a alpha numeric character
30666				if (text && offset > 0 && offset < text.length && /\w/.test(text.charAt(offset)) && /\w/.test(text.charAt(offset - 1))) {
30667					// Get bookmark of caret position
30668					bookmark = selection.getBookmark();
30669
30670					// Collapse bookmark range (WebKit)
30671					rng.collapse(true);
30672
30673					// Expand the range to the closest word and split it at those points
30674					rng = expandRng(rng, get(name));
30675					rng = rangeUtils.split(rng);
30676
30677					// Apply the format to the range
30678					apply(name, vars, rng);
30679
30680					// Move selection back to caret position
30681					selection.moveToBookmark(bookmark);
30682				} else {
30683					if (!caretContainer || textNode.nodeValue !== INVISIBLE_CHAR) {
30684						caretContainer = createCaretContainer(true);
30685						textNode = caretContainer.firstChild;
30686
30687						rng.insertNode(caretContainer);
30688						offset = 1;
30689
30690						apply(name, vars, caretContainer);
30691					} else {
30692						apply(name, vars, caretContainer);
30693					}
30694
30695					// Move selection to text node
30696					selection.setCursorLocation(textNode, offset);
30697				}
30698			}
30699
30700			function removeCaretFormat() {
30701				var rng = selection.getRng(true), container, offset, bookmark,
30702					hasContentAfter, node, formatNode, parents = [], i, caretContainer;
30703
30704				container = rng.startContainer;
30705				offset = rng.startOffset;
30706				node = container;
30707
30708				if (container.nodeType == 3) {
30709					if (offset != container.nodeValue.length) {
30710						hasContentAfter = true;
30711					}
30712
30713					node = node.parentNode;
30714				}
30715
30716				while (node) {
30717					if (matchNode(node, name, vars, similar)) {
30718						formatNode = node;
30719						break;
30720					}
30721
30722					if (node.nextSibling) {
30723						hasContentAfter = true;
30724					}
30725
30726					parents.push(node);
30727					node = node.parentNode;
30728				}
30729
30730				// Node doesn't have the specified format
30731				if (!formatNode) {
30732					return;
30733				}
30734
30735				// Is there contents after the caret then remove the format on the element
30736				if (hasContentAfter) {
30737					// Get bookmark of caret position
30738					bookmark = selection.getBookmark();
30739
30740					// Collapse bookmark range (WebKit)
30741					rng.collapse(true);
30742
30743					// Expand the range to the closest word and split it at those points
30744					rng = expandRng(rng, get(name), true);
30745					rng = rangeUtils.split(rng);
30746
30747					// Remove the format from the range
30748					remove(name, vars, rng);
30749
30750					// Move selection back to caret position
30751					selection.moveToBookmark(bookmark);
30752				} else {
30753					caretContainer = createCaretContainer();
30754
30755					node = caretContainer;
30756					for (i = parents.length - 1; i >= 0; i--) {
30757						node.appendChild(dom.clone(parents[i], false));
30758						node = node.firstChild;
30759					}
30760
30761					// Insert invisible character into inner most format element
30762					node.appendChild(dom.doc.createTextNode(INVISIBLE_CHAR));
30763					node = node.firstChild;
30764
30765					var block = dom.getParent(formatNode, isTextBlock);
30766
30767					if (block && dom.isEmpty(block)) {
30768						// Replace formatNode with caretContainer when removing format from empty block like <p><b>|</b></p>
30769						formatNode.parentNode.replaceChild(caretContainer, formatNode);
30770					} else {
30771						// Insert caret container after the formatted node
30772						dom.insertAfter(caretContainer, formatNode);
30773					}
30774
30775					// Move selection to text node
30776					selection.setCursorLocation(node, 1);
30777
30778					// If the formatNode is empty, we can remove it safely.
30779					if (dom.isEmpty(formatNode)) {
30780						dom.remove(formatNode);
30781					}
30782				}
30783			}
30784
30785			// Checks if the parent caret container node isn't empty if that is the case it
30786			// will remove the bogus state on all children that isn't empty
30787			function unmarkBogusCaretParents() {
30788				var caretContainer;
30789
30790				caretContainer = getParentCaretContainer(selection.getStart());
30791				if (caretContainer && !dom.isEmpty(caretContainer)) {
30792					walk(caretContainer, function(node) {
30793						if (node.nodeType == 1 && node.id !== caretContainerId && !dom.isEmpty(node)) {
30794							dom.setAttrib(node, 'data-mce-bogus', null);
30795						}
30796					}, 'childNodes');
30797				}
30798			}
30799
30800			// Only bind the caret events once
30801			if (!ed._hasCaretEvents) {
30802				// Mark current caret container elements as bogus when getting the contents so we don't end up with empty elements
30803				markCaretContainersBogus = function() {
30804					var nodes = [], i;
30805
30806					if (isCaretContainerEmpty(getParentCaretContainer(selection.getStart()), nodes)) {
30807						// Mark children
30808						i = nodes.length;
30809						while (i--) {
30810							dom.setAttrib(nodes[i], 'data-mce-bogus', '1');
30811						}
30812					}
30813				};
30814
30815				disableCaretContainer = function(e) {
30816					var keyCode = e.keyCode;
30817
30818					removeCaretContainer();
30819
30820					// Remove caret container if it's empty
30821					if (keyCode == 8 && selection.isCollapsed() && selection.getStart().innerHTML == INVISIBLE_CHAR) {
30822						removeCaretContainer(getParentCaretContainer(selection.getStart()));
30823					}
30824
30825					// Remove caret container on keydown and it's left/right arrow keys
30826					if (keyCode == 37 || keyCode == 39) {
30827						removeCaretContainer(getParentCaretContainer(selection.getStart()));
30828					}
30829
30830					unmarkBogusCaretParents();
30831				};
30832
30833				// Remove bogus state if they got filled by contents using editor.selection.setContent
30834				ed.on('SetContent', function(e) {
30835					if (e.selection) {
30836						unmarkBogusCaretParents();
30837					}
30838				});
30839				ed._hasCaretEvents = true;
30840			}
30841
30842			// Do apply or remove caret format
30843			if (type == "apply") {
30844				applyCaretFormat();
30845			} else {
30846				removeCaretFormat();
30847			}
30848		}
30849
30850		/**
30851		 * Moves the start to the first suitable text node.
30852		 */
30853		function moveStart(rng) {
30854			var container = rng.startContainer,
30855					offset = rng.startOffset, isAtEndOfText,
30856					walker, node, nodes, tmpNode;
30857
30858			if (rng.startContainer == rng.endContainer) {
30859				if (isInlineBlock(rng.startContainer.childNodes[rng.startOffset])) {
30860					return;
30861				}
30862			}
30863
30864			// Convert text node into index if possible
30865			if (container.nodeType == 3 && offset >= container.nodeValue.length) {
30866				// Get the parent container location and walk from there
30867				offset = nodeIndex(container);
30868				container = container.parentNode;
30869				isAtEndOfText = true;
30870			}
30871
30872			// Move startContainer/startOffset in to a suitable node
30873			if (container.nodeType == 1) {
30874				nodes = container.childNodes;
30875				container = nodes[Math.min(offset, nodes.length - 1)];
30876				walker = new TreeWalker(container, dom.getParent(container, dom.isBlock));
30877
30878				// If offset is at end of the parent node walk to the next one
30879				if (offset >
30879 nodes.length - 1 || isAtEndOfText) {
30880					walker.next();
30881				}
30882
30883				for (node = walker.current(); node; node = walker.next()) {
30884					if (node.nodeType == 3 && !isWhiteSpaceNode(node)) {
30885						// IE has a "neat" feature where it moves the start node into the closest element
30886						// we can avoid this by inserting an element before it and then remove it after we set the selection
30887						tmpNode = dom.create('a', {'data-mce-bogus': 'all'}, INVISIBLE_CHAR);
30888						node.parentNode.insertBefore(tmpNode, node);
30889
30890						// Set selection and remove tmpNode
30891						rng.setStart(node, 0);
30892						selection.setRng(rng);
30893						dom.remove(tmpNode);
30894
30895						return;
30896					}
30897				}
30898			}
30899		}
30900	};
30901});
30902
30903// Included from: js/tinymce/classes/UndoManager.js
30904
30905/**
30906 * UndoManager.js
30907 *
30908 * Released under LGPL License.
30909 * Copyright (c) 1999-2015 Ephox Corp. All rights reserved
30910 *
30911 * License: http://www.tinymce.com/license
30912 * Contributing: http://www.tinymce.com/contributing
30913 */
30914
30915/**
30916 * This class handles the undo/redo history levels for the editor. Since the built-in undo/redo has major drawbacks a custom one was needed.
30917 *
30918 * @class tinymce.UndoManager
30919 */
30920define("tinymce/UndoManager", [
30921	"tinymce/util/VK",
30922	"tinymce/Env"
30923], function(VK, Env) {
30924	return function(editor) {
30925		var self = this, index = 0, data = [], beforeBookmark, isFirstTypedCharacter, locks = 0;
30926
30927		function getContent() {
30928			return editor.serializer.getTrimmedContent();
30929		}
30930
30931		function setDirty(state) {
30932			editor.setDirty(state);
30933		}
30934
30935		function addNonTypingUndoLevel(e) {
30936			self.typing = false;
30937			self.add({}, e);
30938		}
30939
30940		// Add initial undo level when the editor is initialized
30941		editor.on('init', function() {
30942			self.add();
30943		});
30944
30945		// Get position before an execCommand is processed
30946		editor.on('BeforeExecCommand', function(e) {
30947			var cmd = e.command;
30948
30949			if (cmd != 'Undo' && cmd != 'Redo' && cmd != 'mceRepaint') {
30950				self.beforeChange();
30951			}
30952		});
30953
30954		// Add undo level after an execCommand call was made
30955		editor.on('ExecCommand', function(e) {
30956			var cmd = e.command;
30957
30958			if (cmd != 'Undo' && cmd != 'Redo' && cmd != 'mceRepaint') {
30959				addNonTypingUndoLevel(e);
30960			}
30961		});
30962
30963		editor.on('ObjectResizeStart Cut', function() {
30964			self.beforeChange();
30965		});
30966
30967		editor.on('SaveContent ObjectResized blur', addNonTypingUndoLevel);
30968		editor.on('DragEnd', addNonTypingUndoLevel);
30969
30970		editor.on('KeyUp', function(e) {
30971			var keyCode = e.keyCode;
30972
30973			// If key is prevented then don't add undo level
30974			// This would happen on keyboard shortcuts for example
30975			if (e.isDefaultPrevented()) {
30976				return;
30977			}
30978
30979			if ((keyCode >= 33 && keyCode <= 36) || (keyCode >= 37 && keyCode <= 40) || keyCode == 45 || keyCode == 13 || e.ctrlKey) {
30980				addNonTypingUndoLevel();
30981				editor.nodeChanged();
30982			}
30983
30984			if (keyCode == 46 || keyCode == 8 || (Env.mac && (keyCode == 91 || keyCode == 93))) {
30985				editor.nodeChanged();
30986			}
30987
30988			// Fire a TypingUndo event on the first character entered
30989			if (isFirstTypedCharacter && self.typing) {
30990				// Make it dirty if the content was changed after typing the first character
30991				if (!editor.isDirty()) {
30992					setDirty(data[0] && getContent() != data[0].content);
30993
30994					// Fire initial change event
30995					if (editor.isDirty()) {
30996						editor.fire('change', {level: data[0], lastLevel: null});
30997					}
30998				}
30999
31000				editor.fire('TypingUndo');
31001				isFirstTypedCharacter = false;
31002				editor.nodeChanged();
31003			}
31004		});
31005
31006		editor.on('KeyDown', function(e) {
31007			var keyCode = e.keyCode;
31008
31009			// If key is prevented then don't add undo level
31010			// This would happen on keyboard shortcuts for example
31011			if (e.isDefaultPrevented()) {
31012				return;
31013			}
31014
31015			// Is character position keys left,right,up,down,home,end,pgdown,pgup,enter
31016			if ((keyCode >= 33 && keyCode <= 36) || (keyCode >= 37 && keyCode <= 40) || keyCode == 45) {
31017				if (self.typing) {
31018					addNonTypingUndoLevel(e);
31019				}
31020
31021				return;
31022			}
31023
31024			// If key isn't Ctrl+Alt/AltGr
31025			var modKey = (e.ctrlKey && !e.altKey) || e.metaKey;
31026			if ((keyCode < 16 || keyCode > 20) && keyCode != 224 && keyCode != 91 && !self.typing && !modKey) {
31027				self.beforeChange();
31028				self.typing = true;
31029				self.add({}, e);
31030				isFirstTypedCharacter = true;
31031			}
31032		});
31033
31034		editor.on('MouseDown', function(e) {
31035			if (self.typing) {
31036				addNonTypingUndoLevel(e);
31037			}
31038		});
31039
31040		// Add keyboard shortcuts for undo/redo keys
31041		editor.addShortcut('meta+z', '', 'Undo');
31042		editor.addShortcut('meta+y,meta+shift+z', '', 'Redo');
31043
31044		editor.on('AddUndo Undo Redo ClearUndos', function(e) {
31045			if (!e.isDefaultPrevented()) {
31046				editor.nodeChanged();
31047			}
31048		});
31049
31050		/*eslint consistent-this:0 */
31051		self = {
31052			// Explode for debugging reasons
31053			data: data,
31054
31055			/**
31056			 * State if the user is currently typing or not. This will add a typing operation into one undo
31057			 * level instead of one new level for each keystroke.
31058			 *
31059			 * @field {Boolean} typing
31060			 */
31061			typing: false,
31062
31063			/**
31064			 * Stores away a bookmark to be used when performing an undo action so that the selection is before
31065			 * the change has been made.
31066			 *
31067			 * @method beforeChange
31068			 */
31069			beforeChange: function() {
31070				if (!locks) {
31071					beforeBookmark = editor.selection.getBookmark(2, true);
31072				}
31073			},
31074
31075			/**
31076			 * Adds a new undo level/snapshot to the undo list.
31077			 *
31078			 * @method add
31079			 * @param {Object} level Optional undo level object to add.
31080			 * @param {DOMEvent} event Optional event responsible for the creation of the undo level.
31081			 * @return {Object} Undo level that got added or null it a level wasn't needed.
31082			 */
31083			add: function(level, event) {
31084				var i, settings = editor.settings, lastLevel;
31085
31086				level = level || {};
31087				level.content = getContent();
31088
31089				if (locks || editor.removed) {
31090					return null;
31091				}
31092
31093				lastLevel = data[index];
31094				if (editor.fire('BeforeAddUndo', {level: level, lastLevel: lastLevel, originalEvent: event}).isDefaultPrevented()) {
31095					return null;
31096				}
31097
31098				// Add undo level if needed
31099				if (lastLevel && lastLevel.content == level.content) {
31100					return null;
31101				}
31102
31103				// Set before bookmark on previous level
31104				if (data[index]) {
31105					data[index].beforeBookmark = beforeBookmark;
31106				}
31107
31108				// Time to compress
31109				if (settings.custom_undo_redo_levels) {
31110					if (data.length > settings.custom_undo_redo_levels) {
31111						for (i = 0; i < data.length - 1; i++) {
31112							data[i] = data[i + 1];
31113						}
31114
31115						data.length--;
31116						index = data.length;
31117					}
31118				}
31119
31120				// Get a non intrusive normalized bookmark
31121				level.bookmark = editor.selection.getBookmark(2, true);
31122
31123				// Crop array if needed
31124				if (index < data.length - 1) {
31125					data.length = index + 1;
31126				}
31127
31128				data.push(level);
31129				index = data.length - 1;
31130
31131				var args = {level: level, lastLevel: lastLevel, originalEvent: event};
31132
31133				editor.fire('AddUndo', args);
31134
31135				if (index > 0) {
31136					setDirty(true);
31137					editor.fire('change', args);
31138				}
31139
31140				return level;
31141			},
31142
31143			/**
31144			 * Undoes the last action.
31145			 *
31146			 * @method undo
31147			 * @return {Object} Undo level or null if no undo was performed.
31148			 */
31149			undo: function() {
31150				var level;
31151
31152				if (self.typing) {
31153					self.add();
31154					self.typing = false;
31155				}
31156
31157				if (index > 0) {
31158					level = data[--index];
31159
31160					editor.setContent(level.content, {format: 'raw'});
31161					editor.selection.moveToBookmark(level.beforeBookmark);
31162					setDirty(true);
31163
31164					editor.fire('undo', {level: level});
31165				}
31166
31167				return level;
31168			},
31169
31170			/**
31171			 * Redoes the last action.
31172			 *
31173			 * @method redo
31174			 * @return {Object} Redo level or null if no redo was performed.
31175			 */
31176			redo: function() {
31177				var level;
31178
31179				if (index < data.length - 1) {
31180					level = data[++index];
31181
31182					editor.setContent(level.content, {format: 'raw'});
31183					editor.selection.moveToBookmark(level.bookmark);
31184					setDirty(true);
31185
31186					editor.fire('redo', {level: level});
31187				}
31188
31189				return level;
31190			},
31191
31192			/**
31193			 * Removes all undo levels.
31194			 *
31195			 * @method clear
31196			 */
31197			clear: function() {
31198				data = [];
31199				index = 0;
31200				self.typing = false;
31201				editor.fire('ClearUndos');
31202			},
31203
31204			/**
31205			 * Returns true/false if the undo manager has any undo levels.
31206			 *
31207			 * @method hasUndo
31208			 * @return {Boolean} true/false if the undo manager has any undo levels.
31209			 */
31210			hasUndo: function() {
31211				// Has undo levels or typing and content isn't the same as the initial level
31212				return index > 0 || (self.typing && data[0] && getContent() != data[0].content);
31213			},
31214
31215			/**
31216			 * Returns true/false if the undo manager has any redo levels.
31217			 *
31218			 * @method hasRedo
31219			 * @return {Boolean} true/false if the undo manager has any redo levels.
31220			 */
31221			hasRedo: function() {
31222				return index < data.length - 1 && !this.typing;
31223			},
31224
31225			/**
31226			 * Executes the specified function in an undo translation. The selection
31227			 * before the modification will be stored to the undo stack and if the DOM changes
31228			 * it will add a new undo level. Any methods within the translation that adds undo levels will
31229			 * be ignored. So a translation can include calls to execCommand or editor.insertContent.
31230			 *
31231			 * @method transact
31232			 * @param {function} callback Function to execute dom manipulation logic in.
31233			 */
31234			transact: function(callback) {
31235				self.beforeChange();
31236
31237				try {
31238					locks++;
31239					callback();
31240				} finally {
31241					locks--;
31242				}
31243
31244				self.add();
31245			}
31246		};
31247
31248		return self;
31249	};
31250});
31251
31252// Included from: js/tinymce/classes/EnterKey.js
31253
31254/**
31255 * EnterKey.js
31256 *
31257 * Released under LGPL License.
31258 * Copyright (c) 1999-2015 Ephox Corp. All rights reserved
31259 *
31260 * License: http://www.tinymce.com/license
31261 * Contributing: http://www.tinymce.com/contributing
31262 */
31263
31264/**
31265 * Contains logic for handling the enter key to split/generate block elements.
31266 *
31267 * @private
31268 * @class tinymce.EnterKey
31269 */
31270define("tinymce/EnterKey", [
31271	"tinymce/dom/TreeWalker",
31272	"tinymce/dom/RangeUtils",
31273	"tinymce/Env"
31274], function(TreeWalker, RangeUtils, Env) {
31275	var isIE = Env.ie && Env.ie < 11;
31276
31277	return function(editor) {
31278		var dom = editor.dom, selection = editor.selection, settings = editor.settings;
31279		var undoManager = editor.undoManager, schema = editor.schema, nonEmptyElementsMap = schema.getNonEmptyElements(),
31280			moveCaretBeforeOnEnterElementsMap = schema.getMoveCaretBeforeOnEnterElements();
31281
31282		function handleEnterKey(evt) {
31283			var rng, tmpRng, editableRoot, container, offset, parentBlock, documentMode, shiftKey,
31284				newBlock, fragment, containerBlock, parentBlockName, containerBlockName, newBlockName, isAfterLastNodeInContainer;
31285
31286			// Returns true if the block can be split into two blocks or not
31287			function canSplitBlock(node) {
31288				return node &&
31289					dom.isBlock(node) &&
31290					!/^(TD|TH|CAPTION|FORM)$/.test(node.nodeName) &&
31291					!/^(fixed|absolute)/i.test(node.style.position) &&
31292					dom.getContentEditable(node) !== "true";
31293			}
31294
31295			function isTableCell(node) {
31296				return node && /^(TD|TH|CAPTION)$/.test(node.nodeName);
31297			}
31298
31299			// Renders empty block on IE
31300			function renderBlockOnIE(block) {
31301				var oldRng;
31302
31303				if (dom.isBlock(block)) {
31304					oldRng = selection.getRng();
31305					block.appendChild(dom.create('span', null, '\u00a0'));
31306					selection.select(block);
31307					block.lastChild.outerHTML = '';
31308					selection.setRng(oldRng);
31309				}
31310			}
31311
31312			// Remove the first empty inline element of the block so this: <p><b><em></em></b>x</p> becomes this: <p>
31312x</p>
31313			function trimInlineElementsOnLeftSideOfBlock(block) {
31314				var node = block, firstChilds = [], i;
31315
31316				if (!node) {
31317					return;
31318				}
31319
31320				// Find inner most first child ex: <p><i><b>*</b></i></p>
31321				while ((node = node.firstChild)) {
31322					if (dom.isBlock(node)) {
31323						return;
31324					}
31325
31326					if (node.nodeType == 1 && !nonEmptyElementsMap[node.nodeName.toLowerCase()]) {
31327						firstChilds.push(node);
31328					}
31329				}
31330
31331				i = firstChilds.length;
31332				while (i--) {
31333					node = firstChilds[i];
31334					if (!node.hasChildNodes() || (node.firstChild == node.lastChild && node.firstChild.nodeValue === '')) {
31335						dom.remove(node);
31336					} else {
31337						// Remove <a> </a> see #5381
31338						if (node.nodeName == "A" && (node.innerText || node.textContent) === ' ') {
31339							dom.remove(node);
31340						}
31341					}
31342				}
31343			}
31344
31345			// Moves the caret to a suitable position within the root for example in the first non
31346			// pure whitespace text node or before an image
31347			function moveToCaretPosition(root) {
31348				var walker, node, rng, lastNode = root, tempElm;
31349				function firstNonWhiteSpaceNodeSibling(node) {
31350					while (node) {
31351						if (node.nodeType == 1 || (node.nodeType == 3 && node.data && /[\r\n\s]/.test(node.data))) {
31352							return node;
31353						}
31354
31355						node = node.nextSibling;
31356					}
31357				}
31358
31359				if (!root) {
31360					return;
31361				}
31362
31363				// Old IE versions doesn't properly render blocks with br elements in them
31364				// For example <p><br></p> wont be rendered correctly in a contentEditable area
31365				// until you remove the br producing <p></p>
31366				if (Env.ie && Env.ie < 9 && parentBlock && parentBlock.firstChild) {
31367					if (parentBlock.firstChild == parentBlock.lastChild && parentBlock.firstChild.tagName == 'BR') {
31368						dom.remove(parentBlock.firstChild);
31369					}
31370				}
31371
31372				if (/^(LI|DT|DD)$/.test(root.nodeName)) {
31373					var firstChild = firstNonWhiteSpaceNodeSibling(root.firstChild);
31374
31375					if (firstChild && /^(UL|OL|DL)$/.test(firstChild.nodeName)) {
31376						root.insertBefore(dom.doc.createTextNode('\u00a0'), root.firstChild);
31377					}
31378				}
31379
31380				rng = dom.createRng();
31381
31382				// Normalize whitespace to remove empty text nodes. Fix for: #6904
31383				// Gecko will be able to place the caret in empty text nodes but it won't render propery
31384				// Older IE versions will sometimes crash so for now ignore all IE versions
31385				if (!Env.ie) {
31386					root.normalize();
31387				}
31388
31389				if (root.hasChildNodes()) {
31390					walker = new TreeWalker(root, root);
31391
31392					while ((node = walker.current())) {
31393						if (node.nodeType == 3) {
31394							rng.setStart(node, 0);
31395							rng.setEnd(node, 0);
31396							break;
31397						}
31398
31399						if (moveCaretBeforeOnEnterElementsMap[node.nodeName.toLowerCase()]) {
31400							rng.setStartBefore(node);
31401							rng.setEndBefore(node);
31402							break;
31403						}
31404
31405						lastNode = node;
31406						node = walker.next();
31407					}
31408
31409					if (!node) {
31410						rng.setStart(lastNode, 0);
31411						rng.setEnd(lastNode, 0);
31412					}
31413				} else {
31414					if (root.nodeName == 'BR') {
31415						if (root.nextSibling && dom.isBlock(root.nextSibling)) {
31416							// Trick on older IE versions to render the caret before the BR between two lists
31417							if (!documentMode || documentMode < 9) {
31418								tempElm = dom.create('br');
31419								root.parentNode.insertBefore(tempElm, root);
31420							}
31421
31422							rng.setStartBefore(root);
31423							rng.setEndBefore(root);
31424						} else {
31425							rng.setStartAfter(root);
31426							rng.setEndAfter(root);
31427						}
31428					} else {
31429						rng.setStart(root, 0);
31430						rng.setEnd(root, 0);
31431					}
31432				}
31433
31434				selection.setRng(rng);
31435
31436				// Remove tempElm created for old IE:s
31437				dom.remove(tempElm);
31438				selection.scrollIntoView(root);
31439			}
31440
31441			function setForcedBlockAttrs(node) {
31442				var forcedRootBlockName = settings.forced_root_block;
31443
31444				if (forcedRootBlockName && forcedRootBlockName.toLowerCase() === node.tagName.toLowerCase()) {
31445					dom.setAttribs(node, settings.forced_root_block_attrs);
31446				}
31447			}
31448
31449			function emptyBlock(elm) {
31450				// BR is needed in empty blocks on non IE browsers
31451				elm.innerHTML = !isIE ? '<br data-mce-bogus="1">' : '';
31452			}
31453
31454			// Creates a new block element by cloning the current one or creating a new one if the name is specified
31455			// This function will also copy any text formatting from the parent block and add it to the new one
31456			function createNewBlock(name) {
31457				var node = container, block, clonedNode, caretNode, textInlineElements = schema.getTextInlineElements();
31458
31459				if (name || parentBlockName == "TABLE") {
31460					block = dom.create(name || newBlockName);
31461					setForcedBlockAttrs(block);
31462				} else {
31463					block = parentBlock.cloneNode(false);
31464				}
31465
31466				caretNode = block;
31467
31468				// Clone any parent styles
31469				if (settings.keep_styles !== false) {
31470					do {
31471						if (textInlineElements[node.nodeName]) {
31472							// Never clone a caret containers
31473							if (node.id == '_mce_caret') {
31474								continue;
31475							}
31476
31477							clonedNode = node.cloneNode(false);
31478							dom.setAttrib(clonedNode, 'id', ''); // Remove ID since it needs to be document unique
31479
31480							if (block.hasChildNodes()) {
31481								clonedNode.appendChild(block.firstChild);
31482								block.appendChild(clonedNode);
31483							} else {
31484								caretNode = clonedNode;
31485								block.appendChild(clonedNode);
31486							}
31487						}
31488					} while ((node = node.parentNode));
31489				}
31490
31491				// BR is needed in empty blocks on non IE browsers
31492				if (!isIE) {
31493					caretNode.innerHTML = '<br data-mce-bogus="1">';
31494				}
31495
31496				return block;
31497			}
31498
31499			// Returns true/false if the caret is at the start/end of the parent block element
31500			function isCaretAtStartOrEndOfBlock(start) {
31501				var walker, node, name;
31502
31503				// Caret is in the middle of a text node like "a|b"
31504				if (container.nodeType == 3 && (start ? offset > 0 : offset < container.nodeValue.length)) {
31505					return false;
31506				}
31507
31508				// If after the last element in block node edge case for #5091
31509				if (container.parentNode == parentBlock && isAfterLastNodeInContainer && !start) {
31510					return true;
31511				}
31512
31513				// If the caret if before the first element in parentBlock
31514				if (start && container.nodeType == 1 && container == parentBlock.firstChild) {
31515					return true;
31516				}
31517
31518				// Caret can be before/after a table
31519				if (container.nodeName === "TABLE" || (container.previousSibling && container.previousSibling.nodeName == "TABLE")) {
31520					return (isAfterLastNodeInContainer && !start) || (!isAfterLastNodeInContainer && start);
31521				}
31522
31523				// Walk the DOM and look for text nodes or non empty elements
31524				walker = new TreeWalker(container, parentBlock);
31525
31526				// If caret is in beginning or end of a text block then jump to the next/previous node
31527				if (container.nodeType == 3) {
31528					if (start && offset === 0) {
31529						walker.prev();
31530					} else if (!start && offset == container.nodeValue.length) {
31531						walker.next();
31532					}
31533				}
31534
31535				while ((node = walker.current())) {
31536					if (node.nodeType === 1) {
31537						// Ignore bogus elements
31538						if (!node.getAttribute('data-mce-bogus')) {
31539							// Keep empty elements like <img /> <input /> but not trailing br:s like <p>text|<br></p>
31540							name = node.nodeName.toLowerCase();
31541							if (nonEmptyElementsMap[name] && name !== 'br') {
31542								return false;
31543							}
31544						}
31545					} else if (node.nodeType === 3 && !/^[ \t\r\n]*$/.test(node.nodeValue)) {
31546						return false;
31547					}
31548
31549					if (start) {
31550						walker.prev();
31551					} else {
31552						walker.next();
31553					}
31554				}
31555
31556				return true;
31557			}
31558
31559			// Wraps any text nodes or inline elements in the specified forced root block name
31560			function wrapSelfAndSiblingsInDefaultBlock(container, offset) {
31561				var newBlock, parentBlock, startNode, node, next, rootBlockName, blockName = newBlockName || 'P';
31562
31563				// Not in a block element or in a table cell or caption
31564				parentBlock = dom.getParent(container, dom.isBlock);
31565				if (!parentBlock || !canSplitBlock(parentBlock)) {
31566					parentBlock = parentBlock || editableRoot;
31567
31568					if (parentBlock == editor.getBody() || isTableCell(parentBlock)) {
31569						rootBlockName = parentBlock.nodeName.toLowerCase();
31570					} else {
31571						rootBlockName = parentBlock.parentNode.nodeName.toLowerCase();
31572					}
31573
31574					if (!parentBlock.hasChildNodes()) {
31575						newBlock = dom.create(blockName);
31576						setForcedBlockAttrs(newBlock);
31577						parentBlock.appendChild(newBlock);
31578						rng.setStart(newBlock, 0);
31579						rng.setEnd(newBlock, 0);
31580						return newBlock;
31581					}
31582
31583					// Find parent that is the first child of parentBlock
31584					node = container;
31585					while (node.parentNode != parentBlock) {
31586						node = node.parentNode;
31587					}
31588
31589					// Loop left to find start node start wrapping at
31590					while (node && !dom.isBlock(node)) {
31591						startNode = node;
31592						node = node.previousSibling;
31593					}
31594
31595					if (startNode && schema.isValidChild(rootBlockName, blockName.toLowerCase())) {
31596						newBlock = dom.create(blockName);
31597						setForcedBlockAttrs(newBlock);
31598						startNode.parentNode.insertBefore(newBlock, startNode);
31599
31600						// Start wrapping until we hit a block
31601						node = startNode;
31602						while (node && !dom.isBlock(node)) {
31603							next = node.nextSibling;
31604							newBlock.appendChild(node);
31605							node = next;
31606						}
31607
31608						// Restore range to it's past location
31609						rng.setStart(container, offset);
31610						rng.setEnd(container, offset);
31611					}
31612				}
31613
31614				return container;
31615			}
31616
31617			// Inserts a block or br before/after or in the middle of a split list of the LI is empty
31618			function handleEmptyListItem() {
31619				function isFirstOrLastLi(first) {
31620					var node = containerBlock[first ? 'firstChild' : 'lastChild'];
31621
31622					// Find first/last element since there might be whitespace there
31623					while (node) {
31624						if (node.nodeType == 1) {
31625							break;
31626						}
31627
31628						node = node[first ? 'nextSibling' : 'previousSibling'];
31629					}
31630
31631					return node === parentBlock;
31632				}
31633
31634				function getContainerBlock() {
31635					var containerBlockParent = containerBlock.parentNode;
31636
31637					if (/^(LI|DT|DD)$/.test(containerBlockParent.nodeName)) {
31638						return containerBlockParent;
31639					}
31640
31641					return containerBlock;
31642				}
31643
31644				if (containerBlock == editor.getBody()) {
31645					return;
31646				}
31647
31648				// Check if we are in an nested list
31649				var containerBlockParentName = containerBlock.parentNode.nodeName;
31650				if (/^(OL|UL|LI)$/.test(containerBlockParentName)) {
31651					newBlockName = 'LI';
31652				}
31653
31654				newBlock = newBlockName ? createNewBlock(newBlockName) : dom.create('BR');
31655
31656				if (isFirstOrLastLi(true) && isFirstOrLastLi()) {
31657					if (containerBlockParentName == 'LI') {
31658						// Nested list is inside a LI
31659						dom.insertAfter(newBlock, getContainerBlock());
31660					} else {
31661						// Is first and last list item then replace the OL/UL with a text block
31662						dom.replace(newBlock, containerBlock);
31663					}
31664				} else if (isFirstOrLastLi(true)) {
31665					if (containerBlockParentName == 'LI') {
31666						// List nested in an LI then move the list to a new sibling LI
31667						dom.insertAfter(newBlock, getContainerBlock());
31668						newBlock.appendChild(dom.doc.createTextNode(' ')); // Needed for IE so the caret can be placed
31669						newBlock.appendChild(containerBlock);
31670					} else {
31671						// First LI in list then remove LI and add text block before list
31672						containerBlock.parentNode.insertBefore(newBlock, containerBlock);
31673					}
31674				} else if (isFirstOrLastLi()) {
31675					// Last LI in list then remove LI and add text block after list
31676					dom.insertAfter(newBlock, getContainerBlock());
31677					renderBlockOnIE(newBlock);
31678				} else {
31679					// Middle LI in list the split the list and insert a text block in the middle
31680					// Extract after fragment and insert it after the current block
31681					containerBlock = getContainerBlock();
31682					tmpRng = rng.cloneRange();
31683					tmpRng.setStartAfter(parentBlock);
31684					tmpRng.setEndAfter(containerBlock);
31685					fragment = tmpRng.extractContents();
31686
31687					if (newBlockName == 'LI' && fragment.firstChild.nodeName == 'LI') {
31688						newBlock = fragment.firstChild;
31689						dom.insertAfter(fragment, containerBlock);
31690					} else {
31691						dom.insertAfter(fragment, containerBlock);
31692						dom.insertAfter(newBlock, containerBlock);
31693					}
31694				}
31695
31696				dom.remove(parentBlock);
31697				moveToCaretPosition(newBlock);
31698				undoManager.add();
31699			}
31700
31701			// Inserts a BR element if the forced_root_block option is set to false or empty string
31702			function insertBr() {
31703				editor.execCommand("InsertLineBreak", false, evt);
31704			}
31705
31706			// Trims any linebreaks at the beginning of node user for example when pressing enter in a PRE element
31707			function trimLeadingLineBreaks(node) {
31708				do {
31709					if (node.nodeType === 3) {
31710						node.nodeValue = node.nodeValue.replace(/^[\r\n]+/, '');
31711					}
31712
31713					node = node.firstChild;
31714				} while (node);
31715			}
31716
31717			function getEditableRoot(node) {
31718				var root = dom.getRoot(), parent, editableRoot;
31719
31720				// Get all parents until we hit a non editable parent or the root
31721				parent = node;
31722				while (parent !== root && dom.getContentEditable(parent) !== "false") {
31723					if (dom.getContentEditable(parent) === "true") {
31724						editableRoot = parent;
31725					}
31726
31727					parent = parent.parentNode;
31728				}
31729
31730				return parent !== root ? editableRoot : root;
31731			}
31732
31733			// Adds a BR at the end of blocks that only contains an IMG or INPUT since
31734			// these might be floated and then they won't expand the block
31735			function addBrToBlockIfNeeded(block) {
31736				var lastChild;
31737
31738				// IE will render the blocks correctly other browsers needs a BR
31739				if (!isIE) {
31740					block.normalize(); // Remove empty text nodes that got left behind by the extract
31741
31742					// Check if the block is empty or contains a floated last child
31743					lastChild = block.lastChild;
31744					if (!lastChild || (/^(left|right)$/gi.test(dom.getStyle(lastChild, 'float', true)))) {
31745						dom.add(block, 'br');
31746					}
31747				}
31748			}
31749
31750			function insertNewBlockAfter() {
31751				// If the caret is at the end of a header we produce a P tag after it similar to Word unless we are in a hgroup
31752				if (/^(H[1-6]|PRE|FIGURE)$/.test(parentBlockName) && containerBlockName != 'HGROUP') {
31753					newBlock = createNewBlock(newBlockName);
31754				} else {
31755					newBlock = createNewBlock();
31756				}
31757
31758				// Split the current container block element if enter is pressed inside an empty inner block element
31759				if (settings.end_container_on_empty_block && canSplitBlock(containerBlock) && dom.isEmpty(parentBlock)) {
31760					// Split container block for example a BLOCKQUOTE at the current blockParent location for example a P
31761					newBlock = dom.split(containerBlock, parentBlock);
31762				} else {
31763					dom.insertAfter(newBlock, parentBlock);
31764				}
31765
31766				moveToCaretPosition(newBlock);
31767			}
31768
31769			rng = selection.getRng(true);
31770
31771			// Event is blocked by some other handler for example the lists plugin
31772			if (evt.isDefaultPrevented()) {
31773				return;
31774			}
31775
31776			// Delete any selected contents
31777			if (!rng.collapsed) {
31778				editor.execCommand('Delete');
31779				return;
31780			}
31781
31782			// Setup range items and newBlockName
31783			new RangeUtils(dom).normalize(rng);
31784			container = rng.startContainer;
31785			offset = rng.startOffset;
31786			newBlockName = (settings.force_p_newlines ? 'p' : '') || settings.forced_root_block;
31787			newBlockName = newBlockName ? newBlockName.toUpperCase() : '';
31788			documentMode = dom.doc.documentMode;
31789			shiftKey = evt.shiftKey;
31790
31791			// Resolve node index
31792			if (container.nodeType == 1 && container.hasChildNodes()) {
31793				isAfterLastNodeInContainer = offset > container.childNodes.length - 1;
31794
31795				container = container.childNodes[Math.min(offset, container.childNodes.length - 1)] || container;
31796				if (isAfterLastNodeInContainer && container.nodeType == 3) {
31797					offset = container.nodeValue.length;
31798				} else {
31799					offset = 0;
31800				}
31801			}
31802
31803			// Get editable root node, normally the body element but sometimes a div or span
31804			editableRoot = getEditableRoot(container);
31805
31806			// If there is no editable root then enter is done inside a contentEditable false element
31807			if (!editableRoot) {
31808				return;
31809			}
31810
31811			undoManager.beforeChange();
31812
31813			// If editable root isn't block nor the root of the editor
31814			if (!dom.isBlock(editableRoot) && editableRoot != dom.getRoot()) {
31815				if (!newBlockName || shiftKey) {
31816					insertBr();
31817				}
31818
31819				return;
31820			}
31821
31822			// Wrap the current node and it's sibling in a default block if it's needed.
31823			// for example this <td>text|<b>text2</b></td> will become this <td><p>text|<b>text2</p></b></td>
31824			// This won't happen if root blocks are disabled or the shiftKey is pressed
31825			if ((newBlockName && !shiftKey) || (!newBlockName && shiftKey)) {
31826				container = wrapSelfAndSiblingsInDefaultBlock(container, offset);
31827			}
31828
31829			// Find parent block and setup empty block paddings
31830			parentBlock = dom.getParent(container, dom.isBlock);
31831			containerBlock = parentBlock ? dom.getParent(parentBlock.parentNode, dom.isBlock) : null;
31832
31833			// Setup block names
31834			parentBlockName = parentBlock ? parentBlock.nodeName.toUpperCase() : ''; // IE < 9 & HTML5
31835			containerBlockName = containerBlock ? containerBlock.nodeName.toUpperCase() : ''; // IE < 9 & HTML5
31836
31837			// Enter inside block contained within a LI then split or insert before/after LI
31838			if (containerBlockName == 'LI' && !evt.ctrlKey) {
31839				parentBlock = containerBlock;
31840				parentBlockName = containerBlockName;
31841			}
31842
31843			// Handle enter in list item
31844			if (/^(LI|DT|DD)$/.test(parentBlockName)) {
31845				if (!newBlockName && shiftKey) {
31846					insertBr();
31847					return;
31848				}
31849
31850				// Handle enter inside an empty list item
31851				if (dom.isEmpty(parentBlock)) {
31852					handleEmptyListItem();
31853					return;
31854				}
31855			}
31856
31857			// Don't split PRE tags but insert a BR instead easier when writing code samples etc
31858			if (parentBlockName == 'PRE' && settings.br_in_pre !== false) {
31859				if (!shiftKey) {
31860					insertBr();
31861					return;
31862				}
31863			} else {
31864				// If no root block is configured then insert a BR by default or if the shiftKey is pressed
31865				if ((!newBlockName && !shiftKey && parentBlockName != 'LI') || (newBlockName && shiftKey)) {
31866					insertBr();
31867					return;
31868				}
31869			}
31870
31871			// If parent block is root then never insert new blocks
31872			if (newBlockName && parentBlock === editor.getBody()) {
31873				return;
31874			}
31875
31876			// Default block name if it's not configured
31877			newBlockName = newBlockName || 'P';
31878
31879			// Insert new block before/after the parent block depending on caret location
31880			if (isCaretAtStartOrEndOfBlock()) {
31881				insertNewBlockAfter();
31882			} else if (isCaretAtStartOrEndOfBlock(true)) {
31883				// Insert new block before
31884				newBlock = parentBlock.parentNode.insertBefore(createNewBlock(), parentBlock);
31885				renderBlockOnIE(newBlock);
31886				moveToCaretPosition(parentBlock);
31887			} else {
31888				// Extract after fragment and insert it after the current block
31889				tmpRng = rng.cloneRange();
31890				tmpRng.setEndAfter(parentBlock);
31891				fragment = tmpRng.extractContents();
31892				trimLeadingLineBreaks(fragment);
31893				newBlock = fragment.firstChild;
31894				dom.insertAfter(fragment, parentBlock);
31895				trimInlineElementsOnLeftSideOfBlock(newBlock);
31896				addBrToBlockIfNeeded(parentBlock);
31897
31898				if (dom.isEmpty(parentBlock)) {
31899					emptyBlock(parentBlock);
31900				}
31901
31902				newBlock.normalize();
31903
31904				// New block might become empty if it's <p><b>a |</b></p>
31905				if (dom.isEmpty(newBlock)) {
31906					dom.remove(newBlock);
31907					insertNewBlockAfter();
31908				} else {
31909					moveToCaretPosition(newBlock);
31910				}
31911			}
31912
31913			dom.setAttrib(newBlock, 'id', ''); // Remove ID since it needs to be document unique
31914
31915			// Allow custom handling of new blocks
31916			editor.fire('NewBlock', {newBlock: newBlock});
31917
31918			undoManager.add();
31919		}
31920
31921		editor.on('keydown', function(evt) {
31922			if (evt.keyCode == 13) {
31923				if (handleEnterKey(evt) !== false) {
31924					evt.preventDefault();
31925				}
31926			}
31927		});
31928	};
31929});
31930
31931// Included from: js/tinymce/classes/ForceBlocks.js
31932
31933/**
31934 * ForceBlocks.js
31935 *
31936 * Released under LGPL License.
31937 * Copyright (c) 1999-2015 Ephox Corp. All rights reserved
31938 *
31939 * License: http://www.tinymce.com/license
31940 * Contributing: http://www.tinymce.com/contributing
31941 */
31942
31943/**
31944 * Makes sure that everything gets wrapped in paragraphs.
31945 *
31946 * @private
31947 * @class tinymce.ForceBlocks
31948 */
31949define("tinymce/ForceBlocks", [], function() {
31950	return function(editor) {
31951		var settings = editor.settings, dom = editor.dom, selection = editor.selection;
31952		var schema = editor.schema, blockElements = schema.getBlockElements();
31953
31954		function addRootBlocks() {
31955			var node = selection.getStart(), rootNode = editor.getBody(), rng;
31956			var startContainer, startOffset, endContainer, endOffset, rootBlockNode;
31957			var tempNode, offset = -0xFFFFFF, wrapped, restoreSelection;
31958			var tmpRng, rootNodeName, forcedRootBlock;
31959
31960			forcedRootBlock = settings.forced_root_block;
31961
31962			if (!node || node.nodeType !== 1 || !forcedRootBlock) {
31963				return;
31964			}
31965
31966			// Check if node is wrapped in block
31967			while (node && node != rootNode) {
31968				if (blockElements[node.nodeName]) {
31969					return;
31970				}
31971
31972				node = node.parentNode;
31973			}
31974
31975			// Get current selection
31976			rng = selection.getRng();
31977			if (rng.setStart) {
31978				startContainer = rng.startContainer;
31979				startOffset = rng.startOffset;
31980				endContainer = rng.endContainer;
31981				endOffset = rng.endOffset;
31982
31983				try {
31984					restoreSelection = editor.getDoc().activeElement === rootNode;
31985				} catch (ex) {
31986					// IE throws unspecified error here sometimes
31987				}
31988			} else {
31989				// Force control range into text range
31990				if (rng.item) {
31991					node = rng.item(0);
31992					rng = editor.getDoc().body.createTextRange();
31993					rng.moveToElementText(node);
31994				}
31995
31996				restoreSelection = rng.parentElement().ownerDocument === editor.getDoc();
31997				tmpRng = rng.duplicate();
31998				tmpRng.collapse(true);
31999				startOffset = tmpRng.move('character', offset) * -1;
32000
32001				if (!tmpRng.collapsed) {
32002					tmpRng = rng.duplicate();
32003					tmpRng.collapse(false);
32004					endOffset = (tmpRng.move('character', offset) * -1) - startOffset;
32005				}
32006			}
32007
32008			// Wrap non block elements and text nodes
32009			node = rootNode.firstChild;
32010			rootNodeName = rootNode.nodeName.toLowerCase();
32011			while (node) {
32012				// TODO: Break this up, too complex
32013				if (((node.nodeType === 3 || (node.nodeType == 1 && !blockElements[node.nodeName]))) &&
32014					schema.isValidChild(rootNodeName, forcedRootBlock.toLowerCase())) {
32015					// Remove empty text nodes
32016					if (node.nodeType === 3 && node.nodeValue.length === 0) {
32017						tempNode = node;
32018						node = node.nextSibling;
32019						dom.remove(tempNode);
32020						continue;
32021					}
32022
32023					if (!rootBlockNode) {
32024						rootBlockNode = dom.create(forcedRootBlock, editor.settings.forced_root_block_attrs);
32025						node.parentNode.insertBefore(rootBlockNode, node);
32026						wrapped = true;
32027					}
32028
32029					tempNode = node;
32030					node = node.nextSibling;
32031					rootBlockNode.appendChild(tempNode);
32032				} else {
32033					rootBlockNode = null;
32034					node = node.nextSibling;
32035				}
32036			}
32037
32038			if (wrapped && restoreSelection) {
32039				if (rng.setStart) {
32040					rng.setStart(startContainer, startOffset);
32041					rng.setEnd(endContainer, endOffset);
32042					selection.setRng(rng);
32043				} else {
32044					// Only select if the previous selection was inside the document to prevent auto focus in quirks mode
32045					try {
32046						rng = editor.getDoc().body.createTextRange();
32047						rng.moveToElementText(rootNode);
32048						rng.collapse(true);
32049						rng.moveStart('character', startOffset);
32050
32051						if (endOffset > 0) {
32052							rng.moveEnd('character', endOffset);
32053						}
32054
32055						rng.select();
32056					} catch (ex) {
32057						// Ignore
32058					}
32059				}
32060
32061				editor.nodeChanged();
32062			}
32063		}
32064
32065		// Force root blocks
32066		if (settings.forced_root_block) {
32067			editor.on('NodeChange', addRootBlocks);
32068		}
32069	};
32070});
32071
32072// Included from: js/tinymce/classes/EditorCommands.js
32073
32074/**
32075 * EditorCommands.js
32076 *
32077 * Released under LGPL License.
32078 * Copyright (c) 1999-2015 Ephox Corp. All rights reserved
32079 *
32080 * License: http://www.tinymce.com/license
32081 * Contributing: http://www.tinymce.com/contributing
32082 */
32083
32084/**
32085 * This class enables you to add custom editor commands and it contains
32086 * overrides for native browser commands to address various bugs and issues.
32087 *
32088 * @class tinymce.EditorCommands
32089 */
32090define("tinymce/EditorCommands", [
32091	"tinymce/html/Serializer",
32092	"tinymce/Env",
32093	"tinymce/util/Tools",
32094	"tinymce/dom/ElementUtils",
32095	"tinymce/dom/RangeUtils",
32096	"tinymce/dom/TreeWalker"
32097], function(Serializer, Env, Tools, ElementUtils, RangeUtils, TreeWalker) {
32098	// Added for compression purposes
32099	var each = Tools.each, extend = Tools.extend;
32100	var map = Tools.map, inArray = Tools.inArray, explode = Tools.explode;
32101	var isIE = Env.ie, isOldIE = Env.ie && Env.ie < 11;
32102	var TRUE = true, FALSE = false;
32103
32104	return function(editor) {
32105		var dom, selection, formatter,
32106			commands = {state: {}, exec: {}, value: {}},
32107			settings = editor.settings,
32108			bookmark;
32109
32110		editor.on('PreInit', function() {
32111			dom = editor.dom;
32112			selection = editor.selection;
32113			settings = editor.settings;
32114			formatter = editor.formatter;
32115		});
32116
32117		/**
32118		 * Executes the specified command.
32119		 *
32120		 * @method execCommand
32121		 * @param {String} command Command to execute.
32122		 * @param {Boolean} ui Optional user interface state.
32123		 * @param {Object} value Optional value for command.
32124		 * @param {Object} args Optional extra arguments to the execCommand.
32125		 * @return {Boolean} true/false if the command was found or not.
32126		 */
32127		function execCommand(command, ui, value, args) {
32128			var func, customCommand, state = 0;
32129
32130			if (!/^(mceAddUndoLevel|mceEndUndoLevel|mceBeginUndoLevel|mceRepaint)$/.test(command) && (!args || !args.skip_focus)) {
32131				editor.focus();
32132			}
32133
32134			args = editor.fire('BeforeExecCommand', {command: command, ui: ui, value: value});
32135			if (args.isDefaultPrevented()) {
32136				return false;
32137			}
32138
32139			customCommand = command.toLowerCase();
32140			if ((func = commands.exec[customCommand])) {
32141				func(customCommand, ui, value);
32142				editor.fire('ExecCommand', {command: command, ui: ui, value: value});
32143				return true;
32144			}
32145
32146			// Plugin commands
32147			each(editor.plugins, function(p) {
32148				if (p.execCommand && p.execCommand(command, ui, value)) {
32149					editor.fire('ExecCommand', {command: command, ui: ui, value: value});
32150					state = true;
32151					return false;
32152				}
32153			});
32154
32155			if (state) {
32156				return state;
32157			}
32158
32159			// Theme commands
32160			if (editor.theme && editor.theme.execCommand && editor.theme.execCommand(command, ui, value)) {
32161				editor.fire('ExecCommand', {command: command, ui: ui, value: value});
32162				return true;
32163			}
32164
32165			// Browser commands
32166			try {
32167				state = editor.getDoc().execCommand(command, ui, value);
32168			} catch (ex) {
32169				// Ignore old IE errors
32170			}
32171
32172			if (state) {
32173				editor.fire('ExecCommand', {command: command, ui: ui, value: value});
32174				return true;
32175			}
32176
32177			return false;
32178		}
32179
32180		/**
32181		 * Queries the current state for a command for example if the current selection is "bold".
32182		 *
32183		 * @method queryCommandState
32184		 * @param {String} command Command to check the state of.
32185		 * @return {Boolean/Number} true/false if the selected contents is bold or not, -1 if it's not found.
32186		 */
32187		function queryCommandState(command) {
32188			var func;
32189
32190			// Is hidden then return undefined
32191			if (editor._isHidden()) {
32192				return;
32193			}
32194
32195			command = command.toLowerCase();
32196			if ((func = commands.state[command])) {
32197				return func(command);
32198			}
32199
32200			// Browser commands
32201			try {
32202				return editor.getDoc().queryCommandState(command);
32203			} catch (ex) {
32204				// Fails sometimes see bug: 1896577
32205			}
32206
32207			return false;
32208		}
32209
32210		/**
32211		 * Queries the command value for example the current fontsize.
32212		 *
32213		 * @method queryCommandValue
32214		 * @param {String} command Command to check the value of.
32215		 * @return {Object} Command value of false if it's not found.
32216		 */
32217		function queryCommandValue(command) {
32218			var func;
32219
32220			// Is hidden then return undefined
32221			if (editor._isHidden()) {
32222				return;
32223			}
32224
32225			command = command.toLowerCase();
32226			if ((func = commands.value[command])) {
32227				return func(command);
32228			}
32229
32230			// Browser commands
32231			try {
32232				return editor.getDoc().queryCommandValue(command);
32233			} catch (ex) {
32234				// Fails sometimes see bug: 1896577
32235			}
32236		}
32237
32238		/**
32239		 * Adds commands to the command collection.
32240		 *
32241		 * @method addCommands
32242		 * @param {Object} command_list Name/value collection with commands to add, the names can also be c
32242omma separated.
32243		 * @param {String} type Optional type to add, defaults to exec. Can be value or state as well.
32244		 */
32245		function addCommands(command_list, type) {
32246			type = type || 'exec';
32247
32248			each(command_list, function(callback, command) {
32249				each(command.toLowerCase().split(','), function(command) {
32250					commands[type][command] = callback;
32251				});
32252			});
32253		}
32254
32255		function addCommand(command, callback, scope) {
32256			command = command.toLowerCase();
32257			commands.exec[command] = function(command, ui, value, args) {
32258				return callback.call(scope || editor, ui, value, args);
32259			};
32260		}
32261
32262		/**
32263		 * Returns true/false if the command is supported or not.
32264		 *
32265		 * @method queryCommandSupported
32266		 * @param {String} command Command that we check support for.
32267		 * @return {Boolean} true/false if the command is supported or not.
32268		 */
32269		function queryCommandSupported(command) {
32270			command = command.toLowerCase();
32271
32272			if (commands.exec[command]) {
32273				return true;
32274			}
32275
32276			// Browser commands
32277			try {
32278				return editor.getDoc().queryCommandSupported(command);
32279			} catch (ex) {
32280				// Fails sometimes see bug: 1896577
32281			}
32282
32283			return false;
32284		}
32285
32286		function addQueryStateHandler(command, callback, scope) {
32287			command = command.toLowerCase();
32288			commands.state[command] = function() {
32289				return callback.call(scope || editor);
32290			};
32291		}
32292
32293		function addQueryValueHandler(command, callback, scope) {
32294			command = command.toLowerCase();
32295			commands.value[command] = function() {
32296				return callback.call(scope || editor);
32297			};
32298		}
32299
32300		function hasCustomCommand(command) {
32301			command = command.toLowerCase();
32302			return !!commands.exec[command];
32303		}
32304
32305		// Expose public methods
32306		extend(this, {
32307			execCommand: execCommand,
32308			queryCommandState: queryCommandState,
32309			queryCommandValue: queryCommandValue,
32310			queryCommandSupported: queryCommandSupported,
32311			addCommands: addCommands,
32312			addCommand: addCommand,
32313			addQueryStateHandler: addQueryStateHandler,
32314			addQueryValueHandler: addQueryValueHandler,
32315			hasCustomCommand: hasCustomCommand
32316		});
32317
32318		// Private methods
32319
32320		function execNativeCommand(command, ui, value) {
32321			if (ui === undefined) {
32322				ui = FALSE;
32323			}
32324
32325			if (value === undefined) {
32326				value = null;
32327			}
32328
32329			return editor.getDoc().execCommand(command, ui, value);
32330		}
32331
32332		function isFormatMatch(name) {
32333			return formatter.match(name);
32334		}
32335
32336		function toggleFormat(name, value) {
32337			formatter.toggle(name, value ? {value: value} : undefined);
32338			editor.nodeChanged();
32339		}
32340
32341		function storeSelection(type) {
32342			bookmark = selection.getBookmark(type);
32343		}
32344
32345		function restoreSelection() {
32346			selection.moveToBookmark(bookmark);
32347		}
32348
32349		// Add execCommand overrides
32350		addCommands({
32351			// Ignore these, added for compatibility
32352			'mceResetDesignMode,mceBeginUndoLevel': function() {},
32353
32354			// Add undo manager logic
32355			'mceEndUndoLevel,mceAddUndoLevel': function() {
32356				editor.undoManager.add();
32357			},
32358
32359			'Cut,Copy,Paste': function(command) {
32360				var doc = editor.getDoc(), failed;
32361
32362				// Try executing the native command
32363				try {
32364					execNativeCommand(command);
32365				} catch (ex) {
32366					// Command failed
32367					failed = TRUE;
32368				}
32369
32370				// Present alert message about clipboard access not being available
32371				if (failed || !doc.queryCommandSupported(command)) {
32372					var msg = editor.translate(
32373						"Your browser doesn't support direct access to the clipboard. " +
32374						"Please use the Ctrl+X/C/V keyboard shortcuts instead."
32375					);
32376
32377					if (Env.mac) {
32378						msg = msg.replace(/Ctrl\+/g, '\u2318+');
32379					}
32380
32381					editor.notificationManager.open({text: msg, type: 'error'});
32382				}
32383			},
32384
32385			// Override unlink command
32386			unlink: function() {
32387				if (selection.isCollapsed()) {
32388					var elm = selection.getNode();
32389					if (elm.tagName == 'A') {
32390						editor.dom.remove(elm, true);
32391					}
32392
32393					return;
32394				}
32395
32396				formatter.remove("link");
32397			},
32398
32399			// Override justify commands to use the text formatter engine
32400			'JustifyLeft,JustifyCenter,JustifyRight,JustifyFull,JustifyNone': function(command) {
32401				var align = command.substring(7);
32402
32403				if (align == 'full') {
32404					align = 'justify';
32405				}
32406
32407				// Remove all other alignments first
32408				each('left,center,right,justify'.split(','), function(name) {
32409					if (align != name) {
32410						formatter.remove('align' + name);
32411					}
32412				});
32413
32414				if (align != 'none') {
32415					toggleFormat('align' + align);
32416				}
32417			},
32418
32419			// Override list commands to fix WebKit bug
32420			'InsertUnorderedList,InsertOrderedList': function(command) {
32421				var listElm, listParent;
32422
32423				execNativeCommand(command);
32424
32425				// WebKit produces lists within block elements so we need to split them
32426				// we will replace the native list creation logic to custom logic later on
32427				// TODO: Remove this when the list creation logic is removed
32428				listElm = dom.getParent(selection.getNode(), 'ol,ul');
32429				if (listElm) {
32430					listParent = listElm.parentNode;
32431
32432					// If list is within a text block then split that block
32433					if (/^(H[1-6]|P|ADDRESS|PRE)$/.test(listParent.nodeName)) {
32434						storeSelection();
32435						dom.split(listParent, listElm);
32436						restoreSelection();
32437					}
32438				}
32439			},
32440
32441			// Override commands to use the text formatter engine
32442			'Bold,Italic,Underline,Strikethrough,Superscript,Subscript': function(command) {
32443				toggleFormat(command);
32444			},
32445
32446			// Override commands to use the text formatter engine
32447			'ForeColor,HiliteColor,FontName': function(command, ui, value) {
32448				toggleFormat(command, value);
32449			},
32450
32451			FontSize: function(command, ui, value) {
32452				var fontClasses, fontSizes;
32453
32454				// Convert font size 1-7 to styles
32455				if (value >= 1 && value <= 7) {
32456					fontSizes = explode(settings.font_size_style_values);
32457					fontClasses = explode(settings.font_size_classes);
32458
32459					if (fontClasses) {
32460						value = fontClasses[value - 1] || value;
32461					} else {
32462						value = fontSizes[value - 1] || value;
32463					}
32464				}
32465
32466				toggleFormat(command, value);
32467			},
32468
32469			RemoveFormat: function(command) {
32470				formatter.remove(command);
32471			},
32472
32473			mceBlockQuote: function() {
32474				toggleFormat('blockquote');
32475			},
32476
32477			FormatBlock: function(command, ui, value) {
32478				return toggleFormat(value || 'p');
32479			},
32480
32481			mceCleanup: function() {
32482				var bookmark = selection.getBookmark();
32483
32484				editor.setContent(editor.getContent({cleanup: TRUE}), {cleanup: TRUE});
32485
32486				selection.moveToBookmark(bookmark);
32487			},
32488
32489			mceRemoveNode: function(command, ui, value) {
32490				var node = value || selection.getNode();
32491
32492				// Make sure that the body node isn't removed
32493				if (node != editor.getBody()) {
32494					storeSelection();
32495					editor.dom.remove(node, TRUE);
32496					restoreSelection();
32497				}
32498			},
32499
32500			mceSelectNodeDepth: function(command, ui, value) {
32501				var counter = 0;
32502
32503				dom.getParent(selection.getNode(), function(node) {
32504					if (node.nodeType == 1 && counter++ == value) {
32505						selection.select(node);
32506						return FALSE;
32507					}
32508				}, editor.getBody());
32509			},
32510
32511			mceSelectNode: function(command, ui, value) {
32512				selection.select(value);
32513			},
32514
32515			mceInsertContent: function(command, ui, value) {
32516				var parser, serializer, parentNode, rootNode, fragment, args;
32517				var marker, rng, node, node2, bookmarkHtml, merge, data;
32518				var textInlineElements = editor.schema.getTextInlineElements();
32519
32520				function trimOrPaddLeftRight(html) {
32521					var rng, container, offset;
32522
32523					rng = selection.getRng(true);
32524					container = rng.startContainer;
32525					offset = rng.startOffset;
32526
32527					function hasSiblingText(siblingName) {
32528						return container[siblingName] && container[siblingName].nodeType == 3;
32529					}
32530
32531					if (container.nodeType == 3) {
32532						if (offset > 0) {
32533							html = html.replace(/^&nbsp;/, ' ');
32534						} else if (!hasSiblingText('previousSibling')) {
32535							html = html.replace(/^ /, '&nbsp;');
32536						}
32537
32538						if (offset < container.length) {
32539							html = html.replace(/&nbsp;(<br>|)$/, ' ');
32540						} else if (!hasSiblingText('nextSibling')) {
32541							html = html.replace(/(&nbsp;| )(<br>|)$/, '&nbsp;');
32542						}
32543					}
32544
32545					return html;
32546				}
32547
32548				// Removes &nbsp; from a [b] c -> a &nbsp;c -> a c
32549				function trimNbspAfterDeleteAndPaddValue() {
32550					var rng, container, offset;
32551
32552					rng = selection.getRng(true);
32553					container = rng.startContainer;
32554					offset = rng.startOffset;
32555
32556					if (container.nodeType == 3 && rng.collapsed) {
32557						if (container.data[offset] === '\u00a0') {
32558							container.deleteData(offset, 1);
32559
32560							if (!/[\u00a0| ]$/.test(value)) {
32561								value += ' ';
32562							}
32563						} else if (container.data[offset - 1] === '\u00a0') {
32564							container.deleteData(offset - 1, 1);
32565
32566							if (!/[\u00a0| ]$/.test(value)) {
32567								value = ' ' + value;
32568							}
32569						}
32570					}
32571				}
32572
32573				function markInlineFormatElements(fragment) {
32574					if (merge) {
32575						for (node = fragment.firstChild; node; node = node.walk(true)) {
32576							if (textInlineElements[node.name]) {
32577								node.attr('data-mce-new', "true");
32578							}
32579						}
32580					}
32581				}
32582
32583				function reduceInlineTextElements() {
32584					if (merge) {
32585						var root = editor.getBody(), elementUtils = new ElementUtils(dom);
32586
32587						each(dom.select('*[data-mce-new]'), function(node) {
32588							node.removeAttribute('data-mce-new');
32589
32590							for (var testNode = node.parentNode; testNode && testNode != root; testNode = testNode.parentNode) {
32591								if (elementUtils.compare(testNode, node)) {
32592									dom.remove(node, true);
32593								}
32594							}
32595						});
32596					}
32597				}
32598
32599				function moveSelectionToMarker(marker) {
32600					var parentEditableFalseElm;
32601
32602					function getContentEditableFalseParent(node) {
32603						var root = editor.getBody();
32604
32605						for (; node && node !== root; node = node.parentNode) {
32606							if (editor.dom.getContentEditable(node) === 'false') {
32607								return node;
32608							}
32609						}
32610
32611						return null;
32612					}
32613
32614					if (!marker) {
32615						return;
32616					}
32617
32618					selection.scrollIntoView(marker);
32619
32620					// If marker is in cE=false then move selection to that element instead
32621					parentEditableFalseElm = getContentEditableFalseParent(marker);
32622					if (parentEditableFalseElm) {
32623						dom.remove(marker);
32624						selection.select(parentEditableFalseElm);
32625						return;
32626					}
32627
32628					// Move selection before marker and remove it
32629					rng = dom.createRng();
32630
32631					// If previous sibling is a text node set the selection to the end of that node
32632					node = marker.previousSibling;
32633					if (node && node.nodeType == 3) {
32634						rng.setStart(node, node.nodeValue.length);
32635
32636						// TODO: Why can't we normalize on IE
32637						if (!isIE) {
32638							node2 = marker.nextSibling;
32639							if (node2 && node2.nodeType == 3) {
32640								node.appendData(node2.data);
32641								node2.parentNode.removeChild(node2);
32642							}
32643						}
32644					} else {
32645						// If the previous sibling isn't a text node or doesn't exist set the selection before the marker node
32646						rng.setStartBefore(marker);
32647						rng.setEndBefore(marker);
32648					}
32649
32650					// Remove the marker node and set the new range
32651					dom.remove(marker);
32652					selection.setRng(rng);
32653				}
32654
32655				if (typeof value != 'string') {
32656					merge = value.merge;
32657					data = value.data;
32658					value = value.content;
32659				}
32660
32661				// Check for whitespace before/after value
32662				if (/^ | $/.test(value)) {
32663					value = trimOrPaddLeftRight(value);
32664				}
32665
32666				// Setup parser and serializer
32667				parser = editor.parser;
32668				serializer = new Serializer({
32669					validate: settings.validate
32670				}, editor.schema);
32671				bookmarkHtml = '<span id="mce_marker" data-mce-type="bookmark">&#xFEFF;&#x200B;</span>';
32672
32673				// Run beforeSetContent handlers on the HTML to be inserted
32674				args = {content: value, format: 'html', selection: true};
32675				editor.fire('BeforeSetContent', args);
32676				value = args.content;
32677
32678				// Add caret at end of contents if it's missing
32679				if (value.indexOf('{$caret}') == -1) {
32680					value += '{$caret}';
32681				}
32682
32683				// Replace the caret marker with a span bookmark element
32684				value = value.replace(/\{\$caret\}/, bookmarkHtml);
32685
32686				// If selection is at <body>|<p></p> then move it into <body><p>|</p>
32687				rng = selection.getRng();
32688				var caretElement = rng.startContainer || (rng.parentElement ? rng.parentElement() : null);
32689				var body = editor.getBody();
32690				if (caretElement === body && selection.isCollapsed()) {
32691					if (dom.isBlock(body.firstChild) && dom.isEmpty(body.firstChild)) {
32692						rng = dom.createRng();
32693						rng.setStart(body.firstChild, 0);
32694						rng.setEnd(body.firstChild, 0);
32695						selection.setRng(rng);
32696					}
32697				}
32698
32699				// Insert node maker where we will insert the new HTML and get it's parent
32700				if (!selection.isCollapsed()) {
32701					// Fix for #2595 seems that delete removes one extra character on
32702					// WebKit for some odd reason if you double click select a word
32703					editor.selection.setRng(editor.selection.getRng());
32704					editor.getDoc().execCommand('Delete', false, null);
32705					trimNbspAfterDeleteAndPaddValue();
32706				}
32707
32708				parentNode = selection.getNode();
32709
32710				// Parse the fragment within the context of the parent node
32711				var parserArgs = {context: parentNode.nodeName.toLowerCase(), data: data};
32712				fragment = parser.parse(value, parserArgs);
32713
32714				markInlineFormatElements(fragment);
32715
32716				// Move the caret to a more suitable location
32717				node = fragment.lastChild;
32718				if (node.attr('id') == 'mce_marker') {
32719					marker = node;
32720
32721					for (node = node.prev; node; node = node.walk(true)) {
32722						if (node.type == 3 || !dom.isBlock(node.name)) {
32723							if (editor.schema.isValidChild(node.parent.name, 'span')) {
32724								node.parent.insert(marker, node, node.name === 'br');
32725							}
32726							break;
32727						}
32728					}
32729				}
32730
32731				editor._selectionOverrides.showBlockCaretContainer(parentNode);
32732
32733				// If parser says valid we can insert the contents into that parent
32734				if (!parserArgs.invalid) {
32735					value = serializer.serialize(fragment);
32736
32737					// Check if parent is empty or only has one BR element then set the innerHTML of that parent
32738					node = parentNode.firstChild;
32739					node2 = parentNode.lastChild;
32740					if (!node || (node === node2 && node.nodeName === 'BR')) {
32741						dom.setHTML(parentNode, value);
32742					} else {
32743						selection.setContent(value);
32744					}
32745				} else {
32746					// If the fragment was invalid within that context then we need
32747					// to parse and process the parent it's inserted into
32748
32749					// Insert bookmark node and get the parent
32750					selection.setContent(bookmarkHtml);
32751					parentNode = selection.getNode();
32752					rootNode = editor.getBody();
32753
32754					// Opera will return the document node when selection is in root
32755					if (parentNode.nodeType == 9) {
32756						parentNode = node = rootNode;
32757					} else {
32758						node = parentNode;
32759					}
32760
32761					// Find the ancestor just before the root element
32762					while (node !== rootNode) {
32763						parentNode = node;
32764						node = node.parentNode;
32765					}
32766
32767					// Get the outer/inner HTML depending on if we are in the root and parser and serialize that
32768					value = parentNode == rootNode ? rootNode.innerHTML : dom.getOuterHTML(parentNode);
32769					value = serializer.serialize(
32770						parser.parse(
32771							// Need to replace by using a function since $ in the contents would otherwise be a problem
32772							value.replace(/<span (id="mce_marker"|id=mce_marker).+?<\/span>/i, function() {
32773								return serializer.serialize(fragment);
32774							})
32775						)
32776					);
32777
32778					// Set the inner/outer HTML depending on if we are in the root or not
32779					if (parentNode == rootNode) {
32780						dom.setHTML(rootNode, value);
32781					} else {
32782						dom.setOuterHTML(parentNode, value);
32783					}
32784				}
32785
32786				reduceInlineTextElements();
32787				moveSelectionToMarker(dom.get('mce_marker'));
32788				editor.fire('SetContent', args);
32789				editor.addVisual();
32790			},
32791
32792			mceInsertRawHTML: function(command, ui, value) {
32793				selection.setContent('tiny_mce_marker');
32794				editor.setContent(
32795					editor.getContent().replace(/tiny_mce_marker/g, function() {
32796						return value;
32797					})
32798				);
32799			},
32800
32801			mceToggleFormat: function(command, ui, value) {
32802				toggleFormat(value);
32803			},
32804
32805			mceSetContent: function(command, ui, value) {
32806				editor.setContent(value);
32807			},
32808
32809			'Indent,Outdent': function(command) {
32810				var intentValue, indentUnit, value;
32811
32812				// Setup indent level
32813				intentValue = settings.indentation;
32814				indentUnit = /[a-z%]+$/i.exec(intentValue);
32815				intentValue = parseInt(intentValue, 10);
32816
32817				if (!queryCommandState('InsertUnorderedList') && !queryCommandState('InsertOrderedList')) {
32818					// If forced_root_blocks is set to false we don't have a block to indent so lets create a div
32819					if (!settings.forced_root_block && !dom.getParent(selection.getNode(), dom.isBlock)) {
32820						formatter.apply('div');
32821					}
32822
32823					each(selection.getSelectedBlocks(), function(element) {
32824						if (dom.getContentEditable(element) === "false") {
32825							return;
32826						}
32827
32828						if (element.nodeName != "LI") {
32829							var indentStyleName = editor.getParam('indent_use_margin', false) ? 'margin' : 'padding';
32830
32831							indentStyleName += dom.getStyle(element, 'direction', true) == 'rtl' ? 'Right' : 'Left';
32832
32833							if (command == 'outdent') {
32834								value = Math.max(0, parseInt(element.style[indentStyleName] || 0, 10) - intentValue);
32835								dom.setStyle(element, indentStyleName, value ? value + indentUnit : '');
32836							} else {
32837								value = (parseInt(element.style[indentStyleName] || 0, 10) + intentValue) + indentUnit;
32838								dom.setStyle(element, indentStyleName, value);
32839							}
32840						}
32841					});
32842				} else {
32843					execNativeCommand(command);
32844				}
32845			},
32846
32847			mceRepaint: function() {
32848			},
32849
32850			InsertHorizontalRule: function() {
32851				editor.execCommand('mceInsertContent', false, '<hr />');
32852			},
32853
32854			mceToggleVisualAid: function() {
32855				editor.hasVisual = !editor.hasVisual;
32856				editor.addVisual();
32857			},
32858
32859			mceReplaceContent: function(command, ui, value) {
32860				editor.execCommand('mceInsertContent', false, value.replace(/\{\$selection\}/g, selection.getContent({format: 'text'})));
32861			},
32862
32863			mceInsertLink: function(command, ui, value) {
32864				var anchor;
32865
32866				if (typeof value == 'string') {
32867					value = {href: value};
32868				}
32869
32870				anchor = dom.getParent(selection.getNode(), 'a');
32871
32872				// Spaces are never valid in URLs and it's a very common mistake for people to make so we fix it here.
32873				value.href = value.href.replace(' ', '%20');
32874
32875				// Remove existing links if there could be child links or that the href isn't specified
32876				if (!anchor || !value.href) {
32877					formatter.remove('link');
32878				}
32879
32880				// Apply new link to selection
32881				if (value.href) {
32882					formatter.apply('link', value, anchor);
32883				}
32884			},
32885
32886			selectAll: function() {
32887				var root = dom.getRoot(), rng;
32888
32889				if (selection.getRng().setStart) {
32890					rng = dom.createRng();
32891					rng.setStart(root, 0);
32892					rng.setEnd(root, root.childNodes.length);
32893					selection.setRng(rng);
32894				} else {
32895					// IE will render it's own root level block elements and sometimes
32896					// even put font elements in them when the user starts typing. So we need to
32897					// move the selection to a more suitable element from this:
32898					// <body>|<p></p></body> to this: <body><p>|</p></body>
32899					rng = selection.getRng();
32900					if (!rng.item) {
32901						rng.moveToElementText(root);
32902						rng.select();
32903					}
32904				}
32905			},
32906
32907			"delete": function() {
32908				execNativeCommand("Delete");
32909
32910				// Check if body is empty after the delete call if so then set the contents
32911				// to an empty string and move the caret to any block produced by that operation
32912				// this fixes the issue with root blocks not being properly produced after a delete call on IE
32913				var body = editor.getBody();
32914
32915				if (dom.isEmpty(body)) {
32916					editor.setContent('');
32917
32918					if (body.firstChild && dom.isBlock(body.firstChild)) {
32919						editor.selection.setCursorLocation(body.firstChild, 0);
32920					} else {
32921						editor.selection.setCursorLocation(body, 0);
32922					}
32923				}
32924			},
32925
32926			mceNewDocument: function() {
32927				editor.setContent('');
32928			},
32929
32930			InsertLineBreak: function(command, ui, value) {
32931				// We load the current event in from EnterKey.js when appropriate to heed
32932				// certain event-specific variations such as ctrl-enter in a list
32933				var evt = value;
32934				var brElm, extraBr, marker;
32935				var rng = selection.getRng(true);
32936				new RangeUtils(dom).normalize(rng);
32937
32938				var offset = rng.startOffset;
32939				var container = rng.startContainer;
32940
32941				// Resolve node index
32942				if (container.nodeType == 1 && container.hasChildNodes()) {
32943					var isAfterLastNodeInContainer = offset > container.childNodes.length - 1;
32944
32945					container = container.childNodes[Math.min(offset, container.childNodes.length - 1)] || container;
32946					if (isAfterLastNodeInContainer && container.nodeType == 3) {
32947						offset = container.nodeValue.length;
32948					} else {
32949						offset = 0;
32950					}
32951				}
32952
32953				var parentBlock = dom.getParent(container, dom.isBlock);
32954				var parentBlockName = parentBlock ? parentBlock.nodeName.toUpperCase() : ''; // IE < 9 & HTML5
32955				var containerBlock = parentBlock ? dom.getParent(parentBlock.parentNode, dom.isBlock) : null;
32956				var containerBlockName = containerBlock ? containerBlock.nodeName.toUpperCase() : ''; // IE < 9 & HTML5
32957
32958				// Enter inside block contained within a LI then split or insert before/after LI
32959				var isControlKey = evt && evt.ctrlKey;
32960				if (containerBlockName == 'LI' && !isControlKey) {
32961					parentBlock = containerBlock;
32962					parentBlockName = containerBlockName;
32963				}
32964
32965				// Walks the parent block to the right and look for BR elements
32966				function hasRightSideContent() {
32967					var walker = new TreeWalker(container, parentBlock), node;
32968					var nonEmptyElementsMap = editor.schema.getNonEmptyElements();
32969
32970					while ((node = walker.next())) {
32971						if (nonEmptyElementsMap[node.nodeName.toLowerCase()] || node.length > 0) {
32972							return true;
32973						}
32974					}
32975				}
32976
32977				if (container && container.nodeType == 3 && offset >= container.nodeValue.length) {
32978					// Insert extra BR element at the end block elements
32979					if (!isOldIE && !hasRightSideContent()) {
32980						brElm = dom.create('br');
32981						rng.insertNode(brElm);
32982						rng.setStartAfter(brElm);
32983						rng.setEndAfter(brElm);
32984						extraBr = true;
32985					}
32986				}
32987
32988				brElm = dom.create('br');
32989				rng.insertNode(brElm);
32990
32991				// Rendering modes below IE8 doesn't display BR elements in PRE unless we have a \n before it
32992				var documentMode = dom.doc.documentMode;
32993				if (isOldIE && parentBlockName == 'PRE' && (!documentMode || documentMode < 8)) {
32994					brElm.parentNode.insertBefore(dom.doc.createTextNode('\r'), brElm);
32995				}
32996
32997				// Insert temp marker and scroll to that
32998				marker = dom.create('span', {}, '&nbsp;');
32999				brElm.parentNode.insertBefore(marker, brElm);
33000				selection.scrollIntoView(marker);
33001				dom.remove(marker);
33002
33003				if (!extraBr) {
33004					rng.setStartAfter(brElm);
33005					rng.setEndAfter(brElm);
33006				} else {
33007					rng.setStartBefore(brElm);
33008					rng.setEndBefore(brElm);
33009				}
33010
33011				selection.setRng(rng);
33012				editor.undoManager.add();
33013
33014				return TRUE;
33015			}
33016		});
33017
33018		// Add queryCommandState overrides
33019		addCommands({
33020			// Override justify commands
33021			'JustifyLeft,JustifyCenter,JustifyRight,JustifyFull': function(command) {
33022				var name = 'align' + command.substring(7);
33023				var nodes = selection.isCollapsed() ? [dom.getParent(selection.getNode(), dom.isBlock)] : selection.getSelectedBlocks();
33024				var matches = map(nodes, function(node) {
33025					return !!formatter.matchNode(node, name);
33026				});
33027				return inArray(matches, TRUE) !== -1;
33028			},
33029
33030			'Bold,Italic,Underline,Strikethrough,Superscript,Subscript': function(command) {
33031				return isFormatMatch(command);
33032			},
33033
33034			mceBlockQuote: function() {
33035				return isFormatMatch('blockquote');
33036			},
33037
33038			Outdent: function() {
33039				var node;
33040
33041				if (settings.inline_styles) {
33042					if ((node = dom.getParent(selection.getStart(), dom.isBlock)) && parseInt(node.style.paddingLeft, 10) > 0) {
33043						return TRUE;
33044					}
33045
33046					if ((node = dom.getParent(selection.getEnd(), dom.isBlock)) && parseInt(node.style.paddingLeft, 10) > 0) {
33047						return TRUE;
33048					}
33049				}
33050
33051				return (
33052					queryCommandState('InsertUnorderedList') ||
33053					queryCommandState('InsertOrderedList') ||
33054					(!settings.inline_styles && !!dom.getParent(selection.getNode(), 'BLOCKQUOTE'))
33055				);
33056			},
33057
33058			'InsertUnorderedList,InsertOrderedList': function(command) {
33059				var list = dom.getParent(selection.getNode(), 'ul,ol');
33060
33061				return list &&
33062					(
33063						command === 'insertunorderedlist' && list.tagName === 'UL' ||
33064						command === 'insertorderedlist' && list.tagName === 'OL'
33065					);
33066			}
33067		}, 'state');
33068
33069		// Add queryCommandValue overrides
33070		addCommands({
33071			'FontSize,FontName': function(command) {
33072				var value = 0, parent;
33073
33074				if ((parent = dom.getParent(selection.getNode(), 'span'))) {
33075					if (command == 'fontsize') {
33076						value = parent.style.fontSize;
33077					} else {
33078						value = parent.style.fontFamily.replace(/, /g, ',').replace(/[\'\"]/g, '').toLowerCase();
33079					}
33080				}
33081
33082				return value;
33083			}
33084		}, 'value');
33085
33086		// Add undo manager logic
33087		addCommands({
33088			Undo: function() {
33089				editor.undoManager.undo();
33090			},
33091
33092			Redo: function() {
33093				editor.undoManager.redo();
33094			}
33095		});
33096	};
33097});
33098
33099// Included from: js/tinymce/classes/util/URI.js
33100
33101/**
33102 * URI.js
33103 *
33104 * Released under LGPL License.
33105 * Copyright (c) 1999-2015 Ephox Corp. All rights reserved
33106 *
33107 * License: http://www.tinymce.com/license
33108 * Contributing: http://www.tinymce.com/contributing
33109 */
33110
33111/**
33112 * This class handles parsing, modification and serialization of URI/URL strings.
33113 * @class tinymce.util.URI
33114 */
33115define("tinymce/util/URI", [
33116	"tinymce/util/Tools"
33117], function(Tools) {
33118	var each = Tools.each, trim = Tools.trim;
33119	var queryParts = "source protocol authority userInfo user password host port relative path directory file query anchor".split(' ');
33120	var DEFAULT_PORTS = {
33121		'ftp': 21,
33122		'http': 80,
33123		'https': 443,
33124		'mailto': 25
33125	};
33126
33127	/**
33128	 * Constructs a new URI instance.
33129	 *
33130	 * @constructor
33131	 * @method URI
33132	 * @param {String} url URI string to parse.
33133	 * @param {Object} settings Optional settings object.
33134	 */
33135	function URI(url, settings) {
33136		var self = this, baseUri, base_url;
33137
33138		url = trim(url);
33139		settings = self.settings = settings || {};
33140		baseUri = settings.base_uri;
33141
33142		// Strange app protocol that isn't http/https or local anchor
33143		// For example: mailto,skype,tel etc.
33144		if (/^([\w\-]+):([^\/]{2})/i.test(url) || /^\s*#/.test(url)) {
33145			self.source = url;
33146			return;
33147		}
33148
33149		var isProtocolRelative = url.indexOf('//') === 0;
33150
33151		// Absolute path with no host, fake host and protocol
33152		if (url.indexOf('/') === 0 && !isProtocolRelative) {
33153			url = (baseUri ? baseUri.protocol || 'http' : 'http') + '://mce_host' + url;
33154		}
33155
33156		// Relative path http:// or protocol relative //path
33157		if (!/^[\w\-]*:?\/\//.test(url)) {
33158			base_url = settings.base_uri ? settings.base_uri.path : new URI(location.href).directory;
33159			if (settings.base_uri.protocol === "") {
33160				url = '//mce_host' + self.toAbsPath(base_url, url);
33161			} else {
33162				url = /([^#?]*)([#?]?.*)/.exec(url);
33163				url = ((baseUri && baseUri.protocol) || 'http') + '://mce_host' + self.toAbsPath(base_url, url[1]) + url[2];
33164			}
33165		}
33166
33167		// Parse URL (Credits goes to Steave, http://blog.stevenlevithan.com/archives/parseuri)
33168		url = url.replace(/@@/g, '(mce_at)'); // Zope 3 workaround, they use @@something
33169
33170		/*jshint maxlen: 255 */
33171		/*eslint max-len: 0 */
33172		url = /^(?:(?![^:@]+:[^:@\/]*@)([^:\/?#.]+):)?(?:\/\/)?((?:(([^:@\/]*):?([^:@\/]*))?@)?([^:\/?#]*)(?::(\d*))?)(((\/(?:[^?#](?![^?#\/]*\.[^?#\/.]+(?:[?#]|$)))*\/?)?([^?#\/]*))(?:\?([^#]*))?(?:#(.*))?)/.exec(url);
33173
33174		each(queryParts, function(v, i) {
33175			var part = url[i];
33176
33177			// Zope 3 workaround, they use @@something
33178			if (part) {
33179				part = part.replace(/\(mce_at\)/g, '@@');
33180			}
33181
33182			self[v] = part;
33183		});
33184
33185		if (baseUri) {
33186			if (!self.protocol) {
33187				self.protocol = baseUri.protocol;
33188			}
33189
33190			if (!self.userInfo) {
33191				self.userInfo = baseUri.userInfo;
33192			}
33193
33194			if (!self.port && self.host === 'mce_host') {
33195				self.port = baseUri.port;
33196			}
33197
33198			if (!self.host || self.host === 'mce_host') {
33199				self.host = baseUri.host;
33200			}
33201
33202			self.source = '';
33203		}
33204
33205		if (isProtocolRelative) {
33206			self.protocol = '';
33207		}
33208
33209		//t.path = t.path || '/';
33210	}
33211
33212	URI.prototype = {
33213		/**
33214		 * Sets the internal path part of the URI.
33215		 *
33216		 * @method setPath
33217		 * @param {string} path Path string to set.
33218		 */
33219		setPath: function(path) {
33220			var self = this;
33221
33222			path = /^(.*?)\/?(\w+)?$/.exec(path);
33223
33224			// Update path parts
33225			self.path = path[0];
33226			self.directory = path[1];
33227			self.file = path[2];
33228
33229			// Rebuild source
33230			self.source = '';
33231			self.getURI();
33232		},
33233
33234		/**
33235		 * Converts the specified URI into a relative URI based on the current URI instance location.
33236		 *
33237		 * @method toRelative
33238		 * @param {String} uri URI to convert into a relative path/URI.
33239		 * @return {String} Relative URI from the point specified in the current URI instance.
33240		 * @example
33241		 * // Converts an absolute URL to an relative URL url will be somedir/somefile.htm
33242		 * var url = new tinymce.util.URI('http://www.site.com/dir/').toRelative('http://www.site.com/dir/somedir/somefile.htm');
33243		 */
33244		toRelative: function(uri) {
33245			var self = this, output;
33246
33247			if (uri === "./") {
33248				return uri;
33249			}
33250
33251			uri = new URI(uri, {base_uri: self});
33252
33253			// Not on same domain/port or protocol
33254			if ((uri.host != 'mce_host' && self.host != uri.host && uri.host) || self.port != uri.port ||
33255				(self.protocol != uri.protocol && uri.protocol !== "")) {
33256				return uri.getURI();
33257			}
33258
33259			var tu = self.getURI(), uu = uri.getURI();
33260
33261			// Allow usage of the base_uri when relative_urls = true
33262			if (tu == uu || (tu.charAt(tu.length - 1) == "/" && tu.substr(0, tu.length - 1) == uu)) {
33263				return tu;
33264			}
33265
33266			output = self.toRelPath(self.path, uri.path);
33267
33268			// Add query
33269			if (uri.query) {
33270				output += '?' + uri.query;
33271			}
33272
33273			// Add anchor
33274			if (uri.anchor) {
33275				output += '#' + uri.anchor;
33276			}
33277
33278			return output;
33279		},
33280
33281		/**
33282		 * Converts the specified URI into a absolute URI based on the current URI instance location.
33283		 *
33284		 * @method toAbsolute
33285		 * @param {String} uri URI to convert into a relative path/URI.
33286		 * @param {Boolean} noHost No host and protocol prefix.
33287		 * @return {String} Absolute URI from the point specified in the current URI instance.
33288		 * @example
33289		 * // Converts an relative URL to an absolute URL url will be http://www.site.com/dir/somedir/somefile.htm
33290		 * var url = new tinymce.util.URI('http://www.site.com/dir/').toAbsolute('somedir/somefile.htm');
33291		 */
33292		toAbsolute: function(uri, noHost) {
33293			uri = new URI(uri, {base_uri: this});
33294
33295			return uri.getURI(noHost && this.isSameOrigin(uri));
33296		},
33297
33298		/**
33299		 * Determine whether the given URI has the same origin as this URI.  Based on RFC-6454.
33300		 * Supports default ports for protocols listed in DEFAULT_PORTS.  Unsupported protocols will fail safe: they
33301		 * won't match, if the port specifications differ.
33302		 *
33303		 * @method isSameOrigin
33304		 * @param {tinymce.util.URI} uri Uri instance to compare.
33305		 * @returns {Boolean} True if the origins are the same.
33306		 */
33307		isSameOrigin: function(uri) {
33308			if (this.host == uri.host && this.protocol == uri.protocol) {
33309				if (this.port == uri.port) {
33310					return true;
33311				}
33312
33313				var defaultPort = DEFAULT_PORTS[this.protocol];
33314				if (defaultPort && ((this.port || defaultPort) == (uri.port || defaultPort))) {
33315					return true;
33316				}
33317			}
33318
33319			return false;
33320		},
33321
33322		/**
33323		 * Converts a absolute path into a relative path.
33324		 *
33325		 * @method toRelPath
33326		 * @param {String} base Base point to convert the path from.
33327		 * @param {String} path Absolute path to convert into a relative path.
33328		 */
33329		toRelPath: function(base, path) {
33330			var items, breakPoint = 0, out = '', i, l;
33331
33332			// Split the paths
33333			base = base.substring(0, base.lastIndexOf('/'));
33334			base = base.split('/');
33335			items = path.split('/');
33336
33337			if (base.length >= items.length) {
33338				for (i = 0, l = base.length; i < l; i++) {
33339					if (i >= items.length || base[i] != items[i]) {
33340						breakPoint = i + 1;
33341						break;
33342					}
33343				}
33344			}
33345
33346			if (base.length < items.length) {
33347				for (i = 0, l = items.length; i < l; i++) {
33348					if (i >= base.length || base[i] != items[i]) {
33349						breakPoint = i + 1;
33350						break;
33351					}
33352				}
33353			}
33354
33355			if (breakPoint === 1) {
33356				return path;
33357			}
33358
33359			for (i = 0, l = base.length - (breakPoint - 1); i < l; i++) {
33360				out += "../";
33361			}
33362
33363			for (i = breakPoint - 1, l = items.length; i < l; i++) {
33364				if (i != breakPoint - 1) {
33365					out += "/" + items[i];
33366				} else {
33367					out += items[i];
33368				}
33369			}
33370
33371			return out;
33372		},
33373
33374		/**
33375		 * Converts a relative path into a absolute path.
33376		 *
33377		 * @method toAbsPath
33378		 * @param {String} base Base point to convert the path from.
33379		 * @param {String} path Relative path to convert into an absolute path.
33380		 */
33381		toAbsPath: function(base, path) {
33382			var i, nb = 0, o = [], tr, outPath;
33383
33384			// Split paths
33385			tr = /\/$/.test(path) ? '/' : '';
33386			base = base.split('/');
33387			path = path.split('/');
33388
33389			// Remove empty chunks
33390			each(base, function(k) {
33391				if (k) {
33392					o.push(k);
33393				}
33394			});
33395
33396			base = o;
33397
33398			// Merge relURLParts chunks
33399			for (i = path.length - 1, o = []; i >= 0; i--) {
33400				// Ignore empty or .
33401				if (path[i].length === 0 || path[i] === ".") {
33402					continue;
33403				}
33404
33405				// Is parent
33406				if (path[i] === '..') {
33407					nb++;
33408					continue;
33409				}
33410
33411				// Move up
33412				if (nb > 0) {
33413					nb--;
33414					continue;
33415				}
33416
33417				o.push(path[i]);
33418			}
33419
33420			i = base.length - nb;
33421
33422			// If /a/b/c or /
33423			if (i <= 0) {
33424				outPath = o.reverse().join('/');
33425			} else {
33426				outPath = base.slice(0, i).join('/') + '/' + o.reverse().join('/');
33427			}
33428
33429			// Add front / if it's needed
33430			if (outPath.indexOf('/') !== 0) {
33431				outPath = '/' + outPath;
33432			}
33433
33434			// Add traling / if it's needed
33435			if (tr && outPath.lastIndexOf('/') !== outPath.length - 1) {
33436				outPath += tr;
33437			}
33438
33439			return outPath;
33440		},
33441
33442		/**
33443		 * Returns the full URI of the internal structure.
33444		 *
33445		 * @method getURI
33446		 * @param {Boolean} noProtoHost Optional no host and protocol part. Defaults to false.
33447		 */
33448		getURI: function(noProtoHost) {
33449			var s, self = this;
33450
33451			// Rebuild source
33452			if (!self.source || noProtoHost) {
33453				s = '';
33454
33455				if (!noProtoHost) {
33456					if (self.protocol) {
33457						s += self.protocol + '://';
33458					} else {
33459						s += '//';
33460					}
33461
33462					if (self.userInfo) {
33463						s += self.userInfo + '@';
33464					}
33465
33466					if (self.host) {
33467						s += self.host;
33468					}
33469
33470					if (self.port) {
33471						s += ':' + self.port;
33472					}
33473				}
33474
33475				if (self.path) {
33476					s += self.path;
33477				}
33478
33479				if (self.query) {
33480					s += '?' + self.query;
33481				}
33482
33483				if (self.anchor) {
33484					s += '#' + self.anchor;
33485				}
33486
33487				self.source = s;
33488			}
33489
33490			return self.source;
33491		}
33492	};
33493
33494	URI.parseDataUri = function(uri) {
33495		var type, matches;
33496
33497		uri = decodeURIComponent(uri).split(',');
33498
33499		matches = /data:([^;]+)/.exec(uri[0]);
33500		if (matches) {
33501			type = matches[1];
33502		}
33503
33504		return {
33505			type: type,
33506			data: uri[1]
33507		};
33508	};
33509
33510	return URI;
33511});
33512
33513// Included from: js/tinymce/classes/util/Class.js
33514
33515/**
33516 * Class.js
33517 *
33518 * Released under LGPL License.
33519 * Copyright (c) 1999-2015 Ephox Corp. All rights reserved
33520 *
33521 * License: http://www.tinymce.com/license
33522 * Contributing: http://www.tinymce.com/contributing
33523 */
33524
33525/**
33526 * This utilitiy class is used for easier inheritance.
33527 *
33528 * Features:
33529 * * Exposed super functions: this._super();
33530 * * Mixins
33531 * * Dummy functions
33532 * * Property functions: var value = object.value(); and object.value(newValue);
33533 * * Static functions
33534 * * Defaults settings
33535 */
33536define("tinymce/util/Class", [
33537	"tinymce/util/Tools"
33538], function(Tools) {
33539	var each = Tools.each, extend = Tools.extend;
33540
33541	var extendClass, initializing;
33542
33543	function Class() {
33544	}
33545
33546	// Provides classical inheritance, based on code made by John Resig
33547	Class.extend = extendClass = function(prop) {
33548		var self = this, _super = self.prototype, prototype, name, member;
33549
33550		// The dummy class constructor
33551		function Class() {
33552			var i, mixins, mixin, self = this;
33553
33554			// All construction is actually done in the init method
33555			if (!initializing) {
33556				// Run class constuctor
33557				if (self.init) {
33558					self.init.apply(self, arguments);
33559				}
33560
33561				// Run mixin constructors
33562				mixins = self.Mixins;
33563				if (mixins) {
33564					i = mixins.length;
33565					while (i--) {
33566						mixin = mixins[i];
33567						if (mixin.init) {
33568							mixin.init.apply(self, arguments);
33569						}
33570					}
33571				}
33572			}
33573		}
33574
33575		// Dummy function, needs to be extended in order to provide functionality
33576		function dummy() {
33577			return this;
33578		}
33579
33580		// Creates a overloaded method for the class
33581		// this enables you to use this._super(); to call the super function
33582		function createMethod(name, fn) {
33583			return function() {
33584				var self = this, tmp = self._super, ret;
33585
33586				self._super = _super[name];
33587				ret = fn.apply(self, arguments);
33588				self._super = tmp;
33589
33590				return ret;
33591			};
33592		}
33593
33594		// Instantiate a base class (but only create the instance,
33595		// don't run the init constructor)
33596		initializing = true;
33597
33598		/*eslint new-cap:0 */
33599		prototype = new self();
33600		initializing = false;
33601
33602		// Add mixins
33603		if (prop.Mixins) {
33604			each(prop.Mixins, function(mixin) {
33605				mixin = mixin;
33606
33607				for (var name in mixin) {
33608					if (name !== "init") {
33609						prop[name] = mixin[name];
33610					}
33611				}
33612			});
33613
33614			if (_super.Mixins) {
33615				prop.Mixins = _super.Mixins.concat(prop.Mixins);
33616			}
33617		}
33618
33619		// Generate dummy methods
33620		if (prop.Methods) {
33621			each(prop.Methods.split(','), function(name) {
33622				prop[name] = dummy;
33623			});
33624		}
33625
33626		// Generate property methods
33627		if (prop.Properties) {
33628			each(prop.Properties.split(','), function(name) {
33629				var fieldName = '_' + name;
33630
33631				prop[name] = function(value) {
33632					var self = this, undef;
33633
33634					// Set value
33635					if (value !== undef) {
33636						self[fieldName] = value;
33637
33638						return self;
33639					}
33640
33641					// Get value
33642					return self[fieldName];
33643				};
33644			});
33645		}
33646
33647		// Static functions
33648		if (prop.Statics) {
33649			each(prop.Statics, function(func, name) {
33650				Class[name] = func;
33651			});
33652		}
33653
33654		// Default settings
33655		if (prop.Defaults && _super.Defaults) {
33656			prop.Defaults = extend({}, _super.Defaults, prop.Defaults);
33657		}
33658
33659		// Copy the properties over onto the new prototype
33660		for (name in prop) {
33661			member = prop[name];
33662
33663			if (typeof member == "function" && _super[name]) {
33664				prototype[name] = createMethod(name, member);
33665			} else {
33666				prototype[name] = member;
33667			}
33668		}
33669
33670		// Populate our constructed prototype object
33671		Class.prototype = prototype;
33672
33673		// Enforce the constructor to be what we expect
33674		Class.constructor = Class;
33675
33676		// And make this class extendible
33677		Class.extend = extendClass;
33678
33679		return Class;
33680	};
33681
33682	return Class;
33683});
33684
33685// Included from: js/tinymce/classes/util/EventDispatcher.js
33686
33687/**
33688 * EventDispatcher.js
33689 *
33690 * Released under LGPL License.
33691 * Copyright (c) 1999-2015 Ephox Corp. All rights reserved
33692 *
33693 * License: http://www.tinymce.com/license
33694 * Contributing: http://www.tinymce.com/contributing
33695 */
33696
33697/**
33698 * This class lets you add/remove and fire events by name on the specified scope. This makes
33699 * it easy to add event listener logic to any class.
33700 *
33701 * @class tinymce.util.EventDispatcher
33702 * @example
33703 *  var eventDispatcher = new EventDispatcher();
33704 *
33705 *  eventDispatcher.on('click', function() {console.log('data');});
33706 *  eventDispatcher.fire('click', {data: 123});
33707 */
33708define("tinymce/util/EventDispatcher", [
33709	"tinymce/util/Tools"
33710], function(Tools) {
33711	var nativeEvents = Tools.makeMap(
33712		"focus blur focusin focusout click dblclick mousedown mouseup mousemove mouseover beforepaste paste cut copy selectionchange " +
33713		"mouseout mouseenter mouseleave wheel keydown keypress keyup input contextmenu dragstart dragend dragover " +
33714		"draggesture dragdrop drop drag submit " +
33715		"compositionstart compositionend compositionupdate touchstart touchend",
33716		' '
33717	);
33718
33719	function Dispatcher(settings) {
33720		var self = this, scope, bindings = {}, toggleEvent;
33721
33722		function returnFalse() {
33723			return false;
33724		}
33725
33726		function returnTrue() {
33727			return true;
33728		}
33729
33730		settings = settings || {};
33731		scope = settings.scope || self;
33732		toggleEvent = settings.toggleEvent || returnFalse;
33733
33734		/**
33735		 * Fires the specified event by name.
33736		 *
33737		 * @method fire
33738		 * @param {String} name Name of the event to fire.
33739		 * @param {Object?} args Event arguments.
33740		 * @return {Object} Event args instance passed in.
33741		 * @example
33742		 * instance.fire('event', {...});
33743		 */
33744		function fire(name, args) {
33745			var handlers, i, l, callback;
33746
33747			name = name.toLowerCase();
33748			args = args || {};
33749			args.type = name;
33750
33751			// Setup target is there isn't one
33752			if (!args.target) {
33753				args.target = scope;
33754			}
33755
33756			// Add event delegation methods if they are missing
33757			if (!args.preventDefault) {
33758				// Add preventDefault method
33759				args.preventDefault = function() {
33760					args.isDefaultPrevented = returnTrue;
33761				};
33762
33763				// Add stopPropagation
33764				args.stopPropagation = function() {
33765					args.isPropagationStopped = returnTrue;
33766				};
33767
33768				// Add stopImmediatePropagation
33769				args.stopImmediatePropagation = function() {
33770					args.isImmediatePropagationStopped = returnTrue;
33771				};
33772
33773				// Add event delegation states
33774				args.isDefaultPrevented = returnFalse;
33775				args.isPropagationStopped = returnFalse;
33776				args.isImmediatePropagationStopped = returnFalse;
33777			}
33778
33779			if (settings.beforeFire) {
33780				settings.beforeFire(args);
33781			}
33782
33783			handlers = bindings[name];
33784			if (handlers) {
33785				for (i = 0, l = handlers.length; i < l; i++) {
33786					callback = handlers[i];
33787
33788					// Unbind handlers marked with "once"
33789					if (callback.once) {
33790						off(name, callback.func);
33791					}
33792
33793					// Stop immediate propagation if needed
33794					if (args.isImmediatePropagationStopped()) {
33795						args.stopPropagation();
33796						return args;
33797					}
33798
33799					// If callback returns false then prevent default and stop all propagation
33800					if (callback.func.call(scope, args) === false) {
33801						args.preventDefault();
33802						return args;
33803					}
33804				}
33805			}
33806
33807			return args;
33808		}
33809
33810		/**
33811		 * Binds an event listener to a specific event by name.
33812		 *
33813		 * @method on
33814		 * @param {String} name Event name or space separated list of events to bind.
33815		 * @param {callback} callback Callback to be executed when the event occurs.
33816		 * @param {Boolean} first Optional flag if the event should be prepended. Use this with care.
33817		 * @return {Object} Current class instance.
33818		 * @example
33819		 * instance.on('event', function(e) {
33820		 *     // Callback logic
33821		 * });
33822		 */
33823		function on(name, callback, prepend, extra) {
33824			var handlers, names, i;
33825
33826			if (callback === false) {
33827				callback = returnFalse;
33828			}
33829
33830			if (callback) {
33831				callback = {
33832					func: callback
33833				};
33834
33835				if (extra) {
33836					Tools.extend(callback, extra);
33837				}
33838
33839				names = name.toLowerCase().split(' ');
33840				i = names.length;
33841				while (i--) {
33842					name = names[i];
33843					handlers = bindings[name];
33844					if (!handlers) {
33845						handlers = bindings[name] = [];
33846						toggleEvent(name, true);
33847					}
33848
33849					if (prepend) {
33850						handlers.unshift(callback);
33851					} else {
33852						handlers.push(callback);
33853					}
33854				}
33855			}
33856
33857			return self;
33858		}
33859
33860		/**
33861		 * Unbinds an event listener to a specific event by name.
33862		 *
33863		 * @method off
33864		 * @param {String?} name Name of the event to unbind.
33865		 * @param {callback?} callback Callback to unbind.
33866		 * @return {Object} Current class instance.
33867		 * @example
33868		 * // Unbind specific callback
33869		 * instance.off('event', handler);
33870		 *
33871		 * // Unbind all listeners by name
33872		 * instance.off('event');
33873		 *
33874		 * // Unbind all events
33875		 * instance.off();
33876		 */
33877		function off(name, callback) {
33878			var i, handlers, bindingName, names, hi;
33879
33880			if (name) {
33881				names = name.toLowerCase().split(' ');
33882				i = names.length;
33883				while (i--) {
33884					name = names[i];
33885					handlers = bindings[name];
33886
33887					// Unbind all handlers
33888					if (!name) {
33889						for (bindingName in bindings) {
33890							toggleEvent(bindingName, false);
33891							delete bindings[bindingName];
33892						}
33893
33894						return self;
33895					}
33896
33897					if (handlers) {
33898						// Unbind all by name
33899						if (!callback) {
33900							handlers.length = 0;
33901						} else {
33902							// Unbind specific ones
33903							hi = handlers.length;
33904							while (hi--) {
33905								if (handlers[hi].func === callback) {
33906									handlers = handlers.slice(0, hi).concat(handlers.slice(hi + 1));
33907									bindings[name] = handlers;
33908								}
33909							}
33910						}
33911
33912						if (!handlers.length) {
33913							toggleEvent(name, false);
33914							delete bindings[name];
33915						}
33916					}
33917				}
33918			} else {
33919				for (name in bindings) {
33920					toggleEvent(name, false);
33921				}
33922
33923				bindings = {};
33924			}
33925
33926			return self;
33927		}
33928
33929		/**
33930		 * Binds an event listener to a specific event by name
33931		 * and automatically unbind the event once the callback fires.
33932		 *
33933		 * @method once
33934		 * @param {String} name Event name or space separated list of events to bind.
33935		 * @param {callback} callback Callback to be executed when the event occurs.
33936		 * @param {Boolean} first Optional flag if the event should be prepended. Use this with care.
33937		 * @return {Object} Current class instance.
33938		 * @example
33939		 * instance.once('event', function(e) {
33940		 *     // Callback logic
33941		 * });
33942		 */
33943		function once(name, callback, prepend) {
33944			return on(name, callback, prepend, {once: true});
33945		}
33946
33947		/**
33948		 * Returns true/false if the dispatcher has a event of the specified name.
33949		 *
33950		 * @method has
33951		 * @param {String} name Name of the event to check for.
33952		 * @return {Boolean} true/false if the event exists or not.
33953		 */
33954		function has(name) {
33955			name = name.toLowerCase();
33956			return !(!bindings[name] || bindings[name].length === 0);
33957		}
33958
33959		// Expose
33960		self.fire = fire;
33961		self.on = on;
33962		self.off = off;
33963		self.once = once;
33964		self.has = has;
33965	}
33966
33967	/**
33968	 * Returns true/false if the specified event name is a native browser event or not.
33969	 *
33970	 * @method isNative
33971	 * @param {String} name Name to check if it's native.
33972	 * @return {Boolean} true/false if the event is native or not.
33973	 * @static
33974	 */
33975	Dispatcher.isNative = function(name) {
33976		return !!nativeEvents[name.toLowerCase()];
33977	};
33978
33979	return Dispatcher;
33980});
33981
33982// Included from: js/tinymce/classes/data/Binding.js
33983
33984/**
33985 * Binding.js
33986 *
33987 * Released under LGPL License.
33988 * Copyright (c) 1999-2015 Ephox Corp. All rights reserved
33989 *
33990 * License: http://www.tinymce.com/license
33991 * Contributing: http://www.tinymce.com/contributing
33992 */
33993
33994/**
33995 * This class gets dynamically extended to provide a binding between two models. This makes it possible to
33996 * sync the state of two properties in two models by a layer of abstraction.
33997 *
33998 * @private
33999 * @class tinymce.data.Binding
34000 */
34001define("tinymce/data/Binding", [], function() {
34002	/**
34003	 * Constructs a new bidning.
34004	 *
34005	 * @constructor
34006	 * @method Binding
34007	 * @param {Object} settings Settings to the binding.
34008	 */
34009	function Binding(settings) {
34010		this.create = settings.create;
34011	}
34012
34013	/**
34014	 * Creates a binding for a property on a model.
34015	 *
34016	 * @method create
34017	 * @param {tinymce.data.ObservableObject} model Model to create binding to.
34018	 * @param {String} name Name of property to bind.
34019	 * @return {tinymce.data.Binding} Binding instance.
34020	 */
34021	Binding.create = function(model, name) {
34022		return new Binding({
34023			create: function(otherModel, otherName) {
34024				var bindings;
34025
34026				function fromSelfToOther(e) {
34027					otherModel.set(otherName, e.value);
34028				}
34029
34030				function fromOtherToSelf(e) {
34031					model.set(name, e.value);
34032				}
34033
34034				otherModel.on('change:' + otherName, fromOtherToSelf);
34035				model.on('change:' + name, fromSelfToOther);
34036
34037				// Keep track of the bindings
34038				bindings = otherModel._bindings;
34039
34040				if (!bindings) {
34041					bindings = otherModel._bindings = [];
34042
34043					otherModel.on('destroy', function() {
34044						var i = bindings.length;
34045
34046						while (i--) {
34047							bindings[i]();
34048						}
34049					});
34050				}
34051
34052				bindings.push(function() {
34053					model.off('change:' + name, fromSelfToOther);
34054				});
34055
34056				return model.get(name);
34057			}
34058		});
34059	};
34060
34061	return Binding;
34062});
34063
34064// Included from: js/tinymce/classes/util/Observable.js
34065
34066/**
34067 * Observable.js
34068 *
34069 * Released under LGPL License.
34070 * Copyright (c) 1999-2015 Ephox Corp. All rights reserved
34071 *
34072 * License: http://www.tinymce.com/license
34073 * Contributing: http://www.tinymce.com/contributing
34074 */
34075
34076/**
34077 * This mixin will add event binding logic to classes.
34078 *
34079 * @mixin tinymce.util.Observable
34080 */
34081define("tinymce/util/Observable", [
34082	"tinymce/util/EventDispatcher"
34083], function(EventDispatcher) {
34084	function getEventDispatcher(obj) {
34085		if (!obj._eventDispatcher) {
34086			obj._eventDispatcher = new EventDispatcher({
34087				scope: obj,
34088				toggleEvent: function(name, state) {
34089					if (EventDispatcher.isNative(name) && obj.toggleNativeEvent) {
34090						obj.toggleNativeEvent(name, state);
34091					}
34092				}
34093			});
34094		}
34095
34096		return obj._eventDispatcher;
34097	}
34098
34099	return {
34100		/**
34101		 * Fires the specified event by name.
34102		 *
34103		 * @method fire
34104		 * @param {String} name Name of the event to fire.
34105		 * @param {Object?} args Event arguments.
34106		 * @param {Boolean?} bubble True/false if the event is to be bubbled.
34107		 * @return {Object} Event args instance passed in.
34108		 * @example
34109		 * instance.fire('event', {...});
34110		 */
34111		fire: function(name, args, bubble) {
34112			var self = this;
34113
34114			// Prevent all events except the remove event after the instance has been removed
34115			if (self.removed && name !== "remove") {
34116				return args;
34117			}
34118
34119			args = getEventDispatcher(self).fire(name, args, bubble);
34120
34121			// Bubble event up to parents
34122			if (bubble !== false && self.parent) {
34123				var parent = self.parent();
34124				while (parent && !args.isPropagationStopped()) {
34125					parent.fire(name, args, false);
34126					parent = parent.parent();
34127				}
34128			}
34129
34130			return args;
34131		},
34132
34133		/**
34134		 * Binds an event listener to a specific event by name.
34135		 *
34136		 * @method on
34137		 * @param {String} name Event name or space separated list of events to bind.
34138		 * @param {callback} callback Callback to be executed when the event occurs.
34139		 * @param {Boolean} first Optional flag if the event should be prepended. Use this with care.
34140		 * @return {Object} Current class instance.
34141		 * @example
34142		 * instance.on('event', function(e) {
34143		 *     // Callback logic
34144		 * });
34145		 */
34146		on: function(name, callback, prepend) {
34147			return getEventDispatcher(this).on(name, callback, prepend);
34148		},
34149
34150		/**
34151		 * Unbinds an event listener to a specific event by name.
34152		 *
34153		 * @method off
34154		 * @param {String?} name Name of the event to unbind.
34155		 * @param {callback?} callback Callback to unbind.
34156		 * @return {Object} Current class instance.
34157		 * @example
34158		 * // Unbind specific callback
34159		 * instance.off('event', handler);
34160		 *
34161		 * // Unbind all listeners by name
34162		 * instance.off('event');
34163		 *
34164		 * // Unbind all events
34165		 * instance.off();
34166		 */
34167		off: function(name, callback) {
34168			return getEventDispatcher(this).off(name, callback);
34169		},
34170
34171		/**
34172		 * Bind the event callback and once it fires the callback is removed.
34173		 *
34174		 * @method once
34175		 * @param {String} name Name of the event to bind.
34176		 * @param {callback} callback Callback to bind only once.
34177		 * @return {Object} Current class instance.
34178		 */
34179		once: function(name, callback) {
34180			return getEventDispatcher(this).once(name, callback);
34181		},
34182
34183		/**
34184		 * Returns true/false if the object has a event of the specified name.
34185		 *
34186		 * @method hasEventListeners
34187		 * @param {String} name Name of the event to check for.
34188		 * @return {Boolean} true/false if the event exists or not.
34189		 */
34190		hasEventListeners: function(name) {
34191			return getEventDispatcher(this).has(name);
34192		}
34193	};
34194});
34195
34196// Included from: js/tinymce/classes/data/ObservableObject.js
34197
34198/**
34199 * ObservableObject.js
34200 *
34201 * Released under LGPL License.
34202 * Copyright (c) 1999-2015 Ephox Corp. All rights reserved
34203 *
34204 * License: http://www.tinymce.com/license
34205 * Contributing: http://www.tinymce.com/contributing
34206 */
34207
34208/**
34209 * This class is a object that is observable when properties changes a change event gets emitted.
34210 *
34211 * @private
34212 * @class tinymce.data.ObservableObject
34213 */
34214define("tinymce/data/ObservableObject", [
34215	"tinymce/data/Binding",
34216	"tinymce/util/Observable",
34217	"tinymce/util/Class",
34218	"tinymce/util/Tools"
34219], function(Binding, Observable, Class, Tools) {
34220	function isNode(node) {
34221		return node.nodeType > 0;
34222	}
34223
34224	// Todo: Maybe this should be shallow compare since it might be huge object references
34225	function isEqual(a, b) {
34226		var k, checked;
34227
34228		// Strict equals
34229		if (a === b) {
34230			return true;
34231		}
34232
34233		// Compare null
34234		if (a === null || b === null) {
34235			return a === b;
34236		}
34237
34238		// Compare number, boolean, string, undefined
34239		if (typeof a !== "object" || typeof b !== "object") {
34240			return a === b;
34241		}
34242
34243		// Compare arrays
34244		if (Tools.isArray(b)) {
34245			if (a.length !== b.length) {
34246				return false;
34247			}
34248
34249			k = a.length;
34250			while (k--) {
34251				if (!isEqual(a[k], b[k])) {
34252					return false;
34253				}
34254			}
34255		}
34256
34257		// Shallow compare nodes
34258		if (isNode(a) || isNode(b)) {
34259			return a === b;
34260		}
34261
34262		// Compare objects
34263		checked = {};
34264		for (k in b) {
34265			if (!isEqual(a[k], b[k])) {
34266				return false;
34267			}
34268
34269			checked[k] = true;
34270		}
34271
34272		for (k in a) {
34273			if (!checked[k] && !isEqual(a[k], b[k])) {
34274				return false;
34275			}
34276		}
34277
34278		return true;
34279	}
34280
34281	return Class.extend({
34282		Mixins: [Observable],
34283
34284		/**
34285		 * Constructs a new observable object instance.
34286		 *
34287		 * @constructor
34288		 * @param {Object} data Initial data for the object.
34289		 */
34290		init: function(data) {
34291			var name, value;
34292
34293			data = data || {};
34294
34295			for (name in data) {
34296				value = data[name];
34297
34298				if (value instanceof Binding) {
34299					data[name] = value.create(this, name);
34300				}
34301			}
34302
34303			this.data = data;
34304		},
34305
34306		/**
34307		 * Sets a property on the value this will call
34308		 * observers if the value is a change from the current value.
34309		 *
34310		 * @method set
34311		 * @param {String/object} name Name of the property to set or a object of items to set.
34312		 * @param {Object} value Value to set for the property.
34313		 * @return {tinymce.data.ObservableObject} Observable object instance.
34314		 */
34315		set: function(name, value) {
34316			var key, args, oldValue = this.data[name];
34317
34318			if (value instanceof Binding) {
34319				value = value.create(this, name);
34320			}
34321
34322			if (typeof name === "object") {
34323				for (key in name) {
34324					this.set(key, name[key]);
34325				}
34326
34327				return this;
34328			}
34329
34330			if (!isEqual(oldValue, value)) {
34331				this.data[name] = value;
34332
34333				args = {
34334					target: this,
34335					name: name,
34336					value: value,
34337					oldValue: oldValue
34338				};
34339
34340				this.fire('change:' + name, args);
34341				this.fire('change', args);
34342			}
34343
34344			return this;
34345		},
34346
34347		/**
34348		 * Gets a property by name.
34349		 *
34350		 * @method get
34351		 * @param {String} name Name of the property to get.
34352		 * @return {Object} Object value of propery.
34353		 */
34354		get: function(name) {
34355			return this.data[name];
34356		},
34357
34358		/**
34359		 * Returns true/false if the specified property exists.
34360		 *
34361		 * @method has
34362		 * @param {String} name Name of the property to check for.
34363		 * @return {Boolean} true/false if the item exists.
34364		 */
34365		has: function(name) {
34366			return name in this.data;
34367		},
34368
34369		/**
34370		 * Returns a dynamic property binding for the specified property name. This makes
34371		 * it possible to sync the state of two properties in two ObservableObject instances.
34372		 *
34373		 * @method bind
34374		 * @param {String} name Name of the property to sync with the property it's inserted to.
34375		 * @return {tinymce.data.Binding} Data binding instance.
34376		 */
34377		bind: function(name) {
34378			return Binding.create(this, name);
34379		},
34380
34381		/**
34382		 * Destroys the observable object and fires the "destroy"
34383		 * event and clean up any internal resources.
34384		 *
34385		 * @method destroy
34386		 */
34387		destroy: function() {
34388			this.fire('destroy');
34389		}
34390	});
34391});
34392
34393// Included from: js/tinymce/classes/ui/Selector.js
34394
34395/**
34396 * Selector.js
34397 *
34398 * Released under LGPL License.
34399 * Copyright (c) 1999-2015 Ephox Corp. All rights reserved
34400 *
34401 * License: http://www.tinymce.com/license
34402 * Contributing: http://www.tinymce.com/contributing
34403 */
34404
34405/*eslint no-nested-ternary:0 */
34406
34407/**
34408 * Selector engine, enables you to select controls by using CSS like expressions.
34409 * We currently only support basic CSS expressions to reduce the size of the core
34410 * and the ones we support should be enough for most cases.
34411 *
34412 * @example
34413 * Supported expressions:
34414 *  element
34415 *  element#name
34416 *  element.class
34417 *  element[attr]
34418 *  element[attr*=value]
34419 *  element[attr~=value]
34420 *  element[attr!=value]
34421 *  element[attr^=value]
34422 *  element[attr$=value]
34423 *  element:<state>
34424 *  element:not(<expression>)
34425 *  element:first
34426 *  element:last
34427 *  element:odd
34428 *  element:even
34429 *  element element
34430 *  element > element
34431 *
34432 * @class tinymce.ui.Selector
34433 */
34434define("tinymce/ui/Selector", [
34435	"tinymce/util/Class"
34436], function(Class) {
34437	"use strict";
34438
34439	/**
34440	 * Produces an array with a unique set of objects. It will not compare the values
34441	 * but the references of the objects.
34442	 *
34443	 * @private
34444	 * @method unqiue
34445	 * @param {Array} array Array to make into an array with unique items.
34446	 * @return {Array} Array with unique items.
34447	 */
34448	function unique(array) {
34449		var uniqueItems = [], i = array.length, item;
34450
34451		while (i--) {
34452			item = array[i];
34453
34454			if (!item.__checked) {
34455				uniqueItems.push(item);
34456				item.__checked = 1;
34457			}
34458		}
34459
34460		i = uniqueItems.length;
34461		while (i--) {
34462			delete uniqueItems[i].__checked;
34463		}
34464
34465		return uniqueItems;
34466	}
34467
34468	var expression = /^([\w\\*]+)?(?:#([\w\\]+))?(?:\.([\w\\\.]+))?(?:\[\@?([\w\\]+)([\^\$\*!~]?=)([\w\\]+)\])?(?:\:(.+))?/i;
34469
34470	/*jshint maxlen:255 */
34471	/*eslint max-len:0 */
34472	var chunker = /((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^\[\]]*\]|['"][^'"]*['"]|[^\[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g,
34473		whiteSpace = /^\s*|\s*$/g,
34474		Collection;
34475
34476	var Selector = Class.extend({
34477		/**
34478		 * Constructs a new Selector instance.
34479		 *
34480		 * @constructor
34481		 * @method init
34482		 * @param {String} selector CSS like selector expression.
34483		 */
34484		init: function(selector) {
34485			var match = this.match;
34486
34487			function compileNameFilter(name) {
34488				if (name) {
34489					name = name.toLowerCase();
34490
34491					return function(item) {
34492						return name === '*' || item.type === name;
34493					};
34494				}
34495			}
34496
34497			function compileIdFilter(id) {
34498				if (id) {
34499					return function(item) {
34500						return item._name === id;
34501					};
34502				}
34503			}
34504
34505			function compileClassesFilter(classes) {
34506				if (classes) {
34507					classes = classes.split('.');
34508
34509					return function(item) {
34510						var i = classes.length;
34511
34512						while (i--) {
34513							if (!item.classes.contains(classes[i])) {
34514								return false;
34515							}
34516						}
34517
34518						return true;
34519					};
34520				}
34521			}
34522
34523			function compileAttrFilter(name, cmp, check) {
34524				if (name) {
34525					return function(item) {
34526						var value = item[name] ? item[name]() : '';
34527
34528						return !cmp ? !!check :
34529							cmp === "=" ? value === check :
34530							cmp === "*=" ? value.indexOf(check) >= 0 :
34531							cmp === "~=" ? (" " + value + " ").indexOf(" " + check + " ") >= 0 :
34532							cmp === "!=" ? value != check :
34533							cmp === "^=" ? value.indexOf(check) === 0 :
34534							cmp === "$=" ? value.substr(value.length - check.length) === check :
34535							false;
34536					};
34537				}
34538			}
34539
34540			function compilePsuedoFilter(name) {
34541				var notSelectors;
34542
34543				if (name) {
34544					name = /(?:not\((.+)\))|(.+)/i.exec(name);
34545
34546					if (!name[1]) {
34547						name = name[2];
34548
34549						return function(item, index, length) {
34550							return name === 'first' ? index === 0 :
34551								name === 'last' ? index === length - 1 :
34552								name === 'even' ? index % 2 === 0 :
34553								name === 'odd' ? index % 2 === 1 :
34554								item[name] ? item[name]() :
34555								false;
34556						};
34557					}
34558
34559					// Compile not expression
34560					notSelectors = parseChunks(name[1], []);
34561
34562					return function(item) {
34563						return !match(item, notSelectors);
34564					};
34565				}
34566			}
34567
34568			function compile(selector, filters, direct) {
34569				var parts;
34570
34571				function add(filter) {
34572					if (filter) {
34573						filters.push(filter);
34574					}
34575				}
34576
34577				// Parse expression into parts
34578				parts = expression.exec(selector.replace(whiteSpace, ''));
34579
34580				add(compileNameFilter(parts[1]));
34581				add(compileIdFilter(parts[2]));
34582				add(compileClassesFilter(parts[3]));
34583				add(compileAttrFilter(parts[4], parts[5], parts[6]));
34584				add(compilePsuedoFilter(parts[7]));
34585
34586				// Mark the filter with pseudo for performance
34587				filters.pseudo = !!parts[7];
34588				filters.direct = direct;
34589
34590				return filters;
34591			}
34592
34593			// Parser logic based on Sizzle by John Resig
34594			function parseChunks(selector, selectors) {
34595				var parts = [], extra, matches, i;
34596
34597				do {
34598					chunker.exec("");
34599					matches = chunker.exec(selector);
34600
34601					if (matches) {
34602						selector = matches[3];
34603						parts.push(matches[1]);
34604
34605						if (matches[2]) {
34606							extra = matches[3];
34607							break;
34608						}
34609					}
34610				} while (matches);
34611
34612				if (extra) {
34613					parseChunks(extra, selectors);
34614				}
34615
34616				selector = [];
34617				for (i = 0; i < parts.length; i++) {
34618					if (parts[i] != '>') {
34619						selector.push(compile(parts[i], [], parts[i - 1] === '>'));
34620					}
34621				}
34622
34623				selectors.push(selector);
34624
34625				return selectors;
34626			}
34627
34628			this._selectors = parseChunks(selector, []);
34629		},
34630
34631		/**
34632		 * Returns true/false if the selector matches the specified control.
34633		 *
34634		 * @method match
34635		 * @param {tinymce.ui.Control} control Control to match against the selector.
34636		 * @param {Array} selectors Optional array of selectors, mostly used internally.
34637		 * @return {Boolean} true/false state if the control matches or not.
34638		 */
34639		match: function(control, selectors) {
34640			var i, l, si, sl, selector, fi, fl, filters, index, length, siblings, count, item;
34641
34642			selectors = selectors || this._selectors;
34643			for (i = 0, l = selectors.length; i < l; i++) {
34644				selector = selectors[i];
34645				sl = selector.length;
34646				item = control;
34647				count = 0;
34648
34649				for (si = sl - 1; si >= 0; si--) {
34650					filters = selector[si];
34651
34652					while (item) {
34653						// Find the index and length since a pseudo filter like :first needs it
34654						if (filters.pseudo) {
34655							siblings = item.parent().items();
34656							index = length = siblings.length;
34657							while (index--) {
34658								if (siblings[index] === item) {
34659									break;
34660								}
34661							}
34662						}
34663
34664						for (fi = 0, fl = filters.length; fi < fl; fi++) {
34665							if (!filters[fi](item, index, length)) {
34666								fi = fl + 1;
34667								break;
34668							}
34669						}
34670
34671						if (fi === fl) {
34672							count++;
34673							break;
34674						} else {
34675							// If it didn't match the right most expression then
34676							// break since it's no point looking at the parents
34677							if (si === sl - 1) {
34678								break;
34679							}
34680						}
34681
34682						item = item.parent();
34683					}
34684				}
34685
34686				// If we found all selectors then return true otherwise continue looking
34687				if (count === sl) {
34688					return true;
34689				}
34690			}
34691
34692			return false;
34693		},
34694
34695		/**
34696		 * Returns a tinymce.ui.Collection with matches of the specified selector inside the specified container.
34697		 *
34698		 * @method find
34699		 * @param {tinymce.ui.Control} container Container to look for items in.
34700		 * @return {tinymce.ui.Collection} Collection with matched elements.
34701		 */
34702		find: function(container) {
34703			var matches = [], i, l, selectors = this._selectors;
34704
34705			function collect(items, selector, index) {
34706				var i, l, fi, fl, item, filters = selector[index];
34707
34708				for (i = 0, l = items.length; i < l; i++) {
34709					item = items[i];
34710
34711					// Run each filter against the item
34712					for (fi = 0, fl = filters.length; fi < fl; fi++) {
34713						if (!filters[fi](item, i, l)) {
34714							fi = fl + 1;
34715							break;
34716						}
34717					}
34718
34719					// All filters matched the item
34720					if (fi === fl) {
34721						// Matched item is on the last expression like: panel toolbar [button]
34722						if (index == selector.length - 1) {
34723							matches.push(item);
34724						} else {
34725							// Collect next expression type
34726							if (item.items) {
34727								collect(item.items(), selector, index + 1);
34728							}
34729						}
34730					} else if (filters.direct) {
34731						return;
34732					}
34733
34734					// Collect child items
34735					if (item.items) {
34736						collect(item.items(), selector, index);
34737					}
34738				}
34739			}
34740
34741			if (container.items) {
34742				for (i = 0, l = selectors.length; i < l; i++) {
34743					collect(container.items(), selectors[i], 0);
34744				}
34745
34746				// Unique the matches if needed
34747				if (l > 1) {
34748					matches = unique(matches);
34749				}
34750			}
34751
34752			// Fix for circular reference
34753			if (!Collection) {
34754				// TODO: Fix me!
34755				Collection = Selector.Collection;
34756			}
34757
34758			return new Collection(matches);
34759		}
34760	});
34761
34762	return Selector;
34763});
34764
34765// Included from: js/tinymce/classes/ui/Collection.js
34766
34767/**
34768 * Collection.js
34769 *
34770 * Released under LGPL License.
34771 * Copyright (c) 1999-2015 Ephox Corp. All rights reserved
34772 *
34773 * License: http://www.tinymce.com/license
34774 * Contributing: http://www.tinymce.com/contributing
34775 */
34776
34777/**
34778 * Control collection, this class contains control instances and it enables you to
34779 * perform actions on all the contained items. This is very similar to how jQuery works.
34780 *
34781 * @example
34782 * someCollection.show().disabled(true);
34783 *
34784 * @class tinymce.ui.Collection
34785 */
34786define("tinymce/ui/Collection", [
34787	"tinymce/util/Tools",
34788	"tinymce/ui/Selector",
34789	"tinymce/util/Class"
34790], function(Tools, Selector, Class) {
34791	"use strict";
34792
34793	var Collection, proto, push = Array.prototype.push, slice = Array.prototype.slice;
34794
34795	proto = {
34796		/**
34797		 * Current number of contained control instances.
34798		 *
34799		 * @field length
34800		 * @type Number
34801		 */
34802		length: 0,
34803
34804		/**
34805		 * Constructor for the collection.
34806		 *
34807		 * @constructor
34808		 * @method init
34809		 * @param {Array} items Optional array with items to add.
34810		 */
34811		init: function(items) {
34812			if (items) {
34813				this.add(items);
34814			}
34815		},
34816
34817		/**
34818		 * Adds new items to the control collection.
34819		 *
34820		 * @method add
34821		 * @param {Array} items Array if items to add to collection.
34822		 * @return {tinymce.ui.Collection} Current collection instance.
34823		 */
34824		add: function(items) {
34825			var self = this;
34826
34827			// Force single item into array
34828			if (!Tools.isArray(items)) {
34829				if (items instanceof Collection) {
34830					self.add(items.toArray());
34831				} else {
34832					push.call(self, items);
34833				}
34834			} else {
34835				push.apply(self, items);
34836			}
34837
34838			return self;
34839		},
34840
34841		/**
34842		 * Sets the contents of the collection. This will remove any existing items
34843		 * and replace them with the ones specified in the input array.
34844		 *
34845		 * @method set
34846		 * @param {Array} items Array with items to set into the Collection.
34847		 * @return {tinymce.ui.Collection} Collection instance.
34848		 */
34849		set: function(items) {
34850			var self = this, len = self.length, i;
34851
34852			self.length = 0;
34853			self.add(items);
34854
34855			// Remove old entries
34856			for (i = self.length; i < len; i++) {
34857				delete self[i];
34858			}
34859
34860			return self;
34861		},
34862
34863		/**
34864		 * Filters the collection item based on the specified selector expression or selector function.
34865		 *
34866		 * @method filter
34867		 * @param {String} selector Selector expression to filter items by.
34868		 * @return {tinymce.ui.Collection} Collection containing the filtered items.
34869		 */
34870		filter: function(selector) {
34871			var self = this, i, l, matches = [], item, match;
34872
34873			// Compile string into selector expression
34874			if (typeof selector === "string") {
34875				selector = new Selector(selector);
34876
34877				match = function(item) {
34878					return selector.match(item);
34879				};
34880			} else {
34881				// Use selector as matching function
34882				match = selector;
34883			}
34884
34885			for (i = 0, l = self.length; i < l; i++) {
34886				item = self[i];
34887
34888				if (match(item)) {
34889					matches.push(item);
34890				}
34891			}
34892
34893			return new Collection(matches);
34894		},
34895
34896		/**
34897		 * Slices the items within the collection.
34898		 *
34899		 * @method slice
34900		 * @param {Number} index Index to slice at.
34901		 * @param {Number} len Optional length to slice.
34902		 * @return {tinymce.ui.Collection} Current collection.
34903		 */
34904		slice: function() {
34905			return new Collection(slice.apply(this, arguments));
34906		},
34907
34908		/**
34909		 * Makes the current collection equal to the specified index.
34910		 *
34911		 * @method eq
34912		 * @param {Number} index Index of the item to set the collection to.
34913		 * @return {tinymce.ui.Collection} Current collection.
34914		 */
34915		eq: function(index) {
34916			return index === -1 ? this.slice(index) : this.slice(index, +index + 1);
34917		},
34918
34919		/**
34920		 * Executes the specified callback on each item in collection.
34921		 *
34922		 * @method each
34923		 * @param {function} callback Callback to execute for each item in collection.
34924		 * @return {tinymce.ui.Collection} Current collection instance.
34925		 */
34926		each: function(callback) {
34927			Tools.each(this, callback);
34928
34929			return this;
34930		},
34931
34932		/**
34933		 * Returns an JavaScript array object of the contents inside the collection.
34934		 *
34935		 * @method toArray
34936		 * @return {Array} Array with all items from collection.
34937		 */
34938		toArray: function() {
34939			return Tools.toArray(this);
34940		},
34941
34942		/**
34943		 * Finds the index of the specified control or return -1 if it isn't in the collection.
34944		 *
34945		 * @method indexOf
34946		 * @param {Control} ctrl Control instance to look for.
34947		 * @return {Number} Index of the specified control or -1.
34948		 */
34949		indexOf: function(ctrl) {
34950			var self = this, i = self.length;
34951
34952			while (i--) {
34953				if (self[i] === ctrl) {
34954					break;
34955				}
34956			}
34957
34958			return i;
34959		},
34960
34961		/**
34962		 * Returns a new collection of the contents in reverse order.
34963		 *
34964		 * @method reverse
34965		 * @return {tinymce.ui.Collection} Collection instance with reversed items.
34966		 */
34967		reverse: function() {
34968			return new Collection(Tools.toArray(this).reverse());
34969		},
34970
34971		/**
34972		 * Returns true/false if the class exists or not.
34973		 *
34974		 * @method hasClass
34975		 * @param {String} cls Class to check for.
34976		 * @return {Boolean} true/false state if the class exists or not.
34977		 */
34978		hasClass: function(cls) {
34979			return this[0] ? this[0].classes.contains(cls) : false;
34980		},
34981
34982		/**
34983		 * Sets/gets the specific property on the items in the collection. The same as executing control.<property>(<value>);
34984		 *
34985		 * @method prop
34986		 * @param {String} name Property name to get/set.
34987		 * @param {Object} value Optional object value to set.
34988		 * @return {tinymce.ui.Collection} Current collection instance or value of the first item on a get operation.
34989		 */
34990		prop: function(name, value) {
34991			var self = this, undef, item;
34992
34993			if (value !== undef) {
34994				self.each(function(item) {
34995					if (item[name]) {
34996						item[name](value);
34997					}
34998				});
34999
35000				return self;
35001			}
35002
35003			item = self[0];
35004
35005			if (item && item[name]) {
35006				return item[name]();
35007			}
35008		},
35009
35010		/**
35011		 * Executes the specific function name with optional arguments an all items in collection if it exists.
35012		 *
35013		 * @example collection.exec("myMethod", arg1, arg2, arg3);
35014		 * @method exec
35015		 * @param {String} name Name of the function to execute.
35016		 * @param {Object} ... Multiple arguments to pass to each function.
35017		 * @return {tinymce.ui.Collection} Current collection.
35018		 */
35019		exec: function(name) {
35020			var self = this, args = Tools.toArray(arguments).slice(1);
35021
35022			self.each(function(item) {
35023				if (item[name]) {
35024					item[name].apply(item, args);
35025				}
35026			});
35027
35028			return self;
35029		},
35030
35031		/**
35032		 * Remove all items from collection and DOM.
35033		 *
35034		 * @method remove
35035		 * @return {tinymce.ui.Collection} Current collection.
35036		 */
35037		remove: function() {
35038			var i = this.length;
35039
35040			while (i--) {
35041				this[i].remove();
35042			}
35043
35044			return this;
35045		},
35046
35047		/**
35048		 * Adds a class to all items in the collection.
35049		 *
35050		 * @method addClass
35051		 * @param {String} cls Class to add to each item.
35052		 * @return {tinymce.ui.Collection} Current collection instance.
35053		 */
35054		addClass: function(cls) {
35055			return this.each(function(item) {
35056				item.classes.add(cls);
35057			});
35058		},
35059
35060		/**
35061		 * Removes the specified class from all items in collection.
35062		 *
35063		 * @method removeClass
35064		 * @param {String} cls Class to remove from each item.
35065		 * @return {tinymce.ui.Collection} Current collection instance.
35066		 */
35067		removeClass: function(cls) {
35068			return this.each(function(item) {
35069				item.classes.remove(cls);
35070			});
35071		}
35072
35073		/**
35074		 * Fires the specified event by name and arguments on the control. This will execute all
35075		 * bound event handlers.
35076		 *
35077		 * @method fire
35078		 * @param {String} name Name of the event to fire.
35079		 * @param {Object} args Optional arguments to pass to the event.
35080		 * @return {tinymce.ui.Collection} Current collection instance.
35081		 */
35082		// fire: function(event, args) {}, -- Generated by code below
35083
35084		/**
35085		 * Binds a callback to the specified event. This event can both be
35086		 * native browser events like "click" or custom ones like PostRender.
35087		 *
35088		 * The callback function will have two parameters the first one being the control that received the event
35089		 * the second one will be the event object either the browsers native event object or a custom JS object.
35090		 *
35091		 * @method on
35092		 * @param {String} name Name of the event to bind. For example "click".
35093		 * @param {String/function} callback Callback function to execute ones the event occurs.
35094		 * @return {tinymce.ui.Collection} Current collection instance.
35095		 */
35096		// on: function(name, callback) {}, -- Generated by code below
35097
35098		/**
35099		 * Unbinds the specified event and optionally a specific callback. If you omit the name
35100		 * parameter all event handlers will be removed. If you omit the callback all event handles
35101		 * by the specified name will be removed.
35102		 *
35103		 * @method off
35104		 * @param {String} name Optional name for the event to unbind.
35105		 * @param {function} callback Optional callback function to unbind.
35106		 * @return {tinymce.ui.Collection} Current collection instance.
35107		 */
35108		// off: function(name, callback) {}, -- Generated by code below
35109
35110		/**
35111		 * Shows the items in the current collection.
35112		 *
35113		 * @method show
35114		 * @return {tinymce.ui.Collection} Current collection instance.
35115		 */
35116		// show: function() {}, -- Generated by code below
35117
35118		/**
35119		 * Hides the items in the current collection.
35120		 *
35121		 * @method hide
35122		 * @return {tinymce.ui.Collection} Current collection instance.
35123		 */
35124		// hide: function() {}, -- Generated by code below
35125
35126		/**
35127		 * Sets/gets the text contents of the items in the current collection.
35128		 *
35129		 * @method text
35130		 * @return {tinymce.ui.Collection} Current collection instance or text value of the first item on a get operation.
35131		 */
35132		// text: function(value) {}, -- Generated by code below
35133
35134		/**
35135		 * Sets/gets the name contents of the items in the current collection.
35136		 *
35137		 * @method name
35138		 * @return {tinymce.ui.Collection} Current collection instance or name value of the first item on a get operation.
35139		 */
35140		// name: function(value) {}, -- Generated by code below
35141
35142		/**
35143		 * Sets/gets the disabled state on the items in the current collection.
35144		 *
35145		 * @method disabled
35146		 * @return {tinymce.ui.Collection} Current collection instance or disabled state of the first item on a get operation.
35147		 */
35148		// disabled: function(state) {}, -- Generated by code below
35149
35150		/**
35151		 * Sets/gets the active state on the items in the current collection.
35152		 *
35153		 * @method active
35154		 * @return {tinymce.ui.Collection} Current collection instance or active state of the first item on a get operation.
35155		 */
35156		// active: function(state) {}, -- Generated by code below
35157
35158		/**
35159		 * Sets/gets the selected state on the items in the current collection.
35160		 *
35161		 * @method selected
35162		 * @return {tinymce.ui.Collection} Current collection instance or selected state of the first item on a get operation.
35163		 */
35164		// selected: function(state) {}, -- Generated by code below
35165
35166		/**
35167		 * Sets/gets the selected state on the items in the current collection.
35168		 *
35169		 * @method visible
35170		 * @return {tinymce.ui.Collection} Current collection instance or visible state of the first item on a get operation.
35171		 */
35172		// visible: function(state) {}, -- Generated by code below
35173	};
35174
35175	// Extend tinymce.ui.Collection prototype with some generated control specific methods
35176	Tools.each('fire on off show hide append prepend before after reflow'.split(' '), function(name) {
35177		proto[name] = function() {
35178			var args = Tools.toArray(arguments);
35179
35180			this.each(function(ctrl) {
35181				if (name in ctrl) {
35182					ctrl[name].apply(ctrl, args);
35183				}
35184			});
35185
35186			return this;
35187		};
35188	});
35189
35190	// Extend tinymce.ui.Collection prototype with some property methods
35191	Tools.each('text name disabled active selected checked visible parent value data'.split(' '), function(name) {
35192		proto[name] = function(value) {
35193			return this.prop(name, value);
35194		};
35195	});
35196
35197	// Create class based on the new prototype
35198	Collection = Class.extend(proto);
35199
35200	// Stick Collection into Selector to prevent circual references
35201	Selector.Collection = Collection;
35202
35203	return Collection;
35204});
35205
35206// Included from: js/tinymce/classes/ui/DomUtils.js
35207
35208/**
35209 * DomUtils.js
35210 *
35211 * Released under LGPL License.
35212 * Copyright (c) 1999-2015 Ephox Corp. All rights reserved
35213 *
35214 * License: http://www.tinymce.com/license
35215 * Contributing: http://www.tinymce.com/contributing
35216 */
35217
35218/**
35219 * Private UI DomUtils proxy.
35220 *
35221 * @private
35222 * @class tinymce.ui.DomUtils
35223 */
35224define("tinymce/ui/DomUtils", [
35225	"tinymce/util/Tools",
35226	"tinymce/dom/DOMUtils"
35227], function(Tools, DOMUtils) {
35228	"use strict";
35229
35230	var count = 0;
35231
35232	return {
35233		id: function() {
35234			return 'mceu_' + (count++);
35235		},
35236
35237		createFragment: function(html) {
35238			return DOMUtils.DOM.createFragment(html);
35239		},
35240
35241		getWindowSize: function() {
35242			return DOMUtils.DOM.getViewPort();
35243		},
35244
35245		getSize: function(elm) {
35246			var width, height;
35247
35248			if (elm.getBoundingClientRect) {
35249				var rect = elm.getBoundingClientRect();
35250
35251				width = Math.max(rect.width || (rect.right - rect.left), elm.offsetWidth);
35252				height = Math.max(rect.height || (rect.bottom - rect.bottom), elm.offsetHeight);
35253			} else {
35254				width = elm.offsetWidth;
35255				height = elm.offsetHeight;
35256			}
35257
35258			return {width: width, height: height};
35259		},
35260
35261		getPos: function(elm, root) {
35262			return DOMUtils.DOM.getPos(elm, root);
35263		},
35264
35265		getViewPort: function(win) {
35266			return DOMUtils.DOM.getViewPort(win);
35267		},
35268
35269		get: function(id) {
35270			return document.getElementById(id);
35271		},
35272
35273		addClass: function(elm, cls) {
35274			return DOMUtils.DOM.addClass(elm, cls);
35275		},
35276
35277		removeClass: function(elm, cls) {
35278			return DOMUtils.DOM.removeClass(elm, cls);
35279		},
35280
35281		hasClass: function(elm, cls) {
35282			return DOMUtils.DOM.hasClass(elm, cls);
35283		},
35284
35285		toggleClass: function(elm, cls, state) {
35286			return DOMUtils.DOM.toggleClass(elm, cls, state);
35287		},
35288
35289		css: function(elm, name, value) {
35290			return DOMUtils.DOM.setStyle(elm, name, value);
35291		},
35292
35293		getRuntimeStyle: function(elm, name) {
35294			return DOMUtils.DOM.getStyle(elm, name, true);
35295		},
35296
35297		on: function(target, name, callback, scope) {
35298			return DOMUtils.DOM.bind(target, name, callback, scope);
35299		},
35300
35301		off: function(target, name, callback) {
35302			return DOMUtils.DOM.unbind(target, name, callback);
35303		},
35304
35305		fire: function(target, name, args) {
35306			return DOMUtils.DOM.fire(target, name, args);
35307		},
35308
35309		innerHtml: function(elm, html) {
35310			// Workaround for <div> in <p> bug on IE 8 #6178
35311			DOMUtils.DOM.setHTML(elm, html);
35312		}
35313	};
35314});
35315
35316// Included from: js/tinymce/classes/ui/BoxUtils.js
35317
35318/**
35319 * BoxUtils.js
35320 *
35321 * Released under LGPL License.
35322 * Copyright (c) 1999-2015 Ephox Corp. All rights reserved
35323 *
35324 * License: http://www.tinymce.com/license
35325 * Contributing: http://www.tinymce.com/contributing
35326 */
35327
35328/**
35329 * Utility class for box parsing and measuring.
35330 *
35331 * @private
35332 * @class tinymce.ui.BoxUtils
35333 */
35334define("tinymce/ui/BoxUtils", [
35335], function() {
35336	"use strict";
35337
35338	return {
35339		/**
35340		 * Parses the specified box value. A box value contains 1-4 properties in clockwise order.
35341		 *
35342		 * @method parseBox
35343		 * @param {String/Number} value Box value "0 1 2 3" or "0" etc.
35344		 * @return {Object} Object with top/right/bottom/left properties.
35345		 * @private
35346		 */
35347		parseBox: function(value) {
35348			var len, radix = 10;
35349
35350			if (!value) {
35351				return;
35352			}
35353
35354			if (typeof value === "number") {
35355				value = value || 0;
35356
35357				return {
35358					top: value,
35359					left: value,
35360					bottom: value,
35361					right: value
35362				};
35363			}
35364
35365			value = value.split(' ');
35366			len = value.length;
35367
35368			if (len === 1) {
35369				value[1] = value[2] = value[3] = value[0];
35370			} else if (len === 2) {
35371				value[2] = value[0];
35372				value[3] = value[1];
35373			} else if (len === 3) {
35374				value[3] = value[1];
35375			}
35376
35377			return {
35378				top: parseInt(value[0], radix) || 0,
35379				right: parseInt(value[1], radix) || 0,
35380				bottom: parseInt(value[2], radix) || 0,
35381				left: parseInt(value[3], radix) || 0
35382			};
35383		},
35384
35385		measureBox: function(elm, prefix) {
35386			function getStyle(name) {
35387				var defaultView = document.defaultView;
35388
35389				if (defaultView) {
35390					// Remove camelcase
35391					name = name.replace(/[A-Z]/g, function(a) {
35392						return '-' + a;
35393					});
35394
35395					return defaultView.getComputedStyle(elm, null).getPropertyValue(name);
35396				}
35397
35398				return elm.currentStyle[name];
35399			}
35400
35401			function getSide(name) {
35402				var val = parseFloat(getStyle(name), 10);
35403
35404				return isNaN(val) ? 0 : val;
35405			}
35406
35407			return {
35408				top: getSide(prefix + "TopWidth"),
35409				right: getSide(prefix + "RightWidth"),
35410				bottom: getSide(prefix + "BottomWidth"),
35411				left: getSide(prefix + "LeftWidth")
35412			};
35413		}
35414	};
35415});
35416
35417// Included from: js/tinymce/classes/ui/ClassList.js
35418
35419/**
35420 * ClassList.js
35421 *
35422 * Released under LGPL License.
35423 * Copyright (c) 1999-2015 Ephox Corp. All rights reserved
35424 *
35425 * License: http://www.tinymce.com/license
35426 * Contributing: http://www.tinymce.com/contributing
35427 */
35428
35429/**
35430 * Handles adding and removal of classes.
35431 *
35432 * @private
35433 * @class tinymce.ui.ClassList
35434 */
35435define("tinymce/ui/ClassList", [
35436	"tinymce/util/Tools"
35437], function(Tools) {
35438	"use strict";
35439
35440	function noop() {
35441	}
35442
35443	/**
35444	 * Constructs a new class list the specified onchange
35445	 * callback will be executed when the class list gets modifed.
35446	 *
35447	 * @constructor ClassList
35448	 * @param {function} onchange Onchange callback to be executed.
35449	 */
35450	function ClassList(onchange) {
35451		this.cls = [];
35452		this.cls._map = {};
35453		this.onchange = onchange || noop;
35454		this.prefix = '';
35455	}
35456
35457	Tools.extend(ClassList.prototype, {
35458		/**
35459		 * Adds a new class to the class list.
35460		 *
35461		 * @method add
35462		 * @param {String} cls Class to be added.
35463		 * @return {tinymce.ui.ClassList} Current class list instance.
35464		 */
35465		add: function(cls) {
35466			if (cls && !this.contains(cls)) {
35467				this.cls._map[cls] = true;
35468				this.cls.push(cls);
35469				this._change();
35470			}
35471
35472			return this;
35473		},
35474
35475		/**
35476		 * Removes the specified class from the class list.
35477		 *
35478		 * @method remove
35479		 * @param {String} cls Class to be removed.
35480		 * @return {tinymce.ui.ClassList} Current class list instance.
35481		 */
35482		remove: function(cls) {
35483			if (this.contains(cls)) {
35484				for (var i = 0; i < this.cls.length; i++) {
35485					if (this.cls[i] === cls) {
35486						break;
35487					}
35488				}
35489
35490				this.cls.splice(i, 1);
35491				delete this.cls._map[cls];
35492				this._change();
35493			}
35494
35495			return this;
35496		},
35497
35498		/**
35499		 * Toggles a class in the class list.
35500		 *
35501		 * @method toggle
35502		 * @param {String} cls Class to be added/removed.
35503		 * @param {Boolean} state Optional state if it should be added/removed.
35504		 * @return {tinymce.ui.ClassList} Current class list instance.
35505		 */
35506		toggle: function(cls, state) {
35507			var curState = this.contains(cls);
35508
35509			if (curState !== state) {
35510				if (curState) {
35511					this.remove(cls);
35512				} else {
35513					this.add(cls);
35514				}
35515
35516				this._change();
35517			}
35518
35519			return this;
35520		},
35521
35522		/**
35523		 * Returns true if the class list has the specified class.
35524		 *
35525		 * @method contains
35526		 * @param {String} cls Class to look for.
35527		 * @return {Boolean} true/false if the class exists or not.
35528		 */
35529		contains: function(cls) {
35530			return !!this.cls._map[cls];
35531		},
35532
35533		/**
35534		 * Returns a space separated list of classes.
35535		 *
35536		 * @method toString
35537		 * @return {String} Space separated list of classes.
35538		 */
35539
35540		_change: function() {
35541			delete this.clsValue;
35542			this.onchange.call(this);
35543		}
35544	});
35545
35546	// IE 8 compatibility
35547	ClassList.prototype.toString = function() {
35548		var value;
35549
35550		if (this.clsValue) {
35551			return this.clsValue;
35552		}
35553
35554		value = '';
35555		for (var i = 0; i < this.cls.length; i++) {
35556			if (i > 0) {
35557				value += ' ';
35558			}
35559
35560			value += this.prefix + this.cls[i];
35561		}
35562
35563		return value;
35564	};
35565
35566	return ClassList;
35567});
35568
35569// Included from: js/tinymce/classes/ui/ReflowQueue.js
35570
35571/**
35572 * ReflowQueue.js
35573 *
35574 * Released under LGPL License.
35575 * Copyright (c) 1999-2015 Ephox Corp. All rights reserved
35576 *
35577 * License: http://www.tinymce.com/license
35578 * Contributing: http://www.tinymce.com/contributing
35579 */
35580
35581/**
35582 * This class will automatically reflow controls on the next animation frame within a few milliseconds on older browsers.
35583 * If the user manually reflows then the automatic reflow will be cancelled. This class is used internally when various control states
35584 * changes that triggers a reflow.
35585 *
35586 * @class tinymce.ui.ReflowQueue
35587 * @static
35588 */
35589define("tinymce/ui/ReflowQueue", [
35590	"tinymce/util/Delay"
35591], function(Delay) {
35592	var dirtyCtrls = {}, animationFrameRequested;
35593
35594	return {
35595		/**
35596		 * Adds a control to the next automatic reflow call. This is the control that had a state
35597		 * change for example if the control was hidden/shown.
35598		 *
35599		 * @method add
35600		 * @param {tinymce.ui.Control} ctrl Control to add to queue.
35601		 */
35602		add: function(ctrl) {
35603			var parent = ctrl.parent();
35604
35605			if (parent) {
35606				if (!parent._layout || parent._layout.isNative()) {
35607					return;
35608				}
35609
35610				if (!dirtyCtrls[parent._id]) {
35611					dirtyCtrls[parent._id] = parent;
35612				}
35613
35614				if (!animationFrameRequested) {
35615					animationFrameRequested = true;
35616
35617					Delay.requestAnimationFrame(function() {
35618						var id, ctrl;
35619
35620						animationFrameRequested = false;
35621
35622						for (id in dirtyCtrls) {
35623							ctrl = dirtyCtrls[id];
35624
35625							if (ctrl.state.get('rendered')) {
35626								ctrl.reflow();
35627							}
35628						}
35629
35630						dirtyCtrls = {};
35631					}, document.body);
35632				}
35633			}
35634		},
35635
35636		/**
35637		 * Removes the specified control from the automatic reflow. This will happen when for example the user
35638		 * manually triggers a reflow.
35639		 *
35640		 * @method remove
35641		 * @param {tinymce.ui.Control} ctrl Control to remove from queue.
35642		 */
35643		remove: function(ctrl) {
35644			if (dirtyCtrls[ctrl._id]) {
35645				delete dirtyCtrls[ctrl._id];
35646			}
35647		}
35648	};
35649});
35650
35651// Included from: js/tinymce/classes/ui/Control.js
35652
35653/**
35654 * Control.js
35655 *
35656 * Released under LGPL License.
35657 * Copyright (c) 1999-2015 Ephox Corp. All rights reserved
35658 *
35659 * License: http://www.tinymce.com/license
35660 * Contributing: http://www.tinymce.com/contributing
35661 */
35662
35663/*eslint consistent-this:0 */
35664
35665/**
35666 * This is the base class for all controls and containers. All UI control instances inherit
35667 * from this one as it has the base logic needed by all of them.
35668 *
35669 * @class tinymce.ui.Control
35670 */
35671define("tinymce/ui/Control", [
35672	"tinymce/util/Class",
35673	"tinymce/util/Tools",
35674	"tinymce/util/EventDispatcher",
35675	"tinymce/data/ObservableObject",
35676	"tinymce/ui/Collection",
35677	"tinymce/ui/DomUtils",
35678	"tinymce/dom/DomQuery",
35679	"tinymce/ui/BoxUtils",
35680	"tinymce/ui/ClassList",
35681	"tinymce/ui/ReflowQueue"
35682], function(Class, Tools, EventDispatcher, ObservableObject, Collection, DomUtils, $, BoxUtils, ClassList, ReflowQueue) {
35683	"use strict";
35684
35685	var hasMouseWheelEventSupport = "onmousewheel" in document;
35686	var hasWheelEventSupport = false;
35687	var classPrefix = "mce-";
35688	var Control, idCounter = 0;
35689
35690	var proto = {
35691		Statics: {
35692			classPrefix: classPrefix
35693		},
35694
35695		isRtl: function() {
35696			return Control.rtl;
35697		},
35698
35699		/**
35700		 * Class/id prefix to use for all controls.
35701		 *
35702		 * @final
35703		 * @field {String} classPrefix
35704		 */
35705		classPrefix: classPrefix,
35706
35707		/**
35708		 * Constructs a new control instance with the specified settings.
35709		 *
35710		 * @constructor
35711		 * @param {Object} settings Name/value object with settings.
35712		 * @setting {String} style Style CSS properties to add.
35713		 * @setting {String} border Border box values example: 1 1 1 1
35714		 * @setting {String} padding Padding box values example: 1 1 1 1
35715		 * @setting {String} margin Margin box values example: 1 1 1 1
35716		 * @setting {Number} minWidth Minimal width for the control.
35717		 * @setting {Number} minHeight Minimal height for the control.
35718		 * @setting {String} classes Space separated list of classes to add.
35719		 * @setting {String} role WAI-ARIA role to use for control.
35720		 * @setting {Boolean} hidden Is the control hidden by default.
35721		 * @setting {Boolean} disabled Is the control disabled by default.
35722		 * @setting {String} name Name of the control instance.
35723		 */
35724		init: function(settings) {
35725			var self = this, classes, defaultClasses;
35726
35727			function applyClasses(classes) {
35728				var i;
35729
35730				classes = classes.split(' ');
35731				for (i = 0; i < classes.length; i++) {
35732					self.classes.add(classes[i]);
35733				}
35734			}
35735
35736			self.settings = settings = Tools.extend({}, self.Defaults, settings);
35737
35738			// Initial states
35739			self._id = settings.id || ('mceu_' + (idCounter++));
35740			self._aria = {role: settings.role};
35741			self._elmCache = {};
35742			self.$ = $;
35743
35744			self.state = new ObservableObject({
35745				visible: true,
35746				active: false,
35747				disabled: false,
35748				value: ''
35749			});
35750
35751			self.data = new ObservableObject(settings.data);
35752
35753			self.classes = new ClassList(function() {
35754				if (self.state.get('rendered')) {
35755					self.getEl().className = this.toString();
35756				}
35757			});
35758			self.classes.prefix = self.classPrefix;
35759
35760			// Setup classes
35761			classes = settings.classes;
35762			if (classes) {
35763				if (self.Defaults) {
35764					defaultClasses = self.Defaults.classes;
35765
35766					if (defaultClasses && classes != defaultClasses) {
35767						applyClasses(defaultClasses);
35768					}
35769				}
35770
35771				applyClasses(classes);
35772			}
35773
35774			Tools.each('title text name visible disabled active value'.split(' '), function(name) {
35775				if (name in settings) {
35776					self[name](settings[name]);
35777				}
35778			});
35779
35780			self.on('click', function() {
35781				if (self.disabled()) {
35782					return false;
35783				}
35784			});
35785
35786			/**
35787			 * Name/value object with settings for the current control.
35788			 *
35789			 * @field {Object} settings
35790			 */
35791			self.settings = settings;
35792
35793			self.borderBox = BoxUtils.parseBox(settings.border);
35794			self.paddingBox = BoxUtils.parseBox(settings.padding);
35795			self.marginBox = BoxUtils.parseBox(settings.margin);
35796
35797			if (settings.hidden) {
35798				self.hide();
35799			}
35800		},
35801
35802		// Will generate getter/setter methods for these properties
35803		Properties: 'parent,name',
35804
35805		/**
35806		 * Returns the root element to render controls into.
35807		 *
35808		 * @method getContainerElm
35809		 * @return {Element} HTML DOM element to render into.
35810		 */
35811		getContainerElm: function() {
35812			return document.body;
35813		},
35814
35815		/**
35816		 * Returns a control instance for the current DOM element.
35817		 *
35818		 * @method getParentCtrl
35819		 * @param {Element} elm HTML dom element to get parent control from.
35820		 * @return {tinymce.ui.Control} Control instance or undefined.
35821		 */
35822		getParentCtrl: function(elm) {
35823			var ctrl, lookup = this.getRoot().controlIdLookup;
35824
35825			while (elm && lookup) {
35826				ctrl = lookup[elm.id];
35827				if (ctrl) {
35828					break;
35829				}
35830
35831				elm = elm.parentNode;
35832			}
35833
35834			return ctrl;
35835		},
35836
35837		/**
35838		 * Initializes the current controls layout rect.
35839		 * This will be executed by the layout managers to determine the
35840		 * default minWidth/minHeight etc.
35841		 *
35842		 * @method initLayoutRect
35843		 * @return {Object} Layout rect instance.
35844		 */
35845		initLayoutRect: function() {
35846			var self = this, settings = self.settings, borderBox, layoutRect;
35847			var elm = self.getEl(), width, height, minWidth, minHeight, autoResize;
35848			var startMinWidth, startMinHeight, initialSize;
35849
35850			// Measure the current element
35851			borderBox = self.borderBox = self.borderBox || BoxUtils.measureBox(elm, 'border');
35852			self.paddingBox = self.paddingBox || BoxUtils.measureBox(elm, 'padding');
35853			self.marginBox = self.marginBox || BoxUtils.measureBox(elm, 'margin');
35854			initialSize = DomUtils.getSize(elm);
35855
35856			// Setup minWidth/minHeight and width/height
35857			startMinWidth = settings.minWidth;
35858			startMinHeight = settings.minHeight;
35859			minWidth = startMinWidth || initialSize.width;
35860			minHeight = startMinHeight || initialSize.height;
35861			width = settings.width;
35862			height = settings.height;
35863			autoResize = settings.autoResize;
35864			autoResize = typeof autoResize != "undefined" ? autoResize : !width && !height;
35865
35866			width = width || minWidth;
35867			height = height || minHeight;
35868
35869			var deltaW = borderBox.left + borderBox.right;
35870			var deltaH = borderBox.top + borderBox.bottom;
35871
35872			var maxW = settings.maxWidth || 0xFFFF;
35873			var maxH = settings.maxHeight || 0xFFFF;
35874
35875			// Setup initial layout rect
35876			self._layoutRect = layoutRect = {
35877				x: settings.x || 0,
35878				y: settings.y || 0,
35879				w: width,
35880				h: height,
35881				deltaW: deltaW,
35882				deltaH: deltaH,
35883				contentW: width - deltaW,
35884				contentH: height - deltaH,
35885				innerW: width - deltaW,
35886				innerH: height - deltaH,
35887				startMinWidth: startMinWidth || 0,
35888				startMinHeight: startMinHeight || 0,
35889				minW: Math.min(minWidth, maxW),
35890				minH: Math.min(minHeight, maxH),
35891				maxW: maxW,
35892				maxH: maxH,
35893				autoResize: autoResize,
35894				scrollW: 0
35895			};
35896
35897			self._lastLayoutRect = {};
35898
35899			return layoutRect;
35900		},
35901
35902		/**
35903		 * Getter/setter for the current layout rect.
35904		 *
35905		 * @method layoutRect
35906		 * @param {Object} [newRect] Optional new layout rect.
35907		 * @return {tinymce.ui.Control/Object} Current control or rect object.
35908		 */
35909		layoutRect: function(newRect) {
35910			var self = this, curRect = self._layoutRect, lastLayoutRect, size, deltaWidth, deltaHeight, undef, repaintControls;
35911
35912			// Initialize default layout rect
35913			if (!curRect) {
35914				curRect = self.initLayoutRect();
35915			}
35916
35917			// Set new rect values
35918			if (newRect) {
35919				// Calc deltas between inner and outer sizes
35920				deltaWidth = curRect.deltaW;
35921				deltaHeight = curRect.deltaH;
35922
35923				// Set x position
35924				if (newRect.x !== undef) {
35925					curRect.x = newRect.x;
35926				}
35927
35928				// Set y position
35929				if (newRect.y !== undef) {
35930					curRect.y = newRect.y;
35931				}
35932
35933				// Set minW
35934				if (newRect.minW !== undef) {
35935					curRect.minW = newRect.minW;
35936				}
35937
35938				// Set minH
35939				if (newRect.minH !== undef) {
35940					curRect.minH = newRect.minH;
35941				}
35942
35943				// Set new width and calculate inner width
35944				size = newRect.w;
35945				if (size !== undef) {
35946					size = size < curRect.minW ? curRect.minW : size;
35947					size = size > curRect.maxW ? curRect.maxW : size;
35948					curRect.w = size;
35949					curRect.innerW = size - deltaWidth;
35950				}
35951
35952				// Set new height and calculate inner height
35953				size = newRect.h;
35954				if (size !== undef) {
35955					size = size < curRect.minH ? curRect.minH : size;
35956					size = size > curRect.maxH ? curRect.maxH : size;
35957					curRect.h = size;
35958					curRect.innerH = size - deltaHeight;
35959				}
35960
35961				// Set new inner width and calculate width
35962				size = newRect.innerW;
35963				if (size !== undef) {
35964					size = size < curRect.minW - deltaWidth ? curRect.minW - deltaWidth : size;
35965					size = size > curRect.maxW - deltaWidth ? curRect.maxW - deltaWidth : size;
35966					curRect.innerW = size;
35967					curRect.w = size + deltaWidth;
35968				}
35969
35970				// Set new height and calculate inner height
35971				size = newRect.innerH;
35972				if (size !== undef) {
35973					size = size < curRect.minH - deltaHeight ? curRect.minH - deltaHeight : size;
35974					size = size > curRect.maxH - deltaHeight ? curRect.maxH - deltaHeight : size;
35975					curRect.innerH = size;
35976					curRect.h = size + deltaHeight;
35977				}
35978
35979				// Set new contentW
35980				if (newRect.contentW !== undef) {
35981					curRect.contentW = newRect.contentW;
35982				}
35983
35984				// Set new contentH
35985				if (newRect.contentH !== undef) {
35986					curRect.contentH = newRect.contentH;
35987				}
35988
35989				// Compare last layout rect with the current one to see if we need to repaint or not
35990				lastLayoutRect = self._lastLayoutRect;
35991				if (lastLayoutRect.x !== curRect.x || lastLayoutRect.y !== curRect.y ||
35992					lastLayoutRect.w !== curRect.w || lastLayoutRect.h !== curRect.h) {
35993					repaintControls = Control.repaintControls;
35994
35995					if (repaintControls) {
35996						if (repaintControls.map && !repaintControls.map[self._id]) {
35997							repaintControls.push(self);
35998							repaintControls.map[self._id] = true;
35999						}
36000					}
36001
36002					lastLayoutRect.x = curRect.x;
36003					lastLayoutRect.y = curRect.y;
36004					lastLayoutRect.w = curRect.w;
36005					lastLayoutRect.h = curRect.h;
36006				}
36007
36008				return self;
36009			}
36010
36011			return curRect;
36012		},
36013
36014		/**
36015		 * Repaints the control after a layout operation.
36016		 *
36017		 * @method repaint
36018		 */
36019		repaint: function() {
36020			var self = this, style, bodyStyle, bodyElm, rect, borderBox;
36021			var borderW, borderH, lastRepaintRect, round, value;
36022
36023			// Use Math.round on all values on IE < 9
36024			round = !document.createRange ? Math.round : function(value) {
36025				return value;
36026			};
36027
36028			style = self.getEl().style;
36029			rect = self._layoutRect;
36030			lastRepaintRect = self._lastRepaintRect || {};
36031
36032			borderBox = self.borderBox;
36033			borderW = borderBox.left + borderBox.right;
36034			borderH = borderBox.top + borderBox.bottom;
36035
36036			if (rect.x !== lastRepaintRect.x) {
36037				style.left = round(rect.x) + 'px';
36038				lastRepaintRect.x = rect.x;
36039			}
36040
36041			if (rect.y !== lastRepaintRect.y) {
36042				style.top = round(rect.y) + 'px';
36043				lastRepaintRect.y = rect.y;
36044			}
36045
36046			if (rect.w !== lastRepaintRect.w) {
36047				value = round(rect.w - borderW);
36048				style.width = (value >= 0 ? value : 0) + 'px';
36049				lastRepaintRect.w = rect.w;
36050			}
36051
36052			if (rect.h !== lastRepaintRect.h) {
36053				value = round(rect.h - borderH);
36054				style.height = (value >= 0 ? value : 0) + 'px';
36055				lastRepaintRect.h = rect.h;
36056			}
36057
36058			// Update body if needed
36059			if (self._hasBody && rect.innerW !== lastRepaintRect.innerW) {
36060				value = round(rect.innerW);
36061
36062				bodyElm = self.getEl('body');
36063				if (bodyElm) {
36064					bodyStyle = bodyElm.style;
36065					bodyStyle.width = (value >= 0 ? value : 0) + 'px';
36066				}
36067
36068				lastRepaintRect.innerW = rect.innerW;
36069			}
36070
36071			if (self._hasBody && rect.innerH !== lastRepaintRect.innerH) {
36072				value = round(rect.innerH);
36073
36074				bodyElm = bodyElm || self.getEl('body');
36075				if (bodyElm) {
36076					bodyStyle = bodyStyle || bodyElm.style;
36077					bodyStyle.height = (value >= 0 ? value : 0) + 'px';
36078				}
36079
36080				lastRepaintRect.innerH = rect.innerH;
36081			}
36082
36083			self._lastRepaintRect = lastRepaintRect;
36084			self.fire('repaint', {}, false);
36085		},
36086
36087		/**
36088		 * Binds a callback to the specified event. This event can both be
36089		 * native browser events like "click" or custom ones like PostRender.
36090		 *
36091		 * The callback function will be passed a DOM event like object that enables yout do stop propagation.
36092		 *
36093		 * @method on
36094		 * @param {String} name Name of the event to bind. For example "click".
36095		 * @param {String/function} callback Callback function to execute ones the event occurs.
36096		 * @return {tinymce.ui.Control} Current control object.
36097		 */
36098		on: function(name, callback) {
36099			var self = this;
36100
36101			function resolveCallbackName(name) {
36102				var callback, scope;
36103
36104				if (typeof name != 'string') {
36105					return name;
36106				}
36107
36108				return function(e) {
36109					if (!callback) {
36110						self.parentsAndSelf().each(function(ctrl) {
36111							var callbacks = ctrl.settings.callbacks;
36112
36113							if (callbacks && (callback = callbacks[name])) {
36114								scope = ctrl;
36115								return false;
36116							}
36117						});
36118					}
36119
36120					if (!callback) {
36121						e.action = name;
36122						this.fire('execute', e);
36123						return;
36124					}
36125
36126					return callback.call(scope, e);
36127				};
36128			}
36129
36130			getEventDispatcher(self).on(name, resolveCallbackName(callback));
36131
36132			return self;
36133		},
36134
36135		/**
36136		 * Unbinds the specified event and optionally a specific callback. If you omit the name
36137		 * parameter all event handlers will be removed. If you omit the callback all event handles
36138		 * by the specified name will be removed.
36139		 *
36140		 * @method off
36141		 * @param {String} [name] Name for the event to unbind.
36142		 * @param {function} [callback] Callback function to unbind.
36143		 * @return {tinymce.ui.Control} Current control object.
36144		 */
36145		off: function(name, callback) {
36146			getEventDispatcher(this).off(name, callback);
36147			return this;
36148		},
36149
36150		/**
36151		 * Fires the specified event by name and arguments on the control. This will execute all
36152		 * bound event handlers.
36153		 *
36154		 * @method fire
36155		 * @param {String} name Name of the event to fire.
36156		 * @param {Object} [args] Arguments to pass to the event.
36157		 * @param {Boolean} [bubble] Value to control bubbling. Defaults to true.
36158		 * @return {Object} Current arguments object.
36159		 */
36160		fire: function(name, args, bubble) {
36161			var self = this;
36162
36163			args = args || {};
36164
36165			if (!args.control) {
36166				args.control = self;
36167			}
36168
36169			args = getEventDispatcher(self).fire(name, args);
36170
36171			// Bubble event up to parents
36172			if (bubble !== false && self.parent) {
36173				var parent = self.parent();
36174				while (parent && !args.isPropagationStopped()) {
36175					parent.fire(name, args, false);
36176					parent = parent.parent();
36177				}
36178			}
36179
36180			return args;
36181		},
36182
36183		/**
36184		 * Returns true/false if the specified event has any listeners.
36185		 *
36186		 * @method hasEventListeners
36187		 * @param {String} name Name of the event to check for.
36188		 * @return {Boolean} True/false state if the event has listeners.
36189		 */
36190		hasEventListeners: function(name) {
36191			return getEventDispatcher(this).has(name);
36192		},
36193
36194		/**
36195		 * Returns a control collection with all parent controls.
36196		 *
36197		 * @method parents
36198		 * @param {String} selector Optional selector expression to find parents.
36199		 * @return {tinymce.ui.Collection} Collection with all parent controls.
36200		 */
36201		parents: function(selector) {
36202			var self = this, ctrl, parents = new Collection();
36203
36204			// Add each parent to collection
36205			for (ctrl = self.parent(); ctrl; ctrl = ctrl.parent()) {
36206				parents.add(ctrl);
36207			}
36208
36209			// Filter away everything that doesn't match the selector
36210			if (selector) {
36211				parents = parents.filter(selector);
36212			}
36213
36214			return parents;
36215		},
36216
36217		/**
36218		 * Returns the current control and it's parents.
36219		 *
36220		 * @method parentsAndSelf
36221		 * @param {String} selector Optional selector expression to find parents.
36222		 * @return {tinymce.ui.Collection} Collection with all parent controls.
36223		 */
36224		parentsAndSelf: function(selector) {
36225			return new Collection(this).add(this.parents(selector));
36226		},
36227
36228		/**
36229		 * Returns the control next to the current control.
36230		 *
36231		 * @method next
36232		 * @return {tinymce.ui.Control} Next control instance.
36233		 */
36234		next: function() {
36235			var parentControls = this.parent().items();
36236
36237			return parentControls[parentControls.indexOf(this) + 1];
36238		},
36239
36240		/**
36241		 * Returns the control previous to the current control.
36242		 *
36243		 * @method prev
36244		 * @return {tinymce.ui.Control} Previous control instance.
36245		 */
36246		prev: function() {
36247			var parentControls = this.parent().items();
36248
36249			return parentControls[parentControls.indexOf(this) - 1];
36250		},
36251
36252		/**
36253		 * Sets the inner HTML of the control element.
36254		 *
36255		 * @method innerHtml
36256		 * @param {String} html Html string to set as inner html.
36257		 * @return {tinymce.ui.Control} Current control object.
36258		 */
36259		innerHtml: function(html) {
36260			this.$el.html(html);
36261			return this;
36262		},
36263
36264		/**
36265		 * Returns the control DOM element or sub element.
36266		 *
36267		 * @method getEl
36268		 * @param {String} [suffix] Suffix to get element by.
36269		 * @return {Element} HTML DOM element for the current control or it's children.
36270		 */
36271		getEl: function(suffix) {
36272			var id = suffix ? this._id + '-' + suffix : this._id;
36273
36274			if (!this._elmCache[id]) {
36275				this._elmCache[id] = $('#' + id)[0];
36276			}
36277
36278			return this._elmCache[id];
36279		},
36280
36281		/**
36282		 * Sets the visible state to true.
36283		 *
36284		 * @method show
36285		 * @return {tinymce.ui.Control} Current control instance.
36286		 */
36287		show: function() {
36288			return this.visible(true);
36289		},
36290
36291		/**
36292		 * Sets the visible state to false.
36293		 *
36294		 * @method hide
36295		 * @return {tinymce.ui.Control} Current control instance.
36296		 */
36297		hide: function() {
36298			return this.visible(false);
36299		},
36300
36301		/**
36302		 * Focuses the current control.
36303		 *
36304		 * @method focus
36305		 * @return {tinymce.ui.Control} Current control instance.
36306		 */
36307		focus: function() {
36308			try {
36309				this.getEl().focus();
36310			} catch (ex) {
36311				// Ignore IE error
36312			}
36313
36314			return this;
36315		},
36316
36317		/**
36318		 * Blurs the current control.
36319		 *
36320		 * @method blur
36321		 * @return {tinymce.ui.Control} Current control instance.
36322		 */
36323		blur: function() {
36324			this.getEl().blur();
36325
36326			return this;
36327		},
36328
36329		/**
36330		 * Sets the specified aria property.
36331		 *
36332		 * @method aria
36333		 * @param {String} name Name of the aria property to set.
36334		 * @param {String} value Value of the aria property.
36335		 * @return {tinymce.ui.Control} Current control instance.
36336		 */
36337		aria: function(name, value) {
36338			var self = this, elm = self.getEl(self.ariaTarget);
36339
36340			if (typeof value === "undefined") {
36341				return self._aria[name];
36342			}
36343
36344			self._aria[name] = value;
36345
36346			if (self.state.get('rendered')) {
36347				elm.setAttribute(name == 'role' ? name : 'aria-' + name, value);
36348			}
36349
36350			return self;
36351		},
36352
36353		/**
36354		 * Encodes the specified string with HTML entities. It will also
36355		 * translate the string to different languages.
36356		 *
36357		 * @method encode
36358		 * @param {String/Object/Array} text Text to entity encode.
36359		 * @param {Boolean} [translate=true] False if the contents shouldn't be translated.
36360		 * @return {String} Encoded and possible traslated string.
36361		 */
36362		encode: function(text, translate) {
36363			if (translate !== false) {
36364				text = this.translate(text);
36365			}
36366
36367			return (text || '').replace(/[&<>"]/g, function(match) {
36368				return '&#' + match.charCodeAt(0) + ';';
36369			});
36370		},
36371
36372		/**
36373		 * Returns the translated string.
36374		 *
36375		 * @method translate
36376		 * @param {String} text Text to translate.
36377		 * @return {String} Translated string or the same as the input.
36378		 */
36379		translate: function(text) {
36380			return Control.translate ? Control.translate(text) : text;
36381		},
36382
36383		/**
36384		 * Adds items before the current control.
36385		 *
36386		 * @method before
36387		 * @param {Array/tinymce.ui.Collection} items Array of items to prepend before this control.
36388		 * @return {tinymce.ui.Control} Current control instance.
36389		 */
36390		before: function(items) {
36391			var self = this, parent = self.parent();
36392
36393			if (parent) {
36394				parent.insert(items, parent.items().indexOf(self), true);
36395			}
36396
36397			return self;
36398		},
36399
36400		/**
36401		 * Adds items after the current control.
36402		 *
36403		 * @method after
36404		 * @param {Array/tinymce.ui.Collection} items Array of items to append after this control.
36405		 * @return {tinymce.ui.Control} Current control instance.
36406		 */
36407		after: function(items) {
36408			var self = this, parent = self.parent();
36409
36410			if (parent) {
36411				parent.insert(items, parent.items().indexOf(self));
36412			}
36413
36414			return self;
36415		},
36416
36417		/**
36418		 * Removes the current control from DOM and from UI collections.
36419		 *
36420		 * @method remove
36421		 * @return {tinymce.ui.Control} Current control instance.
36422		 */
36423		remove: function() {
36424			var self = this, elm = self.getEl(), parent = self.parent(), newItems, i;
36425
36426			if (self.items) {
36427				var controls = self.items().toArray();
36428				i = controls.length;
36429				while (i--) {
36430					controls[i].remove();
36431				}
36432			}
36433
36434			if (parent && parent.items) {
36435				newItems = [];
36436
36437				parent.items().each(function(item) {
36438					if (item !== self) {
36439						newItems.push(item);
36440					}
36441				});
36442
36443				parent.items().set(newItems);
36444				parent._lastRect = null;
36445			}
36446
36447			if (self._eventsRoot && self._eventsRoot == self) {
36448				$(elm).off();
36449			}
36450
36451			var lookup = self.getRoot().controlIdLookup;
36452			if (lookup) {
36453				delete lookup[self._id];
36454			}
36455
36456			if (elm && elm.parentNode) {
36457				elm.parentNode.removeChild(elm);
36458			}
36459
36460			self.state.set('rendered', false);
36461			self.state.destroy();
36462
36463			self.fire('remove');
36464
36465			return self;
36466		},
36467
36468		/**
36469		 * Renders the control before the specified element.
36470		 *
36471		 * @method renderBefore
36472		 * @param {Element} elm Element to render before.
36473		 * @return {tinymce.ui.Control} Current control instance.
36474		 */
36475		renderBefore: function(elm) {
36476			$(elm).before(this.renderHtml());
36477			this.postRender();
36478			return this;
36479		},
36480
36481		/**
36482		 * Renders the control to the specified element.
36483		 *
36484		 * @method renderBefore
36485		 * @param {Element} elm Element to render to.
36486		 * @return {tinymce.ui.Control} Current control instance.
36487		 */
36488		renderTo: function(elm) {
36489			$(elm || this.getContainerElm()).append(this.renderHtml());
36490			this.postRender();
36491			return this;
36492		},
36493
36494		preRender: function() {
36495		},
36496
36497		render: function() {
36498		},
36499
36500		renderHtml: function() {
36501			return '<div id="' + this._id + '" class="' + this.classes + '"></div>';
36502		},
36503
36504		/**
36505		 * Post render method. Called after the control has been rendered to the target.
36506		 *
36507		 * @method postRender
36508		 * @return {tinymce.ui.Control} Current control instance.
36509		 */
36510		postRender: function() {
36511			var self = this, settings = self.settings, elm, box, parent, name, parentEventsRoot;
36512
36513			self.$el = $(self.getEl());
36514			self.state.set('rendered', true);
36515
36516			// Bind on<event> settings
36517			for (name in settings) {
36518				if (name.indexOf("on") === 0) {
36519					self.on(name.substr(2), settings[name]);
36520				}
36521			}
36522
36523			if (self._eventsRoot) {
36524				for (parent = self.parent(); !parentEventsRoot && parent; parent = parent.parent()) {
36525					parentEventsRoot = parent._eventsRoot;
36526				}
36527
36528				if (parentEventsRoot) {
36529					for (name in parentEventsRoot._nativeEvents) {
36530						self._nativeEvents[name] = true;
36531					}
36532				}
36533			}
36534
36535			bindPendingEvents(self);
36536
36537			if (settings.style) {
36538				elm = self.getEl();
36539				if (elm) {
36540					elm.setAttribute('style', settings.style);
36541					elm.style.cssText = settings.style;
36542				}
36543			}
36544
36545			if (self.settings.border) {
36546				box = self.borderBox;
36547				self.$el.css({
36548					'border-top-width': box.top,
36549					'border-right-width': box.right,
36550					'border-bottom-width': box.bottom,
36551					'border-left-width': box.left
36552				});
36553			}
36554
36555			// Add instance to lookup
36556			var root = self.getRoot();
36557			if (!root.controlIdLookup) {
36558				root.controlIdLookup = {};
36559			}
36560
36561			root.controlIdLookup[self._id] = self;
36562
36563			for (var key in self._aria) {
36564				self.aria(key, self._aria[key]);
36565			}
36566
36567			if (self.state.get('visible') === false) {
36568				self.getEl().style.display = 'none';
36569			}
36570
36571			self.bindStates();
36572
36573			self.state.on('change:visible', function(e) {
36574				var state = e.value, parentCtrl;
36575
36576				if (self.state.get('rendered')) {
36577					self.getEl().style.display = state === false ? 'none' : '';
36578
36579					// Need to force a reflow here on IE 8
36580					self.getEl().getBoundingClientRect();
36581				}
36582
36583				// Parent container needs to reflow
36584				parentCtrl = self.parent();
36585				if (parentCtrl) {
36586					parentCtrl._lastRect = null;
36587				}
36588
36589				self.fire(state ? 'show' : 'hide');
36590
36591				ReflowQueue.add(self);
36592			});
36593
36594			self.fire('postrender', {}, false);
36595		},
36596
36597		bindStates: function() {
36598		},
36599
36600		/**
36601		 * Scrolls the current control into view.
36602		 *
36603		 * @method scrollIntoView
36604		 * @param {String} align Alignment in view top|center|bottom.
36605		 * @return {tinymce.ui.Control} Current control instance.
36606		 */
36607		scrollIntoView: function(align) {
36608			function getOffset(elm, rootElm) {
36609				var x, y, parent = elm;
36610
36611				x = y = 0;
36612				while (parent && parent != rootElm && parent.nodeType) {
36613					x += parent.offsetLeft || 0;
36614					y += parent.offsetTop || 0;
36615					parent = parent.offsetParent;
36616				}
36617
36618				return {x: x, y: y};
36619			}
36620
36621			var elm = this.getEl(), parentElm = elm.parentNode;
36622			var x, y, width, height, parentWidth, parentHeight;
36623			var pos = getOffset(elm, parentElm);
36624
36625			x = pos.x;
36626			y = pos.y;
36627			width = elm.offsetWidth;
36628			height = elm.offsetHeight;
36629			parentWidth = parentElm.clientWidth;
36630			parentHeight = parentElm.clientHeight;
36631
36632			if (align == "end") {
36633				x -= parentWidth - width;
36634				y -= parentHeight - height;
36635			} else if (align == "center") {
36636				x -= (parentWidth / 2) - (width / 2);
36637				y -= (parentHeight / 2) - (height / 2);
36638			}
36639
36640			parentElm.scrollLeft = x;
36641			parentElm.scrollTop = y;
36642
36643			return this;
36644		},
36645
36646		getRoot: function() {
36647			var ctrl = this, rootControl, parents = [];
36648
36649			while (ctrl) {
36650				if (ctrl.rootControl) {
36651					rootControl = ctrl.rootControl;
36652					break;
36653				}
36654
36655				parents.push(ctrl);
36656				rootControl = ctrl;
36657				ctrl = ctrl.parent();
36658			}
36659
36660			if (!rootControl) {
36661				rootControl = this;
36662			}
36663
36664			var i = parents.length;
36665			while (i--) {
36666				parents[i].rootControl = rootControl;
36667			}
36668
36669			return rootControl;
36670		},
36671
36672		/**
36673		 * Reflows the current control and it's parents.
36674		 * This should be used after you for example append children to the current control so
36675		 * that the layout managers know that they need to reposition everything.
36676		 *
36677		 * @example
36678		 * container.append({type: 'button', text: 'My button'}).reflow();
36679		 *
36680		 * @method reflow
36681		 * @return {tinymce.ui.Control} Current control instance.
36682		 */
36683		reflow: function() {
36684			ReflowQueue.remove(this);
36685
36686			var parent = this.parent();
36687			if (parent._layout && !parent._layout.isNative()) {
36688				parent.reflow();
36689			}
36690
36691			return this;
36692		}
36693
36694		/**
36695		 * Sets/gets the parent container for the control.
36696		 *
36697		 * @method parent
36698		 * @param {tinymce.ui.Container} parent Optional parent to set.
36699		 * @return {tinymce.ui.Control} Parent control or the current control on a set action.
36700		 */
36701		// parent: function(parent) {} -- Generated
36702
36703		/**
36704		 * Sets/gets the text for the control.
36705		 *
36706		 * @method text
36707		 * @param {String} value Value to set to control.
36708		 * @return {String/tinymce.ui.Control} Current control on a set operation or current value on a get.
36709		 */
36710		// text: function(value) {} -- Generated
36711
36712		/**
36713		 * Sets/gets the disabled state on the control.
36714		 *
36715		 * @method disabled
36716		 * @param {Boolean} state Value to set to control.
36717		 * @return {Boolean/tinymce.ui.Control} Current control on a set operation or current state on a get.
36718		 */
36719		// disabled: function(state) {} -- Generated
36720
36721		/**
36722		 * Sets/gets the active for the control.
36723		 *
36724		 * @method active
36725		 * @param {Boolean} state Value to set to control.
36726		 * @return {Boolean/tinymce.ui.Control} Current control on a set operation or current state on a get.
36727		 */
36728		// active: function(state) {} -- Generated
36729
36730		/**
36731		 * Sets/gets the name for the control.
36732		 *
36733		 * @method name
36734		 * @param {String} value Value to set to control.
36735		 * @return {String/tinymce.ui.Control} Current control on a set operation or current value on a get.
36736		 */
36737		// name: function(value) {} -- Generated
36738
36739		/**
36740		 * Sets/gets the title for the control.
36741		 *
36742		 * @method title
36743		 * @param {String} value Value to set to control.
36744		 * @return {String/tinymce.ui.Control} Current control on a set operation or current value on a get.
36745		 */
36746		// title: function(value) {} -- Generated
36747
36748		/**
36749		 * Sets/gets the visible for the control.
36750		 *
36751		 * @method visible
36752		 * @param {Boolean} state Value to set to control.
36753		 * @return {Boolean/tinymce.ui.Control} Current control on a set operation or current state on a get.
36754		 */
36755		// visible: function(value) {} -- Generated
36756	};
36757
36758	/**
36759	 * Setup state properties.
36760	 */
36761	Tools.each('text title visible disabled active value'.split(' '), function(name) {
36762		proto[name] = function(value) {
36763			if (arguments.length === 0) {
36764				return this.state.get(name);
36765			}
36766
36767			if (typeof value != "undefined") {
36768				this.state.set(name, value);
36769			}
36770
36771			return this;
36772		};
36773	});
36774
36775	Control = Class.extend(proto);
36776
36777	function getEventDispatcher(obj) {
36778		if (!obj._eventDispatcher) {
36779			obj._eventDispatcher = new EventDispatcher({
36780				scope: obj,
36781				toggleEvent: function(name, state) {
36782					if (state && EventDispatcher.isNative(name)) {
36783						if (!obj._nativeEvents) {
36784							obj._nativeEvents = {};
36785						}
36786
36787						obj._nativeEvents[name] = true;
36788
36789						if (obj.state.get('rendered')) {
36790							bindPendingEvents(obj);
36791						}
36792					}
36793				}
36794			});
36795		}
36796
36797		return obj._eventDispatcher;
36798	}
36799
36800	function bindPendingEvents(eventCtrl) {
36801		var i, l, parents, eventRootCtrl, nativeEvents, name;
36802
36803		function delegate(e) {
36804			var control = eventCtrl.getParentCtrl(e.target);
36805
36806			if (control) {
36807				control.fire(e.type, e);
36808			}
36809		}
36810
36811		function mouseLeaveHandler() {
36812			var ctrl = eventRootCtrl._lastHoverCtrl;
36813
36814			if (ctrl) {
36815				ctrl.fire("mouseleave", {target: ctrl.getEl()});
36816
36817				ctrl.parents().each(function(ctrl) {
36818					ctrl.fire("mouseleave", {target: ctrl.getEl()});
36819				});
36820
36821				eventRootCtrl._lastHoverCtrl = null;
36822			}
36823		}
36824
36825		function mouseEnterHandler(e) {
36826			var ctrl = eventCtrl.getParentCtrl(e.target), lastCtrl = eventRootCtrl._lastHoverCtrl, idx = 0, i, parents, lastParents;
36827
36828			// Over on a new control
36829			if (ctrl !== lastCtrl) {
36830				eventRootCtrl._lastHoverCtrl = ctrl;
36831
36832				parents = ctrl.parents().toArray().reverse();
36833				parents.push(ctrl);
36834
36835				if (lastCtrl) {
36836					lastParents = lastCtrl.parents().toArray().reverse();
36837					lastParents.push(lastCtrl);
36838
36839					for (idx = 0; idx < lastParents.length; idx++) {
36840						if (parents[idx] !== lastParents[idx]) {
36841							break;
36842						}
36843					}
36844
36845					for (i = lastParents.length - 1; i >= idx; i--) {
36846						lastCtrl = lastParents[i];
36847						lastCtrl.fire("mouseleave", {
36848							target: lastCtrl.getEl()
36849						});
36850					}
36851				}
36852
36853				for (i = idx; i < parents.length; i++) {
36854					ctrl = parents[i];
36855					ctrl.fire("mouseenter", {
36856						target: ctrl.getEl()
36857					});
36858				}
36859			}
36860		}
36861
36862		function fixWheelEvent(e) {
36863			e.preventDefault();
36864
36865			if (e.type == "mousewheel") {
36866				e.deltaY = -1 / 40 * e.wheelDelta;
36867
36868				if (e.wheelDeltaX) {
36869					e.deltaX = -1 / 40 * e.wheelDeltaX;
36870				}
36871			} else {
36872				e.deltaX = 0;
36873				e.deltaY = e.detail;
36874			}
36875
36876			e = eventCtrl.fire("wheel", e);
36877		}
36878
36879		nativeEvents = eventCtrl._nativeEvents;
36880		if (nativeEvents) {
36881			// Find event root element if it exists
36882			parents = eventCtrl.parents().toArray();
36883			parents.unshift(eventCtrl);
36884			for (i = 0, l = parents.length; !eventRootCtrl && i < l; i++) {
36885				eventRootCtrl = parents[i]._eventsRoot;
36886			}
36887
36888			// Event root wasn't found the use the root control
36889			if (!eventRootCtrl) {
36890				eventRootCtrl = parents[parents.length - 1] || eventCtrl;
36891			}
36892
36893			// Set the eventsRoot property on children that didn't have it
36894			eventCtrl._eventsRoot = eventRootCtrl;
36895			for (l = i, i = 0; i < l; i++) {
36896				parents[i]._eventsRoot = eventRootCtrl;
36897			}
36898
36899			var eventRootDelegates = eventRootCtrl._delegates;
36900			if (!eventRootDelegates) {
36901				eventRootDelegates = eventRootCtrl._delegates = {};
36902			}
36903
36904			// Bind native event delegates
36905			for (name in nativeEvents) {
36906				if (!nativeEvents) {
36907					return false;
36908				}
36909
36910				if (name === "wheel" && !hasWheelEventSupport) {
36911					if (hasMouseWheelEventSupport) {
36912						$(eventCtrl.getEl()).on("mousewheel", fixWheelEvent);
36913					} else {
36914						$(eventCtrl.getEl()).on("DOMMouseScroll", fixWheelEvent);
36915					}
36916
36917					continue;
36918				}
36919
36920				// Special treatment for mousenter/mouseleave since these doesn't bubble
36921				if (name === "mouseenter" || name === "mouseleave") {
36922					// Fake mousenter/mouseleave
36923					if (!eventRootCtrl._hasMouseEnter) {
36924						$(eventRootCtrl.getEl()).on("mouseleave", mouseLeaveHandler).on("mouseover", mouseEnterHandler);
36925						eventRootCtrl._hasMouseEnter = 1;
36926					}
36927				} else if (!eventRootDelegates[name]) {
36928					$(eventRootCtrl.getEl()).on(name, delegate);
36929					eventRootDelegates[name] = true;
36930				}
36931
36932				// Remove the event once it's bound
36933				nativeEvents[name] = false;
36934			}
36935		}
36936	}
36937
36938	return Control;
36939});
36940
36941// Included from: js/tinymce/classes/ui/Factory.js
36942
36943/**
36944 * Factory.js
36945 *
36946 * Released under LGPL License.
36947 * Copyright (c) 1999-2015 Ephox Corp. All rights reserved
36948 *
36949 * License: http://www.tinymce.com/license
36950 * Contributing: http://www.tinymce.com/contributing
36951 */
36952
36953/*global tinymce:true */
36954
36955/**
36956 * This class is a factory for control instances. This enables you
36957 * to create instances of controls without having to require the UI controls directly.
36958 *
36959 * It also allow you to override or add new control types.
36960 *
36961 * @class tinymce.ui.Factory
36962 */
36963define("tinymce/ui/Factory", [], function() {
36964	"use strict";
36965
36966	var types = {}, namespaceInit;
36967
36968	return {
36969		/**
36970		 * Adds a new control instance type to the factory.
36971		 *
36972		 * @method add
36973		 * @param {String} type Type name for example "button".
36974		 * @param {function} typeClass Class type function.
36975		 */
36976		add: function(type, typeClass) {
36977			types[type.toLowerCase()] = typeClass;
36978		},
36979
36980		/**
36981		 * Returns true/false if the specified type exists or not.
36982		 *
36983		 * @method has
36984		 * @param {String} type Type to look for.
36985		 * @return {Boolean} true/false if the control by name exists.
36986		 */
36987		has: function(type) {
36988			return !!types[type.toLowerCase()];
36989		},
36990
36991		/**
36992		 * Creates a new control instance based on the settings provided. The instance created will be
36993		 * based on the specified type property it can also create whole structures of components out of
36994		 * the specified JSON object.
36995		 *
36996		 * @example
36997		 * tinymce.ui.Factory.create({
36998		 *     type: 'button',
36999		 *     text: 'Hello world!'
37000		 * });
37001		 *
37002		 * @method create
37003		 * @param {Object/String} settings Name/Value object with items used to create the type.
37004		 * @return {tinymce.ui.Control} Control instance based on the specified type.
37005		 */
37006		create: function(type, settings) {
37007			var ControlType, name, namespace;
37008
37009			// Build type lookup
37010			if (!namespaceInit) {
37011				namespace = tinymce.ui;
37012
37013				for (name in namespace) {
37014					types[name.toLowerCase()] = namespace[name];
37015				}
37016
37017				namespaceInit = true;
37018			}
37019
37020			// If string is specified then use it as the type
37021			if (typeof type == 'string') {
37022				settings = settings || {};
37023				settings.type = type;
37024			} else {
37025				settings = type;
37026				type = settings.type;
37027			}
37028
37029			// Find control type
37030			type = type.toLowerCase();
37031			ControlType = types[type];
37032
37033			// #if debug
37034
37035			if (!ControlType) {
37036				throw new Error("Could not find control by type: " + type);
37037			}
37038
37039			// #endif
37040
37041			ControlType = new ControlType(settings);
37042			ControlType.type = type; // Set the type on the instance, this will be used by the Selector engine
37043
37044			return ControlType;
37045		}
37046	};
37047});
37048
37049// Included from: js/tinymce/classes/ui/KeyboardNavigation.js
37050
37051/**
37052 * KeyboardNavigation.js
37053 *
37054 * Released under LGPL License.
37055 * Copyright (c) 1999-2015 Ephox Corp. All rights reserved
37056 *
37057 * License: http://www.tinymce.com/license
37058 * Contributing: http://www.tinymce.com/contributing
37059 */
37060
37061/**
37062 * This class handles keyboard navigation of controls and elements.
37063 *
37064 * @class tinymce.ui.KeyboardNavigation
37065 */
37066define("tinymce/ui/KeyboardNavigation", [
37067], function() {
37068	"use strict";
37069
37070	/**
37071	 * This class handles all keyboard navigation for WAI-ARIA support. Each root container
37072	 * gets an instance of this class.
37073	 *
37074	 * @constructor
37075	 */
37076	return function(settings) {
37077		var root = settings.root, focusedElement, focusedControl;
37078
37079		function isElement(node) {
37080			return node && node.nodeType === 1;
37081		}
37082
37083		try {
37084			focusedElement = document.activeElement;
37085		} catch (ex) {
37086			// IE sometimes fails to return a proper element
37087			focusedElement = document.body;
37088		}
37089
37090		focusedControl = root.getParentCtrl(focusedElement);
37091
37092		/**
37093		 * Returns the currently focused elements wai aria role of the currently
37094		 * focused element or specified element.
37095		 *
37096		 * @private
37097		 * @param {Element} elm Optional element to get role from.
37098		 * @return {String} Role of specified element.
37099		 */
37100		function getRole(elm) {
37101			elm = elm || focusedElement;
37102
37103			if (isElement(elm)) {
37104				return elm.getAttribute('role');
37105			}
37106
37107			return null;
37108		}
37109
37110		/**
37111		 * Returns the wai role of the parent element of the currently
37112		 * focused element or specified element.
37113		 *
37114		 * @private
37115		 * @param {Element} elm Optional element to get parent role from.
37116		 * @return {String} Role of the first parent that has a role.
37117		 */
37118		function getParentRole(elm) {
37119			var role, parent = elm || focusedElement;
37120
37121			while ((parent = parent.parentNode)) {
37122				if ((role = getRole(parent))) {
37123					return role;
37124				}
37125			}
37126		}
37127
37128		/**
37129		 * Returns a wai aria property by name for example aria-selected.
37130		 *
37131		 * @private
37132		 * @param {String} name Name of the aria property to get for example "disabled".
37133		 * @return {String} Aria property value.
37134		 */
37135		function getAriaProp(name) {
37136			var elm = focusedElement;
37137
37138			if (isElement(elm)) {
37139				return elm.getAttribute('aria-' + name);
37140			}
37141		}
37142
37143		/**
37144		 * Is the element a text input element or not.
37145		 *
37146		 * @private
37147		 * @param {Element} elm Element to check if it's an text input element or not.
37148		 * @return {Boolean} True/false if the element is a text element or not.
37149		 */
37150		function isTextInputElement(elm) {
37151			var tagName = elm.tagName.toUpperCase();
37152
37153			// Notice: since type can be "email" etc we don't check the type
37154			// So all input elements gets treated as text input elements
37155			return tagName == "INPUT" || tagName == "TEXTAREA";
37156		}
37157
37158		/**
37159		 * Returns true/false if the specified element can be focused or not.
37160		 *
37161		 * @private
37162		 * @param {Element} elm DOM element to check if it can be focused or not.
37163		 * @return {Boolean} True/false if the element can have focus.
37164		 */
37165		function canFocus(elm) {
37166			if (isTextInputElement(elm) && !elm.hidden) {
37167				return true;
37168			}
37169
37170			if (/^(button|menuitem|checkbox|tab|menuitemcheckbox|option|gridcell)$/.test(getRole(elm))) {
37171				return true;
37172			}
37173
37174			return false;
37175		}
37176
37177		/**
37178		 * Returns an array of focusable visible elements within the specified container element.
37179		 *
37180		 * @private
37181		 * @param {Element} elm DOM element to find focusable elements within.
37182		 * @return {Array} Array of focusable elements.
37183		 */
37184		function getFocusElements(elm) {
37185			var elements = [];
37186
37187			function collect(elm) {
37188				if (elm.nodeType != 1 || elm.style.display == 'none') {
37189					return;
37190				}
37191
37192				if (canFocus(elm)) {
37193					elements.push(elm);
37194				}
37195
37196				for (var i = 0; i < elm.childNodes.length; i++) {
37197					collect(elm.childNodes[i]);
37198				}
37199			}
37200
37201			collect(elm || root.getEl());
37202
37203			return elements;
37204		}
37205
37206		/**
37207		 * Returns the navigation root control for the specified control. The navigation root
37208		 * is the control that the keyboard navigation gets scoped to for example a menubar or toolbar group.
37209		 * It will look for parents of the specified target control or the currently focused control if this option is omitted.
37210		 *
37211		 * @private
37212		 * @param {tinymce.ui.Control} targetControl Optional target control to find root of.
37213		 * @return {tinymce.ui.Control} Navigation root control.
37214		 */
37215		function getNavigationRoot(targetControl) {
37216			var navigationRoot, controls;
37217
37218			targetControl = targetControl || focusedControl;
37219			controls = targetControl.parents().toArray();
37220			controls.unshift(targetControl);
37221
37222			for (var i = 0; i < controls.length; i++) {
37223				navigationRoot = controls[i];
37224
37225				if (navigationRoot.settings.ariaRoot) {
37226					break;
37227				}
37228			}
37229
37230			return navigationRoot;
37231		}
37232
37233		/**
37234		 * Focuses the first item in the specified targetControl element or the last aria index if the
37235		 * navigation root has the ariaRemember option enabled.
37236		 *
37237		 * @private
37238		 * @param {tinymce.ui.Control} targetControl Target control to focus the first item in.
37239		 */
37240		function focusFirst(targetControl) {
37241			var navigationRoot = getNavigationRoot(targetControl);
37242			var focusElements = getFocusElements(navigationRoot.getEl());
37243
37244			if (navigationRoot.settings.ariaRemember && "lastAriaIndex" in navigationRoot) {
37245				moveFocusToIndex(navigationRoot.lastAriaIndex, focusElements);
37246			} else {
37247				moveFocusToIndex(0, focusElements);
37248			}
37249		}
37250
37251		/**
37252		 * Moves the focus to the specified index within the elements list.
37253		 * This will scope the index to the size of the element list if it changed.
37254		 *
37255		 * @private
37256		 * @param {Number} idx Specified index to move to.
37257		 * @param {Array} elements Array with dom elements to move focus within.
37258		 * @return {Number} Input index or a changed index if it was out of range.
37259		 */
37260		function moveFocusToIndex(idx, elements) {
37261			if (idx < 0) {
37262				idx = elements.length - 1;
37263			} else if (idx >= elements.length) {
37264				idx = 0;
37265			}
37266
37267			if (elements[idx]) {
37268				elements[idx].focus();
37269			}
37270
37271			return idx;
37272		}
37273
37274		/**
37275		 * Moves the focus forwards or backwards.
37276		 *
37277		 * @private
37278		 * @param {Number} dir Direction to move in positive means forward, negative means backwards.
37279		 * @param {Array} elements Optional array of elements to move within defaults to the current navigation roots elements.
37280		 */
37281		function moveFocus(dir, elements) {
37282			var idx = -1, navigationRoot = getNavigationRoot();
37283
37284			elements = elements || getFocusElements(navigationRoot.getEl());
37285
37286			for (var i = 0; i < elements.length; i++) {
37287				if (elements[i] === focusedElement) {
37288					idx = i;
37289				}
37290			}
37291
37292			idx += dir;
37293			navigationRoot.lastAriaIndex = moveFocusToIndex(idx, elements);
37294		}
37295
37296		/**
37297		 * Moves the focus to the left this is called by the left key.
37298		 *
37299		 * @private
37300		 */
37301		function left() {
37302			var parentRole = getParentRole();
37303
37304			if (parentRole == "tablist") {
37305				moveFocus(-1, getFocusElements(focusedElement.parentNode));
37306			} else if (focusedControl.parent().submenu) {
37307				cancel();
37308			} else {
37309				moveFocus(-1);
37310			}
37311		}
37312
37313		/**
37314		 * Moves the focus to the right this is called by the right key.
37315		 *
37316		 * @private
37317		 */
37318		function right() {
37319			var role = getRole(), parentRole = getParentRole();
37320
37321			if (parentRole == "tablist") {
37322				moveFocus(1, getFocusElements(focusedElement.parentNode));
37323			} else if (role == "menuitem" && parentRole == "menu" && getAriaProp('haspopup')) {
37324				enter();
37325			} else {
37326				moveFocus(1);
37327			}
37328		}
37329
37330		/**
37331		 * Moves the focus to the up this is called by the up key.
37332		 *
37333		 * @private
37334		 */
37335		function up() {
37336			moveFocus(-1);
37337		}
37338
37339		/**
37340		 * Moves the focus to the up this is called by the down key.
37341		 *
37342		 * @private
37343		 */
37344		function down() {
37345			var role = getRole(), parentRole = getParentRole();
37346
37347			if (role == "menuitem" && parentRole == "menubar") {
37348				enter();
37349			} else if (role == "button" && getAriaProp('haspopup')) {
37350				enter({key: 'down'});
37351			} else {
37352				moveFocus(1);
37353			}
37354		}
37355
37356		/**
37357		 * Moves the focus to the next item or previous item depending on shift key.
37358		 *
37359		 * @private
37360		 * @param {DOMEvent} e DOM event object.
37361		 */
37362		function tab(e) {
37363			var parentRole = getParentRole();
37364
37365			if (parentRole == "tablist") {
37366				var elm = getFocusElements(focusedControl.getEl('body'))[0];
37367
37368				if (elm) {
37369					elm.focus();
37370				}
37371			} else {
37372				moveFocus(e.shiftKey ? -1 : 1);
37373			}
37374		}
37375
37376		/**
37377		 * Calls the cancel event on the currently focused control. This is normally done using the Esc key.
37378		 *
37379		 * @private
37380		 */
37381		function cancel() {
37382			focusedControl.fire('cancel');
37383		}
37384
37385		/**
37386		 * Calls the click event on the currently focused control. This is normally done using the Enter/Space keys.
37387		 *
37388		 * @private
37389		 * @param {Object} aria Optional aria data to pass along with the enter event.
37390		 */
37391		function enter(aria) {
37392			aria = aria || {};
37393			focusedControl.fire('click', {target: focusedElement, aria: aria});
37394		}
37395
37396		root.on('keydown', function(e) {
37397			function handleNonTabOrEscEvent(e, handler) {
37398				// Ignore non tab keys for text elements
37399				if (isTextInputElement(focusedElement)) {
37400					return;
37401				}
37402
37403				if (handler(e) !== false) {
37404					e.preventDefault();
37405				}
37406			}
37407
37408			if (e.isDefaultPrevented()) {
37409				return;
37410			}
37411
37412			switch (e.keyCode) {
37413				case 37: // DOM_VK_LEFT
37414					handleNonTabOrEscEvent(e, left);
37415					break;
37416
37417				case 39: // DOM_VK_RIGHT
37418					handleNonTabOrEscEvent(e, right);
37419					break;
37420
37421				case 38: // DOM_VK_UP
37422					handleNonTabOrEscEvent(e, up);
37423					break;
37424
37425				case 40: // DOM_VK_DOWN
37426					handleNonTabOrEscEvent(e, down);
37427					break;
37428
37429				case 27: // DOM_VK_ESCAPE
37430					cancel();
37431					break;
37432
37433				case 14: // DOM_VK_ENTER
37434				case 13: // DOM_VK_RETURN
37435				case 32: // DOM_VK_SPACE
37436					handleNonTabOrEscEvent(e, enter);
37437					break;
37438
37439				case 9: // DOM_VK_TAB
37440					if (tab(e) !== false) {
37441						e.preventDefault();
37442					}
37443					break;
37444			}
37445		});
37446
37447		root.on('focusin', function(e) {
37448			focusedElement = e.target;
37449			focusedControl = e.control;
37450		});
37451
37452		return {
37453			focusFirst: focusFirst
37454		};
37455	};
37456});
37457
37458// Included from: js/tinymce/classes/ui/Container.js
37459
37460/**
37461 * Container.js
37462 *
37463 * Released under LGPL License.
37464 * Copyright (c) 1999-2015 Ephox Corp. All rights reserved
37465 *
37466 * License: http://www.tinymce.com/license
37467 * Contributing: http://www.tinymce.com/contributing
37468 */
37469
37470/**
37471 * Container control. This is extended by all controls that can have
37472 * children such as panels etc. You can also use this class directly as an
37473 * generic container instance. The container doesn't have any specific role or style.
37474 *
37475 * @-x-less Container.less
37476 * @class tinymce.ui.Container
37477 * @extends tinymce.ui.Control
37478 */
37479define("tinymce/ui/Container", [
37480	"tinymce/ui/Control",
37481	"tinymce/ui/Collection",
37482	"tinymce/ui/Selector",
37483	"tinymce/ui/Factory",
37484	"tinymce/ui/KeyboardNavigation",
37485	"tinymce/util/Tools",
37486	"tinymce/dom/DomQuery",
37487	"tinymce/ui/ClassList",
37488	"tinymce/ui/ReflowQueue"
37489], function(Control, Collection, Selector, Factory, KeyboardNavigation, Tools, $, ClassList, ReflowQueue) {
37490	"use strict";
37491
37492	var selectorCache = {};
37493
37494	return Control.extend({
37495		/**
37496		 * Constructs a new control instance with the specified settings.
37497		 *
37498		 * @constructor
37499		 * @param {Object} settings Name/value object with settings.
37500		 * @setting {Array} items Items to add to container in JSON format or control instances.
37501		 * @setting {String} layout Layout manager by name to use.
37502		 * @setting {Object} defaults Default settings to apply to all items.
37503		 */
37504		init: function(settings) {
37505			var self = this;
37506
37507			self._super(settings);
37508			settings = self.settings;
37509
37510			if (settings.fixed) {
37511				self.state.set('fixed', true);
37512			}
37513
37514			self._items = new Collection();
37515
37516			if (self.isRtl()) {
37517				self.classes.add('rtl');
37518			}
37519
37520			self.bodyClasses = new ClassList(function() {
37521				if (self.state.get('rendered')) {
37522					self.getEl('body').className = this.toString();
37523				}
37524			});
37525			self.bodyClasses.prefix = self.classPrefix;
37526
37527			self.classes.add('container');
37528			self.bodyClasses.add('container-body');
37529
37530			if (settings.containerCls) {
37531				self.classes.add(settings.containerCls);
37532			}
37533
37534			self._layout = Factory.create((settings.layout || '') + 'layout');
37535
37536			if (self.settings.items) {
37537				self.add(self.settings.items);
37538			} else {
37539				self.add(self.render());
37540			}
37541
37542			// TODO: Fix this!
37543			self._hasBody = true;
37544		},
37545
37546		/**
37547		 * Returns a collection of child items that the container currently have.
37548		 *
37549		 * @method items
37550		 * @return {tinymce.ui.Collection} Control collection direct child controls.
37551		 */
37552		items: function() {
37553			return this._items;
37554		},
37555
37556		/**
37557		 * Find child controls by selector.
37558		 *
37559		 * @method find
37560		 * @param {String} selector Selector CSS pattern to find children by.
37561		 * @return {tinymce.ui.Collection} Control collection with child controls.
37562		 */
37563		find: function(selector) {
37564			selector = selectorCache[selector] = selectorCache[selector] || new Selector(selector);
37565
37566			return selector.find(this);
37567		},
37568
37569		/**
37570		 * Adds one or many items to the current container. This will create instances of
37571		 * the object representations if needed.
37572		 *
37573		 * @method add
37574		 * @param {Array/Object/tinymce.ui.Control} items Array or item that will be added to the container.
37575		 * @return {tinymce.ui.Collection} Current collection control.
37576		 */
37577		add: function(items) {
37578			var self = this;
37579
37580			self.items().add(self.create(items)).parent(self);
37581
37582			return self;
37583		},
37584
37585		/**
37586		 * Focuses the current container instance. This will look
37587		 * for the first control in the container and focus that.
37588		 *
37589		 * @method focus
37590		 * @param {Boolean} keyboard Optional true/false if the focus was a keyboard focus or not.
37591		 * @return {tinymce.ui.Collection} Current instance.
37592		 */
37593		focus: function(keyboard) {
37594			var self = this, focusCtrl, keyboardNav, items;
37595
37596			if (keyboard) {
37597				keyboardNav = self.keyboardNav || self.parents().eq(-1)[0].keyboardNav;
37598
37599				if (keyboardNav) {
37600					keyboardNav.focusFirst(self);
37601					return;
37602				}
37603			}
37604
37605			items = self.find('*');
37606
37607			// TODO: Figure out a better way to auto focus alert dialog buttons
37608			if (self.statusbar) {
37609				items.add(self.statusbar.items());
37610			}
37611
37612			items.each(function(ctrl) {
37613				if (ctrl.settings.autofocus) {
37614					focusCtrl = null;
37615					return false;
37616				}
37617
37618				if (ctrl.canFocus) {
37619					focusCtrl = focusCtrl || ctrl;
37620				}
37621			});
37622
37623			if (focusCtrl) {
37624				focusCtrl.focus();
37625			}
37626
37627			return self;
37628		},
37629
37630		/**
37631		 * Replaces the specified child control with a new control.
37632		 *
37633		 * @method replace
37634		 * @param {tinymce.ui.Control} oldItem Old item to be replaced.
37635		 * @param {tinymce.ui.Control} newItem New item to be inserted.
37636		 */
37637		replace: function(oldItem, newItem) {
37638			var ctrlElm, items = this.items(), i = items.length;
37639
37640			// Replace the item in collection
37641			while (i--) {
37642				if (items[i] === oldItem) {
37643					items[i] = newItem;
37644					break;
37645				}
37646			}
37647
37648			if (i >= 0) {
37649				// Remove new item from DOM
37650				ctrlElm = newItem.getEl();
37651				if (ctrlElm) {
37652					ctrlElm.parentNode.removeChild(ctrlElm);
37653				}
37654
37655				// Remove old item from DOM
37656				ctrlElm = oldItem.getEl();
37657				if (ctrlElm) {
37658					ctrlElm.parentNode.removeChild(ctrlElm);
37659				}
37660			}
37661
37662			// Adopt the item
37663			newItem.parent(this);
37664		},
37665
37666		/**
37667		 * Creates the specified items. If any of the items is plain JSON style objects
37668		 * it will convert these into real tinymce.ui.Control instances.
37669		 *
37670		 * @method create
37671		 * @param {Array} items Array of items to convert into control instances.
37672		 * @return {Array} Array with control instances.
37673		 */
37674		create: function(items) {
37675			var self = this, settings, ctrlItems = [];
37676
37677			// Non array structure, then force it into an array
37678			if (!Tools.isArray(items)) {
37679				items = [items];
37680			}
37681
37682			// Add default type to each child control
37683			Tools.each(items, function(item) {
37684				if (item) {
37685					// Construct item if needed
37686					if (!(item instanceof Control)) {
37687						// Name only then convert it to an object
37688						if (typeof item == "string") {
37689							item = {type: item};
37690						}
37691
37692						// Create control instance based on input settings and default settings
37693						settings = Tools.extend({}, self.settings.defaults, item);
37694						item.type = settings.type = settings.type || item.type || self.settings.defaultType ||
37695							(settings.defaults ? settings.defaults.type : null);
37696						item = Factory.create(settings);
37697					}
37698
37699					ctrlItems.push(item);
37700				}
37701			});
37702
37703			return ctrlItems;
37704		},
37705
37706		/**
37707		 * Renders new control instances.
37708		 *
37709		 * @private
37710		 */
37711		renderNew: function() {
37712			var self = this;
37713
37714			// Render any new items
37715			self.items().each(function(ctrl, index) {
37716				var containerElm;
37717
37718				ctrl.parent(self);
37719
37720				if (!ctrl.state.get('rendered')) {
37721					containerElm = self.getEl('body');
37722
37723					// Insert or append the item
37724					if (containerElm.hasChildNodes() && index <= containerElm.childNodes.length - 1) {
37725						$(containerElm.childNodes[index]).before(ctrl.renderHtml());
37726					} else {
37727						$(containerElm).append(ctrl.renderHtml());
37728					}
37729
37730					ctrl.postRender();
37731					ReflowQueue.add(ctrl);
37732				}
37733			});
37734
37735			self._layout.applyClasses(self.items().filter(':visible'));
37736			self._lastRect = null;
37737
37738			return self;
37739		},
37740
37741		/**
37742		 * Appends new instances to the current container.
37743		 *
37744		 * @method append
37745		 * @param {Array/tinymce.ui.Collection} items Array if controls to append.
37746		 * @return {tinymce.ui.Container} Current container instance.
37747		 */
37748		append: function(items) {
37749			return this.add(items).renderNew();
37750		},
37751
37752		/**
37753		 * Prepends new instances to the current container.
37754		 *
37755		 * @method prepend
37756		 * @param {Array/tinymce.ui.Collection} items Array if controls to prepend.
37757		 * @return {tinymce.ui.Container} Current container instance.
37758		 */
37759		prepend: function(items) {
37760			var self = this;
37761
37762			self.items().set(self.create(items).concat(self.items().toArray()));
37763
37764			return self.renderNew();
37765		},
37766
37767		/**
37768		 * Inserts an control at a specific index.
37769		 *
37770		 * @method insert
37771		 * @param {Array/tinymce.ui.Collection} items Array if controls to insert.
37772		 * @param {Number} index Index to insert controls at.
37773		 * @param {Boolean} [before=false] Inserts controls before the index.
37774		 */
37775		insert: function(items, index, before) {
37776			var self = this, curItems, beforeItems, afterItems;
37777
37778			items = self.create(items);
37779			curItems = self.items();
37780
37781			if (!before && index < curItems.length - 1) {
37782				index += 1;
37783			}
37784
37785			if (index >= 0 && index < curItems.length) {
37786				beforeItems = curItems.slice(0, index).toArray();
37787				afterItems = curItems.slice(index).toArray();
37788				curItems.set(beforeItems.concat(items, afterItems));
37789			}
37790
37791			return self.renderNew();
37792		},
37793
37794		/**
37795		 * Populates the form fields from the specified JSON data object.
37796		 *
37797		 * Control items in the form that matches the data will have it's value set.
37798		 *
37799		 * @method fromJSON
37800		 * @param {Object} data JSON data object to set control values by.
37801		 * @return {tinymce.ui.Container} Current form instance.
37802		 */
37803		fromJSON: function(data) {
37804			var self = this;
37805
37806			for (var name in data) {
37807				self.find('#' + name).value(data[name]);
37808			}
37809
37810			return self;
37811		},
37812
37813		/**
37814		 * Serializes the form into a JSON object by getting all items
37815		 * that has a name and a value.
37816		 *
37817		 * @method toJSON
37818		 * @return {Object} JSON object with form data.
37819		 */
37820		toJSON: function() {
37821			var self = this, data = {};
37822
37823			self.find('*').each(function(ctrl) {
37824				var name = ctrl.name(), value = ctrl.value();
37825
37826				if (name && typeof value != "undefined") {
37827					data[name] = value;
37828				}
37829			});
37830
37831			return data;
37832		},
37833
37834		/**
37835		 * Renders the control as a HTML string.
37836		 *
37837		 * @method renderHtml
37838		 * @return {String} HTML representing the control.
37839		 */
37840		renderHtml: function() {
37841			var self = this, layout = self._layout, role = this.settings.role;
37842
37843			self.preRender();
37844			layout.preRender(self);
37845
37846			return (
37847				'<div id="' + self._id + '" class="' + self.classes + '"' + (role ? ' role="' + this.settings.role + '"' : '') + '>
37847' +
37848					'<div id="' + self._id + '-body" class="' + self.bodyClasses + '">' +
37849						(self.settings.html || '') + layout.renderHtml(self) +
37850					'</div>' +
37851				'</div>'
37852			);
37853		},
37854
37855		/**
37856		 * Post render method. Called after the control has been rendered to the target.
37857		 *
37858		 * @method postRender
37859		 * @return {tinymce.ui.Container} Current combobox instance.
37860		 */
37861		postRender: function() {
37862			var self = this, box;
37863
37864			self.items().exec('postRender');
37865			self._super();
37866
37867			self._layout.postRender(self);
37868			self.state.set('rendered', true);
37869
37870			if (self.settings.style) {
37871				self.$el.css(self.settings.style);
37872			}
37873
37874			if (self.settings.border) {
37875				box = self.borderBox;
37876				self.$el.css({
37877					'border-top-width': box.top,
37878					'border-right-width': box.right,
37879					'border-bottom-width': box.bottom,
37880					'border-left-width': box.left
37881				});
37882			}
37883
37884			if (!self.parent()) {
37885				self.keyboardNav = new KeyboardNavigation({
37886					root: self
37887				});
37888			}
37889
37890			return self;
37891		},
37892
37893		/**
37894		 * Initializes the current controls layout rect.
37895		 * This will be executed by the layout managers to determine the
37896		 * default minWidth/minHeight etc.
37897		 *
37898		 * @method initLayoutRect
37899		 * @return {Object} Layout rect instance.
37900		 */
37901		initLayoutRect: function() {
37902			var self = this, layoutRect = self._super();
37903
37904			// Recalc container size by asking layout manager
37905			self._layout.recalc(self);
37906
37907			return layoutRect;
37908		},
37909
37910		/**
37911		 * Recalculates the positions of the controls in the current container.
37912		 * This is invoked by the reflow method and shouldn't be called directly.
37913		 *
37914		 * @method recalc
37915		 */
37916		recalc: function() {
37917			var self = this, rect = self._layoutRect, lastRect = self._lastRect;
37918
37919			if (!lastRect || lastRect.w != rect.w || lastRect.h != rect.h) {
37920				self._layout.recalc(self);
37921				rect = self.layoutRect();
37922				self._lastRect = {x: rect.x, y: rect.y, w: rect.w, h: rect.h};
37923				return true;
37924			}
37925		},
37926
37927		/**
37928		 * Reflows the current container and it's children and possible parents.
37929		 * This should be used after you for example append children to the current control so
37930		 * that the layout managers know that they need to reposition everything.
37931		 *
37932		 * @example
37933		 * container.append({type: 'button', text: 'My button'}).reflow();
37934		 *
37935		 * @method reflow
37936		 * @return {tinymce.ui.Container} Current container instance.
37937		 */
37938		reflow: function() {
37939			var i;
37940
37941			ReflowQueue.remove(this);
37942
37943			if (this.visible()) {
37944				Control.repaintControls = [];
37945				Control.repaintControls.map = {};
37946
37947				this.recalc();
37948				i = Control.repaintControls.length;
37949
37950				while (i--) {
37951					Control.repaintControls[i].repaint();
37952				}
37953
37954				// TODO: Fix me!
37955				if (this.settings.layout !== "flow" && this.settings.layout !== "stack") {
37956					this.repaint();
37957				}
37958
37959				Control.repaintControls = [];
37960			}
37961
37962			return this;
37963		}
37964	});
37965});
37966
37967// Included from: js/tinymce/classes/ui/DragHelper.js
37968
37969/**
37970 * DragHelper.js
37971 *
37972 * Released under LGPL License.
37973 * Copyright (c) 1999-2015 Ephox Corp. All rights reserved
37974 *
37975 * License: http://www.tinymce.com/license
37976 * Contributing: http://www.tinymce.com/contributing
37977 */
37978
37979/**
37980 * Drag/drop helper class.
37981 *
37982 * @example
37983 * var dragHelper = new tinymce.ui.DragHelper('mydiv', {
37984 *     start: function(evt) {
37985 *     },
37986 *
37987 *     drag: function(evt) {
37988 *     },
37989 *
37990 *     end: function(evt) {
37991 *     }
37992 * });
37993 *
37994 * @class tinymce.ui.DragHelper
37995 */
37996define("tinymce/ui/DragHelper", [
37997	"tinymce/dom/DomQuery"
37998], function($) {
37999	"use strict";
38000
38001	function getDocumentSize(doc) {
38002		var documentElement, body, scrollWidth, clientWidth;
38003		var offsetWidth, scrollHeight, clientHeight, offsetHeight, max = Math.max;
38004
38005		documentElement = doc.documentElement;
38006		body = doc.body;
38007
38008		scrollWidth = max(documentElement.scrollWidth, body.scrollWidth);
38009		clientWidth = max(documentElement.clientWidth, body.clientWidth);
38010		offsetWidth = max(documentElement.offsetWidth, body.offsetWidth);
38011
38012		scrollHeight = max(documentElement.scrollHeight, body.scrollHeight);
38013		clientHeight = max(documentElement.clientHeight, body.clientHeight);
38014		offsetHeight = max(documentElement.offsetHeight, body.offsetHeight);
38015
38016		return {
38017			width: scrollWidth < offsetWidth ? clientWidth : scrollWidth,
38018			height: scrollHeight < offsetHeight ? clientHeight : scrollHeight
38019		};
38020	}
38021
38022	function updateWithTouchData(e) {
38023		var keys, i;
38024
38025		if (e.changedTouches) {
38026			keys = "screenX screenY pageX pageY clientX clientY".split(' ');
38027			for (i = 0; i < keys.length; i++) {
38028				e[keys[i]] = e.changedTouches[0][keys[i]];
38029			}
38030		}
38031	}
38032
38033	return function(id, settings) {
38034		var $eventOverlay, doc = settings.document || document, downButton, start, stop, drag, startX, startY;
38035
38036		settings = settings || {};
38037
38038		function getHandleElm() {
38039			return doc.getElementById(settings.handle || id);
38040		}
38041
38042		start = function(e) {
38043			var docSize = getDocumentSize(doc), handleElm, cursor;
38044
38045			updateWithTouchData(e);
38046
38047			e.preventDefault();
38048			downButton = e.button;
38049			handleElm = getHandleElm();
38050			startX = e.screenX;
38051			startY = e.screenY;
38052
38053			// Grab cursor from handle so we can place it on overlay
38054			if (window.getComputedStyle) {
38055				cursor = window.getComputedStyle(handleElm, null).getPropertyValue("cursor");
38056			} else {
38057				cursor = handleElm.runtimeStyle.cursor;
38058			}
38059
38060			$eventOverlay = $('<div>').css({
38061				position: "absolute",
38062				top: 0, left: 0,
38063				width: docSize.width,
38064				height: docSize.height,
38065				zIndex: 0x7FFFFFFF,
38066				opacity: 0.0001,
38067				cursor: cursor
38068			}).appendTo(doc.body);
38069
38070			$(doc).on('mousemove touchmove', drag).on('mouseup touchend', stop);
38071
38072			settings.start(e);
38073		};
38074
38075		drag = function(e) {
38076			updateWithTouchData(e);
38077
38078			if (e.button !== downButton) {
38079				return stop(e);
38080			}
38081
38082			e.deltaX = e.screenX - startX;
38083			e.deltaY = e.screenY - startY;
38084
38085			e.preventDefault();
38086			settings.drag(e);
38087		};
38088
38089		stop = function(e) {
38090			updateWithTouchData(e);
38091
38092			$(doc).off('mousemove touchmove', drag).off('mouseup touchend', stop);
38093
38094			$eventOverlay.remove();
38095
38096			if (settings.stop) {
38097				settings.stop(e);
38098			}
38099		};
38100
38101		/**
38102		 * Destroys the drag/drop helper instance.
38103		 *
38104		 * @method destroy
38105		 */
38106		this.destroy = function() {
38107			$(getHandleElm()).off();
38108		};
38109
38110		$(getHandleElm()).on('mousedown touchstart', start);
38111	};
38112});
38113
38114// Included from: js/tinymce/classes/ui/Scrollable.js
38115
38116/**
38117 * Scrollable.js
38118 *
38119 * Released under LGPL License.
38120 * Copyright (c) 1999-2015 Ephox Corp. All rights reserved
38121 *
38122 * License: http://www.tinymce.com/license
38123 * Contributing: http://www.tinymce.com/contributing
38124 */
38125
38126/**
38127 * This mixin makes controls scrollable using custom scrollbars.
38128 *
38129 * @-x-less Scrollable.less
38130 * @mixin tinymce.ui.Scrollable
38131 */
38132define("tinymce/ui/Scrollable", [
38133	"tinymce/dom/DomQuery",
38134	"tinymce/ui/DragHelper"
38135], function($, DragHelper) {
38136	"use strict";
38137
38138	return {
38139		init: function() {
38140			var self = this;
38141			self.on('repaint', self.renderScroll);
38142		},
38143
38144		renderScroll: function() {
38145			var self = this, margin = 2;
38146
38147			function repaintScroll() {
38148				var hasScrollH, hasScrollV, bodyElm;
38149
38150				function repaintAxis(axisName, posName, sizeName, contentSizeName, hasScroll, ax) {
38151					var containerElm, scrollBarElm, scrollThumbElm;
38152					var containerSize, scrollSize, ratio, rect;
38153					var posNameLower, sizeNameLower;
38154
38155					scrollBarElm = self.getEl('scroll' + axisName);
38156					if (scrollBarElm) {
38157						posNameLower = posName.toLowerCase();
38158						sizeNameLower = sizeName.toLowerCase();
38159
38160						$(self.getEl('absend')).css(posNameLower, self.layoutRect()[contentSizeName] - 1);
38161
38162						if (!hasScroll) {
38163							$(scrollBarElm).css('display', 'none');
38164							return;
38165						}
38166
38167						$(scrollBarElm).css('display', 'block');
38168						containerElm = self.getEl('body');
38169						scrollThumbElm = self.getEl('scroll' + axisName + "t");
38170						containerSize = containerElm["client" + sizeName] - (margin * 2);
38171						containerSize -= hasScrollH && hasScrollV ? scrollBarElm["client" + ax] : 0;
38172						scrollSize = containerElm["scroll" + sizeName];
38173						ratio = containerSize / scrollSize;
38174
38175						rect = {};
38176						rect[posNameLower] = containerElm["offset" + posName] + margin;
38177						rect[sizeNameLower] = containerSize;
38178						$(scrollBarElm).css(rect);
38179
38180						rect = {};
38181						rect[posNameLower] = containerElm["scroll" + posName] * ratio;
38182						rect[sizeNameLower] = containerSize * ratio;
38183						$(scrollThumbElm).css(rect);
38184					}
38185				}
38186
38187				bodyElm = self.getEl('body');
38188				hasScrollH = bodyElm.scrollWidth > bodyElm.clientWidth;
38189				hasScrollV = bodyElm.scrollHeight > bodyElm.clientHeight;
38190
38191				repaintAxis("h", "Left", "Width", "contentW", hasScrollH, "Height");
38192				repaintAxis("v", "Top", "Height", "contentH", hasScrollV, "Width");
38193			}
38194
38195			function addScroll() {
38196				function addScrollAxis(axisName, posName, sizeName, deltaPosName, ax) {
38197					var scrollStart, axisId = self._id + '-scroll' + axisName, prefix = self.classPrefix;
38198
38199					$(self.getEl()).append(
38200						'<div id="' + axisId + '" class="' + prefix + 'scrollbar ' + prefix + 'scrollbar-' + axisName + '">' +
38201							'<div id="' + axisId + 't" class="' + prefix + 'scrollbar-thumb"></div>' +
38202						'</div>'
38203					);
38204
38205					self.draghelper = new DragHelper(axisId + 't', {
38206						start: function() {
38207							scrollStart = self.getEl('body')["scroll" + posName];
38208							$('#' + axisId).addClass(prefix + 'active');
38209						},
38210
38211						drag: function(e) {
38212							var ratio, hasScrollH, hasScrollV, containerSize, layoutRect = self.layoutRect();
38213
38214							hasScrollH = layoutRect.contentW > layoutRect.innerW;
38215							hasScrollV = layoutRect.contentH > layoutRect.innerH;
38216							containerSize = self.getEl('body')["client" + sizeName] - (margin * 2);
38217							containerSize -= hasScrollH && hasScrollV ? self.getEl('scroll' + axisName)["client" + ax] : 0;
38218
38219							ratio = containerSize / self.getEl('body')["scroll" + sizeName];
38220							self.getEl('body')["scroll" + posName] = scrollStart + (e["delta" + deltaPosName] / ratio);
38221						},
38222
38223						stop: function() {
38224							$('#' + axisId).removeClass(prefix + 'active');
38225						}
38226					});
38227				}
38228
38229				self.classes.add('scroll');
38230
38231				addScrollAxis("v", "Top", "Height", "Y", "Width");
38232				addScrollAxis("h", "Left", "Width", "X", "Height");
38233			}
38234
38235			if (self.settings.autoScroll) {
38236				if (!self._hasScroll) {
38237					self._hasScroll = true;
38238					addScroll();
38239
38240					self.on('wheel', function(e) {
38241						var bodyEl = self.getEl('body');
38242
38243						bodyEl.scrollLeft += (e.deltaX || 0) * 10;
38244						bodyEl.scrollTop += e.deltaY * 10;
38245
38246						repaintScroll();
38247					});
38248
38249					$(self.getEl('body')).on("scroll", repaintScroll);
38250				}
38251
38252				repaintScroll();
38253			}
38254		}
38255	};
38256});
38257
38258// Included from: js/tinymce/classes/ui/Panel.js
38259
38260/**
38261 * Panel.js
38262 *
38263 * Released under LGPL License.
38264 * Copyright (c) 1999-2015 Ephox Corp. All rights reserved
38265 *
38266 * License: http://www.tinymce.com/license
38267 * Contributing: http://www.tinymce.com/contributing
38268 */
38269
38270/**
38271 * Creates a new panel.
38272 *
38273 * @-x-less Panel.less
38274 * @class tinymce.ui.Panel
38275 * @extends tinymce.ui.Container
38276 * @mixes tinymce.ui.Scrollable
38277 */
38278define("tinymce/ui/Panel", [
38279	"tinymce/ui/Container",
38280	"tinymce/ui/Scrollable"
38281], function(Container, Scrollable) {
38282	"use strict";
38283
38284	return Container.extend({
38285		Defaults: {
38286			layout: 'fit',
38287			containerCls: 'panel'
38288		},
38289
38290		Mixins: [Scrollable],
38291
38292		/**
38293		 * Renders the control as a HTML string.
38294		 *
38295		 * @method renderHtml
38296		 * @return {String} HTML representing the control.
38297		 */
38298		renderHtml: function() {
38299			var self = this, layout = self._layout, innerHtml = self.settings.html;
38300
38301			self.preRender();
38302			layout.preRender(self);
38303
38304			if (typeof innerHtml == "undefined") {
38305				innerHtml = (
38306					'<div id="' + self._id + '-body" class="' + self.bodyClasses + '">' +
38307						layout.renderHtml(self) +
38308					'</div>'
38309				);
38310			} else {
38311				if (typeof innerHtml == 'function') {
38312					innerHtml = innerHtml.call(self);
38313				}
38314
38315				self._hasBody = false;
38316			}
38317
38318			return (
38319				'<div id="' + self._id + '" class="' + self.classes + '" hidefocus="1" tabindex="-1" role="group">' +
38320					(self._preBodyHtml || '') +
38321					innerHtml +
38322				'</div>'
38323			);
38324		}
38325	});
38326});
38327
38328// Included from: js/tinymce/classes/ui/Movable.js
38329
38330/**
38331 * Movable.js
38332 *
38333 * Released under LGPL License.
38334 * Copyright (c) 1999-2015 Ephox Corp. All rights reserved
38335 *
38336 * License: http://www.tinymce.com/license
38337 * Contributing: http://www.tinymce.com/contributing
38338 */
38339
38340/**
38341 * Movable mixin. Makes controls movable absolute and relative to other elements.
38342 *
38343 * @mixin tinymce.ui.Movable
38344 */
38345define("tinymce/ui/Movable", [
38346	"tinymce/ui/DomUtils"
38347], function(DomUtils) {
38348	"use strict";
38349
38350	function calculateRelativePosition(ctrl, targetElm, rel) {
38351		var ctrlElm, pos, x, y, selfW, selfH, targetW, targetH, viewport, size;
38352
38353		viewport = DomUtils.getViewPort();
38354
38355		// Get pos of target
38356		pos = DomUtils.getPos(targetElm);
38357		x = pos.x;
38358		y = pos.y;
38359
38360		if (ctrl.state.get('fixed') && DomUtils.getRuntimeStyle(document.body, 'position') == 'static') {
38361			x -= viewport.x;
38362			y -= viewport.y;
38363		}
38364
38365		// Get size of self
38366		ctrlElm = ctrl.getEl();
38367		size = DomUtils.getSize(ctrlElm);
38368		selfW = size.width;
38369		selfH = size.height;
38370
38371		// Get size of target
38372		size = DomUtils.getSize(targetElm);
38373		targetW = size.width;
38374		targetH = size.height;
38375
38376		// Parse align string
38377		rel = (rel || '').split('');
38378
38379		// Target corners
38380		if (rel[0] === 'b') {
38381			y += targetH;
38382		}
38383
38384		if (rel[1] === 'r') {
38385			x += targetW;
38386		}
38387
38388		if (rel[0] === 'c') {
38389			y += Math.round(targetH / 2);
38390		}
38391
38392		if (rel[1] === 'c') {
38393			x += Math.round(targetW / 2);
38394		}
38395
38396		// Self corners
38397		if (rel[3] === 'b') {
38398			y -= selfH;
38399		}
38400
38401		if (rel[4] === 'r') {
38402			x -= selfW;
38403		}
38404
38405		if (rel[3] === 'c') {
38406			y -= Math.round(selfH / 2);
38407		}
38408
38409		if (rel[4] === 'c') {
38410			x -= Math.round(selfW / 2);
38411		}
38412
38413		return {
38414			x: x,
38415			y: y,
38416			w: selfW,
38417			h: selfH
38418		};
38419	}
38420
38421	return {
38422		/**
38423		 * Tests various positions to get the most suitable one.
38424		 *
38425		 * @method testMoveRel
38426		 * @param {DOMElement} elm Element to position against.
38427		 * @param {Array} rels Array with relative positions.
38428		 * @return {String} Best suitable relative position.
38429		 */
38430		testMoveRel: function(elm, rels) {
38431			var viewPortRect = DomUtils.getViewPort();
38432
38433			for (var i = 0; i < rels.length; i++) {
38434				var pos = calculateRelativePosition(this, elm, rels[i]);
38435
38436				if (this.state.get('fixed')) {
38437					if (pos.x > 0 && pos.x + pos.w < viewPortRect.w && pos.y > 0 && pos.y + pos.h < viewPortRect.h) {
38438						return rels[i];
38439					}
38440				} else {
38441					if (pos.x > viewPortRect.x && pos.x + pos.w < viewPortRect.w + viewPortRect.x &&
38442						pos.y > viewPortRect.y && pos.y + pos.h < viewPortRect.h + viewPortRect.y) {
38443						return rels[i];
38444					}
38445				}
38446			}
38447
38448			return rels[0];
38449		},
38450
38451		/**
38452		 * Move relative to the specified element.
38453		 *
38454		 * @method moveRel
38455		 * @param {Element} elm Element to move relative to.
38456		 * @param {String} rel Relative mode. For example: br-tl.
38457		 * @return {tinymce.ui.Control} Current control instance.
38458		 */
38459		moveRel: function(elm, rel) {
38460			if (typeof rel != 'string') {
38461				rel = this.testMoveRel(elm, rel);
38462			}
38463
38464			var pos = calculateRelativePosition(this, elm, rel);
38465			return this.moveTo(pos.x, pos.y);
38466		},
38467
38468		/**
38469		 * Move by a relative x, y values.
38470		 *
38471		 * @method moveBy
38472		 * @param {Number} dx Relative x position.
38473		 * @param {Number} dy Relative y position.
38474		 * @return {tinymce.ui.Control} Current control instance.
38475		 */
38476		moveBy: function(dx, dy) {
38477			var self = this, rect = self.layoutRect();
38478
38479			self.moveTo(rect.x + dx, rect.y + dy);
38480
38481			return self;
38482		},
38483
38484		/**
38485		 * Move to absolute position.
38486		 *
38487		 * @method moveTo
38488		 * @param {Number} x Absolute x position.
38489		 * @param {Number} y Absolute y position.
38490		 * @return {tinymce.ui.Control} Current control instance.
38491		 */
38492		moveTo: function(x, y) {
38493			var self = this;
38494
38495			// TODO: Move this to some global class
38496			function constrain(value, max, size) {
38497				if (value < 0) {
38498					return 0;
38499				}
38500
38501				if (value + size > max) {
38502					value = max - size;
38503					return value < 0 ? 0 : value;
38504				}
38505
38506				return value;
38507			}
38508
38509			if (self.settings.constrainToViewport) {
38510				var viewPortRect = DomUtils.getViewPort(window);
38511				var layoutRect = self.layoutRect();
38512
38513				x = constrain(x, viewPortRect.w + viewPortRect.x, layoutRect.w);
38514				y = constrain(y, viewPortRect.h + viewPortRect.y, layoutRect.h);
38515			}
38516
38517			if (self.state.get('rendered')) {
38518				self.layoutRect({x: x, y: y}).repaint();
38519			} else {
38520				self.settings.x = x;
38521				self.settings.y = y;
38522			}
38523
38524			self.fire('move', {x: x, y: y});
38525
38526			return self;
38527		}
38528	};
38529});
38530
38531// Included from: js/tinymce/classes/ui/Resizable.js
38532
38533/**
38534 * Resizable.js
38535 *
38536 * Released under LGPL License.
38537 * Copyright (c) 1999-2015 Ephox Corp. All rights reserved
38538 *
38539 * License: http://www.tinymce.com/license
38540 * Contributing: http://www.tinymce.com/contributing
38541 */
38542
38543/**
38544 * Resizable mixin. Enables controls to be resized.
38545 *
38546 * @mixin tinymce.ui.Resizable
38547 */
38548define("tinymce/ui/Resizable", [
38549	"tinymce/ui/DomUtils"
38550], function(DomUtils) {
38551	"use strict";
38552
38553	return {
38554		/**
38555		 * Resizes the control to contents.
38556		 *
38557		 * @method resizeToContent
38558		 */
38559		resizeToContent: function() {
38560			this._layoutRect.autoResize = true;
38561			this._lastRect = null;
38562			this.reflow();
38563		},
38564
38565		/**
38566		 * Resizes the control to a specific width/height.
38567		 *
38568		 * @method resizeTo
38569		 * @param {Number} w Control width.
38570		 * @param {Number} h Control height.
38571		 * @return {tinymce.ui.Control} Current control instance.
38572		 */
38573		resizeTo: function(w, h) {
38574			// TODO: Fix hack
38575			if (w <= 1 || h <= 1) {
38576				var rect = DomUtils.getWindowSize();
38577
38578				w = w <= 1 ? w * rect.w : w;
38579				h = h <= 1 ? h * rect.h : h;
38580			}
38581
38582			this._layoutRect.autoResize = false;
38583			return this.layoutRect({minW: w, minH: h, w: w, h: h}).reflow();
38584		},
38585
38586		/**
38587		 * Resizes the control to a specific relative width/height.
38588		 *
38589		 * @method resizeBy
38590		 * @param {Number} dw Relative control width.
38591		 * @param {Number} dh Relative control height.
38592		 * @return {tinymce.ui.Control} Current control instance.
38593		 */
38594		resizeBy: function(dw, dh) {
38595			var self = this, rect = self.layoutRect();
38596
38597			return self.resizeTo(rect.w + dw, rect.h + dh);
38598		}
38599	};
38600});
38601
38602// Included from: js/tinymce/classes/ui/FloatPanel.js
38603
38604/**
38605 * FloatPanel.js
38606 *
38607 * Released under LGPL License.
38608 * Copyright (c) 1999-2015 Ephox Corp. All rights reserved
38609 *
38610 * License: http://www.tinymce.com/license
38611 * Contributing: http://www.tinymce.com/contributing
38612 */
38613
38614/**
38615 * This class creates a floating panel.
38616 *
38617 * @-x-less FloatPanel.less
38618 * @class tinymce.ui.FloatPanel
38619 * @extends tinymce.ui.Panel
38620 * @mixes tinymce.ui.Movable
38621 * @mixes tinymce.ui.Resizable
38622 */
38623define("tinymce/ui/FloatPanel", [
38624	"tinymce/ui/Panel",
38625	"tinymce/ui/Movable",
38626	"tinymce/ui/Resizable",
38627	"tinymce/ui/DomUtils",
38628	"tinymce/dom/DomQuery",
38629	"tinymce/util/Delay"
38630], function(Panel, Movable, Resizable, DomUtils, $, Delay) {
38631	"use strict";
38632
38633	var documentClickHandler, documentScrollHandler, windowResizeHandler, visiblePanels = [];
38634	var zOrder = [], hasModal;
38635
38636	function isChildOf(ctrl, parent) {
38637		while (ctrl) {
38638			if (ctrl == parent) {
38639				return true;
38640			}
38641
38642			ctrl = ctrl.parent();
38643		}
38644	}
38645
38646	function skipOrHidePanels(e) {
38647		// Hide any float panel when a click/focus out is out side that float panel and the
38648		// float panels direct parent for example a click on a menu button
38649		var i = visiblePanels.length;
38650
38651		while (i--) {
38652			var panel = visiblePanels[i], clickCtrl = panel.getParentCtrl(e.target);
38653
38654			if (panel.settings.autohide) {
38655				if (clickCtrl) {
38656					if (isChildOf(clickCtrl, panel) || panel.parent() === clickCtrl) {
38657						continue;
38658					}
38659				}
38660
38661				e = panel.fire('autohide', {target: e.target});
38662				if (!e.isDefaultPrevented()) {
38663					panel.hide();
38664				}
38665			}
38666		}
38667	}
38668
38669	function bindDocumentClickHandler() {
38670
38671		if (!documentClickHandler) {
38672			documentClickHandler = function(e) {
38673				// Gecko fires click event and in the wrong order on Mac so lets normalize
38674				if (e.button == 2) {
38675					return;
38676				}
38677
38678				skipOrHidePanels(e);
38679			};
38680
38681			$(document).on('click touchstart', documentClickHandler);
38682		}
38683	}
38684
38685	function bindDocumentScrollHandler() {
38686		if (!documentScrollHandler) {
38687			documentScrollHandler = function() {
38688				var i;
38689
38690				i = visiblePanels.length;
38691				while (i--) {
38692					repositionPanel(visiblePanels[i]);
38693				}
38694			};
38695
38696			$(window).on('scroll', documentScrollHandler);
38697		}
38698	}
38699
38700	function bindWindowResizeHandler() {
38701		if (!windowResizeHandler) {
38702			var docElm = document.documentElement, clientWidth = docElm.clientWidth, clientHeight = docElm.clientHeight;
38703
38704			windowResizeHandler = function() {
38705				// Workaround for #7065 IE 7 fires resize events event though the window wasn't resized
38706				if (!document.all || clientWidth != docElm.clientWidth || clientHeight != docElm.clientHeight) {
38707					clientWidth = docElm.clientWidth;
38708					clientHeight = docElm.clientHeight;
38709					FloatPanel.hideAll();
38710				}
38711			};
38712
38713			$(window).on('resize', windowResizeHandler);
38714		}
38715	}
38716
38717	/**
38718	 * Repositions the panel to the top of page if the panel is outside of the visual viewport. It will
38719	 * also reposition all child panels of the current panel.
38720	 */
38721	function repositionPanel(panel) {
38722		var scrollY = DomUtils.getViewPort().y;
38723
38724		function toggleFixedChildPanels(fixed, deltaY) {
38725			var parent;
38726
38727			for (var i = 0; i < visiblePanels.length; i++) {
38728				if (visiblePanels[i] != panel) {
38729					parent = visiblePanels[i].parent();
38730
38731					while (parent && (parent = parent.parent())) {
38732						if (parent == panel) {
38733							visiblePanels[i].fixed(fixed).moveBy(0, deltaY).repaint();
38734						}
38735					}
38736				}
38737			}
38738		}
38739
38740		if (panel.settings.autofix) {
38741			if (!panel.state.get('fixed')) {
38742				panel._autoFixY = panel.layoutRect().y;
38743
38744				if (panel._autoFixY < scrollY) {
38745					panel.fixed(true).layoutRect({y: 0}).repaint();
38746					toggleFixedChildPanels(true, scrollY - panel._autoFixY);
38747				}
38748			} else {
38749				if (panel._autoFixY > scrollY) {
38750					panel.fixed(false).layoutRect({y: panel._autoFixY}).repaint();
38751					toggleFixedChildPanels(false, panel._autoFixY - scrollY);
38752				}
38753			}
38754		}
38755	}
38756
38757	function addRemove(add, ctrl) {
38758		var i, zIndex = FloatPanel.zIndex || 0xFFFF, topModal;
38759
38760		if (add) {
38761			zOrder.push(ctrl);
38762		} else {
38763			i = zOrder.length;
38764
38765			while (i--) {
38766				if (zOrder[i] === ctrl) {
38767					zOrder.splice(i, 1);
38768				}
38769			}
38770		}
38771
38772		if (zOrder.length) {
38773			for (i = 0; i < zOrder.length; i++) {
38774				if (zOrder[i].modal) {
38775					zIndex++;
38776					topModal = zOrder[i];
38777				}
38778
38779				zOrder[i].getEl().style.zIndex = zIndex;
38780				zOrder[i].zIndex = zIndex;
38781				zIndex++;
38782			}
38783		}
38784
38785		var modalBlockEl = document.getElementById(ctrl.classPrefix + 'modal-block');
38786
38787		if (topModal) {
38788			$(modalBlockEl).css('z-index', topModal.zIndex - 1);
38789		} else if (modalBlockEl) {
38790			modalBlockEl.parentNode.removeChild(modalBlockEl);
38791			hasModal = false;
38792		}
38793
38794		FloatPanel.currentZIndex = zIndex;
38795	}
38796
38797	var FloatPanel = Panel.extend({
38798		Mixins: [Movable, Resizable],
38799
38800		/**
38801		 * Constructs a new control instance with the specified settings.
38802		 *
38803		 * @constructor
38804		 * @param {Object} settings Name/value object with settings.
38805		 * @setting {Boolean} autohide Automatically hide the panel.
38806		 */
38807		init: function(settings) {
38808			var self = this;
38809
38810			self._super(settings);
38811			self._eventsRoot = self;
38812
38813			self.classes.add('floatpanel');
38814
38815			// Hide floatpanes on click out side the root button
38816			if (settings.autohide) {
38817				bindDocumentClickHandler();
38818				bindWindowResizeHandler();
38819				visiblePanels.push(self);
38820			}
38821
38822			if (settings.autofix) {
38823				bindDocumentScrollHandler();
38824
38825				self.on('move', function() {
38826					repositionPanel(this);
38827				});
38828			}
38829
38830			self.on('postrender show', function(e) {
38831				if (e.control == self) {
38832					var $modalBlockEl, prefix = self.classPrefix;
38833
38834					if (self.modal && !hasModal) {
38835						$modalBlockEl = $('#' + prefix + 'modal-block');
38836						if (!$modalBlockEl[0]) {
38837							$modalBlockEl = $(
38838								'<div id="' + prefix + 'modal-block" class="' + prefix + 'reset ' + prefix + 'fade"></div>'
38839							).appendTo(self.getContainerElm());
38840						}
38841
38842						Delay.setTimeout(function() {
38843							$modalBlockEl.addClass(prefix + 'in');
38844							$(self.getEl()).addClass(prefix + 'in');
38845						});
38846
38847						hasModal = true;
38848					}
38849
38850					addRemove(true, self);
38851				}
38852			});
38853
38854			self.on('show', function() {
38855				self.parents().each(function(ctrl) {
38856					if (ctrl.state.get('fixed')) {
38857						self.fixed(true);
38858						return false;
38859					}
38860				});
38861			});
38862
38863			if (settings.popover) {
38864				self._preBodyHtml = '<div class="' + self.classPrefix + 'arrow"></div>';
38865				self.classes.add('popover').add('bottom').add(self.isRtl() ? 'end' : 'start');
38866			}
38867		},
38868
38869		fixed: function(state) {
38870			var self = this;
38871
38872			if (self.state.get('fixed') != state) {
38873				if (self.state.get('rendered')) {
38874					var viewport = DomUtils.getViewPort();
38875
38876					if (state) {
38877						self.layoutRect().y -= viewport.y;
38878					} else {
38879						self.layoutRect().y += viewport.y;
38880					}
38881				}
38882
38883				self.classes.toggle('fixed', state);
38884				self.state.set('fixed', state);
38885			}
38886
38887			return self;
38888		},
38889
38890		/**
38891		 * Shows the current float panel.
38892		 *
38893		 * @method show
38894		 * @return {tinymce.ui.FloatPanel} Current floatpanel instance.
38895		 */
38896		show: function() {
38897			var self = this, i, state = self._super();
38898
38899			i = visiblePanels.length;
38900			while (i--) {
38901				if (visiblePanels[i] === self) {
38902					break;
38903				}
38904			}
38905
38906			if (i === -1) {
38907				visiblePanels.push(self);
38908			}
38909
38910			return state;
38911		},
38912
38913		/**
38914		 * Hides the current float panel.
38915		 *
38916		 * @method hide
38917		 * @return {tinymce.ui.FloatPanel} Current floatpanel instance.
38918		 */
38919		hide: function() {
38920			removeVisiblePanel(this);
38921			addRemove(false, this);
38922
38923			return this._super();
38924		},
38925
38926		/**
38927		 * Hide all visible float panels with he autohide setting enabled. This is for
38928		 * manually hiding floating menus or panels.
38929		 *
38930		 * @method hideAll
38931		 */
38932		hideAll: function() {
38933			FloatPanel.hideAll();
38934		},
38935
38936		/**
38937		 * Closes the float panel. This will remove the float panel from page and fire the close event.
38938		 *
38939		 * @method close
38940		 */
38941		close: function() {
38942			var self = this;
38943
38944			if (!self.fire('close').isDefaultPrevented()) {
38945				self.remove();
38946				addRemove(false, self);
38947			}
38948
38949			return self;
38950		},
38951
38952		/**
38953		 * Removes the float panel from page.
38954		 *
38955		 * @method remove
38956		 */
38957		remove: function() {
38958			removeVisiblePanel(this);
38959			this._super();
38960		},
38961
38962		postRender: function() {
38963			var self = this;
38964
38965			if (self.settings.bodyRole) {
38966				this.getEl('body').setAttribute('role', self.settings.bodyRole);
38967			}
38968
38969			return self._super();
38970		}
38971	});
38972
38973	/**
38974	 * Hide all visible float panels with he autohide setting enabled. This is for
38975	 * manually hiding floating menus or panels.
38976	 *
38977	 * @static
38978	 * @method hideAll
38979	 */
38980	FloatPanel.hideAll = function() {
38981		var i = visiblePanels.length;
38982
38983		while (i--) {
38984			var panel = visiblePanels[i];
38985
38986			if (panel && panel.settings.autohide) {
38987				panel.hide();
38988				visiblePanels.splice(i, 1);
38989			}
38990		}
38991	};
38992
38993	function removeVisiblePanel(panel) {
38994		var i;
38995
38996		i = visiblePanels.length;
38997		while (i--) {
38998			if (visiblePanels[i] === panel) {
38999				visiblePanels.splice(i, 1);
39000			}
39001		}
39002
39003		i = zOrder.length;
39004		while (i--) {
39005			if (zOrder[i] === panel) {
39006				zOrder.splice(i, 1);
39007			}
39008		}
39009	}
39010
39011	return FloatPanel;
39012});
39013
39014// Included from: js/tinymce/classes/ui/Window.js
39015
39016/**
39017 * Window.js
39018 *
39019 * Released under LGPL License.
39020 * Copyright (c) 1999-2015 Ephox Corp. All rights reserved
39021 *
39022 * License: http://www.tinymce.com/license
39023 * Contributing: http://www.tinymce.com/contributing
39024 */
39025
39026/**
39027 * Creates a new window.
39028 *
39029 * @-x-less Window.less
39030 * @class tinymce.ui.Window
39031 * @extends tinymce.ui.FloatPanel
39032 */
39033define("tinymce/ui/Window", [
39034	"tinymce/ui/FloatPanel",
39035	"tinymce/ui/Panel",
39036	"tinymce/ui/DomUtils",
39037	"tinymce/dom/DomQuery",
39038	"tinymce/ui/DragHelper",
39039	"tinymce/ui/BoxUtils",
39040	"tinymce/Env",
39041	"tinymce/util/Delay"
39042], function(FloatPanel, Panel, DomUtils, $, DragHelper, BoxUtils, Env, Delay) {
39043	"use strict";
39044
39045	var windows = [], oldMetaValue = '';
39046
39047	function toggleFullScreenState(state) {
39048		var noScaleMetaValue = 'width=device-width,initial-scale=1.0,user-scalable=0,minimum-scale=1.0,maximum-scale=1.0',
39049			viewport = $("meta[name=viewport]")[0],
39050			contentValue;
39051
39052		if (Env.overrideViewPort === false) {
39053			return;
39054		}
39055
39056		if (!viewport) {
39057			viewport = document.createElement('meta');
39058			viewport.setAttribute('name', 'viewport');
39059			document.getElementsByTagName('head')[0].appendChild(viewport);
39060		}
39061
39062		contentValue = viewport.getAttribute('content');
39063		if (contentValue && typeof oldMetaValue != 'undefined') {
39064			oldMetaValue = contentValue;
39065		}
39066
39067		viewport.setAttribute('content', state ? noScaleMetaValue : oldMetaValue);
39068	}
39069
39070	function toggleBodyFullScreenClasses(classPrefix) {
39071		for (var i = 0; i < windows.length; i++) {
39072			if (windows[i]._fullscreen) {
39073				return;
39074			}
39075		}
39076
39077		$([document.documentElement, document.body]).removeClass(classPrefix + 'fullscreen');
39078	}
39079
39080	function handleWindowResize() {
39081		var lastSize = {
39082			w: window.innerWidth,
39083			h: window.innerHeight
39084		};
39085
39086		Delay.setInterval(function() {
39087			var w = window.innerWidth,
39088				h = window.innerHeight;
39089
39090			if (lastSize.w != w || lastSize.h != h) {
39091				lastSize = {
39092					w: w,
39093					h: h
39094				};
39095
39096				$(window).trigger('resize');
39097			}
39098		}, 100);
39099
39100		function reposition() {
39101			var i, rect = DomUtils.getWindowSize(), layoutRect;
39102
39103			for (i = 0; i < windows.length; i++) {
39104				layoutRect = windows[i].layoutRect();
39105
39106				windows[i].moveTo(
39107					windows[i].settings.x || Math.max(0, rect.w / 2 - layoutRect.w / 2),
39108					windows[i].settings.y || Math.max(0, rect.h / 2 - layoutRect.h / 2)
39109				);
39110			}
39111		}
39112
39113		$(window).on('resize', reposition);
39114	}
39115
39116	var Window = FloatPanel.extend({
39117		modal: true,
39118
39119		Defaults: {
39120			border: 1,
39121			layout: 'flex',
39122			containerCls: 'panel',
39123			role: 'dialog',
39124			callbacks: {
39125				submit: function() {
39126					this.fire('submit', {data: this.toJSON()});
39127				},
39128
39129				close: function() {
39130					this.close();
39131				}
39132			}
39133		},
39134
39135		/**
39136		 * Constructs a instance with the specified settings.
39137		 *
39138		 * @constructor
39139		 * @param {Object} settings Name/value object with settings.
39140		 */
39141		init: function(settings) {
39142			var self = this;
39143
39144			self._super(settings);
39145
39146			if (self.isRtl()) {
39147				self.classes.add('rtl');
39148			}
39149
39150			self.classes.add('window');
39151			self.bodyClasses.add('window-body');
39152			self.state.set('fixed', true);
39153
39154			// Create statusbar
39155			if (settings.buttons) {
39156				self.statusbar = new Panel({
39157					layout: 'flex',
39158					border: '1 0 0 0',
39159					spacing: 3,
39160					padding: 10,
39161					align: 'center',
39162					pack: self.isRtl() ? 'start' : 'end',
39163					defaults: {
39164						type: 'button'
39165					},
39166					items: settings.buttons
39167				});
39168
39169				self.statusbar.classes.add('foot');
39170				self.statusbar.parent(self);
39171			}
39172
39173			self.on('click', function(e) {
39174				if (e.target.className.indexOf(self.classPrefix + 'close') != -1) {
39175					self.close();
39176				}
39177			});
39178
39179			self.on('cancel', function() {
39180				self.close();
39181			});
39182
39183			self.aria('describedby', self.describedBy || self._id + '-none');
39184			self.aria('label', settings.title);
39185			self._fullscreen = false;
39186		},
39187
39188		/**
39189		 * Recalculates the positions of the controls in the current container.
39190		 * This is invoked by the reflow method and shouldn't be called directly.
39191		 *
39192		 * @method recalc
39193		 */
39194		recalc: function() {
39195			var self = this, statusbar = self.statusbar, layoutRect, width, x, needsRecalc;
39196
39197			if (self._fullscreen) {
39198				self.layoutRect(DomUtils.getWindowSize());
39199				self.layoutRect().contentH = self.layoutRect().innerH;
39200			}
39201
39202			self._super();
39203
39204			layoutRect = self.layoutRect();
39205
39206			// Resize window based on title width
39207			if (self.settings.title && !self._fullscreen) {
39208				width = layoutRect.headerW;
39209				if (width > layoutRect.w) {
39210					x = layoutRect.x - Math.max(0, width / 2);
39211					self.layoutRect({w: width, x: x});
39212					needsRecalc = true;
39213				}
39214			}
39215
39216			// Resize window based on statusbar width
39217			if (statusbar) {
39218				statusbar.layoutRect({w: self.layoutRect().innerW}).recalc();
39219
39220				width = statusbar.layoutRect().minW + layoutRect.deltaW;
39221				if (width > layoutRect.w) {
39222					x = layoutRect.x - Math.max(0, width - layoutRect.w);
39223					self.layoutRect({w: width, x: x});
39224					needsRecalc = true;
39225				}
39226			}
39227
39228			// Recalc body and disable auto resize
39229			if (needsRecalc) {
39230				self.recalc();
39231			}
39232		},
39233
39234		/**
39235		 * Initializes the current controls layout rect.
39236		 * This will be executed by the layout managers to determine the
39237		 * default minWidth/minHeight etc.
39238		 *
39239		 * @method initLayoutRect
39240		 * @return {Object} Layout rect instance.
39241		 */
39242		initLayoutRect: function() {
39243			var self = this, layoutRect = self._super(), deltaH = 0, headEl;
39244
39245			// Reserve vertical space for title
39246			if (self.settings.title && !self._fullscreen) {
39247				headEl = self.getEl('head');
39248
39249				var size = DomUtils.getSize(headEl);
39250
39251				layoutRect.headerW = size.width;
39252				layoutRect.headerH = size.height;
39253
39254				deltaH += layoutRect.headerH;
39255			}
39256
39257			// Reserve vertical space for statusbar
39258			if (self.statusbar) {
39259				deltaH += self.statusbar.layoutRect().h;
39260			}
39261
39262			layoutRect.deltaH += deltaH;
39263			layoutRect.minH += deltaH;
39264			//layoutRect.innerH -= deltaH;
39265			layoutRect.h += deltaH;
39266
39267			var rect = DomUtils.getWindowSize();
39268
39269			layoutRect.x = self.settings.x || Math.max(0, rect.w / 2 - layoutRect.w / 2);
39270			layoutRect.y = self.settings.y || Math.max(0, rect.h / 2 - layoutRect.h / 2);
39271
39272			return layoutRect;
39273		},
39274
39275		/**
39276		 * Renders the control as a HTML string.
39277		 *
39278		 * @method renderHtml
39279		 * @return {String} HTML representing the control.
39280		 */
39281		renderHtml: function() {
39282			var self = this, layout = self._layout, id = self._id, prefix = self.classPrefix;
39283			var settings = self.settings, headerHtml = '', footerHtml = '', html = settings.html;
39284
39285			self.preRender();
39286			layout.preRender(self);
39287
39288			if (settings.title) {
39289				headerHtml = (
39290					'<div id="' + id + '-head" class="' + prefix + 'window-head">' +
39291						'<div id="' + id + '-title" class="' + prefix + 'title">' + self.encode(settings.title) + '</div>' +
39292						'<button type="button" class="' + prefix + 'close" aria-hidden="true">\u00d7</button>' +
39293						'<div id="' + id + '-dragh" class="' + prefix + 'dragh"></div>' +
39294					'</div>'
39295				);
39296			}
39297
39298			if (settings.url) {
39299				html = '<iframe src="' + settings.url + '" tabindex="-1"></iframe>';
39300			}
39301
39302			if (typeof html == "undefined") {
39303				html = layout.renderHtml(self);
39304			}
39305
39306			if (self.statusbar) {
39307				footerHtml = self.statusbar.renderHtml();
39308			}
39309
39310			return (
39311				'<div id="' + id + '" class="' + self.classes + '" hidefocus="1">' +
39312					'<div class="' + self.classPrefix + 'reset" role="application">' +
39313						headerHtml +
39314						'<div id="' + id + '-body" class="' + self.bodyClasses + '">' +
39315							html +
39316						'</div>' +
39317						footerHtml +
39318					'</div>' +
39319				'</div>'
39320			);
39321		},
39322
39323		/**
39324		 * Switches the window fullscreen mode.
39325		 *
39326		 * @method fullscreen
39327		 * @param {Boolean} state True/false state.
39328		 * @return {tinymce.ui.Window} Current window instance.
39329		 */
39330		fullscreen: function(state) {
39331			var self = this, documentElement = document.documentElement, slowRendering, prefix = self.classPrefix, layoutRect;
39332
39333			if (state != self._fullscreen) {
39334				$(window).on('resize', function() {
39335					var time;
39336
39337					if (self._fullscreen) {
39338						// Time the layout time if it's to slow use a timeout to not hog the CPU
39339						if (!slowRendering) {
39340							time = new Date().getTime();
39341
39342							var rect = DomUtils.getWindowSize();
39343							self.moveTo(0, 0).resizeTo(rect.w, rect.h);
39344
39345							if ((new Date().getTime()) - time > 50) {
39346								slowRendering = true;
39347							}
39348						} else {
39349							if (!self._timer) {
39350								self._timer = Delay.setTimeout(function() {
39351									var rect = DomUtils.getWindowSize();
39352									self.moveTo(0, 0).resizeTo(rect.w, rect.h);
39353
39354									self._timer = 0;
39355								}, 50);
39356							}
39357						}
39358					}
39359				});
39360
39361				layoutRect = self.layoutRect();
39362				self._fullscreen = state;
39363
39364				if (!state) {
39365					self.borderBox = BoxUtils.parseBox(self.settings.border);
39366					self.getEl('head').style.display = '';
39367					layoutRect.deltaH += layoutRect.headerH;
39368					$([documentElement, document.body]).removeClass(prefix + 'fullscreen');
39369					self.classes.remove('fullscreen');
39370					self.moveTo(self._initial.x, self._initial.y).resizeTo(self._initial.w, self._initial.h);
39371				} else {
39372					self._initial = {x: layoutRect.x, y: layoutRect.y, w: layoutRect.w, h: layoutRect.h};
39373
39374					self.borderBox = BoxUtils.parseBox('0');
39375					self.getEl('head').style.display = 'none';
39376					layoutRect.deltaH -= layoutRect.headerH + 2;
39377					$([documentElement, document.body]).addClass(prefix + 'fullscreen');
39378					self.classes.add('fullscreen');
39379
39380					var rect = DomUtils.getWindowSize();
39381					self.moveTo(0, 0).resizeTo(rect.w, rect.h);
39382				}
39383			}
39384
39385			return self.reflow();
39386		},
39387
39388		/**
39389		 * Called after the control has been rendered.
39390		 *
39391		 * @method postRender
39392		 */
39393		postRender: function() {
39394			var self = this, startPos;
39395
39396			setTimeout(function() {
39397				self.classes.add('in');
39398			}, 0);
39399
39400			self._super();
39401
39402			if (self.statusbar) {
39403				self.statusbar.postRender();
39404			}
39405
39406			self.focus();
39407
39408			this.dragHelper = new DragHelper(self._id + '-dragh', {
39409				start: function() {
39410					startPos = {
39411						x: self.layoutRect().x,
39412						y: self.layoutRect().y
39413					};
39414				},
39415
39416				drag: function(e) {
39417					self.moveTo(startPos.x + e.deltaX, startPos.y + e.deltaY);
39418				}
39419			});
39420
39421			self.on('submit', function(e) {
39422				if (!e.isDefaultPrevented()) {
39423					self.close();
39424				}
39425			});
39426
39427			windows.push(self);
39428			toggleFullScreenState(true);
39429		},
39430
39431		/**
39432		 * Fires a submit event with the serialized form.
39433		 *
39434		 * @method submit
39435		 * @return {Object} Event arguments object.
39436		 */
39437		submit: function() {
39438			return this.fire('submit', {data: this.toJSON()});
39439		},
39440
39441		/**
39442		 * Removes the current control from DOM and from UI collections.
39443		 *
39444		 * @method remove
39445		 * @return {tinymce.ui.Control} Current control instance.
39446		 */
39447		remove: function() {
39448			var self = this, i;
39449
39450			self.dragHelper.destroy();
39451			self._super();
39452
39453			if (self.statusbar) {
39454				this.statusbar.remove();
39455			}
39456
39457			i = windows.length;
39458			while (i--) {
39459				if (windows[i] === self) {
39460					windows.splice(i, 1);
39461				}
39462			}
39463
39464			toggleFullScreenState(windows.length > 0);
39465			toggleBodyFullScreenClasses(self.classPrefix);
39466		},
39467
39468		/**
39469		 * Returns the contentWindow object of the iframe if it exists.
39470		 *
39471		 * @method getContentWindow
39472		 * @return {Window} window object or null.
39473		 */
39474		getContentWindow: function() {
39475			var ifr = this.getEl().getElementsByTagName('iframe')[0];
39476			return ifr ? ifr.contentWindow : null;
39477		}
39478	});
39479
39480	if (!Env.desktop) {
39481		handleWindowResize();
39482	}
39483
39484	return Window;
39485});
39486
39487// Included from: js/tinymce/classes/ui/MessageBox.js
39488
39489/**
39490 * MessageBox.js
39491 *
39492 * Released under LGPL License.
39493 * Copyright (c) 1999-2015 Ephox Corp. All rights reserved
39494 *
39495 * License: http://www.tinymce.com/license
39496 * Contributing: http://www.tinymce.com/contributing
39497 */
39498
39499/**
39500 * This class is used to create MessageBoxes like alerts/confirms etc.
39501 *
39502 * @class tinymce.ui.MessageBox
39503 * @extends tinymce.ui.FloatPanel
39504 */
39505define("tinymce/ui/MessageBox", [
39506	"tinymce/ui/Window"
39507], function(Window) {
39508	"use strict";
39509
39510	var MessageBox = Window.extend({
39511		/**
39512		 * Constructs a instance with the specified settings.
39513		 *
39514		 * @constructor
39515		 * @param {Object} settings Name/value object with settings.
39516		 */
39517		init: function(settings) {
39518			settings = {
39519				border: 1,
39520				padding: 20,
39521				layout: 'flex',
39522				pack: "center",
39523				align: "center",
39524				containerCls: 'panel',
39525				autoScroll: true,
39526				buttons: {type: "button", text: "Ok", action: "ok"},
39527				items: {
39528					type: "label",
39529					multiline: true,
39530					maxWidth: 500,
39531					maxHeight: 200
39532				}
39533			};
39534
39535			this._super(settings);
39536		},
39537
39538		Statics: {
39539			/**
39540			 * Ok buttons constant.
39541			 *
39542			 * @static
39543			 * @final
39544			 * @field {Number} OK
39545			 */
39546			OK: 1,
39547
39548			/**
39549			 * Ok/cancel buttons constant.
39550			 *
39551			 * @static
39552			 * @final
39553			 * @field {Number} OK_CANCEL
39554			 */
39555			OK_CANCEL: 2,
39556
39557			/**
39558			 * yes/no buttons constant.
39559			 *
39560			 * @static
39561			 * @final
39562			 * @field {Number} YES_NO
39563			 */
39564			YES_NO: 3,
39565
39566			/**
39567			 * yes/no/cancel buttons constant.
39568			 *
39569			 * @static
39570			 * @final
39571			 * @field {Number} YES_NO_CANCEL
39572			 */
39573			YES_NO_CANCEL: 4,
39574
39575			/**
39576			 * Constructs a new message box and renders it to the body element.
39577			 *
39578			 * @static
39579			 * @method msgBox
39580			 * @param {Object} settings Name/value object with settings.
39581			 */
39582			msgBox: function(settings) {
39583				var buttons, callback = settings.callback || function() {};
39584
39585				function createButton(text, status, primary) {
39586					return {
39587						type: "button",
39588						text: text,
39589						subtype: primary ? 'primary' : '',
39590						onClick: function(e) {
39591							e.control.parents()[1].close();
39592							callback(status);
39593						}
39594					};
39595				}
39596
39597				switch (settings.buttons) {
39598					case MessageBox.OK_CANCEL:
39599						buttons = [
39600							createButton('Ok', true, true),
39601							createButton('Cancel', false)
39602						];
39603						break;
39604
39605					case MessageBox.YES_NO:
39606					case MessageBox.YES_NO_CANCEL:
39607						buttons = [
39608							createButton('Yes', 1, true),
39609							createButton('No', 0)
39610						];
39611
39612						if (settings.buttons == MessageBox.YES_NO_CANCEL) {
39613							buttons.push(createButton('Cancel', -1));
39614						}
39615						break;
39616
39617					default:
39618						buttons = [
39619							createButton('Ok', true, true)
39620						];
39621						break;
39622				}
39623
39624				return new Window({
39625					padding: 20,
39626					x: settings.x,
39627					y: settings.y,
39628					minWidth: 300,
39629					minHeight: 100,
39630					layout: "flex",
39631					pack: "center",
39632					align: "center",
39633					buttons: buttons,
39634					title: settings.title,
39635					role: 'alertdialog',
39636					items: {
39637						type: "label",
39638						multiline: true,
39639						maxWidth: 500,
39640						maxHeight: 200,
39641						text: settings.text
39642					},
39643					onPostRender: function() {
39644						this.aria('describedby', this.items()[0]._id);
39645					},
39646					onClose: settings.onClose,
39647					onCancel: function() {
39648						callback(false);
39649					}
39650				}).renderTo(document.body).reflow();
39651			},
39652
39653			/**
39654			 * Creates a new alert dialog.
39655			 *
39656			 * @method alert
39657			 * @param {Object} settings Settings for the alert dialog.
39658			 * @param {function} [callback] Callback to execute when the user makes a choice.
39659			 */
39660			alert: function(settings, callback) {
39661				if (typeof settings == "string") {
39662					settings = {text: settings};
39663				}
39664
39665				settings.callback = callback;
39666				return MessageBox.msgBox(settings);
39667			},
39668
39669			/**
39670			 * Creates a new confirm dialog.
39671			 *
39672			 * @method confirm
39673			 * @param {Object} settings Settings for the confirm dialog.
39674			 * @param {function} [callback] Callback to execute when the user makes a choice.
39675			 */
39676			confirm: function(settings, callback) {
39677				if (typeof settings == "string") {
39678					settings = {text: settings};
39679				}
39680
39681				settings.callback = callback;
39682				settings.buttons = MessageBox.OK_CANCEL;
39683
39684				return MessageBox.msgBox(settings);
39685			}
39686		}
39687	});
39688
39689	return MessageBox;
39690});
39691
39692// Included from: js/tinymce/classes/WindowManager.js
39693
39694/**
39695 * WindowManager.js
39696 *
39697 * Released under LGPL License.
39698 * Copyright (c) 1999-2015 Ephox Corp. All rights reserved
39699 *
39700 * License: http://www.tinymce.com/license
39701 * Contributing: http://www.tinymce.com/contributing
39702 */
39703
39704/**
39705 * This class handles the creation of native windows and dialogs. This class can be extended to provide for example inline dialogs.
39706 *
39707 * @class tinymce.WindowManager
39708 * @example
39709 * // Opens a new dialog with the file.htm file and the size 320x240
39710 * // It also adds a custom parameter this can be retrieved by using tinyMCEPopup.getWindowArg inside the dialog.
39711 * tinymce.activeEditor.windowManager.open({
39712 *    url: 'file.htm',
39713 *    width: 320,
39714 *    height: 240
39715 * }, {
39716 *    custom_param: 1
39717 * });
39718 *
39719 * // Displays an alert box using the active editors window manager instance
39720 * tinymce.activeEditor.windowManager.alert('Hello world!');
39721 *
39722 * // Displays an confirm box and an alert message will be displayed depending on what you choose in the confirm
39723 * tinymce.activeEditor.windowManager.confirm("Do you want to do something", function(s) {
39724 *    if (s)
39725 *       tinymce.activeEditor.windowManager.alert("Ok");
39726 *    else
39727 *       tinymce.activeEditor.windowManager.alert("Cancel");
39728 * });
39729 */
39730define("tinymce/WindowManager", [
39731	"tinymce/ui/Window",
39732	"tinymce/ui/MessageBox"
39733], function(Window, MessageBox) {
39734	return function(editor) {
39735		var self = this, windows = [];
39736
39737		function getTopMostWindow() {
39738			if (windows.length) {
39739				return windows[windows.length - 1];
39740			}
39741		}
39742
39743		self.windows = windows;
39744
39745		editor.on('remove', function() {
39746			var i = windows.length;
39747
39748			while (i--) {
39749				windows[i].close();
39750			}
39751		});
39752
39753		/**
39754		 * Opens a new window.
39755		 *
39756		 * @method open
39757		 * @param {Object} args Optional name/value settings collection contains things like width/height/url etc.
39758		 * @param {Object} params Options like title, file, width, height etc.
39759		 * @option {String} title Window title.
39760		 * @option {String} file URL of the file to open in the window.
39761		 * @option {Number} width Width in pixels.
39762		 * @option {Number} height Height in pixels.
39763		 * @option {Boolean} autoScroll Specifies whether the popup window can have scrollbars if required (i.e. content
39764		 * larger than the popup size specified).
39765		 */
39766		self.open = function(args, params) {
39767			var win;
39768
39769			editor.editorManager.setActive(editor);
39770
39771			args.title = args.title || ' ';
39772
39773			// Handle URL
39774			args.url = args.url || args.file; // Legacy
39775			if (args.url) {
39776				args.width = parseInt(args.width || 320, 10);
39777				args.height = parseInt(args.height || 240, 10);
39778			}
39779
39780			// Handle body
39781			if (args.body) {
39782				args.items = {
39783					defaults: args.defaults,
39784					type: args.bodyType || 'form',
39785					items: args.body
39786				};
39787			}
39788
39789			if (!args.url && !args.buttons) {
39790				args.buttons = [
39791					{text: 'Ok', subtype: 'primary', onclick: function() {
39792						win.find('form')[0].submit();
39793					}},
39794
39795					{text: 'Cancel', onclick: function() {
39796						win.close();
39797					}}
39798				];
39799			}
39800
39801			win = new Window(args);
39802			windows.push(win);
39803
39804			win.on('close', function() {
39805				var i = windows.length;
39806
39807				while (i--) {
39808					if (windows[i] === win) {
39809						windows.splice(i, 1);
39810					}
39811				}
39812
39813				if (!windows.length) {
39814					editor.focus();
39815				}
39816			});
39817
39818			// Handle data
39819			if (args.data) {
39820				win.on('postRender', function() {
39821					this.find('*').each(function(ctrl) {
39822						var name = ctrl.name();
39823
39824						if (name in args.data) {
39825							ctrl.value(args.data[name]);
39826						}
39827					});
39828				});
39829			}
39830
39831			// store args and parameters
39832			win.features = args || {};
39833			win.params = params || {};
39834
39835			// Takes a snapshot in the FocusManager of the selection before focus is lost to dialog
39836			if (windows.length === 1) {
39837				editor.nodeChanged();
39838			}
39839
39840			return win.renderTo().reflow();
39841		};
39842
39843		/**
39844		 * Creates a alert dialog. Please don't use the blocking behavior of this
39845		 * native version use the callback method instead then it can be extended.
39846		 *
39847		 * @method alert
39848		 * @param {String} message Text to display in the new alert dialog.
39849		 * @param {function} callback Callback function to be executed after the user has selected ok.
39850		 * @param {Object} scope Optional scope to execute the callback in.
39851		 * @example
39852		 * // Displays an alert box using the active editors window manager instance
39853		 * tinymce.activeEditor.windowManager.alert('Hello world!');
39854		 */
39855		self.alert = function(message, callback, scope) {
39856			MessageBox.alert(message, function() {
39857				if (callback) {
39858					callback.call(scope || this);
39859				} else {
39860					editor.focus();
39861				}
39862			});
39863		};
39864
39865		/**
39866		 * Creates a confirm dialog. Please don't use the blocking behavior of this
39867		 * native version use the callback method instead then it can be extended.
39868		 *
39869		 * @method confirm
39870		 * @param {String} message Text to display in the new confirm dialog.
39871		 * @param {function} callback Callback function to be executed after the user has selected ok or cancel.
39872		 * @param {Object} scope Optional scope to execute the callback in.
39873		 * @example
39874		 * // Displays an confirm box and an alert message will be displayed depending on what you choose in the confirm
39875		 * tinymce.activeEditor.windowManager.confirm("Do you want to do something", function(s) {
39876		 *    if (s)
39877		 *       tinymce.activeEditor.windowManager.alert("Ok");
39878		 *    else
39879		 *       tinymce.activeEditor.windowManager.alert("Cancel");
39880		 * });
39881		 */
39882		self.confirm = function(message, callback, scope) {
39883			MessageBox.confirm(message, function(state) {
39884				callback.call(scope || this, state);
39885			});
39886		};
39887
39888		/**
39889		 * Closes the top most window.
39890		 *
39891		 * @method close
39892		 */
39893		self.close = function() {
39894			if (getTopMostWindow()) {
39895				getTopMostWindow().close();
39896			}
39897		};
39898
39899		/**
39900		 * Returns the params of the last window open call. This can be used in iframe based
39901		 * dialog to get params passed from the tinymce plugin.
39902		 *
39903		 * @example
39904		 * var dialogArguments = top.tinymce.activeEditor.windowManager.getParams();
39905		 *
39906		 * @method getParams
39907		 * @return {Object} Name/value object with parameters passed from windowManager.open call.
39908		 */
39909		self.getParams = function() {
39910			return getTopMostWindow() ? getTopMostWindow().params : null;
39911		};
39912
39913		/**
39914		 * Sets the params of the last opened window.
39915		 *
39916		 * @method setParams
39917		 * @param {Object} params Params object to set for the last opened window.
39918		 */
39919		self.setParams = function(params) {
39920			if (getTopMostWindow()) {
39921				getTopMostWindow().params = params;
39922			}
39923		};
39924
39925		/**
39926		 * Returns the currently opened window objects.
39927		 *
39928		 * @method getWindows
39929		 * @return {Array} Array of the currently opened windows.
39930		 */
39931		self.getWindows = function() {
39932			return windows;
39933		};
39934	};
39935});
39936
39937// Included from: js/tinymce/classes/ui/Tooltip.js
39938
39939/**
39940 * Tooltip.js
39941 *
39942 * Released under LGPL License.
39943 * Copyright (c) 1999-2015 Ephox Corp. All rights reserved
39944 *
39945 * License: http://www.tinymce.com/license
39946 * Contributing: http://www.tinymce.com/contributing
39947 */
39948
39949/**
39950 * Creates a tooltip instance.
39951 *
39952 * @-x-less ToolTip.less
39953 * @class tinymce.ui.ToolTip
39954 * @extends tinymce.ui.Control
39955 * @mixes tinymce.ui.Movable
39956 */
39957define("tinymce/ui/Tooltip", [
39958	"tinymce/ui/Control",
39959	"tinymce/ui/Movable"
39960], function(Control, Movable) {
39961	return Control.extend({
39962		Mixins: [Movable],
39963
39964		Defaults: {
39965			classes: 'widget tooltip tooltip-n'
39966		},
39967
39968		/**
39969		 * Renders the control as a HTML string.
39970		 *
39971		 * @method renderHtml
39972		 * @return {String} HTML representing the control.
39973		 */
39974		renderHtml: function() {
39975			var self = this, prefix = self.classPrefix;
39976
39977			return (
39978				'<div id="' + self._id + '" class="' + self.classes + '" role="presentation">' +
39979					'<div class="' + prefix + 'tooltip-arrow"></div>' +
39980					'<div class="' + prefix + 'tooltip-inner">' + self.encode(self.state.get('text')) + '</div>' +
39981				'</div>'
39982			);
39983		},
39984
39985		bindStates: function() {
39986			var self = this;
39987
39988			self.state.on('change:text', function(e) {
39989				self.getEl().lastChild.innerHTML = self.encode(e.value);
39990			});
39991
39992			return self._super();
39993		},
39994
39995		/**
39996		 * Repaints the control after a layout operation.
39997		 *
39998		 * @method repaint
39999		 */
40000		repaint: function() {
40001			var self = this, style, rect;
40002
40003			style = self.getEl().style;
40004			rect = self._layoutRect;
40005
40006			style.left = rect.x + 'px';
40007			style.top = rect.y + 'px';
40008			style.zIndex = 0xFFFF + 0xFFFF;
40009		}
40010	});
40011});
40012
40013// Included from: js/tinymce/classes/ui/Widget.js
40014
40015/**
40016 * Widget.js
40017 *
40018 * Released under LGPL License.
40019 * Copyright (c) 1999-2015 Ephox Corp. All rights reserved
40020 *
40021 * License: http://www.tinymce.com/license
40022 * Contributing: http://www.tinymce.com/contributing
40023 */
40024
40025/**
40026 * Widget base class a widget is a control that has a tooltip and s
40026ome basic states.
40027 *
40028 * @class tinymce.ui.Widget
40029 * @extends tinymce.ui.Control
40030 */
40031define("tinymce/ui/Widget", [
40032	"tinymce/ui/Control",
40033	"tinymce/ui/Tooltip"
40034], function(Control, Tooltip) {
40035	"use strict";
40036
40037	var tooltip;
40038
40039	var Widget = Control.extend({
40040		/**
40041		 * Constructs a instance with the specified settings.
40042		 *
40043		 * @constructor
40044		 * @param {Object} settings Name/value object with settings.
40045		 * @setting {String} tooltip Tooltip text to display when hovering.
40046		 * @setting {Boolean} autofocus True if the control should be focused when rendered.
40047		 * @setting {String} text Text to display inside widget.
40048		 */
40049		init: function(settings) {
40050			var self = this;
40051
40052			self._super(settings);
40053			settings = self.settings;
40054			self.canFocus = true;
40055
40056			if (settings.tooltip && Widget.tooltips !== false) {
40057				self.on('mouseenter', function(e) {
40058					var tooltip = self.tooltip().moveTo(-0xFFFF);
40059
40060					if (e.control == self) {
40061						var rel = tooltip.text(settings.tooltip).show().testMoveRel(self.getEl(), ['bc-tc', 'bc-tl', 'bc-tr']);
40062
40063						tooltip.classes.toggle('tooltip-n', rel == 'bc-tc');
40064						tooltip.classes.toggle('tooltip-nw', rel == 'bc-tl');
40065						tooltip.classes.toggle('tooltip-ne', rel == 'bc-tr');
40066
40067						tooltip.moveRel(self.getEl(), rel);
40068					} else {
40069						tooltip.hide();
40070					}
40071				});
40072
40073				self.on('mouseleave mousedown click', function() {
40074					self.tooltip().hide();
40075				});
40076			}
40077
40078			self.aria('label', settings.ariaLabel || settings.tooltip);
40079		},
40080
40081		/**
40082		 * Returns the current tooltip instance.
40083		 *
40084		 * @method tooltip
40085		 * @return {tinymce.ui.Tooltip} Tooltip instance.
40086		 */
40087		tooltip: function() {
40088			if (!tooltip) {
40089				tooltip = new Tooltip({type: 'tooltip'});
40090				tooltip.renderTo();
40091			}
40092
40093			return tooltip;
40094		},
40095
40096		/**
40097		 * Called after the control has been rendered.
40098		 *
40099		 * @method postRender
40100		 */
40101		postRender: function() {
40102			var self = this, settings = self.settings;
40103
40104			self._super();
40105
40106			if (!self.parent() && (settings.width || settings.height)) {
40107				self.initLayoutRect();
40108				self.repaint();
40109			}
40110
40111			if (settings.autofocus) {
40112				self.focus();
40113			}
40114		},
40115
40116		bindStates: function() {
40117			var self = this;
40118
40119			function disable(state) {
40120				self.aria('disabled', state);
40121				self.classes.toggle('disabled', state);
40122			}
40123
40124			function active(state) {
40125				self.aria('pressed', state);
40126				self.classes.toggle('active', state);
40127			}
40128
40129			self.state.on('change:disabled', function(e) {
40130				disable(e.value);
40131			});
40132
40133			self.state.on('change:active', function(e) {
40134				active(e.value);
40135			});
40136
40137			if (self.state.get('disabled')) {
40138				disable(true);
40139			}
40140
40141			if (self.state.get('active')) {
40142				active(true);
40143			}
40144
40145			return self._super();
40146		},
40147
40148		/**
40149		 * Removes the current control from DOM and from UI collections.
40150		 *
40151		 * @method remove
40152		 * @return {tinymce.ui.Control} Current control instance.
40153		 */
40154		remove: function() {
40155			this._super();
40156
40157			if (tooltip) {
40158				tooltip.remove();
40159				tooltip = null;
40160			}
40161		}
40162	});
40163
40164	return Widget;
40165});
40166
40167// Included from: js/tinymce/classes/ui/Progress.js
40168
40169/**
40170 * Progress.js
40171 *
40172 * Released under LGPL License.
40173 * Copyright (c) 1999-2015 Ephox Corp. All rights reserved
40174 *
40175 * License: http://www.tinymce.com/license
40176 * Contributing: http://www.tinymce.com/contributing
40177 */
40178
40179/**
40180 * Progress control.
40181 *
40182 * @-x-less Progress.less
40183 * @class tinymce.ui.Progress
40184 * @extends tinymce.ui.Control
40185 */
40186define("tinymce/ui/Progress", [
40187	"tinymce/ui/Widget"
40188], function(Widget) {
40189	"use strict";
40190
40191	return Widget.extend({
40192		Defaults: {
40193			value: 0
40194		},
40195
40196		init: function(settings) {
40197			var self = this;
40198
40199			self._super(settings);
40200			self.classes.add('progress');
40201
40202			if (!self.settings.filter) {
40203				self.settings.filter = function(value) {
40204					return Math.round(value);
40205				};
40206			}
40207		},
40208
40209		renderHtml: function() {
40210			var self = this, id = self._id, prefix = this.classPrefix;
40211
40212			return (
40213				'<div id="' + id + '" class="' + self.classes + '">' +
40214					'<div class="' + prefix + 'bar-container">' +
40215						'<div class="' + prefix + 'bar"></div>' +
40216					'</div>' +
40217					'<div class="' + prefix + 'text">0%</div>' +
40218				'</div>'
40219			);
40220		},
40221
40222		postRender: function() {
40223			var self = this;
40224
40225			self._super();
40226			self.value(self.settings.value);
40227
40228			return self;
40229		},
40230
40231		bindStates: function() {
40232			var self = this;
40233
40234			function setValue(value) {
40235				value = self.settings.filter(value);
40236				self.getEl().lastChild.innerHTML = value + '%';
40237				self.getEl().firstChild.firstChild.style.width = value + '%';
40238			}
40239
40240			self.state.on('change:value', function(e) {
40241				setValue(e.value);
40242			});
40243
40244			setValue(self.state.get('value'));
40245
40246			return self._super();
40247		}
40248	});
40249});
40250
40251// Included from: js/tinymce/classes/ui/Notification.js
40252
40253/**
40254 * Notification.js
40255 *
40256 * Released under LGPL License.
40257 * Copyright (c) 1999-2015 Ephox Corp. All rights reserved
40258 *
40259 * License: http://www.tinymce.com/license
40260 * Contributing: http://www.tinymce.com/contributing
40261 */
40262
40263/**
40264 * Creates a notification instance.
40265 *
40266 * @-x-less Notification.less
40267 * @class tinymce.ui.Notification
40268 * @extends tinymce.ui.Container
40269 * @mixes tinymce.ui.Movable
40270 */
40271define("tinymce/ui/Notification", [
40272	"tinymce/ui/Control",
40273	"tinymce/ui/Movable",
40274    "tinymce/ui/Progress"
40275], function(Control, Movable, Progress) {
40276	return Control.extend({
40277		Mixins: [Movable],
40278
40279		Defaults: {
40280			classes: 'widget notification'
40281		},
40282
40283		init: function(settings) {
40284			var self = this;
40285
40286			self._super(settings);
40287
40288			if (settings.text) {
40289				self.text(settings.text);
40290			}
40291
40292			if (settings.icon) {
40293				self.icon = settings.icon;
40294			}
40295
40296			if (settings.color) {
40297				self.color = settings.color;
40298			}
40299
40300			if (settings.type) {
40301				self.classes.add('notification-' + settings.type);
40302			}
40303
40304			if (settings.timeout && (settings.timeout < 0 || settings.timeout > 0) && !settings.closeButton) {
40305				self.closeButton = false;
40306			} else {
40307				self.classes.add('has-close');
40308				self.closeButton = true;
40309			}
40310
40311			if (settings.progressBar) {
40312				self.progressBar = new Progress();
40313			}
40314
40315			self.on('click', function(e) {
40316				if (e.target.className.indexOf(self.classPrefix + 'close') != -1) {
40317					self.close();
40318				}
40319			});
40320		},
40321
40322		/**
40323		 * Renders the control as a HTML string.
40324		 *
40325		 * @method renderHtml
40326		 * @return {String} HTML representing the control.
40327		 */
40328		renderHtml: function() {
40329			var self = this, prefix = self.classPrefix, icon = '', closeButton = '', progressBar = '', notificationStyle = '';
40330
40331			if (self.icon) {
40332				icon = '<i class="' + prefix + 'ico' + ' ' + prefix + 'i-' + self.icon + '"></i>';
40333			}
40334
40335			if (self.color) {
40336				notificationStyle = ' style="background-color: ' + self.color + '"';
40337			}
40338
40339			if (self.closeButton) {
40340				closeButton = '<button type="button" class="' + prefix + 'close" aria-hidden="true">\u00d7</button>';
40341			}
40342
40343			if (self.progressBar) {
40344				progressBar = self.progressBar.renderHtml();
40345			}
40346
40347			return (
40348				'<div id="' + self._id + '" class="' + self.classes + '"' + notificationStyle + ' role="presentation">' +
40349					icon +
40350					'<div class="' + prefix + 'notification-inner">' + self.state.get('text') + '</div>' +
40351					progressBar +
40352					closeButton +
40353				'</div>'
40354			);
40355		},
40356
40357		bindStates: function() {
40358			var self = this;
40359
40360			self.state.on('change:text', function(e) {
40361				self.getEl().childNodes[1].innerHTML = e.value;
40362			});
40363			if (self.progressBar) {
40364				self.progressBar.bindStates();
40365			}
40366			return self._super();
40367		},
40368
40369		close: function() {
40370			var self = this;
40371
40372			if (!self.fire('close').isDefaultPrevented()) {
40373				self.remove();
40374			}
40375
40376			return self;
40377		},
40378
40379		/**
40380		 * Repaints the control after a layout operation.
40381		 *
40382		 * @method repaint
40383		 */
40384		repaint: function() {
40385			var self = this, style, rect;
40386
40387			style = self.getEl().style;
40388			rect = self._layoutRect;
40389
40390			style.left = rect.x + 'px';
40391			style.top = rect.y + 'px';
40392			style.zIndex = 0xFFFF + 0xFFFF;
40393		}
40394	});
40395});
40396
40397// Included from: js/tinymce/classes/NotificationManager.js
40398
40399/**
40400 * NotificationManager.js
40401 *
40402 * Released under LGPL License.
40403 * Copyright (c) 1999-2015 Ephox Corp. All rights reserved
40404 *
40405 * License: http://www.tinymce.com/license
40406 * Contributing: http://www.tinymce.com/contributing
40407 */
40408
40409/**
40410 * This class handles the creation of TinyMCE's notifications.
40411 *
40412 * @class tinymce.notificationManager
40413 * @example
40414 * // Opens a new notification of type "error" with text "An error occurred."
40415 * tinymce.activeEditor.notificationManager.open({
40416 *    text: 'An error occurred.',
40417 *    type: 'error'
40418 * });
40419 */
40420define("tinymce/NotificationManager", [
40421	"tinymce/ui/Notification",
40422	"tinymce/util/Delay"
40423], function(Notification, Delay) {
40424	return function(editor) {
40425		var self = this, notifications = [];
40426
40427		function getLastNotification() {
40428			if (notifications.length) {
40429				return notifications[notifications.length - 1];
40430			}
40431		}
40432
40433		self.notifications = notifications;
40434
40435		function resizeWindowEvent() {
40436			Delay.requestAnimationFrame(function() {
40437				prePositionNotifications();
40438				positionNotifications();
40439			});
40440		}
40441
40442		// Since the viewport will change based on the present notifications, we need to move them all to the
40443		// top left of the viewport to give an accurate size measurement so we can position them later.
40444		function prePositionNotifications() {
40445			for (var i = 0; i < notifications.length; i++) {
40446				notifications[i].moveTo(0, 0);
40447			}
40448		}
40449
40450		function positionNotifications() {
40451			if (notifications.length > 0) {
40452				var firstItem = notifications.slice(0, 1)[0];
40453				var container = editor.inline ? editor.getElement() : editor.getContentAreaContainer();
40454				firstItem.moveRel(container, 'tc-tc');
40455				if (notifications.length > 1) {
40456					for (var i = 1; i < notifications.length; i++) {
40457						notifications[i].moveRel(notifications[i - 1].getEl(), 'bc-tc');
40458					}
40459				}
40460			}
40461		}
40462
40463		editor.on('remove', function() {
40464			var i = notifications.length;
40465
40466			while (i--) {
40467				notifications[i].close();
40468			}
40469		});
40470
40471		editor.on('ResizeEditor', positionNotifications);
40472		editor.on('ResizeWindow', resizeWindowEvent);
40473
40474		/**
40475		 * Opens a new notification.
40476		 *
40477		 * @method open
40478		 * @param {Object} args Optional name/value settings collection contains things like timeout/color/message etc.
40479		 */
40480		self.open = function(args) {
40481			var notif;
40482
40483			editor.editorManager.setActive(editor);
40484
40485			notif = new Notification(args);
40486			notifications.push(notif);
40487
40488			//If we have a timeout value
40489			if (args.timeout > 0) {
40490				notif.timer = setTimeout(function() {
40491					notif.close();
40492				}, args.timeout);
40493			}
40494
40495			notif.on('close', function() {
40496				var i = notifications.length;
40497
40498				if (notif.timer) {
40499					editor.getWin().clearTimeout(notif.timer);
40500				}
40501
40502				while (i--) {
40503					if (notifications[i] === notif) {
40504						notifications.splice(i, 1);
40505					}
40506				}
40507
40508				positionNotifications();
40509			});
40510
40511			notif.renderTo();
40512
40513			positionNotifications();
40514
40515			return notif;
40516		};
40517
40518		/**
40519		 * Closes the top most notification.
40520		 *
40521		 * @method close
40522		 */
40523		self.close = function() {
40524			if (getLastNotification()) {
40525				getLastNotification().close();
40526			}
40527		};
40528
40529		/**
40530		 * Returns the currently opened notification objects.
40531		 *
40532		 * @method getNotifications
40533		 * @return {Array} Array of the currently opened notifications.
40534		 */
40535		self.getNotifications = function() {
40536			return notifications;
40537		};
40538
40539		//self.positionNotifications = positionNotifications;
40540	};
40541});
40542
40543// Included from: js/tinymce/classes/dom/NodePath.js
40544
40545/**
40546 * NodePath.js
40547 *
40548 * Released under LGPL License.
40549 * Copyright (c) 1999-2015 Ephox Corp. All rights reserved
40550 *
40551 * License: http://www.tinymce.com/license
40552 * Contributing: http://www.tinymce.com/contributing
40553 */
40554
40555/**
40556 * Handles paths of nodes within an element.
40557 *
40558 * @private
40559 * @class tinymce.dom.NodePath
40560 */
40561define("tinymce/dom/NodePath", [
40562	"tinymce/dom/DOMUtils"
40563], function(DOMUtils) {
40564	function create(rootNode, targetNode, normalized) {
40565		var path = [];
40566
40567		for (; targetNode && targetNode != rootNode; targetNode = targetNode.parentNode) {
40568			path.push(DOMUtils.nodeIndex(targetNode, normalized));
40569		}
40570
40571		return path;
40572	}
40573
40574	function resolve(rootNode, path) {
40575		var i, node, children;
40576
40577		for (node = rootNode, i = path.length - 1; i >= 0; i--) {
40578			children = node.childNodes;
40579
40580			if (path[i] > children.length - 1) {
40581				return null;
40582			}
40583
40584			node = children[path[i]];
40585		}
40586
40587		return node;
40588	}
40589
40590	return {
40591		create: create,
40592		resolve: resolve
40593	};
40594});
40595
40596// Included from: js/tinymce/classes/util/Quirks.js
40597
40598/**
40599 * Quirks.js
40600 *
40601 * Released under LGPL License.
40602 * Copyright (c) 1999-2015 Ephox Corp. All rights reserved
40603 *
40604 * License: http://www.tinymce.com/license
40605 * Contributing: http://www.tinymce.com/contributing
40606 *
40607 * @ignore-file
40608 */
40609
40610/**
40611 * This file includes fixes for various browser quirks it's made to make it easy to add/remove browser specific fixes.
40612 *
40613 * @private
40614 * @class tinymce.util.Quirks
40615 */
40616define("tinymce/util/Quirks", [
40617	"tinymce/util/VK",
40618	"tinymce/dom/RangeUtils",
40619	"tinymce/dom/TreeWalker",
40620	"tinymce/dom/NodePath",
40621	"tinymce/html/Node",
40622	"tinymce/html/Entities",
40623	"tinymce/Env",
40624	"tinymce/util/Tools",
40625	"tinymce/util/Delay",
40626	"tinymce/caret/CaretContainer"
40627], function(VK, RangeUtils, TreeWalker, NodePath, Node, Entities, Env, Tools, Delay, CaretContainer) {
40628	return function(editor) {
40629		var each = Tools.each, $ = editor.$;
40630		var BACKSPACE = VK.BACKSPACE, DELETE = VK.DELETE, dom = editor.dom, selection = editor.selection,
40631			settings = editor.settings, parser = editor.parser, serializer = editor.serializer;
40632		var isGecko = Env.gecko, isIE = Env.ie, isWebKit = Env.webkit;
40633		var mceInternalUrlPrefix = 'data:text/mce-internal,';
40634		var mceInternalDataType = isIE ? 'Text' : 'URL';
40635
40636		/**
40637		 * Executes a command with a specific state this can be to enable/disable browser editing features.
40638		 */
40639		function setEditorCommandState(cmd, state) {
40640			try {
40641				editor.getDoc().execCommand(cmd, false, state);
40642			} catch (ex) {
40643				// Ignore
40644			}
40645		}
40646
40647		/**
40648		 * Returns current IE document mode.
40649		 */
40650		function getDocumentMode() {
40651			var documentMode = editor.getDoc().documentMode;
40652
40653			return documentMode ? documentMode : 6;
40654		}
40655
40656		/**
40657		 * Returns true/false if the event is prevented or not.
40658		 *
40659		 * @private
40660		 * @param {Event} e Event object.
40661		 * @return {Boolean} true/false if the event is prevented or not.
40662		 */
40663		function isDefaultPrevented(e) {
40664			return e.isDefaultPrevented();
40665		}
40666
40667		/**
40668		 * Sets Text/URL data on the event's dataTransfer object to a special data:text/mce-internal url.
40669		 * This is to workaround the inability to set custom contentType on IE and Safari.
40670		 * The editor's selected content is encoded into this url so drag and drop between editors will work.
40671		 *
40672		 * @private
40673		 * @param {DragEvent} e Event object
40674		 */
40675		function setMceInteralContent(e) {
40676			var selectionHtml, internalContent;
40677
40678			if (e.dataTransfer) {
40679				if (editor.selection.isCollapsed() && e.target.tagName == 'IMG') {
40680					selection.select(e.target);
40681				}
40682
40683				selectionHtml = editor.selection.getContent();
40684
40685				// Safari/IE doesn't support custom dataTransfer items so we can only use URL and Text
40686				if (selectionHtml.length > 0) {
40687					internalContent = mceInternalUrlPrefix + escape(editor.id) + ',' + escape(selectionHtml);
40688					e.dataTransfer.setData(mceInternalDataType, internalContent);
40689				}
40690			}
40691		}
40692
40693		/**
40694		 * Gets content of special data:text/mce-internal url on the event's dataTransfer object.
40695		 * This is to workaround the inability to set custom contentType on IE and Safari.
40696		 * The editor's selected content is encoded into this url so drag and drop between editors will work.
40697		 *
40698		 * @private
40699		 * @param {DragEvent} e Event object
40700		 * @returns {String}
40700 mce-internal content
40701		 */
40702		function getMceInternalContent(e) {
40703			var internalContent;
40704
40705			if (e.dataTransfer) {
40706				internalContent = e.dataTransfer.getData(mceInternalDataType);
40707
40708				if (internalContent && internalContent.indexOf(mceInternalUrlPrefix) >= 0) {
40709					internalContent = internalContent.substr(mceInternalUrlPrefix.length).split(',');
40710
40711					return {
40712						id: unescape(internalContent[0]),
40713						html: unescape(internalContent[1])
40714					};
40715				}
40716			}
40717
40718			return null;
40719		}
40720
40721		/**
40722		 * Inserts contents using the paste clipboard command if it's available if it isn't it will fallback
40723		 * to the core command.
40724		 *
40725		 * @private
40726		 * @param {String} content Content to insert at selection.
40727		 */
40728		function insertClipboardContents(content) {
40729			if (editor.queryCommandSupported('mceInsertClipboardContent')) {
40730				editor.execCommand('mceInsertClipboardContent', false, {content: content});
40731			} else {
40732				editor.execCommand('mceInsertContent', false, content);
40733			}
40734		}
40735
40736		/**
40737		 * Fixes a WebKit bug when deleting contents using backspace or delete key.
40738		 * WebKit will produce a span element if you delete across two block elements.
40739		 *
40740		 * Example:
40741		 * <h1>a</h1><p>|b</p>
40742		 *
40743		 * Will produce this on backspace:
40744		 * <h1>a<span style="<all runtime styles>">b</span></p>
40745		 *
40746		 * This fixes the backspace to produce:
40747		 * <h1>a|b</p>
40748		 *
40749		 * See bug: https://bugs.webkit.org/show_bug.cgi?id=45784
40750		 *
40751		 * This fixes the following delete scenarios:
40752		 *  1. Delete by pressing backspace key.
40753		 *  2. Delete by pressing delete key.
40754		 *  3. Delete by pressing backspace key with ctrl/cmd (Word delete).
40755		 *  4. Delete by pressing delete key with ctrl/cmd (Word delete).
40756		 *  5. Delete by drag/dropping contents inside the editor.
40757		 *  6. Delete by using Cut Ctrl+X/Cmd+X.
40758		 *  7. Delete by selecting contents and writing a character.
40759		 *
40760		 * This code is a ugly hack since writing full custom delete logic for just this bug
40761		 * fix seemed like a huge task. I hope we can remove this before the year 2030.
40762		 */
40763		function cleanupStylesWhenDeleting() {
40764			var doc = editor.getDoc(), dom = editor.dom, selection = editor.selection;
40765			var MutationObserver = window.MutationObserver, olderWebKit, dragStartRng;
40766
40767			// Add mini polyfill for older WebKits
40768			// TODO: Remove this when old Safari versions gets updated
40769			if (!MutationObserver) {
40770				olderWebKit = true;
40771
40772				MutationObserver = function() {
40773					var records = [], target;
40774
40775					function nodeInsert(e) {
40776						var target = e.relatedNode || e.target;
40777						records.push({target: target, addedNodes: [target]});
40778					}
40779
40780					function attrModified(e) {
40781						var target = e.relatedNode || e.target;
40782						records.push({target: target, attributeName: e.attrName});
40783					}
40784
40785					this.observe = function(node) {
40786						target = node;
40787						target.addEventListener('DOMSubtreeModified', nodeInsert, false);
40788						target.addEventListener('DOMNodeInsertedIntoDocument', nodeInsert, false);
40789						target.addEventListener('DOMNodeInserted', nodeInsert, false);
40790						target.addEventListener('DOMAttrModified', attrModified, false);
40791					};
40792
40793					this.disconnect = function() {
40794						target.removeEventListener('DOMSubtreeModified', nodeInsert, false);
40795						target.removeEventListener('DOMNodeInsertedIntoDocument', nodeInsert, false);
40796						target.removeEventListener('DOMNodeInserted', nodeInsert, false);
40797						target.removeEventListener('DOMAttrModified', attrModified, false);
40798					};
40799
40800					this.takeRecords = function() {
40801						return records;
40802					};
40803				};
40804			}
40805
40806			function isTrailingBr(node) {
40807				var blockElements = dom.schema.getBlockElements(), rootNode = editor.getBody();
40808
40809				if (node.nodeName != 'BR') {
40810					return false;
40811				}
40812
40813				for (node = node; node != rootNode && !blockElements[node.nodeName]; node = node.parentNode) {
40814					if (node.nextSibling) {
40815						return false;
40816					}
40817				}
40818
40819				return true;
40820			}
40821
40822			function isSiblingsIgnoreWhiteSpace(node1, node2) {
40823				var node;
40824
40825				for (node = node1.nextSibling; node && node != node2; node = node.nextSibling) {
40826					if (node.nodeType == 3 && $.trim(node.data).length === 0) {
40827						continue;
40828					}
40829
40830					if (node !== node2) {
40831						return false;
40832					}
40833				}
40834
40835				return node === node2;
40836			}
40837
40838			function findCaretNode(node, forward, startNode) {
40839				var walker, current, nonEmptyElements;
40840
40841				nonEmptyElements = dom.schema.getNonEmptyElements();
40842
40843				walker = new TreeWalker(startNode || node, node);
40844
40845				while ((current = walker[forward ? 'next' : 'prev']())) {
40846					if (nonEmptyElements[current.nodeName] && !isTrailingBr(current)) {
40847						return current;
40848					}
40849
40850					if (current.nodeType == 3 && current.data.length > 0) {
40851						return current;
40852					}
40853				}
40854			}
40855
40856			function deleteRangeBetweenTextBlocks(rng) {
40857				var startBlock, endBlock, caretNodeBefore, caretNodeAfter, textBlockElements;
40858
40859				if (rng.collapsed) {
40860					return;
40861				}
40862
40863				startBlock = dom.getParent(RangeUtils.getNode(rng.startContainer, rng.startOffset), dom.isBlock);
40864				endBlock = dom.getParent(RangeUtils.getNode(rng.endContainer, rng.endOffset), dom.isBlock);
40865				textBlockElements = editor.schema.getTextBlockElements();
40866
40867				if (startBlock == endBlock) {
40868					return;
40869				}
40870
40871				if (!textBlockElements[startBlock.nodeName] || !textBlockElements[endBlock.nodeName]) {
40872					return;
40873				}
40874
40875				if (dom.getContentEditable(startBlock) === "false" || dom.getContentEditable(endBlock) === "false") {
40876					return;
40877				}
40878
40879				rng.deleteContents();
40880
40881				caretNodeBefore = findCaretNode(startBlock, false);
40882				caretNodeAfter = findCaretNode(endBlock, true);
40883
40884				if (!dom.isEmpty(endBlock)) {
40885					$(startBlock).append(endBlock.childNodes);
40886				}
40887
40888				$(endBlock).remove();
40889
40890				if (caretNodeBefore) {
40891					if (caretNodeBefore.nodeType == 1) {
40892						if (caretNodeBefore.nodeName == "BR") {
40893							rng.setStartBefore(caretNodeBefore);
40894							rng.setEndBefore(caretNodeBefore);
40895						} else {
40896							rng.setStartAfter(caretNodeBefore);
40897							rng.setEndAfter(caretNodeBefore);
40898						}
40899					} else {
40900						rng.setStart(caretNodeBefore, caretNodeBefore.data.length);
40901						rng.setEnd(caretNodeBefore, caretNodeBefore.data.length);
40902					}
40903				} else if (caretNodeAfter) {
40904					if (caretNodeAfter.nodeType == 1) {
40905						rng.setStartBefore(caretNodeAfter);
40906						rng.setEndBefore(caretNodeAfter);
40907					} else {
40908						rng.setStart(caretNodeAfter, 0);
40909						rng.setEnd(caretNodeAfter, 0);
40910					}
40911				}
40912
40913				selection.setRng(rng);
40914
40915				return true;
40916			}
40917
40918			function expandBetweenBlocks(rng, isForward) {
40919				var caretNode, targetCaretNode, textBlock, targetTextBlock, container, offset;
40920
40921				if (!rng.collapsed) {
40922					return rng;
40923				}
40924
40925				container = rng.startContainer;
40926				offset = rng.startOffset;
40927
40928				if (container.nodeType == 3) {
40929					if (isForward) {
40930						if (offset < container.data.length) {
40931							return rng;
40932						}
40933					} else {
40934						if (offset > 0) {
40935							return rng;
40936						}
40937					}
40938				}
40939
40940				caretNode = RangeUtils.getNode(rng.startContainer, rng.startOffset);
40941				textBlock = dom.getParent(caretNode, dom.isBlock);
40942				targetCaretNode = findCaretNode(editor.getBody(), isForward, caretNode);
40943				targetTextBlock = dom.getParent(targetCaretNode, dom.isBlock);
40944
40945				if (!caretNode || !targetCaretNode) {
40946					return rng;
40947				}
40948
40949				if (targetTextBlock && textBlock != targetTextBlock) {
40950					if (!isForward) {
40951						if (!isSiblingsIgnoreWhiteSpace(targetTextBlock, textBlock)) {
40952							return rng;
40953						}
40954
40955						if (targetCaretNode.nodeType == 1) {
40956							if (targetCaretNode.nodeName == "BR") {
40957								rng.setStartBefore(targetCaretNode);
40958							} else {
40959								rng.setStartAfter(targetCaretNode);
40960							}
40961						} else {
40962							rng.setStart(targetCaretNode, targetCaretNode.data.length);
40963						}
40964
40965						if (caretNode.nodeType == 1) {
40966							rng.setEnd(caretNode, 0);
40967						} else {
40968							rng.setEndBefore(caretNode);
40969						}
40970					} else {
40971						if (!isSiblingsIgnoreWhiteSpace(textBlock, targetTextBlock)) {
40972							return rng;
40973						}
40974
40975						if (caretNode.nodeType == 1) {
40976							if (caretNode.nodeName == "BR") {
40977								rng.setStartBefore(caretNode);
40978							} else {
40979								rng.setStartAfter(caretNode);
40980							}
40981						} else {
40982							rng.setStart(caretNode, caretNode.data.length);
40983						}
40984
40985						if (targetCaretNode.nodeType == 1) {
40986							rng.setEnd(targetCaretNode, 0);
40987						} else {
40988							rng.setEndBefore(targetCaretNode);
40989						}
40990					}
40991				}
40992
40993				return rng;
40994			}
40995
40996			function handleTextBlockMergeDelete(isForward) {
40997				var rng = selection.getRng();
40998
40999				rng = expandBetweenBlocks(rng, isForward);
41000
41001				if (deleteRangeBetweenTextBlocks(rng)) {
41002					return true;
41003				}
41004			}
41005
41006			/**
41007			 * This retains the formatting if the last character is to be deleted.
41008			 *
41009			 * Backspace on this: <p><b><i>a|</i></b></p> would become <p>|</p> in WebKit.
41010			 * With this patch: <p><b><i>|<br></i></b></p>
41011			 */
41012			function handleLastBlockCharacterDelete(isForward, rng) {
41013				var path, blockElm, newBlockElm, clonedBlockElm, sibling,
41014					container, offset, br, currentFormatNodes;
41015
41016				function cloneTextBlockWithFormats(blockElm, node) {
41017					currentFormatNodes = $(node).parents().filter(function(idx, node) {
41018						return !!editor.schema.getTextInlineElements()[node.nodeName];
41019					});
41020
41021					newBlockElm = blockElm.cloneNode(false);
41022
41023					currentFormatNodes = Tools.map(currentFormatNodes, function(formatNode) {
41024						formatNode = formatNode.cloneNode(false);
41025
41026						if (newBlockElm.hasChildNodes()) {
41027							formatNode.appendChild(newBlockElm.firstChild);
41028							newBlockElm.appendChild(formatNode);
41029						} else {
41030							newBlockElm.appendChild(formatNode);
41031						}
41032
41033						newBlockElm.appendChild(formatNode);
41034
41035						return formatNode;
41036					});
41037
41038					if (currentFormatNodes.length) {
41039						br = dom.create('br');
41040						currentFormatNodes[0].appendChild(br);
41041						dom.replace(newBlockElm, blockElm);
41042
41043						rng.setStartBefore(br);
41044						rng.setEndBefore(br);
41045						editor.selection.setRng(rng);
41046
41047						return br;
41048					}
41049
41050					return null;
41051				}
41052
41053				function isTextBlock(node) {
41054					return node && editor.schema.getTextBlockElements()[node.tagName];
41055				}
41056
41057				if (!rng.collapsed) {
41058					return;
41059				}
41060
41061				container = rng.startContainer;
41062				offset = rng.startOffset;
41063				blockElm = dom.getParent(container, dom.isBlock);
41064				if (!isTextBlock(blockElm)) {
41065					return;
41066				}
41067
41068				if (container.nodeType == 1) {
41069					container = container.childNodes[offset];
41070					if (container && container.tagName != 'BR') {
41071						return;
41072					}
41073
41074					if (isForward) {
41075						sibling = blockElm.nextSibling;
41076					} else {
41077						sibling = blockElm.previousSibling;
41078					}
41079
41080					if (dom.isEmpty(blockElm) && isTextBlock(sibling) && dom.isEmpty(sibling)) {
41081						if (cloneTextBlockWithFormats(blockElm, container)) {
41082							dom.remove(sibling);
41083							return true;
41084						}
41085					}
41086				} else if (container.nodeType == 3) {
41087					path = NodePath.create(blockElm, container);
41088					clonedBlockElm = blockElm.cloneNode(true);
41089					container = NodePath.resolve(clonedBlockElm, path);
41090
41091					if (isForward) {
41092						if (offset >= container.data.length) {
41093							return;
41094						}
41095
41096						container.deleteData(offset, 1);
41097					} else {
41098						if (offset <= 0) {
41099							return;
41100						}
41101
41102						container.deleteData(offset - 1, 1);
41103					}
41104
41105					if (dom.isEmpty(clonedBlockElm)) {
41106						return cloneTextBlockWithFormats(blockElm, container);
41107					}
41108				}
41109			}
41110
41111			function customDelete(isForward) {
41112				var mutationObserver, rng, caretElement;
41113
41114				if (handleTextBlockMergeDelete(isForward)) {
41115					return;
41116				}
41117
41118				Tools.each(editor.getBody().getElementsByTagName('*'), function(elm) {
41119					// Mark existing spans
41120					if (elm.tagName == 'SPAN') {
41121						elm.setAttribute('mce-data-marked', 1);
41122					}
41123
41124					// Make sure all elements has a data-mce-style attribute
41125					if (!elm.hasAttribute('data-mce-style') && elm.hasAttribute('style')) {
41126						editor.dom.setAttrib(elm, 'style', editor.dom.getAttrib(elm, 'style'));
41127					}
41128				});
41129
41130				// Observe added nodes and style attribute changes
41131				mutationObserver = new MutationObserver(function() {});
41132				mutationObserver.observe(editor.getDoc(), {
41133					childList: true,
41134					attributes: true,
41135					subtree: true,
41136					attributeFilter: ['style']
41137				});
41138
41139				editor.getDoc().execCommand(isForward ? 'ForwardDelete' : 'Delete', false, null);
41140
41141				rng = editor.selection.getRng();
41142				caretElement = rng.startContainer.parentNode;
41143
41144				Tools.each(mutationObserver.takeRecords(), function(record) {
41145					if (!dom.isChildOf(record.target, editor.getBody())) {
41146						return;
41147					}
41148
41149					// Restore style attribute to previous value
41150					if (record.attributeName == "style") {
41151						var oldValue = record.target.getAttribute('data-mce-style');
41152
41153						if (oldValue) {
41154							record.target.setAttribute("style", oldValue);
41155						} else {
41156							record.target.removeAttribute("style");
41157						}
41158					}
41159
41160					// Remove all spans that aren't marked and retain selection
41161					Tools.each(record.addedNodes, function(node) {
41162						if (node.nodeName == "SPAN" && !node.getAttribute('mce-data-marked')) {
41163							var offset, container;
41164
41165							if (node == caretElement) {
41166								offset = rng.startOffset;
41167								container = node.firstChild;
41168							}
41169
41170							dom.remove(node, true);
41171
41172							if (container) {
41173								rng.setStart(container, offset);
41174								rng.setEnd(container, offset);
41175								editor.selection.setRng(rng);
41176							}
41177						}
41178					});
41179				});
41180
41181				mutationObserver.disconnect();
41182
41183				// Remove any left over marks
41184				Tools.each(editor.dom.select('span[mce-data-marked]'), function(span) {
41185					span.removeAttribute('mce-data-marked');
41186				});
41187			}
41188
41189			editor.on('keydown', function(e) {
41190				var isForward = e.keyCode == DELETE, isMetaOrCtrl = e.ctrlKey || e.metaKey;
41191
41192				if (!isDefaultPrevented(e) && (isForward || e.keyCode == BACKSPACE)) {
41193					var rng = editor.selection.getRng(), container = rng.startContainer, offset = rng.startOffset;
41194
41195					// Shift+Delete is cut
41196					if (isForward && e.shiftKey) {
41197						return;
41198					}
41199
41200					if (handleLastBlockCharacterDelete(isForward, rng)) {
41201						e.preventDefault();
41202						return;
41203					}
41204
41205					// Ignore non meta delete in the where there is text before/after the caret
41206					if (!isMetaOrCtrl && rng.collapsed && container.nodeType == 3) {
41207						if (isForward ? offset < container.data.length : offset > 0) {
41208							return;
41209						}
41210					}
41211
41212					e.preventDefault();
41213
41214					if (isMetaOrCtrl) {
41215						editor.selection.getSel().modify("extend", isForward ? "forward" : "backward", e.metaKey ? "lineboundary" : "word");
41216					}
41217
41218					customDelete(isForward);
41219				}
41220			});
41221
41222			// Handle case where text is deleted by typing over
41223			editor.on('keypress', function(e) {
41224				if (!isDefaultPrevented(e) && !selection.isCollapsed() && e.charCode > 31 && !VK.metaKeyPressed(e)) {
41225					var rng, currentFormatNodes, fragmentNode, blockParent, caretNode, charText;
41226
41227					rng = editor.selection.getRng();
41228					charText = String.fromCharCode(e.charCode);
41229					e.preventDefault();
41230
41231					// Keep track of current format nodes
41232					currentFormatNodes = $(rng.startContainer).parents().filter(function(idx, node) {
41233						return !!editor.schema.getTextInlineElements()[node.nodeName];
41234					});
41235
41236					customDelete(true);
41237
41238					// Check if the browser removed them
41239					currentFormatNodes = currentFormatNodes.filter(function(idx, node) {
41240						return !$.contains(editor.getBody(), node);
41241					});
41242
41243					// Then re-add them
41244					if (currentFormatNodes.length) {
41245						fragmentNode = dom.createFragment();
41246
41247						currentFormatNodes.each(function(idx, formatNode) {
41248							formatNode = formatNode.cloneNode(false);
41249
41250							if (fragmentNode.hasChildNodes()) {
41251								formatNode.appendChild(fragmentNode.firstChild);
41252								fragmentNode.appendChild(formatNode);
41253							} else {
41254								caretNode = formatNode;
41255								fragmentNode.appendChild(formatNode);
41256							}
41257
41258							fragmentNode.appendChild(formatNode);
41259						});
41260
41261						caretNode.appendChild(editor.getDoc().createTextNode(charText));
41262
41263						// Prevent edge case where older WebKit would add an extra BR element
41264						blockParent = dom.getParent(rng.startContainer, dom.isBlock);
41265						if (dom.isEmpty(blockParent)) {
41266							$(blockParent).empty().append(fragmentNode);
41267						} else {
41268							rng.insertNode(fragmentNode);
41269						}
41270
41271						rng.setStart(caretNode.firstChild, 1);
41272						rng.setEnd(caretNode.firstChild, 1);
41273						editor.selection.setRng(rng);
41274					} else {
41275						editor.selection.setContent(charText);
41276					}
41277				}
41278			});
41279
41280			editor.addCommand('Delete', function() {
41281				customDelete();
41282			});
41283
41284			editor.addCommand('ForwardDelete', function() {
41285				customDelete(true);
41286			});
41287
41288			// Older WebKits doesn't properly handle the clipboard so we can't add the rest
41289			if (olderWebKit) {
41290				return;
41291			}
41292
41293			editor.on('dragstart', function(e) {
41294				dragStartRng = selection.getRng();
41295				setMceInteralContent(e);
41296			});
41297
41298			editor.on('drop', function(e) {
41299				if (!isDefaultPrevented(e)) {
41300					var internalContent = getMceInternalContent(e);
41301
41302					if (internalContent) {
41303						e.preventDefault();
41304
41305						// Safari has a weird issue where drag/dropping images sometimes
41306						// produces a green plus icon. When this happens the caretRangeFromPoint
41307						// will return "null" even though the x, y coordinate is correct.
41308						// But if we detach the insert from the drop event we will get a proper range
41309						Delay.setEditorTimeout(editor, function() {
41310							var pointRng = RangeUtils.getCaretRangeFromPoint(e.x, e.y, doc);
41311
41312							if (dragStartRng) {
41313								selection.setRng(dragStartRng);
41314								dragStartRng = null;
41315							}
41316
41317							customDelete();
41318							selection.setRng(pointRng);
41319							insertClipboardContents(internalContent.html);
41320						});
41321					}
41322				}
41323			});
41324
41325			editor.on('cut', function(e) {
41326				if (!isDefaultPrevented(e) && e.clipboardData && !editor.selection.isCollapsed()) {
41327					e.preventDefault();
41328					e.clipboardData.clearData();
41329					e.clipboardData.setData('text/html', editor.selection.getContent());
41330					e.clipboardData.setData('text/plain', editor.selection.getContent({format: 'text'}));
41331
41332					// Needed delay for https://code.google.com/p/chromium/issues/detail?id=363288#c3
41333					// Nested delete/forwardDelete not allowed on execCommand("cut")
41334					// This is ugly but not sure how to work around it otherwise
41335					Delay.setEditorTimeout(editor, function() {
41336						customDelete(true);
41337					});
41338				}
41339			});
41340		}
41341
41342		/**
41343		 * Makes sure that the editor body becomes empty when backspace or delete is pressed in empty editors.
41344		 *
41345		 * For example:
41346		 * <p><b>|</b></p>
41347		 *
41348		 * Or:
41349		 * <h1>|</h1>
41350		 *
41351		 * Or:
41352		 * [<h1></h1>]
41353		 */
41354		function emptyEditorWhenDeleting() {
41355			function serializeRng(rng) {
41356				var body = dom.create("body");
41357				var contents = rng.cloneContents();
41358				body.appendChild(contents);
41359				return selection.serializer.serialize(body, {format: 'html'});
41360			}
41361
41362			function allContentsSelected(rng) {
41363				if (!rng.setStart) {
41364					if (rng.item) {
41365						return false;
41366					}
41367
41368					var bodyRng = rng.duplicate();
41369					bodyRng.moveToElementText(editor.getBody());
41370					return RangeUtils.compareRanges(rng, bodyRng);
41371				}
41372
41373				var selection = serializeRng(rng);
41374
41375				var allRng = dom.createRng();
41376				allRng.selectNode(editor.getBody());
41377
41378				var allSelection = serializeRng(allRng);
41379				return selection === allSelection;
41380			}
41381
41382			editor.on('keydown', function(e) {
41383				var keyCode = e.keyCode, isCollapsed, body;
41384
41385				// Empty the editor if it's needed for example backspace at <p><b>|</b></p>
41386				if (!isDefaultPrevented(e) && (keyCode == DELETE || keyCode == BACKSPACE)) {
41387					isCollapsed = editor.selection.isCollapsed();
41388					body = editor.getBody();
41389
41390					// Selection is collapsed but the editor isn't empty
41391					if (isCollapsed && !dom.isEmpty(body)) {
41392						return;
41393					}
41394
41395					// Selection isn't collapsed but not all the contents is selected
41396					if (!isCollapsed && !allContentsSelected(editor.selection.getRng())) {
41397						return;
41398					}
41399
41400					// Manually empty the editor
41401					e.preventDefault();
41402					editor.setContent('');
41403
41404					if (body.firstChild && dom.isBlock(body.firstChild)) {
41405						editor.selection.setCursorLocation(body.firstChild, 0);
41406					} else {
41407						editor.selection.setCursorLocation(body, 0);
41408					}
41409
41410					editor.nodeChanged();
41411				}
41412			});
41413		}
41414
41415		/**
41416		 * WebKit doesn't select all the nodes in the body when you press Ctrl+A.
41417		 * IE selects more than the contents <body>[<p>a</p>]</body> instead of <body><p>[a]</p]</body> see bug #6438
41418		 * This selects the whole body so that backspace/delete logic will delete everything
41419		 */
41420		function selectAll() {
41421			editor.shortcuts.add('meta+a', null, 'SelectAll');
41422		}
41423
41424		/**
41425		 * WebKit has a weird issue where it some times fails to properly convert keypresses to input method keystrokes.
41426		 * The IME on Mac doesn't initialize when it doesn't fire a proper focus event.
41427		 *
41428		 * This seems to happen when the user manages to click the documentElement element then the window doesn't get proper focus until
41429		 * you enter a character into the editor.
41430		 *
41431		 * It also happens when the first focus in made to the body.
41432		 *
41433		 * See: https://bugs.webkit.org/show_bug.cgi?id=83566
41434		 */
41435		function inputMethodFocus() {
41436			if (!editor.settings.content_editable) {
41437				// Case 1 IME doesn't initialize if you focus the document
41438				// Disabled since it was interferring with the cE=false logic
41439				// Also coultn't reproduce the issue on Safari 9
41440				/*dom.bind(editor.getDoc(), 'focusin', function() {
41441					selection.setRng(selection.getRng());
41442				});*/
41443
41444				// Case 2 IME doesn't initialize if you click the documentElement it also doesn't properly fire the focusin event
41445				// Needs to be both down/up due to weird rendering bug on Chrome Windows
41446				dom.bind(editor.getDoc(), 'mousedown mouseup', function(e) {
41447					var rng;
41448
41449					if (e.target == editor.getDoc().documentElement) {
41450						rng = selection.getRng();
41451						editor.getBody().focus();
41452
41453						if (e.type == 'mousedown') {
41454							if (CaretContainer.isCaretContainer(rng.startContainer)) {
41455								return;
41456							}
41457
41458							// Edge case for mousedown, drag select and mousedown again within selection on Chrome Windows to render caret
41459							selection.placeCaretAt(e.clientX, e.clientY);
41460						} else {
41461							selection.setRng(rng);
41462						}
41463					}
41464				});
41465			}
41466		}
41467
41468		/**
41469		 * Backspacing in FireFox/IE from a paragraph into a horizontal rule results in a floating text node because the
41470		 * browser just deletes the paragraph - the browser fails to merge the text node with a horizontal rule so it is
41471		 * left there. TinyMCE sees a floating text node and wraps it in a paragraph on the key up event (ForceBlocks.js
41472		 * addRootBlocks), meaning the action does nothing. With this code, FireFox/IE matche the behaviour of other
41473		 * browsers.
41474		 *
41475		 * It also fixes a bug on Firefox where it's impossible to delete HR elements.
41476		 */
41477		function removeHrOnBackspace() {
41478			editor.on('keydown', function(e) {
41479				if (!isDefaultPrevented(e) && e.keyCode === BACKSPACE) {
41480					// Check if there is any HR elements this is faster since getRng on IE 7 & 8 is slow
41481					if (!editor.getBody().getElementsByTagName('hr').length) {
41482						return;
41483					}
41484
41485					if (selection.isCollapsed() && selection.getRng(true).startOffset === 0) {
41486						var node = selection.getNode();
41487						var previousSibling = node.previousSibling;
41488
41489						if (node.nodeName == 'HR') {
41490							dom.remove(node);
41491							e.preventDefault();
41492							return;
41493						}
41494
41495						if (previousSibling && previousSibling.nodeName && previousSibling.nodeName.toLowerCase() === "hr") {
41496							dom.remove(previousSibling);
41497							e.preventDefault();
41498						}
41499					}
41500				}
41501			});
41502		}
41503
41504		/**
41505		 * Firefox 3.x has an issue where the body element won't get proper focus if you click out
41506		 * side it's rectangle.
41507		 */
41508		function focusBody() {
41509			// Fix for a focus bug in FF 3.x where the body element
41510			// wouldn't get proper focus if the user clicked on the HTML element
41511			if (!window.Range.prototype.getClientRects) { // Detect getClientRects got introduced in FF 4
41512				editor.on('mousedown', function(e) {
41513					if (!isDefaultPrevented(e) && e.target.nodeName === "HTML") {
41514						var body = editor.getBody();
41515
41516						// Blur the body it's focused but not correctly focused
41517						body.blur();
41518
41519						// Refocus the body after a little while
41520						Delay.setEditorTimeout(editor, function() {
41521							body.focus();
41522						});
41523					}
41524				});
41525			}
41526		}
41527
41528		/**
41529		 * WebKit has a bug where it isn't possible to select image, hr or anchor elements
41530		 * by clicking on them so we need to fake that.
41531		 */
41532		function selectControlElements() {
41533			editor.on('click', function(e) {
41534				var target = e.target;
41535
41536				// Workaround for bug, http://bugs.webkit.org/show_bug.cgi?id=12250
41537				// WebKit can't even do simple things like selecting an image
41538				// Needs to be the setBaseAndExtend or it will fail to select floated images
41539				if (/^(IMG|HR)$/.test(target.nodeName) && dom.getContentEditableParent(target) !== "false") {
41540					e.preventDefault();
41541					selection.getSel().setBaseAndExtent(target, 0, target, 1);
41542					editor.nodeChanged();
41543				}
41544
41545				if (target.nodeName == 'A' && dom.hasClass(target, 'mce-item-anchor')) {
41546					e.preventDefault();
41547					selection.select(target);
41548				}
41549			});
41550		}
41551
41552		/**
41553		 * Fixes a Gecko bug where the style attribute gets added to the wrong element when deleting between two block elements.
41554		 *
41555		 * Fixes do backspace/delete on this:
41556		 * <p>bla[ck</p><p style="color:red">r]ed</p>
41557		 *
41558		 * Would become:
41559		 * <p>bla|ed</p>
41560		 *
41561		 * Instead of:
41562		 * <p style="color:red">bla|ed</p>
41563		 */
41564		function removeStylesWhenDeletingAcrossBlockElements() {
41565			function getAttributeApplyFunction() {
41566				var template = dom.getAttribs(selection.getStart().cloneNode(false));
41567
41568				return function() {
41569					var target = selection.getStart();
41570
41571					if (target !== editor.getBody()) {
41572						dom.setAttrib(target, "style", null);
41573
41574						each(template, function(attr) {
41575							target.setAttributeNode(attr.cloneNode(true));
41576						});
41577					}
41578				};
41579			}
41580
41581			function isSelectionAcrossElements() {
41582				return !selection.isCollapsed() &&
41583					dom.getParent(selection.getStart(), dom.isBlock) != dom.getParent(selection.getEnd(), dom.isBlock);
41584			}
41585
41586			editor.on('keypress', function(e) {
41587				var applyAttributes;
41588
41589				if (!isDefaultPrevented(e) && (e.keyCode == 8 || e.keyCode == 46) && isSelectionAcrossElements()) {
41590					applyAttributes = getAttributeApplyFunction();
41591					editor.getDoc().execCommand('delete', false, null);
41592					applyAttributes();
41593					e.preventDefault();
41594					return false;
41595				}
41596			});
41597
41598			dom.bind(editor.getDoc(), 'cut', function(e) {
41599				var applyAttributes;
41600
41601				if (!isDefaultPrevented(e) && isSelectionAcrossElements()) {
41602					applyAttributes = getAttributeApplyFunction();
41603
41604					Delay.setEditorTimeout(editor, function() {
41605						applyAttributes();
41606					});
41607				}
41608			});
41609		}
41610
41611		/**
41612		 * Screen readers on IE needs to have the role application set on the body.
41613		 */
41614		function ensureBodyHasRoleApplication() {
41615			document.body.setAttribute("role", "application");
41616		}
41617
41618		/**
41619		 * Backspacing into a table behaves differently depending upon browser type.
41620		 * Therefore, disable Backspace when cursor immediately follows a table.
41621		 */
41622		function disableBackspaceIntoATable() {
41623			editor.on('keydown', function(e) {
41624				if (!isDefaultPrevented(e) && e.keyCode === BACKSPACE) {
41625					if (selection.isCollapsed() && selection.getRng(true).startOffset === 0) {
41626						var previousSibling = selection.getNode().previousSibling;
41627						if (previousSibling && previousSibling.nodeName && previousSibling.nodeName.toLowerCase() === "table") {
41628							e.preventDefault();
41629							return false;
41630						}
41631					}
41632				}
41633			});
41634		}
41635
41636		/**
41637		 * Old IE versions can't properly render BR elements in PRE tags white in contentEditable mode. So this
41638		 * logic adds a \n before the BR so that it will get rendered.
41639		 */
41640		function addNewLinesBeforeBrInPre() {
41641			// IE8+ rendering mode does the right thing with BR in PRE
41642			if (getDocumentMode() > 7) {
41643				return;
41644			}
41645
41646			// Enable display: none in area and add a specific class that hides all BR elements in PRE to
41647			// avoid the caret from getting stuck at the BR elements while pressing the right arrow key
41648			setEditorCommandState('RespectVisibilityInDesign', true);
41649			editor.contentStyles.push('.mceHideBrInPre pre br {display: none}');
41650			dom.addClass(editor.getBody(), 'mceHideBrInPre');
41651
41652			// Adds a \n before all BR elements in PRE to get them visual
41653			parser.addNodeFilter('pre', function(nodes) {
41654				var i = nodes.length, brNodes, j, brElm, sibling;
41655
41656				while (i--) {
41657					brNodes = nodes[i].getAll('br');
41658					j = brNodes.length;
41659					while (j--) {
41660						brElm = brNodes[j];
41661
41662						// Add \n before BR in PRE elements on older IE:s so the new lines get rendered
41663						sibling = brElm.prev;
41664						if (sibling && sibling.type === 3 && sibling.value.charAt(sibling.value - 1) != '\n') {
41665							sibling.value += '\n';
41666						} else {
41667							brElm.parent.insert(new Node('#text', 3), brElm, true).value = '\n';
41668						}
41669					}
41670				}
41671			});
41672
41673			// Removes any \n before BR elements in PRE since other browsers and in contentEditable=false mode they will be visible
41674			serializer.addNodeFilter('pre', function(nodes) {
41675				var i = nodes.length, brNodes, j, brElm, sibling;
41676
41677				while (i--) {
41678					brNodes = nodes[i].getAll('br');
41679					j = brNodes.length;
41680					while (j--) {
41681						brElm = brNodes[j];
41682						sibling = brElm.prev;
41683						if (sibling && sibling.type == 3) {
41684							sibling.value = sibling.value.replace(/\r?\n$/, '');
41685						}
41686					}
41687				}
41688			});
41689		}
41690
41691		/**
41692		 * Moves style width/height to attribute width/height when the user resizes an image on IE.
41693		 */
41694		function removePreSerializedStylesWhenSelectingControls() {
41695			dom.bind(editor.getBody(), 'mouseup', function() {
41696				var value, node = selection.getNode();
41697
41698				// Moved styles to attributes on IMG eements
41699				if (node.nodeName == 'IMG') {
41700					// Convert style width to width attribute
41701					if ((value = dom.getStyle(node, 'width'))) {
41702						dom.setAttrib(node, 'width', value.replace(/[^0-9%]+/g, ''));
41703						dom.setStyle(node, 'width', '');
41704					}
41705
41706					// Convert style height to height attribute
41707					if ((value = dom.getStyle(node, 'height'))) {
41708						dom.setAttrib(node, 'height', value.replace(/[^0-9%]+/g, ''));
41709						dom.setStyle(node, 'height', '');
41710					}
41711				}
41712			});
41713		}
41714
41715		/**
41716		 * Removes a blockquote when backspace is pressed at the beginning of it.
41717		 *
41718		 * For example:
41719		 * <blockquote><p>|x</p></blockquote>
41720		 *
41721		 * Becomes:
41722		 * <p>|x</p>
41723		 */
41724		function removeBlockQuoteOnBackSpace() {
41725			// Add block quote deletion handler
41726			editor.on('keydown', function(e) {
41727				var rng, container, offset, root, parent;
41728
41729				if (isDefaultPrevented(e) || e.keyCode != VK.BACKSPACE) {
41730					return;
41731				}
41732
41733				rng = selection.getRng();
41734				container = rng.startContainer;
41735				offset = rng.startOffset;
41736				root = dom.getRoot();
41737				parent = container;
41738
41739				if (!rng.collapsed || offset !== 0) {
41740					return;
41741				}
41742
41743				while (parent && parent.parentNode && parent.parentNode.firstChild == parent && parent.parentNode != root) {
41744					parent = parent.parentNode;
41745				}
41746
41747				// Is the cursor at the beginning of a blockquote?
41748				if (parent.tagName === 'BLOCKQUOTE') {
41749					// Remove the blockquote
41750					editor.formatter.toggle('blockquote', null, parent);
41751
41752					// Move the caret to the beginning of container
41753					rng = dom.createRng();
41754					rng.setStart(container, 0);
41755					rng.setEnd(container, 0);
41756					selection.setRng(rng);
41757				}
41758			});
41759		}
41760
41761		/**
41762		 * Sets various Gecko editing options on mouse down and before a execCommand to disable inline table editing that is broken etc.
41763		 */
41764		function setGeckoEditingOptions() {
41765			function setOpts() {
41766				editor._refreshContentEditable();
41767
41768				setEditorCommandState("StyleWithCSS", false);
41769				setEditorCommandState("enableInlineTableEditing", false);
41770
41771				if (!settings.object_resizing) {
41772					setEditorCommandState("enableObjectResizing", false);
41773				}
41774			}
41775
41776			if (!settings.readonly) {
41777				editor.on('BeforeExecCommand MouseDown', setOpts);
41778			}
41779		}
41780
41781		/**
41782		 * Fixes a gecko link bug, when a link is placed at the end of block elements there is
41783		 * no way to move the caret behind the link. This fix adds a bogus br element after the link.
41784		 *
41785		 * For example this:
41786		 * <p><b><a href="#">x</a></b></p>
41787		 *
41788		 * Becomes this:
41789		 * <p><b><a href="#">x</a></b><br></p>
41790		 */
41791		function addBrAfterLastLinks() {
41792			function fixLinks() {
41793				each(dom.select('a'), function(node) {
41794					var parentNode = node.parentNode, root = dom.getRoot();
41795
41796					if (parentNode.lastChild === node) {
41797						while (parentNode && !dom.isBlock(parentNode)) {
41798							if (parentNode.parentNode.lastChild !== parentNode || parentNode === root) {
41799								return;
41800							}
41801
41802							parentNode = parentNode.parentNode;
41803						}
41804
41805						dom.add(parentNode, 'br', {'data-mce-bogus': 1});
41806					}
41807				});
41808			}
41809
41810			editor.on('SetContent ExecCommand', function(e) {
41811				if (e.type == "setcontent" || e.command === 'mceInsertLink') {
41812					fixLinks();
41813				}
41814			});
41815		}
41816
41817		/**
41818		 * WebKit will produce DIV elements here and there by default. But since TinyMCE uses paragraphs by
41819		 * default we want to change that behavior.
41820		 */
41821		function setDefaultBlockType() {
41822			if (settings.forced_root_block) {
41823				editor.on('init', function() {
41824					setEditorCommandState('DefaultParagraphSeparator', settings.forced_root_block);
41825				});
41826			}
41827		}
41828
41829		/**
41830		 * Deletes the selected image on IE instead of navigating to previous page.
41831		 */
41832		function deleteControlItemOnBackSpace() {
41833			editor.on('keydown', function(e) {
41834				var rng;
41835
41836				if (!isDefaultPrevented(e) && e.keyCode == BACKSPACE) {
41837					rng = editor.getDoc().selection.createRange();
41838					if (rng && rng.item) {
41839						e.preventDefault();
41840						editor.undoManager.beforeChange();
41841						dom.remove(rng.item(0));
41842						editor.undoManager.add();
41843					}
41844				}
41845			});
41846		}
41847
41848		/**
41849		 * IE10 doesn't properly render block elements with the right height until you add contents to them.
41850		 * This fixes that by adding a padding-right to all empty text block elements.
41851		 * See: https://connect.microsoft.com/IE/feedback/details/743881
41852		 */
41853		function renderEmptyBlocksFix() {
41854			var emptyBlocksCSS;
41855
41856			// IE10+
41857			if (getDocumentMode() >= 10) {
41858				emptyBlocksCSS = '';
41859				each('p div h1 h2 h3 h4 h5 h6'.split(' '), function(name, i) {
41860					emptyBlocksCSS += (i > 0 ? ',' : '') + name + ':empty';
41861				});
41862
41863				editor.contentStyles.push(emptyBlocksCSS + '{padding-right: 1px !important}');
41864			}
41865		}
41866
41867		/**
41868		 * Old IE versions can't retain contents within noscript elements so this logic will store the contents
41869		 * as a attribute and the insert that value as it's raw text when the DOM is serialized.
41870		 */
41871		function keepNoScriptContents() {
41872			if (getDocumentMode() < 9) {
41873				parser.addNodeFilter('noscript', function(nodes) {
41874					var i = nodes.length, node, textNode;
41875
41876					while (i--) {
41877						node = nodes[i];
41878						textNode = node.firstChild;
41879
41880						if (textNode) {
41881							node.attr('data-mce-innertext', textNode.value);
41882						}
41883					}
41884				});
41885
41886				serializer.addNodeFilter('noscript', function(nodes) {
41887					var i = nodes.length, node, textNode, value;
41888
41889					while (i--) {
41890						node = nodes[i];
41891						textNode = nodes[i].firstChild;
41892
41893						if (textNode) {
41894							textNode.value = Entities.decode(textNode.value);
41895						} else {
41896							// Old IE can't retain noscript value so an attribute is used to store it
41897							value = node.attributes.map['data-mce-innertext'];
41898							if (value) {
41899								node.attr('data-mce-innertext', null);
41900								textNode = new Node('#text', 3);
41901								textNode.value = value;
41902								textNode.raw = true;
41903								node.append(textNode);
41904							}
41905						}
41906					}
41907				});
41908			}
41909		}
41910
41911		/**
41912		 * IE has an issue where you can't select/move the caret by clicking outside the body if the document is in standards mode.
41913		 */
41914		function fixCaretSelectionOfDocumentElementOnIe() {
41915			var doc = dom.doc, body = doc.body, started, startRng, htmlElm;
41916
41917			// Return range from point or null if it failed
41918			function rngFromPoint(x, y) {
41919				var rng = body.createTextRange();
41920
41921				try {
41922					rng.moveToPoint(x, y);
41923				} catch (ex) {
41924					// IE sometimes throws and exception, so lets just ignore it
41925					rng = null;
41926				}
41927
41928				return rng;
41929			}
41930
41931			// Fires while the selection is changing
41932			function selectionChange(e) {
41933				var pointRng;
41934
41935				// Check if the button is down or not
41936				if (e.button) {
41937					// Create range from mouse position
41938					pointRng = rngFromPoint(e.x, e.y);
41939
41940					if (pointRng) {
41941						// Check if pointRange is before/after selection then change the endPoint
41942						if (pointRng.compareEndPoints('StartToStart', startRng) > 0) {
41943							pointRng.setEndPoint('StartToStart', startRng);
41944						} else {
41945							pointRng.setEndPoint('EndToEnd', startRng);
41946						}
41947
41948						pointRng.select();
41949					}
41950				} else {
41951					endSelection();
41952				}
41953			}
41954
41955			// Removes listeners
41956			function endSelection() {
41957				var rng = doc.selection.createRange();
41958
41959				// If the range is collapsed then use the last start range
41960				if (startRng && !rng.item && rng.compareEndPoints('StartToEnd', rng) === 0) {
41961					startRng.select();
41962				}
41963
41964				dom.unbind(doc, 'mouseup', endSelection);
41965				dom.unbind(doc, 'mousemove', selectionChange);
41966				startRng = started = 0;
41967			}
41968
41969			// Make HTML element unselectable since we are going to handle selection by hand
41970			doc.documentElement.unselectable = true;
41971
41972			// Detect when user selects outside BODY
41973			dom.bind(doc, 'mousedown contextmenu', function(e) {
41974				if (e.target.nodeName === 'HTML') {
41975					if (started) {
41976						endSelection();
41977					}
41978
41979					// Detect vertical scrollbar, since IE will fire a mousedown on the scrollbar and have target set as HTML
41980					htmlElm = doc.documentElement;
41981					if (htmlElm.scrollHeight > htmlElm.clientHeight) {
41982						return;
41983					}
41984
41985					started = 1;
41986					// Setup start position
41987					startRng = rngFromPoint(e.x, e.y);
41988					if (startRng) {
41989						// Listen for selection change events
41990						dom.bind(doc, 'mouseup', endSelection);
41991						dom.bind(doc, 'mousemove', selectionChange);
41992
41993						dom.getRoot().focus();
41994						startRng.select();
41995					}
41996				}
41997			});
41998		}
41999
42000		/**
42001		 * Fixes selection issues where the caret can be placed between two inline elements like <b>a</b>|<b>b</b>
42002		 * this fix will lean the caret right into the closest inline element.
42003		 */
42004		function normalizeSelection() {
42005			// Normalize selection for example <b>a</b><i>|a</i> becomes <b>a|</b><i>a</i> except for Ctrl+A since it selects everything
42006			editor.on('keyup focusin mouseup', function(e) {
42007				if (e.keyCode != 65 || !VK.metaKeyPressed(e)) {
42008					selection.normalize();
42009				}
42010			}, true);
42011		}
42012
42013		/**
42014		 * Forces Gecko to render a broken image icon if it fails to load an image.
42015		 */
42016		function showBrokenImageIcon() {
42017			editor.contentStyles.push(
42018				'img:-moz-broken {' +
42019					'-moz-force-broken-image-icon:1;' +
42020					'min-width:24px;' +
42021					'min-height:24px' +
42022				'}'
42023			);
42024		}
42025
42026		/**
42027		 * iOS has a bug where it's impossible to type if the document has a touchstart event
42028		 * bound and the user touches the document while having the on screen keyboard visible.
42029		 *
42030		 * The touch event moves the focus to the parent document while having the caret inside the iframe
42031		 * this fix moves the focus back into the iframe document.
42032		 */
42033		function restoreFocusOnKeyDown() {
42034			if (!editor.inline) {
42035				editor.on('keydown', function() {
42036					if (document.activeElement == document.body) {
42037						editor.getWin().focus();
42038					}
42039				});
42040			}
42041		}
42042
42043		/**
42044		 * IE 11 has an annoying issue where you can't move focus into the editor
42045		 * by clicking on the white area HTML element. We used to be able to to fix this with
42046		 * the fixCaretSelectionOfDocumentElementOnIe fix. But since M$ removed the selection
42047		 * object it's not possible anymore. So we need to hack in a ungly CSS to force the
42048		 * body to be at least 150px. If the user clicks the HTML element out side this 150px region
42049		 * we simply move the focus into the first paragraph. Not ideal since you loose the
42050		 * positioning of the caret but goot enough for most cases.
42051		 */
42052		function bodyHeight() {
42053			if (!editor.inline) {
42054				editor.contentStyles.push('body {min-height: 150px}');
42055				editor.on('click', function(e) {
42056					var rng;
42057
42058					if (e.target.nodeName == 'HTML') {
42059						// Edge seems to only need focus if we set the range
42060						// the caret will become invisible and moved out of the iframe!!
42061						if (Env.ie > 11) {
42062							editor.getBody().focus();
42063							return;
42064						}
42065
42066						// Need to store away non collapsed ranges since the focus call will mess that up see #7382
42067						rng = editor.selection.getRng();
42068						editor.getBody().focus();
42069						editor.selection.setRng(rng);
42070						editor.selection.normalize();
42071						editor.nodeChanged();
42072					}
42073				});
42074			}
42075		}
42076
42077		/**
42078		 * Firefox on Mac OS will move the browser back to the previous page if you press CMD+Left arrow.
42079		 * You might then loose all your work so we need to block that behavior and replace it with our own.
42080		 */
42081		function blockCmdArrowNavigation() {
42082			if (Env.mac) {
42083				editor.on('keydown', function(e) {
42084					if (VK.metaKeyPressed(e) && !e.shiftKey && (e.keyCode == 37 || e.keyCode == 39)) {
42085						e.preventDefault();
42086						editor.selection.getSel().modify('move', e.keyCode == 37 ? 'backward' : 'forward', 'lineboundary');
42087					}
42088				});
42089			}
42090		}
42091
42092		/**
42093		 * Disables the autolinking in IE 9+ this is then re-enabled by the autolink plugin.
42094		 */
42095		function disableAutoUrlDetect() {
42096			setEditorCommandState("AutoUrlDetect", false);
42097		}
42098
42099		/**
42100		 * iOS 7.1 introduced two new bugs:
42101		 * 1) It's possible to open links within a contentEditable area by clicking on them.
42102		 * 2) If you hold down the finger it will display the link/image touch callout menu.
42103		 */
42104		function tapLinksAndImages() {
42105			editor.on('click', function(e) {
42106				var elm = e.target;
42107
42108				do {
42109					if (elm.tagName === 'A') {
42110						e.preventDefault();
42111						return;
42112					}
42113				} while ((elm = elm.parentNode));
42114			});
42115
42116			editor.contentStyles.push('.mce-content-body {-webkit-touch-callout: none}');
42117		}
42118
42119		/**
42120		 * iOS Safari and possible other browsers have a bug where it won't fire
42121		 * a click event when a contentEditable is focused. This function fakes click events
42122		 * by using touchstart/touchend and measuring the time and distance travelled.
42123		 */
42124		/*
42125		function touchClickEvent() {
42126			editor.on('touchstart', function(e) {
42127				var elm, time, startTouch, changedTouches;
42128
42129				elm = e.target;
42130				time = new Date().getTime();
42131				changedTouches = e.changedTouches;
42132
42133				if (!changedTouches || changedTouches.length > 1) {
42134					return;
42135				}
42136
42137				startTouch = changedTouches[0];
42138
42139				editor.once('touchend', function(e) {
42140					var endTouch = e.changedTouches[0], args;
42141
42142					if (new Date().getTime() - time > 500) {
42143						return;
42144					}
42145
42146					if (Math.abs(startTouch.clientX - endTouch.clientX) > 5) {
42147						return;
42148					}
42149
42150					if (Math.abs(startTouch.clientY - endTouch.clientY) > 5) {
42151						return;
42152					}
42153
42154					args = {
42155						target: elm
42156					};
42157
42158					each('pageX pageY clientX clientY screenX screenY'.split(' '), function(key) {
42159						args[key] = endTouch[key];
42160					});
42161
42162					args = editor.fire('click', args);
42163
42164					if (!args.isDefaultPrevented()) {
42165						// iOS WebKit can't place the caret properly once
42166						// you bind touch events so we need to do this manually
42167						// TODO: Expand to the closest word? Touble tap still works.
42168						editor.selection.placeCaretAt(endTouch.clientX, endTouch.clientY);
42169						editor.nodeChanged();
42170					}
42171				});
42172			});
42173		}
42174		*/
42175
42176		/**
42177		 * WebKit has a bug where it will allow forms to be submitted if they are inside a contentEditable element.
42178		 * For example this: <form><button></form>
42179		 */
42180		function blockFormSubmitInsideEditor() {
42181			editor.on('init', function() {
42182				editor.dom.bind(editor.getBody(), 'submit', function(e) {
42183					e.preventDefault();
42184				});
42185			});
42186		}
42187
42188		/**
42189		 * Sometimes WebKit/Blink generates BR elements with the Apple-interchange-newline class.
42190		 *
42191		 * Scenario:
42192		 *  1) Create a table 2x2.
42193		 *  2) Select and copy cells A2-B2.
42194		 *  3) Paste and it will add BR element to table cell.
42195		 */
42196		function removeAppleInterchangeBrs() {
42197			parser.addNodeFilter('br', function(nodes) {
42198				var i = nodes.length;
42199
42200				while (i--) {
42201					if (nodes[i].attr('class') == 'Apple-interchange-newline') {
42202						nodes[i].remove();
42203					}
42204				}
42205			});
42206		}
42207
42208		/**
42209		 * IE cannot set custom contentType's on drag events, and also does not properly drag/drop between
42210		 * editors. This uses a special data:text/mce-internal URL to pass data when drag/drop between editors.
42211		 */
42212		function ieInternalDragAndDrop() {
42213			editor.on('dragstart', function(e) {
42214				setMceInteralContent(e);
42215			});
42216
42217			editor.on('drop', function(e) {
42218				if (!isDefaultPrevented(e)) {
42219					var internalContent = getMceInternalContent(e);
42220
42221					if (internalContent && internalContent.id != editor.id) {
42222						e.preventDefault();
42223
42224						var rng = RangeUtils.getCaretRangeFromPoint(e.x, e.y, editor.getDoc());
42225						selection.setRng(rng);
42226						insertClipboardContents(internalContent.html);
42227					}
42228				}
42229			});
42230		}
42231
42232		// All browsers
42233		removeBlockQuoteOnBackSpace();
42234		emptyEditorWhenDeleting();
42235		normalizeSelection();
42236
42237		// WebKit
42238		if (isWebKit) {
42239			cleanupStylesWhenDeleting();
42240			inputMethodFocus();
42241			selectControlElements();
42242			setDefaultBlockType();
42243			blockFormSubmitInsideEditor();
42244			disableBackspaceIntoATable();
42245			removeAppleInterchangeBrs();
42246			//touchClickEvent();
42247
42248			// iOS
42249			if (Env.iOS) {
42250				restoreFocusOnKeyDown();
42251				bodyHeight();
42252				tapLinksAndImages();
42253			} else {
42254				selectAll();
42255			}
42256		}
42257
42258		// IE
42259		if (isIE && Env.ie < 11) {
42260			removeHrOnBackspace();
42261			ensureBodyHasRoleApplication();
42262			addNewLinesBeforeBrInPre();
42263			removePreSerializedStylesWhenSelectingControls();
42264			deleteControlItemOnBackSpace();
42265			renderEmptyBlocksFix();
42266			keepNoScriptContents();
42267			fixCaretSelectionOfDocumentElementOnIe();
42268		}
42269
42270		if (Env.ie >= 11) {
42271			bodyHeight();
42272			disableBackspaceIntoATable();
42273		}
42274
42275		if (Env.ie) {
42276			selectAll();
42277			disableAutoUrlDetect();
42278			ieInternalDragAndDrop();
42279		}
42280
42281		// Gecko
42282		if (isGecko) {
42283			removeHrOnBackspace();
42284			focusBody();
42285			removeStylesWhenDeletingAcrossBlockElements();
42286			setGeckoEditingOptions();
42287			addBrAfterLastLinks();
42288			showBrokenImageIcon();
42289			blockCmdArrowNavigation();
42290			disableBackspaceIntoATable();
42291		}
42292	};
42293});
42294
42295// Included from: js/tinymce/classes/EditorObservable.js
42296
42297/**
42298 * EditorObservable.js
42299 *
42300 * Released under LGPL License.
42301 * Copyright (c) 1999-2015 Ephox Corp. All rights reserved
42302 *
42303 * License: http://www.tinymce.com/license
42304 * Contributing: http://www.tinymce.com/contributing
42305 */
42306
42307/**
42308 * This mixin contains the event logic for the tinymce.Editor class.
42309 *
42310 * @mixin tinymce.EditorObservable
42311 * @extends tinymce.util.Observable
42312 */
42313define("tinymce/EditorObservable", [
42314	"tinymce/util/Observable",
42315	"tinymce/dom/DOMUtils",
42316	"tinymce/util/Tools"
42317], function(Observable, DOMUtils, Tools) {
42318	var DOM = DOMUtils.DOM, customEventRootDelegates;
42319
42320	/**
42321	 * Returns the event target so for the specified event. Some events fire
42322	 * only on document, some fire on documentElement etc. This also handles the
42323	 * custom event root setting where it returns that element instead of the body.
42324	 *
42325	 * @private
42326	 * @param {tinymce.Editor} editor Editor instance to get event target from.
42327	 * @param {String} eventName Name of the event for example "click".
42328	 * @return {Element/Document} HTML Element or document target to bind on.
42329	 */
42330	function getEventTarget(editor, eventName) {
42331		if (eventName == 'selectionchange') {
42332			return editor.getDoc();
42333		}
42334
42335		// Need to bind mousedown/mouseup etc to document not body in iframe mode
42336		// Since the user might click on the HTML element not the BODY
42337		if (!editor.inline && /^mouse|click|contextmenu|drop|dragover|dragend/.test(eventName)) {
42338			return editor.getDoc().documentElement;
42339		}
42340
42341		// Bind to event root instead of body if it's defined
42342		if (editor.settings.event_root) {
42343			if (!editor.eventRoot) {
42344				editor.eventRoot = DOM.select(editor.settings.event_root)[0];
42345			}
42346
42347			return editor.eventRoot;
42348		}
42349
42350		return editor.getBody();
42351	}
42352
42353	/**
42354	 * Binds a event delegate for the specified name this delegate will fire
42355	 * the event to the editor dispatcher.
42356	 *
42357	 * @private
42358	 * @param {tinymce.Editor} editor Editor instance to get event target from.
42359	 * @param {String} eventName Name of the event for example "click".
42360	 */
42361	function bindEventDelegate(editor, eventName) {
42362		var eventRootElm = getEventTarget(editor, eventName), delegate;
42363
42364		function isListening(editor) {
42365			return !editor.hidden && !editor.readonly;
42366		}
42367
42368		if (!editor.delegates) {
42369			editor.delegates = {};
42370		}
42371
42372		if (editor.delegates[eventName]) {
42373			return;
42374		}
42375
42376		if (editor.settings.event_root) {
42377			if (!customEventRootDelegates) {
42378				customEventRootDelegates = {};
42379				editor.editorManager.on('removeEditor', function() {
42380					var name;
42381
42382					if (!editor.editorManager.activeEditor) {
42383						if (customEventRootDelegates) {
42384							for (name in customEventRootDelegates) {
42385								editor.dom.unbind(getEventTarget(editor, name));
42386							}
42387
42388							customEventRootDelegates = null;
42389						}
42390					}
42391				});
42392			}
42393
42394			if (customEventRootDelegates[eventName]) {
42395				return;
42396			}
42397
42398			delegate = function(e) {
42399				var target = e.target, editors = editor.editorManager.editors, i = editors.length;
42400
42401				while (i--) {
42402					var body = editors[i].getBody();
42403
42404					if (body === target || DOM.isChildOf(target, body)) {
42405						if (isListening(editors[i])) {
42406							editors[i].fire(eventName, e);
42407						}
42408					}
42409				}
42410			};
42411
42412			customEventRootDelegates[eventName] = delegate;
42413			DOM.bind(eventRootElm, eventName, delegate);
42414		} else {
42415			delegate = function(e) {
42416				if (isListening(editor)) {
42417					editor.fire(eventName, e);
42418				}
42419			};
42420
42421			DOM.bind(eventRootElm, eventName, delegate);
42422			editor.delegates[eventName] = delegate;
42423		}
42424	}
42425
42426	var EditorObservable = {
42427		/**
42428		 * Bind any pending event delegates. This gets executed after the target body/document is created.
42429		 *
42430		 * @private
42431		 */
42432		bindPendingEventDelegates: function() {
42433			var self = this;
42434
42435			Tools.each(self._pendingNativeEvents, function(name) {
42436				bindEventDelegate(self, name);
42437			});
42438		},
42439
42440		/**
42441		 * Toggles a native event on/off this is called by the EventDispatcher when
42442		 * the first native event handler is added and when the last native event handler is removed.
42443		 *
42444		 * @private
42445		 */
42446		toggleNativeEvent: function(name, state) {
42447			var self = this;
42448
42449			// Never bind focus/blur since the FocusManager fakes those
42450			if (name == "focus" || name == "blur") {
42451				return;
42452			}
42453
42454			if (state) {
42455				if (self.initialized) {
42456					bindEventDelegate(self, name);
42457				} else {
42458					if (!self._pendingNativeEvents) {
42459						self._pendingNativeEvents = [name];
42460					} else {
42461						self._pendingNativeEvents.push(name);
42462					}
42463				}
42464			} else if (self.initialized) {
42465				self.dom.unbind(getEventTarget(self, name), name, self.delegates[name]);
42466				delete self.delegates[name];
42467			}
42468		},
42469
42470		/**
42471		 * Unbinds all native event handlers that means delegates, custom events bound using the Events API etc.
42472		 *
42473		 * @private
42474		 */
42475		unbindAllNativeEvents: function() {
42476			var self = this, name;
42477
42478			if (self.delegates) {
42479				for (name in self.delegates) {
42480					self.dom.unbind(getEventTarget(self, name), name, self.delegates[name]);
42481				}
42482
42483				delete self.delegates;
42484			}
42485
42486			if (!self.inline) {
42487				self.getBody().onload = null;
42488				self.dom.unbind(self.getWin());
42489				self.dom.unbind(self.getDoc());
42490			}
42491
42492			self.dom.unbind(self.getBody());
42493			self.dom.unbind(self.getContainer());
42494		}
42495	};
42496
42497	EditorObservable = Tools.extend({}, Observable, EditorObservable);
42498
42499	return EditorObservable;
42500});
42501
42502// Included from: js/tinymce/classes/Mode.js
42503
42504/**
42505 * Mode.js
42506 *
42507 * Released under LGPL License.
42508 * Copyright (c) 1999-2015 Ephox Corp. All rights reserved
42509 *
42510 * License: http://www.tinymce.com/license
42511 * Contributing: http://www.tinymce.com/contributing
42512 */
42513
42514/**
42515 * Mode switcher logic.
42516 *
42517 * @private
42518 * @class tinymce.Mode
42519 */
42520define("tinymce/Mode", [], function() {
42521	function setEditorCommandState(editor, cmd, state) {
42522		try {
42523			editor.getDoc().execCommand(cmd, false, state);
42524		} catch (ex) {
42525			// Ignore
42526		}
42527	}
42528
42529	function setMode(editor, mode) {
42530		var currentMode = editor.readonly ? 'readonly' : 'design';
42531
42532		if (mode == currentMode) {
42533			return;
42534		}
42535
42536		if (mode == 'readonly') {
42537			editor.selection.controlSelection.hideResizeRect();
42538			editor.readonly = true;
42539			editor.getBody().contentEditable = false;
42540		} else {
42541			editor.readonly = false;
42542			editor.getBody().contentEditable = true;
42543			setEditorCommandState(editor, "StyleWithCSS", false);
42544			setEditorCommandState(editor, "enableInlineTableEditing", false);
42545			setEditorCommandState(editor, "enableObjectResizing", false);
42546			editor.focus();
42547			editor.nodeChanged();
42548		}
42549
42550		// Event is NOT preventable
42551		editor.fire('SwitchMode', {mode: mode});
42552	}
42553
42554	return {
42555		setMode: setMode
42556	};
42557});
42558
42559// Included from: js/tinymce/classes/Shortcuts.js
42560
42561/**
42562 * Shortcuts.js
42563 *
42564 * Released under LGPL License.
42565 * Copyright (c) 1999-2015 Ephox Corp. All rights reserved
42566 *
42567 * License: http://www.tinymce.com/license
42568 * Contributing: http://www.tinymce.com/contributing
42569 */
42570
42571/**
42572 * Contains all logic for handling of keyboard shortcuts.
42573 *
42574 * @class tinymce.Shortcuts
42575 * @example
42576 * editor.shortcuts.add('ctrl+a', function() {});
42577 * editor.shortcuts.add('meta+a', function() {}); // "meta" maps to Command on Mac and Ctrl on PC
42578 * editor.shortcuts.add('ctrl+alt+a', function() {});
42579 * editor.shortcuts.add('access+a', function() {}); // "access" maps to ctrl+alt on Mac and shift+alt on PC
42580 */
42581define("tinymce/Shortcuts", [
42582	"tinymce/util/Tools",
42583	"tinymce/Env"
42584], function(Tools, Env) {
42585	var each = Tools.each, explode = Tools.explode;
42586
42587	var keyCodeLookup = {
42588		"f9": 120,
42589		"f10": 121,
42590		"f11": 122
42591	};
42592
42593	var modifierNames = Tools.makeMap('alt,ctrl,shift,meta,access');
42594
42595	return function(editor) {
42596		var self = this, shortcuts = {};
42597
42598		function createShortcut(pattern, desc, cmdFunc, scope) {
42599			var id, key, shortcut;
42600
42601			shortcut = {
42602				func: cmdFunc,
42603				scope: scope || editor,
42604				desc: editor.translate(desc)
42605			};
42606
42607			// Parse modifiers and keys ctrl+alt+b for example
42608			each(explode(pattern, '+'), function(value) {
42609				if (value in modifierNames) {
42610					shortcut[value] = true;
42611				} else {
42612					// Allow numeric keycodes like ctrl+219 for ctrl+[
42613					if (/^[0-9]{2,}$/.test(value)) {
42614						shortcut.keyCode = parseInt(value, 10);
42615					} else {
42616						shortcut.charCode = value.charCodeAt(0);
42617						shortcut.keyCode = keyCodeLookup[value] || value.toUpperCase().charCodeAt(0);
42618					}
42619				}
42620			});
42621
42622			// Generate unique id for modifier combination and set default state for unused modifiers
42623			id = [shortcut.keyCode];
42624			for (key in modifierNames) {
42625				if (shortcut[key]) {
42626					id.push(key);
42627				} else {
42628					shortcut[key] = false;
42629				}
42630			}
42631			shortcut.id = id.join(',');
42632
42633			// Handle special access modifier differently depending on Mac/Win
42634			if (shortcut.access) {
42635				shortcut.alt = true;
42636
42637				if (Env.mac) {
42638					shortcut.ctrl = true;
42639				} else {
42640					shortcut.shift = true;
42641				}
42642			}
42643
42644			// Handle special meta modifier differently depending on Mac/Win
42645			if (shortcut.meta) {
42646				if (Env.mac) {
42647					shortcut.meta = true;
42648				} else {
42649					shortcut.ctrl = true;
42650					shortcut.meta = false;
42651				}
42652			}
42653
42654			return shortcut;
42655		}
42656
42657		editor.on('keyup keypress keydown', function(e) {
42658			if ((e.altKey || e.ctrlKey || e.metaKey) && !e.isDefaultPrevented()) {
42659				each(shortcuts, function(shortcut) {
42660					if (shortcut.ctrl != e.ctrlKey || shortcut.meta != e.metaKey) {
42661						return;
42662					}
42663
42664					if (shortcut.alt != e.altKey || shortcut.shift != e.shiftKey) {
42665						return;
42666					}
42667
42668					if (e.keyCode == shortcut.keyCode || (e.charCode && e.charCode == shortcut.charCode)) {
42669						e.preventDefault();
42670
42671						if (e.type == "keydown") {
42672							shortcut.func.call(shortcut.scope);
42673						}
42674
42675						return true;
42676					}
42677				});
42678			}
42679		});
42680
42681		/**
42682		 * Adds a keyboard shortcut for some command or function.
42683		 *
42684		 * @method addShortcut
42685		 * @param {String} pattern Shortcut pattern. Like for example: ctrl+alt+o.
42686		 * @param {String} desc Text description for the command.
42687		 * @param {String/Function} cmdFunc Command name string or function to execute when the key is pressed.
42688		 * @param {Object} scope Optional scope to execute the function in.
42689		 * @return {Boolean} true/false state if the shortcut was added or not.
42690		 */
42691		self.add = function(pattern, desc, cmdFunc, scope) {
42692			var cmd;
42693
42694			cmd = cmdFunc;
42695
42696			if (typeof cmdFunc === 'string') {
42697				cmdFunc = function() {
42698					editor.execCommand(cmd, false, null);
42699				};
42700			} else if (Tools.isArray(cmd)) {
42701				cmdFunc = function() {
42702					editor.execCommand(cmd[0], cmd[1], cmd[2]);
42703				};
42704			}
42705
42706			each(explode(pattern.toLowerCase()), function(pattern) {
42707				var shortcut = createShortcut(pattern, desc, cmdFunc, scope);
42708				shortcuts[shortcut.id] = shortcut;
42709			});
42710
42711			return true;
42712		};
42713
42714		/**
42715		 * Remove a keyboard shortcut by pattern.
42716		 *
42717		 * @method remove
42718		 * @param {String} pattern Shortcut pattern. Like for example: ctrl+alt+o.
42719		 * @return {Boolean} true/false state if the shortcut was removed or not.
42720		 */
42721		self.remove = function(pattern) {
42722			var shortcut = createShortcut(pattern);
42723
42724			if (shortcuts[shortcut.id]) {
42725				delete shortcuts[shortcut.id];
42726				return true;
42727			}
42728
42729			return false;
42730		};
42731	};
42732});
42733
42734// Included from: js/tinymce/classes/file/Uploader.js
42735
42736/**
42737 * Uploader.js
42738 *
42739 * Released under LGPL License.
42740 * Copyright (c) 1999-2015 Ephox Corp. All rights reserved
42741 *
42742 * License: http://www.tinymce.com/license
42743 * Contributing: http://www.tinymce.com/contributing
42744 */
42745
42746/**
42747 * Upload blobs or blob infos to the specified URL or handler.
42748 *
42749 * @private
42750 * @class tinymce.file.Uploader
42751 * @example
42752 * var uploader = new Uploader({
42753 *     url: '/upload.php',
42754 *     basePath: '/base/path',
42755 *     credentials: true,
42756 *     handler: function(data, success, failure) {
42757 *         ...
42758 *     }
42759 * });
42760 *
42761 * uploader.upload(blobInfos).then(function(result) {
42762 *     ...
42763 * });
42764 */
42765define("tinymce/file/Uploader", [
42766	"tinymce/util/Promise",
42767	"tinymce/util/Tools",
42768	"tinymce/util/Fun"
42769], function(Promise, Tools, Fun) {
42770	return function(settings) {
42771		var cachedPromises = {};
42772
42773		function fileName(blobInfo) {
42774			var ext, extensions;
42775
42776			extensions = {
42777				'image/jpeg': 'jpg',
42778				'image/jpg': 'jpg',
42779				'image/gif': 'gif',
42780				'image/png': 'png'
42781			};
42782
42783			ext = extensions[blobInfo.blob().type.toLowerCase()] || 'dat';
42784
42785			return blobInfo.id() + '.' + ext;
42786		}
42787
42788		function pathJoin(path1, path2) {
42789			if (path1) {
42790				return path1.replace(/\/$/, '') + '/' + path2.replace(/^\//, '');
42791			}
42792
42793			return path2;
42794		}
42795
42796		function blobInfoToData(blobInfo) {
42797			return {
42798				id: blobInfo.id,
42799				blob: blobInfo.blob,
42800				base64: blobInfo.base64,
42801				filename: Fun.constant(fileName(blobInfo))
42802			};
42803		}
42804
42805		function defaultHandler(blobInfo, success, failure, openNotification) {
42806			var xhr, formData, notification;
42807
42808			xhr = new XMLHttpRequest();
42809			xhr.open('POST', settings.url);
42810			xhr.withCredentials = settings.credentials;
42811
42812			notification = openNotification();
42813
42814			xhr.upload.onprogress = function(e) {
42815				var percentLoaded = Math.round(e.loaded / e.total * 100);
42816				notification.progressBar.value(percentLoaded);
42817			};
42818
42819			xhr.onload = function() {
42820				var json;
42821
42822				notification.close();
42823
42824				if (xhr.status != 200) {
42825					failure("HTTP Error: " + xhr.status);
42826					return;
42827				}
42828
42829				json = JSON.parse(xhr.responseText);
42830
42831				if (!json || typeof json.location != "string") {
42832					failure("Invalid JSON: " + xhr.responseText);
42833					return;
42834				}
42835
42836				success(pathJoin(settings.basePath, json.location));
42837			};
42838
42839			formData = new FormData();
42840			formData.append('file', blobInfo.blob(), fileName(blobInfo));
42841
42842			xhr.send(formData);
42843		}
42844
42845		function noUpload() {
42846			return new Promise(function(resolve) {
42847				resolve([]);
42848			});
42849		}
42850
42851		function interpretResult(promise) {
42852			return promise.then(function(result) {
42853				return result;
42854			})['catch'](function(error) {
42855				return error;
42856			});
42857		}
42858
42859		function registerPromise(handler, id, blobInfo) {
42860			var response = handler(blobInfo);
42861			var promise = interpretResult(response);
42862			delete cachedPromises[id];
42863			cachedPromises[id] = promise;
42864			return promise;
42865		}
42866
42867		function collectUploads(blobInfos, uploadBlobInfo) {
42868			return Tools.map(blobInfos, function(blobInfo) {
42869				var id = blobInfo.id();
42870				return cachedPromises[id] ? cachedPromises[id] : registerPromise(uploadBlobInfo, id, blobInfo);
42871			});
42872		}
42873
42874		function uploadBlobs(blobInfos, openNotification) {
42875			function uploadBlobInfo(blobInfo) {
42876				return new Promise(function(resolve) {
42877					var handler = settings.handler;
42878
42879					handler(blobInfoToData(blobInfo), function(url) {
42880						resolve({
42881							url: url,
42882							blobInfo: blobInfo,
42883							status: true
42884						});
42885					}, function(failure) {
42886						resolve({
42887							url: '',
42888							blobInfo: blobInfo,
42889							status: false,
42890							error: failure
42891						});
42892					}, openNotification);
42893				});
42894			}
42895
42896			var promises = collectUploads(blobInfos, uploadBlobInfo);
42897			return Promise.all(promises);
42898		}
42899
42900		function upload(blobInfos, openNotification) {
42901			return (!settings.url && settings.handler === defaultHandler) ? noUpload() : uploadBlobs(blobInfos, openNotification);
42902		}
42903
42904		settings = Tools.extend({
42905			credentials: false,
42906			// We are adding a notify argument to this (at the moment, until it doesn't work)
42907			handler: defaultHandler
42908		}, settings);
42909
42910		return {
42911			upload: upload
42912		};
42913	};
42914});
42915
42916// Included from: js/tinymce/classes/file/Conversions.js
42917
42918/**
42919 * Conversions.js
42920 *
42921 * Released under LGPL License.
42922 * Copyright (c) 1999-2015 Ephox Corp. All rights reserved
42923 *
42924 * License: http://www.tinymce.com/license
42925 * Contributing: http://www.tinymce.com/contributing
42926 */
42927
42928/**
42929 * Converts blob/uris back and forth.
42930 *
42931 * @private
42932 * @class tinymce.file.Conversions
42933 */
42934define("tinymce/file/Conversions", [
42935	"tinymce/util/Promise"
42936], function(Promise) {
42937	function blobUriToBlob(url) {
42938		return new Promise(function(resolve) {
42939			var xhr = new XMLHttpRequest();
42940
42941			xhr.open('GET', url, true);
42942			xhr.responseType = 'blob';
42943
42944			xhr.onload = function() {
42945				if (this.status == 200) {
42946					resolve(this.response);
42947				}
42948			};
42949
42950			xhr.send();
42951		});
42952	}
42953
42954	function parseDataUri(uri) {
42955		var type, matches;
42956
42957		uri = decodeURIComponent(uri).split(',');
42958
42959		matches = /data:([^;]+)/.exec(uri[0]);
42960		if (matches) {
42961			type = matches[1];
42962		}
42963
42964		return {
42965			type: type,
42966			data: uri[1]
42967		};
42968	}
42969
42970	function dataUriToBlob(uri) {
42971		return new Promise(function(resolve) {
42972			var str, arr, i;
42973
42974			uri = parseDataUri(uri);
42975
42976			// Might throw error if data isn't proper base64
42977			try {
42978				str = atob(uri.data);
42979			} catch (e) {
42980				resolve(new Blob([]));
42981				return;
42982			}
42983
42984			arr = new Uint8Array(str.length);
42985
42986			for (i = 0; i < arr.length; i++) {
42987				arr[i] = str.charCodeAt(i);
42988			}
42989
42990			resolve(new Blob([arr], {type: uri.type}));
42991		});
42992	}
42993
42994	function uriToBlob(url) {
42995		if (url.indexOf('blob:') === 0) {
42996			return blobUriToBlob(url);
42997		}
42998
42999		if (url.indexOf('data:') === 0) {
43000			return dataUriToBlob(url);
43001		}
43002
43003		return null;
43004	}
43005
43006	function blobToDataUri(blob) {
43007		return new Promise(function(resolve) {
43008			var reader = new FileReader();
43009
43010			reader.onloadend = function() {
43011				resolve(reader.result);
43012			};
43013
43014			reader.readAsDataURL(blob);
43015		});
43016	}
43017
43018	return {
43019		uriToBlob: uriToBlob,
43020		blobToDataUri: blobToDataUri,
43021		parseDataUri: parseDataUri
43022	};
43023});
43024
43025// Included from: js/tinymce/classes/file/ImageScanner.js
43026
43027/**
43028 * ImageScanner.js
43029 *
43030 * Released under LGPL License.
43031 * Copyright (c) 1999-2015 Ephox Corp. All rights reserved
43032 *
43033 * License: http://www.tinymce.com/license
43034 * Contributing: http://www.tinymce.com/contributing
43035 */
43036
43037/**
43038 * Finds images with data uris or blob uris. If data uris are found it will convert them into blob uris.
43039 *
43040 * @private
43041 * @class tinymce.file.ImageScanner
43042 */
43043define("tinymce/file/ImageScanner", [
43044	"tinymce/util/Promise",
43045	"tinymce/util/Arr",
43046	"tinymce/util/Fun",
43047	"tinymce/file/Conversions",
43048	"tinymce/Env"
43049], function(Promise, Arr, Fun, Conversions, Env) {
43050	var count = 0;
43051
43052	return function(blobCache) {
43053		var cachedPromises = {};
43054
43055		function findAll(elm, predicate) {
43056			var images, promises;
43057
43058			function imageToBlobInfo(img, resolve) {
43059				var base64, blobInfo;
43060
43061				if (img.src.indexOf('blob:') === 0) {
43062					blobInfo = blobCache.getByUri(img.src);
43063
43064					if (blobInfo) {
43065						resolve({
43066							image: img,
43067							blobInfo: blobInfo
43068						});
43069					}
43070
43071					return;
43072				}
43073
43074				base64 = Conversions.parseDataUri(img.src).data;
43075				blobInfo = blobCache.findFirst(function(cachedBlobInfo) {
43076					return cachedBlobInfo.base64() === base64;
43077				});
43078
43079				if (blobInfo) {
43080					resolve({
43081						image: img,
43082						blobInfo: blobInfo
43083					});
43084				} else {
43085					Conversions.uriToBlob(img.src).then(function(blob) {
43086						var blobInfoId = 'blobid' + (count++),
43087							blobInfo = blobCache.create(blobInfoId, blob, base64);
43088
43089						blobCache.add(blobInfo);
43090
43091						resolve({
43092							image: img,
43093							blobInfo: blobInfo
43094						});
43095					});
43096				}
43097			}
43098
43099			if (!predicate) {
43100				predicate = Fun.constant(true);
43101			}
43102
43103			images = Arr.filter(elm.getElementsByTagName('img'), function(img) {
43104				var src = img.src;
43105
43106				if (!Env.fileApi) {
43107					return false;
43108				}
43109
43110				if (img.hasAttribute('data-mce-bogus')) {
43111					return false;
43112				}
43113
43114				if (img.hasAttribute('data-mce-placeholder')) {
43115					return false;
43116				}
43117
43118				if (!src || src == Env.transparentSrc) {
43119					return false;
43120				}
43121
43122				if (src.indexOf('blob:') === 0) {
43123					return true;
43124				}
43125
43126				if (src.indexOf('data:') === 0) {
43127					return predicate(img);
43128				}
43129
43130				return false;
43131			});
43132
43133			promises = Arr.map(images, function(img) {
43134				var newPromise;
43135
43136				if (cachedPromises[img.src]) {
43137					// Since the cached promise will return the cached image
43138					// We need to wrap it and resolve with the actual image
43139					return new Promise(function(resolve) {
43140						cachedPromises[img.src].then(function(imageInfo) {
43141							resolve({
43142								image: img,
43143								blobInfo: imageInfo.blobInfo
43144							});
43145						});
43146					});
43147				}
43148
43149				newPromise = new Promise(function(resolve) {
43150					imageToBlobInfo(img, resolve);
43151				}).then(function(result) {
43152					delete cachedPromises[result.image.src];
43153					return result;
43154				})['catch'](function(error) {
43155					delete cachedPromises[img.src];
43156					return error;
43157				});
43158
43159				cachedPromises[img.src] = newPromise;
43160
43161				return newPromise;
43162			});
43163
43164			return Promise.all(promises);
43165		}
43166
43167		return {
43168			findAll: findAll
43169		};
43170	};
43171});
43172
43173// Included from: js/tinymce/classes/file/BlobCache.js
43174
43175/**
43176 * BlobCache.js
43177 *
43178 * Released under LGPL License.
43179 * Copyright (c) 1999-2015 Ephox Corp. All rights reserved
43180 *
43181 * License: http://www.tinymce.com/license
43182 * Contributing: http://www.tinymce.com/contributing
43183 */
43184
43185/**
43186 * Hold blob info objects where a blob has extra internal information.
43187 *
43188 * @private
43189 * @class tinymce.file.BlobCache
43190 */
43191define("tinymce/file/BlobCache", [
43192	"tinymce/util/Arr",
43193	"tinymce/util/Fun"
43194], function(Arr, Fun) {
43195	return function() {
43196		var cache = [], constant = Fun.constant;
43197
43198		function create(id, blob, base64) {
43199			return {
43200				id: constant(id),
43201				blob: constant(blob),
43202				base64: constant(base64),
43203				blobUri: constant(URL.createObjectURL(blob))
43204			};
43205		}
43206
43207		function add(blobInfo) {
43208			if (!get(blobInfo.id())) {
43209				cache.push(blobInfo);
43210			}
43211		}
43212
43213		function get(id) {
43214			return findFirst(function(cachedBlobInfo) {
43215				return cachedBlobInfo.id() === id;
43216			});
43217		}
43218
43219		function findFirst(predicate) {
43220			return Arr.filter(cache, predicate)[0];
43221		}
43222
43223		function getByUri(blobUri) {
43224			return findFirst(function(blobInfo) {
43225				return blobInfo.blobUri() == blobUri;
43226			});
43227		}
43228
43229		function destroy() {
43230			Arr.each(cache, function(cachedBlobInfo) {
43231				URL.revokeObjectURL(cachedBlobInfo.blobUri());
43232			});
43233
43234			cache = [];
43235		}
43236
43237		return {
43238			create: create,
43239			add: add,
43240			get: get,
43241			getByUri: getByUri,
43242			findFirst: findFirst,
43243			destroy: destroy
43244		};
43245	};
43246});
43247
43248// Included from: js/tinymce/classes/EditorUpload.js
43249
43250/**
43251 * EditorUpload.js
43252 *
43253 * Released under LGPL License.
43254 * Copyright (c) 1999-2015 Ephox Corp. All rights reserved
43255 *
43256 * License: http://www.tinymce.com/license
43257 * Contributing: http://www.tinymce.com/contributing
43258 */
43259
43260/**
43261 * Handles image uploads, updates undo stack and patches over various internal functions.
43262 *
43263 * @private
43264 * @class tinymce.EditorUpload
43265 */
43266define("tinymce/EditorUpload", [
43267	"tinymce/util/Arr",
43268	"tinymce/file/Uploader",
43269	"tinymce/file/ImageScanner",
43270	"tinymce/file/BlobCache"
43271], function(Arr, Uploader, ImageScanner, BlobCache) {
43272	return function(editor) {
43273		var blobCache = new BlobCache(), uploader, imageScanner, settings = editor.settings;
43274
43275		function aliveGuard(callback) {
43276			return function(result) {
43277				if (editor.selection) {
43278					return callback(result);
43279				}
43280
43281				return [];
43282			};
43283		}
43284
43285		// Replaces strings without regexps to avoid FF regexp to big issue
43286		function replaceString(content, search, replace) {
43287			var index = 0;
43288
43289			do {
43290				index = content.indexOf(search, index);
43291
43292				if (index !== -1) {
43293					content = content.substring(0, index) + replace + content.substr(index + search.length);
43294					index += replace.length - search.length + 1;
43295				}
43296			} while (index !== -1);
43297
43298			return content;
43299		}
43300
43301		function replaceImageUrl(content, targetUrl, replacementUrl) {
43302			content = replaceString(content, 'src="' + targetUrl + '"', 'src="' + replacementUrl + '"');
43303			content = replaceString(content, 'data-mce-src="' + targetUrl + '"', 'data-mce-src="' + replacementUrl + '"');
43304
43305			return content;
43306		}
43307
43308		function replaceUrlInUndoStack(targetUrl, replacementUrl) {
43309			Arr.each(editor.undoManager.data, function(level) {
43310				level.content = replaceImageUrl(level.content, targetUrl, replacementUrl);
43311			});
43312		}
43313
43314		function openNotification() {
43315			return editor.notificationManager.open({
43316				text: editor.translate('Image uploading...'),
43317				type: 'info',
43318				timeout: -1,
43319				progressBar: true
43320			});
43321		}
43322
43323		function uploadImages(callback) {
43324			if (!uploader) {
43325				uploader = new Uploader({
43326					url: settings.images_upload_url,
43327					basePath: settings.images_upload_base_path,
43328					credentials: settings.images_upload_credentials,
43329					handler: settings.images_upload_handler
43330				});
43331			}
43332
43333			return scanForImages().then(aliveGuard(function(imageInfos) {
43334				var blobInfos;
43335
43336				blobInfos = Arr.map(imageInfos, function(imageInfo) {
43337					return imageInfo.blobInfo;
43338				});
43339
43340				return uploader.upload(blobInfos, openNotification).then(aliveGuard(function(result) {
43341					result = Arr.map(result, function(uploadInfo, index) {
43342						var image = imageInfos[index].image;
43343
43344						replaceUrlInUndoStack(image.src, uploadInfo.url);
43345
43346						editor.$(image).attr({
43347							src: uploadInfo.url,
43348							'data-mce-src': editor.convertURL(uploadInfo.url, 'src')
43349						});
43350
43351						return {
43352							element: image,
43353							status: uploadInfo.status
43354						};
43355					});
43356
43357					if (callback) {
43358						callback(result);
43359					}
43360
43361					return result;
43362				}));
43363			}));
43364		}
43365
43366		function uploadImagesAuto(callback) {
43367			if (settings.automatic_uploads !== false) {
43368				return uploadImages(callback);
43369			}
43370		}
43371
43372		function scanForImages() {
43373			if (!imageScanner) {
43374				imageScanner = new ImageScanner(blobCache);
43375			}
43376
43377			return imageScanner.findAll(editor.getBody(), settings.images_dataimg_filter).then(aliveGuard(function(result) {
43378				Arr.each(result, function(resultItem) {
43379					replaceUrlInUndoStack(resultItem.image.src, resultItem.blobInfo.blobUri());
43380					resultItem.image.src = resultItem.blobInfo.blobUri();
43381				});
43382
43383				return result;
43384			}));
43385		}
43386
43387		function destroy() {
43388			blobCache.destroy();
43389			imageScanner = uploader = null;
43390		}
43391
43392		function replaceBlobWithBase64(content) {
43393			return content.replace(/src="(blob:[^"]+)"/g, function(match, blobUri) {
43394				var blobInfo = blobCache.getByUri(blobUri);
43395
43396				if (!blobInfo) {
43397					blobInfo = Arr.reduce(editor.editorManager.editors, function(result, editor) {
43398						return result || editor.editorUpload.blobCache.getByUri(blobUri);
43399					}, null);
43400				}
43401
43402				if (blobInfo) {
43403					return 'src="data:' + blobInfo.blob().type + ';base64,' + blobInfo.base64() + '"';
43404				}
43405
43406				return match;
43407			});
43408		}
43409
43410		editor.on('setContent', function() {
43411			if (editor.settings.automatic_uploads !== false) {
43412				uploadImagesAuto();
43413			} else {
43414				scanForImages();
43415			}
43416		});
43417
43418		editor.on('RawSaveContent', function(e) {
43419			e.content = replaceBlobWithBase64(e.content);
43420		});
43421
43422		editor.on('getContent', function(e) {
43423			if (e.source_view || e.format == 'raw') {
43424				return;
43425			}
43426
43427			e.content = replaceBlobWithBase64(e.content);
43428		});
43429
43430		return {
43431			blobCache: blobCache,
43432			uploadImages: uploadImages,
43433			uploadImagesAuto: uploadImagesAuto,
43434			scanForImages: scanForImages,
43435			destroy: destroy
43436		};
43437	};
43438});
43439
43440// Included from: js/tinymce/classes/caret/CaretUtils.js
43441
43442/**
43443 * CaretUtils.js
43444 *
43445 * Released under LGPL License.
43446 * Copyright (c) 1999-2015 Ephox Corp. All rights reserved
43447 *
43448 * License: http://www.tinymce.com/license
43449 * Contributing: http://www.tinymce.com/contributing
43450 */
43451
43452/**
43453 * Utility functions shared by the caret logic.
43454 *
43455 * @private
43456 * @class tinymce.caret.CaretUtils
43457 */
43458define("tinymce/caret/CaretUtils", [
43459	"tinymce/util/Fun",
43460	"tinymce/dom/TreeWalker",
43461	"tinymce/dom/NodeType",
43462	"tinymce/caret/CaretPosition",
43463	"tinymce/caret/CaretContainer",
43464	"tinymce/caret/CaretCandidate"
43465], function(Fun, TreeWalker, NodeType, CaretPosition, CaretContainer, CaretCandidate) {
43466	var isContentEditableTrue = NodeType.isContentEditableTrue,
43467		isContentEditableFalse = NodeType.isContentEditableFalse,
43468		isBlockLike = NodeType.matchStyleValues('display', 'block table table-cell table-caption'),
43469		isCaretContainer = CaretContainer.isCaretContainer,
43470		curry = Fun.curry,
43471		isElement = NodeType.isElement,
43472		isCaretCandidate = CaretCandidate.isCaretCandidate;
43473
43474	function isForwards(direction) {
43475		return direction > 0;
43476	}
43477
43478	function isBackwards(direction) {
43479		return direction < 0;
43480	}
43481
43482	function findNode(node, direction, predicateFn, rootNode, shallow) {
43483		var walker = new TreeWalker(node, rootNode);
43484
43485		if (isBackwards(direction)) {
43486			if (isContentEditableFalse(node)) {
43487				node = walker.prev(true);
43488				if (predicateFn(node)) {
43489					return node;
43490				}
43491			}
43492
43493			while ((node = walker.prev(shallow))) {
43494				if (predicateFn(node)) {
43495					return node;
43496				}
43497			}
43498		}
43499
43500		if (isForwards(direction)) {
43501			if (isContentEditableFalse(node)) {
43502				node = walker.next(true);
43503				if (predicateFn(node)) {
43504					return node;
43505				}
43506			}
43507
43508			while ((node = walker.next(shallow))) {
43509				if (predicateFn(node)) {
43510					return node;
43511				}
43512			}
43513		}
43514
43515		return null;
43516	}
43517
43518	function getEditingHost(node, rootNode) {
43519		for (node = node.parentNode; node && node != rootNode; node = node.parentNode) {
43520			if (isContentEditableTrue(node)) {
43521				return node;
43522			}
43523		}
43524
43525		return rootNode;
43526	}
43527
43528	function getParentBlock(node, rootNode) {
43529		while (node && node != rootNode) {
43530			if (isBlockLike(node)) {
43531				return node;
43532			}
43533
43534			node = node.parentNode;
43535		}
43536
43537		return null;
43538	}
43539
43540	function isInSameBlock(caretPosition1, caretPosition2, rootNode) {
43541		return getParentBlock(caretPosition1.container(), rootNode) == getParentBlock(caretPosition2.container(), rootNode);
43542	}
43543
43544	function isInSameEditingHost(caretPosition1, caretPosition2, rootNode) {
43545		return getEditingHost(caretPosition1.container(), rootNode) == getEditingHost(caretPosition2.container(), rootNode);
43546	}
43547
43548	function getChildNodeAtRelativeOffset(relativeOffset, caretPosition) {
43549		var container, offset;
43550
43551		if (!caretPosition) {
43552			return null;
43553		}
43554
43555		container = caretPosition.container();
43556		offset = caretPosition.offset();
43557
43558		if (!isElement(container)) {
43559			return null;
43560		}
43561
43562		return container.childNodes[offset + relativeOffset];
43563	}
43564
43565	function beforeAfter(before, node) {
43566		var range = node.ownerDocument.createRange();
43567
43568		if (before) {
43569			range.setStartBefore(node);
43570			range.setEndBefore(node);
43571		} else {
43572			range.setStartAfter(node);
43573			range.setEndAfter(node);
43574		}
43575
43576		return range;
43577	}
43578
43579	function isNodesInSameBlock(rootNode, node1, node2) {
43580		return getParentBlock(node1, rootNode) == getParentBlock(node2, rootNode);
43581	}
43582
43583	function lean(left, rootNode, node) {
43584		var sibling, siblingName;
43585
43586		if (left) {
43587			siblingName = 'previousSibling';
43588		} else {
43589			siblingName = 'nextSibling';
43590		}
43591
43592		while (node && node != rootNode) {
43593			sibling = node[siblingName];
43594
43595			if (isCaretContainer(sibling)) {
43596				sibling = sibling[siblingName];
43597			}
43598
43599			if (isContentEditableFalse(sibling)) {
43600				if (isNodesInSameBlock(rootNode, sibling, node)) {
43601					return sibling;
43602				}
43603
43604				break;
43605			}
43606
43607			if (isCaretCandidate(sibling)) {
43608				break;
43609			}
43610
43611			node = node.parentNode;
43612		}
43613
43614		return null;
43615	}
43616
43617	var before = curry(beforeAfter, true);
43618	var after = curry(beforeAfter, false);
43619
43620	function normalizeRange(direction, rootNode, range) {
43621		var node, container, offset, location;
43622		var leanLeft = curry(lean, true, rootNode);
43623		var leanRight = curry(lean, false, rootNode);
43624
43625		container = range.startContainer;
43626		offset = range.startOffset;
43627
43628		if (CaretContainer.isCaretContainerBlock(container)) {
43629			if (!isElement(container)) {
43630				container = container.parentNode;
43631			}
43632
43633			location = container.getAttribute('data-mce-caret');
43634
43635			if (location == 'before') {
43636				node = container.nextSibling;
43637				if (isContentEditableFalse(node)) {
43638					return before(node);
43639				}
43640			}
43641
43642			if (location == 'after') {
43643				node = container.previousSibling;
43644				if (isContentEditableFalse(node)) {
43645					return after(node);
43646				}
43647			}
43648		}
43649
43650		if (!range.collapsed) {
43651			return range;
43652		}
43653
43654		if (NodeType.isText(container)) {
43655			if (isCaretContainer(container)) {
43656				if (direction === 1) {
43657					node = leanRight(container);
43658					if (node) {
43659						return before(node);
43660					}
43661
43662					node = leanLeft(container);
43663					if (node) {
43664						return after(node);
43665					}
43666				}
43667
43668				if (direction === -1) {
43669					node = leanLeft(container);
43670					if (node) {
43671						return after(node);
43672					}
43673
43674					node = leanRight(container);
43675					if (node) {
43676						return before(node);
43677					}
43678				}
43679
43680				return range;
43681			}
43682
43683			if (CaretContainer.endsWithCaretContainer(container) && offset >= container.data.length - 1) {
43684				if (direction === 1) {
43685					node = leanRight(container);
43686					if (node) {
43687						return before(node);
43688					}
43689				}
43690
43691				return range;
43692			}
43693
43694			if (CaretContainer.startsWithCaretContainer(container) && offset <= 1) {
43695				if (direction === -1) {
43696					node = leanLeft(container);
43697					if (node) {
43698						return after(node);
43699					}
43700				}
43701
43702				return range;
43703			}
43704
43705			if (offset === container.data.length) {
43706				node = leanRight(container);
43707				if (node) {
43708					return before(node);
43709				}
43710
43711				return range;
43712			}
43713
43714			if (offset === 0) {
43715				node = leanLeft(container);
43716				if (node) {
43717					return after(node);
43718				}
43719
43720				return range;
43721			}
43722		}
43723
43724		return range;
43725	}
43726
43727	function isNextToContentEditableFalse(relativeOffset, caretPosition) {
43728		return isContentEditableFalse(getChildNodeAtRelativeOffset(relativeOffset, caretPosition));
43729	}
43730
43731	return {
43732		isForwards: isForwards,
43733		isBackwards: isBackwards,
43734		findNode: findNode,
43735		getEditingHost: getEditingHost,
43736		getParentBlock: getParentBlock,
43737		isInSameBlock: isInSameBlock,
43738		isInSameEditingHost: isInSameEditingHost,
43739		isBeforeContentEditableFalse: curry(isNextToContentEditableFalse, 0),
43740		isAfterContentEditableFalse: curry(isNextToContentEditableFalse, -1),
43741		normalizeRange: normalizeRange
43742	};
43743});
43744
43745// Included from: js/tinymce/classes/caret/CaretWalker.js
43746
43747/**
43748 * CaretWalker.js
43749 *
43750 * Released under LGPL License.
43751 * Copyright (c) 1999-2015 Ephox Corp. All rights reserved
43752 *
43753 * License: http://www.tinymce.com/license
43754 * Contributing: http://www.tinymce.com/contributing
43755 */
43756
43757/**
43758 * This module contains logic for moving around a virtual caret in logical order within a DOM element.
43759 *
43760 * It ignores the most obvious invalid caret locations such as within a script element or within a
43761 * contentEditable=false element but it will return locations that isn't possible to render visually.
43762 *
43763 * @private
43764 * @class tinymce.caret.CaretWalker
43765 * @example
43766 * var caretWalker = new CaretWalker(rootElm);
43767 *
43768 * var prevLogicalCaretPosition = caretWalker.prev(CaretPosition.fromRangeStart(range));
43769 * var nextLogicalCaretPosition = caretWalker.next(CaretPosition.fromRangeEnd(range));
43770 */
43771define("tinymce/caret/CaretWalker", [
43772	"tinymce/dom/NodeType",
43773	"tinymce/caret/CaretCandidate",
43774	"tinymce/caret/CaretPosition",
43775	"tinymce/caret/CaretUtils",
43776	"tinymce/util/Arr",
43777	"tinymce/util/Fun"
43778], function(NodeType, CaretCandidate, CaretPosition, CaretUtils, Arr, Fun) {
43779	var isContentEditableFalse = NodeType.isContentEditableFalse,
43780		isText = NodeType.isText,
43781		isElement = NodeType.isElement,
43782		isForwards = CaretUtils.isForwards,
43783		isBackwards = CaretUtils.isBackwards,
43784		isCaretCandidate = CaretCandidate.isCaretCandidate,
43785		isAtomic = CaretCandidate.isAtomic,
43786		isEditableCaretCandidate = CaretCandidate.isEditableCaretCandidate;
43787
43788	function getParents(node, rootNode) {
43789		var parents = [];
43790
43791		while (node && node != rootNode) {
43792			parents.push(node);
43793			node = node.parentNode;
43794		}
43795
43796		return parents;
43797	}
43798
43799	function nodeAtIndex(container, offset) {
43800		if (container.hasChildNodes() && offset < container.childNodes.length) {
43801			return container.childNodes[offset];
43802		}
43803
43804		return null;
43805	}
43806
43807	function getCaretCandidatePosition(direction, node) {
43808		if (isForwards(direction)) {
43809			if (isCaretCandidate(node.previousSibling) && !isText(node.previousSibling)) {
43810				return CaretPosition.before(node);
43811			}
43812
43813			if (isText(node)) {
43814				return CaretPosition(node, 0);
43815			}
43816		}
43817
43818		if (isBackwards(direction)) {
43819			if (isCaretCandidate(node.nextSibling) && !isText(node.nextSibling)) {
43820				return CaretPosition.after(node);
43821			}
43822
43823			if (isText(node)) {
43824				return CaretPosition(node, node.data.length);
43825			}
43826		}
43827
43828		if (isBackwards(direction)) {
43829			return CaretPosition.after(node);
43830		}
43831
43832		return CaretPosition.before(node);
43833	}
43834
43835	function findCaretPosition(direction, startCaretPosition, rootNode) {
43836		var container, offset, node, nextNode, innerNode,
43837			rootContentEditableFalseElm, caretPosition;
43838
43839		if (!isElement(rootNode) || !startCaretPosition) {
43840			return null;
43841		}
43842
43843		caretPosition = startCaretPosition;
43844		container = caretPosition.container();
43845		offset = caretPosition.offset();
43846
43847		if (isText(container)) {
43848			if (isBackwards(direction) && offset > 0) {
43849				return CaretPosition(container, --offset);
43850			}
43851
43852			if (isForwards(direction) && offset < container.length) {
43853				return CaretPosition(container, ++offset);
43854			}
43855
43856			node = container;
43857		} else {
43858			if (isBackwards(direction) && offset > 0) {
43859				nextNode = nodeAtIndex(container, offset - 1);
43860				if (isCaretCandidate(nextNode)) {
43861					if (!isAtomic(nextNode)) {
43862						innerNode = CaretUtils.findNode(nextNode, direction, isEditableCaretCandidate, nextNode);
43863						if (innerNode) {
43864							if (isText(innerNode)) {
43865								return CaretPosition(innerNode, innerNode.data.length);
43866							}
43867
43868							return CaretPosition.after(innerNode);
43869						}
43870					}
43871
43872					if (isText(nextNode)) {
43873						return CaretPosition(nextNode, nextNode.data.length);
43874					}
43875
43876					return CaretPosition.before(nextNode);
43877				}
43878			}
43879
43880			if (isForwards(direction) && offset < container.childNodes.length) {
43881				nextNode = nodeAtIndex(container, offset);
43882				if (isCaretCandidate(nextNode)) {
43883					if (!isAtomic(nextNode)) {
43884						innerNode = CaretUtils.findNode(nextNode, direction, isEditableCaretCandidate, nextNode);
43885						if (innerNode) {
43886							if (isText(innerNode)) {
43887								return CaretPosition(innerNode, 0);
43888							}
43889
43890							return CaretPosition.before(innerNode);
43891						}
43892					}
43893
43894					if (isText(nextNode)) {
43895						return CaretPosition(nextNode, 0);
43896					}
43897
43898					return CaretPosition.after(nextNode);
43899				}
43900			}
43901
43902			node = caretPosition.getNode();
43903		}
43904
43905		if ((isForwards(direction) && caretPosition.isAtEnd()) || (isBackwards(direction) && caretPosition.isAtStart())) {
43906			node = CaretUtils.findNode(node, direction, Fun.constant(true), rootNode, true);
43907			if (isEditableCaretCandidate(node)) {
43908				return getCaretCandidatePosition(direction, node);
43909			}
43910		}
43911
43912		nextNode = CaretUtils.findNode(node, direction, isEditableCaretCandidate, rootNode);
43913
43914		rootContentEditableFalseElm = Arr.last(Arr.filter(getParents(container, rootNode), isContentEditableFalse));
43915		if (rootContentEditableFalseElm && (!nextNode || !rootContentEditableFalseElm.contains(nextNode))) {
43916			if (isForwards(direction)) {
43917				caretPosition = CaretPosition.after(rootContentEditableFalseElm);
43918			} else {
43919				caretPosition = CaretPosition.before(rootContentEditableFalseElm);
43920			}
43921
43922			return caretPosition;
43923		}
43924
43925		if (nextNode) {
43926			return getCaretCandidatePosition(direction, nextNode);
43927		}
43928
43929		return null;
43930	}
43931
43932	return function(rootNode) {
43933		return {
43934			/**
43935			 * Returns the next logical caret position from the specificed input
43936			 * caretPoisiton or null if there isn't any more positions left for example
43937			 * at the end specified root element.
43938			 *
43939			 * @method next
43940			 * @param {tinymce.caret.CaretPosition} caretPosition Caret position to start from.
43941			 * @return {tinymce.caret.CaretPosition} CaretPosition or null if no position was found.
43942			 */
43943			next: function(caretPosition) {
43944				return findCaretPosition(1, caretPosition, rootNode);
43945			},
43946
43947			/**
43948			 * Returns the previous logical caret position from the specificed input
43949			 * caretPoisiton or null if there isn't any more positions left for example
43950			 * at the end specified root element.
43951			 *
43952			 * @method prev
43953			 * @param {tinymce.caret.CaretPosition} caretPosition Caret position to start from.
43954			 * @return {tinymce.caret.CaretPosition} CaretPosition or null if no position was found.
43955			 */
43956			prev: function(caretPosition) {
43957				return findCaretPosition(-1, caretPosition, rootNode);
43958			}
43959		};
43960	};
43961});
43962
43963// Included from: js/tinymce/classes/caret/FakeCaret.js
43964
43965/**
43966 * FakeCaret.js
43967 *
43968 * Released under LGPL License.
43969 * Copyright (c) 1999-2015 Ephox Corp. All rights reserved
43970 *
43971 * License: http://www.tinymce.com/license
43972 * Contributing: http://www.tinymce.com/contributing
43973 */
43974
43975/**
43976 * This module contains logic for rendering a fake visual caret.
43977 *
43978 * @private
43979 * @class tinymce.caret.FakeCaret
43980 */
43981define("tinymce/caret/FakeCaret", [
43982	"tinymce/caret/CaretContainer",
43983	"tinymce/caret/CaretPosition",
43984	"tinymce/dom/NodeType",
43985	"tinymce/dom/RangeUtils",
43986	"tinymce/dom/DomQuery",
43987	"tinymce/geom/ClientRect",
43988	"tinymce/util/Delay"
43989], function(CaretContainer, CaretPosition, NodeType, RangeUtils, $, ClientRect, Delay) {
43990	var isContentEditableFalse = NodeType.isContentEditableFalse;
43991
43992	return function(rootNode, isBlock) {
43993		var cursorInterval, $lastVisualCaret, caretContainerNode;
43994
43995		function getAbsoluteClientRect(node, before) {
43996			var clientRect = ClientRect.collapse(node.getBoundingClientRect(), before),
43997				docElm, scrollX, scrollY, margin, rootRect;
43998
43999			if (rootNode.tagName == 'BODY') {
44000				docElm = rootNode.ownerDocument.documentElement;
44001				scrollX = rootNode.scrollLeft || docElm.scrollLeft;
44002				scrollY = rootNode.scrollTop || docElm.scrollTop;
44003			} else {
44004				rootRect = rootNode.getBoundingClientRect();
44005				scrollX = rootNode.scrollLeft - rootRect.left;
44006				scrollY = rootNode.scrollTop - rootRect.top;
44007			}
44008
44009			clientRect.left += scrollX;
44010			clientRect.right += scrollX;
44011			clientRect.top += scrollY;
44012			clientRect.bottom += scrollY;
44013			clientRect.width = 1;
44014
44015			margin = node.offsetWidth - node.clientWidth;
44016
44017			if (margin > 0) {
44018				if (before) {
44019					margin *= -1;
44020				}
44021
44022				clientRect.left += margin;
44023				clientRect.right += margin;
44024			}
44025
44026			return clientRect;
44027		}
44028
44029		function trimInlineCaretContainers() {
44030			var contentEditableFalseNodes, node, sibling, i, data;
44031
44032			contentEditableFalseNodes = $('*[contentEditable=false]', rootNode);
44033			for (i = 0; i < contentEditableFalseNodes.length; i++) {
44034				node = contentEditableFalseNodes[i];
44035
44036				sibling = node.previousSibling;
44037				if (CaretContainer.endsWithCaretContainer(sibling)) {
44038					data = sibling.data;
44039
44040					if (data.length == 1) {
44041						sibling.parentNode.removeChild(sibling);
44042					} else {
44043						sibling.deleteData(data.length - 1, 1);
44044					}
44045				}
44046
44047				sibling = node.nextSibling;
44048				if (CaretContainer.startsWithCaretContainer(sibling)) {
44049					data = sibling.data;
44050
44051					if (data.length == 1) {
44052						sibling.parentNode.removeChild(sibling);
44053					} else {
44054						sibling.deleteData(0, 1);
44055					}
44056				}
44057			}
44058
44059			return null;
44060		}
44061
44062		function show(before, node) {
44063			var clientRect, rng, container;
44064
44065			hide();
44066
44067			if (isBlock(node)) {
44068				caretContainerNode = CaretContainer.insertBlock('p', node, before);
44069				clientRect = getAbsoluteClientRect(node, before);
44070				$(caretContainerNode).css('top', clientRect.top);
44071
44072				$lastVisualCaret = $('<div class="mce-visual-caret" data-mce-bogus="all"></div>').css(clientRect).appendTo(rootNode);
44073
44074				if (before) {
44075					$lastVisualCaret.addClass('mce-visual-caret-before');
44076				}
44077
44078				startBlink();
44079
44080				rng = node.ownerDocument.createRange();
44081				container = caretContainerNode.firstChild;
44082				rng.setStart(container, 0);
44083				rng.setEnd(container, 1);
44084			} else {
44085				caretContainerNode = CaretContainer.insertInline(node, before);
44086				rng = node.ownerDocument.createRange();
44087
44088				if (isContentEditableFalse(caretContainerNode.nextSibling)) {
44089					rng.setStart(caretContainerNode, 0);
44090					rng.setEnd(caretContainerNode, 0);
44091				} else {
44092					rng.setStart(caretContainerNode, 1);
44093					rng.setEnd(caretContainerNode, 1);
44094				}
44095
44096				return rng;
44097			}
44098
44099			return rng;
44100		}
44101
44102		function hide() {
44103			trimInlineCaretContainers();
44104
44105			if (caretContainerNode) {
44106				CaretContainer.remove(caretContainerNode);
44107				caretContainerNode = null;
44108			}
44109
44110			if ($lastVisualCaret) {
44111				$lastVisualCaret.remove();
44112				$lastVisualCaret = null;
44113			}
44114
44115			clearInterval(cursorInterval);
44116		}
44117
44118		function startBlink() {
44119			cursorInterval = Delay.setInterval(function() {
44120				$('div.mce-visual-caret', rootNode).toggleClass('mce-visual-caret-hidden');
44121			}, 500);
44122		}
44123
44124		function destroy() {
44125			Delay.clearInterval(cursorInterval);
44126		}
44127
44128		function getCss() {
44129			return (
44130				'.mce-visual-caret {' +
44131					'position: absolute;' +
44132					'background-color: black;' +
44133					'background-color: currentcolor;' +
44134				'}' +
44135				'.mce-visual-caret-hidden {' +
44136					'display: none;' +
44137				'}' +
44138				'*[data-mce-caret] {' +
44139					'position: absolute;' +
44140					'left: -1000px;' +
44141					'right: auto;' +
44142					'top: 0;' +
44143					'margin: 0;' +
44144					'padding: 0;' +
44145				'}'
44146			);
44147		}
44148
44149		return {
44150			show: show,
44151			hide: hide,
44152			getCss: getCss,
44153			destroy: destroy
44154		};
44155	};
44156});
44157
44158// Included from: js/tinymce/classes/dom/Dimensions.js
44159
44160/**
44161 * Dimensions.js
44162 *
44163 * Released under LGPL License.
44164 * Copyright (c) 1999-2015 Ephox Corp. All rights reserved
44165 *
44166 * License: http://www.tinymce.com/license
44167 * Contributing: http://www.tinymce.com/contributing
44168 */
44169
44170/**
44171 * This module measures nodes and returns client rects. The client rects has an
44172 * extra node property.
44173 *
44174 * @private
44175 * @class tinymce.dom.Dimensions
44176 */
44177define("tinymce/dom/Dimensions", [
44178	"tinymce/util/Arr",
44179	"tinymce/dom/NodeType",
44180	"tinymce/geom/ClientRect"
44181], function(Arr, NodeType, ClientRect) {
44182
44183	function getClientRects(node) {
44184		function toArrayWithNode(clientRects) {
44185			return Arr.map(clientRects, function(clientRect) {
44186				clientRect = ClientRect.clone(clientRect);
44187				clientRect.node = node;
44188
44189				return clientRect;
44190			});
44191		}
44192
44193		if (Arr.isArray(node)) {
44194			return Arr.reduce(node, function(result, node) {
44195				return result.concat(getClientRects(node));
44196			}, []);
44197		}
44198
44199		if (NodeType.isElement(node)) {
44200			return toArrayWithNode(node.getClientRects());
44201		}
44202
44203		if (NodeType.isText(node)) {
44204			var rng = node.ownerDocument.createRange();
44205
44206			rng.setStart(node, 0);
44207			rng.setEnd(node, node.data.length);
44208
44209			return toArrayWithNode(rng.getClientRects());
44210		}
44211	}
44212
44213	return {
44214		/**
44215		 * Returns the client rects for a specific node.
44216		 *
44217		 * @method getClientRects
44218		 * @param {Array/DOMNode} node Node or array of nodes to get client rects on.
44219		 * @param {Array} Array of client rects with a extra node property.
44220		 */
44221		getClientRects: getClientRects
44222	};
44223});
44224
44225// Included from: js/tinymce/classes/caret/LineWalker.js
44226
44227/**
44228 * LineWalker.js
44229 *
44230 * Released under LGPL License.
44231 * Copyright (c) 1999-2015 Ephox Corp. All rights reserved
44232 *
44233 * License: http://www.tinymce.com/license
44234 * Contributing: http://www.tinymce.com/contributing
44235 */
44236
44237/**
44238 * This module lets you walk the document line by line
44239 * returing nodes and client rects for each line.
44240 *
44241 * @private
44242 * @class tinymce.caret.LineWalker
44243 */
44244define("tinymce/caret/LineWalker", [
44245	"tinymce/util/Fun",
44246	"tinymce/util/Arr",
44247	"tinymce/dom/Dimensions",
44248	"tinymce/caret/CaretCandidate",
44249	"tinymce/caret/CaretUtils",
44250	"tinymce/caret/CaretWalker",
44251	"tinymce/caret/CaretPosition",
44252	"tinymce/geom/ClientRect"
44253], function(Fun, Arr, Dimensions, CaretCandidate, CaretUtils, CaretWalker, CaretPosition, ClientRect) {
44254	var curry = Fun.curry;
44255
44256	function findUntil(direction, rootNode, predicateFn, node) {
44257		while ((node = CaretUtils.findNode(node, direction, CaretCandidate.isEditableCaretCandidate, rootNode))) {
44258			if (predicateFn(node)) {
44259				return;
44260			}
44261		}
44262	}
44263
44264	function walkUntil(direction, isAboveFn, isBeflowFn, rootNode, predicateFn, caretPosition) {
44265		var line = 0, node, result = [], targetClientRect;
44266
44267		function add(node) {
44268			var i, clientRect, clientRects;
44269
44270			clientRects = Dimensions.getClientRects(node);
44271			if (direction == -1) {
44272				clientRects = clientRects.reverse();
44273			}
44274
44275			for (i = 0; i < clientRects.length; i++) {
44276				clientRect = clientRects[i];
44277				if (isBeflowFn(clientRect, targetClientRect)) {
44278					continue;
44279				}
44280
44281				if (result.length > 0 && isAboveFn(clientRect, Arr.last(result))) {
44282					line++;
44283				}
44284
44285				clientRect.line = line;
44286
44287				if (predicateFn(clientRect)) {
44288					return true;
44289				}
44290
44291				result.push(clientRect);
44292			}
44293		}
44294
44295		targetClientRect = Arr.last(caretPosition.getClientRects());
44296		if (!targetClientRect) {
44297			return result;
44298		}
44299
44300		node = caretPosition.getNode();
44301		add(node);
44302		findUntil(direction, rootNode, add, node);
44303
44304		return result;
44305	}
44306
44307	function aboveLineNumber(lineNumber, clientRect) {
44308		return clientRect.line > lineNumber;
44309	}
44310
44311	function isLine(lineNumber, clientRect) {
44312		return clientRect.line === lineNumber;
44313	}
44314
44315	var upUntil = curry(walkUntil, -1, ClientRect.isAbove, ClientRect.isBelow);
44316	var downUntil = curry(walkUntil, 1, ClientRect.isBelow, ClientRect.isAbove);
44317
44318	function positionsUntil(direction, rootNode, predicateFn, node) {
44319		var caretWalker = new CaretWalker(rootNode), walkFn, isBelowFn, isAboveFn,
44320			caretPosition, result = [], line = 0, clientRect, targetClientRect;
44321
44322		function getClientRect(caretPosition) {
44323			if (direction == 1) {
44324				return Arr.last(caretPosition.getClientRects());
44325			}
44326
44327			return Arr.last(caretPosition.getClientRects());
44328		}
44329
44330		if (direction == 1) {
44331			walkFn = caretWalker.next;
44332			isBelowFn = ClientRect.isBelow;
44333			isAboveFn = ClientRect.isAbove;
44334			caretPosition = CaretPosition.after(node);
44335		} else {
44336			walkFn = caretWalker.prev;
44337			isBelowFn = ClientRect.isAbove;
44338			isAboveFn = ClientRect.isBelow;
44339			caretPosition = CaretPosition.before(node);
44340		}
44341
44342		targetClientRect = getClientRect(caretPosition);
44343
44344		do {
44345			if (!caretPosition.isVisible()) {
44346				continue;
44347			}
44348
44349			clientRect = getClientRect(caretPosition);
44350
44351			if (isAboveFn(clientRect, targetClientRect)) {
44352				continue;
44353			}
44354
44355			if (result.length > 0 && isBelowFn(clientRect, Arr.last(result))) {
44356				line++;
44357			}
44358
44359			clientRect = ClientRect.clone(clientRect);
44360			clientRect.position = caretPosition;
44361			clientRect.line = line;
44362
44363			if (predicateFn(clientRect)) {
44364				return result;
44365			}
44366
44367			result.push(clientRect);
44368		} while ((caretPosition = walkFn(caretPosition)));
44369
44370		return result;
44371	}
44372
44373	return {
44374		upUntil: upUntil,
44375		downUntil: downUntil,
44376
44377		/**
44378		 * Find client rects with line and caret position until the predicate returns true.
44379		 *
44380		 * @method positionsUntil
44381		 * @param {Number} direction Direction forward/backward 1/-1.
44382		 * @param {DOMNode} rootNode Root node to walk within.
44383		 * @param {function} predicateFn Gets the client rect as it's input.
44384		 * @param {DOMNode} node Node to start walking from.
44385		 * @return {Array} Array of client rects with line and position properties.
44386		 */
44387		positionsUntil: positionsUntil,
44388
44389		isAboveLine: curry(aboveLineNumber),
44390		isLine: curry(isLine)
44391	};
44392});
44393
44394// Included from: js/tinymce/classes/caret/LineUtils.js
44395
44396/**
44397 * LineUtils.js
44398 *
44399 * Released under LGPL License.
44400 * Copyright (c) 1999-2015 Ephox Corp. All rights reserved
44401 *
44402 * License: http://www.tinymce.com/license
44403 * Contributing: http://www.tinymce.com/contributing
44404 */
44405
44406/**
44407 * Utility functions for working with lines.
44408 *
44409 * @private
44410 * @class tinymce.caret.LineUtils
44411 */
44412define("tinymce/caret/LineUtils", [
44413	"tinymce/util/Fun",
44414	"tinymce/util/Arr",
44415	"tinymce/dom/NodeType",
44416	"tinymce/dom/Dimensions",
44417	"tinymce/geom/ClientRect",
44418	"tinymce/caret/CaretUtils",
44419	"tinymce/caret/CaretCandidate"
44420], function(Fun, Arr, NodeType, Dimensions, ClientRect, CaretUtils, CaretCandidate) {
44421	var isContentEditableFalse = NodeType.isContentEditableFalse,
44422		findNode = CaretUtils.findNode,
44423		curry = Fun.curry;
44424
44425	function distanceToRectLeft(clientRect, clientX) {
44426		return Math.abs(clientRect.left - clientX);
44427	}
44428
44429	function distanceToRectRight(clientRect, clientX) {
44430		return Math.abs(clientRect.right - clientX);
44431	}
44432
44433	function findClosestClientRect(clientRects, clientX) {
44434		function isInside(clientX, clientRect) {
44435			return clientX >= clientRect.left && clientX <= clientRect.right;
44436		}
44437
44438		return Arr.reduce(clientRects, function(oldClientRect, clientRect) {
44439			var oldDistance, newDistance;
44440
44441			oldDistance = Math.min(distanceToRectLeft(oldClientRect, clientX), distanceToRectRight(oldClientRect, clientX));
44442			newDistance = Math.min(distanceToRectLeft(clientRect, clientX), distanceToRectRight(clientRect, clientX));
44443
44444			if (isInside(clientX, clientRect)) {
44445				return clientRect;
44446			}
44447
44448			if (isInside(clientX, oldClientRect)) {
44449				return oldClientRect;
44450			}
44451
44452			// cE=false has higher priority
44453			if (newDistance == oldDistance && isContentEditableFalse(clientRect.node)) {
44454				return clientRect;
44455			}
44456
44457			if (newDistance < oldDistance) {
44458				return clientRect;
44459			}
44460
44461			return oldClientRect;
44462		});
44463	}
44464
44465	function walkUntil(direction, rootNode, predicateFn, node) {
44466		while ((node = findNode(node, direction, CaretCandidate.isEditableCaretCandidate, rootNode))) {
44467			if (predicateFn(node)) {
44468				return;
44469			}
44470		}
44471	}
44472
44473	function findLineNodeRects(rootNode, targetNodeRect) {
44474		var clientRects = [];
44475
44476		function collect(checkPosFn, node) {
44477			var lineRects;
44478
44479			lineRects = Arr.filter(Dimensions.getClientRects(node), function(clientRect) {
44480				return !checkPosFn(clientRect, targetNodeRect);
44481			});
44482
44483			clientRects = clientRects.concat(lineRects);
44484
44485			return lineRects.length === 0;
44486		}
44487
44488		clientRects.push(targetNodeRect);
44489		walkUntil(-1, rootNode, curry(collect, ClientRect.isAbove), targetNodeRect.node);
44490		walkUntil(1, rootNode, curry(collect, ClientRect.isBelow), targetNodeRect.node);
44491
44492		return clientRects;
44493	}
44494
44495	function getContentEditableFalseChildren(rootNode) {
44496		return Arr.filter(Arr.toArray(rootNode.getElementsByTagName('*')), isContentEditableFalse);
44497	}
44498
44499	function caretInfo(clientRect, clientX) {
44500		return {
44501			node: clientRect.node,
44502			before: distanceToRectLeft(clientRect, clientX) < distanceToRectRight(clientRect, clientX)
44503		};
44504	}
44505
44506	function closestCaret(rootNode, clientX, clientY) {
44507		var contentEditableFalseNodeRects, closestNodeRect;
44508
44509		contentEditableFalseNodeRects = Dimensions.getClientRects(getContentEditableFalseChildren(rootNode));
44510		contentEditableFalseNodeRects = Arr.filter(contentEditableFalseNodeRects, function(clientRect) {
44511			return clientY >= clientRect.top && clientY <= clientRect.bottom;
44512		});
44513
44514		closestNodeRect = findClosestClientRect(contentEditableFalseNodeRects, clientX);
44515		if (closestNodeRect) {
44516			closestNodeRect = findClosestClientRect(findLineNodeRects(rootNode, closestNodeRect), clientX);
44517			if (closestNodeRect && isContentEditableFalse(closestNodeRect.node)) {
44518				return caretInfo(closestNodeRect, clientX);
44519			}
44520		}
44521
44522		return null;
44523	}
44524
44525	return {
44526		findClosestClientRect: findClosestClientRect,
44527		findLineNodeRects: findLineNodeRects,
44528		closestCaret: closestCaret
44529	};
44530});
44531
44532// Included from: js/tinymce/classes/DragDropOverrides.js
44533
44534/**
44535 * DragDropOverrides.js
44536 *
44537 * Released under LGPL License.
44538 * Copyright (c) 1999-2015 Ephox Corp. All rights reserved
44539 *
44540 * License: http://www.tinymce.com/license
44541 * Contributing: http://www.tinymce.com/contributing
44542 */
44543
44544/**
44545 * This module contains logic overriding the drag/drop logic of the editor.
44546 *
44547 * @private
44548 * @class tinymce.DragDropOverrides
44549 */
44550define("tinymce/DragDropOverrides", [
44551	"tinymce/dom/NodeType"
44552], function(
44553	NodeType
44554) {
44555	var isContentEditableFalse = NodeType.isContentEditableFalse;
44556
44557	function init(editor) {
44558		var $ = editor.$, rootDocument = document,
44559			editableDoc = editor.getDoc(),
44560			dom = editor.dom, state = {};
44561
44562		function isDraggable(elm) {
44563			return isContentEditableFalse(elm);
44564		}
44565
44566		function setBodyCursor(cursor) {
44567			$(editor.getBody()).css('cursor', cursor);
44568		}
44569
44570		function isValidDropTarget(elm) {
44571			if (elm == state.element || editor.dom.isChildOf(elm, state.element)) {
44572				return false;
44573			}
44574
44575			if (isContentEditableFalse(elm)) {
44576				return false;
44577			}
44578
44579			return true;
44580		}
44581
44582		function move(e) {
44583			var deltaX, deltaY, pos, viewPort,
44584				overflowX = 0, overflowY = 0, movement,
44585				clientX, clientY, rootClientRect;
44586
44587			if (e.button !== 0) {
44588				return;
44589			}
44590
44591			deltaX = e.screenX - state.screenX;
44592			deltaY = e.screenY - state.screenY;
44593			movement = Math.max(Math.abs(deltaX), Math.abs(deltaY));
44594
44595			if (!state.dragging && movement > 10) {
44596				state.dragging = true;
44597				setBodyCursor('default');
44598
44599				state.clone = state.element.cloneNode(true);
44600
44601				pos = dom.getPos(state.element);
44602				state.relX = state.clientX - pos.x;
44603				state.relY = state.clientY - pos.y;
44604				state.width = state.element.offsetWidth;
44605				state.height = state.element.offsetHeight;
44606
44607				$(state.clone).css({
44608					width: state.width,
44609					height: state.height
44610				}).removeAttr('data-mce-selected');
44611
44612				state.ghost = $('<div>').css({
44613					position: 'absolute',
44614					opacity: 0.5,
44615					overflow: 'hidden',
44616					width: state.width,
44617					height: state.height
44618				}).attr({
44619					'data-mce-bogus': 'all',
44620					unselectable: 'on',
44621					contenteditable: 'false'
44622				}).addClass('mce-drag-container mce-reset').
44623					append(state.clone).
44624					appendTo(editor.getBody())[0];
44625
44626				viewPort = editor.dom.getViewPort(editor.getWin());
44627				state.maxX = viewPort.w;
44628				state.maxY = viewPort.h;
44629			}
44630
44631			if (state.dragging) {
44632				editor.selection.placeCaretAt(e.clientX, e.clientY);
44633
44634				clientX = state.clientX + deltaX - state.relX;
44635				clientY = state.clientY + deltaY + 5;
44636
44637				if (clientX + state.width > state.maxX) {
44638					overflowX = (clientX + state.width) - state.maxX;
44639				}
44640
44641				if (clientY + state.height > state.maxY) {
44642					overflowY = (clientY + state.height) - state.maxY;
44643				}
44644
44645				if (editor.getBody().nodeName != 'BODY') {
44646					rootClientRect = editor.getBody().getBoundingClientRect();
44647				} else {
44648					rootClientRect = {left: 0, top: 0};
44649				}
44650
44651				$(state.ghost).css({
44652					left: clientX - rootClientRect.left,
44653					top: clientY - rootClientRect.top,
44654					width: state.width - overflowX,
44655					height: state.height - overflowY
44656				});
44657			}
44658		}
44659
44660		function drop() {
44661			if (state.dragging) {
44662				// Hack for IE since it doesn't sync W3C Range with IE Specific range
44663				editor.selection.setRng(editor.selection.getSel().getRangeAt(0));
44664
44665				if (isValidDropTarget(editor.selection.getNode())) {
44666					editor.undoManager.transact(function() {
44667						editor.insertContent(dom.getOuterHTML(state.element));
44668						$(state.element).remove();
44669					});
44670				}
44671			}
44672
44673			stop();
44674		}
44675
44676		function start(e) {
44677			stop();
44678
44679			if (isDraggable(e.target)) {
44680				if (editor.fire('dragstart', {target: e.target}).isDefaultPrevented()) {
44681					return;
44682				}
44683
44684				editor.on('mousemove', move);
44685				editor.on('mouseup', drop);
44686
44687				if (rootDocument != editableDoc) {
44688					dom.bind(rootDocument, 'mousemove', move);
44689					dom.bind(rootDocument, 'mouseup', drop);
44690				}
44691
44692				state = {
44693					screenX: e.screenX,
44694					screenY: e.screenY,
44695					clientX: e.clientX,
44696					clientY: e.clientY,
44697					element: e.target
44698				};
44699			}
44700		}
44701
44702		function stop() {
44703			$(state.ghost).remove();
44704			setBodyCursor(null);
44705
44706			editor.off('mousemove', move);
44707			editor.off('mouseup', stop);
44708
44709			if (rootDocument != editableDoc) {
44710				dom.unbind(rootDocument, 'mousemove', move);
44711				dom.unbind(rootDocument, 'mouseup', stop);
44712			}
44713
44714			state = {};
44715		}
44716
44717		editor.on('mousedown', start);
44718
44719		// Blocks drop inside cE=false on IE
44720		editor.on('drop', function(e) {
44721			var realTarget = editor.getDoc().elementFromPoint(e.clientX, e.clientY);
44722
44723			if (isContentEditableFalse(realTarget) || isContentEditableFalse(editor.dom.getContentEditableParent(realTarget))) {
44724				e.preventDefault();
44725			}
44726		});
44727	}
44728
44729	return {
44730		init: init
44731	};
44732});
44733
44734// Included from: js/tinymce/classes/SelectionOverrides.js
44735
44736/**
44737 * SelectionOverrides.js
44738 *
44739 * Released under LGPL License.
44740 * Copyright (c) 1999-2015 Ephox Corp. All rights reserved
44741 *
44742 * License: http://www.tinymce.com/license
44743 * Contributing: http://www.tinymce.com/contributing
44744 */
44745
44746/**
44747 * This module contains logic overriding the selection with keyboard/mouse
44748 * around contentEditable=false regions.
44749 *
44750 * @example
44751 * // Disable the default cE=false selection
44752 * tinymce.activeEditor.on('ShowCaret ObjectSelected', function(e) {
44753 *     e.preventDefault();
44754 * });
44755 *
44756 * @private
44757 * @class tinymce.SelectionOverrides
44758 */
44759define("tinymce/SelectionOverrides", [
44760	"tinymce/Env",
44761	"tinymce/caret/CaretWalker",
44762	"tinymce/caret/CaretPosition",
44763	"tinymce/caret/CaretContainer",
44764	"tinymce/caret/CaretUtils",
44765	"tinymce/caret/FakeCaret",
44766	"tinymce/caret/LineWalker",
44767	"tinymce/caret/LineUtils",
44768	"tinymce/dom/NodeType",
44769	"tinymce/dom/RangeUtils",
44770	"tinymce/util/VK",
44771	"tinymce/util/Fun",
44772	"tinymce/util/Arr",
44773	"tinymce/util/Delay",
44774	"tinymce/DragDropOverrides"
44775], function(
44776	Env, CaretWalker, CaretPosition, CaretContainer, CaretUtils, FakeCaret, LineWalker,
44777	LineUtils, NodeType, RangeUtils, VK, Fun, Arr, Delay, DragDropOverrides
44778) {
44779	var curry = Fun.curry,
44780		isContentEditableTrue = NodeType.isContentEditableTrue,
44781		isContentEditableFalse = NodeType.isContentEditableFalse,
44782		isElement = NodeType.isElement,
44783		isAfterContentEditableFalse = CaretUtils.isAfterContentEditableFalse,
44784		isBeforeContentEditableFalse = CaretUtils.isBeforeContentEditableFalse,
44785		getSelectedNode = RangeUtils.getSelectedNode;
44786
44787	function getVisualCaretPosition(walkFn, caretPosition) {
44788		while ((caretPosition = walkFn(caretPosition))) {
44789			if (caretPosition.isVisible()) {
44790				return caretPosition;
44791			}
44792		}
44793
44794		return caretPosition;
44795	}
44796
44797	function SelectionOverrides(editor) {
44798		var rootNode = editor.getBody(), caretWalker = new CaretWalker(rootNode);
44799		var getNextVisualCaretPosition = curry(getVisualCaretPosition, caretWalker.next);
44800		var getPrevVisualCaretPosition = curry(getVisualCaretPosition, caretWalker.prev),
44801			fakeCaret = new FakeCaret(editor.getBody(), isBlock),
44802			realSelectionId = 'sel-' + editor.dom.uniqueId(),
44803			selectedContentEditableNode, $ = editor.$;
44804
44805		function isBlock(node) {
44806			return editor.dom.isBlock(node);
44807		}
44808
44809		function setRange(range) {
44810			//console.log('setRange', range);
44811			if (range) {
44812				editor.selection.setRng(range);
44813			}
44814		}
44815
44816		function getRange() {
44817			return editor.selection.getRng();
44818		}
44819
44820		function scrollIntoView(node, alignToTop) {
44821			editor.selection.scrollIntoView(node, alignToTop);
44822		}
44823
44824		function showCaret(direction, node, before) {
44825			var e;
44826
44827			e = editor.fire('ShowCaret', {
44828				target: node,
44829				direction: direction,
44830				before: before
44831			});
44832
44833			if (e.isDefaultPrevented()) {
44834				return null;
44835			}
44836
44837			scrollIntoView(node, direction === -1);
44838
44839			return fakeCaret.show(before, node);
44840		}
44841
44842		function selectNode(node) {
44843			var e;
44844
44845			e = editor.fire('ObjectSelected', {target: node});
44846			if (e.isDefaultPrevented()) {
44847				return null;
44848			}
44849
44850			fakeCaret.hide();
44851			return getNodeRange(node);
44852		}
44853
44854		function getNodeRange(node) {
44855			var rng = node.ownerDocument.createRange();
44856
44857			rng.selectNode(node);
44858
44859			return rng;
44860		}
44861
44862		function isMoveInsideSameBlock(fromCaretPosition, toCaretPosition) {
44863			var inSameBlock = CaretUtils.isInSameBlock(fromCaretPosition, toCaretPosition);
44864
44865			// Handle bogus BR <p>abc|<br></p>
44866			if (!inSameBlock && NodeType.isBr(fromCaretPosition.getNode())) {
44867				return true;
44868			}
44869
44870			return inSameBlock;
44871		}
44872
44873		function getNormalizedRangeEndPoint(direction, range) {
44874			range = CaretUtils.normalizeRange(direction, rootNode, range);
44875
44876			if (direction == -1) {
44877				return CaretPosition.fromRangeStart(range);
44878			}
44879
44880			return CaretPosition.fromRangeEnd(range);
44881		}
44882
44883		function isRangeInCaretContainerBlock(range) {
44884			return CaretContainer.isCaretContainerBlock(range.startContainer);
44885		}
44886
44887		function moveToCeFalseHorizontally(direction, getNextPosFn, isBeforeContentEditableFalseFn, range) {
44888			var node, caretPosition, peekCaretPosition, rangeIsInContainerBlock;
44889
44890			if (!range.collapsed) {
44891				node = getSelectedNode(range);
44892				if (isContentEditableFalse(node)) {
44893					return showCaret(direction, node, direction == -1);
44894				}
44895			}
44896
44897			rangeIsInContainerBlock = isRangeInCaretContainerBlock(range);
44898			caretPosition = getNormalizedRangeEndPoint(direction, range);
44899
44900			if (isBeforeContentEditableFalseFn(caretPosition)) {
44901				return selectNode(caretPosition.getNode(direction == -1));
44902			}
44903
44904			caretPosition = getNextPosFn(caretPosition);
44905			if (!caretPosition) {
44906				if (rangeIsInContainerBlock) {
44907					return range;
44908				}
44909
44910				return null;
44911			}
44912
44913			if (isBeforeContentEditableFalseFn(caretPosition)) {
44914				return showCaret(direction, caretPosition.getNode(direction == -1), direction == 1);
44915			}
44916
44917			// Peek ahead for handling of ab|c<span cE=false> -> abc|<span cE=false>
44918			peekCaretPosition = getNextPosFn(caretPosition);
44919			if (isBeforeContentEditableFalseFn(peekCaretPosition)) {
44920				if (isMoveInsideSameBlock(caretPosition, peekCaretPosition)) {
44921					return showCaret(direction, peekCaretPosition.getNode(direction == -1), direction == 1);
44922				}
44923			}
44924
44925			if (rangeIsInContainerBlock) {
44926				return renderRangeCaret(caretPosition.toRange());
44927			}
44928
44929			return null;
44930		}
44931
44932		function moveToCeFalseVertically(direction, walkerFn, range) {
44933			var caretPosition, linePositions, nextLinePositions,
44934				closestNextLineRect, caretClientRect, clientX,
44935				dist1, dist2, contentEditableFalseNode;
44936
44937			contentEditableFalseNode = getSelectedNode(range);
44938			caretPosition = getNormalizedRangeEndPoint(direction, range);
44939			linePositions = walkerFn(rootNode, LineWalker.isAboveLine(1), caretPosition);
44940			nextLinePositions = Arr.filter(linePositions, LineWalker.isLine(1));
44941			caretClientRect = Arr.last(caretPosition.getClientRects());
44942
44943			if (isBeforeContentEditableFalse(caretPosition)) {
44944				contentEditableFalseNode = caretPosition.getNode();
44945			}
44946
44947			if (isAfterContentEditableFalse(caretPosition)) {
44948				contentEditableFalseNode = caretPosition.getNode(true);
44949			}
44950
44951			if (!caretClientRect) {
44952				return null;
44953			}
44954
44955			clientX = caretClientRect.left;
44956
44957			closestNextLineRect = LineUtils.findClosestClientRect(nextLinePositions, clientX);
44958			if (closestNextLineRect) {
44959				if (isContentEditableFalse(closestNextLineRect.node)) {
44960					dist1 = Math.abs(clientX - closestNextLineRect.left);
44961					dist2 = Math.abs(clientX - closestNextLineRect.right);
44962
44963					return showCaret(direction, closestNextLineRect.node, dist1 < dist2);
44964				}
44965			}
44966
44967			if (contentEditableFalseNode) {
44968				var caretPositions = LineWalker.positionsUntil(direction, rootNode, LineWalker.isAboveLine(1), contentEditableFalseNode);
44969
44970				closestNextLineRect = LineUtils.findClosestClientRect(Arr.filter(caretPositions, LineWalker.isLine(1)), clientX);
44971				if (closestNextLineRect) {
44972					return renderRangeCaret(closestNextLineRect.position.toRange());
44973				}
44974
44975				closestNextLineRect = Arr.last(Arr.filter(caretPositions, LineWalker.isLine(0)));
44976				if (closestNextLineRect) {
44977					return renderRangeCaret(closestNextLineRect.position.toRange());
44978				}
44979			}
44980		}
44981
44982		function exitPreBlock(direction, range) {
44983			var pre, caretPos, newBlock;
44984
44985			function createTextBlock() {
44986				var textBlock = editor.dom.create(editor.settings.forced_root_block);
44987
44988				if (!Env.ie || Env.ie >= 11) {
44989					textBlock.innerHTML = '<br data-mce-bogus="1">';
44990				}
44991
44992				return textBlock;
44993			}
44994
44995			if (range.collapsed && editor.settings.forced_root_block) {
44996				pre = editor.dom.getParent(range.startContainer, 'PRE');
44997				if (!pre) {
44998					return;
44999				}
45000
45001				if (direction == 1) {
45002					caretPos = getNextVisualCaretPosition(CaretPosition.fromRangeStart(range));
45003				} else {
45004					caretPos = getPrevVisualCaretPosition(CaretPosition.fromRangeStart(range));
45005				}
45006
45007				if (!caretPos) {
45008					newBlock = createTextBlock();
45009
45010					if (direction == 1) {
45011						editor.$(pre).after(newBlock);
45012					} else {
45013						editor.$(pre).before(newBlock);
45014					}
45015
45016					editor.selection.select(newBlock, true);
45017					editor.selection.collapse();
45018				}
45019			}
45020		}
45021
45022		function moveH(direction, getNextPosFn, isBeforeContentEditableFalseFn, range) {
45023			var newRange;
45024
45025			newRange = moveToCeFalseHorizontally(direction, getNextPosFn, isBeforeContentEditableFalseFn, range);
45026			if (newRange) {
45027				return newRange;
45028			}
45029
45030			newRange = exitPreBlock(direction, range);
45031			if (newRange) {
45032				return newRange;
45033			}
45034
45035			return null;
45036		}
45037
45038		function moveV(direction, walkerFn, range) {
45039			var newRange;
45040
45041			newRange = moveToCeFalseVertically(direction, walkerFn, range);
45042			if (newRange) {
45043				return newRange;
45044			}
45045
45046			newRange = exitPreBlock(direction, range);
45047			if (newRange) {
45048				return newRange;
45049			}
45050
45051			return null;
45052		}
45053
45054		function getBlockCaretContainer() {
45055			return $('*[data-mce-caret]')[0];
45056		}
45057
45058		function showBlockCaretContainer(blockCaretContainer) {
45059			blockCaretContainer = $(blockCaretContainer);
45060
45061			if (blockCaretContainer.attr('data-mce-caret')) {
45062				fakeCaret.hide();
45063				blockCaretContainer.removeAttr('data-mce-caret');
45064				blockCaretContainer.removeAttr('data-mce-bogus');
45065				blockCaretContainer.removeAttr('style');
45066
45067				// Removes control rect on IE
45068				setRange(getRange());
45069				scrollIntoView(blockCaretContainer[0]);
45070			}
45071		}
45072
45073		function renderCaretAtRange(range) {
45074			var caretPosition;
45075
45076			range = CaretUtils.normalizeRange(1, rootNode, range);
45077			caretPosition = CaretPosition.fromRangeStart(range);
45078
45079			if (isContentEditableFalse(caretPosition.getNode())) {
45080				return showCaret(1, caretPosition.getNode(), !caretPosition.isAt
45080End());
45081			}
45082
45083			if (isContentEditableFalse(caretPosition.getNode(true))) {
45084				return showCaret(1, caretPosition.getNode(true), false);
45085			}
45086
45087			fakeCaret.hide();
45088
45089			return null;
45090		}
45091
45092		function renderRangeCaret(range) {
45093			var caretRange;
45094
45095			if (!range || !range.collapsed) {
45096				return range;
45097			}
45098
45099			caretRange = renderCaretAtRange(range);
45100			if (caretRange) {
45101				return caretRange;
45102			}
45103
45104			return range;
45105		}
45106
45107		function deleteContentEditableNode(node) {
45108			var nextCaretPosition, prevCaretPosition, prevCeFalseElm, nextElement;
45109
45110			if (!isContentEditableFalse(node)) {
45111				return null;
45112			}
45113
45114			if (isContentEditableFalse(node.previousSibling)) {
45115				prevCeFalseElm = node.previousSibling;
45116			}
45117
45118			prevCaretPosition = getPrevVisualCaretPosition(CaretPosition.before(node));
45119			if (!prevCaretPosition) {
45120				nextCaretPosition = getNextVisualCaretPosition(CaretPosition.after(node));
45121			}
45122
45123			if (nextCaretPosition && isElement(nextCaretPosition.getNode())) {
45124				nextElement = nextCaretPosition.getNode();
45125			}
45126
45127			CaretContainer.remove(node.previousSibling);
45128			CaretContainer.remove(node.nextSibling);
45129			editor.dom.remove(node);
45130			clearContentEditableSelection();
45131
45132			if (editor.dom.isEmpty(editor.getBody())) {
45133				editor.setContent('');
45134				editor.focus();
45135				return;
45136			}
45137
45138			if (prevCeFalseElm) {
45139				return CaretPosition.after(prevCeFalseElm).toRange();
45140			}
45141
45142			if (nextElement) {
45143				return CaretPosition.before(nextElement).toRange();
45144			}
45145
45146			if (prevCaretPosition) {
45147				return prevCaretPosition.toRange();
45148			}
45149
45150			if (nextCaretPosition) {
45151				return nextCaretPosition.toRange();
45152			}
45153
45154			return null;
45155		}
45156
45157		function backspaceDelete(direction, beforeFn, range) {
45158			var node, caretPosition;
45159
45160			if (!range.collapsed) {
45161				node = getSelectedNode(range);
45162				if (isContentEditableFalse(node)) {
45163					return renderRangeCaret(deleteContentEditableNode(node));
45164				}
45165			}
45166
45167			caretPosition = getNormalizedRangeEndPoint(direction, range);
45168
45169			if (beforeFn(caretPosition)) {
45170				return renderRangeCaret(deleteContentEditableNode(caretPosition.getNode(direction == -1)));
45171			}
45172		}
45173
45174		function registerEvents() {
45175			var right = curry(moveH, 1, getNextVisualCaretPosition, isBeforeContentEditableFalse);
45176			var left = curry(moveH, -1, getPrevVisualCaretPosition, isAfterContentEditableFalse);
45177			var deleteForward = curry(backspaceDelete, 1, isBeforeContentEditableFalse);
45178			var backspace = curry(backspaceDelete, -1, isAfterContentEditableFalse);
45179			var up = curry(moveV, -1, LineWalker.upUntil);
45180			var down = curry(moveV, 1, LineWalker.downUntil);
45181
45182			function override(moveFn) {
45183				var range = moveFn(getRange());
45184
45185				if (range) {
45186					setRange(range);
45187					return true;
45188				}
45189
45190				return false;
45191			}
45192
45193			function getContentEditableRoot(node) {
45194				var root = editor.getBody();
45195
45196				while (node && node != root) {
45197					if (isContentEditableTrue(node) || isContentEditableFalse(node)) {
45198						return node;
45199					}
45200
45201					node = node.parentNode;
45202				}
45203
45204				return null;
45205			}
45206
45207			// Some browsers (Chrome) lets you place the caret after a cE=false
45208			// Make sure we render the caret container in this case
45209			editor.on('mouseup', function() {
45210				var range = getRange();
45211
45212				if (range.collapsed) {
45213					setRange(renderCaretAtRange(range));
45214				}
45215			});
45216
45217			editor.on('mousedown', function(e) {
45218				var contentEditableRoot;
45219
45220				contentEditableRoot	= getContentEditableRoot(e.target);
45221				if (contentEditableRoot) {
45222					if (isContentEditableFalse(contentEditableRoot)) {
45223						e.preventDefault();
45224						setContentEditableSelection(selectNode(contentEditableRoot), false);
45225					} else {
45226						editor.selection.placeCaretAt(e.clientX, e.clientY);
45227					}
45228				} else {
45229					clearContentEditableSelection();
45230					fakeCaret.hide();
45231
45232					var caretInfo = LineUtils.closestCaret(rootNode, e.clientX, e.clientY);
45233					if (caretInfo) {
45234						e.preventDefault();
45235						editor.getBody().focus();
45236						setRange(showCaret(1, caretInfo.node, caretInfo.before));
45237					}
45238				}
45239			});
45240
45241			editor.on('keydown', function(e) {
45242				var prevent;
45243
45244				if (VK.modifierPressed(e)) {
45245					return;
45246				}
45247
45248				switch (e.keyCode) {
45249					case VK.RIGHT:
45250						prevent = override(right);
45251						break;
45252
45253					case VK.DOWN:
45254						prevent = override(down);
45255						break;
45256
45257					case VK.LEFT:
45258						prevent = override(left);
45259						break;
45260
45261					case VK.UP:
45262						prevent = override(up);
45263						break;
45264
45265					case VK.DELETE:
45266						prevent = override(deleteForward);
45267						break;
45268
45269					case VK.BACKSPACE:
45270						prevent = override(backspace);
45271						break;
45272
45273					default:
45274						prevent = isContentEditableFalse(editor.selection.getNode());
45275						break;
45276				}
45277
45278				if (prevent) {
45279					e.preventDefault();
45280				}
45281			});
45282
45283			function paddEmptyContentEditableArea() {
45284				var br, ceRoot = getContentEditableRoot(editor.selection.getNode());
45285
45286				if (isContentEditableTrue(ceRoot) && isBlock(ceRoot) && editor.dom.isEmpty(ceRoot)) {
45287					br = editor.dom.create('br', {"data-mce-bogus": "1"});
45288					editor.$(ceRoot).empty().append(br);
45289					editor.selection.setRng(CaretPosition.before(br).toRange());
45290				}
45291			}
45292
45293			function handleBlockContainer(e) {
45294				var blockCaretContainer = getBlockCaretContainer();
45295
45296				if (!blockCaretContainer) {
45297					return;
45298				}
45299
45300				if (e.type == 'compositionstart') {
45301					e.preventDefault();
45302					e.stopPropagation();
45303					showBlockCaretContainer(blockCaretContainer);
45304					return;
45305				}
45306
45307				if (blockCaretContainer.innerHTML != '&nbsp;') {
45308					showBlockCaretContainer(blockCaretContainer);
45309				}
45310			}
45311
45312			function handleEmptyBackspaceDelete(e) {
45313				var prevent;
45314
45315				switch (e.keyCode) {
45316					case VK.DELETE:
45317						prevent = paddEmptyContentEditableArea();
45318						break;
45319
45320					case VK.BACKSPACE:
45321						prevent = paddEmptyContentEditableArea();
45322						break;
45323				}
45324
45325				if (prevent) {
45326					e.preventDefault();
45327				}
45328			}
45329
45330			// Must be added to "top" since undoManager needs to be executed after
45331			editor.on('keyup compositionstart', function(e) {
45332				handleBlockContainer(e);
45333				handleEmptyBackspaceDelete(e);
45334			}, true);
45335
45336			editor.on('cut', function() {
45337				var node = editor.selection.getNode();
45338
45339				if (isContentEditableFalse(node)) {
45340					Delay.setEditorTimeout(editor, function() {
45341						setRange(renderRangeCaret(deleteContentEditableNode(node)));
45342					});
45343				}
45344			});
45345
45346			editor.on('getSelectionRange', function(e) {
45347				var rng = e.range;
45348
45349				if (selectedContentEditableNode) {
45350					if (!selectedContentEditableNode.parentNode) {
45351						selectedContentEditableNode = null;
45352						return;
45353					}
45354
45355					rng = rng.cloneRange();
45356					rng.selectNode(selectedContentEditableNode);
45357					e.range = rng;
45358				}
45359			});
45360
45361			editor.on('setSelectionRange', function(e) {
45362				var rng;
45363
45364				rng = setContentEditableSelection(e.range);
45365				if (rng) {
45366					e.range = rng;
45367				}
45368			});
45369
45370			editor.on('focus', function() {
45371				// Make sure we have a proper fake caret on focus
45372				Delay.setEditorTimeout(editor, function() {
45373					editor.selection.setRng(renderRangeCaret(editor.selection.getRng()));
45374				});
45375			});
45376
45377			DragDropOverrides.init(editor);
45378		}
45379
45380		function addCss() {
45381			var styles = editor.contentStyles, rootClass = '.mce-content-body';
45382
45383			styles.push(fakeCaret.getCss());
45384			styles.push(
45385				rootClass + ' .mce-offscreen-selection {' +
45386					'position: absolute;' +
45387					'left: -9999999999px;' +
45388					'width: 100px' +
45389					'height: 100px' +
45390				'}' +
45391				rootClass + ' *[contentEditable=false] {' +
45392					'cursor: default;' +
45393				'}' +
45394				rootClass + ' *[contentEditable=true] {' +
45395					'cursor: text;' +
45396				'}'
45397			);
45398		}
45399
45400		function isRangeInCaretContainer(rng) {
45401			return CaretContainer.isCaretContainer(rng.startContainer) || CaretContainer.isCaretContainer(rng.endContainer);
45402		}
45403
45404		function setContentEditableSelection(range, fireEvent) {
45405			var node, $ = editor.$, dom = editor.dom, $realSelectionContainer, sel,
45406				startContainer, startOffset, endOffset, e, caretPosition;
45407
45408			if (!range) {
45409				clearContentEditableSelection();
45410				return null;
45411			}
45412
45413			if (range.collapsed) {
45414				clearContentEditableSelection();
45415
45416				if (!isRangeInCaretContainer(range)) {
45417					caretPosition = getNormalizedRangeEndPoint(1, range);
45418
45419					if (isContentEditableFalse(caretPosition.getNode())) {
45420						return showCaret(1, caretPosition.getNode(), !caretPosition.isAtEnd());
45421					}
45422
45423					if (isContentEditableFalse(caretPosition.getNode(true))) {
45424						return showCaret(1, caretPosition.getNode(true), false);
45425					}
45426				}
45427
45428				return null;
45429			}
45430
45431			startContainer = range.startContainer;
45432			startOffset = range.startOffset;
45433			endOffset = range.endOffset;
45434
45435			// Normalizes <span cE=false>[</span>] to [<span cE=false></span>]
45436			if (startContainer.nodeType == 3 && startOffset == 0 && isContentEditableFalse(startContainer.parentNode)) {
45437				startContainer = startContainer.parentNode;
45438				startOffset = dom.nodeIndex(startContainer);
45439				startContainer = startContainer.parentNode;
45440			}
45441
45442			if (startContainer.nodeType != 1) {
45443				clearContentEditableSelection();
45444				return null;
45445			}
45446
45447			if (endOffset == startOffset + 1) {
45448				node = startContainer.childNodes[startOffset];
45449			}
45450
45451			if (isContentEditableFalse(node)) {
45452				if (fireEvent !== false) {
45453					e = editor.fire('ObjectSelected', {target: node});
45454					if (e.isDefaultPrevented()) {
45455						clearContentEditableSelection();
45456						return null;
45457					}
45458				}
45459
45460				$realSelectionContainer = $('#' + realSelectionId);
45461				if ($realSelectionContainer.length === 0) {
45462					$realSelectionContainer = $(
45463						'<div data-mce-bogus="all" class="mce-offscreen-selection"></div>'
45464					).attr('id', realSelectionId);
45465
45466					$realSelectionContainer.appendTo(editor.getBody());
45467				}
45468
45469				$realSelectionContainer.empty().append('\u00a0').append(node.cloneNode(true)).append('\u00a0').css({
45470					top: dom.getPos(node, editor.getBody()).y
45471				});
45472
45473				range = editor.dom.createRng();
45474				range.setStart($realSelectionContainer[0].firstChild, 1);
45475				range.setEnd($realSelectionContainer[0].lastChild, 0);
45476
45477				editor.getBody().focus();
45478				$realSelectionContainer[0].focus();
45479				sel = editor.selection.getSel();
45480				sel.removeAllRanges();
45481				sel.addRange(range);
45482
45483				editor.$('*[data-mce-selected]').removeAttr('data-mce-selected');
45484				node.setAttribute('data-mce-selected', 1);
45485				selectedContentEditableNode = node;
45486
45487				return range;
45488			}
45489
45490			clearContentEditableSelection();
45491			return null;
45492		}
45493
45494		function clearContentEditableSelection() {
45495			if (selectedContentEditableNode) {
45496				selectedContentEditableNode.removeAttribute('data-mce-selected');
45497				editor.$('#' + realSelectionId).remove();
45498				selectedContentEditableNode = null;
45499			}
45500		}
45501
45502		function destroy() {
45503			fakeCaret.destroy();
45504			selectedContentEditableNode = null;
45505		}
45506
45507		if (Env.ceFalse) {
45508			registerEvents();
45509			addCss();
45510		}
45511
45512		return {
45513			showBlockCaretContainer: showBlockCaretContainer,
45514			destroy: destroy
45515		};
45516	}
45517
45518	return SelectionOverrides;
45519});
45520
45521// Included from: js/tinymce/classes/Editor.js
45522
45523/**
45524 * Editor.js
45525 *
45526 * Released under LGPL License.
45527 * Copyright (c) 1999-2015 Ephox Corp. All rights reserved
45528 *
45529 * License: http://www.tinymce.com/license
45530 * Contributing: http://www.tinymce.com/contributing
45531 */
45532
45533/*jshint scripturl:true */
45534
45535/**
45536 * Include the base event class documentation.
45537 *
45538 * @include ../../../tools/docs/tinymce.Event.js
45539 */
45540
45541/**
45542 * This class contains the core logic for a TinyMCE editor.
45543 *
45544 * @class tinymce.Editor
45545 * @mixes tinymce.util.Observable
45546 * @example
45547 * // Add a class to all paragraphs in the editor.
45548 * tinymce.activeEditor.dom.addClass(tinymce.activeEditor.dom.select('p'), 'someclass');
45549 *
45550 * // Gets the current editors selection as text
45551 * tinymce.activeEditor.selection.getContent({format: 'text'});
45552 *
45553 * // Creates a new editor instance
45554 * var ed = new tinymce.Editor('textareaid', {
45555 *     some_setting: 1
45556 * }, tinymce.EditorManager);
45557 *
45558 * // Select each item the user clicks on
45559 * ed.on('click', function(e) {
45560 *     ed.selection.select(e.target);
45561 * });
45562 *
45563 * ed.render();
45564 */
45565define("tinymce/Editor", [
45566	"tinymce/dom/DOMUtils",
45567	"tinymce/dom/DomQuery",
45568	"tinymce/AddOnManager",
45569	"tinymce/NodeChange",
45570	"tinymce/html/Node",
45571	"tinymce/dom/Serializer",
45572	"tinymce/html/Serializer",
45573	"tinymce/dom/Selection",
45574	"tinymce/Formatter",
45575	"tinymce/UndoManager",
45576	"tinymce/EnterKey",
45577	"tinymce/ForceBlocks",
45578	"tinymce/EditorCommands",
45579	"tinymce/util/URI",
45580	"tinymce/dom/ScriptLoader",
45581	"tinymce/dom/EventUtils",
45582	"tinymce/WindowManager",
45583	"tinymce/NotificationManager",
45584	"tinymce/html/Schema",
45585	"tinymce/html/DomParser",
45586	"tinymce/util/Quirks",
45587	"tinymce/Env",
45588	"tinymce/util/Tools",
45589	"tinymce/util/Delay",
45590	"tinymce/EditorObservable",
45591	"tinymce/Mode",
45592	"tinymce/Shortcuts",
45593	"tinymce/EditorUpload",
45594	"tinymce/SelectionOverrides"
45595], function(
45596	DOMUtils, DomQuery, AddOnManager, NodeChange, Node, DomSerializer, Serializer,
45597	Selection, Formatter, UndoManager, EnterKey, ForceBlocks, EditorCommands,
45598	URI, ScriptLoader, EventUtils, WindowManager, NotificationManager,
45599	Schema, DomParser, Quirks, Env, Tools, Delay, EditorObservable, Mode, Shortcuts, EditorUpload,
45600	SelectionOverrides
45601) {
45602	// Shorten these names
45603	var DOM = DOMUtils.DOM, ThemeManager = AddOnManager.ThemeManager, PluginManager = AddOnManager.PluginManager;
45604	var extend = Tools.extend, each = Tools.each, explode = Tools.explode;
45605	var inArray = Tools.inArray, trim = Tools.trim, resolve = Tools.resolve;
45606	var Event = EventUtils.Event;
45607	var isGecko = Env.gecko, ie = Env.ie;
45608
45609	/**
45610	 * Include documentation for all the events.
45611	 *
45612	 * @include ../../../tools/docs/tinymce.Editor.js
45613	 */
45614
45615	/**
45616	 * Constructs a editor instance by id.
45617	 *
45618	 * @constructor
45619	 * @method Editor
45620	 * @param {String} id Unique id for the editor.
45621	 * @param {Object} settings Settings for the editor.
45622	 * @param {tinymce.EditorManager} editorManager EditorManager instance.
45623	 */
45624	function Editor(id, settings, editorManager) {
45625		var self = this, documentBaseUrl, baseUri;
45626
45627		documentBaseUrl = self.documentBaseUrl = editorManager.documentBaseURL;
45628		baseUri = editorManager.baseURI;
45629
45630		/**
45631		 * Name/value collection with editor settings.
45632		 *
45633		 * @property settings
45634		 * @type Object
45635		 * @example
45636		 * // Get the value of the theme setting
45637		 * tinymce.activeEditor.windowManager.alert("You are using the " + tinymce.activeEditor.settings.theme + " theme");
45638		 */
45639		self.settings = settings = extend({
45640			id: id,
45641			theme: 'modern',
45642			delta_width: 0,
45643			delta_height: 0,
45644			popup_css: '',
45645			plugins: '',
45646			document_base_url: documentBaseUrl,
45647			add_form_submit_trigger: true,
45648			submit_patch: true,
45649			add_unload_trigger: true,
45650			convert_urls: true,
45651			relative_urls: true,
45652			remove_script_host: true,
45653			object_resizing: true,
45654			doctype: '<!DOCTYPE html>',
45655			visual: true,
45656			font_size_style_values: 'xx-small,x-small,small,medium,large,x-large,xx-large',
45657
45658			// See: http://www.w3.org/TR/CSS2/fonts.html#propdef-font-size
45659			font_size_legacy_values: 'xx-small,small,medium,large,x-large,xx-large,300%',
45660			forced_root_block: 'p',
45661			hidden_input: true,
45662			padd_empty_editor: true,
45663			render_ui: true,
45664			indentation: '30px',
45665			inline_styles: true,
45666			convert_fonts_to_spans: true,
45667			indent: 'simple',
45668			indent_before: 'p,h1,h2,h3,h4,h5,h6,blockquote,div,title,style,pre,script,td,th,ul,ol,li,dl,dt,dd,area,table,thead,' +
45669				'tfoot,tbody,tr,section,article,hgroup,aside,figure,figcaption,option,optgroup,datalist',
45670			indent_after: 'p,h1,h2,h3,h4,h5,h6,blockquote,div,title,style,pre,script,td,th,ul,ol,li,dl,dt,dd,area,table,thead,' +
45671				'tfoot,tbody,tr,section,article,hgroup,aside,figure,figcaption,option,optgroup,datalist',
45672			validate: true,
45673			entity_encoding: 'named',
45674			url_converter: self.convertURL,
45675			url_converter_scope: self,
45676			ie7_compat: true
45677		}, settings);
45678
45679		AddOnManager.language = settings.language || 'en';
45680		AddOnManager.languageLoad = settings.language_load;
45681
45682		AddOnManager.baseURL = editorManager.baseURL;
45683
45684		/**
45685		 * Editor instance id, normally the same as the div/textarea that was replaced.
45686		 *
45687		 * @property id
45688		 * @type String
45689		 */
45690		self.id = settings.id = id;
45691
45692		/**
45693		 * State to force the editor to return false on a isDirty call.
45694		 *
45695		 * @property isNotDirty
45696		 * @type Boolean
45697		 * @deprecated Use editor.setDirty instead.
45698		 */
45699		self.setDirty(false);
45700
45701		/**
45702		 * Name/Value object containing plugin instances.
45703		 *
45704		 * @property plugins
45705		 * @type Object
45706		 * @example
45707		 * // Execute a method inside a plugin directly
45708		 * tinymce.activeEditor.plugins.someplugin.someMethod();
45709		 */
45710		self.plugins = {};
45711
45712		/**
45713		 * URI object to document configured for the TinyMCE instance.
45714		 *
45715		 * @property documentBaseURI
45716		 * @type tinymce.util.URI
45717		 * @example
45718		 * // Get relative URL from the location of document_base_url
45719		 * tinymce.activeEditor.documentBaseURI.toRelative('/somedir/somefile.htm');
45720		 *
45721		 * // Get absolute URL from the location of document_base_url
45722		 * tinymce.activeEditor.documentBaseURI.toAbsolute('somefile.htm');
45723		 */
45724		self.documentBaseURI = new URI(settings.document_base_url || documentBaseUrl, {
45725			base_uri: baseUri
45726		});
45727
45728		/**
45729		 * URI object to current document that holds the TinyMCE editor instance.
45730		 *
45731		 * @property baseURI
45732		 * @type tinymce.util.URI
45733		 * @example
45734		 * // Get relative URL from the location of the API
45735		 * tinymce.activeEditor.baseURI.toRelative('/somedir/somefile.htm');
45736		 *
45737		 * // Get absolute URL from the location of the API
45738		 * tinymce.activeEditor.baseURI.toAbsolute('somefile.htm');
45739		 */
45740		self.baseURI = baseUri;
45741
45742		/**
45743		 * Array with CSS files to load into the iframe.
45744		 *
45745		 * @property contentCSS
45746		 * @type Array
45747		 */
45748		self.contentCSS = [];
45749
45750		/**
45751		 * Array of CSS styles to add to head of document when the editor loads.
45752		 *
45753		 * @property contentStyles
45754		 * @type Array
45755		 */
45756		self.contentStyles = [];
45757
45758		// Creates all events like onClick, onSetContent etc see Editor.Events.js for the actual logic
45759		self.shortcuts = new Shortcuts(self);
45760		self.loadedCSS = {};
45761		self.editorCommands = new EditorCommands(self);
45762
45763		if (settings.target) {
45764			self.targetElm = settings.target;
45765		}
45766
45767		self.suffix = editorManager.suffix;
45768		self.editorManager = editorManager;
45769		self.inline = settings.inline;
45770
45771		if (settings.cache_suffix) {
45772			Env.cacheSuffix = settings.cache_suffix.replace(/^[\?\&]+/, '');
45773		}
45774
45775		if (settings.override_viewport === false) {
45776			Env.overrideViewPort = false;
45777		}
45778
45779		// Call setup
45780		editorManager.fire('SetupEditor', self);
45781		self.execCallback('setup', self);
45782
45783		/**
45784		 * Dom query instance with default scope to the editor document and default element is the body of the editor.
45785		 *
45786		 * @property $
45787		 * @type tinymce.dom.DomQuery
45788		 * @example
45789		 * tinymce.activeEditor.$('p').css('color', 'red');
45790		 * tinymce.activeEditor.$().append('<p>new</p>');
45791		 */
45792		self.$ = DomQuery.overrideDefaults(function() {
45793			return {
45794				context: self.inline ? self.getBody() : self.getDoc(),
45795				element: self.getBody()
45796			};
45797		});
45798	}
45799
45800	Editor.prototype = {
45801		/**
45802		 * Renders the editor/adds it to the page.
45803		 *
45804		 * @method render
45805		 */
45806		render: function() {
45807			var self = this, settings = self.settings, id = self.id, suffix = self.suffix;
45808
45809			function readyHandler() {
45810				DOM.unbind(window, 'ready', readyHandler);
45811				self.render();
45812			}
45813
45814			// Page is not loaded yet, wait for it
45815			if (!Event.domLoaded) {
45816				DOM.bind(window, 'ready', readyHandler);
45817				return;
45818			}
45819
45820			// Element not found, then skip initialization
45821			if (!self.getElement()) {
45822				return;
45823			}
45824
45825			// No editable support old iOS versions etc
45826			if (!Env.contentEditable) {
45827				return;
45828			}
45829
45830			// Hide target element early to prevent content flashing
45831			if (!settings.inline) {
45832				self.orgVisibility = self.getElement().style.visibility;
45833				self.getElement().style.visibility = 'hidden';
45834			} else {
45835				self.inline = true;
45836			}
45837
45838			var form = self.getElement().form || DOM.getParent(id, 'form');
45839			if (form) {
45840				self.formElement = form;
45841
45842				// Add hidden input for non input elements inside form elements
45843				if (settings.hidden_input && !/TEXTAREA|INPUT/i.test(self.getElement().nodeName)) {
45844					DOM.insertAfter(DOM.create('input', {type: 'hidden', name: id}), id);
45845					self.hasHiddenInput = true;
45846				}
45847
45848				// Pass submit/reset from form to editor instance
45849				self.formEventDelegate = function(e) {
45850					self.fire(e.type, e);
45851				};
45852
45853				DOM.bind(form, 'submit reset', self.formEventDelegate);
45854
45855				// Reset contents in editor when the form is reset
45856				self.on('reset', function() {
45857					self.setContent(self.startContent, {format: 'raw'});
45858				});
45859
45860				// Check page uses id="submit" or name="submit" for it's submit button
45861				if (settings.submit_patch && !form.submit.nodeType && !form.submit.length && !form._mceOldSubmit) {
45862					form._mceOldSubmit = form.submit;
45863					form.submit = function() {
45864						self.editorManager.triggerSave();
45865						self.setDirty(false);
45866
45867						return form._mceOldSubmit(form);
45868					};
45869				}
45870			}
45871
45872			/**
45873			 * Window manager reference, use this to open new windows and dialogs.
45874			 *
45875			 * @property windowManager
45876			 * @type tinymce.WindowManager
45877			 * @example
45878			 * // Shows an alert message
45879			 * tinymce.activeEditor.windowManager.alert('Hello world!');
45880			 *
45881			 * // Opens a new dialog with the file.htm file and the size 320x240
45882			 * // It also adds a custom parameter this can be retrieved by using tinyMCEPopup.getWindowArg inside the dialog.
45883			 * tinymce.activeEditor.windowManager.open({
45884			 *    url: 'file.htm',
45885			 *    width: 320,
45886			 *    height: 240
45887			 * }, {
45888			 *    custom_param: 1
45889			 * });
45890			 */
45891			self.windowManager = new WindowManager(self);
45892
45893			/**
45894			 * Notification manager reference, use this to open new windows and dialogs.
45895			 *
45896			 * @property notificationManager
45897			 * @type tinymce.NotificationManager
45898			 * @example
45899			 * // Shows a notification info message.
45900			 * tinymce.activeEditor.notificationManager.open({text: 'Hello world!', type: 'info'});
45901			 */
45902			self.notificationManager = new NotificationManager(self);
45903
45904			if (settings.encoding == 'xml') {
45905				self.on('GetContent', function(e) {
45906					if (e.save) {
45907						e.content = DOM.encode(e.content);
45908					}
45909				});
45910			}
45911
45912			if (settings.add_form_submit_trigger) {
45913				self.on('submit', function() {
45914					if (self.initialized) {
45915						self.save();
45916					}
45917				});
45918			}
45919
45920			if (settings.add_unload_trigger) {
45921				self._beforeUnload = function() {
45922					if (self.initialized && !self.destroyed && !self.isHidden()) {
45923						self.save({format: 'raw', no_events: true, set_dirty: false});
45924					}
45925				};
45926
45927				self.editorManager.on('BeforeUnload', self._beforeUnload);
45928			}
45929
45930			// Load scripts
45931			function loadScripts() {
45932				var scriptLoader = ScriptLoader.ScriptLoader;
45933
45934				if (settings.language && settings.language != 'en' && !settings.language_url) {
45935					settings.language_url = self.editorManager.baseURL + '/langs/' + settings.language + '.js';
45936				}
45937
45938				if (settings.language_url) {
45939					scriptLoader.add(settings.language_url);
45940				}
45941
45942				if (settings.theme && typeof settings.theme != "function" &&
45943					settings.theme.charAt(0) != '-' && !ThemeManager.urls[settings.theme]) {
45944					var themeUrl = settings.theme_url;
45945
45946					if (themeUrl) {
45947						themeUrl = self.documentBaseURI.toAbsolute(themeUrl);
45948					} else {
45949						themeUrl = 'themes/' + settings.theme + '/theme' + suffix + '.js';
45950					}
45951
45952					ThemeManager.load(settings.theme, themeUrl);
45953				}
45954
45955				if (Tools.isArray(settings.plugins)) {
45956					settings.plugins = settings.plugins.join(' ');
45957				}
45958
45959				each(settings.external_plugins, function(url, name) {
45960					PluginManager.load(name, url);
45961					settings.plugins += ' ' + name;
45962				});
45963
45964				each(settings.plugins.split(/[ ,]/), function(plugin) {
45965					plugin = trim(plugin);
45966
45967					if (plugin && !PluginManager.urls[plugin]) {
45968						if (plugin.charAt(0) == '-') {
45969							plugin = plugin.substr(1, plugin.length);
45970
45971							var dependencies = PluginManager.dependencies(plugin);
45972
45973							each(dependencies, function(dep) {
45974								var defaultSettings = {
45975									prefix: 'plugins/',
45976									resource: dep,
45977									suffix: '/plugin' + suffix + '.js'
45978								};
45979
45980								dep = PluginManager.createUrl(defaultSettings, dep);
45981								PluginManager.load(dep.resource, dep);
45982							});
45983						} else {
45984							PluginManager.load(plugin, {
45985								prefix: 'plugins/',
45986								resource: plugin,
45987								suffix: '/plugin' + suffix + '.js'
45988							});
45989						}
45990					}
45991				});
45992
45993				scriptLoader.loadQueue(function() {
45994					if (!self.removed) {
45995						self.init();
45996					}
45997				});
45998			}
45999
46000			loadScripts();
46001		},
46002
46003		/**
46004		 * Initializes the editor this will be called automatically when
46005		 * all plugins/themes and language packs are loaded by the rendered method.
46006		 * This method will setup the iframe and create the theme and plugin instances.
46007		 *
46008		 * @method init
46009		 */
46010		init: function() {
46011			var self = this, settings = self.settings, elm = self.getElement();
46012			var w, h, minHeight, n, o, Theme, url, bodyId, bodyClass, re, i, initializedPlugins = [];
46013
46014			this.editorManager.i18n.setCode(settings.language);
46015			self.rtl = settings.rtl_ui || this.editorManager.i18n.rtl;
46016			self.editorManager.add(self);
46017
46018			settings.aria_label = settings.aria_label || DOM.getAttrib(elm, 'aria-label', self.getLang('aria.rich_text_area'));
46019
46020			/**
46021			 * Reference to the theme instance that was used to generate the UI.
46022			 *
46023			 * @property theme
46024			 * @type tinymce.Theme
46025			 * @example
46026			 * // Executes a method on the theme directly
46027			 * tinymce.activeEditor.theme.someMethod();
46028			 */
46029			if (settings.theme) {
46030				if (typeof settings.theme != "function") {
46031					settings.theme = settings.theme.replace(/-/, '');
46032					Theme = ThemeManager.get(settings.theme);
46033					self.theme = new Theme(self, ThemeManager.urls[settings.theme]);
46034
46035					if (self.theme.init) {
46036						self.theme.init(self, ThemeManager.urls[settings.theme] || self.documentBaseUrl.replace(/\/$/, ''), self.$);
46037					}
46038				} else {
46039					self.theme = settings.theme;
46040				}
46041			}
46042
46043			function initPlugin(plugin) {
46044				var Plugin = PluginManager.get(plugin), pluginUrl, pluginInstance;
46045
46046				pluginUrl = PluginManager.urls[plugin] || self.documentBaseUrl.replace(/\/$/, '');
46047				plugin = trim(plugin);
46048				if (Plugin && inArray(initializedPlugins, plugin) === -1) {
46049					each(PluginManager.dependencies(plugin), function(dep) {
46050						initPlugin(dep);
46051					});
46052
46053					if (self.plugins[plugin]) {
46054						return;
46055					}
46056
46057					pluginInstance = new Plugin(self, pluginUrl, self.$);
46058
46059					self.plugins[plugin] = pluginInstance;
46060
46061					if (pluginInstance.init) {
46062						pluginInstance.init(self, pluginUrl);
46063						initializedPlugins.push(plugin);
46064					}
46065				}
46066			}
46067
46068			// Create all plugins
46069			each(settings.plugins.replace(/\-/g, '').split(/[ ,]/), initPlugin);
46070
46071			// Measure box
46072			if (settings.render_ui && self.theme) {
46073				self.orgDisplay = elm.style.display;
46074
46075				if (typeof settings.theme != "function") {
46076					w = settings.width || elm.style.width || elm.offsetWidth;
46077					h = settings.height || elm.style.height || elm.offsetHeight;
46078					minHeight = settings.min_height || 100;
46079					re = /^[0-9\.]+(|px)$/i;
46080
46081					if (re.test('' + w)) {
46082						w = Math.max(parseInt(w, 10), 100);
46083					}
46084
46085					if (re.test('' + h)) {
46086						h = Math.max(parseInt(h, 10), minHeight);
46087					}
46088
46089					// Render UI
46090					o = self.theme.renderUI({
46091						targetNode: elm,
46092						width: w,
46093						height: h,
46094						deltaWidth: settings.delta_width,
46095						deltaHeight: settings.delta_height
46096					});
46097
46098					// Resize editor
46099					if (!settings.content_editable) {
46100						h = (o.iframeHeight || h) + (typeof h == 'number' ? (o.deltaHeight || 0
46100) : '');
46101						if (h < minHeight) {
46102							h = minHeight;
46103						}
46104					}
46105				} else {
46106					o = settings.theme(self, elm);
46107
46108					// Convert element type to id:s
46109					if (o.editorContainer.nodeType) {
46110						o.editorContainer = o.editorContainer.id = o.editorContainer.id || self.id + "_parent";
46111					}
46112
46113					// Convert element type to id:s
46114					if (o.iframeContainer.nodeType) {
46115						o.iframeContainer = o.iframeContainer.id = o.iframeContainer.id || self.id + "_iframecontainer";
46116					}
46117
46118					// Use specified iframe height or the targets offsetHeight
46119					h = o.iframeHeight || elm.offsetHeight;
46120				}
46121
46122				self.editorContainer = o.editorContainer;
46123			}
46124
46125			// Load specified content CSS last
46126			if (settings.content_css) {
46127				each(explode(settings.content_css), function(u) {
46128					self.contentCSS.push(self.documentBaseURI.toAbsolute(u));
46129				});
46130			}
46131
46132			// Load specified content CSS last
46133			if (settings.content_style) {
46134				self.contentStyles.push(settings.content_style);
46135			}
46136
46137			// Content editable mode ends here
46138			if (settings.content_editable) {
46139				elm = n = o = null; // Fix IE leak
46140				return self.initContentBody();
46141			}
46142
46143			self.iframeHTML = settings.doctype + '<html><head>';
46144
46145			// We only need to override paths if we have to
46146			// IE has a bug where it remove site absolute urls to relative ones if this is specified
46147			if (settings.document_base_url != self.documentBaseUrl) {
46148				self.iframeHTML += '<base href="' + self.documentBaseURI.getURI() + '" />';
46149			}
46150
46151			// IE8 doesn't support carets behind images setting ie7_compat would force IE8+ to run in IE7 compat mode.
46152			if (!Env.caretAfter && settings.ie7_compat) {
46153				self.iframeHTML += '<meta http-equiv="X-UA-Compatible" content="IE=7" />';
46154			}
46155
46156			self.iframeHTML += '<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />';
46157
46158			// Load the CSS by injecting them into the HTML this will reduce "flicker"
46159			for (i = 0; i < self.contentCSS.length; i++) {
46160				var cssUrl = self.contentCSS[i];
46161				self.iframeHTML += (
46162					'<link type="text/css" ' +
46163						'rel="stylesheet" ' +
46164						'href="' + Tools._addCacheSuffix(cssUrl) + '" />'
46165				);
46166				self.loadedCSS[cssUrl] = true;
46167			}
46168
46169			bodyId = settings.body_id || 'tinymce';
46170			if (bodyId.indexOf('=') != -1) {
46171				bodyId = self.getParam('body_id', '', 'hash');
46172				bodyId = bodyId[self.id] || bodyId;
46173			}
46174
46175			bodyClass = settings.body_class || '';
46176			if (bodyClass.indexOf('=') != -1) {
46177				bodyClass = self.getParam('body_class', '', 'hash');
46178				bodyClass = bodyClass[self.id] || '';
46179			}
46180
46181			if (settings.content_security_policy) {
46182				self.iframeHTML += '<meta http-equiv="Content-Security-Policy" content="' + settings.content_security_policy + '" />';
46183			}
46184
46185			self.iframeHTML += '</head><body id="' + bodyId +
46186				'" class="mce-content-body ' + bodyClass +
46187				'" data-id="' + self.id + '"><br></body></html>';
46188
46189			/*eslint no-script-url:0 */
46190			var domainRelaxUrl = 'javascript:(function(){' +
46191				'document.open();document.domain="' + document.domain + '";' +
46192				'var ed = window.parent.tinymce.get("' + self.id + '");document.write(ed.iframeHTML);' +
46193				'document.close();ed.initContentBody(true);})()';
46194
46195			// Domain relaxing is required since the user has messed around with document.domain
46196			if (document.domain != location.hostname) {
46197				// Edge seems to be able to handle domain relaxing
46198				if (Env.ie && Env.ie < 12) {
46199					url = domainRelaxUrl;
46200				}
46201			}
46202
46203			// Create iframe
46204			// TODO: ACC add the appropriate description on this.
46205			var ifr = DOM.create('iframe', {
46206				id: self.id + "_ifr",
46207				//src: url || 'javascript:""', // Workaround for HTTPS warning in IE6/7
46208				frameBorder: '0',
46209				allowTransparency: "true",
46210				title: self.editorManager.translate(
46211						"Rich Text Area. Press ALT-F9 for menu. " +
46212						"Press ALT-F10 for toolbar. Press ALT-0 for help"
46213				),
46214				style: {
46215					width: '100%',
46216					height: h,
46217					display: 'block' // Important for Gecko to render the iframe correctly
46218				}
46219			});
46220
46221			ifr.onload = function() {
46222				ifr.onload = null;
46223				self.fire("load");
46224			};
46225
46226			DOM.setAttrib(ifr, "src", url || 'javascript:""');
46227
46228			self.contentAreaContainer = o.iframeContainer;
46229			self.iframeElement = ifr;
46230
46231			n = DOM.add(o.iframeContainer, ifr);
46232
46233			// Try accessing the document this will fail on IE when document.domain is set to the same as location.hostname
46234			// Then we have to force domain relaxing using the domainRelaxUrl approach very ugly!!
46235			if (ie) {
46236				try {
46237					self.getDoc();
46238				} catch (e) {
46239					n.src = url = domainRelaxUrl;
46240				}
46241			}
46242
46243			if (o.editorContainer) {
46244				DOM.get(o.editorContainer).style.display = self.orgDisplay;
46245				self.hidden = DOM.isHidden(o.editorContainer);
46246			}
46247
46248			self.getElement().style.display = 'none';
46249			DOM.setAttrib(self.id, 'aria-hidden', true);
46250
46251			if (!url) {
46252				self.initContentBody();
46253			}
46254
46255			elm = n = o = null; // Cleanup
46256		},
46257
46258		/**
46259		 * This method get called by the init method once the iframe is loaded.
46260		 * It will fill the iframe with contents, sets up DOM and selection objects for the iframe.
46261		 *
46262		 * @method initContentBody
46263		 * @private
46264		 */
46265		initContentBody: function(skipWrite) {
46266			var self = this, settings = self.settings, targetElm = self.getElement(), doc = self.getDoc(), body, contentCssText;
46267
46268			// Restore visibility on target element
46269			if (!settings.inline) {
46270				self.getElement().style.visibility = self.orgVisibility;
46271			}
46272
46273			// Setup iframe body
46274			if (!skipWrite && !settings.content_editable) {
46275				doc.open();
46276				doc.write(self.iframeHTML);
46277				doc.close();
46278			}
46279
46280			if (settings.content_editable) {
46281				self.on('remove', function() {
46282					var bodyEl = this.getBody();
46283
46284					DOM.removeClass(bodyEl, 'mce-content-body');
46285					DOM.removeClass(bodyEl, 'mce-edit-focus');
46286					DOM.setAttrib(bodyEl, 'contentEditable', null);
46287				});
46288
46289				DOM.addClass(targetElm, 'mce-content-body');
46290				self.contentDocument = doc = settings.content_document || document;
46291				self.contentWindow = settings.content_window || window;
46292				self.bodyElement = targetElm;
46293
46294				// Prevent leak in IE
46295				settings.content_document = settings.content_window = null;
46296
46297				// TODO: Fix this
46298				settings.root_name = targetElm.nodeName.toLowerCase();
46299			}
46300
46301			// It will not steal focus while setting contentEditable
46302			body = self.getBody();
46303			body.disabled = true;
46304			self.readonly = settings.readonly;
46305
46306			if (!self.readonly) {
46307				if (self.inline && DOM.getStyle(body, 'position', true) == 'static') {
46308					body.style.position = 'relative';
46309				}
46310
46311				body.contentEditable = self.getParam('content_editable_state', true);
46312			}
46313
46314			body.disabled = false;
46315
46316			self.editorUpload = new EditorUpload(self);
46317
46318			/**
46319			 * Schema instance, enables you to validate elements and its children.
46320			 *
46321			 * @property schema
46322			 * @type tinymce.html.Schema
46323			 */
46324			self.schema = new Schema(settings);
46325
46326			/**
46327			 * DOM instance for the editor.
46328			 *
46329			 * @property dom
46330			 * @type tinymce.dom.DOMUtils
46331			 * @example
46332			 * // Adds a class to all paragraphs within the editor
46333			 * tinymce.activeEditor.dom.addClass(tinymce.activeEditor.dom.select('p'), 'someclass');
46334			 */
46335			self.dom = new DOMUtils(doc, {
46336				keep_values: true,
46337				url_converter: self.convertURL,
46338				url_converter_scope: self,
46339				hex_colors: settings.force_hex_style_colors,
46340				class_filter: settings.class_filter,
46341				update_styles: true,
46342				root_element: self.inline ? self.getBody() : null,
46343				collect: settings.content_editable,
46344				schema: self.schema,
46345				onSetAttrib: function(e) {
46346					self.fire('SetAttrib', e);
46347				}
46348			});
46349
46350			/**
46351			 * HTML parser will be used when contents is inserted into the editor.
46352			 *
46353			 * @property parser
46354			 * @type tinymce.html.DomParser
46355			 */
46356			self.parser = new DomParser(settings, self.schema);
46357
46358			// Convert src and href into data-mce-src, data-mce-href and data-mce-style
46359			self.parser.addAttributeFilter('src,href,style,tabindex', function(nodes, name) {
46360				var i = nodes.length, node, dom = self.dom, value, internalName;
46361
46362				while (i--) {
46363					node = nodes[i];
46364					value = node.attr(name);
46365					internalName = 'data-mce-' + name;
46366
46367					// Add internal attribute if we need to we don't on a refresh of the document
46368					if (!node.attributes.map[internalName]) {
46369						// Don't duplicate these since they won't get modified by any browser
46370						if (value.indexOf('data:') === 0 || value.indexOf('blob:') === 0) {
46371							continue;
46372						}
46373
46374						if (name === "style") {
46375							value = dom.serializeStyle(dom.parseStyle(value), node.name);
46376
46377							if (!value.length) {
46378								value = null;
46379							}
46380
46381							node.attr(internalName, value);
46382							node.attr(name, value);
46383						} else if (name === "tabindex") {
46384							node.attr(internalName, value);
46385							node.attr(name, null);
46386						} else {
46387							node.attr(internalName, self.convertURL(value, name, node.name));
46388						}
46389					}
46390				}
46391			});
46392
46393			// Keep scripts from executing
46394			self.parser.addNodeFilter('script', function(nodes) {
46395				var i = nodes.length, node, type;
46396
46397				while (i--) {
46398					node = nodes[i];
46399					type = node.attr('type') || 'no/type';
46400					if (type.indexOf('mce-') !== 0) {
46401						node.attr('type', 'mce-' + type);
46402					}
46403				}
46404			});
46405
46406			self.parser.addNodeFilter('#cdata', function(nodes) {
46407				var i = nodes.length, node;
46408
46409				while (i--) {
46410					node = nodes[i];
46411					node.type = 8;
46412					node.name = '#comment';
46413					node.value = '[CDATA[' + node.value + ']]';
46414				}
46415			});
46416
46417			self.parser.addNodeFilter('p,h1,h2,h3,h4,h5,h6,div', function(nodes) {
46418				var i = nodes.length, node, nonEmptyElements = self.schema.getNonEmptyElements();
46419
46420				while (i--) {
46421					node = nodes[i];
46422
46423					if (node.isEmpty(nonEmptyElements)) {
46424						node.append(new Node('br', 1)).shortEnded = true;
46425					}
46426				}
46427			});
46428
46429			/**
46430			 * DOM serializer for the editor. Will be used when contents is extracted from the editor.
46431			 *
46432			 * @property serializer
46433			 * @type tinymce.dom.Serializer
46434			 * @example
46435			 * // Serializes the first paragraph in the editor into a string
46436			 * tinymce.activeEditor.serializer.serialize(tinymce.activeEditor.dom.select('p')[0]);
46437			 */
46438			self.serializer = new DomSerializer(settings, self);
46439
46440			/**
46441			 * Selection instance for the editor.
46442			 *
46443			 * @property selection
46444			 * @type tinymce.dom.Selection
46445			 * @example
46446			 * // Sets some contents to the current selection in the editor
46447			 * tinymce.activeEditor.selection.setContent('Some contents');
46448			 *
46449			 * // Gets the current selection
46450			 * alert(tinymce.activeEditor.selection.getContent());
46451			 *
46452			 * // Selects the first paragraph found
46453			 * tinymce.activeEditor.selection.select(tinymce.activeEditor.dom.select('p')[0]);
46454			 */
46455			self.selection = new Selection(self.dom, self.getWin(), self.serializer, self);
46456
46457			/**
46458			 * Formatter instance.
46459			 *
46460			 * @property formatter
46461			 * @type tinymce.Formatter
46462			 */
46463			self.formatter = new Formatter(self);
46464
46465			/**
46466			 * Undo manager instance, responsible for handling undo levels.
46467			 *
46468			 * @property undoManager
46469			 * @type tinymce.UndoManager
46470			 * @example
46471			 * // Undoes the last modification to the editor
46472			 * tinymce.activeEditor.undoManager.undo();
46473			 */
46474			self.undoManager = new UndoManager(self);
46475
46476			self.forceBlocks = new ForceBlocks(self);
46477			self.enterKey = new EnterKey(self);
46478			self._nodeChangeDispatcher = new NodeChange(self);
46479			self._selectionOverrides = new SelectionOverrides(self);
46480
46481			self.fire('PreInit');
46482
46483			if (!settings.browser_spellcheck && !settings.gecko_spellcheck) {
46484				doc.body.spellcheck = false; // Gecko
46485				DOM.setAttrib(body, "spellcheck", "false");
46486			}
46487
46488			self.fire('PostRender');
46489
46490			self.quirks = new Quirks(self);
46491
46492			if (settings.directionality) {
46493				body.dir = settings.directionality;
46494			}
46495
46496			if (settings.nowrap) {
46497				body.style.whiteSpace = "nowrap";
46498			}
46499
46500			if (settings.protect) {
46501				self.on('BeforeSetContent', function(e) {
46502					each(settings.protect, function(pattern) {
46503						e.content = e.content.replace(pattern, function(str) {
46504							return '<!--mce:protected ' + escape(str) + '-->';
46505						});
46506					});
46507				});
46508			}
46509
46510			self.on('SetContent', function() {
46511				self.addVisual(self.getBody());
46512			});
46513
46514			// Remove empty contents
46515			if (settings.padd_empty_editor) {
46516				self.on('PostProcess', function(e) {
46517					e.content = e.content.replace(/^(<p[^>]*>(&nbsp;|&#160;|\s|\u00a0|)<\/p>[\r\n]*|<br \/>[\r\n]*)$/, '');
46518				});
46519			}
46520
46521			self.load({initial: true, format: 'html'});
46522			self.startContent = self.getContent({format: 'raw'});
46523
46524			/**
46525			 * Is set to true after the editor instance has been initialized
46526			 *
46527			 * @property initialized
46528			 * @type Boolean
46529			 * @example
46530			 * function isEditorInitialized(editor) {
46531			 *     return editor && editor.initialized;
46532			 * }
46533			 */
46534			self.initialized = true;
46535			self.bindPendingEventDelegates();
46536
46537			self.fire('init');
46538			self.focus(true);
46539			self.nodeChanged({initial: true});
46540			self.execCallback('init_instance_callback', self);
46541
46542			// Add editor specific CSS styles
46543			if (self.contentStyles.length > 0) {
46544				contentCssText = '';
46545
46546				each(self.contentStyles, function(style) {
46547					contentCssText += style + "\r\n";
46548				});
46549
46550				self.dom.addStyle(contentCssText);
46551			}
46552
46553			// Load specified content CSS last
46554			each(self.contentCSS, function(cssUrl) {
46555				if (!self.loadedCSS[cssUrl]) {
46556					self.dom.loadCSS(cssUrl);
46557					self.loadedCSS[cssUrl] = true;
46558				}
46559			});
46560
46561			// Handle auto focus
46562			if (settings.auto_focus) {
46563				Delay.setEditorTimeout(self, function() {
46564					var editor;
46565
46566					if (settings.auto_focus === true) {
46567						editor = self;
46568					} else {
46569						editor = self.editorManager.get(settings.auto_focus);
46570					}
46571
46572					if (!editor.destroyed) {
46573						editor.focus();
46574					}
46575				}, 100);
46576			}
46577
46578			// Clean up references for IE
46579			targetElm = doc = body = null;
46580		},
46581
46582		/**
46583		 * Focuses/activates the editor. This will set this editor as the activeEditor in the tinymce collection
46584		 * it will also place DOM focus inside the editor.
46585		 *
46586		 * @method focus
46587		 * @param {Boolean} skipFocus Skip DOM focus. Just set is as the active editor.
46588		 */
46589		focus: function(skipFocus) {
46590			var self = this, selection = self.selection, contentEditable = self.settings.content_editable, rng;
46591			var controlElm, doc = self.getDoc(), body = self.getBody(), contentEditableHost;
46592
46593			function getContentEditableHost(node) {
46594				return self.dom.getParent(node, function(node) {
46595					return self.dom.getContentEditable(node) === "true";
46596				});
46597			}
46598
46599			if (!skipFocus) {
46600				// Get selected control element
46601				rng = selection.getRng();
46602				if (rng.item) {
46603					controlElm = rng.item(0);
46604				}
46605
46606				self._refreshContentEditable();
46607
46608				// Move focus to contentEditable=true child if needed
46609				contentEditableHost = getContentEditableHost(selection.getNode());
46610				if (self.$.contains(body, contentEditableHost)) {
46611					contentEditableHost.focus();
46612					selection.normalize();
46613					self.editorManager.setActive(self);
46614					return;
46615				}
46616
46617				// Focus the window iframe
46618				if (!contentEditable) {
46619					// WebKit needs this call to fire focusin event properly see #5948
46620					// But Opera pre Blink engine will produce an empty selection so skip Opera
46621					if (!Env.opera) {
46622						self.getBody().focus();
46623					}
46624
46625					self.getWin().focus();
46626				}
46627
46628				// Focus the body as well since it's contentEditable
46629				if (isGecko || contentEditable) {
46630					// Check for setActive since it doesn't scroll to the element
46631					if (body.setActive) {
46632						// IE 11 sometimes throws "Invalid function" then fallback to focus
46633						try {
46634							body.setActive();
46635						} catch (ex) {
46636							body.focus();
46637						}
46638					} else {
46639						body.focus();
46640					}
46641
46642					if (contentEditable) {
46643						selection.normalize();
46644					}
46645				}
46646
46647				// Restore selected control element
46648				// This is needed when for example an image is selected within a
46649				// layer a call to focus will then remove the control selection
46650				if (controlElm && controlElm.ownerDocument == doc) {
46651					rng = doc.body.createControlRange();
46652					rng.addElement(controlElm);
46653					rng.select();
46654				}
46655			}
46656
46657			self.editorManager.setActive(self);
46658		},
46659
46660		/**
46661		 * Executes a legacy callback. This method is useful to call old 2.x option callbacks.
46662		 * There new event model is a better way to add callback so this method might be removed in the future.
46663		 *
46664		 * @method execCallback
46665		 * @param {String} name Name of the callback to execute.
46666		 * @return {Object} Return value passed from callback function.
46667		 */
46668		execCallback: function(name) {
46669			var self = this, callback = self.settings[name], scope;
46670
46671			if (!callback) {
46672				return;
46673			}
46674
46675			// Look through lookup
46676			if (self.callbackLookup && (scope = self.callbackLookup[name])) {
46677				callback = scope.func;
46678				scope = scope.scope;
46679			}
46680
46681			if (typeof callback === 'string') {
46682				scope = callback.replace(/\.\w+$/, '');
46683				scope = scope ? resolve(scope) : 0;
46684				callback = resolve(callback);
46685				self.callbackLookup = self.callbackLookup || {};
46686				self.callbackLookup[name] = {func: callback, scope: scope};
46687			}
46688
46689			return callback.apply(scope || self, Array.prototype.slice.call(arguments, 1));
46690		},
46691
46692		/**
46693		 * Translates the specified string by replacing variables with language pack items it will also check if there is
46694		 * a key mathcin the input.
46695		 *
46696		 * @method translate
46697		 * @param {String} text String to translate by the language pack data.
46698		 * @return {String} Translated string.
46699		 */
46700		translate: function(text) {
46701			var lang = this.settings.language || 'en', i18n = this.editorManager.i18n;
46702
46703			if (!text) {
46704				return '';
46705			}
46706
46707			return i18n.data[lang + '.' + text] || text.replace(/\{\#([^\}]+)\}/g, function(a, b) {
46708				return i18n.data[lang + '.' + b] || '{#' + b + '}';
46709			});
46710		},
46711
46712		/**
46713		 * Returns a language pack item by name/key.
46714		 *
46715		 * @method getLang
46716		 * @param {String} name Name/key to get from the language pack.
46717		 * @param {String} defaultVal Optional default value to retrive.
46718		 */
46719		getLang: function(name, defaultVal) {
46720			return (
46721				this.editorManager.i18n.data[(this.settings.language || 'en') + '.' + name] ||
46722				(defaultVal !== undefined ? defaultVal : '{#' + name + '}')
46723			);
46724		},
46725
46726		/**
46727		 * Returns a configuration parameter by name.
46728		 *
46729		 * @method getParam
46730		 * @param {String} name Configruation parameter to retrive.
46731		 * @param {String} defaultVal Optional default value to return.
46732		 * @param {String} type Optional type parameter.
46733		 * @return {String} Configuration parameter value or default value.
46734		 * @example
46735		 * // Returns a specific config value from the currently active editor
46736		 * var someval = tinymce.activeEditor.getParam('myvalue');
46737		 *
46738		 * // Returns a specific config value from a specific editor instance by id
46739		 * var someval2 = tinymce.get('my_editor').getParam('myvalue');
46740		 */
46741		getParam: function(name, defaultVal, type) {
46742			var value = name in this.settings ? this.settings[name] : defaultVal, output;
46743
46744			if (type === 'hash') {
46745				output = {};
46746
46747				if (typeof value === 'string') {
46748					each(value.indexOf('=') > 0 ? value.split(/[;,](?![^=;,]*(?:[;,]|$))/) : value.split(','), function(value) {
46749						value = value.split('=');
46750
46751						if (value.length > 1) {
46752							output[trim(value[0])] = trim(value[1]);
46753						} else {
46754							output[trim(value[0])] = trim(value);
46755						}
46756					});
46757				} else {
46758					output = value;
46759				}
46760
46761				return output;
46762			}
46763
46764			return value;
46765		},
46766
46767		/**
46768		 * Dispatches out a onNodeChange event to all observers. This method should be called when you
46769		 * need to update the UI states or element path etc.
46770		 *
46771		 * @method nodeChanged
46772		 * @param {Object} args Optional args to pass to NodeChange event handlers.
46773		 */
46774		nodeChanged: function(args) {
46775			this._nodeChangeDispatcher.nodeChanged(args);
46776		},
46777
46778		/**
46779		 * Adds a button that later gets created by the theme in the editors toolbars.
46780		 *
46781		 * @method addButton
46782		 * @param {String} name Button name to add.
46783		 * @param {Object} settings Settings object with title, cmd etc.
46784		 * @example
46785		 * // Adds a custom button to the editor that inserts contents when clicked
46786		 * tinymce.init({
46787		 *    ...
46788		 *
46789		 *    toolbar: 'example'
46790		 *
46791		 *    setup: function(ed) {
46792		 *       ed.addButton('example', {
46793		 *          title: 'My title',
46794		 *          image: '../js/tinymce/plugins/example/img/example.gif',
46795		 *          onclick: function() {
46796		 *             ed.insertContent('Hello world!!');
46797		 *          }
46798		 *       });
46799		 *    }
46800		 * });
46801		 */
46802		addButton: function(name, settings) {
46803			var self = this;
46804
46805			if (settings.cmd) {
46806				settings.onclick = function() {
46807					self.execCommand(settings.cmd);
46808				};
46809			}
46810
46811			if (!settings.text && !settings.icon) {
46812				settings.icon = name;
46813			}
46814
46815			self.buttons = self.buttons || {};
46816			settings.tooltip = settings.tooltip || settings.title;
46817			self.buttons[name] = settings;
46818		},
46819
46820		/**
46821		 * Adds a menu item to be used in the menus of the theme. There might be multiple instances
46822		 * of this menu item for example it might be used in the main menus of the theme but also in
46823		 * the context menu so make sure that it's self contained and supports multiple instances.
46824		 *
46825		 * @method addMenuItem
46826		 * @param {String} name Menu item name to add.
46827		 * @param {Object} settings Settings object with title, cmd etc.
46828		 * @example
46829		 * // Adds a custom menu item to the editor that inserts contents when clicked
46830		 * // The context option allows you to add the menu item to an existing default menu
46831		 * tinymce.init({
46832		 *    ...
46833		 *
46834		 *    setup: function(ed) {
46835		 *       ed.addMenuItem('example', {
46836		 *          text: 'My menu item',
46837		 *          context: 'tools',
46838		 *          onclick: function() {
46839		 *             ed.insertContent('Hello world!!');
46840		 *          }
46841		 *       });
46842		 *    }
46843		 * });
46844		 */
46845		addMenuItem: function(name, settings) {
46846			var self = this;
46847
46848			if (settings.cmd) {
46849				settings.onclick = function() {
46850					self.execCommand(settings.cmd);
46851				};
46852			}
46853
46854			self.menuItems = self.menuItems || {};
46855			self.menuItems[name] = settings;
46856		},
46857
46858		/**
46859		 * Adds a contextual toolbar to be rendered when the selector matches.
46860		 *
46861		 * @method addContextToolbar
46862		 * @param {function/string} predicate Predicate that needs to return true if provided strings get converted into CSS predicates.
46863		 * @param {String/Array} items String or array with items to add to the context toolbar.
46864		 */
46865		addContextToolbar: function(predicate, items) {
46866			var self = this, selector;
46867
46868			self.contextToolbars = self.contextToolbars || [];
46869
46870			// Convert selector to predicate
46871			if (typeof predicate == "string") {
46872				selector = predicate;
46873				predicate = function(elm) {
46874					return self.dom.is(elm, selector);
46875				};
46876			}
46877
46878			self.contextToolbars.push({
46879				predicate: predicate,
46880				items: items
46881			});
46882		},
46883
46884		/**
46885		 * Adds a custom command to the editor, you can also override existing commands with this method.
46886		 * The command that you add can be executed with execCommand.
46887		 *
46888		 * @method addCommand
46889		 * @param {String} name Command name to add/override.
46890		 * @param {addCommandCallback} callback Function to execute when the command occurs.
46891		 * @param {Object} scope Optional scope to execute the function in.
46892		 * @example
46893		 * // Adds a custom command that later can be executed using execCommand
46894		 * tinymce.init({
46895		 *    ...
46896		 *
46897		 *    setup: function(ed) {
46898		 *       // Register example command
46899		 *       ed.addCommand('mycommand', function(ui, v) {
46900		 *          ed.windowManager.alert('Hello world!! Selection: ' + ed.selection.getContent({format: 'text'}));
46901		 *       });
46902		 *    }
46903		 * });
46904		 */
46905		addCommand: function(name, callback, scope) {
46906			/**
46907			 * Callback function that gets called when a command is executed.
46908			 *
46909			 * @callback addCommandCallback
46910			 * @param {Boolean} ui Display UI state true/false.
46911			 * @param {Object} value Optional value for command.
46912			 * @return {Boolean} True/false state if the command was handled or not.
46913			 */
46914			this.editorCommands.addCommand(name, callback, scope);
46915		},
46916
46917		/**
46918		 * Adds a custom query state command to the editor, you can also override existing commands with this method.
46919		 * The command that you add can be executed with queryCommandState function.
46920		 *
46921		 * @method addQueryStateHandler
46922		 * @param {String} name Command name to add/override.
46923		 * @param {addQueryStateHandlerCallback} callback Function to execute when the command state retrieval occurs.
46924		 * @param {Object} scope Optional scope to execute the function in.
46925		 */
46926		addQueryStateHandler: function(name, callback, scope) {
46927			/**
46928			 * Callback function that gets called when a queryCommandState is executed.
46929			 *
46930			 * @callback addQueryStateHandlerCallback
46931			 * @return {Boolean} True/false state if the command is enabled or not like is it bold.
46932			 */
46933			this.editorCommands.addQueryStateHandler(name, callback, scope);
46934		},
46935
46936		/**
46937		 * Adds a custom query value command to the editor, you can also override existing commands with this method.
46938		 * The command that you add can be executed with queryCommandValue function.
46939		 *
46940		 * @method addQueryValueHandler
46941		 * @param {String} name Command name to add/override.
46942		 * @param {addQueryValueHandlerCallback} callback Function to execute when the command value retrieval occurs.
46943		 * @param {Object} scope Optional scope to execute the function in.
46944		 */
46945		addQueryValueHandler: function(name, callback, scope) {
46946			/**
46947			 * Callback function that gets called when a queryCommandValue is executed.
46948			 *
46949			 * @callback addQueryValueHandlerCallback
46950			 * @return {Object} Value of the command or undefined.
46951			 */
46952			this.editorCommands.addQueryValueHandler(name, callback, scope);
46953		},
46954
46955		/**
46956		 * Adds a keyboard shortcut for some command or function.
46957		 *
46958		 * @method addShortcut
46959		 * @param {String} pattern Shortcut pattern. Like for example: ctrl+alt+o.
46960		 * @param {String} desc Text description for the command.
46961		 * @param {String/Function} cmdFunc Command name string or function to execute when the key is pressed.
46962		 * @param {Object} sc Optional scope to execute the function in.
46963		 * @return {Boolean} true/false state if the shortcut was added or not.
46964		 */
46965		addShortcut: function(pattern, desc, cmdFunc, scope) {
46966			this.shortcuts.add(pattern, desc, cmdFunc, scope);
46967		},
46968
46969		/**
46970		 * Executes a command on the current instance. These commands can be TinyMCE internal commands prefixed with "mce" or
46971		 * they can be build in browser commands such as "Bold". A compleate list of browser commands is available on MSDN or Mozilla.org.
46972		 * This function will dispatch the execCommand function on each plugin, theme or the execcommand_callback option if none of these
46973		 * return true it will handle the command as a internal browser command.
46974		 *
46975		 * @method execCommand
46976		 * @param {String} cmd Command name to execute, for example mceLink or Bold.
46977		 * @param {Boolean} ui True/false state if a UI (dialog) should be presented or not.
46978		 * @param {mixed} value Optional command value, this can be anything.
46979		 * @param {Object} args Optional arguments object.
46980		 */
46981		execCommand: function(cmd, ui, value, args) {
46982			return this.editorCommands.execCommand(cmd, ui, value, args);
46983		},
46984
46985		/**
46986		 * Returns a command specific state, for example if bold is enabled or not.
46987		 *
46988		 * @method queryCommandState
46989		 * @param {string} cmd Command to query state from.
46990		 * @return {Boolean} Command specific state, for example if bold is enabled or not.
46991		 */
46992		queryCommandState: function(cmd) {
46993			return this.editorCommands.queryCommandState(cmd);
46994		},
46995
46996		/**
46997		 * Returns a command specific value, for example the current font size.
46998		 *
46999		 * @method queryCommandValue
47000		 * @param {string} cmd Command to query value from.
47001		 * @return {Object} Command specific value, for example the current font size.
47002		 */
47003		queryCommandValue: function(cmd) {
47004			return this.editorCommands.queryCommandValue(cmd);
47005		},
47006
47007		/**
47008		 * Returns true/false if the command is supported or not.
47009		 *
47010		 * @method queryCommandSupported
47011		 * @param {String} cmd Command that we check support for.
47012		 * @return {Boolean} true/false if the command is supported or not.
47013		 */
47014		queryCommandSupported: function(cmd) {
47015			return this.editorCommands.queryCommandSupported(cmd);
47016		},
47017
47018		/**
47019		 * Shows the editor and hides any textarea/div that the editor is supposed to replace.
47020		 *
47021		 * @method show
47022		 */
47023		show: function() {
47024			var self = this;
47025
47026			if (self.hidden) {
47027				self.hidden = false;
47028
47029				if (self.inline) {
47030					self.getBody().contentEditable = true;
47031				} else {
47032					DOM.show(self.getContainer());
47033					DOM.hide(self.id);
47034				}
47035
47036				self.load();
47037				self.fire('show');
47038			}
47039		},
47040
47041		/**
47042		 * Hides the editor and shows any textarea/div that the editor is supposed to replace.
47043		 *
47044		 * @method hide
47045		 */
47046		hide: function() {
47047			var self = this, doc = self.getDoc();
47048
47049			if (!self.hidden) {
47050				// Fixed bug where IE has a blinking cursor left from the editor
47051				if (ie && doc && !self.inline) {
47052					doc.execCommand('SelectAll');
47053				}
47054
47055				// We must save before we hide so Safari doesn't crash
47056				self.save();
47057
47058				if (self.inline) {
47059					self.getBody().contentEditable = false;
47060
47061					// Make sure the editor gets blurred
47062					if (self == self.editorManager.focusedEditor) {
47063						self.editorManager.focusedEditor = null;
47064					}
47065				} else {
47066					DOM.hide(self.getContainer());
47067					DOM.setStyle(self.id, 'display', self.orgDisplay);
47068				}
47069
47070				self.hidden = true;
47071				self.fire('hide');
47072			}
47073		},
47074
47075		/**
47076		 * Returns true/false if the editor is hidden or not.
47077		 *
47078		 * @method isHidden
47079		 * @return {Boolean} True/false if the editor is hidden or not.
47080		 */
47081		isHidden: function() {
47082			return !!this.hidden;
47083		},
47084
47085		/**
47086		 * Sets the progress state, this will display a throbber/progess for the editor.
47087		 * This is ideal for asynchronous operations like an AJAX save call.
47088		 *
47089		 * @method setProgressState
47090		 * @param {Boolean} state Boolean state if the progress should be shown or hidden.
47091		 * @param {Number} time Optional time to wait before the progress gets shown.
47092		 * @return {Boolean} Same as the input state.
47093		 * @example
47094		 * // Show progress for the active editor
47095		 * tinymce.activeEditor.setProgressState(true);
47096		 *
47097		 * // Hide progress for the active editor
47098		 * tinymce.activeEditor.setProgressState(false);
47099		 *
47100		 * // Show progress after 3 seconds
47101		 * tinymce.activeEditor.setProgressState(true, 3000);
47102		 */
47103		setProgressState: function(state, time) {
47104			this.fire('ProgressState', {state: state, time: time});
47105		},
47106
47107		/**
47108		 * Loads contents from the textarea or div element that got converted into an editor instance.
47109		 * This method will move the contents from that textarea or div into the editor by using setContent
47110		 * so all events etc that method has will get dispatched as well.
47111		 *
47112		 * @method load
47113		 * @param {Object} args Optional content object, this gets passed around through the whole load process.
47114		 * @return {String} HTML string that got set into the editor.
47115		 */
47116		load: function(args) {
47117			var self = this, elm = self.getElement(), html;
47118
47119			if (elm) {
47120				args = args || {};
47121				args.load = true;
47122
47123				html = self.setContent(elm.value !== undefined ? elm.value : elm.innerHTML, args);
47124				args.element = elm;
47125
47126				if (!args.no_events) {
47127					self.fire('LoadContent', args);
47128				}
47129
47130				args.element = elm = null;
47131
47132				return html;
47133			}
47134		},
47135
47136		/**
47137		 * Saves the contents from a editor out to the textarea or div element that got converted into an editor instance.
47138		 * This method will move the HTML contents from the editor into that textarea or div by getContent
47139		 * so all events etc that method has will get dispatched as well.
47140		 *
47141		 * @method save
47142		 * @param {Object} args Optional content object, this gets passed around through the whole save process.
47143		 * @return {String} HTML string that got set into the textarea/div.
47144		 */
47145		save: function(args) {
47146			var self = this, elm = self.getElement(), html, form;
47147
47148			if (!elm || !self.initialized) {
47149				return;
47150			}
47151
47152			args = args || {};
47153			args.save = true;
47154
47155			args.element = elm;
47156			html = args.content = self.getContent(args);
47157
47158			if (!args.no_events) {
47159				self.fire('SaveContent', args);
47160			}
47161
47162			// Always run this internal event
47163			if (args.format == 'raw') {
47164				self.fire('RawSaveContent', args);
47165			}
47166
47167			html = args.content;
47168
47169			if (!/TEXTAREA|INPUT/i.test(elm.nodeName)) {
47170				// Update DIV element when not in inline mode
47171				if (!self.inline) {
47172					elm.innerHTML = html;
47173				}
47174
47175				// Update hidden form element
47176				if ((form = DOM.getParent(self.id, 'form'))) {
47177					each(form.elements, function(elm) {
47178						if (elm.name == self.id) {
47179							elm.value = html;
47180							return false;
47181						}
47182					});
47183				}
47184			} else {
47185				elm.value = html;
47186			}
47187
47188			args.element = elm = null;
47189
47190			if (args.set_dirty !== false) {
47191				self.setDirty(false);
47192			}
47193
47194			return html;
47195		},
47196
47197		/**
47198		 * Sets the specified content to the editor instance, this will cleanup the content before it gets set using
47199		 * the different cleanup rules options.
47200		 *
47201		 * @method setContent
47202		 * @param {String} content Content to set to editor, normally HTML contents but can be other formats as well.
47203		 * @param {Object} args Optional content object, this gets passed around through the whole set process.
47204		 * @return {String} HTML string that got set into the editor.
47205		 * @example
47206		 * // Sets the HTML contents of the activeEditor editor
47207		 * tinymce.activeEditor.setContent('<span>some</span> html');
47208		 *
47209		 * // Sets the raw contents of the activeEditor editor
47210		 * tinymce.activeEditor.setContent('<span>some</span> html', {format: 'raw'});
47211		 *
47212		 * // Sets the content of a specific editor (my_editor in this example)
47213		 * tinymce.get('my_editor').setContent(data);
47214		 *
47215		 * // Sets the bbcode contents of the activeEditor editor if the bbcode plugin was added
47216		 * tinymce.activeEditor.setContent('[b]some[/b] html', {format: 'bbcode'});
47217		 */
47218		setContent: function(content, args) {
47219			var self = this, body = self.getBody(), forcedRootBlockName, padd;
47220
47221			// Setup args object
47222			args = args || {};
47223			args.format = args.format || 'html';
47224			args.set = true;
47225			args.content = content;
47226
47227			// Do preprocessing
47228			if (!args.no_events) {
47229				self.fire('BeforeSetContent', args);
47230			}
47231
47232			content = args.content;
47233
47234			// Padd empty content in Gecko and Safari. Commands will otherwise fail on the content
47235			// It will also be impossible to place the caret in the editor unless there is a BR element present
47236			if (content.length === 0 || /^\s+$/.test(content)) {
47237				padd = ie && ie < 11 ? '' : '<br data-mce-bogus="1">';
47238
47239				// Todo: There is a lot more root elements that need special padding
47240				// so separate this and add all of them at some point.
47241				if (body.nodeName == 'TABLE') {
47242					content = '<tr><td>' + padd + '</td></tr>';
47243				} else if (/^(UL|OL)$/.test(body.nodeName)) {
47244					content = '<li>' + padd + '</li>';
47245				}
47246
47247				forcedRootBlockName = self.settings.forced_root_block;
47248
47249				// Check if forcedRootBlock is configured and that the block is a valid child of the body
47250				if (forcedRootBlockName && self.schema.isValidChild(body.nodeName.toLowerCase(), forcedRootBlockName.toLowerCase())) {
47251					// Padd with bogus BR elements on modern browsers and IE 7 and 8 since they don't render empty P tags properly
47252					content = padd;
47253					content = self.dom.createHTML(forcedRootBlockName, self.settings.forced_root_block_attrs, content);
47254				} else if (!ie && !content) {
47255					// We need to add a BR when forced_root_block is disabled on non IE browsers to place the caret
47256					content = '<br data-mce-bogus="1">';
47257				}
47258
47259				self.dom.setHTML(body, content);
47260
47261				self.fire('SetContent', args);
47262			} else {
47263				// Parse and serialize the html
47264				if (args.format !== 'raw') {
47265					content = new Serializer({
47266						validate: self.validate
47267					}, self.schema).serialize(
47268						self.parser.parse(content, {isRootContent: true})
47269					);
47270				}
47271
47272				// Set the new cleaned contents to the editor
47273				args.content = trim(content);
47274				self.dom.setHTML(body, args.content);
47275
47276				// Do post processing
47277				if (!args.no_events) {
47278					self.fire('SetContent', args);
47279				}
47280
47281				// Don't normalize selection if the focused element isn't the body in
47282				// content editable mode since it will steal focus otherwise
47283				/*if (!self.settings.content_editable || document.activeElement === self.getBody()) {
47284					self.selection.normalize();
47285				}*/
47286			}
47287
47288			return args.content;
47289		},
47290
47291		/**
47292		 * Gets the content from the editor instance, this will cleanup the content before it gets returned using
47293		 * the different cleanup rules options.
47294		 *
47295		 * @method getContent
47296		 * @param {Object} args Optional content object, this gets passed around through the whole get process.
47297		 * @return {String} Cleaned content string, normally HTML contents.
47298		 * @example
47299		 * // Get the HTML contents of the currently active editor
47300		 * console.debug(tinymce.activeEditor.getContent());
47301		 *
47302		 * // Get the raw contents of the currently active editor
47303		 * tinymce.activeEditor.getContent({format: 'raw'});
47304		 *
47305		 * // Get content of a specific editor:
47306		 * tinymce.get('content id').getContent()
47307		 */
47308		getContent: function(args) {
47309			var self = this, content, body = self.getBody();
47310
47311			// Setup args object
47312			args = args || {};
47313			args.format = args.format || 'html';
47314			args.get = true;
47315			args.getInner = true;
47316
47317			// Do preprocessing
47318			if (!args.no_events) {
47319				self.fire('BeforeGetContent', args);
47320			}
47321
47322			// Get raw contents or by default the cleaned contents
47323			if (args.format == 'raw') {
47324				content = self.serializer.getTrimmedContent();
47325			} else if (args.format == 'text') {
47326				content = body.innerText || body.textContent;
47327			} else {
47328				content = self.serializer.serialize(body, args);
47329			}
47330
47331			// Trim whitespace in beginning/end of HTML
47332			if (args.format != 'text') {
47333				args.content = trim(content);
47334			} else {
47335				args.content = content;
47336			}
47337
47338			// Do post processing
47339			if (!args.no_events) {
47340				self.fire('GetContent', args);
47341			}
47342
47343			return args.content;
47344		},
47345
47346		/**
47347		 * Inserts content at caret position.
47348		 *
47349		 * @method insertContent
47350		 * @param {String} content Content to insert.
47351		 * @param {Object} args Optional args to pass to insert call.
47352		 */
47353		insertContent: function(content, args) {
47354			if (args) {
47355				content = extend({content: content}, args);
47356			}
47357
47358			this.execCommand('mceInsertContent', false, content);
47359		},
47360
47361		/**
47362		 * Returns true/false if the editor is dirty or not. It will get dirty if the user has made modifications to the contents.
47363		 *
47364		 * The dirty state is automatically set to true if you do modifications to the content in other
47365		 * words when new undo levels is created or if you undo/redo to update the contents of the editor. It will also be set
47366		 * to false if you call editor.save().
47367		 *
47368		 * @method isDirty
47369		 * @return {Boolean} True/false if the editor is dirty or not. It will get dirty if the user has made modifications to the contents.
47370		 * @example
47371		 * if (tinymce.activeEditor.isDirty())
47372		 *     alert("You must save your contents.");
47373		 */
47374		isDirty: function() {
47375			return !this.isNotDirty;
47376		},
47377
47378		/**
47379		 * Explicitly sets the dirty state. This will fire the dirty event if the editor dirty state is changed from false to true
47380		 * by invoking this method.
47381		 *
47382		 * @method setDirty
47383		 * @param {Boolean} state True/false if the editor is considered dirty.
47384		 * @example
47385		 * function ajaxSave() {
47386		 *     var editor = tinymce.get('elm1');
47387		 *
47388		 *     // Save contents using some XHR call
47389		 *     alert(editor.getContent());
47390		 *
47391		 *     editor.setDirty(false); // Force not dirty state
47392		 * }
47393		 */
47394		setDirty: function(state) {
47395			var oldState = !this.isNotDirty;
47396
47397			this.isNotDirty = !state;
47398
47399			if (state && state != oldState) {
47400				this.fire('dirty');
47401			}
47402		},
47403
47404		/**
47405		 * Sets the editor mode. Mode can be for example "design", "code" or "readonly".
47406		 *
47407		 * @method setMode
47408		 * @param {String} mode Mode to set the editor in.
47409		 */
47410		setMode: function(mode) {
47411			Mode.setMode(this, mode);
47412		},
47413
47414		/**
47415		 * Returns the editors container element. The container element wrappes in
47416		 * all the elements added to the page for the editor. Such as UI, iframe etc.
47417		 *
47418		 * @method getContainer
47419		 * @return {Element} HTML DOM element for the editor container.
47420		 */
47421		getContainer: function() {
47422			var self = this;
47423
47424			if (!self.container) {
47425				self.container = DOM.get(self.editorContainer || self.id + '_parent');
47426			}
47427
47428			return self.container;
47429		},
47430
47431		/**
47432		 * Returns the editors content area container element. The this element is the one who
47433		 * holds the iframe or the editable element.
47434		 *
47435		 * @method getContentAreaContainer
47436		 * @return {Element} HTML DOM element for the editor area container.
47437		 */
47438		getContentAreaContainer: function() {
47439			return this.contentAreaContainer;
47440		},
47441
47442		/**
47443		 * Returns the target element/textarea that got replaced with a TinyMCE editor instance.
47444		 *
47445		 * @method getElement
47446		 * @return {Element} HTML DOM element for the replaced element.
47447		 */
47448		getElement: function() {
47449			if (!this.targetElm) {
47450				this.targetElm = DOM.get(this.id);
47451			}
47452
47453			return this.targetElm;
47454		},
47455
47456		/**
47457		 * Returns the iframes window object.
47458		 *
47459		 * @method getWin
47460		 * @return {Window} Iframe DOM window object.
47461		 */
47462		getWin: function() {
47463			var self = this, elm;
47464
47465			if (!self.contentWindow) {
47466				elm = self.iframeElement;
47467
47468				if (elm) {
47469					self.contentWindow = elm.contentWindow;
47470				}
47471			}
47472
47473			return self.contentWindow;
47474		},
47475
47476		/**
47477		 * Returns the iframes document object.
47478		 *
47479		 * @method getDoc
47480		 * @return {Document} Iframe DOM document object.
47481		 */
47482		getDoc: function() {
47483			var self = this, win;
47484
47485			if (!self.contentDocument) {
47486				win = self.getWin();
47487
47488				if (win) {
47489					self.contentDocument = win.document;
47490				}
47491			}
47492
47493			return self.contentDocument;
47494		},
47495
47496		/**
47497		 * Returns the root element of the editable area.
47498		 * For a non-inline iframe-based editor, returns the iframe's body element.
47499		 *
47500		 * @method getBody
47501		 * @return {Element} The root element of the editable area.
47502		 */
47503		getBody: function() {
47504			return this.bodyElement || this.getDoc().body;
47505		},
47506
47507		/**
47508		 * URL converter function this gets executed each time a user adds an img, a or
47509		 * any other element that has a URL in it. This will be called both by the DOM and HTML
47510		 * manipulation functions.
47511		 *
47512		 * @method convertURL
47513		 * @param {string} url URL to convert.
47514		 * @param {string} name Attribute name src, href etc.
47515		 * @param {string/HTMLElement} elm Tag name or HTML DOM element depending on HTML or DOM insert.
47516		 * @return {string} Converted URL string.
47517		 */
47518		convertURL: function(url, name, elm) {
47519			var self = this, settings = self.settings;
47520
47521			// Use callback instead
47522			if (settings.urlconverter_callback) {
47523				return self.execCallback('urlconverter_callback', url, elm, true, name);
47524			}
47525
47526			// Don't convert link href since thats the CSS files that gets loaded into the editor also skip local file URLs
47527			if (!settings.convert_urls || (elm && elm.nodeName == 'LINK') || url.indexOf('file:') === 0 || url.length === 0) {
47528				return url;
47529			}
47530
47531			// Convert to relative
47532			if (settings.relative_urls) {
47533				return self.documentBaseURI.toRelative(url);
47534			}
47535
47536			// Convert to absolute
47537			url = self.documentBaseURI.toAbsolute(url, settings.remove_script_host);
47538
47539			return url;
47540		},
47541
47542		/**
47543		 * Adds visual aid for tables, anchors etc so they can be more easily edited inside the editor.
47544		 *
47545		 * @method addVisual
47546		 * @param {Element} elm Optional root element to loop though to find tables etc that needs the visual aid.
47547		 */
47548		addVisual: function(elm) {
47549			var self = this, settings = self.settings, dom = self.dom, cls;
47550
47551			elm = elm || self.getBody();
47552
47553			if (self.hasVisual === undefined) {
47554				self.hasVisual = settings.visual;
47555			}
47556
47557			each(dom.select('table,a', elm), function(elm) {
47558				var value;
47559
47560				switch (elm.nodeName) {
47561					case 'TABLE':
47562						cls = settings.visual_table_class || 'mce-item-table';
47563						value = dom.getAttrib(elm, 'border');
47564
47565						if ((!value || value == '0') && self.hasVisual) {
47566							dom.addClass(elm, cls);
47567						} else {
47568							dom.removeClass(elm, cls);
47569						}
47570
47571						return;
47572
47573					case 'A':
47574						if (!dom.getAttrib(elm, 'href', false)) {
47575							value = dom.getAttrib(elm, 'name') || elm.id;
47576							cls = settings.visual_anchor_class || 'mce-item-anchor';
47577
47578							if (value && self.hasVisual) {
47579								dom.addClass(elm, cls);
47580							} else {
47581								dom.removeClass(elm, cls);
47582							}
47583						}
47584
47585						return;
47586				}
47587			});
47588
47589			self.fire('VisualAid', {element: elm, hasVisual: self.hasVisual});
47590		},
47591
47592		/**
47593		 * Removes the editor from the dom and tinymce collection.
47594		 *
47595		 * @method remove
47596		 */
47597		remove: function() {
47598			var self = this;
47599
47600			if (!self.removed) {
47601				self.save();
47602				self.removed = 1;
47603				self.unbindAllNativeEvents();
47604
47605				// Remove any hidden input
47606				if (self.hasHiddenInput) {
47607					DOM.remove(self.getElement().nextSibling);
47608				}
47609
47610				if (!self.inline) {
47611					// IE 9 has a bug where the selection stops working if you place the
47612					// caret inside the editor then remove the iframe
47613					if (ie && ie < 10) {
47614						self.getDoc().execCommand('SelectAll', false, null);
47615					}
47616
47617					DOM.setStyle(self.id, 'display', self.orgDisplay);
47618					self.getBody().onload = null; // Prevent #6816
47619				}
47620
47621				self.fire('remove');
47622
47623				self.editorManager.remove(self);
47624				DOM.remove(self.getContainer());
47625				self._selectionOverrides.destroy();
47626				self.editorUpload.destroy();
47627				self.destroy();
47628			}
47629		},
47630
47631		/**
47632		 * Destroys the editor instance by removing all events, element references or other resources
47633		 * that could leak memory. This method will be called automatically when the page is unloaded
47634		 * but you can also call it directly if you know what you are doing.
47635		 *
47636		 * @method destroy
47637		 * @param {Boolean} automatic Optional state if the destroy is an automatic destroy or user called one.
47638		 */
47639		destroy: function(automatic) {
47640			var self = this, form;
47641
47642			// One time is enough
47643			if (self.destroyed) {
47644				return;
47645			}
47646
47647			// If user manually calls destroy and not remove
47648			// Users seems to have logic that calls destroy instead of remove
47649			if (!automatic && !self.removed) {
47650				self.remove();
47651				return;
47652			}
47653
47654			if (!automatic) {
47655				self.editorManager.off('beforeunload', self._beforeUnload);
47656
47657				// Manual destroy
47658				if (self.theme && self.theme.destroy) {
47659					self.theme.destroy();
47660				}
47661
47662				// Destroy controls, selection and dom
47663				self.selection.destroy();
47664				self.dom.destroy();
47665			}
47666
47667			form = self.formElement;
47668			if (form) {
47669				if (form._mceOldSubmit) {
47670					form.submit = form._mceOldSubmit;
47671					form._mceOldSubmit = null;
47672				}
47673
47674				DOM.unbind(form, 'submit reset', self.formEventDelegate);
47675			}
47676
47677			self.contentAreaContainer = self.formElement = self.container = self.editorContainer = null;
47678			self.bodyElement = self.contentDocument = self.contentWindow = null;
47679			self.iframeElement = self.targetElm = null;
47680
47681			if (self.selection) {
47682				self.selection = self.selection.win = self.selection.dom = self.selection.dom.doc = null;
47683			}
47684
47685			self.destroyed = 1;
47686		},
47687
47688		/**
47689		 * Uploads all data uri/blob uri images in the editor contents to server.
47690		 *
47691		 * @method uploadImages
47692		 * @param {function} callback Optional callback with images and status for each image.
47693		 * @return {tinymce.util.Promise} Promise instance.
47694		 */
47695		uploadImages: function(callback) {
47696			return this.editorUpload.uploadImages(callback);
47697		},
47698
47699		// Internal functions
47700
47701		_scanForImages: function() {
47702			return this.editorUpload.scanForImages();
47703		},
47704
47705		_refreshContentEditable: function() {
47706			var self = this, body, parent;
47707
47708			// Check if the editor was hidden and the re-initialize contentEditable mode by removing and adding the body again
47709			if (self._isHidden()) {
47710				body = self.getBody();
47711				parent = body.parentNode;
47712
47713				parent.removeChild(body);
47714				parent.appendChild(body);
47715
47716				body.focus();
47717			}
47718		},
47719
47720		_isHidden: function() {
47721			var sel;
47722
47723			if (!isGecko) {
47724				return 0;
47725			}
47726
47727			// Weird, wheres that cursor selection?
47728			sel = this.selection.getSel();
47729			return (!sel || !sel.rangeCount || sel.rangeCount === 0);
47730		}
47731	};
47732
47733	extend(Editor.prototype, EditorObservable);
47734
47735	return Editor;
47736});
47737
47738// Included from: js/tinymce/classes/util/I18n.js
47739
47740/**
47741 * I18n.js
47742 *
47743 * Released under LGPL License.
47744 * Copyright (c) 1999-2015 Ephox Corp. All rights reserved
47745 *
47746 * License: http://www.tinymce.com/license
47747 * Contributing: http://www.tinymce.com/contributing
47748 */
47749
47750/**
47751 * I18n class that handles translation of TinyMCE UI.
47752 * Uses po style with csharp style parameters.
47753 *
47754 * @class tinymce.util.I18n
47755 */
47756define("tinymce/util/I18n", [], function() {
47757	"use strict";
47758
47759	var data = {}, code = "en";
47760
47761	return {
47762		/**
47763		 * Sets the current language code.
47764		 *
47765		 * @method setCode
47766		 * @param {String} newCode Current language code.
47767		 */
47768		setCode: function(newCode) {
47769			if (newCode) {
47770				code = newCode;
47771				this.rtl = this.data[newCode] ? this.data[newCode]._dir === 'rtl' : false;
47772			}
47773		},
47774
47775		/**
47776		 * Returns the current language code.
47777		 *
47778		 * @method getCode
47779		 * @return {String} Current language code.
47780		 */
47781		getCode: function() {
47782			return code;
47783		},
47784
47785		/**
47786		 * Property gets set to true if a RTL language pack was loaded.
47787		 *
47788		 * @property rtl
47789		 * @type Boolean
47790		 */
47791		rtl: false,
47792
47793		/**
47794		 * Adds translations for a specific language code.
47795		 *
47796		 * @method add
47797		 * @param {String} code Language code like sv_SE.
47798		 * @param {Array} items Name/value array with English en_US to sv_SE.
47799		 */
47800		add: function(code, items) {
47801			var langData = data[code];
47802
47803			if (!langData) {
47804				data[code] = langData = {};
47805			}
47806
47807			for (var name in items) {
47808				langData[name] = items[name];
47809			}
47810
47811			this.setCode(code);
47812		},
47813
47814		/**
47815		 * Translates the specified text.
47816		 *
47817		 * It has a few formats:
47818		 * I18n.translate("Text");
47819		 * I18n.translate(["Text {0}/{1}", 0, 1]);
47820		 * I18n.translate({raw: "Raw string"});
47821		 *
47822		 * @method translate
47823		 * @param {String/Object/Array} text Text to translate.
47824		 * @return {String} String that got translated.
47825		 */
47826		translate: function(text) {
47827			var langData;
47828
47829			langData = data[code];
47830			if (!langData) {
47831				langData = {};
47832			}
47833
47834			if (typeof text == "undefined") {
47835				return text;
47836			}
47837
47838			if (typeof text != "string" && text.raw) {
47839				return text.raw;
47840			}
47841
47842			if (text.push) {
47843				var values = text.slice(1);
47844
47845				text = (langData[text[0]] || text[0]).replace(/\{([0-9]+)\}/g, function(match1, match2) {
47846					return values[match2];
47847				});
47848			}
47849
47850			return (langData[text] || text).replace(/{context:\w+}$/, '');
47851		},
47852
47853		data: data
47854	};
47855});
47856
47857// Included from: js/tinymce/classes/FocusManager.js
47858
47859/**
47860 * FocusManager.js
47861 *
47862 * Released under LGPL License.
47863 * Copyright (c) 1999-2015 Ephox Corp. All rights reserved
47864 *
47865 * License: http://www.tinymce.com/license
47866 * Contributing: http://www.tinymce.com/contributing
47867 */
47868
47869/**
47870 * This class manages the focus/blur state of the editor. This class is needed since some
47871 * browsers fire false focus/blur states when the selection is moved to a UI dialog or similar.
47872 *
47873 * This class will fire two events focus and blur on the editor instances that got affected.
47874 * It will also handle the restore of selection when the focus is lost and returned.
47875 *
47876 * @class tinymce.FocusManager
47877 */
47878define("tinymce/FocusManager", [
47879	"tinymce/dom/DOMUtils",
47880	"tinymce/util/Delay",
47881	"tinymce/Env"
47882], function(DOMUtils, Delay, Env) {
47883	var selectionChangeHandler, documentFocusInHandler, documentMouseUpHandler, DOM = DOMUtils.DOM;
47884
47885	/**
47886	 * Constructs a new focus manager instance.
47887	 *
47888	 * @constructor FocusManager
47889	 * @param {tinymce.EditorManager} editorManager Editor manager instance to handle focus for.
47890	 */
47891	function FocusManager(editorManager) {
47892		function getActiveElement() {
47893			try {
47894				return document.activeElement;
47895			} catch (ex) {
47896				// IE sometimes fails to get the activeElement when resizing table
47897				// TODO: Investigate this
47898				return document.body;
47899			}
47900		}
47901
47902		// We can't store a real range on IE 11 since it gets mutated so we need to use a bookmark object
47903		// TODO: Move this to a separate range utils class since it's it's logic is present in Selection as well.
47904		function createBookmark(dom, rng) {
47905			if (rng && rng.startContainer) {
47906				// Verify that the range is within the root of the editor
47907				if (!dom.isChildOf(rng.startContainer, dom.getRoot()) || !dom.isChildOf(rng.endContainer, dom.getRoot())) {
47908					return;
47909				}
47910
47911				return {
47912					startContainer: rng.startContainer,
47913					startOffset: rng.startOffset,
47914					endContainer: rng.endContainer,
47915					endOffset: rng.endOffset
47916				};
47917			}
47918
47919			return rng;
47920		}
47921
47922		function bookmarkToRng(editor, bookmark) {
47923			var rng;
47924
47925			if (bookmark.startContainer) {
47926				rng = editor.getDoc().createRange();
47927				rng.setStart(bookmark.startContainer, bookmark.startOffset);
47928				rng.setEnd(bookmark.endContainer, bookmark.endOffset);
47929			} else {
47930				rng = bookmark;
47931			}
47932
47933			return rng;
47934		}
47935
47936		function isUIElement(elm) {
47937			return !!DOM.getParent(elm, FocusManager.isEditorUIElement);
47938		}
47939
47940		function registerEvents(e) {
47941			var editor = e.editor;
47942
47943			editor.on('init', function() {
47944				// Gecko/WebKit has ghost selections in iframes and IE only has one selection per browser tab
47945				if (editor.inline || Env.ie) {
47946					// Use the onbeforedeactivate event when available since it works better see #7023
47947					if ("onbeforedeactivate" in document && Env.ie < 9) {
47948						editor.dom.bind(editor.getBody(), 'beforedeactivate', function(e) {
47949							if (e.target != editor.getBody()) {
47950								return;
47951							}
47952
47953							try {
47954								editor.lastRng = editor.selection.getRng();
47955							} catch (ex) {
47956								// IE throws "Unexcpected call to method or property access" some times so lets ignore it
47957							}
47958						});
47959					} else {
47960						// On other browsers take snapshot on nodechange in inline mode since they have Ghost selections for iframes
47961						editor.on('nodechange mouseup keyup', function(e) {
47962							var node = getActiveElement();
47963
47964							// Only act on manual nodechanges
47965							if (e.type == 'nodechange' && e.selectionChange) {
47966								return;
47967							}
47968
47969							// IE 11 reports active element as iframe not body of iframe
47970							if (node && node.id == editor.id + '_ifr') {
47971								node = editor.getBody();
47972							}
47973
47974							if (editor.dom.isChildOf(node, editor.getBody())) {
47975								editor.lastRng = editor.selection.getRng();
47976							}
47977						});
47978					}
47979
47980					// Handles the issue with WebKit not retaining selection within inline document
47981					// If the user releases the mouse out side the body since a mouse up event wont occur on the body
47982					if (Env.webkit && !selectionChangeHandler) {
47983						selectionChangeHandler = function() {
47984							var activeEditor = editorManager.activeEditor;
47985
47986							if (activeEditor && activeEditor.selection) {
47987								var rng = activeEditor.selection.getRng();
47988
47989								// Store when it's non collapsed
47990								if (rng && !rng.collapsed) {
47991									editor.lastRng = rng;
47992								}
47993							}
47994						};
47995
47996						DOM.bind(document, 'selectionchange', selectionChangeHandler);
47997					}
47998				}
47999			});
48000
48001			editor.on('setcontent', function() {
48002				editor.lastRng = null;
48003			});
48004
48005			// Remove last selection bookmark on mousedown see #6305
48006			editor.on('mousedown', function() {
48007				editor.selection.lastFocusBookmark = null;
48008			});
48009
48010			editor.on('focusin', function() {
48011				var focusedEditor = editorManager.focusedEditor, lastRng;
48012
48013				if (editor.selection.lastFocusBookmark) {
48014					lastRng = bookmarkToRng(editor, editor.selection.lastFocusBookmark);
48015					editor.selection.lastFocusBookmark = null;
48016					editor.selection.setRng(lastRng);
48017				}
48018
48019				if (focusedEditor != editor) {
48020					if (focusedEditor) {
48021						focusedEditor.fire('blur', {focusedEditor: editor});
48022					}
48023
48024					editorManager.setActive(editor);
48025					editorManager.focusedEditor = editor;
48026					editor.fire('focus', {blurredEditor: focusedEditor});
48027					editor.focus(true);
48028				}
48029
48030				editor.lastRng = null;
48031			});
48032
48033			editor.on('focusout', function() {
48034				Delay.setEditorTimeout(editor, function() {
48035					var focusedEditor = editorManager.focusedEditor;
48036
48037					// Still the same editor the blur was outside any editor UI
48038					if (!isUIElement(getActiveElement()) && focusedEditor == editor) {
48039						editor.fire('blur', {focusedEditor: null});
48040						editorManager.focusedEditor = null;
48041
48042						// Make sure selection is valid could be invalid if the editor is blured and removed before the timeout occurs
48043						if (editor.selection) {
48044							editor.selection.lastFocusBookmark = null;
48045						}
48046					}
48047				});
48048			});
48049
48050			// Check if focus is moved to an element outside the active editor by checking if the target node
48051			// isn't within the body of the activeEditor nor a UI element such as a dialog child control
48052			if (!documentFocusInHandler) {
48053				documentFocusInHandler = function(e) {
48054					var activeEditor = editorManager.activeEditor;
48055
48056					if (activeEditor && e.target.ownerDocument == document) {
48057						// Check to make sure we have a valid selection don't update the bookmark if it's
48058						// a focusin to the body of the editor see #7025
48059						if (activeEditor.selection && e.target != activeEditor.getBody()) {
48060							activeEditor.selection.lastFocusBookmark = createBookmark(activeEditor.dom, activeEditor.lastRng);
48061						}
48062
48063						// Fire a blur event if the element isn't a UI element
48064						if (e.target != document.body && !isUIElement(e.target) && editorManager.focusedEditor == activeEditor) {
48065							activeEditor.fire('blur', {focusedEditor: null});
48066							editorManager.focusedEditor = null;
48067						}
48068					}
48069				};
48070
48071				DOM.bind(document, 'focusin', documentFocusInHandler);
48072			}
48073
48074			// Handle edge case when user starts the selection inside the editor and releases
48075			// the mouse outside the editor producing a new selection. This weird workaround is needed since
48076			// Gecko doesn't have the "selectionchange" event we need to do this. Fixes: #6843
48077			if (editor.inline && !documentMouseUpHandler) {
48078				documentMouseUpHandler = function(e) {
48079					var activeEditor = editorManager.activeEditor;
48080
48081					if (activeEditor.inline && !activeEditor.dom.isChildOf(e.target, activeEditor.getBody())) {
48082						var rng = activeEditor.selection.getRng();
48083
48084						if (!rng.collapsed) {
48085							activeEditor.lastRng = rng;
48086						}
48087					}
48088				};
48089
48090				DOM.bind(document, 'mouseup', documentMouseUpHandler);
48091			}
48092		}
48093
48094		function unregisterDocumentEvents(e) {
48095			if (editorManager.focusedEditor == e.editor) {
48096				editorManager.focusedEditor = null;
48097			}
48098
48099			if (!editorManager.activeEditor) {
48100				DOM.unbind(document, 'selectionchange', selectionChangeHandler);
48101				DOM.unbind(document, 'focusin', documentFocusInHandler);
48102				DOM.unbind(document, 'mouseup', documentMouseUpHandler);
48103				selectionChangeHandler = documentFocusInHandler = documentMouseUpHandler = null;
48104			}
48105		}
48106
48107		editorManager.on('AddEditor', registerEvents);
48108		editorManager.on('RemoveEditor', unregisterDocumentEvents);
48109	}
48110
48111	/**
48112	 * Returns true if the specified element is part of the UI for example an button or text input.
48113	 *
48114	 * @method isEditorUIElement
48115	 * @param  {Element} elm Element to check if it's part of the UI or not.
48116	 * @return {Boolean} True/false state if the element is part of the UI or not.
48117	 */
48118	FocusManager.isEditorUIElement = function(elm) {
48119		// Needs to be converted to string since svg can have focus: #6776
48120		return elm.className.toString().indexOf('mce-') !== -1;
48121	};
48122
48123	return FocusManager;
48124});
48125
48126// Included from: js/tinymce/classes/EditorManager.js
48127
48128/**
48129 * EditorManager.js
48130 *
48131 * Released under LGPL License.
48132 * Copyright (c) 1999-2015 Ephox Corp. All rights reserved
48133 *
48134 * License: http://www.tinymce.com/license
48135 * Contributing: http://www.tinymce.com/contributing
48136 */
48137
48138/**
48139 * This class used as a factory for manager for tinymce.Editor instances.
48140 *
48141 * @example
48142 * tinymce.EditorManager.init({});
48143 *
48144 * @class tinymce.EditorManager
48145 * @mixes tinymce.util.Observable
48146 * @static
48147 */
48148define("tinymce/EditorManager", [
48149	"tinymce/Editor",
48150	"tinymce/dom/DomQuery",
48151	"tinymce/dom/DOMUtils",
48152	"tinymce/util/URI",
48153	"tinymce/Env",
48154	"tinymce/util/Tools",
48155	"tinymce/util/Observable",
48156	"tinymce/util/I18n",
48157	"tinymce/FocusManager"
48158], function(Editor, $, DOMUtils, URI, Env, Tools, Observable, I18n, FocusManager) {
48159	var DOM = DOMUtils.DOM;
48160	var explode = Tools.explode, each = Tools.each, extend = Tools.extend;
48161	var instanceCounter = 0, beforeUnloadDelegate, EditorManager, boundGlobalEvents = false;
48162
48163	function globalEventDelegate(e) {
48164		each(EditorManager.editors, function(editor) {
48165			editor.fire('ResizeWindow', e);
48166		});
48167	}
48168
48169	function toggleGlobalEvents(editors, state) {
48170		if (state !== boundGlobalEvents) {
48171			if (state) {
48172				$(window).on('resize', globalEventDelegate);
48173			} else {
48174				$(window).off('resize', globalEventDelegate);
48175			}
48176
48177			boundGlobalEvents = state;
48178		}
48179	}
48180
48181	function removeEditorFromList(editor) {
48182		var editors = EditorManager.editors, removedFromList;
48183
48184		delete editors[editor.id];
48185
48186		for (var i = 0; i < editors.length; i++) {
48187			if (editors[i] == editor) {
48188				editors.splice(i, 1);
48189				removedFromList = true;
48190				break;
48191			}
48192		}
48193
48194		// Select another editor since the active one was removed
48195		if (EditorManager.activeEditor == editor) {
48196			EditorManager.activeEditor = editors[0];
48197		}
48198
48199		// Clear focusedEditor if necessary, so that we don't try to blur the destroyed editor
48200		if (EditorManager.focusedEditor == editor) {
48201			EditorManager.focusedEditor = null;
48202		}
48203
48204		return removedFromList;
48205	}
48206
48207	function purgeDestroyedEditor(editor) {
48208		// User has manually destroyed the editor lets clean up the mess
48209		if (editor && !(editor.getContainer() || editor.getBody()).parentNode) {
48210			removeEditorFromList(editor);
48211			editor.unbindAllNativeEvents();
48212			editor.destroy(true);
48213			editor = null;
48214		}
48215
48216		return editor;
48217	}
48218
48219	EditorManager = {
48220		/**
48221		 * Dom query instance.
48222		 *
48223		 * @property $
48224		 * @type tinymce.dom.DomQuery
48225		 */
48226		$: $,
48227
48228		/**
48229		 * Major version of TinyMCE build.
48230		 *
48231		 * @property majorVersion
48232		 * @type String
48233		 */
48234		majorVersion: '4',
48235
48236		/**
48237		 * Minor version of TinyMCE build.
48238		 *
48239		 * @property minorVersion
48240		 * @type String
48241		 */
48242		minorVersion: '3.3',
48243
48244		/**
48245		 * Release date of TinyMCE build.
48246		 *
48247		 * @property releaseDate
48248		 * @type String
48249		 */
48250		releaseDate: '2016-01-14',
48251
48252		/**
48253		 * Collection of editor instances.
48254		 *
48255		 * @property editors
48256		 * @type Object
48257		 * @example
48258		 * for (edId in tinymce.editors)
48259		 *     tinymce.editors[edId].save();
48260		 */
48261		editors: [],
48262
48263		/**
48264		 * Collection of language pack data.
48265		 *
48266		 * @property i18n
48267		 * @type Object
48268		 */
48269		i18n: I18n,
48270
48271		/**
48272		 * Currently active editor instance.
48273		 *
48274		 * @property activeEditor
48275		 * @type tinymce.Editor
48276		 * @example
48277		 * tinyMCE.activeEditor.selection.getContent();
48278		 * tinymce.EditorManager.activeEditor.selection.getContent();
48279		 */
48280		activeEditor: null,
48281
48282		setup: function() {
48283			var self = this, baseURL, documentBaseURL, suffix = "", preInit, src;
48284
48285			// Get base URL for the current document
48286			documentBaseURL = document.location.href;
48287
48288			// Check if the URL is a document based format like: http://site/dir/file and file:///
48289			// leave other formats like applewebdata://... intact
48290			if (/^[^:]+:\/\/\/?[^\/]+\//.test(documentBaseURL)) {
48291				documentBaseURL = documentBaseURL.replace(/[\?#].*$/, '').replace(/[\/\\][^\/]+$/, '');
48292
48293				if (!/[\/\\]$/.test(documentBaseURL)) {
48294					documentBaseURL += '/';
48295				}
48296			}
48297
48298			// If tinymce is defined and has a base use that or use the old tinyMCEPreInit
48299			preInit = window.tinymce || window.tinyMCEPreInit;
48300			if (preInit) {
48301				baseURL = preInit.base || preInit.baseURL;
48302				suffix = preInit.suffix;
48303			} else {
48304				// Get base where the tinymce script is located
48305				var scripts = document.getElementsByTagName('script');
48306				for (var i = 0; i < scripts.length; i++) {
48307					src = scripts[i].src;
48308
48309					// Script types supported:
48310					// tinymce.js tinymce.min.js tinymce.dev.js
48311					// tinymce.jquery.js tinymce.jquery.min.js tinymce.jquery.dev.js
48312					// tinymce.full.js tinymce.full.min.js tinymce.full.dev.js
48313					var srcScript = src.substring(src.lastIndexOf('/'));
48314					if (/tinymce(\.full|\.jquery|)(\.min|\.dev|)\.js/.test(src)) {
48315						if (srcScript.indexOf('.min') != -1) {
48316							suffix = '.min';
48317						}
48318
48319						baseURL = src.substring(0, src.lastIndexOf('/'));
48320						break;
48321					}
48322				}
48323
48324				// We didn't find any baseURL by looking at the script elements
48325				// Try to use the document.currentScript as a fallback
48326				if (!baseURL && document.currentScript) {
48327					src = document.currentScript.src;
48328
48329					if (src.indexOf('.min') != -1) {
48330						suffix = '.min';
48331					}
48332
48333					baseURL = src.substring(0, src.lastIndexOf('/'));
48334				}
48335			}
48336
48337			/**
48338			 * Base URL where the root directory if TinyMCE is located.
48339			 *
48340			 * @property baseURL
48341			 * @type String
48342			 */
48343			self.baseURL = new URI(documentBaseURL).toAbsolute(baseURL);
48344
48345			/**
48346			 * Document base URL where the current document is located.
48347			 *
48348			 * @property documentBaseURL
48349			 * @type String
48350			 */
48351			self.documentBaseURL = documentBaseURL;
48352
48353			/**
48354			 * Absolute baseURI for the installation path of TinyMCE.
48355			 *
48356			 * @property baseURI
48357			 * @type tinymce.util.URI
48358			 */
48359			self.baseURI = new URI(self.baseURL);
48360
48361			/**
48362			 * Current suffix to add to each plugin/theme that gets loaded for example ".min".
48363			 *
48364			 * @property suffix
48365			 * @type String
48366			 */
48367			self.suffix = suffix;
48368
48369			self.focusManager = new FocusManager(self);
48370		},
48371
48372		/**
48373		 * Initializes a set of editors. This method will create editors based on various settings.
48374		 *
48375		 * @method init
48376		 * @param {Object} settings Settings object to be passed to each editor instance.
48377		 * @example
48378		 * // Initializes a editor using the longer method
48379		 * tinymce.EditorManager.init({
48380		 *    some_settings : 'some value'
48381		 * });
48382		 *
48383		 * // Initializes a editor instance using the shorter version
48384		 * tinyMCE.init({
48385		 *    some_settings : 'some value'
48386		 * });
48387		 */
48388		init: function(settings) {
48389			var self = this, editors = [];
48390
48391			function createId(elm) {
48392				var id = elm.id;
48393
48394				// Use element id, or unique name or generate a unique id
48395				if (!id) {
48396					id = elm.name;
48397
48398					if (id && !DOM.get(id)) {
48399						id = elm.name;
48400					} else {
48401						// Generate unique name
48402						id = DOM.uniqueId();
48403					}
48404
48405					elm.setAttribute('id', id);
48406				}
48407
48408				return id;
48409			}
48410
48411			function createEditor(id, settings, targetElm) {
48412				if (!purgeDestroyedEditor(self.get(id))) {
48413					var editor = new Editor(id, settings, self);
48414
48415					editor.targetElm = editor.targetElm || targetElm;
48416					editors.push(editor);
48417					editor.render();
48418				}
48419			}
48420
48421			function execCallback(name) {
48422				var callback = settings[name];
48423
48424				if (!callback) {
48425					return;
48426				}
48427
48428				return callback.apply(self, Array.prototype.slice.call(arguments, 2));
48429			}
48430
48431			function hasClass(elm, className) {
48432				return className.constructor === RegExp ? className.test(elm.className) : DOM.hasClass(elm, className);
48433			}
48434
48435			function readyHandler() {
48436				var l, co;
48437
48438				DOM.unbind(window, 'ready', readyHandler);
48439
48440				execCallback('onpageload');
48441
48442				if (settings.types) {
48443					// Process type specific selector
48444					each(settings.types, function(type) {
48445						each(DOM.select(type.selector), function(elm) {
48446							createEditor(createId(elm), extend({}, settings, type), elm);
48447						});
48448					});
48449
48450					return;
48451				} else if (settings.selector) {
48452					// Process global selector
48453					each(DOM.select(settings.selector), function(elm) {
48454						createEditor(createId(elm), settings, elm);
48455					});
48456
48457					return;
48458				} else if (settings.target) {
48459					createEditor(createId(settings.target), settings);
48460				}
48461
48462				// Fallback to old setting
48463				switch (settings.mode) {
48464					case "exact":
48465						l = settings.elements || '';
48466
48467						if (l.length > 0) {
48468							each(explode(l), function(id) {
48469								var elm;
48470
48471								if ((elm = DOM.get(id))) {
48472									createEditor(id, settings, elm);
48473								} else {
48474									each(document.forms, function(f) {
48475										each(f.elements, function(e) {
48476											if (e.name === id) {
48477												id = 'mce_editor_' + instanceCounter++;
48478												DOM.setAttrib(e, 'id', id);
48479												createEditor(id, settings, e);
48480											}
48481										});
48482									});
48483								}
48484							});
48485						}
48486						break;
48487
48488					case "textareas":
48489					case "specific_textareas":
48490						each(DOM.select('textarea'), function(elm) {
48491							if (settings.editor_deselector && hasClass(elm, settings.editor_deselector)) {
48492								return;
48493							}
48494
48495							if (!settings.editor_selector || hasClass(elm, settings.editor_selector)) {
48496								createEditor(createId(elm), settings, elm);
48497							}
48498						});
48499						break;
48500				}
48501
48502				// Call onInit when all editors are initialized
48503				if (settings.oninit) {
48504					l = co = 0;
48505
48506					each(editors, function(ed) {
48507						co++;
48508
48509						if (!ed.initialized) {
48510							// Wait for it
48511							ed.on('init', function() {
48512								l++;
48513
48514								// All done
48515								if (l == co) {
48516									execCallback('oninit');
48517								}
48518							});
48519						} else {
48520							l++;
48521						}
48522
48523						// All done
48524						if (l == co) {
48525							execCallback('oninit');
48526						}
48527					});
48528				}
48529			}
48530
48531			self.settings = settings;
48532
48533			DOM.bind(window, 'ready', readyHandler);
48534		},
48535
48536		/**
48537		 * Returns a editor instance by id.
48538		 *
48539		 * @method get
48540		 * @param {String/Number} id Editor instance id or index to return.
48541		 * @return {tinymce.Editor} Editor instance to return.
48542		 * @example
48543		 * // Adds an onclick event to an editor by id (shorter version)
48544		 * tinymce.get('mytextbox').on('click', function(e) {
48545		 *    ed.windowManager.alert('Hello world!');
48546		 * });
48547		 *
48548		 * // Adds an onclick event to an editor by id (longer version)
48549		 * tinymce.EditorManager.get('mytextbox').on('click', function(e) {
48550		 *    ed.windowManager.alert('Hello world!');
48551		 * });
48552		 */
48553		get: function(id) {
48554			if (!arguments.length) {
48555				return this.editors;
48556			}
48557
48558			return id in this.editors ? this.editors[id] : null;
48559		},
48560
48561		/**
48562		 * Adds an editor instance to the editor collection. This will also set it as the active editor.
48563		 *
48564		 * @method add
48565		 * @param {tinymce.Editor} editor Editor instance to add to the collection.
48566		 * @return {tinymce.Editor} The same instance that got passed in.
48567		 */
48568		add: function(editor) {
48569			var self = this, editors = self.editors;
48570
48571			// Add named and index editor instance
48572			editors[editor.id] = editor;
48573			editors.push(editor);
48574
48575			toggleGlobalEvents(editors, true);
48576
48577			// Doesn't call setActive method since we don't want
48578			// to fire a bunch of activate/deactivate calls while initializing
48579			self.activeEditor = editor;
48580
48581			/**
48582			 * Fires when an editor is added to the EditorManager collection.
48583			 *
48584			 * @event AddEditor
48585			 * @param {Object} e Event arguments.
48586			 */
48587			self.fire('AddEditor', {editor: editor});
48588
48589			if (!beforeUnloadDelegate) {
48590				beforeUnloadDelegate = function() {
48591					self.fire('BeforeUnload');
48592				};
48593
48594				DOM.bind(window, 'beforeunload', beforeUnloadDelegate);
48595			}
48596
48597			return editor;
48598		},
48599
48600		/**
48601		 * Creates an editor instance and adds it to the EditorManager collection.
48602		 *
48603		 * @method createEditor
48604		 * @param {String} id Instance id to use for editor.
48605		 * @param {Object} settings Editor instance settings.
48606		 * @return {tinymce.Editor} Editor instance that got created.
48607		 */
48608		createEditor: function(id, settings) {
48609			return this.add(new Editor(id, settings, this));
48610		},
48611
48612		/**
48613		 * Removes a editor or editors form page.
48614		 *
48615		 * @example
48616		 * // Remove all editors bound to divs
48617		 * tinymce.remove('div');
48618		 *
48619		 * // Remove all editors bound to textareas
48620		 * tinymce.remove('textarea');
48621		 *
48622		 * // Remove all editors
48623		 * tinymce.remove();
48624		 *
48625		 * // Remove specific instance by id
48626		 * tinymce.remove('#id');
48627		 *
48628		 * @method remove
48629		 * @param {tinymce.Editor/String/Object}
48629 [selector] CSS selector or editor instance to remove.
48630		 * @return {tinymce.Editor} The editor that got passed in will be return if it was found otherwise null.
48631		 */
48632		remove: function(selector) {
48633			var self = this, i, editors = self.editors, editor;
48634
48635			// Remove all editors
48636			if (!selector) {
48637				for (i = editors.length - 1; i >= 0; i--) {
48638					self.remove(editors[i]);
48639				}
48640
48641				return;
48642			}
48643
48644			// Remove editors by selector
48645			if (typeof selector == "string") {
48646				selector = selector.selector || selector;
48647
48648				each(DOM.select(selector), function(elm) {
48649					editor = editors[elm.id];
48650
48651					if (editor) {
48652						self.remove(editor);
48653					}
48654				});
48655
48656				return;
48657			}
48658
48659			// Remove specific editor
48660			editor = selector;
48661
48662			// Not in the collection
48663			if (!editors[editor.id]) {
48664				return null;
48665			}
48666
48667			/**
48668			 * Fires when an editor is removed from EditorManager collection.
48669			 *
48670			 * @event RemoveEditor
48671			 * @param {Object} e Event arguments.
48672			 */
48673			if (removeEditorFromList(editor)) {
48674				self.fire('RemoveEditor', {editor: editor});
48675			}
48676
48677			if (!editors.length) {
48678				DOM.unbind(window, 'beforeunload', beforeUnloadDelegate);
48679			}
48680
48681			editor.remove();
48682
48683			toggleGlobalEvents(editors, editors.length > 0);
48684
48685			return editor;
48686		},
48687
48688		/**
48689		 * Executes a specific command on the currently active editor.
48690		 *
48691		 * @method execCommand
48692		 * @param {String} cmd Command to perform for example Bold.
48693		 * @param {Boolean} ui Optional boolean state if a UI should be presented for the command or not.
48694		 * @param {String} value Optional value parameter like for example an URL to a link.
48695		 * @return {Boolean} true/false if the command was executed or not.
48696		 */
48697		execCommand: function(cmd, ui, value) {
48698			var self = this, editor = self.get(value);
48699
48700			// Manager commands
48701			switch (cmd) {
48702				case "mceAddEditor":
48703					if (!self.get(value)) {
48704						new Editor(value, self.settings, self).render();
48705					}
48706
48707					return true;
48708
48709				case "mceRemoveEditor":
48710					if (editor) {
48711						editor.remove();
48712					}
48713
48714					return true;
48715
48716				case 'mceToggleEditor':
48717					if (!editor) {
48718						self.execCommand('mceAddEditor', 0, value);
48719						return true;
48720					}
48721
48722					if (editor.isHidden()) {
48723						editor.show();
48724					} else {
48725						editor.hide();
48726					}
48727
48728					return true;
48729			}
48730
48731			// Run command on active editor
48732			if (self.activeEditor) {
48733				return self.activeEditor.execCommand(cmd, ui, value);
48734			}
48735
48736			return false;
48737		},
48738
48739		/**
48740		 * Calls the save method on all editor instances in the collection. This can be useful when a form is to be submitted.
48741		 *
48742		 * @method triggerSave
48743		 * @example
48744		 * // Saves all contents
48745		 * tinyMCE.triggerSave();
48746		 */
48747		triggerSave: function() {
48748			each(this.editors, function(editor) {
48749				editor.save();
48750			});
48751		},
48752
48753		/**
48754		 * Adds a language pack, this gets called by the loaded language files like en.js.
48755		 *
48756		 * @method addI18n
48757		 * @param {String} code Optional language code.
48758		 * @param {Object} items Name/value object with translations.
48759		 */
48760		addI18n: function(code, items) {
48761			I18n.add(code, items);
48762		},
48763
48764		/**
48765		 * Translates the specified string using the language pack items.
48766		 *
48767		 * @method translate
48768		 * @param {String/Array/Object} text String to translate
48769		 * @return {String} Translated string.
48770		 */
48771		translate: function(text) {
48772			return I18n.translate(text);
48773		},
48774
48775		/**
48776		 * Sets the active editor instance and fires the deactivate/activate events.
48777		 *
48778		 * @method setActive
48779		 * @param {tinymce.Editor} editor Editor instance to set as the active instance.
48780		 */
48781		setActive: function(editor) {
48782			var activeEditor = this.activeEditor;
48783
48784			if (this.activeEditor != editor) {
48785				if (activeEditor) {
48786					activeEditor.fire('deactivate', {relatedTarget: editor});
48787				}
48788
48789				editor.fire('activate', {relatedTarget: activeEditor});
48790			}
48791
48792			this.activeEditor = editor;
48793		}
48794	};
48795
48796	extend(EditorManager, Observable);
48797
48798	EditorManager.setup();
48799
48800	// Export EditorManager as tinymce/tinymce in global namespace
48801	window.tinymce = window.tinyMCE = EditorManager;
48802
48803	return EditorManager;
48804});
48805
48806// Included from: js/tinymce/classes/LegacyInput.js
48807
48808/**
48809 * LegacyInput.js
48810 *
48811 * Released under LGPL License.
48812 * Copyright (c) 1999-2015 Ephox Corp. All rights reserved
48813 *
48814 * License: http://www.tinymce.com/license
48815 * Contributing: http://www.tinymce.com/contributing
48816 */
48817
48818/**
48819 * Converts legacy input to modern HTML.
48820 *
48821 * @class tinymce.LegacyInput
48822 * @private
48823 */
48824define("tinymce/LegacyInput", [
48825	"tinymce/EditorManager",
48826	"tinymce/util/Tools"
48827], function(EditorManager, Tools) {
48828	var each = Tools.each, explode = Tools.explode;
48829
48830	EditorManager.on('AddEditor', function(e) {
48831		var editor = e.editor;
48832
48833		editor.on('preInit', function() {
48834			var filters, fontSizes, dom, settings = editor.settings;
48835
48836			function replaceWithSpan(node, styles) {
48837				each(styles, function(value, name) {
48838					if (value) {
48839						dom.setStyle(node, name, value);
48840					}
48841				});
48842
48843				dom.rename(node, 'span');
48844			}
48845
48846			function convert(e) {
48847				dom = editor.dom;
48848
48849				if (settings.convert_fonts_to_spans) {
48850					each(dom.select('font,u,strike', e.node), function(node) {
48851						filters[node.nodeName.toLowerCase()](dom, node);
48852					});
48853				}
48854			}
48855
48856			if (settings.inline_styles) {
48857				fontSizes = explode(settings.font_size_legacy_values);
48858
48859				filters = {
48860					font: function(dom, node) {
48861						replaceWithSpan(node, {
48862							backgroundColor: node.style.backgroundColor,
48863							color: node.color,
48864							fontFamily: node.face,
48865							fontSize: fontSizes[parseInt(node.size, 10) - 1]
48866						});
48867					},
48868
48869					u: function(dom, node) {
48870						// HTML5 allows U element
48871						if (editor.settings.schema === "html4") {
48872							replaceWithSpan(node, {
48873								textDecoration: 'underline'
48874							});
48875						}
48876					},
48877
48878					strike: function(dom, node) {
48879						replaceWithSpan(node, {
48880							textDecoration: 'line-through'
48881						});
48882					}
48883				};
48884
48885				editor.on('PreProcess SetContent', convert);
48886			}
48887		});
48888	});
48889});
48890
48891// Included from: js/tinymce/classes/util/XHR.js
48892
48893/**
48894 * XHR.js
48895 *
48896 * Released under LGPL License.
48897 * Copyright (c) 1999-2015 Ephox Corp. All rights reserved
48898 *
48899 * License: http://www.tinymce.com/license
48900 * Contributing: http://www.tinymce.com/contributing
48901 */
48902
48903/**
48904 * This class enables you to send XMLHTTPRequests cross browser.
48905 * @class tinymce.util.XHR
48906 * @mixes tinymce.util.Observable
48907 * @static
48908 * @example
48909 * // Sends a low level Ajax request
48910 * tinymce.util.XHR.send({
48911 *    url: 'someurl',
48912 *    success: function(text) {
48913 *       console.debug(text);
48914 *    }
48915 * });
48916 *
48917 * // Add custom header to XHR request
48918 * tinymce.util.XHR.on('beforeSend', function(e) {
48919 *     e.xhr.setRequestHeader('X-Requested-With', 'Something');
48920 * });
48921 */
48922define("tinymce/util/XHR", [
48923	"tinymce/util/Observable",
48924	"tinymce/util/Tools"
48925], function(Observable, Tools) {
48926	var XHR = {
48927		/**
48928		 * Sends a XMLHTTPRequest.
48929		 * Consult the Wiki for details on what settings this method takes.
48930		 *
48931		 * @method send
48932		 * @param {Object} settings Object will target URL, callbacks and other info needed to make the request.
48933		 */
48934		send: function(settings) {
48935			var xhr, count = 0;
48936
48937			function ready() {
48938				if (!settings.async || xhr.readyState == 4 || count++ > 10000) {
48939					if (settings.success && count < 10000 && xhr.status == 200) {
48940						settings.success.call(settings.success_scope, '' + xhr.responseText, xhr, settings);
48941					} else if (settings.error) {
48942						settings.error.call(settings.error_scope, count > 10000 ? 'TIMED_OUT' : 'GENERAL', xhr, settings);
48943					}
48944
48945					xhr = null;
48946				} else {
48947					setTimeout(ready, 10);
48948				}
48949			}
48950
48951			// Default settings
48952			settings.scope = settings.scope || this;
48953			settings.success_scope = settings.success_scope || settings.scope;
48954			settings.error_scope = settings.error_scope || settings.scope;
48955			settings.async = settings.async === false ? false : true;
48956			settings.data = settings.data || '';
48957
48958			XHR.fire('beforeInitialize', {settings: settings});
48959
48960			xhr = new XMLHttpRequest();
48961
48962			if (xhr) {
48963				if (xhr.overrideMimeType) {
48964					xhr.overrideMimeType(settings.content_type);
48965				}
48966
48967				xhr.open(settings.type || (settings.data ? 'POST' : 'GET'), settings.url, settings.async);
48968
48969				if (settings.crossDomain) {
48970					xhr.withCredentials = true;
48971				}
48972
48973				if (settings.content_type) {
48974					xhr.setRequestHeader('Content-Type', settings.content_type);
48975				}
48976
48977				if (settings.requestheaders) {
48978					Tools.each(settings.requestheaders, function(header) {
48979						xhr.setRequestHeader(header.key, header.value);
48980					});
48981				}
48982
48983				xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
48984
48985				xhr = XHR.fire('beforeSend', {xhr: xhr, settings: settings}).xhr;
48986				xhr.send(settings.data);
48987
48988				// Syncronous request
48989				if (!settings.async) {
48990					return ready();
48991				}
48992
48993				// Wait for response, onReadyStateChange can not be used since it leaks memory in IE
48994				setTimeout(ready, 10);
48995			}
48996		}
48997	};
48998
48999	Tools.extend(XHR, Observable);
49000
49001	return XHR;
49002});
49003
49004// Included from: js/tinymce/classes/util/JSON.js
49005
49006/**
49007 * JSON.js
49008 *
49009 * Released under LGPL License.
49010 * Copyright (c) 1999-2015 Ephox Corp. All rights reserved
49011 *
49012 * License: http://www.tinymce.com/license
49013 * Contributing: http://www.tinymce.com/contributing
49014 */
49015
49016/**
49017 * JSON parser and serializer class.
49018 *
49019 * @class tinymce.util.JSON
49020 * @static
49021 * @example
49022 * // JSON parse a string into an object
49023 * var obj = tinymce.util.JSON.parse(somestring);
49024 *
49025 * // JSON serialize a object into an string
49026 * var str = tinymce.util.JSON.serialize(obj);
49027 */
49028define("tinymce/util/JSON", [], function() {
49029	function serialize(o, quote) {
49030		var i, v, t, name;
49031
49032		quote = quote || '"';
49033
49034		if (o === null) {
49035			return 'null';
49036		}
49037
49038		t = typeof o;
49039
49040		if (t == 'string') {
49041			v = '\bb\tt\nn\ff\rr\""\'\'\\\\';
49042
49043			return quote + o.replace(/([\u0080-\uFFFF\x00-\x1f\"\'\\])/g, function(a, b) {
49044				// Make sure single quotes never get encoded inside double quotes for JSON compatibility
49045				if (quote === '"' && a === "'") {
49046					return a;
49047				}
49048
49049				i = v.indexOf(b);
49050
49051				if (i + 1) {
49052					return '\\' + v.charAt(i + 1);
49053				}
49054
49055				a = b.charCodeAt().toString(16);
49056
49057				return '\\u' + '0000'.substring(a.length) + a;
49058			}) + quote;
49059		}
49060
49061		if (t == 'object') {
49062			if (o.hasOwnProperty && Object.prototype.toString.call(o) === '[object Array]') {
49063				for (i = 0, v = '['; i < o.length; i++) {
49064					v += (i > 0 ? ',' : '') + serialize(o[i], quote);
49065				}
49066
49067				return v + ']';
49068			}
49069
49070			v = '{';
49071
49072			for (name in o) {
49073				if (o.hasOwnProperty(name)) {
49074					v += typeof o[name] != 'function' ? (v.length > 1 ? ',' + quote : quote) + name +
49075						quote + ':' + serialize(o[name], quote) : '';
49076				}
49077			}
49078
49079			return v + '}';
49080		}
49081
49082		return '' + o;
49083	}
49084
49085	return {
49086		/**
49087		 * Serializes the specified object as a JSON string.
49088		 *
49089		 * @method serialize
49090		 * @param {Object} obj Object to serialize as a JSON string.
49091		 * @param {String}
49091 quote Optional quote string defaults to ".
49092		 * @return {string} JSON string serialized from input.
49093		 */
49094		serialize: serialize,
49095
49096		/**
49097		 * Unserializes/parses the specified JSON string into a object.
49098		 *
49099		 * @method parse
49100		 * @param {string} s JSON String to parse into a JavaScript object.
49101		 * @return {Object} Object from input JSON string or undefined if it failed.
49102		 */
49103		parse: function(text) {
49104			try {
49105				// Trick uglify JS
49106				return window[String.fromCharCode(101) + 'val']('(' + text + ')');
49107			} catch (ex) {
49108				// Ignore
49109			}
49110		}
49111
49112		/**#@-*/
49113	};
49114});
49115
49116// Included from: js/tinymce/classes/util/JSONRequest.js
49117
49118/**
49119 * JSONRequest.js
49120 *
49121 * Released under LGPL License.
49122 * Copyright (c) 1999-2015 Ephox Corp. All rights reserved
49123 *
49124 * License: http://www.tinymce.com/license
49125 * Contributing: http://www.tinymce.com/contributing
49126 */
49127
49128/**
49129 * This class enables you to use JSON-RPC to call backend methods.
49130 *
49131 * @class tinymce.util.JSONRequest
49132 * @example
49133 * var json = new tinymce.util.JSONRequest({
49134 *     url: 'somebackend.php'
49135 * });
49136 *
49137 * // Send RPC call 1
49138 * json.send({
49139 *     method: 'someMethod1',
49140 *     params: ['a', 'b'],
49141 *     success: function(result) {
49142 *         console.dir(result);
49143 *     }
49144 * });
49145 *
49146 * // Send RPC call 2
49147 * json.send({
49148 *     method: 'someMethod2',
49149 *     params: ['a', 'b'],
49150 *     success: function(result) {
49151 *         console.dir(result);
49152 *     }
49153 * });
49154 */
49155define("tinymce/util/JSONRequest", [
49156	"tinymce/util/JSON",
49157	"tinymce/util/XHR",
49158	"tinymce/util/Tools"
49159], function(JSON, XHR, Tools) {
49160	var extend = Tools.extend;
49161
49162	function JSONRequest(settings) {
49163		this.settings = extend({}, settings);
49164		this.count = 0;
49165	}
49166
49167	/**
49168	 * Simple helper function to send a JSON-RPC request without the need to initialize an object.
49169	 * Consult the Wiki API documentation for more details on what you can pass to this function.
49170	 *
49171	 * @method sendRPC
49172	 * @static
49173	 * @param {Object} o Call object where there are three field id, method and params this object should also contain callbacks etc.
49174	 */
49175	JSONRequest.sendRPC = function(o) {
49176		return new JSONRequest().send(o);
49177	};
49178
49179	JSONRequest.prototype = {
49180		/**
49181		 * Sends a JSON-RPC call. Consult the Wiki API documentation for more details on what you can pass to this function.
49182		 *
49183		 * @method send
49184		 * @param {Object} args Call object where there are three field id, method and params this object should also contain callbacks etc.
49185		 */
49186		send: function(args) {
49187			var ecb = args.error, scb = args.success;
49188
49189			args = extend(this.settings, args);
49190
49191			args.success = function(c, x) {
49192				c = JSON.parse(c);
49193
49194				if (typeof c == 'undefined') {
49195					c = {
49196						error: 'JSON Parse error.'
49197					};
49198				}
49199
49200				if (c.error) {
49201					ecb.call(args.error_scope || args.scope, c.error, x);
49202				} else {
49203					scb.call(args.success_scope || args.scope, c.result);
49204				}
49205			};
49206
49207			args.error = function(ty, x) {
49208				if (ecb) {
49209					ecb.call(args.error_scope || args.scope, ty, x);
49210				}
49211			};
49212
49213			args.data = JSON.serialize({
49214				id: args.id || 'c' + (this.count++),
49215				method: args.method,
49216				params: args.params
49217			});
49218
49219			// JSON content type for Ruby on rails. Bug: #1883287
49220			args.content_type = 'application/json';
49221
49222			XHR.send(args);
49223		}
49224	};
49225
49226	return JSONRequest;
49227});
49228
49229// Included from: js/tinymce/classes/util/JSONP.js
49230
49231/**
49232 * JSONP.js
49233 *
49234 * Released under LGPL License.
49235 * Copyright (c) 1999-2015 Ephox Corp. All rights reserved
49236 *
49237 * License: http://www.tinymce.com/license
49238 * Contributing: http://www.tinymce.com/contributing
49239 */
49240
49241define("tinymce/util/JSONP", [
49242	"tinymce/dom/DOMUtils"
49243], function(DOMUtils) {
49244	return {
49245		callbacks: {},
49246		count: 0,
49247
49248		send: function(settings) {
49249			var self = this, dom = DOMUtils.DOM, count = settings.count !== undefined ? settings.count : self.count;
49250			var id = 'tinymce_jsonp_' + count;
49251
49252			self.callbacks[count] = function(json) {
49253				dom.remove(id);
49254				delete self.callbacks[count];
49255
49256				settings.callback(json);
49257			};
49258
49259			dom.add(dom.doc.body, 'script', {
49260				id: id,
49261				src: settings.url,
49262				type: 'text/javascript'
49263			});
49264
49265			self.count++;
49266		}
49267	};
49268});
49269
49270// Included from: js/tinymce/classes/util/LocalStorage.js
49271
49272/**
49273 * LocalStorage.js
49274 *
49275 * Released under LGPL License.
49276 * Copyright (c) 1999-2015 Ephox Corp. All rights reserved
49277 *
49278 * License: http://www.tinymce.com/license
49279 * Contributing: http://www.tinymce.com/contributing
49280 */
49281
49282/**
49283 * This class will simulate LocalStorage on IE 7 and return the native version on modern browsers.
49284 * Storage is done using userData on IE 7 and a special serialization format. The format is designed
49285 * to be as small as possible by making sure that the keys and values doesn't need to be encoded. This
49286 * makes it possible to store for example HTML data.
49287 *
49288 * Storage format for userData:
49289 * <base 32 key length>,<key string>,<base 32 value length>,<value>,...
49290 *
49291 * For example this data key1=value1,key2=value2 would be:
49292 * 4,key1,6,value1,4,key2,6,value2
49293 *
49294 * @class tinymce.util.LocalStorage
49295 * @static
49296 * @version 4.0
49297 * @example
49298 * tinymce.util.LocalStorage.setItem('key', 'value');
49299 * var value = tinymce.util.LocalStorage.getItem('key');
49300 */
49301define("tinymce/util/LocalStorage", [], function() {
49302	var LocalStorage, storageElm, items, keys, userDataKey, hasOldIEDataSupport;
49303
49304	// Check for native support
49305	try {
49306		if (window.localStorage) {
49307			return localStorage;
49308		}
49309	} catch (ex) {
49310		// Ignore
49311	}
49312
49313	userDataKey = "tinymce";
49314	storageElm = document.documentElement;
49315	hasOldIEDataSupport = !!storageElm.addBehavior;
49316
49317	if (hasOldIEDataSupport) {
49318		storageElm.addBehavior('#default#userData');
49319	}
49320
49321	/**
49322	 * Gets the keys names and updates LocalStorage.length property. Since IE7 doesn't have any getters/setters.
49323	 */
49324	function updateKeys() {
49325		keys = [];
49326
49327		for (var key in items) {
49328			keys.push(key);
49329		}
49330
49331		LocalStorage.length = keys.length;
49332	}
49333
49334	/**
49335	 * Loads the userData string and parses it into the items structure.
49336	 */
49337	function load() {
49338		var key, data, value, pos = 0;
49339
49340		items = {};
49341
49342		// localStorage can be disabled on WebKit/Gecko s
49342o make a dummy storage
49343		if (!hasOldIEDataSupport) {
49344			return;
49345		}
49346
49347		function next(end) {
49348			var value, nextPos;
49349
49350			nextPos = end !== undefined ? pos + end : data.indexOf(',', pos);
49351			if (nextPos === -1 || nextPos > data.length) {
49352				return null;
49353			}
49354
49355			value = data.substring(pos, nextPos);
49356			pos = nextPos + 1;
49357
49358			return value;
49359		}
49360
49361		storageElm.load(userDataKey);
49362		data = storageElm.getAttribute(userDataKey) || '';
49363
49364		do {
49365			var offset = next();
49366			if (offset === null) {
49367				break;
49368			}
49369
49370			key = next(parseInt(offset, 32) || 0);
49371			if (key !== null) {
49372				offset = next();
49373				if (offset === null) {
49374					break;
49375				}
49376
49377				value = next(parseInt(offset, 32) || 0);
49378
49379				if (key) {
49380					items[key] = value;
49381				}
49382			}
49383		} while (key !== null);
49384
49385		updateKeys();
49386	}
49387
49388	/**
49389	 * Saves the items structure into a the userData format.
49390	 */
49391	function save() {
49392		var value, data = '';
49393
49394		// localStorage can be disabled on WebKit/Gecko so make a dummy storage
49395		if (!hasOldIEDataSupport) {
49396			return;
49397		}
49398
49399		for (var key in items) {
49400			value = items[key];
49401			data += (data ? ',' : '') + key.length.toString(32) + ',' + key + ',' + value.length.toString(32) + ',' + value;
49402		}
49403
49404		storageElm.setAttribute(userDataKey, data);
49405
49406		try {
49407			storageElm.save(userDataKey);
49408		} catch (ex) {
49409			// Ignore disk full
49410		}
49411
49412		updateKeys();
49413	}
49414
49415	LocalStorage = {
49416		/**
49417		 * Length of the number of items in storage.
49418		 *
49419		 * @property length
49420		 * @type Number
49421		 * @return {Number} Number of items in storage.
49422		 */
49423		//length:0,
49424
49425		/**
49426		 * Returns the key name by index.
49427		 *
49428		 * @method key
49429		 * @param {Number} index Index of key to return.
49430		 * @return {String} Key value or null if it wasn't found.
49431		 */
49432		key: function(index) {
49433			return keys[index];
49434		},
49435
49436		/**
49437		 * Returns the value if the specified key or null if it wasn't found.
49438		 *
49439		 * @method getItem
49440		 * @param {String} key Key of item to retrive.
49441		 * @return {String} Value of the specified item or null if it wasn't found.
49442		 */
49443		getItem: function(key) {
49444			return key in items ? items[key] : null;
49445		},
49446
49447		/**
49448		 * Sets the value of the specified item by it's key.
49449		 *
49450		 * @method setItem
49451		 * @param {String} key Key of the item to set.
49452		 * @param {String} value Value of the item to set.
49453		 */
49454		setItem: function(key, value) {
49455			items[key] = "" + value;
49456			save();
49457		},
49458
49459		/**
49460		 * Removes the specified item by key.
49461		 *
49462		 * @method removeItem
49463		 * @param {String} key Key of item to remove.
49464		 */
49465		removeItem: function(key) {
49466			delete items[key];
49467			save();
49468		},
49469
49470		/**
49471		 * Removes all items.
49472		 *
49473		 * @method clear
49474		 */
49475		clear: function() {
49476			items = {};
49477			save();
49478		}
49479	};
49480
49481	load();
49482
49483	return LocalStorage;
49484});
49485
49486// Included from: js/tinymce/classes/Compat.js
49487
49488/**
49489 * Compat.js
49490 *
49491 * Released under LGPL License.
49492 * Copyright (c) 1999-2015 Ephox Corp. All rights reserved
49493 *
49494 * License: http://www.tinymce.com/license
49495 * Contributing: http://www.tinymce.com/contributing
49496 */
49497
49498/**
49499 * TinyMCE core class.
49500 *
49501 * @static
49502 * @class tinymce
49503 * @borrow-members tinymce.EditorManager
49504 * @borrow-members tinymce.util.Tools
49505 */
49506define("tinymce/Compat", [
49507	"tinymce/dom/DOMUtils",
49508	"tinymce/dom/EventUtils",
49509	"tinymce/dom/ScriptLoader",
49510	"tinymce/AddOnManager",
49511	"tinymce/util/Tools",
49512	"tinymce/Env"
49513], function(DOMUtils, EventUtils, ScriptLoader, AddOnManager, Tools, Env) {
49514	var tinymce = window.tinymce;
49515
49516	/**
49517	 * @property {tinymce.dom.DOMUtils} DOM Global DOM instance.
49518	 * @property {tinymce.dom.ScriptLoader} ScriptLoader Global ScriptLoader instance.
49519	 * @property {tinymce.AddOnManager} PluginManager Global PluginManager instance.
49520	 * @property {tinymce.AddOnManager} ThemeManager Global ThemeManager instance.
49521	 */
49522	tinymce.DOM = DOMUtils.DOM;
49523	tinymce.ScriptLoader = ScriptLoader.ScriptLoader;
49524	tinymce.PluginManager = AddOnManager.PluginManager;
49525	tinymce.ThemeManager = AddOnManager.ThemeManager;
49526
49527	tinymce.dom = tinymce.dom || {};
49528	tinymce.dom.Event = EventUtils.Event;
49529
49530	Tools.each(Tools, function(func, key) {
49531		tinymce[key] = func;
49532	});
49533
49534	Tools.each('isOpera isWebKit isIE isGecko isMac'.split(' '), function(name) {
49535		tinymce[name] = Env[name.substr(2).toLowerCase()];
49536	});
49537
49538	return {};
49539});
49540
49541// Describe the different namespaces
49542
49543/**
49544 * Root level namespace this contains classes directly related to the TinyMCE editor.
49545 *
49546 * @namespace tinymce
49547 */
49548
49549/**
49550 * Contains classes for handling the browsers DOM.
49551 *
49552 * @namespace tinymce.dom
49553 */
49554
49555/**
49556 * Contains html parser and serializer logic.
49557 *
49558 * @namespace tinymce.html
49559 */
49560
49561/**
49562 * Contains the different UI types such as buttons, listboxes etc.
49563 *
49564 * @namespace tinymce.ui
49565 */
49566
49567/**
49568 * Contains various utility classes such as json parser, cookies etc.
49569 *
49570 * @namespace tinymce.util
49571 */
49572
49573/**
49574 * Contains modules to handle data binding.
49575 *
49576 * @namespace tinymce.data
49577 */
49578
49579// Included from: js/tinymce/classes/ui/Layout.js
49580
49581/**
49582 * Layout.js
49583 *
49584 * Released under LGPL License.
49585 * Copyright (c) 1999-2015 Ephox Corp. All rights reserved
49586 *
49587 * License: http://www.tinymce.com/license
49588 * Contributing: http://www.tinymce.com/contributing
49589 */
49590
49591/**
49592 * Base layout manager class.
49593 *
49594 * @class tinymce.ui.Layout
49595 */
49596define("tinymce/ui/Layout", [
49597	"tinymce/util/Class",
49598	"tinymce/util/Tools"
49599], function(Class, Tools) {
49600	"use strict";
49601
49602	return Class.extend({
49603		Defaults: {
49604			firstControlClass: 'first',
49605			lastControlClass: 'last'
49606		},
49607
49608		/**
49609		 * Constructs a layout instance with the specified settings.
49610		 *
49611		 * @constructor
49612		 * @param {Object} settings Name/value object with settings.
49613		 */
49614		init: function(settings) {
49615			this.settings = Tools.extend({}, this.Defaults, settings);
49616		},
49617
49618		/**
49619		 * This method gets invoked before the layout renders the controls.
49620		 *
49621		 * @method preRender
49622		 * @param {tinymce.ui.Container} container Container instance to preRender.
49623		 */
49624		preRender: function(container) {
49625			container.bodyClasses.add(this.settings.containerClass);
49626		},
49627
49628		/**
49629		 * Applies layout classes to the container.
49630		 *
49631		 * @private
49632		 */
49633		applyClasses: function(items) {
49634			var self = this, settings = self.settings, firstClass, lastClass, firstItem, lastItem;
49635
49636			firstClass = settings.firstControlClass;
49637			lastClass = settings.lastControlClass;
49638
49639			items.each(function(item) {
49640				item.classes.remove(firstClass).remove(lastClass).add(settings.controlClass);
49641
49642				if (item.visible()) {
49643					if (!firstItem) {
49644						firstItem = item;
49645					}
49646
49647					lastItem = item;
49648				}
49649			});
49650
49651			if (firstItem) {
49652				firstItem.classes.add(firstClass);
49653			}
49654
49655			if (lastItem) {
49656				lastItem.classes.add(lastClass);
49657			}
49658		},
49659
49660		/**
49661		 * Renders the specified container and any layout specific HTML.
49662		 *
49663		 * @method renderHtml
49664		 * @param {tinymce.ui.Container} container Container to render HTML for.
49665		 */
49666		renderHtml: function(container) {
49667			var self = this, html = '';
49668
49669			self.applyClasses(container.items());
49670
49671			container.items().each(function(item) {
49672				html += item.renderHtml();
49673			});
49674
49675			return html;
49676		},
49677
49678		/**
49679		 * Recalculates the positions of the controls in the specified container.
49680		 *
49681		 * @method recalc
49682		 * @param {tinymce.ui.Container} container Container instance to recalc.
49683		 */
49684		recalc: function() {
49685		},
49686
49687		/**
49688		 * This method gets invoked after the layout renders the controls.
49689		 *
49690		 * @method postRender
49691		 * @param {tinymce.ui.Container} container Container instance to postRender.
49692		 */
49693		postRender: function() {
49694		},
49695
49696		isNative: function() {
49697			return false;
49698		}
49699	});
49700});
49701
49702// Included from: js/tinymce/classes/ui/AbsoluteLayout.js
49703
49704/**
49705 * AbsoluteLayout.js
49706 *
49707 * Released under LGPL License.
49708 * Copyright (c) 1999-2015 Ephox Corp. All rights reserved
49709 *
49710 * License: http://www.tinymce.com/license
49711 * Contributing: http://www.tinymce.com/contributing
49712 */
49713
49714/**
49715 * LayoutManager for absolute positioning. This layout manager is more of
49716 * a base class for other layouts but can be created and used directly.
49717 *
49718 * @-x-less AbsoluteLayout.less
49719 * @class tinymce.ui.AbsoluteLayout
49720 * @extends tinymce.ui.Layout
49721 */
49722define("tinymce/ui/AbsoluteLayout", [
49723	"tinymce/ui/Layout"
49724], function(Layout) {
49725	"use strict";
49726
49727	return Layout.extend({
49728		Defaults: {
49729			containerClass: 'abs-layout',
49730			controlClass: 'abs-layout-item'
49731		},
49732
49733		/**
49734		 * Recalculates the positions of the controls in the specified container.
49735		 *
49736		 * @method recalc
49737		 * @param {tinymce.ui.Container} container Container instance to recalc.
49738		 */
49739		recalc: function(container) {
49740			container.items().filter(':visible').each(function(ctrl) {
49741				var settings = ctrl.settings;
49742
49743				ctrl.layoutRect({
49744					x: settings.x,
49745					y: settings.y,
49746					w: settings.w,
49747					h: settings.h
49748				});
49749
49750				if (ctrl.recalc) {
49751					ctrl.recalc();
49752				}
49753			});
49754		},
49755
49756		/**
49757		 * Renders the specified container and any layout specific HTML.
49758		 *
49759		 * @method renderHtml
49760		 * @param {tinymce.ui.Container} container Container to render HTML for.
49761		 */
49762		renderHtml: function(container) {
49763			return '<div id="' + container._id + '-absend" class="' + container.classPrefix + 'abs-end"></div>' + this._super(container);
49764		}
49765	});
49766});
49767
49768// Included from: js/tinymce/classes/ui/Button.js
49769
49770/**
49771 * Button.js
49772 *
49773 * Released under LGPL License.
49774 * Copyright (c) 1999-2015 Ephox Corp. All rights reserved
49775 *
49776 * License: http://www.tinymce.com/license
49777 * Contributing: http://www.tinymce.com/contributing
49778 */
49779
49780/**
49781 * This class is used to create buttons. You can create them directly or through the Factory.
49782 *
49783 * @example
49784 * // Create and render a button to the body element
49785 * tinymce.ui.Factory.create({
49786 *     type: 'button',
49787 *     text: 'My button'
49788 * }).renderTo(document.body);
49789 *
49790 * @-x-less Button.less
49791 * @class tinymce.ui.Button
49792 * @extends tinymce.ui.Widget
49793 */
49794define("tinymce/ui/Button", [
49795	"tinymce/ui/Widget"
49796], function(Widget) {
49797	"use strict";
49798
49799	return Widget.extend({
49800		Defaults: {
49801			classes: "widget btn",
49802			role: "button"
49803		},
49804
49805		/**
49806		 * Constructs a new button instance with the specified settings.
49807		 *
49808		 * @constructor
49809		 * @param {Object} settings Name/value object with settings.
49810		 * @setting {String} size Size of the button small|medium|large.
49811		 * @setting {String} image Image to use for icon.
49812		 * @setting {String} icon Icon to use for button.
49813		 */
49814		init: function(settings) {
49815			var self = this, size;
49816
49817			self._super(settings);
49818			settings = self.settings;
49819
49820			size = self.settings.size;
49821
49822			self.on('click mousedown', function(e) {
49823				e.preventDefault();
49824			});
49825
49826			self.on('touchstart', function(e) {
49827				self.fire('click', e);
49828				e.preventDefault();
49829			});
49830
49831			if (settings.subtype) {
49832				self.classes.add(settings.subtype);
49833			}
49834
49835			if (size) {
49836				self.classes.add('btn-' + size);
49837			}
49838
49839			if (settings.icon) {
49840				self.icon(settings.icon);
49841			}
49842		},
49843
49844		/**
49845		 * Sets/gets the current button icon.
49846		 *
49847		 * @method icon
49848		 * @param {String} [icon] New icon identifier.
49849		 * @return {String|tinymce.ui.MenuButton} Current icon or current MenuButton instance.
49850		 */
49851		icon: function(icon) {
49852			if (!arguments.length) {
49853				return this.state.get('icon');
49854			}
49855
49856			this.state.set('icon', icon);
49857
49858			return this;
49859		},
49860
49861		/**
49862		 * Repaints the button for example after it's been resizes by a layout engine.
49863		 *
49864		 * @method repaint
49865		 */
49866		repaint: function() {
49867			var btnElm = this.getEl().firstChild,
49868				btnStyle;
49869
49870			if (btnElm) {
49871				btnStyle = btnElm.style;
49872				btnStyle.width = btnStyle.height = "100%";
49873			}
49874
49875			this._super();
49876		},
49877
49878		/**
49879		 * Renders the control as a HTML string.
49880		 *
49881		 * @method renderHtml
49882		 * @return {String} HTML representing the control.
49883		 */
49884		renderHtml: function() {
49885			var self = this, id = self._id, prefix = self.classPrefix;
49886			var icon = self.state.get('icon'), image, text = self.state.get('text'), textHtml = '';
49887
49888			image = self.settings.image;
49889			if (image) {
49890				icon = 'none';
49891
49892				// Support for [high dpi, low dpi] image sources
49893				if (typeof image != "string") {
49894					image = window.getSelection ? image[0] : image[1];
49895				}
49896
49897				image = ' style="background-image: url(\'' + image + '\')"';
49898			} else {
49899				image = '';
49900			}
49901
49902			if (text) {
49903				self.classes.add('btn-has-text');
49904				textHtml = '<span class="' + prefix + 'txt">' + self.encode(text) + '</span>';
49905			}
49906
49907			icon = self.settings.icon ? prefix + 'ico ' + prefix + 'i-' + icon : '';
49908
49909			return (
49910				'<div id="' + id + '" class="' + self.classes + '" tabindex="-1" aria-labelledby="' + id + '">' +
49911					'<button role="presentation" type="button" tabindex="-1">' +
49912						(icon ? '<i class="' + icon + '"' + image + '></i>' : '') +
49913						textHtml +
49914					'</button>' +
49915				'</div>'
49916			);
49917		},
49918
49919		bindStates: function() {
49920			var self = this, $ = self.$, textCls = self.classPrefix + 'txt';
49921
49922			function setButtonText(text) {
49923				var $span = $('span.' + textCls, self.getEl());
49924
49925				if (text) {
49926					if (!$span[0]) {
49927						$('button:first', self.getEl()).append('<span class="' + textCls + '"></span>');
49928						$span = $('span.' + textCls, self.getEl());
49929					}
49930
49931					$span.html(self.encode(text));
49932				} else {
49933					$span.remove();
49934				}
49935
49936				self.classes.toggle('btn-has-text', !!text);
49937			}
49938
49939			self.state.on('change:text', function(e) {
49940				setButtonText(e.value);
49941			});
49942
49943			self.state.on('change:icon', function(e) {
49944				var icon = e.value, prefix = self.classPrefix;
49945
49946				self.settings.icon = icon;
49947				icon = icon ? prefix + 'ico ' + prefix + 'i-' + self.settings.icon : '';
49948
49949				var btnElm = self.getEl().firstChild, iconElm = btnElm.getElementsByTagName('i')[0];
49950
49951				if (icon) {
49952					if (!iconElm || iconElm != btnElm.firstChild) {
49953						iconElm = document.createElement('i');
49954						btnElm.insertBefore(iconElm, btnElm.firstChild);
49955					}
49956
49957					iconElm.className = icon;
49958				} else if (iconElm) {
49959					btnElm.removeChild(iconElm);
49960				}
49961
49962				setButtonText(self.state.get('text'));
49963			});
49964
49965			return self._super();
49966		}
49967	});
49968});
49969
49970// Included from: js/tinymce/classes/ui/ButtonGroup.js
49971
49972/**
49973 * ButtonGroup.js
49974 *
49975 * Released under LGPL License.
49976 * Copyright (c) 1999-2015 Ephox Corp. All rights reserved
49977 *
49978 * License: http://www.tinymce.com/license
49979 * Contributing: http://www.tinymce.com/contributing
49980 */
49981
49982/**
49983 * This control enables you to put multiple buttons into a group. This is
49984 * useful when you want to combine similar toolbar buttons into a group.
49985 *
49986 * @example
49987 * // Create and render a buttongroup with two buttons to the body element
49988 * tinymce.ui.Factory.create({
49989 *     type: 'buttongroup',
49990 *     items: [
49991 *         {text: 'Button A'},
49992 *         {text: 'Button B'}
49993 *     ]
49994 * }).renderTo(document.body);
49995 *
49996 * @-x-less ButtonGroup.less
49997 * @class tinymce.ui.ButtonGroup
49998 * @extends tinymce.ui.Container
49999 */
50000define("tinymce/ui/ButtonGroup", [
50001	"tinymce/ui/Container"
50002], function(Container) {
50003	"use strict";
50004
50005	return Container.extend({
50006		Defaults: {
50007			defaultType: 'button',
50008			role: 'group'
50009		},
50010
50011		/**
50012		 * Renders the control as a HTML string.
50013		 *
50014		 * @method renderHtml
50015		 * @return {String} HTML representing the control.
50016		 */
50017		renderHtml: function() {
50018			var self = this, layout = self._layout;
50019
50020			self.classes.add('btn-group');
50021			self.preRender();
50022			layout.preRender(self);
50023
50024			return (
50025				'<div id="' + self._id + '" class="' + self.classes + '">' +
50026					'<div id="' + self._id + '-body">' +
50027						(self.settings.html || '') + layout.renderHtml(self) +
50028					'</div>' +
50029				'</div>'
50030			);
50031		}
50032	});
50033});
50034
50035// Included from: js/tinymce/classes/ui/Checkbox.js
50036
50037/**
50038 * Checkbox.js
50039 *
50040 * Released under LGPL License.
50041 * Copyright (c) 1999-2015 Ephox Corp. All rights reserved
50042 *
50043 * License: http://www.tinymce.com/license
50044 * Contributing: http://www.tinymce.com/contributing
50045 */
50046
50047/**
50048 * This control creates a custom checkbox.
50049 *
50050 * @example
50051 * // Create and render a checkbox to the body element
50052 * tinymce.ui.Factory.create({
50053 *     type: 'checkbox',
50054 *     checked: true,
50055 *     text: 'My checkbox'
50056 * }).renderTo(document.body);
50057 *
50058 * @-x-less Checkbox.less
50059 * @class tinymce.ui.Checkbox
50060 * @extends tinymce.ui.Widget
50061 */
50062define("tinymce/ui/Checkbox", [
50063	"tinymce/ui/Widget"
50064], function(Widget) {
50065	"use strict";
50066
50067	return Widget.extend({
50068		Defaults: {
50069			classes: "checkbox",
50070			role: "checkbox",
50071			checked: false
50072		},
50073
50074		/**
50075		 * Constructs a new Checkbox instance with the specified settings.
50076		 *
50077		 * @constructor
50078		 * @param {Object} settings Name/value object with settings.
50079		 * @setting {Boolean} checked True if the checkbox should be checked by default.
50080		 */
50081		init: function(settings) {
50082			var self = this;
50083
50084			self._super(settings);
50085
50086			self.on('click mousedown', function(e) {
50087				e.preventDefault();
50088			});
50089
50090			self.on('click', function(e) {
50091				e.preventDefault();
50092
50093				if (!self.disabled()) {
50094					self.checked(!self.checked());
50095				}
50096			});
50097
50098			self.checked(self.settings.checked);
50099		},
50100
50101		/**
50102		 * Getter/setter function for the checked state.
50103		 *
50104		 * @method checked
50105		 * @param {Boolean} [state] State to be set.
50106		 * @return {Boolean|tinymce.ui.Checkbox} True/false or checkbox if it's a set operation.
50107		 */
50108		checked: function(state) {
50109			if (!arguments.length) {
50110				return this.state.get('checked');
50111			}
50112
50113			this.state.set('checked', state);
50114
50115			return this;
50116		},
50117
50118		/**
50119		 * Getter/setter function for the value state.
50120		 *
50121		 * @method value
50122		 * @param {Boolean} [state] State to be set.
50123		 * @return {Boolean|tinymce.ui.Checkbox} True/false or checkbox if it's a set operation.
50124		 */
50125		value: function(state) {
50126			if (!arguments.length) {
50127				return this.checked();
50128			}
50129
50130			return this.checked(state);
50131		},
50132
50133		/**
50134		 * Renders the control as a HTML string.
50135		 *
50136		 * @method renderHtml
50137		 * @return {String} HTML representing the control.
50138		 */
50139		renderHtml: function() {
50140			var self = this, id = self._id, prefix = self.classPrefix;
50141
50142			return (
50143				'<div id="' + id + '" class="' + self.classes + '" unselectable="on" aria-labelledby="' + id + '-al" tabindex="-1">' +
50144					'<i class="' + prefix + 'ico ' + prefix + 'i-checkbox"></i>' +
50145					'<span id="' + id + '-al" class="' + prefix + 'label">' + self.encode(self.state.get('text')) + '</span>' +
50146				'</div>'
50147			);
50148		},
50149
50150		bindStates: function() {
50151			var self = this;
50152
50153			function checked(state) {
50154				self.classes.toggle("checked", state);
50155				self.aria('checked', state);
50156			}
50157
50158			self.state.on('change:text', function(e) {
50159				self.getEl('al').firstChild.data = self.translate(e.value);
50160			});
50161
50162			self.state.on('change:checked change:value', function(e) {
50163				self.fire('change');
50164				checked(e.value);
50165			});
50166
50167			self.state.on('change:icon', function(e) {
50168				var icon = e.value, prefix = self.classPrefix;
50169
50170				if (typeof icon == 'undefined') {
50171					return self.settings.icon;
50172				}
50173
50174				self.settings.icon = icon;
50175				icon = icon ? prefix + 'ico ' + prefix + 'i-' + self.settings.icon : '';
50176
50177				var btnElm = self.getEl().firstChild, iconElm = btnElm.getElementsByTagName('i')[0];
50178
50179				if (icon) {
50180					if (!iconElm || iconElm != btnElm.firstChild) {
50181						iconElm = document.createElement('i');
50182						btnElm.insertBefore(iconElm, btnElm.firstChild);
50183					}
50184
50185					iconElm.className = icon;
50186				} else if (iconElm) {
50187					btnElm.removeChild(iconElm);
50188				}
50189			});
50190
50191			if (self.state.get('checked')) {
50192				checked(true);
50193			}
50194
50195			return self._super();
50196		}
50197	});
50198});
50199
50200// Included from: js/tinymce/classes/ui/ComboBox.js
50201
50202/**
50203 * ComboBox.js
50204 *
50205 * Released under LGPL License.
50206 * Copyright (c) 1999-2015 Ephox Corp. All rights reserved
50207 *
50208 * License: http://www.tinymce.com/license
50209 * Contributing: http://www.tinymce.com/contributing
50210 */
50211
50212/**
50213 * This class creates a combobox control. Select box that you select a value from or
50214 * type a value into.
50215 *
50216 * @-x-less ComboBox.less
50217 * @class tinymce.ui.ComboBox
50218 * @extends tinymce.ui.Widget
50219 */
50220define("tinymce/ui/ComboBox", [
50221	"tinymce/ui/Widget",
50222	"tinymce/ui/Factory",
50223	"tinymce/ui/DomUtils",
50224	"tinymce/dom/DomQuery"
50225], function(Widget, Factory, DomUtils, $) {
50226	"use strict";
50227
50228	return Widget.extend({
50229		/**
50230		 * Constructs a new control instance with the specified settings.
50231		 *
50232		 * @constructor
50233		 * @param {Object} settings Name/value object with settings.
50234		 * @setting {String} placeholder Placeholder text to display.
50235		 */
50236		init: function(settings) {
50237			var self = this;
50238
50239			self._super(settings);
50240			settings = self.settings;
50241
50242			self.classes.add('combobox');
50243			self.subinput = true;
50244			self.ariaTarget = 'inp'; // TODO: Figure out a better way
50245
50246			settings.menu = settings.menu || settings.values;
50247
50248			if (settings.menu) {
50249				settings.icon = 'caret';
50250			}
50251
50252			self.on('click', function(e) {
50253				var elm = e.target, root = self.getEl();
50254
50255				if (!$.contains(root, elm) && elm != root) {
50256					return;
50257				}
50258
50259				while (elm && elm != root) {
50260					if (elm.id && elm.id.indexOf('-open') != -1) {
50261						self.fire('action');
50262
50263						if (settings.menu) {
50264							self.showMenu();
50265
50266							if (e.aria) {
50267								self.menu.items()[0].focus();
50268							}
50269						}
50270					}
50271
50272					elm = elm.parentNode;
50273				}
50274			});
50275
50276			// TODO: Rework this
50277			self.on('keydown', function(e) {
50278				if (e.target.nodeName == "INPUT" && e.keyCode == 13) {
50279					self.parents().reverse().each(function(ctrl) {
50280						var stateValue = self.state.get('value'), inputValue = self.getEl('inp').value;
50281
50282						e.preventDefault();
50283
50284						self.state.set('value', inputValue);
50285
50286						if (stateValue != inputValue) {
50287							self.fire('change');
50288						}
50289
50290						if (ctrl.hasEventListeners('submit') && ctrl.toJSON) {
50291							ctrl.fire('submit', {data: ctrl.toJSON()});
50292							return false;
50293						}
50294					});
50295				}
50296			});
50297
50298			self.on('keyup', function(e) {
50299				if (e.target.nodeName == "INPUT") {
50300					self.state.set('value', e.target.value);
50301				}
50302			});
50303		},
50304
50305		showMenu: function() {
50306			var self = this, settings = self.settings, menu;
50307
50308			if (!self.menu) {
50309				menu = settings.menu || [];
50310
50311				// Is menu array then auto constuct menu control
50312				if (menu.length) {
50313					menu = {
50314						type: 'menu',
50315						items: menu
50316					};
50317				} else {
50318					menu.type = menu.type || 'menu';
50319				}
50320
50321				self.menu = Factory.create(menu).parent(self).renderTo(self.getContainerElm());
50322				self.fire('createmenu');
50323				self.menu.reflow();
50324				self.menu.on('cancel', function(e) {
50325					if (e.control === self.menu) {
50326						self.focus();
50327					}
50328				});
50329
50330				self.menu.on('show hide', function(e) {
50331					e.control.items().each(function(ctrl) {
50332						ctrl.active(ctrl.value() == self.value());
50333					});
50334				}).fire('show');
50335
50336				self.menu.on('select', function(e) {
50337					self.value(e.control.value());
50338				});
50339
50340				self.on('focusin', function(e) {
50341					if (e.target.tagName.toUpperCase() == 'INPUT') {
50342						self.menu.hide();
50343					}
50344				});
50345
50346				self.aria('expanded', true);
50347			}
50348
50349			self.menu.show();
50350			self.menu.layoutRect({w: self.layoutRect().w});
50351			self.menu.moveRel(self.getEl(), self.isRtl() ? ['br-tr', 'tr-br'] : ['bl-tl', 'tl-bl']);
50352		},
50353
50354		/**
50355		 * Focuses the input area of the control.
50356		 *
50357		 * @method focus
50358		 */
50359		focus: function() {
50360			this.getEl('inp').focus();
50361		},
50362
50363		/**
50364		 * Repaints the control after a layout operation.
50365		 *
50366		 * @method repaint
50367		 */
50368		repaint: function() {
50369			var self = this, elm = self.getEl(), openElm = self.getEl('open'), rect = self.layoutRect();
50370			var width, lineHeight;
50371
50372			if (openElm) {
50373				width = rect.w - DomUtils.getSize(openElm).width - 10;
50374			} else {
50375				width = rect.w - 10;
50376			}
50377
50378			// Detect old IE 7+8 add lineHeight to align caret vertically in the middle
50379			var doc = document;
50380			if (doc.all && (!doc.documentMode || doc.documentMode <= 8)) {
50381				lineHeight = (self.layoutRect().h - 2) + 'px';
50382			}
50383
50384			$(elm.firstChild).css({
50385				width: width,
50386				lineHeight: lineHeight
50387			});
50388
50389			self._super();
50390
50391			return self;
50392		},
50393
50394		/**
50395		 * Post render method. Called after the control has been rendered to the target.
50396		 *
50397		 * @method postRender
50398		 * @return {tinymce.ui.ComboBox} Current combobox instance.
50399		 */
50400		postRender: function() {
50401			var self = this;
50402
50403			$(this.getEl('inp')).on('change', function(e) {
50404				self.state.set('value', e.target.value);
50405				self.fire('change', e);
50406			});
50407
50408			return self._super();
50409		},
50410
50411		/**
50412		 * Renders the control as a HTML string.
50413		 *
50414		 * @method renderHtml
50415		 * @return {String} HTML representing the control.
50416		 */
50417		renderHtml: function() {
50418			var self = this, id = self._id, settings = self.settings, prefix = self.classPrefix;
50419			var value = self.state.get('value') || '';
50420			var icon, text, openBtnHtml = '', extraAttrs = '';
50421
50422			if ("spellcheck" in settings) {
50423				extraAttrs += ' spellcheck="' + settings.spellcheck + '"';
50424			}
50425
50426			if (settings.maxLength) {
50427				extraAttrs += ' maxlength="' + settings.maxLength + '"';
50428			}
50429
50430			if (settings.size) {
50431				extraAttrs += ' size="' + settings.size + '"';
50432			}
50433
50434			if (settings.subtype) {
50435				extraAttrs += ' type="' + settings.subtype + '"';
50436			}
50437
50438			if (self.disabled()) {
50439				extraAttrs += ' disabled="disabled"';
50440			}
50441
50442			icon = settings.icon;
50443			if (icon && icon != 'caret') {
50444				icon = prefix + 'ico ' + prefix + 'i-' + settings.icon;
50445			}
50446
50447			text = self.state.get('text');
50448
50449			if (icon || text) {
50450				openBtnHtml = (
50451					'<div id="' + id + '-open" class="' + prefix + 'btn ' + prefix + 'open" tabIndex="-1" role="button">' +
50452						'<button id="' + id + '-action" type="button" hidefocus="1" tabindex="-1">' +
50453							(icon != 'caret' ? '<i class="' + icon + '"></i>' : '<i class="' + prefix + 'caret"></i>') +
50454							(text ? (icon ? ' ' : '') + text : '') +
50455						'</button>' +
50456					'</div>'
50457				);
50458
50459				self.classes.add('has-open');
50460			}
50461
50462			return (
50463				'<div id="' + id + '" class="' + self.classes + '">' +
50464					'<input id="' + id + '-inp" class="' + prefix + 'textbox" value="' +
50465					self.encode(value, false) + '" hidefocus="1"' + extraAttrs + ' placeholder="' +
50466					self.encode(settings.placeholder) + '" />' +
50467					openBtnHtml +
50468				'</div>'
50469			);
50470		},
50471
50472		value: function(value) {
50473			if (arguments.length) {
50474				this.state.set('value', value);
50475				return this;
50476			}
50477
50478			// Make sure the real state is in sync
50479			if (this.state.get('rendered')) {
50480				this.state.set('value', this.getEl('inp').value);
50481			}
50482
50483			return this.state.get('value');
50484		},
50485
50486		bindStates: function() {
50487			var self = this;
50488
50489			self.state.on('change:value', function(e) {
50490				if (self.getEl('inp').value != e.value) {
50491					self.getEl('inp').value = e.value;
50492				}
50493			});
50494
50495			self.state.on('change:disabled', function(e) {
50496				self.getEl('inp').disabled = e.value;
50497			});
50498
50499			return self._super();
50500		},
50501
50502		remove: function() {
50503			$(this.getEl('inp')).off();
50504			this._super();
50505		}
50506	});
50507});
50508
50509// Included from: js/tinymce/classes/ui/ColorBox.js
50510
50511/**
50512 * ColorBox.js
50513 *
50514 * Released under LGPL License.
50515 * Copyright (c) 1999-2015 Ephox Corp. All rights reserved
50516 *
50517 * License: http://www.tinymce.com/license
50518 * Contributing: http://www.tinymce.com/contributing
50519 */
50520
50521/**
50522 * This widget lets you enter colors and browse for colors by pressing the color button. It also dis
50522plays
50523 * a preview of the current color.
50524 *
50525 * @-x-less ColorBox.less
50526 * @class tinymce.ui.ColorBox
50527 * @extends tinymce.ui.ComboBox
50528 */
50529define("tinymce/ui/ColorBox", [
50530	"tinymce/ui/ComboBox"
50531], function(ComboBox) {
50532	"use strict";
50533
50534	return ComboBox.extend({
50535		/**
50536		 * Constructs a new control instance with the specified settings.
50537		 *
50538		 * @constructor
50539		 * @param {Object} settings Name/value object with settings.
50540		 */
50541		init: function(settings) {
50542			var self = this;
50543
50544			settings.spellcheck = false;
50545
50546			if (settings.onaction) {
50547				settings.icon = 'none';
50548			}
50549
50550			self._super(settings);
50551
50552			self.classes.add('colorbox');
50553			self.on('change keyup postrender', function() {
50554				self.repaintColor(self.value());
50555			});
50556		},
50557
50558		repaintColor: function(value) {
50559			var elm = this.getEl().getElementsByTagName('i')[0];
50560
50561			if (elm) {
50562				try {
50563					elm.style.background = value;
50564				} catch (ex) {
50565					// Ignore
50566				}
50567			}
50568		},
50569
50570		bindStates: function() {
50571			var self = this;
50572
50573			self.state.on('change:value', function(e) {
50574				if (self._rendered) {
50575					self.repaintColor(e.value);
50576				}
50577			});
50578
50579			return self._super();
50580		}
50581	});
50582});
50583
50584// Included from: js/tinymce/classes/ui/PanelButton.js
50585
50586/**
50587 * PanelButton.js
50588 *
50589 * Released under LGPL License.
50590 * Copyright (c) 1999-2015 Ephox Corp. All rights reserved
50591 *
50592 * License: http://www.tinymce.com/license
50593 * Contributing: http://www.tinymce.com/contributing
50594 */
50595
50596/**
50597 * Creates a new panel button.
50598 *
50599 * @class tinymce.ui.PanelButton
50600 * @extends tinymce.ui.Button
50601 */
50602define("tinymce/ui/PanelButton", [
50603	"tinymce/ui/Button",
50604	"tinymce/ui/FloatPanel"
50605], function(Button, FloatPanel) {
50606	"use strict";
50607
50608	return Button.extend({
50609		/**
50610		 * Shows the panel for the button.
50611		 *
50612		 * @method showPanel
50613		 */
50614		showPanel: function() {
50615			var self = this, settings = self.settings;
50616
50617			self.active(true);
50618
50619			if (!self.panel) {
50620				var panelSettings = settings.panel;
50621
50622				// Wrap panel in grid layout if type if specified
50623				// This makes it possible to add forms or other containers directly in the panel option
50624				if (panelSettings.type) {
50625					panelSettings = {
50626						layout: 'grid',
50627						items: panelSettings
50628					};
50629				}
50630
50631				panelSettings.role = panelSettings.role || 'dialog';
50632				panelSettings.popover = true;
50633				panelSettings.autohide = true;
50634				panelSettings.ariaRoot = true;
50635
50636				self.panel = new FloatPanel(panelSettings).on('hide', function() {
50637					self.active(false);
50638				}).on('cancel', function(e) {
50639					e.stopPropagation();
50640					self.focus();
50641					self.hidePanel();
50642				}).parent(self).renderTo(self.getContainerElm());
50643
50644				self.panel.fire('show');
50645				self.panel.reflow();
50646			} else {
50647				self.panel.show();
50648			}
50649
50650			self.panel.moveRel(self.getEl(), settings.popoverAlign || (self.isRtl() ? ['bc-tr', 'bc-tc'] : ['bc-tl', 'bc-tc']));
50651		},
50652
50653		/**
50654		 * Hides the panel for the button.
50655		 *
50656		 * @method hidePanel
50657		 */
50658		hidePanel: function() {
50659			var self = this;
50660
50661			if (self.panel) {
50662				self.panel.hide();
50663			}
50664		},
50665
50666		/**
50667		 * Called after the control has been rendered.
50668		 *
50669		 * @method postRender
50670		 */
50671		postRender: function() {
50672			var self = this;
50673
50674			self.aria('haspopup', true);
50675
50676			self.on('click', function(e) {
50677				if (e.control === self) {
50678					if (self.panel && self.panel.visible()) {
50679						self.hidePanel();
50680					} else {
50681						self.showPanel();
50682						self.panel.focus(!!e.aria);
50683					}
50684				}
50685			});
50686
50687			return self._super();
50688		},
50689
50690		remove: function() {
50691			if (this.panel) {
50692				this.panel.remove();
50693				this.panel = null;
50694			}
50695
50696			return this._super();
50697		}
50698	});
50699});
50700
50701// Included from: js/tinymce/classes/ui/ColorButton.js
50702
50703/**
50704 * ColorButton.js
50705 *
50706 * Released under LGPL License.
50707 * Copyright (c) 1999-2015 Ephox Corp. All rights reserved
50708 *
50709 * License: http://www.tinymce.com/license
50710 * Contributing: http://www.tinymce.com/contributing
50711 */
50712
50713/**
50714 * This class creates a color button control. This is a split button in which the main
50715 * button has a visual representation of the currently selected color. When clicked
50716 * the caret button displays a color picker, allowing the user to select a new color.
50717 *
50718 * @-x-less ColorButton.less
50719 * @class tinymce.ui.ColorButton
50720 * @extends tinymce.ui.PanelButton
50721 */
50722define("tinymce/ui/ColorButton", [
50723	"tinymce/ui/PanelButton",
50724	"tinymce/dom/DOMUtils"
50725], function(PanelButton, DomUtils) {
50726	"use strict";
50727
50728	var DOM = DomUtils.DOM;
50729
50730	return PanelButton.extend({
50731		/**
50732		 * Constructs a new ColorButton instance with the specified settings.
50733		 *
50734		 * @constructor
50735		 * @param {Object} settings Name/value object with settings.
50736		 */
50737		init: function(settings) {
50738			this._super(settings);
50739			this.classes.add('colorbutton');
50740		},
50741
50742		/**
50743		 * Getter/setter for the current color.
50744		 *
50745		 * @method color
50746		 * @param {String} [color] Color to set.
50747		 * @return {String|tinymce.ui.ColorButton} Current color or current instance.
50748		 */
50749		color: function(color) {
50750			if (color) {
50751				this._color = color;
50752				this.getEl('preview').style.backgroundColor = color;
50753				return this;
50754			}
50755
50756			return this._color;
50757		},
50758
50759		/**
50760		 * Resets the current color.
50761		 *
50762		 * @method resetColor
50763		 * @return {tinymce.ui.ColorButton} Current instance.
50764		 */
50765		resetColor: function() {
50766			this._color = null;
50767			this.getEl('preview').style.backgroundColor = null;
50768			return this;
50769		},
50770
50771		/**
50772		 * Renders the control as a HTML string.
50773		 *
50774		 * @method renderHtml
50775		 * @return {String} HTML representing the control.
50776		 */
50777		renderHtml: function() {
50778			var self = this, id = self._id, prefix = self.classPrefix, text = self.state.get('text');
50779			var icon = self.settings.icon ? prefix + 'ico ' + prefix + 'i-' + self.settings.icon : '';
50780			var image = self.settings.image ? ' style="background-image: url(\'' + self.settings.image + '\')"' : '',
50781				textHtml = '';
50782
50783			if (text) {
50784				self.classes.add('btn-has-text');
50785				textHtml = '<span class="' + prefix + 'txt">' + self.encode(text) + '</span>';
50786			}
50787
50788			return (
50789				'<div id="' + id + '" class="' + self.classes + '" role="button" tabindex="-1" aria-haspopup="true">' +
50790					'<button role="presentation" hidefocus="1" type="button" tabindex="-1">' +
50791						(icon ? '<i class="' + icon + '"' + image + '></i>' : '') +
50792						'<span id="' + id + '-preview" class="' + prefix + 'preview"></span>' +
50793						textHtml +
50794					'</button>' +
50795					'<button type="button" class="' + prefix + 'open" hidefocus="1" tabindex="-1">' +
50796						' <i class="' + prefix + 'caret"></i>' +
50797					'</button>' +
50798				'</div>'
50799			);
50800		},
50801
50802		/**
50803		 * Called after the control has been rendered.
50804		 *
50805		 * @method postRender
50806		 */
50807		postRender: function() {
50808			var self = this, onClickHandler = self.settings.onclick;
50809
50810			self.on('click', function(e) {
50811				if (e.aria && e.aria.key == 'down') {
50812					return;
50813				}
50814
50815				if (e.control == self && !DOM.getParent(e.target, '.' + self.classPrefix + 'open')) {
50816					e.stopImmediatePropagation();
50817					onClickHandler.call(self, e);
50818				}
50819			});
50820
50821			delete self.settings.onclick;
50822
50823			return self._super();
50824		}
50825	});
50826});
50827
50828// Included from: js/tinymce/classes/util/Color.js
50829
50830/**
50831 * Color.js
50832 *
50833 * Released under LGPL License.
50834 * Copyright (c) 1999-2015 Ephox Corp. All rights reserved
50835 *
50836 * License: http://www.tinymce.com/license
50837 * Contributing: http://www.tinymce.com/contributing
50838 */
50839
50840/**
50841 * This class lets you parse/serialize colors and convert rgb/hsb.
50842 *
50843 * @class tinymce.util.Color
50844 * @example
50845 * var white = new tinymce.util.Color({r: 255, g: 255, b: 255});
50846 * var red = new tinymce.util.Color('#FF0000');
50847 *
50848 * console.log(white.toHex(), red.toHsv());
50849 */
50850define("tinymce/util/Color", [], function() {
50851	var min = Math.min, max = Math.max, round = Math.round;
50852
50853	/**
50854	 * Constructs a new color instance.
50855	 *
50856	 * @constructor
50857	 * @method Color
50858	 * @param {String} value Optional initial value to parse.
50859	 */
50860	function Color(value) {
50861		var self = this, r = 0, g = 0, b = 0;
50862
50863		function rgb2hsv(r, g, b) {
50864			var h, s, v, d, minRGB, maxRGB;
50865
50866			h = 0;
50867			s = 0;
50868			v = 0;
50869			r = r / 255;
50870			g = g / 255;
50871			b = b / 255;
50872
50873			minRGB = min(r, min(g, b));
50874			maxRGB = max(r, max(g, b));
50875
50876			if (minRGB == maxRGB) {
50877				v = minRGB;
50878
50879				return {
50880					h: 0,
50881					s: 0,
50882					v: v * 100
50883				};
50884			}
50885
50886			/*eslint no-nested-ternary:0 */
50887			d = (r == minRGB) ? g - b : ((b == minRGB) ? r - g : b - r);
50888			h = (r == minRGB) ? 3 : ((b == minRGB) ? 1 : 5);
50889			h = 60 * (h - d / (maxRGB - minRGB));
50890			s = (maxRGB - minRGB) / maxRGB;
50891			v = maxRGB;
50892
50893			return {
50894				h: round(h),
50895				s: round(s * 100),
50896				v: round(v * 100)
50897			};
50898		}
50899
50900		function hsvToRgb(hue, saturation, brightness) {
50901			var side, chroma, x, match;
50902
50903			hue = (parseInt(hue, 10) || 0) % 360;
50904			saturation = parseInt(saturation, 10) / 100;
50905			brightness = parseInt(brightness, 10) / 100;
50906			saturation = max(0, min(saturation, 1));
50907			brightness = max(0, min(brightness, 1));
50908
50909			if (saturation === 0) {
50910				r = g = b = round(255 * brightness);
50911				return;
50912			}
50913
50914			side = hue / 60;
50915			chroma = brightness * saturation;
50916			x = chroma * (1 - Math.abs(side % 2 - 1));
50917			match = brightness - chroma;
50918
50919			switch (Math.floor(side)) {
50920				case 0:
50921					r = chroma;
50922					g = x;
50923					b = 0;
50924					break;
50925
50926				case 1:
50927					r = x;
50928					g = chroma;
50929					b = 0;
50930					break;
50931
50932				case 2:
50933					r = 0;
50934					g = chroma;
50935					b = x;
50936					break;
50937
50938				case 3:
50939					r = 0;
50940					g = x;
50941					b = chroma;
50942					break;
50943
50944				case 4:
50945					r = x;
50946					g = 0;
50947					b = chroma;
50948					break;
50949
50950				case 5:
50951					r = chroma;
50952					g = 0;
50953					b = x;
50954					break;
50955
50956				default:
50957					r = g = b = 0;
50958			}
50959
50960			r = round(255 * (r + match));
50961			g = round(255 * (g + match));
50962			b = round(255 * (b + match));
50963		}
50964
50965		/**
50966		 * Returns the hex string of the current color. For example: #ff00ff
50967		 *
50968		 * @method toHex
50969		 * @return {String} Hex string of current color.
50970		 */
50971		function toHex() {
50972			function hex(val) {
50973				val = parseInt(val, 10).toString(16);
50974
50975				return val.length > 1 ? val : '0' + val;
50976			}
50977
50978			return '#' + hex(r) + hex(g) + hex(b);
50979		}
50980
50981		/**
50982		 * Returns the r, g, b values of the color. Each channel has a range from 0-255.
50983		 *
50984		 * @method toRgb
50985		 * @return {Object} Object with r, g, b fields.
50986		 */
50987		function toRgb() {
50988			return {
50989				r: r,
50990				g: g,
50991				b: b
50992			};
50993		}
50994
50995		/**
50996		 * Returns the h, s, v values of the color. Ranges: h=0-360, s=0-100, v=0-100.
50997		 *
50998		 * @method toHsv
50999		 * @return {Object} Object with h, s, v fields.
51000		 */
51001		function toHsv() {
51002			return rgb2hsv(r, g, b);
51003		}
51004
51005		/**
51006		 * Parses the specified value and populates the color instance.
51007		 *
51008		 * Supported format examples:
51009		 *  * rbg(255,0,0)
51010		 *  * #ff0000
51011		 *  * #fff
51012		 *  * {r: 255, g: 0, b: 0}
51013		 *  * {h: 360, s: 100, v: 100}
51014		 *
51015		 * @method parse
51016		 * @param {Object/String} value Color value to parse.
51017		 * @return {tinymce.util.Color} Current color instance.
51018		 */
51019		function parse(value) {
51020			var matches;
51021
51022			if (typeof value == 'object') {
51023				if ("r" in value) {
51024					r = value.r;
51025					g = value.g;
51026					b = value.b;
51027				} else if ("v" in value) {
51028					hsvToRgb(value.h, value.s, value.v);
51029				}
51030			} else {
51031				if ((matches = /rgb\s*\(\s*([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)[^\)]*\)/gi.exec(value))) {
51032					r = parseInt(matches[1], 10);
51033					g = parseInt(matches[2], 10);
51034					b = parseInt(matches[3], 10);
51035				} else if ((matches = /#([0-F]{2})([0-F]{2})([0-F]{2})/gi.exec(value))) {
51036					r = parseInt(matches[1], 16);
51037					g = parseInt(matches[2], 16);
51038					b = parseInt(matches[3], 16);
51039				} else if ((matches = /#([0-F])([0-F])([0-F])/gi.exec(value))) {
51040					r = parseInt(matches[1] + matches[1], 16);
51041					g = parseInt(matches[2] + matches[2], 16);
51042					b = parseInt(matches[3] + matches[3], 16);
51043				}
51044			}
51045
51046			r = r < 0 ? 0 : (r > 255 ? 255 : r);
51047			g = g < 0 ? 0 : (g > 255 ? 255 : g);
51048			b = b < 0 ? 0 : (b > 255 ? 255 : b);
51049
51050			return self;
51051		}
51052
51053		if (value) {
51054			parse(value);
51055		}
51056
51057		self.toRgb = toRgb;
51058		self.toHsv = toHsv;
51059		self.toHex = toHex;
51060		self.parse = parse;
51061	}
51062
51063	return Color;
51064});
51065
51066// Included from: js/tinymce/classes/ui/ColorPicker.js
51067
51068/**
51069 * ColorPicker.js
51070 *
51071 * Released under LGPL License.
51072 * Copyright (c) 1999-2015 Ephox Corp. All rights reserved
51073 *
51074 * License: http://www.tinymce.com/license
51075 * Contributing: http://www.tinymce.com/contributing
51076 */
51077
51078/**
51079 * Color picker widget lets you select colors.
51080 *
51081 * @-x-less ColorPicker.less
51082 * @class tinymce.ui.ColorPicker
51083 * @extends tinymce.ui.Widget
51084 */
51085define("tinymce/ui/ColorPicker", [
51086	"tinymce/ui/Widget",
51087	"tinymce/ui/DragHelper",
51088	"tinymce/ui/DomUtils",
51089	"tinymce/util/Color"
51090], function(Widget, DragHelper, DomUtils, Color) {
51091	"use strict";
51092
51093	return Widget.extend({
51094		Defaults: {
51095			classes: "widget colorpicker"
51096		},
51097
51098		/**
51099		 * Constructs a new colorpicker instance with the specified settings.
51100		 *
51101		 * @constructor
51102		 * @param {Object} settings Name/value object with settings.
51103		 * @setting {String} color Initial color value.
51104		 */
51105		init: function(settings) {
51106			this._super(settings);
51107		},
51108
51109		postRender: function() {
51110			var self = this, color = self.color(), hsv, hueRootElm, huePointElm, svRootElm, svPointElm;
51111
51112			hueRootElm = self.getEl('h');
51113			huePointElm = self.getEl('hp');
51114			svRootElm = self.getEl('sv');
51115			svPointElm = self.getEl('svp');
51116
51117			function getPos(elm, event) {
51118				var pos = DomUtils.getPos(elm), x, y;
51119
51120				x = event.pageX - pos.x;
51121				y = event.pageY - pos.y;
51122
51123				x = Math.max(0, Math.min(x / elm.clientWidth, 1));
51124				y = Math.max(0, Math.min(y / elm.clientHeight, 1));
51125
51126				return {
51127					x: x,
51128					y: y
51129				};
51130			}
51131
51132			function updateColor(hsv, hueUpdate) {
51133				var hue = (360 - hsv.h) / 360;
51134
51135				DomUtils.css(huePointElm, {
51136					top: (hue * 100) + '%'
51137				});
51138
51139				if (!hueUpdate) {
51140					DomUtils.css(svPointElm, {
51141						left: hsv.s + '%',
51142						top: (100 - hsv.v) + '%'
51143					});
51144				}
51145
51146				svRootElm.style.background = new Color({s: 100, v: 100, h: hsv.h}).toHex();
51147				self.color().parse({s: hsv.s, v: hsv.v, h: hsv.h});
51148			}
51149
51150			function updateSaturationAndValue(e) {
51151				var pos;
51152
51153				pos = getPos(svRootElm, e);
51154				hsv.s = pos.x * 100;
51155				hsv.v = (1 - pos.y) * 100;
51156
51157				updateColor(hsv);
51158				self.fire('change');
51159			}
51160
51161			function updateHue(e) {
51162				var pos;
51163
51164				pos = getPos(hueRootElm, e);
51165				hsv = color.toHsv();
51166				hsv.h = (1 - pos.y) * 360;
51167				updateColor(hsv, true);
51168				self.fire('change');
51169			}
51170
51171			self._repaint = function() {
51172				hsv = color.toHsv();
51173				updateColor(hsv);
51174			};
51175
51176			self._super();
51177
51178			self._svdraghelper = new DragHelper(self._id + '-sv', {
51179				start: updateSaturationAndValue,
51180				drag: updateSaturationAndValue
51181			});
51182
51183			self._hdraghelper = new DragHelper(self._id + '-h', {
51184				start: updateHue,
51185				drag: updateHue
51186			});
51187
51188			self._repaint();
51189		},
51190
51191		rgb: function() {
51192			return this.color().toRgb();
51193		},
51194
51195		value: function(value) {
51196			var self = this;
51197
51198			if (arguments.length) {
51199				self.color().parse(value);
51200
51201				if (self._rendered) {
51202					self._repaint();
51203				}
51204			} else {
51205				return self.color().toHex();
51206			}
51207		},
51208
51209		color: function() {
51210			if (!this._color) {
51211				this._color = new Color();
51212			}
51213
51214			return this._color;
51215		},
51216
51217		/**
51218		 * Renders the control as a HTML string.
51219		 *
51220		 * @method renderHtml
51221		 * @return {String} HTML representing the control.
51222		 */
51223		renderHtml: function() {
51224			var self = this, id = self._id, prefix = self.classPrefix, hueHtml;
51225			var stops = '#ff0000,#ff0080,#ff00ff,#8000ff,#0000ff,#0080ff,#00ffff,#00ff80,#00ff00,#80ff00,#ffff00,#ff8000,#ff0000';
51226
51227			function getOldIeFallbackHtml() {
51228				var i, l, html = '', gradientPrefix, stopsList;
51229
51230				gradientPrefix = 'filter:progid:DXImageTransform.Microsoft.gradient(GradientType=0,startColorstr=';
51231				stopsList = stops.split(',');
51232				for (i = 0, l = stopsList.length - 1; i < l; i++) {
51233					html += (
51234						'<div class="' + prefix + 'colorpicker-h-chunk" style="' +
51235							'height:' + (100 / l) + '%;' +
51236							gradientPrefix + stopsList[i] + ',endColorstr=' + stopsList[i + 1] + ');' +
51237							'-ms-' + gradientPrefix + stopsList[i] + ',endColorstr=' + stopsList[i + 1] + ')' +
51238						'"></div>'
51239					);
51240				}
51241
51242				return html;
51243			}
51244
51245			var gradientCssText = (
51246				'background: -ms-linear-gradient(top,' + stops + ');' +
51247				'background: linear-gradient(to bottom,' + stops + ');'
51248			);
51249
51250			hueHtml = (
51251				'<div id="' + id + '-h" class="' + prefix + 'colorpicker-h" style="' + gradientCssText + '">' +
51252					getOldIeFallbackHtml() +
51253					'<div id="' + id + '-hp" class="' + prefix + 'colorpicker-h-marker"></div>' +
51254				'</div>'
51255			);
51256
51257			return (
51258				'<div id="' + id + '" class="' + self.classes + '">' +
51259					'<div id="' + id + '-sv" class="' + prefix + 'colorpicker-sv">' +
51260						'<div class="' + prefix + 'colorpicker-overlay1">' +
51261							'<div class="' + prefix + 'colorpicker-overlay2">' +
51262								'<div id="' + id + '-svp" class="' + prefix + 'colorpicker-selector1">' +
51263									'<div class="' + prefix + 'colorpicker-selector2"></div>' +
51264								'</div>' +
51265							'</div>' +
51266						'</div>' +
51267					'</div>' +
51268					hueHtml +
51269				'</div>'
51270			);
51271		}
51272	});
51273});
51274
51275// Included from: js/tinymce/classes/ui/Path.js
51276
51277/**
51278 * Path.js
51279 *
51280 * Released under LGPL License.
51281 * Copyright (c) 1999-2015 Ephox Corp. All rights reserved
51282 *
51283 * License: http://www.tinymce.com/license
51284 * Contributing: http://www.tinymce.com/contributing
51285 */
51286
51287/**
51288 * Creates a new path control.
51289 *
51290 * @-x-less Path.less
51291 * @class tinymce.ui.Path
51292 * @extends tinymce.ui.Widget
51293 */
51294define("tinymce/ui/Path", [
51295	"tinymce/ui/Widget"
51296], function(Widget) {
51297	"use strict";
51298
51299	return Widget.extend({
51300		/**
51301		 * Constructs a instance with the specified settings.
51302		 *
51303		 * @constructor
51304		 * @param {Object} settings Name/value object with settings.
51305		 * @setting {String} delimiter Delimiter to display between row in path.
51306		 */
51307		init: function(settings) {
51308			var self = this;
51309
51310			if (!settings.delimiter) {
51311				settings.delimiter = '\u00BB';
51312			}
51313
51314			self._super(settings);
51315			self.classes.add('path');
51316			self.canFocus = true;
51317
51318			self.on('click', function(e) {
51319				var index, target = e.target;
51320
51321				if ((index = target.getAttribute('data-index'))) {
51322					self.fire('select', {value: self.row()[index], index: index});
51323				}
51324			});
51325
51326			self.row(self.settings.row);
51327		},
51328
51329		/**
51330		 * Focuses the current control.
51331		 *
51332		 * @method focus
51333		 * @return {tinymce.ui.Control} Current control instance.
51334		 */
51335		focus: function() {
51336			var self = this;
51337
51338			self.getEl().firstChild.focus();
51339
51340			return self;
51341		},
51342
51343		/**
51344		 * Sets/gets the data to be used for the path.
51345		 *
51346		 * @method row
51347		 * @param {Array} row Array with row name is rendered to path.
51348		 */
51349		row: function(row) {
51350			if (!arguments.length) {
51351				return this.state.get('row');
51352			}
51353
51354			this.state.set('row', row);
51355
51356			return this;
51357		},
51358
51359		/**
51360		 * Renders the control as a HTML string.
51361		 *
51362		 * @method renderHtml
51363		 * @return {String} HTML representing the control.
51364		 */
51365		renderHtml: function() {
51366			var self = this;
51367
51368			return (
51369				'<div id="' + self._id + '" class="' + self.classes + '">' +
51370					self._getDataPathHtml(self.state.get('row')) +
51371				'</div>'
51372			);
51373		},
51374
51375		bindStates: function() {
51376			var self = this;
51377
51378			self.state.on('change:row', function(e) {
51379				self.innerHtml(self._getDataPathHtml(e.value));
51380			});
51381
51382			return self._super();
51383		},
51384
51385		_getDataPathHtml: function(data) {
51386			var self = this, parts = data || [], i, l, html = '', prefix = self.classPrefix;
51387
51388			for (i = 0, l = parts.length; i < l; i++) {
51389				html += (
51390					(i > 0 ? '<div class="' + prefix + 'divider" aria-hidden="true"> ' + self.settings.delimiter + ' </div>' : '') +
51391					'<div role="button" class="' + prefix + 'path-item' + (i == l - 1 ? ' ' + prefix + 'last' : '') + '" data-index="' +
51392					i + '" tabindex="-1" id="' + self._id + '-' + i + '" aria-level="' + i + '">' + parts[i].name + '</div>'
51393				);
51394			}
51395
51396			if (!html) {
51397				html = '<div class="' + prefix + 'path-item">\u00a0</div>';
51398			}
51399
51400			return html;
51401		}
51402	});
51403});
51404
51405// Included from: js/tinymce/classes/ui/ElementPath.js
51406
51407/**
51408 * ElementPath.js
51409 *
51410 * Released under LGPL License.
51411 * Copyright (c) 1999-2015 Ephox Corp. All rights reserved
51412 *
51413 * License: http://www.tinymce.com/license
51414 * Contributing: http://www.tinymce.com/contributing
51415 */
51416
51417/**
51418 * This control creates an path for the current selections parent elements in TinyMCE.
51419 *
51420 * @class tinymce.ui.ElementPath
51421 * @extends tinymce.ui.Path
51422 */
51423define("tinymce/ui/ElementPath", [
51424	"tinymce/ui/Path",
51425	"tinymce/EditorManager"
51426], function(Path, EditorManager) {
51427	return Path.extend({
51428		/**
51429		 * Post render method. Called after the control has been rendered to the target.
51430		 *
51431		 * @method postRender
51432		 * @return {tinymce.ui.ElementPath} Current combobox instance.
51433		 */
51434		postRender: function() {
51435			var self = this, editor = EditorManager.activeEditor;
51436
51437			function isHidden(elm) {
51438				if (elm.nodeType === 1) {
51439					if (elm.nodeName == "BR" || !!elm.getAttribute('data-mce-bogus')) {
51440						return true;
51441					}
51442
51443					if (elm.getAttribute('data-mce-type') === 'bookmark') {
51444						return true;
51445					}
51446				}
51447
51448				return false;
51449			}
51450
51451			if (editor.settings.elementpath !== false) {
51452				self.on('select', function(e) {
51453					editor.focus();
51454					editor.selection.select(this.row()[e.index].element);
51455					editor.nodeChanged();
51456				});
51457
51458				editor.on('nodeChange', function(e) {
51459					var outParents = [], parents = e.parents, i = parents.length;
51460
51461					while (i--) {
51462						if (parents[i].nodeType == 1 && !isHidden(parents[i])) {
51463							var args = editor.fire('ResolveName', {
51464								name: parents[i].nodeName.toLowerCase(),
51465								target: parents[i]
51466							});
51467
51468							if (!args.isDefaultPrevented()) {
51469								outParents.push({name: args.name, element: parents[i]});
51470							}
51471
51472							if (args.isPropagationStopped()) {
51473								break;
51474							}
51475						}
51476					}
51477
51478					self.row(outParents);
51479				});
51480			}
51481
51482			return self._super();
51483		}
51484	});
51485});
51486
51487// Included from: js/tinymce/classes/ui/FormItem.js
51488
51489/**
51490 * FormItem.js
51491 *
51492 * Released under LGPL License.
51493 * Copyright (c) 1999-2015 Ephox Corp. All rights reserved
51494 *
51495 * License: http://www.tinymce.com/license
51496 * Contributing: http://www.tinymce.com/contributing
51497 */
51498
51499/**
51500 * This class is a container created by the form element with
51501 * a label and control item.
51502 *
51503 * @class tinymce.ui.FormItem
51504 * @extends tinymce.ui.Container
51505 * @setting {String} label Label to display for the form item.
51506 */
51507define("tinymce/ui/FormItem", [
51508	"tinymce/ui/Container"
51509], function(Container) {
51510	"use strict";
51511
51512	return Container.extend({
51513		Defaults: {
51514			layout: 'flex',
51515			align: 'center',
51516			defaults: {
51517				flex: 1
51518			}
51519		},
51520
51521		/**
51522		 * Renders the control as a HTML string.
51523		 *
51524		 * @method renderHtml
51525		 * @return {String} HTML representing the control.
51526		 */
51527		renderHtml: function() {
51528			var self = this, layout = self._layout, prefix = self.classPrefix;
51529
51530			self.classes.add('formitem');
51531			layout.preRender(self);
51532
51533			return (
51534				'<div id="' + self._id + '" class="' + self.classes + '" hidefocus="1" tabindex="-1">' +
51535					(self.settings.title ? ('<div id="' + self._id + '-title" class="' + prefix + 'title">' +
51536						self.settings.title + '</div>') : '') +
51537					'<div id="' + self._id + '-body" class="' + self.bodyClasses + '">' +
51538						(self.settings.html || '') + layout.renderHtml(self) +
51539					'</div>' +
51540				'</div>'
51541			);
51542		}
51543	});
51544});
51545
51546// Included from: js/tinymce/classes/ui/Form.js
51547
51548/**
51549 * Form.js
51550 *
51551 * Released under LGPL License.
51552 * Copyright (c) 1999-2015 Ephox Corp. All rights reserved
51553 *
51554 * License: http://www.tinymce.com/license
51555 * Contributing: http://www.tinymce.com/contributing
51556 */
51557
51558/**
51559 * This class creates a form container. A form container has the ability
51560 * to automatically wrap items in tinymce.ui.FormItem instances.
51561 *
51562 * Each FormItem instance is a container for the label and the item.
51563 *
51564 * @example
51565 * tinymce.ui.Factory.create({
51566 *     type: 'form',
51567 *     items: [
51568 *         {type: 'textbox', label: 'My text box'}
51569 *     ]
51570 * }).renderTo(document.body);
51571 *
51572 * @class tinymce.ui.Form
51573 * @extends tinymce.ui.Container
51574 */
51575define("tinymce/ui/Form", [
51576	"tinymce/ui/Container",
51577	"tinymce/ui/FormItem",
51578	"tinymce/util/Tools"
51579], function(Container, FormItem, Tools) {
51580	"use strict";
51581
51582	return Container.extend({
51583		Defaults: {
51584			containerCls: 'form',
51585			layout: 'flex',
51586			direction: 'column',
51587			align: 'stretch',
51588			flex: 1,
51589			padding: 20,
51590			labelGap: 30,
51591			spacing: 10,
51592			callbacks: {
51593				submit: function() {
51594					this.submit();
51595				}
51596			}
51597		},
51598
51599		/**
51600		 * This method gets invoked before the control is rendered.
51601		 *
51602		 * @method preRender
51603		 */
51604		preRender: function() {
51605			var self = this, items = self.items();
51606
51607			if (!self.settings.formItemDefaults) {
51608				self.settings.formItemDefaults = {
51609					layout: 'flex',
51610					autoResize: "overflow",
51611					defaults: {flex: 1}
51612				};
51613			}
51614
51615			// Wrap any labeled items in FormItems
51616			items.each(function(ctrl) {
51617				var formItem, label = ctrl.settings.label;
51618
51619				if (label) {
51620					formItem = new FormItem(Tools.extend({
51621						items: {
51622							type: 'label',
51623							id: ctrl._id + '-l',
51624							text: label,
51625							flex: 0,
51626							forId: ctrl._id,
51627							disabled: ctrl.disabled()
51628						}
51629					}, self.settings.formItemDefaults));
51630
51631					formItem.type = 'formitem';
51632					ctrl.aria('labelledby', ctrl._id + '-l');
51633
51634					if (typeof ctrl.settings.flex == "undefined") {
51635						ctrl.settings.flex = 1;
51636					}
51637
51638					self.replace(ctrl, formItem);
51639					formItem.add(ctrl);
51640				}
51641			});
51642		},
51643
51644		/**
51645		 * Fires a submit event with the serialized form.
51646		 *
51647		 * @method submit
51648		 * @return {Object} Event arguments object.
51649		 */
51650		submit: function() {
51651			return this.fire('submit', {data: this.toJSON()});
51652		},
51653
51654		/**
51655		 * Post render method. Called after the control has been rendered to the target.
51656		 *
51657		 * @method postRender
51658		 * @return {tinymce.ui.ComboBox} Current combobox instance.
51659		 */
51660		postRender: function() {
51661			var self = this;
51662
51663			self._super();
51664			self.fromJSON(self.settings.data);
51665		},
51666
51667		bindStates: function() {
51668			var self = this;
51669
51670			self._super();
51671
51672			function recalcLabels() {
51673				var maxLabelWidth = 0, labels = [], i, labelGap, items;
51674
51675				if (self.settings.labelGapCalc === false) {
51676					return;
51677				}
51678
51679				if (self.settings.labelGapCalc == "children") {
51680					items = self.find('formitem');
51681				} else {
51682					items = self.items();
51683				}
51684
51685				items.filter('formitem').each(function(item) {
51686					var labelCtrl = item.items()[0], labelWidth = labelCtrl.getEl().clientWidth;
51687
51688					maxLabelWidth = labelWidth > maxLabelWidth ? labelWidth : maxLabelWidth;
51689					labels.push(labelCtrl);
51690				});
51691
51692				labelGap = self.settings.labelGap || 0;
51693
51694				i = labels.length;
51695				while (i--) {
51696					labels[i].settings.minWidth = maxLabelWidth + labelGap;
51697				}
51698			}
51699
51700			self.on('show', recalcLabels);
51701			recalcLabels();
51702		}
51703	});
51704});
51705
51706// Included from: js/tinymce/classes/ui/FieldSet.js
51707
51708/**
51709 * FieldSet.js
51710 *
51711 * Released under LGPL License.
51712 * Copyright (c) 1999-2015 Ephox Corp. All rights reserved
51713 *
51714 * License: http://www.tinymce.com/license
51715 * Contributing: http://www.tinymce.com/contributing
51716 */
51717
51718/**
51719 * This class creates fieldset containers.
51720 *
51721 * @-x-less FieldSet.less
51722 * @class tinymce.ui.FieldSet
51723 * @extends tinymce.ui.Form
51724 */
51725define("tinymce/ui/FieldSet", [
51726	"tinymce/ui/Form"
51727], function(Form) {
51728	"use strict";
51729
51730	return Form.extend({
51731		Defaults: {
51732			containerCls: 'fieldset',
51733			layout: 'flex',
51734			direction: 'column',
51735			align: 'stretch',
51736			flex: 1,
51737			padding: "25 15 5 15",
51738			labelGap: 30,
51739			spacing: 10,
51740			border: 1
51741		},
51742
51743		/**
51744		 * Renders the control as a HTML string.
51745		 *
51746		 * @method renderHtml
51747		 * @return {String} HTML representing the control.
51748		 */
51749		renderHtml: function() {
51750			var self = this, layout = self._layout, prefix = self.classPrefix;
51751
51752			self.preRender();
51753			layout.preRender(self);
51754
51755			return (
51756				'<fieldset id="' + self._id + '" class="' + self.classes + '" hidefocus="1" tabindex="-1">' +
51757					(self.settings.title ? ('<legend id="' + self._id + '-title" class="' + prefix + 'fieldset-title">' +
51758						self.settings.title + '</legend>') : '') +
51759					'<div id="' + self._id + '-body" class="' + self.bodyClasses + '">' +
51760						(self.settings.html || '') + layout.renderHtml(self) +
51761					'</div>' +
51762				'</fieldset>'
51763			);
51764		}
51765	});
51766});
51767
51768// Included from: js/tinymce/classes/ui/FilePicker.js
51769
51770/**
51771 * FilePicker.js
51772 *
51773 * Released under LGPL License.
51774 * Copyright (c) 1999-2015 Ephox Corp. All rights reserved
51775 *
51776 * License: http://www.tinymce.com/license
51777 * Contributing: http://www.tinymce.com/contributing
51778 */
51779
51780/*global tinymce:true */
51781
51782/**
51783 * This class creates a file picker control.
51784 *
51785 * @class tinymce.ui.FilePicker
51786 * @extends tinymce.ui.ComboBox
51787 */
51788define("tinymce/ui/FilePicker", [
51789	"tinymce/ui/ComboBox",
51790	"tinymce/util/Tools"
51791], function(ComboBox, Tools) {
51792	"use strict";
51793
51794	return ComboBox.extend({
51795		/**
51796		 * Constructs a new control instance with the specified settings.
51797		 *
51798		 * @constructor
51799		 * @param {Object} settings Name/value object with settings.
51800		 */
51801		init: function(settings) {
51802			var self = this, editor = tinymce.activeEditor, editorSettings = editor.settings;
51803			var actionCallback, fileBrowserCallback, fileBrowserCallbackTypes;
51804
51805			settings.spellcheck = false;
51806
51807			fileBrowserCallbackTypes = editorSettings.file_picker_types || editorSettings.file_browser_callback_types;
51808			if (fileBrowserCallbackTypes) {
51809				fileBrowserCallbackTypes = Tools.makeMap(fileBrowserCallbackTypes, /[, ]/);
51810			}
51811
51812			if (!fileBrowserCallbackTypes || fileBrowserCallbackTypes[settings.filetype]) {
51813				fileBrowserCallback = editorSettings.file_picker_callback;
51814				if (fileBrowserCallback && (!fileBrowserCallbackTypes || fileBrowserCallbackTypes[settings.filetype])) {
51815					actionCallback = function() {
51816						var meta = self.fire('beforecall').meta;
51817
51818						meta = Tools.extend({filetype: settings.filetype}, meta);
51819
51820						// file_picker_callback(callback, currentValue, metaData)
51821						fileBrowserCallback.call(
51822							editor,
51823							function(value, meta) {
51824								self.value(value).fire('change', {meta: meta});
51825							},
51826							self.value(),
51827							meta
51828						);
51829					};
51830				} else {
51831					// Legacy callback: file_picker_callback(id, currentValue, filetype, window)
51832					fileBrowserCallback = editorSettings.file_browser_callback;
51833					if (fileBrowserCallback && (!fileBrowserCallbackTypes || fileBrowserCallbackTypes[settings.filetype])) {
51834						actionCallback = function() {
51835							fileBrowserCallback(
51836								self.getEl('inp').id,
51837								self.value(),
51838								settings.filetype,
51839								window
51840							);
51841						};
51842					}
51843				}
51844			}
51845
51846			if (actionCallback) {
51847				settings.icon = 'browse';
51848				settings.onaction = actionCallback;
51849			}
51850
51851			self._super(settings);
51852		}
51853	});
51854});
51855
51856// Included from: js/tinymce/classes/ui/FitLayout.js
51857
51858/**
51859 * FitLayout.js
51860 *
51861 * Released under LGPL License.
51862 * Copyright (c) 1999-2015 Ephox Corp. All rights reserved
51863 *
51864 * License: http://www.tinymce.com/license
51865 * Contributing: http://www.tinymce.com/contributing
51866 */
51867
51868/**
51869 * This layout manager will resize the control to be the size of it's parent container.
51870 * In other words width: 100% and height: 100%.
51871 *
51872 * @-x-less FitLayout.less
51873 * @class tinymce.ui.FitLayout
51874 * @extends tinymce.ui.AbsoluteLayout
51875 */
51876define("tinymce/ui/FitLayout", [
51877	"tinymce/ui/AbsoluteLayout"
51878], function(AbsoluteLayout) {
51879	"use strict";
51880
51881	return AbsoluteLayout.extend({
51882		/**
51883		 * Recalculates the positions of the controls in the specified container.
51884		 *
51885		 * @method recalc
51886		 * @param {tinymce.ui.Container} container Container instance to recalc.
51887		 */
51888		recalc: function(container) {
51889			var contLayoutRect = container.layoutRect(), paddingBox = container.paddingBox;
51890
51891			container.items().filter(':visible').each(function(ctrl) {
51892				ctrl.layoutRect({
51893					x: paddingBox.left,
51894					y: paddingBox.top,
51895					w: contLayoutRect.innerW - paddingBox.right - paddingBox.left,
51896					h: contLayoutRect.innerH - paddingBox.top - paddingBox.bottom
51897				});
51898
51899				if (ctrl.recalc) {
51900					ctrl.recalc();
51901				}
51902			});
51903		}
51904	});
51905});
51906
51907// Included from: js/tinymce/classes/ui/FlexLayout.js
51908
51909/**
51910 * FlexLayout.js
51911 *
51912 * Released under LGPL License.
51913 * Copyright (c) 1999-2015 Ephox Corp. All rights reserved
51914 *
51915 * License: http://www.tinymce.com/license
51916 * Contributing: http://www.tinymce.com/contributing
51917 */
51918
51919/**
51920 * This layout manager works similar to the CSS flex box.
51921 *
51922 * @setting {String} direction row|row-reverse|column|column-reverse
51923 * @setting {Number} flex A positive-number to flex by.
51924 * @setting {String} align start|end|center|stretch
51925 * @setting {String} pack start|end|justify
51926 *
51927 * @class tinymce.ui.FlexLayout
51928 * @extends tinymce.ui.AbsoluteLayout
51929 */
51930define("tinymce/ui/FlexLayout", [
51931	"tinymce/ui/AbsoluteLayout"
51932], function(AbsoluteLayout) {
51933	"use strict";
51934
51935	return AbsoluteLayout.extend({
51936		/**
51937		 * Recalculates the positions of the controls in the specified container.
51938		 *
51939		 * @method recalc
51940		 * @param {tinymce.ui.Container} container Container instance to recalc.
51941		 */
51942		recalc: function(container) {
51943			// A ton of variables, needs to be in the same scope for performance
51944			var i, l, items, contLayoutRect, contPaddingBox, contSettings, align, pack, spacing, totalFlex, availableSpace, direction;
51945			var ctrl, ctrlLayoutRect, ctrlSettings, flex, maxSizeItems = [], size, maxSize, ratio, rect, pos, maxAlignEndPos;
51946			var sizeName, minSizeName, posName, maxSizeName, beforeName, innerSizeName, deltaSizeName, contentSizeName;
51947			var alignAxisName, alignInnerSizeName, alignSizeName, alignMinSizeName, alignBeforeName, alignAfterName;
51948			var alignDeltaSizeName, alignContentSizeName;
51949			var max = Math.max, min = Math.min;
51950
51951			// Get container items, properties and settings
51952			items = container.items().filter(':visible');
51953			contLayoutRect = container.layoutRect();
51954			contPaddingBox = container.paddingBox;
51955			contSettings = container.settings;
51956			direction = container.isRtl() ? (contSettings.direction || 'row-reversed') : contSettings.direction;
51957			align = contSettings.align;
51958			pack = container.isRtl() ? (contSettings.pack || 'end') : contSettings.pack;
51959			spacing = contSettings.spacing || 0;
51960
51961			if (direction == "row-reversed" || direction == "column-reverse") {
51962				items = items.set(items.toArray().reverse());
51963				direction = direction.split('-')[0];
51964			}
51965
51966			// Setup axis variable name for row/column direction since the calculations is the same
51967			if (direction == "column") {
51968				posName = "y";
51969				sizeName = "h";
51970				minSizeName = "minH";
51971				maxSizeName = "maxH";
51972				innerSizeName = "innerH";
51973				beforeName = 'top';
51974				deltaSizeName = "deltaH";
51975				contentSizeName = "contentH";
51976
51977				alignBeforeName = "left";
51978				alignSizeName = "w";
51979				alignAxisName = "x";
51980				alignInnerSizeName = "innerW";
51981				alignMinSizeName = "minW";
51982				alignAfterName = "right";
51983				alignDeltaSizeName = "deltaW";
51984				alignContentSizeName = "contentW";
51985			} else {
51986				posName = "x";
51987				sizeName = "w";
51988				minSizeName = "minW";
51989				maxSizeName = "maxW";
51990				innerSizeName = "innerW";
51991				beforeName = 'left';
51992				deltaSizeName = "deltaW";
51993				contentSizeName = "contentW";
51994
51995				alignBeforeName = "top";
51996				alignSizeName = "h";
51997				alignAxisName = "y";
51998				alignInnerSizeName = "innerH";
51999				alignMinSizeName = "minH";
52000				alignAfterName = "bottom";
52001				alignDeltaSizeName = "deltaH";
52002				alignContentSizeName = "contentH";
52003			}
52004
52005			// Figure out total flex, availableSpace and collect any max size elements
52006			availableSpace = contLayoutRect[innerSizeName] - contPaddingBox[beforeName] - contPaddingBox[beforeName];
52007			maxAlignEndPos = totalFlex = 0;
52008			for (i = 0, l = items.length; i < l; i++) {
52009				ctrl = items[i];
52010				ctrlLayoutRect = ctrl.layoutRect();
52011				ctrlSettings = ctrl.settings;
52012				flex = ctrlSettings.flex;
52013				availableSpace -= (i < l - 1 ? spacing : 0);
52014
52015				if (flex > 0) {
52016					totalFlex += flex;
52017
52018					// Flexed item has a max size then we need to check if we will hit that size
52019					if (ctrlLayoutRect[maxSizeName]) {
52020						maxSizeItems.push(ctrl);
52021					}
52022
52023					ctrlLayoutRect.flex = flex;
52024				}
52025
52026				availableSpace -= ctrlLayoutRect[minSizeName];
52027
52028				// Calculate the align end position to be used to check for overflow/underflow
52029				size = contPaddingBox[alignBeforeName] + ctrlLayoutRect[alignMinSizeName] + contPaddingBox[alignAfterName];
52030				if (size > maxAlignEndPos) {
52031					maxAlignEndPos = size;
52032				}
52033			}
52034
52035			// Calculate minW/minH
52036			rect = {};
52037			if (availableSpace < 0) {
52038				rect[minSizeName] = contLayoutRect[minSizeName] - availableSpace + contLayoutRect[deltaSizeName];
52039			} else {
52040				rect[minSizeName] = contLayoutRect[innerSizeName] - availableSpace + contLayoutRect[deltaSizeName];
52041			}
52042
52043			rect[alignMinSizeName] = maxAlignEndPos + contLayoutRect[alignDeltaSizeName];
52044
52045			rect[contentSizeName] = contLayoutRect[innerSizeName] - availableSpace;
52046			rect[alignContentSizeName] = maxAlignEndPos;
52047			rect.minW = min(rect.minW, contLayoutRect.maxW);
52048			rect.minH = min(rect.minH, contLayoutRect.maxH);
52049			rect.minW = max(rect.minW, contLayoutRect.startMinWidth);
52050			rect.minH = max(rect.minH, contLayoutRect.startMinHeight);
52051
52052			// Resize container container if minSize was changed
52053			if (contLayoutRect.autoResize && (rect.minW != contLayoutRect.minW || rect.minH != contLayoutRect.minH)) {
52054				rect.w = rect.minW;
52055				rect.h = rect.minH;
52056
52057				container.layoutRect(rect);
52058				this.recalc(container);
52059
52060				// Forced recalc for example if items are hidden/shown
52061				if (container._lastRect === null) {
52062					var parentCtrl = container.parent();
52063					if (parentCtrl) {
52064						parentCtrl._lastRect = null;
52065						parentCtrl.recalc();
52066					}
52067				}
52068
52069				return;
52070			}
52071
52072			// Handle max size elements, check if they will become to wide with current options
52073			ratio = availableSpace / totalFlex;
52074			for (i = 0, l = maxSizeItems.length; i < l; i++) {
52075				ctrl = maxSizeItems[i];
52076				ctrlLayoutRect = ctrl.layoutRect();
52077				maxSize = ctrlLayoutRect[maxSizeName];
52078				size = ctrlLayoutRect[minSizeName] + ctrlLayoutRect.flex * ratio;
52079
52080				if (size > maxSize) {
52081					availableSpace -= (ctrlLayoutRect[maxSizeName] - ctrlLayoutRect[minSizeName]);
52082					totalFlex -= ctrlLayoutRect.flex;
52083					ctrlLayoutRect.flex = 0;
52084					ctrlLayoutRect.maxFlexSize = maxSize;
52085				} else {
52086					ctrlLayoutRect.maxFlexSize = 0;
52087				}
52088			}
52089
52090			// Setup new ratio, target layout rect, start position
52091			ratio = availableSpace / totalFlex;
52092			pos = contPaddingBox[beforeName];
52093			rect = {};
52094
52095			// Handle pack setting moves the start position to end, center
52096			if (totalFlex === 0) {
52097				if (pack == "end") {
52098					pos = availableSpace + contPaddingBox[beforeName];
52099				} else if (pack == "center") {
52100					pos = Math.round(
52101						(contLayoutRect[innerSizeName] / 2) - ((contLayoutRect[innerSizeName] - availableSpace) / 2)
52102					) + contPaddingBox[beforeName];
52103
52104					if (pos < 0) {
52105						pos = contPaddingBox[beforeName];
52106					}
52107				} else if (pack == "justify") {
52108					pos = contPaddingBox[beforeName];
52109					spacing = Math.floor(availableSpace / (items.length - 1));
52110				}
52111			}
52112
52113			// Default aligning (start) the other ones needs to be calculated while doing the layout
52114			rect[alignAxisName] = contPaddingBox[alignBeforeName];
52115
52116			// Start laying out controls
52117			for (i = 0, l = items.length; i < l; i++) {
52118				ctrl = items[i];
52119				ctrlLayoutRect = ctrl.layoutRect();
52120				size = ctrlLayoutRect.maxFlexSize || ctrlLayoutRect[minSizeName];
52121
52122				// Align the control on the other axis
52123				if (align === "center") {
52124					rect[alignAxisName] = Math.round((contLayoutRect[alignInnerSizeName] / 2) - (ctrlLayoutRect[alignSizeName] / 2));
52125				} else if (align === "stretch") {
52126					rect[alignSizeName] = max(
52127						ctrlLayoutRect[alignMinSizeName] || 0,
52128						contLayoutRect[alignInnerSizeName] - contPaddingBox[alignBeforeName] - contPaddingBox[alignAfterName]
52129					);
52130					rect[alignAxisName] = contPaddingBox[alignBeforeName];
52131				} else if (align === "end") {
52132					rect[alignAxisName] = contLayoutRect[alignInnerSizeName] - ctrlLayoutRect[alignSizeName] - contPaddingBox.top;
52133				}
52134
52135				// Calculate new size based on flex
52136				if (ctrlLayoutRect.flex > 0) {
52137					size += ctrlLayoutRect.flex * ratio;
52138				}
52139
52140				rect[sizeName] = size;
52141				rect[posName] = pos;
52142				ctrl.layoutRect(rect);
52143
52144				// Recalculate containers
52145				if (ctrl.recalc) {
52146					ctrl.recalc();
52147				}
52148
52149				// Move x/y position
52150				pos += size + spacing;
52151			}
52152		}
52153	});
52154});
52155
52156// Included from: js/tinymce/classes/ui/FlowLayout.js
52157
52158/**
52159 * FlowLayout.js
52160 *
52161 * Released under LGPL License.
52162 * Copyright (c) 1999-2015 Ephox Corp. All rights reserved
52163 *
52164 * License: http://www.tinymce.com/license
52165 * Contributing: http://www.tinymce.com/contributing
52166 */
52167
52168/**
52169 * This layout manager will place the controls by using the browsers native layout.
52170 *
52171 * @-x-less FlowLayout.less
52172 * @class tinymce.ui.FlowLayout
52173 * @extends tinymce.ui.Layout
52174 */
52175define("tinymce/ui/FlowLayout", [
52176	"tinymce/ui/Layout"
52177], function(Layout) {
52178	return Layout.extend({
52179		Defaults: {
52180			containerClass: 'flow-layout',
52181			controlClass: 'flow-layout-item',
52182			endClass: 'break'
52183		},
52184
52185		/**
52186		 * Recalculates the positions of the controls in the specified container.
52187		 *
52188		 * @method recalc
52189		 * @param {tinymce.ui.Container} container Container instance to recalc.
52190		 */
52191		recalc: function(container) {
52192			container.items().filter(':visible').each(function(ctrl) {
52193				if (ctrl.recalc) {
52194					ctrl.recalc();
52195				}
52196			});
52197		},
52198
52199		isNative: function() {
52200			return true;
52201		}
52202	});
52203});
52204
52205// Included from: js/tinymce/classes/ui/FormatControls.js
52206
52207/**
52208 * FormatControls.js
52209 *
52210 * Released under LGPL License.
52211 * Copyright (c) 1999-2015 Ephox Corp. All rights reserved
52212 *
52213 * License: http://www.tinymce.com/license
52214 * Contributing: http://www.tinymce.com/contributing
52215 */
52216
52217/**
52218 * Internal class containing all TinyMCE specific control types such as
52219 * format listboxes, fontlist boxes, toolbar buttons etc.
52220 *
52221 * @class tinymce.ui.FormatControls
52222 */
52223define("tinymce/ui/FormatControls", [
52224	"tinymce/ui/Control",
52225	"tinymce/ui/Widget",
52226	"tinymce/ui/FloatPanel",
52227	"tinymce/util/Tools",
52228	"tinymce/EditorManager",
52229	"tinymce/Env"
52230], function(Control, Widget, FloatPanel, Tools, EditorManager, Env) {
52231	var each = Tools.each;
52232
52233	EditorManager.on('AddEditor', function(e) {
52234		if (e.editor.rtl) {
52235			Control.rtl = true;
52236		}
52237
52238		registerControls(e.editor);
52239	});
52240
52241	Control.translate = function(text) {
52242		return EditorManager.translate(text);
52243	};
52244
52245	Widget.tooltips = !Env.iOS;
52246
52247	function registerControls(editor) {
52248		var formatMenu;
52249
52250		function createListBoxChangeHandler(items, formatName) {
52251			return function() {
52252				var self = this;
52253
52254				editor.on('nodeChange', function(e) {
52255					var formatter = editor.formatter;
52256					var value = null;
52257
52258					each(e.parents, function(node) {
52259						each(items, function(item) {
52260							if (formatName) {
52261								if (formatter.matchNode(node, formatName, {value: item.value})) {
52262									value = item.value;
52263								}
52264							} else {
52265								if (formatter.matchNode(node, item.value)) {
52266									value = item.value;
52267								}
52268							}
52269
52270							if (value) {
52271								return false;
52272							}
52273						});
52274
52275						if (value) {
52276							return false;
52277						}
52278					});
52279
52280					self.value(value);
52281				});
52282			};
52283		}
52284
52285		function createFormats(formats) {
52286			formats = formats.replace(/;$/, '').split(';');
52287
52288			var i = formats.length;
52289			while (i--) {
52290				formats[i] = formats[i].split('=');
52291			}
52292
52293			return formats;
52294		}
52295
52296		function createFormatMenu() {
52297			var count = 0, newFormats = [];
52298
52299			var defaultStyleFormats = [
52300				{title: 'Headings', items: [
52301					{title: 'Heading 1', format: 'h1'},
52302					{title: 'Heading 2', format: 'h2'},
52303					{title: 'Heading 3', format: 'h3'},
52304					{title: 'Heading 4', format: 'h4'},
52305					{title: 'Heading 5', format: 'h5'},
52306					{title: 'Heading 6', format: 'h6'}
52307				]},
52308
52309				{title: 'Inline', items: [
52310					{title: 'Bold', icon: 'bold', format: 'bold'},
52311					{title: 'Italic', icon: 'italic', format: 'italic'},
52312					{title: 'Underline', icon: 'underline', format: 'underline'},
52313					{title: 'Strikethrough', icon: 'strikethrough', format: 'strikethrough'},
52314					{title: 'Superscript', icon: 'superscript', format: 'superscript'},
52315					{title: 'Subscript', icon: 'subscript', format: 'subscript'},
52316					{title: 'Code', icon: 'code', format: 'code'}
52317				]},
52318
52319				{title: 'Blocks', items: [
52320					{title: 'Paragraph', format: 'p'},
52321					{title: 'Blockquote', format: 'blockquote'},
52322					{title: 'Div', format: 'div'},
52323					{title: 'Pre', format: 'pre'}
52324				]},
52325
52326				{title: 'Alignment', items: [
52327					{title: 'Left', icon: 'alignleft', format: 'alignleft'},
52328					{title: 'Center', icon: 'aligncenter', format: 'aligncenter'},
52329					{title: 'Right', icon: 'alignright', format: 'alignright'},
52330					{title: 'Justify', icon: 'alignjustify', format: 'alignjustify'}
52331				]}
52332			];
52333
52334			function createMenu(formats) {
52335				var menu = [];
52336
52337				if (!formats) {
52338					return;
52339				}
52340
52341				each(formats, function(format) {
52342					var menuItem = {
52343						text: format.title,
52344						icon: format.icon
52345					};
52346
52347					if (format.items) {
52348						menuItem.menu = createMenu(format.items);
52349					} else {
52350						var formatName = format.format || "custom" + count++;
52351
52352						if (!format.format) {
52353							format.name = formatName;
52354							newFormats.push(format);
52355						}
52356
52357						menuItem.format = formatName;
52358						menuItem.cmd = format.cmd;
52359					}
52360
52361					menu.push(menuItem);
52362				});
52363
52364				return menu;
52365			}
52366
52367			function createStylesMenu() {
52368				var menu;
52369
52370				if (editor.settings.style_formats_merge) {
52371					if (editor.settings.style_formats) {
52372						menu = createMenu(defaultStyleFormats.concat(editor.settings.style_formats));
52373					} else {
52374						menu = createMenu(defaultStyleFormats);
52375					}
52376				} else {
52377					menu = createMenu(editor.settings.style_formats || defaultStyleFormats);
52378				}
52379
52380				return menu;
52381			}
52382
52383			editor.on('init', function() {
52384				each(newFormats, function(format) {
52385					editor.formatter.register(format.name, format);
52386				});
52387			});
52388
52389			return {
52390				type: 'menu',
52391				items: createStylesMenu(),
52392				onPostRender: function(e) {
52393					editor.fire('renderFormatsMenu', {control: e.control});
52394				},
52395				itemDefaults: {
52396					preview: true,
52397
52398					textStyle: function() {
52399						if (this.settings.format) {
52400							return editor.formatter.getCssText(this.settings.format);
52401						}
52402					},
52403
52404					onPostRender: function() {
52405						var self = this;
52406
52407						self.parent().on('show', function() {
52408							var formatName, command;
52409
52410							formatName = self.settings.format;
52411							if (formatName) {
52412								self.disabled(!editor.formatter.canApply(formatName));
52413								self.active(editor.formatter.match(formatName));
52414							}
52415
52416							command = self.settings.cmd;
52417							if (command) {
52418								self.active(editor.queryCommandState(command));
52419							}
52420						});
52421					},
52422
52423					onclick: function() {
52424						if (this.settings.format) {
52425							toggleFormat(this.settings.format);
52426						}
52427
52428						if (this.settings.cmd) {
52429							editor.execCommand(this.settings.cmd);
52430						}
52431					}
52432				}
52433			};
52434		}
52435
52436		formatMenu = createFormatMenu();
52437
52438		function initOnPostRender(name) {
52439			return function() {
52440				var self = this;
52441
52442				// TODO: Fix this
52443				if (editor.formatter) {
52444					editor.formatter.formatChanged(name, function(state) {
52445						self.active(state);
52446					});
52447				} else {
52448					editor.on('init', function() {
52449						editor.formatter.formatChanged(name, function(state) {
52450							self.active(state);
52451						});
52452					});
52453				}
52454			};
52455		}
52456
52457		// Simple format controls <control/format>:<UI text>
52458		each({
52459			bold: 'Bold',
52460			italic: 'Italic',
52461			underline: 'Underline',
52462			strikethrough: 'Strikethrough',
52463			subscript: 'Subscript',
52464			superscript: 'Superscript'
52465		}, function(text, name) {
52466			editor.addButton(name, {
52467				tooltip: text,
52468				onPostRender: initOnPostRender(name),
52469				onclick: function() {
52470					toggleFormat(name);
52471				}
52472			});
52473		});
52474
52475		// Simple command controls <control>:[<UI text>,<Command>]
52476		each({
52477			outdent: ['Decrease indent', 'Outdent'],
52478			indent: ['Increase indent', 'Indent'],
52479			cut: ['Cut', 'Cut'],
52480			copy: ['Copy', 'Copy'],
52481			paste: ['Paste', 'Paste'],
52482			help: ['Help', 'mceHelp'],
52483			selectall: ['Select all', 'SelectAll'],
52484			removeformat: ['Clear formatting', 'RemoveFormat'],
52485			visualaid: ['Visual aids', 'mceToggleVisualAid'],
52486			newdocument: ['New document', 'mceNewDocument']
52487		}, function(item, name) {
52488			editor.addButton(name, {
52489				tooltip: item[0],
52490				cmd: item[1]
52491			});
52492		});
52493
52494		// Simple command controls with format state
52495		each({
52496			blockquote: ['Blockquote', 'mceBlockQuote'],
52497			numlist: ['Numbered list', 'InsertOrderedList'],
52498			bullist: ['Bullet list', 'InsertUnorderedList'],
52499			subscript: ['Subscript', 'Subscript'],
52500			superscript: ['Superscript', 'Superscript'],
52501			alignleft: ['Align left', 'JustifyLeft'],
52502			aligncenter: ['Align center', 'JustifyCenter'],
52503			alignright: ['Align right', 'JustifyRight'],
52504			alignjustify: ['Justify', 'JustifyFull'],
52505			alignnone: ['No alignment', 'JustifyNone']
52506		}, function(item, name) {
52507			editor.addButton(name, {
52508				tooltip: item[0],
52509				cmd: item[1],
52510				onPostRender: initOnPostRender(name)
52511			});
52512		});
52513
52514		function toggleUndoRedoState(type) {
52515			return function() {
52516				var self = this;
52517
52518				type = type == 'redo' ? 'hasRedo' : 'hasUndo';
52519
52520				function checkState() {
52521					return editor.undoManager ? editor.undoManager[type]() : false;
52522				}
52523
52524				self.disabled(!checkState());
52525				editor.on('Undo Redo AddUndo TypingUndo ClearUndos SwitchMode', function() {
52526					self.disabled(editor.readonly || !checkState());
52527				});
52528			};
52529		}
52530
52531		function toggleVisualAidState() {
52532			var self = this;
52533
52534			editor.on('VisualAid', function(e) {
52535				self.active(e.hasVisual);
52536			});
52537
52538			self.active(editor.hasVisual);
52539		}
52540
52541		editor.addButton('undo', {
52542			tooltip: 'Undo',
52543			onPostRender: toggleUndoRedoState('undo'),
52544			cmd: 'undo'
52545		});
52546
52547		editor.addButton('redo', {
52548			tooltip: 'Redo',
52549			onPostRender: toggleUndoRedoState('redo'),
52550			cmd: 'redo'
52551		});
52552
52553		editor.addMenuItem('newdocument', {
52554			text: 'New document',
52555			icon: 'newdocument',
52556			cmd: 'mceNewDocument'
52557		});
52558
52559		editor.addMenuItem('undo', {
52560			text: 'Undo',
52561			icon: 'undo',
52562			shortcut: 'Meta+Z',
52563			onPostRender: toggleUndoRedoState('undo'),
52564			cmd: 'undo'
52565		});
52566
52567		editor.addMenuItem('redo', {
52568			text: 'Redo',
52569			icon: 'redo',
52570			shortcut: 'Meta+Y',
52571			onPostRender: toggleUndoRedoState('redo'),
52572			cmd: 'redo'
52573		});
52574
52575		editor.addMenuItem('visualaid', {
52576			text: 'Visual aids',
52577			selectable: true,
52578			onPostRender: toggleVisualAidState,
52579			cmd: 'mceToggleVisualAid'
52580		});
52581
52582		editor.addButton('remove', {
52583			tooltip: 'Remove',
52584			icon: 'remove',
52585			cmd: 'Delete'
52586		});
52587
52588		each({
52589			cut: ['Cut', 'Cut', 'Meta+X'],
52590			copy: ['Copy', 'Copy', 'Meta+C'],
52591			paste: ['Paste', 'Paste', 'Meta+V'],
52592			selectall: ['Select all', 'SelectAll', 'Meta+A'],
52593			bold: ['Bold', 'Bold', 'Meta+B'],
52594			italic: ['Italic', 'Italic', 'Meta+I'],
52595			underline: ['Underline', 'Underline'],
52596			strikethrough: ['Strikethrough', 'Strikethrough'],
52597			subscript: ['Subscript', 'Subscript'],
52598			superscript: ['Superscript', 'Superscript'],
52599			removeformat: ['Clear formatting', 'RemoveFormat']
52600		}, function(item, name) {
52601			editor.addMenuItem(name, {
52602				text: item[0],
52603				icon: name,
52604				shortcut: item[2],
52605				cmd: item[1]
52606			});
52607		});
52608
52609		editor.on('mousedown', function() {
52610			FloatPanel.hideAll();
52611		});
52612
52613		function toggleFormat(fmt) {
52614			if (fmt.control) {
52615				fmt = fmt.control.value();
52616			}
52617
52618			if (fmt) {
52619				editor.execCommand('mceToggleFormat', false, fmt);
52620			}
52621		}
52622
52623		editor.addButton('styleselect', {
52624			type: 'menubutton',
52625			text: 'Formats',
52626			menu: formatMenu
52627		});
52628
52629		editor.addButton('formatselect', function() {
52630			var items = [], blocks = createFormats(editor.settings.block_formats ||
52631				'Paragraph=p;' +
52632				'Heading 1=h1;' +
52633				'Heading 2=h2;' +
52634				'Heading 3=h3;' +
52635				'Heading 4=h4;' +
52636				'Heading 5=h5;' +
52637				'Heading 6=h6;' +
52638				'Preformatted=pre'
52639			);
52640
52641			each(blocks, function(block) {
52642				items.push({
52643					text: block[0],
52644					value: block[1],
52645					textStyle: function() {
52646						return editor.formatter.getCssText(block[1]);
52647					}
52648				});
52649			});
52650
52651			return {
52652				type: 'listbox',
52653				text: blocks[0][0],
52654				values: items,
52655				fixedWidth: true,
52656				onselect: toggleFormat,
52657				onPostRender: createListBoxChangeHandler(items)
52658			};
52659		});
52660
52661		editor.addButton('fontselect', function() {
52662			var defaultFontsFormats =
52663				'Andale Mono=andale mono,monospace;' +
52664				'Arial=arial,helvetica,sans-serif;' +
52665				'Arial Black=arial black,sans-serif;' +
52666				'Book Antiqua=book antiqua,palatino,serif;' +
52667				'Comic Sans MS=comic sans ms,sans-serif;' +
52668				'Courier New=courier new,courier,monospace;' +
52669				'Georgia=georgia,palatino,serif;' +
52670				'Helvetica=helvetica,arial,sans-serif;' +
52671				'Impact=impact,sans-serif;' +
52672				'Symbol=symbol;' +
52673				'Tahoma=tahoma,arial,helvetica,sans-serif;' +
52674				'Terminal=terminal,monaco,monospace;' +
52675				'Times New Roman=times new roman,times,serif;' +
52676				'Trebuchet MS=trebuchet ms,geneva,sans-serif;' +
52677				'Verdana=verdana,geneva,sans-serif;' +
52678				'Webdings=webdings;' +
52679				'Wingdings=wingdings,zapf dingbats';
52680
52681			var items = [], fonts = createFormats(editor.settings.font_formats || defaultFontsFormats);
52682
52683			each(fonts, function(font) {
52684				items.push({
52685					text: {raw: font[0]},
52686					value: font[1],
52687					textStyle: font[1].indexOf('dings') == -1 ? 'font-family:' + font[1] : ''
52688				});
52689			});
52690
52691			return {
52692				type: 'listbox',
52693				text: 'Font Family',
52694				tooltip: 'Font Family',
52695				values: items,
52696				fixedWidth: true,
52697				onPostRender: createListBoxChangeHandler(items, 'fontname'),
52698				onselect: function(e) {
52699					if (e.control.settings.value) {
52700						editor.execCommand('FontName', false, e.control.settings.value);
52701					}
52702				}
52703			};
52704		});
52705
52706		editor.addButton('fontsizeselect', function() {
52707			var items = [], defaultFontsizeFormats = '8pt 10pt 12pt 14pt 18pt 24pt 36pt';
52708			var fontsize_formats = editor.settings.fontsize_formats || defaultFontsizeFormats;
52709
52710			each(fontsize_formats.split(' '), function(item) {
52711				var text = item, value = item;
52712				// Allow text=value font sizes.
52713				var values = item.split('=');
52714				if (values.length > 1) {
52715					text = values[0];
52716					value = values[1];
52717				}
52718				items.push({text: text, value: value});
52719			});
52720
52721			return {
52722				type: 'listbox',
52723				text: 'Font Sizes',
52724				tooltip: 'Font Sizes',
52725				values: items,
52726				fixedWidth: true,
52727				onPostRender: createListBoxChangeHandler(items, 'fontsize'),
52728				onclick: function(e) {
52729					if (e.control.settings.value) {
52730						editor.execCommand('FontSize', false, e.control.settings.value);
52731					}
52732				}
52733			};
52734		});
52735
52736		editor.addMenuItem('formats', {
52737			text: 'Formats',
52738			menu: formatMenu
52739		});
52740	}
52741});
52742
52743// Included from: js/tinymce/classes/ui/GridLayout.js
52744
52745/**
52746 * GridLayout.js
52747 *
52748 * Released under LGPL License.
52749 * Copyright (c) 1999-2015 Ephox Corp. All rights reserved
52750 *
52751 * License: http://www.tinymce.com/license
52752 * Contributing: http://www.tinymce.com/contributing
52753 */
52754
52755/**
52756 * This layout manager places controls in a grid.
52757 *
52758 * @setting {Number} spacing Spacing between controls.
52759 * @setting {Number} spacingH Horizontal spacing between controls.
52760 * @setting {Number} spacingV Vertical spacing between controls.
52761 * @setting {Number} columns Number of columns to use.
52762 * @setting {String/Array} alignH start|end|center|stretch or array of values for each column.
52763 * @setting {String/Array} alignV start|end|center|stretch or array of values for each column.
52764 * @setting {String} pack start|end
52765 *
52766 * @class tinymce.ui.GridLayout
52767 * @extends tinymce.ui.AbsoluteLayout
52768 */
52769define("tinymce/ui/GridLayout", [
52770	"tinymce/ui/AbsoluteLayout"
52771], function(AbsoluteLayout) {
52772	"use strict";
52773
52774	return AbsoluteLayout.extend({
52775		/**
52776		 * Recalculates the positions of the controls in the specified container.
52777		 *
52778		 * @method recalc
52779		 * @param {tinymce.ui.Container} container Container instance to recalc.
52780		 */
52781		recalc: function(container) {
52782			var settings, rows, cols, items, contLayoutRect, width, height, rect,
52783				ctrlLayoutRect, ctrl, x, y, posX, posY, ctrlSettings, contPaddingBox, align, spacingH, spacingV, alignH, alignV, maxX, maxY,
52784				colWidths = [], rowHeights = [], ctrlMinWidth, ctrlMinHeight, availableWidth, availableHeight, reverseRows, idx;
52785
52786			// Get layout settings
52787			settings = container.settings;
52788			items = container.items().filter(':visible');
52789			contLayoutRect = container.layoutRect();
52790			cols = settings.columns || Math.ceil(Math.sqrt(items.length));
52791			rows = Math.ceil(items.length / cols);
52792			spacingH = settings.spacingH || settings.spacing || 0;
52793			spacingV = settings.spacingV || settings.spacing || 0;
52794			alignH = settings.alignH || settings.align;
52795			alignV = settings.alignV || settings.align;
52796			contPaddingBox = container.paddingBox;
52797			reverseRows = 'reverseRows' in settings ? settings.reverseRows : container.isRtl();
52798
52799			if (alignH && typeof alignH == "string") {
52800				alignH = [alignH];
52801			}
52802
52803			if (alignV && typeof alignV == "string") {
52804				alignV = [alignV];
52805			}
52806
52807			// Zero padd columnWidths
52808			for (x = 0; x < cols; x++) {
52809				colWidths.push(0);
52810			}
52811
52812			// Zero padd rowHeights
52813			for (y = 0; y < rows; y++) {
52814				rowHeights.push(0);
52815			}
52816
52817			// Calculate columnWidths and rowHeights
52818			for (y = 0; y < rows; y++) {
52819				for (x = 0; x < cols; x++) {
52820					ctrl = items[y * cols + x];
52821
52822					// Out of bounds
52823					if (!ctrl) {
52824						break;
52825					}
52826
52827					ctrlLayoutRect = ctrl.layoutRect();
52828					ctrlMinWidth = ctrlLayoutRect.minW;
52829					ctrlMinHeight = ctrlLayoutRect.minH;
52830
52831					colWidths[x] = ctrlMinWidth > colWidths[x] ? ctrlMinWidth : colWidths[x];
52832					rowHeights[y] = ctrlMinHeight > rowHeights[y] ? ctrlMinHeight : rowHeights[y];
52833				}
52834			}
52835
52836			// Calculate maxX
52837			availableWidth = contLayoutRect.innerW - contPaddingBox.left - contPaddingBox.right;
52838			for (maxX = 0, x = 0; x < cols; x++) {
52839				maxX += colWidths[x] + (x > 0 ? spacingH : 0);
52840				availableWidth -= (x > 0 ? spacingH : 0) + colWidths[x];
52841			}
52842
52843			// Calculate maxY
52844			availableHeight = contLayoutRect.innerH - contPaddingBox.top - contPaddingBox.bottom;
52845			for (maxY = 0, y = 0; y < rows; y++) {
52846				maxY += rowHeights[y] + (y > 0 ? spacingV : 0);
52847				availableHeight -= (y > 0 ? spacingV : 0) + rowHeights[y];
52848			}
52849
52850			maxX += contPaddingBox.left + contPaddingBox.right;
52851			maxY += contPaddingBox.top + contPaddingBox.bottom;
52852
52853			// Calculate minW/minH
52854			rect = {};
52855			rect.minW = maxX + (contLayoutRect.w - contLayoutRect.innerW);
52856			rect.minH = maxY + (contLayoutRect.h - contLayoutRect.innerH);
52857
52858			rect.contentW = rect.minW - contLayoutRect.deltaW;
52859			rect.contentH = rect.minH - contLayoutRect.deltaH;
52860			rect.minW = Math.min(rect.minW, contLayoutRect.maxW);
52861			rect.minH = Math.min(rect.minH, contLayoutRect.maxH);
52862			rect.minW = Math.max(rect.minW, contLayoutRect.startMinWidth);
52863			rect.minH = Math.max(rect.minH, contLayoutRect.startMinHeight);
52864
52865			// Resize container container if minSize was changed
52866			if (contLayoutRect.autoResize && (rect.minW != contLayoutRect.minW || rect.minH != contLayoutRect.minH)) {
52867				rect.w = rect.minW;
52868				rect.h = rect.minH;
52869
52870				container.layoutRect(rect);
52871				this.recalc(container);
52872
52873				// Forced recalc for example if items are hidden/shown
52874				if (container._lastRect === null) {
52875					var parentCtrl = container.parent();
52876					if (parentCtrl) {
52877						parentCtrl._lastRect = null;
52878						parentCtrl.recalc();
52879					}
52880				}
52881
52882				return;
52883			}
52884
52885			// Update contentW/contentH so absEnd moves correctly
52886			if (contLayoutRect.autoResize) {
52887				rect = container.layoutRect(rect);
52888				rect.contentW = rect.minW - contLayoutRect.deltaW;
52889				rect.contentH = rect.minH - contLayoutRect.deltaH;
52890			}
52891
52892			var flexV;
52893
52894			if (settings.packV == 'start') {
52895				flexV = 0;
52896			} else {
52897				flexV = availableHeight > 0 ? Math.floor(availableHeight / rows) : 0;
52898			}
52899
52900			// Calculate totalFlex
52901			var totalFlex = 0;
52902			var flexWidths = settings.flexWidths;
52903			if (flexWidths) {
52904				for (x = 0; x < flexWidths.length; x++) {
52905					totalFlex += flexWidths[x];
52906				}
52907			} else {
52908				totalFlex = cols;
52909			}
52910
52911			// Calculate new column widths based on flex values
52912			var ratio = availableWidth / totalFlex;
52913			for (x = 0; x < cols; x++) {
52914				colWidths[x] += flexWidths ? flexWidths[x] * ratio : ratio;
52915			}
52916
52917			// Move/resize controls
52918			posY = contPaddingBox.top;
52919			for (y = 0; y < rows; y++) {
52920				posX = contPaddingBox.left;
52921				height = rowHeights[y] + flexV;
52922
52923				for (x = 0; x < cols; x++) {
52924					if (reverseRows) {
52925						idx = y * cols + cols - 1 - x;
52926					} else {
52927						idx = y * cols + x;
52928					}
52929
52930					ctrl = items[idx];
52931
52932					// No more controls to render then break
52933					if (!ctrl) {
52934						break;
52935					}
52936
52937					// Get control settings and calculate x, y
52938					ctrlSettings = ctrl.settings;
52939					ctrlLayoutRect = ctrl.layoutRect();
52940					width = Math.max(colWidths[x], ctrlLayoutRect.startMinWidth);
52941					ctrlLayoutRect.x = posX;
52942					ctrlLayoutRect.y = posY;
52943
52944					// Align control horizontal
52945					align = ctrlSettings.alignH || (alignH ? (alignH[x] || alignH[0]) : null);
52946					if (align == "center") {
52947						ctrlLayoutRect.x = posX + (width / 2) - (ctrlLayoutRect.w / 2);
52948					} else if (align == "right") {
52949						ctrlLayoutRect.x = posX + width - ctrlLayoutRect.w;
52950					} else if (align == "stretch") {
52951						ctrlLayoutRect.w = width;
52952					}
52953
52954					// Align control vertical
52955					align = ctrlSettings.alignV || (alignV ? (alignV[x] || alignV[0]) : null);
52956					if (align == "center") {
52957						ctrlLayoutRect.y = posY + (height / 2) - (ctrlLayoutRect.h / 2);
52958					} else if (align == "bottom") {
52959						ctrlLayoutRect.y = posY + height - ctrlLayoutRect.h;
52960					} else if (align == "stretch") {
52961						ctrlLayoutRect.h = height;
52962					}
52963
52964					ctrl.layoutRect(ctrlLayoutRect);
52965
52966					posX += width + spacingH;
52967
52968					if (ctrl.recalc) {
52969						ctrl.recalc();
52970					}
52971				}
52972
52973				posY += height + spacingV;
52974			}
52975		}
52976	});
52977});
52978
52979// Included from: js/tinymce/classes/ui/Iframe.js
52980
52981/**
52982 * Iframe.js
52983 *
52984 * Released under LGPL License.
52985 * Copyright (c) 1999-2015 Ephox Corp. All rights reserved
52986 *
52987 * License: http://www.tinymce.com/license
52988 * Contributing: http://www.tinymce.com/contributing
52989 */
52990
52991/*jshint scripturl:true */
52992
52993/**
52994 * This class creates an iframe.
52995 *
52996 * @setting {String} url Url to open in the iframe.
52997 *
52998 * @-x-less Iframe.less
52999 * @class tinymce.ui.Iframe
53000 * @extends tinymce.ui.Widget
53001 */
53002define("tinymce/ui/Iframe", [
53003	"tinymce/ui/Widget",
53004	"tinymce/util/Delay"
53005], function(Widget, Delay) {
53006	"use strict";
53007
53008	return Widget.extend({
53009		/**
53010		 * Renders the control as a HTML string.
53011		 *
53012		 * @method renderHtml
53013		 * @return {String} HTML representing the control.
53014		 */
53015		renderHtml: function() {
53016			var self = this;
53017
53018			self.classes.add('iframe');
53019			self.canFocus = false;
53020
53021			/*eslint no-script-url:0 */
53022			return (
53023				'<iframe id="' + self._id + '" class="' + self.classes + '" tabindex="-1" src="' +
53024				(self.settings.url || "javascript:''") + '" frameborder="0"></iframe>'
53025			);
53026		},
53027
53028		/**
53029		 * Setter for the iframe source.
53030		 *
53031		 * @method src
53032		 * @param {String} src Source URL for iframe.
53033		 */
53034		src: function(src) {
53035			this.getEl().src = src;
53036		},
53037
53038		/**
53039		 * Inner HTML for the iframe.
53040		 *
53041		 * @method html
53042		 * @param {String} html HTML string to set as HTML inside the iframe.
53043		 * @param {function} callback Optional callback to execute when the iframe body is filled with contents.
53044		 * @return {tinymce.ui.Iframe} Current iframe control.
53045		 */
53046		html: function(html, callback) {
53047			var self = this, body = this.getEl().contentWindow.document.body;
53048
53049			// Wait for iframe to initialize IE 10 takes time
53050			if (!body) {
53051				Delay.setTimeout(function() {
53052					self.html(html);
53053				});
53054			} else {
53055				body.innerHTML = html;
53056
53057				if (callback) {
53058					callback();
53059				}
53060			}
53061
53062			return this;
53063		}
53064	});
53065});
53066
53067// Included from: js/tinymce/classes/ui/Label.js
53068
53069/**
53070 * Label.js
53071 *
53072 * Released under LGPL License.
53073 * Copyright (c) 1999-2015 Ephox Corp. All rights reserved
53074 *
53075 * License: http://www.tinymce.com/license
53076 * Contributing: http://www.tinymce.com/contributing
53077 */
53078
53079/**
53080 * This class creates a label element. A label is a simple text control
53081 * that can be bound to other controls.
53082 *
53083 * @-x-less Label.less
53084 * @class tinymce.ui.Label
53085 * @extends tinymce.ui.Widget
53086 */
53087define("tinymce/ui/Label", [
53088	"tinymce/ui/Widget",
53089	"tinymce/ui/DomUtils"
53090], function(Widget, DomUtils) {
53091	"use strict";
53092
53093	return Widget.extend({
53094		/**
53095		 * Constructs a instance with the specified settings.
53096		 *
53097		 * @constructor
53098		 * @param {Object} settings Name/value object with settings.
53099		 * @setting {Boolean} multiline Multiline label.
53100		 */
53101		init: function(settings) {
53102			var self = this;
53103
53104			self._super(settings);
53105			self.classes.add('widget').add('label');
53106			self.canFocus = false;
53107
53108			if (settings.multiline) {
53109				self.classes.add('autoscroll');
53110			}
53111
53112			if (settings.strong) {
53113				self.classes.add('strong');
53114			}
53115		},
53116
53117		/**
53118		 * Initializes the current controls layout rect.
53119		 * This will be executed by the layout managers to determine the
53120		 * default minWidth/minHeight etc.
53121		 *
53122		 * @method initLayoutRect
53123		 * @return {Object} Layout rect instance.
53124		 */
53125		initLayoutRect: function() {
53126			var self = this, layoutRect = self._super();
53127
53128			if (self.settings.multiline) {
53129				var size = DomUtils.getSize(self.getEl());
53130
53131				// Check if the text fits within maxW if not then try word wrapping it
53132				if (size.width > layoutRect.maxW) {
53133					layoutRect.minW = layoutRect.maxW;
53134					self.classes.add('multiline');
53135				}
53136
53137				self.getEl().style.width = layoutRect.minW + 'px';
53138				layoutRect.startMinH = layoutRect.h = layoutRect.minH = Math.min(layoutRect.maxH, DomUtils.getSize(self.getEl()).height);
53139			}
53140
53141			return layoutRect;
53142		},
53143
53144		/**
53145		 * Repaints the control after a layout operation.
53146		 *
53147		 * @method repaint
53148		 */
53149		repaint: function() {
53150			var self = this;
53151
53152			if (!self.settings.multiline) {
53153				self.getEl().style.lineHeight = self.layoutRect().h + 'px';
53154			}
53155
53156			return self._super();
53157		},
53158
53159		/**
53160		 * Renders the control as a HTML string.
53161		 *
53162		 * @method renderHtml
53163		 * @return {String} HTML representing the control.
53164		 */
53165		renderHtml: function() {
53166			var self = this, forId = self.settings.forId;
53167
53168			return (
53169				'<label id="' + self._id + '" class="' + self.classes + '"' + (forId ? ' for="' + forId + '"' : '') + '>
53169' +
53170					self.encode(self.state.get('text')) +
53171				'</label>'
53172			);
53173		},
53174
53175		bindStates: function() {
53176			var self = this;
53177
53178			self.state.on('change:text', function(e) {
53179				self.innerHtml(self.encode(e.value));
53180			});
53181
53182			return self._super();
53183		}
53184	});
53185});
53186
53187// Included from: js/tinymce/classes/ui/Toolbar.js
53188
53189/**
53190 * Toolbar.js
53191 *
53192 * Released under LGPL License.
53193 * Copyright (c) 1999-2015 Ephox Corp. All rights reserved
53194 *
53195 * License: http://www.tinymce.com/license
53196 * Contributing: http://www.tinymce.com/contributing
53197 */
53198
53199/**
53200 * Creates a new toolbar.
53201 *
53202 * @class tinymce.ui.Toolbar
53203 * @extends tinymce.ui.Container
53204 */
53205define("tinymce/ui/Toolbar", [
53206	"tinymce/ui/Container"
53207], function(Container) {
53208	"use strict";
53209
53210	return Container.extend({
53211		Defaults: {
53212			role: 'toolbar',
53213			layout: 'flow'
53214		},
53215
53216		/**
53217		 * Constructs a instance with the specified settings.
53218		 *
53219		 * @constructor
53220		 * @param {Object} settings Name/value object with settings.
53221		 */
53222		init: function(settings) {
53223			var self = this;
53224
53225			self._super(settings);
53226			self.classes.add('toolbar');
53227		},
53228
53229		/**
53230		 * Called after the control has been rendered.
53231		 *
53232		 * @method postRender
53233		 */
53234		postRender: function() {
53235			var self = this;
53236
53237			self.items().each(function(ctrl) {
53238				ctrl.classes.add('toolbar-item');
53239			});
53240
53241			return self._super();
53242		}
53243	});
53244});
53245
53246// Included from: js/tinymce/classes/ui/MenuBar.js
53247
53248/**
53249 * MenuBar.js
53250 *
53251 * Released under LGPL License.
53252 * Copyright (c) 1999-2015 Ephox Corp. All rights reserved
53253 *
53254 * License: http://www.tinymce.com/license
53255 * Contributing: http://www.tinymce.com/contributing
53256 */
53257
53258/**
53259 * Creates a new menubar.
53260 *
53261 * @-x-less MenuBar.less
53262 * @class tinymce.ui.MenuBar
53263 * @extends tinymce.ui.Container
53264 */
53265define("tinymce/ui/MenuBar", [
53266	"tinymce/ui/Toolbar"
53267], function(Toolbar) {
53268	"use strict";
53269
53270	return Toolbar.extend({
53271		Defaults: {
53272			role: 'menubar',
53273			containerCls: 'menubar',
53274			ariaRoot: true,
53275			defaults: {
53276				type: 'menubutton'
53277			}
53278		}
53279	});
53280});
53281
53282// Included from: js/tinymce/classes/ui/MenuButton.js
53283
53284/**
53285 * MenuButton.js
53286 *
53287 * Released under LGPL License.
53288 * Copyright (c) 1999-2015 Ephox Corp. All rights reserved
53289 *
53290 * License: http://www.tinymce.com/license
53291 * Contributing: http://www.tinymce.com/contributing
53292 */
53293
53294/**
53295 * Creates a new menu button.
53296 *
53297 * @-x-less MenuButton.less
53298 * @class tinymce.ui.MenuButton
53299 * @extends tinymce.ui.Button
53300 */
53301define("tinymce/ui/MenuButton", [
53302	"tinymce/ui/Button",
53303	"tinymce/ui/Factory",
53304	"tinymce/ui/MenuBar"
53305], function(Button, Factory, MenuBar) {
53306	"use strict";
53307
53308	// TODO: Maybe add as some global function
53309	function isChildOf(node, parent) {
53310		while (node) {
53311			if (parent === node) {
53312				return true;
53313			}
53314
53315			node = node.parentNode;
53316		}
53317
53318		return false;
53319	}
53320
53321	var MenuButton = Button.extend({
53322		/**
53323		 * Constructs a instance with the specified settings.
53324		 *
53325		 * @constructor
53326		 * @param {Object} settings Name/value object with settings.
53327		 */
53328		init: function(settings) {
53329			var self = this;
53330
53331			self._renderOpen = true;
53332
53333			self._super(settings);
53334			settings = self.settings;
53335
53336			self.classes.add('menubtn');
53337
53338			if (settings.fixedWidth) {
53339				self.classes.add('fixed-width');
53340			}
53341
53342			self.aria('haspopup', true);
53343
53344			self.state.set('menu', settings.menu || self.render());
53345		},
53346
53347		/**
53348		 * Shows the menu for the button.
53349		 *
53350		 * @method showMenu
53351		 */
53352		showMenu: function() {
53353			var self = this, menu;
53354
53355			if (self.menu && self.menu.visible()) {
53356				return self.hideMenu();
53357			}
53358
53359			if (!self.menu) {
53360				menu = self.state.get('menu') || [];
53361
53362				// Is menu array then auto constuct menu control
53363				if (menu.length) {
53364					menu = {
53365						type: 'menu',
53366						items: menu
53367					};
53368				} else {
53369					menu.type = menu.type || 'menu';
53370				}
53371
53372				if (!menu.renderTo) {
53373					self.menu = Factory.create(menu).parent(self).renderTo();
53374				} else {
53375					self.menu = menu.parent(self).show().renderTo();
53376				}
53377
53378				self.fire('createmenu');
53379				self.menu.reflow();
53380				self.menu.on('cancel', function(e) {
53381					if (e.control.parent() === self.menu) {
53382						e.stopPropagation();
53383						self.focus();
53384						self.hideMenu();
53385					}
53386				});
53387
53388				// Move focus to button when a menu item is selected/clicked
53389				self.menu.on('select', function() {
53390					self.focus();
53391				});
53392
53393				self.menu.on('show hide', function(e) {
53394					if (e.control == self.menu) {
53395						self.activeMenu(e.type == 'show');
53396					}
53397
53398					self.aria('expanded', e.type == 'show');
53399				}).fire('show');
53400			}
53401
53402			self.menu.show();
53403			self.menu.layoutRect({w: self.layoutRect().w});
53404			self.menu.moveRel(self.getEl(), self.isRtl() ? ['br-tr', 'tr-br'] : ['bl-tl', 'tl-bl']);
53405		},
53406
53407		/**
53408		 * Hides the menu for the button.
53409		 *
53410		 * @method hideMenu
53411		 */
53412		hideMenu: function() {
53413			var self = this;
53414
53415			if (self.menu) {
53416				self.menu.items().each(function(item) {
53417					if (item.hideMenu) {
53418						item.hideMenu();
53419					}
53420				});
53421
53422				self.menu.hide();
53423			}
53424		},
53425
53426		/**
53427		 * Sets the active menu state.
53428		 *
53429		 * @private
53430		 */
53431		activeMenu: function(state) {
53432			this.classes.toggle('active', state);
53433		},
53434
53435		/**
53436		 * Renders the control as a HTML string.
53437		 *
53438		 * @method renderHtml
53439		 * @return {String} HTML representing the control.
53440		 */
53441		renderHtml: function() {
53442			var self = this, id = self._id, prefix = self.classPrefix;
53443			var icon = self.settings.icon, image, text = self.state.get('text'),
53444				textHtml = '';
53445
53446			image = self.settings.image;
53447			if (image) {
53448				icon = 'none';
53449
53450				// Support for [high dpi, low dpi] image sources
53451				if (typeof image != "string") {
53452					image = window.getSelection ? image[0] : image[1];
53453				}
53454
53455				image = ' style="background-image: url(\'' + image + '\')"';
53456			} else {
53457				image = '';
53458			}
53459
53460			if (text) {
53461				self.classes.add('btn-has-text');
53462				textHtml = '<span class="' + prefix + 'txt">' + self.encode(text) + '</span>';
53463			}
53464
53465			icon = self.settings.icon ? prefix + 'ico ' + prefix + 'i-' + icon : '';
53466
53467			self.aria('role', self.parent() instanceof MenuBar ? 'menuitem' : 'button');
53468
53469			return (
53470				'<div id="' + id + '" class="' + self.classes + '" tabindex="-1" aria-labelledby="' + id + '">' +
53471					'<button id="' + id + '-open" role="presentation" type="button" tabindex="-1">' +
53472						(icon ? '<i class="' + icon + '"' + image + '></i>' : '') +
53473						textHtml +
53474						' <i class="' + prefix + 'caret"></i>' +
53475					'</button>' +
53476				'</div>'
53477			);
53478		},
53479
53480		/**
53481		 * Gets invoked after the control has been rendered.
53482		 *
53483		 * @method postRender
53484		 */
53485		postRender: function() {
53486			var self = this;
53487
53488			self.on('click', function(e) {
53489				if (e.control === self && isChildOf(e.target, self.getEl())) {
53490					self.showMenu();
53491
53492					if (e.aria) {
53493						self.menu.items()[0].focus();
53494					}
53495				}
53496			});
53497
53498			self.on('mouseenter', function(e) {
53499				var overCtrl = e.control, parent = self.parent(), hasVisibleSiblingMenu;
53500
53501				if (overCtrl && parent && overCtrl instanceof MenuButton && overCtrl.parent() == parent) {
53502					parent.items().filter('MenuButton').each(function(ctrl) {
53503						if (ctrl.hideMenu && ctrl != overCtrl) {
53504							if (ctrl.menu && ctrl.menu.visible()) {
53505								hasVisibleSiblingMenu = true;
53506							}
53507
53508							ctrl.hideMenu();
53509						}
53510					});
53511
53512					if (hasVisibleSiblingMenu) {
53513						overCtrl.focus(); // Fix for: #5887
53514						overCtrl.showMenu();
53515					}
53516				}
53517			});
53518
53519			return self._super();
53520		},
53521
53522		bindStates: function() {
53523			var self = this;
53524
53525			self.state.on('change:menu', function() {
53526				if (self.menu) {
53527					self.menu.remove();
53528				}
53529
53530				self.menu = null;
53531			});
53532
53533			return self._super();
53534		},
53535
53536		/**
53537		 * Removes the control and it's menus.
53538		 *
53539		 * @method remove
53540		 */
53541		remove: function() {
53542			this._super();
53543
53544			if (this.menu) {
53545				this.menu.remove();
53546			}
53547		}
53548	});
53549
53550	return MenuButton;
53551});
53552
53553// Included from: js/tinymce/classes/ui/MenuItem.js
53554
53555/**
53556 * MenuItem.js
53557 *
53558 * Released under LGPL License.
53559 * Copyright (c) 1999-2015 Ephox Corp. All rights reserved
53560 *
53561 * License: http://www.tinymce.com/license
53562 * Contributing: http://www.tinymce.com/contributing
53563 */
53564
53565/**
53566 * Creates a new menu item.
53567 *
53568 * @-x-less MenuItem.less
53569 * @class tinymce.ui.MenuItem
53570 * @extends tinymce.ui.Control
53571 */
53572define("tinymce/ui/MenuItem", [
53573	"tinymce/ui/Widget",
53574	"tinymce/ui/Factory",
53575	"tinymce/Env"
53576], function(Widget, Factory, Env) {
53577	"use strict";
53578
53579	return Widget.extend({
53580		Defaults: {
53581			border: 0,
53582			role: 'menuitem'
53583		},
53584
53585		/**
53586		 * Constructs a instance with the specified settings.
53587		 *
53588		 * @constructor
53589		 * @param {Object} settings Name/value object with settings.
53590		 * @setting {Boolean} selectable Selectable menu.
53591		 * @setting {Array} menu Submenu array with items.
53592		 * @setting {String} shortcut Shortcut to display for menu item. Example: Ctrl+X
53593		 */
53594		init: function(settings) {
53595			var self = this, text;
53596
53597			self._super(settings);
53598
53599			settings = self.settings;
53600
53601			self.classes.add('menu-item');
53602
53603			if (settings.menu) {
53604				self.classes.add('menu-item-expand');
53605			}
53606
53607			if (settings.preview) {
53608				self.classes.add('menu-item-preview');
53609			}
53610
53611			text = self.state.get('text');
53612			if (text === '-' || text === '|') {
53613				self.classes.add('menu-item-sep');
53614				self.aria('role', 'separator');
53615				self.state.set('text', '-');
53616			}
53617
53618			if (settings.selectable) {
53619				self.aria('role', 'menuitemcheckbox');
53620				self.classes.add('menu-item-checkbox');
53621				settings.icon = 'selected';
53622			}
53623
53624			if (!settings.preview && !settings.selectable) {
53625				self.classes.add('menu-item-normal');
53626			}
53627
53628			self.on('mousedown', function(e) {
53629				e.preventDefault();
53630			});
53631
53632			if (settings.menu && !settings.ariaHideMenu) {
53633				self.aria('haspopup', true);
53634			}
53635		},
53636
53637		/**
53638		 * Returns true/false if the menuitem has sub menu.
53639		 *
53640		 * @method hasMenus
53641		 * @return {Boolean} True/false state if it has submenu.
53642		 */
53643		hasMenus: function() {
53644			return !!this.settings.menu;
53645		},
53646
53647		/**
53648		 * Shows the menu for the menu item.
53649		 *
53650		 * @method showMenu
53651		 */
53652		showMenu: function() {
53653			var self = this, settings = self.settings, menu, parent = self.parent();
53654
53655			parent.items().each(function(ctrl) {
53656				if (ctrl !== self) {
53657					ctrl.hideMenu();
53658				}
53659			});
53660
53661			if (settings.menu) {
53662				menu = self.menu;
53663
53664				if (!menu) {
53665					menu = settings.menu;
53666
53667					// Is menu array then auto constuct menu control
53668					if (menu.length) {
53669						menu = {
53670							type: 'menu',
53671							items: menu
53672						};
53673					} else {
53674						menu.type = menu.type || 'menu';
53675					}
53676
53677					if (parent.settings.itemDefaults) {
53678						menu.itemDefaults = parent.settings.itemDefaults;
53679					}
53680
53681					menu = self.menu = Factory.create(menu).parent(self).renderTo();
53682					menu.reflow();
53683					menu.on('cancel', function(e) {
53684						e.stopPropagation();
53685						self.focus();
53686						menu.hide();
53687					});
53688					menu.on('show hide', function(e) {
53689						e.control.items().each(function(ctrl) {
53690							ctrl.active(ctrl.settings.selected);
53691						});
53692					}).fire('show');
53693
53694					menu.on('hide', function(e) {
53695						if (e.control === menu) {
53696							self.classes.remove('selected');
53697						}
53698					});
53699
53700					menu.submenu = true;
53701				} else {
53702					menu.show();
53703				}
53704
53705				menu._parentMenu = parent;
53706
53707				menu.classes.add('menu-sub');
53708
53709				var rel = menu.testMoveRel(
53710					self.getEl(),
53711					self.isRtl() ? ['tl-tr', 'bl-br', 'tr-tl', 'br-bl'] : ['tr-tl', 'br-bl', 'tl-tr', 'bl-br']
53712				);
53713
53714				menu.moveRel(self.getEl(), rel);
53715				menu.rel = rel;
53716
53717				rel = 'menu-sub-' + rel;
53718				menu.classes.remove(menu._lastRel).add(rel);
53719				menu._lastRel = rel;
53720
53721				self.classes.add('selected');
53722				self.aria('expanded', true);
53723			}
53724		},
53725
53726		/**
53727		 * Hides the menu for the menu item.
53728		 *
53729		 * @method hideMenu
53730		 */
53731		hideMenu: function() {
53732			var self = this;
53733
53734			if (self.menu) {
53735				self.menu.items().each(function(item) {
53736					if (item.hideMenu) {
53737						item.hideMenu();
53738					}
53739				});
53740
53741				self.menu.hide();
53742				self.aria('expanded', false);
53743			}
53744
53745			return self;
53746		},
53747
53748		/**
53749		 * Renders the control as a HTML string.
53750		 *
53751		 * @method renderHtml
53752		 * @return {String} HTML representing the control.
53753		 */
53754		renderHtml: function() {
53755			var self = this, id = self._id, settings = self.settings, prefix = self.classPrefix, text = self.encode(self.state.get('text'));
53756			var icon = self.settings.icon, image = '', shortcut = settings.shortcut;
53757
53758			// Converts shortcut format to Mac/PC variants
53759			function convertShortcut(shortcut) {
53760				var i, value, replace = {};
53761
53762				if (Env.mac) {
53763					replace = {
53764						alt: '&#x2325;',
53765						ctrl: '&#x2318;',
53766						shift: '&#x21E7;',
53767						meta: '&#x2318;'
53768					};
53769				} else {
53770					replace = {
53771						meta: 'Ctrl'
53772					};
53773				}
53774
53775				shortcut = shortcut.split('+');
53776
53777				for (i = 0; i < shortcut.length; i++) {
53778					value = replace[shortcut[i].toLowerCase()];
53779
53780					if (value) {
53781						shortcut[i] = value;
53782					}
53783				}
53784
53785				return shortcut.join('+');
53786			}
53787
53788			if (icon) {
53789				self.parent().classes.add('menu-has-icons');
53790			}
53791
53792			if (settings.image) {
53793				image = ' style="background-image: url(\'' + settings.image + '\')"';
53794			}
53795
53796			if (shortcut) {
53797				shortcut = convertShortcut(shortcut);
53798			}
53799
53800			icon = prefix + 'ico ' + prefix + 'i-' + (self.settings.icon || 'none');
53801
53802			return (
53803				'<div id="' + id + '" class="' + self.classes + '" tabindex="-1">' +
53804					(text !== '-' ? '<i class="' + icon + '"' + image + '></i>\u00a0' : '') +
53805					(text !== '-' ? '<span id="' + id + '-text" class="' + prefix + 'text">' + text + '</span>' : '') +
53806					(shortcut ? '<div id="' + id + '-shortcut" class="' + prefix + 'menu-shortcut">' + shortcut + '</div>' : '') +
53807					(settings.menu ? '<div class="' + prefix + 'caret"></div>' : '') +
53808				'</div>'
53809			);
53810		},
53811
53812		/**
53813		 * Gets invoked after the control has been rendered.
53814		 *
53815		 * @method postRender
53816		 */
53817		postRender: function() {
53818			var self = this, settings = self.settings;
53819
53820			var textStyle = settings.textStyle;
53821			if (typeof textStyle == "function") {
53822				textStyle = textStyle.call(this);
53823			}
53824
53825			if (textStyle) {
53826				var textElm = self.getEl('text');
53827				if (textElm) {
53828					textElm.setAttribute('style', textStyle);
53829				}
53830			}
53831
53832			self.on('mouseenter click', function(e) {
53833				if (e.control === self) {
53834					if (!settings.menu && e.type === 'click') {
53835						self.fire('select');
53836						self.parent().hideAll();
53837					} else {
53838						self.showMenu();
53839
53840						if (e.aria) {
53841							self.menu.focus(true);
53842						}
53843					}
53844				}
53845			});
53846
53847			self._super();
53848
53849			return self;
53850		},
53851
53852		active: function(state) {
53853			if (typeof state != "undefined") {
53854				this.aria('checked', state);
53855			}
53856
53857			return this._super(state);
53858		},
53859
53860		/**
53861		 * Removes the control and it's menus.
53862		 *
53863		 * @method remove
53864		 */
53865		remove: function() {
53866			this._super();
53867
53868			if (this.menu) {
53869				this.menu.remove();
53870			}
53871		}
53872	});
53873});
53874
53875// Included from: js/tinymce/classes/ui/Menu.js
53876
53877/**
53878 * Menu.js
53879 *
53880 * Released under LGPL License.
53881 * Copyright (c) 1999-2015 Ephox Corp. All rights reserved
53882 *
53883 * License: http://www.tinymce.com/license
53884 * Contributing: http://www.tinymce.com/contributing
53885 */
53886
53887/**
53888 * Creates a new menu.
53889 *
53890 * @-x-less Menu.less
53891 * @class tinymce.ui.Menu
53892 * @extends tinymce.ui.FloatPanel
53893 */
53894define("tinymce/ui/Menu", [
53895	"tinymce/ui/FloatPanel",
53896	"tinymce/ui/MenuItem",
53897	"tinymce/util/Tools"
53898], function(FloatPanel, MenuItem, Tools) {
53899	"use strict";
53900
53901	return FloatPanel.extend({
53902		Defaults: {
53903			defaultType: 'menuitem',
53904			border: 1,
53905			layout: 'stack',
53906			role: 'application',
53907			bodyRole: 'menu',
53908			ariaRoot: true
53909		},
53910
53911		/**
53912		 * Constructs a instance with the specified settings.
53913		 *
53914		 * @constructor
53915		 * @param {Object} settings Name/value object with settings.
53916		 */
53917		init: function(settings) {
53918			var self = this;
53919
53920			settings.autohide = true;
53921			settings.constrainToViewport = true;
53922
53923			if (settings.itemDefaults) {
53924				var items = settings.items, i = items.length;
53925
53926				while (i--) {
53927					items[i] = Tools.extend({}, settings.itemDefaults, items[i]);
53928				}
53929			}
53930
53931			self._super(settings);
53932			self.classes.add('menu');
53933		},
53934
53935		/**
53936		 * Repaints the control after a layout operation.
53937		 *
53938		 * @method repaint
53939		 */
53940		repaint: function() {
53941			this.classes.toggle('menu-align', true);
53942
53943			this._super();
53944
53945			this.getEl().style.height = '';
53946			this.getEl('body').style.height = '';
53947
53948			return this;
53949		},
53950
53951		/**
53952		 * Hides/closes the menu.
53953		 *
53954		 * @method cancel
53955		 */
53956		cancel: function() {
53957			var self = this;
53958
53959			self.hideAll();
53960			self.fire('select');
53961		},
53962
53963		/**
53964		 * Hide menu and all sub menus.
53965		 *
53966		 * @method hideAll
53967		 */
53968		hideAll: function() {
53969			var self = this;
53970
53971			this.find('menuitem').exec('hideMenu');
53972
53973			return self._super();
53974		},
53975
53976		/**
53977		 * Invoked before the menu is rendered.
53978		 *
53979		 * @method preRender
53980		 */
53981		preRender: function() {
53982			var self = this;
53983
53984			self.items().each(function(ctrl) {
53985				var settings = ctrl.settings;
53986
53987				if (settings.icon || settings.image || settings.selectable) {
53988					self._hasIcons = true;
53989					return false;
53990				}
53991			});
53992
53993			return self._super();
53994		}
53995	});
53996});
53997
53998// Included from: js/tinymce/classes/ui/ListBox.js
53999
54000/**
54001 * ListBox.js
54002 *
54003 * Released under LGPL License.
54004 * Copyright (c) 1999-2015 Ephox Corp. All rights reserved
54005 *
54006 * License: http://www.tinymce.com/license
54007 * Contributing: http://www.tinymce.com/contributing
54008 */
54009
54010/**
54011 * Creates a new list box control.
54012 *
54013 * @-x-less ListBox.less
54014 * @class tinymce.ui.ListBox
54015 * @extends tinymce.ui.MenuButton
54016 */
54017define("tinymce/ui/ListBox", [
54018	"tinymce/ui/MenuButton",
54019	"tinymce/ui/Menu"
54020], function(MenuButton, Menu) {
54021	"use strict";
54022
54023	return MenuButton.extend({
54024		/**
54025		 * Constructs a instance with the specified settings.
54026		 *
54027		 * @constructor
54028		 * @param {Object} settings Name/value object with settings.
54029		 * @setting {Array} values Array with values to add to list box.
54030		 */
54031		init: function(settings) {
54032			var self = this, values, selected, selectedText, lastItemCtrl;
54033
54034			function setSelected(menuValues) {
54035				// Try to find a selected value
54036				for (var i = 0; i < menuValues.length; i++) {
54037					selected = menuValues[i].selected || settings.value === menuValues[i].value;
54038
54039					if (selected) {
54040						selectedText = selectedText || menuValues[i].text;
54041						self.state.set('value', menuValues[i].value);
54042						return true;
54043					}
54044
54045					// If the value has a submenu, try to find the selected values in that menu
54046					if (menuValues[i].menu) {
54047						if (setSelected(menuValues[i].menu)) {
54048							return true;
54049						}
54050					}
54051				}
54052			}
54053
54054			self._super(settings);
54055			settings = self.settings;
54056
54057			self._values = values = settings.values;
54058			if (values) {
54059				if (typeof settings.value != "undefined") {
54060					setSelected(values);
54061				}
54062
54063				// Default with first item
54064				if (!selected && values.length > 0) {
54065					selectedText = values[0].text;
54066					self.state.set('value', values[0].value);
54067				}
54068
54069				self.state.set('menu', values);
54070			}
54071
54072			self.state.set('text', settings.text || selectedText);
54073
54074			self.classes.add('listbox');
54075
54076			self.on('select', function(e) {
54077				var ctrl = e.control;
54078
54079				if (lastItemCtrl) {
54080					e.lastControl = lastItemCtrl;
54081				}
54082
54083				if (settings.multiple) {
54084					ctrl.active(!ctrl.active());
54085				} else {
54086					self.value(e.control.value());
54087				}
54088
54089				lastItemCtrl = ctrl;
54090			});
54091		},
54092
54093		/**
54094		 * Getter/setter function for the control value.
54095		 *
54096		 * @method value
54097		 * @param {String} [value] Value to be set.
54098		 * @return {Boolean/tinymce.ui.ListBox} Value or self if it's a set operation.
54099		 */
54100		bindStates: function() {
54101			var self = this;
54102
54103			function activateMenuItemsByValue(menu, value) {
54104				if (menu instanceof Menu) {
54105					menu.items().each(function(ctrl) {
54106						if (!ctrl.hasMenus()) {
54107							ctrl.active(ctrl.value() === value);
54108						}
54109					});
54110				}
54111			}
54112
54113			function getSelectedItem(menuValues, value) {
54114				var selectedItem;
54115
54116				if (!menuValues) {
54117					return;
54118				}
54119
54120				for (var i = 0; i < menuValues.length; i++) {
54121					if (menuValues[i].value === value) {
54122						return menuValues[i];
54123					}
54124
54125					if (menuValues[i].menu) {
54126						selectedItem = getSelectedItem(menuValues[i].menu, value);
54127						if (selectedItem) {
54128							return selectedItem;
54129						}
54130					}
54131				}
54132			}
54133
54134			self.on('show', function(e) {
54135				activateMenuItemsByValue(e.control, self.value());
54136			});
54137
54138			self.state.on('change:value', function(e) {
54139				var selectedItem = getSelectedItem(self.state.get('menu'), e.value);
54140
54141				if (selectedItem) {
54142					self.text(selectedItem.text);
54143				} else {
54144					self.text(self.settings.text);
54145				}
54146			});
54147
54148			return self._super();
54149		}
54150	});
54151});
54152
54153// Included from: js/tinymce/classes/ui/Radio.js
54154
54155/**
54156 * Radio.js
54157 *
54158 * Released under LGPL License.
54159 * Copyright (c) 1999-2015 Ephox Corp. All rights reserved
54160 *
54161 * License: http://www.tinymce.com/license
54162 * Contributing: http://www.tinymce.com/contributing
54163 */
54164
54165/**
54166 * Creates a new radio button.
54167 *
54168 * @-x-less Radio.less
54169 * @class tinymce.ui.Radio
54170 * @extends tinymce.ui.Checkbox
54171 */
54172define("tinymce/ui/Radio", [
54173	"tinymce/ui/Checkbox"
54174], function(Checkbox) {
54175	"use strict";
54176
54177	return Checkbox.extend({
54178		Defaults: {
54179			classes: "radio",
54180			role: "radio"
54181		}
54182	});
54183});
54184
54185// Included from: js/tinymce/classes/ui/ResizeHandle.js
54186
54187/**
54188 * ResizeHandle.js
54189 *
54190 * Released under LGPL License.
54191 * Copyright (c) 1999-2015 Ephox Corp. All rights reserved
54192 *
54193 * License: http://www.tinymce.com/license
54194 * Contributing: http://www.tinymce.com/contributing
54195 */
54196
54197/**
54198 * Renders a resize handle that fires ResizeStart, Resize and ResizeEnd events.
54199 *
54200 * @-x-less ResizeHandle.less
54201 * @class tinymce.ui.ResizeHandle
54202 * @extends tinymce.ui.Widget
54203 */
54204define("tinymce/ui/ResizeHandle", [
54205	"tinymce/ui/Widget",
54206	"tinymce/ui/DragHelper"
54207], function(Widget, DragHelper) {
54208	"use strict";
54209
54210	return Widget.extend({
54211		/**
54212		 * Renders the control as a HTML string.
54213		 *
54214		 * @method renderHtml
54215		 * @return {String} HTML representing the control.
54216		 */
54217		renderHtml: function() {
54218			var self = this, prefix = self.classPrefix;
54219
54220			self.classes.add('resizehandle');
54221
54222			if (self.settings.direction == "both") {
54223				self.classes.add('resizehandle-both');
54224			}
54225
54226			self.canFocus = false;
54227
54228			return (
54229				'<div id="' + self._id + '" class="' + self.classes + '">' +
54230					'<i class="' + prefix + 'ico ' + prefix + 'i-resize"></i>' +
54231				'</div>'
54232			);
54233		},
54234
54235		/**
54236		 * Called after the control has been rendered.
54237		 *
54238		 * @method postRender
54239		 */
54240		postRender: function() {
54241			var self = this;
54242
54243			self._super();
54244
54245			self.resizeDragHelper = new DragHelper(this._id, {
54246				start: function() {
54247					self.fire('ResizeStart');
54248				},
54249
54250				drag: function(e) {
54251					if (self.settings.direction != "both") {
54252						e.deltaX = 0;
54253					}
54254
54255					self.fire('Resize', e);
54256				},
54257
54258				stop: function() {
54259					self.fire('ResizeEnd');
54260				}
54261			});
54262		},
54263
54264		remove: function() {
54265			if (this.resizeDragHelper) {
54266				this.resizeDragHelper.destroy();
54267			}
54268
54269			return this._super();
54270		}
54271	});
54272});
54273
54274// Included from: js/tinymce/classes/ui/SelectBox.js
54275
54276/**
54277 * SelectBox.js
54278 *
54279 * Released under LGPL License.
54280 * Copyright (c) 1999-2015 Ephox Corp. All rights reserved
54281 *
54282 * License: http://www.tinymce.com/license
54283 * Contributing: http://www.tinymce.com/contributing
54284 */
54285
54286/**
54287 * Creates a new select box control.
54288 *
54289 * @-x-less SelectBox.less
54290 * @class tinymce.ui.SelectBox
54291 * @extends tinymce.ui.Widget
54292 */
54293define("tinymce/ui/SelectBox", [
54294	"tinymce/ui/Widget"
54295], function(Widget) {
54296	"use strict";
54297
54298	function createOptions(options) {
54299		var strOptions = '';
54300		if (options) {
54301			for (var i = 0; i < options.length; i++) {
54302				strOptions += '<option value="' + options[i] + '">' + options[i] + '</option>';
54303			}
54304		}
54305		return strOptions;
54306	}
54307
54308	return Widget.extend({
54309		Defaults: {
54310			classes: "selectbox",
54311			role: "selectbox",
54312			options: []
54313		},
54314		/**
54315		 * Constructs a instance with the specified settings.
54316		 *
54317		 * @constructor
54318		 * @param {Object} settings Name/value object with settings.
54319		 * @setting {Array} values Array with values to add to list box.
54320		 */
54321		init: function(settings) {
54322			var self = this;
54323
54324			self._super(settings);
54325
54326			if (self.settings.size) {
54327
54328				self.size = self.settings.size;
54329
54330			}
54331
54332			if (self.settings.options) {
54333				self._options = self.settings.options;
54334			}
54335
54336		},
54337
54338		/**
54339		 * Getter/setter function for the options state.
54340		 *
54341		 * @method options
54342		 * @param {Array} [state] State to be set.
54343		 * @return {Array|tinymce.ui.SelectBox} Array of string options.
54344		 */
54345		options: function(state) {
54346			if (!arguments.length) {
54347				return this.state.get('options');
54348			}
54349
54350			this.state.set('options', state);
54351
54352			return this;
54353		},
54354
54355		renderHtml: function() {
54356			var self = this, options, size = '';
54357
54358			options = createOptions(self._options);
54359
54360			if (self.size) {
54361				size = ' size = "' + self.size + '"';
54362			}
54363
54364			return (
54365				'<select id="' + self._id + '" class="' + self.classes + '"' + size + '>' +
54366					options +
54367				'</select>'
54368			);
54369		},
54370
54371		bindStates: function() {
54372			var self = this;
54373
54374			self.state.on('change:options', function(e) {
54375				self.getEl().innerHTML = createOptions(e.value);
54376			});
54377
54378			return self._super();
54379		}
54380	});
54381});
54382
54383// Included from: js/tinymce/classes/ui/Slider.js
54384
54385/**
54386 * Slider.js
54387 *
54388 * Released under LGPL License.
54389 * Copyright (c) 1999-2015 Ephox Corp. All rights reserved
54390 *
54391 * License: http://www.tinymce.com/license
54392 * Contributing: http://www.tinymce.com/contributing
54393 */
54394
54395/**
54396 * Slider control.
54397 *
54398 * @-x-less Slider.less
54399 * @class tinymce.ui.Slider
54400 * @extends tinymce.ui.Widget
54401 */
54402define("tinymce/ui/Slider", [
54403	"tinymce/ui/Widget",
54404	"tinymce/ui/DragHelper",
54405	"tinymce/ui/DomUtils"
54406], function(Widget, DragHelper, DomUtils) {
54407	"use strict";
54408
54409	function constrain(value, minVal, maxVal) {
54410		if (value < minVal) {
54411			value = minVal;
54412		}
54413
54414		if (value > maxVal) {
54415			value = maxVal;
54416		}
54417
54418		return value;
54419	}
54420
54421	function updateSliderHandle(ctrl, value) {
54422		var maxHandlePos, shortSizeName, sizeName, stylePosName, styleValue;
54423
54424		if (ctrl.settings.orientation == "v") {
54425			stylePosName = "top";
54426			sizeName = "height";
54427			shortSizeName = "h";
54428		} else {
54429			stylePosName = "left";
54430			sizeName = "width";
54431			shortSizeName = "w";
54432		}
54433
54434		maxHandlePos = (ctrl.layoutRect()[shortSizeName] || 100) - DomUtils.getSize(ctrl.getEl('handle'))[sizeName];
54435
54436		styleValue = (maxHandlePos * ((value - ctrl._minValue) / (ctrl._maxValue - ctrl._minValue))) + 'px';
54437		ctrl.getEl('handle').style[stylePosName] = styleValue;
54438		ctrl.getEl('handle').style.height = ctrl.layoutRect().h + 'px';
54439	}
54440
54441	return Widget.extend({
54442		init: function(settings) {
54443			var self = this;
54444
54445			if (!settings.previewFilter) {
54446				settings.previewFilter = function(value) {
54447					return Math.round(value * 100) / 100.0;
54448				};
54449			}
54450
54451			self._super(settings);
54452			self.classes.add('slider');
54453
54454			if (settings.orientation == "v") {
54455				self.classes.add('vertical');
54456			}
54457
54458			self._minValue = settings.minValue || 0;
54459			self._maxValue = settings.maxValue || 100;
54460			self._initValue = self.state.get('value');
54461		},
54462
54463		renderHtml: function() {
54464			var self = this, id = self._id, prefix = self.classPrefix;
54465
54466			return (
54467				'<div id="' + id + '" class="' + self.classes + '">' +
54468					'<div id="' + id + '-handle" class="' + prefix + 'slider-handle"></div>' +
54469				'</div>'
54470			);
54471		},
54472
54473		reset: function() {
54474			this.value(this._initValue).repaint();
54475		},
54476
54477		postRender: function() {
54478			var self = this, startPos, startHandlePos, handlePos = 0, value, minValue, maxValue, maxHandlePos;
54479			var screenCordName, stylePosName, sizeName, shortSizeName;
54480
54481			minValue = self._minValue;
54482			maxValue = self._maxValue;
54483			value = self.value();
54484
54485			if (self.settings.orientation == "v") {
54486				screenCordName = "screenY";
54487				stylePosName = "top";
54488				sizeName = "height";
54489				shortSizeName = "h";
54490			} else {
54491				screenCordName = "screenX";
54492				stylePosName = "left";
54493				sizeName = "width";
54494				shortSizeName = "w";
54495			}
54496
54497			self._super();
54498
54499			self._dragHelper = new DragHelper(self._id, {
54500				handle: self._id + "-handle",
54501
54502				start: function(e) {
54503					startPos = e[screenCordName];
54504					startHandlePos = parseInt(self.getEl('handle').style[stylePosName], 10);
54505					maxHandlePos = (self.layoutRect()[shortSizeName] || 100) - DomUtils.getSize(self.getEl('handle'))[sizeName];
54506					self.fire('dragstart', {value: value});
54507				},
54508
54509				drag: function(e) {
54510					var delta = e[screenCordName] - startPos, handleEl = self.getEl('handle');
54511
54512					handlePos = constrain(startHandlePos + delta, 0, maxHandlePos);
54513					handleEl.style[stylePosName] = handlePos + 'px';
54514
54515					value = minValue + (handlePos / maxHandlePos) * (maxValue - minValue);
54516					self.value(value);
54517
54518					self.tooltip().text('' + self.settings.previewFilter(value)).show().moveRel(handleEl, 'bc tc');
54519
54520					self.fire('drag', {value: value});
54521				},
54522
54523				stop: function() {
54524					self.tooltip().hide();
54525					self.fire('dragend', {value: value});
54526				}
54527			});
54528		},
54529
54530		repaint: function() {
54531			this._super();
54532			updateSliderHandle(this, this.value());
54533		},
54534
54535		bindStates: function() {
54536			var self = this;
54537
54538			self.state.on('change:value', function(e) {
54539				updateSliderHandle(self, e.value);
54540			});
54541
54542			return self._super();
54543		}
54544	});
54545});
54546
54547// Included from: js/tinymce/classes/ui/Spacer.js
54548
54549/**
54550 * Spacer.js
54551 *
54552 * Released under LGPL License.
54553 * Copyright (c) 1999-2015 Ephox Corp. All rights reserved
54554 *
54555 * License: http://www.tinymce.com/license
54556 * Contributing: http://www.tinymce.com/contributing
54557 */
54558
54559/**
54560 * Creates a spacer. This control is used in flex layouts for example.
54561 *
54562 * @-x-less Spacer.less
54563 * @class tinymce.ui.Spacer
54564 * @extends tinymce.ui.Widget
54565 */
54566define("tinymce/ui/Spacer", [
54567	"tinymce/ui/Widget"
54568], function(Widget) {
54569	"use strict";
54570
54571	return Widget.extend({
54572		/**
54573		 * Renders the control as a HTML string.
54574		 *
54575		 * @method renderHtml
54576		 * @return {String} HTML representing the control.
54577		 */
54578		renderHtml: function() {
54579			var self = this;
54580
54581			self.classes.add('spacer');
54582			self.canFocus = false;
54583
54584			return '<div id="' + self._id + '" class="' + self.classes + '"></div>';
54585		}
54586	});
54587});
54588
54589// Included from: js/tinymce/classes/ui/SplitButton.js
54590
54591/**
54592 * SplitButton.js
54593 *
54594 * Released under LGPL License.
54595 * Copyright (c) 1999-2015 Ephox Corp. All rights reserved
54596 *
54597 * License: http://www.tinymce.com/license
54598 * Contributing: http://www.tinymce.com/contributing
54599 */
54600
54601/**
54602 * Creates a split button.
54603 *
54604 * @-x-less SplitButton.less
54605 * @class tinymce.ui.SplitButton
54606 * @extends tinymce.ui.Button
54607 */
54608define("tinymce/ui/SplitButton", [
54609	"tinymce/ui/MenuButton",
54610	"tinymce/ui/DomUtils",
54611	"tinymce/dom/DomQuery"
54612], function(MenuButton, DomUtils, $) {
54613	return MenuButton.extend({
54614		Defaults: {
54615			classes: "widget btn splitbtn",
54616			role: "button"
54617		},
54618
54619		/**
54620		 * Repaints the control after a layout operation.
54621		 *
54622		 * @method repaint
54623		 */
54624		repaint: function() {
54625			var self = this, elm = self.getEl(), rect = self.layoutRect(), mainButtonElm, menuButtonElm;
54626
54627			self._super();
54628
54629			mainButtonElm = elm.firstChild;
54630			menuButtonElm = elm.lastChild;
54631
54632			$(mainButtonElm).css({
54633				width: rect.w - DomUtils.getSize(menuButtonElm).width,
54634				height: rect.h - 2
54635			});
54636
54637			$(menuButtonElm).css({
54638				height: rect.h - 2
54639			});
54640
54641			return self;
54642		},
54643
54644		/**
54645		 * Sets the active menu state.
54646		 *
54647		 * @private
54648		 */
54649		activeMenu: function(state) {
54650			var self = this;
54651
54652			$(self.getEl().lastChild).toggleClass(self.classPrefix + 'active', state);
54653		},
54654
54655		/**
54656		 * Renders the control as a HTML string.
54657		 *
54658		 * @method renderHtml
54659		 * @return {String} HTML representing the control.
54660		 */
54661		renderHtml: function() {
54662			var self = this, id = self._id, prefix = self.classPrefix, image;
54663			var icon = self.state.get('icon'), text = self.state.get('text'),
54664				textHtml = '';
54665
54666			image = self.settings.image;
54667			if (image) {
54668				icon = 'none';
54669
54670				// Support for [high dpi, low dpi] image sources
54671				if (typeof image != "string") {
54672					image = window.getSelection ? image[0] : image[1];
54673				}
54674
54675				image = ' style="background-image: url(\'' + image + '\')"';
54676			} else {
54677				image = '';
54678			}
54679
54680			icon = self.settings.icon ? prefix + 'ico ' + prefix + 'i-' + icon : '';
54681
54682			if (text) {
54683				self.classes.add('btn-has-text');
54684				textHtml = '<span class="' + prefix + 'txt">' + self.encode(text) + '</span>';
54685			}
54686
54687			return (
54688				'<div id="' + id + '" class="' + self.classes + '" role="button" tabindex="-1">' +
54689					'<button type="button" hidefocus="1" tabindex="-1">' +
54690						(icon ? '<i class="' + icon + '"' + image + '></i>' : '') +
54691						textHtml +
54692					'</button>' +
54693					'<button type="button" class="' + prefix + 'open" hidefocus="1" tabindex="-1">' +
54694						//(icon ? '<i class="' + icon + '"></i>' : '') +
54695						(self._menuBtnText ? (icon ? '\u00a0' : '') + self._menuBtnText : '') +
54696						' <i class="' + prefix + 'caret"></i>' +
54697					'</button>' +
54698				'</div>'
54699			);
54700		},
54701
54702		/**
54703		 * Called after the control has been rendered.
54704		 *
54705		 * @method postRender
54706		 */
54707		postRender: function() {
54708			var self = this, onClickHandler = self.settings.onclick;
54709
54710			self.on('click', function(e) {
54711				var node = e.target;
54712
54713				if (e.control == this) {
54714					// Find clicks that is on the main button
54715					while (node) {
54716						if ((e.aria && e.aria.key != 'down') || (node.nodeName == 'BUTTON' && node.className.indexOf('open') == -1)) {
54717							e.stopImmediatePropagation();
54718
54719							if (onClickHandler) {
54720								onClickHandler.call(this, e);
54721							}
54722
54723							return;
54724						}
54725
54726						node = node.parentNode;
54727					}
54728				}
54729			});
54730
54731			delete self.settings.onclick;
54732
54733			return self._super();
54734		}
54735	});
54736});
54737
54738// Included from: js/tinymce/classes/ui/StackLayout.js
54739
54740/**
54741 * StackLayout.js
54742 *
54743 * Released under LGPL License.
54744 * Copyright (c) 1999-2015 Ephox Corp. All rights reserved
54745 *
54746 * License: http://www.tinymce.com/license
54747 * Contributing: http://www.tinymce.com/contributing
54748 */
54749
54750/**
54751 * This layout uses the browsers layout when the items are blocks.
54752 *
54753 * @-x-less StackLayout.less
54754 * @class tinymce.ui.StackLayout
54755 * @extends tinymce.ui.FlowLayout
54756 */
54757define("tinymce/ui/StackLayout", [
54758	"tinymce/ui/FlowLayout"
54759], function(FlowLayout) {
54760	"use strict";
54761
54762	return FlowLayout.extend({
54763		Defaults: {
54764			containerClass: 'stack-layout',
54765			controlClass: 'stack-layout-item',
54766			endClass: 'break'
54767		},
54768
54769		isNative: function() {
54770			return true;
54771		}
54772	});
54773});
54774
54775// Included from: js/tinymce/classes/ui/TabPanel.js
54776
54777/**
54778 * TabPanel.js
54779 *
54780 * Released under LGPL License.
54781 * Copyright (c) 1999-2015 Ephox Corp. All rights reserved
54782 *
54783 * License: http://www.tinymce.com/license
54784 * Contributing: http://www.tinymce.com/contributing
54785 */
54786
54787/**
54788 * Creates a tab panel control.
54789 *
54790 * @-x-less TabPanel.less
54791 * @class tinymce.ui.TabPanel
54792 * @extends tinymce.ui.Panel
54793 *
54794 * @setting {Number} activeTab Active tab index.
54795 */
54796define("tinymce/ui/TabPanel", [
54797	"tinymce/ui/Panel",
54798	"tinymce/dom/DomQuery",
54799	"tinymce/ui/DomUtils"
54800], function(Panel, $, DomUtils) {
54801	"use strict";
54802
54803	return Panel.extend({
54804		Defaults: {
54805			layout: 'absolute',
54806			defaults: {
54807				type: 'panel'
54808			}
54809		},
54810
54811		/**
54812		 * Activates the specified tab by index.
54813		 *
54814		 * @method activateTab
54815		 * @param {Number} idx Index of the tab to activate.
54816		 */
54817		activateTab: function(idx) {
54818			var activeTabElm;
54819
54820			if (this.activeTabId) {
54821				activeTabElm = this.getEl(this.activeTabId);
54822				$(activeTabElm).removeClass(this.classPrefix + 'active');
54823				activeTabElm.setAttribute('aria-selected', "false");
54824			}
54825
54826			this.activeTabId = 't' + idx;
54827
54828			activeTabElm = this.getEl('t' + idx);
54829			activeTabElm.setAttribute('aria-selected', "true");
54830			$(activeTabElm).addClass(this.classPrefix + 'active');
54831
54832			this.items()[idx].show().fire('showtab');
54833			this.reflow();
54834
54835			this.items().each(function(item, i) {
54836				if (idx != i) {
54837					item.hide();
54838				}
54839			});
54840		},
54841
54842		/**
54843		 * Renders the control as a HTML string.
54844		 *
54845		 * @method renderHtml
54846		 * @return {String} HTML representing the control.
54847		 */
54848		renderHtml: function() {
54849			var self = this, layout = self._layout, tabsHtml = '', prefix = self.classPrefix;
54850
54851			self.preRender();
54852			layout.preRender(self);
54853
54854			self.items().each(function(ctrl, i) {
54855				var id = self._id + '-t' + i;
54856
54857				ctrl.aria('role', 'tabpanel');
54858				ctrl.aria('labelledby', id);
54859
54860				tabsHtml += (
54861					'<div id="' + id + '" class="' + prefix + 'tab" ' +
54862						'unselectable="on" role="tab" aria-controls="' + ctrl._id + '" aria-selected="false" tabIndex="-1">' +
54863						self.encode(ctrl.settings.title) +
54864					'</div>'
54865				);
54866			});
54867
54868			return (
54869				'<div id="' + self._id + '" class="' + self.classes + '" hidefocus="1" tabindex="-1">' +
54870					'<div id="' + self._id + '-head" class="' + prefix + 'tabs" role="tablist">' +
54871						tabsHtml +
54872					'</div>' +
54873					'<div id="' + self._id + '-body" class="' + self.bodyClasses + '">' +
54874						layout.renderHtml(self) +
54875					'</div>' +
54876				'</div>'
54877			);
54878		},
54879
54880		/**
54881		 * Called after the control has been rendered.
54882		 *
54883		 * @method postRender
54884		 */
54885		postRender: function() {
54886			var self = this;
54887
54888			self._super();
54889
54890			self.settings.activeTab = self.settings.activeTab || 0;
54891			self.activateTab(self.settings.activeTab);
54892
54893			this.on('click', function(e) {
54894				var targetParent = e.target.parentNode;
54895
54896				if (e.target.parentNode.id == self._id + '-head') {
54897					var i = targetParent.childNodes.length;
54898
54899					while (i--) {
54900						if (targetParent.childNodes[i] == e.target) {
54901							self.activateTab(i);
54902						}
54903					}
54904				}
54905			});
54906		},
54907
54908		/**
54909		 * Initializes the current controls layout rect.
54910		 * This will be executed by the layout managers to determine the
54911		 * default minWidth/minHeight etc.
54912		 *
54913		 * @method initLayoutRect
54914		 * @return {Object} Layout rect instance.
54915		 */
54916		initLayoutRect: function() {
54917			var self = this, rect, minW, minH;
54918
54919			minW = DomUtils.getSize(self.getEl('head')).width;
54920			minW = minW < 0 ? 0 : minW;
54921			minH = 0;
54922
54923			self.items().each(function(item) {
54924				minW = Math.max(minW, item.layoutRect().minW);
54925				minH = Math.max(minH, item.layoutRect().minH);
54926			});
54927
54928			self.items().each(function(ctrl) {
54929				ctrl.settings.x = 0;
54930				ctrl.settings.y = 0;
54931				ctrl.settings.w = minW;
54932				ctrl.settings.h = minH;
54933
54934				ctrl.layoutRect({
54935					x: 0,
54936					y: 0,
54937					w: minW,
54938					h: minH
54939				});
54940			});
54941
54942			var headH = DomUtils.getSize(self.getEl('head')).height;
54943
54944			self.settings.minWidth = minW;
54945			self.settings.minHeight = minH + headH;
54946
54947			rect = self._super();
54948			rect.deltaH += headH;
54949			rect.innerH = rect.h - rect.deltaH;
54950
54951			return rect;
54952		}
54953	});
54954});
54955
54956// Included from: js/tinymce/classes/ui/TextBox.js
54957
54958/**
54959 * TextBox.js
54960 *
54961 * Released under LGPL License.
54962 * Copyright (c) 1999-2015 Ephox Corp. All rights reserved
54963 *
54964 * License: http://www.tinymce.com/license
54965 * Contributing: http://www.tinymce.com/contributing
54966 */
54967
54968/**
54969 * Creates a new textbox.
54970 *
54971 * @-x-less TextBox.less
54972 * @class tinymce.ui.TextBox
54973 * @extends tinymce.ui.Widget
54974 */
54975define("tinymce/ui/TextBox", [
54976	"tinymce/ui/Widget"
54977], function(Widget) {
54978	"use strict";
54979
54980	return Widget.extend({
54981		/**
54982		 * Constructs a instance with the specified settings.
54983		 *
54984		 * @constructor
54985		 * @param {Object} settings Name/value object with settings.
54986		 * @setting {Boolean} multiline True if the textbox is a multiline control.
54987		 * @setting {Number} maxLength Max length for the textbox.
54988		 * @setting {Number} size Size of the textbox in characters.
54989		 */
54990		init: function(settings) {
54991			var self = this;
54992
54993			self._super(settings);
54994
54995			self.classes.add('textbox');
54996
54997			if (settings.multiline) {
54998				self.classes.add('multiline');
54999			} else {
55000				self.on('keydown', function(e) {
55001					var rootControl;
55002
55003					if (e.keyCode == 13) {
55004						e.preventDefault();
55005
55006						// Find root control that we can do toJSON on
55007						self.parents().reverse().each(function(ctrl) {
55008							if (ctrl.toJSON) {
55009								rootControl = ctrl;
55010								return false;
55011							}
55012						});
55013
55014						// Fire event on current text box with the serialized data of the whole form
55015						self.fire('submit', {data: rootControl.toJSON()});
55016					}
55017				});
55018
55019				self.on('keyup', function(e) {
55020					self.state.set('value', e.target.value);
55021				});
55022			}
55023		},
55024
55025		/**
55026		 * Repaints the control after a layout operation.
55027		 *
55028		 * @method repaint
55029		 */
55030		repaint: function() {
55031			var self = this, style, rect, borderBox, borderW, borderH = 0, lastRepaintRect;
55032
55033			style = self.getEl().style;
55034			rect = self._layoutRect;
55035			lastRepaintRect = self._lastRepaintRect || {};
55036
55037			// Detect old IE 7+8 add lineHeight to align caret vertically in the middle
55038			var doc = document;
55039			if (!self.settings.multiline && doc.all && (!doc.documentMode || doc.documentMode <= 8)) {
55040				style.lineHeight = (rect.h - borderH) + 'px';
55041			}
55042
55043			borderBox = self.borderBox;
55044			borderW = borderBox.left + borderBox.right + 8;
55045			borderH = borderBox.top + borderBox.bottom + (self.settings.multiline ? 8 : 0);
55046
55047			if (rect.x !== lastRepaintRect.x) {
55048				style.left = rect.x + 'px';
55049				lastRepaintRect.x = rect.x;
55050			}
55051
55052			if (rect.y !== lastRepaintRect.y) {
55053				style.top = rect.y + 'px';
55054				lastRepaintRect.y = rect.y;
55055			}
55056
55057			if (rect.w !== lastRepaintRect.w) {
55058				style.width = (rect.w - borderW) + 'px';
55059				lastRepaintRect.w = rect.w;
55060			}
55061
55062			if (rect.h !== lastRepaintRect.h) {
55063				style.height = (rect.h - borderH) + 'px';
55064				lastRepaintRect.h = rect.h;
55065			}
55066
55067			self._lastRepaintRect = lastRepaintRect;
55068			self.fire('repaint', {}, false);
55069
55070			return self;
55071		},
55072
55073		/**
55074		 * Renders the control as a HTML string.
55075		 *
55076		 * @method renderHtml
55077		 * @return {String} HTML representing the control.
55078		 */
55079		renderHtml: function() {
55080			var self = this, id = self._id, settings = self.settings, value = self.encode(self.state.get('value'), false), extraAttrs = '';
55081
55082			if ("spellcheck" in settings) {
55083				extraAttrs += ' spellcheck="' + settings.spellcheck + '"';
55084			}
55085
55086			if (settings.maxLength) {
55087				extraAttrs += ' maxlength="' + settings.maxLength + '"';
55088			}
55089
55090			if (settings.size) {
55091				extraAttrs += ' size="' + settings.size + '"';
55092			}
55093
55094			if (settings.subtype) {
55095				extraAttrs += ' type="' + settings.subtype + '"';
55096			}
55097
55098			if (self.disabled()) {
55099				extraAttrs += ' disabled="disabled"';
55100			}
55101
55102			if (settings.multiline) {
55103				return (
55104					'<textarea id="' + id + '" class="' + self.classes + '" ' +
55105					(settings.rows ? ' rows="' + settings.rows + '"' : '') +
55106					' hidefocus="1"' + extraAttrs + '>' + value +
55107					'</textarea>'
55108				);
55109			}
55110
55111			return '<input id="' + id + '" class="' + self.classes + '" value="' + value + '" hidefocus="1"' + extraAttrs + ' />';
55112		},
55113
55114		value: function(value) {
55115			if (arguments.length) {
55116				this.state.set('value', value);
55117				return this;
55118			}
55119
55120			// Make sure the real state is in sync
55121			if (this.state.get('rendered')) {
55122				this.state.set('value', this.getEl().value);
55123			}
55124
55125			return this.state.get('value');
55126		},
55127
55128		/**
55129		 * Called after the control has been rendered.
55130		 *
55131		 * @method postRender
55132		 */
55133		postRender: function() {
55134			var self = this;
55135
55136			self._super();
55137
55138			self.$el.on('change', function(e) {
55139				self.state.set('value', e.target.value);
55140				self.fire('change', e);
55141			});
55142		},
55143
55144		bindStates: function() {
55145			var self = this;
55146
55147			self.state.on('change:value', function(e) {
55148				if (self.getEl().value != e.value) {
55149					self.getEl().value = e.value;
55150				}
55151			});
55152
55153			self.state.on('change:disabled', function(e) {
55154				self.getEl().disabled = e.value;
55155			});
55156
55157			return self._super();
55158		},
55159
55160		remove: function() {
55161			this.$el.off();
55162			this._super();
55163		}
55164	});
55165});
55166
55167// Included from: js/tinymce/classes/ui/Throbber.js
55168
55169/**
55170 * Throbber.js
55171 *
55172 * Released under LGPL License.
55173 * Copyright (c) 1999-2015 Ephox Corp. All rights reserved
55174 *
55175 * License: http://www.tinymce.com/license
55176 * Contributing: http://www.tinymce.com/contributing
55177 */
55178
55179/**
55180 * This class enables you to display a Throbber for any element.
55181 *
55182 * @-x-less Throbber.less
55183 * @class tinymce.ui.Throbber
55184 */
55185define("tinymce/ui/Throbber", [
55186	"tinymce/dom/DomQuery",
55187	"tinymce/ui/Control",
55188	"tinymce/util/Delay"
55189], function($, Control, Delay) {
55190	"use strict";
55191
55192	/**
55193	 * Constructs a new throbber.
55194	 *
55195	 * @constructor
55196	 * @param {Element} elm DOM Html element to display throbber in.
55197	 * @param {Boolean} inline Optional true/false state if the throbber should be appended to end of element for infinite scroll.
55198	 */
55199	return function(elm, inline) {
55200		var self = this, state, classPrefix = Control.classPrefix;
55201
55202		/**
55203		 * Shows the throbber.
55204		 *
55205		 * @method show
55206		 * @param {Number} [time] Time to wait before showing.
55207		 * @param {function} [callback] Optional callback to execute when the throbber is shown.
55208		 * @return {tinymce.ui.Throbber} Current throbber instance.
55209		 */
55210		self.show = function(time, callback) {
55211			self.hide();
55212
55213			state = true;
55214
55215			Delay.setTimeout(function() {
55216				if (state) {
55217					$(elm).append(
55218						'<div class="' + classPrefix + 'throbber' + (inline ? ' ' + classPrefix + 'throbber-inline' : '') + '"></div>'
55219					);
55220
55221					if (callback) {
55222						callback();
55223					}
55224				}
55225			}, time);
55226
55227			return self;
55228		};
55229
55230		/**
55231		 * Hides the throbber.
55232		 *
55233		 * @method hide
55234		 * @return {tinymce.ui.Throbber} Current throbber instance.
55235		 */
55236		self.hide = function() {
55237			var child = elm.lastChild;
55238
55239			if (child && child.className.indexOf('throbber') != -1) {
55240				child.parentNode.removeChild(child);
55241			}
55242
55243			state = false;
55244
55245			return self;
55246		};
55247	};
55248});
55249
55250expose(["tinymce/geom/Rect","tinymce/util/Promise","tinymce/util/Delay","tinymce/dom/EventUtils","tinymce/dom/Sizzle","tinymce/Env","tinymce/util/Tools","tinymce/dom/DomQuery","tinymce/html/Styles","tinymce/dom/TreeWalker","tinymce/html/Entities","tinymce/dom/DOMUtils","tinymce/dom/ScriptLoader","tinymce/AddOnManager","tinymce/dom/RangeUtils","tinymce/html/Node","tinymce/html/Schema","tinymce/html/SaxParser","tinymce/html/DomParser","tinymce/html/Writer","tinymce/html/Serializer","tinymce/dom/Serializer","tinymce/util/VK","tinymce/dom/ControlSelection","tinymce/dom/BookmarkManager","tinymce/dom/Selection","tinymce/Formatter","tinymce/UndoManager","tinymce/EditorCommands","tinymce/util/URI","tinymce/util/Class","tinymce/util/EventDispatcher","tinymce/util/Observable","tinymce/ui/Selector","tinymce/ui/Collection","tinymce/ui/ReflowQueue","tinymce/ui/Control","tinymce/ui/Factory","tinymce/ui/KeyboardNavigation","tinymce/ui/Container","tinymce/ui/DragHelper","tinymce/ui/Scrollable","tinymce/ui/Panel","tinymce/ui/Movable","tinymce/ui/Resizable","tinymce/ui/FloatPanel","tinymce/ui/Window","tinymce/ui/MessageBox","tinymce/WindowManager","tinymce/ui/Tooltip","tinymce/ui/Widget","tinymce/ui/Progress","tinymce/ui/Notification","tinymce/NotificationManager","tinymce/EditorObservable","tinymce/Shortcuts","tinymce/Editor","tinymce/util/I18n","tinymce/FocusManager","tinymce/EditorManager","tinymce/util/XHR","tinymce/util/JSON","tinymce/util/JSONRequest","tinymce/util/JSONP","tinymce/util/LocalStorage","tinymce/Compat","tinymce/ui/Layout","tinymce/ui/AbsoluteLayout","tinymce/ui/Button","tinymce/ui/ButtonGroup","tinymce/ui/Checkbox","tinymce/ui/ComboBox","tinymce/ui/ColorBox","tinymce/ui/PanelButton","tinymce/ui/ColorButton","tinymce/util/Color","tinymce/ui/ColorPicker","tinymce/ui/Path","tinymce/ui/ElementPath","tinymce/ui/FormItem","tinymce/ui/Form","tinymce/ui/FieldSet","tinymce/ui/FilePicker","tinymce/ui/FitLayout","tinymce/ui/FlexLayout","tinymce/ui/FlowLayout","tinymce/ui/FormatControls","tinymce/ui/GridLayout","tinymce/ui/Iframe","tinymce/ui/Label","tinymce/ui/Toolbar","tinymce/ui/MenuBar","tinymce/ui/MenuButton","tinymce/ui/MenuItem","tinymce/ui/Menu","tinymce/ui/ListBox","tinymce/ui/Radio","tinymce/ui/ResizeHandle","tinymce/ui/SelectBox","tinymce/ui/Slider","tinymce/ui/Spacer","tinymce/ui/SplitButton","tinymce/ui/StackLayout","tinymce/ui/TabPanel","tinymce/ui/TextBox","tinymce/ui/Throbber"]);
55251})(this);
55252!function(e){function t(){function t(e){"remove"===e&&this.each(function(e,t){var n=i(t);n&&n.remove()}),this.find("span.mceEditor,div.mceEditor").each(function(e,t){var n=tinymce.get(t.id.replace(/_parent$/,""));n&&n.remove()})}function r(e){var n,r=this;if(null!=e)t.call(r),r.each(function(t,n){var r;(r=tinymce.get(n.id))&&r.setContent(e)});else if(r.length>0&&(n=tinymce.get(r[0].id)))return n.getContent()}function i(e){var t=null;return e&&e.id&&a.tinymce&&(t=tinymce.get(e.id)),t}function o(e){return!!(e&&e.length&&a.tinymce&&e.is(":tinymce"))}var s={};e.each(["text","html","val"],function(t,a){var l=s[a]=e.fn[a],c="text"===a;e.fn[a]=function(t){var a=this;
55252if(!o(a))return l.apply(a,arguments);if(t!==n)return r.call(a.filter(":tinymce"),t),l.apply(a.not(":tinymce"),arguments),a;var s="",u=arguments;return(c?a:a.eq(0)).each(function(t,n){var r=i(n);s+=r?c?r.getContent().replace(/<(?:"[^"]*"|'[^']*'|[^'">])*>/g,""):r.getContent({save:!0}):l.apply(e(n),u)}),s}}),e.each(["append","prepend"],function(t,r){var a=s[r]=e.fn[r],l="prepend"===r;e.fn[r]=function(e){var t=this;return o(t)?e!==n?("string"==typeof e&&t.filter(":tinymce").each(function(t,n){var r=i(n);r&&r.setContent(l?e+r.getContent():r.getContent()+e)}),a.apply(t.not(":tinymce"),arguments),t):void 0:a.apply(t,arguments)}}),e.each(["remove","replaceWith","replaceAll","empty"],function(n,r){var i=s[r]=e.fn[r];e.fn[r]=function(){return t.call(this,r),i.apply(this,arguments)}}),s.attr=e.fn.attr,e.fn.attr=function(t,a){var l=this,c=arguments;if(!t||"value"!==t||!o(l))return a!==n?s.attr.apply(l,c):s.attr.apply(l,c);if(a!==n)return r.call(l.filter(":tinymce"),a),s.attr.apply(l.not(":tinymce"),c),l;var u=l[0],d=i(u);return d?d.getContent({save:!0}):s.attr.apply(e(u),c)}}var n,r,i,o=[],a=window;e.fn.tinymce=function(n){function s(){var r=[],o=0;i||(t(),i=!0),d.each(function(e,t){var i,a=t.id,s=n.oninit;a||(t.id=a=tinymce.DOM.uniqueId()),tinymce.get(a)||(i=new tinymce.Editor(a,n,tinymce.EditorManager),r.push(i),i.on("init",function(){var e,t=s;d.css("visibility",""),s&&++o==r.length&&("string"==typeof t&&(e=-1===t.indexOf(".")?null:tinymce.resolve(t.replace(/\.\w+$/,"")),t=tinymce.resolve(t)),t.apply(e||tinymce,r))}))}),e.each(r,function(e,t){t.render()})}var l,c,u,d=this,f="";if(!d.length)return d;if(!n)return window.tinymce?tinymce.get(d[0].id):null;if(d.css("visibility","hidden"),a.tinymce||r||!(l=n.script_url))1===r?o.push(s):s();else{r=1,c=l.substring(0,l.lastIndexOf("/")),-1!=l.indexOf(".min")&&(f=".min"),a.tinymce=a.tinyMCEPreInit||{base:c,suffix:f},-1!=l.indexOf("gzip")&&(u=n.language||"en",l=l+(/\?/.test(l)?"&":"?")+"js=true&core=true&suffix="+escape(f)+"&themes="+escape(n.theme||"modern")+"&plugins="+escape(n.plugins||"")+"&languages="+(u||""),a.tinyMCE_GZ||(a.tinyMCE_GZ={start:function(){function t(e){tinymce.ScriptLoader.markDone(tinymce.baseURI.toAbsolute(e))}t("langs/"+u+".js"),t("themes/"+n.theme+"/theme"+f+".js"),t("themes/"+n.theme+"/langs/"+u+".js"),e.each(n.plugins.split(","),function(e,n){
55252n&&(t("plugins/"+n+"/plugin"+f+".js"),t("plugins/"+n+"/langs/"+u+".js"))})},end:function(){}}));var h=document.createElement("script");h.type="text/javascript",h.onload=h.onreadystatechange=function(t){t=t||window.event,2===r||"load"!=t.type&&!/complete|loaded/.test(h.readyState)||(tinymce.dom.Event.domLoaded=1,r=2,n.script_loaded&&n.script_loaded(),s(),e.each(o,function(e,t){t()}))},h.src=l,document.body.appendChild(h)}return d},e.extend(e.expr[":"],{tinymce:function(e){var t;return e.id&&"tinymce"in window&&(t=tinymce.get(e.id),t&&t.editorManager===tinymce)?!0:!1}})}(jQuery);
55253
55254
55255
55256"use strict";
55257
55258$.validator.addMethod("url_loose", function (val, elem) {
55259  // if no url, don't do anything
55260  if (val.length == 0) {
55261    return true;
55262  }
55263  return (/^(?:(http|https):\/\/)?(?:[\w-]+\.)+[a-z]{3,6}(\/(.+)?)?$/.test(val)
55264  );
55265}, $.validator.format("Please enter a valid URL."));
55266
55267$.validator.addMethod("editor_maxlength", function (val, elem, params) {
55268  if (typeof tinyMCE == 'undefined') return true;
55269  return tinyMCE.editors[elem.id].plugins["charactercount"].getCount() <= params;
55270}, $.validator.format('Maximum characters: {0}'));
55271
55272$.validator.addMethod("greater_than", function (value, elem, params) {
55273  if (!/Invalid|NaN/.test(new Date(value))) {
55274    return new Date(value) > new Date($(params).val());
55275  }
55276  return isNaN(value) && isNaN($(params).val()) || Number(value) > Number($(params).val());
55277}, 'Must be greater than start date.');
55278
55279$.validator.addMethod("number_less_than", function (value, elem, params) {
55280  return Number(value) < Number($(params).val());
55281}, 'Must be less than other field.');
55282
55283$.validator.addMethod("number_less_than_or_equal_to", function (value, elem, params) {
55284  return Number(value) <= Number($(params).val());
55285}, 'Must be less than or equal to other field.');
55286
55287$.validator.addMethod("youtube_url", function (val, elem, params) {
55288  return val.indexOf('youtube.com') > -1 && val.indexOf('v=') > -1;
55289}, 'Must be a Youtube URL');
55290
55291$.validator.addMethod("email", function (val, elem, params) {
55292  if (params['allowBlank'] && this.optional(elem)) {
55293    return true;
55294  }
55295
55296  var regex = /^([a-zA-Z0-9_.+-])+\@(([a-zA-Z0-9-])+\.)+([a-zA-Z0-9]{2,4})+$/;
55297  return regex.test(val);
55298}, 'Must be a valid email address');
55299
55300$.validator.addMethod("google_analytics_key", function (val, elem) {
55301  var regex = /^G-/;
55302  return regex.test(val);
55303}, 'Key must in the format of G-XXXXXXXXXX');
55304
55305$.validator.addMethod("date_format", function (val, elem, params) {
55306  var valid = undefined;
55307
55308  try {
55309    $.datepicker.parseDate('mm/dd/yy', val);
55310    valid = true;
55311  } catch (e) {
55312    valid = false;
55313  }
55314
55315  return valid;
55316}, 'Must be a date: mm/dd/yyyy');
55317
55318$.validator.addMethod("month_day", function (val, elem, params) {
55319  var field1 = $("[name=\"" + params[0] + "\"]").val();
55320  var field2 = $("[name=\"" + params[1] + "\"]").val();
55321
55322  return field1 && field2 || !field1 && !field2;
55323}, 'Month and day are required');
55324
55325$.validator.addMethod("not_in", function (val, elem, params) {
55326  var formattedVal = val.toLowerCase();
55327  var isValid = params[0].indexOf(formattedVal) === -1;
55328  return isValid;
55329}, $.validator.format('Cannot be any of the following: {0}'));
55330
55331$.validator.addMethod("file_required", function (value, elem, params) {
55332  if (elem.files.length) {
55333    return true;
55334  }
55335
55336  return $(elem).data('file-name') !== undefined;
55337}, 'File is required.');
55338
55339$.validator.addMethod("filesize", function (value, element, params) {
55340  // In the validation call the supplied value should be in MB
55341  return this.optional(element) || element.files[0].size <= params * 1000000;
55342}, 'File size must be less than {0}MB');
55343
55344$.validator.addMethod("currency", function (value, element) {
55345  return this.optional(element) || /^(\d{1,3}(\,\d{3})*|(\d+))(\.\d{2})?$/.test(value);
55346}, "Please specify a valid amount");
55347
55348$.validator.addMethod("number_letter_hyphen", function (value, element) {
55349  return this.optional(element) || /^([a-z0-9-]+)?$/.test(value);
55350}, "Please specify a valid amount");
55351'use strict';
55352
55353if (typeof tinyMCE != 'undefined') {
55354  tinyMCE.textareaMaxlengthDefault = 600;
55355}
55356
55357// Define the errorPlacement handler independently
55358// so it can be included and / or overriden elsewhere.
55359window.formValidateErrorPlacement = function (error, element) {
55360  if (element.hasClass('no-error-label')) {
55361    return true;
55362  } else if (element.is(':checkbox')) {
55363    element.parents('.form-item--checkbox').first().append(error);
55364  } else if (element.is('select')) {
55365    element.parents('.form-item__container').first().after(error);
55366  } else if (element.parents('.form-item__container').length) {
55367    element.parents('.form-item__container').first().after(error);
55368  } else {
55369    error.insertAfter(element);
55370  }
55371};
55372
55373window.formValidateDefaults = {
55374  errorPlacement: window.formValidateErrorPlacement,
55375
55376  onfocusout: function onfocusout(element) {
55377    if (!$(element).is('.datepicker')) {
55378      $(element).valid();
55379    }
55380  },
55381
55382  submitHandler: function submitHandler(form) {
55383    var text = $(form).attr('data-loading-text') || 'Please wait...';
55384    var $submit = $(form).find('[type="submit"]');
55385
55386    $(form).get(0).dispatchEvent(new CustomEvent('validated', { bubbles: true }));
55387
55388    $submit.prop('disabled', true);
55389
55390    if ($submit.is('button')) {
55391      $submit.html(text);
55392    } else {
55393      $submit.val(text);
55394    }
55395
55396    if ($(form).attr('data-remote')) {
55397      $.rails.handleRemote($(form));
55398    } else {
55399      form.submit();
55400    }
55401  }
55402};
55403
55404if (window.nestedFormEvents) {
55405  window.nestedFormEvents.insertFields = function (content, assoc, link) {
55406    var target = $(link).data('target');
55407    var closestParent = $(link).data('closest-parent');
55408    if (target && closestParent) {
55409      return $(link).closest(closestParent).find(target).append(content);
55410    } else if (target) {
55411      return $(content).appendTo($(target));
55412    } else {
55413      return $(content).insertBefore(link);
55414    }
55415  };
55416}
55417
55418window.Modal = {
55419  open: function open($sel) {
55420    $sel.addClass('is-active');
55421    $('body').addClass('modal-open');
55422  },
55423
55424  close: function close($sel) {
55425    $sel.removeClass('is-active');
55426    $('body').removeClass('modal-open');
55427  }
55428};
55429
55430$.fn.currency = function () {
55431  return this.each(function () {
55432    $(this).inputmask('Regex', { regex: '[0-9]+\\.[0-9]{2}' });
55433  });
55434};
55435
55436$.fn.numeric = function () {
55437  return this.each(function () {
55438    $(this).inputmask('Regex', { regex: '[0-9]+' });
55439  });
55440};
55441
55442var closeMessage = function closeMessage() {
55443  var closeTrigger = $('[data-flash-message-close]');
55444
55445  closeTrigger.bind('click', function (e) {
55446    e.preventDefault();
55447    $(this).parent().fadeOut(200);
55448  });
55449};
55450
55451$(function () {
55452  closeMessage();
55453});
55454tinymce.PluginManager.add('charactercount', function (editor) {
55455  var self = this;
55456
55457  function update() {
55458    editor.theme.panel.find('#charactercount').text(display(self.getCount(), getMaxLength()));
55459  }
55460
55461  function display(count, max) {
55462    return ['Characters: {0} / {1}', count, max];
55463  }
55464
55465  function getMaxLength() {
55466    if (editor.settings.charactercount_maxlength != undefined) {
55467      return editor.settings.charactercount_maxlength;
55468    }
55469
55470    return $(editor.getElement()).attr('data-editor-maxlength') || tinyMCE.textareaMaxlengthDefault;
55471  }
55472
55473  function decodeHtml(html) {
55474    var txt = document.createElement("textarea");
55475    txt.innerHTML = html;
55476    return txt.value;
55477  }
55478
55479  self.getCount = function () {
55480    var tx = editor.getContent({ format: 'raw' });
55481    var decoded = decodeHtml(tx);
55482    var decodedStripped = decoded.replace(/(<([^>]+)>)/ig, "").trim();
55483    decodedStripped = decodedStripped.replace(/\s\s+/g, ' ');
55484    var tc = decodedStripped.length;
55485    return tc;
55486  };
55487
55488  editor.getCharacterCount = function() {
55489    return self.getCount();
55490  }
55491
55492  editor.on('init', function () {
55493    if(editor.settings.charactercount_enabled === false) {
55494      return;
55495    }
55496
55497    if(editor.settings.charactercount_enabled === undefined && $(editor.getElement()).attr('data-editor-maxlength') == undefined) {
55498      return;
55499    }
55500
55501    var statusbar = editor.theme.panel && editor.theme.panel.find('#statusbar')[0];
55502
55503    if (statusbar) {
55504      window.setTimeout(function () {
55505        statusbar.insert({
55506          type: 'label',
55507          name: 'charactercount',
55508          text: display(self.getCount(), getMaxLength()),
55509          classes: 'charactercount',
55510          disabled: editor.settings.readonly
55511        }, 0);
55512
55513        editor.on('setcontent beforeaddundo', update);
55514
55515        editor.on('keyUp', function (e) {
55516          update();
55517        });
55518      }, 0);
55519    }
55520  });
55521});
55522'use strict';
55523
55524var _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; }; })();
55525
55526function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }
55527
55528var ConditionalInput = (function () {
55529  function ConditionalInput(args) {
55530    _classCallCheck(this, ConditionalInput);
55531
55532    if (args.control.length == 0) return;
55533
55534    this.args = args;
55535    this.$control = this.args.control;
55536    this.inputType = this.args.inputType || 'text';
55537    this.setVisible(this.inputValue());
55538    this.$control.on('change', this.onChange.bind(this));
55539  }
55540
55541  _createClass(ConditionalInput, [{
55542    key: 'onChange',
55543    value: function onChange(e) {
55544      var value = this.inputValue(this.$control);
55545      this.setVisible(value);
55546    }
55547  }, {
55548    key: 'setVisible',
55549    value: function setVisible(value) {
55550      for (var controlValue in this.args.visibility) {
55551        var possibleValues = controlValue.split('|');
55552        if (possibleValues.indexOf(value) != -1) {
55553          this.args.visibility[controlValue].show();
55554        } else {
55555          this.args.visibility[controlValue].hide();
55556        }
55557      }
55558    }
55559  }, {
55560    key: 'inputValue',
55561    value: function inputValue() {
55562      var value = '';
55563      switch (this.inputType) {
55564        case 'checkbox':
55565          value = this.$control.is(':checked') ? 'on' : 'off';
55566          break;
55567        case 'radio':
55568          value = this.$control.filter(':checked').val();
55569          break;
55570        default:
55571          value = this.$control.val();
55572      }
55573      return value;
55574    }
55575  }]);
55576
55577  return ConditionalInput;
55578})();
55579'use strict';
55580
55581var _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; }; })();
55582
55583function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }
55584
55585var DonorFormValidator = (function () {
55586  function DonorFormValidator(args) {
55587    var _this = this;
55588
55589    _classCallCheck(this, DonorFormValidator);
55590
55591    this.$form = args.form;
55592    this.submitHandler = args.submitHandler;
55593
55594    var formHasBeenValidated = false;
55595
55596    var formOptions = $.extend({}, formValidateDefaults, {
55597      rules: {
55598        'donation[gift_name_other]': {
55599          required: function required() {
55600            return _this.isGiftOther();
55601          }
55602        },
55603        'donation[state]': {
55604          required: function required() {
55605            return _this.stateRequired();
55606          }
55607        },
55608        'donation[postal]': {
55609          required: function required() {
55610            return _this.postalRequired();
55611          }
55612        },
55613        'donation[cc_number]': {
55614          required: function required() {
55615            return _this.isCreditCard();
55616          },
55617          creditcard: true
55618        },
55619        'donation[cc_code]': {
55620          required: function required() {
55621            return _this.isCreditCard();
55622          }
55623        },
55624
55625        'donation[check_account_number]': {
55626          required: function required() {
55627            return _this.isEcheck();
55628          }
55629        },
55630        'donation[check_routing_number]': {
55631          required: function required() {
55632            return _this.isEcheck();
55633          },
55634          minlength: 9
55635        },
55636        'donation[payment_method_check_name]': {
55637          required: function required() {
55638            return _this.isEcheck();
55639          }
55640        },
55641        'donation[payment_method_bank_name]': {
55642          required: function required() {
55643            return _this.isEcheck();
55644          }
55645        },
55646        'donation[donation_amount_custom]': {
55647          min: 1
55648        },
55649
55650        'donation[tribute_attributes][tribute_name]': {
55651          required: function required() {
55652            return _this.tributeEnabled();
55653          }
55654        },
55655        'donation[tribute_attributes][email]': {
55656          required: function required() {
55657            return _this.tributeEnabled() && _this.tributeIsEmail();
55658          }
55659        },
55660        'donation[tribute_attributes][first_name]': {
55661          required: function required() {
55662            return _this.tributeEnabled() && _this.tributeIsSnailmail();
55663          }
55664        },
55665        'donation[tribute_attributes][last_name]': {
55666          required: function required() {
55667            return _this.tributeEnabled() && _this.tributeIsSnailmail();
55668          }
55669        },
55670        'donation[tribute_attributes][address_1]': {
55671          required: function required() {
55672            return _this.tributeEnabled() && _this.tributeIsSnailmail();
55673          }
55674        },
55675        'donation[tribute_attributes][city]': {
55676          required: function required() {
55677            return _this.tributeEnabled() && _this.tributeIsSnailmail();
55678          }
55679        },
55680        'donation[tribute_attributes][state]': {
55681          required: function required() {
55682            return _this.tributeEnabled() && _this.tributeIsSnailmail();
55683          }
55684        },
55685        'donation[tribute_attributes][postal]': {
55686          required: function required() {
55687            return _this.tributeEnabled() && _this.tributeIsSnailmail();
55688          }
55689        },
55690        'donation[tribute_attributes][country]': {
55691          required: function required() {
55692            return _this.tributeEnabled() && _this.tributeIsSnailmail();
55693          }
55694        },
55695        'hidden_recaptcha': {
55696          required: function required() {
55697            if (grecaptcha.getResponse() == '') {
55698              return true;
55699            } else {
55700              return false;
55701            }
55702          }
55703        }
55704      },
55705
55706      messages: {
55707        'donation[donation_amount_custom]': {
55708          required: 'Amount is required',
55709          min: 'Amount must be at least $1.00'
55710        },
55711        'donation[first_name]': 'First Name is required',
55712        'donation[last_name]': 'Last Name is required',
55713        'donation[email]': {
55714          required: 'Valid email is required',
55715          email: 'Valid email is required'
55716        },
55717        'donation[address_1]': 'Address 1 is required',
55718        'donation[city]': 'City is required',
55719        'donation[state]': 'State / Province is required',
55720        'donation[postal]': 'Zip / Postal Code is required',
55721
55722        'donation[gift_name_other]': 'Other Gift Designation is required',
55723
55724        'donation[tribute_attributes][tribute_name]': 'Honoree Name is required',
55725        'donation[tribute_attributes][first_name]': 'Recipient First Name is required',
55726        'donation[tribute_attributes][last_name]': 'Recipient Last Name is required',
55727        'donation[tribute_attributes][email]': {
55728          required: 'Valid Recipient Email is required',
55729          email: 'Valid Recipient Email is required'
55730        },
55731        'donation[tribute_attributes][address_1]': 'Recipient Address 1 is required',
55732        'donation[tribute_attributes][city]': 'Recipient City is required',
55733        'donation[tribute_attributes][state]': 'Recipient State / Province is required',
55734        'donation[tribute_attributes][postal]': 'Recipient Zip / Postal Code is required',
55735
55736        'donation[cc_number]': 'Valid Credit Card Number is required',
55737        'donation[cc_expiry_month]': 'Credit Card Expiration Month is required',
55738        'donation[cc_expiry_year]': 'Credit Card Expiration Year is required',
55739        'donation[cc_code]': 'Credit Card CVV Code is required',
55740        'donation[check_account_number]': 'Valid Bank Account Number is required',
55741        'donation[check_routing_number]': 'Valid Bank Routing Number is required',
55742        'donation[payment_method_check_name]': 'Name on Bank Account is required',
55743        'donation[payment_method_bank_name]': 'Bank Name is required',
55744        'donation[google_pay_token]': 'Google Pay button must be clicked before submitting',
55745        'hidden_recaptcha': 'To prove that you are a human, the CAPTCHA box must be checked'
55746      },
55747
55748      focusInvalid: false,
55749
55750      invalidHandler: function invalidHandler(event, validator) {
55751        formHasBeenValidated = true;
55752
55753        var errors = validator.errorList.map(function (error) {
55754          return '<li>' + error.message + '</li>';
55755        });
55756
55757        var message = 'One or more fields are incomplete or incorrect. Please correct the following items: ';
55758        message += '<ul>' + errors.join('') + '</ul>';
55759        $('#error-messages p').html(message);
55760        $('#error-messages').show();
55761
55762        $('html, body').animate({ scrollTop: 0 }, 200);
55763      },
55764
55765      submitHandler: function submitHandler(form, event) {
55766        return regeneratorRuntime.async(function submitHandler$(context$3$0) {
55767          while (1) switch (context$3$0.prev = context$3$0.next) {
55768            case 0:
55769              event.preventDefault();
55770
55771              this.submitHandler(form, event);
55772
55773            case 2:
55774            case 'end':
55775              return context$3$0.stop();
55776          }
55777        }, null, _this);
55778      }
55779    });
55780
55781    this.$form.validate(formOptions);
55782
55783    this.$form.find('#donation_country').on('change', function () {
55784      if (formHasBeenValidated) {
55785        _this.$form.find('#donation_postal').valid();
55786      }
55787    });
55788  }
55789
55790  _createClass(DonorFormValidator, [{
55791    key: 'isCreditCard',
55792    value: function isCreditCard() {
55793      return this.paymentMethodValue() == 'credit_card';
55794    }
55795  }, {
55796    key: 'isEcheck',
55797    value: function isEcheck() {
55798      return this.paymentMethodValue() == 'echeck';
55799    }
55800  }, {
55801    key: 'isGiftOther',
55802    value: function isGiftOther() {
55803      return this.$form.find('#donation_gift_name').val() == 'Other';
55804    }
55805  }, {
55806    key: 'isGooglePay',
55807    value: function isGooglePay() {
55808      return this.paymentMethodValue() == 'google_pay';
55809    }
55810  }, {
55811    key: 'paymentMethodValue',
55812    value: function paymentMethodValue() {
55813      if (this.$form.find('[name="donation[payment_method_type]"]:radio').length > 0) {
55814        return this.$form.find('[name="donation[payment_method_type]"]:checked').val();
55815      }
55816      return this.$form.find('[name="donation[payment_method_type]"]').val();
55817    }
55818  }, {
55819    key: 'tributeEnabled',
55820    value: function tributeEnabled() {
55821      return this.$form.find('[name="donation[tribute_enabled]"]').is(':checked');
55822    }
55823  }, {
55824    key: 'tributeIsEmail',
55825    value: function tributeIsEmail() {
55826      return this.$form.find('#donation_tribute_attributes_notification_type_email').is(':checked');
55827    }
55828  }, {
55829    key: 'tributeIsSnailmail',
55830    value: function tributeIsSnailmail() {
55831      return this.$form.find('#donation_tribute_attributes_notification_type_snailmail').is(':checked');
55832    }
55833  }, {
55834    key: 'stateRequired',
55835    value: function stateRequired() {
55836      return ['US', 'CA'].indexOf(this.$form.find('#donation_country').val()) != -1;
55837    }
55838  }, {
55839    key: 'postalRequired',
55840    value: function postalRequired() {
55841      return ['US', 'CA'].indexOf(this.$form.find('#donation_country').val()) != -1;
55842    }
55843  }]);
55844
55845  return DonorFormValidator;
55846})();
55847'use strict';
55848
55849var _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; }; })();
55850
55851function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }
55852
55853var CountrySelector = (function () {
55854  function CountrySelector(args) {
55855    _classCallCheck(this, CountrySelector);
55856
55857    this.$country = args.country;
55858    this.stateInputName = args.stateInputName;
55859    this.stateContainer = args.stateContainer, $(document).on('change', this.$country, this.onCountryChange.bind(this));
55860  }
55861
55862  _createClass(CountrySelector, [{
55863    key: 'onCountryChange',
55864    value: function onCountryChange(e) {
55865      this.checkState();
55866    }
55867  }, {
55868    key: 'checkState',
55869    value: function checkState() {
55870      var country = $(this.$country).val();
55871      $.get('/api/country_selector', {
55872        country: country,
55873        state_input_name: this.stateInputName,
55874        state_container: this.stateContainer
55875      });
55876    }
55877  }]);
55878
55879  return CountrySelector;
55880})();
55881'use strict';
55882
55883var _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; }; })();
55884
55885function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }
55886
55887var LabelSwitcher = (function () {
55888  function LabelSwitcher(args) {
55889    _classCallCheck(this, LabelSwitcher);
55890
55891    this.$control = args.control;
55892    this.$label = args.label;
55893    this.labelValueDefault = args.labelValueDefault;
55894    this.labelValues = args.labelValues;
55895
55896    $(document).on('change', this.$control, this.onChangeControl.bind(this));
55897    this.setLabel();
55898  }
55899
55900  _createClass(LabelSwitcher, [{
55901    key: 'onChangeControl',
55902    value: function onChangeControl(e) {
55903      this.setLabel();
55904    }
55905  }, {
55906    key: 'setLabel',
55907    value: function setLabel() {
55908      var controlValue = $(this.$control).val();
55909      var index = Object.keys(this.labelValues).indexOf(controlValue);
55910      if (index > -1) {
55911        var labelValue = this.labelValues[controlValue];
55912        $(this.$label).html(labelValue);
55913      } else {
55914        $(this.$label).html(this.labelValueDefault);
55915      }
55916    }
55917  }]);
55918
55919  return LabelSwitcher;
55920})();
55921'use strict';
55922
55923var _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; }; })();
55924
55925function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }
55926
55927var PlaceholderSwitcher = (function () {
55928  function PlaceholderSwitcher(args) {
55929    _classCallCheck(this, PlaceholderSwitcher);
55930
55931    this.$control = args.control;
55932    this.$input = args.input;
55933    this.valueDefault = args.valueDefault;
55934    this.values = args.values;
55935
55936    $(document).on('change', this.$control, this.onChangeControl.bind(this));
55937    this.setPlaceholder();
55938  }
55939
55940  _createClass(PlaceholderSwitcher, [{
55941    key: 'onChangeControl',
55942    value: function onChangeControl(e) {
55943      this.setPlaceholder();
55944    }
55945  }, {
55946    key: 'setPlaceholder',
55947    value: function setPlaceholder() {
55948      var controlValue = $(this.$control).val();
55949      var index = Object.keys(this.values).indexOf(controlValue);
55950      if (index > -1) {
55951        var placeholderValue = this.values[controlValue];
55952        $(this.$input).prop('placeholder', placeholderValue);
55953      } else {
55954        $(this.$input).prop('placeholder', this.valueDefault);
55955      }
55956    }
55957  }]);
55958
55959  return PlaceholderSwitcher;
55960})();
55961'use strict';
55962
55963var _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; }; })();
55964
55965function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }
55966
55967var Tabs = function Tabs() {
55968  _classCallCheck(this, Tabs);
55969
55970  $('.tabs[data-tab-group]').each(function (index, el) {
55971    new Tab($(el));
55972  });
55973};
55974
55975var Tab = (function () {
55976  function Tab($el) {
55977    var options = arguments.length <= 1 || arguments[1] === undefined ? {} : arguments[1];
55978
55979    _classCallCheck(this, Tab);
55980
55981    this.$el = $el;
55982    this.tabGroup = this.$el.attr('data-tab-group');
55983    this.$tabGroup = $('.tab-group[data-tab-group="' + this.tabGroup + '"]');
55984    this.activeClass = options.activeClass || 'is-active';
55985    this.$el.find('.tabs__item').on('click', this.onTabClick.bind(this));
55986
55987    var $firstTab = this.$el.find('.tabs__item').first();
55988    this.setActiveTab($firstTab);
55989    this.setActiveTabContent($firstTab);
55990  }
55991
55992  _createClass(Tab, [{
55993    key: 'onTabClick',
55994    value: function onTabClick(e) {
55995      e.preventDefault();
55996      var $tab = $(e.currentTarget);
55997
55998      if (this.isActive($tab)) {
55999        return;
56000      }
56001
56002      this.setActiveTab($tab);
56003      this.setActiveTabContent($tab);
56004    }
56005  }, {
56006    key: 'setActiveTab',
56007    value: function setActiveTab($tab) {
56008      this.$el.find('.tabs__item').removeClass(this.activeClass);
56009      $tab.addClass(this.activeClass);
56010    }
56011  }, {
56012    key: 'setActiveTabContent',
56013    value: function setActiveTabContent($tab) {
56014      var tabValue = $tab.attr('data-tab');
56015      this.$tabGroup.find('[data-tab]').removeClass(this.activeClass);
56016      this.$tabGroup.find('[data-tab="' + tabValue + '"]').addClass(this.activeClass);
56017    }
56018  }, {
56019    key: 'isActive',
56020    value: function isActive($tab) {
56021      return $tab.hasClass(this.activeClass);
56022    }
56023  }]);
56024
56025  return Tab;
56026})();
56027'use strict';
56028
56029function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }
56030
56031var ModalHandler = function ModalHandler() {
56032  _classCallCheck(this, ModalHandler);
56033
56034  $(document).on('click', '[data-modal-trigger]', function (e) {
56035    e.preventDefault();
56036    var selector = $(e.currentTarget).attr('data-modal-trigger');
56037    Modal.open($(selector));
56038  });
56039
56040  $(document).on('click', '.modal-overlay, .modal-close, [data-modal-close]', function (e) {
56041    e.preventDefault();
56042    Modal.close($(e.currentTarget).parents('.modal'));
56043  });
56044
56045  $(document).on('keydown', function (event) {
56046    if (event.key == 'Escape') {
56047      Modal.close($('.modal'));
56048    }
56049  });
56050};
56051'use strict';
56052
56053var _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; }; })();
56054
56055function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }
56056
56057var FundraiserSearch = (function () {
56058  function FundraiserSearch(args) {
56059    _classCallCheck(this, FundraiserSearch);
56060
56061    this.$control = $(args.control);
56062    if (this.$control.length === 0) {
56063      return;
56064    }
56065    this.setup();
56066  }
56067
56068  _createClass(FundraiserSearch, [{
56069    key: 'setup',
56070    value: function setup() {
56071      var searchUrl = this.$control.attr('data-search-url');
56072
56073      if (!searchUrl) {
56074        var msg = 'FundraiserSearch requires the attribute `data-search-url` ';
56075        msg += 'to be defined on the search input. ';
56076        msg += 'This value must point to a search endpoint';
56077        console.error(msg);
56078        return;
56079      }
56080
56081      this.$control.autocomplete({
56082        serviceUrl: searchUrl,
56083        onSelect: function onSelect(suggestion) {
56084          var redirectUrl = suggestion.data.url;
56085          if (redirectUrl) {
56086            window.location = redirectUrl;
56087          } else {
56088            var msg = 'FundraiserSearch error: no `url` was provided in ';
56089            msg += 'the `data` payload for the selected search result.';
56090            console.error(msg);
56091          }
56092        }
56093      });
56094    }
56095  }]);
56096
56097  return FundraiserSearch;
56098})();
56099'use strict';
56100
56101var _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; }; })();
56102
56103function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }
56104
56105var MoreLoader = (function () {
56106  function MoreLoader(args) {
56107    _classCallCheck(this, MoreLoader);
56108
56109    this.$control = $(args.control);
56110    this.$container = $(args.container);
56111
56112    if (this.$control.length === 0 || this.$container.length === 0) {
56113      return;
56114    }
56115
56116    this.endpoint = args.endpoint;
56117    this.totalPages = args.totalPages;
56118    this.requestType = args.requestType || 'GET';
56119    this.currentPage = args.currentPage || 1;
56120    this.loadingText = args.loadingText || 'Loading...';
56121    this.originalText = this.$control.html();
56122
56123    this.$control.on('click', this.onClickTrigger.bind(this));
56124  }
56125
56126  _createClass(MoreLoader, [{
56127    key: 'onClickTrigger',
56128    value: function onClickTrigger(e) {
56129      var _this = this;
56130
56131      e.preventDefault();
56132
56133      this.disableControl();
56134
56135      $.ajax({
56136        url: this.endpoint,
56137        type: this.requestType,
56138        data: { page: this.getNextPage() }
56139      }).done(function (response) {
56140        if (response) {
56141          _this.$container.append(response);
56142        } else {
56143          _this.hideControl();
56144        }
56145      }).fail(function (error) {
56146        console.error('MoreLoader error: ' + error);
56147      }).always(function () {
56148        _this.enableControl();
56149        _this.checkForLastPage();
56150      });
56151    }
56152  }, {
56153    key: 'getNextPage',
56154    value: function getNextPage() {
56155      return ++this.currentPage;
56156    }
56157  }, {
56158    key: 'disableControl',
56159    value: function disableControl() {
56160      this.$control.prop('disabled', true).html(this.loadingText);
56161    }
56162  }, {
56163    key: 'enableControl',
56164    value: function enableControl() {
56165      this.$control.prop('disabled', false).html(this.originalText);
56166    }
56167  }, {
56168    key: 'hideControl',
56169    value: function hideControl() {
56170      this.$control.hide();
56171    }
56172  }, {
56173    key: 'checkForLastPage',
56174    value: function checkForLastPage() {
56175      if (this.currentPage >= this.totalPages) {
56176        this.hideControl();
56177      }
56178    }
56179  }]);
56180
56181  return MoreLoader;
56182})();
56183'use strict';
56184
56185var _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; }; })();
56186
56187function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }
56188
56189var PasswordShow = (function () {
56190  function PasswordShow(args) {
56191    _classCallCheck(this, PasswordShow);
56192
56193    this.$passwordField = $(args.passwordField);
56194    this.$showControl = $(args.showControl);
56195
56196    if (this.$passwordField.length === 0 || this.$showControl.length === 0) {
56197      return;
56198    }
56199
56200    this.$showControl.on('click', this.onClickShowControl.bind(this));
56201  }
56202
56203  _createClass(PasswordShow, [{
56204    key: 'onClickShowControl',
56205    value: function onClickShowControl(e) {
56206      e.preventDefault();
56207
56208      var currentType = this.$passwordField.attr('type');
56209      if (currentType === 'password') {
56210        this.$passwordField.attr('type', 'text');
56211        this.$showControl.html('Hide');
56212      } else {
56213        this.$passwordField.attr('type', 'password');
56214        this.$showControl.html('Show');
56215      }
56216    }
56217  }]);
56218
56219  return PasswordShow;
56220})();
56221'use strict';
56222
56223var _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; }; })();
56224
56225function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }
56226
56227var BillingInfoCookie = (function () {
56228  function BillingInfoCookie(args) {
56229    _classCallCheck(this, BillingInfoCookie);
56230
56231    this.modelName = args.modelName;
56232    this.initialize();
56233  }
56234
56235  _createClass(BillingInfoCookie, [{
56236    key: 'initialize',
56237    value: function initialize() {
56238      var _this = this;
56239
56240      $(document).on('click', '[data-clear-billing-info]', function (event) {
56241        event.preventDefault();
56242
56243        var cookie = Cookies.get('fagc_billing_info');
56244
56245        if (!cookie) {
56246          return;
56247        }
56248
56249        $('[data-clear-billing-info-wrapper]').hide();
56250
56251        $.each(JSON.parse(cookie), function (inputName) {
56252          if (inputName === 'country') {
56253            $('[name="' + _this.modelName + '[' + inputName + ']"]').val('US').change();
56254          } else {
56255            $('[name="' + _this.modelName + '[' + inputName + ']"]').val('');
56256          }
56257        });
56258
56259        Cookies.remove('fagc_billing_info');
56260      });
56261    }
56262  }]);
56263
56264  return BillingInfoCookie;
56265})();
56266'use strict';
56267
56268var _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; }; })();
56269
56270function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }
56271
56272var GooglePay = (function () {
56273  function GooglePay(args) {
56274    _classCallCheck(this, GooglePay);
56275
56276    this.gateway = args.gateway;
56277    this.key = args.key;
56278    this.env = args.env;
56279    this.merchantId = args.merchantId;
56280    this.paymentsClient = null;
56281    this.tokenField = args.tokenField;
56282    this.lastDigitsField = args.lastDigitsField;
56283    this.expiryMonthField = args.expiryMonthField;
56284    this.expiryYearField = args.expiryYearField;
56285    this.nonprofitName = args.nonprofitName;
56286    this.onSubmit = args.onSubmit;
56287    this.getTotalAmount = args.getTotalAmount;
56288    this.$form = args.$form;
56289
56290    this.baseRequest = {
56291      apiVersion: 2,
56292      apiVersionMinor: 0
56293    };
56294
56295    this.stripeTokenizationSpecification = {
56296      type: 'PAYMENT_GATEWAY',
56297      parameters: {
56298        'gateway': 'stripe',
56299        "stripe:version": "2018-10-31",
56300        'stripe:publishableKey': this.key
56301      }
56302    };
56303
56304    this.authorizeNetTokenizationSpecification = {
56305      type: 'PAYMENT_GATEWAY',
56306      parameters: {
56307        'gateway': 'authorizenet',
56308        'gatewayMerchantId': this.key
56309      }
56310    };
56311
56312    this.allowedCardNetworks = ["AMEX", "DISCOVER", "INTERAC", "JCB", "MASTERCARD", "VISA"];
56313    this.allowedCardAuthMethods = ["PAN_ONLY", "CRYPTOGRAM_3DS"];
56314
56315    this.baseCardPaymentMethod = {
56316      type: 'CARD',
56317      parameters: {
56318        allowedAuthMethods: this.allowedCardAuthMethods,
56319        allowedCardNetworks: this.allowedCardNetworks
56320      }
56321    };
56322
56323    this.cardPaymentMethod = Object.assign({ tokenizationSpecification: this.getTokenizationSpecification() }, this.baseCardPaymentMethod);
56324  }
56325
56326  _createClass(GooglePay, [{
56327    key: 'checkVisibility',
56328    value: function checkVisibility(args) {
56329      var paymentsClient = this.getGooglePaymentsClient();
56330      paymentsClient.isReadyToPay(this.getGoogleIsReadyToPayRequest()).then(function (response) {
56331        if (response.result) {
56332          args.onSuccess();
56333        }
56334      })['catch'](function (err) {
56335        console.error(err);
56336      });
56337    }
56338  }, {
56339    key: 'getGoogleIsReadyToPayRequest',
56340    value: function getGoogleIsReadyToPayRequest() {
56341      return Object.assign({}, this.baseRequest, {
56342        allowedPaymentMethods: [this.baseCardPaymentMethod]
56343      });
56344    }
56345  }, {
56346    key: 'getGooglePaymentsClient',
56347    value: function getGooglePaymentsClient() {
56348      if (this.paymentsClient === null) {
56349        this.paymentsClient = new google.payments.api.PaymentsClient({ environment: this.env });
56350      }
56351      return this.paymentsClient;
56352    }
56353  }, {
56354    key: 'onGooglePaymentButtonClicked',
56355    value: function onGooglePaymentButtonClicked() {
56356      var _this = this;
56357
56358      var paymentDataRequest = this.getGooglePaymentDataRequest();
56359      paymentDataRequest.transactionInfo = this.getGoogleTransactionInfo();
56360
56361      var paymentsClient = this.getGooglePaymentsClient();
56362      paymentsClient.loadPaymentData(paymentDataRequest).then(function (paymentData) {
56363        _this.processPayment(paymentData);
56364      })['catch'](function (err) {
56365        console.error(err);
56366      });
56367    }
56368  }, {
56369    key: 'getGooglePaymentDataRequest',
56370    value: function getGooglePaymentDataRequest() {
56371      var paymentDataRequest = Object.assign({}, this.baseRequest);
56372      paymentDataRequest.allowedPaymentMethods = [this.cardPaymentMethod];
56373      paymentDataRequest.transactionInfo = this.getGoogleTransactionInfo();
56374      paymentDataRequest.merchantInfo = {
56375        merchantName: this.nonprofitName,
56376        merchantId: this.merchantId
56377      };
56378      return paymentDataRequest;
56379    }
56380  }, {
56381    key: 'getGoogleTransactionInfo',
56382    value: function getGoogleTransactionInfo() {
56383      return {
56384        countryCode: 'US',
56385        currencyCode: 'USD',
56386        totalPriceStatus: 'FINAL',
56387        totalPrice: '' + this.getTotalAmount()
56388      };
56389    }
56390  }, {
56391    key: 'processPayment',
56392    value: function processPayment(paymentData) {
56393      var finalToken = null;
56394      var paymentToken = paymentData.paymentMethodData.tokenizationData.token;
56395
56396      switch (this.gateway) {
56397        case 'stripe':
56398          var parsedToken = JSON.parse(paymentToken);
56399          finalToken = parsedToken.id;
56400          $('#' + this.expiryMonthField).val(parsedToken.card.exp_month);
56401          $('#' + this.expiryYearField).val(parsedToken.card.exp_year);
56402          break;
56403
56404        case 'authorize_net':
56405          finalToken = window.btoa(paymentToken);
56406          break;
56407      }
56408
56409      var cardDetails = paymentData.paymentMethodData.info.cardDetails;
56410
56411      $('#' + this.lastDigitsField).val(cardDetails);
56412      $('#' + this.tokenField).val(finalToken);
56413
56414      this.onSubmit();
56415    }
56416  }, {
56417    key: 'getTokenizationSpecification',
56418    value: function getTokenizationSpecification() {
56419      switch (this.gateway) {
56420        case 'authorize_net':
56421          return this.authorizeNetTokenizationSpecification;
56422
56423        case 'stripe':
56424          return this.stripeTokenizationSpecification;
56425      }
56426    }
56427  }]);
56428
56429  return GooglePay;
56430})();
56431'use strict';
56432
56433var _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; }; })();
56434
56435function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }
56436
56437var ApplePay = (function () {
56438  function ApplePay(args) {
56439    _classCallCheck(this, ApplePay);
56440
56441    this.applePaymentSessionUrl = args.applePaymentSessionUrl;
56442    this.appleProcessUrl = args.appleProcessUrl;
56443    this.tokenField = args.tokenField;
56444    this.lastDigitsField = args.lastDigitsField;
56445    this.nonprofitName = args.nonprofitName;
56446    this.onSubmit = args.onSubmit;
56447    this.merchantIdentifier = args.merchantIdentifier;
56448    this.getTotalAmount = args.getTotalAmount;
56449    this.$form = args.$form;
56450  }
56451
56452  _createClass(ApplePay, [{
56453    key: 'checkVisibility',
56454    value: function checkVisibility(args) {
56455      if (!window.ApplePaySession) {
56456        return;
56457      }
56458
56459      ApplePaySession.canMakePaymentsWithActiveCard(this.merchantIdentifier).then(function (canMakePayments) {
56460        console.log('[Apple Pay] Successful Apple Pay call to canMakePaymentsWithActiveCard');
56461        console.log('[Apple Pay] canMakePayments: ', canMakePayments);
56462        if (canMakePayments) {
56463          args.onSuccess();
56464        }
56465      })['catch'](function (err) {
56466        console.log('[Apple Pay] canMakePaymentsWithActiveCard returned an error');
56467        console.error(err);
56468      });
56469    }
56470  }, {
56471    key: 'onApplePayButtonClicked',
56472    value: function onApplePayButtonClicked() {
56473      var _this = this;
56474
56475      if (!window.ApplePaySession) {
56476        return;
56477      }
56478
56479      var request = {
56480        "countryCode": "US",
56481        "currencyCode": "USD",
56482        "merchantCapabilities": ["supports3DS"],
56483        "supportedNetworks": ["visa", "masterCard", "amex", "discover"],
56484        "total": {
56485          "label": this.nonprofitName,
56486          "type": "final",
56487          "amount": this.getTotalAmount()
56488        }
56489      };
56490
56491      var session = new ApplePaySession(3, request);
56492
56493      session.onvalidatemerchant = function callee$2$0(event) {
56494        var merchantSession;
56495        return regeneratorRuntime.async(function callee$2$0$(context$3$0) {
56496          while (1) switch (context$3$0.prev = context$3$0.next) {
56497            case 0:
56498              context$3$0.prev = 0;
56499
56500              console.log('[Apple Pay] validating merchant, session url:', this.applePaymentSessionUrl);
56501              context$3$0.next = 4;
56502              return regeneratorRuntime.awrap(this.validateMerchant());
56503
56504            case 4:
56505              merchantSession = context$3$0.sent;
56506
56507              console.log('[Apple Pay] merchant session:', merchantSession);
56508              session.completeMerchantValidation(merchantSession);
56509              context$3$0.next = 12;
56510              break;
56511
56512            case 9:
56513              context$3$0.prev = 9;
56514              context$3$0.t0 = context$3$0['catch'](0);
56515
56516              console.error('[Apple Pay] merchant validation failed:', context$3$0.t0);
56517
56518            case 12:
56519            case 'end':
56520              return context$3$0.stop();
56521          }
56522        }, null, _this, [[0, 9]]);
56523      };
56524
56525      session.onpaymentmethodselected = function (event) {
56526        console.log('[Apple Pay] onpaymentmethodselected:', event);
56527        try {
56528          var amount = _this.getTotalAmount();
56529          console.log('[Apple Pay] getTotalAmount:', amount);
56530
56531          var update = {
56532            newTotal: {
56533              type: 'final',
56534              label: _this.nonprofitName,
56535              amount: amount
56536            }
56537          };
56538
56539          session.completePaymentMethodSelection(update);
56540        } catch (err) {
56541          console.error('[Apple Pay] onpaymentmethodselected failed:', err);
56542        }
56543      };
56544
56545      session.onpaymentauthorized = function (event) {
56546        console.log('[Apple Pay] onpaymentauthorized:', event);
56547        try {
56548          var finalToken = JSON.stringify(event.payment.token.paymentData);
56549          var lastFour = event.payment.token.paymentMethod.displayName.slice(-4);
56550
56551          $('#' + _this.tokenField).val(finalToken);
56552          $('#' + _this.lastDigitsField).val(lastFour);
56553
56554          var result = {
56555            "status": ApplePaySession.STATUS_SUCCESS
56556          };
56557
56558          session.completePayment(result);
56559
56560          _this.onSubmit();
56561        } catch (err) {
56562          console.error('[Apple Pay] onpaymentauthorized failed:', err);
56563        }
56564      };
56565
56566      session.oncancel = function (event) {
56567        console.warn('[Apple Pay] session.oncancel fired - WebKit aborted the sheet:', event);
56568      };
56569
56570      console.log('[Apple Pay] page origin:', window.location.hostname);
56571      session.begin();
56572    }
56573  }, {
56574    key: 'validateMerchant',
56575    value: function validateMerchant() {
56576      var response;
56577      return regeneratorRuntime.async(function validateMerchant$(context$2$0) {
56578        while (1) switch (context$2$0.prev = context$2$0.next) {
56579          case 0:
56580            context$2$0.prev = 0;
56581            context$2$0.next = 3;
56582            return regeneratorRuntime.awrap($.ajax({ url: this.applePaymentSessionUrl }));
56583
56584          case 3:
56585            response = context$2$0.sent;
56586
56587            console.log('[Apple Pay] validateMerchant response:', response);
56588            return context$2$0.abrupt('return', response);
56589
56590          case 8:
56591            context$2$0.prev = 8;
56592            context$2$0.t0 = context$2$0['catch'](0);
56593
56594            console.error('[Apple Pay] validateMerchant ajax error:', context$2$0.t0);
56595            throw context$2$0.t0;
56596
56597          case 12:
56598          case 'end':
56599            return context$2$0.stop();
56600        }
56601      }, null, this, [[0, 8]]);
56602    }
56603  }]);
56604
56605  return ApplePay;
56606})();
56607'use strict';
56608
56609var _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; }; })();
56610
56611function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }
56612
56613var CommercePage = (function () {
56614  function CommercePage() {
56615    _classCallCheck(this, CommercePage);
56616
56617    this.initialize();
56618  }
56619
56620  _createClass(CommercePage, [{
56621    key: 'initialize',
56622    value: function initialize() {
56623      var _this = this;
56624
56625      // Product: "Show / Hide Info"
56626      $('[data-toggle-trigger="product-info"]').on('click', function (event) {
56627        event.preventDefault();
56628        var isShowing = $(this).attr('data-toggle-showing');
56629        var $parent = $(this).closest('[data-product-choice]');
56630        var $productInfo = $parent.find('[data-toggle="product-info"]');
56631
56632        if (isShowing === 'true') {
56633          $productInfo.hide();
56634          $(this).attr('data-toggle-showing', 'false').html('More Info');
56635        } else {
56636          $productInfo.show();
56637          $(this).attr('data-toggle-showing', 'true').html('Less Info');
56638        }
56639      });
56640
56641      // When any quantity value changes, update
56642      // quantity and cost counter.
56643      $('[data-product-cost]').on('change', function () {
56644        _this.updateTotalQuantity();
56645        _this.updateTotalCost();
56646      });
56647
56648      // When any quantity value changes, update
56649      // quantity and cost counter.
56650      $('#cart_donation').on('keyup', function (event) {
56651        _this.updateTotalCost();
56652      });
56653
56654      // Coupon visibility handler
56655      $('[data-coupon="toggle"]').on('click', function (event) {
56656        event.preventDefault();
56657        $(this).hide();
56658        $('.cart_coupon_code').show();
56659      });
56660
56661      // If a coupon code exists, show the input
56662      // and hide the control.
56663      if ($('#cart_coupon_code').val()) {
56664        $('[data-coupon="toggle"]').hide();
56665      } else {
56666        $('.cart_coupon_code').hide();
56667      }
56668
56669      $('.product-form').validate(window.formValidateDefaults);
56670
56671      this.updateTotalQuantity();
56672      this.updateTotalCost();
56673
56674      // Reveals the hidden description on small screens
56675      $('[data-commerce-page-description-read-more]').on('click', function (event) {
56676        event.preventDefault();
56677        $('[data-commerce-page-description-overlay]').hide();
56678        $('[data-commerce-page-description]').addClass('donate-body__expanded');
56679      });
56680    }
56681  }, {
56682    key: 'updateTotalQuantity',
56683    value: function updateTotalQuantity() {
56684      var totalQuantity = 0;
56685
56686      $('[data-product-cost]').each(function (index, el) {
56687        totalQuantity += parseInt($(this).val());
56688      });
56689
56690      var formattedQuantity = numeral(totalQuantity).format('0,0');
56691      $('[data-commerce-page-quantity]').html(formattedQuantity);
56692    }
56693  }, {
56694    key: 'updateTotalCost',
56695    value: function updateTotalCost() {
56696      var totalCost = 0;
56697
56698      $('[data-product-cost]').each(function (index, el) {
56699        var quantity = parseInt($(this).val());
56700        var cost = parseFloat($(this).attr('data-product-cost'));
56701        totalCost += quantity * cost;
56702      });
56703
56704      var donation = parseFloat($('#cart_donation').val());
56705      if (donation) {
56706        totalCost += donation;
56707      }
56708
56709      var formattedCost = numeral(totalCost).format('$0,0.00');
56710      $('[data-commerce-page-cost]').html(formattedCost);
56711    }
56712  }]);
56713
56714  return CommercePage;
56715})();
56716'use strict';
56717
56718var _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; }; })();
56719
56720function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }
56721
56722var OrderPage = (function () {
56723  function OrderPage(args) {
56724    _classCallCheck(this, OrderPage);
56725
56726    this.formHasBeenValidated = false;
56727    this.paymentFieldsUrl = args.paymentFieldsUrl;
56728    this.changeProcessingFeeUrl = args.changeProcessingFeeUrl;
56729    this.$form = $('#new_order');
56730    this.submitButtonSelector = '[data-donation-submit]';
56731    this.stripeApiKey = args.stripeApiKey;
56732    this.stripePaymentType = null;
56733    this.initialTotalInCents = args.initialTotalInCents;
56734    this.gateway = args.gateway;
56735    this.requiresTransaction = args.requiresTransaction;
56736    this.googlePayEnabled = args.googlePayEnabled;
56737    this.googlePayKey = args.googlePayKey;
56738    this.googlePayEnv = args.googlePayEnv;
56739    this.googlePayMerchantId = args.googlePayMerchantId;
56740    this.applePayEnabled = args.applePayEnabled;
56741    this.applePaymentSessionUrl = args.applePaymentSessionUrl;
56742    this.appleProcessUrl = args.appleProcessUrl;
56743    this.nonprofitName = args.nonprofitName;
56744    this.applePayMerchantIdentifier = args.applePayMerchantIdentifier;
56745    this.mailingFieldsEnabled = args.mailingFieldsEnabled;
56746    this.payPalEnabled = args.payPalEnabled;
56747    this.payPalOrderUrl = args.payPalOrderUrl;
56748
56749    this.initialize();
56750  }
56751
56752  _createClass(OrderPage, [{
56753    key: 'initialize',
56754    value: function initialize() {
56755      var _this = this;
56756
56757      $(document).on('change', '#order_processing_fee_included', function (event) {
56758        var form = $('#new_order').get(0);
56759
56760        $.ajax({
56761          url: _this.changeProcessingFeeUrl,
56762          method: 'post',
56763          dataType: 'script',
56764          data: new FormData(form),
56765          processData: false,
56766          contentType: false
56767        });
56768      });
56769
56770      $(document).on('payment-method-change', function (event, data) {
56771        if (data.paymentMethod == 'credit_card') {
56772          _this.initializeStripeFields();
56773        } else if (data.paymentMethod == 'paypal') {
56774          _this.initializePayPal();
56775        }
56776      });
56777
56778      $(document).on('payment-processing-fee-change', function (event, data) {
56779        if (data.paymentMethod == 'paypal') {
56780          _this.initializePayPal();
56781        }
56782      });
56783
56784      // Handles payment terms visibility
56785      new ConditionalInput({
56786        inputType: 'radio',
56787        control: $('[name="order[payment_method_type]"][type="radio"]'),
56788        visibility: {
56789          'invoice': $('[data-terms-unprocessed-payment]'),
56790          'credit_card|echeck': $('[data-terms-processed-payment]')
56791        }
56792      });
56793
56794      // Handles Order comment visibility
56795      new ConditionalInput({
56796        inputType: 'checkbox',
56797        control: $('#order_comment_enabled'),
56798        visibility: {
56799          'on': $('.form-item--comment-fields')
56800        }
56801      });
56802
56803      // Handles the switching of countries and showing
56804      // appropriate state / province
56805      new CountrySelector({
56806        country: '#order_country',
56807        stateInputName: 'order[state]',
56808        stateContainer: '.form-fieldset--donation-state'
56809      });
56810
56811      new LabelSwitcher({
56812        control: '#order_country',
56813        label: $('label[for="order_postal"]'),
56814        labelValueDefault: 'Postal Code',
56815        labelValues: {
56816          'US': 'Zip Code'
56817        }
56818      });
56819
56820      new PlaceholderSwitcher({
56821        control: '#order_country',
56822        input: '#order_postal',
56823        values: {
56824          'US': '',
56825          'CA': ''
56826        },
56827        valueDefault: 'optional'
56828      });
56829
56830      new BillingInfoCookie({ modelName: 'order' });
56831
56832      this.initializeCountryValidation();
56833      this.initializePaymentFieldSwitcher();
56834      this.initializeStripeFields();
56835      this.initializeGooglePay();
56836      this.initializeApplePay();
56837      this.initializePayPal();
56838      this.validateForm();
56839    }
56840  }, {
56841    key: 'disableButtons',
56842    value: function disableButtons(isDisabled) {
56843      var $submitButton = $(this.submitButtonSelector);
56844
56845      if (isDisabled) {
56846        $submitButton.prop('disabled', true);
56847      } else {
56848        $submitButton.prop('disabled', false);
56849      }
56850    }
56851  }, {
56852    key: 'disableScroll',
56853    value: function disableScroll() {
56854      $('body').addClass('no-scroll');
56855    }
56856  }, {
56857    key: 'initializeCountryValidation',
56858    value: function initializeCountryValidation() {
56859      var _this2 = this;
56860
56861      $('#order_country').on('change', function () {
56862        if (_this2.formHasBeenValidated) {
56863          $('#order_postal').valid();
56864        }
56865      });
56866    }
56867  }, {
56868    key: 'initializePaymentFieldSwitcher',
56869    value: function initializePaymentFieldSwitcher() {
56870      var _this3 = this;
56871
56872      $(document).on('change', '[name="order[payment_method_type]"]', function () {
56873        var form = $('#new_order').get(0);
56874
56875        $.ajax({
56876          url: _this3.paymentFieldsUrl,
56877          method: 'post',
56878          dataType: 'script',
56879          data: new FormData(form),
56880          processData: false,
56881          contentType: false
56882        });
56883      });
56884    }
56885  }, {
56886    key: 'initializeApplePay',
56887    value: function initializeApplePay() {
56888      var _this4 = this;
56889
56890      if (!this.applePayEnabled) {
56891        return;
56892      }
56893
56894      this.applePay = new ApplePay({
56895        applePaymentSessionUrl: this.applePaymentSessionUrl,
56896        appleProcessUrl: this.appleProcessUrl,
56897        totalAmount: this.totalAmount(),
56898        tokenField: 'order_apple_pay_token',
56899        lastDigitsField: 'order_payment_method_last_digits',
56900        nonprofitName: this.nonprofitName,
56901        merchantIdentifier: this.applePayMerchantIdentifier,
56902        $form: this.$form,
56903
56904        getTotalAmount: function getTotalAmount() {
56905          return _this4.totalAmount();
56906        },
56907
56908        onSubmit: function onSubmit() {
56909          _this4.submitForm();
56910        }
56911      });
56912
56913      this.applePay.checkVisibility({
56914        onSuccess: function onSuccess() {
56915          $('[data-payment-method-type="apple_pay"]').show();
56916          $('#order_apple_pay_available').val('true');
56917        }
56918      });
56919    }
56920  }, {
56921    key: 'initializeGooglePay',
56922    value: function initializeGooglePay() {
56923      var _this5 = this;
56924
56925      if (!this.googlePayEnabled) {
56926        return;
56927      }
56928
56929      this.googlePay = new GooglePay({
56930        gateway: this.gateway,
56931        key: this.googlePayKey,
56932        env: this.googlePayEnv,
56933        merchantId: this.googlePayMerchantId,
56934        tokenField: 'order_google_pay_token',
56935        lastDigitsField: 'order_payment_method_last_digits',
56936        expiryMonthField: 'order_cc_expiry_month',
56937        expiryYearField: 'order_cc_expiry_year',
56938        nonprofitName: this.nonprofitName,
56939        $form: this.$form,
56940
56941        getTotalAmount: function getTotalAmount() {
56942          return _this5.totalAmount();
56943        },
56944
56945        onSubmit: function onSubmit() {
56946          _this5.submitForm();
56947        }
56948      });
56949
56950      this.googlePay.checkVisibility({
56951        onSuccess: function onSuccess() {
56952          $('[data-payment-method-type="google_pay"]').show();
56953          $('#order_google_pay_available').val('true');
56954        }
56955      });
56956    }
56957  }, {
56958    key: 'initializePayPal',
56959    value: function initializePayPal() {
56960      var _this6 = this;
56961
56962      if (!this.payPalEnabled) {
56963        return;
56964      }
56965      if (!this.isPayPal()) {
56966        return;
56967      }
56968
56969      paypal.Buttons({
56970        style: {
56971          layout: 'horizontal',
56972          color: 'gold',
56973          shape: 'rect',
56974          label: 'pay',
56975          height: 55,
56976          borderRadius: 8,
56977          disableMaxWidth: true,
56978          tagline: false
56979        },
56980
56981        onClick: function onClick(data, actions) {
56982          var isValid = _this6.$form.valid();
56983
56984          if (!isValid) {
56985            return actions.reject();
56986          }
56987        },
56988
56989        createOrder: function createOrder() {
56990          var form, formData, response;
56991          return regeneratorRuntime.async(function createOrder$(context$3$0) {
56992            while (1) switch (context$3$0.prev = context$3$0.next) {
56993              case 0:
56994                form = this.$form.get(0);
56995                formData = new FormData(form);
56996                context$3$0.next = 4;
56997                return regeneratorRuntime.awrap($.ajax({
56998                  url: this.payPalOrderUrl,
56999                  method: 'post',
57000                  data: formData,
57001                  processData: false,
57002                  contentType: false
57003                }));
57004
57005              case 4:
57006                response = context$3$0.sent;
57007                return context$3$0.abrupt('return', response['id']);
57008
57009              case 6:
57010              case 'end':
57011                return context$3$0.stop();
57012            }
57013          }, null, _this6);
57014        },
57015
57016        onApprove: function onApprove(data) {
57017          return regeneratorRuntime.async(function onApprove$(context$3$0) {
57018            while (1) switch (context$3$0.prev = context$3$0.next) {
57019              case 0:
57020                $('#order_paypal_order_id').val(data.orderID);
57021
57022                this.disableButtons(true);
57023                this.disableScroll();
57024                this.showLoading();
57025
57026                if (window.executeV3RecaptchaAndSubmit) {
57027                  window.executeV3RecaptchaAndSubmit(this.$form.get(0));
57028                } else {
57029                  this.$form.get(0).submit();
57030                }
57031
57032              case 5:
57033              case 'end':
57034                return context$3$0.stop();
57035            }
57036          }, null, _this6);
57037        }
57038      }).render('#paypal-button-container');
57039    }
57040  }, {
57041    key: 'initializeStripeFields',
57042    value: function initializeStripeFields() {
57043      var _this7 = this;
57044
57045      if (this.gateway !== 'stripe') {
57046        return;
57047      }
57048      if (this.requiresTransaction == false) {
57049        return;
57050      }
57051      if (!this.isPaymentMethodTypeCreditCard()) {
57052        return;
57053      }
57054
57055      if (!this.stripeApiKey) {
57056        $('#stripe-card-error').show();
57057
57058        return;
57059      }
57060
57061      this.stripe = Stripe(this.stripeApiKey);
57062
57063      var options = {
57064        // We can use `payment` for every scenario (including subscriptions)
57065        // because only creating a payment method (not an actual payment)
57066        // does not require the type.
57067        mode: 'payment',
57068
57069        amount: this.initialTotalInCents,
57070        currency: 'usd',
57071        paymentMethodCreation: 'manual',
57072
57073        paymentMethodTypes: ['card'],
57074
57075        appearance: {
57076          theme: 'stripe',
57077
57078          variables: {
57079            fontFamily: '"DM Sans", Helvetica, arial, sans-serif',
57080            fontSizeBase: '16px',
57081            colorText: '#262626',
57082            colorDanger: '#ff0000'
57083          },
57084
57085          rules: {
57086            '.Label': {
57087              marginBottom: '4px',
57088              lineHeight: '24px'
57089            },
57090            '.Input': {
57091              borderRadius: '0.25rem',
57092              borderColor: '#6b7280',
57093              boxShadow: 'none',
57094              padding: '14px 12px',
57095              height: '48px'
57096            },
57097            '.Input:focus': {
57098              borderColor: '#2563eb',
57099              boxShadow: 'rgb(255, 255, 255) 0px 0px 0px 0px, rgb(37, 99, 235) 0px 0px 0px 1px, rgba(0, 0, 0, 0) 0px 0px 0px 0px',
57100              transition: 'none'
57101            },
57102            '.Input--invalid': {
57103              boxShadow: 'none'
57104            }
57105          }
57106        }
57107      };
57108
57109      this.stripeElements = this.stripe.elements(options);
57110
57111      this.stripePaymentElement = this.stripeElements.create('payment', {
57112        fields: {
57113          billingDetails: {
57114            address: {
57115              country: this.mailingFieldsEnabled ? 'never' : 'auto',
57116              postalCode: this.mailingFieldsEnabled ? 'never' : 'auto'
57117            }
57118          }
57119        },
57120        wallets: {
57121          applePay: 'never',
57122          googlePay: 'never'
57123        }
57124      });
57125
57126      this.stripePaymentElement.on('loaderror', function (event) {
57127        // event.error.message contains information
57128        $('#stripe-card-error').show();
57129      });
57130
57131      this.stripePaymentElement.on('change', function (event) {
57132        _this7.stripePaymentType = event.value.type;
57133      });
57134
57135      this.stripePaymentElement.mount('#stripe-card-field');
57136    }
57137  }, {
57138    key: 'isPaymentMethodTypeCreditCard',
57139    value: function isPaymentMethodTypeCreditCard() {
57140      return this.paymentMethodValue() === 'credit_card';
57141    }
57142  }, {
57143    key: 'isApplePay',
57144    value: function isApplePay() {
57145      return this.paymentMethodValue() == 'apple_pay';
57146    }
57147  }, {
57148    key: 'isGooglePay',
57149    value: function isGooglePay() {
57150      return this.paymentMethodValue() == 'google_pay';
57151    }
57152  }, {
57153    key: 'isPayPal',
57154    value: function isPayPal() {
57155      return this.paymentMethodValue() === 'paypal';
57156    }
57157  }, {
57158    key: 'isStripe',
57159    value: function isStripe() {
57160      return !!this.stripeApiKey && this.isPaymentMethodTypeCreditCard();
57161    }
57162  }, {
57163    key: 'paymentMethodValue',
57164    value: function paymentMethodValue() {
57165      if (this.$form.find('[name="order[payment_method_type]"]').is(':radio')) {
57166        return this.$form.find('[name="order[payment_method_type]"]:checked').val();
57167      } else {
57168        return this.$form.find('[name="order[payment_method_type]"]').val();
57169      }
57170    }
57171  }, {
57172    key: 'processStripe',
57173    value: function processStripe() {
57174      var validate, createPaymentMethodParams, _ref, error, paymentMethod, message;
57175
57176      return regeneratorRuntime.async(function processStripe$(context$2$0) {
57177        while (1) switch (context$2$0.prev = context$2$0.next) {
57178          case 0:
57179            context$2$0.next = 2;
57180            return regeneratorRuntime.awrap(this.stripeElements.submit());
57181
57182          case 2:
57183            validate = context$2$0.sent;
57184
57185            if (!validate.error) {
57186              context$2$0.next = 6;
57187              break;
57188            }
57189
57190            this.stripePaymentElement.focus();
57191
57192            return context$2$0.abrupt('return', { error: 'Invalid credit card' });
57193
57194          case 6:
57195            createPaymentMethodParams = {
57196              elements: this.stripeElements
57197            };
57198
57199            if (this.mailingFieldsEnabled) {
57200              createPaymentMethodParams['params'] = {
57201                billing_details: {
57202                  address: {
57203                    country: this.$form.find('#order_country').val(),
57204                    postal_code: this.$form.find('#order_postal').val()
57205                  }
57206                }
57207              };
57208            }
57209
57210            context$2$0.next = 10;
57211            return regeneratorRuntime.awrap(this.stripe.createPaymentMethod(createPaymentMethodParams));
57212
57213          case 10:
57214            _ref = context$2$0.sent;
57215            error = _ref.error;
57216            paymentMethod = _ref.paymentMethod;
57217
57218            if (!error) {
57219              context$2$0.next = 17;
57220              break;
57221            }
57222
57223            message = 'Could not accept credit card: ' + error.message;
57224
57225            alert(message);
57226
57227            return context$2$0.abrupt('return', { error: message });
57228
57229          case 17:
57230
57231            $('#order_stripe_payment_method_id').val(paymentMethod.id);
57232
57233            if (paymentMethod.type === 'card') {
57234              $('#order_payment_method_last_digits').val(paymentMethod.card.last4);
57235            }
57236
57237            return context$2$0.abrupt('return', {});
57238
57239          case 20:
57240          case 'end':
57241            return context$2$0.stop();
57242        }
57243      }, null, this);
57244    }
57245  }, {
57246    key: 'showLoading',
57247    value: function showLoading() {
57248      $('#loading').removeClass('hidden');
57249    }
57250  }, {
57251    key: 'submitHandler',
57252    value: function submitHandler(form) {
57253      var stripeResponse;
57254      return regeneratorRuntime.async(function submitHandler$(context$2$0) {
57255        while (1) switch (context$2$0.prev = context$2$0.next) {
57256          case 0:
57257            if (!this.isStripe()) {
57258              context$2$0.next = 10;
57259              break;
57260            }
57261
57262            this.disableButtons(true);
57263
57264            context$2$0.next = 4;
57265            return regeneratorRuntime.awrap(this.processStripe());
57266
57267          case 4:
57268            stripeResponse = context$2$0.sent;
57269
57270            if (!stripeResponse.error) {
57271              context$2$0.next = 8;
57272              break;
57273            }
57274
57275            this.disableButtons(false);
57276
57277            return context$2$0.abrupt('return');
57278
57279          case 8:
57280            context$2$0.next = 18;
57281            break;
57282
57283          case 10:
57284            if (!this.isGooglePay()) {
57285              context$2$0.next = 15;
57286              break;
57287            }
57288
57289            this.googlePay.onGooglePaymentButtonClicked();
57290            return context$2$0.abrupt('return');
57291
57292          case 15:
57293            if (!this.isApplePay()) {
57294              context$2$0.next = 18;
57295              break;
57296            }
57297
57298            this.applePay.onApplePayButtonClicked();
57299            return context$2$0.abrupt('return');
57300
57301          case 18:
57302
57303            this.submitForm();
57304
57305          case 19:
57306          case 'end':
57307            return context$2$0.stop();
57308        }
57309      }, null, this);
57310    }
57311  }, {
57312    key: 'submitForm',
57313    value: function submitForm() {
57314      this.disableButtons(true);
57315      this.disableScroll();
57316      this.showLoading();
57317
57318      if (window.executeV3RecaptchaAndSubmit) {
57319        window.executeV3RecaptchaAndSubmit(this.$form.get(0));
57320      } else {
57321        this.$form.get(0).submit();
57322      }
57323    }
57324  }, {
57325    key: 'totalAmount',
57326    value: function totalAmount() {
57327      var amount = this.$form.find('#order_subtotal').val();
57328      var markup = this.$form.find('#order_markup').val() || 0;
57329      var markdown = this.$form.find('#order_markdown').val() || 0;
57330      var donation = this.$form.find('#order_donation').val() || 0;
57331      return (parseFloat(amount) + parseFloat(markup) + parseFloat(donation) + parseFloat(markdown)).toFixed(2);
57332    }
57333  }, {
57334    key: 'validateForm',
57335    value: function validateForm() {
57336      var _this8 = this;
57337
57338      this.$form.validate($.extend(formValidateDefaults, {
57339        rules: {
57340          'order[state]': {
57341            required: function required() {
57342              return ['US', 'CA'].indexOf($('#order_country').val()) != -1;
57343            }
57344          },
57345          'order[postal]': {
57346            required: function required() {
57347              return ['US', 'CA'].indexOf($('#order_country').val()) != -1;
57348            }
57349          },
57350          'order[email]': 'email',
57351          'order[cc_number]': 'creditcard',
57352          'order[google_pay_token]': {
57353            required: function required() {
57354              if (!_this8.isGooglePay()) {
57355                return false;
57356              }
57357
57358              return $('#order_google_pay_token').val().length;
57359            }
57360          },
57361          'hidden_recaptcha': {
57362            required: function required() {
57363              if (grecaptcha.getResponse() == '') {
57364                return true;
57365              } else {
57366                return false;
57367              }
57368            }
57369          }
57370        },
57371
57372        messages: {
57373          'order[subtotal]': 'Amount is required',
57374          'order[first_name]': 'First Name is required',
57375          'order[last_name]': 'Last Name is required',
57376          'order[email]': {
57377            required: 'Valid email is required',
57378            email: 'Valid email is required'
57379          },
57380          'order[address_1]': 'Address 1 is required',
57381          'order[city]': 'City is required',
57382          'order[state]': 'State / Province is required',
57383          'order[postal]': 'Zip / Postal Code is required',
57384
57385          'order[cc_number]': 'Valid Credit Card Number is required',
57386          'order[cc_expiry_month]': 'Credit Card Expiration Month is required',
57387          'order[cc_expiry_year]': 'Credit Card Expiration Year is required',
57388          'order[cc_code]': 'Credit Card CVV Code is required',
57389          'order[check_account_number]': 'Valid Bank Account Number is required',
57390          'order[check_routing_number]': 'Valid Bank Routing Number is required',
57391          'order[payment_method_check_name]': 'Name on Bank Account is required',
57392          'order[payment_method_bank_name]': 'Bank Name is required',
57393          'hidden_recaptcha': 'To prove that you are a human, the CAPTCHA box must be checked'
57394        },
57395
57396        focusInvalid: false,
57397
57398        invalidHandler: function invalidHandler(event, validator) {
57399          this.formHasBeenValidated = true;
57400
57401          var errors = validator.errorList.map(function (error) {
57402            return '<li>' + error.message + '</li>';
57403          });
57404
57405          var message = 'One or more fields are incomplete or incorrect. Please correct the following items: ';
57406          message += '<ul>' + errors.join('') + '</ul>';
57407          $('#error-messages p').html(message);
57408          $('#error-messages').show();
57409
57410          $('html, body').animate({ scrollTop: 0 }, 200);
57411        },
57412
57413        submitHandler: function submitHandler(form, event) {
57414          _this8.submitHandler(form, event);
57415        }
57416      }));
57417    }
57418  }]);
57419
57420  return OrderPage;
57421})();
57422'use strict';
57423
57424var _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; }; })();
57425
57426function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }
57427
57428var DonorsOrderShowPage = (function () {
57429  function DonorsOrderShowPage() {
57430    _classCallCheck(this, DonorsOrderShowPage);
57431
57432    this.initialize();
57433  }
57434
57435  _createClass(DonorsOrderShowPage, [{
57436    key: 'initialize',
57437    value: function initialize() {
57438      $('[data-fundraiser-show-login]').on('click', function (e) {
57439        e.preventDefault();
57440        $('#fundraiser_user_session_wrapper').show();
57441        $('#fundraiser_user_registration_wrapper').hide();
57442      });
57443
57444      $('[data-fundraiser-show-register]').on('click', function (e) {
57445        e.preventDefault();
57446        $('#fundraiser_user_session_wrapper').hide();
57447        $('#fundraiser_user_registration_wrapper').show();
57448      });
57449
57450      $('form').on('submit', function (e) {
57451        $(this).find('input[type="submit"]').prop('disabled', true).val('Loading...');
57452      });
57453    }
57454  }]);
57455
57456  return DonorsOrderShowPage;
57457})();
57458'use strict';
57459
57460var _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; }; })();
57461
57462function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }
57463
57464var FundraiserPage = (function () {
57465  function FundraiserPage() {
57466    _classCallCheck(this, FundraiserPage);
57467
57468    this.initialize();
57469  }
57470
57471  _createClass(FundraiserPage, [{
57472    key: 'initialize',
57473    value: function initialize() {
57474      $(document).on('ajax:beforeSend', '.pagination', function (event) {
57475        $(event.currentTarget).addClass('loading');
57476      });
57477
57478      $(document).on('ajax:complete', '.pagination', function (event) {
57479        $(event.currentTarget).removeClass('loading');
57480      });
57481
57482      new MoreLoader({
57483        control: '[data-control="team-members-list"]',
57484        container: '[data-list="team-members"]',
57485        endpoint: $('[data-control="team-members-list"]').attr('data-url'),
57486        totalPages: $('[data-control="team-members-list"]').attr('data-total-pages')
57487      });
57488
57489      new MoreLoader({
57490        control: '[data-control="team-siblings-all-list"]',
57491        container: '[data-list="team-siblings-all"]',
57492        endpoint: $('[data-control="team-siblings-all-list"]').attr('data-url'),
57493        totalPages: $('[data-control="team-siblings-all-list"]').attr('data-total-pages')
57494      });
57495
57496      new MoreLoader({
57497        control: '[data-control="team-siblings-fundraisers-list"]',
57498        container: '[data-list="team-siblings-fundraisers"]',
57499        endpoint: $('[data-control="team-siblings-fundraisers-list"]').attr('data-url'),
57500        totalPages: $('[data-control="team-siblings-fundraisers-list"]').attr('data-total-pages')
57501      });
57502
57503      new MoreLoader({
57504        control: '[data-control="team-siblings-teams-list"]',
57505        container: '[data-list="team-siblings-teams"]',
57506        endpoint: $('[data-control="team-siblings-teams-list"]').attr('data-url'),
57507        totalPages: $('[data-control="team-siblings-teams-list"]').attr('data-total-pages')
57508      });
57509    }
57510  }]);
57511
57512  return FundraiserPage;
57513})();
57514'use strict';
57515
57516var _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; }; })();
57517
57518function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }
57519
57520var LandingPage = (function () {
57521  function LandingPage() {
57522    _classCallCheck(this, LandingPage);
57523
57524    this.initialize();
57525  }
57526
57527  _createClass(LandingPage, [{
57528    key: 'initialize',
57529    value: function initialize() {
57530      new MoreLoader({
57531        control: '[data-control="team-siblings-all-list"]',
57532        container: '[data-list="team-siblings-all"]',
57533        endpoint: $('[data-control="team-siblings-all-list"]').attr('data-url'),
57534        totalPages: $('[data-control="team-siblings-all-list"]').attr('data-total-pages')
57535      });
57536
57537      new MoreLoader({
57538        control: '[data-control="team-siblings-fundraisers-list"]',
57539        container: '[data-list="team-siblings-fundraisers"]',
57540        endpoint: $('[data-control="team-siblings-fundraisers-list"]').attr('data-url'),
57541        totalPages: $('[data-control="team-siblings-fundraisers-list"]').attr('data-total-pages')
57542      });
57543
57544      new MoreLoader({
57545        control: '[data-control="team-siblings-teams-list"]',
57546        container: '[data-list="team-siblings-teams"]',
57547        endpoint: $('[data-control="team-siblings-teams-list"]').attr('data-url'),
57548        totalPages: $('[data-control="team-siblings-teams-list"]').attr('data-total-pages')
57549      });
57550    }
57551  }]);
57552
57553  return LandingPage;
57554})();
57555'use strict';
57556
57557var _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; }; })();
57558
57559function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }
57560
57561var DonorsDonationPage = (function () {
57562  function DonorsDonationPage(args) {
57563    _classCallCheck(this, DonorsDonationPage);
57564
57565    this.paymentFieldsUrl = args.paymentFieldsUrl;
57566    this.recurringConversionEnabled = args.recurringConversionEnabled;
57567    this.recurringConversionUrl = args.recurringConversionUrl;
57568    this.recurringConversionMin = args.recurringConversionMin;
57569    this.recurringConversionMax = args.recurringConversionMax;
57570    this.frequency = args.frequency;
57571    this.donationAmountsEnabled = args.donationAmountsEnabled;
57572    this.changeDonationAmountUrl = args.changeDonationAmountUrl;
57573    this.changeCustomAmountUrl = args.changeCustomAmountUrl;
57574    this.changeFrequencyUrl = args.changeFrequencyUrl;
57575    this.changeProcessingFeeUrl = args.changeProcessingFeeUrl;
57576    this.$form = $('#new_donation');
57577    this.submitButtonSelector = '[data-donation-submit]';
57578    this.stripeApiKey = args.stripeApiKey;
57579    this.stripePaymentType = null;
57580    this.initialTotalInCents = args.initialTotalInCents;
57581    this.gateway = args.gateway;
57582    this.googlePayEnabled = args.googlePayEnabled;
57583    this.googlePayKey = args.googlePayKey;
57584    this.googlePayEnv = args.googlePayEnv;
57585    this.googlePayMerchantId = args.googlePayMerchantId;
57586    this.applePayEnabled = args.applePayEnabled;
57587    this.applePaymentSessionUrl = args.applePaymentSessionUrl;
57588    this.appleProcessUrl = args.appleProcessUrl;
57589    this.nonprofitName = args.nonprofitName;
57590    this.applePayMerchantIdentifier = args.applePayMerchantIdentifier;
57591    this.mailingFieldsEnabled = args.mailingFieldsEnabled;
57592    this.payPalEnabled = args.payPalEnabled;
57593    this.payPalOrderUrl = args.payPalOrderUrl;
57594    this.payPalDeactivatePlanUrl = args.payPalDeactivatePlanUrl;
57595
57596    this.initialize();
57597  }
57598
57599  _createClass(DonorsDonationPage, [{
57600    key: 'initialize',
57601    value: function initialize() {
57602      var _this = this;
57603
57604      $(document).on('change', '[name="donation[frequency]"]', function (event) {
57605        $('#donation_amounts_loading').show();
57606
57607        var form = $('#new_donation').get(0);
57608
57609        $.ajax({
57610          url: _this.changeFrequencyUrl,
57611          method: 'post',
57612          dataType: 'script',
57613          data: new FormData(form),
57614          processData: false,
57615          contentType: false
57616        });
57617      });
57618
57619      $(document).on('click', '[name="donation[donation_amount_id]"]', function (event) {
57620        $('#donation_amounts_loading').show();
57621
57622        var form = $('#new_donation').get(0);
57623
57624        $.ajax({
57625          url: _this.changeDonationAmountUrl,
57626          method: 'post',
57627          dataType: 'script',
57628          data: new FormData(form),
57629          processData: false,
57630          contentType: false
57631        });
57632      });
57633
57634      $(document).on('keyup', '#donation_donation_amount_custom', function (event) {
57635        var form = $('#new_donation').get(0);
57636
57637        $.ajax({
57638          url: _this.changeCustomAmountUrl,
57639          method: 'post',
57640          dataType: 'script',
57641          data: new FormData(form),
57642          processData: false,
57643          contentType: false
57644        });
57645      });
57646
57647      $(document).on('change', '#donation_processing_fee_included', function (event) {
57648        var form = $('#new_donation').get(0);
57649
57650        $.ajax({
57651          url: _this.changeProcessingFeeUrl,
57652          method: 'post',
57653          dataType: 'script',
57654          data: new FormData(form),
57655          processData: false,
57656          contentType: false
57657        });
57658      });
57659
57660      $(document).on('change', '[name="donation[payment_method_type]"]', function () {
57661        var form = $('#new_donation').get(0);
57662
57663        $.ajax({
57664          url: _this.paymentFieldsUrl,
57665          method: 'post',
57666          dataType: 'script',
57667          data: new FormData(form),
57668          processData: false,
57669          contentType: false
57670        });
57671      });
57672
57673      $(document).on('payment-method-change', function (event, data) {
57674        if (data.paymentMethod == 'credit_card') {
57675          _this.initializeStripeFields();
57676        } else if (data.paymentMethod == 'paypal') {
57677          _this.initializePayPal();
57678        }
57679      });
57680
57681      $(document).on('payment-frequency-change', function (event, data) {
57682        if (data.paymentMethod == 'paypal') {
57683          _this.initializePayPal();
57684        }
57685      });
57686
57687      $(document).on('payment-amount-change', function (event, data) {
57688        if (data.paymentMethod == 'paypal') {
57689          _this.initializePayPal();
57690        }
57691      });
57692
57693      $(document).on('payment-custom-amount-change', function (event, data) {
57694        if (data.paymentMethod == 'paypal') {
57695          _this.initializePayPal();
57696        }
57697      });
57698
57699      $(document).on('payment-processing-fee-change', function (event, data) {
57700        if (data.paymentMethod == 'paypal') {
57701          _this.initializePayPal();
57702        }
57703      });
57704
57705      new DonorFormValidator({
57706        form: this.$form,
57707        submitHandler: this.submitHandler.bind(this)
57708      });
57709
57710      // Handles gift visibility
57711      new ConditionalInput({
57712        control: $('select#donation_gift_name'),
57713        visibility: {
57714          'Other': $('.donation_gift_name_other')
57715        }
57716      });
57717
57718      // Handles tribute visibility
57719      new ConditionalInput({
57720        inputType: 'checkbox',
57721        control: $('#donation_tribute_enabled'),
57722        visibility: {
57723          'on': $('.form-item--tribute-group')
57724        }
57725      });
57726
57727      // Handles tribute notification type visibility
57728      new ConditionalInput({
57729        inputType: 'radio',
57730        control: $('[name="donation[tribute_attributes][notification_type]"][type="radio"]'),
57731        visibility: {
57732          'email': $('.form-item--tribute-email-group'),
57733          'snailmail': $('.form-item--tribute-snailmail-group')
57734        }
57735      });
57736
57737      // Handles tribute notification message visibility
57738      new ConditionalInput({
57739        inputType: 'radio',
57740        control: $('[name="donation[tribute_attributes][notification_type]"][type="radio"]'),
57741        visibility: {
57742          'email|snailmail': $('.donation_tribute_message')
57743        }
57744      });
57745
57746      new BillingInfoCookie({
57747        modelName: 'donation'
57748      });
57749
57750      this.toggleDescriptionReadMore();
57751      this.initializeRecurringConversion();
57752      this.initializeDescriptionReadMore();
57753      this.initializeStripeFields();
57754      this.initializeGooglePay();
57755      this.initializeApplePay();
57756      this.initializePayPal();
57757
57758      $(window).on('resize', function () {
57759        _this.toggleDescriptionReadMore();
57760      });
57761    }
57762  }, {
57763    key: 'disableButtons',
57764    value: function disableButtons(isDisabled) {
57765      var $submitButton = $(this.submitButtonSelector);
57766
57767      if (isDisabled) {
57768        $submitButton.prop('disabled', true);
57769        $('button[name="recurring_conversion_button"]').prop('disabled', true);
57770      } else {
57771        $submitButton.prop('disabled', false);
57772        $('button[name="recurring_conversion_button"]').prop('disabled', false);
57773      }
57774    }
57775  }, {
57776    key: 'disableScroll',
57777    value: function disableScroll() {
57778      $('body').addClass('no-scroll');
57779    }
57780  }, {
57781    key: 'frequencyValue',
57782    value: function frequencyValue() {
57783      return this.$form.find('[name="donation[frequency]"]:checked').val();
57784    }
57785  }, {
57786    key: 'initializeDescriptionReadMore',
57787    value: function initializeDescriptionReadMore() {
57788      // Reveals the hidden description on small screens
57789      $('[data-donation-page-description-read-more]').on('click', function (event) {
57790        event.preventDefault();
57791        $('[data-donation-page-description-overlay]').hide();
57792        $('[data-donation-page-description]').addClass('donate-body__expanded');
57793      });
57794    }
57795  }, {
57796    key: 'initializeApplePay',
57797    value: function initializeApplePay() {
57798      var _this2 = this;
57799
57800      if (!this.applePayEnabled) {
57801        return;
57802      }
57803
57804      this.applePay = new ApplePay({
57805        applePaymentSessionUrl: this.applePaymentSessionUrl,
57806        appleProcessUrl: this.appleProcessUrl,
57807        tokenField: 'donation_apple_pay_token',
57808        lastDigitsField: 'donation_payment_method_last_digits',
57809        nonprofitName: this.nonprofitName,
57810        merchantIdentifier: this.applePayMerchantIdentifier,
57811        $form: this.$form,
57812
57813        getTotalAmount: function getTotalAmount() {
57814          return _this2.totalAmountWithConversion();
57815        },
57816
57817        onSubmit: function onSubmit() {
57818          _this2.submitForm();
57819        }
57820      });
57821
57822      this.applePay.checkVisibility({
57823        onSuccess: function onSuccess() {
57824          $('[data-payment-method-type="apple_pay"]').show();
57825          $('#donation_apple_pay_available').val('true');
57826        }
57827      });
57828    }
57829  }, {
57830    key: 'initializeGooglePay',
57831    value: function initializeGooglePay() {
57832      var _this3 = this;
57833
57834      if (!this.googlePayEnabled) {
57835        return;
57836      }
57837
57838      this.googlePay = new GooglePay({
57839        gateway: this.gateway,
57840        key: this.googlePayKey,
57841        env: this.googlePayEnv,
57842        merchantId: this.googlePayMerchantId,
57843        tokenField: 'donation_google_pay_token',
57844        lastDigitsField: 'donation_payment_method_last_digits',
57845        expiryMonthField: 'donation_cc_expiry_month',
57846        expiryYearField: 'donation_cc_expiry_year',
57847        nonprofitName: this.nonprofitName,
57848        $form: this.$form,
57849
57850        getTotalAmount: function getTotalAmount() {
57851          return _this3.totalAmountWithConversion();
57852        },
57853
57854        onSubmit: function onSubmit() {
57855          _this3.submitForm();
57856        }
57857      });
57858
57859      this.googlePay.checkVisibility({
57860        onSuccess: function onSuccess() {
57861          $('[data-payment-method-type="google_pay"]').show();
57862          $('#donation_google_pay_available').val('true');
57863        }
57864      });
57865    }
57866  }, {
57867    key: 'initializePayPal',
57868    value: function initializePayPal() {
57869      var _this4 = this;
57870
57871      if (!this.payPalEnabled) {
57872        return;
57873      }
57874      if (!this.isPayPal()) {
57875        return;
57876      }
57877
57878      if (this.isFrequencySingle()) {
57879        paypal.Buttons({
57880          style: {
57881            layout: 'horizontal',
57882            color: 'gold',
57883            shape: 'rect',
57884            label: 'pay',
57885            height: 55,
57886            borderRadius: 8,
57887            disableMaxWidth: true,
57888            tagline: false
57889          },
57890
57891          onClick: function onClick(data, actions) {
57892            var isValid = _this4.$form.valid();
57893
57894            if (!isValid) {
57895              return actions.reject();
57896            }
57897          },
57898
57899          createOrder: function createOrder() {
57900            var form, formData, response;
57901            return regeneratorRuntime.async(function createOrder$(context$3$0) {
57902              while (1) switch (context$3$0.prev = context$3$0.next) {
57903                case 0:
57904                  form = this.$form.get(0);
57905                  formData = new FormData(form);
57906                  context$3$0.next = 4;
57907                  return regeneratorRuntime.awrap($.ajax({
57908                    url: this.payPalOrderUrl,
57909                    method: 'post',
57910                    data: formData,
57911                    processData: false,
57912                    contentType: false
57913                  }));
57914
57915                case 4:
57916                  response = context$3$0.sent;
57917                  return context$3$0.abrupt('return', response['id']);
57918
57919                case 6:
57920                case 'end':
57921                  return context$3$0.stop();
57922              }
57923            }, null, _this4);
57924          },
57925
57926          onApprove: function onApprove(data) {
57927            return regeneratorRuntime.async(function onApprove$(context$3$0) {
57928              while (1) switch (context$3$0.prev = context$3$0.next) {
57929                case 0:
57930                  $('#donation_paypal_order_id').val(data.orderID);
57931
57932                  this.disableButtons(true);
57933                  this.disableScroll();
57934                  this.showLoading();
57935
57936                  if (window.executeV3RecaptchaAndSubmit) {
57937                    window.executeV3RecaptchaAndSubmit(this.$form.get(0));
57938                  } else {
57939                    this.$form.get(0).submit();
57940                  }
57941
57942                case 5:
57943                case 'end':
57944                  return context$3$0.stop();
57945              }
57946            }, null, _this4);
57947          }
57948        }).render('#paypal-button-container');
57949      } else {
57950        (function () {
57951          var paypalCancelToken = null;
57952
57953          paypal.Buttons({
57954            style: {
57955              layout: 'horizontal',
57956              color: 'gold',
57957              shape: 'rect',
57958              label: 'pay',
57959              height: 55,
57960              borderRadius: 8,
57961              disableMaxWidth: true,
57962              tagline: false
57963            },
57964
57965            onClick: function onClick(data, actions) {
57966              var isValid = _this4.$form.valid();
57967
57968              if (!isValid) {
57969                return actions.reject();
57970              }
57971            },
57972
57973            createSubscription: function createSubscription(data, actions) {
57974              var form, formData, response;
57975              return regeneratorRuntime.async(function createSubscription$(context$4$0) {
57976                while (1) switch (context$4$0.prev = context$4$0.next) {
57977                  case 0:
57978                    form = this.$form.get(0);
57979                    formData = new FormData(form);
57980                    context$4$0.next = 4;
57981                    return regeneratorRuntime.awrap($.ajax({
57982                      url: this.payPalOrderUrl,
57983                      method: 'post',
57984                      data: formData,
57985                      processData: false,
57986                      contentType: false
57987                    }));
57988
57989                  case 4:
57990                    response = context$4$0.sent;
57991
57992                    paypalCancelToken = response.cancel_token;
57993
57994                    return context$4$0.abrupt('return', actions.subscription.create({
57995                      plan_id: response.id
57996                    }));
57997
57998                  case 7:
57999                  case 'end':
58000                    return context$4$0.stop();
58001                }
58002              }, null, _this4);
58003            },
58004
58005            onApprove: function onApprove(data) {
58006              return regeneratorRuntime.async(function onApprove$(context$4$0) {
58007                while (1) switch (context$4$0.prev = context$4$0.next) {
58008                  case 0:
58009                    $('#donation_paypal_order_id').val(data.orderID);
58010                    $('#donation_paypal_subscription_id').val(data.subscriptionID);
58011
58012                    this.disableButtons(true);
58013                    this.disableScroll();
58014                    this.showLoading();
58015
58016                    if (window.executeV3RecaptchaAndSubmit) {
58017                      window.executeV3RecaptchaAndSubmit(this.$form.get(0));
58018                    } else {
58019                      this.$form.get(0).submit();
58020                    }
58021
58022                  case 6:
58023                  case 'end':
58024                    return context$4$0.stop();
58025                }
58026              }, null, _this4);
58027            },
58028
58029            onCancel: function onCancel(data) {
58030              $('#donation_paypal_plan_id').val('');
58031
58032              if (paypalCancelToken) {
58033                $.ajax({
58034                  url: _this4.payPalDeactivatePlanUrl,
58035                  method: 'post',
58036                  data: { cancel_token: paypalCancelToken }
58037                });
58038
58039                paypalCancelToken = null;
58040              }
58041            }
58042          }).render('#paypal-button-container');
58043        })();
58044      }
58045    }
58046  }, {
58047    key: 'initializeRecurringConversion',
58048    value: function initializeRecurringConversion() {
58049      var _this5 = this;
58050
58051      $(document).on('click', 'button[name="recurring_conversion_button"]', function (event) {
58052        event.preventDefault();
58053
58054        var value = $(event.target).val();
58055        var total = $(event.target).data('total');
58056
58057        if (value) {
58058          $('#recurring_conversion').val(value);
58059        }
58060
58061        if (total) {
58062          $('#recurring_conversion_total').val(total);
58063        }
58064
58065        _this5.submitHandler(_this5.$form.get(0));
58066      });
58067    }
58068  }, {
58069    key: 'initializeStripeFields',
58070    value: function initializeStripeFields() {
58071      if (this.gateway !== 'stripe') {
58072        return;
58073      };
58074      if (!this.isPaymentMethodTypeCreditCard()) {
58075        return;
58076      };
58077
58078      if (!this.stripeApiKey) {
58079        $('#stripe-card-error').show();
58080
58081        return;
58082      }
58083
58084      this.stripe = Stripe(this.stripeApiKey);
58085
58086      var options = {
58087        // We can use `payment` for every scenario (including subscriptions)
58088        // because only creating a payment method (not an actual payment)
58089        // does not require the type.
58090        mode: 'payment',
58091
58092        amount: this.initialTotalInCents,
58093        currency: 'usd',
58094        paymentMethodCreation: 'manual',
58095
58096        paymentMethodTypes: ['card'],
58097
58098        appearance: {
58099          theme: 'stripe',
58100
58101          variables: {
58102            fontFamily: '"DM Sans", Helvetica, arial, sans-serif',
58103            fontSizeBase: '16px',
58104            colorText: '#262626',
58105            colorDanger: '#ff0000'
58106          },
58107
58108          rules: {
58109            '.Label': {
58110              marginBottom: '4px',
58111              lineHeight: '24px'
58112            },
58113            '.Input': {
58114              borderRadius: '0.25rem',
58115              borderColor: '#6b7280',
58116              boxShadow: 'none',
58117              padding: '14px 12px',
58118              height: '48px'
58119            },
58120            '.Input:focus': {
58121              borderColor: '#2563eb',
58122              boxShadow: 'rgb(255, 255, 255) 0px 0px 0px 0px, rgb(37, 99, 235) 0px 0px 0px 1px, rgba(0, 0, 0, 0) 0px 0px 0px 0px',
58123              transition: 'none'
58124            },
58125            '.Input--invalid': {
58126              boxShadow: 'none'
58127            }
58128          }
58129        }
58130      };
58131
58132      this.stripeElements = this.stripe.elements(options);
58133
58134      this.stripePaymentElement = this.stripeElements.create('payment', {
58135        disableLink: true,
58136        fields: {
58137          billingDetails: {
58138            address: {
58139              country: this.mailingFieldsEnabled ? 'never' : 'auto',
58140              postalCode: this.mailingFieldsEnabled ? 'never' : 'auto'
58141            }
58142          }
58143        },
58144        wallets: {
58145          googlePay: 'never',
58146          applePay: 'never'
58147        }
58148      });
58149
58150      this.stripePaymentElement.on('loaderror', function (event) {
58151        // event.error.message contains information
58152        $('#stripe-card-error').show();
58153      });
58154
58155      this.stripePaymentElement.mount('#stripe-card-field');
58156    }
58157  }, {
58158    key: 'isAllowedAmountForRecurringConversion',
58159    value: function isAllowedAmountForRecurringConversion() {
58160      return this.totalAmount() > this.recurringConversionMin && this.totalAmount() < this.recurringConversionMax;
58161    }
58162  }, {
58163    key: 'isApplePay',
58164    value: function isApplePay() {
58165      return this.paymentMethodTypeValue() == 'apple_pay';
58166    }
58167  }, {
58168    key: 'isGooglePay',
58169    value: function isGooglePay() {
58170      return this.paymentMethodTypeValue() == 'google_pay';
58171    }
58172  }, {
58173    key: 'isStripe',
58174    value: function isStripe() {
58175      return !!this.stripeApiKey && this.isPaymentMethodTypeCredit
58175Card();
58176    }
58177  }, {
58178    key: 'isFrequencySingle',
58179    value: function isFrequencySingle() {
58180      return this.frequencyValue() === 'single';
58181    }
58182  }, {
58183    key: 'isPaymentMethodTypeCreditCard',
58184    value: function isPaymentMethodTypeCreditCard() {
58185      return this.paymentMethodTypeValue() === 'credit_card';
58186    }
58187  }, {
58188    key: 'isPayPal',
58189    value: function isPayPal() {
58190      return this.paymentMethodTypeValue() === 'paypal';
58191    }
58192  }, {
58193    key: 'isRecurringConversionEnabled',
58194    value: function isRecurringConversionEnabled() {
58195      return this.recurringConversionEnabled === true;
58196    }
58197  }, {
58198    key: 'paymentMethodTypeValue',
58199    value: function paymentMethodTypeValue() {
58200      if (this.$form.find('[name="donation[payment_method_type]"]').is(':radio')) {
58201        return this.$form.find('[name="donation[payment_method_type]"]:checked').val();
58202      } else {
58203        return this.$form.find('[name="donation[payment_method_type]"]').val();
58204      }
58205    }
58206  }, {
58207    key: 'processStripe',
58208    value: function processStripe() {
58209      var validate, createPaymentMethodParams, _ref, error, paymentMethod, message;
58210
58211      return regeneratorRuntime.async(function processStripe$(context$2$0) {
58212        while (1) switch (context$2$0.prev = context$2$0.next) {
58213          case 0:
58214            context$2$0.next = 2;
58215            return regeneratorRuntime.awrap(this.stripeElements.submit());
58216
58217          case 2:
58218            validate = context$2$0.sent;
58219
58220            if (!validate.error) {
58221              context$2$0.next = 6;
58222              break;
58223            }
58224
58225            this.stripePaymentElement.focus();
58226
58227            return context$2$0.abrupt('return', { error: 'Invalid credit card' });
58228
58229          case 6:
58230            createPaymentMethodParams = {
58231              elements: this.stripeElements
58232            };
58233
58234            if (this.mailingFieldsEnabled) {
58235              createPaymentMethodParams['params'] = {
58236                billing_details: {
58237                  address: {
58238                    country: this.$form.find('#donation_country').val(),
58239                    postal_code: this.$form.find('#donation_postal').val()
58240                  }
58241                }
58242              };
58243            }
58244
58245            context$2$0.next = 10;
58246            return regeneratorRuntime.awrap(this.stripe.createPaymentMethod(createPaymentMethodParams));
58247
58248          case 10:
58249            _ref = context$2$0.sent;
58250            error = _ref.error;
58251            paymentMethod = _ref.paymentMethod;
58252
58253            if (!error) {
58254              context$2$0.next = 17;
58255              break;
58256            }
58257
58258            message = 'Could not accept credit card: ' + error.message;
58259
58260            alert(message);
58261
58262            return context$2$0.abrupt('return', { error: message });
58263
58264          case 17:
58265
58266            $('#donation_stripe_payment_method_id').val(paymentMethod.id);
58267
58268            if (paymentMethod.type === 'card') {
58269              $('#donation_payment_method_last_digits').val(paymentMethod.card.last4);
58270              $('#donation_cc_expiry_month').val(paymentMethod.card.exp_month);
58271              $('#donation_cc_expiry_year').val(paymentMethod.card.exp_year);
58272            }
58273
58274            return context$2$0.abrupt('return', {});
58275
58276          case 20:
58277          case 'end':
58278            return context$2$0.stop();
58279        }
58280      }, null, this);
58281    }
58282  }, {
58283    key: 'recurringModalIsOpen',
58284    value: function recurringModalIsOpen() {
58285      return $('.recurring-modal.is-active').length > 0;
58286    }
58287  }, {
58288    key: 'shouldShowRecurringConversion',
58289    value: function shouldShowRecurringConversion() {
58290      return !this.recurringModalIsOpen() && this.isFrequencySingle() && this.isRecurringConversionEnabled() && this.isAllowedAmountForRecurringConversion();
58291    }
58292  }, {
58293    key: 'showLoading',
58294    value: function showLoading() {
58295      $('#loading').removeClass('hidden');
58296    }
58297  }, {
58298    key: 'submitHandler',
58299    value: function submitHandler(form) {
58300      var stripeResponse;
58301      return regeneratorRuntime.async(function submitHandler$(context$2$0) {
58302        while (1) switch (context$2$0.prev = context$2$0.next) {
58303          case 0:
58304            if (!this.shouldShowRecurringConversion()) {
58305              context$2$0.next = 3;
58306              break;
58307            }
58308
58309            $.ajax({
58310              url: this.recurringConversionUrl,
58311              method: 'post',
58312              dataType: 'script',
58313              data: new FormData(form),
58314              processData: false,
58315              contentType: false
58316            });
58317
58318            return context$2$0.abrupt('return');
58319
58320          case 3:
58321            if (!this.isStripe()) {
58322              context$2$0.next = 13;
58323              break;
58324            }
58325
58326            this.disableButtons(true);
58327            context$2$0.next = 7;
58328            return regeneratorRuntime.awrap(this.processStripe());
58329
58330          case 7:
58331            stripeResponse = context$2$0.sent;
58332
58333            if (!stripeResponse.error) {
58334              context$2$0.next = 11;
58335              break;
58336            }
58337
58338            this.disableButtons(false);
58339            return context$2$0.abrupt('return');
58340
58341          case 11:
58342            context$2$0.next = 21;
58343            break;
58344
58345          case 13:
58346            if (!this.isGooglePay()) {
58347              context$2$0.next = 18;
58348              break;
58349            }
58350
58351            this.googlePay.onGooglePaymentButtonClicked();
58352            return context$2$0.abrupt('return');
58353
58354          case 18:
58355            if (!this.isApplePay()) {
58356              context$2$0.next = 21;
58357              break;
58358            }
58359
58360            this.applePay.onApplePayButtonClicked();
58361            return context$2$0.abrupt('return');
58362
58363          case 21:
58364
58365            this.submitForm();
58366
58367          case 22:
58368          case 'end':
58369            return context$2$0.stop();
58370        }
58371      }, null, this);
58372    }
58373  }, {
58374    key: 'submitForm',
58375    value: function submitForm() {
58376      this.disableButtons(true);
58377      this.disableScroll();
58378      this.showLoading();
58379
58380      if (window.executeV3RecaptchaAndSubmit) {
58381        window.executeV3RecaptchaAndSubmit(this.$form.get(0));
58382      } else {
58383        this.$form.get(0).submit();
58384      }
58385    }
58386  }, {
58387    key: 'toggleDescriptionReadMore',
58388    value: function toggleDescriptionReadMore() {
58389      if ($('.donate-body__expanded').length) {
58390        return;
58391      }
58392
58393      var width = $(window).width();
58394      var height = $('[data-donation-page-description]').height();
58395      if (width < 740 && height > 74) {
58396        $('[data-donation-page-description-overlay]').show();
58397      } else {
58398        $('[data-donation-page-description-overlay]').hide();
58399      }
58400    }
58401  }, {
58402    key: 'totalAmount',
58403    value: function totalAmount() {
58404      var amount = this.$form.find('#donation_subtotal').val();
58405      var markup = this.$form.find('#donation_markup').val() || 0;
58406      return (parseFloat(amount) + parseFloat(markup)).toFixed(2);
58407    }
58408  }, {
58409    key: 'totalAmountWithConversion',
58410    value: function totalAmountWithConversion() {
58411      var conversionTotal = this.$form.find('#recurring_conversion_total').val();
58412      var conversionType = this.$form.find('#recurring_conversion').val();
58413
58414      if (conversionTotal && conversionType != 'none') {
58415        return conversionTotal;
58416      }
58417
58418      return this.totalAmount();
58419    }
58420  }]);
58421
58422  return DonorsDonationPage;
58423})();
58424'use strict';
58425
58426var _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; }; })();
58427
58428function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }
58429
58430var DonorsDonationSuccessPage = (function () {
58431  function DonorsDonationSuccessPage() {
58432    _classCallCheck(this, DonorsDonationSuccessPage);
58433
58434    this.initialize();
58435  }
58436
58437  _createClass(DonorsDonationSuccessPage, [{
58438    key: 'initialize',
58439    value: function initialize() {
58440      $('[data-fundraiser-show-login]').on('click', function (e) {
58441        e.preventDefault();
58442        $('#fundraiser_user_session_wrapper').show();
58443        $('#fundraiser_user_registration_wrapper').hide();
58444      });
58445
58446      $('[data-fundraiser-show-register]').on('click', function (e) {
58447        e.preventDefault();
58448        $('#fundraiser_user_session_wrapper').hide();
58449        $('#fundraiser_user_registration_wrapper').show();
58450      });
58451
58452      $('form').on('submit', function (e) {
58453        $(this).find('input[type="submit"]').prop('disabled', true).val('Loading...');
58454      });
58455    }
58456  }]);
58457
58458  return DonorsDonationSuccessPage;
58459})();
58460'use strict';
58461
58462$(function () {
58463  new ConditionalInput({
58464    inputType: 'select',
58465    control: $('[name="fundraiser_page[hero_media_type]"]'),
58466    visibility: {
58467      'image': $('.fundraiser_page_hero_image'),
58468      'video': $('.fundraiser_page_hero_video_url')
58469    }
58470  });
58471
58472  new ConditionalInput({
58473    inputType: 'checkbox',
58474    control: $('#fundraiser_page_hero_media_override'),
58475    visibility: {
58476      'on': $('.form-item--hero-media-group')
58477    }
58478  });
58479
58480  new ConditionalInput({
58481    inputType: 'checkbox',
58482    control: $('#donation_comment_enabled'),
58483    visibility: {
58484      'on': $('.form-item--comment-fields')
58485    }
58486  });
58487
58488  new ConditionalInput({
58489    inputType: 'select',
58490    control: $('#fundraiser_page_fundraiser_page_type'),
58491    visibility: {
58492      'team_member': $('.fundraiser_page_fundraiser_team_id'),
58493      'team_owner': $('.fundraiser_page_fundraiser_team_name')
58494    }
58495  });
58496
58497  new ConditionalInput({
58498    inputType: 'select',
58499    control: $('#fundraiser_page_fundraiser_page_type'),
58500    visibility: {
58501      'individual|team_member': $('.form-item--fundraiser_page_title')
58502    }
58503  });
58504
58505  new ConditionalInput({
58506    inputType: 'checkbox',
58507    control: $('#donation_double_the_donation_check'),
58508    visibility: {
58509      'on': $('#dd-wrapper')
58510    }
58511  });
58512
58513  new CountrySelector({
58514    country: '#donation_country',
58515    stateInputName: 'donation[state]',
58516    stateContainer: '.form-fieldset--donation-state'
58517  });
58518
58519  new CountrySelector({
58520    country: '#donation_tribute_attributes_country',
58521    stateInputName: 'donation[tribute_attributes][state]',
58522    stateContainer: '.form-fieldset--tribute-state'
58523  });
58524
58525  new CountrySelector({
58526    country: '#fundraiser_user_country',
58527    stateInputName: 'fundraiser_user[state]',
58528    stateContainer: '.form-fieldset--fundraiser-user-state'
58529  });
58530
58531  var labelSwitcherDefaults = {
58532    labelValueDefault: 'Postal Code',
58533    labelValues: {
58534      'US': 'Zip Code'
58535    }
58536  };
58537
58538  new LabelSwitcher($.extend(labelSwitcherDefaults, {
58539    control: '#donation_country',
58540    label: 'label[for="donation_postal"]'
58541  }));
58542
58543  new LabelSwitcher($.extend(labelSwitcherDefaults, {
58544    control: '#donation_tribute_attributes_country',
58545    label: 'label[for="donation_tribute_attributes_postal"]'
58546  }));
58547
58548  new LabelSwitcher($.extend(labelSwitcherDefaults, {
58549    control: '#fundraiser_user_country',
58550    label: 'label[for="fundraiser_user_postal"]'
58551  }));
58552
58553  new PlaceholderSwitcher({
58554    control: '#donation_country',
58555    input: '#donation_postal',
58556    values: {
58557      'US': '',
58558      'CA': ''
58559    },
58560    valueDefault: 'optional'
58561  });
58562
58563  new FundraiserSearch({
58564    control: '.fundraiser-search__input, [data-fundraiser-search]'
58565  });
58566
58567  new MoreLoader({
58568    control: '.donation-list--control',
58569    container: '.donation-list--donations',
58570    endpoint: $('.donation-list--control').attr('data-donations-url'),
58571    totalPages: $('.donation-list--control').attr('data-total-pages')
58572  });
58573
58574  new MoreLoader({
58575    control: '[data-control="donations-orders-list"]',
58576    container: '[data-list="donations-orders"]',
58577    endpoint: $('[data-control="donations-orders-list"]').attr('data-url'),
58578    totalPages: $('[data-control="donations-orders-list"]').attr('data-total-pages')
58579  });
58580
58581  new MoreLoader({
58582    control: '[data-control="donations-list"]',
58583    container: '[data-list="donations"]',
58584    endpoint: $('[data-control="donations-list"]').attr('data-url'),
58585    totalPages: $('[data-control="donations-list"]').attr('data-total-pages')
58586  });
58587
58588  new MoreLoader({
58589    control: '[data-control="orders-list"]',
58590    container: '[data-list="orders"]',
58591    endpoint: $('[data-control="orders-list"]').attr('data-url'),
58592    totalPages: $('[data-control="orders-list"]').attr('data-total-pages')
58593  });
58594
58595  new PasswordShow({
58596    passwordField: '[name="fundraiser_user[password]"]',
58597    showControl: '[data-password-show]'
58598  });
58599
58600  new ModalHandler();
58601  new Tabs();
58602
58603  // Attach jQuery Validation number rule
58604  // to all inputs with class 'numeric'
58605  $.validator.addClassRules("numeric", {
58606    number: true
58607  });
58608
58609  $.validator.addClassRules("non_zero", {
58610    min: 1
58611  });
58612
58613  // Attach jQuery Validation email rule
58614  // to all inputs with class 'email'
58615  $.validator.addClassRules("email", {
58616    email: true
58617  });
58618
58619  $('#new_fundraiser_user').validate($.extend({}, formValidateDefaults, {
58620    rules: {
58621      'fundraiser_user[email]': {
58622        email: true
58623      }
58624    }
58625  }));
58626
58627  $('.currency').currency();
58628  $('.numeric').numeric();
58629
58630  // Forces a `focusout` event when a select changes
58631  // so that validation can occur on change
58632  $('select').on('change', function () {
58633    $(this).focusout();
58634  });
58635
58636  tinyMCE.init({
58637    selector: 'textarea.editor',
58638    toolbar: 'formatselect | bold italic | link | bullist numlist',
58639    plugins: 'charactercount,link,paste',
58640    menubar: false,
58641    content_style: '* { font-family: "Arial"; font-size: 14px; line-height: 18px; }',
58642    block_formats: 'Paragraph=p;Header 1=h1;Header 2=h2;Header 3=h3',
58643    paste_as_text: true,
58644    min_height: 240,
58645    entity_encoding: 'raw',
58646    elementpath: false,
58647
58648    setup: function setup(editor) {
58649      editor.on('keyUp change', function (ed) {
58650        tinyMCE.triggerSave();
58651      });
58652    }
58653  });
58654
58655  var fundraiserSearchMobile = function fundraiserSearchMobile() {
58656    var header = $('.header');
58657    var searchTrigger = $('.fundraiser-search__btn');
58658    var searchForm = $('.fundraiser-search__form');
58659    var searchIcon = $('.fundraiser-search__icon');
58660
58661    searchTrigger.bind('click', function (e) {
58662      e.preventDefault();
58663      $(this).parent().find(searchForm).toggleClass('is-active');
58664      header.toggleClass('search-active');
58665      searchIcon.toggle();
58666    });
58667  };
58668
58669  fundraiserSearchMobile();
58670});
58671// ==========================================================
58672// VENDOR
58673// ==========================================================
58674
58675
58676
58677
58678
58679
58680
58681
58682
58683
58684
58685
58686
58687
58688// ==========================================================
58689// CUSTOM
58690// ==========================================================
58691
58692
58693
58694
58695
58696
58697
58698
58699
58700
58701
58702
58703
58704
58705
58706
58707
58708
58709
58710
58711
58712
58713
58714;

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.