alexlcdee 8 years ago
commit
1a663e123b
100 changed files with 19390 additions and 0 deletions
  1. 1 0
      .gitignore
  2. 17 0
      composer.json
  3. 26 0
      src/Yii.php
  4. 24 0
      src/assets/BoosterBundle.php
  5. 23 0
      src/assets/FancyboxAsset.php
  6. 18 0
      src/assets/JQueryAsset.php
  7. 25 0
      src/assets/Select2Asset.php
  8. BIN
      src/assets/fancybox/css/blank.gif
  9. BIN
      src/assets/fancybox/css/fancybox_loading.gif
  10. BIN
      src/assets/fancybox/css/fancybox_loading@2x.gif
  11. BIN
      src/assets/fancybox/css/fancybox_overlay.png
  12. BIN
      src/assets/fancybox/css/fancybox_sprite.png
  13. BIN
      src/assets/fancybox/css/fancybox_sprite@2x.png
  14. BIN
      src/assets/fancybox/css/helpers/fancybox_buttons.png
  15. 97 0
      src/assets/fancybox/css/helpers/jquery.fancybox-buttons.css
  16. 122 0
      src/assets/fancybox/css/helpers/jquery.fancybox-buttons.js
  17. 201 0
      src/assets/fancybox/css/helpers/jquery.fancybox-media.js
  18. 55 0
      src/assets/fancybox/css/helpers/jquery.fancybox-thumbs.css
  19. 165 0
      src/assets/fancybox/css/helpers/jquery.fancybox-thumbs.js
  20. 272 0
      src/assets/fancybox/css/jquery.fancybox.css
  21. 2020 0
      src/assets/fancybox/js/jquery.fancybox.js
  22. 46 0
      src/assets/fancybox/js/jquery.fancybox.pack.js
  23. 634 0
      src/assets/select2/css/select2.css
  24. 0 0
      src/assets/select2/css/select2.min.css
  25. 3 0
      src/assets/select2/js/i18n/ar.js
  26. 3 0
      src/assets/select2/js/i18n/az.js
  27. 3 0
      src/assets/select2/js/i18n/bg.js
  28. 3 0
      src/assets/select2/js/i18n/ca.js
  29. 3 0
      src/assets/select2/js/i18n/cs.js
  30. 3 0
      src/assets/select2/js/i18n/da.js
  31. 3 0
      src/assets/select2/js/i18n/de.js
  32. 3 0
      src/assets/select2/js/i18n/el.js
  33. 3 0
      src/assets/select2/js/i18n/en.js
  34. 3 0
      src/assets/select2/js/i18n/es.js
  35. 3 0
      src/assets/select2/js/i18n/et.js
  36. 3 0
      src/assets/select2/js/i18n/eu.js
  37. 3 0
      src/assets/select2/js/i18n/fa.js
  38. 3 0
      src/assets/select2/js/i18n/fi.js
  39. 3 0
      src/assets/select2/js/i18n/fr.js
  40. 3 0
      src/assets/select2/js/i18n/gl.js
  41. 3 0
      src/assets/select2/js/i18n/he.js
  42. 3 0
      src/assets/select2/js/i18n/hi.js
  43. 3 0
      src/assets/select2/js/i18n/hr.js
  44. 3 0
      src/assets/select2/js/i18n/hu.js
  45. 3 0
      src/assets/select2/js/i18n/id.js
  46. 3 0
      src/assets/select2/js/i18n/is.js
  47. 3 0
      src/assets/select2/js/i18n/it.js
  48. 3 0
      src/assets/select2/js/i18n/ja.js
  49. 3 0
      src/assets/select2/js/i18n/km.js
  50. 3 0
      src/assets/select2/js/i18n/ko.js
  51. 3 0
      src/assets/select2/js/i18n/lt.js
  52. 3 0
      src/assets/select2/js/i18n/lv.js
  53. 3 0
      src/assets/select2/js/i18n/mk.js
  54. 3 0
      src/assets/select2/js/i18n/ms.js
  55. 3 0
      src/assets/select2/js/i18n/nb.js
  56. 3 0
      src/assets/select2/js/i18n/nl.js
  57. 3 0
      src/assets/select2/js/i18n/pl.js
  58. 3 0
      src/assets/select2/js/i18n/pt-BR.js
  59. 3 0
      src/assets/select2/js/i18n/pt.js
  60. 3 0
      src/assets/select2/js/i18n/ro.js
  61. 3 0
      src/assets/select2/js/i18n/ru.js
  62. 3 0
      src/assets/select2/js/i18n/sk.js
  63. 3 0
      src/assets/select2/js/i18n/sr-Cyrl.js
  64. 3 0
      src/assets/select2/js/i18n/sr.js
  65. 3 0
      src/assets/select2/js/i18n/sv.js
  66. 3 0
      src/assets/select2/js/i18n/th.js
  67. 3 0
      src/assets/select2/js/i18n/tr.js
  68. 3 0
      src/assets/select2/js/i18n/uk.js
  69. 3 0
      src/assets/select2/js/i18n/vi.js
  70. 3 0
      src/assets/select2/js/i18n/zh-CN.js
  71. 3 0
      src/assets/select2/js/i18n/zh-TW.js
  72. 6438 0
      src/assets/select2/js/select2.full.js
  73. 0 0
      src/assets/select2/js/select2.full.min.js
  74. 5717 0
      src/assets/select2/js/select2.js
  75. 0 0
      src/assets/select2/js/select2.min.js
  76. 211 0
      src/components/ActiveRecord.php
  77. 51 0
      src/components/AliasedBehavior.php
  78. 154 0
      src/components/BatchQueryResult.php
  79. 69 0
      src/components/CompositeUniqueValidation.php
  80. 51 0
      src/components/Enum.php
  81. 37 0
      src/components/EnumValidator.php
  82. 25 0
      src/components/FakeUploadedFile.php
  83. 47 0
      src/components/FancyBox.php
  84. 323 0
      src/components/ImageGD.php
  85. 21 0
      src/components/NSPost.php
  86. 971 0
      src/components/NestedSetBehavior.php
  87. 124 0
      src/components/Select2.php
  88. 65 0
      src/components/Update.php
  89. 52 0
      src/console/Application.php
  90. 95 0
      src/console/CommandRunner.php
  91. 8 0
      src/console/Controller.php
  92. 15 0
      src/exceptions/InvalidConfigException.php
  93. 15 0
      src/exceptions/InvalidParamException.php
  94. 15 0
      src/exceptions/InvalidValueException.php
  95. 639 0
      src/helpers/ArrayHelper.php
  96. 22 0
      src/helpers/HtmlHelper.php
  97. 131 0
      src/helpers/StringsHelper.php
  98. 38 0
      src/helpers/VariableHelper.php
  99. 37 0
      src/web/Action.php
  100. 132 0
      src/web/Application.php

+ 1 - 0
.gitignore

@@ -0,0 +1 @@
+.idea/

+ 17 - 0
composer.json

@@ -0,0 +1,17 @@
+{
+  "name": "adyn/yiins",
+  "description": "Namespaced Yii 1.1 Framework",
+  "type": "library",
+  "autoload": {
+    "psr-4": {
+      "yiins\\": "src"
+    },
+    "classmap": [
+      "src/Yii.php"
+    ]
+  },
+  "require": {
+    "yiisoft/yii": "~1.1.17",
+    "php": ">=7.0"
+  }
+}

+ 26 - 0
src/Yii.php

@@ -0,0 +1,26 @@
+<?php
+
+/**
+ * Created by PhpStorm.
+ * User: AlexLcDee
+ * Date: 23.01.2017
+ * Time: 17:08
+ */
+class Yii extends YiiBase
+{
+    /**
+     * @return \yiins\web\Application
+     */
+    public static function web()
+    {
+        return static::app();
+    }
+
+    /**
+     * @return \yiins\console\Application
+     */
+    public static function cli()
+    {
+        return static::app();
+    }
+}

+ 24 - 0
src/assets/BoosterBundle.php

@@ -0,0 +1,24 @@
+<?php
+
+namespace yiins\assets;
+
+use Yii;
+use yiins\exceptions\InvalidConfigException;
+use yiins\web\AssetBundle;
+
+class BoosterBundle extends AssetBundle
+{
+    public $depends = [
+        JQueryAsset::class,
+    ];
+
+    public function init()
+    {
+        /** @var \Booster $booster */
+        if (($booster = Yii::web()->getComponent('booster')) == null) {
+            throw new InvalidConfigException(__CLASS__ . ' requires booster component configured');
+        }
+        $booster->init();
+        parent::init();
+    }
+}

+ 23 - 0
src/assets/FancyboxAsset.php

@@ -0,0 +1,23 @@
+<?php
+
+namespace yiins\assets;
+
+
+use yiins\web\AssetBundle;
+
+class FancyboxAsset extends AssetBundle
+{
+    public $sourcePath = '@base/assets/fancybox';
+
+    public $depends = [
+        JQueryAsset::class => \CClientScript::POS_HEAD,
+    ];
+
+    public $css = [
+        'css/jquery.fancybox.css',
+    ];
+
+    public $js = [
+        'js/jquery.fancybox.js',
+    ];
+}

+ 18 - 0
src/assets/JQueryAsset.php

@@ -0,0 +1,18 @@
+<?php
+
+namespace yiins\assets;
+
+
+use yiins\web\AssetBundle;
+
+class JQueryAsset extends AssetBundle
+{
+    public $js = [
+        [
+            'https://code.jquery.com/jquery-2.2.4.js',
+            'integrity'   => 'sha256-iT6Q9iMJYuQiMWNd9lDyBUStIq/8PuOW33aOqmvFpqI=',
+            'crossorigin' => 'anonymous',
+        ],
+    ];
+
+}

+ 25 - 0
src/assets/Select2Asset.php

@@ -0,0 +1,25 @@
+<?php
+
+namespace yiins\assets;
+
+use Yii;
+use yiins\web\AssetBundle;
+
+class Select2Asset extends AssetBundle
+{
+    public $sourcePath = '@base/assets/select2';
+
+    public $css = [
+        'css/select2.css',
+    ];
+
+    public $js = [
+        'js/select2.full.js',
+    ];
+
+    public function init()
+    {
+        $this->js[] = 'js/i18n/' . Yii::app()->language . '.js';
+        parent::init();
+    }
+}

BIN
src/assets/fancybox/css/blank.gif


BIN
src/assets/fancybox/css/fancybox_loading.gif


BIN
src/assets/fancybox/css/fancybox_loading@2x.gif


BIN
src/assets/fancybox/css/fancybox_overlay.png


BIN
src/assets/fancybox/css/fancybox_sprite.png


BIN
src/assets/fancybox/css/fancybox_sprite@2x.png


BIN
src/assets/fancybox/css/helpers/fancybox_buttons.png


+ 97 - 0
src/assets/fancybox/css/helpers/jquery.fancybox-buttons.css

@@ -0,0 +1,97 @@
+#fancybox-buttons {
+    position: fixed;
+    left: 0;
+    width: 100%;
+    z-index: 8050;
+}
+
+#fancybox-buttons.top {
+    top: 10px;
+}
+
+#fancybox-buttons.bottom {
+    bottom: 10px;
+}
+
+#fancybox-buttons ul {
+    display: block;
+    width: 166px;
+    height: 30px;
+    margin: 0 auto;
+    padding: 0;
+    list-style: none;
+    border: 1px solid #111;
+    border-radius: 3px;
+    -webkit-box-shadow: inset 0 0 0 1px rgba(255, 255, 255, .05);
+    -moz-box-shadow: inset 0 0 0 1px rgba(255, 255, 255, .05);
+    box-shadow: inset 0 0 0 1px rgba(255, 255, 255, .05);
+    background: rgb(50, 50, 50);
+    background: -moz-linear-gradient(top, rgb(68, 68, 68) 0%, rgb(52, 52, 52) 50%, rgb(41, 41, 41) 50%, rgb(51, 51, 51) 100%);
+    background: -webkit-gradient(linear, left top, left bottom, color-stop(0%, rgb(68, 68, 68)), color-stop(50%, rgb(52, 52, 52)), color-stop(50%, rgb(41, 41, 41)), color-stop(100%, rgb(51, 51, 51)));
+    background: -webkit-linear-gradient(top, rgb(68, 68, 68) 0%, rgb(52, 52, 52) 50%, rgb(41, 41, 41) 50%, rgb(51, 51, 51) 100%);
+    background: -o-linear-gradient(top, rgb(68, 68, 68) 0%, rgb(52, 52, 52) 50%, rgb(41, 41, 41) 50%, rgb(51, 51, 51) 100%);
+    background: -ms-linear-gradient(top, rgb(68, 68, 68) 0%, rgb(52, 52, 52) 50%, rgb(41, 41, 41) 50%, rgb(51, 51, 51) 100%);
+    background: linear-gradient(to bottom, rgb(68, 68, 68) 0%, rgb(52, 52, 52) 50%, rgb(41, 41, 41) 50%, rgb(51, 51, 51) 100%);
+    filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#444444', endColorstr='#222222', GradientType=0);
+}
+
+#fancybox-buttons ul li {
+    float: left;
+    margin: 0;
+    padding: 0;
+}
+
+#fancybox-buttons a {
+    display: block;
+    width: 30px;
+    height: 30px;
+    text-indent: -9999px;
+    background-color: transparent;
+    background-image: url('fancybox_buttons.png');
+    background-repeat: no-repeat;
+    outline: none;
+    opacity: 0.8;
+}
+
+#fancybox-buttons a:hover {
+    opacity: 1;
+}
+
+#fancybox-buttons a.btnPrev {
+    background-position: 5px 0;
+}
+
+#fancybox-buttons a.btnNext {
+    background-position: -33px 0;
+    border-right: 1px solid #3e3e3e;
+}
+
+#fancybox-buttons a.btnPlay {
+    background-position: 0 -30px;
+}
+
+#fancybox-buttons a.btnPlayOn {
+    background-position: -30px -30px;
+}
+
+#fancybox-buttons a.btnToggle {
+    background-position: 3px -60px;
+    border-left: 1px solid #111;
+    border-right: 1px solid #3e3e3e;
+    width: 35px
+}
+
+#fancybox-buttons a.btnToggleOn {
+    background-position: -27px -60px;
+}
+
+#fancybox-buttons a.btnClose {
+    border-left: 1px solid #111;
+    width: 35px;
+    background-position: -56px 0px;
+}
+
+#fancybox-buttons a.btnDisabled {
+    opacity: 0.4;
+    cursor: default;
+}

+ 122 - 0
src/assets/fancybox/css/helpers/jquery.fancybox-buttons.js

@@ -0,0 +1,122 @@
+/*!
+ * Buttons helper for fancyBox
+ * version: 1.0.5 (Mon, 15 Oct 2012)
+ * @requires fancyBox v2.0 or later
+ *
+ * Usage:
+ *     $(".fancybox").fancybox({
+ *         helpers : {
+ *             buttons: {
+ *                 position : 'top'
+ *             }
+ *         }
+ *     });
+ *
+ */
+;(function ($) {
+    //Shortcut for fancyBox object
+    var F = $.fancybox;
+
+    //Add helper object
+    F.helpers.buttons = {
+        defaults: {
+            skipSingle: false, // disables if gallery contains single image
+            position: 'top', // 'top' or 'bottom'
+            tpl: '<div id="fancybox-buttons"><ul><li><a class="btnPrev" title="Previous" href="javascript:;"></a></li><li><a class="btnPlay" title="Start slideshow" href="javascript:;"></a></li><li><a class="btnNext" title="Next" href="javascript:;"></a></li><li><a class="btnToggle" title="Toggle size" href="javascript:;"></a></li><li><a class="btnClose" title="Close" href="javascript:;"></a></li></ul></div>'
+        },
+
+        list: null,
+        buttons: null,
+
+        beforeLoad: function (opts, obj) {
+            //Remove self if gallery do not have at least two items
+
+            if (opts.skipSingle && obj.group.length < 2) {
+                obj.helpers.buttons = false;
+                obj.closeBtn = true;
+
+                return;
+            }
+
+            //Increase top margin to give space for buttons
+            obj.margin[opts.position === 'bottom' ? 2 : 0] += 30;
+        },
+
+        onPlayStart: function () {
+            if (this.buttons) {
+                this.buttons.play.attr('title', 'Pause slideshow').addClass('btnPlayOn');
+            }
+        },
+
+        onPlayEnd: function () {
+            if (this.buttons) {
+                this.buttons.play.attr('title', 'Start slideshow').removeClass('btnPlayOn');
+            }
+        },
+
+        afterShow: function (opts, obj) {
+            var buttons = this.buttons;
+
+            if (!buttons) {
+                this.list = $(opts.tpl).addClass(opts.position).appendTo('body');
+
+                buttons = {
+                    prev: this.list.find('.btnPrev').click(F.prev),
+                    next: this.list.find('.btnNext').click(F.next),
+                    play: this.list.find('.btnPlay').click(F.play),
+                    toggle: this.list.find('.btnToggle').click(F.toggle),
+                    close: this.list.find('.btnClose').click(F.close)
+                }
+            }
+
+            //Prev
+            if (obj.index > 0 || obj.loop) {
+                buttons.prev.removeClass('btnDisabled');
+            } else {
+                buttons.prev.addClass('btnDisabled');
+            }
+
+            //Next / Play
+            if (obj.loop || obj.index < obj.group.length - 1) {
+                buttons.next.removeClass('btnDisabled');
+                buttons.play.removeClass('btnDisabled');
+
+            } else {
+                buttons.next.addClass('btnDisabled');
+                buttons.play.addClass('btnDisabled');
+            }
+
+            this.buttons = buttons;
+
+            this.onUpdate(opts, obj);
+        },
+
+        onUpdate: function (opts, obj) {
+            var toggle;
+
+            if (!this.buttons) {
+                return;
+            }
+
+            toggle = this.buttons.toggle.removeClass('btnDisabled btnToggleOn');
+
+            //Size toggle button
+            if (obj.canShrink) {
+                toggle.addClass('btnToggleOn');
+
+            } else if (!obj.canExpand) {
+                toggle.addClass('btnDisabled');
+            }
+        },
+
+        beforeClose: function () {
+            if (this.list) {
+                this.list.remove();
+            }
+
+            this.list = null;
+            this.buttons = null;
+        }
+    };
+
+}(jQuery));

+ 201 - 0
src/assets/fancybox/css/helpers/jquery.fancybox-media.js

@@ -0,0 +1,201 @@
+/*!
+ * Media helper for fancyBox
+ * version: 1.0.6 (Fri, 14 Jun 2013)
+ * @requires fancyBox v2.0 or later
+ *
+ * Usage:
+ *     $(".fancybox").fancybox({
+ *         helpers : {
+ *             media: true
+ *         }
+ *     });
+ *
+ * Set custom URL parameters:
+ *     $(".fancybox").fancybox({
+ *         helpers : {
+ *             media: {
+ *                 youtube : {
+ *                     params : {
+ *                         autoplay : 0
+ *                     }
+ *                 }
+ *             }
+ *         }
+ *     });
+ *
+ * Or:
+ *     $(".fancybox").fancybox({,
+ *         helpers : {
+ *             media: true
+ *         },
+ *         youtube : {
+ *             autoplay: 0
+ *         }
+ *     });
+ *
+ *  Supports:
+ *
+ *      Youtube
+ *          http://www.youtube.com/watch?v=opj24KnzrWo
+ *          http://www.youtube.com/embed/opj24KnzrWo
+ *          http://youtu.be/opj24KnzrWo
+ *			http://www.youtube-nocookie.com/embed/opj24KnzrWo
+ *      Vimeo
+ *          http://vimeo.com/40648169
+ *          http://vimeo.com/channels/staffpicks/38843628
+ *          http://vimeo.com/groups/surrealism/videos/36516384
+ *          http://player.vimeo.com/video/45074303
+ *      Metacafe
+ *          http://www.metacafe.com/watch/7635964/dr_seuss_the_lorax_movie_trailer/
+ *          http://www.metacafe.com/watch/7635964/
+ *      Dailymotion
+ *          http://www.dailymotion.com/video/xoytqh_dr-seuss-the-lorax-premiere_people
+ *      Twitvid
+ *          http://twitvid.com/QY7MD
+ *      Twitpic
+ *          http://twitpic.com/7p93st
+ *      Instagram
+ *          http://instagr.am/p/IejkuUGxQn/
+ *          http://instagram.com/p/IejkuUGxQn/
+ *      Google maps
+ *          http://maps.google.com/maps?q=Eiffel+Tower,+Avenue+Gustave+Eiffel,+Paris,+France&t=h&z=17
+ *          http://maps.google.com/?ll=48.857995,2.294297&spn=0.007666,0.021136&t=m&z=16
+ *          http://maps.google.com/?ll=48.859463,2.292626&spn=0.000965,0.002642&t=m&z=19&layer=c&cbll=48.859524,2.292532&panoid=YJ0lq28OOy3VT2IqIuVY0g&cbp=12,151.58,,0,-15.56
+ */
+;(function ($) {
+    "use strict";
+
+    //Shortcut for fancyBox object
+    var F = $.fancybox,
+        format = function (url, rez, params) {
+            params = params || '';
+
+            if ($.type(params) === "object") {
+                params = $.param(params, true);
+            }
+
+            $.each(rez, function (key, value) {
+                url = url.replace('$' + key, value || '');
+            });
+
+            if (params.length) {
+                url += ( url.indexOf('?') > 0 ? '&' : '?' ) + params;
+            }
+
+            return url;
+        };
+
+    //Add helper object
+    F.helpers.media = {
+        defaults: {
+            youtube: {
+                matcher: /(youtube\.com|youtu\.be|youtube-nocookie\.com)\/(watch\?v=|v\/|u\/|embed\/?)?(videoseries\?list=(.*)|[\w-]{11}|\?listType=(.*)&list=(.*)).*/i,
+                params: {
+                    autoplay: 1,
+                    autohide: 1,
+                    fs: 1,
+                    rel: 0,
+                    hd: 1,
+                    wmode: 'opaque',
+                    enablejsapi: 1,
+                    ps: 'docs',
+                    controls: 1
+                },
+                type: 'iframe',
+                url: '//www.youtube.com/embed/$3'
+            },
+            vimeo: {
+                matcher: /(?:vimeo(?:pro)?.com)\/(?:[^\d]+)?(\d+)(?:.*)/,
+                params: {
+                    autoplay: 1,
+                    hd: 1,
+                    show_title: 1,
+                    show_byline: 1,
+                    show_portrait: 0,
+                    fullscreen: 1
+                },
+                type: 'iframe',
+                url: '//player.vimeo.com/video/$1'
+            },
+            metacafe: {
+                matcher: /metacafe.com\/(?:watch|fplayer)\/([\w\-]{1,10})/,
+                params: {
+                    autoPlay: 'yes'
+                },
+                type: 'swf',
+                url: function (rez, params, obj) {
+                    obj.swf.flashVars = 'playerVars=' + $.param(params, true);
+
+                    return '//www.metacafe.com/fplayer/' + rez[1] + '/.swf';
+                }
+            },
+            dailymotion: {
+                matcher: /dailymotion.com\/video\/(.*)\/?(.*)/,
+                params: {
+                    additionalInfos: 0,
+                    autoStart: 1
+                },
+                type: 'swf',
+                url: '//www.dailymotion.com/swf/video/$1'
+            },
+            twitvid: {
+                matcher: /twitvid\.com\/([a-zA-Z0-9_\-\?\=]+)/i,
+                params: {
+                    autoplay: 0
+                },
+                type: 'iframe',
+                url: '//www.twitvid.com/embed.php?guid=$1'
+            },
+            twitpic: {
+                matcher: /twitpic\.com\/(?!(?:place|photos|events)\/)([a-zA-Z0-9\?\=\-]+)/i,
+                type: 'image',
+                url: '//twitpic.com/show/full/$1/'
+            },
+            instagram: {
+                matcher: /(instagr\.am|instagram\.com)\/p\/([a-zA-Z0-9_\-]+)\/?/i,
+                type: 'image',
+                url: '//$1/p/$2/media/?size=l'
+            },
+            google_maps: {
+                matcher: /maps\.google\.([a-z]{2,3}(\.[a-z]{2})?)\/(\?ll=|maps\?)(.*)/i,
+                type: 'iframe',
+                url: function (rez) {
+                    return '//maps.google.' + rez[1] + '/' + rez[3] + '' + rez[4] + '&output=' + (rez[4].indexOf('layer=c') > 0 ? 'svembed' : 'embed');
+                }
+            }
+        },
+
+        beforeLoad: function (opts, obj) {
+            var url = obj.href || '',
+                type = false,
+                what,
+                item,
+                rez,
+                params;
+
+            for (what in opts) {
+                if (opts.hasOwnProperty(what)) {
+                    item = opts[what];
+                    rez = url.match(item.matcher);
+
+                    if (rez) {
+                        type = item.type;
+                        params = $.extend(true, {}, item.params, obj[what] || ($.isPlainObject(opts[what]) ? opts[what].params : null));
+
+                        url = $.type(item.url) === "function" ? item.url.call(this, rez, params, obj) : format(item.url, rez, params);
+
+                        break;
+                    }
+                }
+            }
+
+            if (type) {
+                obj.href = url;
+                obj.type = type;
+
+                obj.autoHeight = false;
+            }
+        }
+    };
+
+}(jQuery));

+ 55 - 0
src/assets/fancybox/css/helpers/jquery.fancybox-thumbs.css

@@ -0,0 +1,55 @@
+#fancybox-thumbs {
+    position: fixed;
+    left: 0;
+    width: 100%;
+    overflow: hidden;
+    z-index: 8050;
+}
+
+#fancybox-thumbs.bottom {
+    bottom: 2px;
+}
+
+#fancybox-thumbs.top {
+    top: 2px;
+}
+
+#fancybox-thumbs ul {
+    position: relative;
+    list-style: none;
+    margin: 0;
+    padding: 0;
+}
+
+#fancybox-thumbs ul li {
+    float: left;
+    padding: 1px;
+    opacity: 0.5;
+}
+
+#fancybox-thumbs ul li.active {
+    opacity: 0.75;
+    padding: 0;
+    border: 1px solid #fff;
+}
+
+#fancybox-thumbs ul li:hover {
+    opacity: 1;
+}
+
+#fancybox-thumbs ul li a {
+    display: block;
+    position: relative;
+    overflow: hidden;
+    border: 1px solid #222;
+    background: #111;
+    outline: none;
+}
+
+#fancybox-thumbs ul li img {
+    display: block;
+    position: relative;
+    border: 0;
+    padding: 0;
+    max-width: none;
+}

+ 165 - 0
src/assets/fancybox/css/helpers/jquery.fancybox-thumbs.js

@@ -0,0 +1,165 @@
+/*!
+ * Thumbnail helper for fancyBox
+ * version: 1.0.7 (Mon, 01 Oct 2012)
+ * @requires fancyBox v2.0 or later
+ *
+ * Usage:
+ *     $(".fancybox").fancybox({
+ *         helpers : {
+ *             thumbs: {
+ *                 width  : 50,
+ *                 height : 50
+ *             }
+ *         }
+ *     });
+ *
+ */
+;(function ($) {
+    //Shortcut for fancyBox object
+    var F = $.fancybox;
+
+    //Add helper object
+    F.helpers.thumbs = {
+        defaults: {
+            width: 50,       // thumbnail width
+            height: 50,       // thumbnail height
+            position: 'bottom', // 'top' or 'bottom'
+            source: function (item) {  // function to obtain the URL of the thumbnail image
+                var href;
+
+                if (item.element) {
+                    href = $(item.element).find('img').attr('src');
+                }
+
+                if (!href && item.type === 'image' && item.href) {
+                    href = item.href;
+                }
+
+                return href;
+            }
+        },
+
+        wrap: null,
+        list: null,
+        width: 0,
+
+        init: function (opts, obj) {
+            var that = this,
+                list,
+                thumbWidth = opts.width,
+                thumbHeight = opts.height,
+                thumbSource = opts.source;
+
+            //Build list structure
+            list = '';
+
+            for (var n = 0; n < obj.group.length; n++) {
+                list += '<li><a style="width:' + thumbWidth + 'px;height:' + thumbHeight + 'px;" href="javascript:jQuery.fancybox.jumpto(' + n + ');"></a></li>';
+            }
+
+            this.wrap = $('<div id="fancybox-thumbs"></div>').addClass(opts.position).appendTo('body');
+            this.list = $('<ul>' + list + '</ul>').appendTo(this.wrap);
+
+            //Load each thumbnail
+            $.each(obj.group, function (i) {
+                var el = obj.group[i],
+                    href = thumbSource(el);
+
+                if (!href) {
+                    return;
+                }
+
+                $("<img />").on("load", function () {
+                    var width = this.width,
+                        height = this.height,
+                        widthRatio, heightRatio, parent;
+
+                    if (!that.list || !width || !height) {
+                        return;
+                    }
+
+                    //Calculate thumbnail width/height and center it
+                    widthRatio = width / thumbWidth;
+                    heightRatio = height / thumbHeight;
+
+                    parent = that.list.children().eq(i).find('a');
+
+                    if (widthRatio >= 1 && heightRatio >= 1) {
+                        if (widthRatio > heightRatio) {
+                            width = Math.floor(width / heightRatio);
+                            height = thumbHeight;
+
+                        } else {
+                            width = thumbWidth;
+                            height = Math.floor(height / widthRatio);
+                        }
+                    }
+
+                    $(this).css({
+                        width: width,
+                        height: height,
+                        top: Math.floor(thumbHeight / 2 - height / 2),
+                        left: Math.floor(thumbWidth / 2 - width / 2)
+                    });
+
+                    parent.width(thumbWidth).height(thumbHeight);
+
+                    $(this).hide().appendTo(parent).fadeIn(300);
+
+                })
+                    .attr('src', href)
+                    .attr('title', el.title);
+            });
+
+            //Set initial width
+            this.width = this.list.children().eq(0).outerWidth(true);
+
+            this.list.width(this.width * (obj.group.length + 1)).css('left', Math.floor($(window).width() * 0.5 - (obj.index * this.width + this.width * 0.5)));
+        },
+
+        beforeLoad: function (opts, obj) {
+            //Remove self if gallery do not have at least two items
+            if (obj.group.length < 2) {
+                obj.helpers.thumbs = false;
+
+                return;
+            }
+
+            //Increase bottom margin to give space for thumbs
+            obj.margin[opts.position === 'top' ? 0 : 2] += ((opts.height) + 15);
+        },
+
+        afterShow: function (opts, obj) {
+            //Check if exists and create or update list
+            if (this.list) {
+                this.onUpdate(opts, obj);
+
+            } else {
+                this.init(opts, obj);
+            }
+
+            //Set active element
+            this.list.children().removeClass('active').eq(obj.index).addClass('active');
+        },
+
+        //Center list
+        onUpdate: function (opts, obj) {
+            if (this.list) {
+                this.list.stop(true).animate({
+                    'left': Math.floor($(window).width() * 0.5 - (obj.index * this.width + this.width * 0.5))
+                }, 150);
+            }
+        },
+
+        beforeClose: function () {
+            if (this.wrap) {
+                this.wrap.remove();
+            }
+
+            this.wrap = null;
+            this.list = null;
+            this.width = 0;
+        }
+    }
+
+}(jQuery));

+ 272 - 0
src/assets/fancybox/css/jquery.fancybox.css

@@ -0,0 +1,272 @@
+/*! fancyBox v2.1.5 fancyapps.com | fancyapps.com/fancybox/#license */
+.fancybox-wrap,
+.fancybox-skin,
+.fancybox-outer,
+.fancybox-inner,
+.fancybox-image,
+.fancybox-wrap iframe,
+.fancybox-wrap object,
+.fancybox-nav,
+.fancybox-nav span,
+.fancybox-tmp {
+    padding: 0;
+    margin: 0;
+    border: 0;
+    outline: none;
+    vertical-align: top;
+}
+
+.fancybox-wrap {
+    position: absolute;
+    top: 0;
+    left: 0;
+    -webkit-transform: translate3d(0, 0, 0);
+    transform: translate3d(0, 0, 0);
+    z-index: 8020;
+}
+
+.fancybox-skin {
+    position: relative;
+    background: #f9f9f9;
+    color: #444;
+    text-shadow: none;
+    -webkit-border-radius: 4px;
+    -moz-border-radius: 4px;
+    border-radius: 4px;
+}
+
+.fancybox-opened {
+    z-index: 8030;
+}
+
+.fancybox-opened .fancybox-skin {
+    -webkit-box-shadow: 0 10px 25px rgba(0, 0, 0, 0.5);
+    -moz-box-shadow: 0 10px 25px rgba(0, 0, 0, 0.5);
+    box-shadow: 0 10px 25px rgba(0, 0, 0, 0.5);
+}
+
+.fancybox-outer, .fancybox-inner {
+    position: relative;
+}
+
+.fancybox-inner {
+    overflow: hidden;
+}
+
+.fancybox-type-iframe .fancybox-inner {
+    -webkit-overflow-scrolling: touch;
+}
+
+.fancybox-error {
+    color: #444;
+    font: 14px/20px "Helvetica Neue", Helvetica, Arial, sans-serif;
+    margin: 0;
+    padding: 15px;
+    white-space: nowrap;
+}
+
+.fancybox-image, .fancybox-iframe {
+    display: block;
+    width: 100%;
+    height: 100%;
+}
+
+.fancybox-image {
+    max-width: 100%;
+    max-height: 100%;
+}
+
+#fancybox-loading, .fancybox-close, .fancybox-prev span, .fancybox-next span {
+    background-image: url(fancybox_sprite.png);
+}
+
+#fancybox-loading {
+    position: fixed;
+    top: 50%;
+    left: 50%;
+    margin-top: -22px;
+    margin-left: -22px;
+    background-position: 0 -108px;
+    opacity: 0.8;
+    cursor: pointer;
+    z-index: 8060;
+}
+
+#fancybox-loading div {
+    width: 44px;
+    height: 44px;
+    background: url(fancybox_loading.gif) center center no-repeat;
+}
+
+.fancybox-close {
+    position: absolute;
+    top: -18px;
+    right: -18px;
+    width: 36px;
+    height: 36px;
+    cursor: pointer;
+    z-index: 8040;
+}
+
+.fancybox-nav {
+    position: absolute;
+    top: 0;
+    width: 40%;
+    height: 100%;
+    cursor: pointer;
+    text-decoration: none;
+    background: transparent url(blank.gif); /* helps IE */
+    -webkit-tap-highlight-color: rgba(0, 0, 0, 0);
+    z-index: 8040;
+}
+
+.fancybox-prev {
+    left: 0;
+}
+
+.fancybox-next {
+    right: 0;
+}
+
+.fancybox-nav span {
+    position: absolute;
+    top: 50%;
+    width: 36px;
+    height: 34px;
+    margin-top: -18px;
+    cursor: pointer;
+    z-index: 8040;
+    visibility: hidden;
+}
+
+.fancybox-prev span {
+    left: 10px;
+    background-position: 0 -36px;
+}
+
+.fancybox-next span {
+    right: 10px;
+    background-position: 0 -72px;
+}
+
+.fancybox-nav:hover span {
+    visibility: visible;
+}
+
+.fancybox-tmp {
+    position: absolute;
+    top: -99999px;
+    left: -99999px;
+    max-width: 99999px;
+    max-height: 99999px;
+    overflow: visible !important;
+}
+
+/* Overlay helper */
+
+.fancybox-lock {
+    overflow: visible !important;
+    width: auto;
+}
+
+.fancybox-lock body {
+    overflow: hidden !important;
+}
+
+.fancybox-lock-test {
+    overflow-y: hidden !important;
+}
+
+.fancybox-overlay {
+    position: absolute;
+    top: 0;
+    left: 0;
+    overflow: hidden;
+    display: none;
+    z-index: 8010;
+    background: url(fancybox_overlay.png);
+}
+
+.fancybox-overlay-fixed {
+    position: fixed;
+    bottom: 0;
+    right: 0;
+}
+
+.fancybox-lock .fancybox-overlay {
+    overflow: auto;
+    overflow-y: scroll;
+}
+
+/* Title helper */
+
+.fancybox-title {
+    visibility: hidden;
+    font: normal 13px/20px "Helvetica Neue", Helvetica, Arial, sans-serif;
+    position: relative;
+    text-shadow: none;
+    z-index: 8050;
+}
+
+.fancybox-opened .fancybox-title {
+    visibility: visible;
+}
+
+.fancybox-title-float-wrap {
+    position: absolute;
+    bottom: 0;
+    right: 50%;
+    margin-bottom: -35px;
+    z-index: 8050;
+    text-align: center;
+}
+
+.fancybox-title-float-wrap .child {
+    display: inline-block;
+    margin-right: -100%;
+    padding: 2px 20px;
+    background: transparent; /* Fallback for web browsers that doesn't support RGBa */
+    background: rgba(0, 0, 0, 0.8);
+    -webkit-border-radius: 15px;
+    -moz-border-radius: 15px;
+    border-radius: 15px;
+    text-shadow: 0 1px 2px #222;
+    color: #FFF;
+    font-weight: bold;
+    line-height: 24px;
+    white-space: nowrap;
+}
+
+.fancybox-title-outside-wrap {
+    position: relative;
+    margin-top: 10px;
+    color: #fff;
+}
+
+.fancybox-title-inside-wrap {
+    padding-top: 10px;
+}
+
+.fancybox-title-over-wrap {
+    position: absolute;
+    bottom: 0;
+    left: 0;
+    color: #fff;
+    padding: 10px;
+    background: #000;
+    background: rgba(0, 0, 0, .8);
+}
+
+/*Retina graphics!*/
+@media only screen and (-webkit-min-device-pixel-ratio: 1.5), only screen and (min--moz-device-pixel-ratio: 1.5), only screen and (min-device-pixel-ratio: 1.5) {
+
+    #fancybox-loading, .fancybox-close, .fancybox-prev span, .fancybox-next span {
+        background-image: url(fancybox_sprite@2x.png);
+        background-size: 44px 152px; /*The size of the normal image, half the size of the hi-res image*/
+    }
+
+    #fancybox-loading div {
+        background-image: url(fancybox_loading@2x.gif);
+        background-size: 24px 24px; /*The size of the normal image, half the size of the hi-res image*/
+    }
+}

+ 2020 - 0
src/assets/fancybox/js/jquery.fancybox.js

@@ -0,0 +1,2020 @@
+/*!
+ * fancyBox - jQuery Plugin
+ * version: 2.1.5 (Fri, 14 Jun 2013)
+ * requires jQuery v1.6 or later
+ *
+ * Examples at http://fancyapps.com/fancybox/
+ * License: www.fancyapps.com/fancybox/#license
+ *
+ * Copyright 2012 Janis Skarnelis - janis@fancyapps.com
+ *
+ */
+
+;(function (window, document, $, undefined) {
+    "use strict";
+
+    var H = $("html"),
+        W = $(window),
+        D = $(document),
+        F = $.fancybox = function () {
+            F.open.apply(this, arguments);
+        },
+        IE = navigator.userAgent.match(/msie/i),
+        didUpdate = null,
+        isTouch = document.createTouch !== undefined,
+
+        isQuery = function (obj) {
+            return obj && obj.hasOwnProperty && obj instanceof $;
+        },
+        isString = function (str) {
+            return str && $.type(str) === "string";
+        },
+        isPercentage = function (str) {
+            return isString(str) && str.indexOf('%') > 0;
+        },
+        isScrollable = function (el) {
+            return (el && !(el.style.overflow && el.style.overflow === 'hidden') && ((el.clientWidth && el.scrollWidth > el.clientWidth) || (el.clientHeight && el.scrollHeight > el.clientHeight)));
+        },
+        getScalar = function (orig, dim) {
+            var value = parseInt(orig, 10) || 0;
+
+            if (dim && isPercentage(orig)) {
+                value = F.getViewport()[dim] / 100 * value;
+            }
+
+            return Math.ceil(value);
+        },
+        getValue = function (value, dim) {
+            return getScalar(value, dim) + 'px';
+        };
+
+    $.extend(F, {
+        // The current version of fancyBox
+        version: '2.1.5',
+
+        defaults: {
+            padding: 15,
+            margin: 20,
+
+            width: 800,
+            height: 600,
+            minWidth: 100,
+            minHeight: 100,
+            maxWidth: 9999,
+            maxHeight: 9999,
+            pixelRatio: 1, // Set to 2 for retina display support
+
+            autoSize: true,
+            autoHeight: false,
+            autoWidth: false,
+
+            autoResize: true,
+            autoCenter: !isTouch,
+            fitToView: true,
+            aspectRatio: false,
+            topRatio: 0.5,
+            leftRatio: 0.5,
+
+            scrolling: 'auto', // 'auto', 'yes' or 'no'
+            wrapCSS: '',
+
+            arrows: true,
+            closeBtn: true,
+            closeClick: false,
+            nextClick: false,
+            mouseWheel: true,
+            autoPlay: false,
+            playSpeed: 3000,
+            preload: 3,
+            modal: false,
+            loop: true,
+
+            ajax: {
+                dataType: 'html',
+                headers: {'X-fancyBox': true}
+            },
+            iframe: {
+                scrolling: 'auto',
+                preload: true
+            },
+            swf: {
+                wmode: 'transparent',
+                allowfullscreen: 'true',
+                allowscriptaccess: 'always'
+            },
+
+            keys: {
+                next: {
+                    13: 'left', // enter
+                    34: 'up',   // page down
+                    39: 'left', // right arrow
+                    40: 'up'    // down arrow
+                },
+                prev: {
+                    8: 'right',  // backspace
+                    33: 'down',   // page up
+                    37: 'right',  // left arrow
+                    38: 'down'    // up arrow
+                },
+                close: [27], // escape key
+                play: [32], // space - start/stop slideshow
+                toggle: [70]  // letter "f" - toggle fullscreen
+            },
+
+            direction: {
+                next: 'left',
+                prev: 'right'
+            },
+
+            scrollOutside: true,
+
+            // Override some properties
+            index: 0,
+            type: null,
+            href: null,
+            content: null,
+            title: null,
+
+            // HTML templates
+            tpl: {
+                wrap: '<div class="fancybox-wrap" tabIndex="-1"><div class="fancybox-skin"><div class="fancybox-outer"><div class="fancybox-inner"></div></div></div></div>',
+                image: '<img class="fancybox-image" src="{href}" alt="" />',
+                iframe: '<iframe id="fancybox-frame{rnd}" name="fancybox-frame{rnd}" class="fancybox-iframe" frameborder="0" vspace="0" hspace="0" webkitAllowFullScreen mozallowfullscreen allowFullScreen' + (IE ? ' allowtransparency="true"' : '') + '></iframe>',
+                error: '<p class="fancybox-error">The requested content cannot be loaded.<br/>Please try again later.</p>',
+                closeBtn: '<a title="Close" class="fancybox-item fancybox-close" href="javascript:;"></a>',
+                next: '<a title="Next" class="fancybox-nav fancybox-next" href="javascript:;"><span></span></a>',
+                prev: '<a title="Previous" class="fancybox-nav fancybox-prev" href="javascript:;"><span></span></a>',
+                loading: '<div id="fancybox-loading"><div></div></div>'
+            },
+
+            // Properties for each animation type
+            // Opening fancyBox
+            openEffect: 'fade', // 'elastic', 'fade' or 'none'
+            openSpeed: 250,
+            openEasing: 'swing',
+            openOpacity: true,
+            openMethod: 'zoomIn',
+
+            // Closing fancyBox
+            closeEffect: 'fade', // 'elastic', 'fade' or 'none'
+            closeSpeed: 250,
+            closeEasing: 'swing',
+            closeOpacity: true,
+            closeMethod: 'zoomOut',
+
+            // Changing next gallery item
+            nextEffect: 'elastic', // 'elastic', 'fade' or 'none'
+            nextSpeed: 250,
+            nextEasing: 'swing',
+            nextMethod: 'changeIn',
+
+            // Changing previous gallery item
+            prevEffect: 'elastic', // 'elastic', 'fade' or 'none'
+            prevSpeed: 250,
+            prevEasing: 'swing',
+            prevMethod: 'changeOut',
+
+            // Enable default helpers
+            helpers: {
+                overlay: true,
+                title: true
+            },
+
+            // Callbacks
+            onCancel: $.noop, // If canceling
+            beforeLoad: $.noop, // Before loading
+            afterLoad: $.noop, // After loading
+            beforeShow: $.noop, // Before changing in current item
+            afterShow: $.noop, // After opening
+            beforeChange: $.noop, // Before changing gallery item
+            beforeClose: $.noop, // Before closing
+            afterClose: $.noop  // After closing
+        },
+
+        //Current state
+        group: {}, // Selected group
+        opts: {}, // Group options
+        previous: null,  // Previous element
+        coming: null,  // Element being loaded
+        current: null,  // Currently loaded element
+        isActive: false, // Is activated
+        isOpen: false, // Is currently open
+        isOpened: false, // Have been fully opened at least once
+
+        wrap: null,
+        skin: null,
+        outer: null,
+        inner: null,
+
+        player: {
+            timer: null,
+            isActive: false
+        },
+
+        // Loaders
+        ajaxLoad: null,
+        imgPreload: null,
+
+        // Some collections
+        transitions: {},
+        helpers: {},
+
+        /*
+         *	Static methods
+         */
+
+        open: function (group, opts) {
+            if (!group) {
+                return;
+            }
+
+            if (!$.isPlainObject(opts)) {
+                opts = {};
+            }
+
+            // Close if already active
+            if (false === F.close(true)) {
+                return;
+            }
+
+            // Normalize group
+            if (!$.isArray(group)) {
+                group = isQuery(group) ? $(group).get() : [group];
+            }
+
+            // Recheck if the type of each element is `object` and set content type (image, ajax, etc)
+            $.each(group, function (i, element) {
+                var obj = {},
+                    href,
+                    title,
+                    content,
+                    type,
+                    rez,
+                    hrefParts,
+                    selector;
+
+                if ($.type(element) === "object") {
+                    // Check if is DOM element
+                    if (element.nodeType) {
+                        element = $(element);
+                    }
+
+                    if (isQuery(element)) {
+                        obj = {
+                            href: element.data('fancybox-href') || element.attr('href'),
+                            title: $('<div/>').text(element.data('fancybox-title') || element.attr('title') || '').html(),
+                            isDom: true,
+                            element: element
+                        };
+
+                        if ($.metadata) {
+                            $.extend(true, obj, element.metadata());
+                        }
+
+                    } else {
+                        obj = element;
+                    }
+                }
+
+                href = opts.href || obj.href || (isString(element) ? element : null);
+                title = opts.title !== undefined ? opts.title : obj.title || '';
+
+                content = opts.content || obj.content;
+                type = content ? 'html' : (opts.type || obj.type);
+
+                if (!type && obj.isDom) {
+                    type = element.data('fancybox-type');
+
+                    if (!type) {
+                        rez = element.prop('class').match(/fancybox\.(\w+)/);
+                        type = rez ? rez[1] : null;
+                    }
+                }
+
+                if (isString(href)) {
+                    // Try to guess the content type
+                    if (!type) {
+                        if (F.isImage(href)) {
+                            type = 'image';
+
+                        } else if (F.isSWF(href)) {
+                            type = 'swf';
+
+                        } else if (href.charAt(0) === '#') {
+                            type = 'inline';
+
+                        } else if (isString(element)) {
+                            type = 'html';
+                            content = element;
+                        }
+                    }
+
+                    // Split url into two pieces with source url and content selector, e.g,
+                    // "/mypage.html #my_id" will load "/mypage.html" and display element having id "my_id"
+                    if (type === 'ajax') {
+                        hrefParts = href.split(/\s+/, 2);
+                        href = hrefParts.shift();
+                        selector = hrefParts.shift();
+                    }
+                }
+
+                if (!content) {
+                    if (type === 'inline') {
+                        if (href) {
+                            content = $(isString(href) ? href.replace(/.*(?=#[^\s]+$)/, '') : href); //strip for ie7
+
+                        } else if (obj.isDom) {
+                            content = element;
+                        }
+
+                    } else if (type === 'html') {
+                        content = href;
+
+                    } else if (!type && !href && obj.isDom) {
+                        type = 'inline';
+                        content = element;
+                    }
+                }
+
+                $.extend(obj, {
+                    href: href,
+                    type: type,
+                    content: content,
+                    title: title,
+                    selector: selector
+                });
+
+                group[i] = obj;
+            });
+
+            // Extend the defaults
+            F.opts = $.extend(true, {}, F.defaults, opts);
+
+            // All options are merged recursive except keys
+            if (opts.keys !== undefined) {
+                F.opts.keys = opts.keys ? $.extend({}, F.defaults.keys, opts.keys) : false;
+            }
+
+            F.group = group;
+
+            return F._start(F.opts.index);
+        },
+
+        // Cancel image loading or abort ajax request
+        cancel: function () {
+            var coming = F.coming;
+
+            if (coming && false === F.trigger('onCancel')) {
+                return;
+            }
+
+            F.hideLoading();
+
+            if (!coming) {
+                return;
+            }
+
+            if (F.ajaxLoad) {
+                F.ajaxLoad.abort();
+            }
+
+            F.ajaxLoad = null;
+
+            if (F.imgPreload) {
+                F.imgPreload.onload = F.imgPreload.onerror = null;
+            }
+
+            if (coming.wrap) {
+                coming.wrap.stop(true, true).trigger('onReset').remove();
+            }
+
+            F.coming = null;
+
+            // If the first item has been canceled, then clear everything
+            if (!F.current) {
+                F._afterZoomOut(coming);
+            }
+        },
+
+        // Start closing animation if is open; remove immediately if opening/closing
+        close: function (event) {
+            F.cancel();
+
+            if (false === F.trigger('beforeClose')) {
+                return;
+            }
+
+            F.unbindEvents();
+
+            if (!F.isActive) {
+                return;
+            }
+
+            if (!F.isOpen || event === true) {
+                $('.fancybox-wrap').stop(true).trigger('onReset').remove();
+
+                F._afterZoomOut();
+
+            } else {
+                F.isOpen = F.isOpened = false;
+                F.isClosing = true;
+
+                $('.fancybox-item, .fancybox-nav').remove();
+
+                F.wrap.stop(true, true).removeClass('fancybox-opened');
+
+                F.transitions[F.current.closeMethod]();
+            }
+        },
+
+        // Manage slideshow:
+        //   $.fancybox.play(); - toggle slideshow
+        //   $.fancybox.play( true ); - start
+        //   $.fancybox.play( false ); - stop
+        play: function (action) {
+            var clear = function () {
+                    clearTimeout(F.player.timer);
+                },
+                set = function () {
+                    clear();
+
+                    if (F.current && F.player.isActive) {
+                        F.player.timer = setTimeout(F.next, F.current.playSpeed);
+                    }
+                },
+                stop = function () {
+                    clear();
+
+                    D.unbind('.player');
+
+                    F.player.isActive = false;
+
+                    F.trigger('onPlayEnd');
+                },
+                start = function () {
+                    if (F.current && (F.current.loop || F.current.index < F.group.length - 1)) {
+                        F.player.isActive = true;
+
+                        D.bind({
+                            'onCancel.player beforeClose.player': stop,
+                            'onUpdate.player': set,
+                            'beforeLoad.player': clear
+                        });
+
+                        set();
+
+                        F.trigger('onPlayStart');
+                    }
+                };
+
+            if (action === true || (!F.player.isActive && action !== false)) {
+                start();
+            } else {
+                stop();
+            }
+        },
+
+        // Navigate to next gallery item
+        next: function (direction) {
+            var current = F.current;
+
+            if (current) {
+                if (!isString(direction)) {
+                    direction = current.direction.next;
+                }
+
+                F.jumpto(current.index + 1, direction, 'next');
+            }
+        },
+
+        // Navigate to previous gallery item
+        prev: function (direction) {
+            var current = F.current;
+
+            if (current) {
+                if (!isString(direction)) {
+                    direction = current.direction.prev;
+                }
+
+                F.jumpto(current.index - 1, direction, 'prev');
+            }
+        },
+
+        // Navigate to gallery item by index
+        jumpto: function (index, direction, router) {
+            var current = F.current;
+
+            if (!current) {
+                return;
+            }
+
+            index = getScalar(index);
+
+            F.direction = direction || current.direction[(index >= current.index ? 'next' : 'prev')];
+            F.router = router || 'jumpto';
+
+            if (current.loop) {
+                if (index < 0) {
+                    index = current.group.length + (index % current.group.length);
+                }
+
+                index = index % current.group.length;
+            }
+
+            if (current.group[index] !== undefined) {
+                F.cancel();
+
+                F._start(index);
+            }
+        },
+
+        // Center inside viewport and toggle position type to fixed or absolute if needed
+        reposition: function (e, onlyAbsolute) {
+            var current = F.current,
+                wrap = current ? current.wrap : null,
+                pos;
+
+            if (wrap) {
+                pos = F._getPosition(onlyAbsolute);
+
+                if (e && e.type === 'scroll') {
+                    delete pos.position;
+
+                    wrap.stop(true, true).animate(pos, 200);
+
+                } else {
+                    wrap.css(pos);
+
+                    current.pos = $.extend({}, current.dim, pos);
+                }
+            }
+        },
+
+        update: function (e) {
+            var type = (e && e.originalEvent && e.originalEvent.type),
+                anyway = !type || type === 'orientationchange';
+
+            if (anyway) {
+                clearTimeout(didUpdate);
+
+                didUpdate = null;
+            }
+
+            if (!F.isOpen || didUpdate) {
+                return;
+            }
+
+            didUpdate = setTimeout(function () {
+                var current = F.current;
+
+                if (!current || F.isClosing) {
+                    return;
+                }
+
+                F.wrap.removeClass('fancybox-tmp');
+
+                if (anyway || type === 'load' || (type === 'resize' && current.autoResize)) {
+                    F._setDimension();
+                }
+
+                if (!(type === 'scroll' && current.canShrink)) {
+                    F.reposition(e);
+                }
+
+                F.trigger('onUpdate');
+
+                didUpdate = null;
+
+            }, (anyway && !isTouch ? 0 : 300));
+        },
+
+        // Shrink content to fit inside viewport or restore if resized
+        toggle: function (action) {
+            if (F.isOpen) {
+                F.current.fitToView = $.type(action) === "boolean" ? action : !F.current.fitToView;
+
+                // Help browser to restore document dimensions
+                if (isTouch) {
+                    F.wrap.removeAttr('style').addClass('fancybox-tmp');
+
+                    F.trigger('onUpdate');
+                }
+
+                F.update();
+            }
+        },
+
+        hideLoading: function () {
+            D.unbind('.loading');
+
+            $('#fancybox-loading').remove();
+        },
+
+        showLoading: function () {
+            var el, viewport;
+
+            F.hideLoading();
+
+            el = $(F.opts.tpl.loading).click(F.cancel).appendTo('body');
+
+            // If user will press the escape-button, the request will be canceled
+            D.bind('keydown.loading', function (e) {
+                if ((e.which || e.keyCode) === 27) {
+                    e.preventDefault();
+
+                    F.cancel();
+                }
+            });
+
+            if (!F.defaults.fixed) {
+                viewport = F.getViewport();
+
+                el.css({
+                    position: 'absolute',
+                    top: (viewport.h * 0.5) + viewport.y,
+                    left: (viewport.w * 0.5) + viewport.x
+                });
+            }
+
+            F.trigger('onLoading');
+        },
+
+        getViewport: function () {
+            var locked = (F.current && F.current.locked) || false,
+                rez = {
+                    x: W.scrollLeft(),
+                    y: W.scrollTop()
+                };
+
+            if (locked && locked.length) {
+                rez.w = locked[0].clientWidth;
+                rez.h = locked[0].clientHeight;
+
+            } else {
+                // See http://bugs.jquery.com/ticket/6724
+                rez.w = isTouch && window.innerWidth ? window.innerWidth : W.width();
+                rez.h = isTouch && window.innerHeight ? window.innerHeight : W.height();
+            }
+
+            return rez;
+        },
+
+        // Unbind the keyboard / clicking actions
+        unbindEvents: function () {
+            if (F.wrap && isQuery(F.wrap)) {
+                F.wrap.unbind('.fb');
+            }
+
+            D.unbind('.fb');
+            W.unbind('.fb');
+        },
+
+        bindEvents: function () {
+            var current = F.current,
+                keys;
+
+            if (!current) {
+                return;
+            }
+
+            // Changing document height on iOS devices triggers a 'resize' event,
+            // that can change document height... repeating infinitely
+            W.bind('orientationchange.fb' + (isTouch ? '' : ' resize.fb') + (current.autoCenter && !current.locked ? ' scroll.fb' : ''), F.update);
+
+            keys = current.keys;
+
+            if (keys) {
+                D.bind('keydown.fb', function (e) {
+                    var code = e.which || e.keyCode,
+                        target = e.target || e.srcElement;
+
+                    // Skip esc key if loading, because showLoading will cancel preloading
+                    if (code === 27 && F.coming) {
+                        return false;
+                    }
+
+                    // Ignore key combinations and key events within form elements
+                    if (!e.ctrlKey && !e.altKey && !e.shiftKey && !e.metaKey && !(target && (target.type || $(target).is('[contenteditable]')))) {
+                        $.each(keys, function (i, val) {
+                            if (current.group.length > 1 && val[code] !== undefined) {
+                                F[i](val[code]);
+
+                                e.preventDefault();
+                                return false;
+                            }
+
+                            if ($.inArray(code, val) > -1) {
+                                F[i]();
+
+                                e.preventDefault();
+                                return false;
+                            }
+                        });
+                    }
+                });
+            }
+
+            if ($.fn.mousewheel && current.mouseWheel) {
+                F.wrap.bind('mousewheel.fb', function (e, delta, deltaX, deltaY) {
+                    var target = e.target || null,
+                        parent = $(target),
+                        canScroll = false;
+
+                    while (parent.length) {
+                        if (canScroll || parent.is('.fancybox-skin') || parent.is('.fancybox-wrap')) {
+                            break;
+                        }
+
+                        canScroll = isScrollable(parent[0]);
+                        parent = $(parent).parent();
+                    }
+
+                    if (delta !== 0 && !canScroll) {
+                        if (F.group.length > 1 && !current.canShrink) {
+                            if (deltaY > 0 || deltaX > 0) {
+                                F.prev(deltaY > 0 ? 'down' : 'left');
+
+                            } else if (deltaY < 0 || deltaX < 0) {
+                                F.next(deltaY < 0 ? 'up' : 'right');
+                            }
+
+                            e.preventDefault();
+                        }
+                    }
+                });
+            }
+        },
+
+        trigger: function (event, o) {
+            var ret, obj = o || F.coming || F.current;
+
+            if (obj) {
+                if ($.isFunction(obj[event])) {
+                    ret = obj[event].apply(obj, Array.prototype.slice.call(arguments, 1));
+                }
+
+                if (ret === false) {
+                    return false;
+                }
+
+                if (obj.helpers) {
+                    $.each(obj.helpers, function (helper, opts) {
+                        if (opts && F.helpers[helper] && $.isFunction(F.helpers[helper][event])) {
+                            F.helpers[helper][event]($.extend(true, {}, F.helpers[helper].defaults, opts), obj);
+                        }
+                    });
+                }
+            }
+
+            D.trigger(event);
+        },
+
+        isImage: function (str) {
+            return isString(str) && str.match(/(^data:image\/.*,)|(\.(jp(e|g|eg)|gif|png|bmp|webp|svg)((\?|#).*)?$)/i);
+        },
+
+        isSWF: function (str) {
+            return isString(str) && str.match(/\.(swf)((\?|#).*)?$/i);
+        },
+
+        _start: function (index) {
+            var coming = {},
+                obj,
+                href,
+                type,
+                margin,
+                padding;
+
+            index = getScalar(index);
+            obj = F.group[index] || null;
+
+            if (!obj) {
+                return false;
+            }
+
+            coming = $.extend(true, {}, F.opts, obj);
+
+            // Convert margin and padding properties to array - top, right, bottom, left
+            margin = coming.margin;
+            padding = coming.padding;
+
+            if ($.type(margin) === 'number') {
+                coming.margin = [margin, margin, margin, margin];
+            }
+
+            if ($.type(padding) === 'number') {
+                coming.padding = [padding, padding, padding, padding];
+            }
+
+            // 'modal' propery is just a shortcut
+            if (coming.modal) {
+                $.extend(true, coming, {
+                    closeBtn: false,
+                    closeClick: false,
+                    nextClick: false,
+                    arrows: false,
+                    mouseWheel: false,
+                    keys: null,
+                    helpers: {
+                        overlay: {
+                            closeClick: false
+                        }
+                    }
+                });
+            }
+
+            // 'autoSize' property is a shortcut, too
+            if (coming.autoSize) {
+                coming.autoWidth = coming.autoHeight = true;
+            }
+
+            if (coming.width === 'auto') {
+                coming.autoWidth = true;
+            }
+
+            if (coming.height === 'auto') {
+                coming.autoHeight = true;
+            }
+
+            /*
+             * Add reference to the group, so it`s possible to access from callbacks, example:
+             * afterLoad : function() {
+             *     this.title = 'Image ' + (this.index + 1) + ' of ' + this.group.length + (this.title ? ' - ' + this.title : '');
+             * }
+             */
+
+            coming.group = F.group;
+            coming.index = index;
+
+            // Give a chance for callback or helpers to update coming item (type, title, etc)
+            F.coming = coming;
+
+            if (false === F.trigger('beforeLoad')) {
+                F.coming = null;
+
+                return;
+            }
+
+            type = coming.type;
+            href = coming.href;
+
+            if (!type) {
+                F.coming = null;
+
+                //If we can not determine content type then drop silently or display next/prev item if looping through gallery
+                if (F.current && F.router && F.router !== 'jumpto') {
+                    F.current.index = index;
+
+                    return F[F.router](F.direction);
+                }
+
+                return false;
+            }
+
+            F.isActive = true;
+
+            if (type === 'image' || type === 'swf') {
+                coming.autoHeight = coming.autoWidth = false;
+                coming.scrolling = 'visible';
+            }
+
+            if (type === 'image') {
+                coming.aspectRatio = true;
+            }
+
+            if (type === 'iframe' && isTouch) {
+                coming.scrolling = 'scroll';
+            }
+
+            // Build the neccessary markup
+            coming.wrap = $(coming.tpl.wrap).addClass('fancybox-' + (isTouch ? 'mobile' : 'desktop') + ' fancybox-type-' + type + ' fancybox-tmp ' + coming.wrapCSS).appendTo(coming.parent || 'body');
+
+            $.extend(coming, {
+                skin: $('.fancybox-skin', coming.wrap),
+                outer: $('.fancybox-outer', coming.wrap),
+                inner: $('.fancybox-inner', coming.wrap)
+            });
+
+            $.each(["Top", "Right", "Bottom", "Left"], function (i, v) {
+                coming.skin.css('padding' + v, getValue(coming.padding[i]));
+            });
+
+            F.trigger('onReady');
+
+            // Check before try to load; 'inline' and 'html' types need content, others - href
+            if (type === 'inline' || type === 'html') {
+                if (!coming.content || !coming.content.length) {
+                    return F._error('content');
+                }
+
+            } else if (!href) {
+                return F._error('href');
+            }
+
+            if (type === 'image') {
+                F._loadImage();
+
+            } else if (type === 'ajax') {
+                F._loadAjax();
+
+            } else if (type === 'iframe') {
+                F._loadIframe();
+
+            } else {
+                F._afterLoad();
+            }
+        },
+
+        _error: function (type) {
+            $.extend(F.coming, {
+                type: 'html',
+                autoWidth: true,
+                autoHeight: true,
+                minWidth: 0,
+                minHeight: 0,
+                scrolling: 'no',
+                hasError: type,
+                content: F.coming.tpl.error
+            });
+
+            F._afterLoad();
+        },
+
+        _loadImage: function () {
+            // Reset preload image so it is later possible to check "complete" property
+            var img = F.imgPreload = new Image();
+
+            img.onload = function () {
+                this.onload = this.onerror = null;
+
+                F.coming.width = this.width / F.opts.pixelRatio;
+                F.coming.height = this.height / F.opts.pixelRatio;
+
+                F._afterLoad();
+            };
+
+            img.onerror = function () {
+                this.onload = this.onerror = null;
+
+                F._error('image');
+            };
+
+            img.src = F.coming.href;
+
+            if (img.complete !== true) {
+                F.showLoading();
+            }
+        },
+
+        _loadAjax: function () {
+            var coming = F.coming;
+
+            F.showLoading();
+
+            F.ajaxLoad = $.ajax($.extend({}, coming.ajax, {
+                url: coming.href,
+                error: function (jqXHR, textStatus) {
+                    if (F.coming && textStatus !== 'abort') {
+                        F._error('ajax', jqXHR);
+
+                    } else {
+                        F.hideLoading();
+                    }
+                },
+                success: function (data, textStatus) {
+                    if (textStatus === 'success') {
+                        coming.content = data;
+
+                        F._afterLoad();
+                    }
+                }
+            }));
+        },
+
+        _loadIframe: function () {
+            var coming = F.coming,
+                iframe = $(coming.tpl.iframe.replace(/\{rnd\}/g, new Date().getTime()))
+                    .attr('scrolling', isTouch ? 'auto' : coming.iframe.scrolling)
+                    .attr('src', coming.href);
+
+            // This helps IE
+            $(coming.wrap).bind('onReset', function () {
+                try {
+                    $(this).find('iframe').hide().attr('src', '//about:blank').end().empty();
+                } catch (e) {
+                }
+            });
+
+            if (coming.iframe.preload) {
+                F.showLoading();
+
+                iframe.one('load', function () {
+                    $(this).data('ready', 1);
+
+                    // iOS will lose scrolling if we resize
+                    if (!isTouch) {
+                        $(this).bind('load.fb', F.update);
+                    }
+
+                    // Without this trick:
+                    //   - iframe won't scroll on iOS devices
+                    //   - IE7 sometimes displays empty iframe
+                    $(this).parents('.fancybox-wrap').width('100%').removeClass('fancybox-tmp').show();
+
+                    F._afterLoad();
+                });
+            }
+
+            coming.content = iframe.appendTo(coming.inner);
+
+            if (!coming.iframe.preload) {
+                F._afterLoad();
+            }
+        },
+
+        _preloadImages: function () {
+            var group = F.group,
+                current = F.current,
+                len = group.length,
+                cnt = current.preload ? Math.min(current.preload, len - 1) : 0,
+                item,
+                i;
+
+            for (i = 1; i <= cnt; i += 1) {
+                item = group[(current.index + i ) % len];
+
+                if (item.type === 'image' && item.href) {
+                    new Image().src = item.href;
+                }
+            }
+        },
+
+        _afterLoad: function () {
+            var coming = F.coming,
+                previous = F.current,
+                placeholder = 'fancybox-placeholder',
+                current,
+                content,
+                type,
+                scrolling,
+                href,
+                embed;
+
+            F.hideLoading();
+
+            if (!coming || F.isActive === false) {
+                return;
+            }
+
+            if (false === F.trigger('afterLoad', coming, previous)) {
+                coming.wrap.stop(true).trigger('onReset').remove();
+
+                F.coming = null;
+
+                return;
+            }
+
+            if (previous) {
+                F.trigger('beforeChange', previous);
+
+                previous.wrap.stop(true).removeClass('fancybox-opened')
+                    .find('.fancybox-item, .fancybox-nav')
+                    .remove();
+            }
+
+            F.unbindEvents();
+
+            current = coming;
+            content = coming.content;
+            type = coming.type;
+            scrolling = coming.scrolling;
+
+            $.extend(F, {
+                wrap: current.wrap,
+                skin: current.skin,
+                outer: current.outer,
+                inner: current.inner,
+                current: current,
+                previous: previous
+            });
+
+            href = current.href;
+
+            switch (type) {
+                case 'inline':
+                case 'ajax':
+                case 'html':
+                    if (current.selector) {
+                        content = $('<div>').html(content).find(current.selector);
+
+                    } else if (isQuery(content)) {
+                        if (!content.data(placeholder)) {
+                            content.data(placeholder, $('<div class="' + placeholder + '"></div>').insertAfter(content).hide());
+                        }
+
+                        content = content.show().detach();
+
+                        current.wrap.bind('onReset', function () {
+                            if ($(this).find(content).length) {
+                                content.hide().replaceAll(content.data(placeholder)).data(placeholder, false);
+                            }
+                        });
+                    }
+                    break;
+
+                case 'image':
+                    content = current.tpl.image.replace(/\{href\}/g, href);
+                    break;
+
+                case 'swf':
+                    content = '<object id="fancybox-swf" classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" width="100%" height="100%"><param name="movie" value="' + href + '"></param>';
+                    embed = '';
+
+                    $.each(current.swf, function (name, val) {
+                        content += '<param name="' + name + '" value="' + val + '"></param>';
+                        embed += ' ' + name + '="' + val + '"';
+                    });
+
+                    content += '<embed src="' + href + '" type="application/x-shockwave-flash" width="100%" height="100%"' + embed + '></embed></object>';
+                    break;
+            }
+
+            if (!(isQuery(content) && content.parent().is(current.inner))) {
+                current.inner.append(content);
+            }
+
+            // Give a chance for helpers or callbacks to update elements
+            F.trigger('beforeShow');
+
+            // Set scrolling before calculating dimensions
+            current.inner.css('overflow', scrolling === 'yes' ? 'scroll' : (scrolling === 'no' ? 'hidden' : scrolling));
+
+            // Set initial dimensions and start position
+            F._setDimension();
+
+            F.reposition();
+
+            F.isOpen = false;
+            F.coming = null;
+
+            F.bindEvents();
+
+            if (!F.isOpened) {
+                $('.fancybox-wrap').not(current.wrap).stop(true).trigger('onReset').remove();
+
+            } else if (previous.prevMethod) {
+                F.transitions[previous.prevMethod]();
+            }
+
+            F.transitions[F.isOpened ? current.nextMethod : current.openMethod]();
+
+            F._preloadImages();
+        },
+
+        _setDimension: function () {
+            var viewport = F.getViewport(),
+                steps = 0,
+                canShrink = false,
+                canExpand = false,
+                wrap = F.wrap,
+                skin = F.skin,
+                inner = F.inner,
+                current = F.current,
+                width = current.width,
+                height = current.height,
+                minWidth = current.minWidth,
+                minHeight = current.minHeight,
+                maxWidth = current.maxWidth,
+                maxHeight = current.maxHeight,
+                scrolling = current.scrolling,
+                scrollOut = current.scrollOutside ? current.scrollbarWidth : 0,
+                margin = current.margin,
+                wMargin = getScalar(margin[1] + margin[3]),
+                hMargin = getScalar(margin[0] + margin[2]),
+                wPadding,
+                hPadding,
+                wSpace,
+                hSpace,
+                origWidth,
+                origHeight,
+                origMaxWidth,
+                origMaxHeight,
+                ratio,
+                width_,
+                height_,
+                maxWidth_,
+                maxHeight_,
+                iframe,
+                body;
+
+            // Reset dimensions so we could re-check actual size
+            wrap.add(skin).add(inner).width('auto').height('auto').removeClass('fancybox-tmp');
+
+            wPadding = getScalar(skin.outerWidth(true) - skin.width());
+            hPadding = getScalar(skin.outerHeight(true) - skin.height());
+
+            // Any space between content and viewport (margin, padding, border, title)
+            wSpace = wMargin + wPadding;
+            hSpace = hMargin + hPadding;
+
+            origWidth = isPercentage(width) ? (viewport.w - wSpace) * getScalar(width) / 100 : width;
+            origHeight = isPercentage(height) ? (viewport.h - hSpace) * getScalar(height) / 100 : height;
+
+            if (current.type === 'iframe') {
+                iframe = current.content;
+
+                if (current.autoHeight && iframe && iframe.data('ready') === 1) {
+                    try {
+                        if (iframe[0].contentWindow.document.location) {
+                            inner.width(origWidth).height(9999);
+
+                            body = iframe.contents().find('body');
+
+                            if (scrollOut) {
+                                body.css('overflow-x', 'hidden');
+                            }
+
+                            origHeight = body.outerHeight(true);
+                        }
+
+                    } catch (e) {
+                    }
+                }
+
+            } else if (current.autoWidth || current.autoHeight) {
+                inner.addClass('fancybox-tmp');
+
+                // Set width or height in case we need to calculate only one dimension
+                if (!current.autoWidth) {
+                    inner.width(origWidth);
+                }
+
+                if (!current.autoHeight) {
+                    inner.height(origHeight);
+                }
+
+                if (current.autoWidth) {
+                    origWidth = inner.width();
+                }
+
+                if (current.autoHeight) {
+                    origHeight = inner.height();
+                }
+
+                inner.removeClass('fancybox-tmp');
+            }
+
+            width = getScalar(origWidth);
+            height = getScalar(origHeight);
+
+            ratio = origWidth / origHeight;
+
+            // Calculations for the content
+            minWidth = getScalar(isPercentage(minWidth) ? getScalar(minWidth, 'w') - wSpace : minWidth);
+            maxWidth = getScalar(isPercentage(maxWidth) ? getScalar(maxWidth, 'w') - wSpace : maxWidth);
+
+            minHeight = getScalar(isPercentage(minHeight) ? getScalar(minHeight, 'h') - hSpace : minHeight);
+            maxHeight = getScalar(isPercentage(maxHeight) ? getScalar(maxHeight, 'h') - hSpace : maxHeight);
+
+            // These will be used to determine if wrap can fit in the viewport
+            origMaxWidth = maxWidth;
+            origMaxHeight = maxHeight;
+
+            if (current.fitToView) {
+                maxWidth = Math.min(viewport.w - wSpace, maxWidth);
+                maxHeight = Math.min(viewport.h - hSpace, maxHeight);
+            }
+
+            maxWidth_ = viewport.w - wMargin;
+            maxHeight_ = viewport.h - hMargin;
+
+            if (current.aspectRatio) {
+                if (width > maxWidth) {
+                    width = maxWidth;
+                    height = getScalar(width / ratio);
+                }
+
+                if (height > maxHeight) {
+                    height = maxHeight;
+                    width = getScalar(height * ratio);
+                }
+
+                if (width < minWidth) {
+                    width = minWidth;
+                    height = getScalar(width / ratio);
+                }
+
+                if (height < minHeight) {
+                    height = minHeight;
+                    width = getScalar(height * ratio);
+                }
+
+            } else {
+                width = Math.max(minWidth, Math.min(width, maxWidth));
+
+                if (current.autoHeight && current.type !== 'iframe') {
+                    inner.width(width);
+
+                    height = inner.height();
+                }
+
+                height = Math.max(minHeight, Math.min(height, maxHeight));
+            }
+
+            // Try to fit inside viewport (including the title)
+            if (current.fitToView) {
+                inner.width(width).height(height);
+
+                wrap.width(width + wPadding);
+
+                // Real wrap dimensions
+                width_ = wrap.width();
+                height_ = wrap.height();
+
+                if (current.aspectRatio) {
+                    while ((width_ > maxWidth_ || height_ > maxHeight_) && width > minWidth && height > minHeight) {
+                        if (steps++ > 19) {
+                            break;
+                        }
+
+                        height = Math.max(minHeight, Math.min(maxHeight, height - 10));
+                        width = getScalar(height * ratio);
+
+                        if (width < minWidth) {
+                            width = minWidth;
+                            height = getScalar(width / ratio);
+                        }
+
+                        if (width > maxWidth) {
+                            width = maxWidth;
+                            height = getScalar(width / ratio);
+                        }
+
+                        inner.width(width).height(height);
+
+                        wrap.width(width + wPadding);
+
+                        width_ = wrap.width();
+                        height_ = wrap.height();
+                    }
+
+                } else {
+                    width = Math.max(minWidth, Math.min(width, width - (width_ - maxWidth_)));
+                    height = Math.max(minHeight, Math.min(height, height - (height_ - maxHeight_)));
+                }
+            }
+
+            if (scrollOut && scrolling === 'auto' && height < origHeight && (width + wPadding + scrollOut) < maxWidth_) {
+                width += scrollOut;
+            }
+
+            inner.width(width).height(height);
+
+            wrap.width(width + wPadding);
+
+            width_ = wrap.width();
+            height_ = wrap.height();
+
+            canShrink = (width_ > maxWidth_ || height_ > maxHeight_) && width > minWidth && height > minHeight;
+            canExpand = current.aspectRatio ? (width < origMaxWidth && height < origMaxHeight && width < origWidth && height < origHeight) : ((width < origMaxWidth || height < origMaxHeight) && (width < origWidth || height < origHeight));
+
+            $.extend(current, {
+                dim: {
+                    width: getValue(width_),
+                    height: getValue(height_)
+                },
+                origWidth: origWidth,
+                origHeight: origHeight,
+                canShrink: canShrink,
+                canExpand: canExpand,
+                wPadding: wPadding,
+                hPadding: hPadding,
+                wrapSpace: height_ - skin.outerHeight(true),
+                skinSpace: skin.height() - height
+            });
+
+            if (!iframe && current.autoHeight && height > minHeight && height < maxHeight && !canExpand) {
+                inner.height('auto');
+            }
+        },
+
+        _getPosition: function (onlyAbsolute) {
+            var current = F.current,
+                viewport = F.getViewport(),
+                margin = current.margin,
+                width = F.wrap.width() + margin[1] + margin[3],
+                height = F.wrap.height() + margin[0] + margin[2],
+                rez = {
+                    position: 'absolute',
+                    top: margin[0],
+                    left: margin[3]
+                };
+
+            if (current.autoCenter && current.fixed && !onlyAbsolute && height <= viewport.h && width <= viewport.w) {
+                rez.position = 'fixed';
+
+            } else if (!current.locked) {
+                rez.top += viewport.y;
+                rez.left += viewport.x;
+            }
+
+            rez.top = getValue(Math.max(rez.top, rez.top + ((viewport.h - height) * current.topRatio)));
+            rez.left = getValue(Math.max(rez.left, rez.left + ((viewport.w - width) * current.leftRatio)));
+
+            return rez;
+        },
+
+        _afterZoomIn: function () {
+            var current = F.current;
+
+            if (!current) {
+                return;
+            }
+
+            F.isOpen = F.isOpened = true;
+
+            F.wrap.css('overflow', 'visible').addClass('fancybox-opened').hide().show(0);
+
+            F.update();
+
+            // Assign a click event
+            if (current.closeClick || (current.nextClick && F.group.length > 1)) {
+                F.inner.css('cursor', 'pointer').bind('click.fb', function (e) {
+                    if (!$(e.target).is('a') && !$(e.target).parent().is('a')) {
+                        e.preventDefault();
+
+                        F[current.closeClick ? 'close' : 'next']();
+                    }
+                });
+            }
+
+            // Create a close button
+            if (current.closeBtn) {
+                $(current.tpl.closeBtn).appendTo(F.skin).bind('click.fb', function (e) {
+                    e.preventDefault();
+
+                    F.close();
+                });
+            }
+
+            // Create navigation arrows
+            if (current.arrows && F.group.length > 1) {
+                if (current.loop || current.index > 0) {
+                    $(current.tpl.prev).appendTo(F.outer).bind('click.fb', F.prev);
+                }
+
+                if (current.loop || current.index < F.group.length - 1) {
+                    $(current.tpl.next).appendTo(F.outer).bind('click.fb', F.next);
+                }
+            }
+
+            F.trigger('afterShow');
+
+            // Stop the slideshow if this is the last item
+            if (!current.loop && current.index === current.group.length - 1) {
+
+                F.play(false);
+
+            } else if (F.opts.autoPlay && !F.player.isActive) {
+                F.opts.autoPlay = false;
+
+                F.play(true);
+            }
+        },
+
+        _afterZoomOut: function (obj) {
+            obj = obj || F.current;
+
+            $('.fancybox-wrap').trigger('onReset').remove();
+
+            $.extend(F, {
+                group: {},
+                opts: {},
+                router: false,
+                current: null,
+                isActive: false,
+                isOpened: false,
+                isOpen: false,
+                isClosing: false,
+                wrap: null,
+                skin: null,
+                outer: null,
+                inner: null
+            });
+
+            F.trigger('afterClose', obj);
+        }
+    });
+
+    /*
+     *	Default transitions
+     */
+
+    F.transitions = {
+        getOrigPosition: function () {
+            var current = F.current,
+                element = current.element,
+                orig = current.orig,
+                pos = {},
+                width = 50,
+                height = 50,
+                hPadding = current.hPadding,
+                wPadding = current.wPadding,
+                viewport = F.getViewport();
+
+            if (!orig && current.isDom && element.is(':visible')) {
+                orig = element.find('img:first');
+
+                if (!orig.length) {
+                    orig = element;
+                }
+            }
+
+            if (isQuery(orig)) {
+                pos = orig.offset();
+
+                if (orig.is('img')) {
+                    width = orig.outerWidth();
+                    height = orig.outerHeight();
+                }
+
+            } else {
+                pos.top = viewport.y + (viewport.h - height) * current.topRatio;
+                pos.left = viewport.x + (viewport.w - width) * current.leftRatio;
+            }
+
+            if (F.wrap.css('position') === 'fixed' || current.locked) {
+                pos.top -= viewport.y;
+                pos.left -= viewport.x;
+            }
+
+            pos = {
+                top: getValue(pos.top - hPadding * current.topRatio),
+                left: getValue(pos.left - wPadding * current.leftRatio),
+                width: getValue(width + wPadding),
+                height: getValue(height + hPadding)
+            };
+
+            return pos;
+        },
+
+        step: function (now, fx) {
+            var ratio,
+                padding,
+                value,
+                prop = fx.prop,
+                current = F.current,
+                wrapSpace = current.wrapSpace,
+                skinSpace = current.skinSpace;
+
+            if (prop === 'width' || prop === 'height') {
+                ratio = fx.end === fx.start ? 1 : (now - fx.start) / (fx.end - fx.start);
+
+                if (F.isClosing) {
+                    ratio = 1 - ratio;
+                }
+
+                padding = prop === 'width' ? current.wPadding : current.hPadding;
+                value = now - padding;
+
+                F.skin[prop](getScalar(prop === 'width' ? value : value - (wrapSpace * ratio)));
+                F.inner[prop](getScalar(prop === 'width' ? value : value - (wrapSpace * ratio) - (skinSpace * ratio)));
+            }
+        },
+
+        zoomIn: function () {
+            var current = F.current,
+                startPos = current.pos,
+                effect = current.openEffect,
+                elastic = effect === 'elastic',
+                endPos = $.extend({opacity: 1}, startPos);
+
+            // Remove "position" property that breaks older IE
+            delete endPos.position;
+
+            if (elastic) {
+                startPos = this.getOrigPosition();
+
+                if (current.openOpacity) {
+                    startPos.opacity = 0.1;
+                }
+
+            } else if (effect === 'fade') {
+                startPos.opacity = 0.1;
+            }
+
+            F.wrap.css(startPos).animate(endPos, {
+                duration: effect === 'none' ? 0 : current.openSpeed,
+                easing: current.openEasing,
+                step: elastic ? this.step : null,
+                complete: F._afterZoomIn
+            });
+        },
+
+        zoomOut: function () {
+            var current = F.current,
+                effect = current.closeEffect,
+                elastic = effect === 'elastic',
+                endPos = {opacity: 0.1};
+
+            if (elastic) {
+                endPos = this.getOrigPosition();
+
+                if (current.closeOpacity) {
+                    endPos.opacity = 0.1;
+                }
+            }
+
+            F.wrap.animate(endPos, {
+                duration: effect === 'none' ? 0 : current.closeSpeed,
+                easing: current.closeEasing,
+                step: elastic ? this.step : null,
+                complete: F._afterZoomOut
+            });
+        },
+
+        changeIn: function () {
+            var current = F.current,
+                effect = current.nextEffect,
+                startPos = current.pos,
+                endPos = {opacity: 1},
+                direction = F.direction,
+                distance = 200,
+                field;
+
+            startPos.opacity = 0.1;
+
+            if (effect === 'elastic') {
+                field = direction === 'down' || direction === 'up' ? 'top' : 'left';
+
+                if (direction === 'down' || direction === 'right') {
+                    startPos[field] = getValue(getScalar(startPos[field]) - distance);
+                    endPos[field] = '+=' + distance + 'px';
+
+                } else {
+                    startPos[field] = getValue(getScalar(startPos[field]) + distance);
+                    endPos[field] = '-=' + distance + 'px';
+                }
+            }
+
+            // Workaround for http://bugs.jquery.com/ticket/12273
+            if (effect === 'none') {
+                F._afterZoomIn();
+
+            } else {
+                F.wrap.css(startPos).animate(endPos, {
+                    duration: current.nextSpeed,
+                    easing: current.nextEasing,
+                    complete: F._afterZoomIn
+                });
+            }
+        },
+
+        changeOut: function () {
+            var previous = F.previous,
+                effect = previous.prevEffect,
+                endPos = {opacity: 0.1},
+                direction = F.direction,
+                distance = 200;
+
+            if (effect === 'elastic') {
+                endPos[direction === 'down' || direction === 'up' ? 'top' : 'left'] = ( direction === 'up' || direction === 'left' ? '-' : '+' ) + '=' + distance + 'px';
+            }
+
+            previous.wrap.animate(endPos, {
+                duration: effect === 'none' ? 0 : previous.prevSpeed,
+                easing: previous.prevEasing,
+                complete: function () {
+                    $(this).trigger('onReset').remove();
+                }
+            });
+        }
+    };
+
+    /*
+     *	Overlay helper
+     */
+
+    F.helpers.overlay = {
+        defaults: {
+            closeClick: true,      // if true, fancyBox will be closed when user clicks on the overlay
+            speedOut: 200,       // duration of fadeOut animation
+            showEarly: true,      // indicates if should be opened immediately or wait until the content is ready
+            css: {},        // custom CSS properties
+            locked: !isTouch,  // if true, the content will be locked into overlay
+            fixed: true       // if false, the overlay CSS position property will not be set to "fixed"
+        },
+
+        overlay: null,      // current handle
+        fixed: false,     // indicates if the overlay has position "fixed"
+        el: $('html'), // element that contains "the lock"
+
+        // Public methods
+        create: function (opts) {
+            var parent;
+
+            opts = $.extend({}, this.defaults, opts);
+
+            if (this.overlay) {
+                this.close();
+            }
+
+            parent = F.coming ? F.coming.parent : opts.parent;
+
+            this.overlay = $('<div class="fancybox-overlay"></div>').appendTo(parent && parent.length ? parent : 'body');
+            this.fixed = false;
+
+            if (opts.fixed && F.defaults.fixed) {
+                this.overlay.addClass('fancybox-overlay-fixed');
+
+                this.fixed = true;
+            }
+        },
+
+        open: function (opts) {
+            var that = this;
+
+            opts = $.extend({}, this.defaults, opts);
+
+            if (this.overlay) {
+                this.overlay.unbind('.overlay').width('auto').height('auto');
+
+            } else {
+                this.create(opts);
+            }
+
+            if (!this.fixed) {
+                W.bind('resize.overlay', $.proxy(this.update, this));
+
+                this.update();
+            }
+
+            if (opts.closeClick) {
+                this.overlay.bind('click.overlay', function (e) {
+                    if ($(e.target).hasClass('fancybox-overlay')) {
+                        if (F.isActive) {
+                            F.close();
+                        } else {
+                            that.close();
+                        }
+
+                        return false;
+                    }
+                });
+            }
+
+            this.overlay.css(opts.css).show();
+        },
+
+        close: function () {
+            W.unbind('resize.overlay');
+
+            if (this.el.hasClass('fancybox-lock')) {
+                $('.fancybox-margin').removeClass('fancybox-margin');
+
+                this.el.removeClass('fancybox-lock');
+
+                W.scrollTop(this.scrollV).scrollLeft(this.scrollH);
+            }
+
+            $('.fancybox-overlay').remove().hide();
+
+            $.extend(this, {
+                overlay: null,
+                fixed: false
+            });
+        },
+
+        // Private, callbacks
+
+        update: function () {
+            var width = '100%', offsetWidth;
+
+            // Reset width/height so it will not mess
+            this.overlay.width(width).height('100%');
+
+            // jQuery does not return reliable result for IE
+            if (IE) {
+                offsetWidth = Math.max(document.documentElement.offsetWidth, document.body.offsetWidth);
+
+                if (D.width() > offsetWidth) {
+                    width = D.width();
+                }
+
+            } else if (D.width() > W.width()) {
+                width = D.width();
+            }
+
+            this.overlay.width(width).height(D.height());
+        },
+
+        // This is where we can manipulate DOM, because later it would cause iframes to reload
+        onReady: function (opts, obj) {
+            var overlay = this.overlay;
+
+            $('.fancybox-overlay').stop(true, true);
+
+            if (!overlay) {
+                this.create(opts);
+            }
+
+            if (opts.locked && this.fixed && obj.fixed) {
+                obj.locked = this.overlay.append(obj.wrap);
+                obj.fixed = false;
+            }
+
+            if (opts.showEarly === true) {
+                this.beforeShow.apply(this, arguments);
+            }
+        },
+
+        beforeShow: function (opts, obj) {
+            if (obj.locked && !this.el.hasClass('fancybox-lock')) {
+                if (this.fixPosition !== false) {
+                    $('*:not(object)').filter(function () {
+                        return ($(this).css('position') === 'fixed' && !$(this).hasClass("fancybox-overlay") && !$(this).hasClass("fancybox-wrap") );
+                    }).addClass('fancybox-margin');
+                }
+
+                this.el.addClass('fancybox-margin');
+
+                this.scrollV = W.scrollTop();
+                this.scrollH = W.scrollLeft();
+
+                this.el.addClass('fancybox-lock');
+
+                W.scrollTop(this.scrollV).scrollLeft(this.scrollH);
+            }
+
+            this.open(opts);
+        },
+
+        onUpdate: function () {
+            if (!this.fixed) {
+                this.update();
+            }
+        },
+
+        afterClose: function (opts) {
+            // Remove overlay if exists and fancyBox is not opening
+            // (e.g., it is not being open using afterClose callback)
+            if (this.overlay && !F.coming) {
+                this.overlay.fadeOut(opts.speedOut, $.proxy(this.close, this));
+            }
+        }
+    };
+
+    /*
+     *	Title helper
+     */
+
+    F.helpers.title = {
+        defaults: {
+            type: 'float', // 'float', 'inside', 'outside' or 'over',
+            position: 'bottom' // 'top' or 'bottom'
+        },
+
+        beforeShow: function (opts) {
+            var current = F.current,
+                text = current.title,
+                type = opts.type,
+                title,
+                target;
+
+            if ($.isFunction(text)) {
+                text = text.call(current.element, current);
+            }
+
+            if (!isString(text) || $.trim(text) === '') {
+                return;
+            }
+
+            title = $('<div class="fancybox-title fancybox-title-' + type + '-wrap">' + text + '</div>');
+
+            switch (type) {
+                case 'inside':
+                    target = F.skin;
+                    break;
+
+                case 'outside':
+                    target = F.wrap;
+                    break;
+
+                case 'over':
+                    target = F.inner;
+                    break;
+
+                default: // 'float'
+                    target = F.skin;
+
+                    title.appendTo('body');
+
+                    if (IE) {
+                        title.width(title.width());
+                    }
+
+                    title.wrapInner('<span class="child"></span>');
+
+                    //Increase bottom margin so this title will also fit into viewport
+                    F.current.margin[2] += Math.abs(getScalar(title.css('margin-bottom')));
+                    break;
+            }
+
+            title[(opts.position === 'top' ? 'prependTo' : 'appendTo')](target);
+        }
+    };
+
+    // jQuery plugin initialization
+    $.fn.fancybox = function (options) {
+        var index,
+            that = $(this),
+            selector = this.selector || '',
+            run = function (e) {
+                var what = $(this).blur(), idx = index, relType, relVal;
+
+                if (!(e.ctrlKey || e.altKey || e.shiftKey || e.metaKey) && !what.is('.fancybox-wrap')) {
+                    relType = options.groupAttr || 'data-fancybox-group';
+                    relVal = what.attr(relType);
+
+                    if (!relVal) {
+                        relType = 'rel';
+                        relVal = what.get(0)[relType];
+                    }
+
+                    if (relVal && relVal !== '' && relVal !== 'nofollow') {
+                        what = selector.length ? $(selector) : that;
+                        what = what.filter('[' + relType + '="' + relVal + '"]');
+                        idx = what.index(this);
+                    }
+
+                    options.index = idx;
+
+                    // Stop an event from bubbling if everything is fine
+                    if (F.open(what, options) !== false) {
+                        e.preventDefault();
+                    }
+                }
+            };
+
+        options = options || {};
+        index = options.index || 0;
+
+        if (!selector || options.live === false) {
+            that.unbind('click.fb-start').bind('click.fb-start', run);
+
+        } else {
+            D.undelegate(selector, 'click.fb-start').delegate(selector + ":not('.fancybox-item, .fancybox-nav')", 'click.fb-start', run);
+        }
+
+        this.filter('[data-fancybox-start=1]').trigger('click');
+
+        return this;
+    };
+
+    // Tests that need a body at doc ready
+    D.ready(function () {
+        var w1, w2;
+
+        if ($.scrollbarWidth === undefined) {
+            // http://benalman.com/projects/jquery-misc-plugins/#scrollbarwidth
+            $.scrollbarWidth = function () {
+                var parent = $('<div style="width:50px;height:50px;overflow:auto"><div/></div>').appendTo('body'),
+                    child = parent.children(),
+                    width = child.innerWidth() - child.height(99).innerWidth();
+
+                parent.remove();
+
+                return width;
+            };
+        }
+
+        if ($.support.fixedPosition === undefined) {
+            $.support.fixedPosition = (function () {
+                var elem = $('<div style="position:fixed;top:20px;"></div>').appendTo('body'),
+                    fixed = ( elem[0].offsetTop === 20 || elem[0].offsetTop === 15 );
+
+                elem.remove();
+
+                return fixed;
+            }());
+        }
+
+        $.extend(F.defaults, {
+            scrollbarWidth: $.scrollbarWidth(),
+            fixed: $.support.fixedPosition,
+            parent: $('body')
+        });
+
+        //Get real width of page scroll-bar
+        w1 = $(window).width();
+
+        H.addClass('fancybox-lock-test');
+
+        w2 = $(window).width();
+
+        H.removeClass('fancybox-lock-test');
+
+        $("<style type='text/css'>.fancybox-margin{margin-right:" + (w2 - w1) + "px;}</style>").appendTo("head");
+    });
+
+}(window, document, jQuery));

+ 46 - 0
src/assets/fancybox/js/jquery.fancybox.pack.js

@@ -0,0 +1,46 @@
+/*! fancyBox v2.1.5 fancyapps.com | fancyapps.com/fancybox/#license */
+(function(s,H,f,w){var K=f("html"),q=f(s),p=f(H),b=f.fancybox=function(){b.open.apply(this,arguments)},J=navigator.userAgent.match(/msie/i),C=null,t=H.createTouch!==w,u=function(a){return a&&a.hasOwnProperty&&a instanceof f},r=function(a){return a&&"string"===f.type(a)},F=function(a){return r(a)&&0<a.indexOf("%")},m=function(a,d){var e=parseInt(a,10)||0;d&&F(a)&&(e*=b.getViewport()[d]/100);return Math.ceil(e)},x=function(a,b){return m(a,b)+"px"};f.extend(b,{version:"2.1.5",defaults:{padding:15,margin:20,
+width:800,height:600,minWidth:100,minHeight:100,maxWidth:9999,maxHeight:9999,pixelRatio:1,autoSize:!0,autoHeight:!1,autoWidth:!1,autoResize:!0,autoCenter:!t,fitToView:!0,aspectRatio:!1,topRatio:0.5,leftRatio:0.5,scrolling:"auto",wrapCSS:"",arrows:!0,closeBtn:!0,closeClick:!1,nextClick:!1,mouseWheel:!0,autoPlay:!1,playSpeed:3E3,preload:3,modal:!1,loop:!0,ajax:{dataType:"html",headers:{"X-fancyBox":!0}},iframe:{scrolling:"auto",preload:!0},swf:{wmode:"transparent",allowfullscreen:"true",allowscriptaccess:"always"},
+keys:{next:{13:"left",34:"up",39:"left",40:"up"},prev:{8:"right",33:"down",37:"right",38:"down"},close:[27],play:[32],toggle:[70]},direction:{next:"left",prev:"right"},scrollOutside:!0,index:0,type:null,href:null,content:null,title:null,tpl:{wrap:'<div class="fancybox-wrap" tabIndex="-1"><div class="fancybox-skin"><div class="fancybox-outer"><div class="fancybox-inner"></div></div></div></div>',image:'<img class="fancybox-image" src="{href}" alt="" />',iframe:'<iframe id="fancybox-frame{rnd}" name="fancybox-frame{rnd}" class="fancybox-iframe" frameborder="0" vspace="0" hspace="0" webkitAllowFullScreen mozallowfullscreen allowFullScreen'+
+(J?' allowtransparency="true"':"")+"></iframe>",error:'<p class="fancybox-error">The requested content cannot be loaded.<br/>Please try again later.</p>',closeBtn:'<a title="Close" class="fancybox-item fancybox-close" href="javascript:;"></a>',next:'<a title="Next" class="fancybox-nav fancybox-next" href="javascript:;"><span></span></a>',prev:'<a title="Previous" class="fancybox-nav fancybox-prev" href="javascript:;"><span></span></a>'},openEffect:"fade",openSpeed:250,openEasing:"swing",openOpacity:!0,
+openMethod:"zoomIn",closeEffect:"fade",closeSpeed:250,closeEasing:"swing",closeOpacity:!0,closeMethod:"zoomOut",nextEffect:"elastic",nextSpeed:250,nextEasing:"swing",nextMethod:"changeIn",prevEffect:"elastic",prevSpeed:250,prevEasing:"swing",prevMethod:"changeOut",helpers:{overlay:!0,title:!0},onCancel:f.noop,beforeLoad:f.noop,afterLoad:f.noop,beforeShow:f.noop,afterShow:f.noop,beforeChange:f.noop,beforeClose:f.noop,afterClose:f.noop},group:{},opts:{},previous:null,coming:null,current:null,isActive:!1,
+isOpen:!1,isOpened:!1,wrap:null,skin:null,outer:null,inner:null,player:{timer:null,isActive:!1},ajaxLoad:null,imgPreload:null,transitions:{},helpers:{},open:function(a,d){if(a&&(f.isPlainObject(d)||(d={}),!1!==b.close(!0)))return f.isArray(a)||(a=u(a)?f(a).get():[a]),f.each(a,function(e,c){var l={},g,h,k,n,m;"object"===f.type(c)&&(c.nodeType&&(c=f(c)),u(c)?(l={href:c.data("fancybox-href")||c.attr("href"),title:f("<div/>").text(c.data("fancybox-title")||c.attr("title")).html(),isDom:!0,element:c},
+f.metadata&&f.extend(!0,l,c.metadata())):l=c);g=d.href||l.href||(r(c)?c:null);h=d.title!==w?d.title:l.title||"";n=(k=d.content||l.content)?"html":d.type||l.type;!n&&l.isDom&&(n=c.data("fancybox-type"),n||(n=(n=c.prop("class").match(/fancybox\.(\w+)/))?n[1]:null));r(g)&&(n||(b.isImage(g)?n="image":b.isSWF(g)?n="swf":"#"===g.charAt(0)?n="inline":r(c)&&(n="html",k=c)),"ajax"===n&&(m=g.split(/\s+/,2),g=m.shift(),m=m.shift()));k||("inline"===n?g?k=f(r(g)?g.replace(/.*(?=#[^\s]+$)/,""):g):l.isDom&&(k=c):
+"html"===n?k=g:n||g||!l.isDom||(n="inline",k=c));f.extend(l,{href:g,type:n,content:k,title:h,selector:m});a[e]=l}),b.opts=f.extend(!0,{},b.defaults,d),d.keys!==w&&(b.opts.keys=d.keys?f.extend({},b.defaults.keys,d.keys):!1),b.group=a,b._start(b.opts.index)},cancel:function(){var a=b.coming;a&&!1===b.trigger("onCancel")||(b.hideLoading(),a&&(b.ajaxLoad&&b.ajaxLoad.abort(),b.ajaxLoad=null,b.imgPreload&&(b.imgPreload.onload=b.imgPreload.onerror=null),a.wrap&&a.wrap.stop(!0,!0).trigger("onReset").remove(),
+b.coming=null,b.current||b._afterZoomOut(a)))},close:function(a){b.cancel();!1!==b.trigger("beforeClose")&&(b.unbindEvents(),b.isActive&&(b.isOpen&&!0!==a?(b.isOpen=b.isOpened=!1,b.isClosing=!0,f(".fancybox-item, .fancybox-nav").remove(),b.wrap.stop(!0,!0).removeClass("fancybox-opened"),b.transitions[b.current.closeMethod]()):(f(".fancybox-wrap").stop(!0).trigger("onReset").remove(),b._afterZoomOut())))},play:function(a){var d=function(){clearTimeout(b.player.timer)},e=function(){d();b.current&&b.player.isActive&&
+(b.player.timer=setTimeout(b.next,b.current.playSpeed))},c=function(){d();p.unbind(".player");b.player.isActive=!1;b.trigger("onPlayEnd")};!0===a||!b.player.isActive&&!1!==a?b.current&&(b.current.loop||b.current.index<b.group.length-1)&&(b.player.isActive=!0,p.bind({"onCancel.player beforeClose.player":c,"onUpdate.player":e,"beforeLoad.player":d}),e(),b.trigger("onPlayStart")):c()},next:function(a){var d=b.current;d&&(r(a)||(a=d.direction.next),b.jumpto(d.index+1,a,"next"))},prev:function(a){var d=
+b.current;d&&(r(a)||(a=d.direction.prev),b.jumpto(d.index-1,a,"prev"))},jumpto:function(a,d,e){var c=b.current;c&&(a=m(a),b.direction=d||c.direction[a>=c.index?"next":"prev"],b.router=e||"jumpto",c.loop&&(0>a&&(a=c.group.length+a%c.group.length),a%=c.group.length),c.group[a]!==w&&(b.cancel(),b._start(a)))},reposition:function(a,d){var e=b.current,c=e?e.wrap:null,l;c&&(l=b._getPosition(d),a&&"scroll"===a.type?(delete l.position,c.stop(!0,!0).animate(l,200)):(c.css(l),e.pos=f.extend({},e.dim,l)))},
+update:function(a){var d=a&&a.originalEvent&&a.originalEvent.type,e=!d||"orientationchange"===d;e&&(clearTimeout(C),C=null);b.isOpen&&!C&&(C=setTimeout(function(){var c=b.current;c&&!b.isClosing&&(b.wrap.removeClass("fancybox-tmp"),(e||"load"===d||"resize"===d&&c.autoResize)&&b._setDimension(),"scroll"===d&&c.canShrink||b.reposition(a),b.trigger("onUpdate"),C=null)},e&&!t?0:300))},toggle:function(a){b.isOpen&&(b.current.fitToView="boolean"===f.type(a)?a:!b.current.fitToView,t&&(b.wrap.removeAttr("style").addClass("fancybox-tmp"),
+b.trigger("onUpdate")),b.update())},hideLoading:function(){p.unbind(".loading");f("#fancybox-loading").remove()},showLoading:function(){var a,d;b.hideLoading();a=f('<div id="fancybox-loading"><div></div></div>').click(b.cancel).appendTo("body");p.bind("keydown.loading",function(a){27===(a.which||a.keyCode)&&(a.preventDefault(),b.cancel())});b.defaults.fixed||(d=b.getViewport(),a.css({position:"absolute",top:0.5*d.h+d.y,left:0.5*d.w+d.x}));b.trigger("onLoading")},getViewport:function(){var a=b.current&&
+b.current.locked||!1,d={x:q.scrollLeft(),y:q.scrollTop()};a&&a.length?(d.w=a[0].clientWidth,d.h=a[0].clientHeight):(d.w=t&&s.innerWidth?s.innerWidth:q.width(),d.h=t&&s.innerHeight?s.innerHeight:q.height());return d},unbindEvents:function(){b.wrap&&u(b.wrap)&&b.wrap.unbind(".fb");p.unbind(".fb");q.unbind(".fb")},bindEvents:function(){var a=b.current,d;a&&(q.bind("orientationchange.fb"+(t?"":" resize.fb")+(a.autoCenter&&!a.locked?" scroll.fb":""),b.update),(d=a.keys)&&p.bind("keydown.fb",function(e){var c=
+e.which||e.keyCode,l=e.target||e.srcElement;if(27===c&&b.coming)return!1;e.ctrlKey||e.altKey||e.shiftKey||e.metaKey||l&&(l.type||f(l).is("[contenteditable]"))||f.each(d,function(d,l){if(1<a.group.length&&l[c]!==w)return b[d](l[c]),e.preventDefault(),!1;if(-1<f.inArray(c,l))return b[d](),e.preventDefault(),!1})}),f.fn.mousewheel&&a.mouseWheel&&b.wrap.bind("mousewheel.fb",function(d,c,l,g){for(var h=f(d.target||null),k=!1;h.length&&!(k||h.is(".fancybox-skin")||h.is(".fancybox-wrap"));)k=h[0]&&!(h[0].style.overflow&&
+"hidden"===h[0].style.overflow)&&(h[0].clientWidth&&h[0].scrollWidth>h[0].clientWidth||h[0].clientHeight&&h[0].scrollHeight>h[0].clientHeight),h=f(h).parent();0!==c&&!k&&1<b.group.length&&!a.canShrink&&(0<g||0<l?b.prev(0<g?"down":"left"):(0>g||0>l)&&b.next(0>g?"up":"right"),d.preventDefault())}))},trigger:function(a,d){var e,c=d||b.coming||b.current;if(c){f.isFunction(c[a])&&(e=c[a].apply(c,Array.prototype.slice.call(arguments,1)));if(!1===e)return!1;c.helpers&&f.each(c.helpers,function(d,e){if(e&&
+b.helpers[d]&&f.isFunction(b.helpers[d][a]))b.helpers[d][a](f.extend(!0,{},b.helpers[d].defaults,e),c)})}p.trigger(a)},isImage:function(a){return r(a)&&a.match(/(^data:image\/.*,)|(\.(jp(e|g|eg)|gif|png|bmp|webp|svg)((\?|#).*)?$)/i)},isSWF:function(a){return r(a)&&a.match(/\.(swf)((\?|#).*)?$/i)},_start:function(a){var d={},e,c;a=m(a);e=b.group[a]||null;if(!e)return!1;d=f.extend(!0,{},b.opts,e);e=d.margin;c=d.padding;"number"===f.type(e)&&(d.margin=[e,e,e,e]);"number"===f.type(c)&&(d.padding=[c,c,
+c,c]);d.modal&&f.extend(!0,d,{closeBtn:!1,closeClick:!1,nextClick:!1,arrows:!1,mouseWheel:!1,keys:null,helpers:{overlay:{closeClick:!1}}});d.autoSize&&(d.autoWidth=d.autoHeight=!0);"auto"===d.width&&(d.autoWidth=!0);"auto"===d.height&&(d.autoHeight=!0);d.group=b.group;d.index=a;b.coming=d;if(!1===b.trigger("beforeLoad"))b.coming=null;else{c=d.type;e=d.href;if(!c)return b.coming=null,b.current&&b.router&&"jumpto"!==b.router?(b.current.index=a,b[b.router](b.direction)):!1;b.isActive=!0;if("image"===
+c||"swf"===c)d.autoHeight=d.autoWidth=!1,d.scrolling="visible";"image"===c&&(d.aspectRatio=!0);"iframe"===c&&t&&(d.scrolling="scroll");d.wrap=f(d.tpl.wrap).addClass("fancybox-"+(t?"mobile":"desktop")+" fancybox-type-"+c+" fancybox-tmp "+d.wrapCSS).appendTo(d.parent||"body");f.extend(d,{skin:f(".fancybox-skin",d.wrap),outer:f(".fancybox-outer",d.wrap),inner:f(".fancybox-inner",d.wrap)});f.each(["Top","Right","Bottom","Left"],function(a,b){d.skin.css("padding"+b,x(d.padding[a]))});b.trigger("onReady");
+if("inline"===c||"html"===c){if(!d.content||!d.content.length)return b._error("content")}else if(!e)return b._error("href");"image"===c?b._loadImage():"ajax"===c?b._loadAjax():"iframe"===c?b._loadIframe():b._afterLoad()}},_error:function(a){f.extend(b.coming,{type:"html",autoWidth:!0,autoHeight:!0,minWidth:0,minHeight:0,scrolling:"no",hasError:a,content:b.coming.tpl.error});b._afterLoad()},_loadImage:function(){var a=b.imgPreload=new Image;a.onload=function(){this.onload=this.onerror=null;b.coming.width=
+this.width/b.opts.pixelRatio;b.coming.height=this.height/b.opts.pixelRatio;b._afterLoad()};a.onerror=function(){this.onload=this.onerror=null;b._error("image")};a.src=b.coming.href;!0!==a.complete&&b.showLoading()},_loadAjax:function(){var a=b.coming;b.showLoading();b.ajaxLoad=f.ajax(f.extend({},a.ajax,{url:a.href,error:function(a,e){b.coming&&"abort"!==e?b._error("ajax",a):b.hideLoading()},success:function(d,e){"success"===e&&(a.content=d,b._afterLoad())}}))},_loadIframe:function(){var a=b.coming,
+d=f(a.tpl.iframe.replace(/\{rnd\}/g,(new Date).getTime())).attr("scrolling",t?"auto":a.iframe.scrolling).attr("src",a.href);f(a.wrap).bind("onReset",function(){try{f(this).find("iframe").hide().attr("src","//about:blank").end().empty()}catch(a){}});a.iframe.preload&&(b.showLoading(),d.one("load",function(){f(this).data("ready",1);t||f(this).bind("load.fb",b.update);f(this).parents(".fancybox-wrap").width("100%").removeClass("fancybox-tmp").show();b._afterLoad()}));a.content=d.appendTo(a.inner);a.iframe.preload||
+b._afterLoad()},_preloadImages:function(){var a=b.group,d=b.current,e=a.length,c=d.preload?Math.min(d.preload,e-1):0,f,g;for(g=1;g<=c;g+=1)f=a[(d.index+g)%e],"image"===f.type&&f.href&&((new Image).src=f.href)},_afterLoad:function(){var a=b.coming,d=b.current,e,c,l,g,h;b.hideLoading();if(a&&!1!==b.isActive)if(!1===b.trigger("afterLoad",a,d))a.wrap.stop(!0).trigger("onReset").remove(),b.coming=null;else{d&&(b.trigger("beforeChange",d),d.wrap.stop(!0).removeClass("fancybox-opened").find(".fancybox-item, .fancybox-nav").remove());
+b.unbindEvents();e=a.content;c=a.type;l=a.scrolling;f.extend(b,{wrap:a.wrap,skin:a.skin,outer:a.outer,inner:a.inner,current:a,previous:d});g=a.href;switch(c){case "inline":case "ajax":case "html":a.selector?e=f("<div>").html(e).find(a.selector):u(e)&&(e.data("fancybox-placeholder")||e.data("fancybox-placeholder",f('<div class="fancybox-placeholder"></div>').insertAfter(e).hide()),e=e.show().detach(),a.wrap.bind("onReset",function(){f(this).find(e).length&&e.hide().replaceAll(e.data("fancybox-placeholder")).data("fancybox-placeholder",
+!1)}));break;case "image":e=a.tpl.image.replace(/\{href\}/g,g);break;case "swf":e='<object id="fancybox-swf" classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" width="100%" height="100%"><param name="movie" value="'+g+'"></param>',h="",f.each(a.swf,function(a,b){e+='<param name="'+a+'" value="'+b+'"></param>';h+=" "+a+'="'+b+'"'}),e+='<embed src="'+g+'" type="application/x-shockwave-flash" width="100%" height="100%"'+h+"></embed></object>"}u(e)&&e.parent().is(a.inner)||a.inner.append(e);b.trigger("beforeShow");
+a.inner.css("overflow","yes"===l?"scroll":"no"===l?"hidden":l);b._setDimension();b.reposition();b.isOpen=!1;b.coming=null;b.bindEvents();if(!b.isOpened)f(".fancybox-wrap").not(a.wrap).stop(!0).trigger("onReset").remove();else if(d.prevMethod)b.transitions[d.prevMethod]();b.transitions[b.isOpened?a.nextMethod:a.openMethod]();b._preloadImages()}},_setDimension:function(){var a=b.getViewport(),d=0,e=!1,c=!1,e=b.wrap,l=b.skin,g=b.inner,h=b.current,c=h.width,k=h.height,n=h.minWidth,v=h.minHeight,p=h.maxWidth,
+q=h.maxHeight,t=h.scrolling,r=h.scrollOutside?h.scrollbarWidth:0,y=h.margin,z=m(y[1]+y[3]),s=m(y[0]+y[2]),w,A,u,D,B,G,C,E,I;e.add(l).add(g).width("auto").height("auto").removeClass("fancybox-tmp");y=m(l.outerWidth(!0)-l.width());w=m(l.outerHeight(!0)-l.height());A=z+y;u=s+w;D=F(c)?(a.w-A)*m(c)/100:c;B=F(k)?(a.h-u)*m(k)/100:k;if("iframe"===h.type){if(I=h.content,h.autoHeight&&1===I.data("ready"))try{I[0].contentWindow.document.location&&(g.width(D).height(9999),G=I.contents().find("body"),r&&G.css("overflow-x",
+"hidden"),B=G.outerHeight(!0))}catch(H){}}else if(h.autoWidth||h.autoHeight)g.addClass("fancybox-tmp"),h.autoWidth||g.width(D),h.autoHeight||g.height(B),h.autoWidth&&(D=g.width()),h.autoHeight&&(B=g.height()),g.removeClass("fancybox-tmp");c=m(D);k=m(B);E=D/B;n=m(F(n)?m(n,"w")-A:n);p=m(F(p)?m(p,"w")-A:p);v=m(F(v)?m(v,"h")-u:v);q=m(F(q)?m(q,"h")-u:q);G=p;C=q;h.fitToView&&(p=Math.min(a.w-A,p),q=Math.min(a.h-u,q));A=a.w-z;s=a.h-s;h.aspectRatio?(c>p&&(c=p,k=m(c/E)),k>q&&(k=q,c=m(k*E)),c<n&&(c=n,k=m(c/
+E)),k<v&&(k=v,c=m(k*E))):(c=Math.max(n,Math.min(c,p)),h.autoHeight&&"iframe"!==h.type&&(g.width(c),k=g.height()),k=Math.max(v,Math.min(k,q)));if(h.fitToView)if(g.width(c).height(k),e.width(c+y),a=e.width(),z=e.height(),h.aspectRatio)for(;(a>A||z>s)&&c>n&&k>v&&!(19<d++);)k=Math.max(v,Math.min(q,k-10)),c=m(k*E),c<n&&(c=n,k=m(c/E)),c>p&&(c=p,k=m(c/E)),g.width(c).height(k),e.width(c+y),a=e.width(),z=e.height();else c=Math.max(n,Math.min(c,c-(a-A))),k=Math.max(v,Math.min(k,k-(z-s)));r&&"auto"===t&&k<B&&
+c+y+r<A&&(c+=r);g.width(c).height(k);e.width(c+y);a=e.width();z=e.height();e=(a>A||z>s)&&c>n&&k>v;c=h.aspectRatio?c<G&&k<C&&c<D&&k<B:(c<G||k<C)&&(c<D||k<B);f.extend(h,{dim:{width:x(a),height:x(z)},origWidth:D,origHeight:B,canShrink:e,canExpand:c,wPadding:y,hPadding:w,wrapSpace:z-l.outerHeight(!0),skinSpace:l.height()-k});!I&&h.autoHeight&&k>v&&k<q&&!c&&g.height("auto")},_getPosition:function(a){var d=b.current,e=b.getViewport(),c=d.margin,f=b.wrap.width()+c[1]+c[3],g=b.wrap.height()+c[0]+c[2],c={position:"absolute",
+top:c[0],left:c[3]};d.autoCenter&&d.fixed&&!a&&g<=e.h&&f<=e.w?c.position="fixed":d.locked||(c.top+=e.y,c.left+=e.x);c.top=x(Math.max(c.top,c.top+(e.h-g)*d.topRatio));c.left=x(Math.max(c.left,c.left+(e.w-f)*d.leftRatio));return c},_afterZoomIn:function(){var a=b.current;a&&((b.isOpen=b.isOpened=!0,b.wrap.css("overflow","visible").addClass("fancybox-opened"),b.update(),(a.closeClick||a.nextClick&&1<b.group.length)&&b.inner.css("cursor","pointer").bind("click.fb",function(d){f(d.target).is("a")||f(d.target).parent().is("a")||
+(d.preventDefault(),b[a.closeClick?"close":"next"]())}),a.closeBtn&&f(a.tpl.closeBtn).appendTo(b.skin).bind("click.fb",function(a){a.preventDefault();b.close()}),a.arrows&&1<b.group.length&&((a.loop||0<a.index)&&f(a.tpl.prev).appendTo(b.outer).bind("click.fb",b.prev),(a.loop||a.index<b.group.length-1)&&f(a.tpl.next).appendTo(b.outer).bind("click.fb",b.next)),b.trigger("afterShow"),a.loop||a.index!==a.group.length-1)?b.opts.autoPlay&&!b.player.isActive&&(b.opts.autoPlay=!1,b.play(!0)):b.play(!1))},
+_afterZoomOut:function(a){a=a||b.current;f(".fancybox-wrap").trigger("onReset").remove();f.extend(b,{group:{},opts:{},router:!1,current:null,isActive:!1,isOpened:!1,isOpen:!1,isClosing:!1,wrap:null,skin:null,outer:null,inner:null});b.trigger("afterClose",a)}});b.transitions={getOrigPosition:function(){var a=b.current,d=a.element,e=a.orig,c={},f=50,g=50,h=a.hPadding,k=a.wPadding,n=b.getViewport();!e&&a.isDom&&d.is(":visible")&&(e=d.find("img:first"),e.length||(e=d));u(e)?(c=e.offset(),e.is("img")&&
+(f=e.outerWidth(),g=e.outerHeight())):(c.top=n.y+(n.h-g)*a.topRatio,c.left=n.x+(n.w-f)*a.leftRatio);if("fixed"===b.wrap.css("position")||a.locked)c.top-=n.y,c.left-=n.x;return c={top:x(c.top-h*a.topRatio),left:x(c.left-k*a.leftRatio),width:x(f+k),height:x(g+h)}},step:function(a,d){var e,c,f=d.prop;c=b.current;var g=c.wrapSpace,h=c.skinSpace;if("width"===f||"height"===f)e=d.end===d.start?1:(a-d.start)/(d.end-d.start),b.isClosing&&(e=1-e),c="width"===f?c.wPadding:c.hPadding,c=a-c,b.skin[f](m("width"===
+f?c:c-g*e)),b.inner[f](m("width"===f?c:c-g*e-h*e))},zoomIn:function(){var a=b.current,d=a.pos,e=a.openEffect,c="elastic"===e,l=f.extend({opacity:1},d);delete l.position;c?(d=this.getOrigPosition(),a.openOpacity&&(d.opacity=0.1)):"fade"===e&&(d.opacity=0.1);b.wrap.css(d).animate(l,{duration:"none"===e?0:a.openSpeed,easing:a.openEasing,step:c?this.step:null,complete:b._afterZoomIn})},zoomOut:function(){var a=b.current,d=a.closeEffect,e="elastic"===d,c={opacity:0.1};e&&(c=this.getOrigPosition(),a.closeOpacity&&
+(c.opacity=0.1));b.wrap.animate(c,{duration:"none"===d?0:a.closeSpeed,easing:a.closeEasing,step:e?this.step:null,complete:b._afterZoomOut})},changeIn:function(){var a=b.current,d=a.nextEffect,e=a.pos,c={opacity:1},f=b.direction,g;e.opacity=0.1;"elastic"===d&&(g="down"===f||"up"===f?"top":"left","down"===f||"right"===f?(e[g]=x(m(e[g])-200),c[g]="+=200px"):(e[g]=x(m(e[g])+200),c[g]="-=200px"));"none"===d?b._afterZoomIn():b.wrap.css(e).animate(c,{duration:a.nextSpeed,easing:a.nextEasing,complete:b._afterZoomIn})},
+changeOut:function(){var a=b.previous,d=a.prevEffect,e={opacity:0.1},c=b.direction;"elastic"===d&&(e["down"===c||"up"===c?"top":"left"]=("up"===c||"left"===c?"-":"+")+"=200px");a.wrap.animate(e,{duration:"none"===d?0:a.prevSpeed,easing:a.prevEasing,complete:function(){f(this).trigger("onReset").remove()}})}};b.helpers.overlay={defaults:{closeClick:!0,speedOut:200,showEarly:!0,css:{},locked:!t,fixed:!0},overlay:null,fixed:!1,el:f("html"),create:function(a){var d;a=f.extend({},this.defaults,a);this.overlay&&
+this.close();d=b.coming?b.coming.parent:a.parent;this.overlay=f('<div class="fancybox-overlay"></div>').appendTo(d&&d.lenth?d:"body");this.fixed=!1;a.fixed&&b.defaults.fixed&&(this.overlay.addClass("fancybox-overlay-fixed"),this.fixed=!0)},open:function(a){var d=this;a=f.extend({},this.defaults,a);this.overlay?this.overlay.unbind(".overlay").width("auto").height("auto"):this.create(a);this.fixed||(q.bind("resize.overlay",f.proxy(this.update,this)),this.update());a.closeClick&&this.overlay.bind("click.overlay",
+function(a){if(f(a.target).hasClass("fancybox-overlay"))return b.isActive?b.close():d.close(),!1});this.overlay.css(a.css).show()},close:function(){q.unbind("resize.overlay");this.el.hasClass("fancybox-lock")&&(f(".fancybox-margin").removeClass("fancybox-margin"),this.el.removeClass("fancybox-lock"),q.scrollTop(this.scrollV).scrollLeft(this.scrollH));f(".fancybox-overlay").remove().hide();f.extend(this,{overlay:null,fixed:!1})},update:function(){var a="100%",b;this.overlay.width(a).height("100%");
+J?(b=Math.max(H.documentElement.offsetWidth,H.body.offsetWidth),p.width()>b&&(a=p.width())):p.width()>q.width()&&(a=p.width());this.overlay.width(a).height(p.height())},onReady:function(a,b){var e=this.overlay;f(".fancybox-overlay").stop(!0,!0);e||this.create(a);a.locked&&this.fixed&&b.fixed&&(b.locked=this.overlay.append(b.wrap),b.fixed=!1);!0===a.showEarly&&this.beforeShow.apply(this,arguments)},beforeShow:function(a,b){b.locked&&!this.el.hasClass("fancybox-lock")&&(!1!==this.fixPosition&&f("*").filter(function(){return"fixed"===
+f(this).css("position")&&!f(this).hasClass("fancybox-overlay")&&!f(this).hasClass("fancybox-wrap")}).addClass("fancybox-margin"),this.el.addClass("fancybox-margin"),this.scrollV=q.scrollTop(),this.scrollH=q.scrollLeft(),this.el.addClass("fancybox-lock"),q.scrollTop(this.scrollV).scrollLeft(this.scrollH));this.open(a)},onUpdate:function(){this.fixed||this.update()},afterClose:function(a){this.overlay&&!b.coming&&this.overlay.fadeOut(a.speedOut,f.proxy(this.close,this))}};b.helpers.title={defaults:{type:"float",
+position:"bottom"},beforeShow:function(a){var d=b.current,e=d.title,c=a.type;f.isFunction(e)&&(e=e.call(d.element,d));if(r(e)&&""!==f.trim(e)){d=f('<div class="fancybox-title fancybox-title-'+c+'-wrap">'+e+"</div>");switch(c){case "inside":c=b.skin;break;case "outside":c=b.wrap;break;case "over":c=b.inner;break;default:c=b.skin,d.appendTo("body"),J&&d.width(d.width()),d.wrapInner('<span class="child"></span>'),b.current.margin[2]+=Math.abs(m(d.css("margin-bottom")))}d["top"===a.position?"prependTo":
+"appendTo"](c)}}};f.fn.fancybox=function(a){var d,e=f(this),c=this.selector||"",l=function(g){var h=f(this).blur(),k=d,l,m;g.ctrlKey||g.altKey||g.shiftKey||g.metaKey||h.is(".fancybox-wrap")||(l=a.groupAttr||"data-fancybox-group",m=h.attr(l),m||(l="rel",m=h.get(0)[l]),m&&""!==m&&"nofollow"!==m&&(h=c.length?f(c):e,h=h.filter("["+l+'="'+m+'"]'),k=h.index(this)),a.index=k,!1!==b.open(h,a)&&g.preventDefault())};a=a||{};d=a.index||0;c&&!1!==a.live?p.undelegate(c,"click.fb-start").delegate(c+":not('.fancybox-item, .fancybox-nav')",
+"click.fb-start",l):e.unbind("click.fb-start").bind("click.fb-start",l);this.filter("[data-fancybox-start=1]").trigger("click");return this};p.ready(function(){var a,d;f.scrollbarWidth===w&&(f.scrollbarWidth=function(){var a=f('<div style="width:50px;height:50px;overflow:auto"><div/></div>').appendTo("body"),b=a.children(),b=b.innerWidth()-b.height(99).innerWidth();a.remove();return b});f.support.fixedPosition===w&&(f.support.fixedPosition=function(){var a=f('<div style="position:fixed;top:20px;"></div>').appendTo("body"),
+b=20===a[0].offsetTop||15===a[0].offsetTop;a.remove();return b}());f.extend(b.defaults,{scrollbarWidth:f.scrollbarWidth(),fixed:f.support.fixedPosition,parent:f("body")});a=f(s).width();K.addClass("fancybox-lock-test");d=f(s).width();K.removeClass("fancybox-lock-test");f("<style type='text/css'>.fancybox-margin{margin-right:"+(d-a)+"px;}</style>").appendTo("head")})})(window,document,jQuery);

+ 634 - 0
src/assets/select2/css/select2.css

@@ -0,0 +1,634 @@
+.select2-container {
+    box-sizing: border-box;
+    display: inline-block;
+    margin: 0;
+    position: relative;
+    vertical-align: middle;
+}
+
+.select2-container .select2-selection--single {
+    box-sizing: border-box;
+    cursor: pointer;
+    display: block;
+    height: 28px;
+    user-select: none;
+    -webkit-user-select: none;
+}
+
+.select2-container .select2-selection--single .select2-selection__rendered {
+    display: block;
+    padding-left: 8px;
+    padding-right: 20px;
+    overflow: hidden;
+    text-overflow: ellipsis;
+    white-space: nowrap;
+}
+
+.select2-container .select2-selection--single .select2-selection__clear {
+    position: relative;
+}
+
+.select2-container[dir="rtl"] .select2-selection--single .select2-selection__rendered {
+    padding-right: 8px;
+    padding-left: 20px;
+}
+
+.select2-container .select2-selection--multiple {
+    box-sizing: border-box;
+    cursor: pointer;
+    display: block;
+    min-height: 32px;
+    user-select: none;
+    -webkit-user-select: none;
+}
+
+.select2-container .select2-selection--multiple .select2-selection__rendered {
+    display: inline-block;
+    overflow: hidden;
+    padding-left: 8px;
+    text-overflow: ellipsis;
+    white-space: nowrap;
+}
+
+.select2-container .select2-search--inline {
+    float: left;
+}
+
+.select2-container .select2-search--inline .select2-search__field {
+    box-sizing: border-box;
+    border: none;
+    font-size: 100%;
+    margin-top: 5px;
+    padding: 0;
+}
+
+.select2-container .select2-search--inline .select2-search__field::-webkit-search-cancel-button {
+    -webkit-appearance: none;
+}
+
+.select2-dropdown {
+    background-color: white;
+    border: 1px solid #aaa;
+    border-radius: 4px;
+    box-sizing: border-box;
+    display: block;
+    position: absolute;
+    left: -100000px;
+    width: 100%;
+    z-index: 1051;
+}
+
+.select2-results {
+    display: block;
+}
+
+.select2-results__options {
+    list-style: none;
+    margin: 0;
+    padding: 0;
+}
+
+.select2-results__option {
+    padding: 6px;
+    user-select: none;
+    -webkit-user-select: none;
+}
+
+.select2-results__option[aria-selected] {
+    cursor: pointer;
+}
+
+.select2-container--open .select2-dropdown {
+    left: 0;
+}
+
+.select2-container--open .select2-dropdown--above {
+    border-bottom: none;
+    border-bottom-left-radius: 0;
+    border-bottom-right-radius: 0;
+}
+
+.select2-container--open .select2-dropdown--below {
+    border-top: none;
+    border-top-left-radius: 0;
+    border-top-right-radius: 0;
+}
+
+.select2-search--dropdown {
+    display: block;
+    padding: 4px;
+}
+
+.select2-search--dropdown .select2-search__field {
+    padding: 4px;
+    width: 100%;
+    box-sizing: border-box;
+}
+
+.select2-search--dropdown .select2-search__field::-webkit-search-cancel-button {
+    -webkit-appearance: none;
+}
+
+.select2-search--dropdown.select2-search--hide {
+    display: none;
+}
+
+.select2-close-mask {
+    border: 0;
+    margin: 0;
+    padding: 0;
+    display: block;
+    position: fixed;
+    left: 0;
+    top: 0;
+    min-height: 100%;
+    min-width: 100%;
+    height: auto;
+    width: auto;
+    opacity: 0;
+    z-index: 99;
+    background-color: #fff;
+    filter: alpha(opacity=0);
+}
+
+.select2-hidden-accessible {
+    border: 0 !important;
+    clip: rect(0 0 0 0) !important;
+    height: 1px !important;
+    margin: -1px !important;
+    overflow: hidden !important;
+    padding: 0 !important;
+    position: absolute !important;
+    width: 1px !important;
+}
+
+.select2-container--default .select2-selection--single {
+    background-color: #fff;
+    border: 1px solid #aaa;
+    border-radius: 4px;
+}
+
+.select2-container--default .select2-selection--single .select2-selection__rendered {
+    color: #444;
+    line-height: 28px;
+}
+
+.select2-container--default .select2-selection--single .select2-selection__clear {
+    cursor: pointer;
+    float: right;
+    font-weight: bold;
+}
+
+.select2-container--default .select2-selection--single .select2-selection__placeholder {
+    color: #999;
+}
+
+.select2-container--default .select2-selection--single .select2-selection__arrow {
+    height: 26px;
+    position: absolute;
+    top: 1px;
+    right: 1px;
+    width: 20px;
+}
+
+.select2-container--default .select2-selection--single .select2-selection__arrow b {
+    border-color: #888 transparent transparent transparent;
+    border-style: solid;
+    border-width: 5px 4px 0 4px;
+    height: 0;
+    left: 50%;
+    margin-left: -4px;
+    margin-top: -2px;
+    position: absolute;
+    top: 50%;
+    width: 0;
+}
+
+.select2-container--default[dir="rtl"] .select2-selection--single .select2-selection__clear {
+    float: left;
+}
+
+.select2-container--default[dir="rtl"] .select2-selection--single .select2-selection__arrow {
+    left: 1px;
+    right: auto;
+}
+
+.select2-container--default.select2-container--disabled .select2-selection--single {
+    background-color: #eee;
+    cursor: default;
+}
+
+.select2-container--default.select2-container--disabled .select2-selection--single .select2-selection__clear {
+    display: none;
+}
+
+.select2-container--default.select2-container--open .select2-selection--single .select2-selection__arrow b {
+    border-color: transparent transparent #888 transparent;
+    border-width: 0 4px 5px 4px;
+}
+
+.select2-container--default .select2-selection--multiple {
+    background-color: white;
+    border: 1px solid #aaa;
+    border-radius: 4px;
+    cursor: text;
+}
+
+.select2-container--default .select2-selection--multiple .select2-selection__rendered {
+    box-sizing: border-box;
+    list-style: none;
+    margin: 0;
+    padding: 0 5px;
+    width: 100%;
+}
+
+.select2-container--default .select2-selection--multiple .select2-selection__rendered li {
+    list-style: none;
+}
+
+.select2-container--default .select2-selection--multiple .select2-selection__placeholder {
+    color: #999;
+    margin-top: 5px;
+    float: left;
+}
+
+.select2-container--default .select2-selection--multiple .select2-selection__clear {
+    cursor: pointer;
+    float: right;
+    font-weight: bold;
+    margin-top: 5px;
+    margin-right: 10px;
+}
+
+.select2-container--default .select2-selection--multiple .select2-selection__choice {
+    background-color: #e4e4e4;
+    border: 1px solid #aaa;
+    border-radius: 4px;
+    cursor: default;
+    float: left;
+    margin-right: 5px;
+    margin-top: 5px;
+    padding: 0 5px;
+}
+
+.select2-container--default .select2-selection--multiple .select2-selection__choice__remove {
+    color: #999;
+    cursor: pointer;
+    display: inline-block;
+    font-weight: bold;
+    margin-right: 2px;
+}
+
+.select2-container--default .select2-selection--multiple .select2-selection__choice__remove:hover {
+    color: #333;
+}
+
+.select2-container--default[dir="rtl"] .select2-selection--multiple .select2-selection__choice, .select2-container--default[dir="rtl"] .select2-selection--multiple .select2-selection__placeholder, .select2-container--default[dir="rtl"] .select2-selection--multiple .select2-search--inline {
+    float: right;
+}
+
+.select2-container--default[dir="rtl"] .select2-selection--multiple .select2-selection__choice {
+    margin-left: 5px;
+    margin-right: auto;
+}
+
+.select2-container--default[dir="rtl"] .select2-selection--multiple .select2-selection__choice__remove {
+    margin-left: 2px;
+    margin-right: auto;
+}
+
+.select2-container--default.select2-container--focus .select2-selection--multiple {
+    border: solid black 1px;
+    outline: 0;
+}
+
+.select2-container--default.select2-container--disabled .select2-selection--multiple {
+    background-color: #eee;
+    cursor: default;
+}
+
+.select2-container--default.select2-container--disabled .select2-selection__choice__remove {
+    display: none;
+}
+
+.select2-container--default.select2-container--open.select2-container--above .select2-selection--single, .select2-container--default.select2-container--open.select2-container--above .select2-selection--multiple {
+    border-top-left-radius: 0;
+    border-top-right-radius: 0;
+}
+
+.select2-container--default.select2-container--open.select2-container--below .select2-selection--single, .select2-container--default.select2-container--open.select2-container--below .select2-selection--multiple {
+    border-bottom-left-radius: 0;
+    border-bottom-right-radius: 0;
+}
+
+.select2-container--default .select2-search--dropdown .select2-search__field {
+    border: 1px solid #aaa;
+}
+
+.select2-container--default .select2-search--inline .select2-search__field {
+    background: transparent;
+    border: none;
+    outline: 0;
+    box-shadow: none;
+    -webkit-appearance: textfield;
+}
+
+.select2-container--default .select2-results > .select2-results__options {
+    max-height: 200px;
+    overflow-y: auto;
+}
+
+.select2-container--default .select2-results__option[role=group] {
+    padding: 0;
+}
+
+.select2-container--default .select2-results__option[aria-disabled=true] {
+    color: #999;
+}
+
+.select2-container--default .select2-results__option[aria-selected=true] {
+    background-color: #ddd;
+}
+
+.select2-container--default .select2-results__option .select2-results__option {
+    padding-left: 1em;
+}
+
+.select2-container--default .select2-results__option .select2-results__option .select2-results__group {
+    padding-left: 0;
+}
+
+.select2-container--default .select2-results__option .select2-results__option .select2-results__option {
+    margin-left: -1em;
+    padding-left: 2em;
+}
+
+.select2-container--default .select2-results__option .select2-results__option .select2-results__option .select2-results__option {
+    margin-left: -2em;
+    padding-left: 3em;
+}
+
+.select2-container--default .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option {
+    margin-left: -3em;
+    padding-left: 4em;
+}
+
+.select2-container--default .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option {
+    margin-left: -4em;
+    padding-left: 5em;
+}
+
+.select2-container--default .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option {
+    margin-left: -5em;
+    padding-left: 6em;
+}
+
+.select2-container--default .select2-results__option--highlighted[aria-selected] {
+    background-color: #5897fb;
+    color: white;
+}
+
+.select2-container--default .select2-results__group {
+    cursor: default;
+    display: block;
+    padding: 6px;
+}
+
+.select2-container--classic .select2-selection--single {
+    background-color: #f7f7f7;
+    border: 1px solid #aaa;
+    border-radius: 4px;
+    outline: 0;
+    background-image: -webkit-linear-gradient(top, white 50%, #eeeeee 100%);
+    background-image: -o-linear-gradient(top, white 50%, #eeeeee 100%);
+    background-image: linear-gradient(to bottom, white 50%, #eeeeee 100%);
+    background-repeat: repeat-x;
+    filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#FFFFFFFF', endColorstr='#FFEEEEEE', GradientType=0);
+}
+
+.select2-container--classic .select2-selection--single:focus {
+    border: 1px solid #5897fb;
+}
+
+.select2-container--classic .select2-selection--single .select2-selection__rendered {
+    color: #444;
+    line-height: 28px;
+}
+
+.select2-container--classic .select2-selection--single .select2-selection__clear {
+    cursor: pointer;
+    float: right;
+    font-weight: bold;
+    margin-right: 10px;
+}
+
+.select2-container--classic .select2-selection--single .select2-selection__placeholder {
+    color: #999;
+}
+
+.select2-container--classic .select2-selection--single .select2-selection__arrow {
+    background-color: #ddd;
+    border: none;
+    border-left: 1px solid #aaa;
+    border-top-right-radius: 4px;
+    border-bottom-right-radius: 4px;
+    height: 26px;
+    position: absolute;
+    top: 1px;
+    right: 1px;
+    width: 20px;
+    background-image: -webkit-linear-gradient(top, #eeeeee 50%, #cccccc 100%);
+    background-image: -o-linear-gradient(top, #eeeeee 50%, #cccccc 100%);
+    background-image: linear-gradient(to bottom, #eeeeee 50%, #cccccc 100%);
+    background-repeat: repeat-x;
+    filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#FFEEEEEE', endColorstr='#FFCCCCCC', GradientType=0);
+}
+
+.select2-container--classic .select2-selection--single .select2-selection__arrow b {
+    border-color: #888 transparent transparent transparent;
+    border-style: solid;
+    border-width: 5px 4px 0 4px;
+    height: 0;
+    left: 50%;
+    margin-left: -4px;
+    margin-top: -2px;
+    position: absolute;
+    top: 50%;
+    width: 0;
+}
+
+.select2-container--classic[dir="rtl"] .select2-selection--single .select2-selection__clear {
+    float: left;
+}
+
+.select2-container--classic[dir="rtl"] .select2-selection--single .select2-selection__arrow {
+    border: none;
+    border-right: 1px solid #aaa;
+    border-radius: 0;
+    border-top-left-radius: 4px;
+    border-bottom-left-radius: 4px;
+    left: 1px;
+    right: auto;
+}
+
+.select2-container--classic.select2-container--open .select2-selection--single {
+    border: 1px solid #5897fb;
+}
+
+.select2-container--classic.select2-container--open .select2-selection--single .select2-selection__arrow {
+    background: transparent;
+    border: none;
+}
+
+.select2-container--classic.select2-container--open .select2-selection--single .select2-selection__arrow b {
+    border-color: transparent transparent #888 transparent;
+    border-width: 0 4px 5px 4px;
+}
+
+.select2-container--classic.select2-container--open.select2-container--above .select2-selection--single {
+    border-top: none;
+    border-top-left-radius: 0;
+    border-top-right-radius: 0;
+    background-image: -webkit-linear-gradient(top, white 0%, #eeeeee 50%);
+    background-image: -o-linear-gradient(top, white 0%, #eeeeee 50%);
+    background-image: linear-gradient(to bottom, white 0%, #eeeeee 50%);
+    background-repeat: repeat-x;
+    filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#FFFFFFFF', endColorstr='#FFEEEEEE', GradientType=0);
+}
+
+.select2-container--classic.select2-container--open.select2-container--below .select2-selection--single {
+    border-bottom: none;
+    border-bottom-left-radius: 0;
+    border-bottom-right-radius: 0;
+    background-image: -webkit-linear-gradient(top, #eeeeee 50%, white 100%);
+    background-image: -o-linear-gradient(top, #eeeeee 50%, white 100%);
+    background-image: linear-gradient(to bottom, #eeeeee 50%, white 100%);
+    background-repeat: repeat-x;
+    filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#FFEEEEEE', endColorstr='#FFFFFFFF', GradientType=0);
+}
+
+.select2-container--classic .select2-selection--multiple {
+    background-color: white;
+    border: 1px solid #aaa;
+    border-radius: 4px;
+    cursor: text;
+    outline: 0;
+}
+
+.select2-container--classic .select2-selection--multiple:focus {
+    border: 1px solid #5897fb;
+}
+
+.select2-container--classic .select2-selection--multiple .select2-selection__rendered {
+    list-style: none;
+    margin: 0;
+    padding: 0 5px;
+}
+
+.select2-container--classic .select2-selection--multiple .select2-selection__clear {
+    display: none;
+}
+
+.select2-container--classic .select2-selection--multiple .select2-selection__choice {
+    background-color: #e4e4e4;
+    border: 1px solid #aaa;
+    border-radius: 4px;
+    cursor: default;
+    float: left;
+    margin-right: 5px;
+    margin-top: 5px;
+    padding: 0 5px;
+}
+
+.select2-container--classic .select2-selection--multiple .select2-selection__choice__remove {
+    color: #888;
+    cursor: pointer;
+    display: inline-block;
+    font-weight: bold;
+    margin-right: 2px;
+}
+
+.select2-container--classic .select2-selection--multiple .select2-selection__choice__remove:hover {
+    color: #555;
+}
+
+.select2-container--classic[dir="rtl"] .select2-selection--multiple .select2-selection__choice {
+    float: right;
+}
+
+.select2-container--classic[dir="rtl"] .select2-selection--multiple .select2-selection__choice {
+    margin-left: 5px;
+    margin-right: auto;
+}
+
+.select2-container--classic[dir="rtl"] .select2-selection--multiple .select2-selection__choice__remove {
+    margin-left: 2px;
+    margin-right: auto;
+}
+
+.select2-container--classic.select2-container--open .select2-selection--multiple {
+    border: 1px solid #5897fb;
+}
+
+.select2-container--classic.select2-container--open.select2-container--above .select2-selection--multiple {
+    border-top: none;
+    border-top-left-radius: 0;
+    border-top-right-radius: 0;
+}
+
+.select2-container--classic.select2-container--open.select2-container--below .select2-selection--multiple {
+    border-bottom: none;
+    border-bottom-left-radius: 0;
+    border-bottom-right-radius: 0;
+}
+
+.select2-container--classic .select2-search--dropdown .select2-search__field {
+    border: 1px solid #aaa;
+    outline: 0;
+}
+
+.select2-container--classic .select2-search--inline .select2-search__field {
+    outline: 0;
+    box-shadow: none;
+}
+
+.select2-container--classic .select2-dropdown {
+    background-color: white;
+    border: 1px solid transparent;
+}
+
+.select2-container--classic .select2-dropdown--above {
+    border-bottom: none;
+}
+
+.select2-container--classic .select2-dropdown--below {
+    border-top: none;
+}
+
+.select2-container--classic .select2-results > .select2-results__options {
+    max-height: 200px;
+    overflow-y: auto;
+}
+
+.select2-container--classic .select2-results__option[role=group] {
+    padding: 0;
+}
+
+.select2-container--classic .select2-results__option[aria-disabled=true] {
+    color: grey;
+}
+
+.select2-container--classic .select2-results__option--highlighted[aria-selected] {
+    background-color: #3875d7;
+    color: white;
+}
+
+.select2-container--classic .select2-results__group {
+    cursor: default;
+    display: block;
+    padding: 6px;
+}
+
+.select2-container--classic.select2-container--open .select2-dropdown {
+    border-color: #5897fb;
+}

File diff suppressed because it is too large
+ 0 - 0
src/assets/select2/css/select2.min.css


+ 3 - 0
src/assets/select2/js/i18n/ar.js

@@ -0,0 +1,3 @@
+/*! Select2 4.0.3 | https://github.com/select2/select2/blob/master/LICENSE.md */
+
+(function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var e=jQuery.fn.select2.amd;return e.define("select2/i18n/ar",[],function(){return{errorLoading:function(){return"لا يمكن تحميل النتائج"},inputTooLong:function(e){var t=e.input.length-e.maximum,n="الرجاء حذف "+t+" عناصر";return n},inputTooShort:function(e){var t=e.minimum-e.input.length,n="الرجاء إضافة "+t+" عناصر";return n},loadingMore:function(){return"جاري تحميل نتائج إضافية..."},maximumSelected:function(e){var t="تستطيع إختيار "+e.maximum+" بنود فقط";return t},noResults:function(){return"لم يتم العثور على أي نتائج"},searching:function(){return"جاري البحث…"}}}),{define:e.define,require:e.require}})();

+ 3 - 0
src/assets/select2/js/i18n/az.js

@@ -0,0 +1,3 @@
+/*! Select2 4.0.3 | https://github.com/select2/select2/blob/master/LICENSE.md */
+
+(function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var e=jQuery.fn.select2.amd;return e.define("select2/i18n/az",[],function(){return{inputTooLong:function(e){var t=e.input.length-e.maximum;return t+" simvol silin"},inputTooShort:function(e){var t=e.minimum-e.input.length;return t+" simvol daxil edin"},loadingMore:function(){return"Daha çox nəticə yüklənir…"},maximumSelected:function(e){return"Sadəcə "+e.maximum+" element seçə bilərsiniz"},noResults:function(){return"Nəticə tapılmadı"},searching:function(){return"Axtarılır…"}}}),{define:e.define,require:e.require}})();

+ 3 - 0
src/assets/select2/js/i18n/bg.js

@@ -0,0 +1,3 @@
+/*! Select2 4.0.3 | https://github.com/select2/select2/blob/master/LICENSE.md */
+
+(function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var e=jQuery.fn.select2.amd;return e.define("select2/i18n/bg",[],function(){return{inputTooLong:function(e){var t=e.input.length-e.maximum,n="Моля въведете с "+t+" по-малко символ";return t>1&&(n+="a"),n},inputTooShort:function(e){var t=e.minimum-e.input.length,n="Моля въведете още "+t+" символ";return t>1&&(n+="a"),n},loadingMore:function(){return"Зареждат се още…"},maximumSelected:function(e){var t="Можете да направите до "+e.maximum+" ";return e.maximum>1?t+="избора":t+="избор",t},noResults:function(){return"Няма намерени съвпадения"},searching:function(){return"Търсене…"}}}),{define:e.define,require:e.require}})();

+ 3 - 0
src/assets/select2/js/i18n/ca.js

@@ -0,0 +1,3 @@
+/*! Select2 4.0.3 | https://github.com/select2/select2/blob/master/LICENSE.md */
+
+(function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var e=jQuery.fn.select2.amd;return e.define("select2/i18n/ca",[],function(){return{errorLoading:function(){return"La càrrega ha fallat"},inputTooLong:function(e){var t=e.input.length-e.maximum,n="Si us plau, elimina "+t+" car";return t==1?n+="àcter":n+="àcters",n},inputTooShort:function(e){var t=e.minimum-e.input.length,n="Si us plau, introdueix "+t+" car";return t==1?n+="àcter":n+="àcters",n},loadingMore:function(){return"Carregant més resultats…"},maximumSelected:function(e){var t="Només es pot seleccionar "+e.maximum+" element";return e.maximum!=1&&(t+="s"),t},noResults:function(){return"No s'han trobat resultats"},searching:function(){return"Cercant…"}}}),{define:e.define,require:e.require}})();

+ 3 - 0
src/assets/select2/js/i18n/cs.js

@@ -0,0 +1,3 @@
+/*! Select2 4.0.3 | https://github.com/select2/select2/blob/master/LICENSE.md */
+
+(function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var e=jQuery.fn.select2.amd;return e.define("select2/i18n/cs",[],function(){function e(e,t){switch(e){case 2:return t?"dva":"dvě";case 3:return"tři";case 4:return"čtyři"}return""}return{errorLoading:function(){return"Výsledky nemohly být načteny."},inputTooLong:function(t){var n=t.input.length-t.maximum;return n==1?"Prosím zadejte o jeden znak méně":n<=4?"Prosím zadejte o "+e(n,!0)+" znaky méně":"Prosím zadejte o "+n+" znaků méně"},inputTooShort:function(t){var n=t.minimum-t.input.length;return n==1?"Prosím zadejte ještě jeden znak":n<=4?"Prosím zadejte ještě další "+e(n,!0)+" znaky":"Prosím zadejte ještě dalších "+n+" znaků"},loadingMore:function(){return"Načítají se další výsledky…"},maximumSelected:function(t){var n=t.maximum;return n==1?"Můžete zvolit jen jednu položku":n<=4?"Můžete zvolit maximálně "+e(n,!1)+" položky":"Můžete zvolit maximálně "+n+" položek"},noResults:function(){return"Nenalezeny žádné položky"},searching:function(){return"Vyhledávání…"}}}),{define:e.define,require:e.require}})();

+ 3 - 0
src/assets/select2/js/i18n/da.js

@@ -0,0 +1,3 @@
+/*! Select2 4.0.3 | https://github.com/select2/select2/blob/master/LICENSE.md */
+
+(function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var e=jQuery.fn.select2.amd;return e.define("select2/i18n/da",[],function(){return{errorLoading:function(){return"Resultaterne kunne ikke indlæses."},inputTooLong:function(e){var t=e.input.length-e.maximum,n="Angiv venligst "+t+" tegn mindre";return n},inputTooShort:function(e){var t=e.minimum-e.input.length,n="Angiv venligst "+t+" tegn mere";return n},loadingMore:function(){return"Indlæser flere resultater…"},maximumSelected:function(e){var t="Du kan kun vælge "+e.maximum+" emne";return e.maximum!=1&&(t+="r"),t},noResults:function(){return"Ingen resultater fundet"},searching:function(){return"Søger…"}}}),{define:e.define,require:e.require}})();

+ 3 - 0
src/assets/select2/js/i18n/de.js

@@ -0,0 +1,3 @@
+/*! Select2 4.0.3 | https://github.com/select2/select2/blob/master/LICENSE.md */
+
+(function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var e=jQuery.fn.select2.amd;return e.define("select2/i18n/de",[],function(){return{inputTooLong:function(e){var t=e.input.length-e.maximum;return"Bitte "+t+" Zeichen weniger eingeben"},inputTooShort:function(e){var t=e.minimum-e.input.length;return"Bitte "+t+" Zeichen mehr eingeben"},loadingMore:function(){return"Lade mehr Ergebnisse…"},maximumSelected:function(e){var t="Sie können nur "+e.maximum+" Eintr";return e.maximum===1?t+="ag":t+="äge",t+=" auswählen",t},noResults:function(){return"Keine Übereinstimmungen gefunden"},searching:function(){return"Suche…"}}}),{define:e.define,require:e.require}})();

+ 3 - 0
src/assets/select2/js/i18n/el.js

@@ -0,0 +1,3 @@
+/*! Select2 4.0.3 | https://github.com/select2/select2/blob/master/LICENSE.md */
+
+(function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var e=jQuery.fn.select2.amd;return e.define("select2/i18n/el",[],function(){return{errorLoading:function(){return"Τα αποτελέσματα δεν μπόρεσαν να φορτώσουν."},inputTooLong:function(e){var t=e.input.length-e.maximum,n="Παρακαλώ διαγράψτε "+t+" χαρακτήρ";return t==1&&(n+="α"),t!=1&&(n+="ες"),n},inputTooShort:function(e){var t=e.minimum-e.input.length,n="Παρακαλώ συμπληρώστε "+t+" ή περισσότερους χαρακτήρες";return n},loadingMore:function(){return"Φόρτωση περισσότερων αποτελεσμάτων…"},maximumSelected:function(e){var t="Μπορείτε να επιλέξετε μόνο "+e.maximum+" επιλογ";return e.maximum==1&&(t+="ή"),e.maximum!=1&&(t+="ές"),t},noResults:function(){return"Δεν βρέθηκαν αποτελέσματα"},searching:function(){return"Αναζήτηση…"}}}),{define:e.define,require:e.require}})();

+ 3 - 0
src/assets/select2/js/i18n/en.js

@@ -0,0 +1,3 @@
+/*! Select2 4.0.3 | https://github.com/select2/select2/blob/master/LICENSE.md */
+
+(function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var e=jQuery.fn.select2.amd;return e.define("select2/i18n/en",[],function(){return{errorLoading:function(){return"The results could not be loaded."},inputTooLong:function(e){var t=e.input.length-e.maximum,n="Please delete "+t+" character";return t!=1&&(n+="s"),n},inputTooShort:function(e){var t=e.minimum-e.input.length,n="Please enter "+t+" or more characters";return n},loadingMore:function(){return"Loading more results…"},maximumSelected:function(e){var t="You can only select "+e.maximum+" item";return e.maximum!=1&&(t+="s"),t},noResults:function(){return"No results found"},searching:function(){return"Searching…"}}}),{define:e.define,require:e.require}})();

+ 3 - 0
src/assets/select2/js/i18n/es.js

@@ -0,0 +1,3 @@
+/*! Select2 4.0.3 | https://github.com/select2/select2/blob/master/LICENSE.md */
+
+(function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var e=jQuery.fn.select2.amd;return e.define("select2/i18n/es",[],function(){return{errorLoading:function(){return"La carga falló"},inputTooLong:function(e){var t=e.input.length-e.maximum,n="Por favor, elimine "+t+" car";return t==1?n+="ácter":n+="acteres",n},inputTooShort:function(e){var t=e.minimum-e.input.length,n="Por favor, introduzca "+t+" car";return t==1?n+="ácter":n+="acteres",n},loadingMore:function(){return"Cargando más resultados…"},maximumSelected:function(e){var t="Sólo puede seleccionar "+e.maximum+" elemento";return e.maximum!=1&&(t+="s"),t},noResults:function(){return"No se encontraron resultados"},searching:function(){return"Buscando…"}}}),{define:e.define,require:e.require}})();

+ 3 - 0
src/assets/select2/js/i18n/et.js

@@ -0,0 +1,3 @@
+/*! Select2 4.0.3 | https://github.com/select2/select2/blob/master/LICENSE.md */
+
+(function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var e=jQuery.fn.select2.amd;return e.define("select2/i18n/et",[],function(){return{inputTooLong:function(e){var t=e.input.length-e.maximum,n="Sisesta "+t+" täht";return t!=1&&(n+="e"),n+=" vähem",n},inputTooShort:function(e){var t=e.minimum-e.input.length,n="Sisesta "+t+" täht";return t!=1&&(n+="e"),n+=" rohkem",n},loadingMore:function(){return"Laen tulemusi…"},maximumSelected:function(e){var t="Saad vaid "+e.maximum+" tulemus";return e.maximum==1?t+="e":t+="t",t+=" valida",t},noResults:function(){return"Tulemused puuduvad"},searching:function(){return"Otsin…"}}}),{define:e.define,require:e.require}})();

+ 3 - 0
src/assets/select2/js/i18n/eu.js

@@ -0,0 +1,3 @@
+/*! Select2 4.0.3 | https://github.com/select2/select2/blob/master/LICENSE.md */
+
+(function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var e=jQuery.fn.select2.amd;return e.define("select2/i18n/eu",[],function(){return{inputTooLong:function(e){var t=e.input.length-e.maximum,n="Idatzi ";return t==1?n+="karaktere bat":n+=t+" karaktere",n+=" gutxiago",n},inputTooShort:function(e){var t=e.minimum-e.input.length,n="Idatzi ";return t==1?n+="karaktere bat":n+=t+" karaktere",n+=" gehiago",n},loadingMore:function(){return"Emaitza gehiago kargatzen…"},maximumSelected:function(e){return e.maximum===1?"Elementu bakarra hauta dezakezu":e.maximum+" elementu hauta ditzakezu soilik"},noResults:function(){return"Ez da bat datorrenik aurkitu"},searching:function(){return"Bilatzen…"}}}),{define:e.define,require:e.require}})();

+ 3 - 0
src/assets/select2/js/i18n/fa.js

@@ -0,0 +1,3 @@
+/*! Select2 4.0.3 | https://github.com/select2/select2/blob/master/LICENSE.md */
+
+(function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var e=jQuery.fn.select2.amd;return e.define("select2/i18n/fa",[],function(){return{errorLoading:function(){return"امکان بارگذاری نتایج وجود ندارد."},inputTooLong:function(e){var t=e.input.length-e.maximum,n="لطفاً "+t+" کاراکتر را حذف نمایید";return n},inputTooShort:function(e){var t=e.minimum-e.input.length,n="لطفاً تعداد "+t+" کاراکتر یا بیشتر وارد نمایید";return n},loadingMore:function(){return"در حال بارگذاری نتایج بیشتر..."},maximumSelected:function(e){var t="شما تنها می‌توانید "+e.maximum+" آیتم را انتخاب نمایید";return t},noResults:function(){return"هیچ نتیجه‌ای یافت نشد"},searching:function(){return"در حال جستجو..."}}}),{define:e.define,require:e.require}})();

+ 3 - 0
src/assets/select2/js/i18n/fi.js

@@ -0,0 +1,3 @@
+/*! Select2 4.0.3 | https://github.com/select2/select2/blob/master/LICENSE.md */
+
+(function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var e=jQuery.fn.select2.amd;return e.define("select2/i18n/fi",[],function(){return{inputTooLong:function(e){var t=e.input.length-e.maximum;return"Ole hyvä ja anna "+t+" merkkiä vähemmän"},inputTooShort:function(e){var t=e.minimum-e.input.length;return"Ole hyvä ja anna "+t+" merkkiä lisää"},loadingMore:function(){return"Ladataan lisää tuloksia…"},maximumSelected:function(e){return"Voit valita ainoastaan "+e.maximum+" kpl"},noResults:function(){return"Ei tuloksia"},searching:function(){}}}),{define:e.define,require:e.require}})();

+ 3 - 0
src/assets/select2/js/i18n/fr.js

@@ -0,0 +1,3 @@
+/*! Select2 4.0.3 | https://github.com/select2/select2/blob/master/LICENSE.md */
+
+(function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var e=jQuery.fn.select2.amd;return e.define("select2/i18n/fr",[],function(){return{errorLoading:function(){return"Les résultats ne peuvent pas être chargés."},inputTooLong:function(e){var t=e.input.length-e.maximum,n="Supprimez "+t+" caractère";return t!==1&&(n+="s"),n},inputTooShort:function(e){var t=e.minimum-e.input.length,n="Saisissez "+t+" caractère";return t!==1&&(n+="s"),n},loadingMore:function(){return"Chargement de résultats supplémentaires…"},maximumSelected:function(e){var t="Vous pouvez seulement sélectionner "+e.maximum+" élément";return e.maximum!==1&&(t+="s"),t},noResults:function(){return"Aucun résultat trouvé"},searching:function(){return"Recherche en cours…"}}}),{define:e.define,require:e.require}})();

+ 3 - 0
src/assets/select2/js/i18n/gl.js

@@ -0,0 +1,3 @@
+/*! Select2 4.0.3 | https://github.com/select2/select2/blob/master/LICENSE.md */
+
+(function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var e=jQuery.fn.select2.amd;return e.define("select2/i18n/gl",[],function(){return{inputTooLong:function(e){var t=e.input.length-e.maximum,n="Elimine ";return t===1?n+="un carácter":n+=t+" caracteres",n},inputTooShort:function(e){var t=e.minimum-e.input.length,n="Engada ";return t===1?n+="un carácter":n+=t+" caracteres",n},loadingMore:function(){return"Cargando máis resultados…"},maximumSelected:function(e){var t="Só pode ";return e.maximum===1?t+="un elemento":t+=e.maximum+" elementos",t},noResults:function(){return"Non se atoparon resultados"},searching:function(){return"Buscando…"}}}),{define:e.define,require:e.require}})();

+ 3 - 0
src/assets/select2/js/i18n/he.js

@@ -0,0 +1,3 @@
+/*! Select2 4.0.3 | https://github.com/select2/select2/blob/master/LICENSE.md */
+
+(function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var e=jQuery.fn.select2.amd;return e.define("select2/i18n/he",[],function(){return{errorLoading:function(){return"שגיאה בטעינת התוצאות"},inputTooLong:function(e){var t=e.input.length-e.maximum,n="נא למחוק ";return t===1?n+="תו אחד":n+=t+" תווים",n},inputTooShort:function(e){var t=e.minimum-e.input.length,n="נא להכניס ";return t===1?n+="תו אחד":n+=t+" תווים",n+=" או יותר",n},loadingMore:function(){return"טוען תוצאות נוספות…"},maximumSelected:function(e){var t="באפשרותך לבחור עד ";return e.maximum===1?t+="פריט אחד":t+=e.maximum+" פריטים",t},noResults:function(){return"לא נמצאו תוצאות"},searching:function(){return"מחפש…"}}}),{define:e.define,require:e.require}})();

+ 3 - 0
src/assets/select2/js/i18n/hi.js

@@ -0,0 +1,3 @@
+/*! Select2 4.0.3 | https://github.com/select2/select2/blob/master/LICENSE.md */
+
+(function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var e=jQuery.fn.select2.amd;return e.define("select2/i18n/hi",[],function(){return{errorLoading:function(){return"परिणामों को लोड नहीं किया जा सका।"},inputTooLong:function(e){var t=e.input.length-e.maximum,n=t+" अक्षर को हटा दें";return t>1&&(n=t+" अक्षरों को हटा दें "),n},inputTooShort:function(e){var t=e.minimum-e.input.length,n="कृपया "+t+" या अधिक अक्षर दर्ज करें";return n},loadingMore:function(){return"अधिक परिणाम लोड हो रहे है..."},maximumSelected:function(e){var t="आप केवल "+e.maximum+" आइटम का चयन कर सकते हैं";return t},noResults:function(){return"कोई परिणाम नहीं मिला"},searching:function(){return"खोज रहा है..."}}}),{define:e.define,require:e.require}})();

+ 3 - 0
src/assets/select2/js/i18n/hr.js

@@ -0,0 +1,3 @@
+/*! Select2 4.0.3 | https://github.com/select2/select2/blob/master/LICENSE.md */
+
+(function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var e=jQuery.fn.select2.amd;return e.define("select2/i18n/hr",[],function(){function e(e){var t=" "+e+" znak";return e%10<5&&e%10>0&&(e%100<5||e%100>19)?e%10>1&&(t+="a"):t+="ova",t}return{errorLoading:function(){return"Preuzimanje nije uspjelo."},inputTooLong:function(t){var n=t.input.length-t.maximum;return"Unesite "+e(n)},inputTooShort:function(t){var n=t.minimum-t.input.length;return"Unesite još "+e(n)},loadingMore:function(){return"Učitavanje rezultata…"},maximumSelected:function(e){return"Maksimalan broj odabranih stavki je "+e.maximum},noResults:function(){return"Nema rezultata"},searching:function(){return"Pretraga…"}}}),{define:e.define,require:e.require}})();

+ 3 - 0
src/assets/select2/js/i18n/hu.js

@@ -0,0 +1,3 @@
+/*! Select2 4.0.3 | https://github.com/select2/select2/blob/master/LICENSE.md */
+
+(function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var e=jQuery.fn.select2.amd;return e.define("select2/i18n/hu",[],function(){return{inputTooLong:function(e){var t=e.input.length-e.maximum;return"Túl hosszú. "+t+" karakterrel több, mint kellene."},inputTooShort:function(e){var t=e.minimum-e.input.length;return"Túl rövid. Még "+t+" karakter hiányzik."},loadingMore:function(){return"Töltés…"},maximumSelected:function(e){return"Csak "+e.maximum+" elemet lehet kiválasztani."},noResults:function(){return"Nincs találat."},searching:function(){return"Keresés…"}}}),{define:e.define,require:e.require}})();

+ 3 - 0
src/assets/select2/js/i18n/id.js

@@ -0,0 +1,3 @@
+/*! Select2 4.0.3 | https://github.com/select2/select2/blob/master/LICENSE.md */
+
+(function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var e=jQuery.fn.select2.amd;return e.define("select2/i18n/id",[],function(){return{errorLoading:function(){return"Data tidak boleh diambil."},inputTooLong:function(e){var t=e.input.length-e.maximum;return"Hapuskan "+t+" huruf"},inputTooShort:function(e){var t=e.minimum-e.input.length;return"Masukkan "+t+" huruf lagi"},loadingMore:function(){return"Mengambil data…"},maximumSelected:function(e){return"Anda hanya dapat memilih "+e.maximum+" pilihan"},noResults:function(){return"Tidak ada data yang sesuai"},searching:function(){return"Mencari…"}}}),{define:e.define,require:e.require}})();

+ 3 - 0
src/assets/select2/js/i18n/is.js

@@ -0,0 +1,3 @@
+/*! Select2 4.0.3 | https://github.com/select2/select2/blob/master/LICENSE.md */
+
+(function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var e=jQuery.fn.select2.amd;return e.define("select2/i18n/is",[],function(){return{inputTooLong:function(e){var t=e.input.length-e.maximum,n="Vinsamlegast styttið texta um "+t+" staf";return t<=1?n:n+"i"},inputTooShort:function(e){var t=e.minimum-e.input.length,n="Vinsamlegast skrifið "+t+" staf";return t>1&&(n+="i"),n+=" í viðbót",n},loadingMore:function(){return"Sæki fleiri niðurstöður…"},maximumSelected:function(e){return"Þú getur aðeins valið "+e.maximum+" atriði"},noResults:function(){return"Ekkert fannst"},searching:function(){return"Leita…"}}}),{define:e.define,require:e.require}})();

+ 3 - 0
src/assets/select2/js/i18n/it.js

@@ -0,0 +1,3 @@
+/*! Select2 4.0.3 | https://github.com/select2/select2/blob/master/LICENSE.md */
+
+(function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var e=jQuery.fn.select2.amd;return e.define("select2/i18n/it",[],function(){return{errorLoading:function(){return"I risultati non possono essere caricati."},inputTooLong:function(e){var t=e.input.length-e.maximum,n="Per favore cancella "+t+" caratter";return t!==1?n+="i":n+="e",n},inputTooShort:function(e){var t=e.minimum-e.input.length,n="Per favore inserisci "+t+" o più caratteri";return n},loadingMore:function(){return"Caricando più risultati…"},maximumSelected:function(e){var t="Puoi selezionare solo "+e.maximum+" element";return e.maximum!==1?t+="i":t+="o",t},noResults:function(){return"Nessun risultato trovato"},searching:function(){return"Sto cercando…"}}}),{define:e.define,require:e.require}})();

+ 3 - 0
src/assets/select2/js/i18n/ja.js

@@ -0,0 +1,3 @@
+/*! Select2 4.0.3 | https://github.com/select2/select2/blob/master/LICENSE.md */
+
+(function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var e=jQuery.fn.select2.amd;return e.define("select2/i18n/ja",[],function(){return{errorLoading:function(){return"結果が読み込まれませんでした"},inputTooLong:function(e){var t=e.input.length-e.maximum,n=t+" 文字を削除してください";return n},inputTooShort:function(e){var t=e.minimum-e.input.length,n="少なくとも "+t+" 文字を入力してください";return n},loadingMore:function(){return"読み込み中…"},maximumSelected:function(e){var t=e.maximum+" 件しか選択できません";return t},noResults:function(){return"対象が見つかりません"},searching:function(){return"検索しています…"}}}),{define:e.define,require:e.require}})();

+ 3 - 0
src/assets/select2/js/i18n/km.js

@@ -0,0 +1,3 @@
+/*! Select2 4.0.3 | https://github.com/select2/select2/blob/master/LICENSE.md */
+
+(function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var e=jQuery.fn.select2.amd;return e.define("select2/i18n/km",[],function(){return{errorLoading:function(){return"មិនអាចទាញយកទិន្នន័យ"},inputTooLong:function(e){var t=e.input.length-e.maximum,n="សូមលុបចេញ  "+t+" អក្សរ";return n},inputTooShort:function(e){var t=e.minimum-e.input.length,n="សូមបញ្ចូល"+t+" អក្សរ រឺ ច្រើនជាងនេះ";return n},loadingMore:function(){return"កំពុងទាញយកទិន្នន័យបន្ថែម..."},maximumSelected:function(e){var t="អ្នកអាចជ្រើសរើសបានតែ "+e.maximum+" ជម្រើសប៉ុណ្ណោះ";return t},noResults:function(){return"មិនមានលទ្ធផល"},searching:function(){return"កំពុងស្វែងរក..."}}}),{define:e.define,require:e.require}})();

+ 3 - 0
src/assets/select2/js/i18n/ko.js

@@ -0,0 +1,3 @@
+/*! Select2 4.0.3 | https://github.com/select2/select2/blob/master/LICENSE.md */
+
+(function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var e=jQuery.fn.select2.amd;return e.define("select2/i18n/ko",[],function(){return{errorLoading:function(){return"결과를 불러올 수 없습니다."},inputTooLong:function(e){var t=e.input.length-e.maximum,n="너무 깁니다. "+t+" 글자 지워주세요.";return n},inputTooShort:function(e){var t=e.minimum-e.input.length,n="너무 짧습니다. "+t+" 글자 더 입력해주세요.";return n},loadingMore:function(){return"불러오는 중…"},maximumSelected:function(e){var t="최대 "+e.maximum+"개까지만 선택 가능합니다.";return t},noResults:function(){return"결과가 없습니다."},searching:function(){return"검색 중…"}}}),{define:e.define,require:e.require}})();

+ 3 - 0
src/assets/select2/js/i18n/lt.js

@@ -0,0 +1,3 @@
+/*! Select2 4.0.3 | https://github.com/select2/select2/blob/master/LICENSE.md */
+
+(function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var e=jQuery.fn.select2.amd;return e.define("select2/i18n/lt",[],function(){function e(e,t,n,r){return e%10===1&&(e%100<11||e%100>19)?t:e%10>=2&&e%10<=9&&(e%100<11||e%100>19)?n:r}return{inputTooLong:function(t){var n=t.input.length-t.maximum,r="Pašalinkite "+n+" simbol";return r+=e(n,"į","ius","ių"),r},inputTooShort:function(t){var n=t.minimum-t.input.length,r="Įrašykite dar "+n+" simbol";return r+=e(n,"į","ius","ių"),r},loadingMore:function(){return"Kraunama daugiau rezultatų…"},maximumSelected:function(t){var n="Jūs galite pasirinkti tik "+t.maximum+" element";return n+=e(t.maximum,"ą","us","ų"),n},noResults:function(){return"Atitikmenų nerasta"},searching:function(){return"Ieškoma…"}}}),{define:e.define,require:e.require}})();

+ 3 - 0
src/assets/select2/js/i18n/lv.js

@@ -0,0 +1,3 @@
+/*! Select2 4.0.3 | https://github.com/select2/select2/blob/master/LICENSE.md */
+
+(function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var e=jQuery.fn.select2.amd;return e.define("select2/i18n/lv",[],function(){function e(e,t,n,r){return e===11?t:e%10===1?n:r}return{inputTooLong:function(t){var n=t.input.length-t.maximum,r="Lūdzu ievadiet par  "+n;return r+=" simbol"+e(n,"iem","u","iem"),r+" mazāk"},inputTooShort:function(t){var n=t.minimum-t.input.length,r="Lūdzu ievadiet vēl "+n;return r+=" simbol"+e(n,"us","u","us"),r},loadingMore:function(){return"Datu ielāde…"},maximumSelected:function(t){var n="Jūs varat izvēlēties ne vairāk kā "+t.maximum;return n+=" element"+e(t.maximum,"us","u","us"),n},noResults:function(){return"Sakritību nav"},searching:function(){return"Meklēšana…"}}}),{define:e.define,require:e.require}})();

+ 3 - 0
src/assets/select2/js/i18n/mk.js

@@ -0,0 +1,3 @@
+/*! Select2 4.0.3 | https://github.com/select2/select2/blob/master/LICENSE.md */
+
+(function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var e=jQuery.fn.select2.amd;return e.define("select2/i18n/mk",[],function(){return{inputTooLong:function(e){var t=e.input.length-e.maximum,n="Ве молиме внесете "+e.maximum+" помалку карактер";return e.maximum!==1&&(n+="и"),n},inputTooShort:function(e){var t=e.minimum-e.input.length,n="Ве молиме внесете уште "+e.maximum+" карактер";return e.maximum!==1&&(n+="и"),n},loadingMore:function(){return"Вчитување резултати…"},maximumSelected:function(e){var t="Можете да изберете само "+e.maximum+" ставк";return e.maximum===1?t+="а":t+="и",t},noResults:function(){return"Нема пронајдено совпаѓања"},searching:function(){return"Пребарување…"}}}),{define:e.define,require:e.require}})();

+ 3 - 0
src/assets/select2/js/i18n/ms.js

@@ -0,0 +1,3 @@
+/*! Select2 4.0.3 | https://github.com/select2/select2/blob/master/LICENSE.md */
+
+(function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var e=jQuery.fn.select2.amd;return e.define("select2/i18n/ms",[],function(){return{errorLoading:function(){return"Keputusan tidak berjaya dimuatkan."},inputTooLong:function(e){var t=e.input.length-e.maximum;return"Sila hapuskan "+t+" aksara"},inputTooShort:function(e){var t=e.minimum-e.input.length;return"Sila masukkan "+t+" atau lebih aksara"},loadingMore:function(){return"Sedang memuatkan keputusan…"},maximumSelected:function(e){return"Anda hanya boleh memilih "+e.maximum+" pilihan"},noResults:function(){return"Tiada padanan yang ditemui"},searching:function(){return"Mencari…"}}}),{define:e.define,require:e.require}})();

+ 3 - 0
src/assets/select2/js/i18n/nb.js

@@ -0,0 +1,3 @@
+/*! Select2 4.0.3 | https://github.com/select2/select2/blob/master/LICENSE.md */
+
+(function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var e=jQuery.fn.select2.amd;return e.define("select2/i18n/nb",[],function(){return{errorLoading:function(){return"Kunne ikke hente resultater."},inputTooLong:function(e){var t=e.input.length-e.maximum;return"Vennligst fjern "+t+" tegn"},inputTooShort:function(e){var t=e.minimum-e.input.length,n="Vennligst skriv inn ";return t>1?n+=" flere tegn":n+=" tegn til",n},loadingMore:function(){return"Laster flere resultater…"},maximumSelected:function(e){return"Du kan velge maks "+e.maximum+" elementer"},noResults:function(){return"Ingen treff"},searching:function(){return"Søker…"}}}),{define:e.define,require:e.require}})();

+ 3 - 0
src/assets/select2/js/i18n/nl.js

@@ -0,0 +1,3 @@
+/*! Select2 4.0.3 | https://github.com/select2/select2/blob/master/LICENSE.md */
+
+(function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var e=jQuery.fn.select2.amd;return e.define("select2/i18n/nl",[],function(){return{errorLoading:function(){return"De resultaten konden niet worden geladen."},inputTooLong:function(e){var t=e.input.length-e.maximum,n="Gelieve "+t+" karakters te verwijderen";return n},inputTooShort:function(e){var t=e.minimum-e.input.length,n="Gelieve "+t+" of meer karakters in te voeren";return n},loadingMore:function(){return"Meer resultaten laden…"},maximumSelected:function(e){var t=e.maximum==1?"kan":"kunnen",n="Er "+t+" maar "+e.maximum+" item";return e.maximum!=1&&(n+="s"),n+=" worden geselecteerd",n},noResults:function(){return"Geen resultaten gevonden…"},searching:function(){return"Zoeken…"}}}),{define:e.define,require:e.require}})();

+ 3 - 0
src/assets/select2/js/i18n/pl.js

@@ -0,0 +1,3 @@
+/*! Select2 4.0.3 | https://github.com/select2/select2/blob/master/LICENSE.md */
+
+(function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var e=jQuery.fn.select2.amd;return e.define("select2/i18n/pl",[],function(){var e=["znak","znaki","znaków"],t=["element","elementy","elementów"],n=function(t,n){if(t===1)return n[0];if(t>1&&t<=4)return n[1];if(t>=5)return n[2]};return{errorLoading:function(){return"Nie można załadować wyników."},inputTooLong:function(t){var r=t.input.length-t.maximum;return"Usuń "+r+" "+n(r,e)},inputTooShort:function(t){var r=t.minimum-t.input.length;return"Podaj przynajmniej "+r+" "+n(r,e)},loadingMore:function(){return"Trwa ładowanie…"},maximumSelected:function(e){return"Możesz zaznaczyć tylko "+e.maximum+" "+n(e.maximum,t)},noResults:function(){return"Brak wyników"},searching:function(){return"Trwa wyszukiwanie…"}}}),{define:e.define,require:e.require}})();

+ 3 - 0
src/assets/select2/js/i18n/pt-BR.js

@@ -0,0 +1,3 @@
+/*! Select2 4.0.3 | https://github.com/select2/select2/blob/master/LICENSE.md */
+
+(function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var e=jQuery.fn.select2.amd;return e.define("select2/i18n/pt-BR",[],function(){return{errorLoading:function(){return"Os resultados não puderam ser carregados."},inputTooLong:function(e){var t=e.input.length-e.maximum,n="Apague "+t+" caracter";return t!=1&&(n+="es"),n},inputTooShort:function(e){var t=e.minimum-e.input.length,n="Digite "+t+" ou mais caracteres";return n},loadingMore:function(){return"Carregando mais resultados…"},maximumSelected:function(e){var t="Você só pode selecionar "+e.maximum+" ite";return e.maximum==1?t+="m":t+="ns",t},noResults:function(){return"Nenhum resultado encontrado"},searching:function(){return"Buscando…"}}}),{define:e.define,require:e.require}})();

+ 3 - 0
src/assets/select2/js/i18n/pt.js

@@ -0,0 +1,3 @@
+/*! Select2 4.0.3 | https://github.com/select2/select2/blob/master/LICENSE.md */
+
+(function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var e=jQuery.fn.select2.amd;return e.define("select2/i18n/pt",[],function(){return{errorLoading:function(){return"Os resultados não puderam ser carregados."},inputTooLong:function(e){var t=e.input.length-e.maximum,n="Por favor apague "+t+" ";return n+=t!=1?"caracteres":"carácter",n},inputTooShort:function(e){var t=e.minimum-e.input.length,n="Introduza "+t+" ou mais caracteres";return n},loadingMore:function(){return"A carregar mais resultados…"},maximumSelected:function(e){var t="Apenas pode seleccionar "+e.maximum+" ";return t+=e.maximum!=1?"itens":"item",t},noResults:function(){return"Sem resultados"},searching:function(){return"A procurar…"}}}),{define:e.define,require:e.require}})();

+ 3 - 0
src/assets/select2/js/i18n/ro.js

@@ -0,0 +1,3 @@
+/*! Select2 4.0.3 | https://github.com/select2/select2/blob/master/LICENSE.md */
+
+(function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var e=jQuery.fn.select2.amd;return e.define("select2/i18n/ro",[],function(){return{errorLoading:function(){return"Rezultatele nu au putut fi incărcate."},inputTooLong:function(e){var t=e.input.length-e.maximum,n="Vă rugăm să ștergeți"+t+" caracter";return t!==1&&(n+="e"),n},inputTooShort:function(e){var t=e.minimum-e.input.length,n="Vă rugăm să introduceți "+t+"sau mai multe caractere";return n},loadingMore:function(){return"Se încarcă mai multe rezultate…"},maximumSelected:function(e){var t="Aveți voie să selectați cel mult "+e.maximum;return t+=" element",e.maximum!==1&&(t+="e"),t},noResults:function(){return"Nu au fost găsite rezultate"},searching:function(){return"Căutare…"}}}),{define:e.define,require:e.require}})();

+ 3 - 0
src/assets/select2/js/i18n/ru.js

@@ -0,0 +1,3 @@
+/*! Select2 4.0.3 | https://github.com/select2/select2/blob/master/LICENSE.md */
+
+(function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var e=jQuery.fn.select2.amd;return e.define("select2/i18n/ru",[],function(){function e(e,t,n,r){return e%10<5&&e%10>0&&e%100<5||e%100>20?e%10>1?n:t:r}return{errorLoading:function(){return"Невозможно загрузить результаты"},inputTooLong:function(t){var n=t.input.length-t.maximum,r="Пожалуйста, введите на "+n+" символ";return r+=e(n,"","a","ов"),r+=" меньше",r},inputTooShort:function(t){var n=t.minimum-t.input.length,r="Пожалуйста, введите еще хотя бы "+n+" символ";return r+=e(n,"","a","ов"),r},loadingMore:function(){return"Загрузка данных…"},maximumSelected:function(t){var n="Вы можете выбрать не более "+t.maximum+" элемент";return n+=e(t.maximum,"","a","ов"),n},noResults:function(){return"Совпадений не найдено"},searching:function(){return"Поиск…"}}}),{define:e.define,require:e.require}})();

+ 3 - 0
src/assets/select2/js/i18n/sk.js

@@ -0,0 +1,3 @@
+/*! Select2 4.0.3 | https://github.com/select2/select2/blob/master/LICENSE.md */
+
+(function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var e=jQuery.fn.select2.amd;return e.define("select2/i18n/sk",[],function(){var e={2:function(e){return e?"dva":"dve"},3:function(){return"tri"},4:function(){return"štyri"}};return{inputTooLong:function(t){var n=t.input.length-t.maximum;return n==1?"Prosím, zadajte o jeden znak menej":n>=2&&n<=4?"Prosím, zadajte o "+e[n](!0)+" znaky menej":"Prosím, zadajte o "+n+" znakov menej"},inputTooShort:function(t){var n=t.minimum-t.input.length;return n==1?"Prosím, zadajte ešte jeden znak":n<=4?"Prosím, zadajte ešte ďalšie "+e[n](!0)+" znaky":"Prosím, zadajte ešte ďalších "+n+" znakov"},loadingMore:function(){return"Loading more results…"},maximumSelected:function(t){return t.maximum==1?"Môžete zvoliť len jednu položku":t.maximum>=2&&t.maximum<=4?"Môžete zvoliť najviac "+e[t.maximum](!1)+" položky":"Môžete zvoliť najviac "+t.maximum+" položiek"},noResults:function(){return"Nenašli sa žiadne položky"},searching:function(){return"Vyhľadávanie…"}}}),{define:e.define,require:e.require}})();

+ 3 - 0
src/assets/select2/js/i18n/sr-Cyrl.js

@@ -0,0 +1,3 @@
+/*! Select2 4.0.3 | https://github.com/select2/select2/blob/master/LICENSE.md */
+
+(function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var e=jQuery.fn.select2.amd;return e.define("select2/i18n/sr-Cyrl",[],function(){function e(e,t,n,r){return e%10==1&&e%100!=11?t:e%10>=2&&e%10<=4&&(e%100<12||e%100>14)?n:r}return{errorLoading:function(){return"Преузимање није успело."},inputTooLong:function(t){var n=t.input.length-t.maximum,r="Обришите "+n+" симбол";return r+=e(n,"","а","а"),r},inputTooShort:function(t){var n=t.minimum-t.input.length,r="Укуцајте бар још "+n+" симбол";return r+=e(n,"","а","а"),r},loadingMore:function(){return"Преузимање још резултата…"},maximumSelected:function(t){var n="Можете изабрати само "+t.maximum+" ставк";return n+=e(t.maximum,"у","е","и"),n},noResults:function(){return"Ништа није пронађено"},searching:function(){return"Претрага…"}}}),{define:e.define,require:e.require}})();

+ 3 - 0
src/assets/select2/js/i18n/sr.js

@@ -0,0 +1,3 @@
+/*! Select2 4.0.3 | https://github.com/select2/select2/blob/master/LICENSE.md */
+
+(function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var e=jQuery.fn.select2.amd;return e.define("select2/i18n/sr",[],function(){function e(e,t,n,r){return e%10==1&&e%100!=11?t:e%10>=2&&e%10<=4&&(e%100<12||e%100>14)?n:r}return{errorLoading:function(){return"Preuzimanje nije uspelo."},inputTooLong:function(t){var n=t.input.length-t.maximum,r="Obrišite "+n+" simbol";return r+=e(n,"","a","a"),r},inputTooShort:function(t){var n=t.minimum-t.input.length,r="Ukucajte bar još "+n+" simbol";return r+=e(n,"","a","a"),r},loadingMore:function(){return"Preuzimanje još rezultata…"},maximumSelected:function(t){var n="Možete izabrati samo "+t.maximum+" stavk";return n+=e(t.maximum,"u","e","i"),n},noResults:function(){return"Ništa nije pronađeno"},searching:function(){return"Pretraga…"}}}),{define:e.define,require:e.require}})();

+ 3 - 0
src/assets/select2/js/i18n/sv.js

@@ -0,0 +1,3 @@
+/*! Select2 4.0.3 | https://github.com/select2/select2/blob/master/LICENSE.md */
+
+(function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var e=jQuery.fn.select2.amd;return e.define("select2/i18n/sv",[],function(){return{errorLoading:function(){return"Resultat kunde inte laddas."},inputTooLong:function(e){var t=e.input.length-e.maximum,n="Vänligen sudda ut "+t+" tecken";return n},inputTooShort:function(e){var t=e.minimum-e.input.length,n="Vänligen skriv in "+t+" eller fler tecken";return n},loadingMore:function(){return"Laddar fler resultat…"},maximumSelected:function(e){var t="Du kan max välja "+e.maximum+" element";return t},noResults:function(){return"Inga träffar"},searching:function(){return"Söker…"}}}),{define:e.define,require:e.require}})();

+ 3 - 0
src/assets/select2/js/i18n/th.js

@@ -0,0 +1,3 @@
+/*! Select2 4.0.3 | https://github.com/select2/select2/blob/master/LICENSE.md */
+
+(function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var e=jQuery.fn.select2.amd;return e.define("select2/i18n/th",[],function(){return{inputTooLong:function(e){var t=e.input.length-e.maximum,n="โปรดลบออก "+t+" ตัวอักษร";return n},inputTooShort:function(e){var t=e.minimum-e.input.length,n="โปรดพิมพ์เพิ่มอีก "+t+" ตัวอักษร";return n},loadingMore:function(){return"กำลังค้นข้อมูลเพิ่ม…"},maximumSelected:function(e){var t="คุณสามารถเลือกได้ไม่เกิน "+e.maximum+" รายการ";return t},noResults:function(){return"ไม่พบข้อมูล"},searching:function(){return"กำลังค้นข้อมูล…"}}}),{define:e.define,require:e.require}})();

+ 3 - 0
src/assets/select2/js/i18n/tr.js

@@ -0,0 +1,3 @@
+/*! Select2 4.0.3 | https://github.com/select2/select2/blob/master/LICENSE.md */
+
+(function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var e=jQuery.fn.select2.amd;return e.define("select2/i18n/tr",[],function(){return{inputTooLong:function(e){var t=e.input.length-e.maximum,n=t+" karakter daha girmelisiniz";return n},inputTooShort:function(e){var t=e.minimum-e.input.length,n="En az "+t+" karakter daha girmelisiniz";return n},loadingMore:function(){return"Daha fazla…"},maximumSelected:function(e){var t="Sadece "+e.maximum+" seçim yapabilirsiniz";return t},noResults:function(){return"Sonuç bulunamadı"},searching:function(){return"Aranıyor…"}}}),{define:e.define,require:e.require}})();

+ 3 - 0
src/assets/select2/js/i18n/uk.js

@@ -0,0 +1,3 @@
+/*! Select2 4.0.3 | https://github.com/select2/select2/blob/master/LICENSE.md */
+
+(function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var e=jQuery.fn.select2.amd;return e.define("select2/i18n/uk",[],function(){function e(e,t,n,r){return e%100>10&&e%100<15?r:e%10===1?t:e%10>1&&e%10<5?n:r}return{errorLoading:function(){return"Неможливо завантажити результати"},inputTooLong:function(t){var n=t.input.length-t.maximum;return"Будь ласка, видаліть "+n+" "+e(t.maximum,"літеру","літери","літер")},inputTooShort:function(e){var t=e.minimum-e.input.length;return"Будь ласка, введіть "+t+" або більше літер"},loadingMore:function(){return"Завантаження інших результатів…"},maximumSelected:function(t){return"Ви можете вибрати лише "+t.maximum+" "+e(t.maximum,"пункт","пункти","пунктів")},noResults:function(){return"Нічого не знайдено"},searching:function(){return"Пошук…"}}}),{define:e.define,require:e.require}})();

+ 3 - 0
src/assets/select2/js/i18n/vi.js

@@ -0,0 +1,3 @@
+/*! Select2 4.0.3 | https://github.com/select2/select2/blob/master/LICENSE.md */
+
+(function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var e=jQuery.fn.select2.amd;return e.define("select2/i18n/vi",[],function(){return{inputTooLong:function(e){var t=e.input.length-e.maximum,n="Vui lòng nhập ít hơn "+t+" ký tự";return t!=1&&(n+="s"),n},inputTooShort:function(e){var t=e.minimum-e.input.length,n="Vui lòng nhập nhiều hơn "+t+' ký tự"';return n},loadingMore:function(){return"Đang lấy thêm kết quả…"},maximumSelected:function(e){var t="Chỉ có thể chọn được "+e.maximum+" lựa chọn";return t},noResults:function(){return"Không tìm thấy kết quả"},searching:function(){return"Đang tìm…"}}}),{define:e.define,require:e.require}})();

+ 3 - 0
src/assets/select2/js/i18n/zh-CN.js

@@ -0,0 +1,3 @@
+/*! Select2 4.0.3 | https://github.com/select2/select2/blob/master/LICENSE.md */
+
+(function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var e=jQuery.fn.select2.amd;return e.define("select2/i18n/zh-CN",[],function(){return{errorLoading:function(){return"无法载入结果。"},inputTooLong:function(e){var t=e.input.length-e.maximum,n="请删除"+t+"个字符";return n},inputTooShort:function(e){var t=e.minimum-e.input.length,n="请再输入至少"+t+"个字符";return n},loadingMore:function(){return"载入更多结果…"},maximumSelected:function(e){var t="最多只能选择"+e.maximum+"个项目";return t},noResults:function(){return"未找到结果"},searching:function(){return"搜索中…"}}}),{define:e.define,require:e.require}})();

+ 3 - 0
src/assets/select2/js/i18n/zh-TW.js

@@ -0,0 +1,3 @@
+/*! Select2 4.0.3 | https://github.com/select2/select2/blob/master/LICENSE.md */
+
+(function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var e=jQuery.fn.select2.amd;return e.define("select2/i18n/zh-TW",[],function(){return{inputTooLong:function(e){var t=e.input.length-e.maximum,n="請刪掉"+t+"個字元";return n},inputTooShort:function(e){var t=e.minimum-e.input.length,n="請再輸入"+t+"個字元";return n},loadingMore:function(){return"載入中…"},maximumSelected:function(e){var t="你只能選擇最多"+e.maximum+"項";return t},noResults:function(){return"沒有找到相符的項目"},searching:function(){return"搜尋中…"}}}),{define:e.define,require:e.require}})();

+ 6438 - 0
src/assets/select2/js/select2.full.js

@@ -0,0 +1,6438 @@
+/*!
+ * Select2 4.0.3
+ * https://select2.github.io
+ *
+ * Released under the MIT license
+ * https://github.com/select2/select2/blob/master/LICENSE.md
+ */
+(function (factory) {
+    if (typeof define === 'function' && define.amd) {
+        // AMD. Register as an anonymous module.
+        define(['jquery'], factory);
+    } else if (typeof exports === 'object') {
+        // Node/CommonJS
+        factory(require('jquery'));
+    } else {
+        // Browser globals
+        factory(jQuery);
+    }
+}(function (jQuery) {
+    // This is needed so we can catch the AMD loader configuration and use it
+    // The inner file should be wrapped (by `banner.start.js`) in a function that
+    // returns the AMD loader references.
+    var S2 =
+        (function () {
+            // Restore the Select2 AMD loader so it can be used
+            // Needed mostly in the language files, where the loader is not inserted
+            if (jQuery && jQuery.fn && jQuery.fn.select2 && jQuery.fn.select2.amd) {
+                var S2 = jQuery.fn.select2.amd;
+            }
+            var S2;
+            (function () {
+                if (!S2 || !S2.requirejs) {
+                    if (!S2) {
+                        S2 = {};
+                    } else {
+                        require = S2;
+                    }
+                    /**
+                     * @license almond 0.3.1 Copyright (c) 2011-2014, The Dojo Foundation All Rights Reserved.
+                     * Available via the MIT or new BSD license.
+                     * see: http://github.com/jrburke/almond for details
+                     */
+//Going sloppy to avoid 'use strict' string cost, but strict practices should
+//be followed.
+                    /*jslint sloppy: true */
+                    /*global setTimeout: false */
+
+                    var requirejs, require, define;
+                    (function (undef) {
+                        var main, req, makeMap, handlers,
+                            defined = {},
+                            waiting = {},
+                            config = {},
+                            defining = {},
+                            hasOwn = Object.prototype.hasOwnProperty,
+                            aps = [].slice,
+                            jsSuffixRegExp = /\.js$/;
+
+                        function hasProp(obj, prop) {
+                            return hasOwn.call(obj, prop);
+                        }
+
+                        /**
+                         * Given a relative module name, like ./something, normalize it to
+                         * a real name that can be mapped to a path.
+                         * @param {String} name the relative name
+                         * @param {String} baseName a real name that the name arg is relative
+                         * to.
+                         * @returns {String} normalized name
+                         */
+                        function normalize(name, baseName) {
+                            var nameParts, nameSegment, mapValue, foundMap, lastIndex,
+                                foundI, foundStarMap, starI, i, j, part,
+                                baseParts = baseName && baseName.split("/"),
+                                map = config.map,
+                                starMap = (map && map['*']) || {};
+
+                            //Adjust any relative paths.
+                            if (name && name.charAt(0) === ".") {
+                                //If have a base name, try to normalize against it,
+                                //otherwise, assume it is a top-level require that will
+                                //be relative to baseUrl in the end.
+                                if (baseName) {
+                                    name = name.split('/');
+                                    lastIndex = name.length - 1;
+
+                                    // Node .js allowance:
+                                    if (config.nodeIdCompat && jsSuffixRegExp.test(name[lastIndex])) {
+                                        name[lastIndex] = name[lastIndex].replace(jsSuffixRegExp, '');
+                                    }
+
+                                    //Lop off the last part of baseParts, so that . matches the
+                                    //"directory" and not name of the baseName's module. For instance,
+                                    //baseName of "one/two/three", maps to "one/two/three.js", but we
+                                    //want the directory, "one/two" for this normalization.
+                                    name = baseParts.slice(0, baseParts.length - 1).concat(name);
+
+                                    //start trimDots
+                                    for (i = 0; i < name.length; i += 1) {
+                                        part = name[i];
+                                        if (part === ".") {
+                                            name.splice(i, 1);
+                                            i -= 1;
+                                        } else if (part === "..") {
+                                            if (i === 1 && (name[2] === '..' || name[0] === '..')) {
+                                                //End of the line. Keep at least one non-dot
+                                                //path segment at the front so it can be mapped
+                                                //correctly to disk. Otherwise, there is likely
+                                                //no path mapping for a path starting with '..'.
+                                                //This can still fail, but catches the most reasonable
+                                                //uses of ..
+                                                break;
+                                            } else if (i > 0) {
+                                                name.splice(i - 1, 2);
+                                                i -= 2;
+                                            }
+                                        }
+                                    }
+                                    //end trimDots
+
+                                    name = name.join("/");
+                                } else if (name.indexOf('./') === 0) {
+                                    // No baseName, so this is ID is resolved relative
+                                    // to baseUrl, pull off the leading dot.
+                                    name = name.substring(2);
+                                }
+                            }
+
+                            //Apply map config if available.
+                            if ((baseParts || starMap) && map) {
+                                nameParts = name.split('/');
+
+                                for (i = nameParts.length; i > 0; i -= 1) {
+                                    nameSegment = nameParts.slice(0, i).join("/");
+
+                                    if (baseParts) {
+                                        //Find the longest baseName segment match in the config.
+                                        //So, do joins on the biggest to smallest lengths of baseParts.
+                                        for (j = baseParts.length; j > 0; j -= 1) {
+                                            mapValue = map[baseParts.slice(0, j).join('/')];
+
+                                            //baseName segment has  config, find if it has one for
+                                            //this name.
+                                            if (mapValue) {
+                                                mapValue = mapValue[nameSegment];
+                                                if (mapValue) {
+                                                    //Match, update name to the new value.
+                                                    foundMap = mapValue;
+                                                    foundI = i;
+                                                    break;
+                                                }
+                                            }
+                                        }
+                                    }
+
+                                    if (foundMap) {
+                                        break;
+                                    }
+
+                                    //Check for a star map match, but just hold on to it,
+                                    //if there is a shorter segment match later in a matching
+                                    //config, then favor over this star map.
+                                    if (!foundStarMap && starMap && starMap[nameSegment]) {
+                                        foundStarMap = starMap[nameSegment];
+                                        starI = i;
+                                    }
+                                }
+
+                                if (!foundMap && foundStarMap) {
+                                    foundMap = foundStarMap;
+                                    foundI = starI;
+                                }
+
+                                if (foundMap) {
+                                    nameParts.splice(0, foundI, foundMap);
+                                    name = nameParts.join('/');
+                                }
+                            }
+
+                            return name;
+                        }
+
+                        function makeRequire(relName, forceSync) {
+                            return function () {
+                                //A version of a require function that passes a moduleName
+                                //value for items that may need to
+                                //look up paths relative to the moduleName
+                                var args = aps.call(arguments, 0);
+
+                                //If first arg is not require('string'), and there is only
+                                //one arg, it is the array form without a callback. Insert
+                                //a null so that the following concat is correct.
+                                if (typeof args[0] !== 'string' && args.length === 1) {
+                                    args.push(null);
+                                }
+                                return req.apply(undef, args.concat([relName, forceSync]));
+                            };
+                        }
+
+                        function makeNormalize(relName) {
+                            return function (name) {
+                                return normalize(name, relName);
+                            };
+                        }
+
+                        function makeLoad(depName) {
+                            return function (value) {
+                                defined[depName] = value;
+                            };
+                        }
+
+                        function callDep(name) {
+                            if (hasProp(waiting, name)) {
+                                var args = waiting[name];
+                                delete waiting[name];
+                                defining[name] = true;
+                                main.apply(undef, args);
+                            }
+
+                            if (!hasProp(defined, name) && !hasProp(defining, name)) {
+                                throw new Error('No ' + name);
+                            }
+                            return defined[name];
+                        }
+
+                        //Turns a plugin!resource to [plugin, resource]
+                        //with the plugin being undefined if the name
+                        //did not have a plugin prefix.
+                        function splitPrefix(name) {
+                            var prefix,
+                                index = name ? name.indexOf('!') : -1;
+                            if (index > -1) {
+                                prefix = name.substring(0, index);
+                                name = name.substring(index + 1, name.length);
+                            }
+                            return [prefix, name];
+                        }
+
+                        /**
+                         * Makes a name map, normalizing the name, and using a plugin
+                         * for normalization if necessary. Grabs a ref to plugin
+                         * too, as an optimization.
+                         */
+                        makeMap = function (name, relName) {
+                            var plugin,
+                                parts = splitPrefix(name),
+                                prefix = parts[0];
+
+                            name = parts[1];
+
+                            if (prefix) {
+                                prefix = normalize(prefix, relName);
+                                plugin = callDep(prefix);
+                            }
+
+                            //Normalize according
+                            if (prefix) {
+                                if (plugin && plugin.normalize) {
+                                    name = plugin.normalize(name, makeNormalize(relName));
+                                } else {
+                                    name = normalize(name, relName);
+                                }
+                            } else {
+                                name = normalize(name, relName);
+                                parts = splitPrefix(name);
+                                prefix = parts[0];
+                                name = parts[1];
+                                if (prefix) {
+                                    plugin = callDep(prefix);
+                                }
+                            }
+
+                            //Using ridiculous property names for space reasons
+                            return {
+                                f: prefix ? prefix + '!' + name : name, //fullName
+                                n: name,
+                                pr: prefix,
+                                p: plugin
+                            };
+                        };
+
+                        function makeConfig(name) {
+                            return function () {
+                                return (config && config.config && config.config[name]) || {};
+                            };
+                        }
+
+                        handlers = {
+                            require: function (name) {
+                                return makeRequire(name);
+                            },
+                            exports: function (name) {
+                                var e = defined[name];
+                                if (typeof e !== 'undefined') {
+                                    return e;
+                                } else {
+                                    return (defined[name] = {});
+                                }
+                            },
+                            module: function (name) {
+                                return {
+                                    id: name,
+                                    uri: '',
+                                    exports: defined[name],
+                                    config: makeConfig(name)
+                                };
+                            }
+                        };
+
+                        main = function (name, deps, callback, relName) {
+                            var cjsModule, depName, ret, map, i,
+                                args = [],
+                                callbackType = typeof callback,
+                                usingExports;
+
+                            //Use name if no relName
+                            relName = relName || name;
+
+                            //Call the callback to define the module, if necessary.
+                            if (callbackType === 'undefined' || callbackType === 'function') {
+                                //Pull out the defined dependencies and pass the ordered
+                                //values to the callback.
+                                //Default to [require, exports, module] if no deps
+                                deps = !deps.length && callback.length ? ['require', 'exports', 'module'] : deps;
+                                for (i = 0; i < deps.length; i += 1) {
+                                    map = makeMap(deps[i], relName);
+                                    depName = map.f;
+
+                                    //Fast path CommonJS standard dependencies.
+                                    if (depName === "require") {
+                                        args[i] = handlers.require(name);
+                                    } else if (depName === "exports") {
+                                        //CommonJS module spec 1.1
+                                        args[i] = handlers.exports(name);
+                                        usingExports = true;
+                                    } else if (depName === "module") {
+                                        //CommonJS module spec 1.1
+                                        cjsModule = args[i] = handlers.module(name);
+                                    } else if (hasProp(defined, depName) ||
+                                        hasProp(waiting, depName) ||
+                                        hasProp(defining, depName)) {
+                                        args[i] = callDep(depName);
+                                    } else if (map.p) {
+                                        map.p.load(map.n, makeRequire(relName, true), makeLoad(depName), {});
+                                        args[i] = defined[depName];
+                                    } else {
+                                        throw new Error(name + ' missing ' + depName);
+                                    }
+                                }
+
+                                ret = callback ? callback.apply(defined[name], args) : undefined;
+
+                                if (name) {
+                                    //If setting exports via "module" is in play,
+                                    //favor that over return value and exports. After that,
+                                    //favor a non-undefined return value over exports use.
+                                    if (cjsModule && cjsModule.exports !== undef &&
+                                        cjsModule.exports !== defined[name]) {
+                                        defined[name] = cjsModule.exports;
+                                    } else if (ret !== undef || !usingExports) {
+                                        //Use the return value from the function.
+                                        defined[name] = ret;
+                                    }
+                                }
+                            } else if (name) {
+                                //May just be an object definition for the module. Only
+                                //worry about defining if have a module name.
+                                defined[name] = callback;
+                            }
+                        };
+
+                        requirejs = require = req = function (deps, callback, relName, forceSync, alt) {
+                            if (typeof deps === "string") {
+                                if (handlers[deps]) {
+                                    //callback in this case is really relName
+                                    return handlers[deps](callback);
+                                }
+                                //Just return the module wanted. In this scenario, the
+                                //deps arg is the module name, and second arg (if passed)
+                                //is just the relName.
+                                //Normalize module name, if it contains . or ..
+                                return callDep(makeMap(deps, callback).f);
+                            } else if (!deps.splice) {
+                                //deps is a config object, not an array.
+                                config = deps;
+                                if (config.deps) {
+                                    req(config.deps, config.callback);
+                                }
+                                if (!callback) {
+                                    return;
+                                }
+
+                                if (callback.splice) {
+                                    //callback is an array, which means it is a dependency list.
+                                    //Adjust args if there are dependencies
+                                    deps = callback;
+                                    callback = relName;
+                                    relName = null;
+                                } else {
+                                    deps = undef;
+                                }
+                            }
+
+                            //Support require(['a'])
+                            callback = callback || function () {
+                            };
+
+                            //If relName is a function, it is an errback handler,
+                            //so remove it.
+                            if (typeof relName === 'function') {
+                                relName = forceSync;
+                                forceSync = alt;
+                            }
+
+                            //Simulate async callback;
+                            if (forceSync) {
+                                main(undef, deps, callback, relName);
+                            } else {
+                                //Using a non-zero value because of concern for what old browsers
+                                //do, and latest browsers "upgrade" to 4 if lower value is used:
+                                //http://www.whatwg.org/specs/web-apps/current-work/multipage/timers.html#dom-windowtimers-settimeout:
+                                //If want a value immediately, use require('id') instead -- something
+                                //that works in almond on the global level, but not guaranteed and
+                                //unlikely to work in other AMD implementations.
+                                setTimeout(function () {
+                                    main(undef, deps, callback, relName);
+                                }, 4);
+                            }
+
+                            return req;
+                        };
+
+                        /**
+                         * Just drops the config on the floor, but returns req in case
+                         * the config return value is used.
+                         */
+                        req.config = function (cfg) {
+                            return req(cfg);
+                        };
+
+                        /**
+                         * Expose module registry for debugging and tooling
+                         */
+                        requirejs._defined = defined;
+
+                        define = function (name, deps, callback) {
+                            if (typeof name !== 'string') {
+                                throw new Error('See almond README: incorrect module build, no module name');
+                            }
+
+                            //This module may not have dependencies
+                            if (!deps.splice) {
+                                //deps is not an array, so probably means
+                                //an object literal or factory function for
+                                //the value. Adjust args.
+                                callback = deps;
+                                deps = [];
+                            }
+
+                            if (!hasProp(defined, name) && !hasProp(waiting, name)) {
+                                waiting[name] = [name, deps, callback];
+                            }
+                        };
+
+                        define.amd = {
+                            jQuery: true
+                        };
+                    }());
+
+                    S2.requirejs = requirejs;
+                    S2.require = require;
+                    S2.define = define;
+                }
+            }());
+            S2.define("almond", function () {
+            });
+
+            /* global jQuery:false, $:false */
+            S2.define('jquery', [], function () {
+                var _$ = jQuery || $;
+
+                if (_$ == null && console && console.error) {
+                    console.error(
+                        'Select2: An instance of jQuery or a jQuery-compatible library was not ' +
+                        'found. Make sure that you are including jQuery before Select2 on your ' +
+                        'web page.'
+                    );
+                }
+
+                return _$;
+            });
+
+            S2.define('select2/utils', [
+                'jquery'
+            ], function ($) {
+                var Utils = {};
+
+                Utils.Extend = function (ChildClass, SuperClass) {
+                    var __hasProp = {}.hasOwnProperty;
+
+                    function BaseConstructor() {
+                        this.constructor = ChildClass;
+                    }
+
+                    for (var key in SuperClass) {
+                        if (__hasProp.call(SuperClass, key)) {
+                            ChildClass[key] = SuperClass[key];
+                        }
+                    }
+
+                    BaseConstructor.prototype = SuperClass.prototype;
+                    ChildClass.prototype = new BaseConstructor();
+                    ChildClass.__super__ = SuperClass.prototype;
+
+                    return ChildClass;
+                };
+
+                function getMethods(theClass) {
+                    var proto = theClass.prototype;
+
+                    var methods = [];
+
+                    for (var methodName in proto) {
+                        var m = proto[methodName];
+
+                        if (typeof m !== 'function') {
+                            continue;
+                        }
+
+                        if (methodName === 'constructor') {
+                            continue;
+                        }
+
+                        methods.push(methodName);
+                    }
+
+                    return methods;
+                }
+
+                Utils.Decorate = function (SuperClass, DecoratorClass) {
+                    var decoratedMethods = getMethods(DecoratorClass);
+                    var superMethods = getMethods(SuperClass);
+
+                    function DecoratedClass() {
+                        var unshift = Array.prototype.unshift;
+
+                        var argCount = DecoratorClass.prototype.constructor.length;
+
+                        var calledConstructor = SuperClass.prototype.constructor;
+
+                        if (argCount > 0) {
+                            unshift.call(arguments, SuperClass.prototype.constructor);
+
+                            calledConstructor = DecoratorClass.prototype.constructor;
+                        }
+
+                        calledConstructor.apply(this, arguments);
+                    }
+
+                    DecoratorClass.displayName = SuperClass.displayName;
+
+                    function ctr() {
+                        this.constructor = DecoratedClass;
+                    }
+
+                    DecoratedClass.prototype = new ctr();
+
+                    for (var m = 0; m < superMethods.length; m++) {
+                        var superMethod = superMethods[m];
+
+                        DecoratedClass.prototype[superMethod] =
+                            SuperClass.prototype[superMethod];
+                    }
+
+                    var calledMethod = function (methodName) {
+                        // Stub out the original method if it's not decorating an actual method
+                        var originalMethod = function () {
+                        };
+
+                        if (methodName in DecoratedClass.prototype) {
+                            originalMethod = DecoratedClass.prototype[methodName];
+                        }
+
+                        var decoratedMethod = DecoratorClass.prototype[methodName];
+
+                        return function () {
+                            var unshift = Array.prototype.unshift;
+
+                            unshift.call(arguments, originalMethod);
+
+                            return decoratedMethod.apply(this, arguments);
+                        };
+                    };
+
+                    for (var d = 0; d < decoratedMethods.length; d++) {
+                        var decoratedMethod = decoratedMethods[d];
+
+                        DecoratedClass.prototype[decoratedMethod] = calledMethod(decoratedMethod);
+                    }
+
+                    return DecoratedClass;
+                };
+
+                var Observable = function () {
+                    this.listeners = {};
+                };
+
+                Observable.prototype.on = function (event, callback) {
+                    this.listeners = this.listeners || {};
+
+                    if (event in this.listeners) {
+                        this.listeners[event].push(callback);
+                    } else {
+                        this.listeners[event] = [callback];
+                    }
+                };
+
+                Observable.prototype.trigger = function (event) {
+                    var slice = Array.prototype.slice;
+                    var params = slice.call(arguments, 1);
+
+                    this.listeners = this.listeners || {};
+
+                    // Params should always come in as an array
+                    if (params == null) {
+                        params = [];
+                    }
+
+                    // If there are no arguments to the event, use a temporary object
+                    if (params.length === 0) {
+                        params.push({});
+                    }
+
+                    // Set the `_type` of the first object to the event
+                    params[0]._type = event;
+
+                    if (event in this.listeners) {
+                        this.invoke(this.listeners[event], slice.call(arguments, 1));
+                    }
+
+                    if ('*' in this.listeners) {
+                        this.invoke(this.listeners['*'], arguments);
+                    }
+                };
+
+                Observable.prototype.invoke = function (listeners, params) {
+                    for (var i = 0, len = listeners.length; i < len; i++) {
+                        listeners[i].apply(this, params);
+                    }
+                };
+
+                Utils.Observable = Observable;
+
+                Utils.generateChars = function (length) {
+                    var chars = '';
+
+                    for (var i = 0; i < length; i++) {
+                        var randomChar = Math.floor(Math.random() * 36);
+                        chars += randomChar.toString(36);
+                    }
+
+                    return chars;
+                };
+
+                Utils.bind = function (func, context) {
+                    return function () {
+                        func.apply(context, arguments);
+                    };
+                };
+
+                Utils._convertData = function (data) {
+                    for (var originalKey in data) {
+                        var keys = originalKey.split('-');
+
+                        var dataLevel = data;
+
+                        if (keys.length === 1) {
+                            continue;
+                        }
+
+                        for (var k = 0; k < keys.length; k++) {
+                            var key = keys[k];
+
+                            // Lowercase the first letter
+                            // By default, dash-separated becomes camelCase
+                            key = key.substring(0, 1).toLowerCase() + key.substring(1);
+
+                            if (!(key in dataLevel)) {
+                                dataLevel[key] = {};
+                            }
+
+                            if (k == keys.length - 1) {
+                                dataLevel[key] = data[originalKey];
+                            }
+
+                            dataLevel = dataLevel[key];
+                        }
+
+                        delete data[originalKey];
+                    }
+
+                    return data;
+                };
+
+                Utils.hasScroll = function (index, el) {
+                    // Adapted from the function created by @ShadowScripter
+                    // and adapted by @BillBarry on the Stack Exchange Code Review website.
+                    // The original code can be found at
+                    // http://codereview.stackexchange.com/q/13338
+                    // and was designed to be used with the Sizzle selector engine.
+
+                    var $el = $(el);
+                    var overflowX = el.style.overflowX;
+                    var overflowY = el.style.overflowY;
+
+                    //Check both x and y declarations
+                    if (overflowX === overflowY &&
+                        (overflowY === 'hidden' || overflowY === 'visible')) {
+                        return false;
+                    }
+
+                    if (overflowX === 'scroll' || overflowY === 'scroll') {
+                        return true;
+                    }
+
+                    return ($el.innerHeight() < el.scrollHeight ||
+                        $el.innerWidth() < el.scrollWidth);
+                };
+
+                Utils.escapeMarkup = function (markup) {
+                    var replaceMap = {
+                        '\\': '&#92;',
+                        '&': '&amp;',
+                        '<': '&lt;',
+                        '>': '&gt;',
+                        '"': '&quot;',
+                        '\'': '&#39;',
+                        '/': '&#47;'
+                    };
+
+                    // Do not try to escape the markup if it's not a string
+                    if (typeof markup !== 'string') {
+                        return markup;
+                    }
+
+                    return String(markup).replace(/[&<>"'\/\\]/g, function (match) {
+                        return replaceMap[match];
+                    });
+                };
+
+                // Append an array of jQuery nodes to a given element.
+                Utils.appendMany = function ($element, $nodes) {
+                    // jQuery 1.7.x does not support $.fn.append() with an array
+                    // Fall back to a jQuery object collection using $.fn.add()
+                    if ($.fn.jquery.substr(0, 3) === '1.7') {
+                        var $jqNodes = $();
+
+                        $.map($nodes, function (node) {
+                            $jqNodes = $jqNodes.add(node);
+                        });
+
+                        $nodes = $jqNodes;
+                    }
+
+                    $element.append($nodes);
+                };
+
+                return Utils;
+            });
+
+            S2.define('select2/results', [
+                'jquery',
+                './utils'
+            ], function ($, Utils) {
+                function Results($element, options, dataAdapter) {
+                    this.$element = $element;
+                    this.data = dataAdapter;
+                    this.options = options;
+
+                    Results.__super__.constructor.call(this);
+                }
+
+                Utils.Extend(Results, Utils.Observable);
+
+                Results.prototype.render = function () {
+                    var $results = $(
+                        '<ul class="select2-results__options" role="tree"></ul>'
+                    );
+
+                    if (this.options.get('multiple')) {
+                        $results.attr('aria-multiselectable', 'true');
+                    }
+
+                    this.$results = $results;
+
+                    return $results;
+                };
+
+                Results.prototype.clear = function () {
+                    this.$results.empty();
+                };
+
+                Results.prototype.displayMessage = function (params) {
+                    var escapeMarkup = this.options.get('escapeMarkup');
+
+                    this.clear();
+                    this.hideLoading();
+
+                    var $message = $(
+                        '<li role="treeitem" aria-live="assertive"' +
+                        ' class="select2-results__option"></li>'
+                    );
+
+                    var message = this.options.get('translations').get(params.message);
+
+                    $message.append(
+                        escapeMarkup(
+                            message(params.args)
+                        )
+                    );
+
+                    $message[0].className += ' select2-results__message';
+
+                    this.$results.append($message);
+                };
+
+                Results.prototype.hideMessages = function () {
+                    this.$results.find('.select2-results__message').remove();
+                };
+
+                Results.prototype.append = function (data) {
+                    this.hideLoading();
+
+                    var $options = [];
+
+                    if (data.results == null || data.results.length === 0) {
+                        if (this.$results.children().length === 0) {
+                            this.trigger('results:message', {
+                                message: 'noResults'
+                            });
+                        }
+
+                        return;
+                    }
+
+                    data.results = this.sort(data.results);
+
+                    for (var d = 0; d < data.results.length; d++) {
+                        var item = data.results[d];
+
+                        var $option = this.option(item);
+
+                        $options.push($option);
+                    }
+
+                    this.$results.append($options);
+                };
+
+                Results.prototype.position = function ($results, $dropdown) {
+                    var $resultsContainer = $dropdown.find('.select2-results');
+                    $resultsContainer.append($results);
+                };
+
+                Results.prototype.sort = function (data) {
+                    var sorter = this.options.get('sorter');
+
+                    return sorter(data);
+                };
+
+                Results.prototype.highlightFirstItem = function () {
+                    var $options = this.$results
+                        .find('.select2-results__option[aria-selected]');
+
+                    var $selected = $options.filter('[aria-selected=true]');
+
+                    // Check if there are any selected options
+                    if ($selected.length > 0) {
+                        // If there are selected options, highlight the first
+                        $selected.first().trigger('mouseenter');
+                    } else {
+                        // If there are no selected options, highlight the first option
+                        // in the dropdown
+                        $options.first().trigger('mouseenter');
+                    }
+
+                    this.ensureHighlightVisible();
+                };
+
+                Results.prototype.setClasses = function () {
+                    var self = this;
+
+                    this.data.current(function (selected) {
+                        var selectedIds = $.map(selected, function (s) {
+                            return s.id.toString();
+                        });
+
+                        var $options = self.$results
+                            .find('.select2-results__option[aria-selected]');
+
+                        $options.each(function () {
+                            var $option = $(this);
+
+                            var item = $.data(this, 'data');
+
+                            // id needs to be converted to a string when comparing
+                            var id = '' + item.id;
+
+                            if ((item.element != null && item.element.selected) ||
+                                (item.element == null && $.inArray(id, selectedIds) > -1)) {
+                                $option.attr('aria-selected', 'true');
+                            } else {
+                                $option.attr('aria-selected', 'false');
+                            }
+                        });
+
+                    });
+                };
+
+                Results.prototype.showLoading = function (params) {
+                    this.hideLoading();
+
+                    var loadingMore = this.options.get('translations').get('searching');
+
+                    var loading = {
+                        disabled: true,
+                        loading: true,
+                        text: loadingMore(params)
+                    };
+                    var $loading = this.option(loading);
+                    $loading.className += ' loading-results';
+
+                    this.$results.prepend($loading);
+                };
+
+                Results.prototype.hideLoading = function () {
+                    this.$results.find('.loading-results').remove();
+                };
+
+                Results.prototype.option = function (data) {
+                    var option = document.createElement('li');
+                    option.className = 'select2-results__option';
+
+                    var attrs = {
+                        'role': 'treeitem',
+                        'aria-selected': 'false'
+                    };
+
+                    if (data.disabled) {
+                        delete attrs['aria-selected'];
+                        attrs['aria-disabled'] = 'true';
+                    }
+
+                    if (data.id == null) {
+                        delete attrs['aria-selected'];
+                    }
+
+                    if (data._resultId != null) {
+                        option.id = data._resultId;
+                    }
+
+                    if (data.title) {
+                        option.title = data.title;
+                    }
+
+                    if (data.children) {
+                        attrs.role = 'group';
+                        attrs['aria-label'] = data.text;
+                        delete attrs['aria-selected'];
+                    }
+
+                    for (var attr in attrs) {
+                        var val = attrs[attr];
+
+                        option.setAttribute(attr, val);
+                    }
+
+                    if (data.children) {
+                        var $option = $(option);
+
+                        var label = document.createElement('strong');
+                        label.className = 'select2-results__group';
+
+                        var $label = $(label);
+                        this.template(data, label);
+
+                        var $children = [];
+
+                        for (var c = 0; c < data.children.length; c++) {
+                            var child = data.children[c];
+
+                            var $child = this.option(child);
+
+                            $children.push($child);
+                        }
+
+                        var $childrenContainer = $('<ul></ul>', {
+                            'class': 'select2-results__options select2-results__options--nested'
+                        });
+
+                        $childrenContainer.append($children);
+
+                        $option.append(label);
+                        $option.append($childrenContainer);
+                    } else {
+                        this.template(data, option);
+                    }
+
+                    $.data(option, 'data', data);
+
+                    return option;
+                };
+
+                Results.prototype.bind = function (container, $container) {
+                    var self = this;
+
+                    var id = container.id + '-results';
+
+                    this.$results.attr('id', id);
+
+                    container.on('results:all', function (params) {
+                        self.clear();
+                        self.append(params.data);
+
+                        if (container.isOpen()) {
+                            self.setClasses();
+                            self.highlightFirstItem();
+                        }
+                    });
+
+                    container.on('results:append', function (params) {
+                        self.append(params.data);
+
+                        if (container.isOpen()) {
+                            self.setClasses();
+                        }
+                    });
+
+                    container.on('query', function (params) {
+                        self.hideMessages();
+                        self.showLoading(params);
+                    });
+
+                    container.on('select', function () {
+                        if (!container.isOpen()) {
+                            return;
+                        }
+
+                        self.setClasses();
+                        self.highlightFirstItem();
+                    });
+
+                    container.on('unselect', function () {
+                        if (!container.isOpen()) {
+                            return;
+                        }
+
+                        self.setClasses();
+                        self.highlightFirstItem();
+                    });
+
+                    container.on('open', function () {
+                        // When the dropdown is open, aria-expended="true"
+                        self.$results.attr('aria-expanded', 'true');
+                        self.$results.attr('aria-hidden', 'false');
+
+                        self.setClasses();
+                        self.ensureHighlightVisible();
+                    });
+
+                    container.on('close', function () {
+                        // When the dropdown is closed, aria-expended="false"
+                        self.$results.attr('aria-expanded', 'false');
+                        self.$results.attr('aria-hidden', 'true');
+                        self.$results.removeAttr('aria-activedescendant');
+                    });
+
+                    container.on('results:toggle', function () {
+                        var $highlighted = self.getHighlightedResults();
+
+                        if ($highlighted.length === 0) {
+                            return;
+                        }
+
+                        $highlighted.trigger('mouseup');
+                    });
+
+                    container.on('results:select', function () {
+                        var $highlighted = self.getHighlightedResults();
+
+                        if ($highlighted.length === 0) {
+                            return;
+                        }
+
+                        var data = $highlighted.data('data');
+
+                        if ($highlighted.attr('aria-selected') == 'true') {
+                            self.trigger('close', {});
+                        } else {
+                            self.trigger('select', {
+                                data: data
+                            });
+                        }
+                    });
+
+                    container.on('results:previous', function () {
+                        var $highlighted = self.getHighlightedResults();
+
+                        var $options = self.$results.find('[aria-selected]');
+
+                        var currentIndex = $options.index($highlighted);
+
+                        // If we are already at te top, don't move further
+                        if (currentIndex === 0) {
+                            return;
+                        }
+
+                        var nextIndex = currentIndex - 1;
+
+                        // If none are highlighted, highlight the first
+                        if ($highlighted.length === 0) {
+                            nextIndex = 0;
+                        }
+
+                        var $next = $options.eq(nextIndex);
+
+                        $next.trigger('mouseenter');
+
+                        var currentOffset = self.$results.offset().top;
+                        var nextTop = $next.offset().top;
+                        var nextOffset = self.$results.scrollTop() + (nextTop - currentOffset);
+
+                        if (nextIndex === 0) {
+                            self.$results.scrollTop(0);
+                        } else if (nextTop - currentOffset < 0) {
+                            self.$results.scrollTop(nextOffset);
+                        }
+                    });
+
+                    container.on('results:next', function () {
+                        var $highlighted = self.getHighlightedResults();
+
+                        var $options = self.$results.find('[aria-selected]');
+
+                        var currentIndex = $options.index($highlighted);
+
+                        var nextIndex = currentIndex + 1;
+
+                        // If we are at the last option, stay there
+                        if (nextIndex >= $options.length) {
+                            return;
+                        }
+
+                        var $next = $options.eq(nextIndex);
+
+                        $next.trigger('mouseenter');
+
+                        var currentOffset = self.$results.offset().top +
+                            self.$results.outerHeight(false);
+                        var nextBottom = $next.offset().top + $next.outerHeight(false);
+                        var nextOffset = self.$results.scrollTop() + nextBottom - currentOffset;
+
+                        if (nextIndex === 0) {
+                            self.$results.scrollTop(0);
+                        } else if (nextBottom > currentOffset) {
+                            self.$results.scrollTop(nextOffset);
+                        }
+                    });
+
+                    container.on('results:focus', function (params) {
+                        params.element.addClass('select2-results__option--highlighted');
+                    });
+
+                    container.on('results:message', function (params) {
+                        self.displayMessage(params);
+                    });
+
+                    if ($.fn.mousewheel) {
+                        this.$results.on('mousewheel', function (e) {
+                            var top = self.$results.scrollTop();
+
+                            var bottom = self.$results.get(0).scrollHeight - top + e.deltaY;
+
+                            var isAtTop = e.deltaY > 0 && top - e.deltaY <= 0;
+                            var isAtBottom = e.deltaY < 0 && bottom <= self.$results.height();
+
+                            if (isAtTop) {
+                                self.$results.scrollTop(0);
+
+                                e.preventDefault();
+                                e.stopPropagation();
+                            } else if (isAtBottom) {
+                                self.$results.scrollTop(
+                                    self.$results.get(0).scrollHeight - self.$results.height()
+                                );
+
+                                e.preventDefault();
+                                e.stopPropagation();
+                            }
+                        });
+                    }
+
+                    this.$results.on('mouseup', '.select2-results__option[aria-selected]',
+                        function (evt) {
+                            var $this = $(this);
+
+                            var data = $this.data('data');
+
+                            if ($this.attr('aria-selected') === 'true') {
+                                if (self.options.get('multiple')) {
+                                    self.trigger('unselect', {
+                                        originalEvent: evt,
+                                        data: data
+                                    });
+                                } else {
+                                    self.trigger('close', {});
+                                }
+
+                                return;
+                            }
+
+                            self.trigger('select', {
+                                originalEvent: evt,
+                                data: data
+                            });
+                        });
+
+                    this.$results.on('mouseenter', '.select2-results__option[aria-selected]',
+                        function (evt) {
+                            var data = $(this).data('data');
+
+                            self.getHighlightedResults()
+                                .removeClass('select2-results__option--highlighted');
+
+                            self.trigger('results:focus', {
+                                data: data,
+                                element: $(this)
+                            });
+                        });
+                };
+
+                Results.prototype.getHighlightedResults = function () {
+                    var $highlighted = this.$results
+                        .find('.select2-results__option--highlighted');
+
+                    return $highlighted;
+                };
+
+                Results.prototype.destroy = function () {
+                    this.$results.remove();
+                };
+
+                Results.prototype.ensureHighlightVisible = function () {
+                    var $highlighted = this.getHighlightedResults();
+
+                    if ($highlighted.length === 0) {
+                        return;
+                    }
+
+                    var $options = this.$results.find('[aria-selected]');
+
+                    var currentIndex = $options.index($highlighted);
+
+                    var currentOffset = this.$results.offset().top;
+                    var nextTop = $highlighted.offset().top;
+                    var nextOffset = this.$results.scrollTop() + (nextTop - currentOffset);
+
+                    var offsetDelta = nextTop - currentOffset;
+                    nextOffset -= $highlighted.outerHeight(false) * 2;
+
+                    if (currentIndex <= 2) {
+                        this.$results.scrollTop(0);
+                    } else if (offsetDelta > this.$results.outerHeight() || offsetDelta < 0) {
+                        this.$results.scrollTop(nextOffset);
+                    }
+                };
+
+                Results.prototype.template = function (result, container) {
+                    var template = this.options.get('templateResult');
+                    var escapeMarkup = this.options.get('escapeMarkup');
+
+                    var content = template(result, container);
+
+                    if (content == null) {
+                        container.style.display = 'none';
+                    } else if (typeof content === 'string') {
+                        container.innerHTML = escapeMarkup(content);
+                    } else {
+                        $(container).append(content);
+                    }
+                };
+
+                return Results;
+            });
+
+            S2.define('select2/keys', [], function () {
+                var KEYS = {
+                    BACKSPACE: 8,
+                    TAB: 9,
+                    ENTER: 13,
+                    SHIFT: 16,
+                    CTRL: 17,
+                    ALT: 18,
+                    ESC: 27,
+                    SPACE: 32,
+                    PAGE_UP: 33,
+                    PAGE_DOWN: 34,
+                    END: 35,
+                    HOME: 36,
+                    LEFT: 37,
+                    UP: 38,
+                    RIGHT: 39,
+                    DOWN: 40,
+                    DELETE: 46
+                };
+
+                return KEYS;
+            });
+
+            S2.define('select2/selection/base', [
+                'jquery',
+                '../utils',
+                '../keys'
+            ], function ($, Utils, KEYS) {
+                function BaseSelection($element, options) {
+                    this.$element = $element;
+                    this.options = options;
+
+                    BaseSelection.__super__.constructor.call(this);
+                }
+
+                Utils.Extend(BaseSelection, Utils.Observable);
+
+                BaseSelection.prototype.render = function () {
+                    var $selection = $(
+                        '<span class="select2-selection" role="combobox" ' +
+                        ' aria-haspopup="true" aria-expanded="false">' +
+                        '</span>'
+                    );
+
+                    this._tabindex = 0;
+
+                    if (this.$element.data('old-tabindex') != null) {
+                        this._tabindex = this.$element.data('old-tabindex');
+                    } else if (this.$element.attr('tabindex') != null) {
+                        this._tabindex = this.$element.attr('tabindex');
+                    }
+
+                    $selection.attr('title', this.$element.attr('title'));
+                    $selection.attr('tabindex', this._tabindex);
+
+                    this.$selection = $selection;
+
+                    return $selection;
+                };
+
+                BaseSelection.prototype.bind = function (container, $container) {
+                    var self = this;
+
+                    var id = container.id + '-container';
+                    var resultsId = container.id + '-results';
+
+                    this.container = container;
+
+                    this.$selection.on('focus', function (evt) {
+                        self.trigger('focus', evt);
+                    });
+
+                    this.$selection.on('blur', function (evt) {
+                        self._handleBlur(evt);
+                    });
+
+                    this.$selection.on('keydown', function (evt) {
+                        self.trigger('keypress', evt);
+
+                        if (evt.which === KEYS.SPACE) {
+                            evt.preventDefault();
+                        }
+                    });
+
+                    container.on('results:focus', function (params) {
+                        self.$selection.attr('aria-activedescendant', params.data._resultId);
+                    });
+
+                    container.on('selection:update', function (params) {
+                        self.update(params.data);
+                    });
+
+                    container.on('open', function () {
+                        // When the dropdown is open, aria-expanded="true"
+                        self.$selection.attr('aria-expanded', 'true');
+                        self.$selection.attr('aria-owns', resultsId);
+
+                        self._attachCloseHandler(container);
+                    });
+
+                    container.on('close', function () {
+                        // When the dropdown is closed, aria-expanded="false"
+                        self.$selection.attr('aria-expanded', 'false');
+                        self.$selection.removeAttr('aria-activedescendant');
+                        self.$selection.removeAttr('aria-owns');
+
+                        self.$selection.focus();
+
+                        self._detachCloseHandler(container);
+                    });
+
+                    container.on('enable', function () {
+                        self.$selection.attr('tabindex', self._tabindex);
+                    });
+
+                    container.on('disable', function () {
+                        self.$selection.attr('tabindex', '-1');
+                    });
+                };
+
+                BaseSelection.prototype._handleBlur = function (evt) {
+                    var self = this;
+
+                    // This needs to be delayed as the active element is the body when the tab
+                    // key is pressed, possibly along with others.
+                    window.setTimeout(function () {
+                        // Don't trigger `blur` if the focus is still in the selection
+                        if (
+                            (document.activeElement == self.$selection[0]) ||
+                            ($.contains(self.$selection[0], document.activeElement))
+                        ) {
+                            return;
+                        }
+
+                        self.trigger('blur', evt);
+                    }, 1);
+                };
+
+                BaseSelection.prototype._attachCloseHandler = function (container) {
+                    var self = this;
+
+                    $(document.body).on('mousedown.select2.' + container.id, function (e) {
+                        var $target = $(e.target);
+
+                        var $select = $target.closest('.select2');
+
+                        var $all = $('.select2.select2-container--open');
+
+                        $all.each(function () {
+                            var $this = $(this);
+
+                            if (this == $select[0]) {
+                                return;
+                            }
+
+                            var $element = $this.data('element');
+
+                            $element.select2('close');
+                        });
+                    });
+                };
+
+                BaseSelection.prototype._detachCloseHandler = function (container) {
+                    $(document.body).off('mousedown.select2.' + container.id);
+                };
+
+                BaseSelection.prototype.position = function ($selection, $container) {
+                    var $selectionContainer = $container.find('.selection');
+                    $selectionContainer.append($selection);
+                };
+
+                BaseSelection.prototype.destroy = function () {
+                    this._detachCloseHandler(this.container);
+                };
+
+                BaseSelection.prototype.update = function (data) {
+                    throw new Error('The `update` method must be defined in child classes.');
+                };
+
+                return BaseSelection;
+            });
+
+            S2.define('select2/selection/single', [
+                'jquery',
+                './base',
+                '../utils',
+                '../keys'
+            ], function ($, BaseSelection, Utils, KEYS) {
+                function SingleSelection() {
+                    SingleSelection.__super__.constructor.apply(this, arguments);
+                }
+
+                Utils.Extend(SingleSelection, BaseSelection);
+
+                SingleSelection.prototype.render = function () {
+                    var $selection = SingleSelection.__super__.render.call(this);
+
+                    $selection.addClass('select2-selection--single');
+
+                    $selection.html(
+                        '<span class="select2-selection__rendered"></span>' +
+                        '<span class="select2-selection__arrow" role="presentation">' +
+                        '<b role="presentation"></b>' +
+                        '</span>'
+                    );
+
+                    return $selection;
+                };
+
+                SingleSelection.prototype.bind = function (container, $container) {
+                    var self = this;
+
+                    SingleSelection.__super__.bind.apply(this, arguments);
+
+                    var id = container.id + '-container';
+
+                    this.$selection.find('.select2-selection__rendered').attr('id', id);
+                    this.$selection.attr('aria-labelledby', id);
+
+                    this.$selection.on('mousedown', function (evt) {
+                        // Only respond to left clicks
+                        if (evt.which !== 1) {
+                            return;
+                        }
+
+                        self.trigger('toggle', {
+                            originalEvent: evt
+                        });
+                    });
+
+                    this.$selection.on('focus', function (evt) {
+                        // User focuses on the container
+                    });
+
+                    this.$selection.on('blur', function (evt) {
+                        // User exits the container
+                    });
+
+                    container.on('focus', function (evt) {
+                        if (!container.isOpen()) {
+                            self.$selection.focus();
+                        }
+                    });
+
+                    container.on('selection:update', function (params) {
+                        self.update(params.data);
+                    });
+                };
+
+                SingleSelection.prototype.clear = function () {
+                    this.$selection.find('.select2-selection__rendered').empty();
+                };
+
+                SingleSelection.prototype.display = function (data, container) {
+                    var template = this.options.get('templateSelection');
+                    var escapeMarkup = this.options.get('escapeMarkup');
+
+                    return escapeMarkup(template(data, container));
+                };
+
+                SingleSelection.prototype.selectionContainer = function () {
+                    return $('<span></span>');
+                };
+
+                SingleSelection.prototype.update = function (data) {
+                    if (data.length === 0) {
+                        this.clear();
+                        return;
+                    }
+
+                    var selection = data[0];
+
+                    var $rendered = this.$selection.find('.select2-selection__rendered');
+                    var formatted = this.display(selection, $rendered);
+
+                    $rendered.empty().append(formatted);
+                    $rendered.prop('title', selection.title || selection.text);
+                };
+
+                return SingleSelection;
+            });
+
+            S2.define('select2/selection/multiple', [
+                'jquery',
+                './base',
+                '../utils'
+            ], function ($, BaseSelection, Utils) {
+                function MultipleSelection($element, options) {
+                    MultipleSelection.__super__.constructor.apply(this, arguments);
+                }
+
+                Utils.Extend(MultipleSelection, BaseSelection);
+
+                MultipleSelection.prototype.render = function () {
+                    var $selection = MultipleSelection.__super__.render.call(this);
+
+                    $selection.addClass('select2-selection--multiple');
+
+                    $selection.html(
+                        '<ul class="select2-selection__rendered"></ul>'
+                    );
+
+                    return $selection;
+                };
+
+                MultipleSelection.prototype.bind = function (container, $container) {
+                    var self = this;
+
+                    MultipleSelection.__super__.bind.apply(this, arguments);
+
+                    this.$selection.on('click', function (evt) {
+                        self.trigger('toggle', {
+                            originalEvent: evt
+                        });
+                    });
+
+                    this.$selection.on(
+                        'click',
+                        '.select2-selection__choice__remove',
+                        function (evt) {
+                            // Ignore the event if it is disabled
+                            if (self.options.get('disabled')) {
+                                return;
+                            }
+
+                            var $remove = $(this);
+                            var $selection = $remove.parent();
+
+                            var data = $selection.data('data');
+
+                            self.trigger('unselect', {
+                                originalEvent: evt,
+                                data: data
+                            });
+                        }
+                    );
+                };
+
+                MultipleSelection.prototype.clear = function () {
+                    this.$selection.find('.select2-selection__rendered').empty();
+                };
+
+                MultipleSelection.prototype.display = function (data, container) {
+                    var template = this.options.get('templateSelection');
+                    var escapeMarkup = this.options.get('escapeMarkup');
+
+                    return escapeMarkup(template(data, container));
+                };
+
+                MultipleSelection.prototype.selectionContainer = function () {
+                    var $container = $(
+                        '<li class="select2-selection__choice">' +
+                        '<span class="select2-selection__choice__remove" role="presentation">' +
+                        '&times;' +
+                        '</span>' +
+                        '</li>'
+                    );
+
+                    return $container;
+                };
+
+                MultipleSelection.prototype.update = function (data) {
+                    this.clear();
+
+                    if (data.length === 0) {
+                        return;
+                    }
+
+                    var $selections = [];
+
+                    for (var d = 0; d < data.length; d++) {
+                        var selection = data[d];
+
+                        var $selection = this.selectionContainer();
+                        var formatted = this.display(selection, $selection);
+
+                        $selection.append(formatted);
+                        $selection.prop('title', selection.title || selection.text);
+
+                        $selection.data('data', selection);
+
+                        $selections.push($selection);
+                    }
+
+                    var $rendered = this.$selection.find('.select2-selection__rendered');
+
+                    Utils.appendMany($rendered, $selections);
+                };
+
+                return MultipleSelection;
+            });
+
+            S2.define('select2/selection/placeholder', [
+                '../utils'
+            ], function (Utils) {
+                function Placeholder(decorated, $element, options) {
+                    this.placeholder = this.normalizePlaceholder(options.get('placeholder'));
+
+                    decorated.call(this, $element, options);
+                }
+
+                Placeholder.prototype.normalizePlaceholder = function (_, placeholder) {
+                    if (typeof placeholder === 'string') {
+                        placeholder = {
+                            id: '',
+                            text: placeholder
+                        };
+                    }
+
+                    return placeholder;
+                };
+
+                Placeholder.prototype.createPlaceholder = function (decorated, placeholder) {
+                    var $placeholder = this.selectionContainer();
+
+                    $placeholder.html(this.display(placeholder));
+                    $placeholder.addClass('select2-selection__placeholder')
+                        .removeClass('select2-selection__choice');
+
+                    return $placeholder;
+                };
+
+                Placeholder.prototype.update = function (decorated, data) {
+                    var singlePlaceholder = (
+                        data.length == 1 && data[0].id != this.placeholder.id
+                    );
+                    var multipleSelections = data.length > 1;
+
+                    if (multipleSelections || singlePlaceholder) {
+                        return decorated.call(this, data);
+                    }
+
+                    this.clear();
+
+                    var $placeholder = this.createPlaceholder(this.placeholder);
+
+                    this.$selection.find('.select2-selection__rendered').append($placeholder);
+                };
+
+                return Placeholder;
+            });
+
+            S2.define('select2/selection/allowClear', [
+                'jquery',
+                '../keys'
+            ], function ($, KEYS) {
+                function AllowClear() {
+                }
+
+                AllowClear.prototype.bind = function (decorated, container, $container) {
+                    var self = this;
+
+                    decorated.call(this, container, $container);
+
+                    if (this.placeholder == null) {
+                        if (this.options.get('debug') && window.console && console.error) {
+                            console.error(
+                                'Select2: The `allowClear` option should be used in combination ' +
+                                'with the `placeholder` option.'
+                            );
+                        }
+                    }
+
+                    this.$selection.on('mousedown', '.select2-selection__clear',
+                        function (evt) {
+                            self._handleClear(evt);
+                        });
+
+                    container.on('keypress', function (evt) {
+                        self._handleKeyboardClear(evt, container);
+                    });
+                };
+
+                AllowClear.prototype._handleClear = function (_, evt) {
+                    // Ignore the event if it is disabled
+                    if (this.options.get('disabled')) {
+                        return;
+                    }
+
+                    var $clear = this.$selection.find('.select2-selection__clear');
+
+                    // Ignore the event if nothing has been selected
+                    if ($clear.length === 0) {
+                        return;
+                    }
+
+                    evt.stopPropagation();
+
+                    var data = $clear.data('data');
+
+                    for (var d = 0; d < data.length; d++) {
+                        var unselectData = {
+                            data: data[d]
+                        };
+
+                        // Trigger the `unselect` event, so people can prevent it from being
+                        // cleared.
+                        this.trigger('unselect', unselectData);
+
+                        // If the event was prevented, don't clear it out.
+                        if (unselectData.prevented) {
+                            return;
+                        }
+                    }
+
+                    this.$element.val(this.placeholder.id).trigger('change');
+
+                    this.trigger('toggle', {});
+                };
+
+                AllowClear.prototype._handleKeyboardClear = function (_, evt, container) {
+                    if (container.isOpen()) {
+                        return;
+                    }
+
+                    if (evt.which == KEYS.DELETE || evt.which == KEYS.BACKSPACE) {
+                        this._handleClear(evt);
+                    }
+                };
+
+                AllowClear.prototype.update = function (decorated, data) {
+                    decorated.call(this, data);
+
+                    if (this.$selection.find('.select2-selection__placeholder').length > 0 ||
+                        data.length === 0) {
+                        return;
+                    }
+
+                    var $remove = $(
+                        '<span class="select2-selection__clear">' +
+                        '&times;' +
+                        '</span>'
+                    );
+                    $remove.data('data', data);
+
+                    this.$selection.find('.select2-selection__rendered').prepend($remove);
+                };
+
+                return AllowClear;
+            });
+
+            S2.define('select2/selection/search', [
+                'jquery',
+                '../utils',
+                '../keys'
+            ], function ($, Utils, KEYS) {
+                function Search(decorated, $element, options) {
+                    decorated.call(this, $element, options);
+                }
+
+                Search.prototype.render = function (decorated) {
+                    var $search = $(
+                        '<li class="select2-search select2-search--inline">' +
+                        '<input class="select2-search__field" type="search" tabindex="-1"' +
+                        ' autocomplete="off" autocorrect="off" autocapitalize="off"' +
+                        ' spellcheck="false" role="textbox" aria-autocomplete="list" />' +
+                        '</li>'
+                    );
+
+                    this.$searchContainer = $search;
+                    this.$search = $search.find('input');
+
+                    var $rendered = decorated.call(this);
+
+                    this._transferTabIndex();
+
+                    return $rendered;
+                };
+
+                Search.prototype.bind = function (decorated, container, $container) {
+                    var self = this;
+
+                    decorated.call(this, container, $container);
+
+                    container.on('open', function () {
+                        self.$search.trigger('focus');
+                    });
+
+                    container.on('close', function () {
+                        self.$search.val('');
+                        self.$search.removeAttr('aria-activedescendant');
+                        self.$search.trigger('focus');
+                    });
+
+                    container.on('enable', function () {
+                        self.$search.prop('disabled', false);
+
+                        self._transferTabIndex();
+                    });
+
+                    container.on('disable', function () {
+                        self.$search.prop('disabled', true);
+                    });
+
+                    container.on('focus', function (evt) {
+                        self.$search.trigger('focus');
+                    });
+
+                    container.on('results:focus', function (params) {
+                        self.$search.attr('aria-activedescendant', params.id);
+                    });
+
+                    this.$selection.on('focusin', '.select2-search--inline', function (evt) {
+                        self.trigger('focus', evt);
+                    });
+
+                    this.$selection.on('focusout', '.select2-search--inline', function (evt) {
+                        self._handleBlur(evt);
+                    });
+
+                    this.$selection.on('keydown', '.select2-search--inline', function (evt) {
+                        evt.stopPropagation();
+
+                        self.trigger('keypress', evt);
+
+                        self._keyUpPrevented = evt.isDefaultPrevented();
+
+                        var key = evt.which;
+
+                        if (key === KEYS.BACKSPACE && self.$search.val() === '') {
+                            var $previousChoice = self.$searchContainer
+                                .prev('.select2-selection__choice');
+
+                            if ($previousChoice.length > 0) {
+                                var item = $previousChoice.data('data');
+
+                                self.searchRemoveChoice(item);
+
+                                evt.preventDefault();
+                            }
+                        }
+                    });
+
+                    // Try to detect the IE version should the `documentMode` property that
+                    // is stored on the document. This is only implemented in IE and is
+                    // slightly cleaner than doing a user agent check.
+                    // This property is not available in Edge, but Edge also doesn't have
+                    // this bug.
+                    var msie = document.documentMode;
+                    var disableInputEvents = msie && msie <= 11;
+
+                    // Workaround for browsers which do not support the `input` event
+                    // This will prevent double-triggering of events for browsers which support
+                    // both the `keyup` and `input` events.
+                    this.$selection.on(
+                        'input.searchcheck',
+                        '.select2-search--inline',
+                        function (evt) {
+                            // IE will trigger the `input` event when a placeholder is used on a
+                            // search box. To get around this issue, we are forced to ignore all
+                            // `input` events in IE and keep using `keyup`.
+                            if (disableInputEvents) {
+                                self.$selection.off('input.search input.searchcheck');
+                                return;
+                            }
+
+                            // Unbind the duplicated `keyup` event
+                            self.$selection.off('keyup.search');
+                        }
+                    );
+
+                    this.$selection.on(
+                        'keyup.search input.search',
+                        '.select2-search--inline',
+                        function (evt) {
+                            // IE will trigger the `input` event when a placeholder is used on a
+                            // search box. To get around this issue, we are forced to ignore all
+                            // `input` events in IE and keep using `keyup`.
+                            if (disableInputEvents && evt.type === 'input') {
+                                self.$selection.off('input.search input.searchcheck');
+                                return;
+                            }
+
+                            var key = evt.which;
+
+                            // We can freely ignore events from modifier keys
+                            if (key == KEYS.SHIFT || key == KEYS.CTRL || key == KEYS.ALT) {
+                                return;
+                            }
+
+                            // Tabbing will be handled during the `keydown` phase
+                            if (key == KEYS.TAB) {
+                                return;
+                            }
+
+                            self.handleSearch(evt);
+                        }
+                    );
+                };
+
+                /**
+                 * This method will transfer the tabindex attribute from the rendered
+                 * selection to the search box. This allows for the search box to be used as
+                 * the primary focus instead of the selection container.
+                 *
+                 * @private
+                 */
+                Search.prototype._transferTabIndex = function (decorated) {
+                    this.$search.attr('tabindex', this.$selection.attr('tabindex'));
+                    this.$selection.attr('tabindex', '-1');
+                };
+
+                Search.prototype.createPlaceholder = function (decorated, placeholder) {
+                    this.$search.attr('placeholder', placeholder.text);
+                };
+
+                Search.prototype.update = function (decorated, data) {
+                    var searchHadFocus = this.$search[0] == document.activeElement;
+
+                    this.$search.attr('placeholder', '');
+
+                    decorated.call(this, data);
+
+                    this.$selection.find('.select2-selection__rendered')
+                        .append(this.$searchContainer);
+
+                    this.resizeSearch();
+                    if (searchHadFocus) {
+                        this.$search.focus();
+                    }
+                };
+
+                Search.prototype.handleSearch = function () {
+                    this.resizeSearch();
+
+                    if (!this._keyUpPrevented) {
+                        var input = this.$search.val();
+
+                        this.trigger('query', {
+                            term: input
+                        });
+                    }
+
+                    this._keyUpPrevented = false;
+                };
+
+                Search.prototype.searchRemoveChoice = function (decorated, item) {
+                    this.trigger('unselect', {
+                        data: item
+                    });
+
+                    this.$search.val(item.text);
+                    this.handleSearch();
+                };
+
+                Search.prototype.resizeSearch = function () {
+                    this.$search.css('width', '25px');
+
+                    var width = '';
+
+                    if (this.$search.attr('placeholder') !== '') {
+                        width = this.$selection.find('.select2-selection__rendered').innerWidth();
+                    } else {
+                        var minimumWidth = this.$search.val().length + 1;
+
+                        width = (minimumWidth * 0.75) + 'em';
+                    }
+
+                    this.$search.css('width', width);
+                };
+
+                return Search;
+            });
+
+            S2.define('select2/selection/eventRelay', [
+                'jquery'
+            ], function ($) {
+                function EventRelay() {
+                }
+
+                EventRelay.prototype.bind = function (decorated, container, $container) {
+                    var self = this;
+                    var relayEvents = [
+                        'open', 'opening',
+                        'close', 'closing',
+                        'select', 'selecting',
+                        'unselect', 'unselecting'
+                    ];
+
+                    var preventableEvents = ['opening', 'closing', 'selecting', 'unselecting'];
+
+                    decorated.call(this, container, $container);
+
+                    container.on('*', function (name, params) {
+                        // Ignore events that should not be relayed
+                        if ($.inArray(name, relayEvents) === -1) {
+                            return;
+                        }
+
+                        // The parameters should always be an object
+                        params = params || {};
+
+                        // Generate the jQuery event for the Select2 event
+                        var evt = $.Event('select2:' + name, {
+                            params: params
+                        });
+
+                        self.$element.trigger(evt);
+
+                        // Only handle preventable events if it was one
+                        if ($.inArray(name, preventableEvents) === -1) {
+                            return;
+                        }
+
+                        params.prevented = evt.isDefaultPrevented();
+                    });
+                };
+
+                return EventRelay;
+            });
+
+            S2.define('select2/translation', [
+                'jquery',
+                'require'
+            ], function ($, require) {
+                function Translation(dict) {
+                    this.dict = dict || {};
+                }
+
+                Translation.prototype.all = function () {
+                    return this.dict;
+                };
+
+                Translation.prototype.get = function (key) {
+                    return this.dict[key];
+                };
+
+                Translation.prototype.extend = function (translation) {
+                    this.dict = $.extend({}, translation.all(), this.dict);
+                };
+
+                // Static functions
+
+                Translation._cache = {};
+
+                Translation.loadPath = function (path) {
+                    if (!(path in Translation._cache)) {
+                        var translations = require(path);
+
+                        Translation._cache[path] = translations;
+                    }
+
+                    return new Translation(Translation._cache[path]);
+                };
+
+                return Translation;
+            });
+
+            S2.define('select2/diacritics', [], function () {
+                var diacritics = {
+                    '\u24B6': 'A',
+                    '\uFF21': 'A',
+                    '\u00C0': 'A',
+                    '\u00C1': 'A',
+                    '\u00C2': 'A',
+                    '\u1EA6': 'A',
+                    '\u1EA4': 'A',
+                    '\u1EAA': 'A',
+                    '\u1EA8': 'A',
+                    '\u00C3': 'A',
+                    '\u0100': 'A',
+                    '\u0102': 'A',
+                    '\u1EB0': 'A',
+                    '\u1EAE': 'A',
+                    '\u1EB4': 'A',
+                    '\u1EB2': 'A',
+                    '\u0226': 'A',
+                    '\u01E0': 'A',
+                    '\u00C4': 'A',
+                    '\u01DE': 'A',
+                    '\u1EA2': 'A',
+                    '\u00C5': 'A',
+                    '\u01FA': 'A',
+                    '\u01CD': 'A',
+                    '\u0200': 'A',
+                    '\u0202': 'A',
+                    '\u1EA0': 'A',
+                    '\u1EAC': 'A',
+                    '\u1EB6': 'A',
+                    '\u1E00': 'A',
+                    '\u0104': 'A',
+                    '\u023A': 'A',
+                    '\u2C6F': 'A',
+                    '\uA732': 'AA',
+                    '\u00C6': 'AE',
+                    '\u01FC': 'AE',
+                    '\u01E2': 'AE',
+                    '\uA734': 'AO',
+                    '\uA736': 'AU',
+                    '\uA738': 'AV',
+                    '\uA73A': 'AV',
+                    '\uA73C': 'AY',
+                    '\u24B7': 'B',
+                    '\uFF22': 'B',
+                    '\u1E02': 'B',
+                    '\u1E04': 'B',
+                    '\u1E06': 'B',
+                    '\u0243': 'B',
+                    '\u0182': 'B',
+                    '\u0181': 'B',
+                    '\u24B8': 'C',
+                    '\uFF23': 'C',
+                    '\u0106': 'C',
+                    '\u0108': 'C',
+                    '\u010A': 'C',
+                    '\u010C': 'C',
+                    '\u00C7': 'C',
+                    '\u1E08': 'C',
+                    '\u0187': 'C',
+                    '\u023B': 'C',
+                    '\uA73E': 'C',
+                    '\u24B9': 'D',
+                    '\uFF24': 'D',
+                    '\u1E0A': 'D',
+                    '\u010E': 'D',
+                    '\u1E0C': 'D',
+                    '\u1E10': 'D',
+                    '\u1E12': 'D',
+                    '\u1E0E': 'D',
+                    '\u0110': 'D',
+                    '\u018B': 'D',
+                    '\u018A': 'D',
+                    '\u0189': 'D',
+                    '\uA779': 'D',
+                    '\u01F1': 'DZ',
+                    '\u01C4': 'DZ',
+                    '\u01F2': 'Dz',
+                    '\u01C5': 'Dz',
+                    '\u24BA': 'E',
+                    '\uFF25': 'E',
+                    '\u00C8': 'E',
+                    '\u00C9': 'E',
+                    '\u00CA': 'E',
+                    '\u1EC0': 'E',
+                    '\u1EBE': 'E',
+                    '\u1EC4': 'E',
+                    '\u1EC2': 'E',
+                    '\u1EBC': 'E',
+                    '\u0112': 'E',
+                    '\u1E14': 'E',
+                    '\u1E16': 'E',
+                    '\u0114': 'E',
+                    '\u0116': 'E',
+                    '\u00CB': 'E',
+                    '\u1EBA': 'E',
+                    '\u011A': 'E',
+                    '\u0204': 'E',
+                    '\u0206': 'E',
+                    '\u1EB8': 'E',
+                    '\u1EC6': 'E',
+                    '\u0228': 'E',
+                    '\u1E1C': 'E',
+                    '\u0118': 'E',
+                    '\u1E18': 'E',
+                    '\u1E1A': 'E',
+                    '\u0190': 'E',
+                    '\u018E': 'E',
+                    '\u24BB': 'F',
+                    '\uFF26': 'F',
+                    '\u1E1E': 'F',
+                    '\u0191': 'F',
+                    '\uA77B': 'F',
+                    '\u24BC': 'G',
+                    '\uFF27': 'G',
+                    '\u01F4': 'G',
+                    '\u011C': 'G',
+                    '\u1E20': 'G',
+                    '\u011E': 'G',
+                    '\u0120': 'G',
+                    '\u01E6': 'G',
+                    '\u0122': 'G',
+                    '\u01E4': 'G',
+                    '\u0193': 'G',
+                    '\uA7A0': 'G',
+                    '\uA77D': 'G',
+                    '\uA77E': 'G',
+                    '\u24BD': 'H',
+                    '\uFF28': 'H',
+                    '\u0124': 'H',
+                    '\u1E22': 'H',
+                    '\u1E26': 'H',
+                    '\u021E': 'H',
+                    '\u1E24': 'H',
+                    '\u1E28': 'H',
+                    '\u1E2A': 'H',
+                    '\u0126': 'H',
+                    '\u2C67': 'H',
+                    '\u2C75': 'H',
+                    '\uA78D': 'H',
+                    '\u24BE': 'I',
+                    '\uFF29': 'I',
+                    '\u00CC': 'I',
+                    '\u00CD': 'I',
+                    '\u00CE': 'I',
+                    '\u0128': 'I',
+                    '\u012A': 'I',
+                    '\u012C': 'I',
+                    '\u0130': 'I',
+                    '\u00CF': 'I',
+                    '\u1E2E': 'I',
+                    '\u1EC8': 'I',
+                    '\u01CF': 'I',
+                    '\u0208': 'I',
+                    '\u020A': 'I',
+                    '\u1ECA': 'I',
+                    '\u012E': 'I',
+                    '\u1E2C': 'I',
+                    '\u0197': 'I',
+                    '\u24BF': 'J',
+                    '\uFF2A': 'J',
+                    '\u0134': 'J',
+                    '\u0248': 'J',
+                    '\u24C0': 'K',
+                    '\uFF2B': 'K',
+                    '\u1E30': 'K',
+                    '\u01E8': 'K',
+                    '\u1E32': 'K',
+                    '\u0136': 'K',
+                    '\u1E34': 'K',
+                    '\u0198': 'K',
+                    '\u2C69': 'K',
+                    '\uA740': 'K',
+                    '\uA742': 'K',
+                    '\uA744': 'K',
+                    '\uA7A2': 'K',
+                    '\u24C1': 'L',
+                    '\uFF2C': 'L',
+                    '\u013F': 'L',
+                    '\u0139': 'L',
+                    '\u013D': 'L',
+                    '\u1E36': 'L',
+                    '\u1E38': 'L',
+                    '\u013B': 'L',
+                    '\u1E3C': 'L',
+                    '\u1E3A': 'L',
+                    '\u0141': 'L',
+                    '\u023D': 'L',
+                    '\u2C62': 'L',
+                    '\u2C60': 'L',
+                    '\uA748': 'L',
+                    '\uA746': 'L',
+                    '\uA780': 'L',
+                    '\u01C7': 'LJ',
+                    '\u01C8': 'Lj',
+                    '\u24C2': 'M',
+                    '\uFF2D': 'M',
+                    '\u1E3E': 'M',
+                    '\u1E40': 'M',
+                    '\u1E42': 'M',
+                    '\u2C6E': 'M',
+                    '\u019C': 'M',
+                    '\u24C3': 'N',
+                    '\uFF2E': 'N',
+                    '\u01F8': 'N',
+                    '\u0143': 'N',
+                    '\u00D1': 'N',
+                    '\u1E44': 'N',
+                    '\u0147': 'N',
+                    '\u1E46': 'N',
+                    '\u0145': 'N',
+                    '\u1E4A': 'N',
+                    '\u1E48': 'N',
+                    '\u0220': 'N',
+                    '\u019D': 'N',
+                    '\uA790': 'N',
+                    '\uA7A4': 'N',
+                    '\u01CA': 'NJ',
+                    '\u01CB': 'Nj',
+                    '\u24C4': 'O',
+                    '\uFF2F': 'O',
+                    '\u00D2': 'O',
+                    '\u00D3': 'O',
+                    '\u00D4': 'O',
+                    '\u1ED2': 'O',
+                    '\u1ED0': 'O',
+                    '\u1ED6': 'O',
+                    '\u1ED4': 'O',
+                    '\u00D5': 'O',
+                    '\u1E4C': 'O',
+                    '\u022C': 'O',
+                    '\u1E4E': 'O',
+                    '\u014C': 'O',
+                    '\u1E50': 'O',
+                    '\u1E52': 'O',
+                    '\u014E': 'O',
+                    '\u022E': 'O',
+                    '\u0230': 'O',
+                    '\u00D6': 'O',
+                    '\u022A': 'O',
+                    '\u1ECE': 'O',
+                    '\u0150': 'O',
+                    '\u01D1': 'O',
+                    '\u020C': 'O',
+                    '\u020E': 'O',
+                    '\u01A0': 'O',
+                    '\u1EDC': 'O',
+                    '\u1EDA': 'O',
+                    '\u1EE0': 'O',
+                    '\u1EDE': 'O',
+                    '\u1EE2': 'O',
+                    '\u1ECC': 'O',
+                    '\u1ED8': 'O',
+                    '\u01EA': 'O',
+                    '\u01EC': 'O',
+                    '\u00D8': 'O',
+                    '\u01FE': 'O',
+                    '\u0186': 'O',
+                    '\u019F': 'O',
+                    '\uA74A': 'O',
+                    '\uA74C': 'O',
+                    '\u01A2': 'OI',
+                    '\uA74E': 'OO',
+                    '\u0222': 'OU',
+                    '\u24C5': 'P',
+                    '\uFF30': 'P',
+                    '\u1E54': 'P',
+                    '\u1E56': 'P',
+                    '\u01A4': 'P',
+                    '\u2C63': 'P',
+                    '\uA750': 'P',
+                    '\uA752': 'P',
+                    '\uA754': 'P',
+                    '\u24C6': 'Q',
+                    '\uFF31': 'Q',
+                    '\uA756': 'Q',
+                    '\uA758': 'Q',
+                    '\u024A': 'Q',
+                    '\u24C7': 'R',
+                    '\uFF32': 'R',
+                    '\u0154': 'R',
+                    '\u1E58': 'R',
+                    '\u0158': 'R',
+                    '\u0210': 'R',
+                    '\u0212': 'R',
+                    '\u1E5A': 'R',
+                    '\u1E5C': 'R',
+                    '\u0156': 'R',
+                    '\u1E5E': 'R',
+                    '\u024C': 'R',
+                    '\u2C64': 'R',
+                    '\uA75A': 'R',
+                    '\uA7A6': 'R',
+                    '\uA782': 'R',
+                    '\u24C8': 'S',
+                    '\uFF33': 'S',
+                    '\u1E9E': 'S',
+                    '\u015A': 'S',
+                    '\u1E64': 'S',
+                    '\u015C': 'S',
+                    '\u1E60': 'S',
+                    '\u0160': 'S',
+                    '\u1E66': 'S',
+                    '\u1E62': 'S',
+                    '\u1E68': 'S',
+                    '\u0218': 'S',
+                    '\u015E': 'S',
+                    '\u2C7E': 'S',
+                    '\uA7A8': 'S',
+                    '\uA784': 'S',
+                    '\u24C9': 'T',
+                    '\uFF34': 'T',
+                    '\u1E6A': 'T',
+                    '\u0164': 'T',
+                    '\u1E6C': 'T',
+                    '\u021A': 'T',
+                    '\u0162': 'T',
+                    '\u1E70': 'T',
+                    '\u1E6E': 'T',
+                    '\u0166': 'T',
+                    '\u01AC': 'T',
+                    '\u01AE': 'T',
+                    '\u023E': 'T',
+                    '\uA786': 'T',
+                    '\uA728': 'TZ',
+                    '\u24CA': 'U',
+                    '\uFF35': 'U',
+                    '\u00D9': 'U',
+                    '\u00DA': 'U',
+                    '\u00DB': 'U',
+                    '\u0168': 'U',
+                    '\u1E78': 'U',
+                    '\u016A': 'U',
+                    '\u1E7A': 'U',
+                    '\u016C': 'U',
+                    '\u00DC': 'U',
+                    '\u01DB': 'U',
+                    '\u01D7': 'U',
+                    '\u01D5': 'U',
+                    '\u01D9': 'U',
+                    '\u1EE6': 'U',
+                    '\u016E': 'U',
+                    '\u0170': 'U',
+                    '\u01D3': 'U',
+                    '\u0214': 'U',
+                    '\u0216': 'U',
+                    '\u01AF': 'U',
+                    '\u1EEA': 'U',
+                    '\u1EE8': 'U',
+                    '\u1EEE': 'U',
+                    '\u1EEC': 'U',
+                    '\u1EF0': 'U',
+                    '\u1EE4': 'U',
+                    '\u1E72': 'U',
+                    '\u0172': 'U',
+                    '\u1E76': 'U',
+                    '\u1E74': 'U',
+                    '\u0244': 'U',
+                    '\u24CB': 'V',
+                    '\uFF36': 'V',
+                    '\u1E7C': 'V',
+                    '\u1E7E': 'V',
+                    '\u01B2': 'V',
+                    '\uA75E': 'V',
+                    '\u0245': 'V',
+                    '\uA760': 'VY',
+                    '\u24CC': 'W',
+                    '\uFF37': 'W',
+                    '\u1E80': 'W',
+                    '\u1E82': 'W',
+                    '\u0174': 'W',
+                    '\u1E86': 'W',
+                    '\u1E84': 'W',
+                    '\u1E88': 'W',
+                    '\u2C72': 'W',
+                    '\u24CD': 'X',
+                    '\uFF38': 'X',
+                    '\u1E8A': 'X',
+                    '\u1E8C': 'X',
+                    '\u24CE': 'Y',
+                    '\uFF39': 'Y',
+                    '\u1EF2': 'Y',
+                    '\u00DD': 'Y',
+                    '\u0176': 'Y',
+                    '\u1EF8': 'Y',
+                    '\u0232': 'Y',
+                    '\u1E8E': 'Y',
+                    '\u0178': 'Y',
+                    '\u1EF6': 'Y',
+                    '\u1EF4': 'Y',
+                    '\u01B3': 'Y',
+                    '\u024E': 'Y',
+                    '\u1EFE': 'Y',
+                    '\u24CF': 'Z',
+                    '\uFF3A': 'Z',
+                    '\u0179': 'Z',
+                    '\u1E90': 'Z',
+                    '\u017B': 'Z',
+                    '\u017D': 'Z',
+                    '\u1E92': 'Z',
+                    '\u1E94': 'Z',
+                    '\u01B5': 'Z',
+                    '\u0224': 'Z',
+                    '\u2C7F': 'Z',
+                    '\u2C6B': 'Z',
+                    '\uA762': 'Z',
+                    '\u24D0': 'a',
+                    '\uFF41': 'a',
+                    '\u1E9A': 'a',
+                    '\u00E0': 'a',
+                    '\u00E1': 'a',
+                    '\u00E2': 'a',
+                    '\u1EA7': 'a',
+                    '\u1EA5': 'a',
+                    '\u1EAB': 'a',
+                    '\u1EA9': 'a',
+                    '\u00E3': 'a',
+                    '\u0101': 'a',
+                    '\u0103': 'a',
+                    '\u1EB1': 'a',
+                    '\u1EAF': 'a',
+                    '\u1EB5': 'a',
+                    '\u1EB3': 'a',
+                    '\u0227': 'a',
+                    '\u01E1': 'a',
+                    '\u00E4': 'a',
+                    '\u01DF': 'a',
+                    '\u1EA3': 'a',
+                    '\u00E5': 'a',
+                    '\u01FB': 'a',
+                    '\u01CE': 'a',
+                    '\u0201': 'a',
+                    '\u0203': 'a',
+                    '\u1EA1': 'a',
+                    '\u1EAD': 'a',
+                    '\u1EB7': 'a',
+                    '\u1E01': 'a',
+                    '\u0105': 'a',
+                    '\u2C65': 'a',
+                    '\u0250': 'a',
+                    '\uA733': 'aa',
+                    '\u00E6': 'ae',
+                    '\u01FD': 'ae',
+                    '\u01E3': 'ae',
+                    '\uA735': 'ao',
+                    '\uA737': 'au',
+                    '\uA739': 'av',
+                    '\uA73B': 'av',
+                    '\uA73D': 'ay',
+                    '\u24D1': 'b',
+                    '\uFF42': 'b',
+                    '\u1E03': 'b',
+                    '\u1E05': 'b',
+                    '\u1E07': 'b',
+                    '\u0180': 'b',
+                    '\u0183': 'b',
+                    '\u0253': 'b',
+                    '\u24D2': 'c',
+                    '\uFF43': 'c',
+                    '\u0107': 'c',
+                    '\u0109': 'c',
+                    '\u010B': 'c',
+                    '\u010D': 'c',
+                    '\u00E7': 'c',
+                    '\u1E09': 'c',
+                    '\u0188': 'c',
+                    '\u023C': 'c',
+                    '\uA73F': 'c',
+                    '\u2184': 'c',
+                    '\u24D3': 'd',
+                    '\uFF44': 'd',
+                    '\u1E0B': 'd',
+                    '\u010F': 'd',
+                    '\u1E0D': 'd',
+                    '\u1E11': 'd',
+                    '\u1E13': 'd',
+                    '\u1E0F': 'd',
+                    '\u0111': 'd',
+                    '\u018C': 'd',
+                    '\u0256': 'd',
+                    '\u0257': 'd',
+                    '\uA77A': 'd',
+                    '\u01F3': 'dz',
+                    '\u01C6': 'dz',
+                    '\u24D4': 'e',
+                    '\uFF45': 'e',
+                    '\u00E8': 'e',
+                    '\u00E9': 'e',
+                    '\u00EA': 'e',
+                    '\u1EC1': 'e',
+                    '\u1EBF': 'e',
+                    '\u1EC5': 'e',
+                    '\u1EC3': 'e',
+                    '\u1EBD': 'e',
+                    '\u0113': 'e',
+                    '\u1E15': 'e',
+                    '\u1E17': 'e',
+                    '\u0115': 'e',
+                    '\u0117': 'e',
+                    '\u00EB': 'e',
+                    '\u1EBB': 'e',
+                    '\u011B': 'e',
+                    '\u0205': 'e',
+                    '\u0207': 'e',
+                    '\u1EB9': 'e',
+                    '\u1EC7': 'e',
+                    '\u0229': 'e',
+                    '\u1E1D': 'e',
+                    '\u0119': 'e',
+                    '\u1E19': 'e',
+                    '\u1E1B': 'e',
+                    '\u0247': 'e',
+                    '\u025B': 'e',
+                    '\u01DD': 'e',
+                    '\u24D5': 'f',
+                    '\uFF46': 'f',
+                    '\u1E1F': 'f',
+                    '\u0192': 'f',
+                    '\uA77C': 'f',
+                    '\u24D6': 'g',
+                    '\uFF47': 'g',
+                    '\u01F5': 'g',
+                    '\u011D': 'g',
+                    '\u1E21': 'g',
+                    '\u011F': 'g',
+                    '\u0121': 'g',
+                    '\u01E7': 'g',
+                    '\u0123': 'g',
+                    '\u01E5': 'g',
+                    '\u0260': 'g',
+                    '\uA7A1': 'g',
+                    '\u1D79': 'g',
+                    '\uA77F': 'g',
+                    '\u24D7': 'h',
+                    '\uFF48': 'h',
+                    '\u0125': 'h',
+                    '\u1E23': 'h',
+                    '\u1E27': 'h',
+                    '\u021F': 'h',
+                    '\u1E25': 'h',
+                    '\u1E29': 'h',
+                    '\u1E2B': 'h',
+                    '\u1E96': 'h',
+                    '\u0127': 'h',
+                    '\u2C68': 'h',
+                    '\u2C76': 'h',
+                    '\u0265': 'h',
+                    '\u0195': 'hv',
+                    '\u24D8': 'i',
+                    '\uFF49': 'i',
+                    '\u00EC': 'i',
+                    '\u00ED': 'i',
+                    '\u00EE': 'i',
+                    '\u0129': 'i',
+                    '\u012B': 'i',
+                    '\u012D': 'i',
+                    '\u00EF': 'i',
+                    '\u1E2F': 'i',
+                    '\u1EC9': 'i',
+                    '\u01D0': 'i',
+                    '\u0209': 'i',
+                    '\u020B': 'i',
+                    '\u1ECB': 'i',
+                    '\u012F': 'i',
+                    '\u1E2D': 'i',
+                    '\u0268': 'i',
+                    '\u0131': 'i',
+                    '\u24D9': 'j',
+                    '\uFF4A': 'j',
+                    '\u0135': 'j',
+                    '\u01F0': 'j',
+                    '\u0249': 'j',
+                    '\u24DA': 'k',
+                    '\uFF4B': 'k',
+                    '\u1E31': 'k',
+                    '\u01E9': 'k',
+                    '\u1E33': 'k',
+                    '\u0137': 'k',
+                    '\u1E35': 'k',
+                    '\u0199': 'k',
+                    '\u2C6A': 'k',
+                    '\uA741': 'k',
+                    '\uA743': 'k',
+                    '\uA745': 'k',
+                    '\uA7A3': 'k',
+                    '\u24DB': 'l',
+                    '\uFF4C': 'l',
+                    '\u0140': 'l',
+                    '\u013A': 'l',
+                    '\u013E': 'l',
+                    '\u1E37': 'l',
+                    '\u1E39': 'l',
+                    '\u013C': 'l',
+                    '\u1E3D': 'l',
+                    '\u1E3B': 'l',
+                    '\u017F': 'l',
+                    '\u0142': 'l',
+                    '\u019A': 'l',
+                    '\u026B': 'l',
+                    '\u2C61': 'l',
+                    '\uA749': 'l',
+                    '\uA781': 'l',
+                    '\uA747': 'l',
+                    '\u01C9': 'lj',
+                    '\u24DC': 'm',
+                    '\uFF4D': 'm',
+                    '\u1E3F': 'm',
+                    '\u1E41': 'm',
+                    '\u1E43': 'm',
+                    '\u0271': 'm',
+                    '\u026F': 'm',
+                    '\u24DD': 'n',
+                    '\uFF4E': 'n',
+                    '\u01F9': 'n',
+                    '\u0144': 'n',
+                    '\u00F1': 'n',
+                    '\u1E45': 'n',
+                    '\u0148': 'n',
+                    '\u1E47': 'n',
+                    '\u0146': 'n',
+                    '\u1E4B': 'n',
+                    '\u1E49': 'n',
+                    '\u019E': 'n',
+                    '\u0272': 'n',
+                    '\u0149': 'n',
+                    '\uA791': 'n',
+                    '\uA7A5': 'n',
+                    '\u01CC': 'nj',
+                    '\u24DE': 'o',
+                    '\uFF4F': 'o',
+                    '\u00F2': 'o',
+                    '\u00F3': 'o',
+                    '\u00F4': 'o',
+                    '\u1ED3': 'o',
+                    '\u1ED1': 'o',
+                    '\u1ED7': 'o',
+                    '\u1ED5': 'o',
+                    '\u00F5': 'o',
+                    '\u1E4D': 'o',
+                    '\u022D': 'o',
+                    '\u1E4F': 'o',
+                    '\u014D': 'o',
+                    '\u1E51': 'o',
+                    '\u1E53': 'o',
+                    '\u014F': 'o',
+                    '\u022F': 'o',
+                    '\u0231': 'o',
+                    '\u00F6': 'o',
+                    '\u022B': 'o',
+                    '\u1ECF': 'o',
+                    '\u0151': 'o',
+                    '\u01D2': 'o',
+                    '\u020D': 'o',
+                    '\u020F': 'o',
+                    '\u01A1': 'o',
+                    '\u1EDD': 'o',
+                    '\u1EDB': 'o',
+                    '\u1EE1': 'o',
+                    '\u1EDF': 'o',
+                    '\u1EE3': 'o',
+                    '\u1ECD': 'o',
+                    '\u1ED9': 'o',
+                    '\u01EB': 'o',
+                    '\u01ED': 'o',
+                    '\u00F8': 'o',
+                    '\u01FF': 'o',
+                    '\u0254': 'o',
+                    '\uA74B': 'o',
+                    '\uA74D': 'o',
+                    '\u0275': 'o',
+                    '\u01A3': 'oi',
+                    '\u0223': 'ou',
+                    '\uA74F': 'oo',
+                    '\u24DF': 'p',
+                    '\uFF50': 'p',
+                    '\u1E55': 'p',
+                    '\u1E57': 'p',
+                    '\u01A5': 'p',
+                    '\u1D7D': 'p',
+                    '\uA751': 'p',
+                    '\uA753': 'p',
+                    '\uA755': 'p',
+                    '\u24E0': 'q',
+                    '\uFF51': 'q',
+                    '\u024B': 'q',
+                    '\uA757': 'q',
+                    '\uA759': 'q',
+                    '\u24E1': 'r',
+                    '\uFF52': 'r',
+                    '\u0155': 'r',
+                    '\u1E59': 'r',
+                    '\u0159': 'r',
+                    '\u0211': 'r',
+                    '\u0213': 'r',
+                    '\u1E5B': 'r',
+                    '\u1E5D': 'r',
+                    '\u0157': 'r',
+                    '\u1E5F': 'r',
+                    '\u024D': 'r',
+                    '\u027D': 'r',
+                    '\uA75B': 'r',
+                    '\uA7A7': 'r',
+                    '\uA783': 'r',
+                    '\u24E2': 's',
+                    '\uFF53': 's',
+                    '\u00DF': 's',
+                    '\u015B': 's',
+                    '\u1E65': 's',
+                    '\u015D': 's',
+                    '\u1E61': 's',
+                    '\u0161': 's',
+                    '\u1E67': 's',
+                    '\u1E63': 's',
+                    '\u1E69': 's',
+                    '\u0219': 's',
+                    '\u015F': 's',
+                    '\u023F': 's',
+                    '\uA7A9': 's',
+                    '\uA785': 's',
+                    '\u1E9B': 's',
+                    '\u24E3': 't',
+                    '\uFF54': 't',
+                    '\u1E6B': 't',
+                    '\u1E97': 't',
+                    '\u0165': 't',
+                    '\u1E6D': 't',
+                    '\u021B': 't',
+                    '\u0163': 't',
+                    '\u1E71': 't',
+                    '\u1E6F': 't',
+                    '\u0167': 't',
+                    '\u01AD': 't',
+                    '\u0288': 't',
+                    '\u2C66': 't',
+                    '\uA787': 't',
+                    '\uA729': 'tz',
+                    '\u24E4': 'u',
+                    '\uFF55': 'u',
+                    '\u00F9': 'u',
+                    '\u00FA': 'u',
+                    '\u00FB': 'u',
+                    '\u0169': 'u',
+                    '\u1E79': 'u',
+                    '\u016B': 'u',
+                    '\u1E7B': 'u',
+                    '\u016D': 'u',
+                    '\u00FC': 'u',
+                    '\u01DC': 'u',
+                    '\u01D8': 'u',
+                    '\u01D6': 'u',
+                    '\u01DA': 'u',
+                    '\u1EE7': 'u',
+                    '\u016F': 'u',
+                    '\u0171': 'u',
+                    '\u01D4': 'u',
+                    '\u0215': 'u',
+                    '\u0217': 'u',
+                    '\u01B0': 'u',
+                    '\u1EEB': 'u',
+                    '\u1EE9': 'u',
+                    '\u1EEF': 'u',
+                    '\u1EED': 'u',
+                    '\u1EF1': 'u',
+                    '\u1EE5': 'u',
+                    '\u1E73': 'u',
+                    '\u0173': 'u',
+                    '\u1E77': 'u',
+                    '\u1E75': 'u',
+                    '\u0289': 'u',
+                    '\u24E5': 'v',
+                    '\uFF56': 'v',
+                    '\u1E7D': 'v',
+                    '\u1E7F': 'v',
+                    '\u028B': 'v',
+                    '\uA75F': 'v',
+                    '\u028C': 'v',
+                    '\uA761': 'vy',
+                    '\u24E6': 'w',
+                    '\uFF57': 'w',
+                    '\u1E81': 'w',
+                    '\u1E83': 'w',
+                    '\u0175': 'w',
+                    '\u1E87': 'w',
+                    '\u1E85': 'w',
+                    '\u1E98': 'w',
+                    '\u1E89': 'w',
+                    '\u2C73': 'w',
+                    '\u24E7': 'x',
+                    '\uFF58': 'x',
+                    '\u1E8B': 'x',
+                    '\u1E8D': 'x',
+                    '\u24E8': 'y',
+                    '\uFF59': 'y',
+                    '\u1EF3': 'y',
+                    '\u00FD': 'y',
+                    '\u0177': 'y',
+                    '\u1EF9': 'y',
+                    '\u0233': 'y',
+                    '\u1E8F': 'y',
+                    '\u00FF': 'y',
+                    '\u1EF7': 'y',
+                    '\u1E99': 'y',
+                    '\u1EF5': 'y',
+                    '\u01B4': 'y',
+                    '\u024F': 'y',
+                    '\u1EFF': 'y',
+                    '\u24E9': 'z',
+                    '\uFF5A': 'z',
+                    '\u017A': 'z',
+                    '\u1E91': 'z',
+                    '\u017C': 'z',
+                    '\u017E': 'z',
+                    '\u1E93': 'z',
+                    '\u1E95': 'z',
+                    '\u01B6': 'z',
+                    '\u0225': 'z',
+                    '\u0240': 'z',
+                    '\u2C6C': 'z',
+                    '\uA763': 'z',
+                    '\u0386': '\u0391',
+                    '\u0388': '\u0395',
+                    '\u0389': '\u0397',
+                    '\u038A': '\u0399',
+                    '\u03AA': '\u0399',
+                    '\u038C': '\u039F',
+                    '\u038E': '\u03A5',
+                    '\u03AB': '\u03A5',
+                    '\u038F': '\u03A9',
+                    '\u03AC': '\u03B1',
+                    '\u03AD': '\u03B5',
+                    '\u03AE': '\u03B7',
+                    '\u03AF': '\u03B9',
+                    '\u03CA': '\u03B9',
+                    '\u0390': '\u03B9',
+                    '\u03CC': '\u03BF',
+                    '\u03CD': '\u03C5',
+                    '\u03CB': '\u03C5',
+                    '\u03B0': '\u03C5',
+                    '\u03C9': '\u03C9',
+                    '\u03C2': '\u03C3'
+                };
+
+                return diacritics;
+            });
+
+            S2.define('select2/data/base', [
+                '../utils'
+            ], function (Utils) {
+                function BaseAdapter($element, options) {
+                    BaseAdapter.__super__.constructor.call(this);
+                }
+
+                Utils.Extend(BaseAdapter, Utils.Observable);
+
+                BaseAdapter.prototype.current = function (callback) {
+                    throw new Error('The `current` method must be defined in child classes.');
+                };
+
+                BaseAdapter.prototype.query = function (params, callback) {
+                    throw new Error('The `query` method must be defined in child classes.');
+                };
+
+                BaseAdapter.prototype.bind = function (container, $container) {
+                    // Can be implemented in subclasses
+                };
+
+                BaseAdapter.prototype.destroy = function () {
+                    // Can be implemented in subclasses
+                };
+
+                BaseAdapter.prototype.generateResultId = function (container, data) {
+                    var id = container.id + '-result-';
+
+                    id += Utils.generateChars(4);
+
+                    if (data.id != null) {
+                        id += '-' + data.id.toString();
+                    } else {
+                        id += '-' + Utils.generateChars(4);
+                    }
+                    return id;
+                };
+
+                return BaseAdapter;
+            });
+
+            S2.define('select2/data/select', [
+                './base',
+                '../utils',
+                'jquery'
+            ], function (BaseAdapter, Utils, $) {
+                function SelectAdapter($element, options) {
+                    this.$element = $element;
+                    this.options = options;
+
+                    SelectAdapter.__super__.constructor.call(this);
+                }
+
+                Utils.Extend(SelectAdapter, BaseAdapter);
+
+                SelectAdapter.prototype.current = function (callback) {
+                    var data = [];
+                    var self = this;
+
+                    this.$element.find(':selected').each(function () {
+                        var $option = $(this);
+
+                        var option = self.item($option);
+
+                        data.push(option);
+                    });
+
+                    callback(data);
+                };
+
+                SelectAdapter.prototype.select = function (data) {
+                    var self = this;
+
+                    data.selected = true;
+
+                    // If data.element is a DOM node, use it instead
+                    if ($(data.element).is('option')) {
+                        data.element.selected = true;
+
+                        this.$element.trigger('change');
+
+                        return;
+                    }
+
+                    if (this.$element.prop('multiple')) {
+                        this.current(function (currentData) {
+                            var val = [];
+
+                            data = [data];
+                            data.push.apply(data, currentData);
+
+                            for (var d = 0; d < data.length; d++) {
+                                var id = data[d].id;
+
+                                if ($.inArray(id, val) === -1) {
+                                    val.push(id);
+                                }
+                            }
+
+                            self.$element.val(val);
+                            self.$element.trigger('change');
+                        });
+                    } else {
+                        var val = data.id;
+
+                        this.$element.val(val);
+                        this.$element.trigger('change');
+                    }
+                };
+
+                SelectAdapter.prototype.unselect = function (data) {
+                    var self = this;
+
+                    if (!this.$element.prop('multiple')) {
+                        return;
+                    }
+
+                    data.selected = false;
+
+                    if ($(data.element).is('option')) {
+                        data.element.selected = false;
+
+                        this.$element.trigger('change');
+
+                        return;
+                    }
+
+                    this.current(function (currentData) {
+                        var val = [];
+
+                        for (var d = 0; d < currentData.length; d++) {
+                            var id = currentData[d].id;
+
+                            if (id !== data.id && $.inArray(id, val) === -1) {
+                                val.push(id);
+                            }
+                        }
+
+                        self.$element.val(val);
+
+                        self.$element.trigger('change');
+                    });
+                };
+
+                SelectAdapter.prototype.bind = function (container, $container) {
+                    var self = this;
+
+                    this.container = container;
+
+                    container.on('select', function (params) {
+                        self.select(params.data);
+                    });
+
+                    container.on('unselect', function (params) {
+                        self.unselect(params.data);
+                    });
+                };
+
+                SelectAdapter.prototype.destroy = function () {
+                    // Remove anything added to child elements
+                    this.$element.find('*').each(function () {
+                        // Remove any custom data set by Select2
+                        $.removeData(this, 'data');
+                    });
+                };
+
+                SelectAdapter.prototype.query = function (params, callback) {
+                    var data = [];
+                    var self = this;
+
+                    var $options = this.$element.children();
+
+                    $options.each(function () {
+                        var $option = $(this);
+
+                        if (!$option.is('option') && !$option.is('optgroup')) {
+                            return;
+                        }
+
+                        var option = self.item($option);
+
+                        var matches = self.matches(params, option);
+
+                        if (matches !== null) {
+                            data.push(matches);
+                        }
+                    });
+
+                    callback({
+                        results: data
+                    });
+                };
+
+                SelectAdapter.prototype.addOptions = function ($options) {
+                    Utils.appendMany(this.$element, $options);
+                };
+
+                SelectAdapter.prototype.option = function (data) {
+                    var option;
+
+                    if (data.children) {
+                        option = document.createElement('optgroup');
+                        option.label = data.text;
+                    } else {
+                        option = document.createElement('option');
+
+                        if (option.textContent !== undefined) {
+                            option.textContent = data.text;
+                        } else {
+                            option.innerText = data.text;
+                        }
+                    }
+
+                    if (data.id) {
+                        option.value = data.id;
+                    }
+
+                    if (data.disabled) {
+                        option.disabled = true;
+                    }
+
+                    if (data.selected) {
+                        option.selected = true;
+                    }
+
+                    if (data.title) {
+                        option.title = data.title;
+                    }
+
+                    var $option = $(option);
+
+                    var normalizedData = this._normalizeItem(data);
+                    normalizedData.element = option;
+
+                    // Override the option's data with the combined data
+                    $.data(option, 'data', normalizedData);
+
+                    return $option;
+                };
+
+                SelectAdapter.prototype.item = function ($option) {
+                    var data = {};
+
+                    data = $.data($option[0], 'data');
+
+                    if (data != null) {
+                        return data;
+                    }
+
+                    if ($option.is('option')) {
+                        data = {
+                            id: $option.val(),
+                            text: $option.text(),
+                            disabled: $option.prop('disabled'),
+                            selected: $option.prop('selected'),
+                            title: $option.prop('title')
+                        };
+                    } else if ($option.is('optgroup')) {
+                        data = {
+                            text: $option.prop('label'),
+                            children: [],
+                            title: $option.prop('title')
+                        };
+
+                        var $children = $option.children('option');
+                        var children = [];
+
+                        for (var c = 0; c < $children.length; c++) {
+                            var $child = $($children[c]);
+
+                            var child = this.item($child);
+
+                            children.push(child);
+                        }
+
+                        data.children = children;
+                    }
+
+                    data = this._normalizeItem(data);
+                    data.element = $option[0];
+
+                    $.data($option[0], 'data', data);
+
+                    return data;
+                };
+
+                SelectAdapter.prototype._normalizeItem = function (item) {
+                    if (!$.isPlainObject(item)) {
+                        item = {
+                            id: item,
+                            text: item
+                        };
+                    }
+
+                    item = $.extend({}, {
+                        text: ''
+                    }, item);
+
+                    var defaults = {
+                        selected: false,
+                        disabled: false
+                    };
+
+                    if (item.id != null) {
+                        item.id = item.id.toString();
+                    }
+
+                    if (item.text != null) {
+                        item.text = item.text.toString();
+                    }
+
+                    if (item._resultId == null && item.id && this.container != null) {
+                        item._resultId = this.generateResultId(this.container, item);
+                    }
+
+                    return $.extend({}, defaults, item);
+                };
+
+                SelectAdapter.prototype.matches = function (params, data) {
+                    var matcher = this.options.get('matcher');
+
+                    return matcher(params, data);
+                };
+
+                return SelectAdapter;
+            });
+
+            S2.define('select2/data/array', [
+                './select',
+                '../utils',
+                'jquery'
+            ], function (SelectAdapter, Utils, $) {
+                function ArrayAdapter($element, options) {
+                    var data = options.get('data') || [];
+
+                    ArrayAdapter.__super__.constructor.call(this, $element, options);
+
+                    this.addOptions(this.convertToOptions(data));
+                }
+
+                Utils.Extend(ArrayAdapter, SelectAdapter);
+
+                ArrayAdapter.prototype.select = function (data) {
+                    var $option = this.$element.find('option').filter(function (i, elm) {
+                        return elm.value == data.id.toString();
+                    });
+
+                    if ($option.length === 0) {
+                        $option = this.option(data);
+
+                        this.addOptions($option);
+                    }
+
+                    ArrayAdapter.__super__.select.call(this, data);
+                };
+
+                ArrayAdapter.prototype.convertToOptions = function (data) {
+                    var self = this;
+
+                    var $existing = this.$element.find('option');
+                    var existingIds = $existing.map(function () {
+                        return self.item($(this)).id;
+                    }).get();
+
+                    var $options = [];
+
+                    // Filter out all items except for the one passed in the argument
+                    function onlyItem(item) {
+                        return function () {
+                            return $(this).val() == item.id;
+                        };
+                    }
+
+                    for (var d = 0; d < data.length; d++) {
+                        var item = this._normalizeItem(data[d]);
+
+                        // Skip items which were pre-loaded, only merge the data
+                        if ($.inArray(item.id, existingIds) >= 0) {
+                            var $existingOption = $existing.filter(onlyItem(item));
+
+                            var existingData = this.item($existingOption);
+                            var newData = $.extend(true, {}, item, existingData);
+
+                            var $newOption = this.option(newData);
+
+                            $existingOption.replaceWith($newOption);
+
+                            continue;
+                        }
+
+                        var $option = this.option(item);
+
+                        if (item.children) {
+                            var $children = this.convertToOptions(item.children);
+
+                            Utils.appendMany($option, $children);
+                        }
+
+                        $options.push($option);
+                    }
+
+                    return $options;
+                };
+
+                return ArrayAdapter;
+            });
+
+            S2.define('select2/data/ajax', [
+                './array',
+                '../utils',
+                'jquery'
+            ], function (ArrayAdapter, Utils, $) {
+                function AjaxAdapter($element, options) {
+                    this.ajaxOptions = this._applyDefaults(options.get('ajax'));
+
+                    if (this.ajaxOptions.processResults != null) {
+                        this.processResults = this.ajaxOptions.processResults;
+                    }
+
+                    AjaxAdapter.__super__.constructor.call(this, $element, options);
+                }
+
+                Utils.Extend(AjaxAdapter, ArrayAdapter);
+
+                AjaxAdapter.prototype._applyDefaults = function (options) {
+                    var defaults = {
+                        data: function (params) {
+                            return $.extend({}, params, {
+                                q: params.term
+                            });
+                        },
+                        transport: function (params, success, failure) {
+                            var $request = $.ajax(params);
+
+                            $request.then(success);
+                            $request.fail(failure);
+
+                            return $request;
+                        }
+                    };
+
+                    return $.extend({}, defaults, options, true);
+                };
+
+                AjaxAdapter.prototype.processResults = function (results) {
+                    return results;
+                };
+
+                AjaxAdapter.prototype.query = function (params, callback) {
+                    var matches = [];
+                    var self = this;
+
+                    if (this._request != null) {
+                        // JSONP requests cannot always be aborted
+                        if ($.isFunction(this._request.abort)) {
+                            this._request.abort();
+                        }
+
+                        this._request = null;
+                    }
+
+                    var options = $.extend({
+                        type: 'GET'
+                    }, this.ajaxOptions);
+
+                    if (typeof options.url === 'function') {
+                        options.url = options.url.call(this.$element, params);
+                    }
+
+                    if (typeof options.data === 'function') {
+                        options.data = options.data.call(this.$element, params);
+                    }
+
+                    function request() {
+                        var $request = options.transport(options, function (data) {
+                            var results = self.processResults(data, params);
+
+                            if (self.options.get('debug') && window.console && console.error) {
+                                // Check to make sure that the response included a `results` key.
+                                if (!results || !results.results || !$.isArray(results.results)) {
+                                    console.error(
+                                        'Select2: The AJAX results did not return an array in the ' +
+                                        '`results` key of the response.'
+                                    );
+                                }
+                            }
+
+                            callback(results);
+                        }, function () {
+                            // Attempt to detect if a request was aborted
+                            // Only works if the transport exposes a status property
+                            if ($request.status && $request.status === '0') {
+                                return;
+                            }
+
+                            self.trigger('results:message', {
+                                message: 'errorLoading'
+                            });
+                        });
+
+                        self._request = $request;
+                    }
+
+                    if (this.ajaxOptions.delay && params.term != null) {
+                        if (this._queryTimeout) {
+                            window.clearTimeout(this._queryTimeout);
+                        }
+
+                        this._queryTimeout = window.setTimeout(request, this.ajaxOptions.delay);
+                    } else {
+                        request();
+                    }
+                };
+
+                return AjaxAdapter;
+            });
+
+            S2.define('select2/data/tags', [
+                'jquery'
+            ], function ($) {
+                function Tags(decorated, $element, options) {
+                    var tags = options.get('tags');
+
+                    var createTag = options.get('createTag');
+
+                    if (createTag !== undefined) {
+                        this.createTag = createTag;
+                    }
+
+                    var insertTag = options.get('insertTag');
+
+                    if (insertTag !== undefined) {
+                        this.insertTag = insertTag;
+                    }
+
+                    decorated.call(this, $element, options);
+
+                    if ($.isArray(tags)) {
+                        for (var t = 0; t < tags.length; t++) {
+                            var tag = tags[t];
+                            var item = this._normalizeItem(tag);
+
+                            var $option = this.option(item);
+
+                            this.$element.append($option);
+                        }
+                    }
+                }
+
+                Tags.prototype.query = function (decorated, params, callback) {
+                    var self = this;
+
+                    this._removeOldTags();
+
+                    if (params.term == null || params.page != null) {
+                        decorated.call(this, params, callback);
+                        return;
+                    }
+
+                    function wrapper(obj, child) {
+                        var data = obj.results;
+
+                        for (var i = 0; i < data.length; i++) {
+                            var option = data[i];
+
+                            var checkChildren = (
+                                option.children != null &&
+                                !wrapper({
+                                    results: option.children
+                                }, true)
+                            );
+
+                            var checkText = option.text === params.term;
+
+                            if (checkText || checkChildren) {
+                                if (child) {
+                                    return false;
+                                }
+
+                                obj.data = data;
+                                callback(obj);
+
+                                return;
+                            }
+                        }
+
+                        if (child) {
+                            return true;
+                        }
+
+                        var tag = self.createTag(params);
+
+                        if (tag != null) {
+                            var $option = self.option(tag);
+                            $option.attr('data-select2-tag', true);
+
+                            self.addOptions([$option]);
+
+                            self.insertTag(data, tag);
+                        }
+
+                        obj.results = data;
+
+                        callback(obj);
+                    }
+
+                    decorated.call(this, params, wrapper);
+                };
+
+                Tags.prototype.createTag = function (decorated, params) {
+                    var term = $.trim(params.term);
+
+                    if (term === '') {
+                        return null;
+                    }
+
+                    return {
+                        id: term,
+                        text: term
+                    };
+                };
+
+                Tags.prototype.insertTag = function (_, data, tag) {
+                    data.unshift(tag);
+                };
+
+                Tags.prototype._removeOldTags = function (_) {
+                    var tag = this._lastTag;
+
+                    var $options = this.$element.find('option[data-select2-tag]');
+
+                    $options.each(function () {
+                        if (this.selected) {
+                            return;
+                        }
+
+                        $(this).remove();
+                    });
+                };
+
+                return Tags;
+            });
+
+            S2.define('select2/data/tokenizer', [
+                'jquery'
+            ], function ($) {
+                function Tokenizer(decorated, $element, options) {
+                    var tokenizer = options.get('tokenizer');
+
+                    if (tokenizer !== undefined) {
+                        this.tokenizer = tokenizer;
+                    }
+
+                    decorated.call(this, $element, options);
+                }
+
+                Tokenizer.prototype.bind = function (decorated, container, $container) {
+                    decorated.call(this, container, $container);
+
+                    this.$search = container.dropdown.$search || container.selection.$search ||
+                        $container.find('.select2-search__field');
+                };
+
+                Tokenizer.prototype.query = function (decorated, params, callback) {
+                    var self = this;
+
+                    function createAndSelect(data) {
+                        // Normalize the data object so we can use it for checks
+                        var item = self._normalizeItem(data);
+
+                        // Check if the data object already exists as a tag
+                        // Select it if it doesn't
+                        var $existingOptions = self.$element.find('option').filter(function () {
+                            return $(this).val() === item.id;
+                        });
+
+                        // If an existing option wasn't found for it, create the option
+                        if (!$existingOptions.length) {
+                            var $option = self.option(item);
+                            $option.attr('data-select2-tag', true);
+
+                            self._removeOldTags();
+                            self.addOptions([$option]);
+                        }
+
+                        // Select the item, now that we know there is an option for it
+                        select(item);
+                    }
+
+                    function select(data) {
+                        self.trigger('select', {
+                            data: data
+                        });
+                    }
+
+                    params.term = params.term || '';
+
+                    var tokenData = this.tokenizer(params, this.options, createAndSelect);
+
+                    if (tokenData.term !== params.term) {
+                        // Replace the search term if we have the search box
+                        if (this.$search.length) {
+                            this.$search.val(tokenData.term);
+                            this.$search.focus();
+                        }
+
+                        params.term = tokenData.term;
+                    }
+
+                    decorated.call(this, params, callback);
+                };
+
+                Tokenizer.prototype.tokenizer = function (_, params, options, callback) {
+                    var separators = options.get('tokenSeparators') || [];
+                    var term = params.term;
+                    var i = 0;
+
+                    var createTag = this.createTag || function (params) {
+                        return {
+                            id: params.term,
+                            text: params.term
+                        };
+                    };
+
+                    while (i < term.length) {
+                        var termChar = term[i];
+
+                        if ($.inArray(termChar, separators) === -1) {
+                            i++;
+
+                            continue;
+                        }
+
+                        var part = term.substr(0, i);
+                        var partParams = $.extend({}, params, {
+                            term: part
+                        });
+
+                        var data = createTag(partParams);
+
+                        if (data == null) {
+                            i++;
+                            continue;
+                        }
+
+                        callback(data);
+
+                        // Reset the term to not include the tokenized portion
+                        term = term.substr(i + 1) || '';
+                        i = 0;
+                    }
+
+                    return {
+                        term: term
+                    };
+                };
+
+                return Tokenizer;
+            });
+
+            S2.define('select2/data/minimumInputLength', [], function () {
+                function MinimumInputLength(decorated, $e, options) {
+                    this.minimumInputLength = options.get('minimumInputLength');
+
+                    decorated.call(this, $e, options);
+                }
+
+                MinimumInputLength.prototype.query = function (decorated, params, callback) {
+                    params.term = params.term || '';
+
+                    if (params.term.length < this.minimumInputLength) {
+                        this.trigger('results:message', {
+                            message: 'inputTooShort',
+                            args: {
+                                minimum: this.minimumInputLength,
+                                input: params.term,
+                                params: params
+                            }
+                        });
+
+                        return;
+                    }
+
+                    decorated.call(this, params, callback);
+                };
+
+                return MinimumInputLength;
+            });
+
+            S2.define('select2/data/maximumInputLength', [], function () {
+                function MaximumInputLength(decorated, $e, options) {
+                    this.maximumInputLength = options.get('maximumInputLength');
+
+                    decorated.call(this, $e, options);
+                }
+
+                MaximumInputLength.prototype.query = function (decorated, params, callback) {
+                    params.term = params.term || '';
+
+                    if (this.maximumInputLength > 0 &&
+                        params.term.length > this.maximumInputLength) {
+                        this.trigger('results:message', {
+                            message: 'inputTooLong',
+                            args: {
+                                maximum: this.maximumInputLength,
+                                input: params.term,
+                                params: params
+                            }
+                        });
+
+                        return;
+                    }
+
+                    decorated.call(this, params, callback);
+                };
+
+                return MaximumInputLength;
+            });
+
+            S2.define('select2/data/maximumSelectionLength', [], function () {
+                function MaximumSelectionLength(decorated, $e, options) {
+                    this.maximumSelectionLength = options.get('maximumSelectionLength');
+
+                    decorated.call(this, $e, options);
+                }
+
+                MaximumSelectionLength.prototype.query =
+                    function (decorated, params, callback) {
+                        var self = this;
+
+                        this.current(function (currentData) {
+                            var count = currentData != null ? currentData.length : 0;
+                            if (self.maximumSelectionLength > 0 &&
+                                count >= self.maximumSelectionLength) {
+                                self.trigger('results:message', {
+                                    message: 'maximumSelected',
+                                    args: {
+                                        maximum: self.maximumSelectionLength
+                                    }
+                                });
+                                return;
+                            }
+                            decorated.call(self, params, callback);
+                        });
+                    };
+
+                return MaximumSelectionLength;
+            });
+
+            S2.define('select2/dropdown', [
+                'jquery',
+                './utils'
+            ], function ($, Utils) {
+                function Dropdown($element, options) {
+                    this.$element = $element;
+                    this.options = options;
+
+                    Dropdown.__super__.constructor.call(this);
+                }
+
+                Utils.Extend(Dropdown, Utils.Observable);
+
+                Dropdown.prototype.render = function () {
+                    var $dropdown = $(
+                        '<span class="select2-dropdown">' +
+                        '<span class="select2-results"></span>' +
+                        '</span>'
+                    );
+
+                    $dropdown.attr('dir', this.options.get('dir'));
+
+                    this.$dropdown = $dropdown;
+
+                    return $dropdown;
+                };
+
+                Dropdown.prototype.bind = function () {
+                    // Should be implemented in subclasses
+                };
+
+                Dropdown.prototype.position = function ($dropdown, $container) {
+                    // Should be implmented in subclasses
+                };
+
+                Dropdown.prototype.destroy = function () {
+                    // Remove the dropdown from the DOM
+                    this.$dropdown.remove();
+                };
+
+                return Dropdown;
+            });
+
+            S2.define('select2/dropdown/search', [
+                'jquery',
+                '../utils'
+            ], function ($, Utils) {
+                function Search() {
+                }
+
+                Search.prototype.render = function (decorated) {
+                    var $rendered = decorated.call(this);
+
+                    var $search = $(
+                        '<span class="select2-search select2-search--dropdown">' +
+                        '<input class="select2-search__field" type="search" tabindex="-1"' +
+                        ' autocomplete="off" autocorrect="off" autocapitalize="off"' +
+                        ' spellcheck="false" role="textbox" />' +
+                        '</span>'
+                    );
+
+                    this.$searchContainer = $search;
+                    this.$search = $search.find('input');
+
+                    $rendered.prepend($search);
+
+                    return $rendered;
+                };
+
+                Search.prototype.bind = function (decorated, container, $container) {
+                    var self = this;
+
+                    decorated.call(this, container, $container);
+
+                    this.$search.on('keydown', function (evt) {
+                        self.trigger('keypress', evt);
+
+                        self._keyUpPrevented = evt.isDefaultPrevented();
+                    });
+
+                    // Workaround for browsers which do not support the `input` event
+                    // This will prevent double-triggering of events for browsers which support
+                    // both the `keyup` and `input` events.
+                    this.$search.on('input', function (evt) {
+                        // Unbind the duplicated `keyup` event
+                        $(this).off('keyup');
+                    });
+
+                    this.$search.on('keyup input', function (evt) {
+                        self.handleSearch(evt);
+                    });
+
+                    container.on('open', function () {
+                        self.$search.attr('tabindex', 0);
+
+                        self.$search.focus();
+
+                        window.setTimeout(function () {
+                            self.$search.focus();
+                        }, 0);
+                    });
+
+                    container.on('close', function () {
+                        self.$search.attr('tabindex', -1);
+
+                        self.$search.val('');
+                    });
+
+                    container.on('focus', function () {
+                        if (container.isOpen()) {
+                            self.$search.focus();
+                        }
+                    });
+
+                    container.on('results:all', function (params) {
+                        if (params.query.term == null || params.query.term === '') {
+                            var showSearch = self.showSearch(params);
+
+                            if (showSearch) {
+                                self.$searchContainer.removeClass('select2-search--hide');
+                            } else {
+                                self.$searchContainer.addClass('select2-search--hide');
+                            }
+                        }
+                    });
+                };
+
+                Search.prototype.handleSearch = function (evt) {
+                    if (!this._keyUpPrevented) {
+                        var input = this.$search.val();
+
+                        this.trigger('query', {
+                            term: input
+                        });
+                    }
+
+                    this._keyUpPrevented = false;
+                };
+
+                Search.prototype.showSearch = function (_, params) {
+                    return true;
+                };
+
+                return Search;
+            });
+
+            S2.define('select2/dropdown/hidePlaceholder', [], function () {
+                function HidePlaceholder(decorated, $element, options, dataAdapter) {
+                    this.placeholder = this.normalizePlaceholder(options.get('placeholder'));
+
+                    decorated.call(this, $element, options, dataAdapter);
+                }
+
+                HidePlaceholder.prototype.append = function (decorated, data) {
+                    data.results = this.removePlaceholder(data.results);
+
+                    decorated.call(this, data);
+                };
+
+                HidePlaceholder.prototype.normalizePlaceholder = function (_, placeholder) {
+                    if (typeof placeholder === 'string') {
+                        placeholder = {
+                            id: '',
+                            text: placeholder
+                        };
+                    }
+
+                    return placeholder;
+                };
+
+                HidePlaceholder.prototype.removePlaceholder = function (_, data) {
+                    var modifiedData = data.slice(0);
+
+                    for (var d = data.length - 1; d >= 0; d--) {
+                        var item = data[d];
+
+                        if (this.placeholder.id === item.id) {
+                            modifiedData.splice(d, 1);
+                        }
+                    }
+
+                    return modifiedData;
+                };
+
+                return HidePlaceholder;
+            });
+
+            S2.define('select2/dropdown/infiniteScroll', [
+                'jquery'
+            ], function ($) {
+                function InfiniteScroll(decorated, $element, options, dataAdapter) {
+                    this.lastParams = {};
+
+                    decorated.call(this, $element, options, dataAdapter);
+
+                    this.$loadingMore = this.createLoadingMore();
+                    this.loading = false;
+                }
+
+                InfiniteScroll.prototype.append = function (decorated, data) {
+                    this.$loadingMore.remove();
+                    this.loading = false;
+
+                    decorated.call(this, data);
+
+                    if (this.showLoadingMore(data)) {
+                        this.$results.append(this.$loadingMore);
+                    }
+                };
+
+                InfiniteScroll.prototype.bind = function (decorated, container, $container) {
+                    var self = this;
+
+                    decorated.call(this, container, $container);
+
+                    container.on('query', function (params) {
+                        self.lastParams = params;
+                        self.loading = true;
+                    });
+
+                    container.on('query:append', function (params) {
+                        self.lastParams = params;
+                        self.loading = true;
+                    });
+
+                    this.$results.on('scroll', function () {
+                        var isLoadMoreVisible = $.contains(
+                            document.documentElement,
+                            self.$loadingMore[0]
+                        );
+
+                        if (self.loading || !isLoadMoreVisible) {
+                            return;
+                        }
+
+                        var currentOffset = self.$results.offset().top +
+                            self.$results.outerHeight(false);
+                        var loadingMoreOffset = self.$loadingMore.offset().top +
+                            self.$loadingMore.outerHeight(false);
+
+                        if (currentOffset + 50 >= loadingMoreOffset) {
+                            self.loadMore();
+                        }
+                    });
+                };
+
+                InfiniteScroll.prototype.loadMore = function () {
+                    this.loading = true;
+
+                    var params = $.extend({}, {page: 1}, this.lastParams);
+
+                    params.page++;
+
+                    this.trigger('query:append', params);
+                };
+
+                InfiniteScroll.prototype.showLoadingMore = function (_, data) {
+                    return data.pagination && data.pagination.more;
+                };
+
+                InfiniteScroll.prototype.createLoadingMore = function () {
+                    var $option = $(
+                        '<li ' +
+                        'class="select2-results__option select2-results__option--load-more"' +
+                        'role="treeitem" aria-disabled="true"></li>'
+                    );
+
+                    var message = this.options.get('translations').get('loadingMore');
+
+                    $option.html(message(this.lastParams));
+
+                    return $option;
+                };
+
+                return InfiniteScroll;
+            });
+
+            S2.define('select2/dropdown/attachBody', [
+                'jquery',
+                '../utils'
+            ], function ($, Utils) {
+                function AttachBody(decorated, $element, options) {
+                    this.$dropdownParent = options.get('dropdownParent') || $(document.body);
+
+                    decorated.call(this, $element, options);
+                }
+
+                AttachBody.prototype.bind = function (decorated, container, $container) {
+                    var self = this;
+
+                    var setupResultsEvents = false;
+
+                    decorated.call(this, container, $container);
+
+                    container.on('open', function () {
+                        self._showDropdown();
+                        self._attachPositioningHandler(container);
+
+                        if (!setupResultsEvents) {
+                            setupResultsEvents = true;
+
+                            container.on('results:all', function () {
+                                self._positionDropdown();
+                                self._resizeDropdown();
+                            });
+
+                            container.on('results:append', function () {
+                                self._positionDropdown();
+                                self._resizeDropdown();
+                            });
+                        }
+                    });
+
+                    container.on('close', function () {
+                        self._hideDropdown();
+                        self._detachPositioningHandler(container);
+                    });
+
+                    this.$dropdownContainer.on('mousedown', function (evt) {
+                        evt.stopPropagation();
+                    });
+                };
+
+                AttachBody.prototype.destroy = function (decorated) {
+                    decorated.call(this);
+
+                    this.$dropdownContainer.remove();
+                };
+
+                AttachBody.prototype.position = function (decorated, $dropdown, $container) {
+                    // Clone all of the container classes
+                    $dropdown.attr('class', $container.attr('class'));
+
+                    $dropdown.removeClass('select2');
+                    $dropdown.addClass('select2-container--open');
+
+                    $dropdown.css({
+                        position: 'absolute',
+                        top: -999999
+                    });
+
+                    this.$container = $container;
+                };
+
+                AttachBody.prototype.render = function (decorated) {
+                    var $container = $('<span></span>');
+
+                    var $dropdown = decorated.call(this);
+                    $container.append($dropdown);
+
+                    this.$dropdownContainer = $container;
+
+                    return $container;
+                };
+
+                AttachBody.prototype._hideDropdown = function (decorated) {
+                    this.$dropdownContainer.detach();
+                };
+
+                AttachBody.prototype._attachPositioningHandler =
+                    function (decorated, container) {
+                        var self = this;
+
+                        var scrollEvent = 'scroll.select2.' + container.id;
+                        var resizeEvent = 'resize.select2.' + container.id;
+                        var orientationEvent = 'orientationchange.select2.' + container.id;
+
+                        var $watchers = this.$container.parents().filter(Utils.hasScroll);
+                        $watchers.each(function () {
+                            $(this).data('select2-scroll-position', {
+                                x: $(this).scrollLeft(),
+                                y: $(this).scrollTop()
+                            });
+                        });
+
+                        $watchers.on(scrollEvent, function (ev) {
+                            var position = $(this).data('select2-scroll-position');
+                            $(this).scrollTop(position.y);
+                        });
+
+                        $(window).on(scrollEvent + ' ' + resizeEvent + ' ' + orientationEvent,
+                            function (e) {
+                                self._positionDropdown();
+                                self._resizeDropdown();
+                            });
+                    };
+
+                AttachBody.prototype._detachPositioningHandler =
+                    function (decorated, container) {
+                        var scrollEvent = 'scroll.select2.' + container.id;
+                        var resizeEvent = 'resize.select2.' + container.id;
+                        var orientationEvent = 'orientationchange.select2.' + container.id;
+
+                        var $watchers = this.$container.parents().filter(Utils.hasScroll);
+                        $watchers.off(scrollEvent);
+
+                        $(window).off(scrollEvent + ' ' + resizeEvent + ' ' + orientationEvent);
+                    };
+
+                AttachBody.prototype._positionDropdown = function () {
+                    var $window = $(window);
+
+                    var isCurrentlyAbove = this.$dropdown.hasClass('select2-dropdown--above');
+                    var isCurrentlyBelow = this.$dropdown.hasClass('select2-dropdown--below');
+
+                    var newDirection = null;
+
+                    var offset = this.$container.offset();
+
+                    offset.bottom = offset.top + this.$container.outerHeight(false);
+
+                    var container = {
+                        height: this.$container.outerHeight(false)
+                    };
+
+                    container.top = offset.top;
+                    container.bottom = offset.top + container.height;
+
+                    var dropdown = {
+                        height: this.$dropdown.outerHeight(false)
+                    };
+
+                    var viewport = {
+                        top: $window.scrollTop(),
+                        bottom: $window.scrollTop() + $window.height()
+                    };
+
+                    var enoughRoomAbove = viewport.top < (offset.top - dropdown.height);
+                    var enoughRoomBelow = viewport.bottom > (offset.bottom + dropdown.height);
+
+                    var css = {
+                        left: offset.left,
+                        top: container.bottom
+                    };
+
+                    // Determine what the parent element is to use for calciulating the offset
+                    var $offsetParent = this.$dropdownParent;
+
+                    // For statically positoned elements, we need to get the element
+                    // that is determining the offset
+                    if ($offsetParent.css('position') === 'static') {
+                        $offsetParent = $offsetParent.offsetParent();
+                    }
+
+                    var parentOffset = $offsetParent.offset();
+
+                    css.top -= parentOffset.top;
+                    css.left -= parentOffset.left;
+
+                    if (!isCurrentlyAbove && !isCurrentlyBelow) {
+                        newDirection = 'below';
+                    }
+
+                    if (!enoughRoomBelow && enoughRoomAbove && !isCurrentlyAbove) {
+                        newDirection = 'above';
+                    } else if (!enoughRoomAbove && enoughRoomBelow && isCurrentlyAbove) {
+                        newDirection = 'below';
+                    }
+
+                    if (newDirection == 'above' ||
+                        (isCurrentlyAbove && newDirection !== 'below')) {
+                        css.top = container.top - parentOffset.top - dropdown.height;
+                    }
+
+                    if (newDirection != null) {
+                        this.$dropdown
+                            .removeClass('select2-dropdown--below select2-dropdown--above')
+                            .addClass('select2-dropdown--' + newDirection);
+                        this.$container
+                            .removeClass('select2-container--below select2-container--above')
+                            .addClass('select2-container--' + newDirection);
+                    }
+
+                    this.$dropdownContainer.css(css);
+                };
+
+                AttachBody.prototype._resizeDropdown = function () {
+                    var css = {
+                        width: this.$container.outerWidth(false) + 'px'
+                    };
+
+                    if (this.options.get('dropdownAutoWidth')) {
+                        css.minWidth = css.width;
+                        css.position = 'relative';
+                        css.width = 'auto';
+                    }
+
+                    this.$dropdown.css(css);
+                };
+
+                AttachBody.prototype._showDropdown = function (decorated) {
+                    this.$dropdownContainer.appendTo(this.$dropdownParent);
+
+                    this._positionDropdown();
+                    this._resizeDropdown();
+                };
+
+                return AttachBody;
+            });
+
+            S2.define('select2/dropdown/minimumResultsForSearch', [], function () {
+                function countResults(data) {
+                    var count = 0;
+
+                    for (var d = 0; d < data.length; d++) {
+                        var item = data[d];
+
+                        if (item.children) {
+                            count += countResults(item.children);
+                        } else {
+                            count++;
+                        }
+                    }
+
+                    return count;
+                }
+
+                function MinimumResultsForSearch(decorated, $element, options, dataAdapter) {
+                    this.minimumResultsForSearch = options.get('minimumResultsForSearch');
+
+                    if (this.minimumResultsForSearch < 0) {
+                        this.minimumResultsForSearch = Infinity;
+                    }
+
+                    decorated.call(this, $element, options, dataAdapter);
+                }
+
+                MinimumResultsForSearch.prototype.showSearch = function (decorated, params) {
+                    if (countResults(params.data.results) < this.minimumResultsForSearch) {
+                        return false;
+                    }
+
+                    return decorated.call(this, params);
+                };
+
+                return MinimumResultsForSearch;
+            });
+
+            S2.define('select2/dropdown/selectOnClose', [], function () {
+                function SelectOnClose() {
+                }
+
+                SelectOnClose.prototype.bind = function (decorated, container, $container) {
+                    var self = this;
+
+                    decorated.call(this, container, $container);
+
+                    container.on('close', function (params) {
+                        self._handleSelectOnClose(params);
+                    });
+                };
+
+                SelectOnClose.prototype._handleSelectOnClose = function (_, params) {
+                    if (params && params.originalSelect2Event != null) {
+                        var event = params.originalSelect2Event;
+
+                        // Don't select an item if the close event was triggered from a select or
+                        // unselect event
+                        if (event._type === 'select' || event._type === 'unselect') {
+                            return;
+                        }
+                    }
+
+                    var $highlightedResults = this.getHighlightedResults();
+
+                    // Only select highlighted results
+                    if ($highlightedResults.length < 1) {
+                        return;
+                    }
+
+                    var data = $highlightedResults.data('data');
+
+                    // Don't re-select already selected resulte
+                    if (
+                        (data.element != null && data.element.selected) ||
+                        (data.element == null && data.selected)
+                    ) {
+                        return;
+                    }
+
+                    this.trigger('select', {
+                        data: data
+                    });
+                };
+
+                return SelectOnClose;
+            });
+
+            S2.define('select2/dropdown/closeOnSelect', [], function () {
+                function CloseOnSelect() {
+                }
+
+                CloseOnSelect.prototype.bind = function (decorated, container, $container) {
+                    var self = this;
+
+                    decorated.call(this, container, $container);
+
+                    container.on('select', function (evt) {
+                        self._selectTriggered(evt);
+                    });
+
+                    container.on('unselect', function (evt) {
+                        self._selectTriggered(evt);
+                    });
+                };
+
+                CloseOnSelect.prototype._selectTriggered = function (_, evt) {
+                    var originalEvent = evt.originalEvent;
+
+                    // Don't close if the control key is being held
+                    if (originalEvent && originalEvent.ctrlKey) {
+                        return;
+                    }
+
+                    this.trigger('close', {
+                        originalEvent: originalEvent,
+                        originalSelect2Event: evt
+                    });
+                };
+
+                return CloseOnSelect;
+            });
+
+            S2.define('select2/i18n/en', [], function () {
+                // English
+                return {
+                    errorLoading: function () {
+                        return 'The results could not be loaded.';
+                    },
+                    inputTooLong: function (args) {
+                        var overChars = args.input.length - args.maximum;
+
+                        var message = 'Please delete ' + overChars + ' character';
+
+                        if (overChars != 1) {
+                            message += 's';
+                        }
+
+                        return message;
+                    },
+                    inputTooShort: function (args) {
+                        var remainingChars = args.minimum - args.input.length;
+
+                        var message = 'Please enter ' + remainingChars + ' or more characters';
+
+                        return message;
+                    },
+                    loadingMore: function () {
+                        return 'Loading more results…';
+                    },
+                    maximumSelected: function (args) {
+                        var message = 'You can only select ' + args.maximum + ' item';
+
+                        if (args.maximum != 1) {
+                            message += 's';
+                        }
+
+                        return message;
+                    },
+                    noResults: function () {
+                        return 'No results found';
+                    },
+                    searching: function () {
+                        return 'Searching…';
+                    }
+                };
+            });
+
+            S2.define('select2/defaults', [
+                'jquery',
+                'require',
+
+                './results',
+
+                './selection/single',
+                './selection/multiple',
+                './selection/placeholder',
+                './selection/allowClear',
+                './selection/search',
+                './selection/eventRelay',
+
+                './utils',
+                './translation',
+                './diacritics',
+
+                './data/select',
+                './data/array',
+                './data/ajax',
+                './data/tags',
+                './data/tokenizer',
+                './data/minimumInputLength',
+                './data/maximumInputLength',
+                './data/maximumSelectionLength',
+
+                './dropdown',
+                './dropdown/search',
+                './dropdown/hidePlaceholder',
+                './dropdown/infiniteScroll',
+                './dropdown/attachBody',
+                './dropdown/minimumResultsForSearch',
+                './dropdown/selectOnClose',
+                './dropdown/closeOnSelect',
+
+                './i18n/en'
+            ], function ($, require,
+                         ResultsList,
+                         SingleSelection, MultipleSelection, Placeholder, AllowClear,
+                         SelectionSearch, EventRelay,
+                         Utils, Translation, DIACRITICS,
+                         SelectData, ArrayData, AjaxData, Tags, Tokenizer,
+                         MinimumInputLength, MaximumInputLength, MaximumSelectionLength,
+                         Dropdown, DropdownSearch, HidePlaceholder, InfiniteScroll,
+                         AttachBody, MinimumResultsForSearch, SelectOnClose, CloseOnSelect,
+                         EnglishTranslation) {
+                function Defaults() {
+                    this.reset();
+                }
+
+                Defaults.prototype.apply = function (options) {
+                    options = $.extend(true, {}, this.defaults, options);
+
+                    if (options.dataAdapter == null) {
+                        if (options.ajax != null) {
+                            options.dataAdapter = AjaxData;
+                        } else if (options.data != null) {
+                            options.dataAdapter = ArrayData;
+                        } else {
+                            options.dataAdapter = SelectData;
+                        }
+
+                        if (options.minimumInputLength > 0) {
+                            options.dataAdapter = Utils.Decorate(
+                                options.dataAdapter,
+                                MinimumInputLength
+                            );
+                        }
+
+                        if (options.maximumInputLength > 0) {
+                            options.dataAdapter = Utils.Decorate(
+                                options.dataAdapter,
+                                MaximumInputLength
+                            );
+                        }
+
+                        if (options.maximumSelectionLength > 0) {
+                            options.dataAdapter = Utils.Decorate(
+                                options.dataAdapter,
+                                MaximumSelectionLength
+                            );
+                        }
+
+                        if (options.tags) {
+                            options.dataAdapter = Utils.Decorate(options.dataAdapter, Tags);
+                        }
+
+                        if (options.tokenSeparators != null || options.tokenizer != null) {
+                            options.dataAdapter = Utils.Decorate(
+                                options.dataAdapter,
+                                Tokenizer
+                            );
+                        }
+
+                        if (options.query != null) {
+                            var Query = require(options.amdBase + 'compat/query');
+
+                            options.dataAdapter = Utils.Decorate(
+                                options.dataAdapter,
+                                Query
+                            );
+                        }
+
+                        if (options.initSelection != null) {
+                            var InitSelection = require(options.amdBase + 'compat/initSelection');
+
+                            options.dataAdapter = Utils.Decorate(
+                                options.dataAdapter,
+                                InitSelection
+                            );
+                        }
+                    }
+
+                    if (options.resultsAdapter == null) {
+                        options.resultsAdapter = ResultsList;
+
+                        if (options.ajax != null) {
+                            options.resultsAdapter = Utils.Decorate(
+                                options.resultsAdapter,
+                                InfiniteScroll
+                            );
+                        }
+
+                        if (options.placeholder != null) {
+                            options.resultsAdapter = Utils.Decorate(
+                                options.resultsAdapter,
+                                HidePlaceholder
+                            );
+                        }
+
+                        if (options.selectOnClose) {
+                            options.resultsAdapter = Utils.Decorate(
+                                options.resultsAdapter,
+                                SelectOnClose
+                            );
+                        }
+                    }
+
+                    if (options.dropdownAdapter == null) {
+                        if (options.multiple) {
+                            options.dropdownAdapter = Dropdown;
+                        } else {
+                            var SearchableDropdown = Utils.Decorate(Dropdown, DropdownSearch);
+
+                            options.dropdownAdapter = SearchableDropdown;
+                        }
+
+                        if (options.minimumResultsForSearch !== 0) {
+                            options.dropdownAdapter = Utils.Decorate(
+                                options.dropdownAdapter,
+                                MinimumResultsForSearch
+                            );
+                        }
+
+                        if (options.closeOnSelect) {
+                            options.dropdownAdapter = Utils.Decorate(
+                                options.dropdownAdapter,
+                                CloseOnSelect
+                            );
+                        }
+
+                        if (
+                            options.dropdownCssClass != null ||
+                            options.dropdownCss != null ||
+                            options.adaptDropdownCssClass != null
+                        ) {
+                            var DropdownCSS = require(options.amdBase + 'compat/dropdownCss');
+
+                            options.dropdownAdapter = Utils.Decorate(
+                                options.dropdownAdapter,
+                                DropdownCSS
+                            );
+                        }
+
+                        options.dropdownAdapter = Utils.Decorate(
+                            options.dropdownAdapter,
+                            AttachBody
+                        );
+                    }
+
+                    if (options.selectionAdapter == null) {
+                        if (options.multiple) {
+                            options.selectionAdapter = MultipleSelection;
+                        } else {
+                            options.selectionAdapter = SingleSelection;
+                        }
+
+                        // Add the placeholder mixin if a placeholder was specified
+                        if (options.placeholder != null) {
+                            options.selectionAdapter = Utils.Decorate(
+                                options.selectionAdapter,
+                                Placeholder
+                            );
+                        }
+
+                        if (options.allowClear) {
+                            options.selectionAdapter = Utils.Decorate(
+                                options.selectionAdapter,
+                                AllowClear
+                            );
+                        }
+
+                        if (options.multiple) {
+                            options.selectionAdapter = Utils.Decorate(
+                                options.selectionAdapter,
+                                SelectionSearch
+                            );
+                        }
+
+                        if (
+                            options.containerCssClass != null ||
+                            options.containerCss != null ||
+                            options.adaptContainerCssClass != null
+                        ) {
+                            var ContainerCSS = require(options.amdBase + 'compat/containerCss');
+
+                            options.selectionAdapter = Utils.Decorate(
+                                options.selectionAdapter,
+                                ContainerCSS
+                            );
+                        }
+
+                        options.selectionAdapter = Utils.Decorate(
+                            options.selectionAdapter,
+                            EventRelay
+                        );
+                    }
+
+                    if (typeof options.language === 'string') {
+                        // Check if the language is specified with a region
+                        if (options.language.indexOf('-') > 0) {
+                            // Extract the region information if it is included
+                            var languageParts = options.language.split('-');
+                            var baseLanguage = languageParts[0];
+
+                            options.language = [options.language, baseLanguage];
+                        } else {
+                            options.language = [options.language];
+                        }
+                    }
+
+                    if ($.isArray(options.language)) {
+                        var languages = new Translation();
+                        options.language.push('en');
+
+                        var languageNames = options.language;
+
+                        for (var l = 0; l < languageNames.length; l++) {
+                            var name = languageNames[l];
+                            var language = {};
+
+                            try {
+                                // Try to load it with the original name
+                                language = Translation.loadPath(name);
+                            } catch (e) {
+                                try {
+                                    // If we couldn't load it, check if it wasn't the full path
+                                    name = this.defaults.amdLanguageBase + name;
+                                    language = Translation.loadPath(name);
+                                } catch (ex) {
+                                    // The translation could not be loaded at all. Sometimes this is
+                                    // because of a configuration problem, other times this can be
+                                    // because of how Select2 helps load all possible translation files.
+                                    if (options.debug && window.console && console.warn) {
+                                        console.warn(
+                                            'Select2: The language file for "' + name + '" could not be ' +
+                                            'automatically loaded. A fallback will be used instead.'
+                                        );
+                                    }
+
+                                    continue;
+                                }
+                            }
+
+                            languages.extend(language);
+                        }
+
+                        options.translations = languages;
+                    } else {
+                        var baseTranslation = Translation.loadPath(
+                            this.defaults.amdLanguageBase + 'en'
+                        );
+                        var customTranslation = new Translation(options.language);
+
+                        customTranslation.extend(baseTranslation);
+
+                        options.translations = customTranslation;
+                    }
+
+                    return options;
+                };
+
+                Defaults.prototype.reset = function () {
+                    function stripDiacritics(text) {
+                        // Used 'uni range + named function' from http://jsperf.com/diacritics/18
+                        function match(a) {
+                            return DIACRITICS[a] || a;
+                        }
+
+                        return text.replace(/[^\u0000-\u007E]/g, match);
+                    }
+
+                    function matcher(params, data) {
+                        // Always return the object if there is nothing to compare
+                        if ($.trim(params.term) === '') {
+                            return data;
+                        }
+
+                        // Do a recursive check for options with children
+                        if (data.children && data.children.length > 0) {
+                            // Clone the data object if there are children
+                            // This is required as we modify the object to remove any non-matches
+                            var match = $.extend(true, {}, data);
+
+                            // Check each child of the option
+                            for (var c = data.children.length - 1; c >= 0; c--) {
+                                var child = data.children[c];
+
+                                var matches = matcher(params, child);
+
+                                // If there wasn't a match, remove the object in the array
+                                if (matches == null) {
+                                    match.children.splice(c, 1);
+                                }
+                            }
+
+                            // If any children matched, return the new object
+                            if (match.children.length > 0) {
+                                return match;
+                            }
+
+                            // If there were no matching children, check just the plain object
+                            return matcher(params, match);
+                        }
+
+                        var original = stripDiacritics(data.text).toUpperCase();
+                        var term = stripDiacritics(params.term).toUpperCase();
+
+                        // Check if the text contains the term
+                        if (original.indexOf(term) > -1) {
+                            return data;
+                        }
+
+                        // If it doesn't contain the term, don't return anything
+                        return null;
+                    }
+
+                    this.defaults = {
+                        amdBase: './',
+                        amdLanguageBase: './i18n/',
+                        closeOnSelect: true,
+                        debug: false,
+                        dropdownAutoWidth: false,
+                        escapeMarkup: Utils.escapeMarkup,
+                        language: EnglishTranslation,
+                        matcher: matcher,
+                        minimumInputLength: 0,
+                        maximumInputLength: 0,
+                        maximumSelectionLength: 0,
+                        minimumResultsForSearch: 0,
+                        selectOnClose: false,
+                        sorter: function (data) {
+                            return data;
+                        },
+                        templateResult: function (result) {
+                            return result.text;
+                        },
+                        templateSelection: function (selection) {
+                            return selection.text;
+                        },
+                        theme: 'default',
+                        width: 'resolve'
+                    };
+                };
+
+                Defaults.prototype.set = function (key, value) {
+                    var camelKey = $.camelCase(key);
+
+                    var data = {};
+                    data[camelKey] = value;
+
+                    var convertedData = Utils._convertData(data);
+
+                    $.extend(this.defaults, convertedData);
+                };
+
+                var defaults = new Defaults();
+
+                return defaults;
+            });
+
+            S2.define('select2/options', [
+                'require',
+                'jquery',
+                './defaults',
+                './utils'
+            ], function (require, $, Defaults, Utils) {
+                function Options(options, $element) {
+                    this.options = options;
+
+                    if ($element != null) {
+                        this.fromElement($element);
+                    }
+
+                    this.options = Defaults.apply(this.options);
+
+                    if ($element && $element.is('input')) {
+                        var InputCompat = require(this.get('amdBase') + 'compat/inputData');
+
+                        this.options.dataAdapter = Utils.Decorate(
+                            this.options.dataAdapter,
+                            InputCompat
+                        );
+                    }
+                }
+
+                Options.prototype.fromElement = function ($e) {
+                    var excludedData = ['select2'];
+
+                    if (this.options.multiple == null) {
+                        this.options.multiple = $e.prop('multiple');
+                    }
+
+                    if (this.options.disabled == null) {
+                        this.options.disabled = $e.prop('disabled');
+                    }
+
+                    if (this.options.language == null) {
+                        if ($e.prop('lang')) {
+                            this.options.language = $e.prop('lang').toLowerCase();
+                        } else if ($e.closest('[lang]').prop('lang')) {
+                            this.options.language = $e.closest('[lang]').prop('lang');
+                        }
+                    }
+
+                    if (this.options.dir == null) {
+                        if ($e.prop('dir')) {
+                            this.options.dir = $e.prop('dir');
+                        } else if ($e.closest('[dir]').prop('dir')) {
+                            this.options.dir = $e.closest('[dir]').prop('dir');
+                        } else {
+                            this.options.dir = 'ltr';
+                        }
+                    }
+
+                    $e.prop('disabled', this.options.disabled);
+                    $e.prop('multiple', this.options.multiple);
+
+                    if ($e.data('select2Tags')) {
+                        if (this.options.debug && window.console && console.warn) {
+                            console.warn(
+                                'Select2: The `data-select2-tags` attribute has been changed to ' +
+                                'use the `data-data` and `data-tags="true"` attributes and will be ' +
+                                'removed in future versions of Select2.'
+                            );
+                        }
+
+                        $e.data('data', $e.data('select2Tags'));
+                        $e.data('tags', true);
+                    }
+
+                    if ($e.data('ajaxUrl')) {
+                        if (this.options.debug && window.console && console.warn) {
+                            console.warn(
+                                'Select2: The `data-ajax-url` attribute has been changed to ' +
+                                '`data-ajax--url` and support for the old attribute will be removed' +
+                                ' in future versions of Select2.'
+                            );
+                        }
+
+                        $e.attr('ajax--url', $e.data('ajaxUrl'));
+                        $e.data('ajax--url', $e.data('ajaxUrl'));
+                    }
+
+                    var dataset = {};
+
+                    // Prefer the element's `dataset` attribute if it exists
+                    // jQuery 1.x does not correctly handle data attributes with multiple dashes
+                    if ($.fn.jquery && $.fn.jquery.substr(0, 2) == '1.' && $e[0].dataset) {
+                        dataset = $.extend(true, {}, $e[0].dataset, $e.data());
+                    } else {
+                        dataset = $e.data();
+                    }
+
+                    var data = $.extend(true, {}, dataset);
+
+                    data = Utils._convertData(data);
+
+                    for (var key in data) {
+                        if ($.inArray(key, excludedData) > -1) {
+                            continue;
+                        }
+
+                        if ($.isPlainObject(this.options[key])) {
+                            $.extend(this.options[key], data[key]);
+                        } else {
+                            this.options[key] = data[key];
+                        }
+                    }
+
+                    return this;
+                };
+
+                Options.prototype.get = function (key) {
+                    return this.options[key];
+                };
+
+                Options.prototype.set = function (key, val) {
+                    this.options[key] = val;
+                };
+
+                return Options;
+            });
+
+            S2.define('select2/core', [
+                'jquery',
+                './options',
+                './utils',
+                './keys'
+            ], function ($, Options, Utils, KEYS) {
+                var Select2 = function ($element, options) {
+                    if ($element.data('select2') != null) {
+                        $element.data('select2').destroy();
+                    }
+
+                    this.$element = $element;
+
+                    this.id = this._generateId($element);
+
+                    options = options || {};
+
+                    this.options = new Options(options, $element);
+
+                    Select2.__super__.constructor.call(this);
+
+                    // Set up the tabindex
+
+                    var tabindex = $element.attr('tabindex') || 0;
+                    $element.data('old-tabindex', tabindex);
+                    $element.attr('tabindex', '-1');
+
+                    // Set up containers and adapters
+
+                    var DataAdapter = this.options.get('dataAdapter');
+                    this.dataAdapter = new DataAdapter($element, this.options);
+
+                    var $container = this.render();
+
+                    this._placeContainer($container);
+
+                    var SelectionAdapter = this.options.get('selectionAdapter');
+                    this.selection = new SelectionAdapter($element, this.options);
+                    this.$selection = this.selection.render();
+
+                    this.selection.position(this.$selection, $container);
+
+                    var DropdownAdapter = this.options.get('dropdownAdapter');
+                    this.dropdown = new DropdownAdapter($element, this.options);
+                    this.$dropdown = this.dropdown.render();
+
+                    this.dropdown.position(this.$dropdown, $container);
+
+                    var ResultsAdapter = this.options.get('resultsAdapter');
+                    this.results = new ResultsAdapter($element, this.options, this.dataAdapter);
+                    this.$results = this.results.render();
+
+                    this.results.position(this.$results, this.$dropdown);
+
+                    // Bind events
+
+                    var self = this;
+
+                    // Bind the container to all of the adapters
+                    this._bindAdapters();
+
+                    // Register any DOM event handlers
+                    this._registerDomEvents();
+
+                    // Register any internal event handlers
+                    this._registerDataEvents();
+                    this._registerSelectionEvents();
+                    this._registerDropdownEvents();
+                    this._registerResultsEvents();
+                    this._registerEvents();
+
+                    // Set the initial state
+                    this.dataAdapter.current(function (initialData) {
+                        self.trigger('selection:update', {
+                            data: initialData
+                        });
+                    });
+
+                    // Hide the original select
+                    $element.addClass('select2-hidden-accessible');
+                    $element.attr('aria-hidden', 'true');
+
+                    // Synchronize any monitored attributes
+                    this._syncAttributes();
+
+                    $element.data('select2', this);
+                };
+
+                Utils.Extend(Select2, Utils.Observable);
+
+                Select2.prototype._generateId = function ($element) {
+                    var id = '';
+
+                    if ($element.attr('id') != null) {
+                        id = $element.attr('id');
+                    } else if ($element.attr('name') != null) {
+                        id = $element.attr('name') + '-' + Utils.generateChars(2);
+                    } else {
+                        id = Utils.generateChars(4);
+                    }
+
+                    id = id.replace(/(:|\.|\[|\]|,)/g, '');
+                    id = 'select2-' + id;
+
+                    return id;
+                };
+
+                Select2.prototype._placeContainer = function ($container) {
+                    $container.insertAfter(this.$element);
+
+                    var width = this._resolveWidth(this.$element, this.options.get('width'));
+
+                    if (width != null) {
+                        $container.css('width', width);
+                    }
+                };
+
+                Select2.prototype._resolveWidth = function ($element, method) {
+                    var WIDTH = /^width:(([-+]?([0-9]*\.)?[0-9]+)(px|em|ex|%|in|cm|mm|pt|pc))/i;
+
+                    if (method == 'resolve') {
+                        var styleWidth = this._resolveWidth($element, 'style');
+
+                        if (styleWidth != null) {
+                            return styleWidth;
+                        }
+
+                        return this._resolveWidth($element, 'element');
+                    }
+
+                    if (method == 'element') {
+                        var elementWidth = $element.outerWidth(false);
+
+                        if (elementWidth <= 0) {
+                            return 'auto';
+                        }
+
+                        return elementWidth + 'px';
+                    }
+
+                    if (method == 'style') {
+                        var style = $element.attr('style');
+
+                        if (typeof(style) !== 'string') {
+                            return null;
+                        }
+
+                        var attrs = style.split(';');
+
+                        for (var i = 0, l = attrs.length; i < l; i = i + 1) {
+                            var attr = attrs[i].replace(/\s/g, '');
+                            var matches = attr.match(WIDTH);
+
+                            if (matches !== null && matches.length >= 1) {
+                                return matches[1];
+                            }
+                        }
+
+                        return null;
+                    }
+
+                    return method;
+                };
+
+                Select2.prototype._bindAdapters = function () {
+                    this.dataAdapter.bind(this, this.$container);
+                    this.selection.bind(this, this.$container);
+
+                    this.dropdown.bind(this, this.$container);
+                    this.results.bind(this, this.$container);
+                };
+
+                Select2.prototype._registerDomEvents = function () {
+                    var self = this;
+
+                    this.$element.on('change.select2', function () {
+                        self.dataAdapter.current(function (data) {
+                            self.trigger('selection:update', {
+                                data: data
+                            });
+                        });
+                    });
+
+                    this.$element.on('focus.select2', function (evt) {
+                        self.trigger('focus', evt);
+                    });
+
+                    this._syncA = Utils.bind(this._syncAttributes, this);
+                    this._syncS = Utils.bind(this._syncSubtree, this);
+
+                    if (this.$element[0].attachEvent) {
+                        this.$element[0].attachEvent('onpropertychange', this._syncA);
+                    }
+
+                    var observer = window.MutationObserver ||
+                        window.WebKitMutationObserver ||
+                        window.MozMutationObserver
+                    ;
+
+                    if (observer != null) {
+                        this._observer = new observer(function (mutations) {
+                            $.each(mutations, self._syncA);
+                            $.each(mutations, self._syncS);
+                        });
+                        this._observer.observe(this.$element[0], {
+                            attributes: true,
+                            childList: true,
+                            subtree: false
+                        });
+                    } else if (this.$element[0].addEventListener) {
+                        this.$element[0].addEventListener(
+                            'DOMAttrModified',
+                            self._syncA,
+                            false
+                        );
+                        this.$element[0].addEventListener(
+                            'DOMNodeInserted',
+                            self._syncS,
+                            false
+                        );
+                        this.$element[0].addEventListener(
+                            'DOMNodeRemoved',
+                            self._syncS,
+                            false
+                        );
+                    }
+                };
+
+                Select2.prototype._registerDataEvents = function () {
+                    var self = this;
+
+                    this.dataAdapter.on('*', function (name, params) {
+                        self.trigger(name, params);
+                    });
+                };
+
+                Select2.prototype._registerSelectionEvents = function () {
+                    var self = this;
+                    var nonRelayEvents = ['toggle', 'focus'];
+
+                    this.selection.on('toggle', function () {
+                        self.toggleDropdown();
+                    });
+
+                    this.selection.on('focus', function (params) {
+                        self.focus(params);
+                    });
+
+                    this.selection.on('*', function (name, params) {
+                        if ($.inArray(name, nonRelayEvents) !== -1) {
+                            return;
+                        }
+
+                        self.trigger(name, params);
+                    });
+                };
+
+                Select2.prototype._registerDropdownEvents = function () {
+                    var self = this;
+
+                    this.dropdown.on('*', function (name, params) {
+                        self.trigger(name, params);
+                    });
+                };
+
+                Select2.prototype._registerResultsEvents = function () {
+                    var self = this;
+
+                    this.results.on('*', function (name, params) {
+                        self.trigger(name, params);
+                    });
+                };
+
+                Select2.prototype._registerEvents = function () {
+                    var self = this;
+
+                    this.on('open', function () {
+                        self.$container.addClass('select2-container--open');
+                    });
+
+                    this.on('close', function () {
+                        self.$container.removeClass('select2-container--open');
+                    });
+
+                    this.on('enable', function () {
+                        self.$container.removeClass('select2-container--disabled');
+                    });
+
+                    this.on('disable', function () {
+                        self.$container.addClass('select2-container--disabled');
+                    });
+
+                    this.on('blur', function () {
+                        self.$container.removeClass('select2-container--focus');
+                    });
+
+                    this.on('query', function (params) {
+                        if (!self.isOpen()) {
+                            self.trigger('open', {});
+                        }
+
+                        this.dataAdapter.query(params, function (data) {
+                            self.trigger('results:all', {
+                                data: data,
+                                query: params
+                            });
+                        });
+                    });
+
+                    this.on('query:append', function (params) {
+                        this.dataAdapter.query(params, function (data) {
+                            self.trigger('results:append', {
+                                data: data,
+                                query: params
+                            });
+                        });
+                    });
+
+                    this.on('keypress', function (evt) {
+                        var key = evt.which;
+
+                        if (self.isOpen()) {
+                            if (key === KEYS.ESC || key === KEYS.TAB ||
+                                (key === KEYS.UP && evt.altKey)) {
+                                self.close();
+
+                                evt.preventDefault();
+                            } else if (key === KEYS.ENTER) {
+                                self.trigger('results:select', {});
+
+                                evt.preventDefault();
+                            } else if ((key === KEYS.SPACE && evt.ctrlKey)) {
+                                self.trigger('results:toggle', {});
+
+                                evt.preventDefault();
+                            } else if (key === KEYS.UP) {
+                                self.trigger('results:previous', {});
+
+                                evt.preventDefault();
+                            } else if (key === KEYS.DOWN) {
+                                self.trigger('results:next', {});
+
+                                evt.preventDefault();
+                            }
+                        } else {
+                            if (key === KEYS.ENTER || key === KEYS.SPACE ||
+                                (key === KEYS.DOWN && evt.altKey)) {
+                                self.open();
+
+                                evt.preventDefault();
+                            }
+                        }
+                    });
+                };
+
+                Select2.prototype._syncAttributes = function () {
+                    this.options.set('disabled', this.$element.prop('disabled'));
+
+                    if (this.options.get('disabled')) {
+                        if (this.isOpen()) {
+                            this.close();
+                        }
+
+                        this.trigger('disable', {});
+                    } else {
+                        this.trigger('enable', {});
+                    }
+                };
+
+                Select2.prototype._syncSubtree = function (evt, mutations) {
+                    var changed = false;
+                    var self = this;
+
+                    // Ignore any mutation events raised for elements that aren't options or
+                    // optgroups. This handles the case when the select element is destroyed
+                    if (
+                        evt && evt.target && (
+                            evt.target.nodeName !== 'OPTION' && evt.target.nodeName !== 'OPTGROUP'
+                        )
+                    ) {
+                        return;
+                    }
+
+                    if (!mutations) {
+                        // If mutation events aren't supported, then we can only assume that the
+                        // change affected the selections
+                        changed = true;
+                    } else if (mutations.addedNodes && mutations.addedNodes.length > 0) {
+                        for (var n = 0; n < mutations.addedNodes.length; n++) {
+                            var node = mutations.addedNodes[n];
+
+                            if (node.selected) {
+                                changed = true;
+                            }
+                        }
+                    } else if (mutations.removedNodes && mutations.removedNodes.length > 0) {
+                        changed = true;
+                    }
+
+                    // Only re-pull the data if we think there is a change
+                    if (changed) {
+                        this.dataAdapter.current(function (currentData) {
+                            self.trigger('selection:update', {
+                                data: currentData
+                            });
+                        });
+                    }
+                };
+
+                /**
+                 * Override the trigger method to automatically trigger pre-events when
+                 * there are events that can be prevented.
+                 */
+                Select2.prototype.trigger = function (name, args) {
+                    var actualTrigger = Select2.__super__.trigger;
+                    var preTriggerMap = {
+                        'open': 'opening',
+                        'close': 'closing',
+                        'select': 'selecting',
+                        'unselect': 'unselecting'
+                    };
+
+                    if (args === undefined) {
+                        args = {};
+                    }
+
+                    if (name in preTriggerMap) {
+                        var preTriggerName = preTriggerMap[name];
+                        var preTriggerArgs = {
+                            prevented: false,
+                            name: name,
+                            args: args
+                        };
+
+                        actualTrigger.call(this, preTriggerName, preTriggerArgs);
+
+                        if (preTriggerArgs.prevented) {
+                            args.prevented = true;
+
+                            return;
+                        }
+                    }
+
+                    actualTrigger.call(this, name, args);
+                };
+
+                Select2.prototype.toggleDropdown = function () {
+                    if (this.options.get('disabled')) {
+                        return;
+                    }
+
+                    if (this.isOpen()) {
+                        this.close();
+                    } else {
+                        this.open();
+                    }
+                };
+
+                Select2.prototype.open = function () {
+                    if (this.isOpen()) {
+                        return;
+                    }
+
+                    this.trigger('query', {});
+                };
+
+                Select2.prototype.close = function () {
+                    if (!this.isOpen()) {
+                        return;
+                    }
+
+                    this.trigger('close', {});
+                };
+
+                Select2.prototype.isOpen = function () {
+                    return this.$container.hasClass('select2-container--open');
+                };
+
+                Select2.prototype.hasFocus = function () {
+                    return this.$container.hasClass('select2-container--focus');
+                };
+
+                Select2.prototype.focus = function (data) {
+                    // No need to re-trigger focus events if we are already focused
+                    if (this.hasFocus()) {
+                        return;
+                    }
+
+                    this.$container.addClass('select2-container--focus');
+                    this.trigger('focus', {});
+                };
+
+                Select2.prototype.enable = function (args) {
+                    if (this.options.get('debug') && window.console && console.warn) {
+                        console.warn(
+                            'Select2: The `select2("enable")` method has been deprecated and will' +
+                            ' be removed in later Select2 versions. Use $element.prop("disabled")' +
+                            ' instead.'
+                        );
+                    }
+
+                    if (args == null || args.length === 0) {
+                        args = [true];
+                    }
+
+                    var disabled = !args[0];
+
+                    this.$element.prop('disabled', disabled);
+                };
+
+                Select2.prototype.data = function () {
+                    if (this.options.get('debug') &&
+                        arguments.length > 0 && window.console && console.warn) {
+                        console.warn(
+                            'Select2: Data can no longer be set using `select2("data")`. You ' +
+                            'should consider setting the value instead using `$element.val()`.'
+                        );
+                    }
+
+                    var data = [];
+
+                    this.dataAdapter.current(function (currentData) {
+                        data = currentData;
+                    });
+
+                    return data;
+                };
+
+                Select2.prototype.val = function (args) {
+                    if (this.options.get('debug') && window.console && console.warn) {
+                        console.warn(
+                            'Select2: The `select2("val")` method has been deprecated and will be' +
+                            ' removed in later Select2 versions. Use $element.val() instead.'
+                        );
+                    }
+
+                    if (args == null || args.length === 0) {
+                        return this.$element.val();
+                    }
+
+                    var newVal = args[0];
+
+                    if ($.isArray(newVal)) {
+                        newVal = $.map(newVal, function (obj) {
+                            return obj.toString();
+                        });
+                    }
+
+                    this.$element.val(newVal).trigger('change');
+                };
+
+                Select2.prototype.destroy = function () {
+                    this.$container.remove();
+
+                    if (this.$element[0].detachEvent) {
+                        this.$element[0].detachEvent('onpropertychange', this._syncA);
+                    }
+
+                    if (this._observer != null) {
+                        this._observer.disconnect();
+                        this._observer = null;
+                    } else if (this.$element[0].removeEventListener) {
+                        this.$element[0]
+                            .removeEventListener('DOMAttrModified', this._syncA, false);
+                        this.$element[0]
+                            .removeEventListener('DOMNodeInserted', this._syncS, false);
+                        this.$element[0]
+                            .removeEventListener('DOMNodeRemoved', this._syncS, false);
+                    }
+
+                    this._syncA = null;
+                    this._syncS = null;
+
+                    this.$element.off('.select2');
+                    this.$element.attr('tabindex', this.$element.data('old-tabindex'));
+
+                    this.$element.removeClass('select2-hidden-accessible');
+                    this.$element.attr('aria-hidden', 'false');
+                    this.$element.removeData('select2');
+
+                    this.dataAdapter.destroy();
+                    this.selection.destroy();
+                    this.dropdown.destroy();
+                    this.results.destroy();
+
+                    this.dataAdapter = null;
+                    this.selection = null;
+                    this.dropdown = null;
+                    this.results = null;
+                };
+
+                Select2.prototype.render = function () {
+                    var $container = $(
+                        '<span class="select2 select2-container">' +
+                        '<span class="selection"></span>' +
+                        '<span class="dropdown-wrapper" aria-hidden="true"></span>' +
+                        '</span>'
+                    );
+
+                    $container.attr('dir', this.options.get('dir'));
+
+                    this.$container = $container;
+
+                    this.$container.addClass('select2-container--' + this.options.get('theme'));
+
+                    $container.data('element', this.$element);
+
+                    return $container;
+                };
+
+                return Select2;
+            });
+
+            S2.define('select2/compat/utils', [
+                'jquery'
+            ], function ($) {
+                function syncCssClasses($dest, $src, adapter) {
+                    var classes, replacements = [], adapted;
+
+                    classes = $.trim($dest.attr('class'));
+
+                    if (classes) {
+                        classes = '' + classes; // for IE which returns object
+
+                        $(classes.split(/\s+/)).each(function () {
+                            // Save all Select2 classes
+                            if (this.indexOf('select2-') === 0) {
+                                replacements.push(this);
+                            }
+                        });
+                    }
+
+                    classes = $.trim($src.attr('class'));
+
+                    if (classes) {
+                        classes = '' + classes; // for IE which returns object
+
+                        $(classes.split(/\s+/)).each(function () {
+                            // Only adapt non-Select2 classes
+                            if (this.indexOf('select2-') !== 0) {
+                                adapted = adapter(this);
+
+                                if (adapted != null) {
+                                    replacements.push(adapted);
+                                }
+                            }
+                        });
+                    }
+
+                    $dest.attr('class', replacements.join(' '));
+                }
+
+                return {
+                    syncCssClasses: syncCssClasses
+                };
+            });
+
+            S2.define('select2/compat/containerCss', [
+                'jquery',
+                './utils'
+            ], function ($, CompatUtils) {
+                // No-op CSS adapter that discards all classes by default
+                function _containerAdapter(clazz) {
+                    return null;
+                }
+
+                function ContainerCSS() {
+                }
+
+                ContainerCSS.prototype.render = function (decorated) {
+                    var $container = decorated.call(this);
+
+                    var containerCssClass = this.options.get('containerCssClass') || '';
+
+                    if ($.isFunction(containerCssClass)) {
+                        containerCssClass = containerCssClass(this.$element);
+                    }
+
+                    var containerCssAdapter = this.options.get('adaptContainerCssClass');
+                    containerCssAdapter = containerCssAdapter || _containerAdapter;
+
+                    if (containerCssClass.indexOf(':all:') !== -1) {
+                        containerCssClass = containerCssClass.replace(':all:', '');
+
+                        var _cssAdapter = containerCssAdapter;
+
+                        containerCssAdapter = function (clazz) {
+                            var adapted = _cssAdapter(clazz);
+
+                            if (adapted != null) {
+                                // Append the old one along with the adapted one
+                                return adapted + ' ' + clazz;
+                            }
+
+                            return clazz;
+                        };
+                    }
+
+                    var containerCss = this.options.get('containerCss') || {};
+
+                    if ($.isFunction(containerCss)) {
+                        containerCss = containerCss(this.$element);
+                    }
+
+                    CompatUtils.syncCssClasses($container, this.$element, containerCssAdapter);
+
+                    $container.css(containerCss);
+                    $container.addClass(containerCssClass);
+
+                    return $container;
+                };
+
+                return ContainerCSS;
+            });
+
+            S2.define('select2/compat/dropdownCss', [
+                'jquery',
+                './utils'
+            ], function ($, CompatUtils) {
+                // No-op CSS adapter that discards all classes by default
+                function _dropdownAdapter(clazz) {
+                    return null;
+                }
+
+                function DropdownCSS() {
+                }
+
+                DropdownCSS.prototype.render = function (decorated) {
+                    var $dropdown = decorated.call(this);
+
+                    var dropdownCssClass = this.options.get('dropdownCssClass') || '';
+
+                    if ($.isFunction(dropdownCssClass)) {
+                        dropdownCssClass = dropdownCssClass(this.$element);
+                    }
+
+                    var dropdownCssAdapter = this.options.get('adaptDropdownCssClass');
+                    dropdownCssAdapter = dropdownCssAdapter || _dropdownAdapter;
+
+                    if (dropdownCssClass.indexOf(':all:') !== -1) {
+                        dropdownCssClass = dropdownCssClass.replace(':all:', '');
+
+                        var _cssAdapter = dropdownCssAdapter;
+
+                        dropdownCssAdapter = function (clazz) {
+                            var adapted = _cssAdapter(clazz);
+
+                            if (adapted != null) {
+                                // Append the old one along with the adapted one
+                                return adapted + ' ' + clazz;
+                            }
+
+                            return clazz;
+                        };
+                    }
+
+                    var dropdownCss = this.options.get('dropdownCss') || {};
+
+                    if ($.isFunction(dropdownCss)) {
+                        dropdownCss = dropdownCss(this.$element);
+                    }
+
+                    CompatUtils.syncCssClasses($dropdown, this.$element, dropdownCssAdapter);
+
+                    $dropdown.css(dropdownCss);
+                    $dropdown.addClass(dropdownCssClass);
+
+                    return $dropdown;
+                };
+
+                return DropdownCSS;
+            });
+
+            S2.define('select2/compat/initSelection', [
+                'jquery'
+            ], function ($) {
+                function InitSelection(decorated, $element, options) {
+                    if (options.get('debug') && window.console && console.warn) {
+                        console.warn(
+                            'Select2: The `initSelection` option has been deprecated in favor' +
+                            ' of a custom data adapter that overrides the `current` method. ' +
+                            'This method is now called multiple times instead of a single ' +
+                            'time when the instance is initialized. Support will be removed ' +
+                            'for the `initSelection` option in future versions of Select2'
+                        );
+                    }
+
+                    this.initSelection = options.get('initSelection');
+                    this._isInitialized = false;
+
+                    decorated.call(this, $element, options);
+                }
+
+                InitSelection.prototype.current = function (decorated, callback) {
+                    var self = this;
+
+                    if (this._isInitialized) {
+                        decorated.call(this, callback);
+
+                        return;
+                    }
+
+                    this.initSelection.call(null, this.$element, function (data) {
+                        self._isInitialized = true;
+
+                        if (!$.isArray(data)) {
+                            data = [data];
+                        }
+
+                        callback(data);
+                    });
+                };
+
+                return InitSelection;
+            });
+
+            S2.define('select2/compat/inputData', [
+                'jquery'
+            ], function ($) {
+                function InputData(decorated, $element, options) {
+                    this._currentData = [];
+                    this._valueSeparator = options.get('valueSeparator') || ',';
+
+                    if ($element.prop('type') === 'hidden') {
+                        if (options.get('debug') && console && console.warn) {
+                            console.warn(
+                                'Select2: Using a hidden input with Select2 is no longer ' +
+                                'supported and may stop working in the future. It is recommended ' +
+                                'to use a `<select>` element instead.'
+                            );
+                        }
+                    }
+
+                    decorated.call(this, $element, options);
+                }
+
+                InputData.prototype.current = function (_, callback) {
+                    function getSelected(data, selectedIds) {
+                        var selected = [];
+
+                        if (data.selected || $.inArray(data.id, selectedIds) !== -1) {
+                            data.selected = true;
+                            selected.push(data);
+                        } else {
+                            data.selected = false;
+                        }
+
+                        if (data.children) {
+                            selected.push.apply(selected, getSelected(data.children, selectedIds));
+                        }
+
+                        return selected;
+                    }
+
+                    var selected = [];
+
+                    for (var d = 0; d < this._currentData.length; d++) {
+                        var data = this._currentData[d];
+
+                        selected.push.apply(
+                            selected,
+                            getSelected(
+                                data,
+                                this.$element.val().split(
+                                    this._valueSeparator
+                                )
+                            )
+                        );
+                    }
+
+                    callback(selected);
+                };
+
+                InputData.prototype.select = function (_, data) {
+                    if (!this.options.get('multiple')) {
+                        this.current(function (allData) {
+                            $.map(allData, function (data) {
+                                data.selected = false;
+                            });
+                        });
+
+                        this.$element.val(data.id);
+                        this.$element.trigger('change');
+                    } else {
+                        var value = this.$element.val();
+                        value += this._valueSeparator + data.id;
+
+                        this.$element.val(value);
+                        this.$element.trigger('change');
+                    }
+                };
+
+                InputData.prototype.unselect = function (_, data) {
+                    var self = this;
+
+                    data.selected = false;
+
+                    this.current(function (allData) {
+                        var values = [];
+
+                        for (var d = 0; d < allData.length; d++) {
+                            var item = allData[d];
+
+                            if (data.id == item.id) {
+                                continue;
+                            }
+
+                            values.push(item.id);
+                        }
+
+                        self.$element.val(values.join(self._valueSeparator));
+                        self.$element.trigger('change');
+                    });
+                };
+
+                InputData.prototype.query = function (_, params, callback) {
+                    var results = [];
+
+                    for (var d = 0; d < this._currentData.length; d++) {
+                        var data = this._currentData[d];
+
+                        var matches = this.matches(params, data);
+
+                        if (matches !== null) {
+                            results.push(matches);
+                        }
+                    }
+
+                    callback({
+                        results: results
+                    });
+                };
+
+                InputData.prototype.addOptions = function (_, $options) {
+                    var options = $.map($options, function ($option) {
+                        return $.data($option[0], 'data');
+                    });
+
+                    this._currentData.push.apply(this._currentData, options);
+                };
+
+                return InputData;
+            });
+
+            S2.define('select2/compat/matcher', [
+                'jquery'
+            ], function ($) {
+                function oldMatcher(matcher) {
+                    function wrappedMatcher(params, data) {
+                        var match = $.extend(true, {}, data);
+
+                        if (params.term == null || $.trim(params.term) === '') {
+                            return match;
+                        }
+
+                        if (data.children) {
+                            for (var c = data.children.length - 1; c >= 0; c--) {
+                                var child = data.children[c];
+
+                                // Check if the child object matches
+                                // The old matcher returned a boolean true or false
+                                var doesMatch = matcher(params.term, child.text, child);
+
+                                // If the child didn't match, pop it off
+                                if (!doesMatch) {
+                                    match.children.splice(c, 1);
+                                }
+                            }
+
+                            if (match.children.length > 0) {
+                                return match;
+                            }
+                        }
+
+                        if (matcher(params.term, data.text, data)) {
+                            return match;
+                        }
+
+                        return null;
+                    }
+
+                    return wrappedMatcher;
+                }
+
+                return oldMatcher;
+            });
+
+            S2.define('select2/compat/query', [], function () {
+                function Query(decorated, $element, options) {
+                    if (options.get('debug') && window.console && console.warn) {
+                        console.warn(
+                            'Select2: The `query` option has been deprecated in favor of a ' +
+                            'custom data adapter that overrides the `query` method. Support ' +
+                            'will be removed for the `query` option in future versions of ' +
+                            'Select2.'
+                        );
+                    }
+
+                    decorated.call(this, $element, options);
+                }
+
+                Query.prototype.query = function (_, params, callback) {
+                    params.callback = callback;
+
+                    var query = this.options.get('query');
+
+                    query.call(null, params);
+                };
+
+                return Query;
+            });
+
+            S2.define('select2/dropdown/attachContainer', [], function () {
+                function AttachContainer(decorated, $element, options) {
+                    decorated.call(this, $element, options);
+                }
+
+                AttachContainer.prototype.position =
+                    function (decorated, $dropdown, $container) {
+                        var $dropdownContainer = $container.find('.dropdown-wrapper');
+                        $dropdownContainer.append($dropdown);
+
+                        $dropdown.addClass('select2-dropdown--below');
+                        $container.addClass('select2-container--below');
+                    };
+
+                return AttachContainer;
+            });
+
+            S2.define('select2/dropdown/stopPropagation', [], function () {
+                function StopPropagation() {
+                }
+
+                StopPropagation.prototype.bind = function (decorated, container, $container) {
+                    decorated.call(this, container, $container);
+
+                    var stoppedEvents = [
+                        'blur',
+                        'change',
+                        'click',
+                        'dblclick',
+                        'focus',
+                        'focusin',
+                        'focusout',
+                        'input',
+                        'keydown',
+                        'keyup',
+                        'keypress',
+                        'mousedown',
+                        'mouseenter',
+                        'mouseleave',
+                        'mousemove',
+                        'mouseover',
+                        'mouseup',
+                        'search',
+                        'touchend',
+                        'touchstart'
+                    ];
+
+                    this.$dropdown.on(stoppedEvents.join(' '), function (evt) {
+                        evt.stopPropagation();
+                    });
+                };
+
+                return StopPropagation;
+            });
+
+            S2.define('select2/selection/stopPropagation', [], function () {
+                function StopPropagation() {
+                }
+
+                StopPropagation.prototype.bind = function (decorated, container, $container) {
+                    decorated.call(this, container, $container);
+
+                    var stoppedEvents = [
+                        'blur',
+                        'change',
+                        'click',
+                        'dblclick',
+                        'focus',
+                        'focusin',
+                        'focusout',
+                        'input',
+                        'keydown',
+                        'keyup',
+                        'keypress',
+                        'mousedown',
+                        'mouseenter',
+                        'mouseleave',
+                        'mousemove',
+                        'mouseover',
+                        'mouseup',
+                        'search',
+                        'touchend',
+                        'touchstart'
+                    ];
+
+                    this.$selection.on(stoppedEvents.join(' '), function (evt) {
+                        evt.stopPropagation();
+                    });
+                };
+
+                return StopPropagation;
+            });
+
+            /*!
+             * jQuery Mousewheel 3.1.13
+             *
+             * Copyright jQuery Foundation and other contributors
+             * Released under the MIT license
+             * http://jquery.org/license
+             */
+
+            (function (factory) {
+                if (typeof S2.define === 'function' && S2.define.amd) {
+                    // AMD. Register as an anonymous module.
+                    S2.define('jquery-mousewheel', ['jquery'], factory);
+                } else if (typeof exports === 'object') {
+                    // Node/CommonJS style for Browserify
+                    module.exports = factory;
+                } else {
+                    // Browser globals
+                    factory(jQuery);
+                }
+            }(function ($) {
+
+                var toFix = ['wheel', 'mousewheel', 'DOMMouseScroll', 'MozMousePixelScroll'],
+                    toBind = ( 'onwheel' in document || document.documentMode >= 9 ) ?
+                        ['wheel'] : ['mousewheel', 'DomMouseScroll', 'MozMousePixelScroll'],
+                    slice = Array.prototype.slice,
+                    nullLowestDeltaTimeout, lowestDelta;
+
+                if ($.event.fixHooks) {
+                    for (var i = toFix.length; i;) {
+                        $.event.fixHooks[toFix[--i]] = $.event.mouseHooks;
+                    }
+                }
+
+                var special = $.event.special.mousewheel = {
+                    version: '3.1.12',
+
+                    setup: function () {
+                        if (this.addEventListener) {
+                            for (var i = toBind.length; i;) {
+                                this.addEventListener(toBind[--i], handler, false);
+                            }
+                        } else {
+                            this.onmousewheel = handler;
+                        }
+                        // Store the line height and page height for this particular element
+                        $.data(this, 'mousewheel-line-height', special.getLineHeight(this));
+                        $.data(this, 'mousewheel-page-height', special.getPageHeight(this));
+                    },
+
+                    teardown: function () {
+                        if (this.removeEventListener) {
+                            for (var i = toBind.length; i;) {
+                                this.removeEventListener(toBind[--i], handler, false);
+                            }
+                        } else {
+                            this.onmousewheel = null;
+                        }
+                        // Clean up the data we added to the element
+                        $.removeData(this, 'mousewheel-line-height');
+                        $.removeData(this, 'mousewheel-page-height');
+                    },
+
+                    getLineHeight: function (elem) {
+                        var $elem = $(elem),
+                            $parent = $elem['offsetParent' in $.fn ? 'offsetParent' : 'parent']();
+                        if (!$parent.length) {
+                            $parent = $('body');
+                        }
+                        return parseInt($parent.css('fontSize'), 10) || parseInt($elem.css('fontSize'), 10) || 16;
+                    },
+
+                    getPageHeight: function (elem) {
+                        return $(elem).height();
+                    },
+
+                    settings: {
+                        adjustOldDeltas: true, // see shouldAdjustOldDeltas() below
+                        normalizeOffset: true  // calls getBoundingClientRect for each event
+                    }
+                };
+
+                $.fn.extend({
+                    mousewheel: function (fn) {
+                        return fn ? this.bind('mousewheel', fn) : this.trigger('mousewheel');
+                    },
+
+                    unmousewheel: function (fn) {
+                        return this.unbind('mousewheel', fn);
+                    }
+                });
+
+
+                function handler(event) {
+                    var orgEvent = event || window.event,
+                        args = slice.call(arguments, 1),
+                        delta = 0,
+                        deltaX = 0,
+                        deltaY = 0,
+                        absDelta = 0,
+                        offsetX = 0,
+                        offsetY = 0;
+                    event = $.event.fix(orgEvent);
+                    event.type = 'mousewheel';
+
+                    // Old school scrollwheel delta
+                    if ('detail' in orgEvent) {
+                        deltaY = orgEvent.detail * -1;
+                    }
+                    if ('wheelDelta' in orgEvent) {
+                        deltaY = orgEvent.wheelDelta;
+                    }
+                    if ('wheelDeltaY' in orgEvent) {
+                        deltaY = orgEvent.wheelDeltaY;
+                    }
+                    if ('wheelDeltaX' in orgEvent) {
+                        deltaX = orgEvent.wheelDeltaX * -1;
+                    }
+
+                    // Firefox < 17 horizontal scrolling related to DOMMouseScroll event
+                    if ('axis' in orgEvent && orgEvent.axis === orgEvent.HORIZONTAL_AXIS) {
+                        deltaX = deltaY * -1;
+                        deltaY = 0;
+                    }
+
+                    // Set delta to be deltaY or deltaX if deltaY is 0 for backwards compatabilitiy
+                    delta = deltaY === 0 ? deltaX : deltaY;
+
+                    // New school wheel delta (wheel event)
+                    if ('deltaY' in orgEvent) {
+                        deltaY = orgEvent.deltaY * -1;
+                        delta = deltaY;
+                    }
+                    if ('deltaX' in orgEvent) {
+                        deltaX = orgEvent.deltaX;
+                        if (deltaY === 0) {
+                            delta = deltaX * -1;
+                        }
+                    }
+
+                    // No change actually happened, no reason to go any further
+                    if (deltaY === 0 && deltaX === 0) {
+                        return;
+                    }
+
+                    // Need to convert lines and pages to pixels if we aren't already in pixels
+                    // There are three delta modes:
+                    //   * deltaMode 0 is by pixels, nothing to do
+                    //   * deltaMode 1 is by lines
+                    //   * deltaMode 2 is by pages
+                    if (orgEvent.deltaMode === 1) {
+                        var lineHeight = $.data(this, 'mousewheel-line-height');
+                        delta *= lineHeight;
+                        deltaY *= lineHeight;
+                        deltaX *= lineHeight;
+                    } else if (orgEvent.deltaMode === 2) {
+                        var pageHeight = $.data(this, 'mousewheel-page-height');
+                        delta *= pageHeight;
+                        deltaY *= pageHeight;
+                        deltaX *= pageHeight;
+                    }
+
+                    // Store lowest absolute delta to normalize the delta values
+                    absDelta = Math.max(Math.abs(deltaY), Math.abs(deltaX));
+
+                    if (!lowestDelta || absDelta < lowestDelta) {
+                        lowestDelta = absDelta;
+
+                        // Adjust older deltas if necessary
+                        if (shouldAdjustOldDeltas(orgEvent, absDelta)) {
+                            lowestDelta /= 40;
+                        }
+                    }
+
+                    // Adjust older deltas if necessary
+                    if (shouldAdjustOldDeltas(orgEvent, absDelta)) {
+                        // Divide all the things by 40!
+                        delta /= 40;
+                        deltaX /= 40;
+                        deltaY /= 40;
+                    }
+
+                    // Get a whole, normalized value for the deltas
+                    delta = Math[delta >= 1 ? 'floor' : 'ceil'](delta / lowestDelta);
+                    deltaX = Math[deltaX >= 1 ? 'floor' : 'ceil'](deltaX / lowestDelta);
+                    deltaY = Math[deltaY >= 1 ? 'floor' : 'ceil'](deltaY / lowestDelta);
+
+                    // Normalise offsetX and offsetY properties
+                    if (special.settings.normalizeOffset && this.getBoundingClientRect) {
+                        var boundingRect = this.getBoundingClientRect();
+                        offsetX = event.clientX - boundingRect.left;
+                        offsetY = event.clientY - boundingRect.top;
+                    }
+
+                    // Add information to the event object
+                    event.deltaX = deltaX;
+                    event.deltaY = deltaY;
+                    event.deltaFactor = lowestDelta;
+                    event.offsetX = offsetX;
+                    event.offsetY = offsetY;
+                    // Go ahead and set deltaMode to 0 since we converted to pixels
+                    // Although this is a little odd since we overwrite the deltaX/Y
+                    // properties with normalized deltas.
+                    event.deltaMode = 0;
+
+                    // Add event and delta to the front of the arguments
+                    args.unshift(event, delta, deltaX, deltaY);
+
+                    // Clearout lowestDelta after sometime to better
+                    // handle multiple device types that give different
+                    // a different lowestDelta
+                    // Ex: trackpad = 3 and mouse wheel = 120
+                    if (nullLowestDeltaTimeout) {
+                        clearTimeout(nullLowestDeltaTimeout);
+                    }
+                    nullLowestDeltaTimeout = setTimeout(nullLowestDelta, 200);
+
+                    return ($.event.dispatch || $.event.handle).apply(this, args);
+                }
+
+                function nullLowestDelta() {
+                    lowestDelta = null;
+                }
+
+                function shouldAdjustOldDeltas(orgEvent, absDelta) {
+                    // If this is an older event and the delta is divisable by 120,
+                    // then we are assuming that the browser is treating this as an
+                    // older mouse wheel event and that we should divide the deltas
+                    // by 40 to try and get a more usable deltaFactor.
+                    // Side note, this actually impacts the reported scroll distance
+                    // in older browsers and can cause scrolling to be slower than native.
+                    // Turn this off by setting $.event.special.mousewheel.settings.adjustOldDeltas to false.
+                    return special.settings.adjustOldDeltas && orgEvent.type === 'mousewheel' && absDelta % 120 === 0;
+                }
+
+            }));
+
+            S2.define('jquery.select2', [
+                'jquery',
+                'jquery-mousewheel',
+
+                './select2/core',
+                './select2/defaults'
+            ], function ($, _, Select2, Defaults) {
+                if ($.fn.select2 == null) {
+                    // All methods that should return the element
+                    var thisMethods = ['open', 'close', 'destroy'];
+
+                    $.fn.select2 = function (options) {
+                        options = options || {};
+
+                        if (typeof options === 'object') {
+                            this.each(function () {
+                                var instanceOptions = $.extend(true, {}, options);
+
+                                var instance = new Select2($(this), instanceOptions);
+                            });
+
+                            return this;
+                        } else if (typeof options === 'string') {
+                            var ret;
+                            var args = Array.prototype.slice.call(arguments, 1);
+
+                            this.each(function () {
+                                var instance = $(this).data('select2');
+
+                                if (instance == null && window.console && console.error) {
+                                    console.error(
+                                        'The select2(\'' + options + '\') method was called on an ' +
+                                        'element that is not using Select2.'
+                                    );
+                                }
+
+                                ret = instance[options].apply(instance, args);
+                            });
+
+                            // Check if we should be returning `this`
+                            if ($.inArray(options, thisMethods) > -1) {
+                                return this;
+                            }
+
+                            return ret;
+                        } else {
+                            throw new Error('Invalid arguments for Select2: ' + options);
+                        }
+                    };
+                }
+
+                if ($.fn.select2.defaults == null) {
+                    $.fn.select2.defaults = Defaults;
+                }
+
+                return Select2;
+            });
+
+            // Return the AMD loader configuration so it can be used outside of this file
+            return {
+                define: S2.define,
+                require: S2.require
+            };
+        }());
+
+    // Autoload the jQuery bindings
+    // We know that all of the modules exist above this, so we're safe
+    var select2 = S2.require('jquery.select2');
+
+    // Hold the AMD module references on the jQuery function that was just loaded
+    // This allows Select2 to use the internal loader outside of this file, such
+    // as in the language files.
+    jQuery.fn.select2.amd = S2;
+
+    // Return the Select2 instance for anyone who is importing it.
+    return select2;
+}));

File diff suppressed because it is too large
+ 0 - 0
src/assets/select2/js/select2.full.min.js


+ 5717 - 0
src/assets/select2/js/select2.js

@@ -0,0 +1,5717 @@
+/*!
+ * Select2 4.0.3
+ * https://select2.github.io
+ *
+ * Released under the MIT license
+ * https://github.com/select2/select2/blob/master/LICENSE.md
+ */
+(function (factory) {
+    if (typeof define === 'function' && define.amd) {
+        // AMD. Register as an anonymous module.
+        define(['jquery'], factory);
+    } else if (typeof exports === 'object') {
+        // Node/CommonJS
+        factory(require('jquery'));
+    } else {
+        // Browser globals
+        factory(jQuery);
+    }
+}(function (jQuery) {
+    // This is needed so we can catch the AMD loader configuration and use it
+    // The inner file should be wrapped (by `banner.start.js`) in a function that
+    // returns the AMD loader references.
+    var S2 =
+        (function () {
+            // Restore the Select2 AMD loader so it can be used
+            // Needed mostly in the language files, where the loader is not inserted
+            if (jQuery && jQuery.fn && jQuery.fn.select2 && jQuery.fn.select2.amd) {
+                var S2 = jQuery.fn.select2.amd;
+            }
+            var S2;
+            (function () {
+                if (!S2 || !S2.requirejs) {
+                    if (!S2) {
+                        S2 = {};
+                    } else {
+                        require = S2;
+                    }
+                    /**
+                     * @license almond 0.3.1 Copyright (c) 2011-2014, The Dojo Foundation All Rights Reserved.
+                     * Available via the MIT or new BSD license.
+                     * see: http://github.com/jrburke/almond for details
+                     */
+//Going sloppy to avoid 'use strict' string cost, but strict practices should
+//be followed.
+                    /*jslint sloppy: true */
+                    /*global setTimeout: false */
+
+                    var requirejs, require, define;
+                    (function (undef) {
+                        var main, req, makeMap, handlers,
+                            defined = {},
+                            waiting = {},
+                            config = {},
+                            defining = {},
+                            hasOwn = Object.prototype.hasOwnProperty,
+                            aps = [].slice,
+                            jsSuffixRegExp = /\.js$/;
+
+                        function hasProp(obj, prop) {
+                            return hasOwn.call(obj, prop);
+                        }
+
+                        /**
+                         * Given a relative module name, like ./something, normalize it to
+                         * a real name that can be mapped to a path.
+                         * @param {String} name the relative name
+                         * @param {String} baseName a real name that the name arg is relative
+                         * to.
+                         * @returns {String} normalized name
+                         */
+                        function normalize(name, baseName) {
+                            var nameParts, nameSegment, mapValue, foundMap, lastIndex,
+                                foundI, foundStarMap, starI, i, j, part,
+                                baseParts = baseName && baseName.split("/"),
+                                map = config.map,
+                                starMap = (map && map['*']) || {};
+
+                            //Adjust any relative paths.
+                            if (name && name.charAt(0) === ".") {
+                                //If have a base name, try to normalize against it,
+                                //otherwise, assume it is a top-level require that will
+                                //be relative to baseUrl in the end.
+                                if (baseName) {
+                                    name = name.split('/');
+                                    lastIndex = name.length - 1;
+
+                                    // Node .js allowance:
+                                    if (config.nodeIdCompat && jsSuffixRegExp.test(name[lastIndex])) {
+                                        name[lastIndex] = name[lastIndex].replace(jsSuffixRegExp, '');
+                                    }
+
+                                    //Lop off the last part of baseParts, so that . matches the
+                                    //"directory" and not name of the baseName's module. For instance,
+                                    //baseName of "one/two/three", maps to "one/two/three.js", but we
+                                    //want the directory, "one/two" for this normalization.
+                                    name = baseParts.slice(0, baseParts.length - 1).concat(name);
+
+                                    //start trimDots
+                                    for (i = 0; i < name.length; i += 1) {
+                                        part = name[i];
+                                        if (part === ".") {
+                                            name.splice(i, 1);
+                                            i -= 1;
+                                        } else if (part === "..") {
+                                            if (i === 1 && (name[2] === '..' || name[0] === '..')) {
+                                                //End of the line. Keep at least one non-dot
+                                                //path segment at the front so it can be mapped
+                                                //correctly to disk. Otherwise, there is likely
+                                                //no path mapping for a path starting with '..'.
+                                                //This can still fail, but catches the most reasonable
+                                                //uses of ..
+                                                break;
+                                            } else if (i > 0) {
+                                                name.splice(i - 1, 2);
+                                                i -= 2;
+                                            }
+                                        }
+                                    }
+                                    //end trimDots
+
+                                    name = name.join("/");
+                                } else if (name.indexOf('./') === 0) {
+                                    // No baseName, so this is ID is resolved relative
+                                    // to baseUrl, pull off the leading dot.
+                                    name = name.substring(2);
+                                }
+                            }
+
+                            //Apply map config if available.
+                            if ((baseParts || starMap) && map) {
+                                nameParts = name.split('/');
+
+                                for (i = nameParts.length; i > 0; i -= 1) {
+                                    nameSegment = nameParts.slice(0, i).join("/");
+
+                                    if (baseParts) {
+                                        //Find the longest baseName segment match in the config.
+                                        //So, do joins on the biggest to smallest lengths of baseParts.
+                                        for (j = baseParts.length; j > 0; j -= 1) {
+                                            mapValue = map[baseParts.slice(0, j).join('/')];
+
+                                            //baseName segment has  config, find if it has one for
+                                            //this name.
+                                            if (mapValue) {
+                                                mapValue = mapValue[nameSegment];
+                                                if (mapValue) {
+                                                    //Match, update name to the new value.
+                                                    foundMap = mapValue;
+                                                    foundI = i;
+                                                    break;
+                                                }
+                                            }
+                                        }
+                                    }
+
+                                    if (foundMap) {
+                                        break;
+                                    }
+
+                                    //Check for a star map match, but just hold on to it,
+                                    //if there is a shorter segment match later in a matching
+                                    //config, then favor over this star map.
+                                    if (!foundStarMap && starMap && starMap[nameSegment]) {
+                                        foundStarMap = starMap[nameSegment];
+                                        starI = i;
+                                    }
+                                }
+
+                                if (!foundMap && foundStarMap) {
+                                    foundMap = foundStarMap;
+                                    foundI = starI;
+                                }
+
+                                if (foundMap) {
+                                    nameParts.splice(0, foundI, foundMap);
+                                    name = nameParts.join('/');
+                                }
+                            }
+
+                            return name;
+                        }
+
+                        function makeRequire(relName, forceSync) {
+                            return function () {
+                                //A version of a require function that passes a moduleName
+                                //value for items that may need to
+                                //look up paths relative to the moduleName
+                                var args = aps.call(arguments, 0);
+
+                                //If first arg is not require('string'), and there is only
+                                //one arg, it is the array form without a callback. Insert
+                                //a null so that the following concat is correct.
+                                if (typeof args[0] !== 'string' && args.length === 1) {
+                                    args.push(null);
+                                }
+                                return req.apply(undef, args.concat([relName, forceSync]));
+                            };
+                        }
+
+                        function makeNormalize(relName) {
+                            return function (name) {
+                                return normalize(name, relName);
+                            };
+                        }
+
+                        function makeLoad(depName) {
+                            return function (value) {
+                                defined[depName] = value;
+                            };
+                        }
+
+                        function callDep(name) {
+                            if (hasProp(waiting, name)) {
+                                var args = waiting[name];
+                                delete waiting[name];
+                                defining[name] = true;
+                                main.apply(undef, args);
+                            }
+
+                            if (!hasProp(defined, name) && !hasProp(defining, name)) {
+                                throw new Error('No ' + name);
+                            }
+                            return defined[name];
+                        }
+
+                        //Turns a plugin!resource to [plugin, resource]
+                        //with the plugin being undefined if the name
+                        //did not have a plugin prefix.
+                        function splitPrefix(name) {
+                            var prefix,
+                                index = name ? name.indexOf('!') : -1;
+                            if (index > -1) {
+                                prefix = name.substring(0, index);
+                                name = name.substring(index + 1, name.length);
+                            }
+                            return [prefix, name];
+                        }
+
+                        /**
+                         * Makes a name map, normalizing the name, and using a plugin
+                         * for normalization if necessary. Grabs a ref to plugin
+                         * too, as an optimization.
+                         */
+                        makeMap = function (name, relName) {
+                            var plugin,
+                                parts = splitPrefix(name),
+                                prefix = parts[0];
+
+                            name = parts[1];
+
+                            if (prefix) {
+                                prefix = normalize(prefix, relName);
+                                plugin = callDep(prefix);
+                            }
+
+                            //Normalize according
+                            if (prefix) {
+                                if (plugin && plugin.normalize) {
+                                    name = plugin.normalize(name, makeNormalize(relName));
+                                } else {
+                                    name = normalize(name, relName);
+                                }
+                            } else {
+                                name = normalize(name, relName);
+                                parts = splitPrefix(name);
+                                prefix = parts[0];
+                                name = parts[1];
+                                if (prefix) {
+                                    plugin = callDep(prefix);
+                                }
+                            }
+
+                            //Using ridiculous property names for space reasons
+                            return {
+                                f: prefix ? prefix + '!' + name : name, //fullName
+                                n: name,
+                                pr: prefix,
+                                p: plugin
+                            };
+                        };
+
+                        function makeConfig(name) {
+                            return function () {
+                                return (config && config.config && config.config[name]) || {};
+                            };
+                        }
+
+                        handlers = {
+                            require: function (name) {
+                                return makeRequire(name);
+                            },
+                            exports: function (name) {
+                                var e = defined[name];
+                                if (typeof e !== 'undefined') {
+                                    return e;
+                                } else {
+                                    return (defined[name] = {});
+                                }
+                            },
+                            module: function (name) {
+                                return {
+                                    id: name,
+                                    uri: '',
+                                    exports: defined[name],
+                                    config: makeConfig(name)
+                                };
+                            }
+                        };
+
+                        main = function (name, deps, callback, relName) {
+                            var cjsModule, depName, ret, map, i,
+                                args = [],
+                                callbackType = typeof callback,
+                                usingExports;
+
+                            //Use name if no relName
+                            relName = relName || name;
+
+                            //Call the callback to define the module, if necessary.
+                            if (callbackType === 'undefined' || callbackType === 'function') {
+                                //Pull out the defined dependencies and pass the ordered
+                                //values to the callback.
+                                //Default to [require, exports, module] if no deps
+                                deps = !deps.length && callback.length ? ['require', 'exports', 'module'] : deps;
+                                for (i = 0; i < deps.length; i += 1) {
+                                    map = makeMap(deps[i], relName);
+                                    depName = map.f;
+
+                                    //Fast path CommonJS standard dependencies.
+                                    if (depName === "require") {
+                                        args[i] = handlers.require(name);
+                                    } else if (depName === "exports") {
+                                        //CommonJS module spec 1.1
+                                        args[i] = handlers.exports(name);
+                                        usingExports = true;
+                                    } else if (depName === "module") {
+                                        //CommonJS module spec 1.1
+                                        cjsModule = args[i] = handlers.module(name);
+                                    } else if (hasProp(defined, depName) ||
+                                        hasProp(waiting, depName) ||
+                                        hasProp(defining, depName)) {
+                                        args[i] = callDep(depName);
+                                    } else if (map.p) {
+                                        map.p.load(map.n, makeRequire(relName, true), makeLoad(depName), {});
+                                        args[i] = defined[depName];
+                                    } else {
+                                        throw new Error(name + ' missing ' + depName);
+                                    }
+                                }
+
+                                ret = callback ? callback.apply(defined[name], args) : undefined;
+
+                                if (name) {
+                                    //If setting exports via "module" is in play,
+                                    //favor that over return value and exports. After that,
+                                    //favor a non-undefined return value over exports use.
+                                    if (cjsModule && cjsModule.exports !== undef &&
+                                        cjsModule.exports !== defined[name]) {
+                                        defined[name] = cjsModule.exports;
+                                    } else if (ret !== undef || !usingExports) {
+                                        //Use the return value from the function.
+                                        defined[name] = ret;
+                                    }
+                                }
+                            } else if (name) {
+                                //May just be an object definition for the module. Only
+                                //worry about defining if have a module name.
+                                defined[name] = callback;
+                            }
+                        };
+
+                        requirejs = require = req = function (deps, callback, relName, forceSync, alt) {
+                            if (typeof deps === "string") {
+                                if (handlers[deps]) {
+                                    //callback in this case is really relName
+                                    return handlers[deps](callback);
+                                }
+                                //Just return the module wanted. In this scenario, the
+                                //deps arg is the module name, and second arg (if passed)
+                                //is just the relName.
+                                //Normalize module name, if it contains . or ..
+                                return callDep(makeMap(deps, callback).f);
+                            } else if (!deps.splice) {
+                                //deps is a config object, not an array.
+                                config = deps;
+                                if (config.deps) {
+                                    req(config.deps, config.callback);
+                                }
+                                if (!callback) {
+                                    return;
+                                }
+
+                                if (callback.splice) {
+                                    //callback is an array, which means it is a dependency list.
+                                    //Adjust args if there are dependencies
+                                    deps = callback;
+                                    callback = relName;
+                                    relName = null;
+                                } else {
+                                    deps = undef;
+                                }
+                            }
+
+                            //Support require(['a'])
+                            callback = callback || function () {
+                            };
+
+                            //If relName is a function, it is an errback handler,
+                            //so remove it.
+                            if (typeof relName === 'function') {
+                                relName = forceSync;
+                                forceSync = alt;
+                            }
+
+                            //Simulate async callback;
+                            if (forceSync) {
+                                main(undef, deps, callback, relName);
+                            } else {
+                                //Using a non-zero value because of concern for what old browsers
+                                //do, and latest browsers "upgrade" to 4 if lower value is used:
+                                //http://www.whatwg.org/specs/web-apps/current-work/multipage/timers.html#dom-windowtimers-settimeout:
+                                //If want a value immediately, use require('id') instead -- something
+                                //that works in almond on the global level, but not guaranteed and
+                                //unlikely to work in other AMD implementations.
+                                setTimeout(function () {
+                                    main(undef, deps, callback, relName);
+                                }, 4);
+                            }
+
+                            return req;
+                        };
+
+                        /**
+                         * Just drops the config on the floor, but returns req in case
+                         * the config return value is used.
+                         */
+                        req.config = function (cfg) {
+                            return req(cfg);
+                        };
+
+                        /**
+                         * Expose module registry for debugging and tooling
+                         */
+                        requirejs._defined = defined;
+
+                        define = function (name, deps, callback) {
+                            if (typeof name !== 'string') {
+                                throw new Error('See almond README: incorrect module build, no module name');
+                            }
+
+                            //This module may not have dependencies
+                            if (!deps.splice) {
+                                //deps is not an array, so probably means
+                                //an object literal or factory function for
+                                //the value. Adjust args.
+                                callback = deps;
+                                deps = [];
+                            }
+
+                            if (!hasProp(defined, name) && !hasProp(waiting, name)) {
+                                waiting[name] = [name, deps, callback];
+                            }
+                        };
+
+                        define.amd = {
+                            jQuery: true
+                        };
+                    }());
+
+                    S2.requirejs = requirejs;
+                    S2.require = require;
+                    S2.define = define;
+                }
+            }());
+            S2.define("almond", function () {
+            });
+
+            /* global jQuery:false, $:false */
+            S2.define('jquery', [], function () {
+                var _$ = jQuery || $;
+
+                if (_$ == null && console && console.error) {
+                    console.error(
+                        'Select2: An instance of jQuery or a jQuery-compatible library was not ' +
+                        'found. Make sure that you are including jQuery before Select2 on your ' +
+                        'web page.'
+                    );
+                }
+
+                return _$;
+            });
+
+            S2.define('select2/utils', [
+                'jquery'
+            ], function ($) {
+                var Utils = {};
+
+                Utils.Extend = function (ChildClass, SuperClass) {
+                    var __hasProp = {}.hasOwnProperty;
+
+                    function BaseConstructor() {
+                        this.constructor = ChildClass;
+                    }
+
+                    for (var key in SuperClass) {
+                        if (__hasProp.call(SuperClass, key)) {
+                            ChildClass[key] = SuperClass[key];
+                        }
+                    }
+
+                    BaseConstructor.prototype = SuperClass.prototype;
+                    ChildClass.prototype = new BaseConstructor();
+                    ChildClass.__super__ = SuperClass.prototype;
+
+                    return ChildClass;
+                };
+
+                function getMethods(theClass) {
+                    var proto = theClass.prototype;
+
+                    var methods = [];
+
+                    for (var methodName in proto) {
+                        var m = proto[methodName];
+
+                        if (typeof m !== 'function') {
+                            continue;
+                        }
+
+                        if (methodName === 'constructor') {
+                            continue;
+                        }
+
+                        methods.push(methodName);
+                    }
+
+                    return methods;
+                }
+
+                Utils.Decorate = function (SuperClass, DecoratorClass) {
+                    var decoratedMethods = getMethods(DecoratorClass);
+                    var superMethods = getMethods(SuperClass);
+
+                    function DecoratedClass() {
+                        var unshift = Array.prototype.unshift;
+
+                        var argCount = DecoratorClass.prototype.constructor.length;
+
+                        var calledConstructor = SuperClass.prototype.constructor;
+
+                        if (argCount > 0) {
+                            unshift.call(arguments, SuperClass.prototype.constructor);
+
+                            calledConstructor = DecoratorClass.prototype.constructor;
+                        }
+
+                        calledConstructor.apply(this, arguments);
+                    }
+
+                    DecoratorClass.displayName = SuperClass.displayName;
+
+                    function ctr() {
+                        this.constructor = DecoratedClass;
+                    }
+
+                    DecoratedClass.prototype = new ctr();
+
+                    for (var m = 0; m < superMethods.length; m++) {
+                        var superMethod = superMethods[m];
+
+                        DecoratedClass.prototype[superMethod] =
+                            SuperClass.prototype[superMethod];
+                    }
+
+                    var calledMethod = function (methodName) {
+                        // Stub out the original method if it's not decorating an actual method
+                        var originalMethod = function () {
+                        };
+
+                        if (methodName in DecoratedClass.prototype) {
+                            originalMethod = DecoratedClass.prototype[methodName];
+                        }
+
+                        var decoratedMethod = DecoratorClass.prototype[methodName];
+
+                        return function () {
+                            var unshift = Array.prototype.unshift;
+
+                            unshift.call(arguments, originalMethod);
+
+                            return decoratedMethod.apply(this, arguments);
+                        };
+                    };
+
+                    for (var d = 0; d < decoratedMethods.length; d++) {
+                        var decoratedMethod = decoratedMethods[d];
+
+                        DecoratedClass.prototype[decoratedMethod] = calledMethod(decoratedMethod);
+                    }
+
+                    return DecoratedClass;
+                };
+
+                var Observable = function () {
+                    this.listeners = {};
+                };
+
+                Observable.prototype.on = function (event, callback) {
+                    this.listeners = this.listeners || {};
+
+                    if (event in this.listeners) {
+                        this.listeners[event].push(callback);
+                    } else {
+                        this.listeners[event] = [callback];
+                    }
+                };
+
+                Observable.prototype.trigger = function (event) {
+                    var slice = Array.prototype.slice;
+                    var params = slice.call(arguments, 1);
+
+                    this.listeners = this.listeners || {};
+
+                    // Params should always come in as an array
+                    if (params == null) {
+                        params = [];
+                    }
+
+                    // If there are no arguments to the event, use a temporary object
+                    if (params.length === 0) {
+                        params.push({});
+                    }
+
+                    // Set the `_type` of the first object to the event
+                    params[0]._type = event;
+
+                    if (event in this.listeners) {
+                        this.invoke(this.listeners[event], slice.call(arguments, 1));
+                    }
+
+                    if ('*' in this.listeners) {
+                        this.invoke(this.listeners['*'], arguments);
+                    }
+                };
+
+                Observable.prototype.invoke = function (listeners, params) {
+                    for (var i = 0, len = listeners.length; i < len; i++) {
+                        listeners[i].apply(this, params);
+                    }
+                };
+
+                Utils.Observable = Observable;
+
+                Utils.generateChars = function (length) {
+                    var chars = '';
+
+                    for (var i = 0; i < length; i++) {
+                        var randomChar = Math.floor(Math.random() * 36);
+                        chars += randomChar.toString(36);
+                    }
+
+                    return chars;
+                };
+
+                Utils.bind = function (func, context) {
+                    return function () {
+                        func.apply(context, arguments);
+                    };
+                };
+
+                Utils._convertData = function (data) {
+                    for (var originalKey in data) {
+                        var keys = originalKey.split('-');
+
+                        var dataLevel = data;
+
+                        if (keys.length === 1) {
+                            continue;
+                        }
+
+                        for (var k = 0; k < keys.length; k++) {
+                            var key = keys[k];
+
+                            // Lowercase the first letter
+                            // By default, dash-separated becomes camelCase
+                            key = key.substring(0, 1).toLowerCase() + key.substring(1);
+
+                            if (!(key in dataLevel)) {
+                                dataLevel[key] = {};
+                            }
+
+                            if (k == keys.length - 1) {
+                                dataLevel[key] = data[originalKey];
+                            }
+
+                            dataLevel = dataLevel[key];
+                        }
+
+                        delete data[originalKey];
+                    }
+
+                    return data;
+                };
+
+                Utils.hasScroll = function (index, el) {
+                    // Adapted from the function created by @ShadowScripter
+                    // and adapted by @BillBarry on the Stack Exchange Code Review website.
+                    // The original code can be found at
+                    // http://codereview.stackexchange.com/q/13338
+                    // and was designed to be used with the Sizzle selector engine.
+
+                    var $el = $(el);
+                    var overflowX = el.style.overflowX;
+                    var overflowY = el.style.overflowY;
+
+                    //Check both x and y declarations
+                    if (overflowX === overflowY &&
+                        (overflowY === 'hidden' || overflowY === 'visible')) {
+                        return false;
+                    }
+
+                    if (overflowX === 'scroll' || overflowY === 'scroll') {
+                        return true;
+                    }
+
+                    return ($el.innerHeight() < el.scrollHeight ||
+                        $el.innerWidth() < el.scrollWidth);
+                };
+
+                Utils.escapeMarkup = function (markup) {
+                    var replaceMap = {
+                        '\\': '&#92;',
+                        '&': '&amp;',
+                        '<': '&lt;',
+                        '>': '&gt;',
+                        '"': '&quot;',
+                        '\'': '&#39;',
+                        '/': '&#47;'
+                    };
+
+                    // Do not try to escape the markup if it's not a string
+                    if (typeof markup !== 'string') {
+                        return markup;
+                    }
+
+                    return String(markup).replace(/[&<>"'\/\\]/g, function (match) {
+                        return replaceMap[match];
+                    });
+                };
+
+                // Append an array of jQuery nodes to a given element.
+                Utils.appendMany = function ($element, $nodes) {
+                    // jQuery 1.7.x does not support $.fn.append() with an array
+                    // Fall back to a jQuery object collection using $.fn.add()
+                    if ($.fn.jquery.substr(0, 3) === '1.7') {
+                        var $jqNodes = $();
+
+                        $.map($nodes, function (node) {
+                            $jqNodes = $jqNodes.add(node);
+                        });
+
+                        $nodes = $jqNodes;
+                    }
+
+                    $element.append($nodes);
+                };
+
+                return Utils;
+            });
+
+            S2.define('select2/results', [
+                'jquery',
+                './utils'
+            ], function ($, Utils) {
+                function Results($element, options, dataAdapter) {
+                    this.$element = $element;
+                    this.data = dataAdapter;
+                    this.options = options;
+
+                    Results.__super__.constructor.call(this);
+                }
+
+                Utils.Extend(Results, Utils.Observable);
+
+                Results.prototype.render = function () {
+                    var $results = $(
+                        '<ul class="select2-results__options" role="tree"></ul>'
+                    );
+
+                    if (this.options.get('multiple')) {
+                        $results.attr('aria-multiselectable', 'true');
+                    }
+
+                    this.$results = $results;
+
+                    return $results;
+                };
+
+                Results.prototype.clear = function () {
+                    this.$results.empty();
+                };
+
+                Results.prototype.displayMessage = function (params) {
+                    var escapeMarkup = this.options.get('escapeMarkup');
+
+                    this.clear();
+                    this.hideLoading();
+
+                    var $message = $(
+                        '<li role="treeitem" aria-live="assertive"' +
+                        ' class="select2-results__option"></li>'
+                    );
+
+                    var message = this.options.get('translations').get(params.message);
+
+                    $message.append(
+                        escapeMarkup(
+                            message(params.args)
+                        )
+                    );
+
+                    $message[0].className += ' select2-results__message';
+
+                    this.$results.append($message);
+                };
+
+                Results.prototype.hideMessages = function () {
+                    this.$results.find('.select2-results__message').remove();
+                };
+
+                Results.prototype.append = function (data) {
+                    this.hideLoading();
+
+                    var $options = [];
+
+                    if (data.results == null || data.results.length === 0) {
+                        if (this.$results.children().length === 0) {
+                            this.trigger('results:message', {
+                                message: 'noResults'
+                            });
+                        }
+
+                        return;
+                    }
+
+                    data.results = this.sort(data.results);
+
+                    for (var d = 0; d < data.results.length; d++) {
+                        var item = data.results[d];
+
+                        var $option = this.option(item);
+
+                        $options.push($option);
+                    }
+
+                    this.$results.append($options);
+                };
+
+                Results.prototype.position = function ($results, $dropdown) {
+                    var $resultsContainer = $dropdown.find('.select2-results');
+                    $resultsContainer.append($results);
+                };
+
+                Results.prototype.sort = function (data) {
+                    var sorter = this.options.get('sorter');
+
+                    return sorter(data);
+                };
+
+                Results.prototype.highlightFirstItem = function () {
+                    var $options = this.$results
+                        .find('.select2-results__option[aria-selected]');
+
+                    var $selected = $options.filter('[aria-selected=true]');
+
+                    // Check if there are any selected options
+                    if ($selected.length > 0) {
+                        // If there are selected options, highlight the first
+                        $selected.first().trigger('mouseenter');
+                    } else {
+                        // If there are no selected options, highlight the first option
+                        // in the dropdown
+                        $options.first().trigger('mouseenter');
+                    }
+
+                    this.ensureHighlightVisible();
+                };
+
+                Results.prototype.setClasses = function () {
+                    var self = this;
+
+                    this.data.current(function (selected) {
+                        var selectedIds = $.map(selected, function (s) {
+                            return s.id.toString();
+                        });
+
+                        var $options = self.$results
+                            .find('.select2-results__option[aria-selected]');
+
+                        $options.each(function () {
+                            var $option = $(this);
+
+                            var item = $.data(this, 'data');
+
+                            // id needs to be converted to a string when comparing
+                            var id = '' + item.id;
+
+                            if ((item.element != null && item.element.selected) ||
+                                (item.element == null && $.inArray(id, selectedIds) > -1)) {
+                                $option.attr('aria-selected', 'true');
+                            } else {
+                                $option.attr('aria-selected', 'false');
+                            }
+                        });
+
+                    });
+                };
+
+                Results.prototype.showLoading = function (params) {
+                    this.hideLoading();
+
+                    var loadingMore = this.options.get('translations').get('searching');
+
+                    var loading = {
+                        disabled: true,
+                        loading: true,
+                        text: loadingMore(params)
+                    };
+                    var $loading = this.option(loading);
+                    $loading.className += ' loading-results';
+
+                    this.$results.prepend($loading);
+                };
+
+                Results.prototype.hideLoading = function () {
+                    this.$results.find('.loading-results').remove();
+                };
+
+                Results.prototype.option = function (data) {
+                    var option = document.createElement('li');
+                    option.className = 'select2-results__option';
+
+                    var attrs = {
+                        'role': 'treeitem',
+                        'aria-selected': 'false'
+                    };
+
+                    if (data.disabled) {
+                        delete attrs['aria-selected'];
+                        attrs['aria-disabled'] = 'true';
+                    }
+
+                    if (data.id == null) {
+                        delete attrs['aria-selected'];
+                    }
+
+                    if (data._resultId != null) {
+                        option.id = data._resultId;
+                    }
+
+                    if (data.title) {
+                        option.title = data.title;
+                    }
+
+                    if (data.children) {
+                        attrs.role = 'group';
+                        attrs['aria-label'] = data.text;
+                        delete attrs['aria-selected'];
+                    }
+
+                    for (var attr in attrs) {
+                        var val = attrs[attr];
+
+                        option.setAttribute(attr, val);
+                    }
+
+                    if (data.children) {
+                        var $option = $(option);
+
+                        var label = document.createElement('strong');
+                        label.className = 'select2-results__group';
+
+                        var $label = $(label);
+                        this.template(data, label);
+
+                        var $children = [];
+
+                        for (var c = 0; c < data.children.length; c++) {
+                            var child = data.children[c];
+
+                            var $child = this.option(child);
+
+                            $children.push($child);
+                        }
+
+                        var $childrenContainer = $('<ul></ul>', {
+                            'class': 'select2-results__options select2-results__options--nested'
+                        });
+
+                        $childrenContainer.append($children);
+
+                        $option.append(label);
+                        $option.append($childrenContainer);
+                    } else {
+                        this.template(data, option);
+                    }
+
+                    $.data(option, 'data', data);
+
+                    return option;
+                };
+
+                Results.prototype.bind = function (container, $container) {
+                    var self = this;
+
+                    var id = container.id + '-results';
+
+                    this.$results.attr('id', id);
+
+                    container.on('results:all', function (params) {
+                        self.clear();
+                        self.append(params.data);
+
+                        if (container.isOpen()) {
+                            self.setClasses();
+                            self.highlightFirstItem();
+                        }
+                    });
+
+                    container.on('results:append', function (params) {
+                        self.append(params.data);
+
+                        if (container.isOpen()) {
+                            self.setClasses();
+                        }
+                    });
+
+                    container.on('query', function (params) {
+                        self.hideMessages();
+                        self.showLoading(params);
+                    });
+
+                    container.on('select', function () {
+                        if (!container.isOpen()) {
+                            return;
+                        }
+
+                        self.setClasses();
+                        self.highlightFirstItem();
+                    });
+
+                    container.on('unselect', function () {
+                        if (!container.isOpen()) {
+                            return;
+                        }
+
+                        self.setClasses();
+                        self.highlightFirstItem();
+                    });
+
+                    container.on('open', function () {
+                        // When the dropdown is open, aria-expended="true"
+                        self.$results.attr('aria-expanded', 'true');
+                        self.$results.attr('aria-hidden', 'false');
+
+                        self.setClasses();
+                        self.ensureHighlightVisible();
+                    });
+
+                    container.on('close', function () {
+                        // When the dropdown is closed, aria-expended="false"
+                        self.$results.attr('aria-expanded', 'false');
+                        self.$results.attr('aria-hidden', 'true');
+                        self.$results.removeAttr('aria-activedescendant');
+                    });
+
+                    container.on('results:toggle', function () {
+                        var $highlighted = self.getHighlightedResults();
+
+                        if ($highlighted.length === 0) {
+                            return;
+                        }
+
+                        $highlighted.trigger('mouseup');
+                    });
+
+                    container.on('results:select', function () {
+                        var $highlighted = self.getHighlightedResults();
+
+                        if ($highlighted.length === 0) {
+                            return;
+                        }
+
+                        var data = $highlighted.data('data');
+
+                        if ($highlighted.attr('aria-selected') == 'true') {
+                            self.trigger('close', {});
+                        } else {
+                            self.trigger('select', {
+                                data: data
+                            });
+                        }
+                    });
+
+                    container.on('results:previous', function () {
+                        var $highlighted = self.getHighlightedResults();
+
+                        var $options = self.$results.find('[aria-selected]');
+
+                        var currentIndex = $options.index($highlighted);
+
+                        // If we are already at te top, don't move further
+                        if (currentIndex === 0) {
+                            return;
+                        }
+
+                        var nextIndex = currentIndex - 1;
+
+                        // If none are highlighted, highlight the first
+                        if ($highlighted.length === 0) {
+                            nextIndex = 0;
+                        }
+
+                        var $next = $options.eq(nextIndex);
+
+                        $next.trigger('mouseenter');
+
+                        var currentOffset = self.$results.offset().top;
+                        var nextTop = $next.offset().top;
+                        var nextOffset = self.$results.scrollTop() + (nextTop - currentOffset);
+
+                        if (nextIndex === 0) {
+                            self.$results.scrollTop(0);
+                        } else if (nextTop - currentOffset < 0) {
+                            self.$results.scrollTop(nextOffset);
+                        }
+                    });
+
+                    container.on('results:next', function () {
+                        var $highlighted = self.getHighlightedResults();
+
+                        var $options = self.$results.find('[aria-selected]');
+
+                        var currentIndex = $options.index($highlighted);
+
+                        var nextIndex = currentIndex + 1;
+
+                        // If we are at the last option, stay there
+                        if (nextIndex >= $options.length) {
+                            return;
+                        }
+
+                        var $next = $options.eq(nextIndex);
+
+                        $next.trigger('mouseenter');
+
+                        var currentOffset = self.$results.offset().top +
+                            self.$results.outerHeight(false);
+                        var nextBottom = $next.offset().top + $next.outerHeight(false);
+                        var nextOffset = self.$results.scrollTop() + nextBottom - currentOffset;
+
+                        if (nextIndex === 0) {
+                            self.$results.scrollTop(0);
+                        } else if (nextBottom > currentOffset) {
+                            self.$results.scrollTop(nextOffset);
+                        }
+                    });
+
+                    container.on('results:focus', function (params) {
+                        params.element.addClass('select2-results__option--highlighted');
+                    });
+
+                    container.on('results:message', function (params) {
+                        self.displayMessage(params);
+                    });
+
+                    if ($.fn.mousewheel) {
+                        this.$results.on('mousewheel', function (e) {
+                            var top = self.$results.scrollTop();
+
+                            var bottom = self.$results.get(0).scrollHeight - top + e.deltaY;
+
+                            var isAtTop = e.deltaY > 0 && top - e.deltaY <= 0;
+                            var isAtBottom = e.deltaY < 0 && bottom <= self.$results.height();
+
+                            if (isAtTop) {
+                                self.$results.scrollTop(0);
+
+                                e.preventDefault();
+                                e.stopPropagation();
+                            } else if (isAtBottom) {
+                                self.$results.scrollTop(
+                                    self.$results.get(0).scrollHeight - self.$results.height()
+                                );
+
+                                e.preventDefault();
+                                e.stopPropagation();
+                            }
+                        });
+                    }
+
+                    this.$results.on('mouseup', '.select2-results__option[aria-selected]',
+                        function (evt) {
+                            var $this = $(this);
+
+                            var data = $this.data('data');
+
+                            if ($this.attr('aria-selected') === 'true') {
+                                if (self.options.get('multiple')) {
+                                    self.trigger('unselect', {
+                                        originalEvent: evt,
+                                        data: data
+                                    });
+                                } else {
+                                    self.trigger('close', {});
+                                }
+
+                                return;
+                            }
+
+                            self.trigger('select', {
+                                originalEvent: evt,
+                                data: data
+                            });
+                        });
+
+                    this.$results.on('mouseenter', '.select2-results__option[aria-selected]',
+                        function (evt) {
+                            var data = $(this).data('data');
+
+                            self.getHighlightedResults()
+                                .removeClass('select2-results__option--highlighted');
+
+                            self.trigger('results:focus', {
+                                data: data,
+                                element: $(this)
+                            });
+                        });
+                };
+
+                Results.prototype.getHighlightedResults = function () {
+                    var $highlighted = this.$results
+                        .find('.select2-results__option--highlighted');
+
+                    return $highlighted;
+                };
+
+                Results.prototype.destroy = function () {
+                    this.$results.remove();
+                };
+
+                Results.prototype.ensureHighlightVisible = function () {
+                    var $highlighted = this.getHighlightedResults();
+
+                    if ($highlighted.length === 0) {
+                        return;
+                    }
+
+                    var $options = this.$results.find('[aria-selected]');
+
+                    var currentIndex = $options.index($highlighted);
+
+                    var currentOffset = this.$results.offset().top;
+                    var nextTop = $highlighted.offset().top;
+                    var nextOffset = this.$results.scrollTop() + (nextTop - currentOffset);
+
+                    var offsetDelta = nextTop - currentOffset;
+                    nextOffset -= $highlighted.outerHeight(false) * 2;
+
+                    if (currentIndex <= 2) {
+                        this.$results.scrollTop(0);
+                    } else if (offsetDelta > this.$results.outerHeight() || offsetDelta < 0) {
+                        this.$results.scrollTop(nextOffset);
+                    }
+                };
+
+                Results.prototype.template = function (result, container) {
+                    var template = this.options.get('templateResult');
+                    var escapeMarkup = this.options.get('escapeMarkup');
+
+                    var content = template(result, container);
+
+                    if (content == null) {
+                        container.style.display = 'none';
+                    } else if (typeof content === 'string') {
+                        container.innerHTML = escapeMarkup(content);
+                    } else {
+                        $(container).append(content);
+                    }
+                };
+
+                return Results;
+            });
+
+            S2.define('select2/keys', [], function () {
+                var KEYS = {
+                    BACKSPACE: 8,
+                    TAB: 9,
+                    ENTER: 13,
+                    SHIFT: 16,
+                    CTRL: 17,
+                    ALT: 18,
+                    ESC: 27,
+                    SPACE: 32,
+                    PAGE_UP: 33,
+                    PAGE_DOWN: 34,
+                    END: 35,
+                    HOME: 36,
+                    LEFT: 37,
+                    UP: 38,
+                    RIGHT: 39,
+                    DOWN: 40,
+                    DELETE: 46
+                };
+
+                return KEYS;
+            });
+
+            S2.define('select2/selection/base', [
+                'jquery',
+                '../utils',
+                '../keys'
+            ], function ($, Utils, KEYS) {
+                function BaseSelection($element, options) {
+                    this.$element = $element;
+                    this.options = options;
+
+                    BaseSelection.__super__.constructor.call(this);
+                }
+
+                Utils.Extend(BaseSelection, Utils.Observable);
+
+                BaseSelection.prototype.render = function () {
+                    var $selection = $(
+                        '<span class="select2-selection" role="combobox" ' +
+                        ' aria-haspopup="true" aria-expanded="false">' +
+                        '</span>'
+                    );
+
+                    this._tabindex = 0;
+
+                    if (this.$element.data('old-tabindex') != null) {
+                        this._tabindex = this.$element.data('old-tabindex');
+                    } else if (this.$element.attr('tabindex') != null) {
+                        this._tabindex = this.$element.attr('tabindex');
+                    }
+
+                    $selection.attr('title', this.$element.attr('title'));
+                    $selection.attr('tabindex', this._tabindex);
+
+                    this.$selection = $selection;
+
+                    return $selection;
+                };
+
+                BaseSelection.prototype.bind = function (container, $container) {
+                    var self = this;
+
+                    var id = container.id + '-container';
+                    var resultsId = container.id + '-results';
+
+                    this.container = container;
+
+                    this.$selection.on('focus', function (evt) {
+                        self.trigger('focus', evt);
+                    });
+
+                    this.$selection.on('blur', function (evt) {
+                        self._handleBlur(evt);
+                    });
+
+                    this.$selection.on('keydown', function (evt) {
+                        self.trigger('keypress', evt);
+
+                        if (evt.which === KEYS.SPACE) {
+                            evt.preventDefault();
+                        }
+                    });
+
+                    container.on('results:focus', function (params) {
+                        self.$selection.attr('aria-activedescendant', params.data._resultId);
+                    });
+
+                    container.on('selection:update', function (params) {
+                        self.update(params.data);
+                    });
+
+                    container.on('open', function () {
+                        // When the dropdown is open, aria-expanded="true"
+                        self.$selection.attr('aria-expanded', 'true');
+                        self.$selection.attr('aria-owns', resultsId);
+
+                        self._attachCloseHandler(container);
+                    });
+
+                    container.on('close', function () {
+                        // When the dropdown is closed, aria-expanded="false"
+                        self.$selection.attr('aria-expanded', 'false');
+                        self.$selection.removeAttr('aria-activedescendant');
+                        self.$selection.removeAttr('aria-owns');
+
+                        self.$selection.focus();
+
+                        self._detachCloseHandler(container);
+                    });
+
+                    container.on('enable', function () {
+                        self.$selection.attr('tabindex', self._tabindex);
+                    });
+
+                    container.on('disable', function () {
+                        self.$selection.attr('tabindex', '-1');
+                    });
+                };
+
+                BaseSelection.prototype._handleBlur = function (evt) {
+                    var self = this;
+
+                    // This needs to be delayed as the active element is the body when the tab
+                    // key is pressed, possibly along with others.
+                    window.setTimeout(function () {
+                        // Don't trigger `blur` if the focus is still in the selection
+                        if (
+                            (document.activeElement == self.$selection[0]) ||
+                            ($.contains(self.$selection[0], document.activeElement))
+                        ) {
+                            return;
+                        }
+
+                        self.trigger('blur', evt);
+                    }, 1);
+                };
+
+                BaseSelection.prototype._attachCloseHandler = function (container) {
+                    var self = this;
+
+                    $(document.body).on('mousedown.select2.' + container.id, function (e) {
+                        var $target = $(e.target);
+
+                        var $select = $target.closest('.select2');
+
+                        var $all = $('.select2.select2-container--open');
+
+                        $all.each(function () {
+                            var $this = $(this);
+
+                            if (this == $select[0]) {
+                                return;
+                            }
+
+                            var $element = $this.data('element');
+
+                            $element.select2('close');
+                        });
+                    });
+                };
+
+                BaseSelection.prototype._detachCloseHandler = function (container) {
+                    $(document.body).off('mousedown.select2.' + container.id);
+                };
+
+                BaseSelection.prototype.position = function ($selection, $container) {
+                    var $selectionContainer = $container.find('.selection');
+                    $selectionContainer.append($selection);
+                };
+
+                BaseSelection.prototype.destroy = function () {
+                    this._detachCloseHandler(this.container);
+                };
+
+                BaseSelection.prototype.update = function (data) {
+                    throw new Error('The `update` method must be defined in child classes.');
+                };
+
+                return BaseSelection;
+            });
+
+            S2.define('select2/selection/single', [
+                'jquery',
+                './base',
+                '../utils',
+                '../keys'
+            ], function ($, BaseSelection, Utils, KEYS) {
+                function SingleSelection() {
+                    SingleSelection.__super__.constructor.apply(this, arguments);
+                }
+
+                Utils.Extend(SingleSelection, BaseSelection);
+
+                SingleSelection.prototype.render = function () {
+                    var $selection = SingleSelection.__super__.render.call(this);
+
+                    $selection.addClass('select2-selection--single');
+
+                    $selection.html(
+                        '<span class="select2-selection__rendered"></span>' +
+                        '<span class="select2-selection__arrow" role="presentation">' +
+                        '<b role="presentation"></b>' +
+                        '</span>'
+                    );
+
+                    return $selection;
+                };
+
+                SingleSelection.prototype.bind = function (container, $container) {
+                    var self = this;
+
+                    SingleSelection.__super__.bind.apply(this, arguments);
+
+                    var id = container.id + '-container';
+
+                    this.$selection.find('.select2-selection__rendered').attr('id', id);
+                    this.$selection.attr('aria-labelledby', id);
+
+                    this.$selection.on('mousedown', function (evt) {
+                        // Only respond to left clicks
+                        if (evt.which !== 1) {
+                            return;
+                        }
+
+                        self.trigger('toggle', {
+                            originalEvent: evt
+                        });
+                    });
+
+                    this.$selection.on('focus', function (evt) {
+                        // User focuses on the container
+                    });
+
+                    this.$selection.on('blur', function (evt) {
+                        // User exits the container
+                    });
+
+                    container.on('focus', function (evt) {
+                        if (!container.isOpen()) {
+                            self.$selection.focus();
+                        }
+                    });
+
+                    container.on('selection:update', function (params) {
+                        self.update(params.data);
+                    });
+                };
+
+                SingleSelection.prototype.clear = function () {
+                    this.$selection.find('.select2-selection__rendered').empty();
+                };
+
+                SingleSelection.prototype.display = function (data, container) {
+                    var template = this.options.get('templateSelection');
+                    var escapeMarkup = this.options.get('escapeMarkup');
+
+                    return escapeMarkup(template(data, container));
+                };
+
+                SingleSelection.prototype.selectionContainer = function () {
+                    return $('<span></span>');
+                };
+
+                SingleSelection.prototype.update = function (data) {
+                    if (data.length === 0) {
+                        this.clear();
+                        return;
+                    }
+
+                    var selection = data[0];
+
+                    var $rendered = this.$selection.find('.select2-selection__rendered');
+                    var formatted = this.display(selection, $rendered);
+
+                    $rendered.empty().append(formatted);
+                    $rendered.prop('title', selection.title || selection.text);
+                };
+
+                return SingleSelection;
+            });
+
+            S2.define('select2/selection/multiple', [
+                'jquery',
+                './base',
+                '../utils'
+            ], function ($, BaseSelection, Utils) {
+                function MultipleSelection($element, options) {
+                    MultipleSelection.__super__.constructor.apply(this, arguments);
+                }
+
+                Utils.Extend(MultipleSelection, BaseSelection);
+
+                MultipleSelection.prototype.render = function () {
+                    var $selection = MultipleSelection.__super__.render.call(this);
+
+                    $selection.addClass('select2-selection--multiple');
+
+                    $selection.html(
+                        '<ul class="select2-selection__rendered"></ul>'
+                    );
+
+                    return $selection;
+                };
+
+                MultipleSelection.prototype.bind = function (container, $container) {
+                    var self = this;
+
+                    MultipleSelection.__super__.bind.apply(this, arguments);
+
+                    this.$selection.on('click', function (evt) {
+                        self.trigger('toggle', {
+                            originalEvent: evt
+                        });
+                    });
+
+                    this.$selection.on(
+                        'click',
+                        '.select2-selection__choice__remove',
+                        function (evt) {
+                            // Ignore the event if it is disabled
+                            if (self.options.get('disabled')) {
+                                return;
+                            }
+
+                            var $remove = $(this);
+                            var $selection = $remove.parent();
+
+                            var data = $selection.data('data');
+
+                            self.trigger('unselect', {
+                                originalEvent: evt,
+                                data: data
+                            });
+                        }
+                    );
+                };
+
+                MultipleSelection.prototype.clear = function () {
+                    this.$selection.find('.select2-selection__rendered').empty();
+                };
+
+                MultipleSelection.prototype.display = function (data, container) {
+                    var template = this.options.get('templateSelection');
+                    var escapeMarkup = this.options.get('escapeMarkup');
+
+                    return escapeMarkup(template(data, container));
+                };
+
+                MultipleSelection.prototype.selectionContainer = function () {
+                    var $container = $(
+                        '<li class="select2-selection__choice">' +
+                        '<span class="select2-selection__choice__remove" role="presentation">' +
+                        '&times;' +
+                        '</span>' +
+                        '</li>'
+                    );
+
+                    return $container;
+                };
+
+                MultipleSelection.prototype.update = function (data) {
+                    this.clear();
+
+                    if (data.length === 0) {
+                        return;
+                    }
+
+                    var $selections = [];
+
+                    for (var d = 0; d < data.length; d++) {
+                        var selection = data[d];
+
+                        var $selection = this.selectionContainer();
+                        var formatted = this.display(selection, $selection);
+
+                        $selection.append(formatted);
+                        $selection.prop('title', selection.title || selection.text);
+
+                        $selection.data('data', selection);
+
+                        $selections.push($selection);
+                    }
+
+                    var $rendered = this.$selection.find('.select2-selection__rendered');
+
+                    Utils.appendMany($rendered, $selections);
+                };
+
+                return MultipleSelection;
+            });
+
+            S2.define('select2/selection/placeholder', [
+                '../utils'
+            ], function (Utils) {
+                function Placeholder(decorated, $element, options) {
+                    this.placeholder = this.normalizePlaceholder(options.get('placeholder'));
+
+                    decorated.call(this, $element, options);
+                }
+
+                Placeholder.prototype.normalizePlaceholder = function (_, placeholder) {
+                    if (typeof placeholder === 'string') {
+                        placeholder = {
+                            id: '',
+                            text: placeholder
+                        };
+                    }
+
+                    return placeholder;
+                };
+
+                Placeholder.prototype.createPlaceholder = function (decorated, placeholder) {
+                    var $placeholder = this.selectionContainer();
+
+                    $placeholder.html(this.display(placeholder));
+                    $placeholder.addClass('select2-selection__placeholder')
+                        .removeClass('select2-selection__choice');
+
+                    return $placeholder;
+                };
+
+                Placeholder.prototype.update = function (decorated, data) {
+                    var singlePlaceholder = (
+                        data.length == 1 && data[0].id != this.placeholder.id
+                    );
+                    var multipleSelections = data.length > 1;
+
+                    if (multipleSelections || singlePlaceholder) {
+                        return decorated.call(this, data);
+                    }
+
+                    this.clear();
+
+                    var $placeholder = this.createPlaceholder(this.placeholder);
+
+                    this.$selection.find('.select2-selection__rendered').append($placeholder);
+                };
+
+                return Placeholder;
+            });
+
+            S2.define('select2/selection/allowClear', [
+                'jquery',
+                '../keys'
+            ], function ($, KEYS) {
+                function AllowClear() {
+                }
+
+                AllowClear.prototype.bind = function (decorated, container, $container) {
+                    var self = this;
+
+                    decorated.call(this, container, $container);
+
+                    if (this.placeholder == null) {
+                        if (this.options.get('debug') && window.console && console.error) {
+                            console.error(
+                                'Select2: The `allowClear` option should be used in combination ' +
+                                'with the `placeholder` option.'
+                            );
+                        }
+                    }
+
+                    this.$selection.on('mousedown', '.select2-selection__clear',
+                        function (evt) {
+                            self._handleClear(evt);
+                        });
+
+                    container.on('keypress', function (evt) {
+                        self._handleKeyboardClear(evt, container);
+                    });
+                };
+
+                AllowClear.prototype._handleClear = function (_, evt) {
+                    // Ignore the event if it is disabled
+                    if (this.options.get('disabled')) {
+                        return;
+                    }
+
+                    var $clear = this.$selection.find('.select2-selection__clear');
+
+                    // Ignore the event if nothing has been selected
+                    if ($clear.length === 0) {
+                        return;
+                    }
+
+                    evt.stopPropagation();
+
+                    var data = $clear.data('data');
+
+                    for (var d = 0; d < data.length; d++) {
+                        var unselectData = {
+                            data: data[d]
+                        };
+
+                        // Trigger the `unselect` event, so people can prevent it from being
+                        // cleared.
+                        this.trigger('unselect', unselectData);
+
+                        // If the event was prevented, don't clear it out.
+                        if (unselectData.prevented) {
+                            return;
+                        }
+                    }
+
+                    this.$element.val(this.placeholder.id).trigger('change');
+
+                    this.trigger('toggle', {});
+                };
+
+                AllowClear.prototype._handleKeyboardClear = function (_, evt, container) {
+                    if (container.isOpen()) {
+                        return;
+                    }
+
+                    if (evt.which == KEYS.DELETE || evt.which == KEYS.BACKSPACE) {
+                        this._handleClear(evt);
+                    }
+                };
+
+                AllowClear.prototype.update = function (decorated, data) {
+                    decorated.call(this, data);
+
+                    if (this.$selection.find('.select2-selection__placeholder').length > 0 ||
+                        data.length === 0) {
+                        return;
+                    }
+
+                    var $remove = $(
+                        '<span class="select2-selection__clear">' +
+                        '&times;' +
+                        '</span>'
+                    );
+                    $remove.data('data', data);
+
+                    this.$selection.find('.select2-selection__rendered').prepend($remove);
+                };
+
+                return AllowClear;
+            });
+
+            S2.define('select2/selection/search', [
+                'jquery',
+                '../utils',
+                '../keys'
+            ], function ($, Utils, KEYS) {
+                function Search(decorated, $element, options) {
+                    decorated.call(this, $element, options);
+                }
+
+                Search.prototype.render = function (decorated) {
+                    var $search = $(
+                        '<li class="select2-search select2-search--inline">' +
+                        '<input class="select2-search__field" type="search" tabindex="-1"' +
+                        ' autocomplete="off" autocorrect="off" autocapitalize="off"' +
+                        ' spellcheck="false" role="textbox" aria-autocomplete="list" />' +
+                        '</li>'
+                    );
+
+                    this.$searchContainer = $search;
+                    this.$search = $search.find('input');
+
+                    var $rendered = decorated.call(this);
+
+                    this._transferTabIndex();
+
+                    return $rendered;
+                };
+
+                Search.prototype.bind = function (decorated, container, $container) {
+                    var self = this;
+
+                    decorated.call(this, container, $container);
+
+                    container.on('open', function () {
+                        self.$search.trigger('focus');
+                    });
+
+                    container.on('close', function () {
+                        self.$search.val('');
+                        self.$search.removeAttr('aria-activedescendant');
+                        self.$search.trigger('focus');
+                    });
+
+                    container.on('enable', function () {
+                        self.$search.prop('disabled', false);
+
+                        self._transferTabIndex();
+                    });
+
+                    container.on('disable', function () {
+                        self.$search.prop('disabled', true);
+                    });
+
+                    container.on('focus', function (evt) {
+                        self.$search.trigger('focus');
+                    });
+
+                    container.on('results:focus', function (params) {
+                        self.$search.attr('aria-activedescendant', params.id);
+                    });
+
+                    this.$selection.on('focusin', '.select2-search--inline', function (evt) {
+                        self.trigger('focus', evt);
+                    });
+
+                    this.$selection.on('focusout', '.select2-search--inline', function (evt) {
+                        self._handleBlur(evt);
+                    });
+
+                    this.$selection.on('keydown', '.select2-search--inline', function (evt) {
+                        evt.stopPropagation();
+
+                        self.trigger('keypress', evt);
+
+                        self._keyUpPrevented = evt.isDefaultPrevented();
+
+                        var key = evt.which;
+
+                        if (key === KEYS.BACKSPACE && self.$search.val() === '') {
+                            var $previousChoice = self.$searchContainer
+                                .prev('.select2-selection__choice');
+
+                            if ($previousChoice.length > 0) {
+                                var item = $previousChoice.data('data');
+
+                                self.searchRemoveChoice(item);
+
+                                evt.preventDefault();
+                            }
+                        }
+                    });
+
+                    // Try to detect the IE version should the `documentMode` property that
+                    // is stored on the document. This is only implemented in IE and is
+                    // slightly cleaner than doing a user agent check.
+                    // This property is not available in Edge, but Edge also doesn't have
+                    // this bug.
+                    var msie = document.documentMode;
+                    var disableInputEvents = msie && msie <= 11;
+
+                    // Workaround for browsers which do not support the `input` event
+                    // This will prevent double-triggering of events for browsers which support
+                    // both the `keyup` and `input` events.
+                    this.$selection.on(
+                        'input.searchcheck',
+                        '.select2-search--inline',
+                        function (evt) {
+                            // IE will trigger the `input` event when a placeholder is used on a
+                            // search box. To get around this issue, we are forced to ignore all
+                            // `input` events in IE and keep using `keyup`.
+                            if (disableInputEvents) {
+                                self.$selection.off('input.search input.searchcheck');
+                                return;
+                            }
+
+                            // Unbind the duplicated `keyup` event
+                            self.$selection.off('keyup.search');
+                        }
+                    );
+
+                    this.$selection.on(
+                        'keyup.search input.search',
+                        '.select2-search--inline',
+                        function (evt) {
+                            // IE will trigger the `input` event when a placeholder is used on a
+                            // search box. To get around this issue, we are forced to ignore all
+                            // `input` events in IE and keep using `keyup`.
+                            if (disableInputEvents && evt.type === 'input') {
+                                self.$selection.off('input.search input.searchcheck');
+                                return;
+                            }
+
+                            var key = evt.which;
+
+                            // We can freely ignore events from modifier keys
+                            if (key == KEYS.SHIFT || key == KEYS.CTRL || key == KEYS.ALT) {
+                                return;
+                            }
+
+                            // Tabbing will be handled during the `keydown` phase
+                            if (key == KEYS.TAB) {
+                                return;
+                            }
+
+                            self.handleSearch(evt);
+                        }
+                    );
+                };
+
+                /**
+                 * This method will transfer the tabindex attribute from the rendered
+                 * selection to the search box. This allows for the search box to be used as
+                 * the primary focus instead of the selection container.
+                 *
+                 * @private
+                 */
+                Search.prototype._transferTabIndex = function (decorated) {
+                    this.$search.attr('tabindex', this.$selection.attr('tabindex'));
+                    this.$selection.attr('tabindex', '-1');
+                };
+
+                Search.prototype.createPlaceholder = function (decorated, placeholder) {
+                    this.$search.attr('placeholder', placeholder.text);
+                };
+
+                Search.prototype.update = function (decorated, data) {
+                    var searchHadFocus = this.$search[0] == document.activeElement;
+
+                    this.$search.attr('placeholder', '');
+
+                    decorated.call(this, data);
+
+                    this.$selection.find('.select2-selection__rendered')
+                        .append(this.$searchContainer);
+
+                    this.resizeSearch();
+                    if (searchHadFocus) {
+                        this.$search.focus();
+                    }
+                };
+
+                Search.prototype.handleSearch = function () {
+                    this.resizeSearch();
+
+                    if (!this._keyUpPrevented) {
+                        var input = this.$search.val();
+
+                        this.trigger('query', {
+                            term: input
+                        });
+                    }
+
+                    this._keyUpPrevented = false;
+                };
+
+                Search.prototype.searchRemoveChoice = function (decorated, item) {
+                    this.trigger('unselect', {
+                        data: item
+                    });
+
+                    this.$search.val(item.text);
+                    this.handleSearch();
+                };
+
+                Search.prototype.resizeSearch = function () {
+                    this.$search.css('width', '25px');
+
+                    var width = '';
+
+                    if (this.$search.attr('placeholder') !== '') {
+                        width = this.$selection.find('.select2-selection__rendered').innerWidth();
+                    } else {
+                        var minimumWidth = this.$search.val().length + 1;
+
+                        width = (minimumWidth * 0.75) + 'em';
+                    }
+
+                    this.$search.css('width', width);
+                };
+
+                return Search;
+            });
+
+            S2.define('select2/selection/eventRelay', [
+                'jquery'
+            ], function ($) {
+                function EventRelay() {
+                }
+
+                EventRelay.prototype.bind = function (decorated, container, $container) {
+                    var self = this;
+                    var relayEvents = [
+                        'open', 'opening',
+                        'close', 'closing',
+                        'select', 'selecting',
+                        'unselect', 'unselecting'
+                    ];
+
+                    var preventableEvents = ['opening', 'closing', 'selecting', 'unselecting'];
+
+                    decorated.call(this, container, $container);
+
+                    container.on('*', function (name, params) {
+                        // Ignore events that should not be relayed
+                        if ($.inArray(name, relayEvents) === -1) {
+                            return;
+                        }
+
+                        // The parameters should always be an object
+                        params = params || {};
+
+                        // Generate the jQuery event for the Select2 event
+                        var evt = $.Event('select2:' + name, {
+                            params: params
+                        });
+
+                        self.$element.trigger(evt);
+
+                        // Only handle preventable events if it was one
+                        if ($.inArray(name, preventableEvents) === -1) {
+                            return;
+                        }
+
+                        params.prevented = evt.isDefaultPrevented();
+                    });
+                };
+
+                return EventRelay;
+            });
+
+            S2.define('select2/translation', [
+                'jquery',
+                'require'
+            ], function ($, require) {
+                function Translation(dict) {
+                    this.dict = dict || {};
+                }
+
+                Translation.prototype.all = function () {
+                    return this.dict;
+                };
+
+                Translation.prototype.get = function (key) {
+                    return this.dict[key];
+                };
+
+                Translation.prototype.extend = function (translation) {
+                    this.dict = $.extend({}, translation.all(), this.dict);
+                };
+
+                // Static functions
+
+                Translation._cache = {};
+
+                Translation.loadPath = function (path) {
+                    if (!(path in Translation._cache)) {
+                        var translations = require(path);
+
+                        Translation._cache[path] = translations;
+                    }
+
+                    return new Translation(Translation._cache[path]);
+                };
+
+                return Translation;
+            });
+
+            S2.define('select2/diacritics', [], function () {
+                var diacritics = {
+                    '\u24B6': 'A',
+                    '\uFF21': 'A',
+                    '\u00C0': 'A',
+                    '\u00C1': 'A',
+                    '\u00C2': 'A',
+                    '\u1EA6': 'A',
+                    '\u1EA4': 'A',
+                    '\u1EAA': 'A',
+                    '\u1EA8': 'A',
+                    '\u00C3': 'A',
+                    '\u0100': 'A',
+                    '\u0102': 'A',
+                    '\u1EB0': 'A',
+                    '\u1EAE': 'A',
+                    '\u1EB4': 'A',
+                    '\u1EB2': 'A',
+                    '\u0226': 'A',
+                    '\u01E0': 'A',
+                    '\u00C4': 'A',
+                    '\u01DE': 'A',
+                    '\u1EA2': 'A',
+                    '\u00C5': 'A',
+                    '\u01FA': 'A',
+                    '\u01CD': 'A',
+                    '\u0200': 'A',
+                    '\u0202': 'A',
+                    '\u1EA0': 'A',
+                    '\u1EAC': 'A',
+                    '\u1EB6': 'A',
+                    '\u1E00': 'A',
+                    '\u0104': 'A',
+                    '\u023A': 'A',
+                    '\u2C6F': 'A',
+                    '\uA732': 'AA',
+                    '\u00C6': 'AE',
+                    '\u01FC': 'AE',
+                    '\u01E2': 'AE',
+                    '\uA734': 'AO',
+                    '\uA736': 'AU',
+                    '\uA738': 'AV',
+                    '\uA73A': 'AV',
+                    '\uA73C': 'AY',
+                    '\u24B7': 'B',
+                    '\uFF22': 'B',
+                    '\u1E02': 'B',
+                    '\u1E04': 'B',
+                    '\u1E06': 'B',
+                    '\u0243': 'B',
+                    '\u0182': 'B',
+                    '\u0181': 'B',
+                    '\u24B8': 'C',
+                    '\uFF23': 'C',
+                    '\u0106': 'C',
+                    '\u0108': 'C',
+                    '\u010A': 'C',
+                    '\u010C': 'C',
+                    '\u00C7': 'C',
+                    '\u1E08': 'C',
+                    '\u0187': 'C',
+                    '\u023B': 'C',
+                    '\uA73E': 'C',
+                    '\u24B9': 'D',
+                    '\uFF24': 'D',
+                    '\u1E0A': 'D',
+                    '\u010E': 'D',
+                    '\u1E0C': 'D',
+                    '\u1E10': 'D',
+                    '\u1E12': 'D',
+                    '\u1E0E': 'D',
+                    '\u0110': 'D',
+                    '\u018B': 'D',
+                    '\u018A': 'D',
+                    '\u0189': 'D',
+                    '\uA779': 'D',
+                    '\u01F1': 'DZ',
+                    '\u01C4': 'DZ',
+                    '\u01F2': 'Dz',
+                    '\u01C5': 'Dz',
+                    '\u24BA': 'E',
+                    '\uFF25': 'E',
+                    '\u00C8': 'E',
+                    '\u00C9': 'E',
+                    '\u00CA': 'E',
+                    '\u1EC0': 'E',
+                    '\u1EBE': 'E',
+                    '\u1EC4': 'E',
+                    '\u1EC2': 'E',
+                    '\u1EBC': 'E',
+                    '\u0112': 'E',
+                    '\u1E14': 'E',
+                    '\u1E16': 'E',
+                    '\u0114': 'E',
+                    '\u0116': 'E',
+                    '\u00CB': 'E',
+                    '\u1EBA': 'E',
+                    '\u011A': 'E',
+                    '\u0204': 'E',
+                    '\u0206': 'E',
+                    '\u1EB8': 'E',
+                    '\u1EC6': 'E',
+                    '\u0228': 'E',
+                    '\u1E1C': 'E',
+                    '\u0118': 'E',
+                    '\u1E18': 'E',
+                    '\u1E1A': 'E',
+                    '\u0190': 'E',
+                    '\u018E': 'E',
+                    '\u24BB': 'F',
+                    '\uFF26': 'F',
+                    '\u1E1E': 'F',
+                    '\u0191': 'F',
+                    '\uA77B': 'F',
+                    '\u24BC': 'G',
+                    '\uFF27': 'G',
+                    '\u01F4': 'G',
+                    '\u011C': 'G',
+                    '\u1E20': 'G',
+                    '\u011E': 'G',
+                    '\u0120': 'G',
+                    '\u01E6': 'G',
+                    '\u0122': 'G',
+                    '\u01E4': 'G',
+                    '\u0193': 'G',
+                    '\uA7A0': 'G',
+                    '\uA77D': 'G',
+                    '\uA77E': 'G',
+                    '\u24BD': 'H',
+                    '\uFF28': 'H',
+                    '\u0124': 'H',
+                    '\u1E22': 'H',
+                    '\u1E26': 'H',
+                    '\u021E': 'H',
+                    '\u1E24': 'H',
+                    '\u1E28': 'H',
+                    '\u1E2A': 'H',
+                    '\u0126': 'H',
+                    '\u2C67': 'H',
+                    '\u2C75': 'H',
+                    '\uA78D': 'H',
+                    '\u24BE': 'I',
+                    '\uFF29': 'I',
+                    '\u00CC': 'I',
+                    '\u00CD': 'I',
+                    '\u00CE': 'I',
+                    '\u0128': 'I',
+                    '\u012A': 'I',
+                    '\u012C': 'I',
+                    '\u0130': 'I',
+                    '\u00CF': 'I',
+                    '\u1E2E': 'I',
+                    '\u1EC8': 'I',
+                    '\u01CF': 'I',
+                    '\u0208': 'I',
+                    '\u020A': 'I',
+                    '\u1ECA': 'I',
+                    '\u012E': 'I',
+                    '\u1E2C': 'I',
+                    '\u0197': 'I',
+                    '\u24BF': 'J',
+                    '\uFF2A': 'J',
+                    '\u0134': 'J',
+                    '\u0248': 'J',
+                    '\u24C0': 'K',
+                    '\uFF2B': 'K',
+                    '\u1E30': 'K',
+                    '\u01E8': 'K',
+                    '\u1E32': 'K',
+                    '\u0136': 'K',
+                    '\u1E34': 'K',
+                    '\u0198': 'K',
+                    '\u2C69': 'K',
+                    '\uA740': 'K',
+                    '\uA742': 'K',
+                    '\uA744': 'K',
+                    '\uA7A2': 'K',
+                    '\u24C1': 'L',
+                    '\uFF2C': 'L',
+                    '\u013F': 'L',
+                    '\u0139': 'L',
+                    '\u013D': 'L',
+                    '\u1E36': 'L',
+                    '\u1E38': 'L',
+                    '\u013B': 'L',
+                    '\u1E3C': 'L',
+                    '\u1E3A': 'L',
+                    '\u0141': 'L',
+                    '\u023D': 'L',
+                    '\u2C62': 'L',
+                    '\u2C60': 'L',
+                    '\uA748': 'L',
+                    '\uA746': 'L',
+                    '\uA780': 'L',
+                    '\u01C7': 'LJ',
+                    '\u01C8': 'Lj',
+                    '\u24C2': 'M',
+                    '\uFF2D': 'M',
+                    '\u1E3E': 'M',
+                    '\u1E40': 'M',
+                    '\u1E42': 'M',
+                    '\u2C6E': 'M',
+                    '\u019C': 'M',
+                    '\u24C3': 'N',
+                    '\uFF2E': 'N',
+                    '\u01F8': 'N',
+                    '\u0143': 'N',
+                    '\u00D1': 'N',
+                    '\u1E44': 'N',
+                    '\u0147': 'N',
+                    '\u1E46': 'N',
+                    '\u0145': 'N',
+                    '\u1E4A': 'N',
+                    '\u1E48': 'N',
+                    '\u0220': 'N',
+                    '\u019D': 'N',
+                    '\uA790': 'N',
+                    '\uA7A4': 'N',
+                    '\u01CA': 'NJ',
+                    '\u01CB': 'Nj',
+                    '\u24C4': 'O',
+                    '\uFF2F': 'O',
+                    '\u00D2': 'O',
+                    '\u00D3': 'O',
+                    '\u00D4': 'O',
+                    '\u1ED2': 'O',
+                    '\u1ED0': 'O',
+                    '\u1ED6': 'O',
+                    '\u1ED4': 'O',
+                    '\u00D5': 'O',
+                    '\u1E4C': 'O',
+                    '\u022C': 'O',
+                    '\u1E4E': 'O',
+                    '\u014C': 'O',
+                    '\u1E50': 'O',
+                    '\u1E52': 'O',
+                    '\u014E': 'O',
+                    '\u022E': 'O',
+                    '\u0230': 'O',
+                    '\u00D6': 'O',
+                    '\u022A': 'O',
+                    '\u1ECE': 'O',
+                    '\u0150': 'O',
+                    '\u01D1': 'O',
+                    '\u020C': 'O',
+                    '\u020E': 'O',
+                    '\u01A0': 'O',
+                    '\u1EDC': 'O',
+                    '\u1EDA': 'O',
+                    '\u1EE0': 'O',
+                    '\u1EDE': 'O',
+                    '\u1EE2': 'O',
+                    '\u1ECC': 'O',
+                    '\u1ED8': 'O',
+                    '\u01EA': 'O',
+                    '\u01EC': 'O',
+                    '\u00D8': 'O',
+                    '\u01FE': 'O',
+                    '\u0186': 'O',
+                    '\u019F': 'O',
+                    '\uA74A': 'O',
+                    '\uA74C': 'O',
+                    '\u01A2': 'OI',
+                    '\uA74E': 'OO',
+                    '\u0222': 'OU',
+                    '\u24C5': 'P',
+                    '\uFF30': 'P',
+                    '\u1E54': 'P',
+                    '\u1E56': 'P',
+                    '\u01A4': 'P',
+                    '\u2C63': 'P',
+                    '\uA750': 'P',
+                    '\uA752': 'P',
+                    '\uA754': 'P',
+                    '\u24C6': 'Q',
+                    '\uFF31': 'Q',
+                    '\uA756': 'Q',
+                    '\uA758': 'Q',
+                    '\u024A': 'Q',
+                    '\u24C7': 'R',
+                    '\uFF32': 'R',
+                    '\u0154': 'R',
+                    '\u1E58': 'R',
+                    '\u0158': 'R',
+                    '\u0210': 'R',
+                    '\u0212': 'R',
+                    '\u1E5A': 'R',
+                    '\u1E5C': 'R',
+                    '\u0156': 'R',
+                    '\u1E5E': 'R',
+                    '\u024C': 'R',
+                    '\u2C64': 'R',
+                    '\uA75A': 'R',
+                    '\uA7A6': 'R',
+                    '\uA782': 'R',
+                    '\u24C8': 'S',
+                    '\uFF33': 'S',
+                    '\u1E9E': 'S',
+                    '\u015A': 'S',
+                    '\u1E64': 'S',
+                    '\u015C': 'S',
+                    '\u1E60': 'S',
+                    '\u0160': 'S',
+                    '\u1E66': 'S',
+                    '\u1E62': 'S',
+                    '\u1E68': 'S',
+                    '\u0218': 'S',
+                    '\u015E': 'S',
+                    '\u2C7E': 'S',
+                    '\uA7A8': 'S',
+                    '\uA784': 'S',
+                    '\u24C9': 'T',
+                    '\uFF34': 'T',
+                    '\u1E6A': 'T',
+                    '\u0164': 'T',
+                    '\u1E6C': 'T',
+                    '\u021A': 'T',
+                    '\u0162': 'T',
+                    '\u1E70': 'T',
+                    '\u1E6E': 'T',
+                    '\u0166': 'T',
+                    '\u01AC': 'T',
+                    '\u01AE': 'T',
+                    '\u023E': 'T',
+                    '\uA786': 'T',
+                    '\uA728': 'TZ',
+                    '\u24CA': 'U',
+                    '\uFF35': 'U',
+                    '\u00D9': 'U',
+                    '\u00DA': 'U',
+                    '\u00DB': 'U',
+                    '\u0168': 'U',
+                    '\u1E78': 'U',
+                    '\u016A': 'U',
+                    '\u1E7A': 'U',
+                    '\u016C': 'U',
+                    '\u00DC': 'U',
+                    '\u01DB': 'U',
+                    '\u01D7': 'U',
+                    '\u01D5': 'U',
+                    '\u01D9': 'U',
+                    '\u1EE6': 'U',
+                    '\u016E': 'U',
+                    '\u0170': 'U',
+                    '\u01D3': 'U',
+                    '\u0214': 'U',
+                    '\u0216': 'U',
+                    '\u01AF': 'U',
+                    '\u1EEA': 'U',
+                    '\u1EE8': 'U',
+                    '\u1EEE': 'U',
+                    '\u1EEC': 'U',
+                    '\u1EF0': 'U',
+                    '\u1EE4': 'U',
+                    '\u1E72': 'U',
+                    '\u0172': 'U',
+                    '\u1E76': 'U',
+                    '\u1E74': 'U',
+                    '\u0244': 'U',
+                    '\u24CB': 'V',
+                    '\uFF36': 'V',
+                    '\u1E7C': 'V',
+                    '\u1E7E': 'V',
+                    '\u01B2': 'V',
+                    '\uA75E': 'V',
+                    '\u0245': 'V',
+                    '\uA760': 'VY',
+                    '\u24CC': 'W',
+                    '\uFF37': 'W',
+                    '\u1E80': 'W',
+                    '\u1E82': 'W',
+                    '\u0174': 'W',
+                    '\u1E86': 'W',
+                    '\u1E84': 'W',
+                    '\u1E88': 'W',
+                    '\u2C72': 'W',
+                    '\u24CD': 'X',
+                    '\uFF38': 'X',
+                    '\u1E8A': 'X',
+                    '\u1E8C': 'X',
+                    '\u24CE': 'Y',
+                    '\uFF39': 'Y',
+                    '\u1EF2': 'Y',
+                    '\u00DD': 'Y',
+                    '\u0176': 'Y',
+                    '\u1EF8': 'Y',
+                    '\u0232': 'Y',
+                    '\u1E8E': 'Y',
+                    '\u0178': 'Y',
+                    '\u1EF6': 'Y',
+                    '\u1EF4': 'Y',
+                    '\u01B3': 'Y',
+                    '\u024E': 'Y',
+                    '\u1EFE': 'Y',
+                    '\u24CF': 'Z',
+                    '\uFF3A': 'Z',
+                    '\u0179': 'Z',
+                    '\u1E90': 'Z',
+                    '\u017B': 'Z',
+                    '\u017D': 'Z',
+                    '\u1E92': 'Z',
+                    '\u1E94': 'Z',
+                    '\u01B5': 'Z',
+                    '\u0224': 'Z',
+                    '\u2C7F': 'Z',
+                    '\u2C6B': 'Z',
+                    '\uA762': 'Z',
+                    '\u24D0': 'a',
+                    '\uFF41': 'a',
+                    '\u1E9A': 'a',
+                    '\u00E0': 'a',
+                    '\u00E1': 'a',
+                    '\u00E2': 'a',
+                    '\u1EA7': 'a',
+                    '\u1EA5': 'a',
+                    '\u1EAB': 'a',
+                    '\u1EA9': 'a',
+                    '\u00E3': 'a',
+                    '\u0101': 'a',
+                    '\u0103': 'a',
+                    '\u1EB1': 'a',
+                    '\u1EAF': 'a',
+                    '\u1EB5': 'a',
+                    '\u1EB3': 'a',
+                    '\u0227': 'a',
+                    '\u01E1': 'a',
+                    '\u00E4': 'a',
+                    '\u01DF': 'a',
+                    '\u1EA3': 'a',
+                    '\u00E5': 'a',
+                    '\u01FB': 'a',
+                    '\u01CE': 'a',
+                    '\u0201': 'a',
+                    '\u0203': 'a',
+                    '\u1EA1': 'a',
+                    '\u1EAD': 'a',
+                    '\u1EB7': 'a',
+                    '\u1E01': 'a',
+                    '\u0105': 'a',
+                    '\u2C65': 'a',
+                    '\u0250': 'a',
+                    '\uA733': 'aa',
+                    '\u00E6': 'ae',
+                    '\u01FD': 'ae',
+                    '\u01E3': 'ae',
+                    '\uA735': 'ao',
+                    '\uA737': 'au',
+                    '\uA739': 'av',
+                    '\uA73B': 'av',
+                    '\uA73D': 'ay',
+                    '\u24D1': 'b',
+                    '\uFF42': 'b',
+                    '\u1E03': 'b',
+                    '\u1E05': 'b',
+                    '\u1E07': 'b',
+                    '\u0180': 'b',
+                    '\u0183': 'b',
+                    '\u0253': 'b',
+                    '\u24D2': 'c',
+                    '\uFF43': 'c',
+                    '\u0107': 'c',
+                    '\u0109': 'c',
+                    '\u010B': 'c',
+                    '\u010D': 'c',
+                    '\u00E7': 'c',
+                    '\u1E09': 'c',
+                    '\u0188': 'c',
+                    '\u023C': 'c',
+                    '\uA73F': 'c',
+                    '\u2184': 'c',
+                    '\u24D3': 'd',
+                    '\uFF44': 'd',
+                    '\u1E0B': 'd',
+                    '\u010F': 'd',
+                    '\u1E0D': 'd',
+                    '\u1E11': 'd',
+                    '\u1E13': 'd',
+                    '\u1E0F': 'd',
+                    '\u0111': 'd',
+                    '\u018C': 'd',
+                    '\u0256': 'd',
+                    '\u0257': 'd',
+                    '\uA77A': 'd',
+                    '\u01F3': 'dz',
+                    '\u01C6': 'dz',
+                    '\u24D4': 'e',
+                    '\uFF45': 'e',
+                    '\u00E8': 'e',
+                    '\u00E9': 'e',
+                    '\u00EA': 'e',
+                    '\u1EC1': 'e',
+                    '\u1EBF': 'e',
+                    '\u1EC5': 'e',
+                    '\u1EC3': 'e',
+                    '\u1EBD': 'e',
+                    '\u0113': 'e',
+                    '\u1E15': 'e',
+                    '\u1E17': 'e',
+                    '\u0115': 'e',
+                    '\u0117': 'e',
+                    '\u00EB': 'e',
+                    '\u1EBB': 'e',
+                    '\u011B': 'e',
+                    '\u0205': 'e',
+                    '\u0207': 'e',
+                    '\u1EB9': 'e',
+                    '\u1EC7': 'e',
+                    '\u0229': 'e',
+                    '\u1E1D': 'e',
+                    '\u0119': 'e',
+                    '\u1E19': 'e',
+                    '\u1E1B': 'e',
+                    '\u0247': 'e',
+                    '\u025B': 'e',
+                    '\u01DD': 'e',
+                    '\u24D5': 'f',
+                    '\uFF46': 'f',
+                    '\u1E1F': 'f',
+                    '\u0192': 'f',
+                    '\uA77C': 'f',
+                    '\u24D6': 'g',
+                    '\uFF47': 'g',
+                    '\u01F5': 'g',
+                    '\u011D': 'g',
+                    '\u1E21': 'g',
+                    '\u011F': 'g',
+                    '\u0121': 'g',
+                    '\u01E7': 'g',
+                    '\u0123': 'g',
+                    '\u01E5': 'g',
+                    '\u0260': 'g',
+                    '\uA7A1': 'g',
+                    '\u1D79': 'g',
+                    '\uA77F': 'g',
+                    '\u24D7': 'h',
+                    '\uFF48': 'h',
+                    '\u0125': 'h',
+                    '\u1E23': 'h',
+                    '\u1E27': 'h',
+                    '\u021F': 'h',
+                    '\u1E25': 'h',
+                    '\u1E29': 'h',
+                    '\u1E2B': 'h',
+                    '\u1E96': 'h',
+                    '\u0127': 'h',
+                    '\u2C68': 'h',
+                    '\u2C76': 'h',
+                    '\u0265': 'h',
+                    '\u0195': 'hv',
+                    '\u24D8': 'i',
+                    '\uFF49': 'i',
+                    '\u00EC': 'i',
+                    '\u00ED': 'i',
+                    '\u00EE': 'i',
+                    '\u0129': 'i',
+                    '\u012B': 'i',
+                    '\u012D': 'i',
+                    '\u00EF': 'i',
+                    '\u1E2F': 'i',
+                    '\u1EC9': 'i',
+                    '\u01D0': 'i',
+                    '\u0209': 'i',
+                    '\u020B': 'i',
+                    '\u1ECB': 'i',
+                    '\u012F': 'i',
+                    '\u1E2D': 'i',
+                    '\u0268': 'i',
+                    '\u0131': 'i',
+                    '\u24D9': 'j',
+                    '\uFF4A': 'j',
+                    '\u0135': 'j',
+                    '\u01F0': 'j',
+                    '\u0249': 'j',
+                    '\u24DA': 'k',
+                    '\uFF4B': 'k',
+                    '\u1E31': 'k',
+                    '\u01E9': 'k',
+                    '\u1E33': 'k',
+                    '\u0137': 'k',
+                    '\u1E35': 'k',
+                    '\u0199': 'k',
+                    '\u2C6A': 'k',
+                    '\uA741': 'k',
+                    '\uA743': 'k',
+                    '\uA745': 'k',
+                    '\uA7A3': 'k',
+                    '\u24DB': 'l',
+                    '\uFF4C': 'l',
+                    '\u0140': 'l',
+                    '\u013A': 'l',
+                    '\u013E': 'l',
+                    '\u1E37': 'l',
+                    '\u1E39': 'l',
+                    '\u013C': 'l',
+                    '\u1E3D': 'l',
+                    '\u1E3B': 'l',
+                    '\u017F': 'l',
+                    '\u0142': 'l',
+                    '\u019A': 'l',
+                    '\u026B': 'l',
+                    '\u2C61': 'l',
+                    '\uA749': 'l',
+                    '\uA781': 'l',
+                    '\uA747': 'l',
+                    '\u01C9': 'lj',
+                    '\u24DC': 'm',
+                    '\uFF4D': 'm',
+                    '\u1E3F': 'm',
+                    '\u1E41': 'm',
+                    '\u1E43': 'm',
+                    '\u0271': 'm',
+                    '\u026F': 'm',
+                    '\u24DD': 'n',
+                    '\uFF4E': 'n',
+                    '\u01F9': 'n',
+                    '\u0144': 'n',
+                    '\u00F1': 'n',
+                    '\u1E45': 'n',
+                    '\u0148': 'n',
+                    '\u1E47': 'n',
+                    '\u0146': 'n',
+                    '\u1E4B': 'n',
+                    '\u1E49': 'n',
+                    '\u019E': 'n',
+                    '\u0272': 'n',
+                    '\u0149': 'n',
+                    '\uA791': 'n',
+                    '\uA7A5': 'n',
+                    '\u01CC': 'nj',
+                    '\u24DE': 'o',
+                    '\uFF4F': 'o',
+                    '\u00F2': 'o',
+                    '\u00F3': 'o',
+                    '\u00F4': 'o',
+                    '\u1ED3': 'o',
+                    '\u1ED1': 'o',
+                    '\u1ED7': 'o',
+                    '\u1ED5': 'o',
+                    '\u00F5': 'o',
+                    '\u1E4D': 'o',
+                    '\u022D': 'o',
+                    '\u1E4F': 'o',
+                    '\u014D': 'o',
+                    '\u1E51': 'o',
+                    '\u1E53': 'o',
+                    '\u014F': 'o',
+                    '\u022F': 'o',
+                    '\u0231': 'o',
+                    '\u00F6': 'o',
+                    '\u022B': 'o',
+                    '\u1ECF': 'o',
+                    '\u0151': 'o',
+                    '\u01D2': 'o',
+                    '\u020D': 'o',
+                    '\u020F': 'o',
+                    '\u01A1': 'o',
+                    '\u1EDD': 'o',
+                    '\u1EDB': 'o',
+                    '\u1EE1': 'o',
+                    '\u1EDF': 'o',
+                    '\u1EE3': 'o',
+                    '\u1ECD': 'o',
+                    '\u1ED9': 'o',
+                    '\u01EB': 'o',
+                    '\u01ED': 'o',
+                    '\u00F8': 'o',
+                    '\u01FF': 'o',
+                    '\u0254': 'o',
+                    '\uA74B': 'o',
+                    '\uA74D': 'o',
+                    '\u0275': 'o',
+                    '\u01A3': 'oi',
+                    '\u0223': 'ou',
+                    '\uA74F': 'oo',
+                    '\u24DF': 'p',
+                    '\uFF50': 'p',
+                    '\u1E55': 'p',
+                    '\u1E57': 'p',
+                    '\u01A5': 'p',
+                    '\u1D7D': 'p',
+                    '\uA751': 'p',
+                    '\uA753': 'p',
+                    '\uA755': 'p',
+                    '\u24E0': 'q',
+                    '\uFF51': 'q',
+                    '\u024B': 'q',
+                    '\uA757': 'q',
+                    '\uA759': 'q',
+                    '\u24E1': 'r',
+                    '\uFF52': 'r',
+                    '\u0155': 'r',
+                    '\u1E59': 'r',
+                    '\u0159': 'r',
+                    '\u0211': 'r',
+                    '\u0213': 'r',
+                    '\u1E5B': 'r',
+                    '\u1E5D': 'r',
+                    '\u0157': 'r',
+                    '\u1E5F': 'r',
+                    '\u024D': 'r',
+                    '\u027D': 'r',
+                    '\uA75B': 'r',
+                    '\uA7A7': 'r',
+                    '\uA783': 'r',
+                    '\u24E2': 's',
+                    '\uFF53': 's',
+                    '\u00DF': 's',
+                    '\u015B': 's',
+                    '\u1E65': 's',
+                    '\u015D': 's',
+                    '\u1E61': 's',
+                    '\u0161': 's',
+                    '\u1E67': 's',
+                    '\u1E63': 's',
+                    '\u1E69': 's',
+                    '\u0219': 's',
+                    '\u015F': 's',
+                    '\u023F': 's',
+                    '\uA7A9': 's',
+                    '\uA785': 's',
+                    '\u1E9B': 's',
+                    '\u24E3': 't',
+                    '\uFF54': 't',
+                    '\u1E6B': 't',
+                    '\u1E97': 't',
+                    '\u0165': 't',
+                    '\u1E6D': 't',
+                    '\u021B': 't',
+                    '\u0163': 't',
+                    '\u1E71': 't',
+                    '\u1E6F': 't',
+                    '\u0167': 't',
+                    '\u01AD': 't',
+                    '\u0288': 't',
+                    '\u2C66': 't',
+                    '\uA787': 't',
+                    '\uA729': 'tz',
+                    '\u24E4': 'u',
+                    '\uFF55': 'u',
+                    '\u00F9': 'u',
+                    '\u00FA': 'u',
+                    '\u00FB': 'u',
+                    '\u0169': 'u',
+                    '\u1E79': 'u',
+                    '\u016B': 'u',
+                    '\u1E7B': 'u',
+                    '\u016D': 'u',
+                    '\u00FC': 'u',
+                    '\u01DC': 'u',
+                    '\u01D8': 'u',
+                    '\u01D6': 'u',
+                    '\u01DA': 'u',
+                    '\u1EE7': 'u',
+                    '\u016F': 'u',
+                    '\u0171': 'u',
+                    '\u01D4': 'u',
+                    '\u0215': 'u',
+                    '\u0217': 'u',
+                    '\u01B0': 'u',
+                    '\u1EEB': 'u',
+                    '\u1EE9': 'u',
+                    '\u1EEF': 'u',
+                    '\u1EED': 'u',
+                    '\u1EF1': 'u',
+                    '\u1EE5': 'u',
+                    '\u1E73': 'u',
+                    '\u0173': 'u',
+                    '\u1E77': 'u',
+                    '\u1E75': 'u',
+                    '\u0289': 'u',
+                    '\u24E5': 'v',
+                    '\uFF56': 'v',
+                    '\u1E7D': 'v',
+                    '\u1E7F': 'v',
+                    '\u028B': 'v',
+                    '\uA75F': 'v',
+                    '\u028C': 'v',
+                    '\uA761': 'vy',
+                    '\u24E6': 'w',
+                    '\uFF57': 'w',
+                    '\u1E81': 'w',
+                    '\u1E83': 'w',
+                    '\u0175': 'w',
+                    '\u1E87': 'w',
+                    '\u1E85': 'w',
+                    '\u1E98': 'w',
+                    '\u1E89': 'w',
+                    '\u2C73': 'w',
+                    '\u24E7': 'x',
+                    '\uFF58': 'x',
+                    '\u1E8B': 'x',
+                    '\u1E8D': 'x',
+                    '\u24E8': 'y',
+                    '\uFF59': 'y',
+                    '\u1EF3': 'y',
+                    '\u00FD': 'y',
+                    '\u0177': 'y',
+                    '\u1EF9': 'y',
+                    '\u0233': 'y',
+                    '\u1E8F': 'y',
+                    '\u00FF': 'y',
+                    '\u1EF7': 'y',
+                    '\u1E99': 'y',
+                    '\u1EF5': 'y',
+                    '\u01B4': 'y',
+                    '\u024F': 'y',
+                    '\u1EFF': 'y',
+                    '\u24E9': 'z',
+                    '\uFF5A': 'z',
+                    '\u017A': 'z',
+                    '\u1E91': 'z',
+                    '\u017C': 'z',
+                    '\u017E': 'z',
+                    '\u1E93': 'z',
+                    '\u1E95': 'z',
+                    '\u01B6': 'z',
+                    '\u0225': 'z',
+                    '\u0240': 'z',
+                    '\u2C6C': 'z',
+                    '\uA763': 'z',
+                    '\u0386': '\u0391',
+                    '\u0388': '\u0395',
+                    '\u0389': '\u0397',
+                    '\u038A': '\u0399',
+                    '\u03AA': '\u0399',
+                    '\u038C': '\u039F',
+                    '\u038E': '\u03A5',
+                    '\u03AB': '\u03A5',
+                    '\u038F': '\u03A9',
+                    '\u03AC': '\u03B1',
+                    '\u03AD': '\u03B5',
+                    '\u03AE': '\u03B7',
+                    '\u03AF': '\u03B9',
+                    '\u03CA': '\u03B9',
+                    '\u0390': '\u03B9',
+                    '\u03CC': '\u03BF',
+                    '\u03CD': '\u03C5',
+                    '\u03CB': '\u03C5',
+                    '\u03B0': '\u03C5',
+                    '\u03C9': '\u03C9',
+                    '\u03C2': '\u03C3'
+                };
+
+                return diacritics;
+            });
+
+            S2.define('select2/data/base', [
+                '../utils'
+            ], function (Utils) {
+                function BaseAdapter($element, options) {
+                    BaseAdapter.__super__.constructor.call(this);
+                }
+
+                Utils.Extend(BaseAdapter, Utils.Observable);
+
+                BaseAdapter.prototype.current = function (callback) {
+                    throw new Error('The `current` method must be defined in child classes.');
+                };
+
+                BaseAdapter.prototype.query = function (params, callback) {
+                    throw new Error('The `query` method must be defined in child classes.');
+                };
+
+                BaseAdapter.prototype.bind = function (container, $container) {
+                    // Can be implemented in subclasses
+                };
+
+                BaseAdapter.prototype.destroy = function () {
+                    // Can be implemented in subclasses
+                };
+
+                BaseAdapter.prototype.generateResultId = function (container, data) {
+                    var id = container.id + '-result-';
+
+                    id += Utils.generateChars(4);
+
+                    if (data.id != null) {
+                        id += '-' + data.id.toString();
+                    } else {
+                        id += '-' + Utils.generateChars(4);
+                    }
+                    return id;
+                };
+
+                return BaseAdapter;
+            });
+
+            S2.define('select2/data/select', [
+                './base',
+                '../utils',
+                'jquery'
+            ], function (BaseAdapter, Utils, $) {
+                function SelectAdapter($element, options) {
+                    this.$element = $element;
+                    this.options = options;
+
+                    SelectAdapter.__super__.constructor.call(this);
+                }
+
+                Utils.Extend(SelectAdapter, BaseAdapter);
+
+                SelectAdapter.prototype.current = function (callback) {
+                    var data = [];
+                    var self = this;
+
+                    this.$element.find(':selected').each(function () {
+                        var $option = $(this);
+
+                        var option = self.item($option);
+
+                        data.push(option);
+                    });
+
+                    callback(data);
+                };
+
+                SelectAdapter.prototype.select = function (data) {
+                    var self = this;
+
+                    data.selected = true;
+
+                    // If data.element is a DOM node, use it instead
+                    if ($(data.element).is('option')) {
+                        data.element.selected = true;
+
+                        this.$element.trigger('change');
+
+                        return;
+                    }
+
+                    if (this.$element.prop('multiple')) {
+                        this.current(function (currentData) {
+                            var val = [];
+
+                            data = [data];
+                            data.push.apply(data, currentData);
+
+                            for (var d = 0; d < data.length; d++) {
+                                var id = data[d].id;
+
+                                if ($.inArray(id, val) === -1) {
+                                    val.push(id);
+                                }
+                            }
+
+                            self.$element.val(val);
+                            self.$element.trigger('change');
+                        });
+                    } else {
+                        var val = data.id;
+
+                        this.$element.val(val);
+                        this.$element.trigger('change');
+                    }
+                };
+
+                SelectAdapter.prototype.unselect = function (data) {
+                    var self = this;
+
+                    if (!this.$element.prop('multiple')) {
+                        return;
+                    }
+
+                    data.selected = false;
+
+                    if ($(data.element).is('option')) {
+                        data.element.selected = false;
+
+                        this.$element.trigger('change');
+
+                        return;
+                    }
+
+                    this.current(function (currentData) {
+                        var val = [];
+
+                        for (var d = 0; d < currentData.length; d++) {
+                            var id = currentData[d].id;
+
+                            if (id !== data.id && $.inArray(id, val) === -1) {
+                                val.push(id);
+                            }
+                        }
+
+                        self.$element.val(val);
+
+                        self.$element.trigger('change');
+                    });
+                };
+
+                SelectAdapter.prototype.bind = function (container, $container) {
+                    var self = this;
+
+                    this.container = container;
+
+                    container.on('select', function (params) {
+                        self.select(params.data);
+                    });
+
+                    container.on('unselect', function (params) {
+                        self.unselect(params.data);
+                    });
+                };
+
+                SelectAdapter.prototype.destroy = function () {
+                    // Remove anything added to child elements
+                    this.$element.find('*').each(function () {
+                        // Remove any custom data set by Select2
+                        $.removeData(this, 'data');
+                    });
+                };
+
+                SelectAdapter.prototype.query = function (params, callback) {
+                    var data = [];
+                    var self = this;
+
+                    var $options = this.$element.children();
+
+                    $options.each(function () {
+                        var $option = $(this);
+
+                        if (!$option.is('option') && !$option.is('optgroup')) {
+                            return;
+                        }
+
+                        var option = self.item($option);
+
+                        var matches = self.matches(params, option);
+
+                        if (matches !== null) {
+                            data.push(matches);
+                        }
+                    });
+
+                    callback({
+                        results: data
+                    });
+                };
+
+                SelectAdapter.prototype.addOptions = function ($options) {
+                    Utils.appendMany(this.$element, $options);
+                };
+
+                SelectAdapter.prototype.option = function (data) {
+                    var option;
+
+                    if (data.children) {
+                        option = document.createElement('optgroup');
+                        option.label = data.text;
+                    } else {
+                        option = document.createElement('option');
+
+                        if (option.textContent !== undefined) {
+                            option.textContent = data.text;
+                        } else {
+                            option.innerText = data.text;
+                        }
+                    }
+
+                    if (data.id) {
+                        option.value = data.id;
+                    }
+
+                    if (data.disabled) {
+                        option.disabled = true;
+                    }
+
+                    if (data.selected) {
+                        option.selected = true;
+                    }
+
+                    if (data.title) {
+                        option.title = data.title;
+                    }
+
+                    var $option = $(option);
+
+                    var normalizedData = this._normalizeItem(data);
+                    normalizedData.element = option;
+
+                    // Override the option's data with the combined data
+                    $.data(option, 'data', normalizedData);
+
+                    return $option;
+                };
+
+                SelectAdapter.prototype.item = function ($option) {
+                    var data = {};
+
+                    data = $.data($option[0], 'data');
+
+                    if (data != null) {
+                        return data;
+                    }
+
+                    if ($option.is('option')) {
+                        data = {
+                            id: $option.val(),
+                            text: $option.text(),
+                            disabled: $option.prop('disabled'),
+                            selected: $option.prop('selected'),
+                            title: $option.prop('title')
+                        };
+                    } else if ($option.is('optgroup')) {
+                        data = {
+                            text: $option.prop('label'),
+                            children: [],
+                            title: $option.prop('title')
+                        };
+
+                        var $children = $option.children('option');
+                        var children = [];
+
+                        for (var c = 0; c < $children.length; c++) {
+                            var $child = $($children[c]);
+
+                            var child = this.item($child);
+
+                            children.push(child);
+                        }
+
+                        data.children = children;
+                    }
+
+                    data = this._normalizeItem(data);
+                    data.element = $option[0];
+
+                    $.data($option[0], 'data', data);
+
+                    return data;
+                };
+
+                SelectAdapter.prototype._normalizeItem = function (item) {
+                    if (!$.isPlainObject(item)) {
+                        item = {
+                            id: item,
+                            text: item
+                        };
+                    }
+
+                    item = $.extend({}, {
+                        text: ''
+                    }, item);
+
+                    var defaults = {
+                        selected: false,
+                        disabled: false
+                    };
+
+                    if (item.id != null) {
+                        item.id = item.id.toString();
+                    }
+
+                    if (item.text != null) {
+                        item.text = item.text.toString();
+                    }
+
+                    if (item._resultId == null && item.id && this.container != null) {
+                        item._resultId = this.generateResultId(this.container, item);
+                    }
+
+                    return $.extend({}, defaults, item);
+                };
+
+                SelectAdapter.prototype.matches = function (params, data) {
+                    var matcher = this.options.get('matcher');
+
+                    return matcher(params, data);
+                };
+
+                return SelectAdapter;
+            });
+
+            S2.define('select2/data/array', [
+                './select',
+                '../utils',
+                'jquery'
+            ], function (SelectAdapter, Utils, $) {
+                function ArrayAdapter($element, options) {
+                    var data = options.get('data') || [];
+
+                    ArrayAdapter.__super__.constructor.call(this, $element, options);
+
+                    this.addOptions(this.convertToOptions(data));
+                }
+
+                Utils.Extend(ArrayAdapter, SelectAdapter);
+
+                ArrayAdapter.prototype.select = function (data) {
+                    var $option = this.$element.find('option').filter(function (i, elm) {
+                        return elm.value == data.id.toString();
+                    });
+
+                    if ($option.length === 0) {
+                        $option = this.option(data);
+
+                        this.addOptions($option);
+                    }
+
+                    ArrayAdapter.__super__.select.call(this, data);
+                };
+
+                ArrayAdapter.prototype.convertToOptions = function (data) {
+                    var self = this;
+
+                    var $existing = this.$element.find('option');
+                    var existingIds = $existing.map(function () {
+                        return self.item($(this)).id;
+                    }).get();
+
+                    var $options = [];
+
+                    // Filter out all items except for the one passed in the argument
+                    function onlyItem(item) {
+                        return function () {
+                            return $(this).val() == item.id;
+                        };
+                    }
+
+                    for (var d = 0; d < data.length; d++) {
+                        var item = this._normalizeItem(data[d]);
+
+                        // Skip items which were pre-loaded, only merge the data
+                        if ($.inArray(item.id, existingIds) >= 0) {
+                            var $existingOption = $existing.filter(onlyItem(item));
+
+                            var existingData = this.item($existingOption);
+                            var newData = $.extend(true, {}, item, existingData);
+
+                            var $newOption = this.option(newData);
+
+                            $existingOption.replaceWith($newOption);
+
+                            continue;
+                        }
+
+                        var $option = this.option(item);
+
+                        if (item.children) {
+                            var $children = this.convertToOptions(item.children);
+
+                            Utils.appendMany($option, $children);
+                        }
+
+                        $options.push($option);
+                    }
+
+                    return $options;
+                };
+
+                return ArrayAdapter;
+            });
+
+            S2.define('select2/data/ajax', [
+                './array',
+                '../utils',
+                'jquery'
+            ], function (ArrayAdapter, Utils, $) {
+                function AjaxAdapter($element, options) {
+                    this.ajaxOptions = this._applyDefaults(options.get('ajax'));
+
+                    if (this.ajaxOptions.processResults != null) {
+                        this.processResults = this.ajaxOptions.processResults;
+                    }
+
+                    AjaxAdapter.__super__.constructor.call(this, $element, options);
+                }
+
+                Utils.Extend(AjaxAdapter, ArrayAdapter);
+
+                AjaxAdapter.prototype._applyDefaults = function (options) {
+                    var defaults = {
+                        data: function (params) {
+                            return $.extend({}, params, {
+                                q: params.term
+                            });
+                        },
+                        transport: function (params, success, failure) {
+                            var $request = $.ajax(params);
+
+                            $request.then(success);
+                            $request.fail(failure);
+
+                            return $request;
+                        }
+                    };
+
+                    return $.extend({}, defaults, options, true);
+                };
+
+                AjaxAdapter.prototype.processResults = function (results) {
+                    return results;
+                };
+
+                AjaxAdapter.prototype.query = function (params, callback) {
+                    var matches = [];
+                    var self = this;
+
+                    if (this._request != null) {
+                        // JSONP requests cannot always be aborted
+                        if ($.isFunction(this._request.abort)) {
+                            this._request.abort();
+                        }
+
+                        this._request = null;
+                    }
+
+                    var options = $.extend({
+                        type: 'GET'
+                    }, this.ajaxOptions);
+
+                    if (typeof options.url === 'function') {
+                        options.url = options.url.call(this.$element, params);
+                    }
+
+                    if (typeof options.data === 'function') {
+                        options.data = options.data.call(this.$element, params);
+                    }
+
+                    function request() {
+                        var $request = options.transport(options, function (data) {
+                            var results = self.processResults(data, params);
+
+                            if (self.options.get('debug') && window.console && console.error) {
+                                // Check to make sure that the response included a `results` key.
+                                if (!results || !results.results || !$.isArray(results.results)) {
+                                    console.error(
+                                        'Select2: The AJAX results did not return an array in the ' +
+                                        '`results` key of the response.'
+                                    );
+                                }
+                            }
+
+                            callback(results);
+                        }, function () {
+                            // Attempt to detect if a request was aborted
+                            // Only works if the transport exposes a status property
+                            if ($request.status && $request.status === '0') {
+                                return;
+                            }
+
+                            self.trigger('results:message', {
+                                message: 'errorLoading'
+                            });
+                        });
+
+                        self._request = $request;
+                    }
+
+                    if (this.ajaxOptions.delay && params.term != null) {
+                        if (this._queryTimeout) {
+                            window.clearTimeout(this._queryTimeout);
+                        }
+
+                        this._queryTimeout = window.setTimeout(request, this.ajaxOptions.delay);
+                    } else {
+                        request();
+                    }
+                };
+
+                return AjaxAdapter;
+            });
+
+            S2.define('select2/data/tags', [
+                'jquery'
+            ], function ($) {
+                function Tags(decorated, $element, options) {
+                    var tags = options.get('tags');
+
+                    var createTag = options.get('createTag');
+
+                    if (createTag !== undefined) {
+                        this.createTag = createTag;
+                    }
+
+                    var insertTag = options.get('insertTag');
+
+                    if (insertTag !== undefined) {
+                        this.insertTag = insertTag;
+                    }
+
+                    decorated.call(this, $element, options);
+
+                    if ($.isArray(tags)) {
+                        for (var t = 0; t < tags.length; t++) {
+                            var tag = tags[t];
+                            var item = this._normalizeItem(tag);
+
+                            var $option = this.option(item);
+
+                            this.$element.append($option);
+                        }
+                    }
+                }
+
+                Tags.prototype.query = function (decorated, params, callback) {
+                    var self = this;
+
+                    this._removeOldTags();
+
+                    if (params.term == null || params.page != null) {
+                        decorated.call(this, params, callback);
+                        return;
+                    }
+
+                    function wrapper(obj, child) {
+                        var data = obj.results;
+
+                        for (var i = 0; i < data.length; i++) {
+                            var option = data[i];
+
+                            var checkChildren = (
+                                option.children != null &&
+                                !wrapper({
+                                    results: option.children
+                                }, true)
+                            );
+
+                            var checkText = option.text === params.term;
+
+                            if (checkText || checkChildren) {
+                                if (child) {
+                                    return false;
+                                }
+
+                                obj.data = data;
+                                callback(obj);
+
+                                return;
+                            }
+                        }
+
+                        if (child) {
+                            return true;
+                        }
+
+                        var tag = self.createTag(params);
+
+                        if (tag != null) {
+                            var $option = self.option(tag);
+                            $option.attr('data-select2-tag', true);
+
+                            self.addOptions([$option]);
+
+                            self.insertTag(data, tag);
+                        }
+
+                        obj.results = data;
+
+                        callback(obj);
+                    }
+
+                    decorated.call(this, params, wrapper);
+                };
+
+                Tags.prototype.createTag = function (decorated, params) {
+                    var term = $.trim(params.term);
+
+                    if (term === '') {
+                        return null;
+                    }
+
+                    return {
+                        id: term,
+                        text: term
+                    };
+                };
+
+                Tags.prototype.insertTag = function (_, data, tag) {
+                    data.unshift(tag);
+                };
+
+                Tags.prototype._removeOldTags = function (_) {
+                    var tag = this._lastTag;
+
+                    var $options = this.$element.find('option[data-select2-tag]');
+
+                    $options.each(function () {
+                        if (this.selected) {
+                            return;
+                        }
+
+                        $(this).remove();
+                    });
+                };
+
+                return Tags;
+            });
+
+            S2.define('select2/data/tokenizer', [
+                'jquery'
+            ], function ($) {
+                function Tokenizer(decorated, $element, options) {
+                    var tokenizer = options.get('tokenizer');
+
+                    if (tokenizer !== undefined) {
+                        this.tokenizer = tokenizer;
+                    }
+
+                    decorated.call(this, $element, options);
+                }
+
+                Tokenizer.prototype.bind = function (decorated, container, $container) {
+                    decorated.call(this, container, $container);
+
+                    this.$search = container.dropdown.$search || container.selection.$search ||
+                        $container.find('.select2-search__field');
+                };
+
+                Tokenizer.prototype.query = function (decorated, params, callback) {
+                    var self = this;
+
+                    function createAndSelect(data) {
+                        // Normalize the data object so we can use it for checks
+                        var item = self._normalizeItem(data);
+
+                        // Check if the data object already exists as a tag
+                        // Select it if it doesn't
+                        var $existingOptions = self.$element.find('option').filter(function () {
+                            return $(this).val() === item.id;
+                        });
+
+                        // If an existing option wasn't found for it, create the option
+                        if (!$existingOptions.length) {
+                            var $option = self.option(item);
+                            $option.attr('data-select2-tag', true);
+
+                            self._removeOldTags();
+                            self.addOptions([$option]);
+                        }
+
+                        // Select the item, now that we know there is an option for it
+                        select(item);
+                    }
+
+                    function select(data) {
+                        self.trigger('select', {
+                            data: data
+                        });
+                    }
+
+                    params.term = params.term || '';
+
+                    var tokenData = this.tokenizer(params, this.options, createAndSelect);
+
+                    if (tokenData.term !== params.term) {
+                        // Replace the search term if we have the search box
+                        if (this.$search.length) {
+                            this.$search.val(tokenData.term);
+                            this.$search.focus();
+                        }
+
+                        params.term = tokenData.term;
+                    }
+
+                    decorated.call(this, params, callback);
+                };
+
+                Tokenizer.prototype.tokenizer = function (_, params, options, callback) {
+                    var separators = options.get('tokenSeparators') || [];
+                    var term = params.term;
+                    var i = 0;
+
+                    var createTag = this.createTag || function (params) {
+                        return {
+                            id: params.term,
+                            text: params.term
+                        };
+                    };
+
+                    while (i < term.length) {
+                        var termChar = term[i];
+
+                        if ($.inArray(termChar, separators) === -1) {
+                            i++;
+
+                            continue;
+                        }
+
+                        var part = term.substr(0, i);
+                        var partParams = $.extend({}, params, {
+                            term: part
+                        });
+
+                        var data = createTag(partParams);
+
+                        if (data == null) {
+                            i++;
+                            continue;
+                        }
+
+                        callback(data);
+
+                        // Reset the term to not include the tokenized portion
+                        term = term.substr(i + 1) || '';
+                        i = 0;
+                    }
+
+                    return {
+                        term: term
+                    };
+                };
+
+                return Tokenizer;
+            });
+
+            S2.define('select2/data/minimumInputLength', [], function () {
+                function MinimumInputLength(decorated, $e, options) {
+                    this.minimumInputLength = options.get('minimumInputLength');
+
+                    decorated.call(this, $e, options);
+                }
+
+                MinimumInputLength.prototype.query = function (decorated, params, callback) {
+                    params.term = params.term || '';
+
+                    if (params.term.length < this.minimumInputLength) {
+                        this.trigger('results:message', {
+                            message: 'inputTooShort',
+                            args: {
+                                minimum: this.minimumInputLength,
+                                input: params.term,
+                                params: params
+                            }
+                        });
+
+                        return;
+                    }
+
+                    decorated.call(this, params, callback);
+                };
+
+                return MinimumInputLength;
+            });
+
+            S2.define('select2/data/maximumInputLength', [], function () {
+                function MaximumInputLength(decorated, $e, options) {
+                    this.maximumInputLength = options.get('maximumInputLength');
+
+                    decorated.call(this, $e, options);
+                }
+
+                MaximumInputLength.prototype.query = function (decorated, params, callback) {
+                    params.term = params.term || '';
+
+                    if (this.maximumInputLength > 0 &&
+                        params.term.length > this.maximumInputLength) {
+                        this.trigger('results:message', {
+                            message: 'inputTooLong',
+                            args: {
+                                maximum: this.maximumInputLength,
+                                input: params.term,
+                                params: params
+                            }
+                        });
+
+                        return;
+                    }
+
+                    decorated.call(this, params, callback);
+                };
+
+                return MaximumInputLength;
+            });
+
+            S2.define('select2/data/maximumSelectionLength', [], function () {
+                function MaximumSelectionLength(decorated, $e, options) {
+                    this.maximumSelectionLength = options.get('maximumSelectionLength');
+
+                    decorated.call(this, $e, options);
+                }
+
+                MaximumSelectionLength.prototype.query =
+                    function (decorated, params, callback) {
+                        var self = this;
+
+                        this.current(function (currentData) {
+                            var count = currentData != null ? currentData.length : 0;
+                            if (self.maximumSelectionLength > 0 &&
+                                count >= self.maximumSelectionLength) {
+                                self.trigger('results:message', {
+                                    message: 'maximumSelected',
+                                    args: {
+                                        maximum: self.maximumSelectionLength
+                                    }
+                                });
+                                return;
+                            }
+                            decorated.call(self, params, callback);
+                        });
+                    };
+
+                return MaximumSelectionLength;
+            });
+
+            S2.define('select2/dropdown', [
+                'jquery',
+                './utils'
+            ], function ($, Utils) {
+                function Dropdown($element, options) {
+                    this.$element = $element;
+                    this.options = options;
+
+                    Dropdown.__super__.constructor.call(this);
+                }
+
+                Utils.Extend(Dropdown, Utils.Observable);
+
+                Dropdown.prototype.render = function () {
+                    var $dropdown = $(
+                        '<span class="select2-dropdown">' +
+                        '<span class="select2-results"></span>' +
+                        '</span>'
+                    );
+
+                    $dropdown.attr('dir', this.options.get('dir'));
+
+                    this.$dropdown = $dropdown;
+
+                    return $dropdown;
+                };
+
+                Dropdown.prototype.bind = function () {
+                    // Should be implemented in subclasses
+                };
+
+                Dropdown.prototype.position = function ($dropdown, $container) {
+                    // Should be implmented in subclasses
+                };
+
+                Dropdown.prototype.destroy = function () {
+                    // Remove the dropdown from the DOM
+                    this.$dropdown.remove();
+                };
+
+                return Dropdown;
+            });
+
+            S2.define('select2/dropdown/search', [
+                'jquery',
+                '../utils'
+            ], function ($, Utils) {
+                function Search() {
+                }
+
+                Search.prototype.render = function (decorated) {
+                    var $rendered = decorated.call(this);
+
+                    var $search = $(
+                        '<span class="select2-search select2-search--dropdown">' +
+                        '<input class="select2-search__field" type="search" tabindex="-1"' +
+                        ' autocomplete="off" autocorrect="off" autocapitalize="off"' +
+                        ' spellcheck="false" role="textbox" />' +
+                        '</span>'
+                    );
+
+                    this.$searchContainer = $search;
+                    this.$search = $search.find('input');
+
+                    $rendered.prepend($search);
+
+                    return $rendered;
+                };
+
+                Search.prototype.bind = function (decorated, container, $container) {
+                    var self = this;
+
+                    decorated.call(this, container, $container);
+
+                    this.$search.on('keydown', function (evt) {
+                        self.trigger('keypress', evt);
+
+                        self._keyUpPrevented = evt.isDefaultPrevented();
+                    });
+
+                    // Workaround for browsers which do not support the `input` event
+                    // This will prevent double-triggering of events for browsers which support
+                    // both the `keyup` and `input` events.
+                    this.$search.on('input', function (evt) {
+                        // Unbind the duplicated `keyup` event
+                        $(this).off('keyup');
+                    });
+
+                    this.$search.on('keyup input', function (evt) {
+                        self.handleSearch(evt);
+                    });
+
+                    container.on('open', function () {
+                        self.$search.attr('tabindex', 0);
+
+                        self.$search.focus();
+
+                        window.setTimeout(function () {
+                            self.$search.focus();
+                        }, 0);
+                    });
+
+                    container.on('close', function () {
+                        self.$search.attr('tabindex', -1);
+
+                        self.$search.val('');
+                    });
+
+                    container.on('focus', function () {
+                        if (container.isOpen()) {
+                            self.$search.focus();
+                        }
+                    });
+
+                    container.on('results:all', function (params) {
+                        if (params.query.term == null || params.query.term === '') {
+                            var showSearch = self.showSearch(params);
+
+                            if (showSearch) {
+                                self.$searchContainer.removeClass('select2-search--hide');
+                            } else {
+                                self.$searchContainer.addClass('select2-search--hide');
+                            }
+                        }
+                    });
+                };
+
+                Search.prototype.handleSearch = function (evt) {
+                    if (!this._keyUpPrevented) {
+                        var input = this.$search.val();
+
+                        this.trigger('query', {
+                            term: input
+                        });
+                    }
+
+                    this._keyUpPrevented = false;
+                };
+
+                Search.prototype.showSearch = function (_, params) {
+                    return true;
+                };
+
+                return Search;
+            });
+
+            S2.define('select2/dropdown/hidePlaceholder', [], function () {
+                function HidePlaceholder(decorated, $element, options, dataAdapter) {
+                    this.placeholder = this.normalizePlaceholder(options.get('placeholder'));
+
+                    decorated.call(this, $element, options, dataAdapter);
+                }
+
+                HidePlaceholder.prototype.append = function (decorated, data) {
+                    data.results = this.removePlaceholder(data.results);
+
+                    decorated.call(this, data);
+                };
+
+                HidePlaceholder.prototype.normalizePlaceholder = function (_, placeholder) {
+                    if (typeof placeholder === 'string') {
+                        placeholder = {
+                            id: '',
+                            text: placeholder
+                        };
+                    }
+
+                    return placeholder;
+                };
+
+                HidePlaceholder.prototype.removePlaceholder = function (_, data) {
+                    var modifiedData = data.slice(0);
+
+                    for (var d = data.length - 1; d >= 0; d--) {
+                        var item = data[d];
+
+                        if (this.placeholder.id === item.id) {
+                            modifiedData.splice(d, 1);
+                        }
+                    }
+
+                    return modifiedData;
+                };
+
+                return HidePlaceholder;
+            });
+
+            S2.define('select2/dropdown/infiniteScroll', [
+                'jquery'
+            ], function ($) {
+                function InfiniteScroll(decorated, $element, options, dataAdapter) {
+                    this.lastParams = {};
+
+                    decorated.call(this, $element, options, dataAdapter);
+
+                    this.$loadingMore = this.createLoadingMore();
+                    this.loading = false;
+                }
+
+                InfiniteScroll.prototype.append = function (decorated, data) {
+                    this.$loadingMore.remove();
+                    this.loading = false;
+
+                    decorated.call(this, data);
+
+                    if (this.showLoadingMore(data)) {
+                        this.$results.append(this.$loadingMore);
+                    }
+                };
+
+                InfiniteScroll.prototype.bind = function (decorated, container, $container) {
+                    var self = this;
+
+                    decorated.call(this, container, $container);
+
+                    container.on('query', function (params) {
+                        self.lastParams = params;
+                        self.loading = true;
+                    });
+
+                    container.on('query:append', function (params) {
+                        self.lastParams = params;
+                        self.loading = true;
+                    });
+
+                    this.$results.on('scroll', function () {
+                        var isLoadMoreVisible = $.contains(
+                            document.documentElement,
+                            self.$loadingMore[0]
+                        );
+
+                        if (self.loading || !isLoadMoreVisible) {
+                            return;
+                        }
+
+                        var currentOffset = self.$results.offset().top +
+                            self.$results.outerHeight(false);
+                        var loadingMoreOffset = self.$loadingMore.offset().top +
+                            self.$loadingMore.outerHeight(false);
+
+                        if (currentOffset + 50 >= loadingMoreOffset) {
+                            self.loadMore();
+                        }
+                    });
+                };
+
+                InfiniteScroll.prototype.loadMore = function () {
+                    this.loading = true;
+
+                    var params = $.extend({}, {page: 1}, this.lastParams);
+
+                    params.page++;
+
+                    this.trigger('query:append', params);
+                };
+
+                InfiniteScroll.prototype.showLoadingMore = function (_, data) {
+                    return data.pagination && data.pagination.more;
+                };
+
+                InfiniteScroll.prototype.createLoadingMore = function () {
+                    var $option = $(
+                        '<li ' +
+                        'class="select2-results__option select2-results__option--load-more"' +
+                        'role="treeitem" aria-disabled="true"></li>'
+                    );
+
+                    var message = this.options.get('translations').get('loadingMore');
+
+                    $option.html(message(this.lastParams));
+
+                    return $option;
+                };
+
+                return InfiniteScroll;
+            });
+
+            S2.define('select2/dropdown/attachBody', [
+                'jquery',
+                '../utils'
+            ], function ($, Utils) {
+                function AttachBody(decorated, $element, options) {
+                    this.$dropdownParent = options.get('dropdownParent') || $(document.body);
+
+                    decorated.call(this, $element, options);
+                }
+
+                AttachBody.prototype.bind = function (decorated, container, $container) {
+                    var self = this;
+
+                    var setupResultsEvents = false;
+
+                    decorated.call(this, container, $container);
+
+                    container.on('open', function () {
+                        self._showDropdown();
+                        self._attachPositioningHandler(container);
+
+                        if (!setupResultsEvents) {
+                            setupResultsEvents = true;
+
+                            container.on('results:all', function () {
+                                self._positionDropdown();
+                                self._resizeDropdown();
+                            });
+
+                            container.on('results:append', function () {
+                                self._positionDropdown();
+                                self._resizeDropdown();
+                            });
+                        }
+                    });
+
+                    container.on('close', function () {
+                        self._hideDropdown();
+                        self._detachPositioningHandler(container);
+                    });
+
+                    this.$dropdownContainer.on('mousedown', function (evt) {
+                        evt.stopPropagation();
+                    });
+                };
+
+                AttachBody.prototype.destroy = function (decorated) {
+                    decorated.call(this);
+
+                    this.$dropdownContainer.remove();
+                };
+
+                AttachBody.prototype.position = function (decorated, $dropdown, $container) {
+                    // Clone all of the container classes
+                    $dropdown.attr('class', $container.attr('class'));
+
+                    $dropdown.removeClass('select2');
+                    $dropdown.addClass('select2-container--open');
+
+                    $dropdown.css({
+                        position: 'absolute',
+                        top: -999999
+                    });
+
+                    this.$container = $container;
+                };
+
+                AttachBody.prototype.render = function (decorated) {
+                    var $container = $('<span></span>');
+
+                    var $dropdown = decorated.call(this);
+                    $container.append($dropdown);
+
+                    this.$dropdownContainer = $container;
+
+                    return $container;
+                };
+
+                AttachBody.prototype._hideDropdown = function (decorated) {
+                    this.$dropdownContainer.detach();
+                };
+
+                AttachBody.prototype._attachPositioningHandler =
+                    function (decorated, container) {
+                        var self = this;
+
+                        var scrollEvent = 'scroll.select2.' + container.id;
+                        var resizeEvent = 'resize.select2.' + container.id;
+                        var orientationEvent = 'orientationchange.select2.' + container.id;
+
+                        var $watchers = this.$container.parents().filter(Utils.hasScroll);
+                        $watchers.each(function () {
+                            $(this).data('select2-scroll-position', {
+                                x: $(this).scrollLeft(),
+                                y: $(this).scrollTop()
+                            });
+                        });
+
+                        $watchers.on(scrollEvent, function (ev) {
+                            var position = $(this).data('select2-scroll-position');
+                            $(this).scrollTop(position.y);
+                        });
+
+                        $(window).on(scrollEvent + ' ' + resizeEvent + ' ' + orientationEvent,
+                            function (e) {
+                                self._positionDropdown();
+                                self._resizeDropdown();
+                            });
+                    };
+
+                AttachBody.prototype._detachPositioningHandler =
+                    function (decorated, container) {
+                        var scrollEvent = 'scroll.select2.' + container.id;
+                        var resizeEvent = 'resize.select2.' + container.id;
+                        var orientationEvent = 'orientationchange.select2.' + container.id;
+
+                        var $watchers = this.$container.parents().filter(Utils.hasScroll);
+                        $watchers.off(scrollEvent);
+
+                        $(window).off(scrollEvent + ' ' + resizeEvent + ' ' + orientationEvent);
+                    };
+
+                AttachBody.prototype._positionDropdown = function () {
+                    var $window = $(window);
+
+                    var isCurrentlyAbove = this.$dropdown.hasClass('select2-dropdown--above');
+                    var isCurrentlyBelow = this.$dropdown.hasClass('select2-dropdown--below');
+
+                    var newDirection = null;
+
+                    var offset = this.$container.offset();
+
+                    offset.bottom = offset.top + this.$container.outerHeight(false);
+
+                    var container = {
+                        height: this.$container.outerHeight(false)
+                    };
+
+                    container.top = offset.top;
+                    container.bottom = offset.top + container.height;
+
+                    var dropdown = {
+                        height: this.$dropdown.outerHeight(false)
+                    };
+
+                    var viewport = {
+                        top: $window.scrollTop(),
+                        bottom: $window.scrollTop() + $window.height()
+                    };
+
+                    var enoughRoomAbove = viewport.top < (offset.top - dropdown.height);
+                    var enoughRoomBelow = viewport.bottom > (offset.bottom + dropdown.height);
+
+                    var css = {
+                        left: offset.left,
+                        top: container.bottom
+                    };
+
+                    // Determine what the parent element is to use for calciulating the offset
+                    var $offsetParent = this.$dropdownParent;
+
+                    // For statically positoned elements, we need to get the element
+                    // that is determining the offset
+                    if ($offsetParent.css('position') === 'static') {
+                        $offsetParent = $offsetParent.offsetParent();
+                    }
+
+                    var parentOffset = $offsetParent.offset();
+
+                    css.top -= parentOffset.top;
+                    css.left -= parentOffset.left;
+
+                    if (!isCurrentlyAbove && !isCurrentlyBelow) {
+                        newDirection = 'below';
+                    }
+
+                    if (!enoughRoomBelow && enoughRoomAbove && !isCurrentlyAbove) {
+                        newDirection = 'above';
+                    } else if (!enoughRoomAbove && enoughRoomBelow && isCurrentlyAbove) {
+                        newDirection = 'below';
+                    }
+
+                    if (newDirection == 'above' ||
+                        (isCurrentlyAbove && newDirection !== 'below')) {
+                        css.top = container.top - parentOffset.top - dropdown.height;
+                    }
+
+                    if (newDirection != null) {
+                        this.$dropdown
+                            .removeClass('select2-dropdown--below select2-dropdown--above')
+                            .addClass('select2-dropdown--' + newDirection);
+                        this.$container
+                            .removeClass('select2-container--below select2-container--above')
+                            .addClass('select2-container--' + newDirection);
+                    }
+
+                    this.$dropdownContainer.css(css);
+                };
+
+                AttachBody.prototype._resizeDropdown = function () {
+                    var css = {
+                        width: this.$container.outerWidth(false) + 'px'
+                    };
+
+                    if (this.options.get('dropdownAutoWidth')) {
+                        css.minWidth = css.width;
+                        css.position = 'relative';
+                        css.width = 'auto';
+                    }
+
+                    this.$dropdown.css(css);
+                };
+
+                AttachBody.prototype._showDropdown = function (decorated) {
+                    this.$dropdownContainer.appendTo(this.$dropdownParent);
+
+                    this._positionDropdown();
+                    this._resizeDropdown();
+                };
+
+                return AttachBody;
+            });
+
+            S2.define('select2/dropdown/minimumResultsForSearch', [], function () {
+                function countResults(data) {
+                    var count = 0;
+
+                    for (var d = 0; d < data.length; d++) {
+                        var item = data[d];
+
+                        if (item.children) {
+                            count += countResults(item.children);
+                        } else {
+                            count++;
+                        }
+                    }
+
+                    return count;
+                }
+
+                function MinimumResultsForSearch(decorated, $element, options, dataAdapter) {
+                    this.minimumResultsForSearch = options.get('minimumResultsForSearch');
+
+                    if (this.minimumResultsForSearch < 0) {
+                        this.minimumResultsForSearch = Infinity;
+                    }
+
+                    decorated.call(this, $element, options, dataAdapter);
+                }
+
+                MinimumResultsForSearch.prototype.showSearch = function (decorated, params) {
+                    if (countResults(params.data.results) < this.minimumResultsForSearch) {
+                        return false;
+                    }
+
+                    return decorated.call(this, params);
+                };
+
+                return MinimumResultsForSearch;
+            });
+
+            S2.define('select2/dropdown/selectOnClose', [], function () {
+                function SelectOnClose() {
+                }
+
+                SelectOnClose.prototype.bind = function (decorated, container, $container) {
+                    var self = this;
+
+                    decorated.call(this, container, $container);
+
+                    container.on('close', function (params) {
+                        self._handleSelectOnClose(params);
+                    });
+                };
+
+                SelectOnClose.prototype._handleSelectOnClose = function (_, params) {
+                    if (params && params.originalSelect2Event != null) {
+                        var event = params.originalSelect2Event;
+
+                        // Don't select an item if the close event was triggered from a select or
+                        // unselect event
+                        if (event._type === 'select' || event._type === 'unselect') {
+                            return;
+                        }
+                    }
+
+                    var $highlightedResults = this.getHighlightedResults();
+
+                    // Only select highlighted results
+                    if ($highlightedResults.length < 1) {
+                        return;
+                    }
+
+                    var data = $highlightedResults.data('data');
+
+                    // Don't re-select already selected resulte
+                    if (
+                        (data.element != null && data.element.selected) ||
+                        (data.element == null && data.selected)
+                    ) {
+                        return;
+                    }
+
+                    this.trigger('select', {
+                        data: data
+                    });
+                };
+
+                return SelectOnClose;
+            });
+
+            S2.define('select2/dropdown/closeOnSelect', [], function () {
+                function CloseOnSelect() {
+                }
+
+                CloseOnSelect.prototype.bind = function (decorated, container, $container) {
+                    var self = this;
+
+                    decorated.call(this, container, $container);
+
+                    container.on('select', function (evt) {
+                        self._selectTriggered(evt);
+                    });
+
+                    container.on('unselect', function (evt) {
+                        self._selectTriggered(evt);
+                    });
+                };
+
+                CloseOnSelect.prototype._selectTriggered = function (_, evt) {
+                    var originalEvent = evt.originalEvent;
+
+                    // Don't close if the control key is being held
+                    if (originalEvent && originalEvent.ctrlKey) {
+                        return;
+                    }
+
+                    this.trigger('close', {
+                        originalEvent: originalEvent,
+                        originalSelect2Event: evt
+                    });
+                };
+
+                return CloseOnSelect;
+            });
+
+            S2.define('select2/i18n/en', [], function () {
+                // English
+                return {
+                    errorLoading: function () {
+                        return 'The results could not be loaded.';
+                    },
+                    inputTooLong: function (args) {
+                        var overChars = args.input.length - args.maximum;
+
+                        var message = 'Please delete ' + overChars + ' character';
+
+                        if (overChars != 1) {
+                            message += 's';
+                        }
+
+                        return message;
+                    },
+                    inputTooShort: function (args) {
+                        var remainingChars = args.minimum - args.input.length;
+
+                        var message = 'Please enter ' + remainingChars + ' or more characters';
+
+                        return message;
+                    },
+                    loadingMore: function () {
+                        return 'Loading more results…';
+                    },
+                    maximumSelected: function (args) {
+                        var message = 'You can only select ' + args.maximum + ' item';
+
+                        if (args.maximum != 1) {
+                            message += 's';
+                        }
+
+                        return message;
+                    },
+                    noResults: function () {
+                        return 'No results found';
+                    },
+                    searching: function () {
+                        return 'Searching…';
+                    }
+                };
+            });
+
+            S2.define('select2/defaults', [
+                'jquery',
+                'require',
+
+                './results',
+
+                './selection/single',
+                './selection/multiple',
+                './selection/placeholder',
+                './selection/allowClear',
+                './selection/search',
+                './selection/eventRelay',
+
+                './utils',
+                './translation',
+                './diacritics',
+
+                './data/select',
+                './data/array',
+                './data/ajax',
+                './data/tags',
+                './data/tokenizer',
+                './data/minimumInputLength',
+                './data/maximumInputLength',
+                './data/maximumSelectionLength',
+
+                './dropdown',
+                './dropdown/search',
+                './dropdown/hidePlaceholder',
+                './dropdown/infiniteScroll',
+                './dropdown/attachBody',
+                './dropdown/minimumResultsForSearch',
+                './dropdown/selectOnClose',
+                './dropdown/closeOnSelect',
+
+                './i18n/en'
+            ], function ($, require,
+                         ResultsList,
+                         SingleSelection, MultipleSelection, Placeholder, AllowClear,
+                         SelectionSearch, EventRelay,
+                         Utils, Translation, DIACRITICS,
+                         SelectData, ArrayData, AjaxData, Tags, Tokenizer,
+                         MinimumInputLength, MaximumInputLength, MaximumSelectionLength,
+                         Dropdown, DropdownSearch, HidePlaceholder, InfiniteScroll,
+                         AttachBody, MinimumResultsForSearch, SelectOnClose, CloseOnSelect,
+                         EnglishTranslation) {
+                function Defaults() {
+                    this.reset();
+                }
+
+                Defaults.prototype.apply = function (options) {
+                    options = $.extend(true, {}, this.defaults, options);
+
+                    if (options.dataAdapter == null) {
+                        if (options.ajax != null) {
+                            options.dataAdapter = AjaxData;
+                        } else if (options.data != null) {
+                            options.dataAdapter = ArrayData;
+                        } else {
+                            options.dataAdapter = SelectData;
+                        }
+
+                        if (options.minimumInputLength > 0) {
+                            options.dataAdapter = Utils.Decorate(
+                                options.dataAdapter,
+                                MinimumInputLength
+                            );
+                        }
+
+                        if (options.maximumInputLength > 0) {
+                            options.dataAdapter = Utils.Decorate(
+                                options.dataAdapter,
+                                MaximumInputLength
+                            );
+                        }
+
+                        if (options.maximumSelectionLength > 0) {
+                            options.dataAdapter = Utils.Decorate(
+                                options.dataAdapter,
+                                MaximumSelectionLength
+                            );
+                        }
+
+                        if (options.tags) {
+                            options.dataAdapter = Utils.Decorate(options.dataAdapter, Tags);
+                        }
+
+                        if (options.tokenSeparators != null || options.tokenizer != null) {
+                            options.dataAdapter = Utils.Decorate(
+                                options.dataAdapter,
+                                Tokenizer
+                            );
+                        }
+
+                        if (options.query != null) {
+                            var Query = require(options.amdBase + 'compat/query');
+
+                            options.dataAdapter = Utils.Decorate(
+                                options.dataAdapter,
+                                Query
+                            );
+                        }
+
+                        if (options.initSelection != null) {
+                            var InitSelection = require(options.amdBase + 'compat/initSelection');
+
+                            options.dataAdapter = Utils.Decorate(
+                                options.dataAdapter,
+                                InitSelection
+                            );
+                        }
+                    }
+
+                    if (options.resultsAdapter == null) {
+                        options.resultsAdapter = ResultsList;
+
+                        if (options.ajax != null) {
+                            options.resultsAdapter = Utils.Decorate(
+                                options.resultsAdapter,
+                                InfiniteScroll
+                            );
+                        }
+
+                        if (options.placeholder != null) {
+                            options.resultsAdapter = Utils.Decorate(
+                                options.resultsAdapter,
+                                HidePlaceholder
+                            );
+                        }
+
+                        if (options.selectOnClose) {
+                            options.resultsAdapter = Utils.Decorate(
+                                options.resultsAdapter,
+                                SelectOnClose
+                            );
+                        }
+                    }
+
+                    if (options.dropdownAdapter == null) {
+                        if (options.multiple) {
+                            options.dropdownAdapter = Dropdown;
+                        } else {
+                            var SearchableDropdown = Utils.Decorate(Dropdown, DropdownSearch);
+
+                            options.dropdownAdapter = SearchableDropdown;
+                        }
+
+                        if (options.minimumResultsForSearch !== 0) {
+                            options.dropdownAdapter = Utils.Decorate(
+                                options.dropdownAdapter,
+                                MinimumResultsForSearch
+                            );
+                        }
+
+                        if (options.closeOnSelect) {
+                            options.dropdownAdapter = Utils.Decorate(
+                                options.dropdownAdapter,
+                                CloseOnSelect
+                            );
+                        }
+
+                        if (
+                            options.dropdownCssClass != null ||
+                            options.dropdownCss != null ||
+                            options.adaptDropdownCssClass != null
+                        ) {
+                            var DropdownCSS = require(options.amdBase + 'compat/dropdownCss');
+
+                            options.dropdownAdapter = Utils.Decorate(
+                                options.dropdownAdapter,
+                                DropdownCSS
+                            );
+                        }
+
+                        options.dropdownAdapter = Utils.Decorate(
+                            options.dropdownAdapter,
+                            AttachBody
+                        );
+                    }
+
+                    if (options.selectionAdapter == null) {
+                        if (options.multiple) {
+                            options.selectionAdapter = MultipleSelection;
+                        } else {
+                            options.selectionAdapter = SingleSelection;
+                        }
+
+                        // Add the placeholder mixin if a placeholder was specified
+                        if (options.placeholder != null) {
+                            options.selectionAdapter = Utils.Decorate(
+                                options.selectionAdapter,
+                                Placeholder
+                            );
+                        }
+
+                        if (options.allowClear) {
+                            options.selectionAdapter = Utils.Decorate(
+                                options.selectionAdapter,
+                                AllowClear
+                            );
+                        }
+
+                        if (options.multiple) {
+                            options.selectionAdapter = Utils.Decorate(
+                                options.selectionAdapter,
+                                SelectionSearch
+                            );
+                        }
+
+                        if (
+                            options.containerCssClass != null ||
+                            options.containerCss != null ||
+                            options.adaptContainerCssClass != null
+                        ) {
+                            var ContainerCSS = require(options.amdBase + 'compat/containerCss');
+
+                            options.selectionAdapter = Utils.Decorate(
+                                options.selectionAdapter,
+                                ContainerCSS
+                            );
+                        }
+
+                        options.selectionAdapter = Utils.Decorate(
+                            options.selectionAdapter,
+                            EventRelay
+                        );
+                    }
+
+                    if (typeof options.language === 'string') {
+                        // Check if the language is specified with a region
+                        if (options.language.indexOf('-') > 0) {
+                            // Extract the region information if it is included
+                            var languageParts = options.language.split('-');
+                            var baseLanguage = languageParts[0];
+
+                            options.language = [options.language, baseLanguage];
+                        } else {
+                            options.language = [options.language];
+                        }
+                    }
+
+                    if ($.isArray(options.language)) {
+                        var languages = new Translation();
+                        options.language.push('en');
+
+                        var languageNames = options.language;
+
+                        for (var l = 0; l < languageNames.length; l++) {
+                            var name = languageNames[l];
+                            var language = {};
+
+                            try {
+                                // Try to load it with the original name
+                                language = Translation.loadPath(name);
+                            } catch (e) {
+                                try {
+                                    // If we couldn't load it, check if it wasn't the full path
+                                    name = this.defaults.amdLanguageBase + name;
+                                    language = Translation.loadPath(name);
+                                } catch (ex) {
+                                    // The translation could not be loaded at all. Sometimes this is
+                                    // because of a configuration problem, other times this can be
+                                    // because of how Select2 helps load all possible translation files.
+                                    if (options.debug && window.console && console.warn) {
+                                        console.warn(
+                                            'Select2: The language file for "' + name + '" could not be ' +
+                                            'automatically loaded. A fallback will be used instead.'
+                                        );
+                                    }
+
+                                    continue;
+                                }
+                            }
+
+                            languages.extend(language);
+                        }
+
+                        options.translations = languages;
+                    } else {
+                        var baseTranslation = Translation.loadPath(
+                            this.defaults.amdLanguageBase + 'en'
+                        );
+                        var customTranslation = new Translation(options.language);
+
+                        customTranslation.extend(baseTranslation);
+
+                        options.translations = customTranslation;
+                    }
+
+                    return options;
+                };
+
+                Defaults.prototype.reset = function () {
+                    function stripDiacritics(text) {
+                        // Used 'uni range + named function' from http://jsperf.com/diacritics/18
+                        function match(a) {
+                            return DIACRITICS[a] || a;
+                        }
+
+                        return text.replace(/[^\u0000-\u007E]/g, match);
+                    }
+
+                    function matcher(params, data) {
+                        // Always return the object if there is nothing to compare
+                        if ($.trim(params.term) === '') {
+                            return data;
+                        }
+
+                        // Do a recursive check for options with children
+                        if (data.children && data.children.length > 0) {
+                            // Clone the data object if there are children
+                            // This is required as we modify the object to remove any non-matches
+                            var match = $.extend(true, {}, data);
+
+                            // Check each child of the option
+                            for (var c = data.children.length - 1; c >= 0; c--) {
+                                var child = data.children[c];
+
+                                var matches = matcher(params, child);
+
+                                // If there wasn't a match, remove the object in the array
+                                if (matches == null) {
+                                    match.children.splice(c, 1);
+                                }
+                            }
+
+                            // If any children matched, return the new object
+                            if (match.children.length > 0) {
+                                return match;
+                            }
+
+                            // If there were no matching children, check just the plain object
+                            return matcher(params, match);
+                        }
+
+                        var original = stripDiacritics(data.text).toUpperCase();
+                        var term = stripDiacritics(params.term).toUpperCase();
+
+                        // Check if the text contains the term
+                        if (original.indexOf(term) > -1) {
+                            return data;
+                        }
+
+                        // If it doesn't contain the term, don't return anything
+                        return null;
+                    }
+
+                    this.defaults = {
+                        amdBase: './',
+                        amdLanguageBase: './i18n/',
+                        closeOnSelect: true,
+                        debug: false,
+                        dropdownAutoWidth: false,
+                        escapeMarkup: Utils.escapeMarkup,
+                        language: EnglishTranslation,
+                        matcher: matcher,
+                        minimumInputLength: 0,
+                        maximumInputLength: 0,
+                        maximumSelectionLength: 0,
+                        minimumResultsForSearch: 0,
+                        selectOnClose: false,
+                        sorter: function (data) {
+                            return data;
+                        },
+                        templateResult: function (result) {
+                            return result.text;
+                        },
+                        templateSelection: function (selection) {
+                            return selection.text;
+                        },
+                        theme: 'default',
+                        width: 'resolve'
+                    };
+                };
+
+                Defaults.prototype.set = function (key, value) {
+                    var camelKey = $.camelCase(key);
+
+                    var data = {};
+                    data[camelKey] = value;
+
+                    var convertedData = Utils._convertData(data);
+
+                    $.extend(this.defaults, convertedData);
+                };
+
+                var defaults = new Defaults();
+
+                return defaults;
+            });
+
+            S2.define('select2/options', [
+                'require',
+                'jquery',
+                './defaults',
+                './utils'
+            ], function (require, $, Defaults, Utils) {
+                function Options(options, $element) {
+                    this.options = options;
+
+                    if ($element != null) {
+                        this.fromElement($element);
+                    }
+
+                    this.options = Defaults.apply(this.options);
+
+                    if ($element && $element.is('input')) {
+                        var InputCompat = require(this.get('amdBase') + 'compat/inputData');
+
+                        this.options.dataAdapter = Utils.Decorate(
+                            this.options.dataAdapter,
+                            InputCompat
+                        );
+                    }
+                }
+
+                Options.prototype.fromElement = function ($e) {
+                    var excludedData = ['select2'];
+
+                    if (this.options.multiple == null) {
+                        this.options.multiple = $e.prop('multiple');
+                    }
+
+                    if (this.options.disabled == null) {
+                        this.options.disabled = $e.prop('disabled');
+                    }
+
+                    if (this.options.language == null) {
+                        if ($e.prop('lang')) {
+                            this.options.language = $e.prop('lang').toLowerCase();
+                        } else if ($e.closest('[lang]').prop('lang')) {
+                            this.options.language = $e.closest('[lang]').prop('lang');
+                        }
+                    }
+
+                    if (this.options.dir == null) {
+                        if ($e.prop('dir')) {
+                            this.options.dir = $e.prop('dir');
+                        } else if ($e.closest('[dir]').prop('dir')) {
+                            this.options.dir = $e.closest('[dir]').prop('dir');
+                        } else {
+                            this.options.dir = 'ltr';
+                        }
+                    }
+
+                    $e.prop('disabled', this.options.disabled);
+                    $e.prop('multiple', this.options.multiple);
+
+                    if ($e.data('select2Tags')) {
+                        if (this.options.debug && window.console && console.warn) {
+                            console.warn(
+                                'Select2: The `data-select2-tags` attribute has been changed to ' +
+                                'use the `data-data` and `data-tags="true"` attributes and will be ' +
+                                'removed in future versions of Select2.'
+                            );
+                        }
+
+                        $e.data('data', $e.data('select2Tags'));
+                        $e.data('tags', true);
+                    }
+
+                    if ($e.data('ajaxUrl')) {
+                        if (this.options.debug && window.console && console.warn) {
+                            console.warn(
+                                'Select2: The `data-ajax-url` attribute has been changed to ' +
+                                '`data-ajax--url` and support for the old attribute will be removed' +
+                                ' in future versions of Select2.'
+                            );
+                        }
+
+                        $e.attr('ajax--url', $e.data('ajaxUrl'));
+                        $e.data('ajax--url', $e.data('ajaxUrl'));
+                    }
+
+                    var dataset = {};
+
+                    // Prefer the element's `dataset` attribute if it exists
+                    // jQuery 1.x does not correctly handle data attributes with multiple dashes
+                    if ($.fn.jquery && $.fn.jquery.substr(0, 2) == '1.' && $e[0].dataset) {
+                        dataset = $.extend(true, {}, $e[0].dataset, $e.data());
+                    } else {
+                        dataset = $e.data();
+                    }
+
+                    var data = $.extend(true, {}, dataset);
+
+                    data = Utils._convertData(data);
+
+                    for (var key in data) {
+                        if ($.inArray(key, excludedData) > -1) {
+                            continue;
+                        }
+
+                        if ($.isPlainObject(this.options[key])) {
+                            $.extend(this.options[key], data[key]);
+                        } else {
+                            this.options[key] = data[key];
+                        }
+                    }
+
+                    return this;
+                };
+
+                Options.prototype.get = function (key) {
+                    return this.options[key];
+                };
+
+                Options.prototype.set = function (key, val) {
+                    this.options[key] = val;
+                };
+
+                return Options;
+            });
+
+            S2.define('select2/core', [
+                'jquery',
+                './options',
+                './utils',
+                './keys'
+            ], function ($, Options, Utils, KEYS) {
+                var Select2 = function ($element, options) {
+                    if ($element.data('select2') != null) {
+                        $element.data('select2').destroy();
+                    }
+
+                    this.$element = $element;
+
+                    this.id = this._generateId($element);
+
+                    options = options || {};
+
+                    this.options = new Options(options, $element);
+
+                    Select2.__super__.constructor.call(this);
+
+                    // Set up the tabindex
+
+                    var tabindex = $element.attr('tabindex') || 0;
+                    $element.data('old-tabindex', tabindex);
+                    $element.attr('tabindex', '-1');
+
+                    // Set up containers and adapters
+
+                    var DataAdapter = this.options.get('dataAdapter');
+                    this.dataAdapter = new DataAdapter($element, this.options);
+
+                    var $container = this.render();
+
+                    this._placeContainer($container);
+
+                    var SelectionAdapter = this.options.get('selectionAdapter');
+                    this.selection = new SelectionAdapter($element, this.options);
+                    this.$selection = this.selection.render();
+
+                    this.selection.position(this.$selection, $container);
+
+                    var DropdownAdapter = this.options.get('dropdownAdapter');
+                    this.dropdown = new DropdownAdapter($element, this.options);
+                    this.$dropdown = this.dropdown.render();
+
+                    this.dropdown.position(this.$dropdown, $container);
+
+                    var ResultsAdapter = this.options.get('resultsAdapter');
+                    this.results = new ResultsAdapter($element, this.options, this.dataAdapter);
+                    this.$results = this.results.render();
+
+                    this.results.position(this.$results, this.$dropdown);
+
+                    // Bind events
+
+                    var self = this;
+
+                    // Bind the container to all of the adapters
+                    this._bindAdapters();
+
+                    // Register any DOM event handlers
+                    this._registerDomEvents();
+
+                    // Register any internal event handlers
+                    this._registerDataEvents();
+                    this._registerSelectionEvents();
+                    this._registerDropdownEvents();
+                    this._registerResultsEvents();
+                    this._registerEvents();
+
+                    // Set the initial state
+                    this.dataAdapter.current(function (initialData) {
+                        self.trigger('selection:update', {
+                            data: initialData
+                        });
+                    });
+
+                    // Hide the original select
+                    $element.addClass('select2-hidden-accessible');
+                    $element.attr('aria-hidden', 'true');
+
+                    // Synchronize any monitored attributes
+                    this._syncAttributes();
+
+                    $element.data('select2', this);
+                };
+
+                Utils.Extend(Select2, Utils.Observable);
+
+                Select2.prototype._generateId = function ($element) {
+                    var id = '';
+
+                    if ($element.attr('id') != null) {
+                        id = $element.attr('id');
+                    } else if ($element.attr('name') != null) {
+                        id = $element.attr('name') + '-' + Utils.generateChars(2);
+                    } else {
+                        id = Utils.generateChars(4);
+                    }
+
+                    id = id.replace(/(:|\.|\[|\]|,)/g, '');
+                    id = 'select2-' + id;
+
+                    return id;
+                };
+
+                Select2.prototype._placeContainer = function ($container) {
+                    $container.insertAfter(this.$element);
+
+                    var width = this._resolveWidth(this.$element, this.options.get('width'));
+
+                    if (width != null) {
+                        $container.css('width', width);
+                    }
+                };
+
+                Select2.prototype._resolveWidth = function ($element, method) {
+                    var WIDTH = /^width:(([-+]?([0-9]*\.)?[0-9]+)(px|em|ex|%|in|cm|mm|pt|pc))/i;
+
+                    if (method == 'resolve') {
+                        var styleWidth = this._resolveWidth($element, 'style');
+
+                        if (styleWidth != null) {
+                            return styleWidth;
+                        }
+
+                        return this._resolveWidth($element, 'element');
+                    }
+
+                    if (method == 'element') {
+                        var elementWidth = $element.outerWidth(false);
+
+                        if (elementWidth <= 0) {
+                            return 'auto';
+                        }
+
+                        return elementWidth + 'px';
+                    }
+
+                    if (method == 'style') {
+                        var style = $element.attr('style');
+
+                        if (typeof(style) !== 'string') {
+                            return null;
+                        }
+
+                        var attrs = style.split(';');
+
+                        for (var i = 0, l = attrs.length; i < l; i = i + 1) {
+                            var attr = attrs[i].replace(/\s/g, '');
+                            var matches = attr.match(WIDTH);
+
+                            if (matches !== null && matches.length >= 1) {
+                                return matches[1];
+                            }
+                        }
+
+                        return null;
+                    }
+
+                    return method;
+                };
+
+                Select2.prototype._bindAdapters = function () {
+                    this.dataAdapter.bind(this, this.$container);
+                    this.selection.bind(this, this.$container);
+
+                    this.dropdown.bind(this, this.$container);
+                    this.results.bind(this, this.$container);
+                };
+
+                Select2.prototype._registerDomEvents = function () {
+                    var self = this;
+
+                    this.$element.on('change.select2', function () {
+                        self.dataAdapter.current(function (data) {
+                            self.trigger('selection:update', {
+                                data: data
+                            });
+                        });
+                    });
+
+                    this.$element.on('focus.select2', function (evt) {
+                        self.trigger('focus', evt);
+                    });
+
+                    this._syncA = Utils.bind(this._syncAttributes, this);
+                    this._syncS = Utils.bind(this._syncSubtree, this);
+
+                    if (this.$element[0].attachEvent) {
+                        this.$element[0].attachEvent('onpropertychange', this._syncA);
+                    }
+
+                    var observer = window.MutationObserver ||
+                        window.WebKitMutationObserver ||
+                        window.MozMutationObserver
+                    ;
+
+                    if (observer != null) {
+                        this._observer = new observer(function (mutations) {
+                            $.each(mutations, self._syncA);
+                            $.each(mutations, self._syncS);
+                        });
+                        this._observer.observe(this.$element[0], {
+                            attributes: true,
+                            childList: true,
+                            subtree: false
+                        });
+                    } else if (this.$element[0].addEventListener) {
+                        this.$element[0].addEventListener(
+                            'DOMAttrModified',
+                            self._syncA,
+                            false
+                        );
+                        this.$element[0].addEventListener(
+                            'DOMNodeInserted',
+                            self._syncS,
+                            false
+                        );
+                        this.$element[0].addEventListener(
+                            'DOMNodeRemoved',
+                            self._syncS,
+                            false
+                        );
+                    }
+                };
+
+                Select2.prototype._registerDataEvents = function () {
+                    var self = this;
+
+                    this.dataAdapter.on('*', function (name, params) {
+                        self.trigger(name, params);
+                    });
+                };
+
+                Select2.prototype._registerSelectionEvents = function () {
+                    var self = this;
+                    var nonRelayEvents = ['toggle', 'focus'];
+
+                    this.selection.on('toggle', function () {
+                        self.toggleDropdown();
+                    });
+
+                    this.selection.on('focus', function (params) {
+                        self.focus(params);
+                    });
+
+                    this.selection.on('*', function (name, params) {
+                        if ($.inArray(name, nonRelayEvents) !== -1) {
+                            return;
+                        }
+
+                        self.trigger(name, params);
+                    });
+                };
+
+                Select2.prototype._registerDropdownEvents = function () {
+                    var self = this;
+
+                    this.dropdown.on('*', function (name, params) {
+                        self.trigger(name, params);
+                    });
+                };
+
+                Select2.prototype._registerResultsEvents = function () {
+                    var self = this;
+
+                    this.results.on('*', function (name, params) {
+                        self.trigger(name, params);
+                    });
+                };
+
+                Select2.prototype._registerEvents = function () {
+                    var self = this;
+
+                    this.on('open', function () {
+                        self.$container.addClass('select2-container--open');
+                    });
+
+                    this.on('close', function () {
+                        self.$container.removeClass('select2-container--open');
+                    });
+
+                    this.on('enable', function () {
+                        self.$container.removeClass('select2-container--disabled');
+                    });
+
+                    this.on('disable', function () {
+                        self.$container.addClass('select2-container--disabled');
+                    });
+
+                    this.on('blur', function () {
+                        self.$container.removeClass('select2-container--focus');
+                    });
+
+                    this.on('query', function (params) {
+                        if (!self.isOpen()) {
+                            self.trigger('open', {});
+                        }
+
+                        this.dataAdapter.query(params, function (data) {
+                            self.trigger('results:all', {
+                                data: data,
+                                query: params
+                            });
+                        });
+                    });
+
+                    this.on('query:append', function (params) {
+                        this.dataAdapter.query(params, function (data) {
+                            self.trigger('results:append', {
+                                data: data,
+                                query: params
+                            });
+                        });
+                    });
+
+                    this.on('keypress', function (evt) {
+                        var key = evt.which;
+
+                        if (self.isOpen()) {
+                            if (key === KEYS.ESC || key === KEYS.TAB ||
+                                (key === KEYS.UP && evt.altKey)) {
+                                self.close();
+
+                                evt.preventDefault();
+                            } else if (key === KEYS.ENTER) {
+                                self.trigger('results:select', {});
+
+                                evt.preventDefault();
+                            } else if ((key === KEYS.SPACE && evt.ctrlKey)) {
+                                self.trigger('results:toggle', {});
+
+                                evt.preventDefault();
+                            } else if (key === KEYS.UP) {
+                                self.trigger('results:previous', {});
+
+                                evt.preventDefault();
+                            } else if (key === KEYS.DOWN) {
+                                self.trigger('results:next', {});
+
+                                evt.preventDefault();
+                            }
+                        } else {
+                            if (key === KEYS.ENTER || key === KEYS.SPACE ||
+                                (key === KEYS.DOWN && evt.altKey)) {
+                                self.open();
+
+                                evt.preventDefault();
+                            }
+                        }
+                    });
+                };
+
+                Select2.prototype._syncAttributes = function () {
+                    this.options.set('disabled', this.$element.prop('disabled'));
+
+                    if (this.options.get('disabled')) {
+                        if (this.isOpen()) {
+                            this.close();
+                        }
+
+                        this.trigger('disable', {});
+                    } else {
+                        this.trigger('enable', {});
+                    }
+                };
+
+                Select2.prototype._syncSubtree = function (evt, mutations) {
+                    var changed = false;
+                    var self = this;
+
+                    // Ignore any mutation events raised for elements that aren't options or
+                    // optgroups. This handles the case when the select element is destroyed
+                    if (
+                        evt && evt.target && (
+                            evt.target.nodeName !== 'OPTION' && evt.target.nodeName !== 'OPTGROUP'
+                        )
+                    ) {
+                        return;
+                    }
+
+                    if (!mutations) {
+                        // If mutation events aren't supported, then we can only assume that the
+                        // change affected the selections
+                        changed = true;
+                    } else if (mutations.addedNodes && mutations.addedNodes.length > 0) {
+                        for (var n = 0; n < mutations.addedNodes.length; n++) {
+                            var node = mutations.addedNodes[n];
+
+                            if (node.selected) {
+                                changed = true;
+                            }
+                        }
+                    } else if (mutations.removedNodes && mutations.removedNodes.length > 0) {
+                        changed = true;
+                    }
+
+                    // Only re-pull the data if we think there is a change
+                    if (changed) {
+                        this.dataAdapter.current(function (currentData) {
+                            self.trigger('selection:update', {
+                                data: currentData
+                            });
+                        });
+                    }
+                };
+
+                /**
+                 * Override the trigger method to automatically trigger pre-events when
+                 * there are events that can be prevented.
+                 */
+                Select2.prototype.trigger = function (name, args) {
+                    var actualTrigger = Select2.__super__.trigger;
+                    var preTriggerMap = {
+                        'open': 'opening',
+                        'close': 'closing',
+                        'select': 'selecting',
+                        'unselect': 'unselecting'
+                    };
+
+                    if (args === undefined) {
+                        args = {};
+                    }
+
+                    if (name in preTriggerMap) {
+                        var preTriggerName = preTriggerMap[name];
+                        var preTriggerArgs = {
+                            prevented: false,
+                            name: name,
+                            args: args
+                        };
+
+                        actualTrigger.call(this, preTriggerName, preTriggerArgs);
+
+                        if (preTriggerArgs.prevented) {
+                            args.prevented = true;
+
+                            return;
+                        }
+                    }
+
+                    actualTrigger.call(this, name, args);
+                };
+
+                Select2.prototype.toggleDropdown = function () {
+                    if (this.options.get('disabled')) {
+                        return;
+                    }
+
+                    if (this.isOpen()) {
+                        this.close();
+                    } else {
+                        this.open();
+                    }
+                };
+
+                Select2.prototype.open = function () {
+                    if (this.isOpen()) {
+                        return;
+                    }
+
+                    this.trigger('query', {});
+                };
+
+                Select2.prototype.close = function () {
+                    if (!this.isOpen()) {
+                        return;
+                    }
+
+                    this.trigger('close', {});
+                };
+
+                Select2.prototype.isOpen = function () {
+                    return this.$container.hasClass('select2-container--open');
+                };
+
+                Select2.prototype.hasFocus = function () {
+                    return this.$container.hasClass('select2-container--focus');
+                };
+
+                Select2.prototype.focus = function (data) {
+                    // No need to re-trigger focus events if we are already focused
+                    if (this.hasFocus()) {
+                        return;
+                    }
+
+                    this.$container.addClass('select2-container--focus');
+                    this.trigger('focus', {});
+                };
+
+                Select2.prototype.enable = function (args) {
+                    if (this.options.get('debug') && window.console && console.warn) {
+                        console.warn(
+                            'Select2: The `select2("enable")` method has been deprecated and will' +
+                            ' be removed in later Select2 versions. Use $element.prop("disabled")' +
+                            ' instead.'
+                        );
+                    }
+
+                    if (args == null || args.length === 0) {
+                        args = [true];
+                    }
+
+                    var disabled = !args[0];
+
+                    this.$element.prop('disabled', disabled);
+                };
+
+                Select2.prototype.data = function () {
+                    if (this.options.get('debug') &&
+                        arguments.length > 0 && window.console && console.warn) {
+                        console.warn(
+                            'Select2: Data can no longer be set using `select2("data")`. You ' +
+                            'should consider setting the value instead using `$element.val()`.'
+                        );
+                    }
+
+                    var data = [];
+
+                    this.dataAdapter.current(function (currentData) {
+                        data = currentData;
+                    });
+
+                    return data;
+                };
+
+                Select2.prototype.val = function (args) {
+                    if (this.options.get('debug') && window.console && console.warn) {
+                        console.warn(
+                            'Select2: The `select2("val")` method has been deprecated and will be' +
+                            ' removed in later Select2 versions. Use $element.val() instead.'
+                        );
+                    }
+
+                    if (args == null || args.length === 0) {
+                        return this.$element.val();
+                    }
+
+                    var newVal = args[0];
+
+                    if ($.isArray(newVal)) {
+                        newVal = $.map(newVal, function (obj) {
+                            return obj.toString();
+                        });
+                    }
+
+                    this.$element.val(newVal).trigger('change');
+                };
+
+                Select2.prototype.destroy = function () {
+                    this.$container.remove();
+
+                    if (this.$element[0].detachEvent) {
+                        this.$element[0].detachEvent('onpropertychange', this._syncA);
+                    }
+
+                    if (this._observer != null) {
+                        this._observer.disconnect();
+                        this._observer = null;
+                    } else if (this.$element[0].removeEventListener) {
+                        this.$element[0]
+                            .removeEventListener('DOMAttrModified', this._syncA, false);
+                        this.$element[0]
+                            .removeEventListener('DOMNodeInserted', this._syncS, false);
+                        this.$element[0]
+                            .removeEventListener('DOMNodeRemoved', this._syncS, false);
+                    }
+
+                    this._syncA = null;
+                    this._syncS = null;
+
+                    this.$element.off('.select2');
+                    this.$element.attr('tabindex', this.$element.data('old-tabindex'));
+
+                    this.$element.removeClass('select2-hidden-accessible');
+                    this.$element.attr('aria-hidden', 'false');
+                    this.$element.removeData('select2');
+
+                    this.dataAdapter.destroy();
+                    this.selection.destroy();
+                    this.dropdown.destroy();
+                    this.results.destroy();
+
+                    this.dataAdapter = null;
+                    this.selection = null;
+                    this.dropdown = null;
+                    this.results = null;
+                };
+
+                Select2.prototype.render = function () {
+                    var $container = $(
+                        '<span class="select2 select2-container">' +
+                        '<span class="selection"></span>' +
+                        '<span class="dropdown-wrapper" aria-hidden="true"></span>' +
+                        '</span>'
+                    );
+
+                    $container.attr('dir', this.options.get('dir'));
+
+                    this.$container = $container;
+
+                    this.$container.addClass('select2-container--' + this.options.get('theme'));
+
+                    $container.data('element', this.$element);
+
+                    return $container;
+                };
+
+                return Select2;
+            });
+
+            S2.define('jquery-mousewheel', [
+                'jquery'
+            ], function ($) {
+                // Used to shim jQuery.mousewheel for non-full builds.
+                return $;
+            });
+
+            S2.define('jquery.select2', [
+                'jquery',
+                'jquery-mousewheel',
+
+                './select2/core',
+                './select2/defaults'
+            ], function ($, _, Select2, Defaults) {
+                if ($.fn.select2 == null) {
+                    // All methods that should return the element
+                    var thisMethods = ['open', 'close', 'destroy'];
+
+                    $.fn.select2 = function (options) {
+                        options = options || {};
+
+                        if (typeof options === 'object') {
+                            this.each(function () {
+                                var instanceOptions = $.extend(true, {}, options);
+
+                                var instance = new Select2($(this), instanceOptions);
+                            });
+
+                            return this;
+                        } else if (typeof options === 'string') {
+                            var ret;
+                            var args = Array.prototype.slice.call(arguments, 1);
+
+                            this.each(function () {
+                                var instance = $(this).data('select2');
+
+                                if (instance == null && window.console && console.error) {
+                                    console.error(
+                                        'The select2(\'' + options + '\') method was called on an ' +
+                                        'element that is not using Select2.'
+                                    );
+                                }
+
+                                ret = instance[options].apply(instance, args);
+                            });
+
+                            // Check if we should be returning `this`
+                            if ($.inArray(options, thisMethods) > -1) {
+                                return this;
+                            }
+
+                            return ret;
+                        } else {
+                            throw new Error('Invalid arguments for Select2: ' + options);
+                        }
+                    };
+                }
+
+                if ($.fn.select2.defaults == null) {
+                    $.fn.select2.defaults = Defaults;
+                }
+
+                return Select2;
+            });
+
+            // Return the AMD loader configuration so it can be used outside of this file
+            return {
+                define: S2.define,
+                require: S2.require
+            };
+        }());
+
+    // Autoload the jQuery bindings
+    // We know that all of the modules exist above this, so we're safe
+    var select2 = S2.require('jquery.select2');
+
+    // Hold the AMD module references on the jQuery function that was just loaded
+    // This allows Select2 to use the internal loader outside of this file, such
+    // as in the language files.
+    jQuery.fn.select2.amd = S2;
+
+    // Return the Select2 instance for anyone who is importing it.
+    return select2;
+}));

File diff suppressed because it is too large
+ 0 - 0
src/assets/select2/js/select2.min.js


+ 211 - 0
src/components/ActiveRecord.php

@@ -0,0 +1,211 @@
+<?php
+
+namespace yiins\components;
+
+/**
+ * Class ActiveRecord
+ * @package base\components
+ */
+class ActiveRecord extends \CActiveRecord
+{
+    use NSPost;
+
+    /**
+     * @param string $className
+     * @return $this
+     */
+    public static function model($className = '')
+    {
+        if (!$className) {
+            $className = static::class;
+        }
+        return parent::model($className);
+    }
+
+    /**
+     * @param string $condition
+     * @param array $params
+     * @return $this[]
+     */
+    public function findAll($condition = '', $params = array())
+    {
+        return parent::findAll($condition, $params);
+    }
+
+    /**
+     * @param string $condition
+     * @param array $params
+     * @return $this
+     */
+    public function find($condition = '', $params = array())
+    {
+        return parent::find($condition, $params);
+    }
+
+    /**
+     * @param mixed $pk
+     * @param string $condition
+     * @param array $params
+     * @return $this
+     */
+    public function findByPk($pk, $condition = '', $params = array())
+    {
+        return parent::findByPk($pk, $condition, $params);
+    }
+
+    /**
+     * @param array $attributes
+     * @param string $condition
+     * @param array $params
+     * @return $this
+     */
+    public function findByAttributes($attributes, $condition = '', $params = array())
+    {
+        return parent::findByAttributes($attributes, $condition, $params);
+    }
+
+    /**
+     * @param array $attributes
+     * @param string $condition
+     * @param array $params
+     * @return $this[]
+     */
+    public function findAllByAttributes($attributes, $condition = '', $params = array())
+    {
+        return parent::findAllByAttributes($attributes, $condition, $params);
+    }
+
+    /**
+     * @param mixed $pk
+     * @param string $condition
+     * @param array $params
+     * @return $this[]
+     */
+    public function findAllByPk($pk, $condition = '', $params = array())
+    {
+        return parent::findAllByPk($pk, $condition, $params);
+    }
+
+    /**
+     * @param string $sql
+     * @param array $params
+     * @return $this[]
+     */
+    public function findAllBySql($sql, $params = array())
+    {
+        return parent::findAllBySql($sql, $params);
+    }
+
+    /**
+     * @param string $sql
+     * @param array $params
+     * @return $this
+     */
+    public function findBySql($sql, $params = array())
+    {
+        return parent::findBySql($sql, $params);
+    }
+
+    /**
+     * Starts a batch query.
+     *
+     * A batch query supports fetching data in batches, which can keep the memory usage under a limit.
+     * This method will return a [[BatchQueryResult]] object which implements the [[\Iterator]] interface
+     * and can be traversed to retrieve the data in batches.
+     *
+     * For example,
+     *
+     * ```php
+     * foreach (User::model()->batch() as $rows) {
+     *     // $rows is an array of 100 or fewer rows from user table
+     * }
+     * ```
+     *
+     * @param \CDbCriteria|array $criteria
+     * @param int $batchSize the number of records to be fetched in each batch.
+     * @return BatchQueryResult the batch query result. It implements the [[\Iterator]] interface
+     * and can be traversed to retrieve the data in batches.
+     */
+    public function batch($criteria = [], $batchSize = 100)
+    {
+        return new BatchQueryResult($this->getDbConnection(), $batchSize, $this, $criteria, false);
+    }
+
+    /**
+     * Starts a batch query and retrieves data row by row.
+     * This method is similar to [[batch()]] except that in each iteration of the result,
+     * only one row of data is returned. For example,
+     *
+     * ```php
+     * foreach (User::model()->each() as $row) {
+     *     // $row is an instance of ActiveRecord
+     * }
+     * ```
+     *
+     * @param \CDbCriteria|array $criteria
+     * @param int $batchSize the number of records to be fetched in each batch.
+     * @return BatchQueryResult the batch query result. It implements the [[\Iterator]] interface
+     * and can be traversed to retrieve the data in batches.
+     */
+    public function each($criteria = [], $batchSize = 100)
+    {
+        return new BatchQueryResult($this->getDbConnection(), $batchSize, $this, $criteria, true);
+    }
+
+    /**
+     * Class casting
+     *
+     * @param string $destination
+     * @return object|bool
+     */
+    public function castAs($destination)
+    {
+        if (get_class($this) === $destination) {
+            return $this;
+        }
+        if (!is_subclass_of($destination, get_class($this)) && !is_subclass_of(get_class($this), $destination)) {
+            return false;
+        }
+        $result = unserialize(preg_replace('/^O:\d+:"[^"]++"/', 'O:' . strlen($destination) . ':"' . $destination . '"',
+            serialize($this)));
+        $result->refresh();
+        return $result;
+    }
+
+    public function getJSONAttributes()
+    {
+        $this->scenario = 'json';
+        $attributes = [];
+        foreach ($this->getSafeAttributeNames() as $name) {
+            if (property_exists($this, $name)) {
+                $attributes[$name] = $this->$name;
+            } elseif ($this->hasAttribute($name)) {
+                $attributes[$name] = $this->getAttribute($name);
+            } elseif ($this->getMetaData()->hasRelation($name)) {
+                $related = $this->getRelated($name);
+                if ($related !== null && $related instanceof ActiveRecord) {
+                    $attributes[$name] = $related->getJSONAttributes();
+                } elseif (is_array($related) && count($related) && $related[0] instanceof ActiveRecord) {
+                    foreach ($related as &$one) {
+                        $one = $one->getJSONAttributes();
+                    }
+                    $attributes[$name] = $related;
+                }
+            }
+        }
+        return $attributes;
+    }
+
+    public function tableName()
+    {
+        if (($tableName = static::getTableName()) === null) {
+            return parent::tableName();
+        }
+        return $tableName;
+    }
+
+    public static function getTableName()
+    {
+        return null;
+    }
+}

+ 51 - 0
src/components/AliasedBehavior.php

@@ -0,0 +1,51 @@
+<?php
+/**
+ * Created by PhpStorm.
+ * User: lcdee
+ * Date: 04.04.2017
+ * Time: 14:57
+ */
+
+namespace yiins\components;
+
+
+use yiins\helpers\StringsHelper;
+
+/**
+ * Class AliasedBehavior
+ * @package base\components
+ */
+class AliasedBehavior extends \CActiveRecordBehavior
+{
+    public $captionField = 'caption';
+    public $aliasField = 'alias';
+    public $idField = 'id';
+
+    public function beforeValidate($event)
+    {
+        if (!$this->owner->getAttribute($this->aliasField)) {
+            $this->createAlias();
+        }
+        return parent::beforeValidate($event);
+    }
+
+    private function createAlias()
+    {
+        $nextId = $this->owner->getAttribute($this->idField);
+        if (!$nextId) {
+            $nextId = 1;
+            $queryRes = $this->owner
+                ->getDbConnection()
+                ->createCommand()
+                ->select("MAX({$this->idField}) AS {$this->idField}")
+                ->from($this->owner->tableName())
+                ->queryColumn();
+
+            if (count($queryRes)) {
+                $nextId = $queryRes[0];
+            }
+        }
+        $this->owner->setAttribute($this->aliasField,
+            StringsHelper::URLize($this->owner->getAttribute($this->captionField) . '-' . $nextId));
+    }
+}

+ 154 - 0
src/components/BatchQueryResult.php

@@ -0,0 +1,154 @@
+<?php
+/**
+ * Created by PhpStorm.
+ * User: lcdee
+ * Date: 16.03.2017
+ * Time: 19:39
+ */
+
+namespace yiins\components;
+
+
+class BatchQueryResult implements \Iterator, \Countable
+{
+    protected $dbConnection;
+    protected $batchSize;
+    protected $ar;
+    protected $criteria;
+    protected $isEach;
+
+    protected $totalRowsCount = 0;
+    protected $offset = 0;
+
+    protected $batch = null;
+    protected $key = null;
+    protected $value = null;
+
+    public function __construct(\CDbConnection $dbConnection, $batchSize, ActiveRecord $ar, $criteria, $isEach)
+    {
+        $this->dbConnection = $dbConnection;
+        $this->batchSize = $batchSize;
+        $this->ar = $ar;
+        $this->isEach = $isEach;
+
+        $this->criteria = new \CDbCriteria();
+        $this->criteria->mergeWith($this->ar->getDbCriteria());
+        $this->criteria->mergeWith($criteria);
+
+        $this->countTotalRows();
+    }
+
+    protected function countTotalRows()
+    {
+        $criteria = clone $this->criteria;
+        $criteria->select = 'COUNT(id)';
+        $command = $this->dbConnection->getCommandBuilder()->createFindCommand($this->ar->getTableSchema(), $criteria);
+        $this->totalRowsCount = $command->queryScalar();
+    }
+
+    /**
+     * Return the current element
+     * @link http://php.net/manual/en/iterator.current.php
+     * @return ActiveRecord|ActiveRecord[]
+     * @since 5.0.0
+     */
+    public function current()
+    {
+        return $this->value;
+    }
+
+    /**
+     * Return the key of the current element
+     * @link http://php.net/manual/en/iterator.key.php
+     * @return mixed scalar on success, or null on failure.
+     * @since 5.0.0
+     */
+    public function key()
+    {
+        return $this->key;
+    }
+
+    /**
+     * Checks if current position is valid
+     * @link http://php.net/manual/en/iterator.valid.php
+     * @return boolean The return value will be casted to boolean and then evaluated.
+     * Returns true on success or false on failure.
+     * @since 5.0.0
+     */
+    public function valid()
+    {
+        return !empty($this->batch);
+    }
+
+    /**
+     * Rewind the Iterator to the first element
+     * @link http://php.net/manual/en/iterator.rewind.php
+     * @return void Any returned value is ignored.
+     * @since 5.0.0
+     */
+    public function rewind()
+    {
+        $this->key = null;
+        $this->value = null;
+        $this->offset = 0;
+        $this->next();
+    }
+
+    /**
+     * Move forward to next element
+     * @link http://php.net/manual/en/iterator.next.php
+     * @return void Any returned value is ignored.
+     * @since 5.0.0
+     */
+    public function next()
+    {
+        if ($this->batch === null || !$this->isEach || $this->isEach && next($this->batch) === false) {
+            $this->batch = $this->fetchData();
+            reset($this->batch);
+        }
+
+        if ($this->isEach) {
+            $this->value = current($this->batch);
+            if (key($this->batch) !== null) {
+                $this->key = $this->key === null ? 0 : $this->key + 1;
+            } else {
+                $this->key = null;
+            }
+        } else {
+            $this->value = $this->batch;
+            $this->key = $this->key === null ? 0 : $this->key + 1;
+        }
+    }
+
+    /**
+     * @return ActiveRecord[]
+     */
+    protected function fetchData()
+    {
+        if ($this->offset > $this->totalRowsCount) {
+            return [];
+        }
+
+        $criteria = clone $this->criteria;
+        $criteria->limit = $this->batchSize;
+        $criteria->offset = $this->offset;
+        $this->offset += $this->batchSize;
+
+        $className = get_class($this->ar);
+        return (new $className)->findAll($criteria);
+    }
+
+    /**
+     * Count elements of an object
+     * @link http://php.net/manual/en/countable.count.php
+     * @return int The custom count as an integer.
+     * </p>
+     * <p>
+     * The return value is cast to an integer.
+     * @since 5.1.0
+     */
+    public function count()
+    {
+        return $this->totalRowsCount;
+    }
+}

+ 69 - 0
src/components/CompositeUniqueValidation.php

@@ -0,0 +1,69 @@
+<?php
+/**
+ * Created by PhpStorm.
+ * User: lcdee
+ * Date: 04.04.2017
+ * Time: 16:26
+ */
+
+namespace yiins\components;
+
+
+trait CompositeUniqueValidation
+{
+    public function compositeUnique($attribute, $params)
+    {
+        $compositeWith = $params['compositeWith'];
+        $value1 = $this->$attribute;
+        $criteria = new \CDbCriteria();
+        $criteria->addCondition($attribute . ' = :' . $attribute);
+        $criteria->params = [
+            ':' . $attribute => $value1,
+        ];
+        if (!is_array($compositeWith)) {
+            $value2 = $this->$compositeWith;
+            $criteria->addCondition($compositeWith . ' = :' . $compositeWith);
+            $criteria->params[':' . $compositeWith] = $value2;
+            $messageAttributes = [
+                '{value1}'     => \CHtml::encode($value1),
+                '{value2}'     => \CHtml::encode($value2),
+                '{attribute1}' => $this->getAttributeLabel($attribute),
+                '{attribute2}' => $this->getAttributeLabel($compositeWith),
+            ];
+            $message = '{attribute1} "{value1}" в сочетании с {attribute2} "{value2}" уже заняты.';
+        } else {
+            $messageAttributes = [
+                '{value1}'     => \CHtml::encode($value1),
+                '{attribute1}' => $this->getAttributeLabel($attribute),
+            ];
+            $message = '{attribute1} "{value1}" в сочетании с ';
+            foreach ($compositeWith as $k => $field) {
+                $criteria->addCondition($field . ' = :' . $field);
+                $criteria->params[':' . $field] = $this->$field;
+                $messageAttributes['{value' . ($k + 2) . '}'] = \CHtml::encode($this->$field);
+                $messageAttributes['{attribute' . ($k + 2) . '}'] = $this->getAttributeLabel($field);
+                $message .= '{attribute' . ($k + 2) . '} "{value' . ($k + 2) . '}"';
+                if (end($compositeWith) !== $field) {
+                    $message .= ' и ';
+                }
+            }
+            $message .= ' уже заняты';
+        }
+        if (!$this->isNewRecord) {
+            if (!is_array($this->primaryKey)) {
+                $criteria->addCondition($this->getTableSchema()->primaryKey . ' != ' . $this->primaryKey);
+            } else {
+                foreach ($this->primaryKey as $keyName => $keyValue) {
+                    $criteria->addCondition("{$keyName} != {$keyValue}");
+                }
+            }
+        }
+        if ($this->count($criteria)) {
+            if (isset($params['message'])) {
+                $message = $params['message'];
+            }
+            $message = \Yii::t('yii', $message, $messageAttributes);
+            $this->addError($attribute, $message);
+        }
+    }
+}

+ 51 - 0
src/components/Enum.php

@@ -0,0 +1,51 @@
+<?php
+
+namespace yiins\components;
+
+
+use yiins\exceptions\InvalidValueException;
+
+abstract class Enum
+{
+    const __default = null;
+
+    private static $constCacheArray = null;
+
+    protected $currentValue;
+
+    final public function __construct($value, $useStrictMode = true)
+    {
+        $className = get_class($this);
+        if (!self::isValidValue($value, $useStrictMode)) {
+            throw new InvalidValueException(\Yii::t('errors', "Value {value} not found in enum {class}", [
+                '{class}' => $className,
+                '{value}' => $value,
+            ]));
+        }
+        $this->currentValue = $value;
+    }
+
+    final public function isValidValue($value, $useStrictMode = true)
+    {
+        $values = array_values(self::getConstants());
+        return in_array($value, $values, $useStrictMode);
+    }
+
+    final protected static function getConstants()
+    {
+        if (self::$constCacheArray === null) {
+            self::$constCacheArray = [];
+        }
+        $calledClass = get_called_class();
+        if (!array_key_exists($calledClass, self::$constCacheArray)) {
+            $reflect = new \ReflectionClass($calledClass);
+            self::$constCacheArray[$calledClass] = $reflect->getConstants();
+        }
+        return self::$constCacheArray[$calledClass];
+    }
+
+    final public function __toString()
+    {
+        return $this->currentValue;
+    }
+}

+ 37 - 0
src/components/EnumValidator.php

@@ -0,0 +1,37 @@
+<?php
+/**
+ * Created by PhpStorm.
+ * User: lcdee
+ * Date: 29.03.2017
+ * Time: 17:03
+ */
+
+namespace yiins\components;
+
+
+use CModel;
+use yiins\exceptions\InvalidValueException;
+
+class EnumValidator extends \CValidator
+{
+    public $enumClass;
+    public $useStrictMode = true;
+
+    /**
+     * Validates a single attribute.
+     * This method should be overridden by child classes.
+     * @param CModel $object the data object being validated
+     * @param string $attribute the name of the attribute to be validated.
+     */
+    protected function validateAttribute($object, $attribute)
+    {
+        $className = $this->enumClass;
+        try {
+            new $className($object->$attribute, $this->useStrictMode);
+        } catch (InvalidValueException $e) {
+            $this->addError($object, $attribute, $e->getMessage());
+        } catch (\UnexpectedValueException $e) {
+            $this->addError($object, $attribute, $e->getMessage());
+        }
+    }
+}

+ 25 - 0
src/components/FakeUploadedFile.php

@@ -0,0 +1,25 @@
+<?php
+
+namespace yiins\components;
+
+use yiins\web\UploadedFile;
+
+class FakeUploadedFile extends UploadedFile
+{
+    public static function clean()
+    {
+        static::$_files = null;
+    }
+
+    public function saveAs($file, $deleteTempFile = true)
+    {
+        if (file_exists($this->_tempName)) {
+            $copied = copy($this->_tempName, $file);
+            if ($copied && $deleteTempFile) {
+                unlink($this->_tempName);
+            }
+            return $copied;
+        }
+        return false;
+    }
+}

+ 47 - 0
src/components/FancyBox.php

@@ -0,0 +1,47 @@
+<?php
+/**
+ * Created by PhpStorm.
+ * User: AlexLcDee
+ * Date: 27.01.2017
+ * Time: 14:44
+ */
+
+namespace yiins\components;
+
+use CHtml;
+use yiins\assets\FancyboxAsset;
+
+class FancyBox
+{
+    public $target;
+    public $config = [];
+
+    protected $defaultConfig = [
+        'padding' => 0,
+        'margin'  => 0,
+    ];
+
+    public static function attach($target, $params = [])
+    {
+        $fancyBox = new static();
+        $fancyBox->target = $target;
+        $fancyBox->config = $params;
+        $fancyBox->init();
+        $fancyBox->run();
+    }
+
+    public function init()
+    {
+        FancyboxAsset::register(false);
+    }
+
+    public function run()
+    {
+        $config = \CJavaScript::encode(\CMap::mergeArray($this->defaultConfig, $this->config));
+        $script = <<<JS
+$("$this->target").fancybox($config);
+JS;
+        \Yii::web()->getClientScript()->registerScript('fancybox-' . (CHtml::$count++), $script,
+            \CClientScript::POS_READY);
+    }
+}

+ 323 - 0
src/components/ImageGD.php

@@ -0,0 +1,323 @@
+<?php
+/**
+ * Created by PhpStorm.
+ * User: AlexLcDee
+ * Date: 15.02.2017
+ * Time: 17:34
+ */
+
+namespace yiins\components;
+
+
+class ImageGD
+{
+    const WM_POS_LEFT_TOP = 'leftTop';
+    const WM_POS_CENTER_TOP = 'top';
+    const WM_POS_RIGHT_TOP = 'rightTop';
+    const WM_POS_LEFT_CENTER = 'leftCenter';
+    const WM_POS_CENTER_CENTER = 'center';
+    const WM_POS_RIGHT_CENTER = 'rightCenter';
+    const WM_POS_LEFT_BOTTOM = 'leftBottom';
+    const WM_POS_CENTER_BOTTOM = 'bottom';
+    const WM_POS_RIGHT_BOTTOM = 'rightBottom';
+
+    protected $sourceResource;
+    protected $destinationResource;
+    protected $watermarkSource;
+
+    protected $imageSize;
+    protected $qualityMultiplier = 1;
+    protected $mustCopy = false;
+
+    protected $outputWidth;
+    protected $outputHeight;
+    protected $quality = 100;
+    protected $offsetX = 0;
+    protected $offsetY = 0;
+    protected $cropWidth = 0;
+    protected $cropHeight = 0;
+
+    private function __construct($sourcePath)
+    {
+        $this->imageSize = getimagesize($sourcePath);
+        $this->outputWidth = $this->cropWidth = $this->imageSize[0];
+        $this->outputHeight = $this->cropHeight = $this->imageSize[1];
+
+        $this->destinationResource = imagecreatetruecolor($this->outputWidth, $this->outputHeight);
+
+        list($creationFunction, $outputFunction) = $this->functionNames();
+
+        $this->sourceResource = $creationFunction($sourcePath);
+        $this->outputFunction = $outputFunction;
+    }
+
+    /**
+     * Set up the appropriate image handling functions based on the original image's mime type
+     * @return array
+     */
+    protected function functionNames()
+    {
+        $size = $this->imageSize;
+        switch ($size['mime']) {
+            case 'image/gif':
+                // We will be converting GIFs to PNGs to avoid transparency issues when resizing GIFs
+                // This is maybe not the ideal solution, but IE6 can suck it
+                $creationFunction = 'ImageCreateFromGif';
+                $outputFunction = 'ImagePng';
+                break;
+
+            case 'image/x-png':
+            case 'image/png':
+                $creationFunction = 'ImageCreateFromPng';
+                $outputFunction = 'ImagePng';
+                $this->qualityMultiplier = 0.01;
+                break;
+
+            default:
+                $creationFunction = 'ImageCreateFromJpeg';
+                $outputFunction = 'ImageJpeg';
+                break;
+        }
+        return [
+            $creationFunction,
+            $outputFunction,
+        ];
+    }
+
+    public static function resize($sourcePath, $width, $height, $offsetX, $offsetY, $cropWidth, $cropHeight)
+    {
+        $instance = new static($sourcePath);
+        $instance->outputWidth($width);
+        $instance->outputHeight($height);
+        $instance->offsetX($offsetX);
+        $instance->offsetY($offsetY);
+        $instance->cropWidth($cropWidth);
+        $instance->cropHeight($cropHeight);
+        $instance->copyResampled($instance->sourceResource, $instance->destinationResource);
+        return $instance;
+    }
+
+    public function outputWidth($width, $reset = true)
+    {
+        $this->outputWidth = $width;
+        if ($reset) {
+            $this->setDestination();
+        }
+        return $this;
+    }
+
+    protected function setDestination()
+    {
+        $this->destinationResource = imageCreateTruecolor($this->outputWidth, $this->outputHeight);
+        $this->preserveAlfa();
+        if ($this->mustCopy) {
+            call_user_func_array([$this, $this->mustCopy], [$this->sourceResource, $this->destinationResource]);
+        }
+    }
+
+    private function preserveAlfa()
+    {
+        if (in_array($this->imageSize['mime'], array('image/gif', 'image/png'))) {
+            // If this is a GIF or a PNG, we need to set up transparency
+            imageAlphaBlending($this->destinationResource, false);
+            imageSaveAlpha($this->destinationResource, true);
+        }
+    }
+
+    public function outputHeight($height, $reset = true)
+    {
+        $this->outputHeight = $height;
+        if ($reset) {
+            $this->setDestination();
+        }
+        return $this;
+    }
+
+    public function offsetX($value)
+    {
+        $this->offsetX = $value;
+        if ($this->mustCopy) {
+            call_user_func_array([$this, $this->mustCopy], [$this->sourceResource, $this->destinationResource]);
+        }
+        return $this;
+    }
+
+    public function offsetY($value)
+    {
+        $this->offsetY = $value;
+        if ($this->mustCopy) {
+            call_user_func_array([$this, $this->mustCopy], [$this->sourceResource, $this->destinationResource]);
+        }
+        return $this;
+    }
+
+    public function cropWidth($width)
+    {
+        $this->cropWidth = $width;
+        if ($this->mustCopy) {
+            call_user_func_array([$this, $this->mustCopy], [$this->sourceResource, $this->destinationResource]);
+        }
+        return $this;
+    }
+
+    public function cropHeight($height)
+    {
+        $this->cropHeight = $height;
+        if ($this->mustCopy) {
+            call_user_func_array([$this, $this->mustCopy], [$this->sourceResource, $this->destinationResource]);
+        }
+        return $this;
+    }
+
+    public function copyResampled($source, $destination)
+    {
+        $this->mustCopy = 'copyResampled';
+        imageCopyResampled(
+            $destination,
+            $source,
+            0, 0,
+            $this->offsetX,
+            $this->offsetY,
+            $this->outputWidth,
+            $this->outputHeight,
+            $this->cropWidth,
+            $this->cropHeight);
+        return $this;
+    }
+
+    public static function watermark($sourcePath, $watermarkPath, $position = false)
+    {
+        if (!$position) {
+            $position = static::WM_POS_CENTER_CENTER;
+        }
+
+        $instance = new static($sourcePath);
+        $instance->setWatermark($watermarkPath);
+        $watermarkSizes = getimagesize($watermarkPath);
+        $instance->copy($instance->sourceResource, $instance->destinationResource);
+        $offsets = $instance->calculatePosition($position,
+            ['width' => $watermarkSizes[0], 'height' => $watermarkSizes[1]]);
+        $instance->mustCopy = false;
+        $instance->offsetX($offsets['x']);
+        $instance->offsetY($offsets['y']);
+        $instance->outputWidth($watermarkSizes[0], false);
+        $instance->outputHeight($watermarkSizes[1], false);
+        $instance->copy($instance->watermarkSource, $instance->destinationResource);
+        return $instance;
+    }
+
+    protected function setWatermark($watermarkPath)
+    {
+        $this->watermarkSource = imagecreatefrompng($watermarkPath);
+        return $this;
+    }
+
+    public function copy($source, $destination)
+    {
+        $this->mustCopy = 'copy';
+        imagecopy(
+            $destination,
+            $source,
+            $this->offsetX, $this->offsetY,
+            0,
+            0,
+            $this->outputWidth,
+            $this->outputHeight
+        );
+        return $this;
+    }
+
+    protected function calculatePosition($position, $wmSizes = ['width' => 0, 'height' => 0])
+    {
+        return call_user_func_array([$this, 'pos' . ucfirst($position)], $wmSizes);
+    }
+
+    public function quality($quality)
+    {
+        $this->quality = $quality;
+        return $this;
+    }
+
+    public function output($destination = null)
+    {
+        $outputFunction = $this->outputFunction;
+        $path = dirname($destination);
+        if (!file_exists($path)) {
+            mkdir($path, 0777, true);
+        }
+        $outputFunction($this->destinationResource, $destination, $this->quality * $this->qualityMultiplier);
+    }
+
+    protected function posLeftTop($width, $height)
+    {
+        return [
+            'x' => 10,
+            'y' => 10,
+        ];
+    }
+
+    protected function posTop($width, $height)
+    {
+        return [
+            'x' => $this->outputWidth / 2 - $width / 2,
+            'y' => 10,
+        ];
+    }
+
+    protected function posRightTop($width, $height)
+    {
+        return [
+            'x' => $this->outputWidth - $width - 10,
+            'y' => 10,
+        ];
+    }
+
+    protected function posLeftCenter($width, $height)
+    {
+        return [
+            'x' => 10,
+            'y' => $this->outputHeight / 2 - $height / 2,
+        ];
+    }
+
+    protected function posCenter($width, $height)
+    {
+        return [
+            'x' => $this->outputWidth / 2 - $width / 2,
+            'y' => $this->outputHeight / 2 - $height / 2,
+        ];
+    }
+
+    protected function posRightCenter($width, $height)
+    {
+        return [
+            'x' => $this->outputWidth - $width - 10,
+            'y' => $this->outputHeight / 2 - $height / 2,
+        ];
+    }
+
+    protected function posLeftBottom($width, $height)
+    {
+        return [
+            'x' => 10,
+            'y' => $this->outputHeight - $height - 10,
+        ];
+    }
+
+    protected function posBottom($width, $height)
+    {
+        return [
+            'x' => $this->outputWidth / 2 - $width / 2,
+            'y' => $this->outputHeight - $height - 10,
+        ];
+    }
+
+    protected function posRightBottom($width, $height)
+    {
+        return [
+            'x' => $this->outputWidth - $width - 10,
+            'y' => $this->outputHeight - $height - 10,
+        ];
+    }
+
+}

+ 21 - 0
src/components/NSPost.php

@@ -0,0 +1,21 @@
+<?php
+/**
+ * Created by PhpStorm.
+ * User: lcdee
+ * Date: 28.01.2017
+ * Time: 14:22
+ */
+
+namespace yiins\components;
+
+
+trait NSPost
+{
+    /**
+     * @return string
+     */
+    public static function getPostName()
+    {
+        return \yiins\helpers\HtmlHelper::modelName(static::class);
+    }
+}

+ 971 - 0
src/components/NestedSetBehavior.php

@@ -0,0 +1,971 @@
+<?php
+/**
+ * Created by PhpStorm.
+ * User: AlexLcDee
+ * Date: 25.01.2017
+ * Time: 13:41
+ */
+
+namespace yiins\components;
+
+
+/**
+ * NestedSetBehavior class file.
+ *
+ * @author Alexander Kochetov <creocoder@gmail.com>
+ * @link https://github.com/yiiext/nested-set-behavior
+ */
+
+use CActiveRecord;
+use CActiveRecordBehavior;
+use CDbCriteria;
+use CDbException;
+use CDbExpression;
+use CException;
+use Exception;
+use Yii;
+
+/**
+ * Provides nested set functionality for a model.
+ *
+ * @version 1.06
+ * @package yiiext.behaviors.model.trees
+ *
+ * @method \CActiveRecord getOwner()
+ */
+class NestedSetBehavior extends CActiveRecordBehavior
+{
+    private static $_cached;
+    private static $_c = 0;
+    public $hasManyRoots = false;
+    public $rootAttribute = 'root';
+    public $leftAttribute = 'lft';
+    public $rightAttribute = 'rgt';
+    public $levelAttribute = 'level';
+    private $_ignoreEvent = false;
+    private $_deleted = false;
+    private $_id;
+
+    /**
+     * Named scope. Gets children for node (direct descendants only).
+     * @return CActiveRecord the owner.
+     */
+    public function children()
+    {
+        return $this->descendants(1);
+    }
+
+    /**
+     * Named scope. Gets descendants for node.
+     * @param int $depth the depth.
+     * @return CActiveRecord the owner.
+     */
+    public function descendants($depth = null)
+    {
+        $owner = $this->getOwner();
+        $db = $owner->getDbConnection();
+        $criteria = $owner->getDbCriteria();
+        $alias = $db->quoteColumnName($owner->getTableAlias());
+        $criteria->mergeWith(array(
+            'condition' => $alias . '.' . $db->quoteColumnName($this->leftAttribute) . '>' . $owner->{$this->leftAttribute} .
+                ' AND ' . $alias . '.' . $db->quoteColumnName($this->rightAttribute) . '<' . $owner->{$this->rightAttribute},
+            'order'     => $alias . '.' . $db->quoteColumnName($this->leftAttribute),
+        ));
+        if ($depth !== null) {
+            $criteria->addCondition($alias . '.' . $db->quoteColumnName($this->levelAttribute) . '<=' . ($owner->{$this->levelAttribute} + $depth));
+        }
+        if ($this->hasManyRoots) {
+            $criteria->addCondition($alias . '.' . $db->quoteColumnName($this->rootAttribute) . '=' . CDbCriteria::PARAM_PREFIX . CDbCriteria::$paramCount);
+            $criteria->params[CDbCriteria::PARAM_PREFIX . CDbCriteria::$paramCount++] = $owner->{$this->rootAttribute};
+        }
+        return $owner;
+    }
+
+    /**
+     * Named scope. Gets ancestors for node.
+     * @param int $depth the depth.
+     * @return CActiveRecord the owner.
+     */
+    public function ancestors($depth = null)
+    {
+        $owner = $this->getOwner();
+        $db = $owner->getDbConnection();
+        $criteria = $owner->getDbCriteria();
+        $alias = $db->quoteColumnName($owner->getTableAlias());
+        $criteria->mergeWith(array(
+            'condition' => $alias . '.' . $db->quoteColumnName($this->leftAttribute) . '<' . $owner->{$this->leftAttribute} .
+                ' AND ' . $alias . '.' . $db->quoteColumnName($this->rightAttribute) . '>' . $owner->{$this->rightAttribute},
+            'order'     => $alias . '.' . $db->quoteColumnName($this->leftAttribute),
+        ));
+        if ($depth !== null) {
+            $criteria->addCondition($alias . '.' . $db->quoteColumnName($this->levelAttribute) . '>=' . ($owner->{$this->levelAttribute} - $depth));
+        }
+        if ($this->hasManyRoots) {
+            $criteria->addCondition($alias . '.' . $db->quoteColumnName($this->rootAttribute) . '=' . CDbCriteria::PARAM_PREFIX . CDbCriteria::$paramCount);
+            $criteria->params[CDbCriteria::PARAM_PREFIX . CDbCriteria::$paramCount++] = $owner->{$this->rootAttribute};
+        }
+        return $owner;
+    }
+
+    /**
+     * Named scope. Gets root node(s).
+     * @return CActiveRecord the owner.
+     */
+    public function roots()
+    {
+        $owner = $this->getOwner();
+        $db = $owner->getDbConnection();
+        $owner->getDbCriteria()->addCondition($db->quoteColumnName($owner->getTableAlias()) . '.' . $db->quoteColumnName($this->leftAttribute) . '=1');
+        return $owner;
+    }
+
+    /**
+     * Named scope. Gets root node(s).
+     * @return CActiveRecord the owner.
+     */
+    public function leafs()
+    {
+        $owner = $this->getOwner();
+        $db = $owner->getDbConnection();
+        $leftKey = $db->quoteColumnName($owner->getTableAlias()) . '.' . $db->quoteColumnName($this->leftAttribute);
+        $rightKey = $db->quoteColumnName($owner->getTableAlias()) . '.' . $db->quoteColumnName($this->rightAttribute);
+        $condition = "({$rightKey} - {$leftKey}) = 1";
+        $owner->getDbCriteria()->addCondition($condition);
+        return $owner;
+    }
+
+    /**
+     * Named scope. Gets parent of node.
+     * @return CActiveRecord the owner.
+     */
+    public function parent()
+    {
+        $owner = $this->getOwner();
+        $db = $owner->getDbConnection();
+        $criteria = $owner->getDbCriteria();
+        $alias = $db->quoteColumnName($owner->getTableAlias());
+        $criteria->mergeWith(array(
+            'condition' => $alias . '.' . $db->quoteColumnName($this->leftAttribute) . '<' . $owner->{$this->leftAttribute} .
+                ' AND ' . $alias . '.' . $db->quoteColumnName($this->rightAttribute) . '>' . $owner->{$this->rightAttribute},
+            'order'     => $alias . '.' . $db->quoteColumnName($this->rightAttribute),
+        ));
+        if ($this->hasManyRoots) {
+            $criteria->addCondition($alias . '.' . $db->quoteColumnName($this->rootAttribute) . '=' . CDbCriteria::PARAM_PREFIX . CDbCriteria::$paramCount);
+            $criteria->params[CDbCriteria::PARAM_PREFIX . CDbCriteria::$paramCount++] = $owner->{$this->rootAttribute};
+        }
+        return $owner;
+    }
+
+    /**
+     * Named scope. Gets previous sibling of node.
+     * @return CActiveRecord the owner.
+     */
+    public function prev()
+    {
+        $owner = $this->getOwner();
+        $db = $owner->getDbConnection();
+        $criteria = $owner->getDbCriteria();
+        $alias = $db->quoteColumnName($owner->getTableAlias());
+        $criteria->addCondition($alias . '.' . $db->quoteColumnName($this->rightAttribute) . '=' . ($owner->{$this->leftAttribute} - 1));
+        if ($this->hasManyRoots) {
+            $criteria->addCondition($alias . '.' . $db->quoteColumnName($this->rootAttribute) . '=' . CDbCriteria::PARAM_PREFIX . CDbCriteria::$paramCount);
+            $criteria->params[CDbCriteria::PARAM_PREFIX . CDbCriteria::$paramCount++] = $owner->{$this->rootAttribute};
+        }
+        return $owner;
+    }
+
+    /**
+     * Named scope. Gets next sibling of node.
+     * @return CActiveRecord the owner.
+     */
+    public function next()
+    {
+        $owner = $this->getOwner();
+        $db = $owner->getDbConnection();
+        $criteria = $owner->getDbCriteria();
+        $alias = $db->quoteColumnName($owner->getTableAlias());
+        $criteria->addCondition($alias . '.' . $db->quoteColumnName($this->leftAttribute) . '=' . ($owner->{$this->rightAttribute} + 1));
+        if ($this->hasManyRoots) {
+            $criteria->addCondition($alias . '.' . $db->quoteColumnName($this->rootAttribute) . '=' . CDbCriteria::PARAM_PREFIX . CDbCriteria::$paramCount);
+            $criteria->params[CDbCriteria::PARAM_PREFIX . CDbCriteria::$paramCount++] = $owner->{$this->rootAttribute};
+        }
+        return $owner;
+    }
+
+    /**
+     * Create root node if multiple-root tree mode. Update node if it's not new.
+     * @param boolean $runValidation whether to perform validation.
+     * @param boolean $attributes list of attributes.
+     * @return boolean whether the saving succeeds.
+     */
+    public function saveNode($runValidation = true, $attributes = null)
+    {
+        return $this->save($runValidation, $attributes);
+    }
+
+    /**
+     * Create root node if multiple-root tree mode. Update node if it's not new.
+     * @param boolean $runValidation whether to perform validation.
+     * @param boolean $attributes list of attributes.
+     * @return boolean whether the saving succeeds.
+     */
+    public function save($runValidation = true, $attributes = null)
+    {
+        $owner = $this->getOwner();
+        if ($runValidation && !$owner->validate($attributes)) {
+            return false;
+        }
+        if ($owner->getIsNewRecord()) {
+            return $this->makeRoot($attributes);
+        }
+        $this->_ignoreEvent = true;
+        $result = $owner->update($attributes);
+        $this->_ignoreEvent = false;
+        return $result;
+    }
+
+    /**
+     * @param array $attributes .
+     * @return boolean.
+     * @throws CException
+     * @throws Exception
+     */
+    private function makeRoot($attributes)
+    {
+        $owner = $this->getOwner();
+        $owner->{$this->leftAttribute} = 1;
+        $owner->{$this->rightAttribute} = 2;
+        $owner->{$this->levelAttribute} = 1;
+        if ($this->hasManyRoots) {
+            $db = $owner->getDbConnection();
+            if ($db->getCurrentTransaction() === null) {
+                $transaction = $db->beginTransaction();
+            }
+            try {
+                $this->_ignoreEvent = true;
+                $result = $owner->insert($attributes);
+                $this->_ignoreEvent = false;
+                if (!$result) {
+                    if (isset($transaction)) {
+                        $transaction->rollback();
+                    }
+                    return false;
+                }
+                $pk = $owner->{$this->rootAttribute} = $owner->getPrimaryKey();
+                $owner->updateByPk($pk, array($this->rootAttribute => $pk));
+                if (isset($transaction)) {
+                    $transaction->commit();
+                }
+            } catch (Exception $e) {
+                if (isset($transaction)) {
+                    $transaction->rollback();
+                }
+                throw $e;
+            }
+        } else {
+            if ($owner->roots()->exists()) {
+                throw new CException(Yii::t('yiiext', 'Cannot create more than one root in single root mode.'));
+            }
+            $this->_ignoreEvent = true;
+            $result = $owner->insert($attributes);
+            $this->_ignoreEvent = false;
+            if (!$result) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    /**
+     * Deletes node and it's descendants.
+     * @return boolean whether the deletion is successful.
+     */
+    public function deleteNode()
+    {
+        return $this->delete();
+    }
+
+    /**
+     * Deletes node and it's descendants.
+     * @return boolean whether the deletion is successful.
+     * @throws CDbException
+     * @throws Exception
+     */
+    public function delete()
+    {
+        $owner = $this->getOwner();
+        if ($owner->getIsNewRecord()) {
+            throw new CDbException(Yii::t('yiiext', 'The node cannot be deleted because it is new.'));
+        }
+        if ($this->getIsDeletedRecord()) {
+            throw new CDbException(Yii::t('yiiext', 'The node cannot be deleted because it is already deleted.'));
+        }
+        $db = $owner->getDbConnection();
+        if ($db->getCurrentTransaction() === null) {
+            $transaction = $db->beginTransaction();
+        }
+        try {
+            if ($owner->isLeaf()) {
+                $this->_ignoreEvent = true;
+                $result = $owner->delete();
+                $this->_ignoreEvent = false;
+            } else {
+                $condition = $db->quoteColumnName($this->leftAttribute) . '>=' . $owner->{$this->leftAttribute} . ' AND ' .
+                    $db->quoteColumnName($this->rightAttribute) . '<=' . $owner->{$this->rightAttribute};
+                $params = array();
+                if ($this->hasManyRoots) {
+                    $condition .= ' AND ' . $db->quoteColumnName($this->rootAttribute) . '=' . CDbCriteria::PARAM_PREFIX . CDbCriteria::$paramCount;
+                    $params[CDbCriteria::PARAM_PREFIX . CDbCriteria::$paramCount++] = $owner->{$this->rootAttribute};
+                }
+                $result = $owner->deleteAll($condition, $params) > 0;
+            }
+            if (!$result) {
+                if (isset($transaction)) {
+                    $transaction->rollback();
+                }
+                return false;
+            }
+            $this->shiftLeftRight($owner->{$this->rightAttribute} + 1,
+                $owner->{$this->leftAttribute} - $owner->{$this->rightAttribute} - 1);
+            if (isset($transaction)) {
+                $transaction->commit();
+            }
+            $this->correctCachedOnDelete();
+        } catch (Exception $e) {
+            if (isset($transaction)) {
+                $transaction->rollback();
+            }
+            throw $e;
+        }
+        return true;
+    }
+
+    /**
+     * Returns if the current node is deleted.
+     * @return boolean whether the node is deleted.
+     */
+    public function getIsDeletedRecord()
+    {
+        return $this->_deleted;
+    }
+
+    /**
+     * @param int $key .
+     * @param int $delta .
+     */
+    private function shiftLeftRight($key, $delta)
+    {
+        $owner = $this->getOwner();
+        $db = $owner->getDbConnection();
+        foreach (array($this->leftAttribute, $this->rightAttribute) as $attribute) {
+            $condition = $db->quoteColumnName($attribute) . '>=' . $key;
+            $params = array();
+            if ($this->hasManyRoots) {
+                $condition .= ' AND ' . $db->quoteColumnName($this->rootAttribute) . '=' . CDbCriteria::PARAM_PREFIX . CDbCriteria::$paramCount;
+                $params[CDbCriteria::PARAM_PREFIX . CDbCriteria::$paramCount++] = $owner->{$this->rootAttribute};
+            }
+            $owner->updateAll(array(
+                $attribute => new CDbExpression($db->quoteColumnName($attribute) . sprintf('%+d', $delta)),
+            ), $condition, $params);
+        }
+    }
+
+    /**
+     * Correct cache for {@link NestedSetBehavior::delete()} and {@link NestedSetBehavior::deleteNode()}.
+     */
+    private function correctCachedOnDelete()
+    {
+        $owner = $this->getOwner();
+        $left = $owner->{$this->leftAttribute};
+        $right = $owner->{$this->rightAttribute};
+        $key = $right + 1;
+        $delta = $left - $right - 1;
+        foreach (self::$_cached[get_class($owner)] as $node) {
+            if ($node->getIsNewRecord() || $node->getIsDeletedRecord()) {
+                continue;
+            }
+            if ($this->hasManyRoots && $owner->{$this->rootAttribute} !== $node->{$this->rootAttribute}) {
+                continue;
+            }
+            if ($node->{$this->leftAttribute} >= $left && $node->{$this->rightAttribute} <= $right) {
+                $node->setIsDeletedRecord(true);
+            } else {
+                if ($node->{$this->leftAttribute} >= $key) {
+                    $node->{$this->leftAttribute} += $delta;
+                }
+                if ($node->{$this->rightAttribute} >= $key) {
+                    $node->{$this->rightAttribute} += $delta;
+                }
+            }
+        }
+    }
+
+    /**
+     * Prepends node to target as first child.
+     * @param CActiveRecord $target the target.
+     * @param boolean $runValidation whether to perform validation.
+     * @param array $attributes list of attributes.
+     * @return boolean whether the prepending succeeds.
+     */
+    public function prependTo($target, $runValidation = true, $attributes = null)
+    {
+        return $this->addNode($target, $target->{$this->leftAttribute} + 1, 1, $runValidation, $attributes);
+    }
+
+    /**
+     * @param CActiveRecord $target .
+     * @param int $key .
+     * @param int $levelUp .
+     * @param boolean $runValidation .
+     * @param array $attributes .
+     * @return boolean.
+     * @throws CDbException
+     * @throws CException
+     * @throws Exception
+     */
+    private function addNode($target, $key, $levelUp, $runValidation, $attributes)
+    {
+        $owner = $this->getOwner();
+        if (!$owner->getIsNewRecord()) {
+            throw new CDbException(Yii::t('yiiext', 'The node cannot be inserted because it is not new.'));
+        }
+        if ($this->getIsDeletedRecord()) {
+            throw new CDbException(Yii::t('yiiext', 'The node cannot be inserted because it is deleted.'));
+        }
+        if ($target->getIsDeletedRecord()) {
+            throw new CDbException(Yii::t('yiiext', 'The node cannot be inserted because target node is deleted.'));
+        }
+        if ($owner->equals($target)) {
+            throw new CException(Yii::t('yiiext', 'The target node should not be self.'));
+        }
+        if (!$levelUp && $target->isRoot()) {
+            throw new CException(Yii::t('yiiext', 'The target node should not be root.'));
+        }
+        if ($runValidation && !$owner->validate()) {
+            return false;
+        }
+        if ($this->hasManyRoots) {
+            $owner->{$this->rootAttribute} = $target->{$this->rootAttribute};
+        }
+        $db = $owner->getDbConnection();
+        if ($db->getCurrentTransaction() === null) {
+            $transaction = $db->beginTransaction();
+        }
+        try {
+            $this->shiftLeftRight($key, 2);
+            $owner->{$this->leftAttribute} = $key;
+            $owner->{$this->rightAttribute} = $key + 1;
+            $owner->{$this->levelAttribute} = $target->{$this->levelAttribute} + $levelUp;
+            $this->_ignoreEvent = true;
+            $result = $owner->insert($attributes);
+            $this->_ignoreEvent = false;
+            if (!$result) {
+                if (isset($transaction)) {
+                    $transaction->rollback();
+                }
+                return false;
+            }
+            if (isset($transaction)) {
+                $transaction->commit();
+            }
+            $this->correctCachedOnAddNode($key);
+        } catch (Exception $e) {
+            if (isset($transaction)) {
+                $transaction->rollback();
+            }
+            throw $e;
+        }
+        return true;
+    }
+
+    /**
+     * Correct cache for {@link NestedSetBehavior::addNode()}.
+     * @param int $key .
+     */
+    private function correctCachedOnAddNode($key)
+    {
+        $owner = $this->getOwner();
+        foreach (self::$_cached[get_class($owner)] as $node) {
+            if ($node->getIsNewRecord() || $node->getIsDeletedRecord()) {
+                continue;
+            }
+            if ($this->hasManyRoots && $owner->{$this->rootAttribute} !== $node->{$this->rootAttribute}) {
+                continue;
+            }
+            if ($owner === $node) {
+                continue;
+            }
+            if ($node->{$this->leftAttribute} >= $key) {
+                $node->{$this->leftAttribute} += 2;
+            }
+            if ($node->{$this->rightAttribute} >= $key) {
+                $node->{$this->rightAttribute} += 2;
+            }
+        }
+    }
+
+    /**
+     * Prepends target to node as first child.
+     * @param CActiveRecord $target the target.
+     * @param boolean $runValidation whether to perform validation.
+     * @param array $attributes list of attributes.
+     * @return boolean whether the prepending succeeds.
+     */
+    public function prepend($target, $runValidation = true, $attributes = null)
+    {
+        return $target->prependTo($this->getOwner(), $runValidation, $attributes);
+    }
+
+    /**
+     * Appends node to target as last child.
+     * @param CActiveRecord $target the target.
+     * @param boolean $runValidation whether to perform validation.
+     * @param array $attributes list of attributes.
+     * @return boolean whether the appending succeeds.
+     */
+    public function appendTo($target, $runValidation = true, $attributes = null)
+    {
+        return $this->addNode($target, $target->{$this->rightAttribute}, 1, $runValidation, $attributes);
+    }
+
+    /**
+     * Appends target to node as last child.
+     * @param CActiveRecord $target the target.
+     * @param boolean $runValidation whether to perform validation.
+     * @param array $attributes list of attributes.
+     * @return boolean whether the appending succeeds.
+     */
+    public function append($target, $runValidation = true, $attributes = null)
+    {
+        return $target->appendTo($this->getOwner(), $runValidation, $attributes);
+    }
+
+    /**
+     * Inserts node as previous sibling of target.
+     * @param CActiveRecord $target the target.
+     * @param boolean $runValidation whether to perform validation.
+     * @param array $attributes list of attributes.
+     * @return boolean whether the inserting succeeds.
+     */
+    public function insertBefore($target, $runValidation = true, $attributes = null)
+    {
+        return $this->addNode($target, $target->{$this->leftAttribute}, 0, $runValidation, $attributes);
+    }
+
+    /**
+     * Inserts node as next sibling of target.
+     * @param CActiveRecord $target the target.
+     * @param boolean $runValidation whether to perform validation.
+     * @param array $attributes list of attributes.
+     * @return boolean whether the inserting succeeds.
+     */
+    public function insertAfter($target, $runValidation = true, $attributes = null)
+    {
+        return $this->addNode($target, $target->{$this->rightAttribute} + 1, 0, $runValidation, $attributes);
+    }
+
+    /**
+     * Move node as previous sibling of target.
+     * @param CActiveRecord $target the target.
+     * @return boolean whether the moving succeeds.
+     */
+    public function moveBefore($target)
+    {
+        return $this->moveNode($target, $target->{$this->leftAttribute}, 0);
+    }
+
+    /**
+     * @param CActiveRecord $target .
+     * @param int $key .
+     * @param int $levelUp .
+     * @return boolean.
+     * @throws CDbException
+     * @throws CException
+     * @throws Exception
+     */
+    private function moveNode($target, $key, $levelUp)
+    {
+        $owner = $this->getOwner();
+        if ($owner->getIsNewRecord()) {
+            throw new CException(Yii::t('yiiext', 'The node should not be new record.'));
+        }
+        if ($this->getIsDeletedRecord()) {
+            throw new CDbException(Yii::t('yiiext', 'The node should not be deleted.'));
+        }
+        if ($target->getIsDeletedRecord()) {
+            throw new CDbException(Yii::t('yiiext', 'The target node should not be deleted.'));
+        }
+        if ($owner->equals($target)) {
+            throw new CException(Yii::t('yiiext', 'The target node should not be self.'));
+        }
+        if ($target->isDescendantOf($owner)) {
+            throw new CException(Yii::t('yiiext', 'The target node should not be descendant.'));
+        }
+        if (!$levelUp && $target->isRoot()) {
+            throw new CException(Yii::t('yiiext', 'The target node should not be root.'));
+        }
+        $db = $owner->getDbConnection();
+        if ($db->getCurrentTransaction() === null) {
+            $transaction = $db->beginTransaction();
+        }
+        try {
+            $left = $owner->{$this->leftAttribute};
+            $right = $owner->{$this->rightAttribute};
+            $levelDelta = $target->{$this->levelAttribute} - $owner->{$this->levelAttribute} + $levelUp;
+            if ($this->hasManyRoots && $owner->{$this->rootAttribute} !== $target->{$this->rootAttribute}) {
+                foreach (array($this->leftAttribute, $this->rightAttribute) as $attribute) {
+                    $owner->updateAll(array(
+                        $attribute => new CDbExpression($db->quoteColumnName($attribute) . sprintf('%+d',
+                                $right - $left + 1)),
+                    ),
+                        $db->quoteColumnName($attribute) . '>=' . $key . ' AND ' . $db->quoteColumnName($this->rootAttribute) . '=' . CDbCriteria::PARAM_PREFIX . CDbCriteria::$paramCount,
+                        array(CDbCriteria::PARAM_PREFIX . CDbCriteria::$paramCount++ => $target->{$this->rootAttribute}));
+                }
+                $delta = $key - $left;
+                $owner->updateAll(
+                    array(
+                        $this->leftAttribute  => new CDbExpression($db->quoteColumnName($this->leftAttribute) . sprintf('%+d',
+                                $delta)),
+                        $this->rightAttribute => new CDbExpression($db->quoteColumnName($this->rightAttribute) . sprintf('%+d',
+                                $delta)),
+                        $this->levelAttribute => new CDbExpression($db->quoteColumnName($this->levelAttribute) . sprintf('%+d',
+                                $levelDelta)),
+                        $this->rootAttribute  => $target->{$this->rootAttribute},
+                    ),
+                    $db->quoteColumnName($this->leftAttribute) . '>=' . $left . ' AND ' .
+                    $db->quoteColumnName($this->rightAttribute) . '<=' . $right . ' AND ' .
+                    $db->quoteColumnName($this->rootAttribute) . '=' . CDbCriteria::PARAM_PREFIX . CDbCriteria::$paramCount,
+                    array(CDbCriteria::PARAM_PREFIX . CDbCriteria::$paramCount++ => $owner->{$this->rootAttribute}));
+                $this->shiftLeftRight($right + 1, $left - $right - 1);
+                if (isset($transaction)) {
+                    $transaction->commit();
+                }
+                $this->correctCachedOnMoveBetweenTrees($key, $levelDelta, $target->{$this->rootAttribute});
+            } else {
+                $delta = $right - $left + 1;
+                $this->shiftLeftRight($key, $delta);
+                if ($left >= $key) {
+                    $left += $delta;
+                    $right += $delta;
+                }
+                $condition = $db->quoteColumnName($this->leftAttribute) . '>=' . $left . ' AND ' . $db->quoteColumnName($this->rightAttribute) . '<=' . $right;
+                $params = array();
+                if ($this->hasManyRoots) {
+                    $condition .= ' AND ' . $db->quoteColumnName($this->rootAttribute) . '=' . CDbCriteria::PARAM_PREFIX . CDbCriteria::$paramCount;
+                    $params[CDbCriteria::PARAM_PREFIX . CDbCriteria::$paramCount++] = $owner->{$this->rootAttribute};
+                }
+                $owner->updateAll(array(
+                    $this->levelAttribute => new CDbExpression($db->quoteColumnName($this->levelAttribute) . sprintf('%+d',
+                            $levelDelta)),
+                ), $condition, $params);
+                foreach (array($this->leftAttribute, $this->rightAttribute) as $attribute) {
+                    $condition = $db->quoteColumnName($attribute) . '>=' . $left . ' AND ' . $db->quoteColumnName($attribute) . '<=' . $right;
+                    $params = array();
+                    if ($this->hasManyRoots) {
+                        $condition .= ' AND ' . $db->quoteColumnName($this->rootAttribute) . '=' . CDbCriteria::PARAM_PREFIX . CDbCriteria::$paramCount;
+                        $params[CDbCriteria::PARAM_PREFIX . CDbCriteria::$paramCount++] = $owner->{$this->rootAttribute};
+                    }
+                    $owner->updateAll(array(
+                        $attribute => new CDbExpression($db->quoteColumnName($attribute) . sprintf('%+d', $key - $left)),
+                    ), $condition, $params);
+                }
+                $this->shiftLeftRight($right + 1, -$delta);
+                if (isset($transaction)) {
+                    $transaction->commit();
+                }
+                $this->correctCachedOnMoveNode($key, $levelDelta);
+            }
+        } catch (Exception $e) {
+            if (isset($transaction)) {
+                $transaction->rollback();
+            }
+            throw $e;
+        }
+        return true;
+    }
+
+    /**
+     * Correct cache for {@link NestedSetBehavior::moveNode()}.
+     * @param int $key .
+     * @param int $levelDelta .
+     * @param int $root .
+     */
+    private function correctCachedOnMoveBetweenTrees($key, $levelDelta, $root)
+    {
+        $owner = $this->getOwner();
+        $left = $owner->{$this->leftAttribute};
+        $right = $owner->{$this->rightAttribute};
+        $delta = $right - $left + 1;
+        $delta2 = $key - $left;
+        $delta3 = $left - $right - 1;
+        foreach (self::$_cached[get_class($owner)] as $node) {
+            if ($node->getIsNewRecord() || $node->getIsDeletedRecord()) {
+                continue;
+            }
+            if ($node->{$this->rootAttribute} === $root) {
+                if ($node->{$this->leftAttribute} >= $key) {
+                    $node->{$this->leftAttribute} += $delta;
+                }
+                if ($node->{$this->rightAttribute} >= $key) {
+                    $node->{$this->rightAttribute} += $delta;
+                }
+            } else {
+                if ($node->{$this->rootAttribute} === $owner->{$this->rootAttribute}) {
+                    if ($node->{$this->leftAttribute} >= $left && $node->{$this->rightAttribute} <= $right) {
+                        $node->{$this->leftAttribute} += $delta2;
+                        $node->{$this->rightAttribute} += $delta2;
+                        $node->{$this->levelAttribute} += $levelDelta;
+                        $node->{$this->rootAttribute} = $root;
+                    } else {
+                        if ($node->{$this->leftAttribute} >= $right + 1) {
+                            $node->{$this->leftAttribute} += $delta3;
+                        }
+                        if ($node->{$this->rightAttribute} >= $right + 1) {
+                            $node->{$this->rightAttribute} += $delta3;
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    /**
+     * Correct cache for {@link NestedSetBehavior::moveNode()}.
+     * @param int $key .
+     * @param int $levelDelta .
+     */
+    private function correctCachedOnMoveNode($key, $levelDelta)
+    {
+        $owner = $this->getOwner();
+        $left = $owner->{$this->leftAttribute};
+        $right = $owner->{$this->rightAttribute};
+        $delta = $right - $left + 1;
+        if ($left >= $key) {
+            $left += $delta;
+            $right += $delta;
+        }
+        $delta2 = $key - $left;
+        foreach (self::$_cached[get_class($owner)] as $node) {
+            if ($node->getIsNewRecord() || $node->getIsDeletedRecord()) {
+                continue;
+            }
+            if ($this->hasManyRoots && $owner->{$this->rootAttribute} !== $node->{$this->rootAttribute}) {
+                continue;
+            }
+            if ($node->{$this->leftAttribute} >= $key) {
+                $node->{$this->leftAttribute} += $delta;
+            }
+            if ($node->{$this->rightAttribute} >= $key) {
+                $node->{$this->rightAttribute} += $delta;
+            }
+            if ($node->{$this->leftAttribute} >= $left && $node->{$this->rightAttribute} <= $right) {
+                $node->{$this->levelAttribute} += $levelDelta;
+            }
+            if ($node->{$this->leftAttribute} >= $left && $node->{$this->leftAttribute} <= $right) {
+                $node->{$this->leftAttribute} += $delta2;
+            }
+            if ($node->{$this->rightAttribute} >= $left && $node->{$this->rightAttribute} <= $right) {
+                $node->{$this->rightAttribute} += $delta2;
+            }
+            if ($node->{$this->leftAttribute} >= $right + 1) {
+                $node->{$this->leftAttribute} -= $delta;
+            }
+            if ($node->{$this->rightAttribute} >= $right + 1) {
+                $node->{$this->rightAttribute} -= $delta;
+            }
+        }
+    }
+
+    /**
+     * Move node as next sibling of target.
+     * @param CActiveRecord $target the target.
+     * @return boolean whether the moving succeeds.
+     */
+    public function moveAfter($target)
+    {
+        return $this->moveNode($target, $target->{$this->rightAttribute} + 1, 0);
+    }
+
+    /**
+     * Move node as first child of target.
+     * @param CActiveRecord $target the target.
+     * @return boolean whether the moving succeeds.
+     */
+    public function moveAsFirst($target)
+    {
+        return $this->moveNode($target, $target->{$this->leftAttribute} + 1, 1);
+    }
+
+    /**
+     * Move node as last child of target.
+     * @param CActiveRecord $target the target.
+     * @return boolean whether the moving succeeds.
+     */
+    public function moveAsLast($target)
+    {
+        return $this->moveNode($target, $target->{$this->rightAttribute}, 1);
+    }
+
+    /**
+     * Move node as new root.
+     * @return boolean whether the moving succeeds.
+     * @throws CDbException
+     * @throws CException
+     * @throws Exception
+     */
+    public function moveAsRoot()
+    {
+        $owner = $this->getOwner();
+        if (!$this->hasManyRoots) {
+            throw new CException(Yii::t('yiiext', 'Many roots mode is off.'));
+        }
+        if ($owner->getIsNewRecord()) {
+            throw new CException(Yii::t('yiiext', 'The node should not be new record.'));
+        }
+        if ($this->getIsDeletedRecord()) {
+            throw new CDbException(Yii::t('yiiext', 'The node should not be deleted.'));
+        }
+        if ($owner->isRoot()) {
+            throw new CException(Yii::t('yiiext', 'The node already is root node.'));
+        }
+        $db = $owner->getDbConnection();
+        if ($db->getCurrentTransaction() === null) {
+            $transaction = $db->beginTransaction();
+        }
+        try {
+            $left = $owner->{$this->leftAttribute};
+            $right = $owner->{$this->rightAttribute};
+            $levelDelta = 1 - $owner->{$this->levelAttribute};
+            $delta = 1 - $left;
+            $owner->updateAll(
+                array(
+                    $this->leftAttribute  => new CDbExpression($db->quoteColumnName($this->leftAttribute) . sprintf('%+d',
+                            $delta)),
+                    $this->rightAttribute => new CDbExpression($db->quoteColumnName($this->rightAttribute) . sprintf('%+d',
+                            $delta)),
+                    $this->levelAttribute => new CDbExpression($db->quoteColumnName($this->levelAttribute) . sprintf('%+d',
+                            $levelDelta)),
+                    $this->rootAttribute  => $owner->getPrimaryKey(),
+                ),
+                $db->quoteColumnName($this->leftAttribute) . '>=' . $left . ' AND ' .
+                $db->quoteColumnName($this->rightAttribute) . '<=' . $right . ' AND ' .
+                $db->quoteColumnName($this->rootAttribute) . '=' . CDbCriteria::PARAM_PREFIX . CDbCriteria::$paramCount,
+                array(CDbCriteria::PARAM_PREFIX . CDbCriteria::$paramCount++ => $owner->{$this->rootAttribute}));
+            $this->shiftLeftRight($right + 1, $left - $right - 1);
+            if (isset($transaction)) {
+                $transaction->commit();
+            }
+            $this->correctCachedOnMoveBetweenTrees(1, $levelDelta, $owner->getPrimaryKey());
+        } catch (Exception $e) {
+            if (isset($transaction)) {
+                $transaction->rollback();
+            }
+            throw $e;
+        }
+        return true;
+    }
+
+    /**
+     * Determines if node is descendant of subject node.
+     * @param CActiveRecord $subj the subject node.
+     * @return boolean whether the node is descendant of subject node.
+     */
+    public function isDescendantOf($subj)
+    {
+        $owner = $this->getOwner();
+        $result = ($owner->{$this->leftAttribute} > $subj->{$this->leftAttribute})
+            && ($owner->{$this->rightAttribute} < $subj->{$this->rightAttribute});
+        if ($this->hasManyRoots) {
+            $result = $result && ($owner->{$this->rootAttribute} === $subj->{$this->rootAttribute});
+        }
+        return $result;
+    }
+
+    /**
+     * Determines if node is leaf.
+     * @return boolean whether the node is leaf.
+     */
+    public function isLeaf()
+    {
+        $owner = $this->getOwner();
+        return $owner->{$this->rightAttribute} - $owner->{$this->leftAttribute} === 1;
+    }
+
+    /**
+     * Determines if node is root.
+     * @return boolean whether the node is root.
+     */
+    public function isRoot()
+    {
+        return $this->getOwner()->{$this->leftAttribute} == 1;
+    }
+
+    /**
+     * Sets if the current node is deleted.
+     * @param boolean $value whether the node is deleted.
+     */
+    public function setIsDeletedRecord($value)
+    {
+        $this->_deleted = $value;
+    }
+
+    /**
+     * Handle 'afterConstruct' event of the owner.
+     * @param \CEvent $event event parameter.
+     */
+    public function afterConstruct($event)
+    {
+        $owner = $this->getOwner();
+        self::$_cached[get_class($owner)][$this->_id = self::$_c++] = $owner;
+    }
+
+    /**
+     * Handle 'afterFind' event of the owner.
+     * @param \CEvent $event event parameter.
+     */
+    public function afterFind($event)
+    {
+        $owner = $this->getOwner();
+        self::$_cached[get_class($owner)][$this->_id = self::$_c++] = $owner;
+    }
+
+    /**
+     * Handle 'beforeSave' event of the owner.
+     * @param \CEvent $event event parameter.
+     * @return boolean.
+     * @throws CDbException
+     */
+    public function beforeSave($event)
+    {
+        if ($this->_ignoreEvent) {
+            return true;
+        } else {
+            throw new CDbException(Yii::t('yiiext',
+                'You should not use CActiveRecord::save() method when NestedSetBehavior attached.'));
+        }
+    }
+
+    /**
+     * Handle 'beforeDelete' event of the owner.
+     * @param \CEvent $event event parameter.
+     * @return boolean.
+     * @throws CDbException
+     */
+    public function beforeDelete($event)
+    {
+        if ($this->_ignoreEvent) {
+            return true;
+        } else {
+            throw new CDbException(Yii::t('yiiext',
+                'You should not use CActiveRecord::delete() method when NestedSetBehavior attached.'));
+        }
+    }
+
+    /**
+     * Destructor.
+     */
+    public function __destruct()
+    {
+        unset(self::$_cached[get_class($this->getOwner())][$this->_id]);
+    }
+}

+ 124 - 0
src/components/Select2.php

@@ -0,0 +1,124 @@
+<?php
+/**
+ * Created by PhpStorm.
+ * User: AlexLcDee
+ * Date: 27.01.2017
+ * Time: 18:37
+ */
+
+namespace yiins\components;
+
+use Yii;
+use yiins\assets\Select2Asset;
+
+class Select2
+{
+    protected $options = [];
+
+    protected $defaultOptions = [];
+
+    protected $data = [];
+
+    protected $htmlOptions = [
+        'class' => 'form-control',
+    ];
+
+    protected $containerOptions = [
+        'class' => 'form-group',
+    ];
+    protected $labelOptions = [
+        'class' => 'control-label',
+    ];
+
+    protected $errorOptions = [
+        'class' => 'help-block error',
+    ];
+
+    protected $attribute;
+
+    protected $model;
+
+    public static function renderActiveSingle(ActiveRecord $model, $attribute, $params = [])
+    {
+        $instance = static::instantiate($model, $attribute, $params);
+        return $instance->renderSingle();
+    }
+
+    protected static function instantiate(ActiveRecord $model, $attribute, $params = [])
+    {
+        $instance = new static;
+        $instance->setModel($model);
+        $instance->setAttribute($attribute);
+        foreach ($params as $param => $value) {
+            $instance->set($param, $value);
+        }
+        $instance->init();
+        return $instance;
+    }
+
+    protected function setModel(ActiveRecord $model)
+    {
+        $this->model = $model;
+    }
+
+    protected function setAttribute($attribute)
+    {
+        $this->attribute = $attribute;
+    }
+
+    protected function set($key, $value)
+    {
+        if (is_array($this->$key) && is_array($value)) {
+            $this->$key = \CMap::mergeArray($this->$key, $value);
+        }
+    }
+
+    public function init()
+    {
+        Select2Asset::register();
+        $this->defaultOptions = \CMap::mergeArray($this->defaultOptions, [
+            'language' => Yii::app()->language,
+        ]);
+
+        $this->options = \CMap::mergeArray($this->defaultOptions, $this->options);
+    }
+
+    protected function renderSingle()
+    {
+        \CHtml::resolveNameID($this->model, $this->attribute, $this->htmlOptions);
+        $group = \CHtml::activeDropDownList($this->model, $this->attribute, $this->data, $this->htmlOptions);
+        $id = $this->htmlOptions['id'];
+        $options = \CJavaScript::encode($this->options);
+        $script = <<<JS
+$("#$id").select2($options);
+JS;
+        Yii::web()->clientScript->registerScript('select2-' . (\CHtml::$count++), $script);
+        return $group;
+    }
+
+    public static function renderActive(ActiveRecord $model, $attribute, $params = [])
+    {
+        $instance = static::instantiate($model, $attribute, $params);
+        return $instance->renderActiveField();
+    }
+
+    protected function renderActiveField()
+    {
+        \CHtml::resolveNameID($this->model, $this->attribute, $this->htmlOptions);
+
+        $group = \CHtml::openTag('div', $this->containerOptions);
+        $group .= \CHtml::activeLabelEx($this->model, $this->attribute, $this->labelOptions);
+        $group .= \CHtml::activeDropDownList($this->model, $this->attribute, $this->data, $this->htmlOptions);
+        $group .= \CHtml::error($this->model, $this->attribute);
+        $group .= \CHtml::closeTag('div');
+
+        $id = $this->htmlOptions['id'];
+        $options = \CJavaScript::encode($this->options);
+        $script = <<<JS
+$("#$id").select2($options);
+JS;
+
+        Yii::web()->clientScript->registerScript('select2-' . (\CHtml::$count++), $script);
+        return $group;
+    }
+}

+ 65 - 0
src/components/Update.php

@@ -0,0 +1,65 @@
+<?php
+/**
+ * Created by PhpStorm.
+ * User: lcdee
+ * Date: 28.01.2017
+ * Time: 1:25
+ */
+
+namespace yiins\components;
+
+
+class Update extends \CComponent
+{
+    private $_db;
+
+    public function setDbConnection($db)
+    {
+        $this->_db = $db;
+    }
+
+    /**
+     * @return \CDbConnection
+     */
+    public function getDbConnection()
+    {
+        return $this->_db;
+    }
+
+    public function createDir($path, $mode = 0644)
+    {
+        @mkdir($path, $mode, true);
+    }
+
+    public function chmod($path, $mode)
+    {
+        @chmod($path, $mode);
+    }
+
+    public function symlink($target, $link)
+    {
+        @symlink($target, $link);
+    }
+
+    public function cleanDir($path, $includeDir = false, $initPath = false)
+    {
+        if (!$initPath) {
+            $initPath = $path;
+        }
+        if (is_dir($path)) {
+            $objects = scandir($path);
+            foreach ($objects as $object) {
+                if ($object != "." && $object != "..") {
+                    if (is_dir($path . "/" . $object)) {
+                        $this->cleanDir($path . "/" . $object, $includeDir, $initPath);
+                    } else {
+                        unlink($path . "/" . $object);
+                    }
+                }
+            }
+            if (($initPath == $path && $includeDir) || $initPath != $path) {
+                rmdir($path);
+            }
+        }
+    }
+}

+ 52 - 0
src/console/Application.php

@@ -0,0 +1,52 @@
+<?php
+
+namespace yiins\console;
+
+use Yii;
+
+/**
+ * Description of Application
+ *
+ * @author Alexander Larkin <lcdee@andex.ru>
+ */
+class Application extends \CConsoleApplication
+{
+    protected $_controllerNamespace;
+    private $_commandPath;
+    /**
+     * @var CommandRunner
+     */
+    private $_runner;
+
+    public function setControllerNamespace($value)
+    {
+        $this->_controllerNamespace = $value;
+        $this->_commandPath = dirname(Yii::cli()->basePath) . DIRECTORY_SEPARATOR . str_replace('\\',
+                DIRECTORY_SEPARATOR, $value);
+    }
+
+    protected function init()
+    {
+        parent::init();
+        if (empty($_SERVER['argv'])) {
+            die('This script must be run from the command line.');
+        }
+        $this->_runner = $this->createCommandRunner();
+        $this->_runner->commands = $this->commandMap;
+        $this->_runner->addCommands($this->getCommandPath());
+    }
+
+    /**
+     * @return CommandRunner
+     */
+    protected function createCommandRunner()
+    {
+        $runner = new CommandRunner($this->_controllerNamespace);
+        return $runner;
+    }
+
+    public function getCommandPath()
+    {
+        return $this->_commandPath;
+    }
+}

+ 95 - 0
src/console/CommandRunner.php

@@ -0,0 +1,95 @@
+<?php
+
+/*
+ * To change this license header, choose License Headers in Project Properties.
+ * To change this template file, choose Tools | Templates
+ * and open the template in the editor.
+ */
+
+namespace yiins\console;
+
+/**
+ * Description of CommandRunner
+ *
+ * @author Alexander Larkin <lcdee@andex.ru>
+ */
+class CommandRunner extends \CConsoleCommandRunner
+{
+
+    private $namespace;
+
+    public function __construct($namespace)
+    {
+        $this->namespace = $namespace;
+    }
+
+    /**
+     * Searches for commands under the specified directory.
+     * @param string $path the directory containing the command class assets.
+     * @return array list of commands (command name=>command class file)
+     */
+    public function findCommands($path)
+    {
+        if (($dir = @opendir($path)) === false) {
+            return array();
+        }
+        $commands = array();
+        while (($name = readdir($dir)) !== false) {
+            $file = $path . DIRECTORY_SEPARATOR . $name;
+            if (!strcasecmp(substr($name, -14), 'Controller.php') && is_file($file)) {
+                $commands[strtolower(substr($name, 0, -14))] = $file;
+            }
+            if (!strcasecmp(substr($name, -11), 'Command.php') && is_file($file)) {
+                $commands[strtolower(substr($name, 0, -11))] = $file;
+            }
+        }
+        closedir($dir);
+        return $commands;
+    }
+
+    /**
+     * @param string $name command name (case-insensitive)
+     * @return \CConsoleCommand the command object. Null if the name is invalid.
+     */
+    public function createCommand($name)
+    {
+        $name = strtolower($name);
+
+        $command = null;
+        if (isset($this->commands[$name])) {
+            $command = $this->commands[$name];
+        } else {
+            $commands = array_change_key_case($this->commands);
+            if (isset($commands[$name])) {
+                $command = $commands[$name];
+            }
+        }
+
+        if ($command !== null) {
+            if (is_string($command)) { // class file path or alias
+                if (strpos($command, '/') !== false || strpos($command, '\\') !== false) {
+                    $className = substr(basename($command), 0, -4);
+                    if (!class_exists($className, false)) {
+                        require_once($command);
+                    }
+                } else // an alias
+                {
+                    $className = \Yii::import($command);
+                }
+                if (class_exists($this->namespace . '\\' . $className)) {
+                    $nsClassName = $this->namespace . '\\' . $className;
+                    return new $nsClassName($name, $this);
+                }
+                return new $className($name, $this);
+            } else // an array configuration
+            {
+                return \Yii::createComponent($command, $name, $this);
+            }
+        } elseif ($name === 'help') {
+            return new \CHelpCommand('help', $this);
+        } else {
+            return null;
+        }
+    }
+
+}

+ 8 - 0
src/console/Controller.php

@@ -0,0 +1,8 @@
+<?php
+
+namespace yiins\console;
+
+class Controller extends \CConsoleCommand
+{
+
+}

+ 15 - 0
src/exceptions/InvalidConfigException.php

@@ -0,0 +1,15 @@
+<?php
+/**
+ * Created by PhpStorm.
+ * User: AlexLcDee
+ * Date: 24.01.2017
+ * Time: 11:54
+ */
+
+namespace yiins\exceptions;
+
+
+class InvalidConfigException extends \Exception
+{
+
+}

+ 15 - 0
src/exceptions/InvalidParamException.php

@@ -0,0 +1,15 @@
+<?php
+/**
+ * Created by PhpStorm.
+ * User: AlexLcDee
+ * Date: 24.01.2017
+ * Time: 11:55
+ */
+
+namespace yiins\exceptions;
+
+
+class InvalidParamException extends \Exception
+{
+
+}

+ 15 - 0
src/exceptions/InvalidValueException.php

@@ -0,0 +1,15 @@
+<?php
+/**
+ * Created by PhpStorm.
+ * User: AlexLcDee
+ * Date: 16.02.2017
+ * Time: 15:45
+ */
+
+namespace yiins\exceptions;
+
+
+class InvalidValueException extends \Exception
+{
+
+}

+ 639 - 0
src/helpers/ArrayHelper.php

@@ -0,0 +1,639 @@
+<?php
+
+namespace yiins\helpers;
+
+
+use yiins\exceptions\InvalidValueException;
+
+class ArrayHelper
+{
+    /**
+     * @param callable $callback must accept one argument - array item and return null or array containing key=>value pair for resulting array
+     * @param array $array
+     * @return array
+     * @throws InvalidValueException
+     */
+    public static function mapAssoc(callable $callback, array $array)
+    {
+        $result = [];
+        foreach ($array as $value) {
+            /** @var array $callbackResult */
+            $callbackResult = $callback($value);
+            if ($callbackResult === null) {
+                continue;
+            }
+            if (!is_array($callbackResult)) {
+                throw new InvalidValueException('Callback must return null or array contains key=>value pair for resulting array');
+            }
+            $result[key($callbackResult)] = $callbackResult[key($callbackResult)];
+        }
+        return $result;
+    }
+
+    /**
+     * Returns the values of a specified column in an array.
+     * The input array should be multidimensional or an array of objects.
+     *
+     * For example,
+     *
+     * ```php
+     * $array = [
+     *     ['id' => '123', 'data' => 'abc'],
+     *     ['id' => '345', 'data' => 'def'],
+     * ];
+     * $result = ArrayHelper::getColumn($array, 'id');
+     * // the result is: ['123', '345']
+     *
+     * // using anonymous function
+     * $result = ArrayHelper::getColumn($array, function ($element) {
+     *     return $element['id'];
+     * });
+     * ```
+     *
+     * @param array $array
+     * @param string|\Closure $name
+     * @param bool $keepKeys whether to maintain the array keys. If false, the resulting array
+     * will be re-indexed with integers.
+     * @return array the list of column values
+     */
+    public static function getColumn($array, $name, $keepKeys = true)
+    {
+        $result = [];
+        if ($keepKeys) {
+            foreach ($array as $k => $element) {
+                $result[$k] = static::getValue($element, $name);
+            }
+        } else {
+            foreach ($array as $element) {
+                $result[] = static::getValue($element, $name);
+            }
+        }
+        return $result;
+    }
+
+    /**
+     * Retrieves the value of an array element or object property with the given key or property name.
+     * If the key does not exist in the array or object, the default value will be returned instead.
+     *
+     * The key may be specified in a dot format to retrieve the value of a sub-array or the property
+     * of an embedded object. In particular, if the key is `x.y.z`, then the returned value would
+     * be `$array['x']['y']['z']` or `$array->x->y->z` (if `$array` is an object). If `$array['x']`
+     * or `$array->x` is neither an array nor an object, the default value will be returned.
+     * Note that if the array already has an element `x.y.z`, then its value will be returned
+     * instead of going through the sub-arrays. So it is better to be done specifying an array of key names
+     * like `['x', 'y', 'z']`.
+     *
+     * Below are some usage examples,
+     *
+     * ```php
+     * // working with array
+     * $username = ArrayHelper::getValue($_POST, 'username');
+     * // working with object
+     * $username = ArrayHelper::getValue($user, 'username');
+     * // working with anonymous function
+     * $fullName = ArrayHelper::getValue($user, function ($user, $defaultValue) {
+     *     return $user->firstName . ' ' . $user->lastName;
+     * });
+     * // using dot format to retrieve the property of embedded object
+     * $street = ArrayHelper::getValue($users, 'address.street');
+     * // using an array of keys to retrieve the value
+     * $value = ArrayHelper::getValue($versions, ['1.0', 'date']);
+     * ```
+     *
+     * @param array|object $array array or object to extract value from
+     * @param string|\Closure|array $key key name of the array element, an array of keys or property name of the object,
+     * or an anonymous function returning the value. The anonymous function signature should be:
+     * `function($array, $defaultValue)`.
+     * The possibility to pass an array of keys is available since version 2.0.4.
+     * @param mixed $default the default value to be returned if the specified array key does not exist. Not used when
+     * getting value from an object.
+     * @return mixed the value of the element if found, default value otherwise
+     */
+    public static function getValue($array, $key, $default = null)
+    {
+        if ($key instanceof \Closure) {
+            return $key($array, $default);
+        }
+        if (is_array($key)) {
+            $lastKey = array_pop($key);
+            foreach ($key as $keyPart) {
+                $array = static::getValue($array, $keyPart);
+            }
+            $key = $lastKey;
+        }
+        if (is_array($array) && (isset($array[$key]) || array_key_exists($key, $array))) {
+            return $array[$key];
+        }
+        if (($pos = strrpos($key, '.')) !== false) {
+            $array = static::getValue($array, substr($key, 0, $pos), $default);
+            $key = substr($key, $pos + 1);
+        }
+        if (is_object($array)) {
+            // this is expected to fail if the property does not exist, or __get() is not implemented
+            // it is not reliably possible to check whether a property is accessible beforehand
+            return $array->$key;
+        } elseif (is_array($array)) {
+            return (isset($array[$key]) || array_key_exists($key, $array)) ? $array[$key] : $default;
+        } else {
+            return $default;
+        }
+    }
+
+    /**
+     * Builds a map (key-value pairs) from a multidimensional array or an array of objects.
+     * The `$from` and `$to` parameters specify the key names or property names to set up the map.
+     * Optionally, one can further group the map according to a grouping field `$group`.
+     *
+     * For example,
+     *
+     * ```php
+     * $array = [
+     *     ['id' => '123', 'name' => 'aaa', 'class' => 'x'],
+     *     ['id' => '124', 'name' => 'bbb', 'class' => 'x'],
+     *     ['id' => '345', 'name' => 'ccc', 'class' => 'y'],
+     * ];
+     *
+     * $result = ArrayHelper::map($array, 'id', 'name');
+     * // the result is:
+     * // [
+     * //     '123' => 'aaa',
+     * //     '124' => 'bbb',
+     * //     '345' => 'ccc',
+     * // ]
+     *
+     * $result = ArrayHelper::map($array, 'id', 'name', 'class');
+     * // the result is:
+     * // [
+     * //     'x' => [
+     * //         '123' => 'aaa',
+     * //         '124' => 'bbb',
+     * //     ],
+     * //     'y' => [
+     * //         '345' => 'ccc',
+     * //     ],
+     * // ]
+     * ```
+     *
+     * @param array $array
+     * @param string|\Closure $from
+     * @param string|\Closure $to
+     * @param string|\Closure $group
+     * @return array
+     */
+    public static function map($array, $from, $to, $group = null)
+    {
+        $result = [];
+        foreach ($array as $element) {
+            $key = static::getValue($element, $from);
+            $value = static::getValue($element, $to);
+            if ($group !== null) {
+                $result[static::getValue($element, $group)][$key] = $value;
+            } else {
+                $result[$key] = $value;
+            }
+        }
+        return $result;
+    }
+
+    /**
+     * Indexes and/or groups the array according to a specified key.
+     * The input should be either multidimensional array or an array of objects.
+     *
+     * The $key can be either a key name of the sub-array, a property name of object, or an anonymous
+     * function that must return the value that will be used as a key.
+     *
+     * $groups is an array of keys, that will be used to group the input array into one or more sub-arrays based
+     * on keys specified.
+     *
+     * If the `$key` is specified as `null` or a value of an element corresponding to the key is `null` in addition
+     * to `$groups` not specified then the element is discarded.
+     *
+     * For example:
+     *
+     * ```php
+     * $array = [
+     *     ['id' => '123', 'data' => 'abc', 'device' => 'laptop'],
+     *     ['id' => '345', 'data' => 'def', 'device' => 'tablet'],
+     *     ['id' => '345', 'data' => 'hgi', 'device' => 'smartphone'],
+     * ];
+     * $result = ArrayHelper::index($array, 'id');
+     * ```
+     *
+     * The result will be an associative array, where the key is the value of `id` attribute
+     *
+     * ```php
+     * [
+     *     '123' => ['id' => '123', 'data' => 'abc', 'device' => 'laptop'],
+     *     '345' => ['id' => '345', 'data' => 'hgi', 'device' => 'smartphone']
+     *     // The second element of an original array is overwritten by the last element because of the same id
+     * ]
+     * ```
+     *
+     * An anonymous function can be used in the grouping array as well.
+     *
+     * ```php
+     * $result = ArrayHelper::index($array, function ($element) {
+     *     return $element['id'];
+     * });
+     * ```
+     *
+     * Passing `id` as a third argument will group `$array` by `id`:
+     *
+     * ```php
+     * $result = ArrayHelper::index($array, null, 'id');
+     * ```
+     *
+     * The result will be a multidimensional array grouped by `id` on the first level, by `device` on the second level
+     * and indexed by `data` on the third level:
+     *
+     * ```php
+     * [
+     *     '123' => [
+     *         ['id' => '123', 'data' => 'abc', 'device' => 'laptop']
+     *     ],
+     *     '345' => [ // all elements with this index are present in the result array
+     *         ['id' => '345', 'data' => 'def', 'device' => 'tablet'],
+     *         ['id' => '345', 'data' => 'hgi', 'device' => 'smartphone'],
+     *     ]
+     * ]
+     * ```
+     *
+     * The anonymous function can be used in the array of grouping keys as well:
+     *
+     * ```php
+     * $result = ArrayHelper::index($array, 'data', [function ($element) {
+     *     return $element['id'];
+     * }, 'device']);
+     * ```
+     *
+     * The result will be a multidimensional array grouped by `id` on the first level, by the `device` on the second one
+     * and indexed by the `data` on the third level:
+     *
+     * ```php
+     * [
+     *     '123' => [
+     *         'laptop' => [
+     *             'abc' => ['id' => '123', 'data' => 'abc', 'device' => 'laptop']
+     *         ]
+     *     ],
+     *     '345' => [
+     *         'tablet' => [
+     *             'def' => ['id' => '345', 'data' => 'def', 'device' => 'tablet']
+     *         ],
+     *         'smartphone' => [
+     *             'hgi' => ['id' => '345', 'data' => 'hgi', 'device' => 'smartphone']
+     *         ]
+     *     ]
+     * ]
+     * ```
+     *
+     * @param array $array the array that needs to be indexed or grouped
+     * @param string|\Closure|null $key the column name or anonymous function which result will be used to index the array
+     * @param string|string[]|\Closure[]|null $groups the array of keys, that will be used to group the input array
+     * by one or more keys. If the $key attribute or its value for the particular element is null and $groups is not
+     * defined, the array element will be discarded. Otherwise, if $groups is specified, array element will be added
+     * to the result array without any key. This parameter is available since version 2.0.8.
+     * @return array the indexed and/or grouped array
+     */
+    public static function index($array, $key, $groups = [])
+    {
+        $result = [];
+        $groups = (array)$groups;
+        foreach ($array as $element) {
+            $lastArray = &$result;
+            foreach ($groups as $group) {
+                $value = static::getValue($element, $group);
+                if (!array_key_exists($value, $lastArray)) {
+                    $lastArray[$value] = [];
+                }
+                $lastArray = &$lastArray[$value];
+            }
+            if ($key === null) {
+                if (!empty($groups)) {
+                    $lastArray[] = $element;
+                }
+            } else {
+                $value = static::getValue($element, $key);
+                if ($value !== null) {
+                    if (is_float($value)) {
+                        $value = (string)$value;
+                    }
+                    $lastArray[$value] = $element;
+                }
+            }
+            unset($lastArray);
+        }
+        return $result;
+    }
+
+    /**
+     * Sort an array by values using a user-defined comparison function
+     * @link http://php.net/manual/en/function.usort.php
+     * @param array $array <p>
+     * The input array.
+     * </p>
+     * @param callback $callback <p>
+     * The comparison function must return an integer less than, equal to, or
+     * greater than zero if the first argument is considered to be
+     * respectively less than, equal to, or greater than the second.
+     * </p>
+     * @return array sorted array.
+     */
+    public static function userSort(array $array, callable $callback)
+    {
+        VariableHelper::initialize($array, []);
+        usort($array, $callback);
+        return $array;
+    }
+
+    /**
+     * Sort an array with a user-defined comparison function and maintain index association
+     * @link http://php.net/manual/en/function.uasort.php
+     * @param array $array <p>
+     * The input array.
+     * </p>
+     * @param callback $callback <p>
+     * See usort and uksort for
+     * examples of user-defined comparison functions.
+     * </p>
+     * @return array sorted array.
+     */
+    public static function userAssociativeSort(array $array, callable $callback)
+    {
+        VariableHelper::initialize($array, []);
+        uasort($array, $callback);
+        return $array;
+    }
+
+    /**
+     * Sort an array by keys using a user-defined comparison function
+     * @link http://php.net/manual/en/function.uksort.php
+     * @param array $array <p>
+     * The input array.
+     * </p>
+     * @param callback $callback <p>
+     * The callback comparison function.
+     * </p>
+     * <p>
+     * Function callback should accept two
+     * parameters which will be filled by pairs of array keys.
+     * The comparison function must return an integer less than, equal
+     * to, or greater than zero if the first argument is considered to
+     * be respectively less than, equal to, or greater than the
+     * second.
+     * </p>
+     * @return array sorted array.
+     */
+    public static function userKeySort(array $array, $callback)
+    {
+        VariableHelper::initialize($array, []);
+        uksort($array, $callback);
+        return $array;
+    }
+
+    /**
+     * Shuffle an array
+     * @link http://php.net/manual/en/function.shuffle.php
+     * @param array $array <p>
+     * The array.
+     * </p>
+     * @return array shuffled array.
+     */
+    public static function shuffle(array $array)
+    {
+        VariableHelper::initialize($array, []);
+        shuffle($array);
+        return $array;
+    }
+
+    /**
+     * Apply a user function to every member of an array
+     * @link http://php.net/manual/en/function.array-walk.php
+     * @param array|\ArrayObject $array <p>
+     * The input array.
+     * </p>
+     * @param callback $callback <p>
+     * Typically, callback takes on two parameters.
+     * The array parameter's value being the first, and
+     * the key/index second.
+     * </p>
+     * <p>
+     * If callback needs to be working with the
+     * actual values of the array, specify the first parameter of
+     * callback as a
+     * reference. Then,
+     * any changes made to those elements will be made in the
+     * original array itself.
+     * </p>
+     * <p>
+     * Users may not change the array itself from the
+     * callback function. e.g. Add/delete elements, unset elements, etc. If
+     * the array that array_walk is applied to is
+     * changed, the behavior of this function is undefined, and unpredictable.
+     * </p>
+     * @param mixed $userData [optional] <p>
+     * If the optional userData parameter is supplied,
+     * it will be passed as the third parameter to the callback
+     * funcname.
+     * </p>
+     * @return array modified array.
+     */
+    public static function walk(array $array, callable $callback, $userData = null)
+    {
+        VariableHelper::initialize($array, []);
+        if (is_object($array)) {
+            $array = clone $array;
+        }
+        array_walk($array, $callback, $userData);
+        return $array;
+    }
+
+    /**
+     * Apply a user function recursively to every member of an array
+     * @link http://php.net/manual/en/function.array-walk-recursive.php
+     * @param array|\ArrayObject $array <p>
+     * The input array.
+     * </p>
+     * @param callback $callback <p>
+     * Typically, callback takes on two parameters.
+     * The input parameter's value being the first, and
+     * the key/index second.
+     * </p>
+     * <p>
+     * If callback needs to be working with the
+     * actual values of the array, specify the first parameter of
+     * callback as a
+     * reference. Then,
+     * any changes made to those elements will be made in the
+     * original array itself.
+     * </p>
+     * @param mixed $userData [optional] <p>
+     * If the optional userData parameter is supplied,
+     * it will be passed as the third parameter to the callback
+     * funcname.
+     * </p>
+     * @return array modified array.
+     */
+    public static function walkRecursive(array $array, callable $callback, $userData = null)
+    {
+        VariableHelper::initialize($array, []);
+        if (is_object($array)) {
+            $array = clone $array;
+        }
+        array_walk_recursive($array, $callback, $userData);
+        return $array;
+    }
+
+    /**
+     * Sort an array
+     * @link http://php.net/manual/en/function.sort.php
+     * @param array $array <p>
+     * The input array.
+     * </p>
+     * @param int $sortFlags [optional] <p>
+     * The optional second parameter sort_flags
+     * may be used to modify the sorting behavior using these values:
+     * </p>
+     * <p>
+     * Sorting type flags:
+     * SORT_REGULAR - compare items normally
+     * (don't change types)
+     * @return array the sorted array.
+     */
+    public static function sort(array $array, $sortFlags = null)
+    {
+        VariableHelper::initialize($array, []);
+        sort($array, $sortFlags);
+        return $array;
+    }
+
+    /**
+     * Sort an array in reverse order
+     * @link http://php.net/manual/en/function.rsort.php
+     * @param array $array <p>
+     * The input array.
+     * </p>
+     * @param int $sortFlags [optional] <p>
+     * You may modify the behavior of the sort using the optional
+     * parameter sortFlags, for details see
+     * sort.
+     * </p>
+     * @return array the sorted array.
+     */
+    public static function reverseSort(array $array, $sortFlags = null)
+    {
+        VariableHelper::initialize($array, []);
+        rsort($array, $sortFlags);
+        return $array;
+    }
+
+    /**
+     * Sort an array and maintain index association
+     * @link http://php.net/manual/en/function.asort.php
+     * @param array $array <p>
+     * The input array.
+     * </p>
+     * @param int $sortFlags [optional] <p>
+     * You may modify the behavior of the sort using the optional
+     * parameter sortFlags, for details
+     * see sort.
+     * </p>
+     * @return array the sorted array.
+     */
+    public static function associativeSort(array $array, $sortFlags = null)
+    {
+        VariableHelper::initialize($array, []);
+        asort($array, $sortFlags);
+        return $array;
+    }
+
+    /**
+     * Sort an array in reverse order and maintain index association
+     * @link http://php.net/manual/en/function.arsort.php
+     * @param array $array <p>
+     * The input array.
+     * </p>
+     * @param int $sortFlags [optional] <p>
+     * You may modify the behavior of the sort using the optional
+     * parameter sortFlags, for details
+     * see sort.
+     * </p>
+     * @return array the sorted array.
+     */
+    public static function associativeReverseSort(array $array, $sortFlags = null)
+    {
+        VariableHelper::initialize($array, []);
+        arsort($array, $sortFlags);
+        return $array;
+    }
+
+    /**
+     * Sort an array by key
+     * @link http://php.net/manual/en/function.ksort.php
+     * @param array $array <p>
+     * The input array.
+     * </p>
+     * @param int $sortFlags [optional] <p>
+     * You may modify the behavior of the sort using the optional
+     * parameter sortFlags, for details
+     * see sort.
+     * </p>
+     * @return array the sorted array.
+     */
+    public static function keySort(array $array, $sortFlags = null)
+    {
+        VariableHelper::initialize($array, []);
+        ksort($array, $sortFlags);
+        return $array;
+    }
+
+    /**
+     * Sort an array by key in reverse order
+     * @link http://php.net/manual/en/function.krsort.php
+     * @param array $array <p>
+     * The input array.
+     * </p>
+     * @param int $sortFlags [optional] <p>
+     * You may modify the behavior of the sort using the optional parameter
+     * sortFlags, for details see
+     * sort.
+     * </p>
+     * @return array the sorted array.
+     */
+    public static function keyReverseSort(array $array, $sortFlags = null)
+    {
+        VariableHelper::initialize($array, []);
+        krsort($array, $sortFlags);
+        return $array;
+    }
+
+    /**
+     * Sort an array using a "natural order" algorithm
+     * @link http://php.net/manual/en/function.natsort.php
+     * @param array $array <p>
+     * The input array.
+     * </p>
+     * @return array the sorted array.
+     */
+    public static function naturalSort(array $array)
+    {
+        VariableHelper::initialize($array, []);
+        natsort($array);
+        return $array;
+    }
+
+    /**
+     * Sort an array using a case insensitive "natural order" algorithm
+     * @link http://php.net/manual/en/function.natcasesort.php
+     * @param array $array <p>
+     * The input array.
+     * </p>
+     * @return array the sorted array.
+     */
+    public static function naturalCaseInsensitiveSort(array $array)
+    {
+        VariableHelper::initialize($array, []);
+        natcasesort($array);
+        return $array;
+    }
+}

+ 22 - 0
src/helpers/HtmlHelper.php

@@ -0,0 +1,22 @@
+<?php
+
+namespace yiins\helpers;
+
+/**
+ * Created by PhpStorm.
+ * User: AlexLcDee
+ * Date: 06.02.2017
+ * Time: 19:52
+ */
+class HtmlHelper
+{
+    /**
+     * @param object|string $model
+     * @return string
+     */
+    public static function modelName($model)
+    {
+        $modelNameArr = is_object($model) ? explode('\\', get_class($model)) : explode('\\', $model);
+        return array_pop($modelNameArr);
+    }
+}

+ 131 - 0
src/helpers/StringsHelper.php

@@ -0,0 +1,131 @@
+<?php
+
+namespace yiins\helpers;
+
+
+use JBZoo\Utils\Slug;
+use JBZoo\Utils\Str;
+
+final class StringsHelper
+{
+    protected $string = '';
+
+    private function __construct($string)
+    {
+        $this->string = $string;
+    }
+
+    public static function crop($text, $maxCount = 200, $allowedTags = null, $ellipses = true)
+    {
+        $text = strip_tags($text, $allowedTags);
+        return Str::truncateSafe($text, $maxCount, $ellipses ? '...' : false);
+
+//        $helper = new self($text);
+//        $helper->stripTags($allowedTags);
+//        $helper->cropString($maxCount, $ellipses);
+//        return $helper->string;
+    }
+
+    public static function URLize($string)
+    {
+        $string = Slug::downcode($string, 'ru');
+        return Slug::filter($string);
+//        $helper = new self($string);
+//        $helper->stripTags();
+//        $helper->cropString(100);
+//        $helper->lower();
+//        $helper->dashes();
+//        $helper->transliterate();
+//        return $helper->string;
+    }
+
+    public static function ucWords($string)
+    {
+        $words = preg_split('/\ /ui', $string);
+        foreach ($words as &$word) {
+            $word = static::ucFirst($word);
+        }
+        return implode(' ', $words);
+    }
+
+    public static function ucFirst($string)
+    {
+        $firstLetter = mb_strtoupper(mb_substr(mb_strtolower($string), 0, 1));
+        return $firstLetter . mb_substr($string, 1);
+    }
+
+    public static function declensionOfNum($number, $titles)
+    {
+        $cases = array(2, 0, 1, 1, 1, 2);
+        return $titles[($number % 100 > 4 && $number % 100 < 20) ? 2 : $cases[min($number % 10, 5)]];
+    }
+
+    private function stripTags($allowedTags = null)
+    {
+        $this->string = strip_tags($this->string, $allowedTags);
+    }
+
+    private function cropString($maxCount, $ellipses = true)
+    {
+        if (mb_strlen($this->string) <= $maxCount) {
+            return;
+        }
+        $lastSpace = mb_strrpos(mb_substr($this->string, 0, $maxCount), ' ');
+        $strCrop = mb_substr($this->string, 0, $lastSpace);
+        if ($ellipses) {
+            $strCrop .= '...';
+        }
+        $this->string = $strCrop;
+        return;
+    }
+
+    private function lower()
+    {
+        $this->string = mb_strtolower($this->string);
+    }
+
+    private function dashes()
+    {
+        $this->string = preg_replace('/[^A-Za-z0-9А-Яа-я]+/ui', '-', $this->string);
+        $this->string = trim($this->string, '-');
+    }
+
+    private function transliterate()
+    {
+        $this->string = strtr($this->string, [
+            'а' => 'a',
+            'б' => 'b',
+            'в' => 'v',
+            'г' => 'g',
+            'д' => 'd',
+            'е' => 'e',
+            'ё' => 'e',
+            'ж' => 'zh',
+            'з' => 'z',
+            'и' => 'i',
+            'й' => 'y',
+            'к' => 'k',
+            'л' => 'l',
+            'м' => 'm',
+            'н' => 'n',
+            'о' => 'o',
+            'п' => 'p',
+            'р' => 'r',
+            'с' => 's',
+            'т' => 't',
+            'у' => 'u',
+            'ф' => 'f',
+            'х' => 'h',
+            'ц' => 'c',
+            'ч' => 'ch',
+            'ш' => 'sh',
+            'щ' => 'sch',
+            'ь' => '',
+            'ы' => 'y',
+            'ъ' => '',
+            'э' => 'e',
+            'ю' => 'yu',
+            'я' => 'ya',
+        ]);
+    }
+}

+ 38 - 0
src/helpers/VariableHelper.php

@@ -0,0 +1,38 @@
+<?php
+
+namespace yiins\helpers;
+
+
+use yiins\exceptions\InvalidConfigException;
+
+class VariableHelper
+{
+    /**
+     * Initializes variable with default value if it is null
+     * @param mixed $variable to be initialized
+     * @param mixed|callable $default default value for variable or callable for creation of it
+     */
+    public static function initialize(&$variable, $default = null)
+    {
+        if ($variable === null) {
+            if (is_callable($default)) {
+                $variable = call_user_func($default, $variable);
+            } else {
+                $variable = $default;
+            }
+        }
+    }
+
+    /**
+     * Checks if variable is null and throws InvalidConfigException if it is null
+     * @param mixed $variable variable to check
+     * @param string $message message to print if variable is null
+     * @throws InvalidConfigException
+     */
+    public static function required(&$variable, $message = null)
+    {
+        if ($variable === null) {
+            throw new InvalidConfigException($message ?? 'Required param not found.');
+        }
+    }
+}

+ 37 - 0
src/web/Action.php

@@ -0,0 +1,37 @@
+<?php
+/**
+ * Created by PhpStorm.
+ * User: AlexLcDee
+ * Date: 24.01.2017
+ * Time: 11:11
+ */
+
+namespace yiins\web;
+
+use Yii;
+
+class Action extends \CAction
+{
+    protected function runWithParamsInternal($object, $method, $params)
+    {
+        $ps = array();
+        foreach ($method->getParameters() as $i => $param) {
+            $name = $param->getName();
+            if (isset($params[$name])) {
+                if ($param->isArray()) {
+                    $ps[] = is_array($params[$name]) ? $params[$name] : array($params[$name]);
+                } elseif (!is_array($params[$name])) {
+                    $ps[] = $params[$name];
+                } else {
+                    return false;
+                }
+            } elseif ($param->isDefaultValueAvailable()) {
+                $ps[] = $param->getDefaultValue();
+            } else {
+                return false;
+            }
+        }
+        Yii::web()->response->result = $method->invokeArgs($object, $ps);
+        return true;
+    }
+}

+ 132 - 0
src/web/Application.php

@@ -0,0 +1,132 @@
+<?php
+
+
+namespace yiins\web;
+
+/**
+ * Description of Application
+ *
+ * @author Alexander Larkin <lcdee@andex.ru>
+ *
+ * @property Response $response
+ * @property UploadSaver $uploadSaver
+ */
+class Application extends \CWebApplication
+{
+
+    private $_module;
+
+    /**
+     * @return UploadSaver
+     */
+    public function getUploadSaver()
+    {
+        return $this->getComponent('uploadSaver');
+    }
+
+    /**
+     * Processes the current request.
+     * It first resolves the request into controller and action,
+     * and then creates the controller to perform the action.
+     */
+    public function processRequest()
+    {
+        parent::processRequest();
+        $this->getResponse()->send();
+    }
+
+    /**
+     * @return Response
+     */
+    public function getResponse()
+    {
+        return $this->getComponent('response');
+    }
+
+    public function init()
+    {
+        parent::init();
+        \Yii::setPathOfAlias('@base', dirname(dirname(__FILE__)));
+        // preload 'response'.
+        $this->getResponse();
+        $this->getClientScript()->scriptMap = [
+            'jquery.js'     => false,
+            'jquery.min.js' => false,
+        ];
+        $this->getClientScript()->defaultScriptFilePosition = \CClientScript::POS_END;
+        $this->getClientScript()->coreScriptPosition = \CClientScript::POS_END;
+    }
+
+    /**
+     * Creates the controller and performs the specified action.
+     * @param string $route the route of the current request. See {@link createController} for more details.
+     * @throws \CHttpException if the controller could not be created.
+     */
+    public function runController($route)
+    {
+        $module = $this->getModule();
+        if (($ca = $this->createController($route, $module)) !== null) {
+            list($controller, $actionID) = $ca;
+            $oldController = $this->getController();
+            $this->setController($controller);
+            $controller->init();
+            $controller->run($actionID);
+            $this->setController($oldController);
+        } else {
+            throw new \CHttpException(404, \Yii::t('yii', 'Unable to resolve the request "{route}".',
+                array('{route}' => $route === '' ? $this->defaultController : $route)));
+        }
+    }
+
+    /**
+     * Retrieves the named application module.
+     * The module has to be declared in {@link modules}. A new instance will be created
+     * when calling this method with the given ID for the first time.
+     * @param string $id application module ID (case-sensitive)
+     * @return \CModule the module instance, null if the module is disabled or does not exist.
+     */
+    public function getModule($id = '')
+    {
+        if ($id === '') {
+            if ($this->_module !== null) {
+                return $this->_module;
+            }
+            return $this;
+        }
+        return parent::getModule($id);
+    }
+
+    public function setModule(\CModule $module)
+    {
+        $this->_module = $module;
+    }
+
+    public function getController()
+    {
+        return parent::getController();
+    }
+
+    public function setController($value)
+    {
+        parent::setController($value);
+    }
+
+    protected function registerCoreComponents()
+    {
+        parent::registerCoreComponents();
+
+        $components = [
+            'request'     => [
+                'class' => Request::class,
+            ],
+            'response'    => [
+                'class' => Response::class,
+            ],
+            'uploadSaver' => [
+                'class' => UploadSaver::class,
+            ],
+        ];
+
+        $this->setComponents($components);
+    }
+}

Some files were not shown because too many files changed in this diff