diff options
| -rw-r--r-- | module/web/static/css/default/dashboard.less | 12 | ||||
| -rw-r--r-- | module/web/static/css/select2.css | 32 | ||||
| -rw-r--r-- | module/web/static/js/config.js | 2 | ||||
| -rw-r--r-- | module/web/static/js/libs/require-2.1.5.js (renamed from module/web/static/js/libs/require-2.1.2.js) | 96 | ||||
| -rw-r--r-- | module/web/static/js/libs/select2-3.3.1.js (renamed from module/web/static/js/libs/select2-3.2.js) | 601 | ||||
| -rw-r--r-- | module/web/static/js/views/filterView.js | 17 | ||||
| -rw-r--r-- | module/web/templates/default/base.html | 4 | 
7 files changed, 487 insertions, 277 deletions
| diff --git a/module/web/static/css/default/dashboard.less b/module/web/static/css/default/dashboard.less index e2cc739c6..2a4adf0e7 100644 --- a/module/web/static/css/default/dashboard.less +++ b/module/web/static/css/default/dashboard.less @@ -279,6 +279,18 @@ FANCY CHECKBOXES    Actionbar
  */
 +.form-search {
 +  position: relative;
 +
 +  .dropdown-menu {
 +    min-width: 100%;
 +    position: absolute;
 +    right: 0;
 +    left: auto;
 +  }
 +
 +}
 +
  li.finished > a, li.finished:hover > a {
    background-color: @green;
    color: @light;
 diff --git a/module/web/static/css/select2.css b/module/web/static/css/select2.css index d8b43d876..1ff2abb1b 100644 --- a/module/web/static/css/select2.css +++ b/module/web/static/css/select2.css @@ -1,5 +1,5 @@  /* -Version: @@ver@@ Timestamp: @@timestamp@@ +Version: 3.3.1 Timestamp: Wed Feb 20 09:57:22 PST 2013  */  .select2-container {      position: relative; @@ -48,6 +48,13 @@ Version: @@ver@@ Timestamp: @@timestamp@@         -moz-background-clip: padding;              background-clip: padding-box; +    -webkit-touch-callout: none; +      -webkit-user-select: none; +       -khtml-user-select: none; +         -moz-user-select: none; +          -ms-user-select: none; +              user-select: none; +      background-color: #fff;      background-image: -webkit-gradient(linear, left bottom, left top, color-stop(0, #eeeeee), color-stop(0.5, white));      background-image: -webkit-linear-gradient(center bottom, #eeeeee 0%, white 50%); @@ -133,7 +140,6 @@ Version: @@ver@@ Timestamp: @@timestamp@@      -webkit-box-shadow: 0 4px 5px rgba(0, 0, 0, .15);         -moz-box-shadow: 0 4px 5px rgba(0, 0, 0, .15); -         -o-box-shadow: 0 4px 5px rgba(0, 0, 0, .15);              box-shadow: 0 4px 5px rgba(0, 0, 0, .15);  } @@ -148,7 +154,6 @@ Version: @@ver@@ Timestamp: @@timestamp@@      -webkit-box-shadow: 0 -4px 5px rgba(0, 0, 0, .15);         -moz-box-shadow: 0 -4px 5px rgba(0, 0, 0, .15); -         -o-box-shadow: 0 -4px 5px rgba(0, 0, 0, .15);              box-shadow: 0 -4px 5px rgba(0, 0, 0, .15);  } @@ -256,16 +261,13 @@ Version: @@ver@@ Timestamp: @@timestamp@@      -webkit-box-shadow: 0 0 5px rgba(0,0,0,.3);         -moz-box-shadow: 0 0 5px rgba(0,0,0,.3); -         -o-box-shadow: 0 0 5px rgba(0,0,0,.3);              box-shadow: 0 0 5px rgba(0,0,0,.3);  }  .select2-dropdown-open .select2-choice { -    border: 1px solid #aaa;      border-bottom-color: transparent;      -webkit-box-shadow: 0 1px 0 #fff inset;         -moz-box-shadow: 0 1px 0 #fff inset; -         -o-box-shadow: 0 1px 0 #fff inset;              box-shadow: 0 1px 0 #fff inset;      -webkit-border-bottom-left-radius: 0; @@ -303,6 +305,7 @@ Version: @@ver@@ Timestamp: @@timestamp@@      position: relative;      overflow-x: hidden;      overflow-y: auto; +    -webkit-tap-highlight-color: rgba(0,0,0,0);  }  .select2-results ul.select2-result-sub { @@ -331,6 +334,13 @@ Version: @@ver@@ Timestamp: @@timestamp@@      padding: 3px 7px 4px;      margin: 0;      cursor: pointer; + +    -webkit-touch-callout: none; +      -webkit-user-select: none; +       -khtml-user-select: none; +         -moz-user-select: none; +          -ms-user-select: none; +              user-select: none;  }  .select2-results .select2-highlighted { @@ -444,7 +454,6 @@ disabled look for disabled choices in the results dropdown      -webkit-box-shadow: 0 0 5px rgba(0,0,0,.3);         -moz-box-shadow: 0 0 5px rgba(0,0,0,.3); -         -o-box-shadow: 0 0 5px rgba(0,0,0,.3);              box-shadow: 0 0 5px rgba(0,0,0,.3);  }  .select2-container-multi .select2-choices li { @@ -458,7 +467,6 @@ disabled look for disabled choices in the results dropdown  }  .select2-container-multi .select2-choices .select2-search-field input { -    height: 15px;      padding: 5px;      margin: 1px 0; @@ -469,7 +477,6 @@ disabled look for disabled choices in the results dropdown      border: 0;      -webkit-box-shadow: none;         -moz-box-shadow: none; -         -o-box-shadow: none;              box-shadow: none;      background: transparent !important;  } @@ -504,6 +511,13 @@ disabled look for disabled choices in the results dropdown         -moz-background-clip: padding;              background-clip: padding-box; +    -webkit-touch-callout: none; +      -webkit-user-select: none; +       -khtml-user-select: none; +         -moz-user-select: none; +          -ms-user-select: none; +              user-select: none; +      background-color: #e4e4e4;      filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#eeeeee', endColorstr='#f4f4f4', GradientType=0 );      background-image: -webkit-gradient(linear, 0% 0%, 0% 100%, color-stop(20%, #f4f4f4), color-stop(50%, #f0f0f0), color-stop(52%, #e8e8e8), color-stop(100%, #eeeeee)); diff --git a/module/web/static/js/config.js b/module/web/static/js/config.js index 86704740c..474a77dc3 100644 --- a/module/web/static/js/config.js +++ b/module/web/static/js/config.js @@ -10,7 +10,7 @@ require.config({          transit: "libs/jquery.transit-0.9.9",          animate: "libs/jquery.animate-enhanced-0.99",          omniwindow: "libs/jquery.omniwindow", -        select2: "libs/select2-3.2", +        select2: "libs/select2-3.3.1",          bootstrap: "libs/bootstrap-2.3",          underscore: "libs/lodash-1.0.1", diff --git a/module/web/static/js/libs/require-2.1.2.js b/module/web/static/js/libs/require-2.1.5.js index 0e7b81bc4..062516acd 100644 --- a/module/web/static/js/libs/require-2.1.2.js +++ b/module/web/static/js/libs/require-2.1.5.js @@ -1,18 +1,18 @@  /** vim: et:ts=4:sw=4:sts=4 - * @license RequireJS 2.1.2 Copyright (c) 2010-2012, The Dojo Foundation All Rights Reserved. + * @license RequireJS 2.1.5 Copyright (c) 2010-2012, The Dojo Foundation All Rights Reserved.   * Available via the MIT or new BSD license.   * see: http://github.com/jrburke/requirejs for details   */  //Not using strict: uneven strict support in browsers, #392, and causes  //problems with requirejs.exec()/transpiler plugins that may not be strict.  /*jslint regexp: true, nomen: true, sloppy: true */ -/*global window, navigator, document, importScripts, jQuery, setTimeout, opera */ +/*global window, navigator, document, importScripts, setTimeout, opera */  var requirejs, require, define;  (function (global) {      var req, s, head, baseElement, dataMain, src,          interactiveScript, currentlyAddingScript, mainScript, subPath, -        version = '2.1.2', +        version = '2.1.5',          commentRegExp = /(\/\*([\s\S]*?)\*\/|([^:]|^)\/\/(.*)$)/mg,          cjsRequireRegExp = /[^.]\s*require\s*\(\s*["']([^'"\s]+)["']\s*\)/g,          jsSuffixRegExp = /\.js$/, @@ -21,7 +21,6 @@ var requirejs, require, define;          ostring = op.toString,          hasOwn = op.hasOwnProperty,          ap = Array.prototype, -        aps = ap.slice,          apsp = ap.splice,          isBrowser = !!(typeof window !== 'undefined' && navigator && document),          isWebWorker = !isBrowser && typeof importScripts !== 'undefined', @@ -192,15 +191,21 @@ var requirejs, require, define;          var inCheckLoaded, Module, context, handlers,              checkLoadedTimeoutId,              config = { +                //Defaults. Do not set a default for map +                //config to speed up normalize(), which +                //will run faster if there is no default.                  waitSeconds: 7,                  baseUrl: './',                  paths: {},                  pkgs: {},                  shim: {}, -                map: {},                  config: {}              },              registry = {}, +            //registry of just enabled modules, to speed +            //cycle breaking code when lots of modules +            //are registered, but not activated. +            enabledRegistry = {},              undefEvents = {},              defQueue = [],              defined = {}, @@ -296,7 +301,7 @@ var requirejs, require, define;              }              //Apply map config if available. -            if (applyMap && (baseParts || starMap) && map) { +            if (applyMap && map && (baseParts || starMap)) {                  nameParts = name.split('/');                  for (i = nameParts.length; i > 0; i -= 1) { @@ -577,6 +582,7 @@ var requirejs, require, define;          function cleanRegistry(id) {              //Clean up machinery used for waiting modules.              delete registry[id]; +            delete enabledRegistry[id];          }          function breakCycle(mod, traced, processed) { @@ -625,7 +631,7 @@ var requirejs, require, define;              inCheckLoaded = true;              //Figure out the state of all the modules. -            eachProp(registry, function (mod) { +            eachProp(enabledRegistry, function (mod) {                  map = mod.map;                  modId = map.id; @@ -806,7 +812,7 @@ var requirejs, require, define;              },              /** -             * Checks is the module is ready to define itself, and if so, +             * Checks if the module is ready to define itself, and if so,               * define it.               */              check: function () { @@ -884,7 +890,7 @@ var requirejs, require, define;                          }                          //Clean up -                        delete registry[id]; +                        cleanRegistry(id);                          this.defined = true;                      } @@ -918,8 +924,7 @@ var requirejs, require, define;                          name = this.map.name,                          parentName = this.map.parentMap ? this.map.parentMap.name : null,                          localRequire = context.makeRequire(map.parentMap, { -                            enableBuildCallback: true, -                            skipMap: true +                            enableBuildCallback: true                          });                      //If current map is not normalized, wait for that @@ -1017,8 +1022,11 @@ var requirejs, require, define;                          try {                              req.exec(text);                          } catch (e) { -                            throw new Error('fromText eval for ' + moduleName + -                                            ' failed: ' + e); +                            return onError(makeError('fromtexteval', +                                             'fromText eval for ' + id + +                                            ' failed: ' + e, +                                             e, +                                             [id]));                          }                          if (hasInteractive) { @@ -1048,6 +1056,7 @@ var requirejs, require, define;              },              enable: function () { +                enabledRegistry[this.map.id] = this;                  this.enabled = true;                  //Set flag mentioning that the module is enabling, @@ -1207,6 +1216,7 @@ var requirejs, require, define;              Module: Module,              makeModuleMap: makeModuleMap,              nextTick: req.nextTick, +            onError: onError,              /**               * Set a configuration for the context. @@ -1233,6 +1243,9 @@ var requirejs, require, define;                  eachProp(cfg, function (value, prop) {                      if (objs[prop]) {                          if (prop === 'map') { +                            if (!config.map) { +                                config.map = {}; +                            }                              mixin(config[prop], value, true, true);                          } else {                              mixin(config[prop], value, true); @@ -1344,7 +1357,7 @@ var requirejs, require, define;                          //Synchronous access to one module. If require.get is                          //available (as in the Node adapter), prefer that.                          if (req.get) { -                            return req.get(context, deps, relMap); +                            return req.get(context, deps, relMap, localRequire);                          }                          //Normalize module name, if it contains . or .. @@ -1395,16 +1408,20 @@ var requirejs, require, define;                       * plain URLs like nameToUrl.                       */                      toUrl: function (moduleNamePlusExt) { -                        var index = moduleNamePlusExt.lastIndexOf('.'), -                            ext = null; - -                        if (index !== -1) { +                        var ext, +                            index = moduleNamePlusExt.lastIndexOf('.'), +                            segment = moduleNamePlusExt.split('/')[0], +                            isRelative = segment === '.' || segment === '..'; + +                        //Have a file extension alias, and it is not the +                        //dots from a relative path. +                        if (index !== -1 && (!isRelative || index > 1)) {                              ext = moduleNamePlusExt.substring(index, moduleNamePlusExt.length);                              moduleNamePlusExt = moduleNamePlusExt.substring(0, index);                          }                          return context.nameToUrl(normalize(moduleNamePlusExt, -                                                relMap && relMap.id, true), ext); +                                                relMap && relMap.id, true), ext,  true);                      },                      defined: function (id) { @@ -1449,10 +1466,11 @@ var requirejs, require, define;              /**               * Called to enable a module if it is still in the registry -             * awaiting enablement. parent module is passed in for context, -             * used by the optimizer. +             * awaiting enablement. A second arg, parent, the parent module, +             * is passed in for context, when this method is overriden by +             * the optimizer. Not shown here to keep code compact.               */ -            enable: function (depMap, parent) { +            enable: function (depMap) {                  var mod = getOwn(registry, depMap.id);                  if (mod) {                      getModule(depMap).enable(); @@ -1522,7 +1540,7 @@ var requirejs, require, define;               * it is assumed to have already been normalized. This is an               * internal API, not a public one. Use toUrl for the public API.               */ -            nameToUrl: function (moduleName, ext) { +            nameToUrl: function (moduleName, ext, skipExt) {                  var paths, pkgs, pkg, pkgPath, syms, i, parentModule, url,                      parentPath; @@ -1571,7 +1589,7 @@ var requirejs, require, define;                      //Join the path parts together, then figure out if baseUrl is needed.                      url = syms.join('/'); -                    url += (ext || (/\?/.test(url) ? '' : '.js')); +                    url += (ext || (/\?/.test(url) || skipExt ? '' : '.js'));                      url = (url.charAt(0) === '/' || url.match(/^[\w\+\.\-]+:/) ? '' : config.baseUrl) + url;                  } @@ -1810,7 +1828,7 @@ var requirejs, require, define;                  node.attachEvent('onreadystatechange', context.onScriptLoad);                  //It would be great to add an error handler here to catch                  //404s in IE9+. However, onreadystatechange will fire before -                //the error handler, so that does not help. If addEvenListener +                //the error handler, so that does not help. If addEventListener                  //is used, then IE will fire error before load, but we cannot                  //use that pathway given the connect.microsoft.com issue                  //mentioned above about not doing the 'script execute, @@ -1839,16 +1857,24 @@ var requirejs, require, define;              return node;          } else if (isWebWorker) { -            //In a web worker, use importScripts. This is not a very -            //efficient use of importScripts, importScripts will block until -            //its script is downloaded and evaluated. However, if web workers -            //are in play, the expectation that a build has been done so that -            //only one script needs to be loaded anyway. This may need to be -            //reevaluated if other use cases become common. -            importScripts(url); - -            //Account for anonymous modules -            context.completeLoad(moduleName); +            try { +                //In a web worker, use importScripts. This is not a very +                //efficient use of importScripts, importScripts will block until +                //its script is downloaded and evaluated. However, if web workers +                //are in play, the expectation that a build has been done so that +                //only one script needs to be loaded anyway. This may need to be +                //reevaluated if other use cases become common. +                importScripts(url); + +                //Account for anonymous modules +                context.completeLoad(moduleName); +            } catch (e) { +                context.onError(makeError('importscripts', +                                'importScripts failed for ' + +                                    moduleName + ' at ' + url, +                                e, +                                [moduleName])); +            }          }      }; diff --git a/module/web/static/js/libs/select2-3.2.js b/module/web/static/js/libs/select2-3.3.1.js index d41f090d7..8be2c7e9f 100644 --- a/module/web/static/js/libs/select2-3.2.js +++ b/module/web/static/js/libs/select2-3.3.1.js @@ -1,7 +1,7 @@  /*  Copyright 2012 Igor Vaynberg -Version: @@ver@@ Timestamp: @@timestamp@@ +Version: 3.3.1 Timestamp: Wed Feb 20 09:57:22 PST 2013  This software is licensed under the Apache License, Version 2.0 (the "Apache License") or the GNU  General Public License version 2 (the "GPL License"). You may choose either license to govern your @@ -103,7 +103,9 @@ the specific language governing permissions and limitations under the Apache Lic      function indexOf(value, array) {          var i = 0, l = array.length; -        for (; i < l; i = i + 1) if (value === array[i]) return i; +        for (; i < l; i = i + 1) { +            if (equal(value, array[i])) return i; +        }          return -1;      } @@ -113,7 +115,12 @@ the specific language governing permissions and limitations under the Apache Lic       * @param b       */      function equal(a, b) { -        return a===b; +        if (a === b) return true; +        if (a === undefined || b === undefined) return false; +        if (a === null || b === null) return false; +        if (a.constructor === String) return a === b+''; +        if (b.constructor === String) return b === a+''; +        return false;      }      /** @@ -211,6 +218,34 @@ the specific language governing permissions and limitations under the Apache Lic          });      } +    function focus($el) { +        if ($el[0] === document.activeElement) return; + +        /* set the focus in a 0 timeout - that way the focus is set after the processing +            of the current event has finished - which seems like the only reliable way +            to set focus */ +        window.setTimeout(function() { +            var el=$el[0], pos=$el.val().length, range; + +            $el.focus(); + +            /* after the focus is set move the caret to the end, necessary when we val() +                just before setting focus */ +            if(el.setSelectionRange) +            { +                el.setSelectionRange(pos, pos); +            } +            else if (el.createTextRange) { +                range = el.createTextRange(); +                range.collapse(true); +                range.moveEnd('character', pos); +                range.moveStart('character', pos); +                range.select(); +            } + +        }, 0); +    } +      function killEvent(event) {          event.preventDefault();          event.stopPropagation(); @@ -243,6 +278,32 @@ the specific language governing permissions and limitations under the Apache Lic          return sizer.width();      } +    function syncCssClasses(dest, src, adapter) { +        var classes, replacements = [], adapted; + +        classes = dest.attr("class"); +        if (typeof classes === "string") { +            $(classes.split(" ")).each2(function() { +                if (this.indexOf("select2-") === 0) { +                    replacements.push(this); +                } +            }); +        } +        classes = src.attr("class"); +        if (typeof classes === "string") { +            $(classes.split(" ")).each2(function() { +                if (this.indexOf("select2-") !== 0) { +                    adapted = adapter(this); +                    if (typeof adapted === "string" && adapted.length > 0) { +                        replacements.push(this); +                    } +                } +            }); +        } +        dest.attr("class", replacements.join(" ")); +    } + +      function markMatch(text, term, markup, escapeMarkup) {          var match=text.toUpperCase().indexOf(term.toUpperCase()),              tl=term.length; @@ -279,7 +340,9 @@ the specific language governing permissions and limitations under the Apache Lic          var timeout, // current scheduled but not yet executed request              requestSequence = 0, // sequence used to drop out-of-order responses              handler = null, -            quietMillis = options.quietMillis || 100; +            quietMillis = options.quietMillis || 100, +            ajaxUrl = options.url, +            self = this;          return function (query) {              window.clearTimeout(timeout); @@ -287,31 +350,40 @@ the specific language governing permissions and limitations under the Apache Lic                  requestSequence += 1; // increment the sequence                  var requestNumber = requestSequence, // this request's sequence number                      data = options.data, // ajax data function -                    url = options.url, // ajax url string or function +                    url = ajaxUrl, // ajax url string or function                      transport = options.transport || $.ajax, -                    traditional = options.traditional || false, -                    type = options.type || 'GET'; // set type of request (GET or POST) +                    type = options.type || 'GET', // set type of request (GET or POST) +                    params = {}; -                data = data ? data.call(this, query.term, query.page, query.context) : null; -                url = (typeof url === 'function') ? url.call(this, query.term, query.page, query.context) : url; +                data = data ? data.call(self, query.term, query.page, query.context) : null; +                url = (typeof url === 'function') ? url.call(self, query.term, query.page, query.context) : url;                  if( null !== handler) { handler.abort(); } -                handler = transport.call(null, { +                if (options.params) { +                    if ($.isFunction(options.params)) { +                        $.extend(params, options.params.call(self)); +                    } else { +                        $.extend(params, options.params); +                    } +                } + +                $.extend(params, {                      url: url,                      dataType: options.dataType,                      data: data,                      type: type, -                    traditional: traditional, +                    cache: false,                      success: function (data) {                          if (requestNumber < requestSequence) {                              return;                          } -                        // TODO 3.0 - replace query.page with query so users have access to term, page, etc. +                        // TODO - replace query.page with query so users have access to term, page, etc.                          var results = options.results(data, query.page);                          query.callback(results);                      }                  }); +                handler = transport.call(self, params);              }, quietMillis);          };      } @@ -333,22 +405,33 @@ the specific language governing permissions and limitations under the Apache Lic      function local(options) {          var data = options, // data elements              dataText, +            tmp,              text = function (item) { return ""+item.text; }; // function used to retrieve the text portion of a data item that is matched against the search -        if (!$.isArray(data)) { -            text = data.text; +		 if ($.isArray(data)) { +            tmp = data; +            data = { results: tmp }; +        } + +		 if ($.isFunction(data) === false) { +            tmp = data; +            data = function() { return tmp; }; +        } + +        var dataItem = data(); +        if (dataItem.text) { +            text = dataItem.text;              // if text is not a function we assume it to be a key name              if (!$.isFunction(text)) { -              dataText = data.text; // we need to store this in a separate variable because in the next step data gets reset and data.text is no longer available -              text = function (item) { return item[dataText]; }; +                dataText = data.text; // we need to store this in a separate variable because in the next step data gets reset and data.text is no longer available +                text = function (item) { return item[dataText]; };              } -            data = data.results;          }          return function (query) {              var t = query.term, filtered = { results: [] }, process;              if (t === "") { -                query.callback({results: data}); +                query.callback(data());                  return;              } @@ -372,7 +455,7 @@ the specific language governing permissions and limitations under the Apache Lic                  }              }; -            $(data).each2(function(i, datum) { process(datum, filtered.results); }); +            $(data().results).each2(function(i, datum) { process(datum, filtered.results); });              query.callback(filtered);          };      } @@ -526,28 +609,7 @@ the specific language governing permissions and limitations under the Apache Lic              // cache the body so future lookups are cheap              this.body = thunk(function() { return opts.element.closest("body"); }); -            // create the dropdown mask if doesnt already exist -            mask = $("#select2-drop-mask"); -            if (mask.length == 0) { -                mask = $(document.createElement("div")); -                mask.attr("id","select2-drop-mask").attr("class","select2-drop-mask"); -                mask.hide(); -                mask.appendTo(this.body()); -                mask.bind("mousedown touchstart", function (e) { -                    var dropdown = $("#select2-drop"), self; -                    if (dropdown.length > 0) { -                        self=dropdown.data("select2"); -                        if (self.opts.selectOnBlur) { -                            self.selectHighlighted({noFocus: true}); -                        } -                        self.close(); -                    } -                }); -            } - -            if (opts.element.attr("class") !== undefined) { -                this.container.addClass(opts.element.attr("class").replace(/validate\[[\S ]+] ?/, '')); -            } +            syncCssClasses(this.container, this.opts.element, this.opts.adaptContainerCssClass);              this.container.css(evaluate(opts.containerCss));              this.container.addClass(evaluate(opts.containerCssClass)); @@ -558,7 +620,7 @@ the specific language governing permissions and limitations under the Apache Lic              this.opts.element                  .data("select2", this)                  .addClass("select2-offscreen") -                .bind("focus.select2", function() { $(this).select2("focus")}) +                .bind("focus.select2", function() { $(this).select2("focus"); })                  .attr("tabIndex", "-1")                  .before(this.container);              this.container.data("select2", this); @@ -577,10 +639,9 @@ the specific language governing permissions and limitations under the Apache Lic              // initialize the container              this.initContainer(); -            this.initContainerWidth();              installFilteredMouseMove(this.results); -            this.dropdown.delegate(resultsSelector, "mousemove-filtered", this.bind(this.highlightUnderEvent)); +            this.dropdown.delegate(resultsSelector, "mousemove-filtered touchstart touchmove touchend", this.bind(this.highlightUnderEvent));              installDebouncedScroll(80, this.results);              this.dropdown.delegate(resultsSelector, "scroll-debounced", this.bind(this.loadMoreIfNeeded)); @@ -600,18 +661,15 @@ the specific language governing permissions and limitations under the Apache Lic              }              installKeyUpChangeEvent(search); -            search.bind("keyup-change", this.bind(this.updateResults)); -            search.bind("focus", function () { search.addClass("select2-focused"); if (search.val() === " ") search.val(""); }); +            search.bind("keyup-change input paste", this.bind(this.updateResults)); +            search.bind("focus", function () { search.addClass("select2-focused"); });              search.bind("blur", function () { search.removeClass("select2-focused");});              this.dropdown.delegate(resultsSelector, "mouseup", this.bind(function (e) {                  if ($(e.target).closest(".select2-result-selectable").length > 0) {                      this.highlightUnderEvent(e);                      this.selectHighlighted(e); -                } else { -                    this.focusSearch();                  } -                killEvent(e);              }));              // trap all mouse events from leaving the dropdown. sometimes there may be a modal that is listening @@ -642,9 +700,10 @@ the specific language governing permissions and limitations under the Apache Lic                  select2.container.remove();                  select2.dropdown.remove();                  select2.opts.element +                    .removeClass("select2-offscreen")                      .removeData("select2")                      .unbind(".select2") -                    .attr("tabIndex", this.elementTabIndex) +                    .attr({"tabIndex": this.elementTabIndex})                      .show();              }          }, @@ -698,7 +757,7 @@ the specific language governing permissions and limitations under the Apache Lic                              label=$(document.createElement("div"));                              label.addClass("select2-result-label"); -                            formatted=opts.formatResult(result, label, query); +                            formatted=opts.formatResult(result, label, query, self.opts.escapeMarkup);                              if (formatted!==undefined) {                                  label.html(formatted);                              } @@ -771,7 +830,7 @@ the specific language governing permissions and limitations under the Apache Lic                  });                  // this is needed because inside val() we construct choices from options and there id is hardcoded                  opts.id=function(e) { return e.id; }; -                opts.formatResultCssClass = function(data) { return data.css; } +                opts.formatResultCssClass = function(data) { return data.css; };              } else {                  if (!("query" in opts)) { @@ -780,7 +839,7 @@ the specific language governing permissions and limitations under the Apache Lic                          if (ajaxUrl && ajaxUrl.length > 0) {                              opts.ajax.url = ajaxUrl;                          } -                        opts.query = ajax(opts.ajax); +                        opts.query = ajax.call(opts.element, opts.ajax);                      } else if ("data" in opts) {                          opts.query = local(opts.data);                      } else if ("tags" in opts) { @@ -788,17 +847,19 @@ the specific language governing permissions and limitations under the Apache Lic                          if (opts.createSearchChoice === undefined) {                              opts.createSearchChoice = function (term) { return {id: term, text: term}; };                          } -                        opts.initSelection = function (element, callback) { -                            var data = []; -                            $(splitVal(element.val(), opts.separator)).each(function () { -                                var id = this, text = this, tags=opts.tags; -                                if ($.isFunction(tags)) tags=tags(); -                                $(tags).each(function() { if (equal(this.id, id)) { text = this.text; return false; } }); -                                data.push({id: id, text: text}); -                            }); - -                            callback(data); -                        }; +                        if (opts.initSelection === undefined) { +                            opts.initSelection = function (element, callback) { +                                var data = []; +                                $(splitVal(element.val(), opts.separator)).each(function () { +                                    var id = this, text = this, tags=opts.tags; +                                    if ($.isFunction(tags)) tags=tags(); +                                    $(tags).each(function() { if (equal(this.id, id)) { text = this.text; return false; } }); +                                    data.push({id: id, text: text}); +                                }); + +                                callback(data); +                            }; +                        }                      }                  }              } @@ -823,8 +884,13 @@ the specific language governing permissions and limitations under the Apache Lic              }));              sync = this.bind(function () { -                var enabled = this.opts.element.attr("disabled") !== "disabled"; -                var readonly = this.opts.element.attr("readonly") === "readonly"; + +                var enabled, readonly, self = this; + +                // sync enabled state + +                enabled = this.opts.element.attr("disabled") !== "disabled"; +                readonly = this.opts.element.attr("readonly") === "readonly";                  enabled = enabled && !readonly; @@ -835,6 +901,14 @@ the specific language governing permissions and limitations under the Apache Lic                          this.disable();                      }                  } + + +                syncCssClasses(this.container, this.opts.element, this.opts.adaptContainerCssClass); +                this.container.addClass(evaluate(this.opts.containerCssClass)); + +                syncCssClasses(this.dropdown, this.opts.element, this.opts.adaptDropdownCssClass); +                this.dropdown.addClass(evaluate(this.opts.dropdownCssClass)); +              });              // mozilla and IE @@ -903,21 +977,21 @@ the specific language governing permissions and limitations under the Apache Lic                  height = this.container.outerHeight(false),                  width = this.container.outerWidth(false),                  dropHeight = this.dropdown.outerHeight(false), -	        viewPortRight = $(window).scrollLeft() + document.documentElement.clientWidth, -                viewportBottom = $(window).scrollTop() + document.documentElement.clientHeight, +	            viewPortRight = $(window).scrollLeft() + $(window).width(), +                viewportBottom = $(window).scrollTop() + $(window).height(),                  dropTop = offset.top + height,                  dropLeft = offset.left,                  enoughRoomBelow = dropTop + dropHeight <= viewportBottom,                  enoughRoomAbove = (offset.top - dropHeight) >= this.body().scrollTop(), -	        dropWidth = this.dropdown.outerWidth(false), -	        enoughRoomOnRight = dropLeft + dropWidth <= viewPortRight, +	            dropWidth = this.dropdown.outerWidth(false), +	            enoughRoomOnRight = dropLeft + dropWidth <= viewPortRight,                  aboveNow = this.dropdown.hasClass("select2-drop-above"),                  bodyOffset,                  above,                  css; -            // console.log("below/ droptop:", dropTop, "dropHeight", dropHeight, "sum", (dropTop+dropHeight)+" viewport bottom", viewportBottom, "enough?", enoughRoomBelow); -            // console.log("above/ offset.top", offset.top, "dropHeight", dropHeight, "top", (offset.top-dropHeight), "scrollTop", this.body().scrollTop(), "enough?", enoughRoomAbove); +            //console.log("below/ droptop:", dropTop, "dropHeight", dropHeight, "sum", (dropTop+dropHeight)+" viewport bottom", viewportBottom, "enough?", enoughRoomBelow); +            //console.log("above/ offset.top", offset.top, "dropHeight", dropHeight, "top", (offset.top-dropHeight), "scrollTop", this.body().scrollTop(), "enough?", enoughRoomAbove);              // fix positioning when body has an offset and is not position: static @@ -937,9 +1011,9 @@ the specific language governing permissions and limitations under the Apache Lic                  if (!enoughRoomBelow && enoughRoomAbove) above = true;              } -	    if (!enoughRoomOnRight) { -		   dropLeft = offset.left + width - dropWidth; -	    } +            if (!enoughRoomOnRight) { +               dropLeft = offset.left + width - dropWidth; +            }              if (above) {                  dropTop = offset.top - dropHeight; @@ -966,7 +1040,7 @@ the specific language governing permissions and limitations under the Apache Lic              if (this.opened()) return false; -            event = $.Event("open"); +            event = $.Event("opening");              this.opts.element.trigger(event);              return !event.isDefaultPrevented();          }, @@ -1007,8 +1081,6 @@ the specific language governing permissions and limitations under the Apache Lic              this.clearDropdownAlignmentPreference(); -            if (this.search.val() === " ") { this.search.val(""); } -              this.container.addClass("select2-dropdown-open").addClass("select2-container-active"); @@ -1018,7 +1090,24 @@ the specific language governing permissions and limitations under the Apache Lic              this.updateResults(true); +            // create the dropdown mask if doesnt already exist              mask = $("#select2-drop-mask"); +            if (mask.length == 0) { +                mask = $(document.createElement("div")); +                mask.attr("id","select2-drop-mask").attr("class","select2-drop-mask"); +                mask.hide(); +                mask.appendTo(this.body()); +                mask.bind("mousedown touchstart", function (e) { +                    var dropdown = $("#select2-drop"), self; +                    if (dropdown.length > 0) { +                        self=dropdown.data("select2"); +                        if (self.opts.selectOnBlur) { +                            self.selectHighlighted({noFocus: true}); +                        } +                        self.close(); +                    } +                }); +            }              // ensure the mask is always right before the dropdown              if (this.dropdown.prev()[0] !== mask[0]) { @@ -1042,12 +1131,13 @@ the specific language governing permissions and limitations under the Apache Lic              // attach listeners to events that can change the position of the container and thus require              // the position of the dropdown to be updated as well so it does not come unglued from the container +            var that = this;              this.container.parents().add(window).each(function () {                  $(this).bind(resize+" "+scroll+" "+orient, function (e) {                      $("#select2-drop-mask").css({                          width:document.documentElement.scrollWidth,                          height:document.documentElement.scrollHeight}); -                    $("#select2-drop").data("select2").positionDropdown(); +                    that.positionDropdown();                  });              }); @@ -1071,7 +1161,7 @@ the specific language governing permissions and limitations under the Apache Lic              $("#select2-drop-mask").hide();              this.dropdown.removeAttr("id"); // only the active dropdown has the select2-drop id              this.dropdown.hide(); -            this.container.removeClass("select2-dropdown-open").removeClass("select2-container-active"); +            this.container.removeClass("select2-dropdown-open");              this.results.empty();              this.clearSearch(); @@ -1083,6 +1173,11 @@ the specific language governing permissions and limitations under the Apache Lic          }, +        //abstract +        getMaximumSelectionSize: function() { +            return evaluate(this.opts.maximumSelectionSize); +        }, +          // abstract          ensureHighlightVisible: function () {              var results = this.results, children, index, child, hb, rb, y, more; @@ -1150,7 +1245,9 @@ the specific language governing permissions and limitations under the Apache Lic          // abstract          highlight: function (index) { -            var choices = this.findHighlightableChoices(); +            var choices = this.findHighlightableChoices(), +                choice, +                data;              if (arguments.length === 0) {                  return indexOf(choices.filter(".select2-highlighted")[0], choices.get()); @@ -1161,8 +1258,15 @@ the specific language governing permissions and limitations under the Apache Lic              this.results.find(".select2-highlighted").removeClass("select2-highlighted"); -            $(choices[index]).addClass("select2-highlighted"); +            choice = $(choices[index]); +            choice.addClass("select2-highlighted"); +              this.ensureHighlightVisible(); + +            data = choice.data("select2-data"); +            if (data) { +                this.opts.element.trigger({ type: "highlight", val: this.id(data), choice: data }); +            }          },          // abstract @@ -1199,6 +1303,7 @@ the specific language governing permissions and limitations under the Apache Lic              if (below <= this.opts.loadMorePadding) {                  more.addClass("select2-active");                  this.opts.query({ +                        element: this.opts.element,                          term: term,                          page: page,                          context: context, @@ -1219,6 +1324,7 @@ the specific language governing permissions and limitations under the Apache Lic                      }                      self.positionDropdown();                      self.resultsPage = page; +                    self.context = data.context;                  })});              }          }, @@ -1255,10 +1361,11 @@ the specific language governing permissions and limitations under the Apache Lic                  postRender();              } -            if (opts.maximumSelectionSize >=1) { +            var maxSelSize = this.getMaximumSelectionSize(); +            if (maxSelSize >=1) {                  data = this.data(); -                if ($.isArray(data) && data.length >= opts.maximumSelectionSize && checkFormatter(opts.formatSelectionTooBig, "formatSelectionTooBig")) { -            	    render("<li class='select2-selection-limit'>" + opts.formatSelectionTooBig(opts.maximumSelectionSize) + "</li>"); +                if ($.isArray(data) && data.length >= maxSelSize && checkFormatter(opts.formatSelectionTooBig, "formatSelectionTooBig")) { +            	    render("<li class='select2-selection-limit'>" + opts.formatSelectionTooBig(maxSelSize) + "</li>");              	    return;                  }              } @@ -1291,7 +1398,9 @@ the specific language governing permissions and limitations under the Apache Lic              }              this.resultsPage = 1; +              opts.query({ +                element: opts.element,                      term: search.val(),                      page: this.resultsPage,                      context: null, @@ -1304,7 +1413,6 @@ the specific language governing permissions and limitations under the Apache Lic                  // save context, if any                  this.context = (data.context===undefined) ? null : data.context; -                  // create a default choice and prepend it to the list                  if (this.opts.createSearchChoice && search.val() !== "") {                      def = this.opts.createSearchChoice.call(null, search.val(), data.results); @@ -1350,28 +1458,15 @@ the specific language governing permissions and limitations under the Apache Lic              this.close();              this.container.removeClass("select2-container-active"); -            this.dropdown.removeClass("select2-drop-active");              // synonymous to .is(':focus'), which is available in jquery >= 1.6              if (this.search[0] === document.activeElement) { this.search.blur(); }              this.clearSearch();              this.selection.find(".select2-search-choice-focus").removeClass("select2-search-choice-focus"); -            this.opts.element.triggerHandler("blur");          },          // abstract          focusSearch: function () { -            // need to do it here as well as in timeout so it works in IE -            this.search.show(); -            this.search.focus(); - -            /* we do this in a timeout so that current event processing can complete before this code is executed. -             this makes sure the search field is focussed even if the current event would blur it */ -            window.setTimeout(this.bind(function () { -                // reset the value so IE places the cursor at the end of the input box -                this.search.show(); -                this.search.focus(); -                this.search.val(this.search.val()); -            }), 10); +            focus(this.search);          },          // abstract @@ -1388,14 +1483,6 @@ the specific language governing permissions and limitations under the Apache Lic          // abstract          getPlaceholder: function () { - -            // if a placeholder is specified on a select without the first empty option ignore it -            if (this.select) { -               if (this.select.find("option").first().text() !== "") { -                   return undefined; -               } -            } -              return this.opts.element.attr("placeholder") ||                  this.opts.element.attr("data-placeholder") || // jquery 1.4 compat                  this.opts.element.data("placeholder") || @@ -1450,7 +1537,7 @@ the specific language governing permissions and limitations under the Apache Lic              var width = resolveContainerWidth.call(this);              if (width !== null) { -                this.container.attr("style", "width: "+width); +                this.container.css("width", width);              }          }      }); @@ -1463,11 +1550,12 @@ the specific language governing permissions and limitations under the Apache Lic              var container = $(document.createElement("div")).attr({                  "class": "select2-container"              }).html([ -                "    <a href='javascript:void(0)' onclick='return false;' class='select2-choice'>", +                "<a href='javascript:void(0)' onclick='return false;' class='select2-choice' tabindex='-1'>",                  "   <span></span><abbr class='select2-search-choice-close' style='display:none;'></abbr>",                  "   <div><b></b></div>" ,                  "</a>", -                "    <div class='select2-drop select2-offscreen'>" , +                "<input class='select2-focusser select2-offscreen' type='text'/>", +                "<div class='select2-drop' style='display:none'>" ,                  "   <div class='select2-search'>" ,                  "       <input type='text' autocomplete='off' class='select2-input'/>" ,                  "   </div>" , @@ -1478,34 +1566,59 @@ the specific language governing permissions and limitations under the Apache Lic          },          // single +        disable: function() { +            if (!this.enabled) return; + +            this.parent.disable.apply(this, arguments); + +            this.focusser.attr("disabled", "disabled"); +        }, + +        // single +        enable: function() { +            if (this.enabled) return; + +            this.parent.enable.apply(this, arguments); + +            this.focusser.removeAttr("disabled"); +        }, + +        // single          opening: function () { -            this.search.show();              this.parent.opening.apply(this, arguments); -            this.dropdown.removeClass("select2-offscreen"); +            this.focusser.attr("disabled", "disabled"); + +            this.opts.element.trigger($.Event("open"));          },          // single          close: function () {              if (!this.opened()) return;              this.parent.close.apply(this, arguments); -            this.dropdown.removeAttr("style").addClass("select2-offscreen").insertAfter(this.selection).show(); +            this.focusser.removeAttr("disabled"); +            focus(this.focusser);          },          // single          focus: function () { -            this.close(); -            this.selection.focus(); +            if (this.opened()) { +                this.close(); +            } else { +                this.focusser.removeAttr("disabled"); +                this.focusser.focus(); +            }          },          // single          isFocused: function () { -            return this.selection[0] === document.activeElement; +            return this.container.hasClass("select2-container-active");          },          // single          cancel: function () {              this.parent.cancel.apply(this, arguments); -            this.selection.focus(); +            this.focusser.removeAttr("disabled"); +            this.focusser.focus();          },          // single @@ -1516,8 +1629,12 @@ the specific language governing permissions and limitations under the Apache Lic                  dropdown = this.dropdown,                  clickingInside = false; +            this.showSearch(this.opts.minimumResultsForSearch >= 0); +              this.selection = selection = container.find(".select2-choice"); +            this.focusser = container.find(".select2-focusser"); +              this.search.bind("keydown", this.bind(function (e) {                  if (!this.enabled) return; @@ -1527,56 +1644,62 @@ the specific language governing permissions and limitations under the Apache Lic                      return;                  } -                if (this.opened()) { -                    switch (e.which) { -                        case KEY.UP: -                        case KEY.DOWN: -                            this.moveHighlight((e.which === KEY.UP) ? -1 : 1); -                            killEvent(e); -                            return; -                        case KEY.TAB: -                        case KEY.ENTER: -                            this.selectHighlighted(); -                            killEvent(e); -                            return; -                        case KEY.ESC: -                            this.cancel(e); -                            killEvent(e); -                            return; -                    } -                } else { - -                    if (e.which === KEY.TAB || KEY.isControl(e) || KEY.isFunctionKey(e) || e.which === KEY.ESC) { +                switch (e.which) { +                    case KEY.UP: +                    case KEY.DOWN: +                        this.moveHighlight((e.which === KEY.UP) ? -1 : 1); +                        killEvent(e);                          return; -                    } - -                    if (this.opts.openOnEnter === false && e.which === KEY.ENTER) { +                    case KEY.TAB: +                    case KEY.ENTER: +                        this.selectHighlighted(); +                        killEvent(e);                          return; -                    } +                    case KEY.ESC: +                        this.cancel(e); +                        killEvent(e); +                        return; +                } +            })); + +            this.focusser.bind("keydown", this.bind(function (e) { +                if (!this.enabled) return; + +                if (e.which === KEY.TAB || KEY.isControl(e) || KEY.isFunctionKey(e) || e.which === KEY.ESC) { +                    return; +                } +                if (this.opts.openOnEnter === false && e.which === KEY.ENTER) { +                    killEvent(e); +                    return; +                } + +                if (e.which == KEY.DOWN || e.which == KEY.UP +                    || (e.which == KEY.ENTER && this.opts.openOnEnter)) {                      this.open(); +                    killEvent(e); +                    return; +                } -                    if (e.which === KEY.ENTER) { -                        // do not propagate the event otherwise we open, and propagate enter which closes -                        return; +                if (e.which == KEY.DELETE || e.which == KEY.BACKSPACE) { +                    if (this.opts.allowClear) { +                        this.clear();                      } +                    killEvent(e); +                    return;                  }              })); -            this.search.bind("focus", this.bind(function() { -                this.selection.attr("tabIndex", "-1"); -            })); -            this.search.bind("blur", this.bind(function() { -                if (!this.opened()) this.container.removeClass("select2-container-active"); -                window.setTimeout(this.bind(function() { -                    // restore original tab index -                    var ti=this.elementTabIndex || 0; -                    if (ti) { -                        this.selection.attr("tabIndex", ti); -                    } else { -                        this.selection.removeAttr("tabIndex"); -                    } -                }), 10); + +            installKeyUpChangeEvent(this.focusser); +            this.focusser.bind("keyup-change input", this.bind(function(e) { +                if (this.opened()) return; +                this.open(); +                if (this.showSearchInput !== false) { +                    this.search.val(this.focusser.val()); +                } +                this.focusser.val(""); +                killEvent(e);              }));              selection.delegate("abbr", "mousedown", this.bind(function (e) { @@ -1584,7 +1707,6 @@ the specific language governing permissions and limitations under the Apache Lic                  this.clear();                  killEventImmediately(e);                  this.close(); -                this.triggerChange();                  this.selection.focus();              })); @@ -1593,69 +1715,47 @@ the specific language governing permissions and limitations under the Apache Lic                  if (this.opened()) {                      this.close(); -                    this.selection.focus();                  } else if (this.enabled) {                      this.open();                  } +                killEvent(e); +                  clickingInside = false;              }));              dropdown.bind("mousedown", this.bind(function() { this.search.focus(); })); -            selection.bind("focus", this.bind(function() { -                this.container.addClass("select2-container-active"); -                // hide the search so the tab key does not focus on it -                this.search.attr("tabIndex", "-1"); +            selection.bind("focus", this.bind(function(e) { +                killEvent(e);              })); -            selection.bind("blur", this.bind(function() { +            this.focusser.bind("focus", this.bind(function(){ +                this.container.addClass("select2-container-active"); +            })).bind("blur", this.bind(function() {                  if (!this.opened()) {                      this.container.removeClass("select2-container-active");                  } -                window.setTimeout(this.bind(function() { this.search.attr("tabIndex", this.elementTabIndex || 0); }), 10); -            })); - -            selection.bind("keydown", this.bind(function(e) { -                if (!this.enabled) return; - -                if (e.which == KEY.DOWN || e.which == KEY.UP -                    || (e.which == KEY.ENTER && this.opts.openOnEnter)) { -                    this.open(); -                    killEvent(e); -                    return; -                } - -                if (e.which == KEY.DELETE || e.which == KEY.BACKSPACE) { -                    if (this.opts.allowClear) { -                        this.clear(); -                    } -                    killEvent(e); -                    return; -                } -            })); -            selection.bind("keypress", this.bind(function(e) { -		if (e.which == KEY.DELETE || e.which == KEY.BACKSPACE || e.which == KEY.TAB || e.which == KEY.ENTER || e.which == 0) { -			return -		} -                var key = String.fromCharCode(e.which); -                this.search.val(key); -                this.open();              })); +            this.search.bind("focus", this.bind(function(){ +                this.container.addClass("select2-container-active"); +            })) +            this.initContainerWidth();              this.setPlaceholder(); -            this.search.bind("focus", this.bind(function() { -                this.container.addClass("select2-container-active"); -            }));          },          // single          clear: function() { +            var data=this.selection.data("select2-data");              this.opts.element.val("");              this.selection.find("span").empty();              this.selection.removeData("select2-data");              this.setPlaceholder(); + +            this.opts.element.trigger({ type: "removed", val: this.id(data), choice: data }); +            this.triggerChange({removed:data});          },          /** @@ -1711,6 +1811,18 @@ the specific language governing permissions and limitations under the Apache Lic          },          // single +        getPlaceholder: function() { +            // if a placeholder is specified on a single select without the first empty option ignore it +            if (this.select) { +                if (this.select.find("option").first().text() !== "") { +                    return undefined; +                } +            } + +            return this.parent.getPlaceholder.apply(this, arguments); +        }, + +        // single          setPlaceholder: function () {              var placeholder = this.getPlaceholder(); @@ -1747,21 +1859,31 @@ the specific language governing permissions and limitations under the Apache Lic              // hide the search box if this is the first we got the results and there are a few of them              if (initial === true) { -                showSearchInput = this.showSearchInput = countResults(data.results) >= this.opts.minimumResultsForSearch; -                this.dropdown.find(".select2-search")[showSearchInput ? "removeClass" : "addClass"]("select2-search-hidden"); - -                //add "select2-with-searchbox" to the container if search box is shown -                $(this.dropdown, this.container)[showSearchInput ? "addClass" : "removeClass"]("select2-with-searchbox"); +                var min=this.opts.minimumResultsForSearch; +                showSearchInput  = min < 0 ? false : countResults(data.results) >= min; +                this.showSearch(showSearchInput);              }          },          // single +        showSearch: function(showSearchInput) { +            this.showSearchInput = showSearchInput; + +            this.dropdown.find(".select2-search")[showSearchInput ? "removeClass" : "addClass"]("select2-search-hidden"); +            //add "select2-with-searchbox" to the container if search box is shown +            $(this.dropdown, this.container)[showSearchInput ? "addClass" : "removeClass"]("select2-with-searchbox"); +        }, + +        // single          onSelect: function (data, options) {              var old = this.opts.element.val();              this.opts.element.val(this.id(data));              this.updateSelection(data); + +            this.opts.element.trigger({ type: "selected", val: this.id(data), choice: data }); +              this.close();              if (!options || !options.noFocus) @@ -1833,7 +1955,9 @@ the specific language governing permissions and limitations under the Apache Lic                      self.opts.element.val(!data ? "" : self.id(data));                      self.updateSelection(data);                      self.setPlaceholder(); -                    self.triggerChange(); +                    if (triggerChange) { +                        self.triggerChange(); +                    }                  });              }          }, @@ -1841,6 +1965,7 @@ the specific language governing permissions and limitations under the Apache Lic          // single          clearSearch: function () {              this.search.val(""); +            this.focusser.val("");          },          // single @@ -1890,15 +2015,14 @@ the specific language governing permissions and limitations under the Apache Lic              if (opts.element.get(0).tagName.toLowerCase() === "select") {                  // install sthe selection initializer -                opts.initSelection = function (element,callback) { +                opts.initSelection = function (element, callback) {                      var data = []; +                      element.find(":selected").each2(function (i, elm) { -                        data.push({id: elm.attr("value"), text: elm.text(), element: elm}); +                        data.push({id: elm.attr("value"), text: elm.text(), element: elm[0]});                      }); - -                    if ($.isFunction(callback)) -                        callback(data); +                    callback(data);                  };              } else if ("data" in opts) {                  // install default initSelection when applied to hidden input and data is local @@ -1929,6 +2053,13 @@ the specific language governing permissions and limitations under the Apache Lic              this.searchContainer = this.container.find(".select2-search-field");              this.selection = selection = this.container.find(selector); +            this.search.bind("input paste", this.bind(function() { +                if (!this.enabled) return; +                if (!this.opened()) { +                    this.open(); +                } +            })); +              this.search.bind("keydown", this.bind(function (e) {                  if (!this.enabled) return; @@ -1976,8 +2107,12 @@ the specific language governing permissions and limitations under the Apache Lic                      return;                  } -                if (this.opts.openOnEnter === false && e.which === KEY.ENTER) { -                    return; +                if (e.which === KEY.ENTER) { +                    if (this.opts.openOnEnter === false) { +                        return; +                    } else if (e.altKey || e.ctrlKey || e.shiftKey || e.metaKey) { +                        return; +                    }                  }                  this.open(); @@ -1993,7 +2128,7 @@ the specific language governing permissions and limitations under the Apache Lic              this.search.bind("blur", this.bind(function(e) {                  this.container.removeClass("select2-container-active");                  this.search.removeClass("select2-focused"); -                this.clearSearch(); +                if (!this.opened()) this.clearSearch();                  e.stopImmediatePropagation();              })); @@ -2016,6 +2151,8 @@ the specific language governing permissions and limitations under the Apache Lic                  this.clearPlaceholder();              })); +            this.initContainerWidth(); +              // set the placeholder if necessary              this.clearSearch();          }, @@ -2069,9 +2206,7 @@ the specific language governing permissions and limitations under the Apache Lic                  // stretch the search box to full width of the container so as much of the placeholder is visible as possible                  this.resizeSearch();              } else { -                // we set this to " " instead of "" and later clear it on focus() because there is a firefox bug -                // that does not properly render the caret when the field starts out blank -                this.search.val(" ").width(10); +                this.search.val("").width(10);              }          }, @@ -2079,9 +2214,6 @@ the specific language governing permissions and limitations under the Apache Lic          clearPlaceholder: function () {              if (this.search.hasClass("select2-default")) {                  this.search.val("").removeClass("select2-default"); -            } else { -                // work around for the space character we set to avoid firefox caret bug -                if (this.search.val() === " ") this.search.val("");              }          }, @@ -2092,6 +2224,8 @@ the specific language governing permissions and limitations under the Apache Lic              this.clearPlaceholder();  			this.resizeSearch();              this.focusSearch(); + +            this.opts.element.trigger($.Event("open"));          },          // multi @@ -2104,6 +2238,7 @@ the specific language governing permissions and limitations under the Apache Lic          focus: function () {              this.close();              this.search.focus(); +            this.opts.element.triggerHandler("focus");          },          // multi @@ -2146,6 +2281,9 @@ the specific language governing permissions and limitations under the Apache Lic          // multi          onSelect: function (data, options) {              this.addSelectedChoice(data); + +            this.opts.element.trigger({ type: "selected", val: this.id(data), choice: data }); +              if (this.select || !this.opts.closeOnSelect) this.postprocessResults();              if (this.opts.closeOnSelect) { @@ -2155,10 +2293,16 @@ the specific language governing permissions and limitations under the Apache Lic                  if (this.countSelectableResults()>0) {                      this.search.width(10);                      this.resizeSearch(); +                    if (this.val().length >= this.getMaximumSelectionSize()) { +                        // if we reached max selection size repaint the results so choices +                        // are replaced with the max selection reached message +                        this.updateResults(true); +                    }                      this.positionDropdown();                  } else {                      // if nothing left to select close                      this.close(); +                    this.search.width(10);                  }              } @@ -2252,6 +2396,8 @@ the specific language governing permissions and limitations under the Apache Lic                  if (this.select) this.postprocessResults();              }              selected.remove(); + +            this.opts.element.trigger({ type: "removed", val: this.id(data), choice: data });              this.triggerChange({ removed: data });          }, @@ -2287,7 +2433,6 @@ the specific language governing permissions and limitations under the Apache Lic          // multi          resizeSearch: function () { -              var minimumWidth, left, maxWidth, containerLeft, searchWidth,              	sideBorderPadding = getSideBorderPadding(this.search); @@ -2299,6 +2444,7 @@ the specific language governing permissions and limitations under the Apache Lic              containerLeft = this.selection.offset().left;              searchWidth = maxWidth - (left - containerLeft) - sideBorderPadding; +              if (searchWidth < minimumWidth) {                  searchWidth = maxWidth - sideBorderPadding;              } @@ -2308,7 +2454,7 @@ the specific language governing permissions and limitations under the Apache Lic              }              if (searchWidth <= 0) { -              searchWidth = minimumWidth +              searchWidth = minimumWidth;              }              this.search.width(searchWidth); @@ -2370,16 +2516,13 @@ the specific language governing permissions and limitations under the Apache Lic              this.setVal(val);              if (this.select) { -                this.select.find(":selected").each(function () { -                    data.push({id: $(this).attr("value"), text: $(this).text()}); -                }); -                this.updateSelection(data); +                this.opts.initSelection(this.select, this.bind(this.updateSelection));                  if (triggerChange) {                      this.triggerChange();                  }              } else {                  if (this.opts.initSelection === undefined) { -                    throw new Error("val() cannot be called if initSelection() is not defined") +                    throw new Error("val() cannot be called if initSelection() is not defined");                  }                  this.opts.initSelection(this.opts.element, function(data){ @@ -2438,7 +2581,7 @@ the specific language governing permissions and limitations under the Apache Lic                       .get();              } else {                  if (!values) { values = []; } -                ids = $.map(values, function(e) { return self.opts.id(e)}); +                ids = $.map(values, function(e) { return self.opts.id(e); });                  this.setVal(ids);                  this.updateSelection(values);                  this.clearSearch(); @@ -2499,9 +2642,9 @@ the specific language governing permissions and limitations under the Apache Lic          dropdownCss: {},          containerCssClass: "",          dropdownCssClass: "", -        formatResult: function(result, container, query) { +        formatResult: function(result, container, query, escapeMarkup) {              var markup=[]; -            markMatch(result.text, query.term, markup, this.escapeMarkup); +            markMatch(result.text, query.term, markup, escapeMarkup);              return markup.join("");          },          formatSelection: function (data, container) { @@ -2544,7 +2687,9 @@ the specific language governing permissions and limitations under the Apache Lic              });          },          blurOnChange: false, -        selectOnBlur: false +        selectOnBlur: false, +        adaptContainerCssClass: function(c) { return c; }, +        adaptDropdownCssClass: function(c) { return null; }      };      // exports diff --git a/module/web/static/js/views/filterView.js b/module/web/static/js/views/filterView.js index 792edfee2..ea0aff0d5 100644 --- a/module/web/static/js/views/filterView.js +++ b/module/web/static/js/views/filterView.js @@ -1,6 +1,16 @@  define(['jquery', 'backbone', 'underscore', 'app', 'utils/apitypes'],      function($, Backbone, _, App, Api) { +        // Modified version of type ahead show, nearly the same without absolute positioning +        function show() { +            this.$menu +                .insertAfter(this.$element) +                .show(); + +            this.shown = true; +            return this; +        } +          // Renders the actionbar for the dashboard, handles everything related to filtering displayed files          return Backbone.View.extend({              el: 'ul.actionbar', @@ -14,6 +24,9 @@ define(['jquery', 'backbone', 'underscore', 'app', 'utils/apitypes'],              stateMenu: null,              initialize: function() { + +                // use our modified method +                $.fn.typeahead.Constructor.prototype.show = show;                  this.$('.search-query').typeahead({                      minLength: 2,                      source: this.getAutocompletion @@ -24,7 +37,6 @@ define(['jquery', 'backbone', 'underscore', 'app', 'utils/apitypes'],                  // Apply the filter before the content is shown                  App.vent.on('dashboard:contentReady', _.bind(this.apply_filter, this)); -              },              render: function() { @@ -33,7 +45,8 @@ define(['jquery', 'backbone', 'underscore', 'app', 'utils/apitypes'],              getAutocompletion: function() {                  return ["static", "autocompletion", "demo", "with", "some", "keywords", -                    "a very long proposal for autocompletion"]; +                    "a very long proposal for autocompletion", "autobot", +                    "a very long proposal for autocompletion second one"];              },              switch_filter: function(e) { diff --git a/module/web/templates/default/base.html b/module/web/templates/default/base.html index 77789e790..621059c8c 100644 --- a/module/web/templates/default/base.html +++ b/module/web/templates/default/base.html @@ -13,13 +13,13 @@      <link href="/static/css/bootstrap.css" rel="stylesheet" type="text/css"/>
      <link href="/static/css/select2.css" rel="stylesheet" type="text/css"/>
      <link href="/static/css/font.css" rel="stylesheet" type="text/css"/>
 -    <link href="static/css/fontawesome.css" rel="stylesheet" type="text/css"/>
 +    <link href="/static/css/fontawesome.css" rel="stylesheet" type="text/css"/>
      <link href="/static/css/default/style.less" rel="stylesheet/less" type="text/css" media="screen"/>
      {% block css %}
      {% endblock %}
      <script src="/static/js/libs/less-1.3.0.min.js" type="text/javascript"></script>
 -    <script type="text/javascript" data-main="static/js/config" src="/static/js/libs/require-2.1.2.js"></script>
 +    <script type="text/javascript" data-main="static/js/config" src="/static/js/libs/require-2.1.5.js"></script>
      <script>
          require(['default'], function(App) {
              App.init();
 | 
