diff --git a/src/JShrink/Minifier.php b/src/JShrink/Minifier.php index 4986c53..088d264 100644 --- a/src/JShrink/Minifier.php +++ b/src/JShrink/Minifier.php @@ -618,7 +618,11 @@ protected function saveString() */ protected function saveRegex() { - $this->echo($this->a . $this->b); + if ($this->a != " ") { + $this->echo($this->a); + } + + $this->echo($this->b); while (($this->a = $this->getChar()) !== false) { if ($this->a === '/') { @@ -656,10 +660,7 @@ protected function endsInKeyword() { $testOutput = $this->output . $this->a; foreach(static::$keywords as $keyword) { - if (str_ends_with($testOutput, $keyword)) { - return true; - } - if (str_ends_with($testOutput, $keyword . " ")) { + if (preg_match('/[^\w]'.$keyword.'[ ]?$/i', $testOutput) === 1) { return true; } } diff --git a/tests/JShrink/Test/JShrinkTest.php b/tests/JShrink/Test/JShrinkTest.php index 1a9d31e..cb12475 100644 --- a/tests/JShrink/Test/JShrinkTest.php +++ b/tests/JShrink/Test/JShrinkTest.php @@ -65,6 +65,15 @@ public function testRequests($testName, $input, $output) $this->assertEquals($output, \JShrink\Minifier::minify($input), 'Running User Requested Test: ' . $testName); } + /** + * @dataProvider librariesProvider + */ + public function testLibraries($testName, $input) + { + $this->expectNotToPerformAssertions(); + \JShrink\Minifier::minify($input); + } + // /** // * @group development // * @dataProvider developmentProvider @@ -111,6 +120,23 @@ public static function getTestFiles($group) return $returnData; } + public static function getTestLibraries() + { + $testDir = __DIR__ . '/../../Resources/libraries/'; + + $returnData = array(); + + $testFiles = scandir($testDir); + foreach ($testFiles as $testFile) { + if (substr($testFile, -3) !== '.js') { + continue; + } + $returnData["Libraries:" . $testFile] = [$testFile, file_get_contents($testDir . $testFile)]; + } + + return $returnData; + } + public static function uglifyProvider() { return self::getTestFiles('uglify'); @@ -130,4 +156,9 @@ public static function developmentProvider() { return self::getTestFiles('development'); } + + public static function librariesProvider() + { + return self::getTestLibraries(); + } } diff --git a/tests/Resources/jshrink/input/regex_close.js b/tests/Resources/jshrink/input/regex_close.js new file mode 100644 index 0000000..f8cf16b --- /dev/null +++ b/tests/Resources/jshrink/input/regex_close.js @@ -0,0 +1,6 @@ +function test (string) { + return (string || '').replace( + /([\\!"#$%&'()*+,./:;<=>?@\[\]^`{|}~])/g, + '\\$1' + ) +} \ No newline at end of file diff --git a/tests/Resources/jshrink/input/regex_keyword_overhang.js b/tests/Resources/jshrink/input/regex_keyword_overhang.js new file mode 100644 index 0000000..f3e293d --- /dev/null +++ b/tests/Resources/jshrink/input/regex_keyword_overhang.js @@ -0,0 +1 @@ +aboveMin = Math.round(aboveMin / options.step) * options.step diff --git a/tests/Resources/jshrink/output/regex_close.js b/tests/Resources/jshrink/output/regex_close.js new file mode 100644 index 0000000..4f80faf --- /dev/null +++ b/tests/Resources/jshrink/output/regex_close.js @@ -0,0 +1,4 @@ +function test(string){return(string||'').replace(/([\\!"#$%&'()*+,./:;<=>?@\[\]^`{|}~])/g, + '\\$1' + ) +} diff --git a/tests/Resources/jshrink/output/regex_keyword_overhang.js b/tests/Resources/jshrink/output/regex_keyword_overhang.js new file mode 100644 index 0000000..3255cc8 --- /dev/null +++ b/tests/Resources/jshrink/output/regex_keyword_overhang.js @@ -0,0 +1 @@ +aboveMin=Math.round(aboveMin / options.step)*options.step \ No newline at end of file diff --git a/tests/Resources/jshrink/output/regex_keywords.js b/tests/Resources/jshrink/output/regex_keywords.js index cc2a2c4..a69e0e2 100644 --- a/tests/Resources/jshrink/output/regex_keywords.js +++ b/tests/Resources/jshrink/output/regex_keywords.js @@ -1,2 +1,2 @@ -return /'/ -typeof /'/ \ No newline at end of file +return/'/ +typeof/'/ \ No newline at end of file diff --git a/tests/Resources/jshrink/output/regex_spaces.js b/tests/Resources/jshrink/output/regex_spaces.js index c4e39cd..0f85786 100644 --- a/tests/Resources/jshrink/output/regex_spaces.js +++ b/tests/Resources/jshrink/output/regex_spaces.js @@ -1,2 +1,2 @@ -function airplaneIsCarrierBased(model){return /^(FI-167|Swordfish|Fulmar|Firefly|F4F Wildcat|F6F-[35] Hellcat|Latécoère 298|A[567]M)$/.test(model)} +function airplaneIsCarrierBased(model){return/^(FI-167|Swordfish|Fulmar|Firefly|F4F Wildcat|F6F-[35] Hellcat|Latécoère 298|A[567]M)$/.test(model)} console.log(airplaneIsCarrierBased('F6F-5 Hellcat')) \ No newline at end of file diff --git a/tests/Resources/jshrink/output/regex_with_quote_real.js b/tests/Resources/jshrink/output/regex_with_quote_real.js index 15b0b48..cb3ee27 100644 --- a/tests/Resources/jshrink/output/regex_with_quote_real.js +++ b/tests/Resources/jshrink/output/regex_with_quote_real.js @@ -1 +1 @@ -function test(input){return /^(אחה"צ|אחרי הצהריים|בערב)$/.test(input)} \ No newline at end of file +function test(input){return/^(אחה"צ|אחרי הצהריים|בערב)$/.test(input)} \ No newline at end of file diff --git a/tests/Resources/libraries/jquery_ui.js b/tests/Resources/libraries/jquery_ui.js new file mode 100644 index 0000000..c1b8830 --- /dev/null +++ b/tests/Resources/libraries/jquery_ui.js @@ -0,0 +1,20602 @@ +/*! jQuery UI - v1.13.1 - 2022-02-22 + * http://jqueryui.com + * Includes: widget.js, position.js, data.js, disable-selection.js, focusable.js, form-reset-mixin.js, jquery-patch.js, keycode.js, labels.js, scroll-parent.js, tabbable.js, unique-id.js, widgets/draggable.js, widgets/droppable.js, widgets/resizable.js, widgets/selectable.js, widgets/sortable.js, widgets/accordion.js, widgets/autocomplete.js, widgets/button.js, widgets/checkboxradio.js, widgets/controlgroup.js, widgets/datepicker.js, widgets/dialog.js, widgets/menu.js, widgets/mouse.js, widgets/progressbar.js, widgets/selectmenu.js, widgets/slider.js, widgets/spinner.js, widgets/tabs.js, widgets/tooltip.js, effect.js, effects/effect-blind.js, effects/effect-bounce.js, effects/effect-clip.js, effects/effect-drop.js, effects/effect-explode.js, effects/effect-fade.js, effects/effect-fold.js, effects/effect-highlight.js, effects/effect-puff.js, effects/effect-pulsate.js, effects/effect-scale.js, effects/effect-shake.js, effects/effect-size.js, effects/effect-slide.js, effects/effect-transfer.js + * Copyright jQuery Foundation and other contributors; Licensed MIT */ + +;(function (factory) { + 'use strict' + + if (typeof define === 'function' && define.amd) { + // AMD. Register as an anonymous module. + define(['jquery'], factory) + } else { + // Browser globals + factory(jQuery) + } +})(function ($) { + 'use strict' + + $.ui = $.ui || {} + + var version = ($.ui.version = '1.13.1') + + /*! + * jQuery UI Widget 1.13.1 + * http://jqueryui.com + * + * Copyright jQuery Foundation and other contributors + * Released under the MIT license. + * http://jquery.org/license + */ + + //>>label: Widget + //>>group: Core + //>>description: Provides a factory for creating stateful widgets with a common API. + //>>docs: http://api.jqueryui.com/jQuery.widget/ + //>>demos: http://jqueryui.com/widget/ + + var widgetUuid = 0 + var widgetHasOwnProperty = Array.prototype.hasOwnProperty + var widgetSlice = Array.prototype.slice + + $.cleanData = (function (orig) { + return function (elems) { + var events, elem, i + for (i = 0; (elem = elems[i]) != null; i++) { + // Only trigger remove when necessary to save time + events = $._data(elem, 'events') + if (events && events.remove) { + $(elem).triggerHandler('remove') + } + } + orig(elems) + } + })($.cleanData) + + $.widget = function (name, base, prototype) { + var existingConstructor, constructor, basePrototype + + // ProxiedPrototype allows the provided prototype to remain unmodified + // so that it can be used as a mixin for multiple widgets (#8876) + var proxiedPrototype = {} + + var namespace = name.split('.')[0] + name = name.split('.')[1] + var fullName = namespace + '-' + name + + if (!prototype) { + prototype = base + base = $.Widget + } + + if (Array.isArray(prototype)) { + prototype = $.extend.apply(null, [{}].concat(prototype)) + } + + // Create selector for plugin + $.expr.pseudos[fullName.toLowerCase()] = function (elem) { + return !!$.data(elem, fullName) + } + + $[namespace] = $[namespace] || {} + existingConstructor = $[namespace][name] + constructor = $[namespace][name] = function (options, element) { + // Allow instantiation without "new" keyword + if (!this || !this._createWidget) { + return new constructor(options, element) + } + + // Allow instantiation without initializing for simple inheritance + // must use "new" keyword (the code above always passes args) + if (arguments.length) { + this._createWidget(options, element) + } + } + + // Extend with the existing constructor to carry over any static properties + $.extend(constructor, existingConstructor, { + version: prototype.version, + + // Copy the object used to create the prototype in case we need to + // redefine the widget later + _proto: $.extend({}, prototype), + + // Track widgets that inherit from this widget in case this widget is + // redefined after a widget inherits from it + _childConstructors: [] + }) + + basePrototype = new base() + + // We need to make the options hash a property directly on the new instance + // otherwise we'll modify the options hash on the prototype that we're + // inheriting from + basePrototype.options = $.widget.extend({}, basePrototype.options) + $.each(prototype, function (prop, value) { + if (typeof value !== 'function') { + proxiedPrototype[prop] = value + return + } + proxiedPrototype[prop] = (function () { + function _super () { + return base.prototype[prop].apply(this, arguments) + } + + function _superApply (args) { + return base.prototype[prop].apply(this, args) + } + + return function () { + var __super = this._super + var __superApply = this._superApply + var returnValue + + this._super = _super + this._superApply = _superApply + + returnValue = value.apply(this, arguments) + + this._super = __super + this._superApply = __superApply + + return returnValue + } + })() + }) + constructor.prototype = $.widget.extend( + basePrototype, + { + // TODO: remove support for widgetEventPrefix + // always use the name + a colon as the prefix, e.g., draggable:start + // don't prefix for widgets that aren't DOM-based + widgetEventPrefix: existingConstructor + ? basePrototype.widgetEventPrefix || name + : name + }, + proxiedPrototype, + { + constructor: constructor, + namespace: namespace, + widgetName: name, + widgetFullName: fullName + } + ) + + // If this widget is being redefined then we need to find all widgets that + // are inheriting from it and redefine all of them so that they inherit from + // the new version of this widget. We're essentially trying to replace one + // level in the prototype chain. + if (existingConstructor) { + $.each(existingConstructor._childConstructors, function (i, child) { + var childPrototype = child.prototype + + // Redefine the child widget using the same prototype that was + // originally used, but inherit from the new version of the base + $.widget( + childPrototype.namespace + '.' + childPrototype.widgetName, + constructor, + child._proto + ) + }) + + // Remove the list of existing child constructors from the old constructor + // so the old child constructors can be garbage collected + delete existingConstructor._childConstructors + } else { + base._childConstructors.push(constructor) + } + + $.widget.bridge(name, constructor) + + return constructor + } + + $.widget.extend = function (target) { + var input = widgetSlice.call(arguments, 1) + var inputIndex = 0 + var inputLength = input.length + var key + var value + + for (; inputIndex < inputLength; inputIndex++) { + for (key in input[inputIndex]) { + value = input[inputIndex][key] + if ( + widgetHasOwnProperty.call(input[inputIndex], key) && + value !== undefined + ) { + // Clone objects + if ($.isPlainObject(value)) { + target[key] = $.isPlainObject(target[key]) + ? $.widget.extend({}, target[key], value) + : // Don't extend strings, arrays, etc. with objects + $.widget.extend({}, value) + + // Copy everything else by reference + } else { + target[key] = value + } + } + } + } + return target + } + + $.widget.bridge = function (name, object) { + var fullName = object.prototype.widgetFullName || name + $.fn[name] = function (options) { + var isMethodCall = typeof options === 'string' + var args = widgetSlice.call(arguments, 1) + var returnValue = this + + if (isMethodCall) { + // If this is an empty collection, we need to have the instance method + // return undefined instead of the jQuery instance + if (!this.length && options === 'instance') { + returnValue = undefined + } else { + this.each(function () { + var methodValue + var instance = $.data(this, fullName) + + if (options === 'instance') { + returnValue = instance + return false + } + + if (!instance) { + return $.error( + 'cannot call methods on ' + + name + + ' prior to initialization; ' + + "attempted to call method '" + + options + + "'" + ) + } + + if ( + typeof instance[options] !== 'function' || + options.charAt(0) === '_' + ) { + return $.error( + "no such method '" + + options + + "' for " + + name + + ' widget instance' + ) + } + + methodValue = instance[options].apply(instance, args) + + if (methodValue !== instance && methodValue !== undefined) { + returnValue = + methodValue && methodValue.jquery + ? returnValue.pushStack(methodValue.get()) + : methodValue + return false + } + }) + } + } else { + // Allow multiple hashes to be passed on init + if (args.length) { + options = $.widget.extend.apply(null, [options].concat(args)) + } + + this.each(function () { + var instance = $.data(this, fullName) + if (instance) { + instance.option(options || {}) + if (instance._init) { + instance._init() + } + } else { + $.data(this, fullName, new object(options, this)) + } + }) + } + + return returnValue + } + } + + $.Widget = function (/* options, element */) {} + $.Widget._childConstructors = [] + + $.Widget.prototype = { + widgetName: 'widget', + widgetEventPrefix: '', + defaultElement: '
', + + options: { + classes: {}, + disabled: false, + + // Callbacks + create: null + }, + + _createWidget: function (options, element) { + element = $(element || this.defaultElement || this)[0] + this.element = $(element) + this.uuid = widgetUuid++ + this.eventNamespace = '.' + this.widgetName + this.uuid + + this.bindings = $() + this.hoverable = $() + this.focusable = $() + this.classesElementLookup = {} + + if (element !== this) { + $.data(element, this.widgetFullName, this) + this._on(true, this.element, { + remove: function (event) { + if (event.target === element) { + this.destroy() + } + } + }) + this.document = $( + element.style + ? // Element within the document + element.ownerDocument + : // Element is window or document + element.document || element + ) + this.window = $( + this.document[0].defaultView || this.document[0].parentWindow + ) + } + + this.options = $.widget.extend( + {}, + this.options, + this._getCreateOptions(), + options + ) + + this._create() + + if (this.options.disabled) { + this._setOptionDisabled(this.options.disabled) + } + + this._trigger('create', null, this._getCreateEventData()) + this._init() + }, + + _getCreateOptions: function () { + return {} + }, + + _getCreateEventData: $.noop, + + _create: $.noop, + + _init: $.noop, + + destroy: function () { + var that = this + + this._destroy() + $.each(this.classesElementLookup, function (key, value) { + that._removeClass(value, key) + }) + + // We can probably remove the unbind calls in 2.0 + // all event bindings should go through this._on() + this.element.off(this.eventNamespace).removeData(this.widgetFullName) + this.widget().off(this.eventNamespace).removeAttr('aria-disabled') + + // Clean up events and states + this.bindings.off(this.eventNamespace) + }, + + _destroy: $.noop, + + widget: function () { + return this.element + }, + + option: function (key, value) { + var options = key + var parts + var curOption + var i + + if (arguments.length === 0) { + // Don't return a reference to the internal hash + return $.widget.extend({}, this.options) + } + + if (typeof key === 'string') { + // Handle nested keys, e.g., "foo.bar" => { foo: { bar: ___ } } + options = {} + parts = key.split('.') + key = parts.shift() + if (parts.length) { + curOption = options[key] = $.widget.extend({}, this.options[key]) + for (i = 0; i < parts.length - 1; i++) { + curOption[parts[i]] = curOption[parts[i]] || {} + curOption = curOption[parts[i]] + } + key = parts.pop() + if (arguments.length === 1) { + return curOption[key] === undefined ? null : curOption[key] + } + curOption[key] = value + } else { + if (arguments.length === 1) { + return this.options[key] === undefined ? null : this.options[key] + } + options[key] = value + } + } + + this._setOptions(options) + + return this + }, + + _setOptions: function (options) { + var key + + for (key in options) { + this._setOption(key, options[key]) + } + + return this + }, + + _setOption: function (key, value) { + if (key === 'classes') { + this._setOptionClasses(value) + } + + this.options[key] = value + + if (key === 'disabled') { + this._setOptionDisabled(value) + } + + return this + }, + + _setOptionClasses: function (value) { + var classKey, elements, currentElements + + for (classKey in value) { + currentElements = this.classesElementLookup[classKey] + if ( + value[classKey] === this.options.classes[classKey] || + !currentElements || + !currentElements.length + ) { + continue + } + + // We are doing this to create a new jQuery object because the _removeClass() call + // on the next line is going to destroy the reference to the current elements being + // tracked. We need to save a copy of this collection so that we can add the new classes + // below. + elements = $(currentElements.get()) + this._removeClass(currentElements, classKey) + + // We don't use _addClass() here, because that uses this.options.classes + // for generating the string of classes. We want to use the value passed in from + // _setOption(), this is the new value of the classes option which was passed to + // _setOption(). We pass this value directly to _classes(). + elements.addClass( + this._classes({ + element: elements, + keys: classKey, + classes: value, + add: true + }) + ) + } + }, + + _setOptionDisabled: function (value) { + this._toggleClass( + this.widget(), + this.widgetFullName + '-disabled', + null, + !!value + ) + + // If the widget is becoming disabled, then nothing is interactive + if (value) { + this._removeClass(this.hoverable, null, 'ui-state-hover') + this._removeClass(this.focusable, null, 'ui-state-focus') + } + }, + + enable: function () { + return this._setOptions({ disabled: false }) + }, + + disable: function () { + return this._setOptions({ disabled: true }) + }, + + _classes: function (options) { + var full = [] + var that = this + + options = $.extend( + { + element: this.element, + classes: this.options.classes || {} + }, + options + ) + + function bindRemoveEvent () { + var nodesToBind = [] + + options.element.each(function (_, element) { + var isTracked = $.map(that.classesElementLookup, function (elements) { + return elements + }).some(function (elements) { + return elements.is(element) + }) + + if (!isTracked) { + nodesToBind.push(element) + } + }) + + that._on($(nodesToBind), { + remove: '_untrackClassesElement' + }) + } + + function processClassString (classes, checkOption) { + var current, i + for (i = 0; i < classes.length; i++) { + current = that.classesElementLookup[classes[i]] || $() + if (options.add) { + bindRemoveEvent() + current = $( + $.uniqueSort(current.get().concat(options.element.get())) + ) + } else { + current = $(current.not(options.element).get()) + } + that.classesElementLookup[classes[i]] = current + full.push(classes[i]) + if (checkOption && options.classes[classes[i]]) { + full.push(options.classes[classes[i]]) + } + } + } + + if (options.keys) { + processClassString(options.keys.match(/\S+/g) || [], true) + } + if (options.extra) { + processClassString(options.extra.match(/\S+/g) || []) + } + + return full.join(' ') + }, + + _untrackClassesElement: function (event) { + var that = this + $.each(that.classesElementLookup, function (key, value) { + if ($.inArray(event.target, value) !== -1) { + that.classesElementLookup[key] = $(value.not(event.target).get()) + } + }) + + this._off($(event.target)) + }, + + _removeClass: function (element, keys, extra) { + return this._toggleClass(element, keys, extra, false) + }, + + _addClass: function (element, keys, extra) { + return this._toggleClass(element, keys, extra, true) + }, + + _toggleClass: function (element, keys, extra, add) { + add = typeof add === 'boolean' ? add : extra + var shift = typeof element === 'string' || element === null, + options = { + extra: shift ? keys : extra, + keys: shift ? element : keys, + element: shift ? this.element : element, + add: add + } + options.element.toggleClass(this._classes(options), add) + return this + }, + + _on: function (suppressDisabledCheck, element, handlers) { + var delegateElement + var instance = this + + // No suppressDisabledCheck flag, shuffle arguments + if (typeof suppressDisabledCheck !== 'boolean') { + handlers = element + element = suppressDisabledCheck + suppressDisabledCheck = false + } + + // No element argument, shuffle and use this.element + if (!handlers) { + handlers = element + element = this.element + delegateElement = this.widget() + } else { + element = delegateElement = $(element) + this.bindings = this.bindings.add(element) + } + + $.each(handlers, function (event, handler) { + function handlerProxy () { + // Allow widgets to customize the disabled handling + // - disabled as an array instead of boolean + // - disabled class as method for disabling individual parts + if ( + !suppressDisabledCheck && + (instance.options.disabled === true || + $(this).hasClass('ui-state-disabled')) + ) { + return + } + return ( + typeof handler === 'string' ? instance[handler] : handler + ).apply(instance, arguments) + } + + // Copy the guid so direct unbinding works + if (typeof handler !== 'string') { + handlerProxy.guid = handler.guid = + handler.guid || handlerProxy.guid || $.guid++ + } + + var match = event.match(/^([\w:-]*)\s*(.*)$/) + var eventName = match[1] + instance.eventNamespace + var selector = match[2] + + if (selector) { + delegateElement.on(eventName, selector, handlerProxy) + } else { + element.on(eventName, handlerProxy) + } + }) + }, + + _off: function (element, eventName) { + eventName = + (eventName || '').split(' ').join(this.eventNamespace + ' ') + + this.eventNamespace + element.off(eventName) + + // Clear the stack to avoid memory leaks (#10056) + this.bindings = $(this.bindings.not(element).get()) + this.focusable = $(this.focusable.not(element).get()) + this.hoverable = $(this.hoverable.not(element).get()) + }, + + _delay: function (handler, delay) { + function handlerProxy () { + return ( + typeof handler === 'string' ? instance[handler] : handler + ).apply(instance, arguments) + } + var instance = this + return setTimeout(handlerProxy, delay || 0) + }, + + _hoverable: function (element) { + this.hoverable = this.hoverable.add(element) + this._on(element, { + mouseenter: function (event) { + this._addClass($(event.currentTarget), null, 'ui-state-hover') + }, + mouseleave: function (event) { + this._removeClass($(event.currentTarget), null, 'ui-state-hover') + } + }) + }, + + _focusable: function (element) { + this.focusable = this.focusable.add(element) + this._on(element, { + focusin: function (event) { + this._addClass($(event.currentTarget), null, 'ui-state-focus') + }, + focusout: function (event) { + this._removeClass($(event.currentTarget), null, 'ui-state-focus') + } + }) + }, + + _trigger: function (type, event, data) { + var prop, orig + var callback = this.options[type] + + data = data || {} + event = $.Event(event) + event.type = ( + type === this.widgetEventPrefix ? type : this.widgetEventPrefix + type + ).toLowerCase() + + // The original event may come from any element + // so we need to reset the target on the new event + event.target = this.element[0] + + // Copy original event properties over to the new event + orig = event.originalEvent + if (orig) { + for (prop in orig) { + if (!(prop in event)) { + event[prop] = orig[prop] + } + } + } + + this.element.trigger(event, data) + return !( + (typeof callback === 'function' && + callback.apply(this.element[0], [event].concat(data)) === false) || + event.isDefaultPrevented() + ) + } + } + + $.each({ show: 'fadeIn', hide: 'fadeOut' }, function (method, defaultEffect) { + $.Widget.prototype['_' + method] = function (element, options, callback) { + if (typeof options === 'string') { + options = { effect: options } + } + + var hasOptions + var effectName = !options + ? method + : options === true || typeof options === 'number' + ? defaultEffect + : options.effect || defaultEffect + + options = options || {} + if (typeof options === 'number') { + options = { duration: options } + } else if (options === true) { + options = {} + } + + hasOptions = !$.isEmptyObject(options) + options.complete = callback + + if (options.delay) { + element.delay(options.delay) + } + + if (hasOptions && $.effects && $.effects.effect[effectName]) { + element[method](options) + } else if (effectName !== method && element[effectName]) { + element[effectName](options.duration, options.easing, callback) + } else { + element.queue(function (next) { + $(this)[method]() + if (callback) { + callback.call(element[0]) + } + next() + }) + } + } + }) + + var widget = $.widget + + /*! + * jQuery UI Position 1.13.1 + * http://jqueryui.com + * + * Copyright jQuery Foundation and other contributors + * Released under the MIT license. + * http://jquery.org/license + * + * http://api.jqueryui.com/position/ + */ + + //>>label: Position + //>>group: Core + //>>description: Positions elements relative to other elements. + //>>docs: http://api.jqueryui.com/position/ + //>>demos: http://jqueryui.com/position/ + + ;(function () { + var cachedScrollbarWidth, + max = Math.max, + abs = Math.abs, + rhorizontal = /left|center|right/, + rvertical = /top|center|bottom/, + roffset = /[\+\-]\d+(\.[\d]+)?%?/, + rposition = /^\w+/, + rpercent = /%$/, + _position = $.fn.position + + function getOffsets (offsets, width, height) { + return [ + parseFloat(offsets[0]) * (rpercent.test(offsets[0]) ? width / 100 : 1), + parseFloat(offsets[1]) * (rpercent.test(offsets[1]) ? height / 100 : 1) + ] + } + + function parseCss (element, property) { + return parseInt($.css(element, property), 10) || 0 + } + + function isWindow (obj) { + return obj != null && obj === obj.window + } + + function getDimensions (elem) { + var raw = elem[0] + if (raw.nodeType === 9) { + return { + width: elem.width(), + height: elem.height(), + offset: { top: 0, left: 0 } + } + } + if (isWindow(raw)) { + return { + width: elem.width(), + height: elem.height(), + offset: { top: elem.scrollTop(), left: elem.scrollLeft() } + } + } + if (raw.preventDefault) { + return { + width: 0, + height: 0, + offset: { top: raw.pageY, left: raw.pageX } + } + } + return { + width: elem.outerWidth(), + height: elem.outerHeight(), + offset: elem.offset() + } + } + + $.position = { + scrollbarWidth: function () { + if (cachedScrollbarWidth !== undefined) { + return cachedScrollbarWidth + } + var w1, + w2, + div = $( + '
" + + "
" + ), + innerDiv = div.children()[0] + + $('body').append(div) + w1 = innerDiv.offsetWidth + div.css('overflow', 'scroll') + + w2 = innerDiv.offsetWidth + + if (w1 === w2) { + w2 = div[0].clientWidth + } + + div.remove() + + return (cachedScrollbarWidth = w1 - w2) + }, + getScrollInfo: function (within) { + var overflowX = + within.isWindow || within.isDocument + ? '' + : within.element.css('overflow-x'), + overflowY = + within.isWindow || within.isDocument + ? '' + : within.element.css('overflow-y'), + hasOverflowX = + overflowX === 'scroll' || + (overflowX === 'auto' && + within.width < within.element[0].scrollWidth), + hasOverflowY = + overflowY === 'scroll' || + (overflowY === 'auto' && + within.height < within.element[0].scrollHeight) + return { + width: hasOverflowY ? $.position.scrollbarWidth() : 0, + height: hasOverflowX ? $.position.scrollbarWidth() : 0 + } + }, + getWithinInfo: function (element) { + var withinElement = $(element || window), + isElemWindow = isWindow(withinElement[0]), + isDocument = !!withinElement[0] && withinElement[0].nodeType === 9, + hasOffset = !isElemWindow && !isDocument + return { + element: withinElement, + isWindow: isElemWindow, + isDocument: isDocument, + offset: hasOffset ? $(element).offset() : { left: 0, top: 0 }, + scrollLeft: withinElement.scrollLeft(), + scrollTop: withinElement.scrollTop(), + width: withinElement.outerWidth(), + height: withinElement.outerHeight() + } + } + } + + $.fn.position = function (options) { + if (!options || !options.of) { + return _position.apply(this, arguments) + } + + // Make a copy, we don't want to modify arguments + options = $.extend({}, options) + + var atOffset, + targetWidth, + targetHeight, + targetOffset, + basePosition, + dimensions, + // Make sure string options are treated as CSS selectors + target = + typeof options.of === 'string' + ? $(document).find(options.of) + : $(options.of), + within = $.position.getWithinInfo(options.within), + scrollInfo = $.position.getScrollInfo(within), + collision = (options.collision || 'flip').split(' '), + offsets = {} + + dimensions = getDimensions(target) + if (target[0].preventDefault) { + // Force left top to allow flipping + options.at = 'left top' + } + targetWidth = dimensions.width + targetHeight = dimensions.height + targetOffset = dimensions.offset + + // Clone to reuse original targetOffset later + basePosition = $.extend({}, targetOffset) + + // Force my and at to have valid horizontal and vertical positions + // if a value is missing or invalid, it will be converted to center + $.each(['my', 'at'], function () { + var pos = (options[this] || '').split(' '), + horizontalOffset, + verticalOffset + + if (pos.length === 1) { + pos = rhorizontal.test(pos[0]) + ? pos.concat(['center']) + : rvertical.test(pos[0]) + ? ['center'].concat(pos) + : ['center', 'center'] + } + pos[0] = rhorizontal.test(pos[0]) ? pos[0] : 'center' + pos[1] = rvertical.test(pos[1]) ? pos[1] : 'center' + + // Calculate offsets + horizontalOffset = roffset.exec(pos[0]) + verticalOffset = roffset.exec(pos[1]) + offsets[this] = [ + horizontalOffset ? horizontalOffset[0] : 0, + verticalOffset ? verticalOffset[0] : 0 + ] + + // Reduce to just the positions without the offsets + options[this] = [rposition.exec(pos[0])[0], rposition.exec(pos[1])[0]] + }) + + // Normalize collision option + if (collision.length === 1) { + collision[1] = collision[0] + } + + if (options.at[0] === 'right') { + basePosition.left += targetWidth + } else if (options.at[0] === 'center') { + basePosition.left += targetWidth / 2 + } + + if (options.at[1] === 'bottom') { + basePosition.top += targetHeight + } else if (options.at[1] === 'center') { + basePosition.top += targetHeight / 2 + } + + atOffset = getOffsets(offsets.at, targetWidth, targetHeight) + basePosition.left += atOffset[0] + basePosition.top += atOffset[1] + + return this.each(function () { + var collisionPosition, + using, + elem = $(this), + elemWidth = elem.outerWidth(), + elemHeight = elem.outerHeight(), + marginLeft = parseCss(this, 'marginLeft'), + marginTop = parseCss(this, 'marginTop'), + collisionWidth = + elemWidth + + marginLeft + + parseCss(this, 'marginRight') + + scrollInfo.width, + collisionHeight = + elemHeight + + marginTop + + parseCss(this, 'marginBottom') + + scrollInfo.height, + position = $.extend({}, basePosition), + myOffset = getOffsets( + offsets.my, + elem.outerWidth(), + elem.outerHeight() + ) + + if (options.my[0] === 'right') { + position.left -= elemWidth + } else if (options.my[0] === 'center') { + position.left -= elemWidth / 2 + } + + if (options.my[1] === 'bottom') { + position.top -= elemHeight + } else if (options.my[1] === 'center') { + position.top -= elemHeight / 2 + } + + position.left += myOffset[0] + position.top += myOffset[1] + + collisionPosition = { + marginLeft: marginLeft, + marginTop: marginTop + } + + $.each(['left', 'top'], function (i, dir) { + if ($.ui.position[collision[i]]) { + $.ui.position[collision[i]][dir](position, { + targetWidth: targetWidth, + targetHeight: targetHeight, + elemWidth: elemWidth, + elemHeight: elemHeight, + collisionPosition: collisionPosition, + collisionWidth: collisionWidth, + collisionHeight: collisionHeight, + offset: [atOffset[0] + myOffset[0], atOffset[1] + myOffset[1]], + my: options.my, + at: options.at, + within: within, + elem: elem + }) + } + }) + + if (options.using) { + // Adds feedback as second argument to using callback, if present + using = function (props) { + var left = targetOffset.left - position.left, + right = left + targetWidth - elemWidth, + top = targetOffset.top - position.top, + bottom = top + targetHeight - elemHeight, + feedback = { + target: { + element: target, + left: targetOffset.left, + top: targetOffset.top, + width: targetWidth, + height: targetHeight + }, + element: { + element: elem, + left: position.left, + top: position.top, + width: elemWidth, + height: elemHeight + }, + horizontal: right < 0 ? 'left' : left > 0 ? 'right' : 'center', + vertical: bottom < 0 ? 'top' : top > 0 ? 'bottom' : 'middle' + } + if (targetWidth < elemWidth && abs(left + right) < targetWidth) { + feedback.horizontal = 'center' + } + if (targetHeight < elemHeight && abs(top + bottom) < targetHeight) { + feedback.vertical = 'middle' + } + if (max(abs(left), abs(right)) > max(abs(top), abs(bottom))) { + feedback.important = 'horizontal' + } else { + feedback.important = 'vertical' + } + options.using.call(this, props, feedback) + } + } + + elem.offset($.extend(position, { using: using })) + }) + } + + $.ui.position = { + fit: { + left: function (position, data) { + var within = data.within, + withinOffset = within.isWindow + ? within.scrollLeft + : within.offset.left, + outerWidth = within.width, + collisionPosLeft = + position.left - data.collisionPosition.marginLeft, + overLeft = withinOffset - collisionPosLeft, + overRight = + collisionPosLeft + + data.collisionWidth - + outerWidth - + withinOffset, + newOverRight + + // Element is wider than within + if (data.collisionWidth > outerWidth) { + // Element is initially over the left side of within + if (overLeft > 0 && overRight <= 0) { + newOverRight = + position.left + + overLeft + + data.collisionWidth - + outerWidth - + withinOffset + position.left += overLeft - newOverRight + + // Element is initially over right side of within + } else if (overRight > 0 && overLeft <= 0) { + position.left = withinOffset + + // Element is initially over both left and right sides of within + } else { + if (overLeft > overRight) { + position.left = withinOffset + outerWidth - data.collisionWidth + } else { + position.left = withinOffset + } + } + + // Too far left -> align with left edge + } else if (overLeft > 0) { + position.left += overLeft + + // Too far right -> align with right edge + } else if (overRight > 0) { + position.left -= overRight + + // Adjust based on position and margin + } else { + position.left = max(position.left - collisionPosLeft, position.left) + } + }, + top: function (position, data) { + var within = data.within, + withinOffset = within.isWindow + ? within.scrollTop + : within.offset.top, + outerHeight = data.within.height, + collisionPosTop = position.top - data.collisionPosition.marginTop, + overTop = withinOffset - collisionPosTop, + overBottom = + collisionPosTop + + data.collisionHeight - + outerHeight - + withinOffset, + newOverBottom + + // Element is taller than within + if (data.collisionHeight > outerHeight) { + // Element is initially over the top of within + if (overTop > 0 && overBottom <= 0) { + newOverBottom = + position.top + + overTop + + data.collisionHeight - + outerHeight - + withinOffset + position.top += overTop - newOverBottom + + // Element is initially over bottom of within + } else if (overBottom > 0 && overTop <= 0) { + position.top = withinOffset + + // Element is initially over both top and bottom of within + } else { + if (overTop > overBottom) { + position.top = withinOffset + outerHeight - data.collisionHeight + } else { + position.top = withinOffset + } + } + + // Too far up -> align with top + } else if (overTop > 0) { + position.top += overTop + + // Too far down -> align with bottom edge + } else if (overBottom > 0) { + position.top -= overBottom + + // Adjust based on position and margin + } else { + position.top = max(position.top - collisionPosTop, position.top) + } + } + }, + flip: { + left: function (position, data) { + var within = data.within, + withinOffset = within.offset.left + within.scrollLeft, + outerWidth = within.width, + offsetLeft = within.isWindow + ? within.scrollLeft + : within.offset.left, + collisionPosLeft = + position.left - data.collisionPosition.marginLeft, + overLeft = collisionPosLeft - offsetLeft, + overRight = + collisionPosLeft + data.collisionWidth - outerWidth - offsetLeft, + myOffset = + data.my[0] === 'left' + ? -data.elemWidth + : data.my[0] === 'right' + ? data.elemWidth + : 0, + atOffset = + data.at[0] === 'left' + ? data.targetWidth + : data.at[0] === 'right' + ? -data.targetWidth + : 0, + offset = -2 * data.offset[0], + newOverRight, + newOverLeft + + if (overLeft < 0) { + newOverRight = + position.left + + myOffset + + atOffset + + offset + + data.collisionWidth - + outerWidth - + withinOffset + if (newOverRight < 0 || newOverRight < abs(overLeft)) { + position.left += myOffset + atOffset + offset + } + } else if (overRight > 0) { + newOverLeft = + position.left - + data.collisionPosition.marginLeft + + myOffset + + atOffset + + offset - + offsetLeft + if (newOverLeft > 0 || abs(newOverLeft) < overRight) { + position.left += myOffset + atOffset + offset + } + } + }, + top: function (position, data) { + var within = data.within, + withinOffset = within.offset.top + within.scrollTop, + outerHeight = within.height, + offsetTop = within.isWindow ? within.scrollTop : within.offset.top, + collisionPosTop = position.top - data.collisionPosition.marginTop, + overTop = collisionPosTop - offsetTop, + overBottom = + collisionPosTop + data.collisionHeight - outerHeight - offsetTop, + top = data.my[1] === 'top', + myOffset = top + ? -data.elemHeight + : data.my[1] === 'bottom' + ? data.elemHeight + : 0, + atOffset = + data.at[1] === 'top' + ? data.targetHeight + : data.at[1] === 'bottom' + ? -data.targetHeight + : 0, + offset = -2 * data.offset[1], + newOverTop, + newOverBottom + if (overTop < 0) { + newOverBottom = + position.top + + myOffset + + atOffset + + offset + + data.collisionHeight - + outerHeight - + withinOffset + if (newOverBottom < 0 || newOverBottom < abs(overTop)) { + position.top += myOffset + atOffset + offset + } + } else if (overBottom > 0) { + newOverTop = + position.top - + data.collisionPosition.marginTop + + myOffset + + atOffset + + offset - + offsetTop + if (newOverTop > 0 || abs(newOverTop) < overBottom) { + position.top += myOffset + atOffset + offset + } + } + } + }, + flipfit: { + left: function () { + $.ui.position.flip.left.apply(this, arguments) + $.ui.position.fit.left.apply(this, arguments) + }, + top: function () { + $.ui.position.flip.top.apply(this, arguments) + $.ui.position.fit.top.apply(this, arguments) + } + } + } + })() + + var position = $.ui.position + + /*! + * jQuery UI :data 1.13.1 + * http://jqueryui.com + * + * Copyright jQuery Foundation and other contributors + * Released under the MIT license. + * http://jquery.org/license + */ + + //>>label: :data Selector + //>>group: Core + //>>description: Selects elements which have data stored under the specified key. + //>>docs: http://api.jqueryui.com/data-selector/ + + var data = $.extend($.expr.pseudos, { + data: $.expr.createPseudo + ? $.expr.createPseudo(function (dataName) { + return function (elem) { + return !!$.data(elem, dataName) + } + }) + : // Support: jQuery <1.8 + function (elem, i, match) { + return !!$.data(elem, match[3]) + } + }) + + /*! + * jQuery UI Disable Selection 1.13.1 + * http://jqueryui.com + * + * Copyright jQuery Foundation and other contributors + * Released under the MIT license. + * http://jquery.org/license + */ + + //>>label: disableSelection + //>>group: Core + //>>description: Disable selection of text content within the set of matched elements. + //>>docs: http://api.jqueryui.com/disableSelection/ + + // This file is deprecated + + var disableSelection = $.fn.extend({ + disableSelection: (function () { + var eventType = + 'onselectstart' in document.createElement('div') + ? 'selectstart' + : 'mousedown' + + return function () { + return this.on(eventType + '.ui-disableSelection', function (event) { + event.preventDefault() + }) + } + })(), + + enableSelection: function () { + return this.off('.ui-disableSelection') + } + }) + + /*! + * jQuery UI Focusable 1.13.1 + * http://jqueryui.com + * + * Copyright jQuery Foundation and other contributors + * Released under the MIT license. + * http://jquery.org/license + */ + + //>>label: :focusable Selector + //>>group: Core + //>>description: Selects elements which can be focused. + //>>docs: http://api.jqueryui.com/focusable-selector/ + + // Selectors + $.ui.focusable = function (element, hasTabindex) { + var map, + mapName, + img, + focusableIfVisible, + fieldset, + nodeName = element.nodeName.toLowerCase() + + if ('area' === nodeName) { + map = element.parentNode + mapName = map.name + if (!element.href || !mapName || map.nodeName.toLowerCase() !== 'map') { + return false + } + img = $("img[usemap='#" + mapName + "']") + return img.length > 0 && img.is(':visible') + } + + if (/^(input|select|textarea|button|object)$/.test(nodeName)) { + focusableIfVisible = !element.disabled + + if (focusableIfVisible) { + // Form controls within a disabled fieldset are disabled. + // However, controls within the fieldset's legend do not get disabled. + // Since controls generally aren't placed inside legends, we skip + // this portion of the check. + fieldset = $(element).closest('fieldset')[0] + if (fieldset) { + focusableIfVisible = !fieldset.disabled + } + } + } else if ('a' === nodeName) { + focusableIfVisible = element.href || hasTabindex + } else { + focusableIfVisible = hasTabindex + } + + return ( + focusableIfVisible && $(element).is(':visible') && visible($(element)) + ) + } + + // Support: IE 8 only + // IE 8 doesn't resolve inherit to visible/hidden for computed values + function visible (element) { + var visibility = element.css('visibility') + while (visibility === 'inherit') { + element = element.parent() + visibility = element.css('visibility') + } + return visibility === 'visible' + } + + $.extend($.expr.pseudos, { + focusable: function (element) { + return $.ui.focusable(element, $.attr(element, 'tabindex') != null) + } + }) + + var focusable = $.ui.focusable + + // Support: IE8 Only + // IE8 does not support the form attribute and when it is supplied. It overwrites the form prop + // with a string, so we need to find the proper form. + var form = ($.fn._form = function () { + return typeof this[0].form === 'string' + ? this.closest('form') + : $(this[0].form) + }) + + /*! + * jQuery UI Form Reset Mixin 1.13.1 + * http://jqueryui.com + * + * Copyright jQuery Foundation and other contributors + * Released under the MIT license. + * http://jquery.org/license + */ + + //>>label: Form Reset Mixin + //>>group: Core + //>>description: Refresh input widgets when their form is reset + //>>docs: http://api.jqueryui.com/form-reset-mixin/ + + var formResetMixin = ($.ui.formResetMixin = { + _formResetHandler: function () { + var form = $(this) + + // Wait for the form reset to actually happen before refreshing + setTimeout(function () { + var instances = form.data('ui-form-reset-instances') + $.each(instances, function () { + this.refresh() + }) + }) + }, + + _bindFormResetHandler: function () { + this.form = this.element._form() + if (!this.form.length) { + return + } + + var instances = this.form.data('ui-form-reset-instances') || [] + if (!instances.length) { + // We don't use _on() here because we use a single event handler per form + this.form.on('reset.ui-form-reset', this._formResetHandler) + } + instances.push(this) + this.form.data('ui-form-reset-instances', instances) + }, + + _unbindFormResetHandler: function () { + if (!this.form.length) { + return + } + + var instances = this.form.data('ui-form-reset-instances') + instances.splice($.inArray(this, instances), 1) + if (instances.length) { + this.form.data('ui-form-reset-instances', instances) + } else { + this.form + .removeData('ui-form-reset-instances') + .off('reset.ui-form-reset') + } + } + }) + + /*! + * jQuery UI Support for jQuery core 1.8.x and newer 1.13.1 + * http://jqueryui.com + * + * Copyright jQuery Foundation and other contributors + * Released under the MIT license. + * http://jquery.org/license + * + */ + + //>>label: jQuery 1.8+ Support + //>>group: Core + //>>description: Support version 1.8.x and newer of jQuery core + + // Support: jQuery 1.9.x or older + // $.expr[ ":" ] is deprecated. + if (!$.expr.pseudos) { + $.expr.pseudos = $.expr[':'] + } + + // Support: jQuery 1.11.x or older + // $.unique has been renamed to $.uniqueSort + if (!$.uniqueSort) { + $.uniqueSort = $.unique + } + + // Support: jQuery 2.2.x or older. + // This method has been defined in jQuery 3.0.0. + // Code from https://github.com/jquery/jquery/blob/e539bac79e666bba95bba86d690b4e609dca2286/src/selector/escapeSelector.js + if (!$.escapeSelector) { + // CSS string/identifier serialization + // https://drafts.csswg.org/cssom/#common-serializing-idioms + var rcssescape = /([\0-\x1f\x7f]|^-?\d)|^-$|[^\x80-\uFFFF\w-]/g + + var fcssescape = function (ch, asCodePoint) { + if (asCodePoint) { + // U+0000 NULL becomes U+FFFD REPLACEMENT CHARACTER + if (ch === '\0') { + return '\uFFFD' + } + + // Control characters and (dependent upon position) numbers get escaped as code points + return ( + ch.slice(0, -1) + + '\\' + + ch.charCodeAt(ch.length - 1).toString(16) + + ' ' + ) + } + + // Other potentially-special ASCII characters get backslash-escaped + return '\\' + ch + } + + $.escapeSelector = function (sel) { + return (sel + '').replace(rcssescape, fcssescape) + } + } + + // Support: jQuery 3.4.x or older + // These methods have been defined in jQuery 3.5.0. + if (!$.fn.even || !$.fn.odd) { + $.fn.extend({ + even: function () { + return this.filter(function (i) { + return i % 2 === 0 + }) + }, + odd: function () { + return this.filter(function (i) { + return i % 2 === 1 + }) + } + }) + } + + /*! + * jQuery UI Keycode 1.13.1 + * http://jqueryui.com + * + * Copyright jQuery Foundation and other contributors + * Released under the MIT license. + * http://jquery.org/license + */ + + //>>label: Keycode + //>>group: Core + //>>description: Provide keycodes as keynames + //>>docs: http://api.jqueryui.com/jQuery.ui.keyCode/ + + var keycode = ($.ui.keyCode = { + BACKSPACE: 8, + COMMA: 188, + DELETE: 46, + DOWN: 40, + END: 35, + ENTER: 13, + ESCAPE: 27, + HOME: 36, + LEFT: 37, + PAGE_DOWN: 34, + PAGE_UP: 33, + PERIOD: 190, + RIGHT: 39, + SPACE: 32, + TAB: 9, + UP: 38 + }) + + /*! + * jQuery UI Labels 1.13.1 + * http://jqueryui.com + * + * Copyright jQuery Foundation and other contributors + * Released under the MIT license. + * http://jquery.org/license + */ + + //>>label: labels + //>>group: Core + //>>description: Find all the labels associated with a given input + //>>docs: http://api.jqueryui.com/labels/ + + var labels = ($.fn.labels = function () { + var ancestor, selector, id, labels, ancestors + + if (!this.length) { + return this.pushStack([]) + } + + // Check control.labels first + if (this[0].labels && this[0].labels.length) { + return this.pushStack(this[0].labels) + } + + // Support: IE <= 11, FF <= 37, Android <= 2.3 only + // Above browsers do not support control.labels. Everything below is to support them + // as well as document fragments. control.labels does not work on document fragments + labels = this.eq(0).parents('label') + + // Look for the label based on the id + id = this.attr('id') + if (id) { + // We don't search against the document in case the element + // is disconnected from the DOM + ancestor = this.eq(0).parents().last() + + // Get a full set of top level ancestors + ancestors = ancestor.add( + ancestor.length ? ancestor.siblings() : this.siblings() + ) + + // Create a selector for the label based on the id + selector = "label[for='" + $.escapeSelector(id) + "']" + + labels = labels.add(ancestors.find(selector).addBack(selector)) + } + + // Return whatever we have found for labels + return this.pushStack(labels) + }) + + /*! + * jQuery UI Scroll Parent 1.13.1 + * http://jqueryui.com + * + * Copyright jQuery Foundation and other contributors + * Released under the MIT license. + * http://jquery.org/license + */ + + //>>label: scrollParent + //>>group: Core + //>>description: Get the closest ancestor element that is scrollable. + //>>docs: http://api.jqueryui.com/scrollParent/ + + var scrollParent = ($.fn.scrollParent = function (includeHidden) { + var position = this.css('position'), + excludeStaticParent = position === 'absolute', + overflowRegex = includeHidden ? /(auto|scroll|hidden)/ : /(auto|scroll)/, + scrollParent = this.parents() + .filter(function () { + var parent = $(this) + if (excludeStaticParent && parent.css('position') === 'static') { + return false + } + return overflowRegex.test( + parent.css('overflow') + + parent.css('overflow-y') + + parent.css('overflow-x') + ) + }) + .eq(0) + + return position === 'fixed' || !scrollParent.length + ? $(this[0].ownerDocument || document) + : scrollParent + }) + + /*! + * jQuery UI Tabbable 1.13.1 + * http://jqueryui.com + * + * Copyright jQuery Foundation and other contributors + * Released under the MIT license. + * http://jquery.org/license + */ + + //>>label: :tabbable Selector + //>>group: Core + //>>description: Selects elements which can be tabbed to. + //>>docs: http://api.jqueryui.com/tabbable-selector/ + + var tabbable = $.extend($.expr.pseudos, { + tabbable: function (element) { + var tabIndex = $.attr(element, 'tabindex'), + hasTabindex = tabIndex != null + return ( + (!hasTabindex || tabIndex >= 0) && $.ui.focusable(element, hasTabindex) + ) + } + }) + + /*! + * jQuery UI Unique ID 1.13.1 + * http://jqueryui.com + * + * Copyright jQuery Foundation and other contributors + * Released under the MIT license. + * http://jquery.org/license + */ + + //>>label: uniqueId + //>>group: Core + //>>description: Functions to generate and remove uniqueId's + //>>docs: http://api.jqueryui.com/uniqueId/ + + var uniqueId = $.fn.extend({ + uniqueId: (function () { + var uuid = 0 + + return function () { + return this.each(function () { + if (!this.id) { + this.id = 'ui-id-' + ++uuid + } + }) + } + })(), + + removeUniqueId: function () { + return this.each(function () { + if (/^ui-id-\d+$/.test(this.id)) { + $(this).removeAttr('id') + } + }) + } + }) + + // This file is deprecated + var ie = ($.ui.ie = !!/msie [\w.]+/.exec(navigator.userAgent.toLowerCase())) + + /*! + * jQuery UI Mouse 1.13.1 + * http://jqueryui.com + * + * Copyright jQuery Foundation and other contributors + * Released under the MIT license. + * http://jquery.org/license + */ + + //>>label: Mouse + //>>group: Widgets + //>>description: Abstracts mouse-based interactions to assist in creating certain widgets. + //>>docs: http://api.jqueryui.com/mouse/ + + var mouseHandled = false + $(document).on('mouseup', function () { + mouseHandled = false + }) + + var widgetsMouse = $.widget('ui.mouse', { + version: '1.13.1', + options: { + cancel: 'input, textarea, button, select, option', + distance: 1, + delay: 0 + }, + _mouseInit: function () { + var that = this + + this.element + .on('mousedown.' + this.widgetName, function (event) { + return that._mouseDown(event) + }) + .on('click.' + this.widgetName, function (event) { + if ( + true === + $.data(event.target, that.widgetName + '.preventClickEvent') + ) { + $.removeData(event.target, that.widgetName + '.preventClickEvent') + event.stopImmediatePropagation() + return false + } + }) + + this.started = false + }, + + // TODO: make sure destroying one instance of mouse doesn't mess with + // other instances of mouse + _mouseDestroy: function () { + this.element.off('.' + this.widgetName) + if (this._mouseMoveDelegate) { + this.document + .off('mousemove.' + this.widgetName, this._mouseMoveDelegate) + .off('mouseup.' + this.widgetName, this._mouseUpDelegate) + } + }, + + _mouseDown: function (event) { + // don't let more than one widget handle mouseStart + if (mouseHandled) { + return + } + + this._mouseMoved = false + + // We may have missed mouseup (out of window) + if (this._mouseStarted) { + this._mouseUp(event) + } + + this._mouseDownEvent = event + + var that = this, + btnIsLeft = event.which === 1, + // event.target.nodeName works around a bug in IE 8 with + // disabled inputs (#7620) + elIsCancel = + typeof this.options.cancel === 'string' && event.target.nodeName + ? $(event.target).closest(this.options.cancel).length + : false + if (!btnIsLeft || elIsCancel || !this._mouseCapture(event)) { + return true + } + + this.mouseDelayMet = !this.options.delay + if (!this.mouseDelayMet) { + this._mouseDelayTimer = setTimeout(function () { + that.mouseDelayMet = true + }, this.options.delay) + } + + if (this._mouseDistanceMet(event) && this._mouseDelayMet(event)) { + this._mouseStarted = this._mouseStart(event) !== false + if (!this._mouseStarted) { + event.preventDefault() + return true + } + } + + // Click event may never have fired (Gecko & Opera) + if ( + true === $.data(event.target, this.widgetName + '.preventClickEvent') + ) { + $.removeData(event.target, this.widgetName + '.preventClickEvent') + } + + // These delegates are required to keep context + this._mouseMoveDelegate = function (event) { + return that._mouseMove(event) + } + this._mouseUpDelegate = function (event) { + return that._mouseUp(event) + } + + this.document + .on('mousemove.' + this.widgetName, this._mouseMoveDelegate) + .on('mouseup.' + this.widgetName, this._mouseUpDelegate) + + event.preventDefault() + + mouseHandled = true + return true + }, + + _mouseMove: function (event) { + // Only check for mouseups outside the document if you've moved inside the document + // at least once. This prevents the firing of mouseup in the case of IE<9, which will + // fire a mousemove event if content is placed under the cursor. See #7778 + // Support: IE <9 + if (this._mouseMoved) { + // IE mouseup check - mouseup happened when mouse was out of window + if ( + $.ui.ie && + (!document.documentMode || document.documentMode < 9) && + !event.button + ) { + return this._mouseUp(event) + + // Iframe mouseup check - mouseup occurred in another document + } else if (!event.which) { + // Support: Safari <=8 - 9 + // Safari sets which to 0 if you press any of the following keys + // during a drag (#14461) + if ( + event.originalEvent.altKey || + event.originalEvent.ctrlKey || + event.originalEvent.metaKey || + event.originalEvent.shiftKey + ) { + this.ignoreMissingWhich = true + } else if (!this.ignoreMissingWhich) { + return this._mouseUp(event) + } + } + } + + if (event.which || event.button) { + this._mouseMoved = true + } + + if (this._mouseStarted) { + this._mouseDrag(event) + return event.preventDefault() + } + + if (this._mouseDistanceMet(event) && this._mouseDelayMet(event)) { + this._mouseStarted = + this._mouseStart(this._mouseDownEvent, event) !== false + if (this._mouseStarted) { + this._mouseDrag(event) + } else { + this._mouseUp(event) + } + } + + return !this._mouseStarted + }, + + _mouseUp: function (event) { + this.document + .off('mousemove.' + this.widgetName, this._mouseMoveDelegate) + .off('mouseup.' + this.widgetName, this._mouseUpDelegate) + + if (this._mouseStarted) { + this._mouseStarted = false + + if (event.target === this._mouseDownEvent.target) { + $.data(event.target, this.widgetName + '.preventClickEvent', true) + } + + this._mouseStop(event) + } + + if (this._mouseDelayTimer) { + clearTimeout(this._mouseDelayTimer) + delete this._mouseDelayTimer + } + + this.ignoreMissingWhich = false + mouseHandled = false + event.preventDefault() + }, + + _mouseDistanceMet: function (event) { + return ( + Math.max( + Math.abs(this._mouseDownEvent.pageX - event.pageX), + Math.abs(this._mouseDownEvent.pageY - event.pageY) + ) >= this.options.distance + ) + }, + + _mouseDelayMet: function (/* event */) { + return this.mouseDelayMet + }, + + // These are placeholder methods, to be overriden by extending plugin + _mouseStart: function (/* event */) {}, + _mouseDrag: function (/* event */) {}, + _mouseStop: function (/* event */) {}, + _mouseCapture: function (/* event */) { + return true + } + }) + + // $.ui.plugin is deprecated. Use $.widget() extensions instead. + var plugin = ($.ui.plugin = { + add: function (module, option, set) { + var i, + proto = $.ui[module].prototype + for (i in set) { + proto.plugins[i] = proto.plugins[i] || [] + proto.plugins[i].push([option, set[i]]) + } + }, + call: function (instance, name, args, allowDisconnected) { + var i, + set = instance.plugins[name] + + if (!set) { + return + } + + if ( + !allowDisconnected && + (!instance.element[0].parentNode || + instance.element[0].parentNode.nodeType === 11) + ) { + return + } + + for (i = 0; i < set.length; i++) { + if (instance.options[set[i][0]]) { + set[i][1].apply(instance.element, args) + } + } + } + }) + + var safeActiveElement = ($.ui.safeActiveElement = function (document) { + var activeElement + + // Support: IE 9 only + // IE9 throws an "Unspecified error" accessing document.activeElement from an