1/*! 2 * Select2 4.1.0-rc
vendor: 17,404 bytes, lines 2-501
2.0 3 * https://select2.github.io 4 * 5 * Released under the MIT license 6 * https://github.com/select2/select2/blob/master/LICENSE.md 7 */ 8;(function (factory) { 9 if (typeof define === 'function' && define.amd) { 10 // AMD. Register as an anonymous module. 11 define(['jquery'], factory); 12 } else if (typeof module === 'object' && module.exports) { 13 // Node/CommonJS 14 module.exports = function (root, jQuery) { 15 if (jQuery === undefined) { 16 // require('jQuery') returns a factory that requires window to 17 // build a jQuery instance, we normalize how we use modules 18 // that require this pattern but the window provided is a noop 19 // if it's defined (how jquery works) 20 if (typeof window !== 'undefined') { 21 jQuery = require('jquery'); 22 } 23 else { 24 jQuery = require('jquery')(root); 25 } 26 } 27 factory(jQuery); 28 return jQuery; 29 }; 30 } else { 31 // Browser globals 32 factory(jQuery); 33 } 34} (function (jQuery) { 35 // This is needed so we can catch the AMD loader configuration and use it 36 // The inner file should be wrapped (by `banner.start.js`) in a function that 37 // returns the AMD loader references. 38 var S2 =(function () { 39 // Restore the Select2 AMD loader so it can be used 40 // Needed mostly in the language files, where the loader is not inserted 41 if (jQuery && jQuery.fn && jQuery.fn.select2 && jQuery.fn.select2.amd) { 42 var S2 = jQuery.fn.select2.amd; 43 } 44var S2;(function () { if (!S2 || !S2.requirejs) { 45if (!S2) { S2 = {}; } else { require = S2; } 46/** 47 * @license almond 0.3.3 Copyright jQuery Foundation and other contributors. 48 * Released under MIT license, http://github.com/requirejs/almond/LICENSE 49 */ 50//Going sloppy to avoid 'use strict' string cost, but strict practices should 51//be followed. 52/*global setTimeout: false */ 53 54var requirejs, require, define; 55(function (undef) { 56 var main, req, makeMap, handlers, 57 defined = {}, 58 waiting = {}, 59 config = {}, 60 defining = {}, 61 hasOwn = Object.prototype.hasOwnProperty, 62 aps = [].slice, 63 jsSuffixRegExp = /\.js$/; 64 65 function hasProp(obj, prop) { 66 return hasOwn.call(obj, prop); 67 } 68 69 /** 70 * Given a relative module name, like ./something, normalize it to 71 * a real name that can be mapped to a path. 72 * @param {String} name the relative name 73 * @param {String} baseName a real name that the name arg is relative 74 * to. 75 * @returns {String} normalized name 76 */ 77 function normalize(name, baseName) { 78 var nameParts, nameSegment, mapValue, foundMap, lastIndex, 79 foundI, foundStarMap, starI, i, j, part, normalizedBaseParts, 80 baseParts = baseName && baseName.split("/"), 81 map = config.map, 82 starMap = (map && map['*']) || {}; 83 84 //Adjust any relative paths. 85 if (name) { 86 name = name.split('/'); 87 lastIndex = name.length - 1; 88 89 // If wanting node ID compatibility, strip .js from end 90 // of IDs. Have to do this here, and not in nameToUrl 91 // because node allows either .js or non .js to map 92 // to same file. 93 if (config.nodeIdCompat && jsSuffixRegExp.test(name[lastIndex])) { 94 name[lastIndex] = name[lastIndex].replace(jsSuffixRegExp, ''); 95 } 96 97 // Starts with a '.' so need the baseName 98 if (name[0].charAt(0) === '.' && baseParts) { 99 //Convert baseName to array, and lop off the last part, 100 //so that . matches that 'directory' and not name of the baseName's 101 //module. For instance, baseName of 'one/two/three', maps to 102 //'one/two/three.js', but we want the directory, 'one/two' for 103 //this normalization. 104 normalizedBaseParts = baseParts.slice(0, baseParts.length - 1); 105 name = normalizedBaseParts.concat(name); 106 } 107 108 //start trimDots 109 for (i = 0; i < name.length; i++) { 110 part = name[i]; 111 if (part === '.') { 112 name.splice(i, 1); 113 i -= 1; 114 } else if (part === '..') { 115 // If at the start, or previous value is still .., 116 // keep them so that when converted to a path it may 117 // still work when converted to a path, even though 118 // as an ID it is less than ideal. In larger point 119 // releases, may be better to just kick out an error. 120 if (i === 0 || (i === 1 && name[2] === '..') || name[i - 1] === '..') { 121 continue; 122 } else if (i > 0) { 123 name.splice(i - 1, 2); 124 i -= 2; 125 } 126 } 127 } 128 //end trimDots 129 130 name = name.join('/'); 131 } 132 133 //Apply map config if available. 134 if ((baseParts || starMap) && map) { 135 nameParts = name.split('/'); 136 137 for (i = nameParts.length; i > 0; i -= 1) { 138 nameSegment = nameParts.slice(0, i).join("/"); 139 140 if (baseParts) { 141 //Find the longest baseName segment match in the config. 142 //So, do joins on the biggest to smallest lengths of baseParts. 143 for (j = baseParts.length; j > 0; j -= 1) { 144 mapValue = map[baseParts.slice(0, j).join('/')]; 145 146 //baseName segment has config, find if it has one for 147 //this name. 148 if (mapValue) { 149 mapValue = mapValue[nameSegment]; 150 if (mapValue) { 151 //Match, update name to the new value. 152 foundMap = mapValue; 153 foundI = i; 154 break; 155 } 156 } 157 } 158 } 159 160 if (foundMap) { 161 break; 162 } 163 164 //Check for a star map match, but just hold on to it, 165 //if there is a shorter segment match later in a matching 166 //config, then favor over this star map. 167 if (!foundStarMap && starMap && starMap[nameSegment]) { 168 foundStarMap = starMap[nameSegment]; 169 starI = i; 170 } 171 } 172 173 if (!foundMap && foundStarMap) { 174 foundMap = foundStarMap; 175 foundI = starI; 176 } 177 178 if (foundMap) { 179 nameParts.splice(0, foundI, foundMap); 180 name = nameParts.join('/'); 181 } 182 } 183 184 return name; 185 } 186 187 function makeRequire(relName, forceSync) { 188 return function () { 189 //A version of a require function that passes a moduleName 190 //value for items that may need to 191 //look up paths relative to the moduleName 192 var args = aps.call(arguments, 0); 193 194 //If first arg is not require('string'), and there is only 195 //one arg, it is the array form without a callback. Insert 196 //a null so that the following concat is correct. 197 if (typeof args[0] !== 'string' && args.length === 1) { 198 args.push(null); 199 } 200 return req.apply(undef, args.concat([relName, forceSync])); 201 }; 202 } 203 204 function makeNormalize(relName) { 205 return function (name) { 206 return normalize(name, relName); 207 }; 208 } 209 210 function makeLoad(depName) { 211 return function (value) { 212 defined[depName] = value; 213 }; 214 } 215 216 function callDep(name) { 217 if (hasProp(waiting, name)) { 218 var args = waiting[name]; 219 delete waiting[name]; 220 defining[name] = true; 221 main.apply(undef, args); 222 } 223 224 if (!hasProp(defined, name) && !hasProp(defining, name)) { 225 throw new Error('No ' + name); 226 } 227 return defined[name]; 228 } 229 230 //Turns a plugin!resource to [plugin, resource] 231 //with the plugin being undefined if the name 232 //did not have a plugin prefix. 233 function splitPrefix(name) { 234 var prefix, 235 index = name ? name.indexOf('!') : -1; 236 if (index > -1) { 237 prefix = name.substring(0, index); 238 name = name.substring(index + 1, name.length); 239 } 240 return [prefix, name]; 241 } 242 243 //Creates a parts array for a relName where first part is plugin ID, 244 //second part is resource ID. Assumes relName has already been normalized. 245 function makeRelParts(relName) { 246 return relName ? splitPrefix(relName) : []; 247 } 248 249 /** 250 * Makes a name map, normalizing the name, and using a plugin 251 * for normalization if necessary. Grabs a ref to plugin 252 * too, as an optimization. 253 */ 254 makeMap = function (name, relParts) { 255 var plugin, 256 parts = splitPrefix(name), 257 prefix = parts[0], 258 relResourceName = relParts[1]; 259 260 name = parts[1]; 261 262 if (prefix) { 263 prefix = normalize(prefix, relResourceName); 264 plugin = callDep(prefix); 265 } 266 267 //Normalize according 268 if (prefix) { 269 if (plugin && plugin.normalize) { 270 name = plugin.normalize(name, makeNormalize(relResourceName)); 271 } else { 272 name = normalize(name, relResourceName); 273 } 274 } else { 275 name = normalize(name, relResourceName); 276 parts = splitPrefix(name); 277 prefix = parts[0]; 278 name = parts[1]; 279 if (prefix) { 280 plugin = callDep(prefix); 281 } 282 } 283 284 //Using ridiculous property names for space reasons 285 return { 286 f: prefix ? prefix + '!' + name : name, //fullName 287 n: name, 288 pr: prefix, 289 p: plugin 290 }; 291 }; 292 293 function makeConfig(name) { 294 return function () { 295 return (config && config.config && config.config[name]) || {}; 296 }; 297 } 298 299 handlers = { 300 require: function (name) { 301 return makeRequire(name); 302 }, 303 exports: function (name) { 304 var e = defined[name]; 305 if (typeof e !== 'undefined') { 306 return e; 307 } else { 308 return (defined[name] = {}); 309 } 310 }, 311 module: function (name) { 312 return { 313 id: name, 314 uri: '', 315 exports: defined[name], 316 config: makeConfig(name) 317 }; 318 } 319 }; 320 321 main = function (name, deps, callback, relName) { 322 var cjsModule, depName, ret, map, i, relParts, 323 args = [], 324 callbackType = typeof callback, 325 usingExports; 326 327 //Use name if no relName 328 relName = relName || name; 329 relParts = makeRelParts(relName); 330 331 //Call the callback to define the module, if necessary. 332 if (callbackType === 'undefined' || callbackType === 'function') { 333 //Pull out the defined dependencies and pass the ordered 334 //values to the callback. 335 //Default to [require, exports, module] if no deps 336 deps = !deps.length && callback.length ? ['require', 'exports', 'module'] : deps; 337 for (i = 0; i < deps.length; i += 1) { 338 map = makeMap(deps[i], relParts); 339 depName = map.f; 340 341 //Fast path CommonJS standard dependencies. 342 if (depName === "require") { 343 args[i] = handlers.require(name); 344 } else if (depName === "exports") { 345 //CommonJS module spec 1.1 346 args[i] = handlers.exports(name); 347 usingExports = true; 348 } else if (depName === "module") { 349 //CommonJS module spec 1.1 350 cjsModule = args[i] = handlers.module(name); 351 } else if (hasProp(defined, depName) || 352 hasProp(waiting, depName) || 353 hasProp(defining, depName)) { 354 args[i] = callDep(depName); 355 } else if (map.p) { 356 map.p.load(map.n, makeRequire(relName, true), makeLoad(depName), {}); 357 args[i] = defined[depName]; 358 } else { 359 throw new Error(name + ' missing ' + depName); 360 } 361 } 362 363 ret = callback ? callback.apply(defined[name], args) : undefined; 364 365 if (name) { 366 //If setting exports via "module" is in play, 367 //favor that over return value and exports. After that, 368 //favor a non-undefined return value over exports use. 369 if (cjsModule && cjsModule.exports !== undef && 370 cjsModule.exports !== defined[name]) { 371 defined[name] = cjsModule.exports; 372 } else if (ret !== undef || !usingExports) { 373 //Use the return value from the function. 374 defined[name] = ret; 375 } 376 } 377 } else if (name) { 378 //May just be an object definition for the module. Only 379 //worry about defining if have a module name. 380 defined[name] = callback; 381 } 382 }; 383 384 requirejs = require = req = function (deps, callback, relName, forceSync, alt) { 385 if (typeof deps === "string") { 386 if (handlers[deps]) { 387 //callback in this case is really relName 388 return handlers[deps](callback); 389 } 390 //Just return the module wanted. In this scenario, the 391 //deps arg is the module name, and second arg (if passed) 392 //is just the relName. 393 //Normalize module name, if it contains . or .. 394 return callDep(makeMap(deps, makeRelParts(callback)).f); 395 } else if (!deps.splice) { 396 //deps is a config object, not an array. 397 config = deps; 398 if (config.deps) { 399 req(config.deps, config.callback); 400 } 401 if (!callback) { 402 return; 403 } 404 405 if (callback.splice) { 406 //callback is an array, which means it is a dependency list. 407 //Adjust args if there are dependencies 408 deps = callback; 409 callback = relName; 410 relName = null; 411 } else { 412 deps = undef; 413 } 414 } 415 416 //Support require(['a']) 417 callback = callback || function () {}; 418 419 //If relName is a function, it is an errback handler, 420 //so remove it. 421 if (typeof relName === 'function') { 422 relName = forceSync; 423 forceSync = alt; 424 } 425 426 //Simulate async callback; 427 if (forceSync) { 428 main(undef, deps, callback, relName); 429 } else { 430 //Using a non-zero value because of concern for what old browsers 431 //do, and latest browsers "upgrade" to 4 if lower value is used: 432 //http://www.whatwg.org/specs/web-apps/current-work/multipage/timers.html#dom-windowtimers-settimeout: 433 //If want a value immediately, use require('id') instead -- something 434 //that works in almond on the global level, but not guaranteed and 435 //unlikely to work in other AMD implementations. 436 setTimeout(function () { 437 main(undef, deps, callback, relName); 438 }, 4); 439 } 440 441 return req; 442 }; 443 444 /** 445 * Just drops the config on the floor, but returns req in case 446 * the config return value is used. 447 */ 448 req.config = function (cfg) { 449 return req(cfg); 450 }; 451 452 /** 453 * Expose module registry for debugging and tooling 454 */ 455 requirejs._defined = defined; 456 457 define = function (name, deps, callback) { 458 if (typeof name !== 'string') { 459 throw new Error('See almond README: incorrect module build, no module name'); 460 } 461 462 //This module may not have dependencies 463 if (!deps.splice) { 464 //deps is not an array, so probably means 465 //an object literal or factory function for 466 //the value. Adjust args. 467 callback = deps; 468 deps = []; 469 } 470 471 if (!hasProp(defined, name) && !hasProp(waiting, name)) { 472 waiting[name] = [name, deps, callback]; 473 } 474 }; 475 476 define.amd = { 477 jQuery: true 478 }; 479}()); 480 481S2.requirejs = requirejs;S2.require = require;S2.define = define; 482} 483}()); 484S2.define("almond", function(){}); 485 486/* global jQuery:false, $:false */ 487S2.define('jquery',[],function () { 488 var _$ = jQuery || $; 489 490 if (_$ == null && console && console.error) { 491 console.error( 492 'Select2: An instance of jQuery or a jQuery-compatible library was not ' + 493 'found. Make sure that you are including jQuery before Select2 on your ' + 494 'web page.' 495 ); 496 } 497 498 return _$; 499}); 500 501S2.define('select2/utils',[
vendor: 105,775 bytes, lines 502-4914
502 'jquery' 503], function ($) { 504 var Utils = {}; 505 506 Utils.Extend = function (ChildClass, SuperClass) { 507 var __hasProp = {}.hasOwnProperty; 508 509 function BaseConstructor () { 510 this.constructor = ChildClass; 511 } 512 513 for (var key in SuperClass) { 514 if (__hasProp.call(SuperClass, key)) { 515 ChildClass[key] = SuperClass[key]; 516 } 517 } 518 519 BaseConstructor.prototype = SuperClass.prototype; 520 ChildClass.prototype = new BaseConstructor(); 521 ChildClass.__super__ = SuperClass.prototype; 522 523 return ChildClass; 524 }; 525 526 function getMethods (theClass) { 527 var proto = theClass.prototype; 528 529 var methods = []; 530 531 for (var methodName in proto) { 532 var m = proto[methodName]; 533 534 if (typeof m !== 'function') { 535 continue; 536 } 537 538 if (methodName === 'constructor') { 539 continue; 540 } 541 542 methods.push(methodName); 543 } 544 545 return methods; 546 } 547 548 Utils.Decorate = function (SuperClass, DecoratorClass) { 549 var decoratedMethods = getMethods(DecoratorClass); 550 var superMethods = getMethods(SuperClass); 551 552 function DecoratedClass () { 553 var unshift = Array.prototype.unshift; 554 555 var argCount = DecoratorClass.prototype.constructor.length; 556 557 var calledConstructor = SuperClass.prototype.constructor; 558 559 if (argCount > 0) { 560 unshift.call(arguments, SuperClass.prototype.constructor); 561 562 calledConstructor = DecoratorClass.prototype.constructor; 563 } 564 565 calledConstructor.apply(this, arguments); 566 } 567 568 DecoratorClass.displayName = SuperClass.displayName; 569 570 function ctr () { 571 this.constructor = DecoratedClass; 572 } 573 574 DecoratedClass.prototype = new ctr(); 575 576 for (var m = 0; m < superMethods.length; m++) { 577 var superMethod = superMethods[m]; 578 579 DecoratedClass.prototype[superMethod] = 580 SuperClass.prototype[superMethod]; 581 } 582 583 var calledMethod = function (methodName) { 584 // Stub out the original method if it's not decorating an actual method 585 var originalMethod = function () {}; 586 587 if (methodName in DecoratedClass.prototype) { 588 originalMethod = DecoratedClass.prototype[methodName]; 589 } 590 591 var decoratedMethod = DecoratorClass.prototype[methodName]; 592 593 return function () { 594 var unshift = Array.prototype.unshift; 595 596 unshift.call(arguments, originalMethod); 597 598 return decoratedMethod.apply(this, arguments); 599 }; 600 }; 601 602 for (var d = 0; d < decoratedMethods.length; d++) { 603 var decoratedMethod = decoratedMethods[d]; 604 605 DecoratedClass.prototype[decoratedMethod] = calledMethod(decoratedMethod); 606 } 607 608 return DecoratedClass; 609 }; 610 611 var Observable = function () { 612 this.listeners = {}; 613 }; 614 615 Observable.prototype.on = function (event, callback) { 616 this.listeners = this.listeners || {}; 617 618 if (event in this.listeners) { 619 this.listeners[event].push(callback); 620 } else { 621 this.listeners[event] = [callback]; 622 } 623 }; 624 625 Observable.prototype.trigger = function (event) { 626 var slice = Array.prototype.slice; 627 var params = slice.call(arguments, 1); 628 629 this.listeners = this.listeners || {}; 630 631 // Params should always come in as an array 632 if (params == null) { 633 params = []; 634 } 635 636 // If there are no arguments to the event, use a temporary object 637 if (params.length === 0) { 638 params.push({}); 639 } 640 641 // Set the `_type` of the first object to the event 642 params[0]._type = event; 643 644 if (event in this.listeners) { 645 this.invoke(this.listeners[event], slice.call(arguments, 1)); 646 } 647 648 if ('*' in this.listeners) { 649 this.invoke(this.listeners['*'], arguments); 650 } 651 }; 652 653 Observable.prototype.invoke = function (listeners, params) { 654 for (var i = 0, len = listeners.length; i < len; i++) { 655 listeners[i].apply(this, params); 656 } 657 }; 658 659 Utils.Observable = Observable; 660 661 Utils.generateChars = function (length) { 662 var chars = ''; 663 664 for (var i = 0; i < length; i++) { 665 var randomChar = Math.floor(Math.random() * 36); 666 chars += randomChar.toString(36); 667 } 668 669 return chars; 670 }; 671 672 Utils.bind = function (func, context) { 673 return function () { 674 func.apply(context, arguments); 675 }; 676 }; 677 678 Utils._convertData = function (data) { 679 for (var originalKey in data) { 680 var keys = originalKey.split('-'); 681 682 var dataLevel = data; 683 684 if (keys.length === 1) { 685 continue; 686 } 687 688 for (var k = 0; k < keys.length; k++) { 689 var key = keys[k]; 690 691 // Lowercase the first letter 692 // By default, dash-separated becomes camelCase 693 key = key.substring(0, 1).toLowerCase() + key.substring(1); 694 695 if (!(key in dataLevel)) { 696 dataLevel[key] = {}; 697 } 698 699 if (k == keys.length - 1) { 700 dataLevel[key] = data[originalKey]; 701 } 702 703 dataLevel = dataLevel[key]; 704 } 705 706 delete data[originalKey]; 707 } 708 709 return data; 710 }; 711 712 Utils.hasScroll = function (index, el) { 713 // Adapted from the function created by @ShadowScripter 714 // and adapted by @BillBarry on the Stack Exchange Code Review website. 715 // The original code can be found at 716 // http://codereview.stackexchange.com/q/13338 717 // and was designed to be used with the Sizzle selector engine. 718 719 var $el = $(el); 720 var overflowX = el.style.overflowX; 721 var overflowY = el.style.overflowY; 722 723 //Check both x and y declarations 724 if (overflowX === overflowY && 725 (overflowY === 'hidden' || overflowY === 'visible')) { 726 return false; 727 } 728 729 if (overflowX === 'scroll' || overflowY === 'scroll') { 730 return true; 731 } 732 733 return ($el.innerHeight() < el.scrollHeight || 734 $el.innerWidth() < el.scrollWidth); 735 }; 736 737 Utils.escapeMarkup = function (markup) { 738 var replaceMap = { 739 '\\': '\', 740 '&': '&', 741 '<': '<', 742 '>': '>', 743 '"': '"', 744 '\'': ''', 745 '/': '/' 746 }; 747 748 // Do not try to escape the markup if it's not a string 749 if (typeof markup !== 'string') { 750 return markup; 751 } 752 753 return String(markup).replace(/[&<>"'\/\\]/g, function (match) { 754 return replaceMap[match]; 755 }); 756 }; 757 758 // Cache objects in Utils.__cache instead of $.data (see #4346) 759 Utils.__cache = {}; 760 761 var id = 0; 762 Utils.GetUniqueElementId = function (element) { 763 // Get a unique element Id. If element has no id, 764 // creates a new unique number, stores it in the id 765 // attribute and returns the new id with a prefix. 766 // If an id already exists, it simply returns it with a prefix. 767 768 var select2Id = element.getAttribute('data-select2-id'); 769 770 if (select2Id != null) { 771 return select2Id; 772 } 773 774 // If element has id, use it. 775 if (element.id) { 776 select2Id = 'select2-data-' + element.id; 777 } else { 778 select2Id = 'select2-data-' + (++id).toString() + 779 '-' + Utils.generateChars(4); 780 } 781 782 element.setAttribute('data-select2-id', select2Id); 783 784 return select2Id; 785 }; 786 787 Utils.StoreData = function (element, name, value) { 788 // Stores an item in the cache for a specified element. 789 // name is the cache key. 790 var id = Utils.GetUniqueElementId(element); 791 if (!Utils.__cache[id]) { 792 Utils.__cache[id] = {}; 793 } 794 795 Utils.__cache[id][name] = value; 796 }; 797 798 Utils.GetData = function (element, name) { 799 // Retrieves a value from the cache by its key (name) 800 // name is optional. If no name specified, return 801 // all cache items for the specified element. 802 // and for a specified element. 803 var id = Utils.GetUniqueElementId(element); 804 if (name) { 805 if (Utils.__cache[id]) { 806 if (Utils.__cache[id][name] != null) { 807 return Utils.__cache[id][name]; 808 } 809 return $(element).data(name); // Fallback to HTML5 data attribs. 810 } 811 return $(element).data(name); // Fallback to HTML5 data attribs. 812 } else { 813 return Utils.__cache[id]; 814 } 815 }; 816 817 Utils.RemoveData = function (element) { 818 // Removes all cached items for a specified element. 819 var id = Utils.GetUniqueElementId(element); 820 if (Utils.__cache[id] != null) { 821 delete Utils.__cache[id]; 822 } 823 824 element.removeAttribute('data-select2-id'); 825 }; 826 827 Utils.copyNonInternalCssClasses = function (dest, src) { 828 var classes; 829 830 var destinationClasses = dest.getAttribute('class').trim().split(/\s+/); 831 832 destinationClasses = destinationClasses.filter(function (clazz) { 833 // Save all Select2 classes 834 return clazz.indexOf('select2-') === 0; 835 }); 836 837 var sourceClasses = src.getAttribute('class').trim().split(/\s+/); 838 839 sourceClasses = sourceClasses.filter(function (clazz) { 840 // Only copy non-Select2 classes 841 return clazz.indexOf('select2-') !== 0; 842 }); 843 844 var replacements = destinationClasses.concat(sourceClasses); 845 846 dest.setAttribute('class', replacements.join(' ')); 847 }; 848 849 return Utils; 850}); 851 852S2.define('select2/results',[ 853 'jquery', 854 './utils' 855], function ($, Utils) { 856 function Results ($element, options, dataAdapter) { 857 this.$element = $element; 858 this.data = dataAdapter; 859 this.options = options; 860 861 Results.__super__.constructor.call(this); 862 } 863 864 Utils.Extend(Results, Utils.Observable); 865 866 Results.prototype.render = function () { 867 var $results = $( 868 '<ul class="select2-results__options" role="listbox"></ul>' 869 ); 870 871 if (this.options.get('multiple')) { 872 $results.attr('aria-multiselectable', 'true'); 873 } 874 875 this.$results = $results; 876 877 return $results; 878 }; 879 880 Results.prototype.clear = function () { 881 this.$results.empty(); 882 }; 883 884 Results.prototype.displayMessage = function (params) { 885 var escapeMarkup = this.options.get('escapeMarkup'); 886 887 this.clear(); 888 this.hideLoading(); 889 890 var $message = $( 891 '<li role="alert" aria-live="assertive"' + 892 ' class="select2-results__option"></li>' 893 ); 894 895 var message = this.options.get('translations').get(params.message); 896 897 $message.append( 898 escapeMarkup( 899 message(params.args) 900 ) 901 ); 902 903 $message[0].className += ' select2-results__message'; 904 905 this.$results.append($message); 906 }; 907 908 Results.prototype.hideMessages = function () { 909 this.$results.find('.select2-results__message').remove(); 910 }; 911 912 Results.prototype.append = function (data) { 913 this.hideLoading(); 914 915 var $options = []; 916 917 if (data.results == null || data.results.length === 0) { 918 if (this.$results.children().length === 0) { 919 this.trigger('results:message', { 920 message: 'noResults' 921 }); 922 } 923 924 return; 925 } 926 927 data.results = this.sort(data.results); 928 929 for (var d = 0; d < data.results.length; d++) { 930 var item = data.results[d]; 931 932 var $option = this.option(item); 933 934 $options.push($option); 935 } 936 937 this.$results.append($options); 938 }; 939 940 Results.prototype.position = function ($results, $dropdown) { 941 var $resultsContainer = $dropdown.find('.select2-results'); 942 $resultsContainer.append($results); 943 }; 944 945 Results.prototype.sort = function (data) { 946 var sorter = this.options.get('sorter'); 947 948 return sorter(data); 949 }; 950 951 Results.prototype.highlightFirstItem = function () { 952 var $options = this.$results 953 .find('.select2-results__option--selectable'); 954 955 var $selected = $options.filter('.select2-results__option--selected'); 956 957 // Check if there are any selected options 958 if ($selected.length > 0) { 959 // If there are selected options, highlight the first 960 $selected.first().trigger('mouseenter'); 961 } else { 962 // If there are no selected options, highlight the first option 963 // in the dropdown 964 $options.first().trigger('mouseenter'); 965 } 966 967 this.ensureHighlightVisible(); 968 }; 969 970 Results.prototype.setClasses = function () { 971 var self = this; 972 973 this.data.current(function (selected) { 974 var selectedIds = selected.map(function (s) { 975 return s.id.toString(); 976 }); 977 978 var $options = self.$results 979 .find('.select2-results__option--selectable'); 980 981 $options.each(function () { 982 var $option = $(this); 983 984 var item = Utils.GetData(this, 'data'); 985 986 // id needs to be converted to a string when comparing 987 var id = '' + item.id; 988 989 if ((item.element != null && item.element.selected) || 990 (item.element == null && selectedIds.indexOf(id) > -1)) { 991 this.classList.add('select2-results__option--selected'); 992 $option.attr('aria-selected', 'true'); 993 } else { 994 this.classList.remove('select2-results__option--selected'); 995 $option.attr('aria-selected', 'false'); 996 } 997 }); 998 999 }); 1000 }; 1001 1002 Results.prototype.showLoading = function (params) { 1003 this.hideLoading(); 1004 1005 var loadingMore = this.options.get('translations').get('searching'); 1006 1007 var loading = { 1008 disabled: true, 1009 loading: true, 1010 text: loadingMore(params) 1011 }; 1012 var $loading = this.option(loading); 1013 $loading.className += ' loading-results'; 1014 1015 this.$results.prepend($loading); 1016 }; 1017 1018 Results.prototype.hideLoading = function () { 1019 this.$results.find('.loading-results').remove(); 1020 }; 1021 1022 Results.prototype.option = function (data) { 1023 var option = document.createElement('li'); 1024 option.classList.add('select2-results__option'); 1025 option.classList.add('select2-results__option--selectable'); 1026 1027 var attrs = { 1028 'role': 'option' 1029 }; 1030 1031 var matches = window.Element.prototype.matches || 1032 window.Element.prototype.msMatchesSelector || 1033 window.Element.prototype.webkitMatchesSelector; 1034 1035 if ((data.element != null && matches.call(data.element, ':disabled')) || 1036 (data.element == null && data.disabled)) { 1037 attrs['aria-disabled'] = 'true'; 1038 1039 option.classList.remove('select2-results__option--selectable'); 1040 option.classList.add('select2-results__option--disabled'); 1041 } 1042 1043 if (data.id == null) { 1044 option.classList.remove('select2-results__option--selectable'); 1045 } 1046 1047 if (data._resultId != null) { 1048 option.id = data._resultId; 1049 } 1050 1051 if (data.title) { 1052 option.title = data.title; 1053 } 1054 1055 if (data.children) { 1056 attrs.role = 'group'; 1057 attrs['aria-label'] = data.text; 1058 1059 option.classList.remove('select2-results__option--selectable'); 1060 option.classList.add('select2-results__option--group'); 1061 } 1062 1063 for (var attr in attrs) { 1064 var val = attrs[attr]; 1065 1066 option.setAttribute(attr, val); 1067 } 1068 1069 if (data.children) { 1070 var $option = $(option); 1071 1072 var label = document.createElement('strong'); 1073 label.className = 'select2-results__group'; 1074 1075 this.template(data, label); 1076 1077 var $children = []; 1078 1079 for (var c = 0; c < data.children.length; c++) { 1080 var child = data.children[c]; 1081 1082 var $child = this.option(child); 1083 1084 $children.push($child); 1085 } 1086 1087 var $childrenContainer = $('<ul></ul>', { 1088 'class': 'select2-results__options select2-results__options--nested', 1089 'role': 'none' 1090 }); 1091 1092 $childrenContainer.append($children); 1093 1094 $option.append(label); 1095 $option.append($childrenContainer); 1096 } else { 1097 this.template(data, option); 1098 } 1099 1100 Utils.StoreData(option, 'data', data); 1101 1102 return option; 1103 }; 1104 1105 Results.prototype.bind = function (container, $container) { 1106 var self = this; 1107 1108 var id = container.id + '-results'; 1109 1110 this.$results.attr('id', id); 1111 1112 container.on('results:all', function (params) { 1113 self.clear(); 1114 self.append(params.data); 1115 1116 if (container.isOpen()) { 1117 self.setClasses(); 1118 self.highlightFirstItem(); 1119 } 1120 }); 1121 1122 container.on('results:append', function (params) { 1123 self.append(params.data); 1124 1125 if (container.isOpen()) { 1126 self.setClasses(); 1127 } 1128 }); 1129 1130 container.on('query', function (params) { 1131 self.hideMessages(); 1132 self.showLoading(params); 1133 }); 1134 1135 container.on('select', function () { 1136 if (!container.isOpen()) { 1137 return; 1138 } 1139 1140 self.setClasses(); 1141 1142 if (self.options.get('scrollAfterSelect')) { 1143 self.highlightFirstItem(); 1144 } 1145 }); 1146 1147 container.on('unselect', function () { 1148 if (!container.isOpen()) { 1149 return; 1150 } 1151 1152 self.setClasses(); 1153 1154 if (self.options.get('scrollAfterSelect')) { 1155 self.highlightFirstItem(); 1156 } 1157 }); 1158 1159 container.on('open', function () { 1160 // When the dropdown is open, aria-expended="true" 1161 self.$results.attr('aria-expanded', 'true'); 1162 self.$results.attr('aria-hidden', 'false'); 1163 1164 self.setClasses(); 1165 self.ensureHighlightVisible(); 1166 }); 1167 1168 container.on('close', function () { 1169 // When the dropdown is closed, aria-expended="false" 1170 self.$results.attr('aria-expanded', 'false'); 1171 self.$results.attr('aria-hidden', 'true'); 1172 self.$results.removeAttr('aria-activedescendant'); 1173 }); 1174 1175 container.on('results:toggle', function () { 1176 var $highlighted = self.getHighlightedResults(); 1177 1178 if ($highlighted.length === 0) { 1179 return; 1180 } 1181 1182 $highlighted.trigger('mouseup'); 1183 }); 1184 1185 container.on('results:select', function () { 1186 var $highlighted = self.getHighlightedResults(); 1187 1188 if ($highlighted.length === 0) { 1189 return; 1190 } 1191 1192 var data = Utils.GetData($highlighted[0], 'data'); 1193 1194 if ($highlighted.hasClass('select2-results__option--selected')) { 1195 self.trigger('close', {}); 1196 } else { 1197 self.trigger('select', { 1198 data: data 1199 }); 1200 } 1201 }); 1202 1203 container.on('results:previous', function () { 1204 var $highlighted = self.getHighlightedResults(); 1205 1206 var $options = self.$results.find('.select2-results__option--selectable'); 1207 1208 var currentIndex = $options.index($highlighted); 1209 1210 // If we are already at the top, don't move further 1211 // If no options, currentIndex will be -1 1212 if (currentIndex <= 0) { 1213 return; 1214 } 1215 1216 var nextIndex = currentIndex - 1; 1217 1218 // If none are highlighted, highlight the first 1219 if ($highlighted.length === 0) { 1220 nextIndex = 0; 1221 } 1222 1223 var $next = $options.eq(nextIndex); 1224 1225 $next.trigger('mouseenter'); 1226 1227 var currentOffset = self.$results.offset().top; 1228 var nextTop = $next.offset().top; 1229 var nextOffset = self.$results.scrollTop() + (nextTop - currentOffset); 1230 1231 if (nextIndex === 0) { 1232 self.$results.scrollTop(0); 1233 } else if (nextTop - currentOffset < 0) { 1234 self.$results.scrollTop(nextOffset); 1235 } 1236 }); 1237 1238 container.on('results:next', function () { 1239 var $highlighted = self.getHighlightedResults(); 1240 1241 var $options = self.$results.find('.select2-results__option--selectable'); 1242 1243 var currentIndex = $options.index($highlighted); 1244 1245 var nextIndex = currentIndex + 1; 1246 1247 // If we are at the last option, stay there 1248 if (nextIndex >= $options.length) { 1249 return; 1250 } 1251 1252 var $next = $options.eq(nextIndex); 1253 1254 $next.trigger('mouseenter'); 1255 1256 var currentOffset = self.$results.offset().top + 1257 self.$results.outerHeight(false); 1258 var nextBottom = $next.offset().top + $next.outerHeight(false); 1259 var nextOffset = self.$results.scrollTop() + nextBottom - currentOffset; 1260 1261 if (nextIndex === 0) { 1262 self.$results.scrollTop(0); 1263 } else if (nextBottom > currentOffset) { 1264 self.$results.scrollTop(nextOffset); 1265 } 1266 }); 1267 1268 container.on('results:focus', function (params) { 1269 params.element[0].classList.add('select2-results__option--highlighted'); 1270 params.element[0].setAttribute('aria-selected', 'true'); 1271 }); 1272 1273 container.on('results:message', function (params) { 1274 self.displayMessage(params); 1275 }); 1276 1277 if ($.fn.mousewheel) { 1278 this.$results.on('mousewheel', function (e) { 1279 var top = self.$results.scrollTop(); 1280 1281 var bottom = self.$results.get(0).scrollHeight - top + e.deltaY; 1282 1283 var isAtTop = e.deltaY > 0 && top - e.deltaY <= 0; 1284 var isAtBottom = e.deltaY < 0 && bottom <= self.$results.height(); 1285 1286 if (isAtTop) { 1287 self.$results.scrollTop(0); 1288 1289 e.preventDefault(); 1290 e.stopPropagation(); 1291 } else if (isAtBottom) { 1292 self.$results.scrollTop( 1293 self.$results.get(0).scrollHeight - self.$results.height() 1294 ); 1295 1296 e.preventDefault(); 1297 e.stopPropagation(); 1298 } 1299 }); 1300 } 1301 1302 this.$results.on('mouseup', '.select2-results__option--selectable', 1303 function (evt) { 1304 var $this = $(this); 1305 1306 var data = Utils.GetData(this, 'data'); 1307 1308 if ($this.hasClass('select2-results__option--selected')) { 1309 if (self.options.get('multiple')) { 1310 self.trigger('unselect', { 1311 originalEvent: evt, 1312 data: data 1313 }); 1314 } else { 1315 self.trigger('close', {}); 1316 } 1317 1318 return; 1319 } 1320 1321 self.trigger('select', { 1322 originalEvent: evt, 1323 data: data 1324 }); 1325 }); 1326 1327 this.$results.on('mouseenter', '.select2-results__option--selectable', 1328 function (evt) { 1329 var data = Utils.GetData(this, 'data'); 1330 1331 self.getHighlightedResults() 1332 .removeClass('select2-results__option--highlighted') 1333 .attr('aria-selected', 'false'); 1334 1335 self.trigger('results:focus', { 1336 data: data, 1337 element: $(this) 1338 }); 1339 }); 1340 }; 1341 1342 Results.prototype.getHighlightedResults = function () { 1343 var $highlighted = this.$results 1344 .find('.select2-results__option--highlighted'); 1345 1346 return $highlighted; 1347 }; 1348 1349 Results.prototype.destroy = function () { 1350 this.$results.remove(); 1351 }; 1352 1353 Results.prototype.ensureHighlightVisible = function () { 1354 var $highlighted = this.getHighlightedResults(); 1355 1356 if ($highlighted.length === 0) { 1357 return; 1358 } 1359 1360 var $options = this.$results.find('.select2-results__option--selectable'); 1361 1362 var currentIndex = $options.index($highlighted); 1363 1364 var currentOffset = this.$results.offset().top; 1365 var nextTop = $highlighted.offset().top; 1366 var nextOffset = this.$results.scrollTop() + (nextTop - currentOffset); 1367 1368 var offsetDelta = nextTop - currentOffset; 1369 nextOffset -= $highlighted.outerHeight(false) * 2; 1370 1371 if (currentIndex <= 2) { 1372 this.$results.scrollTop(0); 1373 } else if (offsetDelta > this.$results.outerHeight() || offsetDelta < 0) { 1374 this.$results.scrollTop(nextOffset); 1375 } 1376 }; 1377 1378 Results.prototype.template = function (result, container) { 1379 var template = this.options.get('templateResult'); 1380 var escapeMarkup = this.options.get('escapeMarkup'); 1381 1382 var content = template(result, container); 1383 1384 if (content == null) { 1385 container.style.display = 'none'; 1386 } else if (typeof content === 'string') { 1387 container.innerHTML = escapeMarkup(content); 1388 } else { 1389 $(container).append(content); 1390 } 1391 }; 1392 1393 return Results; 1394}); 1395 1396S2.define('select2/keys',[ 1397 1398], function () { 1399 var KEYS = { 1400 BACKSPACE: 8, 1401 TAB: 9, 1402 ENTER: 13, 1403 SHIFT: 16, 1404 CTRL: 17, 1405 ALT: 18, 1406 ESC: 27, 1407 SPACE: 32, 1408 PAGE_UP: 33, 1409 PAGE_DOWN: 34, 1410 END: 35, 1411 HOME: 36, 1412 LEFT: 37, 1413 UP: 38, 1414 RIGHT: 39, 1415 DOWN: 40, 1416 DELETE: 46 1417 }; 1418 1419 return KEYS; 1420}); 1421 1422S2.define('select2/selection/base',[ 1423 'jquery', 1424 '../utils', 1425 '../keys' 1426], function ($, Utils, KEYS) { 1427 function BaseSelection ($element, options) { 1428 this.$element = $element; 1429 this.options = options; 1430 1431 BaseSelection.__super__.constructor.call(this); 1432 } 1433 1434 Utils.Extend(BaseSelection, Utils.Observable); 1435 1436 BaseSelection.prototype.render = function () { 1437 var $selection = $( 1438 '<span class="select2-selection" role="combobox" ' + 1439 ' aria-haspopup="true" aria-expanded="false">' + 1440 '</span>' 1441 ); 1442 1443 this._tabindex = 0; 1444 1445 if (Utils.GetData(this.$element[0], 'old-tabindex') != null) { 1446 this._tabindex = Utils.GetData(this.$element[0], 'old-tabindex'); 1447 } else if (this.$element.attr('tabindex') != null) { 1448 this._tabindex = this.$element.attr('tabindex'); 1449 } 1450 1451 $selection.attr('title', this.$element.attr('title')); 1452 $selection.attr('tabindex', this._tabindex); 1453 $selection.attr('aria-disabled', 'false'); 1454 1455 this.$selection = $selection; 1456 1457 return $selection; 1458 }; 1459 1460 BaseSelection.prototype.bind = function (container, $container) { 1461 var self = this; 1462 1463 var resultsId = container.id + '-results'; 1464 1465 this.container = container; 1466 1467 this.$selection.on('focus', function (evt) { 1468 self.trigger('focus', evt); 1469 }); 1470 1471 this.$selection.on('blur', function (evt) { 1472 self._handleBlur(evt); 1473 }); 1474 1475 this.$selection.on('keydown', function (evt) { 1476 self.trigger('keypress', evt); 1477 1478 if (evt.which === KEYS.SPACE) { 1479 evt.preventDefault(); 1480 } 1481 }); 1482 1483 container.on('results:focus', function (params) { 1484 self.$selection.attr('aria-activedescendant', params.data._resultId); 1485 }); 1486 1487 container.on('selection:update', function (params) { 1488 self.update(params.data); 1489 }); 1490 1491 container.on('open', function () { 1492 // When the dropdown is open, aria-expanded="true" 1493 self.$selection.attr('aria-expanded', 'true'); 1494 self.$selection.attr('aria-owns', resultsId); 1495 1496 self._attachCloseHandler(container); 1497 }); 1498 1499 container.on('close', function () { 1500 // When the dropdown is closed, aria-expanded="false" 1501 self.$selection.attr('aria-expanded', 'false'); 1502 self.$selection.removeAttr('aria-activedescendant'); 1503 self.$selection.removeAttr('aria-owns'); 1504 1505 self.$selection.trigger('focus'); 1506 1507 self._detachCloseHandler(container); 1508 }); 1509 1510 container.on('enable', function () { 1511 self.$selection.attr('tabindex', self._tabindex); 1512 self.$selection.attr('aria-disabled', 'false'); 1513 }); 1514 1515 container.on('disable', function () { 1516 self.$selection.attr('tabindex', '-1'); 1517 self.$selection.attr('aria-disabled', 'true'); 1518 }); 1519 }; 1520 1521 BaseSelection.prototype._handleBlur = function (evt) { 1522 var self = this; 1523 1524 // This needs to be delayed as the active element is the body when the tab 1525 // key is pressed, possibly along with others. 1526 window.setTimeout(function () { 1527 // Don't trigger `blur` if the focus is still in the selection 1528 if ( 1529 (document.activeElement == self.$selection[0]) || 1530 ($.contains(self.$selection[0], document.activeElement)) 1531 ) { 1532 return; 1533 } 1534 1535 self.trigger('blur', evt); 1536 }, 1); 1537 }; 1538 1539 BaseSelection.prototype._attachCloseHandler = function (container) { 1540 1541 $(document.body).on('mousedown.select2.' + container.id, function (e) { 1542 var $target = $(e.target); 1543 1544 var $select = $target.closest('.select2'); 1545 1546 var $all = $('.select2.select2-container--open'); 1547 1548 $all.each(function () { 1549 if (this == $select[0]) { 1550 return; 1551 } 1552 1553 var $element = Utils.GetData(this, 'element'); 1554 1555 $element.select2('close'); 1556 }); 1557 }); 1558 }; 1559 1560 BaseSelection.prototype._detachCloseHandler = function (container) { 1561 $(document.body).off('mousedown.select2.' + container.id); 1562 }; 1563 1564 BaseSelection.prototype.position = function ($selection, $container) { 1565 var $selectionContainer = $container.find('.selection'); 1566 $selectionContainer.append($selection); 1567 }; 1568 1569 BaseSelection.prototype.destroy = function () { 1570 this._detachCloseHandler(this.container); 1571 }; 1572 1573 BaseSelection.prototype.update = function (data) { 1574 throw new Error('The `update` method must be defined in child classes.'); 1575 }; 1576 1577 /** 1578 * Helper method to abstract the "enabled" (not "disabled") state of this 1579 * object. 1580 * 1581 * @return {true} if the instance is not disabled. 1582 * @return {false} if the instance is disabled. 1583 */ 1584 BaseSelection.prototype.isEnabled = function () { 1585 return !this.isDisabled(); 1586 }; 1587 1588 /** 1589 * Helper method to abstract the "disabled" state of this object. 1590 * 1591 * @return {true} if the disabled option is true. 1592 * @return {false} if the disabled option is false. 1593 */ 1594 BaseSelection.prototype.isDisabled = function () { 1595 return this.options.get('disabled'); 1596 }; 1597 1598 return BaseSelection; 1599}); 1600 1601S2.define('select2/selection/single',[ 1602 'jquery', 1603 './base', 1604 '../utils', 1605 '../keys' 1606], function ($, BaseSelection, Utils, KEYS) { 1607 function SingleSelection () { 1608 SingleSelection.__super__.constructor.apply(this, arguments); 1609 } 1610 1611 Utils.Extend(SingleSelection, BaseSelection); 1612 1613 SingleSelection.prototype.render = function () { 1614 var $selection = SingleSelection.__super__.render.call(this); 1615 1616 $selection[0].classList.add('select2-selection--single'); 1617 1618 $selection.html( 1619 '<span class="select2-selection__rendered"></span>' + 1620 '<span class="select2-selection__arrow" role="presentation">' + 1621 '<b role="presentation"></b>' + 1622 '</span>' 1623 ); 1624 1625 return $selection; 1626 }; 1627 1628 SingleSelection.prototype.bind = function (container, $container) { 1629 var self = this; 1630 1631 SingleSelection.__super__.bind.apply(this, arguments); 1632 1633 var id = container.id + '-container'; 1634 1635 this.$selection.find('.select2-selection__rendered') 1636 .attr('id', id) 1637 .attr('role', 'textbox') 1638 .attr('aria-readonly', 'true'); 1639 this.$selection.attr('aria-labelledby', id); 1640 this.$selection.attr('aria-controls', id); 1641 1642 this.$selection.on('mousedown', function (evt) { 1643 // Only respond to left clicks 1644 if (evt.which !== 1) { 1645 return; 1646 } 1647 1648 self.trigger('toggle', { 1649 originalEvent: evt 1650 }); 1651 }); 1652 1653 this.$selection.on('focus', function (evt) { 1654 // User focuses on the container 1655 }); 1656 1657 this.$selection.on('blur', function (evt) { 1658 // User exits the container 1659 }); 1660 1661 container.on('focus', function (evt) { 1662 if (!container.isOpen()) { 1663 self.$selection.trigger('focus'); 1664 } 1665 }); 1666 }; 1667 1668 SingleSelection.prototype.clear = function () { 1669 var $rendered = this.$selection.find('.select2-selection__rendered'); 1670 $rendered.empty(); 1671 $rendered.removeAttr('title'); // clear tooltip on empty 1672 }; 1673 1674 SingleSelection.prototype.display = function (data, container) { 1675 var template = this.options.get('templateSelection'); 1676 var escapeMarkup = this.options.get('escapeMarkup'); 1677 1678 return escapeMarkup(template(data, container)); 1679 }; 1680 1681 SingleSelection.prototype.selectionContainer = function () { 1682 return $('<span></span>'); 1683 }; 1684 1685 SingleSelection.prototype.update = function (data) { 1686 if (data.length === 0) { 1687 this.clear(); 1688 return; 1689 } 1690 1691 var selection = data[0]; 1692 1693 var $rendered = this.$selection.find('.select2-selection__rendered'); 1694 var formatted = this.display(selection, $rendered); 1695 1696 $rendered.empty().append(formatted); 1697 1698 var title = selection.title || selection.text; 1699 1700 if (title) { 1701 $rendered.attr('title', title); 1702 } else { 1703 $rendered.removeAttr('title'); 1704 } 1705 }; 1706 1707 return SingleSelection; 1708}); 1709 1710S2.define('select2/selection/multiple',[ 1711 'jquery', 1712 './base', 1713 '../utils' 1714], function ($, BaseSelection, Utils) { 1715 function MultipleSelection ($element, options) { 1716 MultipleSelection.__super__.constructor.apply(this, arguments); 1717 } 1718 1719 Utils.Extend(MultipleSelection, BaseSelection); 1720 1721 MultipleSelection.prototype.render = function () { 1722 var $selection = MultipleSelection.__super__.render.call(this); 1723 1724 $selection[0].classList.add('select2-selection--multiple'); 1725 1726 $selection.html( 1727 '<ul class="select2-selection__rendered"></ul>' 1728 ); 1729 1730 return $selection; 1731 }; 1732 1733 MultipleSelection.prototype.bind = function (container, $container) { 1734 var self = this; 1735 1736 MultipleSelection.__super__.bind.apply(this, arguments); 1737 1738 var id = container.id + '-container'; 1739 this.$selection.find('.select2-selection__rendered').attr('id', id); 1740 1741 this.$selection.on('click', function (evt) { 1742 self.trigger('toggle', { 1743 originalEvent: evt 1744 }); 1745 }); 1746 1747 this.$selection.on( 1748 'click', 1749 '.select2-selection__choice__remove', 1750 function (evt) { 1751 // Ignore the event if it is disabled 1752 if (self.isDisabled()) { 1753 return; 1754 } 1755 1756 var $remove = $(this); 1757 var $selection = $remove.parent(); 1758 1759 var data = Utils.GetData($selection[0], 'data'); 1760 1761 self.trigger('unselect', { 1762 originalEvent: evt, 1763 data: data 1764 }); 1765 } 1766 ); 1767 1768 this.$selection.on( 1769 'keydown', 1770 '.select2-selection__choice__remove', 1771 function (evt) { 1772 // Ignore the event if it is disabled 1773 if (self.isDisabled()) { 1774 return; 1775 } 1776 1777 evt.stopPropagation(); 1778 } 1779 ); 1780 }; 1781 1782 MultipleSelection.prototype.clear = function () { 1783 var $rendered = this.$selection.find('.select2-selection__rendered'); 1784 $rendered.empty(); 1785 $rendered.removeAttr('title'); 1786 }; 1787 1788 MultipleSelection.prototype.display = function (data, container) { 1789 var template = this.options.get('templateSelection'); 1790 var escapeMarkup = this.options.get('escapeMarkup'); 1791 1792 return escapeMarkup(template(data, container)); 1793 }; 1794 1795 MultipleSelection.prototype.selectionContainer = function () { 1796 var $container = $( 1797 '<li class="select2-selection__choice">' + 1798 '<button type="button" class="select2-selection__choice__remove" ' + 1799 'tabindex="-1">' + 1800 '<span aria-hidden="true">×</span>' + 1801 '</button>' + 1802 '<span class="select2-selection__choice__display"></span>' + 1803 '</li>' 1804 ); 1805 1806 return $container; 1807 }; 1808 1809 MultipleSelection.prototype.update = function (data) { 1810 this.clear(); 1811 1812 if (data.length === 0) { 1813 return; 1814 } 1815 1816 var $selections = []; 1817 1818 var selectionIdPrefix = this.$selection.find('.select2-selection__rendered') 1819 .attr('id') + '-choice-'; 1820 1821 for (var d = 0; d < data.length; d++) { 1822 var selection = data[d]; 1823 1824 var $selection = this.selectionContainer(); 1825 var formatted = this.display(selection, $selection); 1826 1827 var selectionId = selectionIdPrefix + Utils.generateChars(4) + '-'; 1828 1829 if (selection.id) { 1830 selectionId += selection.id; 1831 } else { 1832 selectionId += Utils.generateChars(4); 1833 } 1834 1835 $selection.find('.select2-selection__choice__display') 1836 .append(formatted) 1837 .attr('id', selectionId); 1838 1839 var title = selection.title || selection.text; 1840 1841 if (title) { 1842 $selection.attr('title', title); 1843 } 1844 1845 var removeItem = this.options.get('translations').get('removeItem'); 1846 1847 var $remove = $selection.find('.select2-selection__choice__remove'); 1848 1849 $remove.attr('title', removeItem()); 1850 $remove.attr('aria-label', removeItem()); 1851 $remove.attr('aria-describedby', selectionId); 1852 1853 Utils.StoreData($selection[0], 'data', selection); 1854 1855 $selections.push($selection); 1856 } 1857 1858 var $rendered = this.$selection.find('.select2-selection__rendered'); 1859 1860 $rendered.append($selections); 1861 }; 1862 1863 return MultipleSelection; 1864}); 1865 1866S2.define('select2/selection/placeholder',[ 1867 1868], function () { 1869 function Placeholder (decorated, $element, options) { 1870 this.placeholder = this.normalizePlaceholder(options.get('placeholder')); 1871 1872 decorated.call(this, $element, options); 1873 } 1874 1875 Placeholder.prototype.normalizePlaceholder = function (_, placeholder) { 1876 if (typeof placeholder === 'string') { 1877 placeholder = { 1878 id: '', 1879 text: placeholder 1880 }; 1881 } 1882 1883 return placeholder; 1884 }; 1885 1886 Placeholder.prototype.createPlaceholder = function (decorated, placeholder) { 1887 var $placeholder = this.selectionContainer(); 1888 1889 $placeholder.html(this.display(placeholder)); 1890 $placeholder[0].classList.add('select2-selection__placeholder'); 1891 $placeholder[0].classList.remove('select2-selection__choice'); 1892 1893 var placeholderTitle = placeholder.title || 1894 placeholder.text || 1895 $placeholder.text(); 1896 1897 this.$selection.find('.select2-selection__rendered').attr( 1898 'title', 1899 placeholderTitle 1900 ); 1901 1902 return $placeholder; 1903 }; 1904 1905 Placeholder.prototype.update = function (decorated, data) { 1906 var singlePlaceholder = ( 1907 data.length == 1 && data[0].id != this.placeholder.id 1908 ); 1909 var multipleSelections = data.length > 1; 1910 1911 if (multipleSelections || singlePlaceholder) { 1912 return decorated.call(this, data); 1913 } 1914 1915 this.clear(); 1916 1917 var $placeholder = this.createPlaceholder(this.placeholder); 1918 1919 this.$selection.find('.select2-selection__rendered').append($placeholder); 1920 }; 1921 1922 return Placeholder; 1923}); 1924 1925S2.define('select2/selection/allowClear',[ 1926 'jquery', 1927 '../keys', 1928 '../utils' 1929], function ($, KEYS, Utils) { 1930 function AllowClear () { } 1931 1932 AllowClear.prototype.bind = function (decorated, container, $container) { 1933 var self = this; 1934 1935 decorated.call(this, container, $container); 1936 1937 if (this.placeholder == null) { 1938 if (this.options.get('debug') && window.console && console.error) { 1939 console.error( 1940 'Select2: The `allowClear` option should be used in combination ' + 1941 'with the `placeholder` option.' 1942 ); 1943 } 1944 } 1945 1946 this.$selection.on('mousedown', '.select2-selection__clear', 1947 function (evt) { 1948 self._handleClear(evt); 1949 }); 1950 1951 container.on('keypress', function (evt) { 1952 self._handleKeyboardClear(evt, container); 1953 }); 1954 }; 1955 1956 AllowClear.prototype._handleClear = function (_, evt) { 1957 // Ignore the event if it is disabled 1958 if (this.isDisabled()) { 1959 return; 1960 } 1961 1962 var $clear = this.$selection.find('.select2-selection__clear'); 1963 1964 // Ignore the event if nothing has been selected 1965 if ($clear.length === 0) { 1966 return; 1967 } 1968 1969 evt.stopPropagation(); 1970 1971 var data = Utils.GetData($clear[0], 'data'); 1972 1973 var previousVal = this.$element.val(); 1974 this.$element.val(this.placeholder.id); 1975 1976 var unselectData = { 1977 data: data 1978 }; 1979 this.trigger('clear', unselectData); 1980 if (unselectData.prevented) { 1981 this.$element.val(previousVal); 1982 return; 1983 } 1984 1985 for (var d = 0; d < data.length; d++) { 1986 unselectData = { 1987 data: data[d] 1988 }; 1989 1990 // Trigger the `unselect` event, so people can prevent it from being 1991 // cleared. 1992 this.trigger('unselect', unselectData); 1993 1994 // If the event was prevented, don't clear it out. 1995 if (unselectData.prevented) { 1996 this.$element.val(previousVal); 1997 return; 1998 } 1999 } 2000 2001 this.$element.trigger('input').trigger('change'); 2002 2003 this.trigger('toggle', {}); 2004 }; 2005 2006 AllowClear.prototype._handleKeyboardClear = function (_, evt, container) { 2007 if (container.isOpen()) { 2008 return; 2009 } 2010 2011 if (evt.which == KEYS.DELETE || evt.which == KEYS.BACKSPACE) { 2012 this._handleClear(evt); 2013 } 2014 }; 2015 2016 AllowClear.prototype.update = function (decorated, data) { 2017 decorated.call(this, data); 2018 2019 this.$selection.find('.select2-selection__clear').remove(); 2020 this.$selection[0].classList.remove('select2-selection--clearable'); 2021 2022 if (this.$selection.find('.select2-selection__placeholder').length > 0 || 2023 data.length === 0) { 2024 return; 2025 } 2026 2027 var selectionId = this.$selection.find('.select2-selection__rendered') 2028 .attr('id'); 2029 2030 var removeAll = this.options.get('translations').get('removeAllItems'); 2031 2032 var $remove = $( 2033 '<button type="button" class="select2-selection__clear" tabindex="-1">' + 2034 '<span aria-hidden="true">×</span>' + 2035 '</button>' 2036 ); 2037 $remove.attr('title', removeAll()); 2038 $remove.attr('aria-label', removeAll()); 2039 $remove.attr('aria-describedby', selectionId); 2040 Utils.StoreData($remove[0], 'data', data); 2041 2042 this.$selection.prepend($remove); 2043 this.$selection[0].classList.add('select2-selection--clearable'); 2044 }; 2045 2046 return AllowClear; 2047}); 2048 2049S2.define('select2/selection/search',[ 2050 'jquery', 2051 '../utils', 2052 '../keys' 2053], function ($, Utils, KEYS) { 2054 function Search (decorated, $element, options) { 2055 decorated.call(this, $element, options); 2056 } 2057 2058 Search.prototype.render = function (decorated) { 2059 var searchLabel = this.options.get('translations').get('search'); 2060 var $search = $( 2061 '<span class="select2-search select2-search--inline">' + 2062 '<textarea class="select2-search__field"'+ 2063 ' type="search" tabindex="-1"' + 2064 ' autocorrect="off" autocapitalize="none"' + 2065 ' spellcheck="false" role="searchbox" aria-autocomplete="list" >' + 2066 '</textarea>' + 2067 '</span>' 2068 ); 2069 2070 this.$searchContainer = $search; 2071 this.$search = $search.find('textarea'); 2072 2073 this.$search.prop('autocomplete', this.options.get('autocomplete')); 2074 this.$search.attr('aria-label', searchLabel()); 2075 2076 var $rendered = decorated.call(this); 2077 2078 this._transferTabIndex(); 2079 $rendered.append(this.$searchContainer); 2080 2081 return $rendered; 2082 }; 2083 2084 Search.prototype.bind = function (decorated, container, $container) { 2085 var self = this; 2086 2087 var resultsId = container.id + '-results'; 2088 var selectionId = container.id + '-container'; 2089 2090 decorated.call(this, container, $container); 2091 2092 self.$search.attr('aria-describedby', selectionId); 2093 2094 container.on('open', function () { 2095 self.$search.attr('aria-controls', resultsId); 2096 self.$search.trigger('focus'); 2097 }); 2098 2099 container.on('close', function () { 2100 self.$search.val(''); 2101 self.resizeSearch(); 2102 self.$search.removeAttr('aria-controls'); 2103 self.$search.removeAttr('aria-activedescendant'); 2104 self.$search.trigger('focus'); 2105 }); 2106 2107 container.on('enable', function () { 2108 self.$search.prop('disabled', false); 2109 2110 self._transferTabIndex(); 2111 }); 2112 2113 container.on('disable', function () { 2114 self.$search.prop('disabled', true); 2115 }); 2116 2117 container.on('focus', function (evt) { 2118 self.$search.trigger('focus'); 2119 }); 2120 2121 container.on('results:focus', function (params) { 2122 if (params.data._resultId) { 2123 self.$search.attr('aria-activedescendant', params.data._resultId); 2124 } else { 2125 self.$search.removeAttr('aria-activedescendant'); 2126 } 2127 }); 2128 2129 this.$selection.on('focusin', '.select2-search--inline', function (evt) { 2130 self.trigger('focus', evt); 2131 }); 2132 2133 this.$selection.on('focusout', '.select2-search--inline', function (evt) { 2134 self._handleBlur(evt); 2135 }); 2136 2137 this.$selection.on('keydown', '.select2-search--inline', function (evt) { 2138 evt.stopPropagation(); 2139 2140 self.trigger('keypress', evt); 2141 2142 self._keyUpPrevented = evt.isDefaultPrevented(); 2143 2144 var key = evt.which; 2145 2146 if (key === KEYS.BACKSPACE && self.$search.val() === '') { 2147 var $previousChoice = self.$selection 2148 .find('.select2-selection__choice').last(); 2149 2150 if ($previousChoice.length > 0) { 2151 var item = Utils.GetData($previousChoice[0], 'data'); 2152 2153 self.searchRemoveChoice(item); 2154 2155 evt.preventDefault(); 2156 } 2157 } 2158 }); 2159 2160 this.$selection.on('click', '.select2-search--inline', function (evt) { 2161 if (self.$search.val()) { 2162 evt.stopPropagation(); 2163 } 2164 }); 2165 2166 // Try to detect the IE version should the `documentMode` property that 2167 // is stored on the document. This is only implemented in IE and is 2168 // slightly cleaner than doing a user agent check. 2169 // This property is not available in Edge, but Edge also doesn't have 2170 // this bug. 2171 var msie = document.documentMode; 2172 var disableInputEvents = msie && msie <= 11; 2173 2174 // Workaround for browsers which do not support the `input` event 2175 // This will prevent double-triggering of events for browsers which support 2176 // both the `keyup` and `input` events. 2177 this.$selection.on( 2178 'input.searchcheck', 2179 '.select2-search--inline', 2180 function (evt) { 2181 // IE will trigger the `input` event when a placeholder is used on a 2182 // search box. To get around this issue, we are forced to ignore all 2183 // `input` events in IE and keep using `keyup`. 2184 if (disableInputEvents) { 2185 self.$selection.off('input.search input.searchcheck'); 2186 return; 2187 } 2188 2189 // Unbind the duplicated `keyup` event 2190 self.$selection.off('keyup.search'); 2191 } 2192 ); 2193 2194 this.$selection.on( 2195 'keyup.search input.search', 2196 '.select2-search--inline', 2197 function (evt) { 2198 // IE will trigger the `input` event when a placeholder is used on a 2199 // search box. To get around this issue, we are forced to ignore all 2200 // `input` events in IE and keep using `keyup`. 2201 if (disableInputEvents && evt.type === 'input') { 2202 self.$selection.off('input.search input.searchcheck'); 2203 return; 2204 } 2205 2206 var key = evt.which; 2207 2208 // We can freely ignore events from modifier keys 2209 if (key == KEYS.SHIFT || key == KEYS.CTRL || key == KEYS.ALT) { 2210 return; 2211 } 2212 2213 // Tabbing will be handled during the `keydown` phase 2214 if (key == KEYS.TAB) { 2215 return; 2216 } 2217 2218 self.handleSearch(evt); 2219 } 2220 ); 2221 }; 2222 2223 /** 2224 * This method will transfer the tabindex attribute from the rendered 2225 * selection to the search box. This allows for the search box to be used as 2226 * the primary focus instead of the selection container. 2227 * 2228 * @private 2229 */ 2230 Search.prototype._transferTabIndex = function (decorated) { 2231 this.$search.attr('tabindex', this.$selection.attr('tabindex')); 2232 this.$selection.attr('tabindex', '-1'); 2233 }; 2234 2235 Search.prototype.createPlaceholder = function (decorated, placeholder) { 2236 this.$search.attr('placeholder', placeholder.text); 2237 }; 2238 2239 Search.prototype.update = function (decorated, data) { 2240 var searchHadFocus = this.$search[0] == document.activeElement; 2241 2242 this.$search.attr('placeholder', ''); 2243 2244 decorated.call(this, data); 2245 2246 this.resizeSearch(); 2247 if (searchHadFocus) { 2248 this.$search.trigger('focus'); 2249 } 2250 }; 2251 2252 Search.prototype.handleSearch = function () { 2253 this.resizeSearch(); 2254 2255 if (!this._keyUpPrevented) { 2256 var input = this.$search.val(); 2257 2258 this.trigger('query', { 2259 term: input 2260 }); 2261 } 2262 2263 this._keyUpPrevented = false; 2264 }; 2265 2266 Search.prototype.searchRemoveChoice = function (decorated, item) { 2267 this.trigger('unselect', { 2268 data: item 2269 }); 2270 2271 this.$search.val(item.text); 2272 this.handleSearch(); 2273 }; 2274 2275 Search.prototype.resizeSearch = function () { 2276 this.$search.css('width', '25px'); 2277 2278 var width = '100%'; 2279 2280 if (this.$search.attr('placeholder') === '') { 2281 var minimumWidth = this.$search.val().length + 1; 2282 2283 width = (minimumWidth * 0.75) + 'em'; 2284 } 2285 2286 this.$search.css('width', width); 2287 }; 2288 2289 return Search; 2290}); 2291 2292S2.define('select2/selection/selectionCss',[ 2293 '../utils' 2294], function (Utils) { 2295 function SelectionCSS () { } 2296 2297 SelectionCSS.prototype.render = function (decorated) { 2298 var $selection = decorated.call(this); 2299 2300 var selectionCssClass = this.options.get('selectionCssClass') || ''; 2301 2302 if (selectionCssClass.indexOf(':all:') !== -1) { 2303 selectionCssClass = selectionCssClass.replace(':all:', ''); 2304 2305 Utils.copyNonInternalCssClasses($selection[0], this.$element[0]); 2306 } 2307 2308 $selection.addClass(selectionCssClass); 2309 2310 return $selection; 2311 }; 2312 2313 return SelectionCSS; 2314}); 2315 2316S2.define('select2/selection/eventRelay',[ 2317 'jquery' 2318], function ($) { 2319 function EventRelay () { } 2320 2321 EventRelay.prototype.bind = function (decorated, container, $container) { 2322 var self = this; 2323 var relayEvents = [ 2324 'open', 'opening', 2325 'close', 'closing', 2326 'select', 'selecting', 2327 'unselect', 'unselecting', 2328 'clear', 'clearing' 2329 ]; 2330 2331 var preventableEvents = [ 2332 'opening', 'closing', 'selecting', 'unselecting', 'clearing' 2333 ]; 2334 2335 decorated.call(this, container, $container); 2336 2337 container.on('*', function (name, params) { 2338 // Ignore events that should not be relayed 2339 if (relayEvents.indexOf(name) === -1) { 2340 return; 2341 } 2342 2343 // The parameters should always be an object 2344 params = params || {}; 2345 2346 // Generate the jQuery event for the Select2 event 2347 var evt = $.Event('select2:' + name, { 2348 params: params 2349 }); 2350 2351 self.$element.trigger(evt); 2352 2353 // Only handle preventable events if it was one 2354 if (preventableEvents.indexOf(name) === -1) { 2355 return; 2356 } 2357 2358 params.prevented = evt.isDefaultPrevented(); 2359 }); 2360 }; 2361 2362 return EventRelay; 2363}); 2364 2365S2.define('select2/translation',[ 2366 'jquery', 2367 'require' 2368], function ($, require) { 2369 function Translation (dict) { 2370 this.dict = dict || {}; 2371 } 2372 2373 Translation.prototype.all = function () { 2374 return this.dict; 2375 }; 2376 2377 Translation.prototype.get = function (key) { 2378 return this.dict[key]; 2379 }; 2380 2381 Translation.prototype.extend = function (translation) { 2382 this.dict = $.extend({}, translation.all(), this.dict); 2383 }; 2384 2385 // Static functions 2386 2387 Translation._cache = {}; 2388 2389 Translation.loadPath = function (path) { 2390 if (!(path in Translation._cache)) { 2391 var translations = require(path); 2392 2393 Translation._cache[path] = translations; 2394 } 2395 2396 return new Translation(Translation._cache[path]); 2397 }; 2398 2399 return Translation; 2400}); 2401 2402S2.define('select2/diacritics',[ 2403 2404], function () { 2405 var diacritics = { 2406 '\u24B6': 'A', 2407 '\uFF21': 'A', 2408 '\u00C0': 'A', 2409 '\u00C1': 'A', 2410 '\u00C2': 'A', 2411 '\u1EA6': 'A', 2412 '\u1EA4': 'A', 2413 '\u1EAA': 'A', 2414 '\u1EA8': 'A', 2415 '\u00C3': 'A', 2416 '\u0100': 'A', 2417 '\u0102': 'A', 2418 '\u1EB0': 'A', 2419 '\u1EAE': 'A', 2420 '\u1EB4': 'A', 2421 '\u1EB2': 'A', 2422 '\u0226': 'A', 2423 '\u01E0': 'A', 2424 '\u00C4': 'A', 2425 '\u01DE': 'A', 2426 '\u1EA2': 'A', 2427 '\u00C5': 'A', 2428 '\u01FA': 'A', 2429 '\u01CD': 'A', 2430 '\u0200': 'A', 2431 '\u0202': 'A', 2432 '\u1EA0': 'A', 2433 '\u1EAC': 'A', 2434 '\u1EB6': 'A', 2435 '\u1E00': 'A', 2436 '\u0104': 'A', 2437 '\u023A': 'A', 2438 '\u2C6F': 'A', 2439 '\uA732': 'AA', 2440 '\u00C6': 'AE', 2441 '\u01FC': 'AE', 2442 '\u01E2': 'AE', 2443 '\uA734': 'AO', 2444 '\uA736': 'AU', 2445 '\uA738': 'AV', 2446 '\uA73A': 'AV', 2447 '\uA73C': 'AY', 2448 '\u24B7': 'B', 2449 '\uFF22': 'B', 2450 '\u1E02': 'B', 2451 '\u1E04': 'B', 2452 '\u1E06': 'B', 2453 '\u0243': 'B', 2454 '\u0182': 'B', 2455 '\u0181': 'B', 2456 '\u24B8': 'C', 2457 '\uFF23': 'C', 2458 '\u0106': 'C', 2459 '\u0108': 'C', 2460 '\u010A': 'C', 2461 '\u010C': 'C', 2462 '\u00C7': 'C', 2463 '\u1E08': 'C', 2464 '\u0187': 'C', 2465 '\u023B': 'C', 2466 '\uA73E': 'C', 2467 '\u24B9': 'D', 2468 '\uFF24': 'D', 2469 '\u1E0A': 'D', 2470 '\u010E': 'D', 2471 '\u1E0C': 'D', 2472 '\u1E10': 'D', 2473 '\u1E12': 'D', 2474 '\u1E0E': 'D', 2475 '\u0110': 'D', 2476 '\u018B': 'D', 2477 '\u018A': 'D', 2478 '\u0189': 'D', 2479 '\uA779': 'D', 2480 '\u01F1': 'DZ', 2481 '\u01C4': 'DZ', 2482 '\u01F2': 'Dz', 2483 '\u01C5': 'Dz', 2484 '\u24BA': 'E', 2485 '\uFF25': 'E', 2486 '\u00C8': 'E', 2487 '\u00C9': 'E', 2488 '\u00CA': 'E', 2489 '\u1EC0': 'E', 2490 '\u1EBE': 'E', 2491 '\u1EC4': 'E', 2492 '\u1EC2': 'E', 2493 '\u1EBC': 'E', 2494 '\u0112': 'E', 2495 '\u1E14': 'E', 2496 '\u1E16': 'E', 2497 '\u0114': 'E', 2498 '\u0116': 'E', 2499 '\u00CB': 'E', 2500 '\u1EBA': 'E', 2501 '\u011A': 'E', 2502 '\u0204': 'E', 2503 '\u0206': 'E', 2504 '\u1EB8': 'E', 2505 '\u1EC6': 'E', 2506 '\u0228': 'E', 2507 '\u1E1C': 'E', 2508 '\u0118': 'E', 2509 '\u1E18': 'E', 2510 '\u1E1A': 'E', 2511 '\u0190': 'E', 2512 '\u018E': 'E', 2513 '\u24BB': 'F', 2514 '\uFF26': 'F', 2515 '\u1E1E': 'F', 2516 '\u0191': 'F', 2517 '\uA77B': 'F', 2518 '\u24BC': 'G', 2519 '\uFF27': 'G', 2520 '\u01F4': 'G', 2521 '\u011C': 'G', 2522 '\u1E20': 'G', 2523 '\u011E': 'G', 2524 '\u0120': 'G', 2525 '\u01E6': 'G', 2526 '\u0122': 'G', 2527 '\u01E4': 'G', 2528 '\u0193': 'G', 2529 '\uA7A0': 'G', 2530 '\uA77D': 'G', 2531 '\uA77E': 'G', 2532 '\u24BD': 'H', 2533 '\uFF28': 'H', 2534 '\u0124': 'H', 2535 '\u1E22': 'H', 2536 '\u1E26': 'H', 2537 '\u021E': 'H', 2538 '\u1E24': 'H', 2539 '\u1E28': 'H', 2540 '\u1E2A': 'H', 2541 '\u0126': 'H', 2542 '\u2C67': 'H', 2543 '\u2C75': 'H', 2544 '\uA78D': 'H', 2545 '\u24BE': 'I', 2546 '\uFF29': 'I', 2547 '\u00CC': 'I', 2548 '\u00CD': 'I', 2549 '\u00CE': 'I', 2550 '\u0128': 'I', 2551 '\u012A': 'I', 2552 '\u012C': 'I', 2553 '\u0130': 'I', 2554 '\u00CF': 'I', 2555 '\u1E2E': 'I', 2556 '\u1EC8': 'I', 2557 '\u01CF': 'I', 2558 '\u0208': 'I', 2559 '\u020A': 'I', 2560 '\u1ECA': 'I', 2561 '\u012E': 'I', 2562 '\u1E2C': 'I', 2563 '\u0197': 'I', 2564 '\u24BF': 'J', 2565 '\uFF2A': 'J', 2566 '\u0134': 'J', 2567 '\u0248': 'J', 2568 '\u24C0': 'K', 2569 '\uFF2B': 'K', 2570 '\u1E30': 'K', 2571 '\u01E8': 'K', 2572 '\u1E32': 'K', 2573 '\u0136': 'K', 2574 '\u1E34': 'K', 2575 '\u0198': 'K', 2576 '\u2C69': 'K', 2577 '\uA740': 'K', 2578 '\uA742': 'K', 2579 '\uA744': 'K', 2580 '\uA7A2': 'K', 2581 '\u24C1': 'L', 2582 '\uFF2C': 'L', 2583 '\u013F': 'L', 2584 '\u0139': 'L', 2585 '\u013D': 'L', 2586 '\u1E36': 'L', 2587 '\u1E38': 'L', 2588 '\u013B': 'L', 2589 '\u1E3C': 'L', 2590 '\u1E3A': 'L', 2591 '\u0141': 'L', 2592 '\u023D': 'L', 2593 '\u2C62': 'L', 2594 '\u2C60': 'L', 2595 '\uA748': 'L', 2596 '\uA746': 'L', 2597 '\uA780': 'L', 2598 '\u01C7': 'LJ', 2599 '\u01C8': 'Lj', 2600 '\u24C2': 'M', 2601 '\uFF2D': 'M', 2602 '\u1E3E': 'M', 2603 '\u1E40': 'M', 2604 '\u1E42': 'M', 2605 '\u2C6E': 'M', 2606 '\u019C': 'M', 2607 '\u24C3': 'N', 2608 '\uFF2E': 'N', 2609 '\u01F8': 'N', 2610 '\u0143': 'N', 2611 '\u00D1': 'N', 2612 '\u1E44': 'N', 2613 '\u0147': 'N', 2614 '\u1E46': 'N', 2615 '\u0145': 'N', 2616 '\u1E4A': 'N', 2617 '\u1E48': 'N', 2618 '\u0220': 'N', 2619 '\u019D': 'N', 2620 '\uA790': 'N', 2621 '\uA7A4': 'N', 2622 '\u01CA': 'NJ', 2623 '\u01CB': 'Nj', 2624 '\u24C4': 'O', 2625 '\uFF2F': 'O', 2626 '\u00D2': 'O', 2627 '\u00D3': 'O', 2628 '\u00D4': 'O', 2629 '\u1ED2': 'O', 2630 '\u1ED0': 'O', 2631 '\u1ED6': 'O', 2632 '\u1ED4': 'O', 2633 '\u00D5': 'O', 2634 '\u1E4C': 'O', 2635 '\u022C': 'O', 2636 '\u1E4E': 'O', 2637 '\u014C': 'O', 2638 '\u1E50': 'O', 2639 '\u1E52': 'O', 2640 '\u014E': 'O', 2641 '\u022E': 'O', 2642 '\u0230': 'O', 2643 '\u00D6': 'O', 2644 '\u022A': 'O', 2645 '\u1ECE': 'O', 2646 '\u0150': 'O', 2647 '\u01D1': 'O', 2648 '\u020C': 'O', 2649 '\u020E': 'O', 2650 '\u01A0': 'O', 2651 '\u1EDC': 'O', 2652 '\u1EDA': 'O', 2653 '\u1EE0': 'O', 2654 '\u1EDE': 'O', 2655 '\u1EE2': 'O', 2656 '\u1ECC': 'O', 2657 '\u1ED8': 'O', 2658 '\u01EA': 'O', 2659 '\u01EC': 'O', 2660 '\u00D8': 'O', 2661 '\u01FE': 'O', 2662 '\u0186': 'O', 2663 '\u019F': 'O', 2664 '\uA74A': 'O', 2665 '\uA74C': 'O', 2666 '\u0152': 'OE', 2667 '\u01A2': 'OI', 2668 '\uA74E': 'OO', 2669 '\u0222': 'OU', 2670 '\u24C5': 'P', 2671 '\uFF30': 'P', 2672 '\u1E54': 'P', 2673 '\u1E56': 'P', 2674 '\u01A4': 'P', 2675 '\u2C63': 'P', 2676 '\uA750': 'P', 2677 '\uA752': 'P', 2678 '\uA754': 'P', 2679 '\u24C6': 'Q', 2680 '\uFF31': 'Q', 2681 '\uA756': 'Q', 2682 '\uA758': 'Q', 2683 '\u024A': 'Q', 2684 '\u24C7': 'R', 2685 '\uFF32': 'R', 2686 '\u0154': 'R', 2687 '\u1E58': 'R', 2688 '\u0158': 'R', 2689 '\u0210': 'R', 2690 '\u0212': 'R', 2691 '\u1E5A': 'R', 2692 '\u1E5C': 'R', 2693 '\u0156': 'R', 2694 '\u1E5E': 'R', 2695 '\u024C': 'R', 2696 '\u2C64': 'R', 2697 '\uA75A': 'R', 2698 '\uA7A6': 'R', 2699 '\uA782': 'R', 2700 '\u24C8': 'S', 2701 '\uFF33': 'S', 2702 '\u1E9E': 'S', 2703 '\u015A': 'S', 2704 '\u1E64': 'S', 2705 '\u015C': 'S', 2706 '\u1E60': 'S', 2707 '\u0160': 'S', 2708 '\u1E66': 'S', 2709 '\u1E62': 'S', 2710 '\u1E68': 'S', 2711 '\u0218': 'S', 2712 '\u015E': 'S', 2713 '\u2C7E': 'S', 2714 '\uA7A8': 'S', 2715 '\uA784': 'S', 2716 '\u24C9': 'T', 2717 '\uFF34': 'T', 2718 '\u1E6A': 'T', 2719 '\u0164': 'T', 2720 '\u1E6C': 'T', 2721 '\u021A': 'T', 2722 '\u0162': 'T', 2723 '\u1E70': 'T', 2724 '\u1E6E': 'T', 2725 '\u0166': 'T', 2726 '\u01AC': 'T', 2727 '\u01AE': 'T', 2728 '\u023E': 'T', 2729 '\uA786': 'T', 2730 '\uA728': 'TZ', 2731 '\u24CA': 'U', 2732 '\uFF35': 'U', 2733 '\u00D9': 'U', 2734 '\u00DA': 'U', 2735 '\u00DB': 'U', 2736 '\u0168': 'U', 2737 '\u1E78': 'U', 2738 '\u016A': 'U', 2739 '\u1E7A': 'U', 2740 '\u016C': 'U', 2741 '\u00DC': 'U', 2742 '\u01DB': 'U', 2743 '\u01D7': 'U', 2744 '\u01D5': 'U', 2745 '\u01D9': 'U', 2746 '\u1EE6': 'U', 2747 '\u016E': 'U', 2748 '\u0170': 'U', 2749 '\u01D3': 'U', 2750 '\u0214': 'U', 2751 '\u0216': 'U', 2752 '\u01AF': 'U', 2753 '\u1EEA': 'U', 2754 '\u1EE8': 'U', 2755 '\u1EEE': 'U', 2756 '\u1EEC': 'U', 2757 '\u1EF0': 'U', 2758 '\u1EE4': 'U', 2759 '\u1E72': 'U', 2760 '\u0172': 'U', 2761 '\u1E76': 'U', 2762 '\u1E74': 'U', 2763 '\u0244': 'U', 2764 '\u24CB': 'V', 2765 '\uFF36': 'V', 2766 '\u1E7C': 'V', 2767 '\u1E7E': 'V', 2768 '\u01B2': 'V', 2769 '\uA75E': 'V', 2770 '\u0245': 'V', 2771 '\uA760': 'VY', 2772 '\u24CC': 'W', 2773 '\uFF37': 'W', 2774 '\u1E80': 'W', 2775 '\u1E82': 'W', 2776 '\u0174': 'W', 2777 '\u1E86': 'W', 2778 '\u1E84': 'W', 2779 '\u1E88': 'W', 2780 '\u2C72': 'W', 2781 '\u24CD': 'X', 2782 '\uFF38': 'X', 2783 '\u1E8A': 'X', 2784 '\u1E8C': 'X', 2785 '\u24CE': 'Y', 2786 '\uFF39': 'Y', 2787 '\u1EF2': 'Y', 2788 '\u00DD': 'Y', 2789 '\u0176': 'Y', 2790 '\u1EF8': 'Y', 2791 '\u0232': 'Y', 2792 '\u1E8E': 'Y', 2793 '\u0178': 'Y', 2794 '\u1EF6': 'Y', 2795 '\u1EF4': 'Y', 2796 '\u01B3': 'Y', 2797 '\u024E': 'Y', 2798 '\u1EFE': 'Y', 2799 '\u24CF': 'Z', 2800 '\uFF3A': 'Z', 2801 '\u0179': 'Z', 2802 '\u1E90': 'Z', 2803 '\u017B': 'Z', 2804 '\u017D': 'Z', 2805 '\u1E92': 'Z', 2806 '\u1E94': 'Z', 2807 '\u01B5': 'Z', 2808 '\u0224': 'Z', 2809 '\u2C7F': 'Z', 2810 '\u2C6B': 'Z', 2811 '\uA762': 'Z', 2812 '\u24D0': 'a', 2813 '\uFF41': 'a', 2814 '\u1E9A': 'a', 2815 '\u00E0': 'a', 2816 '\u00E1': 'a', 2817 '\u00E2': 'a', 2818 '\u1EA7': 'a', 2819 '\u1EA5': 'a', 2820 '\u1EAB': 'a', 2821 '\u1EA9': 'a', 2822 '\u00E3': 'a', 2823 '\u0101': 'a', 2824 '\u0103': 'a', 2825 '\u1EB1': 'a', 2826 '\u1EAF': 'a', 2827 '\u1EB5': 'a', 2828 '\u1EB3': 'a', 2829 '\u0227': 'a', 2830 '\u01E1': 'a', 2831 '\u00E4': 'a', 2832 '\u01DF': 'a', 2833 '\u1EA3': 'a', 2834 '\u00E5': 'a', 2835 '\u01FB': 'a', 2836 '\u01CE': 'a', 2837 '\u0201': 'a', 2838 '\u0203': 'a', 2839 '\u1EA1': 'a', 2840 '\u1EAD': 'a', 2841 '\u1EB7': 'a', 2842 '\u1E01': 'a', 2843 '\u0105': 'a', 2844 '\u2C65': 'a', 2845 '\u0250': 'a', 2846 '\uA733': 'aa', 2847 '\u00E6': 'ae', 2848 '\u01FD': 'ae', 2849 '\u01E3': 'ae', 2850 '\uA735': 'ao', 2851 '\uA737': 'au', 2852 '\uA739': 'av', 2853 '\uA73B': 'av', 2854 '\uA73D': 'ay', 2855 '\u24D1': 'b', 2856 '\uFF42': 'b', 2857 '\u1E03': 'b', 2858 '\u1E05': 'b', 2859 '\u1E07': 'b', 2860 '\u0180': 'b', 2861 '\u0183': 'b', 2862 '\u0253': 'b', 2863 '\u24D2': 'c', 2864 '\uFF43': 'c', 2865 '\u0107': 'c', 2866 '\u0109': 'c', 2867 '\u010B': 'c', 2868 '\u010D': 'c', 2869 '\u00E7': 'c', 2870 '\u1E09': 'c', 2871 '\u0188': 'c', 2872 '\u023C': 'c', 2873 '\uA73F': 'c', 2874 '\u2184': 'c', 2875 '\u24D3': 'd', 2876 '\uFF44': 'd', 2877 '\u1E0B': 'd', 2878 '\u010F': 'd', 2879 '\u1E0D': 'd', 2880 '\u1E11': 'd', 2881 '\u1E13': 'd', 2882 '\u1E0F': 'd', 2883 '\u0111': 'd', 2884 '\u018C': 'd', 2885 '\u0256': 'd', 2886 '\u0257': 'd', 2887 '\uA77A': 'd', 2888 '\u01F3': 'dz', 2889 '\u01C6': 'dz', 2890 '\u24D4': 'e', 2891 '\uFF45': 'e', 2892 '\u00E8': 'e', 2893 '\u00E9': 'e', 2894 '\u00EA': 'e', 2895 '\u1EC1': 'e', 2896 '\u1EBF': 'e', 2897 '\u1EC5': 'e', 2898 '\u1EC3': 'e', 2899 '\u1EBD': 'e', 2900 '\u0113': 'e', 2901 '\u1E15': 'e', 2902 '\u1E17': 'e', 2903 '\u0115': 'e', 2904 '\u0117': 'e', 2905 '\u00EB': 'e', 2906 '\u1EBB': 'e', 2907 '\u011B': 'e', 2908 '\u0205': 'e', 2909 '\u0207': 'e', 2910 '\u1EB9': 'e', 2911 '\u1EC7': 'e', 2912 '\u0229': 'e', 2913 '\u1E1D': 'e', 2914 '\u0119': 'e', 2915 '\u1E19': 'e', 2916 '\u1E1B': 'e', 2917 '\u0247': 'e', 2918 '\u025B': 'e', 2919 '\u01DD': 'e', 2920 '\u24D5': 'f', 2921 '\uFF46': 'f', 2922 '\u1E1F': 'f', 2923 '\u0192': 'f', 2924 '\uA77C': 'f', 2925 '\u24D6': 'g', 2926 '\uFF47': 'g', 2927 '\u01F5': 'g', 2928 '\u011D': 'g', 2929 '\u1E21': 'g', 2930 '\u011F': 'g', 2931 '\u0121': 'g', 2932 '\u01E7': 'g', 2933 '\u0123': 'g', 2934 '\u01E5': 'g', 2935 '\u0260': 'g', 2936 '\uA7A1': 'g', 2937 '\u1D79': 'g', 2938 '\uA77F': 'g', 2939 '\u24D7': 'h', 2940 '\uFF48': 'h', 2941 '\u0125': 'h', 2942 '\u1E23': 'h', 2943 '\u1E27': 'h', 2944 '\u021F': 'h', 2945 '\u1E25': 'h', 2946 '\u1E29': 'h', 2947 '\u1E2B': 'h', 2948 '\u1E96': 'h', 2949 '\u0127': 'h', 2950 '\u2C68': 'h', 2951 '\u2C76': 'h', 2952 '\u0265': 'h', 2953 '\u0195': 'hv', 2954 '\u24D8': 'i', 2955 '\uFF49': 'i', 2956 '\u00EC': 'i', 2957 '\u00ED': 'i', 2958 '\u00EE': 'i', 2959 '\u0129': 'i', 2960 '\u012B': 'i', 2961 '\u012D': 'i', 2962 '\u00EF': 'i', 2963 '\u1E2F': 'i', 2964 '\u1EC9': 'i', 2965 '\u01D0': 'i', 2966 '\u0209': 'i', 2967 '\u020B': 'i', 2968 '\u1ECB': 'i', 2969 '\u012F': 'i', 2970 '\u1E2D': 'i', 2971 '\u0268': 'i', 2972 '\u0131': 'i', 2973 '\u24D9': 'j', 2974 '\uFF4A': 'j', 2975 '\u0135': 'j', 2976 '\u01F0': 'j', 2977 '\u0249': 'j', 2978 '\u24DA': 'k', 2979 '\uFF4B': 'k', 2980 '\u1E31': 'k', 2981 '\u01E9': 'k', 2982 '\u1E33': 'k', 2983 '\u0137': 'k', 2984 '\u1E35': 'k', 2985 '\u0199': 'k', 2986 '\u2C6A': 'k', 2987 '\uA741': 'k', 2988 '\uA743': 'k', 2989 '\uA745': 'k', 2990 '\uA7A3': 'k', 2991 '\u24DB': 'l', 2992 '\uFF4C': 'l', 2993 '\u0140': 'l', 2994 '\u013A': 'l', 2995 '\u013E': 'l', 2996 '\u1E37': 'l', 2997 '\u1E39': 'l', 2998 '\u013C': 'l', 2999 '\u1E3D': 'l', 3000 '\u1E3B': 'l', 3001 '\u017F': 'l', 3002 '\u0142': 'l', 3003 '\u019A': 'l', 3004 '\u026B': 'l', 3005 '\u2C61': 'l', 3006 '\uA749': 'l', 3007 '\uA781': 'l', 3008 '\uA747': 'l', 3009 '\u01C9': 'lj', 3010 '\u24DC': 'm', 3011 '\uFF4D': 'm', 3012 '\u1E3F': 'm', 3013 '\u1E41': 'm', 3014 '\u1E43': 'm', 3015 '\u0271': 'm', 3016 '\u026F': 'm', 3017 '\u24DD': 'n', 3018 '\uFF4E': 'n', 3019 '\u01F9': 'n', 3020 '\u0144': 'n', 3021 '\u00F1': 'n', 3022 '\u1E45': 'n', 3023 '\u0148': 'n', 3024 '\u1E47': 'n', 3025 '\u0146': 'n', 3026 '\u1E4B': 'n', 3027 '\u1E49': 'n', 3028 '\u019E': 'n', 3029 '\u0272': 'n', 3030 '\u0149': 'n', 3031 '\uA791': 'n', 3032 '\uA7A5': 'n', 3033 '\u01CC': 'nj', 3034 '\u24DE': 'o', 3035 '\uFF4F': 'o', 3036 '\u00F2': 'o', 3037 '\u00F3': 'o', 3038 '\u00F4': 'o', 3039 '\u1ED3': 'o', 3040 '\u1ED1': 'o', 3041 '\u1ED7': 'o', 3042 '\u1ED5': 'o', 3043 '\u00F5': 'o', 3044 '\u1E4D': 'o', 3045 '\u022D': 'o', 3046 '\u1E4F': 'o', 3047 '\u014D': 'o', 3048 '\u1E51': 'o', 3049 '\u1E53': 'o', 3050 '\u014F': 'o', 3051 '\u022F': 'o', 3052 '\u0231': 'o', 3053 '\u00F6': 'o', 3054 '\u022B': 'o', 3055 '\u1ECF': 'o', 3056 '\u0151': 'o', 3057 '\u01D2': 'o', 3058 '\u020D': 'o', 3059 '\u020F': 'o', 3060 '\u01A1': 'o', 3061 '\u1EDD': 'o', 3062 '\u1EDB': 'o', 3063 '\u1EE1': 'o', 3064 '\u1EDF': 'o', 3065 '\u1EE3': 'o', 3066 '\u1ECD': 'o', 3067 '\u1ED9': 'o', 3068 '\u01EB': 'o', 3069 '\u01ED': 'o', 3070 '\u00F8': 'o', 3071 '\u01FF': 'o', 3072 '\u0254': 'o', 3073 '\uA74B': 'o', 3074 '\uA74D': 'o', 3075 '\u0275': 'o', 3076 '\u0153': 'oe', 3077 '\u01A3': 'oi', 3078 '\u0223': 'ou', 3079 '\uA74F': 'oo', 3080 '\u24DF': 'p', 3081 '\uFF50': 'p', 3082 '\u1E55': 'p', 3083 '\u1E57': 'p', 3084 '\u01A5': 'p', 3085 '\u1D7D': 'p', 3086 '\uA751': 'p', 3087 '\uA753': 'p', 3088 '\uA755': 'p', 3089 '\u24E0': 'q', 3090 '\uFF51': 'q', 3091 '\u024B': 'q', 3092 '\uA757': 'q', 3093 '\uA759': 'q', 3094 '\u24E1': 'r', 3095 '\uFF52': 'r', 3096 '\u0155': 'r', 3097 '\u1E59': 'r', 3098 '\u0159': 'r', 3099 '\u0211': 'r', 3100 '\u0213': 'r', 3101 '\u1E5B': 'r', 3102 '\u1E5D': 'r', 3103 '\u0157': 'r', 3104 '\u1E5F': 'r', 3105 '\u024D': 'r', 3106 '\u027D': 'r', 3107 '\uA75B': 'r', 3108 '\uA7A7': 'r', 3109 '\uA783': 'r', 3110 '\u24E2': 's', 3111 '\uFF53': 's', 3112 '\u00DF': 's', 3113 '\u015B': 's', 3114 '\u1E65': 's', 3115 '\u015D': 's', 3116 '\u1E61': 's', 3117 '\u0161': 's', 3118 '\u1E67': 's', 3119 '\u1E63': 's', 3120 '\u1E69': 's', 3121 '\u0219': 's', 3122 '\u015F': 's', 3123 '\u023F': 's', 3124 '\uA7A9': 's', 3125 '\uA785': 's', 3126 '\u1E9B': 's', 3127 '\u24E3': 't', 3128 '\uFF54': 't', 3129 '\u1E6B': 't', 3130 '\u1E97': 't', 3131 '\u0165': 't', 3132 '\u1E6D': 't', 3133 '\u021B': 't', 3134 '\u0163': 't', 3135 '\u1E71': 't', 3136 '\u1E6F': 't', 3137 '\u0167': 't', 3138 '\u01AD': 't', 3139 '\u0288': 't', 3140 '\u2C66': 't', 3141 '\uA787': 't', 3142 '\uA729': 'tz', 3143 '\u24E4': 'u', 3144 '\uFF55': 'u', 3145 '\u00F9': 'u', 3146 '\u00FA': 'u', 3147 '\u00FB': 'u', 3148 '\u0169': 'u', 3149 '\u1E79': 'u', 3150 '\u016B': 'u', 3151 '\u1E7B': 'u', 3152 '\u016D': 'u', 3153 '\u00FC': 'u', 3154 '\u01DC': 'u', 3155 '\u01D8': 'u', 3156 '\u01D6': 'u', 3157 '\u01DA': 'u', 3158 '\u1EE7': 'u', 3159 '\u016F': 'u', 3160 '\u0171': 'u', 3161 '\u01D4': 'u', 3162 '\u0215': 'u', 3163 '\u0217': 'u', 3164 '\u01B0': 'u', 3165 '\u1EEB': 'u', 3166 '\u1EE9': 'u', 3167 '\u1EEF': 'u', 3168 '\u1EED': 'u', 3169 '\u1EF1': 'u', 3170 '\u1EE5': 'u', 3171 '\u1E73': 'u', 3172 '\u0173': 'u', 3173 '\u1E77': 'u', 3174 '\u1E75': 'u', 3175 '\u0289': 'u', 3176 '\u24E5': 'v', 3177 '\uFF56': 'v', 3178 '\u1E7D': 'v', 3179 '\u1E7F': 'v', 3180 '\u028B': 'v', 3181 '\uA75F': 'v', 3182 '\u028C': 'v', 3183 '\uA761': 'vy', 3184 '\u24E6': 'w', 3185 '\uFF57': 'w', 3186 '\u1E81': 'w', 3187 '\u1E83': 'w', 3188 '\u0175': 'w', 3189 '\u1E87': 'w', 3190 '\u1E85': 'w', 3191 '\u1E98': 'w', 3192 '\u1E89': 'w', 3193 '\u2C73': 'w', 3194 '\u24E7': 'x', 3195 '\uFF58': 'x', 3196 '\u1E8B': 'x', 3197 '\u1E8D': 'x', 3198 '\u24E8': 'y', 3199 '\uFF59': 'y', 3200 '\u1EF3': 'y', 3201 '\u00FD': 'y', 3202 '\u0177': 'y', 3203 '\u1EF9': 'y', 3204 '\u0233': 'y', 3205 '\u1E8F': 'y', 3206 '\u00FF': 'y', 3207 '\u1EF7': 'y', 3208 '\u1E99': 'y', 3209 '\u1EF5': 'y', 3210 '\u01B4': 'y', 3211 '\u024F': 'y', 3212 '\u1EFF': 'y', 3213 '\u24E9': 'z', 3214 '\uFF5A': 'z', 3215 '\u017A': 'z', 3216 '\u1E91': 'z', 3217 '\u017C': 'z', 3218 '\u017E': 'z', 3219 '\u1E93': 'z', 3220 '\u1E95': 'z', 3221 '\u01B6': 'z', 3222 '\u0225': 'z', 3223 '\u0240': 'z', 3224 '\u2C6C': 'z', 3225 '\uA763': 'z', 3226 '\u0386': '\u0391', 3227 '\u0388': '\u0395', 3228 '\u0389': '\u0397', 3229 '\u038A': '\u0399', 3230 '\u03AA': '\u0399', 3231 '\u038C': '\u039F', 3232 '\u038E': '\u03A5', 3233 '\u03AB': '\u03A5', 3234 '\u038F': '\u03A9', 3235 '\u03AC': '\u03B1', 3236 '\u03AD': '\u03B5', 3237 '\u03AE': '\u03B7', 3238 '\u03AF': '\u03B9', 3239 '\u03CA': '\u03B9', 3240 '\u0390': '\u03B9', 3241 '\u03CC': '\u03BF', 3242 '\u03CD': '\u03C5', 3243 '\u03CB': '\u03C5', 3244 '\u03B0': '\u03C5', 3245 '\u03CE': '\u03C9', 3246 '\u03C2': '\u03C3', 3247 '\u2019': '\'' 3248 }; 3249 3250 return diacritics; 3251}); 3252 3253S2.define('select2/data/base',[ 3254 '../utils' 3255], function (Utils) { 3256 function BaseAdapter ($element, options) { 3257 BaseAdapter.__super__.constructor.call(this); 3258 } 3259 3260 Utils.Extend(BaseAdapter, Utils.Observable); 3261 3262 BaseAdapter.prototype.current = function (callback) { 3263 throw new Error('The `current` method must be defined in child classes.'); 3264 }; 3265 3266 BaseAdapter.prototype.query = function (params, callback) { 3267 throw new Error('The `query` method must be defined in child classes.'); 3268 }; 3269 3270 BaseAdapter.prototype.bind = function (container, $container) { 3271 // Can be implemented in subclasses 3272 }; 3273 3274 BaseAdapter.prototype.destroy = function () { 3275 // Can be implemented in subclasses 3276 }; 3277 3278 BaseAdapter.prototype.generateResultId = function (container, data) { 3279 var id = container.id + '-result-'; 3280 3281 id += Utils.generateChars(4); 3282 3283 if (data.id != null) { 3284 id += '-' + data.id.toString(); 3285 } else { 3286 id += '-' + Utils.generateChars(4); 3287 } 3288 return id; 3289 }; 3290 3291 return BaseAdapter; 3292}); 3293 3294S2.define('select2/data/select',[ 3295 './base', 3296 '../utils', 3297 'jquery' 3298], function (BaseAdapter, Utils, $) { 3299 function SelectAdapter ($element, options) { 3300 this.$element = $element; 3301 this.options = options; 3302 3303 SelectAdapter.__super__.constructor.call(this); 3304 } 3305 3306 Utils.Extend(SelectAdapter, BaseAdapter); 3307 3308 SelectAdapter.prototype.current = function (callback) { 3309 var self = this; 3310 3311 var data = Array.prototype.map.call( 3312 this.$element[0].querySelectorAll(':checked'), 3313 function (selectedElement) { 3314 return self.item($(selectedElement)); 3315 } 3316 ); 3317 3318 callback(data); 3319 }; 3320 3321 SelectAdapter.prototype.select = function (data) { 3322 var self = this; 3323 3324 data.selected = true; 3325 3326 // If data.element is a DOM node, use it instead 3327 if ( 3328 data.element != null && data.element.tagName.toLowerCase() === 'option' 3329 ) { 3330 data.element.selected = true; 3331 3332 this.$element.trigger('input').trigger('change'); 3333 3334 return; 3335 } 3336 3337 if (this.$element.prop('multiple')) { 3338 this.current(function (currentData) { 3339 var val = []; 3340 3341 data = [data]; 3342 data.push.apply(data, currentData); 3343 3344 for (var d = 0; d < data.length; d++) { 3345 var id = data[d].id; 3346 3347 if (val.indexOf(id) === -1) { 3348 val.push(id); 3349 } 3350 } 3351 3352 self.$element.val(val); 3353 self.$element.trigger('input').trigger('change'); 3354 }); 3355 } else { 3356 var val = data.id; 3357 3358 this.$element.val(val); 3359 this.$element.trigger('input').trigger('change'); 3360 } 3361 }; 3362 3363 SelectAdapter.prototype.unselect = function (data) { 3364 var self = this; 3365 3366 if (!this.$element.prop('multiple')) { 3367 return; 3368 } 3369 3370 data.selected = false; 3371 3372 if ( 3373 data.element != null && 3374 data.element.tagName.toLowerCase() === 'option' 3375 ) { 3376 data.element.selected = false; 3377 3378 this.$element.trigger('input').trigger('change'); 3379 3380 return; 3381 } 3382 3383 this.current(function (currentData) { 3384 var val = []; 3385 3386 for (var d = 0; d < currentData.length; d++) { 3387 var id = currentData[d].id; 3388 3389 if (id !== data.id && val.indexOf(id) === -1) { 3390 val.push(id); 3391 } 3392 } 3393 3394 self.$element.val(val); 3395 3396 self.$element.trigger('input').trigger('change'); 3397 }); 3398 }; 3399 3400 SelectAdapter.prototype.bind = function (container, $container) { 3401 var self = this; 3402 3403 this.container = container; 3404 3405 container.on('select', function (params) { 3406 self.select(params.data); 3407 }); 3408 3409 container.on('unselect', function (params) { 3410 self.unselect(params.data); 3411 }); 3412 }; 3413 3414 SelectAdapter.prototype.destroy = function () { 3415 // Remove anything added to child elements 3416 this.$element.find('*').each(function () { 3417 // Remove any custom data set by Select2 3418 Utils.RemoveData(this); 3419 }); 3420 }; 3421 3422 SelectAdapter.prototype.query = function (params, callback) { 3423 var data = []; 3424 var self = this; 3425 3426 var $options = this.$element.children(); 3427 3428 $options.each(function () { 3429 if ( 3430 this.tagName.toLowerCase() !== 'option' && 3431 this.tagName.toLowerCase() !== 'optgroup' 3432 ) { 3433 return; 3434 } 3435 3436 var $option = $(this); 3437 3438 var option = self.item($option); 3439 3440 var matches = self.matches(params, option); 3441 3442 if (matches !== null) { 3443 data.push(matches); 3444 } 3445 }); 3446 3447 callback({ 3448 results: data 3449 }); 3450 }; 3451 3452 SelectAdapter.prototype.addOptions = function ($options) { 3453 this.$element.append($options); 3454 }; 3455 3456 SelectAdapter.prototype.option = function (data) { 3457 var option; 3458 3459 if (data.children) { 3460 option = document.createElement('optgroup'); 3461 option.label = data.text; 3462 } else { 3463 option = document.createElement('option'); 3464 3465 if (option.textContent !== undefined) { 3466 option.textContent = data.text; 3467 } else { 3468 option.innerText = data.text; 3469 } 3470 } 3471 3472 if (data.id !== undefined) { 3473 option.value = data.id; 3474 } 3475 3476 if (data.disabled) { 3477 option.disabled = true; 3478 } 3479 3480 if (data.selected) { 3481 option.selected = true; 3482 } 3483 3484 if (data.title) { 3485 option.title = data.title; 3486 } 3487 3488 var normalizedData = this._normalizeItem(data); 3489 normalizedData.element = option; 3490 3491 // Override the option's data with the combined data 3492 Utils.StoreData(option, 'data', normalizedData); 3493 3494 return $(option); 3495 }; 3496 3497 SelectAdapter.prototype.item = function ($option) { 3498 var data = {}; 3499 3500 data = Utils.GetData($option[0], 'data'); 3501 3502 if (data != null) { 3503 return data; 3504 } 3505 3506 var option = $option[0]; 3507 3508 if (option.tagName.toLowerCase() === 'option') { 3509 data = { 3510 id: $option.val(), 3511 text: $option.text(), 3512 disabled: $option.prop('disabled'), 3513 selected: $option.prop('selected'), 3514 title: $option.prop('title') 3515 }; 3516 } else if (option.tagName.toLowerCase() === 'optgroup') { 3517 data = { 3518 text: $option.prop('label'), 3519 children: [], 3520 title: $option.prop('title') 3521 }; 3522 3523 var $children = $option.children('option'); 3524 var children = []; 3525 3526 for (var c = 0; c < $children.length; c++) { 3527 var $child = $($children[c]); 3528 3529 var child = this.item($child); 3530 3531 children.push(child); 3532 } 3533 3534 data.children = children; 3535 } 3536 3537 data = this._normalizeItem(data); 3538 data.element = $option[0]; 3539 3540 Utils.StoreData($option[0], 'data', data); 3541 3542 return data; 3543 }; 3544 3545 SelectAdapter.prototype._normalizeItem = function (item) { 3546 if (item !== Object(item)) { 3547 item = { 3548 id: item, 3549 text: item 3550 }; 3551 } 3552 3553 item = $.extend({}, { 3554 text: '' 3555 }, item); 3556 3557 var defaults = { 3558 selected: false, 3559 disabled: false 3560 }; 3561 3562 if (item.id != null) { 3563 item.id = item.id.toString(); 3564 } 3565 3566 if (item.text != null) { 3567 item.text = item.text.toString(); 3568 } 3569 3570 if (item._resultId == null && item.id && this.container != null) { 3571 item._resultId = this.generateResultId(this.container, item); 3572 } 3573 3574 return $.extend({}, defaults, item); 3575 }; 3576 3577 SelectAdapter.prototype.matches = function (params, data) { 3578 var matcher = this.options.get('matcher'); 3579 3580 return matcher(params, data); 3581 }; 3582 3583 return SelectAdapter; 3584}); 3585 3586S2.define('select2/data/array',[ 3587 './select', 3588 '../utils', 3589 'jquery' 3590], function (SelectAdapter, Utils, $) { 3591 function ArrayAdapter ($element, options) { 3592 this._dataToConvert = options.get('data') || []; 3593 3594 ArrayAdapter.__super__.constructor.call(this, $element, options); 3595 } 3596 3597 Utils.Extend(ArrayAdapter, SelectAdapter); 3598 3599 ArrayAdapter.prototype.bind = function (container, $container) { 3600 ArrayAdapter.__super__.bind.call(this, container, $container); 3601 3602 this.addOptions(this.convertToOptions(this._dataToConvert)); 3603 }; 3604 3605 ArrayAdapter.prototype.select = function (data) { 3606 var $option = this.$element.find('option').filter(function (i, elm) { 3607 return elm.value == data.id.toString(); 3608 }); 3609 3610 if ($option.length === 0) { 3611 $option = this.option(data); 3612 3613 this.addOptions($option); 3614 } 3615 3616 ArrayAdapter.__super__.select.call(this, data); 3617 }; 3618 3619 ArrayAdapter.prototype.convertToOptions = function (data) { 3620 var self = this; 3621 3622 var $existing = this.$element.find('option'); 3623 var existingIds = $existing.map(function () { 3624 return self.item($(this)).id; 3625 }).get(); 3626 3627 var $options = []; 3628 3629 // Filter out all items except for the one passed in the argument 3630 function onlyItem (item) { 3631 return function () { 3632 return $(this).val() == item.id; 3633 }; 3634 } 3635 3636 for (var d = 0; d < data.length; d++) { 3637 var item = this._normalizeItem(data[d]); 3638 3639 // Skip items which were pre-loaded, only merge the data 3640 if (existingIds.indexOf(item.id) >= 0) { 3641 var $existingOption = $existing.filter(onlyItem(item)); 3642 3643 var existingData = this.item($existingOption); 3644 var newData = $.extend(true, {}, item, existingData); 3645 3646 var $newOption = this.option(newData); 3647 3648 $existingOption.replaceWith($newOption); 3649 3650 continue; 3651 } 3652 3653 var $option = this.option(item); 3654 3655 if (item.children) { 3656 var $children = this.convertToOptions(item.children); 3657 3658 $option.append($children); 3659 } 3660 3661 $options.push($option); 3662 } 3663 3664 return $options; 3665 }; 3666 3667 return ArrayAdapter; 3668}); 3669 3670S2.define('select2/data/ajax',[ 3671 './array', 3672 '../utils', 3673 'jquery' 3674], function (ArrayAdapter, Utils, $) { 3675 function AjaxAdapter ($element, options) { 3676 this.ajaxOptions = this._applyDefaults(options.get('ajax')); 3677 3678 if (this.ajaxOptions.processResults != null) { 3679 this.processResults = this.ajaxOptions.processResults; 3680 } 3681 3682 AjaxAdapter.__super__.constructor.call(this, $element, options); 3683 } 3684 3685 Utils.Extend(AjaxAdapter, ArrayAdapter); 3686 3687 AjaxAdapter.prototype._applyDefaults = function (options) { 3688 var defaults = { 3689 data: function (params) { 3690 return $.extend({}, params, { 3691 q: params.term 3692 }); 3693 }, 3694 transport: function (params, success, failure) { 3695 var $request = $.ajax(params); 3696 3697 $request.then(success); 3698 $request.fail(failure); 3699 3700 return $request; 3701 } 3702 }; 3703 3704 return $.extend({}, defaults, options, true); 3705 }; 3706 3707 AjaxAdapter.prototype.processResults = function (results) { 3708 return results; 3709 }; 3710 3711 AjaxAdapter.prototype.query = function (params, callback) { 3712 var matches = []; 3713 var self = this; 3714 3715 if (this._request != null) { 3716 // JSONP requests cannot always be aborted 3717 if (typeof this._request.abort === 'function') { 3718 this._request.abort(); 3719 } 3720 3721 this._request = null; 3722 } 3723 3724 var options = $.extend({ 3725 type: 'GET' 3726 }, this.ajaxOptions); 3727 3728 if (typeof options.url === 'function') { 3729 options.url = options.url.call(this.$element, params); 3730 } 3731 3732 if (typeof options.data === 'function') { 3733 options.data = options.data.call(this.$element, params); 3734 } 3735 3736 function request () { 3737 var $request = options.transport(options, function (data) { 3738 var results = self.processResults(data, params); 3739 3740 if (self.options.get('debug') && window.console && console.error) { 3741 // Check to make sure that the response included a `results` key. 3742 if (!results || !results.results || !Array.isArray(results.results)) { 3743 console.error( 3744 'Select2: The AJAX results did not return an array in the ' + 3745 '`results` key of the response.' 3746 ); 3747 } 3748 } 3749 3750 callback(results); 3751 }, function () { 3752 // Attempt to detect if a request was aborted 3753 // Only works if the transport exposes a status property 3754 if ('status' in $request && 3755 ($request.status === 0 || $request.status === '0')) { 3756 return; 3757 } 3758 3759 self.trigger('results:message', { 3760 message: 'errorLoading' 3761 }); 3762 }); 3763 3764 self._request = $request; 3765 } 3766 3767 if (this.ajaxOptions.delay && params.term != null) { 3768 if (this._queryTimeout) { 3769 window.clearTimeout(this._queryTimeout); 3770 } 3771 3772 this._queryTimeout = window.setTimeout(request, this.ajaxOptions.delay); 3773 } else { 3774 request(); 3775 } 3776 }; 3777 3778 return AjaxAdapter; 3779}); 3780 3781S2.define('select2/data/tags',[ 3782 'jquery' 3783], function ($) { 3784 function Tags (decorated, $element, options) { 3785 var tags = options.get('tags'); 3786 3787 var createTag = options.get('createTag'); 3788 3789 if (createTag !== undefined) { 3790 this.createTag = createTag; 3791 } 3792 3793 var insertTag = options.get('insertTag'); 3794 3795 if (insertTag !== undefined) { 3796 this.insertTag = insertTag; 3797 } 3798 3799 decorated.call(this, $element, options); 3800 3801 if (Array.isArray(tags)) { 3802 for (var t = 0; t < tags.length; t++) { 3803 var tag = tags[t]; 3804 var item = this._normalizeItem(tag); 3805 3806 var $option = this.option(item); 3807 3808 this.$element.append($option); 3809 } 3810 } 3811 } 3812 3813 Tags.prototype.query = function (decorated, params, callback) { 3814 var self = this; 3815 3816 this._removeOldTags(); 3817 3818 if (params.term == null || params.page != null) { 3819 decorated.call(this, params, callback); 3820 return; 3821 } 3822 3823 function wrapper (obj, child) { 3824 var data = obj.results; 3825 3826 for (var i = 0; i < data.length; i++) { 3827 var option = data[i]; 3828 3829 var checkChildren = ( 3830 option.children != null && 3831 !wrapper({ 3832 results: option.children 3833 }, true) 3834 ); 3835 3836 var optionText = (option.text || '').toUpperCase(); 3837 var paramsTerm = (params.term || '').toUpperCase(); 3838 3839 var checkText = optionText === paramsTerm; 3840 3841 if (checkText || checkChildren) { 3842 if (child) { 3843 return false; 3844 } 3845 3846 obj.data = data; 3847 callback(obj); 3848 3849 return; 3850 } 3851 } 3852 3853 if (child) { 3854 return true; 3855 } 3856 3857 var tag = self.createTag(params); 3858 3859 if (tag != null) { 3860 var $option = self.option(tag); 3861 $option.attr('data-select2-tag', 'true'); 3862 3863 self.addOptions([$option]); 3864 3865 self.insertTag(data, tag); 3866 } 3867 3868 obj.results = data; 3869 3870 callback(obj); 3871 } 3872 3873 decorated.call(this, params, wrapper); 3874 }; 3875 3876 Tags.prototype.createTag = function (decorated, params) { 3877 if (params.term == null) { 3878 return null; 3879 } 3880 3881 var term = params.term.trim(); 3882 3883 if (term === '') { 3884 return null; 3885 } 3886 3887 return { 3888 id: term, 3889 text: term 3890 }; 3891 }; 3892 3893 Tags.prototype.insertTag = function (_, data, tag) { 3894 data.unshift(tag); 3895 }; 3896 3897 Tags.prototype._removeOldTags = function (_) { 3898 var $options = this.$element.find('option[data-select2-tag]'); 3899 3900 $options.each(function () { 3901 if (this.selected) { 3902 return; 3903 } 3904 3905 $(this).remove(); 3906 }); 3907 }; 3908 3909 return Tags; 3910}); 3911 3912S2.define('select2/data/tokenizer',[ 3913 'jquery' 3914], function ($) { 3915 function Tokenizer (decorated, $element, options) { 3916 var tokenizer = options.get('tokenizer'); 3917 3918 if (tokenizer !== undefined) { 3919 this.tokenizer = tokenizer; 3920 } 3921 3922 decorated.call(this, $element, options); 3923 } 3924 3925 Tokenizer.prototype.bind = function (decorated, container, $container) { 3926 decorated.call(this, container, $container); 3927 3928 this.$search = container.dropdown.$search || container.selection.$search || 3929 $container.find('.select2-search__field'); 3930 }; 3931 3932 Tokenizer.prototype.query = function (decorated, params, callback) { 3933 var self = this; 3934 3935 function createAndSelect (data) { 3936 // Normalize the data object so we can use it for checks 3937 var item = self._normalizeItem(data); 3938 3939 // Check if the data object already exists as a tag 3940 // Select it if it doesn't 3941 var $existingOptions = self.$element.find('option').filter(function () { 3942 return $(this).val() === item.id; 3943 }); 3944 3945 // If an existing option wasn't found for it, create the option 3946 if (!$existingOptions.length) { 3947 var $option = self.option(item); 3948 $option.attr('data-select2-tag', true); 3949 3950 self._removeOldTags(); 3951 self.addOptions([$option]); 3952 } 3953 3954 // Select the item, now that we know there is an option for it 3955 select(item); 3956 } 3957 3958 function select (data) { 3959 self.trigger('select', { 3960 data: data 3961 }); 3962 } 3963 3964 params.term = params.term || ''; 3965 3966 var tokenData = this.tokenizer(params, this.options, createAndSelect); 3967 3968 if (tokenData.term !== params.term) { 3969 // Replace the search term if we have the search box 3970 if (this.$search.length) { 3971 this.$search.val(tokenData.term); 3972 this.$search.trigger('focus'); 3973 } 3974 3975 params.term = tokenData.term; 3976 } 3977 3978 decorated.call(this, params, callback); 3979 }; 3980 3981 Tokenizer.prototype.tokenizer = function (_, params, options, callback) { 3982 var separators = options.get('tokenSeparators') || []; 3983 var term = params.term; 3984 var i = 0; 3985 3986 var createTag = this.createTag || function (params) { 3987 return { 3988 id: params.term, 3989 text: params.term 3990 }; 3991 }; 3992 3993 while (i < term.length) { 3994 var termChar = term[i]; 3995 3996 if (separators.indexOf(termChar) === -1) { 3997 i++; 3998 3999 continue; 4000 } 4001 4002 var part = term.substr(0, i); 4003 var partParams = $.extend({}, params, { 4004 term: part 4005 }); 4006 4007 var data = createTag(partParams); 4008 4009 if (data == null) { 4010 i++; 4011 continue; 4012 } 4013 4014 callback(data); 4015 4016 // Reset the term to not include the tokenized portion 4017 term = term.substr(i + 1) || ''; 4018 i = 0; 4019 } 4020 4021 return { 4022 term: term 4023 }; 4024 }; 4025 4026 return Tokenizer; 4027}); 4028 4029S2.define('select2/data/minimumInputLength',[ 4030 4031], function () { 4032 function MinimumInputLength (decorated, $e, options) { 4033 this.minimumInputLength = options.get('minimumInputLength'); 4034 4035 decorated.call(this, $e, options); 4036 } 4037 4038 MinimumInputLength.prototype.query = function (decorated, params, callback) { 4039 params.term = params.term || ''; 4040 4041 if (params.term.length < this.minimumInputLength) { 4042 this.trigger('results:message', { 4043 message: 'inputTooShort', 4044 args: { 4045 minimum: this.minimumInputLength, 4046 input: params.term, 4047 params: params 4048 } 4049 }); 4050 4051 return; 4052 } 4053 4054 decorated.call(this, params, callback); 4055 }; 4056 4057 return MinimumInputLength; 4058}); 4059 4060S2.define('select2/data/maximumInputLength',[ 4061 4062], function () { 4063 function MaximumInputLength (decorated, $e, options) { 4064 this.maximumInputLength = options.get('maximumInputLength'); 4065 4066 decorated.call(this, $e, options); 4067 } 4068 4069 MaximumInputLength.prototype.query = function (decorated, params, callback) { 4070 params.term = params.term || ''; 4071 4072 if (this.maximumInputLength > 0 && 4073 params.term.length > this.maximumInputLength) { 4074 this.trigger('results:message', { 4075 message: 'inputTooLong', 4076 args: { 4077 maximum: this.maximumInputLength, 4078 input: params.term, 4079 params: params 4080 } 4081 }); 4082 4083 return; 4084 } 4085 4086 decorated.call(this, params, callback); 4087 }; 4088 4089 return MaximumInputLength; 4090}); 4091 4092S2.define('select2/data/maximumSelectionLength',[ 4093 4094], function (){ 4095 function MaximumSelectionLength (decorated, $e, options) { 4096 this.maximumSelectionLength = options.get('maximumSelectionLength'); 4097 4098 decorated.call(this, $e, options); 4099 } 4100 4101 MaximumSelectionLength.prototype.bind = 4102 function (decorated, container, $container) { 4103 var self = this; 4104 4105 decorated.call(this, container, $container); 4106 4107 container.on('select', function () { 4108 self._checkIfMaximumSelected(); 4109 }); 4110 }; 4111 4112 MaximumSelectionLength.prototype.query = 4113 function (decorated, params, callback) { 4114 var self = this; 4115 4116 this._checkIfMaximumSelected(function () { 4117 decorated.call(self, params, callback); 4118 }); 4119 }; 4120 4121 MaximumSelectionLength.prototype._checkIfMaximumSelected = 4122 function (_, successCallback) { 4123 var self = this; 4124 4125 this.current(function (currentData) { 4126 var count = currentData != null ? currentData.length : 0; 4127 if (self.maximumSelectionLength > 0 && 4128 count >= self.maximumSelectionLength) { 4129 self.trigger('results:message', { 4130 message: 'maximumSelected', 4131 args: { 4132 maximum: self.maximumSelectionLength 4133 } 4134 }); 4135 return; 4136 } 4137 4138 if (successCallback) { 4139 successCallback(); 4140 } 4141 }); 4142 }; 4143 4144 return MaximumSelectionLength; 4145}); 4146 4147S2.define('select2/dropdown',[ 4148 'jquery', 4149 './utils' 4150], function ($, Utils) { 4151 function Dropdown ($element, options) { 4152 this.$element = $element; 4153 this.options = options; 4154 4155 Dropdown.__super__.constructor.call(this); 4156 } 4157 4158 Utils.Extend(Dropdown, Utils.Observable); 4159 4160 Dropdown.prototype.render = function () { 4161 var $dropdown = $( 4162 '<span class="select2-dropdown">' + 4163 '<span class="select2-results"></span>' + 4164 '</span>' 4165 ); 4166 4167 $dropdown.attr('dir', this.options.get('dir')); 4168 4169 this.$dropdown = $dropdown; 4170 4171 return $dropdown; 4172 }; 4173 4174 Dropdown.prototype.bind = function () { 4175 // Should be implemented in subclasses 4176 }; 4177 4178 Dropdown.prototype.position = function ($dropdown, $container) { 4179 // Should be implemented in subclasses 4180 }; 4181 4182 Dropdown.prototype.destroy = function () { 4183 // Remove the dropdown from the DOM 4184 this.$dropdown.remove(); 4185 }; 4186 4187 return Dropdown; 4188}); 4189 4190S2.define('select2/dropdown/search',[ 4191 'jquery' 4192], function ($) { 4193 function Search () { } 4194 4195 Search.prototype.render = function (decorated) { 4196 var $rendered = decorated.call(this); 4197 var searchLabel = this.options.get('translations').get('search'); 4198 4199 var $search = $( 4200 '<span class="select2-search select2-search--dropdown">' + 4201 '<input class="select2-search__field" type="search" tabindex="-1"' + 4202 ' autocorrect="off" autocapitalize="none"' + 4203 ' spellcheck="false" role="searchbox" aria-autocomplete="list" />' + 4204 '</span>' 4205 ); 4206 4207 this.$searchContainer = $search; 4208 this.$search = $search.find('input'); 4209 4210 this.$search.prop('autocomplete', this.options.get('autocomplete')); 4211 this.$search.attr('aria-label', searchLabel()); 4212 4213 $rendered.prepend($search); 4214 4215 return $rendered; 4216 }; 4217 4218 Search.prototype.bind = function (decorated, container, $container) { 4219 var self = this; 4220 4221 var resultsId = container.id + '-results'; 4222 4223 decorated.call(this, container, $container); 4224 4225 this.$search.on('keydown', function (evt) { 4226 self.trigger('keypress', evt); 4227 4228 self._keyUpPrevented = evt.isDefaultPrevented(); 4229 }); 4230 4231 // Workaround for browsers which do not support the `input` event 4232 // This will prevent double-triggering of events for browsers which support 4233 // both the `keyup` and `input` events. 4234 this.$search.on('input', function (evt) { 4235 // Unbind the duplicated `keyup` event 4236 $(this).off('keyup'); 4237 }); 4238 4239 this.$search.on('keyup input', function (evt) { 4240 self.handleSearch(evt); 4241 }); 4242 4243 container.on('open', function () { 4244 self.$search.attr('tabindex', 0); 4245 self.$search.attr('aria-controls', resultsId); 4246 4247 self.$search.trigger('focus'); 4248 4249 window.setTimeout(function () { 4250 self.$search.trigger('focus'); 4251 }, 0); 4252 }); 4253 4254 container.on('close', function () { 4255 self.$search.attr('tabindex', -1); 4256 self.$search.removeAttr('aria-controls'); 4257 self.$search.removeAttr('aria-activedescendant'); 4258 4259 self.$search.val(''); 4260 self.$search.trigger('blur'); 4261 }); 4262 4263 container.on('focus', function () { 4264 if (!container.isOpen()) { 4265 self.$search.trigger('focus'); 4266 } 4267 }); 4268 4269 container.on('results:all', function (params) { 4270 if (params.query.term == null || params.query.term === '') { 4271 var showSearch = self.showSearch(params); 4272 4273 if (showSearch) { 4274 self.$searchContainer[0].classList.remove('select2-search--hide'); 4275 } else { 4276 self.$searchContainer[0].classList.add('select2-search--hide'); 4277 } 4278 } 4279 }); 4280 4281 container.on('results:focus', function (params) { 4282 if (params.data._resultId) { 4283 self.$search.attr('aria-activedescendant', params.data._resultId); 4284 } else { 4285 self.$search.removeAttr('aria-activedescendant'); 4286 } 4287 }); 4288 }; 4289 4290 Search.prototype.handleSearch = function (evt) { 4291 if (!this._keyUpPrevented) { 4292 var input = this.$search.val(); 4293 4294 this.trigger('query', { 4295 term: input 4296 }); 4297 } 4298 4299 this._keyUpPrevented = false; 4300 }; 4301 4302 Search.prototype.showSearch = function (_, params) { 4303 return true; 4304 }; 4305 4306 return Search; 4307}); 4308 4309S2.define('select2/dropdown/hidePlaceholder',[ 4310 4311], function () { 4312 function HidePlaceholder (decorated, $element, options, dataAdapter) { 4313 this.placeholder = this.normalizePlaceholder(options.get('placeholder')); 4314 4315 decorated.call(this, $element, options, dataAdapter); 4316 } 4317 4318 HidePlaceholder.prototype.append = function (decorated, data) { 4319 data.results = this.removePlaceholder(data.results); 4320 4321 decorated.call(this, data); 4322 }; 4323 4324 HidePlaceholder.prototype.normalizePlaceholder = function (_, placeholder) { 4325 if (typeof placeholder === 'string') { 4326 placeholder = { 4327 id: '', 4328 text: placeholder 4329 }; 4330 } 4331 4332 return placeholder; 4333 }; 4334 4335 HidePlaceholder.prototype.removePlaceholder = function (_, data) { 4336 var modifiedData = data.slice(0); 4337 4338 for (var d = data.length - 1; d >= 0; d--) { 4339 var item = data[d]; 4340 4341 if (this.placeholder.id === item.id) { 4342 modifiedData.splice(d, 1); 4343 } 4344 } 4345 4346 return modifiedData; 4347 }; 4348 4349 return HidePlaceholder; 4350}); 4351 4352S2.define('select2/dropdown/infiniteScroll',[ 4353 'jquery' 4354], function ($) { 4355 function InfiniteScroll (decorated, $element, options, dataAdapter) { 4356 this.lastParams = {}; 4357 4358 decorated.call(this, $element, options, dataAdapter); 4359 4360 this.$loadingMore = this.createLoadingMore(); 4361 this.loading = false; 4362 } 4363 4364 InfiniteScroll.prototype.append = function (decorated, data) { 4365 this.$loadingMore.remove(); 4366 this.loading = false; 4367 4368 decorated.call(this, data); 4369 4370 if (this.showLoadingMore(data)) { 4371 this.$results.append(this.$loadingMore); 4372 this.loadMoreIfNeeded(); 4373 } 4374 }; 4375 4376 InfiniteScroll.prototype.bind = function (decorated, container, $container) { 4377 var self = this; 4378 4379 decorated.call(this, container, $container); 4380 4381 container.on('query', function (params) { 4382 self.lastParams = params; 4383 self.loading = true; 4384 }); 4385 4386 container.on('query:append', function (params) { 4387 self.lastParams = params; 4388 self.loading = true; 4389 }); 4390 4391 this.$results.on('scroll', this.loadMoreIfNeeded.bind(this)); 4392 }; 4393 4394 InfiniteScroll.prototype.loadMoreIfNeeded = function () { 4395 var isLoadMoreVisible = $.contains( 4396 document.documentElement, 4397 this.$loadingMore[0] 4398 ); 4399 4400 if (this.loading || !isLoadMoreVisible) { 4401 return; 4402 } 4403 4404 var currentOffset = this.$results.offset().top + 4405 this.$results.outerHeight(false); 4406 var loadingMoreOffset = this.$loadingMore.offset().top + 4407 this.$loadingMore.outerHeight(false); 4408 4409 if (currentOffset + 50 >= loadingMoreOffset) { 4410 this.loadMore(); 4411 } 4412 }; 4413 4414 InfiniteScroll.prototype.loadMore = function () { 4415 this.loading = true; 4416 4417 var params = $.extend({}, {page: 1}, this.lastParams); 4418 4419 params.page++; 4420 4421 this.trigger('query:append', params); 4422 }; 4423 4424 InfiniteScroll.prototype.showLoadingMore = function (_, data) { 4425 return data.pagination && data.pagination.more; 4426 }; 4427 4428 InfiniteScroll.prototype.createLoadingMore = function () { 4429 var $option = $( 4430 '<li ' + 4431 'class="select2-results__option select2-results__option--load-more"' + 4432 'role="option" aria-disabled="true"></li>' 4433 ); 4434 4435 var message = this.options.get('translations').get('loadingMore'); 4436 4437 $option.html(message(this.lastParams)); 4438 4439 return $option; 4440 }; 4441 4442 return InfiniteScroll; 4443}); 4444 4445S2.define('select2/dropdown/attachBody',[ 4446 'jquery', 4447 '../utils' 4448], function ($, Utils) { 4449 function AttachBody (decorated, $element, options) { 4450 this.$dropdownParent = $(options.get('dropdownParent') || document.body); 4451 4452 decorated.call(this, $element, options); 4453 } 4454 4455 AttachBody.prototype.bind = function (decorated, container, $container) { 4456 var self = this; 4457 4458 decorated.call(this, container, $container); 4459 4460 container.on('open', function () { 4461 self._showDropdown(); 4462 self._attachPositioningHandler(container); 4463 4464 // Must bind after the results handlers to ensure correct sizing 4465 self._bindContainerResultHandlers(container); 4466 }); 4467 4468 container.on('close', function () { 4469 self._hideDropdown(); 4470 self._detachPositioningHandler(container); 4471 }); 4472 4473 this.$dropdownContainer.on('mousedown', function (evt) { 4474 evt.stopPropagation(); 4475 }); 4476 }; 4477 4478 AttachBody.prototype.destroy = function (decorated) { 4479 decorated.call(this); 4480 4481 this.$dropdownContainer.remove(); 4482 }; 4483 4484 AttachBody.prototype.position = function (decorated, $dropdown, $container) { 4485 // Clone all of the container classes 4486 $dropdown.attr('class', $container.attr('class')); 4487 4488 $dropdown[0].classList.remove('select2'); 4489 $dropdown[0].classList.add('select2-container--open'); 4490 4491 $dropdown.css({ 4492 position: 'absolute', 4493 top: -999999 4494 }); 4495 4496 this.$container = $container; 4497 }; 4498 4499 AttachBody.prototype.render = function (decorated) { 4500 var $container = $('<span></span>'); 4501 4502 var $dropdown = decorated.call(this); 4503 $container.append($dropdown); 4504 4505 this.$dropdownContainer = $container; 4506 4507 return $container; 4508 }; 4509 4510 AttachBody.prototype._hideDropdown = function (decorated) { 4511 this.$dropdownContainer.detach(); 4512 }; 4513 4514 AttachBody.prototype._bindContainerResultHandlers = 4515 function (decorated, container) { 4516 4517 // These should only be bound once 4518 if (this._containerResultsHandlersBound) { 4519 return; 4520 } 4521 4522 var self = this; 4523 4524 container.on('results:all', function () { 4525 self._positionDropdown(); 4526 self._resizeDropdown(); 4527 }); 4528 4529 container.on('results:append', function () { 4530 self._positionDropdown(); 4531 self._resizeDropdown(); 4532 }); 4533 4534 container.on('results:message', function () { 4535 self._positionDropdown(); 4536 self._resizeDropdown(); 4537 }); 4538 4539 container.on('select', function () { 4540 self._positionDropdown(); 4541 self._resizeDropdown(); 4542 }); 4543 4544 container.on('unselect', function () { 4545 self._positionDropdown(); 4546 self._resizeDropdown(); 4547 }); 4548 4549 this._containerResultsHandlersBound = true; 4550 }; 4551 4552 AttachBody.prototype._attachPositioningHandler = 4553 function (decorated, container) { 4554 var self = this; 4555 4556 var scrollEvent = 'scroll.select2.' + container.id; 4557 var resizeEvent = 'resize.select2.' + container.id; 4558 var orientationEvent = 'orientationchange.select2.' + container.id; 4559 4560 var $watchers = this.$container.parents().filter(Utils.hasScroll); 4561 $watchers.each(function () { 4562 Utils.StoreData(this, 'select2-scroll-position', { 4563 x: $(this).scrollLeft(), 4564 y: $(this).scrollTop() 4565 }); 4566 }); 4567 4568 $watchers.on(scrollEvent, function (ev) { 4569 var position = Utils.GetData(this, 'select2-scroll-position'); 4570 $(this).scrollTop(position.y); 4571 }); 4572 4573 $(window).on(scrollEvent + ' ' + resizeEvent + ' ' + orientationEvent, 4574 function (e) { 4575 self._positionDropdown(); 4576 self._resizeDropdown(); 4577 }); 4578 }; 4579 4580 AttachBody.prototype._detachPositioningHandler = 4581 function (decorated, container) { 4582 var scrollEvent = 'scroll.select2.' + container.id; 4583 var resizeEvent = 'resize.select2.' + container.id; 4584 var orientationEvent = 'orientationchange.select2.' + container.id; 4585 4586 var $watchers = this.$container.parents().filter(Utils.hasScroll); 4587 $watchers.off(scrollEvent); 4588 4589 $(window).off(scrollEvent + ' ' + resizeEvent + ' ' + orientationEvent); 4590 }; 4591 4592 AttachBody.prototype._positionDropdown = function () { 4593 var $window = $(window); 4594 4595 var isCurrentlyAbove = this.$dropdown[0].classList 4596 .contains('select2-dropdown--above'); 4597 var isCurrentlyBelow = this.$dropdown[0].classList 4598 .contains('select2-dropdown--below'); 4599 4600 var newDirection = null; 4601 4602 var offset = this.$container.offset(); 4603 4604 offset.bottom = offset.top + this.$container.outerHeight(false); 4605 4606 var container = { 4607 height: this.$container.outerHeight(false) 4608 }; 4609 4610 container.top = offset.top; 4611 container.bottom = offset.top + container.height; 4612 4613 var dropdown = { 4614 height: this.$dropdown.outerHeight(false) 4615 }; 4616 4617 var viewport = { 4618 top: $window.scrollTop(), 4619 bottom: $window.scrollTop() + $window.height() 4620 }; 4621 4622 var enoughRoomAbove = viewport.top < (offset.top - dropdown.height); 4623 var enoughRoomBelow = viewport.bottom > (offset.bottom + dropdown.height); 4624 4625 var css = { 4626 left: offset.left, 4627 top: container.bottom 4628 }; 4629 4630 // Determine what the parent element is to use for calculating the offset 4631 var $offsetParent = this.$dropdownParent; 4632 4633 // For statically positioned elements, we need to get the element 4634 // that is determining the offset 4635 if ($offsetParent.css('position') === 'static') { 4636 $offsetParent = $offsetParent.offsetParent(); 4637 } 4638 4639 var parentOffset = { 4640 top: 0, 4641 left: 0 4642 }; 4643 4644 if ( 4645 $.contains(document.body, $offsetParent[0]) || 4646 $offsetParent[0].isConnected 4647 ) { 4648 parentOffset = $offsetParent.offset(); 4649 } 4650 4651 css.top -= parentOffset.top; 4652 css.left -= parentOffset.left; 4653 4654 if (!isCurrentlyAbove && !isCurrentlyBelow) { 4655 newDirection = 'below'; 4656 } 4657 4658 if (!enoughRoomBelow && enoughRoomAbove && !isCurrentlyAbove) { 4659 newDirection = 'above'; 4660 } else if (!enoughRoomAbove && enoughRoomBelow && isCurrentlyAbove) { 4661 newDirection = 'below'; 4662 } 4663 4664 if (newDirection == 'above' || 4665 (isCurrentlyAbove && newDirection !== 'below')) { 4666 css.top = container.top - parentOffset.top - dropdown.height; 4667 } 4668 4669 if (newDirection != null) { 4670 this.$dropdown[0].classList.remove('select2-dropdown--below'); 4671 this.$dropdown[0].classList.remove('select2-dropdown--above'); 4672 this.$dropdown[0].classList.add('select2-dropdown--' + newDirection); 4673 4674 this.$container[0].classList.remove('select2-container--below'); 4675 this.$container[0].classList.remove('select2-container--above'); 4676 this.$container[0].classList.add('select2-container--' + newDirection); 4677 } 4678 4679 this.$dropdownContainer.css(css); 4680 }; 4681 4682 AttachBody.prototype._resizeDropdown = function () { 4683 var css = { 4684 width: this.$container.outerWidth(false) + 'px' 4685 }; 4686 4687 if (this.options.get('dropdownAutoWidth')) { 4688 css.minWidth = css.width; 4689 css.position = 'relative'; 4690 css.width = 'auto'; 4691 } 4692 4693 this.$dropdown.css(css); 4694 }; 4695 4696 AttachBody.prototype._showDropdown = function (decorated) { 4697 this.$dropdownContainer.appendTo(this.$dropdownParent); 4698 4699 this._positionDropdown(); 4700 this._resizeDropdown(); 4701 }; 4702 4703 return AttachBody; 4704}); 4705 4706S2.define('select2/dropdown/minimumResultsForSearch',[ 4707 4708], function () { 4709 function countResults (data) { 4710 var count = 0; 4711 4712 for (var d = 0; d < data.length; d++) { 4713 var item = data[d]; 4714 4715 if (item.children) { 4716 count += countResults(item.children); 4717 } else { 4718 count++; 4719 } 4720 } 4721 4722 return count; 4723 } 4724 4725 function MinimumResultsForSearch (decorated, $element, options, dataAdapter) { 4726 this.minimumResultsForSearch = options.get('minimumResultsForSearch'); 4727 4728 if (this.minimumResultsForSearch < 0) { 4729 this.minimumResultsForSearch = Infinity; 4730 } 4731 4732 decorated.call(this, $element, options, dataAdapter); 4733 } 4734 4735 MinimumResultsForSearch.prototype.showSearch = function (decorated, params) { 4736 if (countResults(params.data.results) < this.minimumResultsForSearch) { 4737 return false; 4738 } 4739 4740 return decorated.call(this, params); 4741 }; 4742 4743 return MinimumResultsForSearch; 4744}); 4745 4746S2.define('select2/dropdown/selectOnClose',[ 4747 '../utils' 4748], function (Utils) { 4749 function SelectOnClose () { } 4750 4751 SelectOnClose.prototype.bind = function (decorated, container, $container) { 4752 var self = this; 4753 4754 decorated.call(this, container, $container); 4755 4756 container.on('close', function (params) { 4757 self._handleSelectOnClose(params); 4758 }); 4759 }; 4760 4761 SelectOnClose.prototype._handleSelectOnClose = function (_, params) { 4762 if (params && params.originalSelect2Event != null) { 4763 var event = params.originalSelect2Event; 4764 4765 // Don't select an item if the close event was triggered from a select or 4766 // unselect event 4767 if (event._type === 'select' || event._type === 'unselect') { 4768 return; 4769 } 4770 } 4771 4772 var $highlightedResults = this.getHighlightedResults(); 4773 4774 // Only select highlighted results 4775 if ($highlightedResults.length < 1) { 4776 return; 4777 } 4778 4779 var data = Utils.GetData($highlightedResults[0], 'data'); 4780 4781 // Don't re-select already selected resulte 4782 if ( 4783 (data.element != null && data.element.selected) || 4784 (data.element == null && data.selected) 4785 ) { 4786 return; 4787 } 4788 4789 this.trigger('select', { 4790 data: data 4791 }); 4792 }; 4793 4794 return SelectOnClose; 4795}); 4796 4797S2.define('select2/dropdown/closeOnSelect',[ 4798 4799], function () { 4800 function CloseOnSelect () { } 4801 4802 CloseOnSelect.prototype.bind = function (decorated, container, $container) { 4803 var self = this; 4804 4805 decorated.call(this, container, $container); 4806 4807 container.on('select', function (evt) { 4808 self._selectTriggered(evt); 4809 }); 4810 4811 container.on('unselect', function (evt) { 4812 self._selectTriggered(evt); 4813 }); 4814 }; 4815 4816 CloseOnSelect.prototype._selectTriggered = function (_, evt) { 4817 var originalEvent = evt.originalEvent; 4818 4819 // Don't close if the control key is being held 4820 if (originalEvent && (originalEvent.ctrlKey || originalEvent.metaKey)) { 4821 return; 4822 } 4823 4824 this.trigger('close', { 4825 originalEvent: originalEvent, 4826 originalSelect2Event: evt 4827 }); 4828 }; 4829 4830 return CloseOnSelect; 4831}); 4832 4833S2.define('select2/dropdown/dropdownCss',[ 4834 '../utils' 4835], function (Utils) { 4836 function DropdownCSS () { } 4837 4838 DropdownCSS.prototype.render = function (decorated) { 4839 var $dropdown = decorated.call(this); 4840 4841 var dropdownCssClass = this.options.get('dropdownCssClass') || ''; 4842 4843 if (dropdownCssClass.indexOf(':all:') !== -1) { 4844 dropdownCssClass = dropdownCssClass.replace(':all:', ''); 4845 4846 Utils.copyNonInternalCssClasses($dropdown[0], this.$element[0]); 4847 } 4848 4849 $dropdown.addClass(dropdownCssClass); 4850 4851 return $dropdown; 4852 }; 4853 4854 return DropdownCSS; 4855}); 4856 4857S2.define('select2/dropdown/tagsSearchHighlight',[ 4858 '../utils' 4859], function (Utils) { 4860 function TagsSearchHighlight () { } 4861 4862 TagsSearchHighlight.prototype.highlightFirstItem = function (decorated) { 4863 var $options = this.$results 4864 .find( 4865 '.select2-results__option--selectable' + 4866 ':not(.select2-results__option--selected)' 4867 ); 4868 4869 if ($options.length > 0) { 4870 var $firstOption = $options.first(); 4871 var data = Utils.GetData($firstOption[0], 'data'); 4872 var firstElement = data.element; 4873 4874 if (firstElement && firstElement.getAttribute) { 4875 if (firstElement.getAttribute('data-select2-tag') === 'true') { 4876 $firstOption.trigger('mouseenter'); 4877 4878 return; 4879 } 4880 } 4881 } 4882 4883 decorated.call(this); 4884 }; 4885 4886 return TagsSearchHighlight; 4887}); 4888 4889S2.define('select2/i18n/en',[],function () { 4890 // English 4891 return { 4892 errorLoading: function () { 4893 return 'The results could not be loaded.'; 4894 }, 4895 inputTooLong: function (args) { 4896 var overChars = args.input.length - args.maximum; 4897 4898 var message = 'Please delete ' + overChars + ' character'; 4899 4900 if (overChars != 1) { 4901 message += 's'; 4902 } 4903 4904 return message; 4905 }, 4906 inputTooShort: function (args) { 4907 var remainingChars = args.minimum - args.input.length; 4908 4909 var message = 'Please enter ' + remainingChars + ' or more characters'; 4910 4911 return message; 4912 }, 4913 loadingMore: function () { 4914 return 'Loading more results…';
4915 }, 4916 maximumSelected: function (args) { 4917 var message = 'You can only select ' + args.maximum + ' item'; 4918 4919 if (args.maximum != 1) { 4920 message += 's'; 4921 } 4922 4923 return message; 4924 }, 4925 noResults: function () { 4926 return 'No results found'; 4927 }, 4928 searching: function () { 4929 return 'Searchingâ€Â
vendor: 8,933 bytes, lines 4929-5271
4929¦'; 4930 }, 4931 removeAllItems: function () { 4932 return 'Remove all items'; 4933 }, 4934 removeItem: function () { 4935 return 'Remove item'; 4936 }, 4937 search: function() { 4938 return 'Search'; 4939 } 4940 }; 4941}); 4942 4943S2.define('select2/defaults',[ 4944 'jquery', 4945 4946 './results', 4947 4948 './selection/single', 4949 './selection/multiple', 4950 './selection/placeholder', 4951 './selection/allowClear', 4952 './selection/search', 4953 './selection/selectionCss', 4954 './selection/eventRelay', 4955 4956 './utils', 4957 './translation', 4958 './diacritics', 4959 4960 './data/select', 4961 './data/array', 4962 './data/ajax', 4963 './data/tags', 4964 './data/tokenizer', 4965 './data/minimumInputLength', 4966 './data/maximumInputLength', 4967 './data/maximumSelectionLength', 4968 4969 './dropdown', 4970 './dropdown/search', 4971 './dropdown/hidePlaceholder', 4972 './dropdown/infiniteScroll', 4973 './dropdown/attachBody', 4974 './dropdown/minimumResultsForSearch', 4975 './dropdown/selectOnClose', 4976 './dropdown/closeOnSelect', 4977 './dropdown/dropdownCss', 4978 './dropdown/tagsSearchHighlight', 4979 4980 './i18n/en' 4981], function ($, 4982 4983 ResultsList, 4984 4985 SingleSelection, MultipleSelection, Placeholder, AllowClear, 4986 SelectionSearch, SelectionCSS, EventRelay, 4987 4988 Utils, Translation, DIACRITICS, 4989 4990 SelectData, ArrayData, AjaxData, Tags, Tokenizer, 4991 MinimumInputLength, MaximumInputLength, MaximumSelectionLength, 4992 4993 Dropdown, DropdownSearch, HidePlaceholder, InfiniteScroll, 4994 AttachBody, MinimumResultsForSearch, SelectOnClose, CloseOnSelect, 4995 DropdownCSS, TagsSearchHighlight, 4996 4997 EnglishTranslation) { 4998 function Defaults () { 4999 this.reset(); 5000 } 5001 5002 Defaults.prototype.apply = function (options) { 5003 options = $.extend(true, {}, this.defaults, options); 5004 5005 if (options.dataAdapter == null) { 5006 if (options.ajax != null) { 5007 options.dataAdapter = AjaxData; 5008 } else if (options.data != null) { 5009 options.dataAdapter = ArrayData; 5010 } else { 5011 options.dataAdapter = SelectData; 5012 } 5013 5014 if (options.minimumInputLength > 0) { 5015 options.dataAdapter = Utils.Decorate( 5016 options.dataAdapter, 5017 MinimumInputLength 5018 ); 5019 } 5020 5021 if (options.maximumInputLength > 0) { 5022 options.dataAdapter = Utils.Decorate( 5023 options.dataAdapter, 5024 MaximumInputLength 5025 ); 5026 } 5027 5028 if (options.maximumSelectionLength > 0) { 5029 options.dataAdapter = Utils.Decorate( 5030 options.dataAdapter, 5031 MaximumSelectionLength 5032 ); 5033 } 5034 5035 if (options.tags) { 5036 options.dataAdapter = Utils.Decorate(options.dataAdapter, Tags); 5037 } 5038 5039 if (options.tokenSeparators != null || options.tokenizer != null) { 5040 options.dataAdapter = Utils.Decorate( 5041 options.dataAdapter, 5042 Tokenizer 5043 ); 5044 } 5045 } 5046 5047 if (options.resultsAdapter == null) { 5048 options.resultsAdapter = ResultsList; 5049 5050 if (options.ajax != null) { 5051 options.resultsAdapter = Utils.Decorate( 5052 options.resultsAdapter, 5053 InfiniteScroll 5054 ); 5055 } 5056 5057 if (options.placeholder != null) { 5058 options.resultsAdapter = Utils.Decorate( 5059 options.resultsAdapter, 5060 HidePlaceholder 5061 ); 5062 } 5063 5064 if (options.selectOnClose) { 5065 options.resultsAdapter = Utils.Decorate( 5066 options.resultsAdapter, 5067 SelectOnClose 5068 ); 5069 } 5070 5071 if (options.tags) { 5072 options.resultsAdapter = Utils.Decorate( 5073 options.resultsAdapter, 5074 TagsSearchHighlight 5075 ); 5076 } 5077 } 5078 5079 if (options.dropdownAdapter == null) { 5080 if (options.multiple) { 5081 options.dropdownAdapter = Dropdown; 5082 } else { 5083 var SearchableDropdown = Utils.Decorate(Dropdown, DropdownSearch); 5084 5085 options.dropdownAdapter = SearchableDropdown; 5086 } 5087 5088 if (options.minimumResultsForSearch !== 0) { 5089 options.dropdownAdapter = Utils.Decorate( 5090 options.dropdownAdapter, 5091 MinimumResultsForSearch 5092 ); 5093 } 5094 5095 if (options.closeOnSelect) { 5096 options.dropdownAdapter = Utils.Decorate( 5097 options.dropdownAdapter, 5098 CloseOnSelect 5099 ); 5100 } 5101 5102 if (options.dropdownCssClass != null) { 5103 options.dropdownAdapter = Utils.Decorate( 5104 options.dropdownAdapter, 5105 DropdownCSS 5106 ); 5107 } 5108 5109 options.dropdownAdapter = Utils.Decorate( 5110 options.dropdownAdapter, 5111 AttachBody 5112 ); 5113 } 5114 5115 if (options.selectionAdapter == null) { 5116 if (options.multiple) { 5117 options.selectionAdapter = MultipleSelection; 5118 } else { 5119 options.selectionAdapter = SingleSelection; 5120 } 5121 5122 // Add the placeholder mixin if a placeholder was specified 5123 if (options.placeholder != null) { 5124 options.selectionAdapter = Utils.Decorate( 5125 options.selectionAdapter, 5126 Placeholder 5127 ); 5128 } 5129 5130 if (options.allowClear) { 5131 options.selectionAdapter = Utils.Decorate( 5132 options.selectionAdapter, 5133 AllowClear 5134 ); 5135 } 5136 5137 if (options.multiple) { 5138 options.selectionAdapter = Utils.Decorate( 5139 options.selectionAdapter, 5140 SelectionSearch 5141 ); 5142 } 5143 5144 if (options.selectionCssClass != null) { 5145 options.selectionAdapter = Utils.Decorate( 5146 options.selectionAdapter, 5147 SelectionCSS 5148 ); 5149 } 5150 5151 options.selectionAdapter = Utils.Decorate( 5152 options.selectionAdapter, 5153 EventRelay 5154 ); 5155 } 5156 5157 // If the defaults were not previously applied from an element, it is 5158 // possible for the language option to have not been resolved 5159 options.language = this._resolveLanguage(options.language); 5160 5161 // Always fall back to English since it will always be complete 5162 options.language.push('en'); 5163 5164 var uniqueLanguages = []; 5165 5166 for (var l = 0; l < options.language.length; l++) { 5167 var language = options.language[l]; 5168 5169 if (uniqueLanguages.indexOf(language) === -1) { 5170 uniqueLanguages.push(language); 5171 } 5172 } 5173 5174 options.language = uniqueLanguages; 5175 5176 options.translations = this._processTranslations( 5177 options.language, 5178 options.debug 5179 ); 5180 5181 return options; 5182 }; 5183 5184 Defaults.prototype.reset = function () { 5185 function stripDiacritics (text) { 5186 // Used 'uni range + named function' from http://jsperf.com/diacritics/18 5187 function match(a) { 5188 return DIACRITICS[a] || a; 5189 } 5190 5191 return text.replace(/[^\u0000-\u007E]/g, match); 5192 } 5193 5194 function matcher (params, data) { 5195 // Always return the object if there is nothing to compare 5196 if (params.term == null || params.term.trim() === '') { 5197 return data; 5198 } 5199 5200 // Do a recursive check for options with children 5201 if (data.children && data.children.length > 0) { 5202 // Clone the data object if there are children 5203 // This is required as we modify the object to remove any non-matches 5204 var match = $.extend(true, {}, data); 5205 5206 // Check each child of the option 5207 for (var c = data.children.length - 1; c >= 0; c--) { 5208 var child = data.children[c]; 5209 5210 var matches = matcher(params, child); 5211 5212 // If there wasn't a match, remove the object in the array 5213 if (matches == null) { 5214 match.children.splice(c, 1); 5215 } 5216 } 5217 5218 // If any children matched, return the new object 5219 if (match.children.length > 0) { 5220 return match; 5221 } 5222 5223 // If there were no matching children, check just the plain object 5224 return matcher(params, match); 5225 } 5226 5227 var original = stripDiacritics(data.text).toUpperCase(); 5228 var term = stripDiacritics(params.term).toUpperCase(); 5229 5230 // Check if the text contains the term 5231 if (original.indexOf(term) > -1) { 5232 return data; 5233 } 5234 5235 // If it doesn't contain the term, don't return anything 5236 return null; 5237 } 5238 5239 this.defaults = { 5240 amdLanguageBase: './i18n/', 5241 autocomplete: 'off', 5242 closeOnSelect: true, 5243 debug: false, 5244 dropdownAutoWidth: false, 5245 escapeMarkup: Utils.escapeMarkup, 5246 language: {}, 5247 matcher: matcher, 5248 minimumInputLength: 0, 5249 maximumInputLength: 0, 5250 maximumSelectionLength: 0, 5251 minimumResultsForSearch: 0, 5252 selectOnClose: false, 5253 scrollAfterSelect: false, 5254 sorter: function (data) { 5255 return data; 5256 }, 5257 templateResult: function (result) { 5258 return result.text; 5259 }, 5260 templateSelection: function (selection) { 5261 return selection.text; 5262 }, 5263 theme: 'default', 5264 width: 'resolve' 5265 }; 5266 }; 5267 5268 Defaults.prototype.applyFromElement = function (options, $element) { 5269 var optionLanguage = options.language; 5270 var defaultLanguage = this.defaults.language; 5271 var elementLanguage = $element
vendor: 19,057 bytes, lines 5271-6002
5271.prop('lang'); 5272 var parentLanguage = $element.closest('[lang]').prop('lang'); 5273 5274 var languages = Array.prototype.concat.call( 5275 this._resolveLanguage(elementLanguage), 5276 this._resolveLanguage(optionLanguage), 5277 this._resolveLanguage(defaultLanguage), 5278 this._resolveLanguage(parentLanguage) 5279 ); 5280 5281 options.language = languages; 5282 5283 return options; 5284 }; 5285 5286 Defaults.prototype._resolveLanguage = function (language) { 5287 if (!language) { 5288 return []; 5289 } 5290 5291 if ($.isEmptyObject(language)) { 5292 return []; 5293 } 5294 5295 if ($.isPlainObject(language)) { 5296 return [language]; 5297 } 5298 5299 var languages; 5300 5301 if (!Array.isArray(language)) { 5302 languages = [language]; 5303 } else { 5304 languages = language; 5305 } 5306 5307 var resolvedLanguages = []; 5308 5309 for (var l = 0; l < languages.length; l++) { 5310 resolvedLanguages.push(languages[l]); 5311 5312 if (typeof languages[l] === 'string' && languages[l].indexOf('-') > 0) { 5313 // Extract the region information if it is included 5314 var languageParts = languages[l].split('-'); 5315 var baseLanguage = languageParts[0]; 5316 5317 resolvedLanguages.push(baseLanguage); 5318 } 5319 } 5320 5321 return resolvedLanguages; 5322 }; 5323 5324 Defaults.prototype._processTranslations = function (languages, debug) { 5325 var translations = new Translation(); 5326 5327 for (var l = 0; l < languages.length; l++) { 5328 var languageData = new Translation(); 5329 5330 var language = languages[l]; 5331 5332 if (typeof language === 'string') { 5333 try { 5334 // Try to load it with the original name 5335 languageData = Translation.loadPath(language); 5336 } catch (e) { 5337 try { 5338 // If we couldn't load it, check if it wasn't the full path 5339 language = this.defaults.amdLanguageBase + language; 5340 languageData = Translation.loadPath(language); 5341 } catch (ex) { 5342 // The translation could not be loaded at all. Sometimes this is 5343 // because of a configuration problem, other times this can be 5344 // because of how Select2 helps load all possible translation files 5345 if (debug && window.console && console.warn) { 5346 console.warn( 5347 'Select2: The language file for "' + language + '" could ' + 5348 'not be automatically loaded. A fallback will be used instead.' 5349 ); 5350 } 5351 } 5352 } 5353 } else if ($.isPlainObject(language)) { 5354 languageData = new Translation(language); 5355 } else { 5356 languageData = language; 5357 } 5358 5359 translations.extend(languageData); 5360 } 5361 5362 return translations; 5363 }; 5364 5365 Defaults.prototype.set = function (key, value) { 5366 var camelKey = $.camelCase(key); 5367 5368 var data = {}; 5369 data[camelKey] = value; 5370 5371 var convertedData = Utils._convertData(data); 5372 5373 $.extend(true, this.defaults, convertedData); 5374 }; 5375 5376 var defaults = new Defaults(); 5377 5378 return defaults; 5379}); 5380 5381S2.define('select2/options',[ 5382 'jquery', 5383 './defaults', 5384 './utils' 5385], function ($, Defaults, Utils) { 5386 function Options (options, $element) { 5387 this.options = options; 5388 5389 if ($element != null) { 5390 this.fromElement($element); 5391 } 5392 5393 if ($element != null) { 5394 this.options = Defaults.applyFromElement(this.options, $element); 5395 } 5396 5397 this.options = Defaults.apply(this.options); 5398 } 5399 5400 Options.prototype.fromElement = function ($e) { 5401 var excludedData = ['select2']; 5402 5403 if (this.options.multiple == null) { 5404 this.options.multiple = $e.prop('multiple'); 5405 } 5406 5407 if (this.options.disabled == null) { 5408 this.options.disabled = $e.prop('disabled'); 5409 } 5410 5411 if (this.options.autocomplete == null && $e.prop('autocomplete')) { 5412 this.options.autocomplete = $e.prop('autocomplete'); 5413 } 5414 5415 if (this.options.dir == null) { 5416 if ($e.prop('dir')) { 5417 this.options.dir = $e.prop('dir'); 5418 } else if ($e.closest('[dir]').prop('dir')) { 5419 this.options.dir = $e.closest('[dir]').prop('dir'); 5420 } else { 5421 this.options.dir = 'ltr'; 5422 } 5423 } 5424 5425 $e.prop('disabled', this.options.disabled); 5426 $e.prop('multiple', this.options.multiple); 5427 5428 if (Utils.GetData($e[0], 'select2Tags')) { 5429 if (this.options.debug && window.console && console.warn) { 5430 console.warn( 5431 'Select2: The `data-select2-tags` attribute has been changed to ' + 5432 'use the `data-data` and `data-tags="true"` attributes and will be ' + 5433 'removed in future versions of Select2.' 5434 ); 5435 } 5436 5437 Utils.StoreData($e[0], 'data', Utils.GetData($e[0], 'select2Tags')); 5438 Utils.StoreData($e[0], 'tags', true); 5439 } 5440 5441 if (Utils.GetData($e[0], 'ajaxUrl')) { 5442 if (this.options.debug && window.console && console.warn) { 5443 console.warn( 5444 'Select2: The `data-ajax-url` attribute has been changed to ' + 5445 '`data-ajax--url` and support for the old attribute will be removed' + 5446 ' in future versions of Select2.' 5447 ); 5448 } 5449 5450 $e.attr('ajax--url', Utils.GetData($e[0], 'ajaxUrl')); 5451 Utils.StoreData($e[0], 'ajax-Url', Utils.GetData($e[0], 'ajaxUrl')); 5452 } 5453 5454 var dataset = {}; 5455 5456 function upperCaseLetter(_, letter) { 5457 return letter.toUpperCase(); 5458 } 5459 5460 // Pre-load all of the attributes which are prefixed with `data-` 5461 for (var attr = 0; attr < $e[0].attributes.length; attr++) { 5462 var attributeName = $e[0].attributes[attr].name; 5463 var prefix = 'data-'; 5464 5465 if (attributeName.substr(0, prefix.length) == prefix) { 5466 // Get the contents of the attribute after `data-` 5467 var dataName = attributeName.substring(prefix.length); 5468 5469 // Get the data contents from the consistent source 5470 // This is more than likely the jQuery data helper 5471 var dataValue = Utils.GetData($e[0], dataName); 5472 5473 // camelCase the attribute name to match the spec 5474 var camelDataName = dataName.replace(/-([a-z])/g, upperCaseLetter); 5475 5476 // Store the data attribute contents into the dataset since 5477 dataset[camelDataName] = dataValue; 5478 } 5479 } 5480 5481 // Prefer the element's `dataset` attribute if it exists 5482 // jQuery 1.x does not correctly handle data attributes with multiple dashes 5483 if ($.fn.jquery && $.fn.jquery.substr(0, 2) == '1.' && $e[0].dataset) { 5484 dataset = $.extend(true, {}, $e[0].dataset, dataset); 5485 } 5486 5487 // Prefer our internal data cache if it exists 5488 var data = $.extend(true, {}, Utils.GetData($e[0]), dataset); 5489 5490 data = Utils._convertData(data); 5491 5492 for (var key in data) { 5493 if (excludedData.indexOf(key) > -1) { 5494 continue; 5495 } 5496 5497 if ($.isPlainObject(this.options[key])) { 5498 $.extend(this.options[key], data[key]); 5499 } else { 5500 this.options[key] = data[key]; 5501 } 5502 } 5503 5504 return this; 5505 }; 5506 5507 Options.prototype.get = function (key) { 5508 return this.options[key]; 5509 }; 5510 5511 Options.prototype.set = function (key, val) { 5512 this.options[key] = val; 5513 }; 5514 5515 return Options; 5516}); 5517 5518S2.define('select2/core',[ 5519 'jquery', 5520 './options', 5521 './utils', 5522 './keys' 5523], function ($, Options, Utils, KEYS) { 5524 var Select2 = function ($element, options) { 5525 if (Utils.GetData($element[0], 'select2') != null) { 5526 Utils.GetData($element[0], 'select2').destroy(); 5527 } 5528 5529 this.$element = $element; 5530 5531 this.id = this._generateId($element); 5532 5533 options = options || {}; 5534 5535 this.options = new Options(options, $element); 5536 5537 Select2.__super__.constructor.call(this); 5538 5539 // Set up the tabindex 5540 5541 var tabindex = $element.attr('tabindex') || 0; 5542 Utils.StoreData($element[0], 'old-tabindex', tabindex); 5543 $element.attr('tabindex', '-1'); 5544 5545 // Set up containers and adapters 5546 5547 var DataAdapter = this.options.get('dataAdapter'); 5548 this.dataAdapter = new DataAdapter($element, this.options); 5549 5550 var $container = this.render(); 5551 5552 this._placeContainer($container); 5553 5554 var SelectionAdapter = this.options.get('selectionAdapter'); 5555 this.selection = new SelectionAdapter($element, this.options); 5556 this.$selection = this.selection.render(); 5557 5558 this.selection.position(this.$selection, $container); 5559 5560 var DropdownAdapter = this.options.get('dropdownAdapter'); 5561 this.dropdown = new DropdownAdapter($element, this.options); 5562 this.$dropdown = this.dropdown.render(); 5563 5564 this.dropdown.position(this.$dropdown, $container); 5565 5566 var ResultsAdapter = this.options.get('resultsAdapter'); 5567 this.results = new ResultsAdapter($element, this.options, this.dataAdapter); 5568 this.$results = this.results.render(); 5569 5570 this.results.position(this.$results, this.$dropdown); 5571 5572 // Bind events 5573 5574 var self = this; 5575 5576 // Bind the container to all of the adapters 5577 this._bindAdapters(); 5578 5579 // Register any DOM event handlers 5580 this._registerDomEvents(); 5581 5582 // Register any internal event handlers 5583 this._registerDataEvents(); 5584 this._registerSelectionEvents(); 5585 this._registerDropdownEvents(); 5586 this._registerResultsEvents(); 5587 this._registerEvents(); 5588 5589 // Set the initial state 5590 this.dataAdapter.current(function (initialData) { 5591 self.trigger('selection:update', { 5592 data: initialData 5593 }); 5594 }); 5595 5596 // Hide the original select 5597 $element[0].classList.add('select2-hidden-accessible'); 5598 $element.attr('aria-hidden', 'true'); 5599 5600 // Synchronize any monitored attributes 5601 this._syncAttributes(); 5602 5603 Utils.StoreData($element[0], 'select2', this); 5604 5605 // Ensure backwards compatibility with $element.data('select2'). 5606 $element.data('select2', this); 5607 }; 5608 5609 Utils.Extend(Select2, Utils.Observable); 5610 5611 Select2.prototype._generateId = function ($element) { 5612 var id = ''; 5613 5614 if ($element.attr('id') != null) { 5615 id = $element.attr('id'); 5616 } else if ($element.attr('name') != null) { 5617 id = $element.attr('name') + '-' + Utils.generateChars(2); 5618 } else { 5619 id = Utils.generateChars(4); 5620 } 5621 5622 id = id.replace(/(:|\.|\[|\]|,)/g, ''); 5623 id = 'select2-' + id; 5624 5625 return id; 5626 }; 5627 5628 Select2.prototype._placeContainer = function ($container) { 5629 $container.insertAfter(this.$element); 5630 5631 var width = this._resolveWidth(this.$element, this.options.get('width')); 5632 5633 if (width != null) { 5634 $container.css('width', width); 5635 } 5636 }; 5637 5638 Select2.prototype._resolveWidth = function ($element, method) { 5639 var WIDTH = /^width:(([-+]?([0-9]*\.)?[0-9]+)(px|em|ex|%|in|cm|mm|pt|pc))/i; 5640 5641 if (method == 'resolve') { 5642 var styleWidth = this._resolveWidth($element, 'style'); 5643 5644 if (styleWidth != null) { 5645 return styleWidth; 5646 } 5647 5648 return this._resolveWidth($element, 'element'); 5649 } 5650 5651 if (method == 'element') { 5652 var elementWidth = $element.outerWidth(false); 5653 5654 if (elementWidth <= 0) { 5655 return 'auto'; 5656 } 5657 5658 return elementWidth + 'px'; 5659 } 5660 5661 if (method == 'style') { 5662 var style = $element.attr('style'); 5663 5664 if (typeof(style) !== 'string') { 5665 return null; 5666 } 5667 5668 var attrs = style.split(';'); 5669 5670 for (var i = 0, l = attrs.length; i < l; i = i + 1) { 5671 var attr = attrs[i].replace(/\s/g, ''); 5672 var matches = attr.match(WIDTH); 5673 5674 if (matches !== null && matches.length >= 1) { 5675 return matches[1]; 5676 } 5677 } 5678 5679 return null; 5680 } 5681 5682 if (method == 'computedstyle') { 5683 var computedStyle = window.getComputedStyle($element[0]); 5684 5685 return computedStyle.width; 5686 } 5687 5688 return method; 5689 }; 5690 5691 Select2.prototype._bindAdapters = function () { 5692 this.dataAdapter.bind(this, this.$container); 5693 this.selection.bind(this, this.$container); 5694 5695 this.dropdown.bind(this, this.$container); 5696 this.results.bind(this, this.$container); 5697 }; 5698 5699 Select2.prototype._registerDomEvents = function () { 5700 var self = this; 5701 5702 this.$element.on('change.select2', function () { 5703 self.dataAdapter.current(function (data) { 5704 self.trigger('selection:update', { 5705 data: data 5706 }); 5707 }); 5708 }); 5709 5710 this.$element.on('focus.select2', function (evt) { 5711 self.trigger('focus', evt); 5712 }); 5713 5714 this._syncA = Utils.bind(this._syncAttributes, this); 5715 this._syncS = Utils.bind(this._syncSubtree, this); 5716 5717 this._observer = new window.MutationObserver(function (mutations) { 5718 self._syncA(); 5719 self._syncS(mutations); 5720 }); 5721 this._observer.observe(this.$element[0], { 5722 attributes: true, 5723 childList: true, 5724 subtree: false 5725 }); 5726 }; 5727 5728 Select2.prototype._registerDataEvents = function () { 5729 var self = this; 5730 5731 this.dataAdapter.on('*', function (name, params) { 5732 self.trigger(name, params); 5733 }); 5734 }; 5735 5736 Select2.prototype._registerSelectionEvents = function () { 5737 var self = this; 5738 var nonRelayEvents = ['toggle', 'focus']; 5739 5740 this.selection.on('toggle', function () { 5741 self.toggleDropdown(); 5742 }); 5743 5744 this.selection.on('focus', function (params) { 5745 self.focus(params); 5746 }); 5747 5748 this.selection.on('*', function (name, params) { 5749 if (nonRelayEvents.indexOf(name) !== -1) { 5750 return; 5751 } 5752 5753 self.trigger(name, params); 5754 }); 5755 }; 5756 5757 Select2.prototype._registerDropdownEvents = function () { 5758 var self = this; 5759 5760 this.dropdown.on('*', function (name, params) { 5761 self.trigger(name, params); 5762 }); 5763 }; 5764 5765 Select2.prototype._registerResultsEvents = function () { 5766 var self = this; 5767 5768 this.results.on('*', function (name, params) { 5769 self.trigger(name, params); 5770 }); 5771 }; 5772 5773 Select2.prototype._registerEvents = function () { 5774 var self = this; 5775 5776 this.on('open', function () { 5777 self.$container[0].classList.add('select2-container--open'); 5778 }); 5779 5780 this.on('close', function () { 5781 self.$container[0].classList.remove('select2-container--open'); 5782 }); 5783 5784 this.on('enable', function () { 5785 self.$container[0].classList.remove('select2-container--disabled'); 5786 }); 5787 5788 this.on('disable', function () { 5789 self.$container[0].classList.add('select2-container--disabled'); 5790 }); 5791 5792 this.on('blur', function () { 5793 self.$container[0].classList.remove('select2-container--focus'); 5794 }); 5795 5796 this.on('query', function (params) { 5797 if (!self.isOpen()) { 5798 self.trigger('open', {}); 5799 } 5800 5801 this.dataAdapter.query(params, function (data) { 5802 self.trigger('results:all', { 5803 data: data, 5804 query: params 5805 }); 5806 }); 5807 }); 5808 5809 this.on('query:append', function (params) { 5810 this.dataAdapter.query(params, function (data) { 5811 self.trigger('results:append', { 5812 data: data, 5813 query: params 5814 }); 5815 }); 5816 }); 5817 5818 this.on('keypress', function (evt) { 5819 var key = evt.which; 5820 5821 if (self.isOpen()) { 5822 if (key === KEYS.ESC || (key === KEYS.UP && evt.altKey)) { 5823 self.close(evt); 5824 5825 evt.preventDefault(); 5826 } else if (key === KEYS.ENTER || key === KEYS.TAB) { 5827 self.trigger('results:select', {}); 5828 5829 evt.preventDefault(); 5830 } else if ((key === KEYS.SPACE && evt.ctrlKey)) { 5831 self.trigger('results:toggle', {}); 5832 5833 evt.preventDefault(); 5834 } else if (key === KEYS.UP) { 5835 self.trigger('results:previous', {}); 5836 5837 evt.preventDefault(); 5838 } else if (key === KEYS.DOWN) { 5839 self.trigger('results:next', {}); 5840 5841 evt.preventDefault(); 5842 } 5843 } else { 5844 if (key === KEYS.ENTER || key === KEYS.SPACE || 5845 (key === KEYS.DOWN && evt.altKey)) { 5846 self.open(); 5847 5848 evt.preventDefault(); 5849 } 5850 } 5851 }); 5852 }; 5853 5854 Select2.prototype._syncAttributes = function () { 5855 this.options.set('disabled', this.$element.prop('disabled')); 5856 5857 if (this.isDisabled()) { 5858 if (this.isOpen()) { 5859 this.close(); 5860 } 5861 5862 this.trigger('disable', {}); 5863 } else { 5864 this.trigger('enable', {}); 5865 } 5866 }; 5867 5868 Select2.prototype._isChangeMutation = function (mutations) { 5869 var self = this; 5870 5871 if (mutations.addedNodes && mutations.addedNodes.length > 0) { 5872 for (var n = 0; n < mutations.addedNodes.length; n++) { 5873 var node = mutations.addedNodes[n]; 5874 5875 if (node.selected) { 5876 return true; 5877 } 5878 } 5879 } else if (mutations.removedNodes && mutations.removedNodes.length > 0) { 5880 return true; 5881 } else if (Array.isArray(mutations)) { 5882 return mutations.some(function (mutation) { 5883 return self._isChangeMutation(mutation); 5884 }); 5885 } 5886 5887 return false; 5888 }; 5889 5890 Select2.prototype._syncSubtree = function (mutations) { 5891 var changed = this._isChangeMutation(mutations); 5892 var self = this; 5893 5894 // Only re-pull the data if we think there is a change 5895 if (changed) { 5896 this.dataAdapter.current(function (currentData) { 5897 self.trigger('selection:update', { 5898 data: currentData 5899 }); 5900 }); 5901 } 5902 }; 5903 5904 /** 5905 * Override the trigger method to automatically trigger pre-events when 5906 * there are events that can be prevented. 5907 */ 5908 Select2.prototype.trigger = function (name, args) { 5909 var actualTrigger = Select2.__super__.trigger; 5910 var preTriggerMap = { 5911 'open': 'opening', 5912 'close': 'closing', 5913 'select': 'selecting', 5914 'unselect': 'unselecting', 5915 'clear': 'clearing' 5916 }; 5917 5918 if (args === undefined) { 5919 args = {}; 5920 } 5921 5922 if (name in preTriggerMap) { 5923 var preTriggerName = preTriggerMap[name]; 5924 var preTriggerArgs = { 5925 prevented: false, 5926 name: name, 5927 args: args 5928 }; 5929 5930 actualTrigger.call(this, preTriggerName, preTriggerArgs); 5931 5932 if (preTriggerArgs.prevented) { 5933 args.prevented = true; 5934 5935 return; 5936 } 5937 } 5938 5939 actualTrigger.call(this, name, args); 5940 }; 5941 5942 Select2.prototype.toggleDropdown = function () { 5943 if (this.isDisabled()) { 5944 return; 5945 } 5946 5947 if (this.isOpen()) { 5948 this.close(); 5949 } else { 5950 this.open(); 5951 } 5952 }; 5953 5954 Select2.prototype.open = function () { 5955 if (this.isOpen()) { 5956 return; 5957 } 5958 5959 if (this.isDisabled()) { 5960 return; 5961 } 5962 5963 this.trigger('query', {}); 5964 }; 5965 5966 Select2.prototype.close = function (evt) { 5967 if (!this.isOpen()) { 5968 return; 5969 } 5970 5971 this.trigger('close', { originalEvent : evt }); 5972 }; 5973 5974 /** 5975 * Helper method to abstract the "enabled" (not "disabled") state of this 5976 * object. 5977 * 5978 * @return {true} if the instance is not disabled. 5979 * @return {false} if the instance is disabled. 5980 */ 5981 Select2.prototype.isEnabled = function () { 5982 return !this.isDisabled(); 5983 }; 5984 5985 /** 5986 * Helper method to abstract the "disabled" state of this object. 5987 * 5988 * @return {true} if the disabled option is true. 5989 * @return {false} if the disabled option is false. 5990 */ 5991 Select2.prototype.isDisabled = function () { 5992 return this.options.get('disabled'); 5993 }; 5994 5995 Select2.prototype.isOpen = function () { 5996 return this.$container[0].classList.contains('select2-container--open'); 5997 }; 5998 5999 Select2.prototype.hasFocus = function () { 6000 return this.$container[0].classList.contains('select2-container--focus'); 6001 }; 6002
vendor: 5,477 bytes, lines 6003-6211
6003 Select2.prototype.focus = function (data) { 6004 // No need to re-trigger focus events if we are already focused 6005 if (this.hasFocus()) { 6006 return; 6007 } 6008 6009 this.$container[0].classList.add('select2-container--focus'); 6010 this.trigger('focus', {}); 6011 }; 6012 6013 Select2.prototype.enable = function (args) { 6014 if (this.options.get('debug') && window.console && console.warn) { 6015 console.warn( 6016 'Select2: The `select2("enable")` method has been deprecated and will' + 6017 ' be removed in later Select2 versions. Use $element.prop("disabled")' + 6018 ' instead.' 6019 ); 6020 } 6021 6022 if (args == null || args.length === 0) { 6023 args = [true]; 6024 } 6025 6026 var disabled = !args[0]; 6027 6028 this.$element.prop('disabled', disabled); 6029 }; 6030 6031 Select2.prototype.data = function () { 6032 if (this.options.get('debug') && 6033 arguments.length > 0 && window.console && console.warn) { 6034 console.warn( 6035 'Select2: Data can no longer be set using `select2("data")`. You ' + 6036 'should consider setting the value instead using `$element.val()`.' 6037 ); 6038 } 6039 6040 var data = []; 6041 6042 this.dataAdapter.current(function (currentData) { 6043 data = currentData; 6044 }); 6045 6046 return data; 6047 }; 6048 6049 Select2.prototype.val = function (args) { 6050 if (this.options.get('debug') && window.console && console.warn) { 6051 console.warn( 6052 'Select2: The `select2("val")` method has been deprecated and will be' + 6053 ' removed in later Select2 versions. Use $element.val() instead.' 6054 ); 6055 } 6056 6057 if (args == null || args.length === 0) { 6058 return this.$element.val(); 6059 } 6060 6061 var newVal = args[0]; 6062 6063 if (Array.isArray(newVal)) { 6064 newVal = newVal.map(function (obj) { 6065 return obj.toString(); 6066 }); 6067 } 6068 6069 this.$element.val(newVal).trigger('input').trigger('change'); 6070 }; 6071 6072 Select2.prototype.destroy = function () { 6073 Utils.RemoveData(this.$container[0]); 6074 this.$container.remove(); 6075 6076 this._observer.disconnect(); 6077 this._observer = null; 6078 6079 this._syncA = null; 6080 this._syncS = null; 6081 6082 this.$element.off('.select2'); 6083 this.$element.attr('tabindex', 6084 Utils.GetData(this.$element[0], 'old-tabindex')); 6085 6086 this.$element[0].classList.remove('select2-hidden-accessible'); 6087 this.$element.attr('aria-hidden', 'false'); 6088 Utils.RemoveData(this.$element[0]); 6089 this.$element.removeData('select2'); 6090 6091 this.dataAdapter.destroy(); 6092 this.selection.destroy(); 6093 this.dropdown.destroy(); 6094 this.results.destroy(); 6095 6096 this.dataAdapter = null; 6097 this.selection = null; 6098 this.dropdown = null; 6099 this.results = null; 6100 }; 6101 6102 Select2.prototype.render = function () { 6103 var $container = $( 6104 '<span class="select2 select2-container">' + 6105 '<span class="selection"></span>' + 6106 '<span class="dropdown-wrapper" aria-hidden="true"></span>' + 6107 '</span>' 6108 ); 6109 6110 $container.attr('dir', this.options.get('dir')); 6111 6112 this.$container = $container; 6113 6114 this.$container[0].classList 6115 .add('select2-container--' + this.options.get('theme')); 6116 6117 Utils.StoreData($container[0], 'element', this.$element); 6118 6119 return $container; 6120 }; 6121 6122 return Select2; 6123}); 6124 6125S2.define('jquery-mousewheel',[ 6126 'jquery' 6127], function ($) { 6128 // Used to shim jQuery.mousewheel for non-full builds. 6129 return $; 6130}); 6131 6132S2.define('jquery.select2',[ 6133 'jquery', 6134 'jquery-mousewheel', 6135 6136 './select2/core', 6137 './select2/defaults', 6138 './select2/utils' 6139], function ($, _, Select2, Defaults, Utils) { 6140 if ($.fn.select2 == null) { 6141 // All methods that should return the element 6142 var thisMethods = ['open', 'close', 'destroy']; 6143 6144 $.fn.select2 = function (options) { 6145 options = options || {}; 6146 6147 if (typeof options === 'object') { 6148 this.each(function () { 6149 var instanceOptions = $.extend(true, {}, options); 6150 6151 var instance = new Select2($(this), instanceOptions); 6152 }); 6153 6154 return this; 6155 } else if (typeof options === 'string') { 6156 var ret; 6157 var args = Array.prototype.slice.call(arguments, 1); 6158 6159 this.each(function () { 6160 var instance = Utils.GetData(this, 'select2'); 6161 6162 if (instance == null && window.console && console.error) { 6163 console.error( 6164 'The select2(\'' + options + '\') method was called on an ' + 6165 'element that is not using Select2.' 6166 ); 6167 } 6168 6169 ret = instance[options].apply(instance, args); 6170 }); 6171 6172 // Check if we should be returning `this` 6173 if (thisMethods.indexOf(options) > -1) { 6174 return this; 6175 } 6176 6177 return ret; 6178 } else { 6179 throw new Error('Invalid arguments for Select2: ' + options); 6180 } 6181 }; 6182 } 6183 6184 if ($.fn.select2.defaults == null) { 6185 $.fn.select2.defaults = Defaults; 6186 } 6187 6188 return Select2; 6189}); 6190 6191 // Return the AMD loader configuration so it can be used outside of this file 6192 return { 6193 define: S2.define, 6194 require: S2.require 6195 }; 6196}()); 6197 6198 // Autoload the jQuery bindings 6199 // We know that all of the modules exist above this, so we're safe 6200 var select2 = S2.require('jquery.select2'); 6201 6202 // Hold the AMD module references on the jQuery function that was just loaded 6203 // This allows Select2 to use the internal loader outside of this file, such 6204 // as in the language files. 6205 jQuery.fn.select2.amd = S2; 6206 6207 // Return the Select2 instance for anyone who is importing it. 6208 return select2; 6209})); 6210 6211/*! Magnific Popup - v1.1.0 - 2016-02-20
vendor: 55,124 bytes, lines 6212-8079
6212* http://dimsemenov.com/plugins/magnific-popup/ 6213* Copyright (c) 2016 Dmitry Semenov; */ 6214;(function (factory) { 6215if (typeof define === 'function' && define.amd) { 6216 // AMD. Register as an anonymous module. 6217 define(['jquery'], factory); 6218 } else if (typeof exports === 'object') { 6219 // Node/CommonJS 6220 factory(require('jquery')); 6221 } else { 6222 // Browser globals 6223 factory(window.jQuery || window.Zepto); 6224 } 6225 }(function($) { 6226 6227/*>>core*/ 6228/** 6229 * 6230 * Magnific Popup Core JS file 6231 * 6232 */ 6233 6234 6235/** 6236 * Private static constants 6237 */ 6238var CLOSE_EVENT = 'Close', 6239 BEFORE_CLOSE_EVENT = 'BeforeClose', 6240 AFTER_CLOSE_EVENT = 'AfterClose', 6241 BEFORE_APPEND_EVENT = 'BeforeAppend', 6242 MARKUP_PARSE_EVENT = 'MarkupParse', 6243 OPEN_EVENT = 'Open', 6244 CHANGE_EVENT = 'Change', 6245 NS = 'mfp', 6246 EVENT_NS = '.' + NS, 6247 READY_CLASS = 'mfp-ready', 6248 REMOVING_CLASS = 'mfp-removing', 6249 PREVENT_CLOSE_CLASS = 'mfp-prevent-close'; 6250 6251 6252/** 6253 * Private vars 6254 */ 6255/*jshint -W079 */ 6256var mfp, // As we have only one instance of MagnificPopup object, we define it locally to not to use 'this' 6257 MagnificPopup = function(){}, 6258 _isJQ = !!(window.jQuery), 6259 _prevStatus, 6260 _window = $(window), 6261 _document, 6262 _prevContentType, 6263 _wrapClasses, 6264 _currPopupType; 6265 6266 6267/** 6268 * Private functions 6269 */ 6270var _mfpOn = function(name, f) { 6271 mfp.ev.on(NS + name + EVENT_NS, f); 6272 }, 6273 _getEl = function(className, appendTo, html, raw) { 6274 var el = document.createElement('div'); 6275 el.className = 'mfp-'+className; 6276 if(html) { 6277 el.innerHTML = html; 6278 } 6279 if(!raw) { 6280 el = $(el); 6281 if(appendTo) { 6282 el.appendTo(appendTo); 6283 } 6284 } else if(appendTo) { 6285 appendTo.appendChild(el); 6286 } 6287 return el; 6288 }, 6289 _mfpTrigger = function(e, data) { 6290 mfp.ev.triggerHandler(NS + e, data); 6291 6292 if(mfp.st.callbacks) { 6293 // converts "mfpEventName" to "eventName" callback and triggers it if it's present 6294 e = e.charAt(0).toLowerCase() + e.slice(1); 6295 if(mfp.st.callbacks[e]) { 6296 mfp.st.callbacks[e].apply(mfp, $.isArray(data) ? data : [data]); 6297 } 6298 } 6299 }, 6300 _getCloseBtn = function(type) { 6301 if(type !== _currPopupType || !mfp.currTemplate.closeBtn) { 6302 mfp.currTemplate.closeBtn = $( mfp.st.closeMarkup.replace('%title%', mfp.st.tClose ) ); 6303 _currPopupType = type; 6304 } 6305 return mfp.currTemplate.closeBtn; 6306 }, 6307 // Initialize Magnific Popup only when called at least once 6308 _checkInstance = function() { 6309 if(!$.magnificPopup.instance) { 6310 /*jshint -W020 */ 6311 mfp = new MagnificPopup(); 6312 mfp.init(); 6313 $.magnificPopup.instance = mfp; 6314 } 6315 }, 6316 // CSS transition detection, http://stackoverflow.com/questions/7264899/detect-css-transitions-using-javascript-and-without-modernizr 6317 supportsTransitions = function() { 6318 var s = document.createElement('p').style, // 's' for style. better to create an element if body yet to exist 6319 v = ['ms','O','Moz','Webkit']; // 'v' for vendor 6320 6321 if( s['transition'] !== undefined ) { 6322 return true; 6323 } 6324 6325 while( v.length ) { 6326 if( v.pop() + 'Transition' in s ) { 6327 return true; 6328 } 6329 } 6330 6331 return false; 6332 }; 6333 6334 6335 6336/** 6337 * Public functions 6338 */ 6339MagnificPopup.prototype = { 6340 6341 constructor: MagnificPopup, 6342 6343 /** 6344 * Initializes Magnific Popup plugin. 6345 * This function is triggered only once when $.fn.magnificPopup or $.magnificPopup is executed 6346 */ 6347 init: function() { 6348 var appVersion = navigator.appVersion; 6349 mfp.isLowIE = mfp.isIE8 = document.all && !document.addEventListener; 6350 mfp.isAndroid = (/android/gi).test(appVersion); 6351 mfp.isIOS = (/iphone|ipad|ipod/gi).test(appVersion); 6352 mfp.supportsTransition = supportsTransitions(); 6353 6354 // We disable fixed positioned lightbox on devices that don't handle it nicely. 6355 // If you know a better way of detecting this - let me know. 6356 mfp.probablyMobile = (mfp.isAndroid || mfp.isIOS || /(Opera Mini)|Kindle|webOS|BlackBerry|(Opera Mobi)|(Windows Phone)|IEMobile/i.test(navigator.userAgent) ); 6357 _document = $(document); 6358 6359 mfp.popupsCache = {}; 6360 }, 6361 6362 /** 6363 * Opens popup 6364 * @param data [description] 6365 */ 6366 open: function(data) { 6367 6368 var i; 6369 6370 if(data.isObj === false) { 6371 // convert jQuery collection to array to avoid conflicts later 6372 mfp.items = data.items.toArray(); 6373 6374 mfp.index = 0; 6375 var items = data.items, 6376 item; 6377 for(i = 0; i < items.length; i++) { 6378 item = items[i]; 6379 if(item.parsed) { 6380 item = item.el[0]; 6381 } 6382 if(item === data.el[0]) { 6383 mfp.index = i; 6384 break; 6385 } 6386 } 6387 } else { 6388 mfp.items = $.isArray(data.items) ? data.items : [data.items]; 6389 mfp.index = data.index || 0; 6390 } 6391 6392 // if popup is already opened - we just update the content 6393 if(mfp.isOpen) { 6394 mfp.updateItemHTML(); 6395 return; 6396 } 6397 6398 mfp.types = []; 6399 _wrapClasses = ''; 6400 if(data.mainEl && data.mainEl.length) { 6401 mfp.ev = data.mainEl.eq(0); 6402 } else { 6403 mfp.ev = _document; 6404 } 6405 6406 if(data.key) { 6407 if(!mfp.popupsCache[data.key]) { 6408 mfp.popupsCache[data.key] = {}; 6409 } 6410 mfp.currTemplate = mfp.popupsCache[data.key]; 6411 } else { 6412 mfp.currTemplate = {}; 6413 } 6414 6415 6416 6417 mfp.st = $.extend(true, {}, $.magnificPopup.defaults, data ); 6418 mfp.fixedContentPos = mfp.st.fixedContentPos === 'auto' ? !mfp.probablyMobile : mfp.st.fixedContentPos; 6419 6420 if(mfp.st.modal) { 6421 mfp.st.closeOnContentClick = false; 6422 mfp.st.closeOnBgClick = false; 6423 mfp.st.showCloseBtn = false; 6424 mfp.st.enableEscapeKey = false; 6425 } 6426 6427 6428 // Building markup 6429 // main containers are created only once 6430 if(!mfp.bgOverlay) { 6431 6432 // Dark overlay 6433 mfp.bgOverlay = _getEl('bg').on('click'+EVENT_NS, function() { 6434 mfp.close(); 6435 }); 6436 6437 mfp.wrap = _getEl('wrap').attr('tabindex', -1).on('click'+EVENT_NS, function(e) { 6438 if(mfp._checkIfClose(e.target)) { 6439 mfp.close(); 6440 } 6441 }); 6442 6443 mfp.container = _getEl('container', mfp.wrap); 6444 } 6445 6446 mfp.contentContainer = _getEl('content'); 6447 if(mfp.st.preloader) { 6448 mfp.preloader = _getEl('preloader', mfp.container, mfp.st.tLoading); 6449 } 6450 6451 6452 // Initializing modules 6453 var modules = $.magnificPopup.modules; 6454 for(i = 0; i < modules.length; i++) { 6455 var n = modules[i]; 6456 n = n.charAt(0).toUpperCase() + n.slice(1); 6457 mfp['init'+n].call(mfp); 6458 } 6459 _mfpTrigger('BeforeOpen'); 6460 6461 6462 if(mfp.st.showCloseBtn) { 6463 // Close button 6464 if(!mfp.st.closeBtnInside) { 6465 mfp.wrap.append( _getCloseBtn() ); 6466 } else { 6467 _mfpOn(MARKUP_PARSE_EVENT, function(e, template, values, item) { 6468 values.close_replaceWith = _getCloseBtn(item.type); 6469 }); 6470 _wrapClasses += ' mfp-close-btn-in'; 6471 } 6472 } 6473 6474 if(mfp.st.alignTop) { 6475 _wrapClasses += ' mfp-align-top'; 6476 } 6477 6478 6479 6480 if(mfp.fixedContentPos) { 6481 mfp.wrap.css({ 6482 overflow: mfp.st.overflowY, 6483 overflowX: 'hidden', 6484 overflowY: mfp.st.overflowY 6485 }); 6486 } else { 6487 mfp.wrap.css({ 6488 top: _window.scrollTop(), 6489 position: 'absolute' 6490 }); 6491 } 6492 if( mfp.st.fixedBgPos === false || (mfp.st.fixedBgPos === 'auto' && !mfp.fixedContentPos) ) { 6493 mfp.bgOverlay.css({ 6494 height: _document.height(), 6495 position: 'absolute' 6496 }); 6497 } 6498 6499 6500 6501 if(mfp.st.enableEscapeKey) { 6502 // Close on ESC key 6503 _document.on('keyup' + EVENT_NS, function(e) { 6504 if(e.keyCode === 27) { 6505 mfp.close(); 6506 } 6507 }); 6508 } 6509 6510 _window.on('resize' + EVENT_NS, function() { 6511 mfp.updateSize(); 6512 }); 6513 6514 6515 if(!mfp.st.closeOnContentClick) { 6516 _wrapClasses += ' mfp-auto-cursor'; 6517 } 6518 6519 if(_wrapClasses) 6520 mfp.wrap.addClass(_wrapClasses); 6521 6522 6523 // this triggers recalculation of layout, so we get it once to not to trigger twice 6524 var windowHeight = mfp.wH = _window.height(); 6525 6526 6527 var windowStyles = {}; 6528 6529 if( mfp.fixedContentPos ) { 6530 if(mfp._hasScrollBar(windowHeight)){ 6531 var s = mfp._getScrollbarSize(); 6532 if(s) { 6533 windowStyles.marginRight = s; 6534 } 6535 } 6536 } 6537 6538 if(mfp.fixedContentPos) { 6539 if(!mfp.isIE7) { 6540 windowStyles.overflow = 'hidden'; 6541 } else { 6542 // ie7 double-scroll bug 6543 $('body, html').css('overflow', 'hidden'); 6544 } 6545 } 6546 6547 6548 6549 var classesToadd = mfp.st.mainClass; 6550 if(mfp.isIE7) { 6551 classesToadd += ' mfp-ie7'; 6552 } 6553 if(classesToadd) { 6554 mfp._addClassToMFP( classesToadd ); 6555 } 6556 6557 // add content 6558 mfp.updateItemHTML(); 6559 6560 _mfpTrigger('BuildControls'); 6561 6562 // remove scrollbar, add margin e.t.c 6563 $('html').css(windowStyles); 6564 6565 // add everything to DOM 6566 mfp.bgOverlay.add(mfp.wrap).prependTo( mfp.st.prependTo || $(document.body) ); 6567 6568 // Save last focused element 6569 mfp._lastFocusedEl = document.activeElement; 6570 6571 // Wait for next cycle to allow CSS transition 6572 setTimeout(function() { 6573 6574 if(mfp.content) { 6575 mfp._addClassToMFP(READY_CLASS); 6576 mfp._setFocus(); 6577 } else { 6578 // if content is not defined (not loaded e.t.c) we add class only for BG 6579 mfp.bgOverlay.addClass(READY_CLASS); 6580 } 6581 6582 // Trap the focus in popup 6583 _document.on('focusin' + EVENT_NS, mfp._onFocusIn); 6584 6585 }, 16); 6586 6587 mfp.isOpen = true; 6588 mfp.updateSize(windowHeight); 6589 _mfpTrigger(OPEN_EVENT); 6590 6591 return data; 6592 }, 6593 6594 /** 6595 * Closes the popup 6596 */ 6597 close: function() { 6598 if(!mfp.isOpen) return; 6599 _mfpTrigger(BEFORE_CLOSE_EVENT); 6600 6601 mfp.isOpen = false; 6602 // for CSS3 animation 6603 if(mfp.st.removalDelay && !mfp.isLowIE && mfp.supportsTransition ) { 6604 mfp._addClassToMFP(REMOVING_CLASS); 6605 setTimeout(function() { 6606 mfp._close(); 6607 }, mfp.st.removalDelay); 6608 } else { 6609 mfp._close(); 6610 } 6611 }, 6612 6613 /** 6614 * Helper for close() function 6615 */ 6616 _close: function() { 6617 _mfpTrigger(CLOSE_EVENT); 6618 6619 var classesToRemove = REMOVING_CLASS + ' ' + READY_CLASS + ' '; 6620 6621 mfp.bgOverlay.detach(); 6622 mfp.wrap.detach(); 6623 mfp.container.empty(); 6624 6625 if(mfp.st.mainClass) { 6626 classesToRemove += mfp.st.mainClass + ' '; 6627 } 6628 6629 mfp._removeClassFromMFP(classesToRemove); 6630 6631 if(mfp.fixedContentPos) { 6632 var windowStyles = {marginRight: ''}; 6633 if(mfp.isIE7) { 6634 $('body, html').css('overflow', ''); 6635 } else { 6636 windowStyles.overflow = ''; 6637 } 6638 $('html').css(windowStyles); 6639 } 6640 6641 _document.off('keyup' + EVENT_NS + ' focusin' + EVENT_NS); 6642 mfp.ev.off(EVENT_NS); 6643 6644 // clean up DOM elements that aren't removed 6645 mfp.wrap.attr('class', 'mfp-wrap').removeAttr('style'); 6646 mfp.bgOverlay.attr('class', 'mfp-bg'); 6647 mfp.container.attr('class', 'mfp-container'); 6648 6649 // remove close button from target element 6650 if(mfp.st.showCloseBtn && 6651 (!mfp.st.closeBtnInside || mfp.currTemplate[mfp.currItem.type] === true)) { 6652 if(mfp.currTemplate.closeBtn) 6653 mfp.currTemplate.closeBtn.detach(); 6654 } 6655 6656 6657 if(mfp.st.autoFocusLast && mfp._lastFocusedEl) { 6658 $(mfp._lastFocusedEl).focus(); // put tab focus back 6659 } 6660 mfp.currItem = null; 6661 mfp.content = null; 6662 mfp.currTemplate = null; 6663 mfp.prevHeight = 0; 6664 6665 _mfpTrigger(AFTER_CLOSE_EVENT); 6666 }, 6667 6668 updateSize: function(winHeight) { 6669 6670 if(mfp.isIOS) { 6671 // fixes iOS nav bars https://github.com/dimsemenov/Magnific-Popup/issues/2 6672 var zoomLevel = document.documentElement.clientWidth / window.innerWidth; 6673 var height = window.innerHeight * zoomLevel; 6674 mfp.wrap.css('height', height); 6675 mfp.wH = height; 6676 } else { 6677 mfp.wH = winHeight || _window.height(); 6678 } 6679 // Fixes #84: popup incorrectly positioned with position:relative on body 6680 if(!mfp.fixedContentPos) { 6681 mfp.wrap.css('height', mfp.wH); 6682 } 6683 6684 _mfpTrigger('Resize'); 6685 6686 }, 6687 6688 /** 6689 * Set content of popup based on current index 6690 */ 6691 updateItemHTML: function() { 6692 var item = mfp.items[mfp.index]; 6693 6694 // Detach and perform modifications 6695 mfp.contentContainer.detach(); 6696 6697 if(mfp.content) 6698 mfp.content.detach(); 6699 6700 if(!item.parsed) { 6701 item = mfp.parseEl( mfp.index ); 6702 } 6703 6704 var type = item.type; 6705 6706 _mfpTrigger('BeforeChange', [mfp.currItem ? mfp.currItem.type : '', type]); 6707 // BeforeChange event works like so: 6708 // _mfpOn('BeforeChange', function(e, prevType, newType) { }); 6709 6710 mfp.currItem = item; 6711 6712 if(!mfp.currTemplate[type]) { 6713 var markup = mfp.st[type] ? mfp.st[type].markup : false; 6714 6715 // allows to modify markup 6716 _mfpTrigger('FirstMarkupParse', markup); 6717 6718 if(markup) { 6719 mfp.currTemplate[type] = $(markup); 6720 } else { 6721 // if there is no markup found we just define that template is parsed 6722 mfp.currTemplate[type] = true; 6723 } 6724 } 6725 6726 if(_prevContentType && _prevContentType !== item.type) { 6727 mfp.container.removeClass('mfp-'+_prevContentType+'-holder'); 6728 } 6729 6730 var newContent = mfp['get' + type.charAt(0).toUpperCase() + type.slice(1)](item, mfp.currTemplate[type]); 6731 mfp.appendContent(newContent, type); 6732 6733 item.preloaded = true; 6734 6735 _mfpTrigger(CHANGE_EVENT, item); 6736 _prevContentType = item.type; 6737 6738 // Append container back after its content changed 6739 mfp.container.prepend(mfp.contentContainer); 6740 6741 _mfpTrigger('AfterChange'); 6742 }, 6743 6744 6745 /** 6746 * Set HTML content of popup 6747 */ 6748 appendContent: function(newContent, type) { 6749 mfp.content = newContent; 6750 6751 if(newContent) { 6752 if(mfp.st.showCloseBtn && mfp.st.closeBtnInside && 6753 mfp.currTemplate[type] === true) { 6754 // if there is no markup, we just append close button element inside 6755 if(!mfp.content.find('.mfp-close').length) { 6756 mfp.content.append(_getCloseBtn()); 6757 } 6758 } else { 6759 mfp.content = newContent; 6760 } 6761 } else { 6762 mfp.content = ''; 6763 } 6764 6765 _mfpTrigger(BEFORE_APPEND_EVENT); 6766 mfp.container.addClass('mfp-'+type+'-holder'); 6767 6768 mfp.contentContainer.append(mfp.content); 6769 }, 6770 6771 6772 /** 6773 * Creates Magnific Popup data object based on given data 6774 * @param {int} index Index of item to parse 6775 */ 6776 parseEl: function(index) { 6777 var item = mfp.items[index], 6778 type; 6779 6780 if(item.tagName) { 6781 item = { el: $(item) }; 6782 } else { 6783 type = item.type; 6784 item = { data: item, src: item.src }; 6785 } 6786 6787 if(item.el) { 6788 var types = mfp.types; 6789 6790 // check for 'mfp-TYPE' class 6791 for(var i = 0; i < types.length; i++) { 6792 if( item.el.hasClass('mfp-'+types[i]) ) { 6793 type = types[i]; 6794 break; 6795 } 6796 } 6797 6798 item.src = item.el.attr('data-mfp-src'); 6799 if(!item.src) { 6800 item.src = item.el.attr('href'); 6801 } 6802 } 6803 6804 item.type = type || mfp.st.type || 'inline'; 6805 item.index = index; 6806 item.parsed = true; 6807 mfp.items[index] = item; 6808 _mfpTrigger('ElementParse', item); 6809 6810 return mfp.items[index]; 6811 }, 6812 6813 6814 /** 6815 * Initializes single popup or a group of popups 6816 */ 6817 addGroup: function(el, options) { 6818 var eHandler = function(e) { 6819 e.mfpEl = this; 6820 mfp._openClick(e, el, options); 6821 }; 6822 6823 if(!options) { 6824 options = {}; 6825 } 6826 6827 var eName = 'click.magnificPopup'; 6828 options.mainEl = el; 6829 6830 if(options.items) { 6831 options.isObj = true; 6832 el.off(eName).on(eName, eHandler); 6833 } else { 6834 options.isObj = false; 6835 if(options.delegate) { 6836 el.off(eName).on(eName, options.delegate , eHandler); 6837 } else { 6838 options.items = el; 6839 el.off(eName).on(eName, eHandler); 6840 } 6841 } 6842 }, 6843 _openClick: function(e, el, options) { 6844 var midClick = options.midClick !== undefined ? options.midClick : $.magnificPopup.defaults.midClick; 6845 6846 6847 if(!midClick && ( e.which === 2 || e.ctrlKey || e.metaKey || e.altKey || e.shiftKey ) ) { 6848 return; 6849 } 6850 6851 var disableOn = options.disableOn !== undefined ? options.disableOn : $.magnificPopup.defaults.disableOn; 6852 6853 if(disableOn) { 6854 if($.isFunction(disableOn)) { 6855 if( !disableOn.call(mfp) ) { 6856 return true; 6857 } 6858 } else { // else it's number 6859 if( _window.width() < disableOn ) { 6860 return true; 6861 } 6862 } 6863 } 6864 6865 if(e.type) { 6866 e.preventDefault(); 6867 6868 // This will prevent popup from closing if element is inside and popup is already opened 6869 if(mfp.isOpen) { 6870 e.stopPropagation(); 6871 } 6872 } 6873 6874 options.el = $(e.mfpEl); 6875 if(options.delegate) { 6876 options.items = el.find(options.delegate); 6877 } 6878 mfp.open(options); 6879 }, 6880 6881 6882 /** 6883 * Updates text on preloader 6884 */ 6885 updateStatus: function(status, text) { 6886 6887 if(mfp.preloader) { 6888 if(_prevStatus !== status) { 6889 mfp.container.removeClass('mfp-s-'+_prevStatus); 6890 } 6891 6892 if(!text && status === 'loading') { 6893 text = mfp.st.tLoading; 6894 } 6895 6896 var data = { 6897 status: status, 6898 text: text 6899 }; 6900 // allows to modify status 6901 _mfpTrigger('UpdateStatus', data); 6902 6903 status = data.status; 6904 text = data.text; 6905 6906 mfp.preloader.html(text); 6907 6908 mfp.preloader.find('a').on('click', function(e) { 6909 e.stopImmediatePropagation(); 6910 }); 6911 6912 mfp.container.addClass('mfp-s-'+status); 6913 _prevStatus = status; 6914 } 6915 }, 6916 6917 6918 /* 6919 "Private" helpers that aren't private at all 6920 */ 6921 // Check to close popup or not 6922 // "target" is an element that was clicked 6923 _checkIfClose: function(target) { 6924 6925 if($(target).hasClass(PREVENT_CLOSE_CLASS)) { 6926 return; 6927 } 6928 6929 var closeOnContent = mfp.st.closeOnContentClick; 6930 var closeOnBg = mfp.st.closeOnBgClick; 6931 6932 if(closeOnContent && closeOnBg) { 6933 return true; 6934 } else { 6935 6936 // We close the popup if click is on close button or on preloader. Or if there is no content. 6937 if(!mfp.content || $(target).hasClass('mfp-close') || (mfp.preloader && target === mfp.preloader[0]) ) { 6938 return true; 6939 } 6940 6941 // if click is outside the content 6942 if( (target !== mfp.content[0] && !$.contains(mfp.content[0], target)) ) { 6943 if(closeOnBg) { 6944 // last check, if the clicked element is in DOM, (in case it's removed onclick) 6945 if( $.contains(document, target) ) { 6946 return true; 6947 } 6948 } 6949 } else if(closeOnContent) { 6950 return true; 6951 } 6952 6953 } 6954 return false; 6955 }, 6956 _addClassToMFP: function(cName) { 6957 mfp.bgOverlay.addClass(cName); 6958 mfp.wrap.addClass(cName); 6959 }, 6960 _removeClassFromMFP: function(cName) { 6961 this.bgOverlay.removeClass(cName); 6962 mfp.wrap.removeClass(cName); 6963 }, 6964 _hasScrollBar: function(winHeight) { 6965 return ( (mfp.isIE7 ? _document.height() : document.body.scrollHeight) > (winHeight || _window.height()) ); 6966 }, 6967 _setFocus: function() { 6968 (mfp.st.focus ? mfp.content.find(mfp.st.focus).eq(0) : mfp.wrap).focus(); 6969 }, 6970 _onFocusIn: function(e) { 6971 if( e.target !== mfp.wrap[0] && !$.contains(mfp.wrap[0], e.target) ) { 6972 mfp._setFocus(); 6973 return false; 6974 } 6975 }, 6976 _parseMarkup: function(template, values, item) { 6977 var arr; 6978 if(item.data) { 6979 values = $.extend(item.data, values); 6980 } 6981 _mfpTrigger(MARKUP_PARSE_EVENT, [template, values, item] ); 6982 6983 $.each(values, function(key, value) { 6984 if(value === undefined || value === false) { 6985 return true; 6986 } 6987 arr = key.split('_'); 6988 if(arr.length > 1) { 6989 var el = template.find(EVENT_NS + '-'+arr[0]); 6990 6991 if(el.length > 0) { 6992 var attr = arr[1]; 6993 if(attr === 'replaceWith') { 6994 if(el[0] !== value[0]) { 6995 el.replaceWith(value); 6996 } 6997 } else if(attr === 'img') { 6998 if(el.is('img')) { 6999 el.attr('src', value); 7000 } else { 7001 el.replaceWith( $('<img>').attr('src', value).attr('class', el.attr('class')) ); 7002 } 7003 } else { 7004 el.attr(arr[1], value); 7005 } 7006 } 7007 7008 } else { 7009 template.find(EVENT_NS + '-'+key).html(value); 7010 } 7011 }); 7012 }, 7013 7014 _getScrollbarSize: function() { 7015 // thx David 7016 if(mfp.scrollbarSize === undefined) { 7017 var scrollDiv = document.createElement("div"); 7018 scrollDiv.style.cssText = 'width: 99px; height: 99px; overflow: scroll; position: absolute; top: -9999px;'; 7019 document.body.appendChild(scrollDiv); 7020 mfp.scrollbarSize = scrollDiv.offsetWidth - scrollDiv.clientWidth; 7021 document.body.removeChild(scrollDiv); 7022 } 7023 return mfp.scrollbarSize; 7024 } 7025 7026}; /* MagnificPopup core prototype end */ 7027 7028 7029 7030 7031/** 7032 * Public static functions 7033 */ 7034$.magnificPopup = { 7035 instance: null, 7036 proto: MagnificPopup.prototype, 7037 modules: [], 7038 7039 open: function(options, index) { 7040 _checkInstance(); 7041 7042 if(!options) { 7043 options = {}; 7044 } else { 7045 options = $.extend(true, {}, options); 7046 } 7047 7048 options.isObj = true; 7049 options.index = index || 0; 7050 return this.instance.open(options); 7051 }, 7052 7053 close: function() { 7054 return $.magnificPopup.instance && $.magnificPopup.instance.close(); 7055 }, 7056 7057 registerModule: function(name, module) { 7058 if(module.options) { 7059 $.magnificPopup.defaults[name] = module.options; 7060 } 7061 $.extend(this.proto, module.proto); 7062 this.modules.push(name); 7063 }, 7064 7065 defaults: { 7066 7067 // Info about options is in docs: 7068 // http://dimsemenov.com/plugins/magnific-popup/documentation.html#options 7069 7070 disableOn: 0, 7071 7072 key: null, 7073 7074 midClick: false, 7075 7076 mainClass: '', 7077 7078 preloader: true, 7079 7080 focus: '', // CSS selector of input to focus after popup is opened 7081 7082 closeOnContentClick: false, 7083 7084 closeOnBgClick: true, 7085 7086 closeBtnInside: true, 7087 7088 showCloseBtn: true, 7089 7090 enableEscapeKey: true, 7091 7092 modal: false, 7093 7094 alignTop: false, 7095 7096 removalDelay: 0, 7097 7098 prependTo: null, 7099 7100 fixedContentPos: 'auto', 7101 7102 fixedBgPos: 'auto', 7103 7104 overflowY: 'auto', 7105 7106 closeMarkup: '<button title="%title%" type="button" class="mfp-close">×</button>', 7107 7108 tClose: 'Close (Esc)', 7109 7110 tLoading: 'Loading...', 7111 7112 autoFocusLast: true 7113 7114 } 7115}; 7116 7117 7118 7119$.fn.magnificPopup = function(options) { 7120 _checkInstance(); 7121 7122 var jqEl = $(this); 7123 7124 // We call some API method of first param is a string 7125 if (typeof options === "string" ) { 7126 7127 if(options === 'open') { 7128 var items, 7129 itemOpts = _isJQ ? jqEl.data('magnificPopup') : jqEl[0].magnificPopup, 7130 index = parseInt(arguments[1], 10) || 0; 7131 7132 if(itemOpts.items) { 7133 items = itemOpts.items[index]; 7134 } else { 7135 items = jqEl; 7136 if(itemOpts.delegate) { 7137 items = items.find(itemOpts.delegate); 7138 } 7139 items = items.eq( index ); 7140 } 7141 mfp._openClick({mfpEl:items}, jqEl, itemOpts); 7142 } else { 7143 if(mfp.isOpen) 7144 mfp[options].apply(mfp, Array.prototype.slice.call(arguments, 1)); 7145 } 7146 7147 } else { 7148 // clone options obj 7149 options = $.extend(true, {}, options); 7150 7151 /* 7152 * As Zepto doesn't support .data() method for objects 7153 * and it works only in normal browsers 7154 * we assign "options" object directly to the DOM element. FTW! 7155 */ 7156 if(_isJQ) { 7157 jqEl.data('magnificPopup', options); 7158 } else { 7159 jqEl[0].magnificPopup = options; 7160 } 7161 7162 mfp.addGroup(jqEl, options); 7163 7164 } 7165 return jqEl; 7166}; 7167 7168/*>>core*/ 7169 7170/*>>inline*/ 7171 7172var INLINE_NS = 'inline', 7173 _hiddenClass, 7174 _inlinePlaceholder, 7175 _lastInlineElement, 7176 _putInlineElementsBack = function() { 7177 if(_lastInlineElement) { 7178 _inlinePlaceholder.after( _lastInlineElement.addClass(_hiddenClass) ).detach(); 7179 _lastInlineElement = null; 7180 } 7181 }; 7182 7183$.magnificPopup.registerModule(INLINE_NS, { 7184 options: { 7185 hiddenClass: 'hide', // will be appended with `mfp-` prefix 7186 markup: '', 7187 tNotFound: 'Content not found' 7188 }, 7189 proto: { 7190 7191 initInline: function() { 7192 mfp.types.push(INLINE_NS); 7193 7194 _mfpOn(CLOSE_EVENT+'.'+INLINE_NS, function() { 7195 _putInlineElementsBack(); 7196 }); 7197 }, 7198 7199 getInline: function(item, template) { 7200 7201 _putInlineElementsBack(); 7202 7203 if(item.src) { 7204 var inlineSt = mfp.st.inline, 7205 el = $(item.src); 7206 7207 if(el.length) { 7208 7209 // If target element has parent - we replace it with placeholder and put it back after popup is closed 7210 var parent = el[0].parentNode; 7211 if(parent && parent.tagName) { 7212 if(!_inlinePlaceholder) { 7213 _hiddenClass = inlineSt.hiddenClass; 7214 _inlinePlaceholder = _getEl(_hiddenClass); 7215 _hiddenClass = 'mfp-'+_hiddenClass; 7216 } 7217 // replace target inline element with placeholder 7218 _lastInlineElement = el.after(_inlinePlaceholder).detach().removeClass(_hiddenClass); 7219 } 7220 7221 mfp.updateStatus('ready'); 7222 } else { 7223 mfp.updateStatus('error', inlineSt.tNotFound); 7224 el = $('<div>'); 7225 } 7226 7227 item.inlineElement = el; 7228 return el; 7229 } 7230 7231 mfp.updateStatus('ready'); 7232 mfp._parseMarkup(template, {}, item); 7233 return template; 7234 } 7235 } 7236}); 7237 7238/*>>inline*/ 7239 7240/*>>ajax*/ 7241var AJAX_NS = 'ajax', 7242 _ajaxCur, 7243 _removeAjaxCursor = function() { 7244 if(_ajaxCur) { 7245 $(document.body).removeClass(_ajaxCur); 7246 } 7247 }, 7248 _destroyAjaxRequest = function() { 7249 _removeAjaxCursor(); 7250 if(mfp.req) { 7251 mfp.req.abort(); 7252 } 7253 }; 7254 7255$.magnificPopup.registerModule(AJAX_NS, { 7256 7257 options: { 7258 settings: null, 7259 cursor: 'mfp-ajax-cur', 7260 tError: '<a href="%url%">The content</a> could not be loaded.' 7261 }, 7262 7263 proto: { 7264 initAjax: function() { 7265 mfp.types.push(AJAX_NS); 7266 _ajaxCur = mfp.st.ajax.cursor; 7267 7268 _mfpOn(CLOSE_EVENT+'.'+AJAX_NS, _destroyAjaxRequest); 7269 _mfpOn('BeforeChange.' + AJAX_NS, _destroyAjaxRequest); 7270 }, 7271 getAjax: function(item) { 7272 7273 if(_ajaxCur) { 7274 $(document.body).addClass(_ajaxCur); 7275 } 7276 7277 mfp.updateStatus('loading'); 7278 7279 var opts = $.extend({ 7280 url: item.src, 7281 success: function(data, textStatus, jqXHR) { 7282 var temp = { 7283 data:data, 7284 xhr:jqXHR 7285 }; 7286 7287 _mfpTrigger('ParseAjax', temp); 7288 7289 mfp.appendContent( $(temp.data), AJAX_NS ); 7290 7291 item.finished = true; 7292 7293 _removeAjaxCursor(); 7294 7295 mfp._setFocus(); 7296 7297 setTimeout(function() { 7298 mfp.wrap.addClass(READY_CLASS); 7299 }, 16); 7300 7301 mfp.updateStatus('ready'); 7302 7303 _mfpTrigger('AjaxContentAdded'); 7304 }, 7305 error: function() { 7306 _removeAjaxCursor(); 7307 item.finished = item.loadError = true; 7308 mfp.updateStatus('error', mfp.st.ajax.tError.replace('%url%', item.src)); 7309 } 7310 }, mfp.st.ajax.settings); 7311 7312 mfp.req = $.ajax(opts); 7313 7314 return ''; 7315 } 7316 } 7317}); 7318 7319/*>>ajax*/ 7320 7321/*>>image*/ 7322var _imgInterval, 7323 _getTitle = function(item) { 7324 if(item.data && item.data.title !== undefined) 7325 return item.data.title; 7326 7327 var src = mfp.st.image.titleSrc; 7328 7329 if(src) { 7330 if($.isFunction(src)) { 7331 return src.call(mfp, item); 7332 } else if(item.el) { 7333 return item.el.attr(src) || ''; 7334 } 7335 } 7336 return ''; 7337 }; 7338 7339$.magnificPopup.registerModule('image', { 7340 7341 options: { 7342 markup: '<div class="mfp-figure">'+ 7343 '<div class="mfp-close"></div>'+ 7344 '<figure>'+ 7345 '<div class="mfp-img"></div>'+ 7346 '<figcaption>'+ 7347 '<div class="mfp-bottom-bar">'+ 7348 '<div class="mfp-title"></div>'+ 7349 '<div class="mfp-counter"></div>'+ 7350 '</div>'+ 7351 '</figcaption>'+ 7352 '</figure>'+ 7353 '</div>', 7354 cursor: 'mfp-zoom-out-cur', 7355 titleSrc: 'title', 7356 verticalFit: true, 7357 tError: '<a href="%url%">The image</a> could not be loaded.' 7358 }, 7359 7360 proto: { 7361 initImage: function() { 7362 var imgSt = mfp.st.image, 7363 ns = '.image'; 7364 7365 mfp.types.push('image'); 7366 7367 _mfpOn(OPEN_EVENT+ns, function() { 7368 if(mfp.currItem.type === 'image' && imgSt.cursor) { 7369 $(document.body).addClass(imgSt.cursor); 7370 } 7371 }); 7372 7373 _mfpOn(CLOSE_EVENT+ns, function() { 7374 if(imgSt.cursor) { 7375 $(document.body).removeClass(imgSt.cursor); 7376 } 7377 _window.off('resize' + EVENT_NS); 7378 }); 7379 7380 _mfpOn('Resize'+ns, mfp.resizeImage); 7381 if(mfp.isLowIE) { 7382 _mfpOn('AfterChange', mfp.resizeImage); 7383 } 7384 }, 7385 resizeImage: function() { 7386 var item = mfp.currItem; 7387 if(!item || !item.img) return; 7388 7389 if(mfp.st.image.verticalFit) { 7390 var decr = 0; 7391 // fix box-sizing in ie7/8 7392 if(mfp.isLowIE) { 7393 decr = parseInt(item.img.css('padding-top'), 10) + parseInt(item.img.css('padding-bottom'),10); 7394 } 7395 item.img.css('max-height', mfp.wH-decr); 7396 } 7397 }, 7398 _onImageHasSize: function(item) { 7399 if(item.img) { 7400 7401 item.hasSize = true; 7402 7403 if(_imgInterval) { 7404 clearInterval(_imgInterval); 7405 } 7406 7407 item.isCheckingImgSize = false; 7408 7409 _mfpTrigger('ImageHasSize', item); 7410 7411 if(item.imgHidden) { 7412 if(mfp.content) 7413 mfp.content.removeClass('mfp-loading'); 7414 7415 item.imgHidden = false; 7416 } 7417 7418 } 7419 }, 7420 7421 /** 7422 * Function that loops until the image has size to display elements that rely on it asap 7423 */ 7424 findImageSize: function(item) { 7425 7426 var counter = 0, 7427 img = item.img[0], 7428 mfpSetInterval = function(delay) { 7429 7430 if(_imgInterval) { 7431 clearInterval(_imgInterval); 7432 } 7433 // decelerating interval that checks for size of an image 7434 _imgInterval = setInterval(function() { 7435 if(img.naturalWidth > 0) { 7436 mfp._onImageHasSize(item); 7437 return; 7438 } 7439 7440 if(counter > 200) { 7441 clearInterval(_imgInterval); 7442 } 7443 7444 counter++; 7445 if(counter === 3) { 7446 mfpSetInterval(10); 7447 } else if(counter === 40) { 7448 mfpSetInterval(50); 7449 } else if(counter === 100) { 7450 mfpSetInterval(500); 7451 } 7452 }, delay); 7453 }; 7454 7455 mfpSetInterval(1); 7456 }, 7457 7458 getImage: function(item, template) { 7459 7460 var guard = 0, 7461 7462 // image load complete handler 7463 onLoadComplete = function() { 7464 if(item) { 7465 if (item.img[0].complete) { 7466 item.img.off('.mfploader'); 7467 7468 if(item === mfp.currItem){ 7469 mfp._onImageHasSize(item); 7470 7471 mfp.updateStatus('ready'); 7472 } 7473 7474 item.hasSize = true; 7475 item.loaded = true; 7476 7477 _mfpTrigger('ImageLoadComplete'); 7478 7479 } 7480 else { 7481 // if image complete check fails 200 times (20 sec), we assume that there was an error. 7482 guard++; 7483 if(guard < 200) { 7484 setTimeout(onLoadComplete,100); 7485 } else { 7486 onLoadError(); 7487 } 7488 } 7489 } 7490 }, 7491 7492 // image error handler 7493 onLoadError = function() { 7494 if(item) { 7495 item.img.off('.mfploader'); 7496 if(item === mfp.currItem){ 7497 mfp._onImageHasSize(item); 7498 mfp.updateStatus('error', imgSt.tError.replace('%url%', item.src) ); 7499 } 7500 7501 item.hasSize = true; 7502 item.loaded = true; 7503 item.loadError = true; 7504 } 7505 }, 7506 imgSt = mfp.st.image; 7507 7508 7509 var el = template.find('.mfp-img'); 7510 if(el.length) { 7511 var img = document.createElement('img'); 7512 img.className = 'mfp-img'; 7513 if(item.el && item.el.find('img').length) { 7514 img.alt = item.el.find('img').attr('alt'); 7515 } 7516 item.img = $(img).on('load.mfploader', onLoadComplete).on('error.mfploader', onLoadError); 7517 img.src = item.src; 7518 7519 // without clone() "error" event is not firing when IMG is replaced by new IMG 7520 // TODO: find a way to avoid such cloning 7521 if(el.is('img')) { 7522 item.img = item.img.clone(); 7523 } 7524 7525 img = item.img[0]; 7526 if(img.naturalWidth > 0) { 7527 item.hasSize = true; 7528 } else if(!img.width) { 7529 item.hasSize = false; 7530 } 7531 } 7532 7533 mfp._parseMarkup(template, { 7534 title: _getTitle(item), 7535 img_replaceWith: item.img 7536 }, item); 7537 7538 mfp.resizeImage(); 7539 7540 if(item.hasSize) { 7541 if(_imgInterval) clearInterval(_imgInterval); 7542 7543 if(item.loadError) { 7544 template.addClass('mfp-loading'); 7545 mfp.updateStatus('error', imgSt.tError.replace('%url%', item.src) ); 7546 } else { 7547 template.removeClass('mfp-loading'); 7548 mfp.updateStatus('ready'); 7549 } 7550 return template; 7551 } 7552 7553 mfp.updateStatus('loading'); 7554 item.loading = true; 7555 7556 if(!item.hasSize) { 7557 item.imgHidden = true; 7558 template.addClass('mfp-loading'); 7559 mfp.findImageSize(item); 7560 } 7561 7562 return template; 7563 } 7564 } 7565}); 7566 7567/*>>image*/ 7568 7569/*>>zoom*/ 7570var hasMozTransform, 7571 getHasMozTransform = function() { 7572 if(hasMozTransform === undefined) { 7573 hasMozTransform = document.createElement('p').style.MozTransform !== undefined; 7574 } 7575 return hasMozTransform; 7576 }; 7577 7578$.magnificPopup.registerModule('zoom', { 7579 7580 options: { 7581 enabled: false, 7582 easing: 'ease-in-out', 7583 duration: 300, 7584 opener: function(element) { 7585 return element.is('img') ? element : element.find('img'); 7586 } 7587 }, 7588 7589 proto: { 7590 7591 initZoom: function() { 7592 var zoomSt = mfp.st.zoom, 7593 ns = '.zoom', 7594 image; 7595 7596 if(!zoomSt.enabled || !mfp.supportsTransition) { 7597 return; 7598 } 7599 7600 var duration = zoomSt.duration, 7601 getElToAnimate = function(image) { 7602 var newImg = image.clone().removeAttr('style').removeAttr('class').addClass('mfp-animated-image'), 7603 transition = 'all '+(zoomSt.duration/1000)+'s ' + zoomSt.easing, 7604 cssObj = { 7605 position: 'fixed', 7606 zIndex: 9999, 7607 left: 0, 7608 top: 0, 7609 '-webkit-backface-visibility': 'hidden' 7610 }, 7611 t = 'transition'; 7612 7613 cssObj['-webkit-'+t] = cssObj['-moz-'+t] = cssObj['-o-'+t] = cssObj[t] = transition; 7614 7615 newImg.css(cssObj); 7616 return newImg; 7617 }, 7618 showMainContent = function() { 7619 mfp.content.css('visibility', 'visible'); 7620 }, 7621 openTimeout, 7622 animatedImg; 7623 7624 _mfpOn('BuildControls'+ns, function() { 7625 if(mfp._allowZoom()) { 7626 7627 clearTimeout(openTimeout); 7628 mfp.content.css('visibility', 'hidden'); 7629 7630 // Basically, all code below does is clones existing image, puts in on top of the current one and animated it 7631 7632 image = mfp._getItemToZoom(); 7633 7634 if(!image) { 7635 showMainContent(); 7636 return; 7637 } 7638 7639 animatedImg = getElToAnimate(image); 7640 7641 animatedImg.css( mfp._getOffset() ); 7642 7643 mfp.wrap.append(animatedImg); 7644 7645 openTimeout = setTimeout(function() { 7646 animatedImg.css( mfp._getOffset( true ) ); 7647 openTimeout = setTimeout(function() { 7648 7649 showMainContent(); 7650 7651 setTimeout(function() { 7652 animatedImg.remove(); 7653 image = animatedImg = null; 7654 _mfpTrigger('ZoomAnimationEnded'); 7655 }, 16); // avoid blink when switching images 7656 7657 }, duration); // this timeout equals animation duration 7658 7659 }, 16); // by adding this timeout we avoid short glitch at the beginning of animation 7660 7661 7662 // Lots of timeouts... 7663 } 7664 }); 7665 _mfpOn(BEFORE_CLOSE_EVENT+ns, function() { 7666 if(mfp._allowZoom()) { 7667 7668 clearTimeout(openTimeout); 7669 7670 mfp.st.removalDelay = duration; 7671 7672 if(!image) { 7673 image = mfp._getItemToZoom(); 7674 if(!image) { 7675 return; 7676 } 7677 animatedImg = getElToAnimate(image); 7678 } 7679 7680 animatedImg.css( mfp._getOffset(true) ); 7681 mfp.wrap.append(animatedImg); 7682 mfp.content.css('visibility', 'hidden'); 7683 7684 setTimeout(function() { 7685 animatedImg.css( mfp._getOffset() ); 7686 }, 16); 7687 } 7688 7689 }); 7690 7691 _mfpOn(CLOSE_EVENT+ns, function() { 7692 if(mfp._allowZoom()) { 7693 showMainContent(); 7694 if(animatedImg) { 7695 animatedImg.remove(); 7696 } 7697 image = null; 7698 } 7699 }); 7700 }, 7701 7702 _allowZoom: function() { 7703 return mfp.currItem.type === 'image'; 7704 }, 7705 7706 _getItemToZoom: function() { 7707 if(mfp.currItem.hasSize) { 7708 return mfp.currItem.img; 7709 } else { 7710 return false; 7711 } 7712 }, 7713 7714 // Get element postion relative to viewport 7715 _getOffset: function(isLarge) { 7716 var el; 7717 if(isLarge) { 7718 el = mfp.currItem.img; 7719 } else { 7720 el = mfp.st.zoom.opener(mfp.currItem.el || mfp.currItem); 7721 } 7722 7723 var offset = el.offset(); 7724 var paddingTop = parseInt(el.css('padding-top'),10); 7725 var paddingBottom = parseInt(el.css('padding-bottom'),10); 7726 offset.top -= ( $(window).scrollTop() - paddingTop ); 7727 7728 7729 /* 7730 7731 Animating left + top + width/height looks glitchy in Firefox, but perfect in Chrome. And vice-versa. 7732 7733 */ 7734 var obj = { 7735 width: el.width(), 7736 // fix Zepto height+padding issue 7737 height: (_isJQ ? el.innerHeight() : el[0].offsetHeight) - paddingBottom - paddingTop 7738 }; 7739 7740 // I hate to do this, but there is no another option 7741 if( getHasMozTransform() ) { 7742 obj['-moz-transform'] = obj['transform'] = 'translate(' + offset.left + 'px,' + offset.top + 'px)'; 7743 } else { 7744 obj.left = offset.left; 7745 obj.top = offset.top; 7746 } 7747 return obj; 7748 } 7749 7750 } 7751}); 7752 7753 7754 7755/*>>zoom*/ 7756 7757/*>>iframe*/ 7758 7759var IFRAME_NS = 'iframe', 7760 _emptyPage = '//about:blank', 7761 7762 _fixIframeBugs = function(isShowing) { 7763 if(mfp.currTemplate[IFRAME_NS]) { 7764 var el = mfp.currTemplate[IFRAME_NS].find('iframe'); 7765 if(el.length) { 7766 // reset src after the popup is closed to avoid "video keeps playing after popup is closed" bug 7767 if(!isShowing) { 7768 el[0].src = _emptyPage; 7769 } 7770 7771 // IE8 black screen bug fix 7772 if(mfp.isIE8) { 7773 el.css('display', isShowing ? 'block' : 'none'); 7774 } 7775 } 7776 } 7777 }; 7778 7779$.magnificPopup.registerModule(IFRAME_NS, { 7780 7781 options: { 7782 markup: '<div class="mfp-iframe-scaler">'+ 7783 '<div class="mfp-close"></div>'+ 7784 '<iframe class="mfp-iframe ytifra" src="//about:blank" frameborder="0" allowfullscreen allow="accelerometer; autoplay; clipboard-write; encrypted-media;"></iframe>'+ 7785 '</div>', 7786 7787 srcAction: 'iframe_src', 7788 7789 7790 // we don't care and support only one default type of URL by default 7791 patterns: { 7792 youtube: { 7793 index: 'youtube-nocookie.com', 7794 id: 'v=', 7795 src: '//www.youtube-nocookie.com/embed/%id%?autoplay=1&modestbranding=1&playsinline=1&rel=0&showinfo=0' 7796 }, 7797 7798 vimeo: { 7799 index: 'vimeo.com/', 7800 id: '/', 7801 src: '//player.vimeo.com/video/%id%?autoplay=1' 7802 }, 7803 gmaps: { 7804 index: '//maps.google.', 7805 src: '%id%&output=embed' 7806 } 7807 } 7808 }, 7809 7810 proto: { 7811 initIframe: function() { 7812 mfp.types.push(IFRAME_NS); 7813 // console.log("initiframe") 7814 // console.log(mfp) 7815 _mfpOn('BeforeChange', function(e, prevType, newType) { 7816 if(prevType !== newType) { 7817 if(prevType === IFRAME_NS) { 7818 _fixIframeBugs(); // iframe if removed 7819 } else if(newType === IFRAME_NS) { 7820 _fixIframeBugs(true); // iframe is showing 7821 } 7822 }// else { 7823 // iframe source is switched, don't do anything 7824 //} 7825 }); 7826 7827 _mfpOn(CLOSE_EVENT + '.' + IFRAME_NS, function() { 7828 _fixIframeBugs(); 7829 }); 7830 }, 7831 7832 getIframe: function(item, template) { 7833 // console.log("ITEM",item); 7834 // console.log("MFP",mfp); 7835 var embedSrc = item.src; 7836 var iframeSt = mfp.st.iframe; 7837 7838 $.each(iframeSt.patterns, function() { 7839 if(embedSrc.indexOf( this.index ) > -1) { 7840 if(this.id) { 7841 if(typeof this.id === 'string') { 7842 embedSrc = embedSrc.substr(embedSrc.lastIndexOf(this.id)+this.id.length, embedSrc.length); 7843 } else { 7844 embedSrc = this.id.call( this, embedSrc ); 7845 } 7846 } 7847 embedSrc = this.src.replace('%id%', embedSrc ); 7848 console.log("embedSrc", embedSrc); 7849 return false; // break; 7850 } 7851 }); 7852 7853 var dataObj = {}; 7854 if(iframeSt.srcAction) { 7855 dataObj[iframeSt.srcAction] = embedSrc; 7856 } 7857 mfp._parseMarkup(template, dataObj, item); 7858 7859 mfp.updateStatus('ready'); 7860 // console.log(template); 7861 // console.log(dataObj); 7862 // console.log(item); 7863 7864 return template; 7865 } 7866 } 7867}); 7868 7869 7870 7871/*>>iframe*/ 7872 7873/*>>gallery*/ 7874/** 7875 * Get looped index depending on number of slides 7876 */ 7877var _getLoopedId = function(index) { 7878 var numSlides = mfp.items.length; 7879 if(index > numSlides - 1) { 7880 return index - numSlides; 7881 } else if(index < 0) { 7882 return numSlides + index; 7883 } 7884 return index; 7885 }, 7886 _replaceCurrTotal = function(text, curr, total) { 7887 return text.replace(/%curr%/gi, curr + 1).replace(/%total%/gi, total); 7888 }; 7889 7890$.magnificPopup.registerModule('gallery', { 7891 7892 options: { 7893 enabled: false, 7894 arrowMarkup: '<button title="%title%" type="button" class="mfp-arrow mfp-arrow-%dir%"></button>', 7895 preload: [0,2], 7896 navigateByImgClick: true, 7897 arrows: true, 7898 7899 tPrev: 'Previous (Left arrow key)', 7900 tNext: 'Next (Right arrow key)', 7901 tCounter: '%curr% of %total%' 7902 }, 7903 7904 proto: { 7905 initGallery: function() { 7906 7907 var gSt = mfp.st.gallery, 7908 ns = '.mfp-gallery'; 7909 7910 mfp.direction = true; // true - next, false - prev 7911 7912 if(!gSt || !gSt.enabled ) return false; 7913 7914 _wrapClasses += ' mfp-gallery'; 7915 7916 _mfpOn(OPEN_EVENT+ns, function() { 7917 7918 if(gSt.navigateByImgClick) { 7919 mfp.wrap.on('click'+ns, '.mfp-img', function() { 7920 if(mfp.items.length > 1) { 7921 mfp.next(); 7922 return false; 7923 } 7924 }); 7925 } 7926 7927 _document.on('keydown'+ns, function(e) { 7928 if (e.keyCode === 37) { 7929 mfp.prev(); 7930 } else if (e.keyCode === 39) { 7931 mfp.next(); 7932 } 7933 }); 7934 }); 7935 7936 _mfpOn('UpdateStatus'+ns, function(e, data) { 7937 if(data.text) { 7938 data.text = _replaceCurrTotal(data.text, mfp.currItem.index, mfp.items.length); 7939 } 7940 }); 7941 7942 _mfpOn(MARKUP_PARSE_EVENT+ns, function(e, element, values, item) { 7943 var l = mfp.items.length; 7944 values.counter = l > 1 ? _replaceCurrTotal(gSt.tCounter, item.index, l) : ''; 7945 }); 7946 7947 _mfpOn('BuildControls' + ns, function() { 7948 if(mfp.items.length > 1 && gSt.arrows && !mfp.arrowLeft) { 7949 var markup = gSt.arrowMarkup, 7950 arrowLeft = mfp.arrowLeft = $( markup.replace(/%title%/gi, gSt.tPrev).replace(/%dir%/gi, 'left') ).addClass(PREVENT_CLOSE_CLASS), 7951 arrowRight = mfp.arrowRight = $( markup.replace(/%title%/gi, gSt.tNext).replace(/%dir%/gi, 'right') ).addClass(PREVENT_CLOSE_CLASS); 7952 7953 arrowLeft.click(function() { 7954 mfp.prev(); 7955 }); 7956 arrowRight.click(function() { 7957 mfp.next(); 7958 }); 7959 7960 mfp.container.append(arrowLeft.add(arrowRight)); 7961 } 7962 }); 7963 7964 _mfpOn(CHANGE_EVENT+ns, function() { 7965 if(mfp._preloadTimeout) clearTimeout(mfp._preloadTimeout); 7966 7967 mfp._preloadTimeout = setTimeout(function() { 7968 mfp.preloadNearbyImages(); 7969 mfp._preloadTimeout = null; 7970 }, 16); 7971 }); 7972 7973 7974 _mfpOn(CLOSE_EVENT+ns, function() { 7975 _document.off(ns); 7976 mfp.wrap.off('click'+ns); 7977 mfp.arrowRight = mfp.arrowLeft = null; 7978 }); 7979 7980 }, 7981 next: function() { 7982 mfp.direction = true; 7983 mfp.index = _getLoopedId(mfp.index + 1); 7984 mfp.updateItemHTML(); 7985 }, 7986 prev: function() { 7987 mfp.direction = false; 7988 mfp.index = _getLoopedId(mfp.index - 1); 7989 mfp.updateItemHTML(); 7990 }, 7991 goTo: function(newIndex) { 7992 mfp.direction = (newIndex >= mfp.index); 7993 mfp.index = newIndex; 7994 mfp.updateItemHTML(); 7995 }, 7996 preloadNearbyImages: function() { 7997 var p = mfp.st.gallery.preload, 7998 preloadBefore = Math.min(p[0], mfp.items.length), 7999 preloadAfter = Math.min(p[1], mfp.items.length), 8000 i; 8001 8002 for(i = 1; i <= (mfp.direction ? preloadAfter : preloadBefore); i++) { 8003 mfp._preloadItem(mfp.index+i); 8004 } 8005 for(i = 1; i <= (mfp.direction ? preloadBefore : preloadAfter); i++) { 8006 mfp._preloadItem(mfp.index-i); 8007 } 8008 }, 8009 _preloadItem: function(index) { 8010 index = _getLoopedId(index); 8011 8012 if(mfp.items[index].preloaded) { 8013 return; 8014 } 8015 8016 var item = mfp.items[index]; 8017 if(!item.parsed) { 8018 item = mfp.parseEl( index ); 8019 } 8020 8021 _mfpTrigger('LazyLoad', item); 8022 8023 if(item.type === 'image') { 8024 item.img = $('<img class="mfp-img" />').on('load.mfploader', function() { 8025 item.hasSize = true; 8026 }).on('error.mfploader', function() { 8027 item.hasSize = true; 8028 item.loadError = true; 8029 _mfpTrigger('LazyLoadError', item); 8030 }).attr('src', item.src); 8031 } 8032 8033 8034 item.preloaded = true; 8035 } 8036 } 8037}); 8038 8039/*>>gallery*/ 8040 8041/*>>retina*/ 8042 8043var RETINA_NS = 'retina'; 8044 8045$.magnificPopup.registerModule(RETINA_NS, { 8046 options: { 8047 replaceSrc: function(item) { 8048 return item.src.replace(/\.\w+$/, function(m) { return '@2x' + m; }); 8049 }, 8050 ratio: 1 // Function or number. Set to 1 to disable. 8051 }, 8052 proto: { 8053 initRetina: function() { 8054 if(window.devicePixelRatio > 1) { 8055 8056 var st = mfp.st.retina, 8057 ratio = st.ratio; 8058 8059 ratio = !isNaN(ratio) ? ratio : ratio(); 8060 8061 if(ratio > 1) { 8062 _mfpOn('ImageHasSize' + '.' + RETINA_NS, function(e, item) { 8063 item.img.css({ 8064 'max-width': item.img[0].naturalWidth / ratio, 8065 'width': '100%' 8066 }); 8067 }); 8068 _mfpOn('ElementParse' + '.' + RETINA_NS, function(e, item) { 8069 item.src = st.replaceSrc(item, ratio); 8070 }); 8071 } 8072 } 8073 8074 } 8075 } 8076}); 8077 8078/*>>retina*/ 8079 _checkInstance(); }));
vendor: 95,083 bytes, lines 8080-11486
8080/*! 8081 * Flickity PACKAGED v3.0.0 8082 * Touch, responsive, flickable carousels 8083 * 8084 * Licensed GPLv3 for open source use 8085 * or Flickity Commercial License for commercial use 8086 * 8087 * https://flickity.metafizzy.co 8088 * Copyright 2015-2022 Metafizzy 8089 */ 8090 8091/** 8092 * Bridget makes jQuery widgets 8093 * v3.0.1 8094 * MIT license 8095 */ 8096 8097( function( window, factory ) { 8098 // module definition 8099 if ( typeof module == 'object' && module.exports ) { 8100 // CommonJS 8101 module.exports = factory( 8102 window, 8103 require('jquery'), 8104 ); 8105 } else { 8106 // browser global 8107 window.jQueryBridget = factory( 8108 window, 8109 window.jQuery, 8110 ); 8111 } 8112 8113}( window, function factory( window, jQuery ) { 8114 8115// ----- utils ----- // 8116 8117// helper function for logging errors 8118// $.error breaks jQuery chaining 8119let console = window.console; 8120let logError = typeof console == 'undefined' ? function() {} : 8121 function( message ) { 8122 console.error( message ); 8123 }; 8124 8125// ----- jQueryBridget ----- // 8126 8127function jQueryBridget( namespace, PluginClass, $ ) { 8128 $ = $ || jQuery || window.jQuery; 8129 if ( !$ ) { 8130 return; 8131 } 8132 8133 // add option method -> $().plugin('option', {...}) 8134 if ( !PluginClass.prototype.option ) { 8135 // option setter 8136 PluginClass.prototype.option = function( opts ) { 8137 if ( !opts ) return; 8138 8139 this.options = Object.assign( this.options || {}, opts ); 8140 }; 8141 } 8142 8143 // make jQuery plugin 8144 $.fn[ namespace ] = function( arg0, ...args ) { 8145 if ( typeof arg0 == 'string' ) { 8146 // method call $().plugin( 'methodName', { options } ) 8147 return methodCall( this, arg0, args ); 8148 } 8149 // just $().plugin({ options }) 8150 plainCall( this, arg0 ); 8151 return this; 8152 }; 8153 8154 // $().plugin('methodName') 8155 function methodCall( $elems, methodName, args ) { 8156 let returnValue; 8157 let pluginMethodStr = `$().${namespace}("${methodName}")`; 8158 8159 $elems.each( function( i, elem ) { 8160 // get instance 8161 let instance = $.data( elem, namespace ); 8162 if ( !instance ) { 8163 logError( `${namespace} not initialized.` + 8164 ` Cannot call method ${pluginMethodStr}` ); 8165 return; 8166 } 8167 8168 let method = instance[ methodName ]; 8169 if ( !method || methodName.charAt( 0 ) == '_' ) { 8170 logError(`${pluginMethodStr} is not a valid method`); 8171 return; 8172 } 8173 8174 // apply method, get return value 8175 let value = method.apply( instance, args ); 8176 // set return value if value is returned, use only first value 8177 returnValue = returnValue === undefined ? value : returnValue; 8178 } ); 8179 8180 return returnValue !== undefined ? returnValue : $elems; 8181 } 8182 8183 function plainCall( $elems, options ) { 8184 $elems.each( function( i, elem ) { 8185 let instance = $.data( elem, namespace ); 8186 if ( instance ) { 8187 // set options & init 8188 instance.option( options ); 8189 instance._init(); 8190 } else { 8191 // initialize new instance 8192 instance = new PluginClass( elem, options ); 8193 $.data( elem, namespace, instance ); 8194 } 8195 } ); 8196 } 8197 8198} 8199 8200// ----- ----- // 8201 8202return jQueryBridget; 8203 8204} ) ); 8205/** 8206 * EvEmitter v2.1.1 8207 * Lil' event emitter 8208 * MIT License 8209 */ 8210 8211( function( global, factory ) { 8212 // universal module definition 8213 if ( typeof module == 'object' && module.exports ) { 8214 // CommonJS - Browserify, Webpack 8215 module.exports = factory(); 8216 } else { 8217 // Browser globals 8218 global.EvEmitter = factory(); 8219 } 8220 8221}( typeof window != 'undefined' ? window : this, function() { 8222 8223function EvEmitter() {} 8224 8225let proto = EvEmitter.prototype; 8226 8227proto.on = function( eventName, listener ) { 8228 if ( !eventName || !listener ) return this; 8229 8230 // set events hash 8231 let events = this._events = this._events || {}; 8232 // set listeners array 8233 let listeners = events[ eventName ] = events[ eventName ] || []; 8234 // only add once 8235 if ( !listeners.includes( listener ) ) { 8236 listeners.push( listener ); 8237 } 8238 8239 return this; 8240}; 8241 8242proto.once = function( eventName, listener ) { 8243 if ( !eventName || !listener ) return this; 8244 8245 // add event 8246 this.on( eventName, listener ); 8247 // set once flag 8248 // set onceEvents hash 8249 let onceEvents = this._onceEvents = this._onceEvents || {}; 8250 // set onceListeners object 8251 let onceListeners = onceEvents[ eventName ] = onceEvents[ eventName ] || {}; 8252 // set flag 8253 onceListeners[ listener ] = true; 8254 8255 return this; 8256}; 8257 8258proto.off = function( eventName, listener ) { 8259 let listeners = this._events && this._events[ eventName ]; 8260 if ( !listeners || !listeners.length ) return this; 8261 8262 let index = listeners.indexOf( listener ); 8263 if ( index != -1 ) { 8264 listeners.splice( index, 1 ); 8265 } 8266 8267 return this; 8268}; 8269 8270proto.emitEvent = function( eventName, args ) { 8271 let listeners = this._events && this._events[ eventName ]; 8272 if ( !listeners || !listeners.length ) return this; 8273 8274 // copy over to avoid interference if .off() in listener 8275 listeners = listeners.slice( 0 ); 8276 args = args || []; 8277 // once stuff 8278 let onceListeners = this._onceEvents && this._onceEvents[ eventName ]; 8279 8280 for ( let listener of listeners ) { 8281 let isOnce = onceListeners && onceListeners[ listener ]; 8282 if ( isOnce ) { 8283 // remove listener 8284 // remove before trigger to prevent recursion 8285 this.off( eventName, listener ); 8286 // unset once flag 8287 delete onceListeners[ listener ]; 8288 } 8289 // trigger listener 8290 listener.apply( this, args ); 8291 } 8292 8293 return this; 8294}; 8295 8296proto.allOff = function() { 8297 delete this._events; 8298 delete this._onceEvents; 8299 return this; 8300}; 8301 8302return EvEmitter; 8303 8304} ) ); 8305/*! 8306 * Infinite Scroll v2.0.4 8307 * measure size of elements 8308 * MIT license 8309 */ 8310 8311( function( window, factory ) { 8312 if ( typeof module == 'object' && module.exports ) { 8313 // CommonJS 8314 module.exports = factory(); 8315 } else { 8316 // browser global 8317 window.getSize = factory(); 8318 } 8319 8320} )( window, function factory() { 8321 8322// -------------------------- helpers -------------------------- // 8323 8324// get a number from a string, not a percentage 8325function getStyleSize( value ) { 8326 let num = parseFloat( value ); 8327 // not a percent like '100%', and a number 8328 let isValid = value.indexOf('%') == -1 && !isNaN( num ); 8329 return isValid && num; 8330} 8331 8332// -------------------------- measurements -------------------------- // 8333 8334let measurements = [ 8335 'paddingLeft', 8336 'paddingRight', 8337 'paddingTop', 8338 'paddingBottom', 8339 'marginLeft', 8340 'marginRight', 8341 'marginTop', 8342 'marginBottom', 8343 'borderLeftWidth', 8344 'borderRightWidth', 8345 'borderTopWidth', 8346 'borderBottomWidth', 8347]; 8348 8349let measurementsLength = measurements.length; 8350 8351function getZeroSize() { 8352 let size = { 8353 width: 0, 8354 height: 0, 8355 innerWidth: 0, 8356 innerHeight: 0, 8357 outerWidth: 0, 8358 outerHeight: 0, 8359 }; 8360 measurements.forEach( ( measurement ) => { 8361 size[ measurement ] = 0; 8362 } ); 8363 return size; 8364} 8365 8366// -------------------------- getSize -------------------------- // 8367 8368function getSize( elem ) { 8369 // use querySeletor if elem is string 8370 if ( typeof elem == 'string' ) elem = document.querySelector( elem ); 8371 8372 // do not proceed on non-objects 8373 let isElement = elem && typeof elem == 'object' && elem.nodeType; 8374 if ( !isElement ) return; 8375 8376 let style = getComputedStyle( elem ); 8377 8378 // if hidden, everything is 0 8379 if ( style.display == 'none' ) return getZeroSize(); 8380 8381 let size = {}; 8382 size.width = elem.offsetWidth; 8383 size.height = elem.offsetHeight; 8384 8385 let isBorderBox = size.isBorderBox = style.boxSizing == 'border-box'; 8386 8387 // get all measurements 8388 measurements.forEach( ( measurement ) => { 8389 let value = style[ measurement ]; 8390 let num = parseFloat( value ); 8391 // any 'auto', 'medium' value will be 0 8392 size[ measurement ] = !isNaN( num ) ? num : 0; 8393 } ); 8394 8395 let paddingWidth = size.paddingLeft + size.paddingRight; 8396 let paddingHeight = size.paddingTop + size.paddingBottom; 8397 let marginWidth = size.marginLeft + size.marginRight; 8398 let marginHeight = size.marginTop + size.marginBottom; 8399 let borderWidth = size.borderLeftWidth + size.borderRightWidth; 8400 let borderHeight = size.borderTopWidth + size.borderBottomWidth; 8401 8402 // overwrite width and height if we can get it from style 8403 let styleWidth = getStyleSize( style.width ); 8404 if ( styleWidth !== false ) { 8405 size.width = styleWidth + 8406 // add padding and border unless it's already including it 8407 ( isBorderBox ? 0 : paddingWidth + borderWidth ); 8408 } 8409 8410 let styleHeight = getStyleSize( style.height ); 8411 if ( styleHeight !== false ) { 8412 size.height = styleHeight + 8413 // add padding and border unless it's already including it 8414 ( isBorderBox ? 0 : paddingHeight + borderHeight ); 8415 } 8416 8417 size.innerWidth = size.width - ( paddingWidth + borderWidth ); 8418 size.innerHeight = size.height - ( paddingHeight + borderHeight ); 8419 8420 size.outerWidth = size.width + marginWidth; 8421 size.outerHeight = size.height + marginHeight; 8422 8423 return size; 8424} 8425 8426return getSize; 8427 8428} ); 8429/** 8430 * Fizzy UI utils v3.0.0 8431 * MIT license 8432 */ 8433 8434( function( global, factory ) { 8435 // universal module definition 8436 if ( typeof module == 'object' && module.exports ) { 8437 // CommonJS 8438 module.exports = factory( global ); 8439 } else { 8440 // browser global 8441 global.fizzyUIUtils = factory( global ); 8442 } 8443 8444}( this, function factory( global ) { 8445 8446let utils = {}; 8447 8448// ----- extend ----- // 8449 8450// extends objects 8451utils.extend = function( a, b ) { 8452 return Object.assign( a, b ); 8453}; 8454 8455// ----- modulo ----- // 8456 8457utils.modulo = function( num, div ) { 8458 return ( ( num % div ) + div ) % div; 8459}; 8460 8461// ----- makeArray ----- // 8462 8463// turn element or nodeList into an array 8464utils.makeArray = function( obj ) { 8465 // use object if already an array 8466 if ( Array.isArray( obj ) ) return obj; 8467 8468 // return empty array if undefined or null. #6 8469 if ( obj === null || obj === undefined ) return []; 8470 8471 let isArrayLike = typeof obj == 'object' && typeof obj.length == 'number'; 8472 // convert nodeList to array 8473 if ( isArrayLike ) return [ ...obj ]; 8474 8475 // array of single index 8476 return [ obj ]; 8477}; 8478 8479// ----- removeFrom ----- // 8480 8481utils.removeFrom = function( ary, obj ) { 8482 let index = ary.indexOf( obj ); 8483 if ( index != -1 ) { 8484 ary.splice( index, 1 ); 8485 } 8486}; 8487 8488// ----- getParent ----- // 8489 8490utils.getParent = function( elem, selector ) { 8491 while ( elem.parentNode && elem != document.body ) { 8492 elem = elem.parentNode; 8493 if ( elem.matches( selector ) ) return elem; 8494 } 8495}; 8496 8497// ----- getQueryElement ----- // 8498 8499// use element as selector string 8500utils.getQueryElement = function( elem ) { 8501 if ( typeof elem == 'string' ) { 8502 return document.querySelector( elem ); 8503 } 8504 return elem; 8505}; 8506 8507// ----- handleEvent ----- // 8508 8509// enable .ontype to trigger from .addEventListener( elem, 'type' ) 8510utils.handleEvent = function( event ) { 8511 let method = 'on' + event.type; 8512 if ( this[ method ] ) { 8513 this[ method ]( event ); 8514 } 8515}; 8516 8517// ----- filterFindElements ----- // 8518 8519utils.filterFindElements = function( elems, selector ) { 8520 // make array of elems 8521 elems = utils.makeArray( elems ); 8522 8523 return elems 8524 // check that elem is an actual element 8525 .filter( ( elem ) => elem instanceof HTMLElement ) 8526 .reduce( ( ffElems, elem ) => { 8527 // add elem if no selector 8528 if ( !selector ) { 8529 ffElems.push( elem ); 8530 return ffElems; 8531 } 8532 // filter & find items if we have a selector 8533 // filter 8534 if ( elem.matches( selector ) ) { 8535 ffElems.push( elem ); 8536 } 8537 // find children 8538 let childElems = elem.querySelectorAll( selector ); 8539 // concat childElems to filterFound array 8540 ffElems = ffElems.concat( ...childElems ); 8541 return ffElems; 8542 }, [] ); 8543}; 8544 8545// ----- debounceMethod ----- // 8546 8547utils.debounceMethod = function( _class, methodName, threshold ) { 8548 threshold = threshold || 100; 8549 // original method 8550 let method = _class.prototype[ methodName ]; 8551 let timeoutName = methodName + 'Timeout'; 8552 8553 _class.prototype[ methodName ] = function() { 8554 clearTimeout( this[ timeoutName ] ); 8555 8556 let args = arguments; 8557 this[ timeoutName ] = setTimeout( () => { 8558 method.apply( this, args ); 8559 delete this[ timeoutName ]; 8560 }, threshold ); 8561 }; 8562}; 8563 8564// ----- docReady ----- // 8565 8566utils.docReady = function( onDocReady ) { 8567 let readyState = document.readyState; 8568 if ( readyState == 'complete' || readyState == 'interactive' ) { 8569 // do async to allow for other scripts to run. metafizzy/flickity#441 8570 setTimeout( onDocReady ); 8571 } else { 8572 document.addEventListener( 'DOMContentLoaded', onDocReady ); 8573 } 8574}; 8575 8576// ----- htmlInit ----- // 8577 8578// http://bit.ly/3oYLusc 8579utils.toDashed = function( str ) { 8580 return str.replace( /(.)([A-Z])/g, function( match, $1, $2 ) { 8581 return $1 + '-' + $2; 8582 } ).toLowerCase(); 8583}; 8584 8585let console = global.console; 8586 8587// allow user to initialize classes via [data-namespace] or .js-namespace class 8588// htmlInit( Widget, 'widgetName' ) 8589// options are parsed from data-namespace-options 8590utils.htmlInit = function( WidgetClass, namespace ) { 8591 utils.docReady( function() { 8592 let dashedNamespace = utils.toDashed( namespace ); 8593 let dataAttr = 'data-' + dashedNamespace; 8594 let dataAttrElems = document.querySelectorAll( `[${dataAttr}]` ); 8595 let jQuery = global.jQuery; 8596 8597 [ ...dataAttrElems ].forEach( ( elem ) => { 8598 let attr = elem.getAttribute( dataAttr ); 8599 let options; 8600 try { 8601 options = attr && JSON.parse( attr ); 8602 } catch ( error ) { 8603 // log error, do not initialize 8604 if ( console ) { 8605 console.error( `Error parsing ${dataAttr} on ${elem.className}: ${error}` ); 8606 } 8607 return; 8608 } 8609 // initialize 8610 let instance = new WidgetClass( elem, options ); 8611 // make available via $().data('namespace') 8612 if ( jQuery ) { 8613 jQuery.data( elem, namespace, instance ); 8614 } 8615 } ); 8616 8617 } ); 8618}; 8619 8620// ----- ----- // 8621 8622return utils; 8623 8624} ) ); 8625/*! 8626 * Unidragger v3.0.0 8627 * Draggable base class 8628 * MIT license 8629 */ 8630 8631( function( window, factory ) { 8632 // universal module definition 8633 if ( typeof module == 'object' && module.exports ) { 8634 // CommonJS 8635 module.exports = factory( 8636 window, 8637 require('ev-emitter'), 8638 ); 8639 } else { 8640 // browser global 8641 window.Unidragger = factory( 8642 window, 8643 window.EvEmitter, 8644 ); 8645 } 8646 8647}( typeof window != 'undefined' ? window : this, function factory( window, EvEmitter ) { 8648 8649function Unidragger() {} 8650 8651// inherit EvEmitter 8652let proto = Unidragger.prototype = Object.create( EvEmitter.prototype ); 8653 8654// ----- bind start ----- // 8655 8656// trigger handler methods for events 8657proto.handleEvent = function( event ) { 8658 let method = 'on' + event.type; 8659 if ( this[ method ] ) { 8660 this[ method ]( event ); 8661 } 8662}; 8663 8664let startEvent, activeEvents; 8665if ( 'ontouchstart' in window ) { 8666 // HACK prefer Touch Events as you can preventDefault on touchstart to 8667 // disable scroll in iOS & mobile Chrome metafizzy/flickity#1177 8668 startEvent = 'touchstart'; 8669 activeEvents = [ 'touchmove', 'touchend', 'touchcancel' ]; 8670} else if ( window.PointerEvent ) { 8671 // Pointer Events 8672 startEvent = 'pointerdown'; 8673 activeEvents = [ 'pointermove', 'pointerup', 'pointercancel' ]; 8674} else { 8675 // mouse events 8676 startEvent = 'mousedown'; 8677 activeEvents = [ 'mousemove', 'mouseup' ]; 8678} 8679 8680// prototype so it can be overwriteable by Flickity 8681proto.touchActionValue = 'none'; 8682 8683proto.bindHandles = function() { 8684 this._bindHandles( 'addEventListener', this.touchActionValue ); 8685}; 8686 8687proto.unbindHandles = function() { 8688 this._bindHandles( 'removeEventListener', '' ); 8689}; 8690 8691/** 8692 * Add or remove start event 8693 * @param {String} bindMethod - addEventListener or removeEventListener 8694 * @param {String} touchAction - value for touch-action CSS property 8695 */ 8696proto._bindHandles = function( bindMethod, touchAction ) { 8697 this.handles.forEach( ( handle ) => { 8698 handle[ bindMethod ]( startEvent, this ); 8699 handle[ bindMethod ]( 'click', this ); 8700 // touch-action: none to override browser touch gestures. metafizzy/flickity#540 8701 if ( window.PointerEvent ) handle.style.touchAction = touchAction; 8702 } ); 8703}; 8704 8705proto.bindActivePointerEvents = function() { 8706 activeEvents.forEach( ( eventName ) => { 8707 window.addEventListener( eventName, this ); 8708 } ); 8709}; 8710 8711proto.unbindActivePointerEvents = function() { 8712 activeEvents.forEach( ( eventName ) => { 8713 window.removeEventListener( eventName, this ); 8714 } ); 8715}; 8716 8717// ----- event handler helpers ----- // 8718 8719// trigger method with matching pointer 8720proto.withPointer = function( methodName, event ) { 8721 if ( event.pointerId == this.pointerIdentifier ) { 8722 this[ methodName ]( event, event ); 8723 } 8724}; 8725 8726// trigger method with matching touch 8727proto.withTouch = function( methodName, event ) { 8728 let touch; 8729 for ( let changedTouch of event.changedTouches ) { 8730 if ( changedTouch.identifier == this.pointerIdentifier ) { 8731 touch = changedTouch; 8732 } 8733 } 8734 if ( touch ) this[ methodName ]( event, touch ); 8735}; 8736 8737// ----- start event ----- // 8738 8739proto.onmousedown = function( event ) { 8740 this.pointerDown( event, event ); 8741}; 8742 8743proto.ontouchstart = function( event ) { 8744 this.pointerDown( event, event.changedTouches[0] ); 8745}; 8746 8747proto.onpointerdown = function( event ) { 8748 this.pointerDown( event, event ); 8749}; 8750 8751// nodes that have text fields 8752const cursorNodes = [ 'TEXTAREA', 'INPUT', 'SELECT', 'OPTION' ]; 8753// input types that do not have text fields 8754const clickTypes = [ 'radio', 'checkbox', 'button', 'submit', 'image', 'file' ]; 8755 8756/** 8757 * any time you set `event, pointer` it refers to: 8758 * @param {Event} event 8759 * @param {Event | Touch} pointer 8760 */ 8761proto.pointerDown = function( event, pointer ) { 8762 // dismiss multi-touch taps, right clicks, and clicks on text fields 8763 let isCursorNode = cursorNodes.includes( event.target.nodeName ); 8764 let isClickType = clickTypes.includes( event.target.type ); 8765 let isOkayElement = !isCursorNode || isClickType; 8766 let isOkay = !this.isPointerDown && !event.button && isOkayElement; 8767 if ( !isOkay ) return; 8768 8769 this.isPointerDown = true; 8770 // save pointer identifier to match up touch events 8771 this.pointerIdentifier = pointer.pointerId !== undefined ? 8772 // pointerId for pointer events, touch.indentifier for touch events 8773 pointer.pointerId : pointer.identifier; 8774 // track position for move 8775 this.pointerDownPointer = { 8776 pageX: pointer.pageX, 8777 pageY: pointer.pageY, 8778 }; 8779 8780 this.bindActivePointerEvents(); 8781 this.emitEvent( 'pointerDown', [ event, pointer ] ); 8782}; 8783 8784// ----- move ----- // 8785 8786proto.onmousemove = function( event ) { 8787 this.pointerMove( event, event ); 8788}; 8789 8790proto.onpointermove = function( event ) { 8791 this.withPointer( 'pointerMove', event ); 8792}; 8793 8794proto.ontouchmove = function( event ) { 8795 this.withTouch( 'pointerMove', event ); 8796}; 8797 8798proto.pointerMove = function( event, pointer ) { 8799 let moveVector = { 8800 x: pointer.pageX - this.pointerDownPointer.pageX, 8801 y: pointer.pageY - this.pointerDownPointer.pageY, 8802 }; 8803 this.emitEvent( 'pointerMove', [ event, pointer, moveVector ] ); 8804 // start drag if pointer has moved far enough to start drag 8805 let isDragStarting = !this.isDragging && this.hasDragStarted( moveVector ); 8806 if ( isDragStarting ) this.dragStart( event, pointer ); 8807 if ( this.isDragging ) this.dragMove( event, pointer, moveVector ); 8808}; 8809 8810// condition if pointer has moved far enough to start drag 8811proto.hasDragStarted = function( moveVector ) { 8812 return Math.abs( moveVector.x ) > 3 || Math.abs( moveVector.y ) > 3; 8813}; 8814 8815// ----- drag ----- // 8816 8817proto.dragStart = function( event, pointer ) { 8818 this.isDragging = true; 8819 this.isPreventingClicks = true; // set flag to prevent clicks 8820 this.emitEvent( 'dragStart', [ event, pointer ] ); 8821}; 8822 8823proto.dragMove = function( event, pointer, moveVector ) { 8824 this.emitEvent( 'dragMove', [ event, pointer, moveVector ] ); 8825}; 8826 8827// ----- end ----- // 8828 8829proto.onmouseup = function( event ) { 8830 this.pointerUp( event, event ); 8831}; 8832 8833proto.onpointerup = function( event ) { 8834 this.withPointer( 'pointerUp', event ); 8835}; 8836 8837proto.ontouchend = function( event ) { 8838 this.withTouch( 'pointerUp', event ); 8839}; 8840 8841proto.pointerUp = function( event, pointer ) { 8842 this.pointerDone(); 8843 this.emitEvent( 'pointerUp', [ event, pointer ] ); 8844 8845 if ( this.isDragging ) { 8846 this.dragEnd( event, pointer ); 8847 } else { 8848 // pointer didn't move enough for drag to start 8849 this.staticClick( event, pointer ); 8850 } 8851}; 8852 8853proto.dragEnd = function( event, pointer ) { 8854 this.isDragging = false; // reset flag 8855 // re-enable clicking async 8856 setTimeout( () => delete this.isPreventingClicks ); 8857 8858 this.emitEvent( 'dragEnd', [ event, pointer ] ); 8859}; 8860 8861// triggered on pointer up & pointer cancel 8862proto.pointerDone = function() { 8863 this.isPointerDown = false; 8864 delete this.pointerIdentifier; 8865 this.unbindActivePointerEvents(); 8866 this.emitEvent('pointerDone'); 8867}; 8868 8869// ----- cancel ----- // 8870 8871proto.onpointercancel = function( event ) { 8872 this.withPointer( 'pointerCancel', event ); 8873}; 8874 8875proto.ontouchcancel = function( event ) { 8876 this.withTouch( 'pointerCancel', event ); 8877}; 8878 8879proto.pointerCancel = function( event, pointer ) { 8880 this.pointerDone(); 8881 this.emitEvent( 'pointerCancel', [ event, pointer ] ); 8882}; 8883 8884// ----- click ----- // 8885 8886// handle all clicks and prevent clicks when dragging 8887proto.onclick = function( event ) { 8888 if ( this.isPreventingClicks ) event.preventDefault(); 8889}; 8890 8891// triggered after pointer down & up with no/tiny movement 8892proto.staticClick = function( event, pointer ) { 8893 // ignore emulated mouse up clicks 8894 let isMouseup = event.type == 'mouseup'; 8895 if ( isMouseup && this.isIgnoringMouseUp ) return; 8896 8897 this.emitEvent( 'staticClick', [ event, pointer ] ); 8898 8899 // set flag for emulated clicks 300ms after touchend 8900 if ( isMouseup ) { 8901 this.isIgnoringMouseUp = true; 8902 // reset flag after 400ms 8903 setTimeout( () => { 8904 delete this.isIgnoringMouseUp; 8905 }, 400 ); 8906 } 8907}; 8908 8909// ----- ----- // 8910 8911return Unidragger; 8912 8913} ) ); 8914/*! 8915 * imagesLoaded v5.0.0 8916 * JavaScript is all like "You images are done yet or what?" 8917 * MIT License 8918 */ 8919 8920( function( window, factory ) { 8921 // universal module definition 8922 if ( typeof module == 'object' && module.exports ) { 8923 // CommonJS 8924 module.exports = factory( window, require('ev-emitter') ); 8925 } else { 8926 // browser global 8927 window.imagesLoaded = factory( window, window.EvEmitter ); 8928 } 8929 8930} )( typeof window !== 'undefined' ? window : this, 8931 function factory( window, EvEmitter ) { 8932 8933let $ = window.jQuery; 8934let console = window.console; 8935 8936// -------------------------- helpers -------------------------- // 8937 8938// turn element or nodeList into an array 8939function makeArray( obj ) { 8940 // use object if already an array 8941 if ( Array.isArray( obj ) ) return obj; 8942 8943 let isArrayLike = typeof obj == 'object' && typeof obj.length == 'number'; 8944 // convert nodeList to array 8945 if ( isArrayLike ) return [ ...obj ]; 8946 8947 // array of single index 8948 return [ obj ]; 8949} 8950 8951// -------------------------- imagesLoaded -------------------------- // 8952 8953/** 8954 * @param {[Array, Element, NodeList, String]} elem 8955 * @param {[Object, Function]} options - if function, use as callback 8956 * @param {Function} onAlways - callback function 8957 * @returns {ImagesLoaded} 8958 */ 8959function ImagesLoaded( elem, options, onAlways ) { 8960 // coerce ImagesLoaded() without new, to be new ImagesLoaded() 8961 if ( !( this instanceof ImagesLoaded ) ) { 8962 return new ImagesLoaded( elem, options, onAlways ); 8963 } 8964 // use elem as selector string 8965 let queryElem = elem; 8966 if ( typeof elem == 'string' ) { 8967 queryElem = document.querySelectorAll( elem ); 8968 } 8969 // bail if bad element 8970 if ( !queryElem ) { 8971 console.error(`Bad element for imagesLoaded ${queryElem || elem}`); 8972 return; 8973 } 8974 8975 this.elements = makeArray( queryElem ); 8976 this.options = {}; 8977 // shift arguments if no options set 8978 if ( typeof options == 'function' ) { 8979 onAlways = options; 8980 } else { 8981 Object.assign( this.options, options ); 8982 } 8983 8984 if ( onAlways ) this.on( 'always', onAlways ); 8985 8986 this.getImages(); 8987 // add jQuery Deferred object 8988 if ( $ ) this.jqDeferred = new $.Deferred(); 8989 8990 // HACK check async to allow time to bind listeners 8991 setTimeout( this.check.bind( this ) ); 8992} 8993 8994ImagesLoaded.prototype = Object.create( EvEmitter.prototype ); 8995 8996ImagesLoaded.prototype.getImages = function() { 8997 this.images = []; 8998 8999 // filter & find items if we have an item selector 9000 this.elements.forEach( this.addElementImages, this ); 9001}; 9002 9003const elementNodeTypes = [ 1, 9, 11 ]; 9004 9005/** 9006 * @param {Node} elem 9007 */ 9008ImagesLoaded.prototype.addElementImages = function( elem ) { 9009 // filter siblings 9010 if ( elem.nodeName === 'IMG' ) { 9011 this.addImage( elem ); 9012 } 9013 // get background image on element 9014 if ( this.options.background === true ) { 9015 this.addElementBackgroundImages( elem ); 9016 } 9017 9018 // find children 9019 // no non-element nodes, #143 9020 let { nodeType } = elem; 9021 if ( !nodeType || !elementNodeTypes.includes( nodeType ) ) return; 9022 9023 let childImgs = elem.querySelectorAll('img'); 9024 // concat childElems to filterFound array 9025 for ( let img of childImgs ) { 9026 this.addImage( img ); 9027 } 9028 9029 // get child background images 9030 if ( typeof this.options.background == 'string' ) { 9031 let children = elem.querySelectorAll( this.options.background ); 9032 for ( let child of children ) { 9033 this.addElementBackgroundImages( child ); 9034 } 9035 } 9036}; 9037 9038const reURL = /url\((['"])?(.*?)\1\)/gi; 9039 9040ImagesLoaded.prototype.addElementBackgroundImages = function( elem ) { 9041 let style = getComputedStyle( elem ); 9042 // Firefox returns null if in a hidden iframe https://bugzil.la/548397 9043 if ( !style ) return; 9044 9045 // get url inside url("...") 9046 let matches = reURL.exec( style.backgroundImage ); 9047 while ( matches !== null ) { 9048 let url = matches && matches[2]; 9049 if ( url ) { 9050 this.addBackground( url, elem ); 9051 } 9052 matches = reURL.exec( style.backgroundImage ); 9053 } 9054}; 9055 9056/** 9057 * @param {Image} img 9058 */ 9059ImagesLoaded.prototype.addImage = function( img ) { 9060 let loadingImage = new LoadingImage( img ); 9061 this.images.push( loadingImage ); 9062}; 9063 9064ImagesLoaded.prototype.addBackground = function( url, elem ) { 9065 let background = new Background( url, elem ); 9066 this.images.push( background ); 9067}; 9068 9069ImagesLoaded.prototype.check = function() { 9070 this.progressedCount = 0; 9071 this.hasAnyBroken = false; 9072 // complete if no images 9073 if ( !this.images.length ) { 9074 this.complete(); 9075 return; 9076 } 9077 9078 /* eslint-disable-next-line func-style */ 9079 let onProgress = ( image, elem, message ) => { 9080 // HACK - Chrome triggers event before object properties have changed. #83 9081 setTimeout( () => { 9082 this.progress( image, elem, message ); 9083 } ); 9084 }; 9085 9086 this.images.forEach( function( loadingImage ) { 9087 loadingImage.once( 'progress', onProgress ); 9088 loadingImage.check(); 9089 } ); 9090}; 9091 9092ImagesLoaded.prototype.progress = function( image, elem, message ) { 9093 this.progressedCount++; 9094 this.hasAnyBroken = this.hasAnyBroken || !image.isLoaded; 9095 // progress event 9096 this.emitEvent( 'progress', [ this, image, elem ] ); 9097 if ( this.jqDeferred && this.jqDeferred.notify ) { 9098 this.jqDeferred.notify( this, image ); 9099 } 9100 // check if completed 9101 if ( this.progressedCount === this.images.length ) { 9102 this.complete(); 9103 } 9104 9105 if ( this.options.debug && console ) { 9106 console.log( `progress: ${message}`, image, elem ); 9107 } 9108}; 9109 9110ImagesLoaded.prototype.complete = function() { 9111 let eventName = this.hasAnyBroken ? 'fail' : 'done'; 9112 this.isComplete = true; 9113 this.emitEvent( eventName, [ this ] ); 9114 this.emitEvent( 'always', [ this ] ); 9115 if ( this.jqDeferred ) { 9116 let jqMethod = this.hasAnyBroken ? 'reject' : 'resolve'; 9117 this.jqDeferred[ jqMethod ]( this ); 9118 } 9119}; 9120 9121// -------------------------- -------------------------- // 9122 9123function LoadingImage( img ) { 9124 this.img = img; 9125} 9126 9127LoadingImage.prototype = Object.create( EvEmitter.prototype ); 9128 9129LoadingImage.prototype.check = function() { 9130 // If complete is true and browser supports natural sizes, 9131 // try to check for image status manually. 9132 let isComplete = this.getIsImageComplete(); 9133 if ( isComplete ) { 9134 // report based on naturalWidth 9135 this.confirm( this.img.naturalWidth !== 0, 'naturalWidth' ); 9136 return; 9137 } 9138 9139 // If none of the checks above matched, simulate loading on detached element. 9140 this.proxyImage = new Image(); 9141 // add crossOrigin attribute. #204 9142 if ( this.img.crossOrigin ) { 9143 this.proxyImage.crossOrigin = this.img.crossOrigin; 9144 } 9145 this.proxyImage.addEventListener( 'load', this ); 9146 this.proxyImage.addEventListener( 'error', this ); 9147 // bind to image as well for Firefox. #191 9148 this.img.addEventListener( 'load', this ); 9149 this.img.addEventListener( 'error', this ); 9150 this.proxyImage.src = this.img.currentSrc || this.img.src; 9151}; 9152 9153LoadingImage.prototype.getIsImageComplete = function() { 9154 // check for non-zero, non-undefined naturalWidth 9155 // fixes Safari+InfiniteScroll+Masonry bug infinite-scroll#671 9156 return this.img.complete && this.img.naturalWidth; 9157}; 9158 9159LoadingImage.prototype.confirm = function( isLoaded, message ) { 9160 this.isLoaded = isLoaded; 9161 let { parentNode } = this.img; 9162 // emit progress with parent <picture> or self <img> 9163 let elem = parentNode.nodeName === 'PICTURE' ? parentNode : this.img; 9164 this.emitEvent( 'progress', [ this, elem, message ] ); 9165}; 9166 9167// ----- events ----- // 9168 9169// trigger specified handler for event type 9170LoadingImage.prototype.handleEvent = function( event ) { 9171 let method = 'on' + event.type; 9172 if ( this[ method ] ) { 9173 this[ method ]( event ); 9174 } 9175}; 9176 9177LoadingImage.prototype.onload = function() { 9178 this.confirm( true, 'onload' ); 9179 this.unbindEvents(); 9180}; 9181 9182LoadingImage.prototype.onerror = function() { 9183 this.confirm( false, 'onerror' ); 9184 this.unbindEvents(); 9185}; 9186 9187LoadingImage.prototype.unbindEvents = function() { 9188 this.proxyImage.removeEventListener( 'load', this ); 9189 this.proxyImage.removeEventListener( 'error', this ); 9190 this.img.removeEventListener( 'load', this ); 9191 this.img.removeEventListener( 'error', this ); 9192}; 9193 9194// -------------------------- Background -------------------------- // 9195 9196function Background( url, element ) { 9197 this.url = url; 9198 this.element = element; 9199 this.img = new Image(); 9200} 9201 9202// inherit LoadingImage prototype 9203Background.prototype = Object.create( LoadingImage.prototype ); 9204 9205Background.prototype.check = function() { 9206 this.img.addEventListener( 'load', this ); 9207 this.img.addEventListener( 'error', this ); 9208 this.img.src = this.url; 9209 // check if image is already complete 9210 let isComplete = this.getIsImageComplete(); 9211 if ( isComplete ) { 9212 this.confirm( this.img.naturalWidth !== 0, 'naturalWidth' ); 9213 this.unbindEvents(); 9214 } 9215}; 9216 9217Background.prototype.unbindEvents = function() { 9218 this.img.removeEventListener( 'load', this ); 9219 this.img.removeEventListener( 'error', this ); 9220}; 9221 9222Background.prototype.confirm = function( isLoaded, message ) { 9223 this.isLoaded = isLoaded; 9224 this.emitEvent( 'progress', [ this, this.element, message ] ); 9225}; 9226 9227// -------------------------- jQuery -------------------------- // 9228 9229ImagesLoaded.makeJQueryPlugin = function( jQuery ) { 9230 jQuery = jQuery || window.jQuery; 9231 if ( !jQuery ) return; 9232 9233 // set local variable 9234 $ = jQuery; 9235 // $().imagesLoaded() 9236 $.fn.imagesLoaded = function( options, onAlways ) { 9237 let instance = new ImagesLoaded( this, options, onAlways ); 9238 return instance.jqDeferred.promise( $( this ) ); 9239 }; 9240}; 9241// try making plugin 9242ImagesLoaded.makeJQueryPlugin(); 9243 9244// -------------------------- -------------------------- // 9245 9246return ImagesLoaded; 9247 9248} ); 9249// Flickity.Cell 9250( function( window, factory ) { 9251 // universal module definition 9252 if ( typeof module == 'object' && module.exports ) { 9253 // CommonJS 9254 module.exports = factory( require('get-size') ); 9255 } else { 9256 // browser global 9257 window.Flickity = window.Flickity || {}; 9258 window.Flickity.Cell = factory( window.getSize ); 9259 } 9260 9261}( typeof window != 'undefined' ? window : this, function factory( getSize ) { 9262 9263const cellClassName = 'flickity-cell'; 9264 9265function Cell( elem ) { 9266 this.element = elem; 9267 this.element.classList.add( cellClassName ); 9268 9269 this.x = 0; 9270 this.unselect(); 9271} 9272 9273let proto = Cell.prototype; 9274 9275proto.destroy = function() { 9276 // reset style 9277 this.unselect(); 9278 this.element.classList.remove( cellClassName ); 9279 this.element.style.transform = ''; 9280 this.element.removeAttribute('aria-hidden'); 9281}; 9282 9283proto.getSize = function() { 9284 this.size = getSize( this.element ); 9285}; 9286 9287proto.select = function() { 9288 this.element.classList.add('is-selected'); 9289 this.element.removeAttribute('aria-hidden'); 9290}; 9291 9292proto.unselect = function() { 9293 this.element.classList.remove('is-selected'); 9294 this.element.setAttribute( 'aria-hidden', 'true' ); 9295}; 9296 9297proto.remove = function() { 9298 this.element.remove(); 9299}; 9300 9301return Cell; 9302 9303} ) ); 9304// slide 9305( function( window, factory ) { 9306 // universal module definition 9307 if ( typeof module == 'object' && module.exports ) { 9308 // CommonJS 9309 module.exports = factory(); 9310 } else { 9311 // browser global 9312 window.Flickity = window.Flickity || {}; 9313 window.Flickity.Slide = factory(); 9314 } 9315 9316}( typeof window != 'undefined' ? window : this, function factory() { 9317 9318function Slide( beginMargin, endMargin, cellAlign ) { 9319 this.beginMargin = beginMargin; 9320 this.endMargin = endMargin; 9321 this.cellAlign = cellAlign; 9322 this.cells = []; 9323 this.outerWidth = 0; 9324 this.height = 0; 9325} 9326 9327let proto = Slide.prototype; 9328 9329proto.addCell = function( cell ) { 9330 this.cells.push( cell ); 9331 this.outerWidth += cell.size.outerWidth; 9332 this.height = Math.max( cell.size.outerHeight, this.height ); 9333 // first cell stuff 9334 if ( this.cells.length === 1 ) { 9335 this.x = cell.x; // x comes from first cell 9336 this.firstMargin = cell.size[ this.beginMargin ]; 9337 } 9338}; 9339 9340proto.updateTarget = function() { 9341 let lastCell = this.getLastCell(); 9342 let lastMargin = lastCell ? lastCell.size[ this.endMargin ] : 0; 9343 let slideWidth = this.outerWidth - ( this.firstMargin + lastMargin ); 9344 this.target = this.x + this.firstMargin + slideWidth * this.cellAlign; 9345}; 9346 9347proto.getLastCell = function() { 9348 return this.cells[ this.cells.length - 1 ]; 9349}; 9350 9351proto.select = function() { 9352 this.cells.forEach( ( cell ) => cell.select() ); 9353}; 9354 9355proto.unselect = function() { 9356 this.cells.forEach( ( cell ) => cell.unselect() ); 9357}; 9358 9359proto.getCellElements = function() { 9360 return this.cells.map( ( cell ) => cell.element ); 9361}; 9362 9363return Slide; 9364 9365} ) ); 9366// animate 9367( function( window, factory ) { 9368 // universal module definition 9369 if ( typeof module == 'object' && module.exports ) { 9370 // CommonJS 9371 module.exports = factory( require('fizzy-ui-utils') ); 9372 } else { 9373 // browser global 9374 window.Flickity = window.Flickity || {}; 9375 window.Flickity.animatePrototype = factory( window.fizzyUIUtils ); 9376 } 9377 9378}( typeof window != 'undefined' ? window : this, function factory( utils ) { 9379 9380// -------------------------- animate -------------------------- // 9381 9382let proto = {}; 9383 9384proto.startAnimation = function() { 9385 if ( this.isAnimating ) return; 9386 9387 this.isAnimating = true; 9388 this.restingFrames = 0; 9389 this.animate(); 9390}; 9391 9392proto.animate = function() { 9393 this.applyDragForce(); 9394 this.applySelectedAttraction(); 9395 9396 let previousX = this.x; 9397 9398 this.integratePhysics(); 9399 this.positionSlider(); 9400 this.settle( previousX ); 9401 // animate next frame 9402 if ( this.isAnimating ) requestAnimationFrame( () => this.animate() ); 9403}; 9404 9405proto.positionSlider = function() { 9406 let x = this.x; 9407 // wrap position around 9408 if ( this.isWrapping ) { 9409 x = utils.modulo( x, this.slideableWidth ) - this.slideableWidth; 9410 this.shiftWrapCells( x ); 9411 } 9412 9413 this.setTranslateX( x, this.isAnimating ); 9414 this.dispatchScrollEvent(); 9415}; 9416 9417proto.setTranslateX = function( x, is3d ) { 9418 x += this.cursorPosition; 9419 // reverse if right-to-left and using transform 9420 if ( this.options.rightToLeft ) x = -x; 9421 let translateX = this.getPositionValue( x ); 9422 // use 3D transforms for hardware acceleration on iOS 9423 // but use 2D when settled, for better font-rendering 9424 this.slider.style.transform = is3d ? 9425 `translate3d(${translateX},0,0)` : `translateX(${translateX})`; 9426}; 9427 9428proto.dispatchScrollEvent = function() { 9429 let firstSlide = this.slides[0]; 9430 if ( !firstSlide ) return; 9431 9432 let positionX = -this.x - firstSlide.target; 9433 let progress = positionX / this.slidesWidth; 9434 this.dispatchEvent( 'scroll', null, [ progress, positionX ] ); 9435}; 9436 9437proto.positionSliderAtSelected = function() { 9438 if ( !this.cells.length ) return; 9439 9440 this.x = -this.selectedSlide.target; 9441 this.velocity = 0; // stop wobble 9442 this.positionSlider(); 9443}; 9444 9445proto.getPositionValue = function( position ) { 9446 if ( this.options.percentPosition ) { 9447 // percent position, round to 2 digits, like 12.34% 9448 return ( Math.round( ( position / this.size.innerWidth ) * 10000 ) * 0.01 ) + '%'; 9449 } else { 9450 // pixel positioning 9451 return Math.round( position ) + 'px'; 9452 } 9453}; 9454 9455proto.settle = function( previousX ) { 9456 // keep track of frames where x hasn't moved 9457 let isResting = !this.isPointerDown && 9458 Math.round( this.x * 100 ) === Math.round( previousX * 100 ); 9459 if ( isResting ) this.restingFrames++; 9460 // stop animating if resting for 3 or more frames 9461 if ( this.restingFrames > 2 ) { 9462 this.isAnimating = false; 9463 delete this.isFreeScrolling; 9464 // render position with translateX when settled 9465 this.positionSlider(); 9466 this.dispatchEvent( 'settle', null, [ this.selectedIndex ] ); 9467 } 9468}; 9469 9470proto.shiftWrapCells = function( x ) { 9471 // shift before cells 9472 let beforeGap = this.cursorPosition + x; 9473 this._shiftCells( this.beforeShiftCells, beforeGap, -1 ); 9474 // shift after cells 9475 let afterGap = this.size.innerWidth - ( x + this.slideableWidth + this.cursorPosition ); 9476 this._shiftCells( this.afterShiftCells, afterGap, 1 ); 9477}; 9478 9479proto._shiftCells = function( cells, gap, shift ) { 9480 cells.forEach( ( cell ) => { 9481 let cellShift = gap > 0 ? shift : 0; 9482 this._wrapShiftCell( cell, cellShift ); 9483 gap -= cell.size.outerWidth; 9484 } ); 9485}; 9486 9487proto._unshiftCells = function( cells ) { 9488 if ( !cells || !cells.length ) return; 9489 9490 cells.forEach( ( cell ) => this._wrapShiftCell( cell, 0 ) ); 9491}; 9492 9493// @param {Integer} shift - 0, 1, or -1 9494proto._wrapShiftCell = function( cell, shift ) { 9495 this._renderCellPosition( cell, cell.x + this.slideableWidth * shift ); 9496}; 9497 9498// -------------------------- physics -------------------------- // 9499 9500proto.integratePhysics = function() { 9501 this.x += this.velocity; 9502 this.velocity *= this.getFrictionFactor(); 9503}; 9504 9505proto.applyForce = function( force ) { 9506 this.velocity += force; 9507}; 9508 9509proto.getFrictionFactor = function() { 9510 return 1 - this.options[ this.isFreeScrolling ? 'freeScrollFriction' : 'friction' ]; 9511}; 9512 9513proto.getRestingPosition = function() { 9514 // my thanks to Steven Wittens, who simplified this math greatly 9515 return this.x + this.velocity / ( 1 - this.getFrictionFactor() ); 9516}; 9517 9518proto.applyDragForce = function() { 9519 if ( !this.isDraggable || !this.isPointerDown ) return; 9520 9521 // change the position to drag position by applying force 9522 let dragVelocity = this.dragX - this.x; 9523 let dragForce = dragVelocity - this.velocity; 9524 this.applyForce( dragForce ); 9525}; 9526 9527proto.applySelectedAttraction = function() { 9528 // do not attract if pointer down or no slides 9529 let dragDown = this.isDraggable && this.isPointerDown; 9530 if ( dragDown || this.isFreeScrolling || !this.slides.length ) return; 9531 9532 let distance = this.selectedSlide.target * -1 - this.x; 9533 let force = distance * this.options.selectedAttraction; 9534 this.applyForce( force ); 9535}; 9536 9537return proto; 9538 9539} ) ); 9540// Flickity main 9541/* eslint-disable max-params */ 9542( function( window, factory ) { 9543 // universal module definition 9544 if ( typeof module == 'object' && module.exports ) { 9545 // CommonJS 9546 module.exports = factory( 9547 window, 9548 require('ev-emitter'), 9549 require('get-size'), 9550 require('fizzy-ui-utils'), 9551 require('./cell'), 9552 require('./slide'), 9553 require('./animate'), 9554 ); 9555 } else { 9556 // browser global 9557 let _Flickity = window.Flickity; 9558 9559 window.Flickity = factory( 9560 window, 9561 window.EvEmitter, 9562 window.getSize, 9563 window.fizzyUIUtils, 9564 _Flickity.Cell, 9565 _Flickity.Slide, 9566 _Flickity.animatePrototype, 9567 ); 9568 } 9569 9570}( typeof window != 'undefined' ? window : this, 9571 function factory( window, EvEmitter, getSize, utils, Cell, Slide, animatePrototype ) { 9572/* eslint-enable max-params */ 9573 9574// vars 9575const { getComputedStyle, console } = window; 9576let { jQuery } = window; 9577 9578// -------------------------- Flickity -------------------------- // 9579 9580// globally unique identifiers 9581let GUID = 0; 9582// internal store of all Flickity intances 9583let instances = {}; 9584 9585function Flickity( element, options ) { 9586 let queryElement = utils.getQueryElement( element ); 9587 if ( !queryElement ) { 9588 if ( console ) console.error(`Bad element for Flickity: ${queryElement || element}`); 9589 return; 9590 } 9591 this.element = queryElement; 9592 // do not initialize twice on same element 9593 if ( this.element.flickityGUID ) { 9594 let instance = instances[ this.element.flickityGUID ]; 9595 if ( instance ) instance.option( options ); 9596 return instance; 9597 } 9598 9599 // add jQuery 9600 if ( jQuery ) { 9601 this.$element = jQuery( this.element ); 9602 } 9603 // options 9604 this.options = { ...this.constructor.defaults }; 9605 this.option( options ); 9606 9607 // kick things off 9608 this._create(); 9609} 9610 9611Flickity.defaults = { 9612 accessibility: true, 9613 // adaptiveHeight: false, 9614 cellAlign: 'center', 9615 // cellSelector: undefined, 9616 // contain: false, 9617 freeScrollFriction: 0.075, // friction when free-scrolling 9618 friction: 0.28, // friction when selecting 9619 namespaceJQueryEvents: true, 9620 // initialIndex: 0, 9621 percentPosition: true, 9622 resize: true, 9623 selectedAttraction: 0.025, 9624 setGallerySize: true, 9625 // watchCSS: false, 9626 // wrapAround: false 9627}; 9628 9629// hash of methods triggered on _create() 9630Flickity.create = {}; 9631 9632let proto = Flickity.prototype; 9633// inherit EventEmitter 9634Object.assign( proto, EvEmitter.prototype ); 9635 9636proto._create = function() { 9637 let { resize, watchCSS, rightToLeft } = this.options; 9638 // add id for Flickity.data 9639 let id = this.guid = ++GUID; 9640 this.element.flickityGUID = id; // expando 9641 instances[ id ] = this; // associate via id 9642 // initial properties 9643 this.selectedIndex = 0; 9644 // how many frames slider has been in same position 9645 this.restingFrames = 0; 9646 // initial physics properties 9647 this.x = 0; 9648 this.velocity = 0; 9649 this.beginMargin = rightToLeft ? 'marginRight' : 'marginLeft'; 9650 this.endMargin = rightToLeft ? 'marginLeft' : 'marginRight'; 9651 // create viewport & slider 9652 this.viewport = document.createElement('div'); 9653 this.viewport.className = 'flickity-viewport'; 9654 this._createSlider(); 9655 // used for keyboard navigation 9656 this.focusableElems = [ this.element ]; 9657 9658 if ( resize || watchCSS ) { 9659 window.addEventListener( 'resize', this ); 9660 } 9661 9662 // add listeners from on option 9663 for ( let eventName in this.options.on ) { 9664 let listener = this.options.on[ eventName ]; 9665 this.on( eventName, listener ); 9666 } 9667 9668 for ( let method in Flickity.create ) { 9669 Flickity.create[ method ].call( this ); 9670 } 9671 9672 if ( watchCSS ) { 9673 this.watchCSS(); 9674 } else { 9675 this.activate(); 9676 } 9677}; 9678 9679/** 9680 * set options 9681 * @param {Object} opts - options to extend 9682 */ 9683proto.option = function( opts ) { 9684 Object.assign( this.options, opts ); 9685}; 9686 9687proto.activate = function() { 9688 if ( this.isActive ) return; 9689 9690 this.isActive = true; 9691 this.element.classList.add('flickity-enabled'); 9692 if ( this.options.rightToLeft ) { 9693 this.element.classList.add('flickity-rtl'); 9694 } 9695 9696 this.getSize(); 9697 // move initial cell elements so they can be loaded as cells 9698 let cellElems = this._filterFindCellElements( this.element.children ); 9699 this.slider.append( ...cellElems ); 9700 this.viewport.append( this.slider ); 9701 this.element.append( this.viewport ); 9702 // get cells from children 9703 this.reloadCells(); 9704 9705 if ( this.options.accessibility ) { 9706 // allow element to focusable 9707 this.element.tabIndex = 0; 9708 // listen for key presses 9709 this.element.addEventListener( 'keydown', this ); 9710 } 9711 9712 this.emitEvent('activate'); 9713 this.selectInitialIndex(); 9714 // flag for initial activation, for using initialIndex 9715 this.isInitActivated = true; 9716 // ready event. #493 9717 this.dispatchEvent('ready'); 9718}; 9719 9720// slider positions the cells 9721proto._createSlider = function() { 9722 // slider element does all the positioning 9723 let slider = document.createElement('div'); 9724 slider.className = 'flickity-slider'; 9725 this.slider = slider; 9726}; 9727 9728proto._filterFindCellElements = function( elems ) { 9729 return utils.filterFindElements( elems, this.options.cellSelector ); 9730}; 9731 9732// goes through all children 9733proto.reloadCells = function() { 9734 // collection of item elements 9735 this.cells = this._makeCells( this.slider.children ); 9736 this.positionCells(); 9737 this._updateWrapShiftCells(); 9738 this.setGallerySize(); 9739}; 9740 9741/** 9742 * turn elements into Flickity.Cells 9743 * @param {[Array, NodeList, HTMLElement]} elems - elements to make into cells 9744 * @returns {Array} items - collection of new Flickity Cells 9745 */ 9746proto._makeCells = function( elems ) { 9747 let cellElems = this._filterFindCellElements( elems ); 9748 9749 // create new Cells for collection 9750 return cellElems.map( ( cellElem ) => new Cell( cellElem ) ); 9751}; 9752 9753proto.getLastCell = function() { 9754 return this.cells[ this.cells.length - 1 ]; 9755}; 9756 9757proto.getLastSlide = function() { 9758 return this.slides[ this.slides.length - 1 ]; 9759}; 9760 9761// positions all cells 9762proto.positionCells = function() { 9763 // size all cells 9764 this._sizeCells( this.cells ); 9765 // position all cells 9766 this._positionCells( 0 ); 9767}; 9768 9769/** 9770 * position certain cells 9771 * @param {Integer} index - which cell to start with 9772 */ 9773proto._positionCells = function( index ) { 9774 index = index || 0; 9775 // also measure maxCellHeight 9776 // start 0 if positioning all cells 9777 this.maxCellHeight = index ? this.maxCellHeight || 0 : 0; 9778 let cellX = 0; 9779 // get cellX 9780 if ( index > 0 ) { 9781 let startCell = this.cells[ index - 1 ]; 9782 cellX = startCell.x + startCell.size.outerWidth; 9783 } 9784 9785 this.cells.slice( index ).forEach( ( cell ) => { 9786 cell.x = cellX; 9787 this._renderCellPosition( cell, cellX ); 9788 cellX += cell.size.outerWidth; 9789 this.maxCellHeight = Math.max( cell.size.outerHeight, this.maxCellHeight ); 9790 } ); 9791 // keep track of cellX for wrap-around 9792 this.slideableWidth = cellX; 9793 // slides 9794 this.updateSlides(); 9795 // contain slides target 9796 this._containSlides(); 9797 // update slidesWidth 9798 this.slidesWidth = this.cells.length ? 9799 this.getLastSlide().target - this.slides[0].target : 0; 9800}; 9801 9802proto._renderCellPosition = function( cell, x ) { 9803 // render position of cell with in slider 9804 let sideOffset = this.options.rightToLeft ? -1 : 1; 9805 let renderX = x * sideOffset; 9806 if ( this.options.percentPosition ) renderX *= this.size.innerWidth / cell.size.width; 9807 let positionValue = this.getPositionValue( renderX ); 9808 cell.element.style.transform = `translateX( ${positionValue} )`; 9809}; 9810 9811/** 9812 * cell.getSize() on multiple cells 9813 * @param {Array} cells - cells to size 9814 */ 9815proto._sizeCells = function( cells ) { 9816 cells.forEach( ( cell ) => cell.getSize() ); 9817}; 9818 9819// -------------------------- -------------------------- // 9820 9821proto.updateSlides = function() { 9822 this.slides = []; 9823 if ( !this.cells.length ) return; 9824 9825 let { beginMargin, endMargin } = this; 9826 let slide = new Slide( beginMargin, endMargin, this.cellAlign ); 9827 this.slides.push( slide ); 9828 9829 let canCellFit = this._getCanCellFit(); 9830 9831 this.cells.forEach( ( cell, i ) => { 9832 // just add cell if first cell in slide 9833 if ( !slide.cells.length ) { 9834 slide.addCell( cell ); 9835 return; 9836 } 9837 9838 let slideWidth = ( slide.outerWidth - slide.firstMargin ) + 9839 ( cell.size.outerWidth - cell.size[ endMargin ] ); 9840 9841 if ( canCellFit( i, slideWidth ) ) { 9842 slide.addCell( cell ); 9843 } else { 9844 // doesn't fit, new slide 9845 slide.updateTarget(); 9846 9847 slide = new Slide( beginMargin, endMargin, this.cellAlign ); 9848 this.slides.push( slide ); 9849 slide.addCell( cell ); 9850 } 9851 } ); 9852 // last slide 9853 slide.updateTarget(); 9854 // update .selectedSlide 9855 this.updateSelectedSlide(); 9856}; 9857 9858proto._getCanCellFit = function() { 9859 let { groupCells } = this.options; 9860 if ( !groupCells ) return () => false; 9861 9862 if ( typeof groupCells == 'number' ) { 9863 // group by number. 3 -> [0,1,2], [3,4,5], ... 9864 let number = parseInt( groupCells, 10 ); 9865 return ( i ) => ( i % number ) !== 0; 9866 } 9867 // default, group by width of slide 9868 let percent = 1; 9869 // parse '75% 9870 let percentMatch = typeof groupCells == 'string' && groupCells.match( /^(\d+)%$/ ); 9871 if ( percentMatch ) percent = parseInt( percentMatch[1], 10 ) / 100; 9872 let groupWidth = ( this.size.innerWidth + 1 ) * percent; 9873 return ( i, slideWidth ) => slideWidth <= groupWidth; 9874}; 9875 9876// alias _init for jQuery plugin .flickity() 9877proto._init = 9878proto.reposition = function() { 9879 this.positionCells(); 9880 this.positionSliderAtSelected(); 9881}; 9882 9883proto.getSize = function() { 9884 this.size = getSize( this.element ); 9885 this.setCellAlign(); 9886 this.cursorPosition = this.size.innerWidth * this.cellAlign; 9887}; 9888 9889let cellAlignShorthands = { 9890 left: 0, 9891 center: 0.5, 9892 right: 1, 9893}; 9894 9895proto.setCellAlign = function() { 9896 let { cellAlign, rightToLeft } = this.options; 9897 let shorthand = cellAlignShorthands[ cellAlign ]; 9898 this.cellAlign = shorthand !== undefined ? shorthand : cellAlign; 9899 if ( rightToLeft ) this.cellAlign = 1 - this.cellAlign; 9900}; 9901 9902proto.setGallerySize = function() { 9903 if ( !this.options.setGallerySize ) return; 9904 9905 let height = this.options.adaptiveHeight && this.selectedSlide ? 9906 this.selectedSlide.height : this.maxCellHeight; 9907 this.viewport.style.height = `${height}px`; 9908}; 9909 9910proto._updateWrapShiftCells = function() { 9911 // update isWrapping 9912 this.isWrapping = this.getIsWrapping(); 9913 // only for wrap-around 9914 if ( !this.isWrapping ) return; 9915 9916 // unshift previous cells 9917 this._unshiftCells( this.beforeShiftCells ); 9918 this._unshiftCells( this.afterShiftCells ); 9919 // get before cells 9920 // initial gap 9921 let beforeGapX = this.cursorPosition; 9922 let lastIndex = this.cells.length - 1; 9923 this.beforeShiftCells = this._getGapCells( beforeGapX, lastIndex, -1 ); 9924 // get after cells 9925 // ending gap between last cell and end of gallery viewport 9926 let afterGapX = this.size.innerWidth - this.cursorPosition; 9927 // start cloning at first cell, working forwards 9928 this.afterShiftCells = this._getGapCells( afterGapX, 0, 1 ); 9929}; 9930 9931proto.getIsWrapping = function() { 9932 let { wrapAround } = this.options; 9933 if ( !wrapAround || this.slides.length < 2 ) return false; 9934 9935 if ( wrapAround !== 'fill' ) return true; 9936 // check that slides can fit 9937 9938 let gapWidth = this.slideableWidth - this.size.innerWidth; 9939 if ( gapWidth > this.size.innerWidth ) return true; // gap * 2x big, all good 9940 // check that content width - shifting cell is bigger than viewport width 9941 for ( let cell of this.cells ) { 9942 if ( cell.size.outerWidth > gapWidth ) return false; 9943 } 9944 return true; 9945}; 9946 9947proto._getGapCells = function( gapX, cellIndex, increment ) { 9948 // keep adding cells until the cover the initial gap 9949 let cells = []; 9950 while ( gapX > 0 ) { 9951 let cell = this.cells[ cellIndex ]; 9952 if ( !cell ) break; 9953 9954 cells.push( cell ); 9955 cellIndex += increment; 9956 gapX -= cell.size.outerWidth; 9957 } 9958 return cells; 9959}; 9960 9961// ----- contain & wrap ----- // 9962 9963// contain cell targets so no excess sliding 9964proto._containSlides = function() { 9965 let isContaining = this.options.contain && !this.isWrapping && 9966 this.cells.length; 9967 if ( !isContaining ) return; 9968 9969 let contentWidth = this.slideableWidth - this.getLastCell().size[ this.endMargin ]; 9970 // content is less than gallery size 9971 let isContentSmaller = contentWidth < this.size.innerWidth; 9972 if ( isContentSmaller ) { 9973 // all cells fit inside gallery 9974 this.slides.forEach( ( slide ) => { 9975 slide.target = contentWidth * this.cellAlign; 9976 } ); 9977 } else { 9978 // contain to bounds 9979 let beginBound = this.cursorPosition + this.cells[0].size[ this.beginMargin ]; 9980 let endBound = contentWidth - this.size.innerWidth * ( 1 - this.cellAlign ); 9981 this.slides.forEach( ( slide ) => { 9982 slide.target = Math.max( slide.target, beginBound ); 9983 slide.target = Math.min( slide.target, endBound ); 9984 } ); 9985 } 9986}; 9987 9988// ----- events ----- // 9989 9990/** 9991 * emits events via eventEmitter and jQuery events 9992 * @param {String} type - name of event 9993 * @param {Event} event - original event 9994 * @param {Array} args - extra arguments 9995 */ 9996proto.dispatchEvent = function( type, event, args ) { 9997 let emitArgs = event ? [ event ].concat( args ) : args; 9998 this.emitEvent( type, emitArgs ); 9999 10000 if ( jQuery && this.$element ) { 10001 // default trigger with type if no event 10002 type += this.options.namespaceJQueryEvents ? '.flickity' : ''; 10003 let $event = type; 10004 if ( event ) { 10005 // create jQuery event 10006 let jQEvent = new jQuery.Event( event ); 10007 jQEvent.type = type; 10008 $event = jQEvent; 10009 } 10010 this.$element.trigger( $event, args ); 10011 } 10012}; 10013 10014const unidraggerEvents = [ 10015 'dragStart', 10016 'dragMove', 10017 'dragEnd', 10018 'pointerDown', 10019 'pointerMove', 10020 'pointerEnd', 10021 'staticClick', 10022]; 10023 10024let _emitEvent = proto.emitEvent; 10025proto.emitEvent = function( eventName, args ) { 10026 if ( eventName === 'staticClick' ) { 10027 // add cellElem and cellIndex args to staticClick 10028 let clickedCell = this.getParentCell( args[0].target ); 10029 let cellElem = clickedCell && clickedCell.element; 10030 let cellIndex = clickedCell && this.cells.indexOf( clickedCell ); 10031 args = args.concat( cellElem, cellIndex ); 10032 } 10033 // do regular thing 10034 _emitEvent.call( this, eventName, args ); 10035 // duck-punch in jQuery events for Unidragger events 10036 let isUnidraggerEvent = unidraggerEvents.includes( eventName ); 10037 if ( !isUnidraggerEvent || !jQuery || !this.$element ) return; 10038 10039 eventName += this.options.namespaceJQueryEvents ? '.flickity' : ''; 10040 let event = args.shift( 0 ); 10041 let jQEvent = new jQuery.Event( event ); 10042 jQEvent.type = eventName; 10043 this.$element.trigger( jQEvent, args ); 10044}; 10045 10046// -------------------------- select -------------------------- // 10047 10048/** 10049 * @param {Integer} index - index of the slide 10050 * @param {Boolean} isWrap - will wrap-around to last/first if at the end 10051 * @param {Boolean} isInstant - will immediately set position at selected cell 10052 */ 10053proto.select = function( index, isWrap, isInstant ) { 10054 if ( !this.isActive ) return; 10055 10056 index = parseInt( index, 10 ); 10057 this._wrapSelect( index ); 10058 10059 if ( this.isWrapping || isWrap ) { 10060 index = utils.modulo( index, this.slides.length ); 10061 } 10062 // bail if invalid index 10063 if ( !this.slides[ index ] ) return; 10064 10065 let prevIndex = this.selectedIndex; 10066 this.selectedIndex = index; 10067 this.updateSelectedSlide(); 10068 if ( isInstant ) { 10069 this.positionSliderAtSelected(); 10070 } else { 10071 this.startAnimation(); 10072 } 10073 if ( this.options.adaptiveHeight ) { 10074 this.setGallerySize(); 10075 } 10076 // events 10077 this.dispatchEvent( 'select', null, [ index ] ); 10078 // change event if new index 10079 if ( index !== prevIndex ) { 10080 this.dispatchEvent( 'change', null, [ index ] ); 10081 } 10082}; 10083 10084// wraps position for wrapAround, to move to closest slide. #113 10085proto._wrapSelect = function( index ) { 10086 if ( !this.isWrapping ) return; 10087 10088 const { selectedIndex, slideableWidth, slides: { length } } = this; 10089 // shift index for wrap, do not wrap dragSelect 10090 if ( !this.isDragSelect ) { 10091 let wrapIndex = utils.modulo( index, length ); 10092 // go to shortest 10093 let delta = Math.abs( wrapIndex - selectedIndex ); 10094 let backWrapDelta = Math.abs( ( wrapIndex + length ) - selectedIndex ); 10095 let forewardWrapDelta = Math.abs( ( wrapIndex - length ) - selectedIndex ); 10096 if ( backWrapDelta < delta ) { 10097 index += length; 10098 } else if ( forewardWrapDelta < delta ) { 10099 index -= length; 10100 } 10101 } 10102 10103 // wrap position so slider is within normal area 10104 if ( index < 0 ) { 10105 this.x -= slideableWidth; 10106 } else if ( index >= length ) { 10107 this.x += slideableWidth; 10108 } 10109}; 10110 10111proto.previous = function( isWrap, isInstant ) { 10112 this.select( this.selectedIndex - 1, isWrap, isInstant ); 10113}; 10114 10115proto.next = function( isWrap, isInstant ) { 10116 this.select( this.selectedIndex + 1, isWrap, isInstant ); 10117}; 10118 10119proto.updateSelectedSlide = function() { 10120 let slide = this.slides[ this.selectedIndex ]; 10121 // selectedIndex could be outside of slides, if triggered before resize() 10122 if ( !slide ) return; 10123 10124 // unselect previous selected slide 10125 this.unselectSelectedSlide(); 10126 // update new selected slide 10127 this.selectedSlide = slide; 10128 slide.select(); 10129 this.selectedCells = slide.cells; 10130 this.selectedElements = slide.getCellElements(); 10131 // HACK: selectedCell & selectedElement is first cell in slide, backwards compatibility 10132 this.selectedCell = slide.cells[0]; 10133 this.selectedElement = this.selectedElements[0]; 10134}; 10135 10136proto.unselectSelectedSlide = function() { 10137 if ( this.selectedSlide ) this.selectedSlide.unselect(); 10138}; 10139 10140proto.selectInitialIndex = function() { 10141 let initialIndex = this.options.initialIndex; 10142 // already activated, select previous selectedIndex 10143 if ( this.isInitActivated ) { 10144 this.select( this.selectedIndex, false, true ); 10145 return; 10146 } 10147 // select with selector string 10148 if ( initialIndex && typeof initialIndex == 'string' ) { 10149 let cell = this.queryCell( initialIndex ); 10150 if ( cell ) { 10151 this.selectCell( initialIndex, false, true ); 10152 return; 10153 } 10154 } 10155 10156 let index = 0; 10157 // select with number 10158 if ( initialIndex && this.slides[ initialIndex ] ) { 10159 index = initialIndex; 10160 } 10161 // select instantly 10162 this.select( index, false, true ); 10163}; 10164 10165/** 10166 * select slide from number or cell element 10167 * @param {[Element, Number]} value - zero-based index or element to select 10168 * @param {Boolean} isWrap - enables wrapping around for extra index 10169 * @param {Boolean} isInstant - disables slide animation 10170 */ 10171proto.selectCell = function( value, isWrap, isInstant ) { 10172 // get cell 10173 let cell = this.queryCell( value ); 10174 if ( !cell ) return; 10175 10176 let index = this.getCellSlideIndex( cell ); 10177 this.select( index, isWrap, isInstant ); 10178}; 10179 10180proto.getCellSlideIndex = function( cell ) { 10181 // get index of slide that has cell 10182 let cellSlide = this.slides.find( ( slide ) => slide.cells.includes( cell ) ); 10183 return this.slides.indexOf( cellSlide ); 10184}; 10185 10186// -------------------------- get cells -------------------------- // 10187 10188/** 10189 * get Flickity.Cell, given an Element 10190 * @param {Element} elem - matching cell element 10191 * @returns {Flickity.Cell} cell - matching cell 10192 */ 10193proto.getCell = function( elem ) { 10194 // loop through cells to get the one that matches 10195 for ( let cell of this.cells ) { 10196 if ( cell.element === elem ) return cell; 10197 } 10198}; 10199 10200/** 10201 * get collection of Flickity.Cells, given Elements 10202 * @param {[Element, Array, NodeList]} elems - multiple elements 10203 * @returns {Array} cells - Flickity.Cells 10204 */ 10205proto.getCells = function( elems ) { 10206 elems = utils.makeArray( elems ); 10207 return elems.map( ( elem ) => this.getCell( elem ) ).filter( Boolean ); 10208}; 10209 10210/** 10211 * get cell elements 10212 * @returns {Array} cellElems 10213 */ 10214proto.getCellElements = function() { 10215 return this.cells.map( ( cell ) => cell.element ); 10216}; 10217 10218/** 10219 * get parent cell from an element 10220 * @param {Element} elem - child element 10221 * @returns {Flickit.Cell} cell - parent cell 10222 */ 10223proto.getParentCell = function( elem ) { 10224 // first check if elem is cell 10225 let cell = this.getCell( elem ); 10226 if ( cell ) return cell; 10227 10228 // try to get parent cell elem 10229 let closest = elem.closest('.flickity-slider > *'); 10230 return this.getCell( closest ); 10231}; 10232 10233/** 10234 * get cells adjacent to a slide 10235 * @param {Integer} adjCount - number of adjacent slides 10236 * @param {Integer} index - index of slide to start 10237 * @returns {Array} cells - array of Flickity.Cells 10238 */ 10239proto.getAdjacentCellElements = function( adjCount, index ) { 10240 if ( !adjCount ) return this.selectedSlide.getCellElements(); 10241 10242 index = index === undefined ? this.selectedIndex : index; 10243 10244 let len = this.slides.length; 10245 if ( 1 + ( adjCount * 2 ) >= len ) { 10246 return this.getCellElements(); // get all 10247 } 10248 10249 let cellElems = []; 10250 for ( let i = index - adjCount; i <= index + adjCount; i++ ) { 10251 let slideIndex = this.isWrapping ? utils.modulo( i, len ) : i; 10252 let slide = this.slides[ slideIndex ]; 10253 if ( slide ) { 10254 cellElems = cellElems.concat( slide.getCellElements() ); 10255 } 10256 } 10257 return cellElems; 10258}; 10259 10260/** 10261 * select slide from number or cell element 10262 * @param {[Element, String, Number]} selector - element, selector string, or index 10263 * @returns {Flickity.Cell} - matching cell 10264 */ 10265proto.queryCell = function( selector ) { 10266 if ( typeof selector == 'number' ) { 10267 // use number as index 10268 return this.cells[ selector ]; 10269 } 10270 // do not select invalid selectors from hash: #123, #/. #791 10271 let isSelectorString = typeof selector == 'string' && !selector.match( /^[#.]?[\d/]/ ); 10272 if ( isSelectorString ) { 10273 // use string as selector, get element 10274 selector = this.element.querySelector( selector ); 10275 } 10276 // get cell from element 10277 return this.getCell( selector ); 10278}; 10279 10280// -------------------------- events -------------------------- // 10281 10282proto.uiChange = function() { 10283 this.emitEvent('uiChange'); 10284}; 10285 10286// ----- resize ----- // 10287 10288proto.onresize = function() { 10289 this.watchCSS(); 10290 this.resize(); 10291}; 10292 10293utils.debounceMethod( Flickity, 'onresize', 150 ); 10294 10295proto.resize = function() { 10296 // #1177 disable resize behavior when animating or dragging for iOS 15 10297 if ( !this.isActive || this.isAnimating || this.isDragging ) return; 10298 this.getSize(); 10299 // wrap values 10300 if ( this.isWrapping ) { 10301 this.x = utils.modulo( this.x, this.slideableWidth ); 10302 } 10303 this.positionCells(); 10304 this._updateWrapShiftCells(); 10305 this.setGallerySize(); 10306 this.emitEvent('resize'); 10307 // update selected index for group slides, instant 10308 // TODO: position can be lost between groups of various numbers 10309 let selectedElement = this.selectedElements && this.selectedElements[0]; 10310 this.selectCell( selectedElement, false, true ); 10311}; 10312 10313// watches the :after property, activates/deactivates 10314proto.watchCSS = function() { 10315 if ( !this.options.watchCSS ) return; 10316 10317 let afterContent = getComputedStyle( this.element, ':after' ).content; 10318 // activate if :after { content: 'flickity' } 10319 if ( afterContent.includes('flickity') ) { 10320 this.activate(); 10321 } else { 10322 this.deactivate(); 10323 } 10324}; 10325 10326// ----- keydown ----- // 10327 10328// go previous/next if left/right keys pressed 10329proto.onkeydown = function( event ) { 10330 let { activeElement } = document; 10331 let handler = Flickity.keyboardHandlers[ event.key ]; 10332 // only work if element is in focus 10333 if ( !this.options.accessibility || !activeElement || !handler ) return; 10334 10335 let isFocused = this.focusableElems.some( ( elem ) => activeElement === elem ); 10336 if ( isFocused ) handler.call( this ); 10337}; 10338 10339Flickity.keyboardHandlers = { 10340 ArrowLeft: function() { 10341 this.uiChange(); 10342 let leftMethod = this.options.rightToLeft ? 'next' : 'previous'; 10343 this[ leftMethod ](); 10344 }, 10345 ArrowRight: function() { 10346 this.uiChange(); 10347 let rightMethod = this.options.rightToLeft ? 'previous' : 'next'; 10348 this[ rightMethod ](); 10349 }, 10350}; 10351 10352// ----- focus ----- // 10353 10354proto.focus = function() { 10355 this.element.focus({ preventScroll: true }); 10356}; 10357 10358// -------------------------- destroy -------------------------- // 10359 10360// deactivate all Flickity functionality, but keep stuff available 10361proto.deactivate = function() { 10362 if ( !this.isActive ) return; 10363 10364 this.element.classList.remove('flickity-enabled'); 10365 this.element.classList.remove('flickity-rtl'); 10366 this.unselectSelectedSlide(); 10367 // destroy cells 10368 this.cells.forEach( ( cell ) => cell.destroy() ); 10369 this.viewport.remove(); 10370 // move child elements back into element 10371 this.element.append( ...this.slider.children ); 10372 if ( this.options.accessibility ) { 10373 this.element.removeAttribute('tabIndex'); 10374 this.element.removeEventListener( 'keydown', this ); 10375 } 10376 // set flags 10377 this.isActive = false; 10378 this.emitEvent('deactivate'); 10379}; 10380 10381proto.destroy = function() { 10382 this.deactivate(); 10383 window.removeEventListener( 'resize', this ); 10384 this.allOff(); 10385 this.emitEvent('destroy'); 10386 if ( jQuery && this.$element ) { 10387 jQuery.removeData( this.element, 'flickity' ); 10388 } 10389 delete this.element.flickityGUID; 10390 delete instances[ this.guid ]; 10391}; 10392 10393// -------------------------- prototype -------------------------- // 10394 10395Object.assign( proto, animatePrototype ); 10396 10397// -------------------------- extras -------------------------- // 10398 10399/** 10400 * get Flickity instance from element 10401 * @param {[Element, String]} elem - element or selector string 10402 * @returns {Flickity} - Flickity instance 10403 */ 10404Flickity.data = function( elem ) { 10405 elem = utils.getQueryElement( elem ); 10406 if ( elem ) return instances[ elem.flickityGUID ]; 10407}; 10408 10409utils.htmlInit( Flickity, 'flickity' ); 10410 10411let { jQueryBridget } = window; 10412if ( jQuery && jQueryBridget ) { 10413 jQueryBridget( 'flickity', Flickity, jQuery ); 10414} 10415 10416// set internal jQuery, for Webpack + jQuery v3, #478 10417Flickity.setJQuery = function( jq ) { 10418 jQuery = jq; 10419}; 10420 10421Flickity.Cell = Cell; 10422Flickity.Slide = Slide; 10423 10424return Flickity; 10425 10426} ) ); 10427// drag 10428( function( window, factory ) { 10429 // universal module definition 10430 if ( typeof module == 'object' && module.exports ) { 10431 // CommonJS 10432 module.exports = factory( 10433 window, 10434 require('./core'), 10435 require('unidragger'), 10436 require('fizzy-ui-utils'), 10437 ); 10438 } else { 10439 // browser global 10440 window.Flickity = factory( 10441 window, 10442 window.Flickity, 10443 window.Unidragger, 10444 window.fizzyUIUtils, 10445 ); 10446 } 10447 10448}( typeof window != 'undefined' ? window : this, 10449 function factory( window, Flickity, Unidragger, utils ) { 10450 10451// ----- defaults ----- // 10452 10453Object.assign( Flickity.defaults, { 10454 draggable: '>1', 10455 dragThreshold: 3, 10456} ); 10457 10458// -------------------------- drag prototype -------------------------- // 10459 10460let proto = Flickity.prototype; 10461Object.assign( proto, Unidragger.prototype ); // inherit Unidragger 10462proto.touchActionValue = ''; 10463 10464// -------------------------- -------------------------- // 10465 10466Flickity.create.drag = function() { 10467 this.on( 'activate', this.onActivateDrag ); 10468 this.on( 'uiChange', this._uiChangeDrag ); 10469 this.on( 'deactivate', this.onDeactivateDrag ); 10470 this.on( 'cellChange', this.updateDraggable ); 10471 this.on( 'pointerDown', this.handlePointerDown ); 10472 this.on( 'pointerUp', this.handlePointerUp ); 10473 this.on( 'pointerDown', this.handlePointerDone ); 10474 this.on( 'dragStart', this.handleDragStart ); 10475 this.on( 'dragMove', this.handleDragMove ); 10476 this.on( 'dragEnd', this.handleDragEnd ); 10477 this.on( 'staticClick', this.handleStaticClick ); 10478 // TODO updateDraggable on resize? if groupCells & slides change 10479}; 10480 10481proto.onActivateDrag = function() { 10482 this.handles = [ this.viewport ]; 10483 this.bindHandles(); 10484 this.updateDraggable(); 10485}; 10486 10487proto.onDeactivateDrag = function() { 10488 this.unbindHandles(); 10489 this.element.classList.remove('is-draggable'); 10490}; 10491 10492proto.updateDraggable = function() { 10493 // disable dragging if less than 2 slides. #278 10494 if ( this.options.draggable === '>1' ) { 10495 this.isDraggable = this.slides.length > 1; 10496 } else { 10497 this.isDraggable = this.options.draggable; 10498 } 10499 this.element.classList.toggle( 'is-draggable', this.isDraggable ); 10500}; 10501 10502proto._uiChangeDrag = function() { 10503 delete this.isFreeScrolling; 10504}; 10505 10506// -------------------------- pointer events -------------------------- // 10507 10508proto.handlePointerDown = function( event ) { 10509 if ( !this.isDraggable ) { 10510 // proceed for staticClick 10511 this.bindActivePointerEvents( event ); 10512 return; 10513 } 10514 10515 let isTouchStart = event.type === 'touchstart'; 10516 let isTouchPointer = event.pointerType === 'touch'; 10517 let isFocusNode = event.target.matches('input, textarea, select'); 10518 if ( !isTouchStart && !isTouchPointer && !isFocusNode ) event.preventDefault(); 10519 if ( !isFocusNode ) this.focus(); 10520 // blur 10521 if ( document.activeElement !== this.element ) document.activeElement.blur(); 10522 // stop if it was moving 10523 this.dragX = this.x; 10524 this.viewport.classList.add('is-pointer-down'); 10525 // track scrolling 10526 this.pointerDownScroll = getScrollPosition(); 10527 window.addEventListener( 'scroll', this ); 10528 this.bindActivePointerEvents( event ); 10529}; 10530 10531// ----- move ----- // 10532 10533proto.hasDragStarted = function( moveVector ) { 10534 return Math.abs( moveVector.x ) > this.options.dragThreshold; 10535}; 10536 10537// ----- up ----- // 10538 10539proto.handlePointerUp = function() { 10540 delete this.isTouchScrolling; 10541 this.viewport.classList.remove('is-pointer-down'); 10542}; 10543 10544proto.handlePointerDone = function() { 10545 window.removeEventListener( 'scroll', this ); 10546 delete this.pointerDownScroll; 10547}; 10548 10549// -------------------------- dragging -------------------------- // 10550 10551proto.handleDragStart = function() { 10552 if ( !this.isDraggable ) return; 10553 10554 this.dragStartPosition = this.x; 10555 this.startAnimation(); 10556 window.removeEventListener( 'scroll', this ); 10557}; 10558 10559proto.handleDragMove = function( event, pointer, moveVector ) { 10560 if ( !this.isDraggable ) return; 10561 10562 event.preventDefault(); 10563 10564 this.previousDragX = this.dragX; 10565 // reverse if right-to-left 10566 let direction = this.options.rightToLeft ? -1 : 1; 10567 // wrap around move. #589 10568 if ( this.isWrapping ) moveVector.x %= this.slideableWidth; 10569 let dragX = this.dragStartPosition + moveVector.x * direction; 10570 10571 if ( !this.isWrapping ) { 10572 // slow drag 10573 let originBound = Math.max( -this.slides[0].target, this.dragStartPosition ); 10574 dragX = dragX > originBound ? ( dragX + originBound ) * 0.5 : dragX; 10575 let endBound = Math.min( -this.getLastSlide().target, this.dragStartPosition ); 10576 dragX = dragX < endBound ? ( dragX + endBound ) * 0.5 : dragX; 10577 } 10578 10579 this.dragX = dragX; 10580 this.dragMoveTime = new Date(); 10581}; 10582 10583proto.handleDragEnd = function() { 10584 if ( !this.isDraggable ) return; 10585 10586 let { freeScroll } = this.options; 10587 if ( freeScroll ) this.isFreeScrolling = true; 10588 // set selectedIndex based on where flick will end up 10589 let index = this.dragEndRestingSelect(); 10590 10591 if ( freeScroll && !this.isWrapping ) { 10592 // if free-scroll & not wrap around 10593 // do not free-scroll if going outside of bounding slides 10594 // so bounding slides can attract slider, and keep it in bounds 10595 let restingX = this.getRestingPosition(); 10596 this.isFreeScrolling = -restingX > this.slides[0].target && 10597 -restingX < this.getLastSlide().target; 10598 } else if ( !freeScroll && index === this.selectedIndex ) { 10599 // boost selection if selected index has not changed 10600 index += this.dragEndBoostSelect(); 10601 } 10602 delete this.previousDragX; 10603 // apply selection 10604 // HACK, set flag so dragging stays in correct direction 10605 this.isDragSelect = this.isWrapping; 10606 this.select( index ); 10607 delete this.isDragSelect; 10608}; 10609 10610proto.dragEndRestingSelect = function() { 10611 let restingX = this.getRestingPosition(); 10612 // how far away from selected slide 10613 let distance = Math.abs( this.getSlideDistance( -restingX, this.selectedIndex ) ); 10614 // get closet resting going up and going down 10615 let positiveResting = this._getClosestResting( restingX, distance, 1 ); 10616 let negativeResting = this._getClosestResting( restingX, distance, -1 ); 10617 // use closer resting for wrap-around 10618 return positiveResting.distance < negativeResting.distance ? 10619 positiveResting.index : negativeResting.index; 10620}; 10621 10622/** 10623 * given resting X and distance to selected cell 10624 * get the distance and index of the closest cell 10625 * @param {Number} restingX - estimated post-flick resting position 10626 * @param {Number} distance - distance to selected cell 10627 * @param {Integer} increment - +1 or -1, going up or down 10628 * @returns {Object} - { distance: {Number}, index: {Integer} } 10629 */ 10630proto._getClosestResting = function( restingX, distance, increment ) { 10631 let index = this.selectedIndex; 10632 let minDistance = Infinity; 10633 let condition = this.options.contain && !this.isWrapping ? 10634 // if containing, keep going if distance is equal to minDistance 10635 ( dist, minDist ) => dist <= minDist : 10636 ( dist, minDist ) => dist < minDist; 10637 10638 while ( condition( distance, minDistance ) ) { 10639 // measure distance to next cell 10640 index += increment; 10641 minDistance = distance; 10642 distance = this.getSlideDistance( -restingX, index ); 10643 if ( distance === null ) break; 10644 10645 distance = Math.abs( distance ); 10646 } 10647 return { 10648 distance: minDistance, 10649 // selected was previous index 10650 index: index - increment, 10651 }; 10652}; 10653 10654/** 10655 * measure distance between x and a slide target 10656 * @param {Number} x - horizontal position 10657 * @param {Integer} index - slide index 10658 * @returns {Number} - slide distance 10659 */ 10660proto.getSlideDistance = function( x, index ) { 10661 let len = this.slides.length; 10662 // wrap around if at least 2 slides 10663 let isWrapAround = this.options.wrapAround && len > 1; 10664 let slideIndex = isWrapAround ? utils.modulo( index, len ) : index; 10665 let slide = this.slides[ slideIndex ]; 10666 if ( !slide ) return null; 10667 10668 // add distance for wrap-around slides 10669 let wrap = isWrapAround ? this.slideableWidth * Math.floor( index/len ) : 0; 10670 return x - ( slide.target + wrap ); 10671}; 10672 10673proto.dragEndBoostSelect = function() { 10674 // do not boost if no previousDragX or dragMoveTime 10675 if ( this.previousDragX === undefined || !this.dragMoveTime || 10676 // or if drag was held for 100 ms 10677 new Date() - this.dragMoveTime > 100 ) { 10678 return 0; 10679 } 10680 10681 let distance = this.getSlideDistance( -this.dragX, this.selectedIndex ); 10682 let delta = this.previousDragX - this.dragX; 10683 if ( distance > 0 && delta > 0 ) { 10684 // boost to next if moving towards the right, and positive velocity 10685 return 1; 10686 } else if ( distance < 0 && delta < 0 ) { 10687 // boost to previous if moving towards the left, and negative velocity 10688 return -1; 10689 } 10690 return 0; 10691}; 10692 10693// ----- scroll ----- // 10694 10695proto.onscroll = function() { 10696 let scroll = getScrollPosition(); 10697 let scrollMoveX = this.pointerDownScroll.x - scroll.x; 10698 let scrollMoveY = this.pointerDownScroll.y - scroll.y; 10699 // cancel click/tap if scroll is too much 10700 if ( Math.abs( scrollMoveX ) > 3 || Math.abs( scrollMoveY ) > 3 ) { 10701 this.pointerDone(); 10702 } 10703}; 10704 10705// ----- utils ----- // 10706 10707function getScrollPosition() { 10708 return { 10709 x: window.pageXOffset, 10710 y: window.pageYOffset, 10711 }; 10712} 10713 10714// ----- ----- // 10715 10716return Flickity; 10717 10718} ) ); 10719// prev/next buttons 10720( function( window, factory ) { 10721 // universal module definition 10722 if ( typeof module == 'object' && module.exports ) { 10723 // CommonJS 10724 module.exports = factory( require('./core') ); 10725 } else { 10726 // browser global 10727 factory( window.Flickity ); 10728 } 10729 10730}( typeof window != 'undefined' ? window : this, function factory( Flickity ) { 10731 10732const svgURI = 'http://www.w3.org/2000/svg'; 10733 10734// -------------------------- PrevNextButton -------------------------- // 10735 10736function PrevNextButton( increment, direction, arrowShape ) { 10737 this.increment = increment; 10738 this.direction = direction; 10739 this.isPrevious = increment === 'previous'; 10740 this.isLeft = direction === 'left'; 10741 this._create( arrowShape ); 10742} 10743 10744PrevNextButton.prototype._create = function( arrowShape ) { 10745 // properties 10746 let element = this.element = document.createElement('button'); 10747 element.className = `flickity-button flickity-prev-next-button ${this.increment}`; 10748 let label = this.isPrevious ? 'Previous' : 'Next'; 10749 // prevent button from submitting form https://stackoverflow.com/a/10836076/182183 10750 element.setAttribute( 'type', 'button' ); 10751 element.setAttribute( 'aria-label', label ); 10752 // init as disabled 10753 this.disable(); 10754 // create arrow 10755 let svg = this.createSVG( label, arrowShape ); 10756 element.append( svg ); 10757}; 10758 10759PrevNextButton.prototype.createSVG = function( label, arrowShape ) { 10760 let svg = document.createElementNS( svgURI, 'svg' ); 10761 svg.setAttribute( 'class', 'flickity-button-icon' ); 10762 svg.setAttribute( 'viewBox', '0 0 100 100' ); 10763 // add title #1189 10764 let title = document.createElementNS( svgURI, 'title' ); 10765 title.append( label ); 10766 // add path 10767 let path = document.createElementNS( svgURI, 'path' ); 10768 let pathMovements = getArrowMovements( arrowShape ); 10769 path.setAttribute( 'd', pathMovements ); 10770 path.setAttribute( 'class', 'arrow' ); 10771 // rotate arrow 10772 if ( !this.isLeft ) { 10773 path.setAttribute( 'transform', 'translate(100, 100) rotate(180)' ); 10774 } 10775 svg.append( title, path ); 10776 return svg; 10777}; 10778 10779// get SVG path movmement 10780function getArrowMovements( shape ) { 10781 // use shape as movement if string 10782 if ( typeof shape == 'string' ) return shape; 10783 10784 let { x0, x1, x2, x3, y1, y2 } = shape; 10785 10786 // create movement string 10787 return `M ${x0}, 50 10788 L ${x1}, ${y1 + 50} 10789 L ${x2}, ${y2 + 50} 10790 L ${x3}, 50 10791 L ${x2}, ${50 - y2} 10792 L ${x1}, ${50 - y1} 10793 Z`; 10794} 10795 10796// ----- ----- // 10797 10798PrevNextButton.prototype.enable = function() { 10799 this.element.removeAttribute('disabled'); 10800}; 10801 10802PrevNextButton.prototype.disable = function() { 10803 this.element.setAttribute( 'disabled', true ); 10804}; 10805 10806// -------------------------- Flickity prototype -------------------------- // 10807 10808Object.assign( Flickity.defaults, { 10809 prevNextButtons: true, 10810 arrowShape: { 10811 x0: 10, 10812 x1: 60, y1: 50, 10813 x2: 70, y2: 40, 10814 x3: 30, 10815 }, 10816} ); 10817 10818Flickity.create.prevNextButtons = function() { 10819 if ( !this.options.prevNextButtons ) return; 10820 10821 let { rightToLeft, arrowShape } = this.options; 10822 let prevDirection = rightToLeft ? 'right' : 'left'; 10823 let nextDirection = rightToLeft ? 'left' : 'right'; 10824 this.prevButton = new PrevNextButton( 'previous', prevDirection, arrowShape ); 10825 this.nextButton = new PrevNextButton( 'next', nextDirection, arrowShape ); 10826 this.focusableElems.push( this.prevButton.element ); 10827 this.focusableElems.push( this.nextButton.element ); 10828 10829 this.handlePrevButtonClick = () => { 10830 this.uiChange(); 10831 this.previous(); 10832 }; 10833 10834 this.handleNextButtonClick = () => { 10835 this.uiChange(); 10836 this.next(); 10837 }; 10838 10839 this.on( 'activate', this.activatePrevNextButtons ); 10840 this.on( 'select', this.updatePrevNextButtons ); 10841}; 10842 10843let proto = Flickity.prototype; 10844 10845proto.updatePrevNextButtons = function() { 10846 let lastIndex = this.slides.length ? this.slides.length - 1 : 0; 10847 this.updatePrevNextButton( this.prevButton, 0 ); 10848 this.updatePrevNextButton( this.nextButton, lastIndex ); 10849}; 10850 10851proto.updatePrevNextButton = function( button, disabledIndex ) { 10852 // enable is wrapAround and at least 2 slides 10853 if ( this.isWrapping && this.slides.length > 1 ) { 10854 button.enable(); 10855 return; 10856 } 10857 10858 let isEnabled = this.selectedIndex !== disabledIndex; 10859 button[ isEnabled ? 'enable' : 'disable' ](); 10860 // if disabling button that is focused, 10861 // shift focus to element to maintain keyboard accessibility 10862 let isDisabledFocused = !isEnabled && document.activeElement === button.element; 10863 if ( isDisabledFocused ) this.focus(); 10864}; 10865 10866proto.activatePrevNextButtons = function() { 10867 this.prevButton.element.addEventListener( 'click', this.handlePrevButtonClick ); 10868 this.nextButton.element.addEventListener( 'click', this.handleNextButtonClick ); 10869 this.element.append( this.prevButton.element, this.nextButton.element ); 10870 this.on( 'deactivate', this.deactivatePrevNextButtons ); 10871}; 10872 10873proto.deactivatePrevNextButtons = function() { 10874 this.prevButton.element.remove(); 10875 this.nextButton.element.remove(); 10876 this.prevButton.element.removeEventListener( 'click', this.handlePrevButtonClick ); 10877 this.nextButton.element.removeEventListener( 'click', this.handleNextButtonClick ); 10878 this.off( 'deactivate', this.deactivatePrevNextButtons ); 10879}; 10880 10881// -------------------------- -------------------------- // 10882 10883Flickity.PrevNextButton = PrevNextButton; 10884 10885return Flickity; 10886 10887} ) ); 10888// page dots 10889( function( window, factory ) { 10890 // universal module definition 10891 if ( typeof module == 'object' && module.exports ) { 10892 // CommonJS 10893 module.exports = factory( 10894 require('./core'), 10895 require('fizzy-ui-utils'), 10896 ); 10897 } else { 10898 // browser global 10899 factory( 10900 window.Flickity, 10901 window.fizzyUIUtils, 10902 ); 10903 } 10904 10905}( typeof window != 'undefined' ? window : this, function factory( Flickity, utils ) { 10906 10907// -------------------------- PageDots -------------------------- // 10908 10909function PageDots() { 10910 // create holder element 10911 this.holder = document.createElement('div'); 10912 this.holder.className = 'flickity-page-dots'; 10913 // create dots, array of elements 10914 this.dots = []; 10915} 10916 10917PageDots.prototype.setDots = function( slidesLength ) { 10918 // get difference between number of slides and number of dots 10919 let delta = slidesLength - this.dots.length; 10920 if ( delta > 0 ) { 10921 this.addDots( delta ); 10922 } else if ( delta < 0 ) { 10923 this.removeDots( -delta ); 10924 } 10925}; 10926 10927PageDots.prototype.addDots = function( count ) { 10928 let newDots = new Array( count ).fill() 10929 .map( ( item, i ) => { 10930 let dot = document.createElement('button'); 10931 dot.setAttribute( 'type', 'button' ); 10932 let num = i + 1 + this.dots.length; 10933 dot.className = 'flickity-page-dot'; 10934 dot.textContent = `View slide ${num}`; 10935 return dot; 10936 } ); 10937 10938 this.holder.append( ...newDots ); 10939 this.dots = this.dots.concat( newDots ); 10940}; 10941 10942PageDots.prototype.removeDots = function( count ) { 10943 // remove from this.dots collection 10944 let removeDots = this.dots.splice( this.dots.length - count, count ); 10945 // remove from DOM 10946 removeDots.forEach( ( dot ) => dot.remove() ); 10947}; 10948 10949PageDots.prototype.updateSelected = function( index ) { 10950 // remove selected class on previous 10951 if ( this.selectedDot ) { 10952 this.selectedDot.classList.remove('is-selected'); 10953 this.selectedDot.removeAttribute('aria-current'); 10954 } 10955 // don't proceed if no dots 10956 if ( !this.dots.length ) return; 10957 10958 this.selectedDot = this.dots[ index ]; 10959 this.selectedDot.classList.add('is-selected'); 10960 this.selectedDot.setAttribute( 'aria-current', 'step' ); 10961}; 10962 10963Flickity.PageDots = PageDots; 10964 10965// -------------------------- Flickity -------------------------- // 10966 10967Object.assign( Flickity.defaults, { 10968 pageDots: true, 10969} ); 10970 10971Flickity.create.pageDots = function() { 10972 if ( !this.options.pageDots ) return; 10973 10974 this.pageDots = new PageDots(); 10975 this.handlePageDotsClick = this.onPageDotsClick.bind( this ); 10976 // events 10977 this.on( 'activate', this.activatePageDots ); 10978 this.on( 'select', this.updateSelectedPageDots ); 10979 this.on( 'cellChange', this.updatePageDots ); 10980 this.on( 'resize', this.updatePageDots ); 10981 this.on( 'deactivate', this.deactivatePageDots ); 10982}; 10983 10984let proto = Flickity.prototype; 10985 10986proto.activatePageDots = function() { 10987 this.pageDots.setDots( this.slides.length ); 10988 this.focusableElems.push( ...this.pageDots.dots ); 10989 this.pageDots.holder.addEventListener( 'click', this.handlePageDotsClick ); 10990 this.element.append( this.pageDots.holder ); 10991}; 10992 10993proto.onPageDotsClick = function( event ) { 10994 let index = this.pageDots.dots.indexOf( event.target ); 10995 if ( index === -1 ) return; // only dot clicks 10996 10997 this.uiChange(); 10998 this.select( index ); 10999}; 11000 11001proto.updateSelectedPageDots = function() { 11002 this.pageDots.updateSelected( this.selectedIndex ); 11003}; 11004 11005proto.updatePageDots = function() { 11006 this.pageDots.dots.forEach( ( dot ) => { 11007 utils.removeFrom( this.focusableElems, dot ); 11008 } ); 11009 this.pageDots.setDots( this.slides.length ); 11010 this.focusableElems.push( ...this.pageDots.dots ); 11011}; 11012 11013proto.deactivatePageDots = function() { 11014 this.pageDots.holder.remove(); 11015 this.pageDots.holder.removeEventListener( 'click', this.handlePageDotsClick ); 11016}; 11017 11018// ----- ----- // 11019 11020Flickity.PageDots = PageDots; 11021 11022return Flickity; 11023 11024} ) ); 11025// player & autoPlay 11026( function( window, factory ) { 11027 // universal module definition 11028 if ( typeof module == 'object' && module.exports ) { 11029 // CommonJS 11030 module.exports = factory( require('./core') ); 11031 } else { 11032 // browser global 11033 factory( window.Flickity ); 11034 } 11035 11036}( typeof window != 'undefined' ? window : this, function factory( Flickity ) { 11037 11038// -------------------------- Player -------------------------- // 11039 11040function Player( autoPlay, onTick ) { 11041 this.autoPlay = autoPlay; 11042 this.onTick = onTick; 11043 this.state = 'stopped'; 11044 // visibility change event handler 11045 this.onVisibilityChange = this.visibilityChange.bind( this ); 11046 this.onVisibilityPlay = this.visibilityPlay.bind( this ); 11047} 11048 11049// start play 11050Player.prototype.play = function() { 11051 if ( this.state === 'playing' ) return; 11052 11053 // do not play if page is hidden, start playing when page is visible 11054 let isPageHidden = document.hidden; 11055 if ( isPageHidden ) { 11056 document.addEventListener( 'visibilitychange', this.onVisibilityPlay ); 11057 return; 11058 } 11059 11060 this.state = 'playing'; 11061 // listen to visibility change 11062 document.addEventListener( 'visibilitychange', this.onVisibilityChange ); 11063 // start ticking 11064 this.tick(); 11065}; 11066 11067Player.prototype.tick = function() { 11068 // do not tick if not playing 11069 if ( this.state !== 'playing' ) return; 11070 11071 // default to 3 seconds 11072 let time = typeof this.autoPlay == 'number' ? this.autoPlay : 3000; 11073 // HACK: reset ticks if stopped and started within interval 11074 this.clear(); 11075 this.timeout = setTimeout( () => { 11076 this.onTick(); 11077 this.tick(); 11078 }, time ); 11079}; 11080 11081Player.prototype.stop = function() { 11082 this.state = 'stopped'; 11083 this.clear(); 11084 // remove visibility change event 11085 document.removeEventListener( 'visibilitychange', this.onVisibilityChange ); 11086}; 11087 11088Player.prototype.clear = function() { 11089 clearTimeout( this.timeout ); 11090}; 11091 11092Player.prototype.pause = function() { 11093 if ( this.state === 'playing' ) { 11094 this.state = 'paused'; 11095 this.clear(); 11096 } 11097}; 11098 11099Player.prototype.unpause = function() { 11100 // re-start play if paused 11101 if ( this.state === 'paused' ) this.play(); 11102}; 11103 11104// pause if page visibility is hidden, unpause if visible 11105Player.prototype.visibilityChange = function() { 11106 let isPageHidden = document.hidden; 11107 this[ isPageHidden ? 'pause' : 'unpause' ](); 11108}; 11109 11110Player.prototype.visibilityPlay = function() { 11111 this.play(); 11112 document.removeEventListener( 'visibilitychange', this.onVisibilityPlay ); 11113}; 11114 11115// -------------------------- Flickity -------------------------- // 11116 11117Object.assign( Flickity.defaults, { 11118 pauseAutoPlayOnHover: true, 11119} ); 11120 11121Flickity.create.player = function() { 11122 this.player = new Player( this.options.autoPlay, () => { 11123 this.next( true ); 11124 } ); 11125 11126 this.on( 'activate', this.activatePlayer ); 11127 this.on( 'uiChange', this.stopPlayer ); 11128 this.on( 'pointerDown', this.stopPlayer ); 11129 this.on( 'deactivate', this.deactivatePlayer ); 11130}; 11131 11132let proto = Flickity.prototype; 11133 11134proto.activatePlayer = function() { 11135 if ( !this.options.autoPlay ) return; 11136 11137 this.player.play(); 11138 this.element.addEventListener( 'mouseenter', this ); 11139}; 11140 11141// Player API, don't hate the ... thanks I know where the door is 11142 11143proto.playPlayer = function() { 11144 this.player.play(); 11145}; 11146 11147proto.stopPlayer = function() { 11148 this.player.stop(); 11149}; 11150 11151proto.pausePlayer = function() { 11152 this.player.pause(); 11153}; 11154 11155proto.unpausePlayer = function() { 11156 this.player.unpause(); 11157}; 11158 11159proto.deactivatePlayer = function() { 11160 this.player.stop(); 11161 this.element.removeEventListener( 'mouseenter', this ); 11162}; 11163 11164// ----- mouseenter/leave ----- // 11165 11166// pause auto-play on hover 11167proto.onmouseenter = function() { 11168 if ( !this.options.pauseAutoPlayOnHover ) return; 11169 11170 this.player.pause(); 11171 this.element.addEventListener( 'mouseleave', this ); 11172}; 11173 11174// resume auto-play on hover off 11175proto.onmouseleave = function() { 11176 this.player.unpause(); 11177 this.element.removeEventListener( 'mouseleave', this ); 11178}; 11179 11180// ----- ----- // 11181 11182Flickity.Player = Player; 11183 11184return Flickity; 11185 11186} ) ); 11187// add, remove cell 11188( function( window, factory ) { 11189 // universal module definition 11190 if ( typeof module == 'object' && module.exports ) { 11191 // CommonJS 11192 module.exports = factory( 11193 require('./core'), 11194 require('fizzy-ui-utils'), 11195 ); 11196 } else { 11197 // browser global 11198 factory( 11199 window.Flickity, 11200 window.fizzyUIUtils, 11201 ); 11202 } 11203 11204}( typeof window != 'undefined' ? window : this, function factory( Flickity, utils ) { 11205 11206// append cells to a document fragment 11207function getCellsFragment( cells ) { 11208 let fragment = document.createDocumentFragment(); 11209 cells.forEach( ( cell ) => fragment.appendChild( cell.element ) ); 11210 return fragment; 11211} 11212 11213// -------------------------- add/remove cell prototype -------------------------- // 11214 11215let proto = Flickity.prototype; 11216 11217/** 11218 * Insert, prepend, or append cells 11219 * @param {[Element, Array, NodeList]} elems - Elements to insert 11220 * @param {Integer} index - Zero-based number to insert 11221 */ 11222proto.insert = function( elems, index ) { 11223 let cells = this._makeCells( elems ); 11224 if ( !cells || !cells.length ) return; 11225 11226 let len = this.cells.length; 11227 // default to append 11228 index = index === undefined ? len : index; 11229 // add cells with document fragment 11230 let fragment = getCellsFragment( cells ); 11231 // append to slider 11232 let isAppend = index === len; 11233 if ( isAppend ) { 11234 this.slider.appendChild( fragment ); 11235 } else { 11236 let insertCellElement = this.cells[ index ].element; 11237 this.slider.insertBefore( fragment, insertCellElement ); 11238 } 11239 // add to this.cells 11240 if ( index === 0 ) { 11241 // prepend, add to start 11242 this.cells = cells.concat( this.cells ); 11243 } else if ( isAppend ) { 11244 // append, add to end 11245 this.cells = this.cells.concat( cells ); 11246 } else { 11247 // insert in this.cells 11248 let endCells = this.cells.splice( index, len - index ); 11249 this.cells = this.cells.concat( cells ).concat( endCells ); 11250 } 11251 11252 this._sizeCells( cells ); 11253 this.cellChange( index ); 11254 this.positionSliderAtSelected(); 11255}; 11256 11257proto.append = function( elems ) { 11258 this.insert( elems, this.cells.length ); 11259}; 11260 11261proto.prepend = function( elems ) { 11262 this.insert( elems, 0 ); 11263}; 11264 11265/** 11266 * Remove cells 11267 * @param {[Element, Array, NodeList]} elems - ELements to remove 11268 */ 11269proto.remove = function( elems ) { 11270 let cells = this.getCells( elems ); 11271 if ( !cells || !cells.length ) return; 11272 11273 let minCellIndex = this.cells.length - 1; 11274 // remove cells from collection & DOM 11275 cells.forEach( ( cell ) => { 11276 cell.remove(); 11277 let index = this.cells.indexOf( cell ); 11278 minCellIndex = Math.min( index, minCellIndex ); 11279 utils.removeFrom( this.cells, cell ); 11280 } ); 11281 11282 this.cellChange( minCellIndex ); 11283 this.positionSliderAtSelected(); 11284}; 11285 11286/** 11287 * logic to be run after a cell's size changes 11288 * @param {Element} elem - cell's element 11289 */ 11290proto.cellSizeChange = function( elem ) { 11291 let cell = this.getCell( elem ); 11292 if ( !cell ) return; 11293 11294 cell.getSize(); 11295 11296 let index = this.cells.indexOf( cell ); 11297 this.cellChange( index ); 11298 // do not position slider after lazy load 11299}; 11300 11301/** 11302 * logic any time a cell is changed: added, removed, or size changed 11303 * @param {Integer} changedCellIndex - index of the changed cell, optional 11304 */ 11305proto.cellChange = function( changedCellIndex ) { 11306 let prevSelectedElem = this.selectedElement; 11307 this._positionCells( changedCellIndex ); 11308 this._updateWrapShiftCells(); 11309 this.setGallerySize(); 11310 // update selectedIndex, try to maintain position & select previous selected element 11311 let cell = this.getCell( prevSelectedElem ); 11312 if ( cell ) this.selectedIndex = this.getCellSlideIndex( cell ); 11313 this.selectedIndex = Math.min( this.slides.length - 1, this.selectedIndex ); 11314 11315 this.emitEvent( 'cellChange', [ changedCellIndex ] ); 11316 // position slider 11317 this.select( this.selectedIndex ); 11318}; 11319 11320// ----- ----- // 11321 11322return Flickity; 11323 11324} ) ); 11325// lazyload 11326( function( window, factory ) { 11327 // universal module definition 11328 if ( typeof module == 'object' && module.exports ) { 11329 // CommonJS 11330 module.exports = factory( 11331 require('./core'), 11332 require('fizzy-ui-utils'), 11333 ); 11334 } else { 11335 // browser global 11336 factory( 11337 window.Flickity, 11338 window.fizzyUIUtils, 11339 ); 11340 } 11341 11342}( typeof window != 'undefined' ? window : this, function factory( Flickity, utils ) { 11343 11344const lazyAttr = 'data-flickity-lazyload'; 11345const lazySrcAttr = `${lazyAttr}-src`; 11346const lazySrcsetAttr = `${lazyAttr}-srcset`; 11347const imgSelector = `img[${lazyAttr}], img[${lazySrcAttr}], ` + 11348 `img[${lazySrcsetAttr}], source[${lazySrcsetAttr}]`; 11349 11350Flickity.create.lazyLoad = function() { 11351 this.on( 'select', this.lazyLoad ); 11352 11353 this.handleLazyLoadComplete = this.onLazyLoadComplete.bind( this ); 11354}; 11355 11356let proto = Flickity.prototype; 11357 11358proto.lazyLoad = function() { 11359 let { lazyLoad } = this.options; 11360 if ( !lazyLoad ) return; 11361 11362 // get adjacent cells, use lazyLoad option for adjacent count 11363 let adjCount = typeof lazyLoad == 'number' ? lazyLoad : 0; 11364 // lazy load images 11365 this.getAdjacentCellElements( adjCount ) 11366 .map( getCellLazyImages ) 11367 .flat() 11368 .forEach( ( img ) => new LazyLoader( img, this.handleLazyLoadComplete ) ); 11369}; 11370 11371function getCellLazyImages( cellElem ) { 11372 // check if cell element is lazy image 11373 if ( cellElem.matches('img') ) { 11374 let cellAttr = cellElem.getAttribute( lazyAttr ); 11375 let cellSrcAttr = cellElem.getAttribute( lazySrcAttr ); 11376 let cellSrcsetAttr = cellElem.getAttribute( lazySrcsetAttr ); 11377 if ( cellAttr || cellSrcAttr || cellSrcsetAttr ) { 11378 return cellElem; 11379 } 11380 } 11381 // select lazy images in cell 11382 return [ ...cellElem.querySelectorAll( imgSelector ) ]; 11383} 11384 11385proto.onLazyLoadComplete = function( img, event ) { 11386 let cell = this.getParentCell( img ); 11387 let cellElem = cell && cell.element; 11388 this.cellSizeChange( cellElem ); 11389 11390 this.dispatchEvent( 'lazyLoad', event, cellElem ); 11391}; 11392 11393// -------------------------- LazyLoader -------------------------- // 11394 11395/** 11396 * class to handle loading images 11397 * @param {Image} img - Image element 11398 * @param {Function} onComplete - callback function 11399 */ 11400function LazyLoader( img, onComplete ) { 11401 this.img = img; 11402 this.onComplete = onComplete; 11403 this.load(); 11404} 11405 11406LazyLoader.prototype.handleEvent = utils.handleEvent; 11407 11408LazyLoader.prototype.load = function() { 11409 this.img.addEventListener( 'load', this ); 11410 this.img.addEventListener( 'error', this ); 11411 // get src & srcset 11412 let src = this.img.getAttribute( lazyAttr ) || 11413 this.img.getAttribute( lazySrcAttr ); 11414 let srcset = this.img.getAttribute( lazySrcsetAttr ); 11415 // set src & serset 11416 this.img.src = src; 11417 if ( srcset ) this.img.setAttribute( 'srcset', srcset ); 11418 // remove attr 11419 this.img.removeAttribute( lazyAttr ); 11420 this.img.removeAttribute( lazySrcAttr ); 11421 this.img.removeAttribute( lazySrcsetAttr ); 11422}; 11423 11424LazyLoader.prototype.onload = function( event ) { 11425 this.complete( event, 'flickity-lazyloaded' ); 11426}; 11427 11428LazyLoader.prototype.onerror = function( event ) { 11429 this.complete( event, 'flickity-lazyerror' ); 11430}; 11431 11432LazyLoader.prototype.complete = function( event, className ) { 11433 // unbind events 11434 this.img.removeEventListener( 'load', this ); 11435 this.img.removeEventListener( 'error', this ); 11436 let mediaElem = this.img.parentNode.matches('picture') ? this.img.parentNode : this.img; 11437 mediaElem.classList.add( className ); 11438 11439 this.onComplete( this.img, event ); 11440}; 11441 11442// ----- ----- // 11443 11444Flickity.LazyLoader = LazyLoader; 11445 11446return Flickity; 11447 11448} ) ); 11449// imagesloaded 11450( function( window, factory ) { 11451 // universal module definition 11452 if ( typeof module == 'object' && module.exports ) { 11453 // CommonJS 11454 module.exports = factory( 11455 require('./core'), 11456 require('imagesloaded'), 11457 ); 11458 } else { 11459 // browser global 11460 factory( 11461 window.Flickity, 11462 window.imagesLoaded, 11463 ); 11464 } 11465 11466}( typeof window != 'undefined' ? window : this, 11467 function factory( Flickity, imagesLoaded ) { 11468 11469Flickity.create.imagesLoaded = function() { 11470 this.on( 'activate', this.imagesLoaded ); 11471}; 11472 11473Flickity.prototype.imagesLoaded = function() { 11474 if ( !this.options.imagesLoaded ) return; 11475 11476 let onImagesLoadedProgress = ( instance, image ) => { 11477 let cell = this.getParentCell( image.img ); 11478 this.cellSizeChange( cell && cell.element ); 11479 if ( !this.options.freeScroll ) this.positionSliderAtSelected(); 11480 }; 11481 imagesLoaded( this.slider ).on( 'progress', onImagesLoadedProgress ); 11482}; 11483 11484return Flickity; 11485 11486} ) );
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.