code.gitea.io/gitea@v1.21.7/web_src/fomantic/build/semantic.js (about)

     1   /*
     2   * # Fomantic UI - 2.8.7
     3   * https://github.com/fomantic/Fomantic-UI
     4   * http://fomantic-ui.com/
     5   *
     6   * Copyright 2014 Contributors
     7   * Released under the MIT license
     8   * http://opensource.org/licenses/MIT
     9   *
    10   */
    11  /*!
    12   * # Fomantic-UI - API
    13   * http://github.com/fomantic/Fomantic-UI/
    14   *
    15   *
    16   * Released under the MIT license
    17   * http://opensource.org/licenses/MIT
    18   *
    19   */
    20  
    21  ;(function ($, window, document, undefined) {
    22  
    23  'use strict';
    24  
    25  $.isWindow = $.isWindow || function(obj) {
    26    return obj != null && obj === obj.window;
    27  };
    28  
    29    window = (typeof window != 'undefined' && window.Math == Math)
    30      ? window
    31      : (typeof self != 'undefined' && self.Math == Math)
    32        ? self
    33        : Function('return this')()
    34  ;
    35  
    36  $.api = $.fn.api = function(parameters) {
    37  
    38    var
    39      // use window context if none specified
    40      $allModules     = $.isFunction(this)
    41          ? $(window)
    42          : $(this),
    43      moduleSelector = $allModules.selector || '',
    44      time           = new Date().getTime(),
    45      performance    = [],
    46  
    47      query          = arguments[0],
    48      methodInvoked  = (typeof query == 'string'),
    49      queryArguments = [].slice.call(arguments, 1),
    50  
    51      returnedValue
    52    ;
    53  
    54    $allModules
    55      .each(function() {
    56        var
    57          settings          = ( $.isPlainObject(parameters) )
    58            ? $.extend(true, {}, $.fn.api.settings, parameters)
    59            : $.extend({}, $.fn.api.settings),
    60  
    61          // internal aliases
    62          namespace       = settings.namespace,
    63          metadata        = settings.metadata,
    64          selector        = settings.selector,
    65          error           = settings.error,
    66          className       = settings.className,
    67  
    68          // define namespaces for modules
    69          eventNamespace  = '.' + namespace,
    70          moduleNamespace = 'module-' + namespace,
    71  
    72          // element that creates request
    73          $module         = $(this),
    74          $form           = $module.closest(selector.form),
    75  
    76          // context used for state
    77          $context        = (settings.stateContext)
    78            ? $(settings.stateContext)
    79            : $module,
    80  
    81          // request details
    82          ajaxSettings,
    83          requestSettings,
    84          url,
    85          data,
    86          requestStartTime,
    87  
    88          // standard module
    89          element         = this,
    90          context         = $context[0],
    91          instance        = $module.data(moduleNamespace),
    92          module
    93        ;
    94  
    95        module = {
    96  
    97          initialize: function() {
    98            if(!methodInvoked) {
    99              module.bind.events();
   100            }
   101            module.instantiate();
   102          },
   103  
   104          instantiate: function() {
   105            module.verbose('Storing instance of module', module);
   106            instance = module;
   107            $module
   108              .data(moduleNamespace, instance)
   109            ;
   110          },
   111  
   112          destroy: function() {
   113            module.verbose('Destroying previous module for', element);
   114            $module
   115              .removeData(moduleNamespace)
   116              .off(eventNamespace)
   117            ;
   118          },
   119  
   120          bind: {
   121            events: function() {
   122              var
   123                triggerEvent = module.get.event()
   124              ;
   125              if( triggerEvent ) {
   126                module.verbose('Attaching API events to element', triggerEvent);
   127                $module
   128                  .on(triggerEvent + eventNamespace, module.event.trigger)
   129                ;
   130              }
   131              else if(settings.on == 'now') {
   132                module.debug('Querying API endpoint immediately');
   133                module.query();
   134              }
   135            }
   136          },
   137  
   138          decode: {
   139            json: function(response) {
   140              if(response !== undefined && typeof response == 'string') {
   141                try {
   142                 response = JSON.parse(response);
   143                }
   144                catch(e) {
   145                  // isnt json string
   146                }
   147              }
   148              return response;
   149            }
   150          },
   151  
   152          read: {
   153            cachedResponse: function(url) {
   154              var
   155                response
   156              ;
   157              if(window.Storage === undefined) {
   158                module.error(error.noStorage);
   159                return;
   160              }
   161              response = sessionStorage.getItem(url);
   162              module.debug('Using cached response', url, response);
   163              response = module.decode.json(response);
   164              return response;
   165            }
   166          },
   167          write: {
   168            cachedResponse: function(url, response) {
   169              if(response && response === '') {
   170                module.debug('Response empty, not caching', response);
   171                return;
   172              }
   173              if(window.Storage === undefined) {
   174                module.error(error.noStorage);
   175                return;
   176              }
   177              if( $.isPlainObject(response) ) {
   178                response = JSON.stringify(response);
   179              }
   180              sessionStorage.setItem(url, response);
   181              module.verbose('Storing cached response for url', url, response);
   182            }
   183          },
   184  
   185          query: function() {
   186  
   187            if(module.is.disabled()) {
   188              module.debug('Element is disabled API request aborted');
   189              return;
   190            }
   191  
   192            if(module.is.loading()) {
   193              if(settings.interruptRequests) {
   194                module.debug('Interrupting previous request');
   195                module.abort();
   196              }
   197              else {
   198                module.debug('Cancelling request, previous request is still pending');
   199                return;
   200              }
   201            }
   202  
   203            // pass element metadata to url (value, text)
   204            if(settings.defaultData) {
   205              $.extend(true, settings.urlData, module.get.defaultData());
   206            }
   207  
   208            // Add form content
   209            if(settings.serializeForm) {
   210              settings.data = module.add.formData(settings.data);
   211            }
   212  
   213            // call beforesend and get any settings changes
   214            requestSettings = module.get.settings();
   215  
   216            // check if before send cancelled request
   217            if(requestSettings === false) {
   218              module.cancelled = true;
   219              module.error(error.beforeSend);
   220              return;
   221            }
   222            else {
   223              module.cancelled = false;
   224            }
   225  
   226            // get url
   227            url = module.get.templatedURL();
   228  
   229            if(!url && !module.is.mocked()) {
   230              module.error(error.missingURL);
   231              return;
   232            }
   233  
   234            // replace variables
   235            url = module.add.urlData( url );
   236            // missing url parameters
   237            if( !url && !module.is.mocked()) {
   238              return;
   239            }
   240  
   241            requestSettings.url = settings.base + url;
   242  
   243            // look for jQuery ajax parameters in settings
   244            ajaxSettings = $.extend(true, {}, settings, {
   245              type       : settings.method || settings.type,
   246              data       : data,
   247              url        : settings.base + url,
   248              beforeSend : settings.beforeXHR,
   249              success    : function() {},
   250              failure    : function() {},
   251              complete   : function() {}
   252            });
   253  
   254            module.debug('Querying URL', ajaxSettings.url);
   255            module.verbose('Using AJAX settings', ajaxSettings);
   256            if(settings.cache === 'local' && module.read.cachedResponse(url)) {
   257              module.debug('Response returned from local cache');
   258              module.request = module.create.request();
   259              module.request.resolveWith(context, [ module.read.cachedResponse(url) ]);
   260              return;
   261            }
   262  
   263            if( !settings.throttle ) {
   264              module.debug('Sending request', data, ajaxSettings.method);
   265              module.send.request();
   266            }
   267            else {
   268              if(!settings.throttleFirstRequest && !module.timer) {
   269                module.debug('Sending request', data, ajaxSettings.method);
   270                module.send.request();
   271                module.timer = setTimeout(function(){}, settings.throttle);
   272              }
   273              else {
   274                module.debug('Throttling request', settings.throttle);
   275                clearTimeout(module.timer);
   276                module.timer = setTimeout(function() {
   277                  if(module.timer) {
   278                    delete module.timer;
   279                  }
   280                  module.debug('Sending throttled request', data, ajaxSettings.method);
   281                  module.send.request();
   282                }, settings.throttle);
   283              }
   284            }
   285  
   286          },
   287  
   288          should: {
   289            removeError: function() {
   290              return ( settings.hideError === true || (settings.hideError === 'auto' && !module.is.form()) );
   291            }
   292          },
   293  
   294          is: {
   295            disabled: function() {
   296              return ($module.filter(selector.disabled).length > 0);
   297            },
   298            expectingJSON: function() {
   299              return settings.dataType === 'json' || settings.dataType === 'jsonp';
   300            },
   301            form: function() {
   302              return $module.is('form') || $context.is('form');
   303            },
   304            mocked: function() {
   305              return (settings.mockResponse || settings.mockResponseAsync || settings.response || settings.responseAsync);
   306            },
   307            input: function() {
   308              return $module.is('input');
   309            },
   310            loading: function() {
   311              return (module.request)
   312                ? (module.request.state() == 'pending')
   313                : false
   314              ;
   315            },
   316            abortedRequest: function(xhr) {
   317              if(xhr && xhr.readyState !== undefined && xhr.readyState === 0) {
   318                module.verbose('XHR request determined to be aborted');
   319                return true;
   320              }
   321              else {
   322                module.verbose('XHR request was not aborted');
   323                return false;
   324              }
   325            },
   326            validResponse: function(response) {
   327              if( (!module.is.expectingJSON()) || !$.isFunction(settings.successTest) ) {
   328                module.verbose('Response is not JSON, skipping validation', settings.successTest, response);
   329                return true;
   330              }
   331              module.debug('Checking JSON returned success', settings.successTest, response);
   332              if( settings.successTest(response) ) {
   333                module.debug('Response passed success test', response);
   334                return true;
   335              }
   336              else {
   337                module.debug('Response failed success test', response);
   338                return false;
   339              }
   340            }
   341          },
   342  
   343          was: {
   344            cancelled: function() {
   345              return (module.cancelled || false);
   346            },
   347            succesful: function() {
   348              module.verbose('This behavior will be deleted due to typo. Use "was successful" instead.');
   349              return module.was.successful();
   350            },
   351            successful: function() {
   352              return (module.request && module.request.state() == 'resolved');
   353            },
   354            failure: function() {
   355              return (module.request && module.request.state() == 'rejected');
   356            },
   357            complete: function() {
   358              return (module.request && (module.request.state() == 'resolved' || module.request.state() == 'rejected') );
   359            }
   360          },
   361  
   362          add: {
   363            urlData: function(url, urlData) {
   364              var
   365                requiredVariables,
   366                optionalVariables
   367              ;
   368              if(url) {
   369                requiredVariables = url.match(settings.regExp.required);
   370                optionalVariables = url.match(settings.regExp.optional);
   371                urlData           = urlData || settings.urlData;
   372                if(requiredVariables) {
   373                  module.debug('Looking for required URL variables', requiredVariables);
   374                  $.each(requiredVariables, function(index, templatedString) {
   375                    var
   376                      // allow legacy {$var} style
   377                      variable = (templatedString.indexOf('$') !== -1)
   378                        ? templatedString.substr(2, templatedString.length - 3)
   379                        : templatedString.substr(1, templatedString.length - 2),
   380                      value   = ($.isPlainObject(urlData) && urlData[variable] !== undefined)
   381                        ? urlData[variable]
   382                        : ($module.data(variable) !== undefined)
   383                          ? $module.data(variable)
   384                          : ($context.data(variable) !== undefined)
   385                            ? $context.data(variable)
   386                            : urlData[variable]
   387                    ;
   388                    // remove value
   389                    if(value === undefined) {
   390                      module.error(error.requiredParameter, variable, url);
   391                      url = false;
   392                      return false;
   393                    }
   394                    else {
   395                      module.verbose('Found required variable', variable, value);
   396                      value = (settings.encodeParameters)
   397                        ? module.get.urlEncodedValue(value)
   398                        : value
   399                      ;
   400                      url = url.replace(templatedString, value);
   401                    }
   402                  });
   403                }
   404                if(optionalVariables) {
   405                  module.debug('Looking for optional URL variables', requiredVariables);
   406                  $.each(optionalVariables, function(index, templatedString) {
   407                    var
   408                      // allow legacy {/$var} style
   409                      variable = (templatedString.indexOf('$') !== -1)
   410                        ? templatedString.substr(3, templatedString.length - 4)
   411                        : templatedString.substr(2, templatedString.length - 3),
   412                      value   = ($.isPlainObject(urlData) && urlData[variable] !== undefined)
   413                        ? urlData[variable]
   414                        : ($module.data(variable) !== undefined)
   415                          ? $module.data(variable)
   416                          : ($context.data(variable) !== undefined)
   417                            ? $context.data(variable)
   418                            : urlData[variable]
   419                    ;
   420                    // optional replacement
   421                    if(value !== undefined) {
   422                      module.verbose('Optional variable Found', variable, value);
   423                      url = url.replace(templatedString, value);
   424                    }
   425                    else {
   426                      module.verbose('Optional variable not found', variable);
   427                      // remove preceding slash if set
   428                      if(url.indexOf('/' + templatedString) !== -1) {
   429                        url = url.replace('/' + templatedString, '');
   430                      }
   431                      else {
   432                        url = url.replace(templatedString, '');
   433                      }
   434                    }
   435                  });
   436                }
   437              }
   438              return url;
   439            },
   440            formData: function(data) {
   441              var
   442                canSerialize = ($.fn.serializeObject !== undefined),
   443                formData     = (canSerialize)
   444                  ? $form.serializeObject()
   445                  : $form.serialize(),
   446                hasOtherData
   447              ;
   448              data         = data || settings.data;
   449              hasOtherData = $.isPlainObject(data);
   450  
   451              if(hasOtherData) {
   452                if(canSerialize) {
   453                  module.debug('Extending existing data with form data', data, formData);
   454                  data = $.extend(true, {}, data, formData);
   455                }
   456                else {
   457                  module.error(error.missingSerialize);
   458                  module.debug('Cant extend data. Replacing data with form data', data, formData);
   459                  data = formData;
   460                }
   461              }
   462              else {
   463                module.debug('Adding form data', formData);
   464                data = formData;
   465              }
   466              return data;
   467            }
   468          },
   469  
   470          send: {
   471            request: function() {
   472              module.set.loading();
   473              module.request = module.create.request();
   474              if( module.is.mocked() ) {
   475                module.mockedXHR = module.create.mockedXHR();
   476              }
   477              else {
   478                module.xhr = module.create.xhr();
   479              }
   480              settings.onRequest.call(context, module.request, module.xhr);
   481            }
   482          },
   483  
   484          event: {
   485            trigger: function(event) {
   486              module.query();
   487              if(event.type == 'submit' || event.type == 'click') {
   488                event.preventDefault();
   489              }
   490            },
   491            xhr: {
   492              always: function() {
   493                // nothing special
   494              },
   495              done: function(response, textStatus, xhr) {
   496                var
   497                  context            = this,
   498                  elapsedTime        = (new Date().getTime() - requestStartTime),
   499                  timeLeft           = (settings.loadingDuration - elapsedTime),
   500                  translatedResponse = ( $.isFunction(settings.onResponse) )
   501                    ? module.is.expectingJSON() && !settings.rawResponse
   502                      ? settings.onResponse.call(context, $.extend(true, {}, response))
   503                      : settings.onResponse.call(context, response)
   504                    : false
   505                ;
   506                timeLeft = (timeLeft > 0)
   507                  ? timeLeft
   508                  : 0
   509                ;
   510                if(translatedResponse) {
   511                  module.debug('Modified API response in onResponse callback', settings.onResponse, translatedResponse, response);
   512                  response = translatedResponse;
   513                }
   514                if(timeLeft > 0) {
   515                  module.debug('Response completed early delaying state change by', timeLeft);
   516                }
   517                setTimeout(function() {
   518                  if( module.is.validResponse(response) ) {
   519                    module.request.resolveWith(context, [response, xhr]);
   520                  }
   521                  else {
   522                    module.request.rejectWith(context, [xhr, 'invalid']);
   523                  }
   524                }, timeLeft);
   525              },
   526              fail: function(xhr, status, httpMessage) {
   527                var
   528                  context     = this,
   529                  elapsedTime = (new Date().getTime() - requestStartTime),
   530                  timeLeft    = (settings.loadingDuration - elapsedTime)
   531                ;
   532                timeLeft = (timeLeft > 0)
   533                  ? timeLeft
   534                  : 0
   535                ;
   536                if(timeLeft > 0) {
   537                  module.debug('Response completed early delaying state change by', timeLeft);
   538                }
   539                setTimeout(function() {
   540                  if( module.is.abortedRequest(xhr) ) {
   541                    module.request.rejectWith(context, [xhr, 'aborted', httpMessage]);
   542                  }
   543                  else {
   544                    module.request.rejectWith(context, [xhr, 'error', status, httpMessage]);
   545                  }
   546                }, timeLeft);
   547              }
   548            },
   549            request: {
   550              done: function(response, xhr) {
   551                module.debug('Successful API Response', response);
   552                if(settings.cache === 'local' && url) {
   553                  module.write.cachedResponse(url, response);
   554                  module.debug('Saving server response locally', module.cache);
   555                }
   556                settings.onSuccess.call(context, response, $module, xhr);
   557              },
   558              complete: function(firstParameter, secondParameter) {
   559                var
   560                  xhr,
   561                  response
   562                ;
   563                // have to guess callback parameters based on request success
   564                if( module.was.successful() ) {
   565                  response = firstParameter;
   566                  xhr      = secondParameter;
   567                }
   568                else {
   569                  xhr      = firstParameter;
   570                  response = module.get.responseFromXHR(xhr);
   571                }
   572                module.remove.loading();
   573                settings.onComplete.call(context, response, $module, xhr);
   574              },
   575              fail: function(xhr, status, httpMessage) {
   576                var
   577                  // pull response from xhr if available
   578                  response     = module.get.responseFromXHR(xhr),
   579                  errorMessage = module.get.errorFromRequest(response, status, httpMessage)
   580                ;
   581                if(status == 'aborted') {
   582                  module.debug('XHR Aborted (Most likely caused by page navigation or CORS Policy)', status, httpMessage);
   583                  settings.onAbort.call(context, status, $module, xhr);
   584                  return true;
   585                }
   586                else if(status == 'invalid') {
   587                  module.debug('JSON did not pass success test. A server-side error has most likely occurred', response);
   588                }
   589                else if(status == 'error') {
   590                  if(xhr !== undefined) {
   591                    module.debug('XHR produced a server error', status, httpMessage);
   592                    // make sure we have an error to display to console
   593                    if( (xhr.status < 200 || xhr.status >= 300) && httpMessage !== undefined && httpMessage !== '') {
   594                      module.error(error.statusMessage + httpMessage, ajaxSettings.url);
   595                    }
   596                    settings.onError.call(context, errorMessage, $module, xhr);
   597                  }
   598                }
   599  
   600                if(settings.errorDuration && status !== 'aborted') {
   601                  module.debug('Adding error state');
   602                  module.set.error();
   603                  if( module.should.removeError() ) {
   604                    setTimeout(module.remove.error, settings.errorDuration);
   605                  }
   606                }
   607                module.debug('API Request failed', errorMessage, xhr);
   608                settings.onFailure.call(context, response, $module, xhr);
   609              }
   610            }
   611          },
   612  
   613          create: {
   614  
   615            request: function() {
   616              // api request promise
   617              return $.Deferred()
   618                .always(module.event.request.complete)
   619                .done(module.event.request.done)
   620                .fail(module.event.request.fail)
   621              ;
   622            },
   623  
   624            mockedXHR: function () {
   625              var
   626                // xhr does not simulate these properties of xhr but must return them
   627                textStatus     = false,
   628                status         = false,
   629                httpMessage    = false,
   630                responder      = settings.mockResponse      || settings.response,
   631                asyncResponder = settings.mockResponseAsync || settings.responseAsync,
   632                asyncCallback,
   633                response,
   634                mockedXHR
   635              ;
   636  
   637              mockedXHR = $.Deferred()
   638                .always(module.event.xhr.complete)
   639                .done(module.event.xhr.done)
   640                .fail(module.event.xhr.fail)
   641              ;
   642  
   643              if(responder) {
   644                if( $.isFunction(responder) ) {
   645                  module.debug('Using specified synchronous callback', responder);
   646                  response = responder.call(context, requestSettings);
   647                }
   648                else {
   649                  module.debug('Using settings specified response', responder);
   650                  response = responder;
   651                }
   652                // simulating response
   653                mockedXHR.resolveWith(context, [ response, textStatus, { responseText: response }]);
   654              }
   655              else if( $.isFunction(asyncResponder) ) {
   656                asyncCallback = function(response) {
   657                  module.debug('Async callback returned response', response);
   658  
   659                  if(response) {
   660                    mockedXHR.resolveWith(context, [ response, textStatus, { responseText: response }]);
   661                  }
   662                  else {
   663                    mockedXHR.rejectWith(context, [{ responseText: response }, status, httpMessage]);
   664                  }
   665                };
   666                module.debug('Using specified async response callback', asyncResponder);
   667                asyncResponder.call(context, requestSettings, asyncCallback);
   668              }
   669              return mockedXHR;
   670            },
   671  
   672            xhr: function() {
   673              var
   674                xhr
   675              ;
   676              // ajax request promise
   677              xhr = $.ajax(ajaxSettings)
   678                .always(module.event.xhr.always)
   679                .done(module.event.xhr.done)
   680                .fail(module.event.xhr.fail)
   681              ;
   682              module.verbose('Created server request', xhr, ajaxSettings);
   683              return xhr;
   684            }
   685          },
   686  
   687          set: {
   688            error: function() {
   689              module.verbose('Adding error state to element', $context);
   690              $context.addClass(className.error);
   691            },
   692            loading: function() {
   693              module.verbose('Adding loading state to element', $context);
   694              $context.addClass(className.loading);
   695              requestStartTime = new Date().getTime();
   696            }
   697          },
   698  
   699          remove: {
   700            error: function() {
   701              module.verbose('Removing error state from element', $context);
   702              $context.removeClass(className.error);
   703            },
   704            loading: function() {
   705              module.verbose('Removing loading state from element', $context);
   706              $context.removeClass(className.loading);
   707            }
   708          },
   709  
   710          get: {
   711            responseFromXHR: function(xhr) {
   712              return $.isPlainObject(xhr)
   713                ? (module.is.expectingJSON())
   714                  ? module.decode.json(xhr.responseText)
   715                  : xhr.responseText
   716                : false
   717              ;
   718            },
   719            errorFromRequest: function(response, status, httpMessage) {
   720              return ($.isPlainObject(response) && response.error !== undefined)
   721                ? response.error // use json error message
   722                : (settings.error[status] !== undefined) // use server error message
   723                  ? settings.error[status]
   724                  : httpMessage
   725              ;
   726            },
   727            request: function() {
   728              return module.request || false;
   729            },
   730            xhr: function() {
   731              return module.xhr || false;
   732            },
   733            settings: function() {
   734              var
   735                runSettings
   736              ;
   737              runSettings = settings.beforeSend.call($module, settings);
   738              if(runSettings) {
   739                if(runSettings.success !== undefined) {
   740                  module.debug('Legacy success callback detected', runSettings);
   741                  module.error(error.legacyParameters, runSettings.success);
   742                  runSettings.onSuccess = runSettings.success;
   743                }
   744                if(runSettings.failure !== undefined) {
   745                  module.debug('Legacy failure callback detected', runSettings);
   746                  module.error(error.legacyParameters, runSettings.failure);
   747                  runSettings.onFailure = runSettings.failure;
   748                }
   749                if(runSettings.complete !== undefined) {
   750                  module.debug('Legacy complete callback detected', runSettings);
   751                  module.error(error.legacyParameters, runSettings.complete);
   752                  runSettings.onComplete = runSettings.complete;
   753                }
   754              }
   755              if(runSettings === undefined) {
   756                module.error(error.noReturnedValue);
   757              }
   758              if(runSettings === false) {
   759                return runSettings;
   760              }
   761              return (runSettings !== undefined)
   762                ? $.extend(true, {}, runSettings)
   763                : $.extend(true, {}, settings)
   764              ;
   765            },
   766            urlEncodedValue: function(value) {
   767              var
   768                decodedValue   = window.decodeURIComponent(value),
   769                encodedValue   = window.encodeURIComponent(value),
   770                alreadyEncoded = (decodedValue !== value)
   771              ;
   772              if(alreadyEncoded) {
   773                module.debug('URL value is already encoded, avoiding double encoding', value);
   774                return value;
   775              }
   776              module.verbose('Encoding value using encodeURIComponent', value, encodedValue);
   777              return encodedValue;
   778            },
   779            defaultData: function() {
   780              var
   781                data = {}
   782              ;
   783              if( !$.isWindow(element) ) {
   784                if( module.is.input() ) {
   785                  data.value = $module.val();
   786                }
   787                else if( module.is.form() ) {
   788  
   789                }
   790                else {
   791                  data.text = $module.text();
   792                }
   793              }
   794              return data;
   795            },
   796            event: function() {
   797              if( $.isWindow(element) || settings.on == 'now' ) {
   798                module.debug('API called without element, no events attached');
   799                return false;
   800              }
   801              else if(settings.on == 'auto') {
   802                if( $module.is('input') ) {
   803                  return (element.oninput !== undefined)
   804                    ? 'input'
   805                    : (element.onpropertychange !== undefined)
   806                      ? 'propertychange'
   807                      : 'keyup'
   808                  ;
   809                }
   810                else if( $module.is('form') ) {
   811                  return 'submit';
   812                }
   813                else {
   814                  return 'click';
   815                }
   816              }
   817              else {
   818                return settings.on;
   819              }
   820            },
   821            templatedURL: function(action) {
   822              action = action || $module.data(metadata.action) || settings.action || false;
   823              url    = $module.data(metadata.url) || settings.url || false;
   824              if(url) {
   825                module.debug('Using specified url', url);
   826                return url;
   827              }
   828              if(action) {
   829                module.debug('Looking up url for action', action, settings.api);
   830                if(settings.api[action] === undefined && !module.is.mocked()) {
   831                  module.error(error.missingAction, settings.action, settings.api);
   832                  return;
   833                }
   834                url = settings.api[action];
   835              }
   836              else if( module.is.form() ) {
   837                url = $module.attr('action') || $context.attr('action') || false;
   838                module.debug('No url or action specified, defaulting to form action', url);
   839              }
   840              return url;
   841            }
   842          },
   843  
   844          abort: function() {
   845            var
   846              xhr = module.get.xhr()
   847            ;
   848            if( xhr && xhr.state() !== 'resolved') {
   849              module.debug('Cancelling API request');
   850              xhr.abort();
   851            }
   852          },
   853  
   854          // reset state
   855          reset: function() {
   856            module.remove.error();
   857            module.remove.loading();
   858          },
   859  
   860          setting: function(name, value) {
   861            module.debug('Changing setting', name, value);
   862            if( $.isPlainObject(name) ) {
   863              $.extend(true, settings, name);
   864            }
   865            else if(value !== undefined) {
   866              if($.isPlainObject(settings[name])) {
   867                $.extend(true, settings[name], value);
   868              }
   869              else {
   870                settings[name] = value;
   871              }
   872            }
   873            else {
   874              return settings[name];
   875            }
   876          },
   877          internal: function(name, value) {
   878            if( $.isPlainObject(name) ) {
   879              $.extend(true, module, name);
   880            }
   881            else if(value !== undefined) {
   882              module[name] = value;
   883            }
   884            else {
   885              return module[name];
   886            }
   887          },
   888          debug: function() {
   889            if(!settings.silent && settings.debug) {
   890              if(settings.performance) {
   891                module.performance.log(arguments);
   892              }
   893              else {
   894                module.debug = Function.prototype.bind.call(console.info, console, settings.name + ':');
   895                module.debug.apply(console, arguments);
   896              }
   897            }
   898          },
   899          verbose: function() {
   900            if(!settings.silent && settings.verbose && settings.debug) {
   901              if(settings.performance) {
   902                module.performance.log(arguments);
   903              }
   904              else {
   905                module.verbose = Function.prototype.bind.call(console.info, console, settings.name + ':');
   906                module.verbose.apply(console, arguments);
   907              }
   908            }
   909          },
   910          error: function() {
   911            if(!settings.silent) {
   912              module.error = Function.prototype.bind.call(console.error, console, settings.name + ':');
   913              module.error.apply(console, arguments);
   914            }
   915          },
   916          performance: {
   917            log: function(message) {
   918              var
   919                currentTime,
   920                executionTime,
   921                previousTime
   922              ;
   923              if(settings.performance) {
   924                currentTime   = new Date().getTime();
   925                previousTime  = time || currentTime;
   926                executionTime = currentTime - previousTime;
   927                time          = currentTime;
   928                performance.push({
   929                  'Name'           : message[0],
   930                  'Arguments'      : [].slice.call(message, 1) || '',
   931                  //'Element'        : element,
   932                  'Execution Time' : executionTime
   933                });
   934              }
   935              clearTimeout(module.performance.timer);
   936              module.performance.timer = setTimeout(module.performance.display, 500);
   937            },
   938            display: function() {
   939              var
   940                title = settings.name + ':',
   941                totalTime = 0
   942              ;
   943              time = false;
   944              clearTimeout(module.performance.timer);
   945              $.each(performance, function(index, data) {
   946                totalTime += data['Execution Time'];
   947              });
   948              title += ' ' + totalTime + 'ms';
   949              if(moduleSelector) {
   950                title += ' \'' + moduleSelector + '\'';
   951              }
   952              if( (console.group !== undefined || console.table !== undefined) && performance.length > 0) {
   953                console.groupCollapsed(title);
   954                if(console.table) {
   955                  console.table(performance);
   956                }
   957                else {
   958                  $.each(performance, function(index, data) {
   959                    console.log(data['Name'] + ': ' + data['Execution Time']+'ms');
   960                  });
   961                }
   962                console.groupEnd();
   963              }
   964              performance = [];
   965            }
   966          },
   967          invoke: function(query, passedArguments, context) {
   968            var
   969              object = instance,
   970              maxDepth,
   971              found,
   972              response
   973            ;
   974            passedArguments = passedArguments || queryArguments;
   975            context         = element         || context;
   976            if(typeof query == 'string' && object !== undefined) {
   977              query    = query.split(/[\. ]/);
   978              maxDepth = query.length - 1;
   979              $.each(query, function(depth, value) {
   980                var camelCaseValue = (depth != maxDepth)
   981                  ? value + query[depth + 1].charAt(0).toUpperCase() + query[depth + 1].slice(1)
   982                  : query
   983                ;
   984                if( $.isPlainObject( object[camelCaseValue] ) && (depth != maxDepth) ) {
   985                  object = object[camelCaseValue];
   986                }
   987                else if( object[camelCaseValue] !== undefined ) {
   988                  found = object[camelCaseValue];
   989                  return false;
   990                }
   991                else if( $.isPlainObject( object[value] ) && (depth != maxDepth) ) {
   992                  object = object[value];
   993                }
   994                else if( object[value] !== undefined ) {
   995                  found = object[value];
   996                  return false;
   997                }
   998                else {
   999                  module.error(error.method, query);
  1000                  return false;
  1001                }
  1002              });
  1003            }
  1004            if ( $.isFunction( found ) ) {
  1005              response = found.apply(context, passedArguments);
  1006            }
  1007            else if(found !== undefined) {
  1008              response = found;
  1009            }
  1010            if(Array.isArray(returnedValue)) {
  1011              returnedValue.push(response);
  1012            }
  1013            else if(returnedValue !== undefined) {
  1014              returnedValue = [returnedValue, response];
  1015            }
  1016            else if(response !== undefined) {
  1017              returnedValue = response;
  1018            }
  1019            return found;
  1020          }
  1021        };
  1022  
  1023        if(methodInvoked) {
  1024          if(instance === undefined) {
  1025            module.initialize();
  1026          }
  1027          module.invoke(query);
  1028        }
  1029        else {
  1030          if(instance !== undefined) {
  1031            instance.invoke('destroy');
  1032          }
  1033          module.initialize();
  1034        }
  1035      })
  1036    ;
  1037  
  1038    return (returnedValue !== undefined)
  1039      ? returnedValue
  1040      : this
  1041    ;
  1042  };
  1043  
  1044  $.api.settings = {
  1045  
  1046    name              : 'API',
  1047    namespace         : 'api',
  1048  
  1049    debug             : false,
  1050    verbose           : false,
  1051    performance       : true,
  1052  
  1053    // object containing all templates endpoints
  1054    api               : {},
  1055  
  1056    // whether to cache responses
  1057    cache             : true,
  1058  
  1059    // whether new requests should abort previous requests
  1060    interruptRequests : true,
  1061  
  1062    // event binding
  1063    on                : 'auto',
  1064  
  1065    // context for applying state classes
  1066    stateContext      : false,
  1067  
  1068    // duration for loading state
  1069    loadingDuration   : 0,
  1070  
  1071    // whether to hide errors after a period of time
  1072    hideError         : 'auto',
  1073  
  1074    // duration for error state
  1075    errorDuration     : 2000,
  1076  
  1077    // whether parameters should be encoded with encodeURIComponent
  1078    encodeParameters  : true,
  1079  
  1080    // API action to use
  1081    action            : false,
  1082  
  1083    // templated URL to use
  1084    url               : false,
  1085  
  1086    // base URL to apply to all endpoints
  1087    base              : '',
  1088  
  1089    // data that will
  1090    urlData           : {},
  1091  
  1092    // whether to add default data to url data
  1093    defaultData          : true,
  1094  
  1095    // whether to serialize closest form
  1096    serializeForm        : false,
  1097  
  1098    // how long to wait before request should occur
  1099    throttle             : 0,
  1100  
  1101    // whether to throttle first request or only repeated
  1102    throttleFirstRequest : true,
  1103  
  1104    // standard ajax settings
  1105    method            : 'get',
  1106    data              : {},
  1107    dataType          : 'json',
  1108  
  1109    // mock response
  1110    mockResponse      : false,
  1111    mockResponseAsync : false,
  1112  
  1113    // aliases for mock
  1114    response          : false,
  1115    responseAsync     : false,
  1116  
  1117  // whether onResponse should work with response value without force converting into an object
  1118    rawResponse       : false,
  1119  
  1120    // callbacks before request
  1121    beforeSend  : function(settings) { return settings; },
  1122    beforeXHR   : function(xhr) {},
  1123    onRequest   : function(promise, xhr) {},
  1124  
  1125    // after request
  1126    onResponse  : false, // function(response) { },
  1127  
  1128    // response was successful, if JSON passed validation
  1129    onSuccess   : function(response, $module) {},
  1130  
  1131    // request finished without aborting
  1132    onComplete  : function(response, $module) {},
  1133  
  1134    // failed JSON success test
  1135    onFailure   : function(response, $module) {},
  1136  
  1137    // server error
  1138    onError     : function(errorMessage, $module) {},
  1139  
  1140    // request aborted
  1141    onAbort     : function(errorMessage, $module) {},
  1142  
  1143    successTest : false,
  1144  
  1145    // errors
  1146    error : {
  1147      beforeSend        : 'The before send function has aborted the request',
  1148      error             : 'There was an error with your request',
  1149      exitConditions    : 'API Request Aborted. Exit conditions met',
  1150      JSONParse         : 'JSON could not be parsed during error handling',
  1151      legacyParameters  : 'You are using legacy API success callback names',
  1152      method            : 'The method you called is not defined',
  1153      missingAction     : 'API action used but no url was defined',
  1154      missingSerialize  : 'jquery-serialize-object is required to add form data to an existing data object',
  1155      missingURL        : 'No URL specified for api event',
  1156      noReturnedValue   : 'The beforeSend callback must return a settings object, beforeSend ignored.',
  1157      noStorage         : 'Caching responses locally requires session storage',
  1158      parseError        : 'There was an error parsing your request',
  1159      requiredParameter : 'Missing a required URL parameter: ',
  1160      statusMessage     : 'Server gave an error: ',
  1161      timeout           : 'Your request timed out'
  1162    },
  1163  
  1164    regExp  : {
  1165      required : /\{\$*[A-z0-9]+\}/g,
  1166      optional : /\{\/\$*[A-z0-9]+\}/g,
  1167    },
  1168  
  1169    className: {
  1170      loading : 'loading',
  1171      error   : 'error'
  1172    },
  1173  
  1174    selector: {
  1175      disabled : '.disabled',
  1176      form      : 'form'
  1177    },
  1178  
  1179    metadata: {
  1180      action  : 'action',
  1181      url     : 'url'
  1182    }
  1183  };
  1184  
  1185  
  1186  
  1187  })( jQuery, window, document );
  1188  
  1189  /*!
  1190   * # Fomantic-UI - Checkbox
  1191   * http://github.com/fomantic/Fomantic-UI/
  1192   *
  1193   *
  1194   * Released under the MIT license
  1195   * http://opensource.org/licenses/MIT
  1196   *
  1197   */
  1198  
  1199  ;(function ($, window, document, undefined) {
  1200  
  1201  'use strict';
  1202  
  1203  $.isFunction = $.isFunction || function(obj) {
  1204    return typeof obj === "function" && typeof obj.nodeType !== "number";
  1205  };
  1206  
  1207  window = (typeof window != 'undefined' && window.Math == Math)
  1208    ? window
  1209    : (typeof self != 'undefined' && self.Math == Math)
  1210      ? self
  1211      : Function('return this')()
  1212  ;
  1213  
  1214  $.fn.checkbox = function(parameters) {
  1215    var
  1216      $allModules    = $(this),
  1217      moduleSelector = $allModules.selector || '',
  1218  
  1219      time           = new Date().getTime(),
  1220      performance    = [],
  1221  
  1222      query          = arguments[0],
  1223      methodInvoked  = (typeof query == 'string'),
  1224      queryArguments = [].slice.call(arguments, 1),
  1225      returnedValue
  1226    ;
  1227  
  1228    $allModules
  1229      .each(function() {
  1230        var
  1231          settings        = $.extend(true, {}, $.fn.checkbox.settings, parameters),
  1232  
  1233          className       = settings.className,
  1234          namespace       = settings.namespace,
  1235          selector        = settings.selector,
  1236          error           = settings.error,
  1237  
  1238          eventNamespace  = '.' + namespace,
  1239          moduleNamespace = 'module-' + namespace,
  1240  
  1241          $module         = $(this),
  1242          $label          = $(this).children(selector.label),
  1243          $input          = $(this).children(selector.input),
  1244          input           = $input[0],
  1245  
  1246          initialLoad     = false,
  1247          shortcutPressed = false,
  1248          instance        = $module.data(moduleNamespace),
  1249  
  1250          observer,
  1251          element         = this,
  1252          module
  1253        ;
  1254  
  1255        module      = {
  1256  
  1257          initialize: function() {
  1258            module.verbose('Initializing checkbox', settings);
  1259  
  1260            module.create.label();
  1261            module.bind.events();
  1262  
  1263            module.set.tabbable();
  1264            module.hide.input();
  1265  
  1266            module.observeChanges();
  1267            module.instantiate();
  1268            module.setup();
  1269          },
  1270  
  1271          instantiate: function() {
  1272            module.verbose('Storing instance of module', module);
  1273            instance = module;
  1274            $module
  1275              .data(moduleNamespace, module)
  1276            ;
  1277          },
  1278  
  1279          destroy: function() {
  1280            module.verbose('Destroying module');
  1281            module.unbind.events();
  1282            module.show.input();
  1283            $module.removeData(moduleNamespace);
  1284          },
  1285  
  1286          fix: {
  1287            reference: function() {
  1288              if( $module.is(selector.input) ) {
  1289                module.debug('Behavior called on <input> adjusting invoked element');
  1290                $module = $module.closest(selector.checkbox);
  1291                module.refresh();
  1292              }
  1293            }
  1294          },
  1295  
  1296          setup: function() {
  1297            module.set.initialLoad();
  1298            if( module.is.indeterminate() ) {
  1299              module.debug('Initial value is indeterminate');
  1300              module.indeterminate();
  1301            }
  1302            else if( module.is.checked() ) {
  1303              module.debug('Initial value is checked');
  1304              module.check();
  1305            }
  1306            else {
  1307              module.debug('Initial value is unchecked');
  1308              module.uncheck();
  1309            }
  1310            module.remove.initialLoad();
  1311          },
  1312  
  1313          refresh: function() {
  1314            $label = $module.children(selector.label);
  1315            $input = $module.children(selector.input);
  1316            input  = $input[0];
  1317          },
  1318  
  1319          hide: {
  1320            input: function() {
  1321              module.verbose('Modifying <input> z-index to be unselectable');
  1322              $input.addClass(className.hidden);
  1323            }
  1324          },
  1325          show: {
  1326            input: function() {
  1327              module.verbose('Modifying <input> z-index to be selectable');
  1328              $input.removeClass(className.hidden);
  1329            }
  1330          },
  1331  
  1332          observeChanges: function() {
  1333            if('MutationObserver' in window) {
  1334              observer = new MutationObserver(function(mutations) {
  1335                module.debug('DOM tree modified, updating selector cache');
  1336                module.refresh();
  1337              });
  1338              observer.observe(element, {
  1339                childList : true,
  1340                subtree   : true
  1341              });
  1342              module.debug('Setting up mutation observer', observer);
  1343            }
  1344          },
  1345  
  1346          attachEvents: function(selector, event) {
  1347            var
  1348              $element = $(selector)
  1349            ;
  1350            event = $.isFunction(module[event])
  1351              ? module[event]
  1352              : module.toggle
  1353            ;
  1354            if($element.length > 0) {
  1355              module.debug('Attaching checkbox events to element', selector, event);
  1356              $element
  1357                .on('click' + eventNamespace, event)
  1358              ;
  1359            }
  1360            else {
  1361              module.error(error.notFound);
  1362            }
  1363          },
  1364  
  1365          preventDefaultOnInputTarget: function() {
  1366            if(typeof event !== 'undefined' && event !== null && $(event.target).is(selector.input)) {
  1367              module.verbose('Preventing default check action after manual check action');
  1368              event.preventDefault();
  1369            }
  1370          },
  1371  
  1372          event: {
  1373            change: function(event) {
  1374              if( !module.should.ignoreCallbacks() ) {
  1375                settings.onChange.call(input);
  1376              }
  1377            },
  1378            click: function(event) {
  1379              var
  1380                $target = $(event.target)
  1381              ;
  1382              if( $target.is(selector.input) ) {
  1383                module.verbose('Using default check action on initialized checkbox');
  1384                return;
  1385              }
  1386              if( $target.is(selector.link) ) {
  1387                module.debug('Clicking link inside checkbox, skipping toggle');
  1388                return;
  1389              }
  1390              module.toggle();
  1391              $input.focus();
  1392              event.preventDefault();
  1393            },
  1394            keydown: function(event) {
  1395              var
  1396                key     = event.which,
  1397                keyCode = {
  1398                  enter  : 13,
  1399                  space  : 32,
  1400                  escape : 27,
  1401                  left   : 37,
  1402                  up     : 38,
  1403                  right  : 39,
  1404                  down   : 40
  1405                }
  1406              ;
  1407  
  1408              var r = module.get.radios(),
  1409                  rIndex = r.index($module),
  1410                  rLen = r.length,
  1411                  checkIndex = false;
  1412  
  1413              if(key == keyCode.left || key == keyCode.up) {
  1414                checkIndex = (rIndex === 0 ? rLen : rIndex) - 1;
  1415              } else if(key == keyCode.right || key == keyCode.down) {
  1416                checkIndex = rIndex === rLen-1 ? 0 : rIndex+1;
  1417              }
  1418  
  1419              if (!module.should.ignoreCallbacks() && checkIndex !== false) {
  1420                if(settings.beforeUnchecked.apply(input)===false) {
  1421                  module.verbose('Option not allowed to be unchecked, cancelling key navigation');
  1422                  return false;
  1423                }
  1424                if (settings.beforeChecked.apply($(r[checkIndex]).children(selector.input)[0])===false) {
  1425                  module.verbose('Next option should not allow check, cancelling key navigation');
  1426                  return false;
  1427                }
  1428              }
  1429  
  1430              if(key == keyCode.escape) {
  1431                module.verbose('Escape key pressed blurring field');
  1432                $input.blur();
  1433                shortcutPressed = true;
  1434              }
  1435              else if(!event.ctrlKey && ( key == keyCode.space || (key == keyCode.enter && settings.enableEnterKey)) ) {
  1436                module.verbose('Enter/space key pressed, toggling checkbox');
  1437                module.toggle();
  1438                shortcutPressed = true;
  1439              }
  1440              else {
  1441                shortcutPressed = false;
  1442              }
  1443            },
  1444            keyup: function(event) {
  1445              if(shortcutPressed) {
  1446                event.preventDefault();
  1447              }
  1448            }
  1449          },
  1450  
  1451          check: function() {
  1452            if( !module.should.allowCheck() ) {
  1453              return;
  1454            }
  1455            module.debug('Checking checkbox', $input);
  1456            module.set.checked();
  1457            if( !module.should.ignoreCallbacks() ) {
  1458              settings.onChecked.call(input);
  1459              module.trigger.change();
  1460            }
  1461            module.preventDefaultOnInputTarget();
  1462          },
  1463  
  1464          uncheck: function() {
  1465            if( !module.should.allowUncheck() ) {
  1466              return;
  1467            }
  1468            module.debug('Unchecking checkbox');
  1469            module.set.unchecked();
  1470            if( !module.should.ignoreCallbacks() ) {
  1471              settings.onUnchecked.call(input);
  1472              module.trigger.change();
  1473            }
  1474            module.preventDefaultOnInputTarget();
  1475          },
  1476  
  1477          indeterminate: function() {
  1478            if( module.should.allowIndeterminate() ) {
  1479              module.debug('Checkbox is already indeterminate');
  1480              return;
  1481            }
  1482            module.debug('Making checkbox indeterminate');
  1483            module.set.indeterminate();
  1484            if( !module.should.ignoreCallbacks() ) {
  1485              settings.onIndeterminate.call(input);
  1486              module.trigger.change();
  1487            }
  1488          },
  1489  
  1490          determinate: function() {
  1491            if( module.should.allowDeterminate() ) {
  1492              module.debug('Checkbox is already determinate');
  1493              return;
  1494            }
  1495            module.debug('Making checkbox determinate');
  1496            module.set.determinate();
  1497            if( !module.should.ignoreCallbacks() ) {
  1498              settings.onDeterminate.call(input);
  1499              module.trigger.change();
  1500            }
  1501          },
  1502  
  1503          enable: function() {
  1504            if( module.is.enabled() ) {
  1505              module.debug('Checkbox is already enabled');
  1506              return;
  1507            }
  1508            module.debug('Enabling checkbox');
  1509            module.set.enabled();
  1510            if( !module.should.ignoreCallbacks() ) {
  1511              settings.onEnable.call(input);
  1512              // preserve legacy callbacks
  1513              settings.onEnabled.call(input);
  1514              module.trigger.change();
  1515            }
  1516          },
  1517  
  1518          disable: function() {
  1519            if( module.is.disabled() ) {
  1520              module.debug('Checkbox is already disabled');
  1521              return;
  1522            }
  1523            module.debug('Disabling checkbox');
  1524            module.set.disabled();
  1525            if( !module.should.ignoreCallbacks() ) {
  1526              settings.onDisable.call(input);
  1527              // preserve legacy callbacks
  1528              settings.onDisabled.call(input);
  1529              module.trigger.change();
  1530            }
  1531          },
  1532  
  1533          get: {
  1534            radios: function() {
  1535              var
  1536                name = module.get.name()
  1537              ;
  1538              return $('input[name="' + name + '"]').closest(selector.checkbox);
  1539            },
  1540            otherRadios: function() {
  1541              return module.get.radios().not($module);
  1542            },
  1543            name: function() {
  1544              return $input.attr('name');
  1545            }
  1546          },
  1547  
  1548          is: {
  1549            initialLoad: function() {
  1550              return initialLoad;
  1551            },
  1552            radio: function() {
  1553              return ($input.hasClass(className.radio) || $input.attr('type') == 'radio');
  1554            },
  1555            indeterminate: function() {
  1556              return $input.prop('indeterminate') !== undefined && $input.prop('indeterminate');
  1557            },
  1558            checked: function() {
  1559              return $input.prop('checked') !== undefined && $input.prop('checked');
  1560            },
  1561            disabled: function() {
  1562              return $input.prop('disabled') !== undefined && $input.prop('disabled');
  1563            },
  1564            enabled: function() {
  1565              return !module.is.disabled();
  1566            },
  1567            determinate: function() {
  1568              return !module.is.indeterminate();
  1569            },
  1570            unchecked: function() {
  1571              return !module.is.checked();
  1572            }
  1573          },
  1574  
  1575          should: {
  1576            allowCheck: function() {
  1577              if(module.is.determinate() && module.is.checked() && !module.is.initialLoad() ) {
  1578                module.debug('Should not allow check, checkbox is already checked');
  1579                return false;
  1580              }
  1581              if(!module.should.ignoreCallbacks() && settings.beforeChecked.apply(input) === false) {
  1582                module.debug('Should not allow check, beforeChecked cancelled');
  1583                return false;
  1584              }
  1585              return true;
  1586            },
  1587            allowUncheck: function() {
  1588              if(module.is.determinate() && module.is.unchecked() && !module.is.initialLoad() ) {
  1589                module.debug('Should not allow uncheck, checkbox is already unchecked');
  1590                return false;
  1591              }
  1592              if(!module.should.ignoreCallbacks() && settings.beforeUnchecked.apply(input) === false) {
  1593                module.debug('Should not allow uncheck, beforeUnchecked cancelled');
  1594                return false;
  1595              }
  1596              return true;
  1597            },
  1598            allowIndeterminate: function() {
  1599              if(module.is.indeterminate() && !module.is.initialLoad() ) {
  1600                module.debug('Should not allow indeterminate, checkbox is already indeterminate');
  1601                return false;
  1602              }
  1603              if(!module.should.ignoreCallbacks() && settings.beforeIndeterminate.apply(input) === false) {
  1604                module.debug('Should not allow indeterminate, beforeIndeterminate cancelled');
  1605                return false;
  1606              }
  1607              return true;
  1608            },
  1609            allowDeterminate: function() {
  1610              if(module.is.determinate() && !module.is.initialLoad() ) {
  1611                module.debug('Should not allow determinate, checkbox is already determinate');
  1612                return false;
  1613              }
  1614              if(!module.should.ignoreCallbacks() && settings.beforeDeterminate.apply(input) === false) {
  1615                module.debug('Should not allow determinate, beforeDeterminate cancelled');
  1616                return false;
  1617              }
  1618              return true;
  1619            },
  1620            ignoreCallbacks: function() {
  1621              return (initialLoad && !settings.fireOnInit);
  1622            }
  1623          },
  1624  
  1625          can: {
  1626            change: function() {
  1627              return !( $module.hasClass(className.disabled) || $module.hasClass(className.readOnly) || $input.prop('disabled') || $input.prop('readonly') );
  1628            },
  1629            uncheck: function() {
  1630              return (typeof settings.uncheckable === 'boolean')
  1631                ? settings.uncheckable
  1632                : !module.is.radio()
  1633              ;
  1634            }
  1635          },
  1636  
  1637          set: {
  1638            initialLoad: function() {
  1639              initialLoad = true;
  1640            },
  1641            checked: function() {
  1642              module.verbose('Setting class to checked');
  1643              $module
  1644                .removeClass(className.indeterminate)
  1645                .addClass(className.checked)
  1646              ;
  1647              if( module.is.radio() ) {
  1648                module.uncheckOthers();
  1649              }
  1650              if(!module.is.indeterminate() && module.is.checked()) {
  1651                module.debug('Input is already checked, skipping input property change');
  1652                return;
  1653              }
  1654              module.verbose('Setting state to checked', input);
  1655              $input
  1656                .prop('indeterminate', false)
  1657                .prop('checked', true)
  1658              ;
  1659            },
  1660            unchecked: function() {
  1661              module.verbose('Removing checked class');
  1662              $module
  1663                .removeClass(className.indeterminate)
  1664                .removeClass(className.checked)
  1665              ;
  1666              if(!module.is.indeterminate() &&  module.is.unchecked() ) {
  1667                module.debug('Input is already unchecked');
  1668                return;
  1669              }
  1670              module.debug('Setting state to unchecked');
  1671              $input
  1672                .prop('indeterminate', false)
  1673                .prop('checked', false)
  1674              ;
  1675            },
  1676            indeterminate: function() {
  1677              module.verbose('Setting class to indeterminate');
  1678              $module
  1679                .addClass(className.indeterminate)
  1680              ;
  1681              if( module.is.indeterminate() ) {
  1682                module.debug('Input is already indeterminate, skipping input property change');
  1683                return;
  1684              }
  1685              module.debug('Setting state to indeterminate');
  1686              $input
  1687                .prop('indeterminate', true)
  1688              ;
  1689            },
  1690            determinate: function() {
  1691              module.verbose('Removing indeterminate class');
  1692              $module
  1693                .removeClass(className.indeterminate)
  1694              ;
  1695              if( module.is.determinate() ) {
  1696                module.debug('Input is already determinate, skipping input property change');
  1697                return;
  1698              }
  1699              module.debug('Setting state to determinate');
  1700              $input
  1701                .prop('indeterminate', false)
  1702              ;
  1703            },
  1704            disabled: function() {
  1705              module.verbose('Setting class to disabled');
  1706              $module
  1707                .addClass(className.disabled)
  1708              ;
  1709              if( module.is.disabled() ) {
  1710                module.debug('Input is already disabled, skipping input property change');
  1711                return;
  1712              }
  1713              module.debug('Setting state to disabled');
  1714              $input
  1715                .prop('disabled', 'disabled')
  1716              ;
  1717            },
  1718            enabled: function() {
  1719              module.verbose('Removing disabled class');
  1720              $module.removeClass(className.disabled);
  1721              if( module.is.enabled() ) {
  1722                module.debug('Input is already enabled, skipping input property change');
  1723                return;
  1724              }
  1725              module.debug('Setting state to enabled');
  1726              $input
  1727                .prop('disabled', false)
  1728              ;
  1729            },
  1730            tabbable: function() {
  1731              module.verbose('Adding tabindex to checkbox');
  1732              if( $input.attr('tabindex') === undefined) {
  1733                $input.attr('tabindex', 0);
  1734              }
  1735            }
  1736          },
  1737  
  1738          remove: {
  1739            initialLoad: function() {
  1740              initialLoad = false;
  1741            }
  1742          },
  1743  
  1744          trigger: {
  1745            change: function() {
  1746              var
  1747                inputElement = $input[0]
  1748              ;
  1749              if(inputElement) {
  1750                var events = document.createEvent('HTMLEvents');
  1751                module.verbose('Triggering native change event');
  1752                events.initEvent('change', true, false);
  1753                inputElement.dispatchEvent(events);
  1754              }
  1755            }
  1756          },
  1757  
  1758  
  1759          create: {
  1760            label: function() {
  1761              if($input.prevAll(selector.label).length > 0) {
  1762                $input.prev(selector.label).detach().insertAfter($input);
  1763                module.debug('Moving existing label', $label);
  1764              }
  1765              else if( !module.has.label() ) {
  1766                $label = $('<label>').insertAfter($input);
  1767                module.debug('Creating label', $label);
  1768              }
  1769            }
  1770          },
  1771  
  1772          has: {
  1773            label: function() {
  1774              return ($label.length > 0);
  1775            }
  1776          },
  1777  
  1778          bind: {
  1779            events: function() {
  1780              module.verbose('Attaching checkbox events');
  1781              $module
  1782                .on('click'   + eventNamespace, module.event.click)
  1783                .on('change'  + eventNamespace, module.event.change)
  1784                .on('keydown' + eventNamespace, selector.input, module.event.keydown)
  1785                .on('keyup'   + eventNamespace, selector.input, module.event.keyup)
  1786              ;
  1787            }
  1788          },
  1789  
  1790          unbind: {
  1791            events: function() {
  1792              module.debug('Removing events');
  1793              $module
  1794                .off(eventNamespace)
  1795              ;
  1796            }
  1797          },
  1798  
  1799          uncheckOthers: function() {
  1800            var
  1801              $radios = module.get.otherRadios()
  1802            ;
  1803            module.debug('Unchecking other radios', $radios);
  1804            $radios.removeClass(className.checked);
  1805          },
  1806  
  1807          toggle: function() {
  1808            if( !module.can.change() ) {
  1809              if(!module.is.radio()) {
  1810                module.debug('Checkbox is read-only or disabled, ignoring toggle');
  1811              }
  1812              return;
  1813            }
  1814            if( module.is.indeterminate() || module.is.unchecked() ) {
  1815              module.debug('Currently unchecked');
  1816              module.check();
  1817            }
  1818            else if( module.is.checked() && module.can.uncheck() ) {
  1819              module.debug('Currently checked');
  1820              module.uncheck();
  1821            }
  1822          },
  1823          setting: function(name, value) {
  1824            module.debug('Changing setting', name, value);
  1825            if( $.isPlainObject(name) ) {
  1826              $.extend(true, settings, name);
  1827            }
  1828            else if(value !== undefined) {
  1829              if($.isPlainObject(settings[name])) {
  1830                $.extend(true, settings[name], value);
  1831              }
  1832              else {
  1833                settings[name] = value;
  1834              }
  1835            }
  1836            else {
  1837              return settings[name];
  1838            }
  1839          },
  1840          internal: function(name, value) {
  1841            if( $.isPlainObject(name) ) {
  1842              $.extend(true, module, name);
  1843            }
  1844            else if(value !== undefined) {
  1845              module[name] = value;
  1846            }
  1847            else {
  1848              return module[name];
  1849            }
  1850          },
  1851          debug: function() {
  1852            if(!settings.silent && settings.debug) {
  1853              if(settings.performance) {
  1854                module.performance.log(arguments);
  1855              }
  1856              else {
  1857                module.debug = Function.prototype.bind.call(console.info, console, settings.name + ':');
  1858                module.debug.apply(console, arguments);
  1859              }
  1860            }
  1861          },
  1862          verbose: function() {
  1863            if(!settings.silent && settings.verbose && settings.debug) {
  1864              if(settings.performance) {
  1865                module.performance.log(arguments);
  1866              }
  1867              else {
  1868                module.verbose = Function.prototype.bind.call(console.info, console, settings.name + ':');
  1869                module.verbose.apply(console, arguments);
  1870              }
  1871            }
  1872          },
  1873          error: function() {
  1874            if(!settings.silent) {
  1875              module.error = Function.prototype.bind.call(console.error, console, settings.name + ':');
  1876              module.error.apply(console, arguments);
  1877            }
  1878          },
  1879          performance: {
  1880            log: function(message) {
  1881              var
  1882                currentTime,
  1883                executionTime,
  1884                previousTime
  1885              ;
  1886              if(settings.performance) {
  1887                currentTime   = new Date().getTime();
  1888                previousTime  = time || currentTime;
  1889                executionTime = currentTime - previousTime;
  1890                time          = currentTime;
  1891                performance.push({
  1892                  'Name'           : message[0],
  1893                  'Arguments'      : [].slice.call(message, 1) || '',
  1894                  'Element'        : element,
  1895                  'Execution Time' : executionTime
  1896                });
  1897              }
  1898              clearTimeout(module.performance.timer);
  1899              module.performance.timer = setTimeout(module.performance.display, 500);
  1900            },
  1901            display: function() {
  1902              var
  1903                title = settings.name + ':',
  1904                totalTime = 0
  1905              ;
  1906              time = false;
  1907              clearTimeout(module.performance.timer);
  1908              $.each(performance, function(index, data) {
  1909                totalTime += data['Execution Time'];
  1910              });
  1911              title += ' ' + totalTime + 'ms';
  1912              if(moduleSelector) {
  1913                title += ' \'' + moduleSelector + '\'';
  1914              }
  1915              if( (console.group !== undefined || console.table !== undefined) && performance.length > 0) {
  1916                console.groupCollapsed(title);
  1917                if(console.table) {
  1918                  console.table(performance);
  1919                }
  1920                else {
  1921                  $.each(performance, function(index, data) {
  1922                    console.log(data['Name'] + ': ' + data['Execution Time']+'ms');
  1923                  });
  1924                }
  1925                console.groupEnd();
  1926              }
  1927              performance = [];
  1928            }
  1929          },
  1930          invoke: function(query, passedArguments, context) {
  1931            var
  1932              object = instance,
  1933              maxDepth,
  1934              found,
  1935              response
  1936            ;
  1937            passedArguments = passedArguments || queryArguments;
  1938            context         = element         || context;
  1939            if(typeof query == 'string' && object !== undefined) {
  1940              query    = query.split(/[\. ]/);
  1941              maxDepth = query.length - 1;
  1942              $.each(query, function(depth, value) {
  1943                var camelCaseValue = (depth != maxDepth)
  1944                  ? value + query[depth + 1].charAt(0).toUpperCase() + query[depth + 1].slice(1)
  1945                  : query
  1946                ;
  1947                if( $.isPlainObject( object[camelCaseValue] ) && (depth != maxDepth) ) {
  1948                  object = object[camelCaseValue];
  1949                }
  1950                else if( object[camelCaseValue] !== undefined ) {
  1951                  found = object[camelCaseValue];
  1952                  return false;
  1953                }
  1954                else if( $.isPlainObject( object[value] ) && (depth != maxDepth) ) {
  1955                  object = object[value];
  1956                }
  1957                else if( object[value] !== undefined ) {
  1958                  found = object[value];
  1959                  return false;
  1960                }
  1961                else {
  1962                  module.error(error.method, query);
  1963                  return false;
  1964                }
  1965              });
  1966            }
  1967            if ( $.isFunction( found ) ) {
  1968              response = found.apply(context, passedArguments);
  1969            }
  1970            else if(found !== undefined) {
  1971              response = found;
  1972            }
  1973            if(Array.isArray(returnedValue)) {
  1974              returnedValue.push(response);
  1975            }
  1976            else if(returnedValue !== undefined) {
  1977              returnedValue = [returnedValue, response];
  1978            }
  1979            else if(response !== undefined) {
  1980              returnedValue = response;
  1981            }
  1982            return found;
  1983          }
  1984        };
  1985  
  1986        if(methodInvoked) {
  1987          if(instance === undefined) {
  1988            module.initialize();
  1989          }
  1990          module.invoke(query);
  1991        }
  1992        else {
  1993          if(instance !== undefined) {
  1994            instance.invoke('destroy');
  1995          }
  1996          module.initialize();
  1997        }
  1998      })
  1999    ;
  2000  
  2001    return (returnedValue !== undefined)
  2002      ? returnedValue
  2003      : this
  2004    ;
  2005  };
  2006  
  2007  $.fn.checkbox.settings = {
  2008  
  2009    name                : 'Checkbox',
  2010    namespace           : 'checkbox',
  2011  
  2012    silent              : false,
  2013    debug               : false,
  2014    verbose             : true,
  2015    performance         : true,
  2016  
  2017    // delegated event context
  2018    uncheckable         : 'auto',
  2019    fireOnInit          : false,
  2020    enableEnterKey      : true,
  2021  
  2022    onChange            : function(){},
  2023  
  2024    beforeChecked       : function(){},
  2025    beforeUnchecked     : function(){},
  2026    beforeDeterminate   : function(){},
  2027    beforeIndeterminate : function(){},
  2028  
  2029    onChecked           : function(){},
  2030    onUnchecked         : function(){},
  2031  
  2032    onDeterminate       : function() {},
  2033    onIndeterminate     : function() {},
  2034  
  2035    onEnable            : function(){},
  2036    onDisable           : function(){},
  2037  
  2038    // preserve misspelled callbacks (will be removed in 3.0)
  2039    onEnabled           : function(){},
  2040    onDisabled          : function(){},
  2041  
  2042    className       : {
  2043      checked       : 'checked',
  2044      indeterminate : 'indeterminate',
  2045      disabled      : 'disabled',
  2046      hidden        : 'hidden',
  2047      radio         : 'radio',
  2048      readOnly      : 'read-only'
  2049    },
  2050  
  2051    error     : {
  2052      method       : 'The method you called is not defined'
  2053    },
  2054  
  2055    selector : {
  2056      checkbox : '.ui.checkbox',
  2057      label    : 'label, .box',
  2058      input    : 'input[type="checkbox"], input[type="radio"]',
  2059      link     : 'a[href]'
  2060    }
  2061  
  2062  };
  2063  
  2064  })( jQuery, window, document );
  2065  
  2066  /*!
  2067   * # Fomantic-UI - Dimmer
  2068   * http://github.com/fomantic/Fomantic-UI/
  2069   *
  2070   *
  2071   * Released under the MIT license
  2072   * http://opensource.org/licenses/MIT
  2073   *
  2074   */
  2075  
  2076  ;(function ($, window, document, undefined) {
  2077  
  2078  'use strict';
  2079  
  2080  $.isFunction = $.isFunction || function(obj) {
  2081    return typeof obj === "function" && typeof obj.nodeType !== "number";
  2082  };
  2083  
  2084  window = (typeof window != 'undefined' && window.Math == Math)
  2085    ? window
  2086    : (typeof self != 'undefined' && self.Math == Math)
  2087      ? self
  2088      : Function('return this')()
  2089  ;
  2090  
  2091  $.fn.dimmer = function(parameters) {
  2092    var
  2093      $allModules     = $(this),
  2094  
  2095      time            = new Date().getTime(),
  2096      performance     = [],
  2097  
  2098      query           = arguments[0],
  2099      methodInvoked   = (typeof query == 'string'),
  2100      queryArguments  = [].slice.call(arguments, 1),
  2101  
  2102      returnedValue
  2103    ;
  2104  
  2105    $allModules
  2106      .each(function() {
  2107        var
  2108          settings        = ( $.isPlainObject(parameters) )
  2109            ? $.extend(true, {}, $.fn.dimmer.settings, parameters)
  2110            : $.extend({}, $.fn.dimmer.settings),
  2111  
  2112          selector        = settings.selector,
  2113          namespace       = settings.namespace,
  2114          className       = settings.className,
  2115          error           = settings.error,
  2116  
  2117          eventNamespace  = '.' + namespace,
  2118          moduleNamespace = 'module-' + namespace,
  2119          moduleSelector  = $allModules.selector || '',
  2120  
  2121          clickEvent = "click", unstableClickEvent = ('ontouchstart' in document.documentElement)
  2122            ? 'touchstart'
  2123            : 'click',
  2124  
  2125          $module = $(this),
  2126          $dimmer,
  2127          $dimmable,
  2128  
  2129          element   = this,
  2130          instance  = $module.data(moduleNamespace),
  2131          module
  2132        ;
  2133  
  2134        module = {
  2135  
  2136          preinitialize: function() {
  2137            if( module.is.dimmer() ) {
  2138  
  2139              $dimmable = $module.parent();
  2140              $dimmer   = $module;
  2141            }
  2142            else {
  2143              $dimmable = $module;
  2144              if( module.has.dimmer() ) {
  2145                if(settings.dimmerName) {
  2146                  $dimmer = $dimmable.find(selector.dimmer).filter('.' + settings.dimmerName);
  2147                }
  2148                else {
  2149                  $dimmer = $dimmable.find(selector.dimmer);
  2150                }
  2151              }
  2152              else {
  2153                $dimmer = module.create();
  2154              }
  2155            }
  2156          },
  2157  
  2158          initialize: function() {
  2159            module.debug('Initializing dimmer', settings);
  2160  
  2161            module.bind.events();
  2162            module.set.dimmable();
  2163            module.instantiate();
  2164          },
  2165  
  2166          instantiate: function() {
  2167            module.verbose('Storing instance of module', module);
  2168            instance = module;
  2169            $module
  2170              .data(moduleNamespace, instance)
  2171            ;
  2172          },
  2173  
  2174          destroy: function() {
  2175            module.verbose('Destroying previous module', $dimmer);
  2176            module.unbind.events();
  2177            module.remove.variation();
  2178            $dimmable
  2179              .off(eventNamespace)
  2180            ;
  2181          },
  2182  
  2183          bind: {
  2184            events: function() {
  2185              if(settings.on == 'hover') {
  2186                $dimmable
  2187                  .on('mouseenter' + eventNamespace, module.show)
  2188                  .on('mouseleave' + eventNamespace, module.hide)
  2189                ;
  2190              }
  2191              else if(settings.on == 'click') {
  2192                $dimmable
  2193                  .on(clickEvent + eventNamespace, module.toggle)
  2194                ;
  2195              }
  2196              if( module.is.page() ) {
  2197                module.debug('Setting as a page dimmer', $dimmable);
  2198                module.set.pageDimmer();
  2199              }
  2200  
  2201              if( module.is.closable() ) {
  2202                module.verbose('Adding dimmer close event', $dimmer);
  2203                $dimmable
  2204                  .on(clickEvent + eventNamespace, selector.dimmer, module.event.click)
  2205                ;
  2206              }
  2207            }
  2208          },
  2209  
  2210          unbind: {
  2211            events: function() {
  2212              $module
  2213                .removeData(moduleNamespace)
  2214              ;
  2215              $dimmable
  2216                .off(eventNamespace)
  2217              ;
  2218            }
  2219          },
  2220  
  2221          event: {
  2222            click: function(event) {
  2223              module.verbose('Determining if event occured on dimmer', event);
  2224              if( $dimmer.find(event.target).length === 0 || $(event.target).is(selector.content) ) {
  2225                module.hide();
  2226                event.stopImmediatePropagation();
  2227              }
  2228            }
  2229          },
  2230  
  2231          addContent: function(element) {
  2232            var
  2233              $content = $(element)
  2234            ;
  2235            module.debug('Add content to dimmer', $content);
  2236            if($content.parent()[0] !== $dimmer[0]) {
  2237              $content.detach().appendTo($dimmer);
  2238            }
  2239          },
  2240  
  2241          create: function() {
  2242            var
  2243              $element = $( settings.template.dimmer(settings) )
  2244            ;
  2245            if(settings.dimmerName) {
  2246              module.debug('Creating named dimmer', settings.dimmerName);
  2247              $element.addClass(settings.dimmerName);
  2248            }
  2249            $element
  2250              .appendTo($dimmable)
  2251            ;
  2252            return $element;
  2253          },
  2254  
  2255          show: function(callback) {
  2256            callback = $.isFunction(callback)
  2257              ? callback
  2258              : function(){}
  2259            ;
  2260            module.debug('Showing dimmer', $dimmer, settings);
  2261            module.set.variation();
  2262            if( (!module.is.dimmed() || module.is.animating()) && module.is.enabled() ) {
  2263              module.animate.show(callback);
  2264              settings.onShow.call(element);
  2265              settings.onChange.call(element);
  2266            }
  2267            else {
  2268              module.debug('Dimmer is already shown or disabled');
  2269            }
  2270          },
  2271  
  2272          hide: function(callback) {
  2273            callback = $.isFunction(callback)
  2274              ? callback
  2275              : function(){}
  2276            ;
  2277            if( module.is.dimmed() || module.is.animating() ) {
  2278              module.debug('Hiding dimmer', $dimmer);
  2279              module.animate.hide(callback);
  2280              settings.onHide.call(element);
  2281              settings.onChange.call(element);
  2282            }
  2283            else {
  2284              module.debug('Dimmer is not visible');
  2285            }
  2286          },
  2287  
  2288          toggle: function() {
  2289            module.verbose('Toggling dimmer visibility', $dimmer);
  2290            if( !module.is.dimmed() ) {
  2291              module.show();
  2292            }
  2293            else {
  2294              if ( module.is.closable() ) {
  2295                module.hide();
  2296              }
  2297            }
  2298          },
  2299  
  2300          animate: {
  2301            show: function(callback) {
  2302              callback = $.isFunction(callback)
  2303                ? callback
  2304                : function(){}
  2305              ;
  2306              if(settings.useCSS && $.fn.transition !== undefined && $dimmer.transition('is supported')) {
  2307                if(settings.useFlex) {
  2308                  module.debug('Using flex dimmer');
  2309                  module.remove.legacy();
  2310                }
  2311                else {
  2312                  module.debug('Using legacy non-flex dimmer');
  2313                  module.set.legacy();
  2314                }
  2315                if(settings.opacity !== 'auto') {
  2316                  module.set.opacity();
  2317                }
  2318                $dimmer
  2319                  .transition({
  2320                    displayType : settings.useFlex
  2321                      ? 'flex'
  2322                      : 'block',
  2323                    animation   : settings.transition + ' in',
  2324                    queue       : false,
  2325                    duration    : module.get.duration(),
  2326                    useFailSafe : true,
  2327                    onStart     : function() {
  2328                      module.set.dimmed();
  2329                    },
  2330                    onComplete  : function() {
  2331                      module.set.active();
  2332                      callback();
  2333                    }
  2334                  })
  2335                ;
  2336              }
  2337              else {
  2338                module.verbose('Showing dimmer animation with javascript');
  2339                module.set.dimmed();
  2340                if(settings.opacity == 'auto') {
  2341                  settings.opacity = 0.8;
  2342                }
  2343                $dimmer
  2344                  .stop()
  2345                  .css({
  2346                    opacity : 0,
  2347                    width   : '100%',
  2348                    height  : '100%'
  2349                  })
  2350                  .fadeTo(module.get.duration(), settings.opacity, function() {
  2351                    $dimmer.removeAttr('style');
  2352                    module.set.active();
  2353                    callback();
  2354                  })
  2355                ;
  2356              }
  2357            },
  2358            hide: function(callback) {
  2359              callback = $.isFunction(callback)
  2360                ? callback
  2361                : function(){}
  2362              ;
  2363              if(settings.useCSS && $.fn.transition !== undefined && $dimmer.transition('is supported')) {
  2364                module.verbose('Hiding dimmer with css');
  2365                $dimmer
  2366                  .transition({
  2367                    displayType : settings.useFlex
  2368                      ? 'flex'
  2369                      : 'block',
  2370                    animation   : settings.transition + ' out',
  2371                    queue       : false,
  2372                    duration    : module.get.duration(),
  2373                    useFailSafe : true,
  2374                    onComplete  : function() {
  2375                      module.remove.dimmed();
  2376                      module.remove.variation();
  2377                      module.remove.active();
  2378                      callback();
  2379                    }
  2380                  })
  2381                ;
  2382              }
  2383              else {
  2384                module.verbose('Hiding dimmer with javascript');
  2385                $dimmer
  2386                  .stop()
  2387                  .fadeOut(module.get.duration(), function() {
  2388                    module.remove.dimmed();
  2389                    module.remove.active();
  2390                    $dimmer.removeAttr('style');
  2391                    callback();
  2392                  })
  2393                ;
  2394              }
  2395            }
  2396          },
  2397  
  2398          get: {
  2399            dimmer: function() {
  2400              return $dimmer;
  2401            },
  2402            duration: function() {
  2403              if(typeof settings.duration == 'object') {
  2404                if( module.is.active() ) {
  2405                  return settings.duration.hide;
  2406                }
  2407                else {
  2408                  return settings.duration.show;
  2409                }
  2410              }
  2411              return settings.duration;
  2412            }
  2413          },
  2414  
  2415          has: {
  2416            dimmer: function() {
  2417              if(settings.dimmerName) {
  2418                return ($module.find(selector.dimmer).filter('.' + settings.dimmerName).length > 0);
  2419              }
  2420              else {
  2421                return ( $module.find(selector.dimmer).length > 0 );
  2422              }
  2423            }
  2424          },
  2425  
  2426          is: {
  2427            active: function() {
  2428              return $dimmer.hasClass(className.active);
  2429            },
  2430            animating: function() {
  2431              return ( $dimmer.is(':animated') || $dimmer.hasClass(className.animating) );
  2432            },
  2433            closable: function() {
  2434              if(settings.closable == 'auto') {
  2435                if(settings.on == 'hover') {
  2436                  return false;
  2437                }
  2438                return true;
  2439              }
  2440              return settings.closable;
  2441            },
  2442            dimmer: function() {
  2443              return $module.hasClass(className.dimmer);
  2444            },
  2445            dimmable: function() {
  2446              return $module.hasClass(className.dimmable);
  2447            },
  2448            dimmed: function() {
  2449              return $dimmable.hasClass(className.dimmed);
  2450            },
  2451            disabled: function() {
  2452              return $dimmable.hasClass(className.disabled);
  2453            },
  2454            enabled: function() {
  2455              return !module.is.disabled();
  2456            },
  2457            page: function () {
  2458              return $dimmable.is('body');
  2459            },
  2460            pageDimmer: function() {
  2461              return $dimmer.hasClass(className.pageDimmer);
  2462            }
  2463          },
  2464  
  2465          can: {
  2466            show: function() {
  2467              return !$dimmer.hasClass(className.disabled);
  2468            }
  2469          },
  2470  
  2471          set: {
  2472            opacity: function(opacity) {
  2473              var
  2474                color      = $dimmer.css('background-color'),
  2475                colorArray = color.split(','),
  2476                isRGB      = (colorArray && colorArray.length >= 3)
  2477              ;
  2478              opacity    = settings.opacity === 0 ? 0 : settings.opacity || opacity;
  2479              if(isRGB) {
  2480                colorArray[2] = colorArray[2].replace(')','');
  2481                colorArray[3] = opacity + ')';
  2482                color         = colorArray.join(',');
  2483              }
  2484              else {
  2485                color = 'rgba(0, 0, 0, ' + opacity + ')';
  2486              }
  2487              module.debug('Setting opacity to', opacity);
  2488              $dimmer.css('background-color', color);
  2489            },
  2490            legacy: function() {
  2491              $dimmer.addClass(className.legacy);
  2492            },
  2493            active: function() {
  2494              $dimmer.addClass(className.active);
  2495            },
  2496            dimmable: function() {
  2497              $dimmable.addClass(className.dimmable);
  2498            },
  2499            dimmed: function() {
  2500              $dimmable.addClass(className.dimmed);
  2501            },
  2502            pageDimmer: function() {
  2503              $dimmer.addClass(className.pageDimmer);
  2504            },
  2505            disabled: function() {
  2506              $dimmer.addClass(className.disabled);
  2507            },
  2508            variation: function(variation) {
  2509              variation = variation || settings.variation;
  2510              if(variation) {
  2511                $dimmer.addClass(variation);
  2512              }
  2513            }
  2514          },
  2515  
  2516          remove: {
  2517            active: function() {
  2518              $dimmer
  2519                .removeClass(className.active)
  2520              ;
  2521            },
  2522            legacy: function() {
  2523              $dimmer.removeClass(className.legacy);
  2524            },
  2525            dimmed: function() {
  2526              $dimmable.removeClass(className.dimmed);
  2527            },
  2528            disabled: function() {
  2529              $dimmer.removeClass(className.disabled);
  2530            },
  2531            variation: function(variation) {
  2532              variation = variation || settings.variation;
  2533              if(variation) {
  2534                $dimmer.removeClass(variation);
  2535              }
  2536            }
  2537          },
  2538  
  2539          setting: function(name, value) {
  2540            module.debug('Changing setting', name, value);
  2541            if( $.isPlainObject(name) ) {
  2542              $.extend(true, settings, name);
  2543            }
  2544            else if(value !== undefined) {
  2545              if($.isPlainObject(settings[name])) {
  2546                $.extend(true, settings[name], value);
  2547              }
  2548              else {
  2549                settings[name] = value;
  2550              }
  2551            }
  2552            else {
  2553              return settings[name];
  2554            }
  2555          },
  2556          internal: function(name, value) {
  2557            if( $.isPlainObject(name) ) {
  2558              $.extend(true, module, name);
  2559            }
  2560            else if(value !== undefined) {
  2561              module[name] = value;
  2562            }
  2563            else {
  2564              return module[name];
  2565            }
  2566          },
  2567          debug: function() {
  2568            if(!settings.silent && settings.debug) {
  2569              if(settings.performance) {
  2570                module.performance.log(arguments);
  2571              }
  2572              else {
  2573                module.debug = Function.prototype.bind.call(console.info, console, settings.name + ':');
  2574                module.debug.apply(console, arguments);
  2575              }
  2576            }
  2577          },
  2578          verbose: function() {
  2579            if(!settings.silent && settings.verbose && settings.debug) {
  2580              if(settings.performance) {
  2581                module.performance.log(arguments);
  2582              }
  2583              else {
  2584                module.verbose = Function.prototype.bind.call(console.info, console, settings.name + ':');
  2585                module.verbose.apply(console, arguments);
  2586              }
  2587            }
  2588          },
  2589          error: function() {
  2590            if(!settings.silent) {
  2591              module.error = Function.prototype.bind.call(console.error, console, settings.name + ':');
  2592              module.error.apply(console, arguments);
  2593            }
  2594          },
  2595          performance: {
  2596            log: function(message) {
  2597              var
  2598                currentTime,
  2599                executionTime,
  2600                previousTime
  2601              ;
  2602              if(settings.performance) {
  2603                currentTime   = new Date().getTime();
  2604                previousTime  = time || currentTime;
  2605                executionTime = currentTime - previousTime;
  2606                time          = currentTime;
  2607                performance.push({
  2608                  'Name'           : message[0],
  2609                  'Arguments'      : [].slice.call(message, 1) || '',
  2610                  'Element'        : element,
  2611                  'Execution Time' : executionTime
  2612                });
  2613              }
  2614              clearTimeout(module.performance.timer);
  2615              module.performance.timer = setTimeout(module.performance.display, 500);
  2616            },
  2617            display: function() {
  2618              var
  2619                title = settings.name + ':',
  2620                totalTime = 0
  2621              ;
  2622              time = false;
  2623              clearTimeout(module.performance.timer);
  2624              $.each(performance, function(index, data) {
  2625                totalTime += data['Execution Time'];
  2626              });
  2627              title += ' ' + totalTime + 'ms';
  2628              if(moduleSelector) {
  2629                title += ' \'' + moduleSelector + '\'';
  2630              }
  2631              if($allModules.length > 1) {
  2632                title += ' ' + '(' + $allModules.length + ')';
  2633              }
  2634              if( (console.group !== undefined || console.table !== undefined) && performance.length > 0) {
  2635                console.groupCollapsed(title);
  2636                if(console.table) {
  2637                  console.table(performance);
  2638                }
  2639                else {
  2640                  $.each(performance, function(index, data) {
  2641                    console.log(data['Name'] + ': ' + data['Execution Time']+'ms');
  2642                  });
  2643                }
  2644                console.groupEnd();
  2645              }
  2646              performance = [];
  2647            }
  2648          },
  2649          invoke: function(query, passedArguments, context) {
  2650            var
  2651              object = instance,
  2652              maxDepth,
  2653              found,
  2654              response
  2655            ;
  2656            passedArguments = passedArguments || queryArguments;
  2657            context         = element         || context;
  2658            if(typeof query == 'string' && object !== undefined) {
  2659              query    = query.split(/[\. ]/);
  2660              maxDepth = query.length - 1;
  2661              $.each(query, function(depth, value) {
  2662                var camelCaseValue = (depth != maxDepth)
  2663                  ? value + query[depth + 1].charAt(0).toUpperCase() + query[depth + 1].slice(1)
  2664                  : query
  2665                ;
  2666                if( $.isPlainObject( object[camelCaseValue] ) && (depth != maxDepth) ) {
  2667                  object = object[camelCaseValue];
  2668                }
  2669                else if( object[camelCaseValue] !== undefined ) {
  2670                  found = object[camelCaseValue];
  2671                  return false;
  2672                }
  2673                else if( $.isPlainObject( object[value] ) && (depth != maxDepth) ) {
  2674                  object = object[value];
  2675                }
  2676                else if( object[value] !== undefined ) {
  2677                  found = object[value];
  2678                  return false;
  2679                }
  2680                else {
  2681                  module.error(error.method, query);
  2682                  return false;
  2683                }
  2684              });
  2685            }
  2686            if ( $.isFunction( found ) ) {
  2687              response = found.apply(context, passedArguments);
  2688            }
  2689            else if(found !== undefined) {
  2690              response = found;
  2691            }
  2692            if(Array.isArray(returnedValue)) {
  2693              returnedValue.push(response);
  2694            }
  2695            else if(returnedValue !== undefined) {
  2696              returnedValue = [returnedValue, response];
  2697            }
  2698            else if(response !== undefined) {
  2699              returnedValue = response;
  2700            }
  2701            return found;
  2702          }
  2703        };
  2704  
  2705        module.preinitialize();
  2706  
  2707        if(methodInvoked) {
  2708          if(instance === undefined) {
  2709            module.initialize();
  2710          }
  2711          module.invoke(query);
  2712        }
  2713        else {
  2714          if(instance !== undefined) {
  2715            instance.invoke('destroy');
  2716          }
  2717          module.initialize();
  2718        }
  2719      })
  2720    ;
  2721  
  2722    return (returnedValue !== undefined)
  2723      ? returnedValue
  2724      : this
  2725    ;
  2726  };
  2727  
  2728  $.fn.dimmer.settings = {
  2729  
  2730    name        : 'Dimmer',
  2731    namespace   : 'dimmer',
  2732  
  2733    silent      : false,
  2734    debug       : false,
  2735    verbose     : false,
  2736    performance : true,
  2737  
  2738    // whether should use flex layout
  2739    useFlex     : true,
  2740  
  2741    // name to distinguish between multiple dimmers in context
  2742    dimmerName  : false,
  2743  
  2744    // whether to add a variation type
  2745    variation   : false,
  2746  
  2747    // whether to bind close events
  2748    closable    : 'auto',
  2749  
  2750    // whether to use css animations
  2751    useCSS      : true,
  2752  
  2753    // css animation to use
  2754    transition  : 'fade',
  2755  
  2756    // event to bind to
  2757    on          : false,
  2758  
  2759    // overriding opacity value
  2760    opacity     : 'auto',
  2761  
  2762    // transition durations
  2763    duration    : {
  2764      show : 500,
  2765      hide : 500
  2766    },
  2767  // whether the dynamically created dimmer should have a loader
  2768    displayLoader: false,
  2769    loaderText  : false,
  2770    loaderVariation : '',
  2771  
  2772    onChange    : function(){},
  2773    onShow      : function(){},
  2774    onHide      : function(){},
  2775  
  2776    error   : {
  2777      method   : 'The method you called is not defined.'
  2778    },
  2779  
  2780    className : {
  2781      active     : 'active',
  2782      animating  : 'animating',
  2783      dimmable   : 'dimmable',
  2784      dimmed     : 'dimmed',
  2785      dimmer     : 'dimmer',
  2786      disabled   : 'disabled',
  2787      hide       : 'hide',
  2788      legacy     : 'legacy',
  2789      pageDimmer : 'page',
  2790      show       : 'show',
  2791      loader     : 'ui loader'
  2792    },
  2793  
  2794    selector: {
  2795      dimmer   : '> .ui.dimmer',
  2796      content  : '.ui.dimmer > .content, .ui.dimmer > .content > .center'
  2797    },
  2798  
  2799    template: {
  2800      dimmer: function(settings) {
  2801          var d = $('<div/>').addClass('ui dimmer'),l;
  2802          if(settings.displayLoader) {
  2803            l = $('<div/>')
  2804                .addClass(settings.className.loader)
  2805                .addClass(settings.loaderVariation);
  2806            if(!!settings.loaderText){
  2807              l.text(settings.loaderText);
  2808              l.addClass('text');
  2809            }
  2810            d.append(l);
  2811          }
  2812          return d;
  2813      }
  2814    }
  2815  
  2816  };
  2817  
  2818  })( jQuery, window, document );
  2819  
  2820  /*!
  2821   * # Fomantic-UI - Dropdown
  2822   * http://github.com/fomantic/Fomantic-UI/
  2823   *
  2824   *
  2825   * Released under the MIT license
  2826   * http://opensource.org/licenses/MIT
  2827   *
  2828   */
  2829  
  2830  ;(function ($, window, document, undefined) {
  2831  
  2832  'use strict';
  2833  
  2834  $.isFunction = $.isFunction || function(obj) {
  2835    return typeof obj === "function" && typeof obj.nodeType !== "number";
  2836  };
  2837  
  2838  window = (typeof window != 'undefined' && window.Math == Math)
  2839    ? window
  2840    : (typeof self != 'undefined' && self.Math == Math)
  2841      ? self
  2842      : Function('return this')()
  2843  ;
  2844  
  2845  $.fn.dropdown = function(parameters) {
  2846    var
  2847      $allModules    = $(this),
  2848      $document      = $(document),
  2849  
  2850      moduleSelector = $allModules.selector || '',
  2851  
  2852      hasTouch       = ('ontouchstart' in document.documentElement),
  2853      clickEvent = "click", unstableClickEvent = hasTouch
  2854          ? 'touchstart'
  2855          : 'click',
  2856  
  2857      time           = new Date().getTime(),
  2858      performance    = [],
  2859  
  2860      query          = arguments[0],
  2861      methodInvoked  = (typeof query == 'string'),
  2862      queryArguments = [].slice.call(arguments, 1),
  2863      returnedValue
  2864    ;
  2865  
  2866    $allModules
  2867      .each(function(elementIndex) {
  2868        var
  2869          settings          = ( $.isPlainObject(parameters) )
  2870            ? $.extend(true, {}, $.fn.dropdown.settings, parameters)
  2871            : $.extend({}, $.fn.dropdown.settings),
  2872  
  2873          className       = settings.className,
  2874          message         = settings.message,
  2875          fields          = settings.fields,
  2876          keys            = settings.keys,
  2877          metadata        = settings.metadata,
  2878          namespace       = settings.namespace,
  2879          regExp          = settings.regExp,
  2880          selector        = settings.selector,
  2881          error           = settings.error,
  2882          templates       = settings.templates,
  2883  
  2884          eventNamespace  = '.' + namespace,
  2885          moduleNamespace = 'module-' + namespace,
  2886  
  2887          $module         = $(this),
  2888          $context        = $(settings.context),
  2889          $text           = $module.find(selector.text),
  2890          $search         = $module.find(selector.search),
  2891          $sizer          = $module.find(selector.sizer),
  2892          $input          = $module.find(selector.input),
  2893          $icon           = $module.find(selector.icon),
  2894          $clear          = $module.find(selector.clearIcon),
  2895  
  2896          $combo = ($module.prev().find(selector.text).length > 0)
  2897            ? $module.prev().find(selector.text)
  2898            : $module.prev(),
  2899  
  2900          $menu           = $module.children(selector.menu),
  2901          $item           = $menu.find(selector.item),
  2902          $divider        = settings.hideDividers ? $item.parent().children(selector.divider) : $(),
  2903  
  2904          activated       = false,
  2905          itemActivated   = false,
  2906          internalChange  = false,
  2907          iconClicked     = false,
  2908          element         = this,
  2909          instance        = $module.data(moduleNamespace),
  2910  
  2911          selectActionActive,
  2912          initialLoad,
  2913          pageLostFocus,
  2914          willRefocus,
  2915          elementNamespace,
  2916          id,
  2917          selectObserver,
  2918          menuObserver,
  2919          classObserver,
  2920          module
  2921        ;
  2922  
  2923        module = {
  2924  
  2925          initialize: function() {
  2926            module.debug('Initializing dropdown', settings);
  2927  
  2928            if( module.is.alreadySetup() ) {
  2929              module.setup.reference();
  2930            }
  2931            else {
  2932              if (settings.ignoreDiacritics && !String.prototype.normalize) {
  2933                settings.ignoreDiacritics = false;
  2934                module.error(error.noNormalize, element);
  2935              }
  2936  
  2937              module.setup.layout();
  2938  
  2939              if(settings.values) {
  2940                module.set.initialLoad();
  2941                module.change.values(settings.values);
  2942                module.remove.initialLoad();
  2943              }
  2944  
  2945              module.refreshData();
  2946  
  2947              module.save.defaults();
  2948              module.restore.selected();
  2949  
  2950              module.create.id();
  2951              module.bind.events();
  2952  
  2953              module.observeChanges();
  2954              module.instantiate();
  2955            }
  2956  
  2957          },
  2958  
  2959          instantiate: function() {
  2960            module.verbose('Storing instance of dropdown', module);
  2961            instance = module;
  2962            $module
  2963              .data(moduleNamespace, module)
  2964            ;
  2965          },
  2966  
  2967          destroy: function() {
  2968            module.verbose('Destroying previous dropdown', $module);
  2969            module.remove.tabbable();
  2970            module.remove.active();
  2971            $menu.transition('stop all');
  2972            $menu.removeClass(className.visible).addClass(className.hidden);
  2973            $module
  2974              .off(eventNamespace)
  2975              .removeData(moduleNamespace)
  2976            ;
  2977            $menu
  2978              .off(eventNamespace)
  2979            ;
  2980            $document
  2981              .off(elementNamespace)
  2982            ;
  2983            module.disconnect.menuObserver();
  2984            module.disconnect.selectObserver();
  2985            module.disconnect.classObserver();
  2986          },
  2987  
  2988          observeChanges: function() {
  2989            if('MutationObserver' in window) {
  2990              selectObserver = new MutationObserver(module.event.select.mutation);
  2991              menuObserver   = new MutationObserver(module.event.menu.mutation);
  2992              classObserver  = new MutationObserver(module.event.class.mutation);
  2993              module.debug('Setting up mutation observer', selectObserver, menuObserver, classObserver);
  2994              module.observe.select();
  2995              module.observe.menu();
  2996              module.observe.class();
  2997            }
  2998          },
  2999  
  3000          disconnect: {
  3001            menuObserver: function() {
  3002              if(menuObserver) {
  3003                menuObserver.disconnect();
  3004              }
  3005            },
  3006            selectObserver: function() {
  3007              if(selectObserver) {
  3008                selectObserver.disconnect();
  3009              }
  3010            },
  3011            classObserver: function() {
  3012              if(classObserver) {
  3013                classObserver.disconnect();
  3014              }
  3015            }
  3016          },
  3017          observe: {
  3018            select: function() {
  3019              if(module.has.input() && selectObserver) {
  3020                selectObserver.observe($module[0], {
  3021                  childList : true,
  3022                  subtree   : true
  3023                });
  3024              }
  3025            },
  3026            menu: function() {
  3027              if(module.has.menu() && menuObserver) {
  3028                menuObserver.observe($menu[0], {
  3029                  childList : true,
  3030                  subtree   : true
  3031                });
  3032              }
  3033            },
  3034            class: function() {
  3035              if(module.has.search() && classObserver) {
  3036                classObserver.observe($module[0], {
  3037                  attributes : true
  3038                });
  3039              }
  3040            }
  3041          },
  3042  
  3043          create: {
  3044            id: function() {
  3045              id = (Math.random().toString(16) + '000000000').substr(2, 8);
  3046              elementNamespace = '.' + id;
  3047              module.verbose('Creating unique id for element', id);
  3048            },
  3049            userChoice: function(values) {
  3050              var
  3051                $userChoices,
  3052                $userChoice,
  3053                isUserValue,
  3054                html
  3055              ;
  3056              values = values || module.get.userValues();
  3057              if(!values) {
  3058                return false;
  3059              }
  3060              values = Array.isArray(values)
  3061                ? values
  3062                : [values]
  3063              ;
  3064              $.each(values, function(index, value) {
  3065                if(module.get.item(value) === false) {
  3066                  html         = settings.templates.addition( module.add.variables(message.addResult, value) );
  3067                  $userChoice  = $('<div />')
  3068                    .html(html)
  3069                    .attr('data-' + metadata.value, value)
  3070                    .attr('data-' + metadata.text, value)
  3071                    .addClass(className.addition)
  3072                    .addClass(className.item)
  3073                  ;
  3074                  if(settings.hideAdditions) {
  3075                    $userChoice.addClass(className.hidden);
  3076                  }
  3077                  $userChoices = ($userChoices === undefined)
  3078                    ? $userChoice
  3079                    : $userChoices.add($userChoice)
  3080                  ;
  3081                  module.verbose('Creating user choices for value', value, $userChoice);
  3082                }
  3083              });
  3084              return $userChoices;
  3085            },
  3086            userLabels: function(value) {
  3087              var
  3088                userValues = module.get.userValues()
  3089              ;
  3090              if(userValues) {
  3091                module.debug('Adding user labels', userValues);
  3092                $.each(userValues, function(index, value) {
  3093                  module.verbose('Adding custom user value');
  3094                  module.add.label(value, value);
  3095                });
  3096              }
  3097            },
  3098            menu: function() {
  3099              $menu = $('<div />')
  3100                .addClass(className.menu)
  3101                .appendTo($module)
  3102              ;
  3103            },
  3104            sizer: function() {
  3105              $sizer = $('<span />')
  3106                .addClass(className.sizer)
  3107                .insertAfter($search)
  3108              ;
  3109            }
  3110          },
  3111  
  3112          search: function(query) {
  3113            query = (query !== undefined)
  3114              ? query
  3115              : module.get.query()
  3116            ;
  3117            module.verbose('Searching for query', query);
  3118            if(module.has.minCharacters(query)) {
  3119              module.filter(query);
  3120            }
  3121            else {
  3122              module.hide(null,true);
  3123            }
  3124          },
  3125  
  3126          select: {
  3127            firstUnfiltered: function() {
  3128              module.verbose('Selecting first non-filtered element');
  3129              module.remove.selectedItem();
  3130              $item
  3131                .not(selector.unselectable)
  3132                .not(selector.addition + selector.hidden)
  3133                  .eq(0)
  3134                  .addClass(className.selected)
  3135              ;
  3136            },
  3137            nextAvailable: function($selected) {
  3138              $selected = $selected.eq(0);
  3139              var
  3140                $nextAvailable = $selected.nextAll(selector.item).not(selector.unselectable).eq(0),
  3141                $prevAvailable = $selected.prevAll(selector.item).not(selector.unselectable).eq(0),
  3142                hasNext        = ($nextAvailable.length > 0)
  3143              ;
  3144              if(hasNext) {
  3145                module.verbose('Moving selection to', $nextAvailable);
  3146                $nextAvailable.addClass(className.selected);
  3147              }
  3148              else {
  3149                module.verbose('Moving selection to', $prevAvailable);
  3150                $prevAvailable.addClass(className.selected);
  3151              }
  3152            }
  3153          },
  3154  
  3155          setup: {
  3156            api: function() {
  3157              var
  3158                apiSettings = {
  3159                  debug   : settings.debug,
  3160                  urlData : {
  3161                    value : module.get.value(),
  3162                    query : module.get.query()
  3163                  },
  3164                  on    : false
  3165                }
  3166              ;
  3167              module.verbose('First request, initializing API');
  3168              $module
  3169                .api(apiSettings)
  3170              ;
  3171            },
  3172            layout: function() {
  3173              if( $module.is('select') ) {
  3174                module.setup.select();
  3175                module.setup.returnedObject();
  3176              }
  3177              if( !module.has.menu() ) {
  3178                module.create.menu();
  3179              }
  3180              if ( module.is.selection() && module.is.clearable() && !module.has.clearItem() ) {
  3181                module.verbose('Adding clear icon');
  3182                $clear = $('<i />')
  3183                  .addClass('remove icon')
  3184                  .insertBefore($text)
  3185                ;
  3186              }
  3187              if( module.is.search() && !module.has.search() ) {
  3188                module.verbose('Adding search input');
  3189                $search = $('<input />')
  3190                  .addClass(className.search)
  3191                  .prop('autocomplete', 'off')
  3192                  .insertBefore($text)
  3193                ;
  3194              }
  3195              if( module.is.multiple() && module.is.searchSelection() && !module.has.sizer()) {
  3196                module.create.sizer();
  3197              }
  3198              if(settings.allowTab) {
  3199                module.set.tabbable();
  3200              }
  3201            },
  3202            select: function() {
  3203              var
  3204                selectValues  = module.get.selectValues()
  3205              ;
  3206              module.debug('Dropdown initialized on a select', selectValues);
  3207              if( $module.is('select') ) {
  3208                $input = $module;
  3209              }
  3210              // see if select is placed correctly already
  3211              if($input.parent(selector.dropdown).length > 0) {
  3212                module.debug('UI dropdown already exists. Creating dropdown menu only');
  3213                $module = $input.closest(selector.dropdown);
  3214                if( !module.has.menu() ) {
  3215                  module.create.menu();
  3216                }
  3217                $menu = $module.children(selector.menu);
  3218                module.setup.menu(selectValues);
  3219              }
  3220              else {
  3221                module.debug('Creating entire dropdown from select');
  3222                $module = $('<div />')
  3223                  .attr('class', $input.attr('class') )
  3224                  .addClass(className.selection)
  3225                  .addClass(className.dropdown)
  3226                  .html( templates.dropdown(selectValues, fields, settings.preserveHTML, settings.className) )
  3227                  .insertBefore($input)
  3228                ;
  3229                if($input.hasClass(className.multiple) && $input.prop('multiple') === false) {
  3230                  module.error(error.missingMultiple);
  3231                  $input.prop('multiple', true);
  3232                }
  3233                if($input.is('[multiple]')) {
  3234                  module.set.multiple();
  3235                }
  3236                if ($input.prop('disabled')) {
  3237                  module.debug('Disabling dropdown');
  3238                  $module.addClass(className.disabled);
  3239                }
  3240                $input
  3241                  .removeAttr('required')
  3242                  .removeAttr('class')
  3243                  .detach()
  3244                  .prependTo($module)
  3245                ;
  3246              }
  3247              module.refresh();
  3248            },
  3249            menu: function(values) {
  3250              $menu.html( templates.menu(values, fields,settings.preserveHTML,settings.className));
  3251              $item    = $menu.find(selector.item);
  3252              $divider = settings.hideDividers ? $item.parent().children(selector.divider) : $();
  3253            },
  3254            reference: function() {
  3255              module.debug('Dropdown behavior was called on select, replacing with closest dropdown');
  3256              // replace module reference
  3257              $module  = $module.parent(selector.dropdown);
  3258              instance = $module.data(moduleNamespace);
  3259              element  = $module.get(0);
  3260              module.refresh();
  3261              module.setup.returnedObject();
  3262            },
  3263            returnedObject: function() {
  3264              var
  3265                $firstModules = $allModules.slice(0, elementIndex),
  3266                $lastModules  = $allModules.slice(elementIndex + 1)
  3267              ;
  3268              // adjust all modules to use correct reference
  3269              $allModules = $firstModules.add($module).add($lastModules);
  3270            }
  3271          },
  3272  
  3273          refresh: function() {
  3274            module.refreshSelectors();
  3275            module.refreshData();
  3276          },
  3277  
  3278          refreshItems: function() {
  3279            $item    = $menu.find(selector.item);
  3280            $divider = settings.hideDividers ? $item.parent().children(selector.divider) : $();
  3281          },
  3282  
  3283          refreshSelectors: function() {
  3284            module.verbose('Refreshing selector cache');
  3285            $text   = $module.find(selector.text);
  3286            $search = $module.find(selector.search);
  3287            $input  = $module.find(selector.input);
  3288            $icon   = $module.find(selector.icon);
  3289            $combo  = ($module.prev().find(selector.text).length > 0)
  3290              ? $module.prev().find(selector.text)
  3291              : $module.prev()
  3292            ;
  3293            $menu    = $module.children(selector.menu);
  3294            $item    = $menu.find(selector.item);
  3295            $divider = settings.hideDividers ? $item.parent().children(selector.divider) : $();
  3296          },
  3297  
  3298          refreshData: function() {
  3299            module.verbose('Refreshing cached metadata');
  3300            $item
  3301              .removeData(metadata.text)
  3302              .removeData(metadata.value)
  3303            ;
  3304          },
  3305  
  3306          clearData: function() {
  3307            module.verbose('Clearing metadata');
  3308            $item
  3309              .removeData(metadata.text)
  3310              .removeData(metadata.value)
  3311            ;
  3312            $module
  3313              .removeData(metadata.defaultText)
  3314              .removeData(metadata.defaultValue)
  3315              .removeData(metadata.placeholderText)
  3316            ;
  3317          },
  3318  
  3319          toggle: function() {
  3320            module.verbose('Toggling menu visibility');
  3321            if( !module.is.active() ) {
  3322              module.show();
  3323            }
  3324            else {
  3325              module.hide();
  3326            }
  3327          },
  3328  
  3329          show: function(callback, preventFocus) {
  3330            callback = $.isFunction(callback)
  3331              ? callback
  3332              : function(){}
  3333            ;
  3334            if(!module.can.show() && module.is.remote()) {
  3335              module.debug('No API results retrieved, searching before show');
  3336              module.queryRemote(module.get.query(), module.show);
  3337            }
  3338            if( module.can.show() && !module.is.active() ) {
  3339              module.debug('Showing dropdown');
  3340              if(module.has.message() && !(module.has.maxSelections() || module.has.allResultsFiltered()) ) {
  3341                module.remove.message();
  3342              }
  3343              if(module.is.allFiltered()) {
  3344                return true;
  3345              }
  3346              if(settings.onShow.call(element) !== false) {
  3347                module.animate.show(function() {
  3348                  if( module.can.click() ) {
  3349                    module.bind.intent();
  3350                  }
  3351                  if(module.has.search() && !preventFocus) {
  3352                    module.focusSearch();
  3353                  }
  3354                  module.set.visible();
  3355                  callback.call(element);
  3356                });
  3357              }
  3358            }
  3359          },
  3360  
  3361          hide: function(callback, preventBlur) {
  3362            callback = $.isFunction(callback)
  3363              ? callback
  3364              : function(){}
  3365            ;
  3366            if( module.is.active() && !module.is.animatingOutward() ) {
  3367              module.debug('Hiding dropdown');
  3368              if(settings.onHide.call(element) !== false) {
  3369                module.animate.hide(function() {
  3370                  module.remove.visible();
  3371                  // hidding search focus
  3372                  if ( module.is.focusedOnSearch() && preventBlur !== true ) {
  3373                    $search.blur();
  3374                  }
  3375                  callback.call(element);
  3376                });
  3377              }
  3378            } else if( module.can.click() ) {
  3379                module.unbind.intent();
  3380            }
  3381            iconClicked = false;
  3382          },
  3383  
  3384          hideOthers: function() {
  3385            module.verbose('Finding other dropdowns to hide');
  3386            $allModules
  3387              .not($module)
  3388                .has(selector.menu + '.' + className.visible)
  3389                  .dropdown('hide')
  3390            ;
  3391          },
  3392  
  3393          hideMenu: function() {
  3394            module.verbose('Hiding menu  instantaneously');
  3395            module.remove.active();
  3396            module.remove.visible();
  3397            $menu.transition('hide');
  3398          },
  3399  
  3400          hideSubMenus: function() {
  3401            var
  3402              $subMenus = $menu.children(selector.item).find(selector.menu)
  3403            ;
  3404            module.verbose('Hiding sub menus', $subMenus);
  3405            $subMenus.transition('hide');
  3406          },
  3407  
  3408          bind: {
  3409            events: function() {
  3410              module.bind.keyboardEvents();
  3411              module.bind.inputEvents();
  3412              module.bind.mouseEvents();
  3413            },
  3414            keyboardEvents: function() {
  3415              module.verbose('Binding keyboard events');
  3416              $module
  3417                .on('keydown' + eventNamespace, module.event.keydown)
  3418              ;
  3419              if( module.has.search() ) {
  3420                $module
  3421                  .on(module.get.inputEvent() + eventNamespace, selector.search, module.event.input)
  3422                ;
  3423              }
  3424              if( module.is.multiple() ) {
  3425                $document
  3426                  .on('keydown' + elementNamespace, module.event.document.keydown)
  3427                ;
  3428              }
  3429            },
  3430            inputEvents: function() {
  3431              module.verbose('Binding input change events');
  3432              $module
  3433                .on('change' + eventNamespace, selector.input, module.event.change)
  3434              ;
  3435            },
  3436            mouseEvents: function() {
  3437              module.verbose('Binding mouse events');
  3438              if(module.is.multiple()) {
  3439                $module
  3440                  .on(clickEvent   + eventNamespace, selector.label,  module.event.label.click)
  3441                  .on(clickEvent   + eventNamespace, selector.remove, module.event.remove.click)
  3442                ;
  3443              }
  3444              if( module.is.searchSelection() ) {
  3445                $module
  3446                  .on('mousedown' + eventNamespace, module.event.mousedown)
  3447                  .on('mouseup'   + eventNamespace, module.event.mouseup)
  3448                  .on('mousedown' + eventNamespace, selector.menu,   module.event.menu.mousedown)
  3449                  .on('mouseup'   + eventNamespace, selector.menu,   module.event.menu.mouseup)
  3450                  .on(clickEvent  + eventNamespace, selector.icon,   module.event.icon.click)
  3451                  .on(clickEvent  + eventNamespace, selector.clearIcon, module.event.clearIcon.click)
  3452                  .on('focus'     + eventNamespace, selector.search, module.event.search.focus)
  3453                  .on(clickEvent  + eventNamespace, selector.search, module.event.search.focus)
  3454                  .on('blur'      + eventNamespace, selector.search, module.event.search.blur)
  3455                  .on(clickEvent  + eventNamespace, selector.text,   module.event.text.focus)
  3456                ;
  3457                if(module.is.multiple()) {
  3458                  $module
  3459                    .on(clickEvent + eventNamespace, module.event.click)
  3460                  ;
  3461                }
  3462              }
  3463              else {
  3464                if(settings.on == 'click') {
  3465                  $module
  3466                    .on(clickEvent + eventNamespace, selector.icon, module.event.icon.click)
  3467                    .on(clickEvent + eventNamespace, module.event.test.toggle)
  3468                  ;
  3469                }
  3470                else if(settings.on == 'hover') {
  3471                  $module
  3472                    .on('mouseenter' + eventNamespace, module.delay.show)
  3473                    .on('mouseleave' + eventNamespace, module.delay.hide)
  3474                  ;
  3475                }
  3476                else {
  3477                  $module
  3478                    .on(settings.on + eventNamespace, module.toggle)
  3479                  ;
  3480                }
  3481                $module
  3482                  .on('mousedown' + eventNamespace, module.event.mousedown)
  3483                  .on('mouseup'   + eventNamespace, module.event.mouseup)
  3484                  .on('focus'     + eventNamespace, module.event.focus)
  3485                  .on(clickEvent  + eventNamespace, selector.clearIcon, module.event.clearIcon.click)
  3486                ;
  3487                if(module.has.menuSearch() ) {
  3488                  $module
  3489                    .on('blur' + eventNamespace, selector.search, module.event.search.blur)
  3490                  ;
  3491                }
  3492                else {
  3493                  $module
  3494                    .on('blur' + eventNamespace, module.event.blur)
  3495                  ;
  3496                }
  3497              }
  3498              $menu
  3499                .on((hasTouch ? 'touchstart' : 'mouseenter') + eventNamespace, selector.item, module.event.item.mouseenter)
  3500                .on('mouseleave' + eventNamespace, selector.item, module.event.item.mouseleave)
  3501                .on('click'      + eventNamespace, selector.item, module.event.item.click)
  3502              ;
  3503            },
  3504            intent: function() {
  3505              module.verbose('Binding hide intent event to document');
  3506              if(hasTouch) {
  3507                $document
  3508                  .on('touchstart' + elementNamespace, module.event.test.touch)
  3509                  .on('touchmove'  + elementNamespace, module.event.test.touch)
  3510                ;
  3511              }
  3512              $document
  3513                .on(clickEvent + elementNamespace, module.event.test.hide)
  3514              ;
  3515            }
  3516          },
  3517  
  3518          unbind: {
  3519            intent: function() {
  3520              module.verbose('Removing hide intent event from document');
  3521              if(hasTouch) {
  3522                $document
  3523                  .off('touchstart' + elementNamespace)
  3524                  .off('touchmove' + elementNamespace)
  3525                ;
  3526              }
  3527              $document
  3528                .off(clickEvent + elementNamespace)
  3529              ;
  3530            }
  3531          },
  3532  
  3533          filter: function(query) {
  3534            var
  3535              searchTerm = (query !== undefined)
  3536                ? query
  3537                : module.get.query(),
  3538              afterFiltered = function() {
  3539                if(module.is.multiple()) {
  3540                  module.filterActive();
  3541                }
  3542                if(query || (!query && module.get.activeItem().length == 0)) {
  3543                  module.select.firstUnfiltered();
  3544                }
  3545                if( module.has.allResultsFiltered() ) {
  3546                  if( settings.onNoResults.call(element, searchTerm) ) {
  3547                    if(settings.allowAdditions) {
  3548                      if(settings.hideAdditions) {
  3549                        module.verbose('User addition with no menu, setting empty style');
  3550                        module.set.empty();
  3551                        module.hideMenu();
  3552                      }
  3553                    }
  3554                    else {
  3555                      module.verbose('All items filtered, showing message', searchTerm);
  3556                      module.add.message(message.noResults);
  3557                    }
  3558                  }
  3559                  else {
  3560                    module.verbose('All items filtered, hiding dropdown', searchTerm);
  3561                    module.hideMenu();
  3562                  }
  3563                }
  3564                else {
  3565                  module.remove.empty();
  3566                  module.remove.message();
  3567                }
  3568                if(settings.allowAdditions) {
  3569                  module.add.userSuggestion(module.escape.htmlEntities(query));
  3570                }
  3571                if(module.is.searchSelection() && module.can.show() && module.is.focusedOnSearch() ) {
  3572                  module.show();
  3573                }
  3574              }
  3575            ;
  3576            if(settings.useLabels && module.has.maxSelections()) {
  3577              return;
  3578            }
  3579            if(settings.apiSettings) {
  3580              if( module.can.useAPI() ) {
  3581                module.queryRemote(searchTerm, function() {
  3582                  if(settings.filterRemoteData) {
  3583                    module.filterItems(searchTerm);
  3584                  }
  3585                  var preSelected = $input.val();
  3586                  if(!Array.isArray(preSelected)) {
  3587                      preSelected = preSelected && preSelected!=="" ? preSelected.split(settings.delimiter) : [];
  3588                  }
  3589                  $.each(preSelected,function(index,value){
  3590                    $item.filter('[data-value="'+value+'"]')
  3591                        .addClass(className.filtered)
  3592                    ;
  3593                  });
  3594                  afterFiltered();
  3595                });
  3596              }
  3597              else {
  3598                module.error(error.noAPI);
  3599              }
  3600            }
  3601            else {
  3602              module.filterItems(searchTerm);
  3603              afterFiltered();
  3604            }
  3605          },
  3606  
  3607          queryRemote: function(query, callback) {
  3608            var
  3609              apiSettings = {
  3610                errorDuration : false,
  3611                cache         : 'local',
  3612                throttle      : settings.throttle,
  3613                urlData       : {
  3614                  query: query
  3615                },
  3616                onError: function() {
  3617                  module.add.message(message.serverError);
  3618                  callback();
  3619                },
  3620                onFailure: function() {
  3621                  module.add.message(message.serverError);
  3622                  callback();
  3623                },
  3624                onSuccess : function(response) {
  3625                  var
  3626                    values          = response[fields.remoteValues]
  3627                  ;
  3628                  if (!Array.isArray(values)){
  3629                      values = [];
  3630                  }
  3631                  module.remove.message();
  3632                  var menuConfig = {};
  3633                  menuConfig[fields.values] = values;
  3634                  module.setup.menu(menuConfig);
  3635  
  3636                  if(values.length===0 && !settings.allowAdditions) {
  3637                    module.add.message(message.noResults);
  3638                  }
  3639                  callback();
  3640                }
  3641              }
  3642            ;
  3643            if( !$module.api('get request') ) {
  3644              module.setup.api();
  3645            }
  3646            apiSettings = $.extend(true, {}, apiSettings, settings.apiSettings);
  3647            $module
  3648              .api('setting', apiSettings)
  3649              .api('query')
  3650            ;
  3651          },
  3652  
  3653          filterItems: function(query) {
  3654            var
  3655              searchTerm = module.remove.diacritics(query !== undefined
  3656                ? query
  3657                : module.get.query()
  3658              ),
  3659              results          =  null,
  3660              escapedTerm      = module.escape.string(searchTerm),
  3661              regExpFlags      = (settings.ignoreSearchCase ? 'i' : '') + 'gm',
  3662              beginsWithRegExp = new RegExp('^' + escapedTerm, regExpFlags)
  3663            ;
  3664            // avoid loop if we're matching nothing
  3665            if( module.has.query() ) {
  3666              results = [];
  3667  
  3668              module.verbose('Searching for matching values', searchTerm);
  3669              $item
  3670                .each(function(){
  3671                  var
  3672                    $choice = $(this),
  3673                    text,
  3674                    value
  3675                  ;
  3676                  if($choice.hasClass(className.unfilterable)) {
  3677                    results.push(this);
  3678                    return true;
  3679                  }
  3680                  if(settings.match === 'both' || settings.match === 'text') {
  3681                    text = module.remove.diacritics(String(module.get.choiceText($choice, false)));
  3682                    if(text.search(beginsWithRegExp) !== -1) {
  3683                      results.push(this);
  3684                      return true;
  3685                    }
  3686                    else if (settings.fullTextSearch === 'exact' && module.exactSearch(searchTerm, text)) {
  3687                      results.push(this);
  3688                      return true;
  3689                    }
  3690                    else if (settings.fullTextSearch === true && module.fuzzySearch(searchTerm, text)) {
  3691                      results.push(this);
  3692                      return true;
  3693                    }
  3694                  }
  3695                  if(settings.match === 'both' || settings.match === 'value') {
  3696                    value = module.remove.diacritics(String(module.get.choiceValue($choice, text)));
  3697                    if(value.search(beginsWithRegExp) !== -1) {
  3698                      results.push(this);
  3699                      return true;
  3700                    }
  3701                    else if (settings.fullTextSearch === 'exact' && module.exactSearch(searchTerm, value)) {
  3702                      results.push(this);
  3703                      return true;
  3704                    }
  3705                    else if (settings.fullTextSearch === true && module.fuzzySearch(searchTerm, value)) {
  3706                      results.push(this);
  3707                      return true;
  3708                    }
  3709                  }
  3710                })
  3711              ;
  3712            }
  3713            module.debug('Showing only matched items', searchTerm);
  3714            module.remove.filteredItem();
  3715            if(results) {
  3716              $item
  3717                .not(results)
  3718                .addClass(className.filtered)
  3719              ;
  3720            }
  3721  
  3722            if(!module.has.query()) {
  3723              $divider
  3724                .removeClass(className.hidden);
  3725            } else if(settings.hideDividers === true) {
  3726              $divider
  3727                .addClass(className.hidden);
  3728            } else if(settings.hideDividers === 'empty') {
  3729              $divider
  3730                .removeClass(className.hidden)
  3731                .filter(function() {
  3732                  // First find the last divider in this divider group
  3733                  // Dividers which are direct siblings are considered a group
  3734                  var lastDivider = $(this).nextUntil(selector.item);
  3735  
  3736                  return (lastDivider.length ? lastDivider : $(this))
  3737                  // Count all non-filtered items until the next divider (or end of the dropdown)
  3738                    .nextUntil(selector.divider)
  3739                    .filter(selector.item + ":not(." + className.filtered + ")")
  3740                    // Hide divider if no items are found
  3741                    .length === 0;
  3742                })
  3743                .addClass(className.hidden);
  3744            }
  3745          },
  3746  
  3747          fuzzySearch: function(query, term) {
  3748            var
  3749              termLength  = term.length,
  3750              queryLength = query.length
  3751            ;
  3752            query = (settings.ignoreSearchCase ? query.toLowerCase() : query);
  3753            term  = (settings.ignoreSearchCase ? term.toLowerCase() : term);
  3754            if(queryLength > termLength) {
  3755              return false;
  3756            }
  3757            if(queryLength === termLength) {
  3758              return (query === term);
  3759            }
  3760            search: for (var characterIndex = 0, nextCharacterIndex = 0; characterIndex < queryLength; characterIndex++) {
  3761              var
  3762                queryCharacter = query.charCodeAt(characterIndex)
  3763              ;
  3764              while(nextCharacterIndex < termLength) {
  3765                if(term.charCodeAt(nextCharacterIndex++) === queryCharacter) {
  3766                  continue search;
  3767                }
  3768              }
  3769              return false;
  3770            }
  3771            return true;
  3772          },
  3773          exactSearch: function (query, term) {
  3774            query = (settings.ignoreSearchCase ? query.toLowerCase() : query);
  3775            term  = (settings.ignoreSearchCase ? term.toLowerCase() : term);
  3776            return term.indexOf(query) > -1;
  3777  
  3778          },
  3779          filterActive: function() {
  3780            if(settings.useLabels) {
  3781              $item.filter('.' + className.active)
  3782                .addClass(className.filtered)
  3783              ;
  3784            }
  3785          },
  3786  
  3787          focusSearch: function(skipHandler) {
  3788            if( module.has.search() && !module.is.focusedOnSearch() ) {
  3789              if(skipHandler) {
  3790                $module.off('focus' + eventNamespace, selector.search);
  3791                $search.focus();
  3792                $module.on('focus'  + eventNamespace, selector.search, module.event.search.focus);
  3793              }
  3794              else {
  3795                $search.focus();
  3796              }
  3797            }
  3798          },
  3799  
  3800          blurSearch: function() {
  3801            if( module.has.search() ) {
  3802              $search.blur();
  3803            }
  3804          },
  3805  
  3806          forceSelection: function() {
  3807            var
  3808              $currentlySelected = $item.not(className.filtered).filter('.' + className.selected).eq(0),
  3809              $activeItem        = $item.not(className.filtered).filter('.' + className.active).eq(0),
  3810              $selectedItem      = ($currentlySelected.length > 0)
  3811                ? $currentlySelected
  3812                : $activeItem,
  3813              hasSelected = ($selectedItem.length > 0)
  3814            ;
  3815            if(settings.allowAdditions || (hasSelected && !module.is.multiple())) {
  3816              module.debug('Forcing partial selection to selected item', $selectedItem);
  3817              module.event.item.click.call($selectedItem, {}, true);
  3818            }
  3819            else {
  3820              module.remove.searchTerm();
  3821            }
  3822          },
  3823  
  3824          change: {
  3825            values: function(values) {
  3826              if(!settings.allowAdditions) {
  3827                module.clear();
  3828              }
  3829              module.debug('Creating dropdown with specified values', values);
  3830              var menuConfig = {};
  3831              menuConfig[fields.values] = values;
  3832              module.setup.menu(menuConfig);
  3833              $.each(values, function(index, item) {
  3834                if(item.selected == true) {
  3835                  module.debug('Setting initial selection to', item[fields.value]);
  3836                  module.set.selected(item[fields.value]);
  3837                  if(!module.is.multiple()) {
  3838                    return false;
  3839                  }
  3840                }
  3841              });
  3842  
  3843              if(module.has.selectInput()) {
  3844                module.disconnect.selectObserver();
  3845                $input.html('');
  3846                $input.append('<option disabled selected value></option>');
  3847                $.each(values, function(index, item) {
  3848                  var
  3849                    value = settings.templates.deQuote(item[fields.value]),
  3850                    name = settings.templates.escape(
  3851                      item[fields.name] || '',
  3852                      settings.preserveHTML
  3853                    )
  3854                  ;
  3855                  $input.append('<option value="' + value + '">' + name + '</option>');
  3856                });
  3857                module.observe.select();
  3858              }
  3859            }
  3860          },
  3861  
  3862          event: {
  3863            change: function() {
  3864              if(!internalChange) {
  3865                module.debug('Input changed, updating selection');
  3866                module.set.selected();
  3867              }
  3868            },
  3869            focus: function() {
  3870              if(settings.showOnFocus && !activated && module.is.hidden() && !pageLostFocus) {
  3871                module.show();
  3872              }
  3873            },
  3874            blur: function(event) {
  3875              pageLostFocus = (document.activeElement === this);
  3876              if(!activated && !pageLostFocus) {
  3877                module.remove.activeLabel();
  3878                module.hide();
  3879              }
  3880            },
  3881            mousedown: function() {
  3882              if(module.is.searchSelection()) {
  3883                // prevent menu hiding on immediate re-focus
  3884                willRefocus = true;
  3885              }
  3886              else {
  3887                // prevents focus callback from occurring on mousedown
  3888                activated = true;
  3889              }
  3890            },
  3891            mouseup: function() {
  3892              if(module.is.searchSelection()) {
  3893                // prevent menu hiding on immediate re-focus
  3894                willRefocus = false;
  3895              }
  3896              else {
  3897                activated = false;
  3898              }
  3899            },
  3900            click: function(event) {
  3901              var
  3902                $target = $(event.target)
  3903              ;
  3904              // focus search
  3905              if($target.is($module)) {
  3906                if(!module.is.focusedOnSearch()) {
  3907                  module.focusSearch();
  3908                }
  3909                else {
  3910                  module.show();
  3911                }
  3912              }
  3913            },
  3914            search: {
  3915              focus: function(event) {
  3916                activated = true;
  3917                if(module.is.multiple()) {
  3918                  module.remove.activeLabel();
  3919                }
  3920                if(settings.showOnFocus || (event.type !== 'focus' && event.type !== 'focusin')) {
  3921                  module.search();
  3922                }
  3923              },
  3924              blur: function(event) {
  3925                pageLostFocus = (document.activeElement === this);
  3926                if(module.is.searchSelection() && !willRefocus) {
  3927                  if(!itemActivated && !pageLostFocus) {
  3928                    if(settings.forceSelection) {
  3929                      module.forceSelection();
  3930                    } else if(!settings.allowAdditions){
  3931                      module.remove.searchTerm();
  3932                    }
  3933                    module.hide();
  3934                  }
  3935                }
  3936                willRefocus = false;
  3937              }
  3938            },
  3939            clearIcon: {
  3940              click: function(event) {
  3941                module.clear();
  3942                if(module.is.searchSelection()) {
  3943                  module.remove.searchTerm();
  3944                }
  3945                module.hide();
  3946                event.stopPropagation();
  3947              }
  3948            },
  3949            icon: {
  3950              click: function(event) {
  3951                iconClicked=true;
  3952                if(module.has.search()) {
  3953                  if(!module.is.active()) {
  3954                      if(settings.showOnFocus){
  3955                        module.focusSearch();
  3956                      } else {
  3957                        module.toggle();
  3958                      }
  3959                  } else {
  3960                    module.blurSearch();
  3961                  }
  3962                } else {
  3963                  module.toggle();
  3964                }
  3965              }
  3966            },
  3967            text: {
  3968              focus: function(event) {
  3969                activated = true;
  3970                module.focusSearch();
  3971              }
  3972            },
  3973            input: function(event) {
  3974              if(module.is.multiple() || module.is.searchSelection()) {
  3975                module.set.filtered();
  3976              }
  3977              clearTimeout(module.timer);
  3978              module.timer = setTimeout(module.search, settings.delay.search);
  3979            },
  3980            label: {
  3981              click: function(event) {
  3982                var
  3983                  $label        = $(this),
  3984                  $labels       = $module.find(selector.label),
  3985                  $activeLabels = $labels.filter('.' + className.active),
  3986                  $nextActive   = $label.nextAll('.' + className.active),
  3987                  $prevActive   = $label.prevAll('.' + className.active),
  3988                  $range = ($nextActive.length > 0)
  3989                    ? $label.nextUntil($nextActive).add($activeLabels).add($label)
  3990                    : $label.prevUntil($prevActive).add($activeLabels).add($label)
  3991                ;
  3992                if(event.shiftKey) {
  3993                  $activeLabels.removeClass(className.active);
  3994                  $range.addClass(className.active);
  3995                }
  3996                else if(event.ctrlKey) {
  3997                  $label.toggleClass(className.active);
  3998                }
  3999                else {
  4000                  $activeLabels.removeClass(className.active);
  4001                  $label.addClass(className.active);
  4002                }
  4003                settings.onLabelSelect.apply(this, $labels.filter('.' + className.active));
  4004              }
  4005            },
  4006            remove: {
  4007              click: function() {
  4008                var
  4009                  $label = $(this).parent()
  4010                ;
  4011                if( $label.hasClass(className.active) ) {
  4012                  // remove all selected labels
  4013                  module.remove.activeLabels();
  4014                }
  4015                else {
  4016                  // remove this label only
  4017                  module.remove.activeLabels( $label );
  4018                }
  4019              }
  4020            },
  4021            test: {
  4022              toggle: function(event) {
  4023                var
  4024                  toggleBehavior = (module.is.multiple())
  4025                    ? module.show
  4026                    : module.toggle
  4027                ;
  4028                if(module.is.bubbledLabelClick(event) || module.is.bubbledIconClick(event)) {
  4029                  return;
  4030                }
  4031                if( module.determine.eventOnElement(event, toggleBehavior) ) {
  4032                  event.preventDefault();
  4033                }
  4034              },
  4035              touch: function(event) {
  4036                module.determine.eventOnElement(event, function() {
  4037                  if(event.type == 'touchstart') {
  4038                    module.timer = setTimeout(function() {
  4039                      module.hide();
  4040                    }, settings.delay.touch);
  4041                  }
  4042                  else if(event.type == 'touchmove') {
  4043                    clearTimeout(module.timer);
  4044                  }
  4045                });
  4046                event.stopPropagation();
  4047              },
  4048              hide: function(event) {
  4049                if(module.determine.eventInModule(event, module.hide)){
  4050                  if(element.id && $(event.target).attr('for') === element.id){
  4051                    event.preventDefault();
  4052                  }
  4053                }
  4054              }
  4055            },
  4056            class: {
  4057              mutation: function(mutations) {
  4058                mutations.forEach(function(mutation) {
  4059                  if(mutation.attributeName === "class") {
  4060                    module.check.disabled();
  4061                  }
  4062                });
  4063              }
  4064            },
  4065            select: {
  4066              mutation: function(mutations) {
  4067                module.debug('<select> modified, recreating menu');
  4068                if(module.is.selectMutation(mutations)) {
  4069                  module.disconnect.selectObserver();
  4070                  module.refresh();
  4071                  module.setup.select();
  4072                  module.set.selected();
  4073                  module.observe.select();
  4074                }
  4075              }
  4076            },
  4077            menu: {
  4078              mutation: function(mutations) {
  4079                var
  4080                  mutation   = mutations[0],
  4081                  $addedNode = mutation.addedNodes
  4082                    ? $(mutation.addedNodes[0])
  4083                    : $(false),
  4084                  $removedNode = mutation.removedNodes
  4085                    ? $(mutation.removedNodes[0])
  4086                    : $(false),
  4087                  $changedNodes  = $addedNode.add($removedNode),
  4088                  isUserAddition = $changedNodes.is(selector.addition) || $changedNodes.closest(selector.addition).length > 0,
  4089                  isMessage      = $changedNodes.is(selector.message)  || $changedNodes.closest(selector.message).length > 0
  4090                ;
  4091                if(isUserAddition || isMessage) {
  4092                  module.debug('Updating item selector cache');
  4093                  module.refreshItems();
  4094                }
  4095                else {
  4096                  module.debug('Menu modified, updating selector cache');
  4097                  module.refresh();
  4098                }
  4099              },
  4100              mousedown: function() {
  4101                itemActivated = true;
  4102              },
  4103              mouseup: function() {
  4104                itemActivated = false;
  4105              }
  4106            },
  4107            item: {
  4108              mouseenter: function(event) {
  4109                var
  4110                  $target        = $(event.target),
  4111                  $item          = $(this),
  4112                  $subMenu       = $item.children(selector.menu),
  4113                  $otherMenus    = $item.siblings(selector.item).children(selector.menu),
  4114                  hasSubMenu     = ($subMenu.length > 0),
  4115                  isBubbledEvent = ($subMenu.find($target).length > 0)
  4116                ;
  4117                if( !isBubbledEvent && hasSubMenu ) {
  4118                  clearTimeout(module.itemTimer);
  4119                  module.itemTimer = setTimeout(function() {
  4120                    module.verbose('Showing sub-menu', $subMenu);
  4121                    $.each($otherMenus, function() {
  4122                      module.animate.hide(false, $(this));
  4123                    });
  4124                    module.animate.show(false, $subMenu);
  4125                  }, settings.delay.show);
  4126                  event.preventDefault();
  4127                }
  4128              },
  4129              mouseleave: function(event) {
  4130                var
  4131                  $subMenu = $(this).children(selector.menu)
  4132                ;
  4133                if($subMenu.length > 0) {
  4134                  clearTimeout(module.itemTimer);
  4135                  module.itemTimer = setTimeout(function() {
  4136                    module.verbose('Hiding sub-menu', $subMenu);
  4137                    module.animate.hide(false, $subMenu);
  4138                  }, settings.delay.hide);
  4139                }
  4140              },
  4141              click: function (event, skipRefocus) {
  4142                var
  4143                  $choice        = $(this),
  4144                  $target        = (event)
  4145                    ? $(event.target)
  4146                    : $(''),
  4147                  $subMenu       = $choice.find(selector.menu),
  4148                  text           = module.get.choiceText($choice),
  4149                  value          = module.get.choiceValue($choice, text),
  4150                  hasSubMenu     = ($subMenu.length > 0),
  4151                  isBubbledEvent = ($subMenu.find($target).length > 0)
  4152                ;
  4153                // prevents IE11 bug where menu receives focus even though `tabindex=-1`
  4154                if (document.activeElement.tagName.toLowerCase() !== 'input') {
  4155                  $(document.activeElement).blur();
  4156                }
  4157                if(!isBubbledEvent && (!hasSubMenu || settings.allowCategorySelection)) {
  4158                  if(module.is.searchSelection()) {
  4159                    if(settings.allowAdditions) {
  4160                      module.remove.userAddition();
  4161                    }
  4162                    module.remove.searchTerm();
  4163                    if(!module.is.focusedOnSearch() && !(skipRefocus == true)) {
  4164                      module.focusSearch(true);
  4165                    }
  4166                  }
  4167                  if(!settings.useLabels) {
  4168                    module.remove.filteredItem();
  4169                    module.set.scrollPosition($choice);
  4170                  }
  4171                  module.determine.selectAction.call(this, text, value);
  4172                }
  4173              }
  4174            },
  4175  
  4176            document: {
  4177              // label selection should occur even when element has no focus
  4178              keydown: function(event) {
  4179                var
  4180                  pressedKey    = event.which,
  4181                  isShortcutKey = module.is.inObject(pressedKey, keys)
  4182                ;
  4183                if(isShortcutKey) {
  4184                  var
  4185                    $label            = $module.find(selector.label),
  4186                    $activeLabel      = $label.filter('.' + className.active),
  4187                    activeValue       = $activeLabel.data(metadata.value),
  4188                    labelIndex        = $label.index($activeLabel),
  4189                    labelCount        = $label.length,
  4190                    hasActiveLabel    = ($activeLabel.length > 0),
  4191                    hasMultipleActive = ($activeLabel.length > 1),
  4192                    isFirstLabel      = (labelIndex === 0),
  4193                    isLastLabel       = (labelIndex + 1 == labelCount),
  4194                    isSearch          = module.is.searchSelection(),
  4195                    isFocusedOnSearch = module.is.focusedOnSearch(),
  4196                    isFocused         = module.is.focused(),
  4197                    caretAtStart      = (isFocusedOnSearch && module.get.caretPosition(false) === 0),
  4198                    isSelectedSearch  = (caretAtStart && module.get.caretPosition(true) !== 0),
  4199                    $nextLabel
  4200                  ;
  4201                  if(isSearch && !hasActiveLabel && !isFocusedOnSearch) {
  4202                    return;
  4203                  }
  4204  
  4205                  if(pressedKey == keys.leftArrow) {
  4206                    // activate previous label
  4207                    if((isFocused || caretAtStart) && !hasActiveLabel) {
  4208                      module.verbose('Selecting previous label');
  4209                      $label.last().addClass(className.active);
  4210                    }
  4211                    else if(hasActiveLabel) {
  4212                      if(!event.shiftKey) {
  4213                        module.verbose('Selecting previous label');
  4214                        $label.removeClass(className.active);
  4215                      }
  4216                      else {
  4217                        module.verbose('Adding previous label to selection');
  4218                      }
  4219                      if(isFirstLabel && !hasMultipleActive) {
  4220                        $activeLabel.addClass(className.active);
  4221                      }
  4222                      else {
  4223                        $activeLabel.prev(selector.siblingLabel)
  4224                          .addClass(className.active)
  4225                          .end()
  4226                        ;
  4227                      }
  4228                      event.preventDefault();
  4229                    }
  4230                  }
  4231                  else if(pressedKey == keys.rightArrow) {
  4232                    // activate first label
  4233                    if(isFocused && !hasActiveLabel) {
  4234                      $label.first().addClass(className.active);
  4235                    }
  4236                    // activate next label
  4237                    if(hasActiveLabel) {
  4238                      if(!event.shiftKey) {
  4239                        module.verbose('Selecting next label');
  4240                        $label.removeClass(className.active);
  4241                      }
  4242                      else {
  4243                        module.verbose('Adding next label to selection');
  4244                      }
  4245                      if(isLastLabel) {
  4246                        if(isSearch) {
  4247                          if(!isFocusedOnSearch) {
  4248                            module.focusSearch();
  4249                          }
  4250                          else {
  4251                            $label.removeClass(className.active);
  4252                          }
  4253                        }
  4254                        else if(hasMultipleActive) {
  4255                          $activeLabel.next(selector.siblingLabel).addClass(className.active);
  4256                        }
  4257                        else {
  4258                          $activeLabel.addClass(className.active);
  4259                        }
  4260                      }
  4261                      else {
  4262                        $activeLabel.next(selector.siblingLabel).addClass(className.active);
  4263                      }
  4264                      event.preventDefault();
  4265                    }
  4266                  }
  4267                  else if(pressedKey == keys.deleteKey || pressedKey == keys.backspace) {
  4268                    if(hasActiveLabel) {
  4269                      module.verbose('Removing active labels');
  4270                      if(isLastLabel) {
  4271                        if(isSearch && !isFocusedOnSearch) {
  4272                          module.focusSearch();
  4273                        }
  4274                      }
  4275                      $activeLabel.last().next(selector.siblingLabel).addClass(className.active);
  4276                      module.remove.activeLabels($activeLabel);
  4277                      event.preventDefault();
  4278                    }
  4279                    else if(caretAtStart && !isSelectedSearch && !hasActiveLabel && pressedKey == keys.backspace) {
  4280                      module.verbose('Removing last label on input backspace');
  4281                      $activeLabel = $label.last().addClass(className.active);
  4282                      module.remove.activeLabels($activeLabel);
  4283                    }
  4284                  }
  4285                  else {
  4286                    $activeLabel.removeClass(className.active);
  4287                  }
  4288                }
  4289              }
  4290            },
  4291  
  4292            keydown: function(event) {
  4293              var
  4294                pressedKey    = event.which,
  4295                isShortcutKey = module.is.inObject(pressedKey, keys)
  4296              ;
  4297              if(isShortcutKey) {
  4298                var
  4299                  $currentlySelected = $item.not(selector.unselectable).filter('.' + className.selected).eq(0),
  4300                  $activeItem        = $menu.children('.' + className.active).eq(0),
  4301                  $selectedItem      = ($currentlySelected.length > 0)
  4302                    ? $currentlySelected
  4303                    : $activeItem,
  4304                  $visibleItems = ($selectedItem.length > 0)
  4305                    ? $selectedItem.siblings(':not(.' + className.filtered +')').addBack()
  4306                    : $menu.children(':not(.' + className.filtered +')'),
  4307                  $subMenu              = $selectedItem.children(selector.menu),
  4308                  $parentMenu           = $selectedItem.closest(selector.menu),
  4309                  inVisibleMenu         = ($parentMenu.hasClass(className.visible) || $parentMenu.hasClass(className.animating) || $parentMenu.parent(selector.menu).length > 0),
  4310                  hasSubMenu            = ($subMenu.length> 0),
  4311                  hasSelectedItem       = ($selectedItem.length > 0),
  4312                  selectedIsSelectable  = ($selectedItem.not(selector.unselectable).length > 0),
  4313                  delimiterPressed      = (pressedKey == keys.delimiter && settings.allowAdditions && module.is.multiple()),
  4314                  isAdditionWithoutMenu = (settings.allowAdditions && settings.hideAdditions && (pressedKey == keys.enter || delimiterPressed) && selectedIsSelectable),
  4315                  $nextItem,
  4316                  isSubMenuItem,
  4317                  newIndex
  4318                ;
  4319                // allow selection with menu closed
  4320                if(isAdditionWithoutMenu) {
  4321                  module.verbose('Selecting item from keyboard shortcut', $selectedItem);
  4322                  module.event.item.click.call($selectedItem, event);
  4323                  if(module.is.searchSelection()) {
  4324                    module.remove.searchTerm();
  4325                  }
  4326                  if(module.is.multiple()){
  4327                      event.preventDefault();
  4328                  }
  4329                }
  4330  
  4331                // visible menu keyboard shortcuts
  4332                if( module.is.visible() ) {
  4333  
  4334                  // enter (select or open sub-menu)
  4335                  if(pressedKey == keys.enter || delimiterPressed) {
  4336                    if(pressedKey == keys.enter && hasSelectedItem && hasSubMenu && !settings.allowCategorySelection) {
  4337                      module.verbose('Pressed enter on unselectable category, opening sub menu');
  4338                      pressedKey = keys.rightArrow;
  4339                    }
  4340                    else if(selectedIsSelectable) {
  4341                      module.verbose('Selecting item from keyboard shortcut', $selectedItem);
  4342                      module.event.item.click.call($selectedItem, event);
  4343                      if(module.is.searchSelection()) {
  4344                        module.remove.searchTerm();
  4345                        if(module.is.multiple()) {
  4346                            $search.focus();
  4347                        }
  4348                      }
  4349                    }
  4350                    event.preventDefault();
  4351                  }
  4352  
  4353                  // sub-menu actions
  4354                  if(hasSelectedItem) {
  4355  
  4356                    if(pressedKey == keys.leftArrow) {
  4357  
  4358                      isSubMenuItem = ($parentMenu[0] !== $menu[0]);
  4359  
  4360                      if(isSubMenuItem) {
  4361                        module.verbose('Left key pressed, closing sub-menu');
  4362                        module.animate.hide(false, $parentMenu);
  4363                        $selectedItem
  4364                          .removeClass(className.selected)
  4365                        ;
  4366                        $parentMenu
  4367                          .closest(selector.item)
  4368                            .addClass(className.selected)
  4369                        ;
  4370                        event.preventDefault();
  4371                      }
  4372                    }
  4373  
  4374                    // right arrow (show sub-menu)
  4375                    if(pressedKey == keys.rightArrow) {
  4376                      if(hasSubMenu) {
  4377                        module.verbose('Right key pressed, opening sub-menu');
  4378                        module.animate.show(false, $subMenu);
  4379                        $selectedItem
  4380                          .removeClass(className.selected)
  4381                        ;
  4382                        $subMenu
  4383                          .find(selector.item).eq(0)
  4384                            .addClass(className.selected)
  4385                        ;
  4386                        event.preventDefault();
  4387                      }
  4388                    }
  4389                  }
  4390  
  4391                  // up arrow (traverse menu up)
  4392                  if(pressedKey == keys.upArrow) {
  4393                    $nextItem = (hasSelectedItem && inVisibleMenu)
  4394                      ? $selectedItem.prevAll(selector.item + ':not(' + selector.unselectable + ')').eq(0)
  4395                      : $item.eq(0)
  4396                    ;
  4397                    if($visibleItems.index( $nextItem ) < 0) {
  4398                      module.verbose('Up key pressed but reached top of current menu');
  4399                      event.preventDefault();
  4400                      return;
  4401                    }
  4402                    else {
  4403                      module.verbose('Up key pressed, changing active item');
  4404                      $selectedItem
  4405                        .removeClass(className.selected)
  4406                      ;
  4407                      $nextItem
  4408                        .addClass(className.selected)
  4409                      ;
  4410                      module.set.scrollPosition($nextItem);
  4411                      if(settings.selectOnKeydown && module.is.single()) {
  4412                        module.set.selectedItem($nextItem);
  4413                      }
  4414                    }
  4415                    event.preventDefault();
  4416                  }
  4417  
  4418                  // down arrow (traverse menu down)
  4419                  if(pressedKey == keys.downArrow) {
  4420                    $nextItem = (hasSelectedItem && inVisibleMenu)
  4421                      ? $nextItem = $selectedItem.nextAll(selector.item + ':not(' + selector.unselectable + ')').eq(0)
  4422                      : $item.eq(0)
  4423                    ;
  4424                    if($nextItem.length === 0) {
  4425                      module.verbose('Down key pressed but reached bottom of current menu');
  4426                      event.preventDefault();
  4427                      return;
  4428                    }
  4429                    else {
  4430                      module.verbose('Down key pressed, changing active item');
  4431                      $item
  4432                        .removeClass(className.selected)
  4433                      ;
  4434                      $nextItem
  4435                        .addClass(className.selected)
  4436                      ;
  4437                      module.set.scrollPosition($nextItem);
  4438                      if(settings.selectOnKeydown && module.is.single()) {
  4439                        module.set.selectedItem($nextItem);
  4440                      }
  4441                    }
  4442                    event.preventDefault();
  4443                  }
  4444  
  4445                  // page down (show next page)
  4446                  if(pressedKey == keys.pageUp) {
  4447                    module.scrollPage('up');
  4448                    event.preventDefault();
  4449                  }
  4450                  if(pressedKey == keys.pageDown) {
  4451                    module.scrollPage('down');
  4452                    event.preventDefault();
  4453                  }
  4454  
  4455                  // escape (close menu)
  4456                  if(pressedKey == keys.escape) {
  4457                    module.verbose('Escape key pressed, closing dropdown');
  4458                    module.hide();
  4459                  }
  4460  
  4461                }
  4462                else {
  4463                  // delimiter key
  4464                  if(delimiterPressed) {
  4465                    event.preventDefault();
  4466                  }
  4467                  // down arrow (open menu)
  4468                  if(pressedKey == keys.downArrow && !module.is.visible()) {
  4469                    module.verbose('Down key pressed, showing dropdown');
  4470                    module.show();
  4471                    event.preventDefault();
  4472                  }
  4473                }
  4474              }
  4475              else {
  4476                if( !module.has.search() ) {
  4477                  module.set.selectedLetter( String.fromCharCode(pressedKey) );
  4478                }
  4479              }
  4480            }
  4481          },
  4482  
  4483          trigger: {
  4484            change: function() {
  4485              var
  4486                inputElement = $input[0]
  4487              ;
  4488              if(inputElement) {
  4489                var events = document.createEvent('HTMLEvents');
  4490                module.verbose('Triggering native change event');
  4491                events.initEvent('change', true, false);
  4492                inputElement.dispatchEvent(events);
  4493              }
  4494            }
  4495          },
  4496  
  4497          determine: {
  4498            selectAction: function(text, value) {
  4499              selectActionActive = true;
  4500              module.verbose('Determining action', settings.action);
  4501              if( $.isFunction( module.action[settings.action] ) ) {
  4502                module.verbose('Triggering preset action', settings.action, text, value);
  4503                module.action[ settings.action ].call(element, text, value, this);
  4504              }
  4505              else if( $.isFunction(settings.action) ) {
  4506                module.verbose('Triggering user action', settings.action, text, value);
  4507                settings.action.call(element, text, value, this);
  4508              }
  4509              else {
  4510                module.error(error.action, settings.action);
  4511              }
  4512              selectActionActive = false;
  4513            },
  4514            eventInModule: function(event, callback) {
  4515              var
  4516                $target    = $(event.target),
  4517                inDocument = ($target.closest(document.documentElement).length > 0),
  4518                inModule   = ($target.closest($module).length > 0)
  4519              ;
  4520              callback = $.isFunction(callback)
  4521                ? callback
  4522                : function(){}
  4523              ;
  4524              if(inDocument && !inModule) {
  4525                module.verbose('Triggering event', callback);
  4526                callback();
  4527                return true;
  4528              }
  4529              else {
  4530                module.verbose('Event occurred in dropdown, canceling callback');
  4531                return false;
  4532              }
  4533            },
  4534            eventOnElement: function(event, callback) {
  4535              var
  4536                $target      = $(event.target),
  4537                $label       = $target.closest(selector.siblingLabel),
  4538                inVisibleDOM = document.body.contains(event.target),
  4539                notOnLabel   = ($module.find($label).length === 0 || !(module.is.multiple() && settings.useLabels)),
  4540                notInMenu    = ($target.closest($menu).length === 0)
  4541              ;
  4542              callback = $.isFunction(callback)
  4543                ? callback
  4544                : function(){}
  4545              ;
  4546              if(inVisibleDOM && notOnLabel && notInMenu) {
  4547                module.verbose('Triggering event', callback);
  4548                callback();
  4549                return true;
  4550              }
  4551              else {
  4552                module.verbose('Event occurred in dropdown menu, canceling callback');
  4553                return false;
  4554              }
  4555            }
  4556          },
  4557  
  4558          action: {
  4559  
  4560            nothing: function() {},
  4561  
  4562            activate: function(text, value, element) {
  4563              value = (value !== undefined)
  4564                ? value
  4565                : text
  4566              ;
  4567              if( module.can.activate( $(element) ) ) {
  4568                module.set.selected(value, $(element));
  4569                if(!module.is.multiple()) {
  4570                  module.hideAndClear();
  4571                }
  4572              }
  4573            },
  4574  
  4575            select: function(text, value, element) {
  4576              value = (value !== undefined)
  4577                ? value
  4578                : text
  4579              ;
  4580              if( module.can.activate( $(element) ) ) {
  4581                module.set.value(value, text, $(element));
  4582                if(!module.is.multiple()) {
  4583                  module.hideAndClear();
  4584                }
  4585              }
  4586            },
  4587  
  4588            combo: function(text, value, element) {
  4589              value = (value !== undefined)
  4590                ? value
  4591                : text
  4592              ;
  4593              module.set.selected(value, $(element));
  4594              module.hideAndClear();
  4595            },
  4596  
  4597            hide: function(text, value, element) {
  4598              module.set.value(value, text, $(element));
  4599              module.hideAndClear();
  4600            }
  4601  
  4602          },
  4603  
  4604          get: {
  4605            id: function() {
  4606              return id;
  4607            },
  4608            defaultText: function() {
  4609              return $module.data(metadata.defaultText);
  4610            },
  4611            defaultValue: function() {
  4612              return $module.data(metadata.defaultValue);
  4613            },
  4614            placeholderText: function() {
  4615              if(settings.placeholder != 'auto' && typeof settings.placeholder == 'string') {
  4616                return settings.placeholder;
  4617              }
  4618              return $module.data(metadata.placeholderText) || '';
  4619            },
  4620            text: function() {
  4621              return settings.preserveHTML ? $text.html() : $text.text();
  4622            },
  4623            query: function() {
  4624              return String($search.val()).trim();
  4625            },
  4626            searchWidth: function(value) {
  4627              value = (value !== undefined)
  4628                ? value
  4629                : $search.val()
  4630              ;
  4631              $sizer.text(value);
  4632              // prevent rounding issues
  4633              return Math.ceil( $sizer.width() + 1);
  4634            },
  4635            selectionCount: function() {
  4636              var
  4637                values = module.get.values(),
  4638                count
  4639              ;
  4640              count = ( module.is.multiple() )
  4641                ? Array.isArray(values)
  4642                  ? values.length
  4643                  : 0
  4644                : (module.get.value() !== '')
  4645                  ? 1
  4646                  : 0
  4647              ;
  4648              return count;
  4649            },
  4650            transition: function($subMenu) {
  4651              return (settings.transition == 'auto')
  4652                ? module.is.upward($subMenu)
  4653                  ? 'slide up'
  4654                  : 'slide down'
  4655                : settings.transition
  4656              ;
  4657            },
  4658            userValues: function() {
  4659              var
  4660                values = module.get.values()
  4661              ;
  4662              if(!values) {
  4663                return false;
  4664              }
  4665              values = Array.isArray(values)
  4666                ? values
  4667                : [values]
  4668              ;
  4669              return $.grep(values, function(value) {
  4670                return (module.get.item(value) === false);
  4671              });
  4672            },
  4673            uniqueArray: function(array) {
  4674              return $.grep(array, function (value, index) {
  4675                  return $.inArray(value, array) === index;
  4676              });
  4677            },
  4678            caretPosition: function(returnEndPos) {
  4679              var
  4680                input = $search.get(0),
  4681                range,
  4682                rangeLength
  4683              ;
  4684              if(returnEndPos && 'selectionEnd' in input){
  4685                return input.selectionEnd;
  4686              }
  4687              else if(!returnEndPos && 'selectionStart' in input) {
  4688                return input.selectionStart;
  4689              }
  4690              if (document.selection) {
  4691                input.focus();
  4692                range       = document.selection.createRange();
  4693                rangeLength = range.text.length;
  4694                if(returnEndPos) {
  4695                  return rangeLength;
  4696                }
  4697                range.moveStart('character', -input.value.length);
  4698                return range.text.length - rangeLength;
  4699              }
  4700            },
  4701            value: function() {
  4702              var
  4703                value = ($input.length > 0)
  4704                  ? $input.val()
  4705                  : $module.data(metadata.value),
  4706                isEmptyMultiselect = (Array.isArray(value) && value.length === 1 && value[0] === '')
  4707              ;
  4708              // prevents placeholder element from being selected when multiple
  4709              return (value === undefined || isEmptyMultiselect)
  4710                ? ''
  4711                : value
  4712              ;
  4713            },
  4714            values: function() {
  4715              var
  4716                value = module.get.value()
  4717              ;
  4718              if(value === '') {
  4719                return '';
  4720              }
  4721              return ( !module.has.selectInput() && module.is.multiple() )
  4722                ? (typeof value == 'string') // delimited string
  4723                  ? module.escape.htmlEntities(value).split(settings.delimiter)
  4724                  : ''
  4725                : value
  4726              ;
  4727            },
  4728            remoteValues: function() {
  4729              var
  4730                values = module.get.values(),
  4731                remoteValues = false
  4732              ;
  4733              if(values) {
  4734                if(typeof values == 'string') {
  4735                  values = [values];
  4736                }
  4737                $.each(values, function(index, value) {
  4738                  var
  4739                    name = module.read.remoteData(value)
  4740                  ;
  4741                  module.verbose('Restoring value from session data', name, value);
  4742                  if(name) {
  4743                    if(!remoteValues) {
  4744                      remoteValues = {};
  4745                    }
  4746                    remoteValues[value] = name;
  4747                  }
  4748                });
  4749              }
  4750              return remoteValues;
  4751            },
  4752            choiceText: function($choice, preserveHTML) {
  4753              preserveHTML = (preserveHTML !== undefined)
  4754                ? preserveHTML
  4755                : settings.preserveHTML
  4756              ;
  4757              if($choice) {
  4758                if($choice.find(selector.menu).length > 0) {
  4759                  module.verbose('Retrieving text of element with sub-menu');
  4760                  $choice = $choice.clone();
  4761                  $choice.find(selector.menu).remove();
  4762                  $choice.find(selector.menuIcon).remove();
  4763                }
  4764                return ($choice.data(metadata.text) !== undefined)
  4765                  ? $choice.data(metadata.text)
  4766                  : (preserveHTML)
  4767                    ? $choice.html().trim()
  4768                    : $choice.text().trim()
  4769                ;
  4770              }
  4771            },
  4772            choiceValue: function($choice, choiceText) {
  4773              choiceText = choiceText || module.get.choiceText($choice);
  4774              if(!$choice) {
  4775                return false;
  4776              }
  4777              return ($choice.data(metadata.value) !== undefined)
  4778                ? String( $choice.data(metadata.value) )
  4779                : (typeof choiceText === 'string')
  4780                  ? String(
  4781                    settings.ignoreSearchCase
  4782                    ? choiceText.toLowerCase()
  4783                    : choiceText
  4784                  ).trim()
  4785                  : String(choiceText)
  4786              ;
  4787            },
  4788            inputEvent: function() {
  4789              var
  4790                input = $search[0]
  4791              ;
  4792              if(input) {
  4793                return (input.oninput !== undefined)
  4794                  ? 'input'
  4795                  : (input.onpropertychange !== undefined)
  4796                    ? 'propertychange'
  4797                    : 'keyup'
  4798                ;
  4799              }
  4800              return false;
  4801            },
  4802            selectValues: function() {
  4803              var
  4804                select = {},
  4805                oldGroup = [],
  4806                values = []
  4807              ;
  4808              $module
  4809                .find('option')
  4810                  .each(function() {
  4811                    var
  4812                      $option  = $(this),
  4813                      name     = $option.html(),
  4814                      disabled = $option.attr('disabled'),
  4815                      value    = ( $option.attr('value') !== undefined )
  4816                        ? $option.attr('value')
  4817                        : name,
  4818                      text     = ( $option.data(metadata.text) !== undefined )
  4819                        ? $option.data(metadata.text)
  4820                        : name,
  4821                      group = $option.parent('optgroup')
  4822                    ;
  4823                    if(settings.placeholder === 'auto' && value === '') {
  4824                      select.placeholder = name;
  4825                    }
  4826                    else {
  4827                      if(group.length !== oldGroup.length || group[0] !== oldGroup[0]) {
  4828                        values.push({
  4829                          type: 'header',
  4830                          divider: settings.headerDivider,
  4831                          name: group.attr('label') || ''
  4832                        });
  4833                        oldGroup = group;
  4834                      }
  4835                      values.push({
  4836                        name     : name,
  4837                        value    : value,
  4838                        text     : text,
  4839                        disabled : disabled
  4840                      });
  4841                    }
  4842                  })
  4843              ;
  4844              if(settings.placeholder && settings.placeholder !== 'auto') {
  4845                module.debug('Setting placeholder value to', settings.placeholder);
  4846                select.placeholder = settings.placeholder;
  4847              }
  4848              if(settings.sortSelect) {
  4849                if(settings.sortSelect === true) {
  4850                  values.sort(function(a, b) {
  4851                    return a.name.localeCompare(b.name);
  4852                  });
  4853                } else if(settings.sortSelect === 'natural') {
  4854                  values.sort(function(a, b) {
  4855                    return (a.name.toLowerCase().localeCompare(b.name.toLowerCase()));
  4856                  });
  4857                } else if($.isFunction(settings.sortSelect)) {
  4858                  values.sort(settings.sortSelect);
  4859                }
  4860                select[fields.values] = values;
  4861                module.debug('Retrieved and sorted values from select', select);
  4862              }
  4863              else {
  4864                select[fields.values] = values;
  4865                module.debug('Retrieved values from select', select);
  4866              }
  4867              return select;
  4868            },
  4869            activeItem: function() {
  4870              return $item.filter('.'  + className.active);
  4871            },
  4872            selectedItem: function() {
  4873              var
  4874                $selectedItem = $item.not(selector.unselectable).filter('.'  + className.selected)
  4875              ;
  4876              return ($selectedItem.length > 0)
  4877                ? $selectedItem
  4878                : $item.eq(0)
  4879              ;
  4880            },
  4881            itemWithAdditions: function(value) {
  4882              var
  4883                $items       = module.get.item(value),
  4884                $userItems   = module.create.userChoice(value),
  4885                hasUserItems = ($userItems && $userItems.length > 0)
  4886              ;
  4887              if(hasUserItems) {
  4888                $items = ($items.length > 0)
  4889                  ? $items.add($userItems)
  4890                  : $userItems
  4891                ;
  4892              }
  4893              return $items;
  4894            },
  4895            item: function(value, strict) {
  4896              var
  4897                $selectedItem = false,
  4898                shouldSearch,
  4899                isMultiple
  4900              ;
  4901              value = (value !== undefined)
  4902                ? value
  4903                : ( module.get.values() !== undefined)
  4904                  ? module.get.values()
  4905                  : module.get.text()
  4906              ;
  4907              isMultiple = (module.is.multiple() && Array.isArray(value));
  4908              shouldSearch = (isMultiple)
  4909                ? (value.length > 0)
  4910                : (value !== undefined && value !== null)
  4911              ;
  4912              strict     = (value === '' || value === false  || value === true)
  4913                ? true
  4914                : strict || false
  4915              ;
  4916              if(shouldSearch) {
  4917                $item
  4918                  .each(function() {
  4919                    var
  4920                      $choice       = $(this),
  4921                      optionText    = module.get.choiceText($choice),
  4922                      optionValue   = module.get.choiceValue($choice, optionText)
  4923                    ;
  4924                    // safe early exit
  4925                    if(optionValue === null || optionValue === undefined) {
  4926                      return;
  4927                    }
  4928                    if(isMultiple) {
  4929                      if($.inArray(module.escape.htmlEntities(String(optionValue)), value.map(function(v){return String(v);})) !== -1) {
  4930                        $selectedItem = ($selectedItem)
  4931                          ? $selectedItem.add($choice)
  4932                          : $choice
  4933                        ;
  4934                      }
  4935                    }
  4936                    else if(strict) {
  4937                      module.verbose('Ambiguous dropdown value using strict type check', $choice, value);
  4938                      if( optionValue === value) {
  4939                        $selectedItem = $choice;
  4940                        return true;
  4941                      }
  4942                    }
  4943                    else {
  4944                      if(settings.ignoreCase) {
  4945                        optionValue = optionValue.toLowerCase();
  4946                        value = value.toLowerCase();
  4947                      }
  4948                      if(module.escape.htmlEntities(String(optionValue)) === module.escape.htmlEntities(String(value))) {
  4949                        module.verbose('Found select item by value', optionValue, value);
  4950                        $selectedItem = $choice;
  4951                        return true;
  4952                      }
  4953                    }
  4954                  })
  4955                ;
  4956              }
  4957              return $selectedItem;
  4958            }
  4959          },
  4960  
  4961          check: {
  4962            maxSelections: function(selectionCount) {
  4963              if(settings.maxSelections) {
  4964                selectionCount = (selectionCount !== undefined)
  4965                  ? selectionCount
  4966                  : module.get.selectionCount()
  4967                ;
  4968                if(selectionCount >= settings.maxSelections) {
  4969                  module.debug('Maximum selection count reached');
  4970                  if(settings.useLabels) {
  4971                    $item.addClass(className.filtered);
  4972                    module.add.message(message.maxSelections);
  4973                  }
  4974                  return true;
  4975                }
  4976                else {
  4977                  module.verbose('No longer at maximum selection count');
  4978                  module.remove.message();
  4979                  module.remove.filteredItem();
  4980                  if(module.is.searchSelection()) {
  4981                    module.filterItems();
  4982                  }
  4983                  return false;
  4984                }
  4985              }
  4986              return true;
  4987            },
  4988            disabled: function(){
  4989              $search.attr('tabindex',module.is.disabled() ? -1 : 0);
  4990            }
  4991          },
  4992  
  4993          restore: {
  4994            defaults: function(preventChangeTrigger) {
  4995              module.clear(preventChangeTrigger);
  4996              module.restore.defaultText();
  4997              module.restore.defaultValue();
  4998            },
  4999            defaultText: function() {
  5000              var
  5001                defaultText     = module.get.defaultText(),
  5002                placeholderText = module.get.placeholderText
  5003              ;
  5004              if(defaultText === placeholderText) {
  5005                module.debug('Restoring default placeholder text', defaultText);
  5006                module.set.placeholderText(defaultText);
  5007              }
  5008              else {
  5009                module.debug('Restoring default text', defaultText);
  5010                module.set.text(defaultText);
  5011              }
  5012            },
  5013            placeholderText: function() {
  5014              module.set.placeholderText();
  5015            },
  5016            defaultValue: function() {
  5017              var
  5018                defaultValue = module.get.defaultValue()
  5019              ;
  5020              if(defaultValue !== undefined) {
  5021                module.debug('Restoring default value', defaultValue);
  5022                if(defaultValue !== '') {
  5023                  module.set.value(defaultValue);
  5024                  module.set.selected();
  5025                }
  5026                else {
  5027                  module.remove.activeItem();
  5028                  module.remove.selectedItem();
  5029                }
  5030              }
  5031            },
  5032            labels: function() {
  5033              if(settings.allowAdditions) {
  5034                if(!settings.useLabels) {
  5035                  module.error(error.labels);
  5036                  settings.useLabels = true;
  5037                }
  5038                module.debug('Restoring selected values');
  5039                module.create.userLabels();
  5040              }
  5041              module.check.maxSelections();
  5042            },
  5043            selected: function() {
  5044              module.restore.values();
  5045              if(module.is.multiple()) {
  5046                module.debug('Restoring previously selected values and labels');
  5047                module.restore.labels();
  5048              }
  5049              else {
  5050                module.debug('Restoring previously selected values');
  5051              }
  5052            },
  5053            values: function() {
  5054              // prevents callbacks from occurring on initial load
  5055              module.set.initialLoad();
  5056              if(settings.apiSettings && settings.saveRemoteData && module.get.remoteValues()) {
  5057                module.restore.remoteValues();
  5058              }
  5059              else {
  5060                module.set.selected();
  5061              }
  5062              var value = module.get.value();
  5063              if(value && value !== '' && !(Array.isArray(value) && value.length === 0)) {
  5064                $input.removeClass(className.noselection);
  5065              } else {
  5066                $input.addClass(className.noselection);
  5067              }
  5068              module.remove.initialLoad();
  5069            },
  5070            remoteValues: function() {
  5071              var
  5072                values = module.get.remoteValues()
  5073              ;
  5074              module.debug('Recreating selected from session data', values);
  5075              if(values) {
  5076                if( module.is.single() ) {
  5077                  $.each(values, function(value, name) {
  5078                    module.set.text(name);
  5079                  });
  5080                }
  5081                else {
  5082                  $.each(values, function(value, name) {
  5083                    module.add.label(value, name);
  5084                  });
  5085                }
  5086              }
  5087            }
  5088          },
  5089  
  5090          read: {
  5091            remoteData: function(value) {
  5092              var
  5093                name
  5094              ;
  5095              if(window.Storage === undefined) {
  5096                module.error(error.noStorage);
  5097                return;
  5098              }
  5099              name = sessionStorage.getItem(value);
  5100              return (name !== undefined)
  5101                ? name
  5102                : false
  5103              ;
  5104            }
  5105          },
  5106  
  5107          save: {
  5108            defaults: function() {
  5109              module.save.defaultText();
  5110              module.save.placeholderText();
  5111              module.save.defaultValue();
  5112            },
  5113            defaultValue: function() {
  5114              var
  5115                value = module.get.value()
  5116              ;
  5117              module.verbose('Saving default value as', value);
  5118              $module.data(metadata.defaultValue, value);
  5119            },
  5120            defaultText: function() {
  5121              var
  5122                text = module.get.text()
  5123              ;
  5124              module.verbose('Saving default text as', text);
  5125              $module.data(metadata.defaultText, text);
  5126            },
  5127            placeholderText: function() {
  5128              var
  5129                text
  5130              ;
  5131              if(settings.placeholder !== false && $text.hasClass(className.placeholder)) {
  5132                text = module.get.text();
  5133                module.verbose('Saving placeholder text as', text);
  5134                $module.data(metadata.placeholderText, text);
  5135              }
  5136            },
  5137            remoteData: function(name, value) {
  5138              if(window.Storage === undefined) {
  5139                module.error(error.noStorage);
  5140                return;
  5141              }
  5142              module.verbose('Saving remote data to session storage', value, name);
  5143              sessionStorage.setItem(value, name);
  5144            }
  5145          },
  5146  
  5147          clear: function(preventChangeTrigger) {
  5148            if(module.is.multiple() && settings.useLabels) {
  5149              module.remove.labels();
  5150            }
  5151            else {
  5152              module.remove.activeItem();
  5153              module.remove.selectedItem();
  5154              module.remove.filteredItem();
  5155            }
  5156            module.set.placeholderText();
  5157            module.clearValue(preventChangeTrigger);
  5158          },
  5159  
  5160          clearValue: function(preventChangeTrigger) {
  5161            module.set.value('', null, null, preventChangeTrigger);
  5162          },
  5163  
  5164          scrollPage: function(direction, $selectedItem) {
  5165            var
  5166              $currentItem  = $selectedItem || module.get.selectedItem(),
  5167              $menu         = $currentItem.closest(selector.menu),
  5168              menuHeight    = $menu.outerHeight(),
  5169              currentScroll = $menu.scrollTop(),
  5170              itemHeight    = $item.eq(0).outerHeight(),
  5171              itemsPerPage  = Math.floor(menuHeight / itemHeight),
  5172              maxScroll     = $menu.prop('scrollHeight'),
  5173              newScroll     = (direction == 'up')
  5174                ? currentScroll - (itemHeight * itemsPerPage)
  5175                : currentScroll + (itemHeight * itemsPerPage),
  5176              $selectableItem = $item.not(selector.unselectable),
  5177              isWithinRange,
  5178              $nextSelectedItem,
  5179              elementIndex
  5180            ;
  5181            elementIndex      = (direction == 'up')
  5182              ? $selectableItem.index($currentItem) - itemsPerPage
  5183              : $selectableItem.index($currentItem) + itemsPerPage
  5184            ;
  5185            isWithinRange = (direction == 'up')
  5186              ? (elementIndex >= 0)
  5187              : (elementIndex < $selectableItem.length)
  5188            ;
  5189            $nextSelectedItem = (isWithinRange)
  5190              ? $selectableItem.eq(elementIndex)
  5191              : (direction == 'up')
  5192                ? $selectableItem.first()
  5193                : $selectableItem.last()
  5194            ;
  5195            if($nextSelectedItem.length > 0) {
  5196              module.debug('Scrolling page', direction, $nextSelectedItem);
  5197              $currentItem
  5198                .removeClass(className.selected)
  5199              ;
  5200              $nextSelectedItem
  5201                .addClass(className.selected)
  5202              ;
  5203              if(settings.selectOnKeydown && module.is.single()) {
  5204                module.set.selectedItem($nextSelectedItem);
  5205              }
  5206              $menu
  5207                .scrollTop(newScroll)
  5208              ;
  5209            }
  5210          },
  5211  
  5212          set: {
  5213            filtered: function() {
  5214              var
  5215                isMultiple       = module.is.multiple(),
  5216                isSearch         = module.is.searchSelection(),
  5217                isSearchMultiple = (isMultiple && isSearch),
  5218                searchValue      = (isSearch)
  5219                  ? module.get.query()
  5220                  : '',
  5221                hasSearchValue   = (typeof searchValue === 'string' && searchValue.length > 0),
  5222                searchWidth      = module.get.searchWidth(),
  5223                valueIsSet       = searchValue !== ''
  5224              ;
  5225              if(isMultiple && hasSearchValue) {
  5226                module.verbose('Adjusting input width', searchWidth, settings.glyphWidth);
  5227                $search.css('width', searchWidth);
  5228              }
  5229              if(hasSearchValue || (isSearchMultiple && valueIsSet)) {
  5230                module.verbose('Hiding placeholder text');
  5231                $text.addClass(className.filtered);
  5232              }
  5233              else if(!isMultiple || (isSearchMultiple && !valueIsSet)) {
  5234                module.verbose('Showing placeholder text');
  5235                $text.removeClass(className.filtered);
  5236              }
  5237            },
  5238            empty: function() {
  5239              $module.addClass(className.empty);
  5240            },
  5241            loading: function() {
  5242              $module.addClass(className.loading);
  5243            },
  5244            placeholderText: function(text) {
  5245              text = text || module.get.placeholderText();
  5246              module.debug('Setting placeholder text', text);
  5247              module.set.text(text);
  5248              $text.addClass(className.placeholder);
  5249            },
  5250            tabbable: function() {
  5251              if( module.is.searchSelection() ) {
  5252                module.debug('Added tabindex to searchable dropdown');
  5253                $search
  5254                  .val('')
  5255                ;
  5256                module.check.disabled();
  5257                $menu
  5258                  .attr('tabindex', -1)
  5259                ;
  5260              }
  5261              else {
  5262                module.debug('Added tabindex to dropdown');
  5263                if( $module.attr('tabindex') === undefined) {
  5264                  $module
  5265                    .attr('tabindex', 0)
  5266                  ;
  5267                  $menu
  5268                    .attr('tabindex', -1)
  5269                  ;
  5270                }
  5271              }
  5272            },
  5273            initialLoad: function() {
  5274              module.verbose('Setting initial load');
  5275              initialLoad = true;
  5276            },
  5277            activeItem: function($item) {
  5278              if( settings.allowAdditions && $item.filter(selector.addition).length > 0 ) {
  5279                $item.addClass(className.filtered);
  5280              }
  5281              else {
  5282                $item.addClass(className.active);
  5283              }
  5284            },
  5285            partialSearch: function(text) {
  5286              var
  5287                length = module.get.query().length
  5288              ;
  5289              $search.val( text.substr(0, length));
  5290            },
  5291            scrollPosition: function($item, forceScroll) {
  5292              var
  5293                edgeTolerance = 5,
  5294                $menu,
  5295                hasActive,
  5296                offset,
  5297                itemHeight,
  5298                itemOffset,
  5299                menuOffset,
  5300                menuScroll,
  5301                menuHeight,
  5302                abovePage,
  5303                belowPage
  5304              ;
  5305  
  5306              $item       = $item || module.get.selectedItem();
  5307              $menu       = $item.closest(selector.menu);
  5308              hasActive   = ($item && $item.length > 0);
  5309              forceScroll = (forceScroll !== undefined)
  5310                ? forceScroll
  5311                : false
  5312              ;
  5313              if(module.get.activeItem().length === 0){
  5314                forceScroll = false;
  5315              }
  5316              if($item && $menu.length > 0 && hasActive) {
  5317                itemOffset = $item.position().top;
  5318  
  5319                $menu.addClass(className.loading);
  5320                menuScroll = $menu.scrollTop();
  5321                menuOffset = $menu.offset().top;
  5322                itemOffset = $item.offset().top;
  5323                offset     = menuScroll - menuOffset + itemOffset;
  5324                if(!forceScroll) {
  5325                  menuHeight = $menu.height();
  5326                  belowPage  = menuScroll + menuHeight < (offset + edgeTolerance);
  5327                  abovePage  = ((offset - edgeTolerance) < menuScroll);
  5328                }
  5329                module.debug('Scrolling to active item', offset);
  5330                if(forceScroll || abovePage || belowPage) {
  5331                  $menu.scrollTop(offset);
  5332                }
  5333                $menu.removeClass(className.loading);
  5334              }
  5335            },
  5336            text: function(text) {
  5337              if(settings.action === 'combo') {
  5338                module.debug('Changing combo button text', text, $combo);
  5339                if(settings.preserveHTML) {
  5340                  $combo.html(text);
  5341                }
  5342                else {
  5343                  $combo.text(text);
  5344                }
  5345              }
  5346              else if(settings.action === 'activate') {
  5347                if(text !== module.get.placeholderText()) {
  5348                  $text.removeClass(className.placeholder);
  5349                }
  5350                module.debug('Changing text', text, $text);
  5351                $text
  5352                  .removeClass(className.filtered)
  5353                ;
  5354                if(settings.preserveHTML) {
  5355                  $text.html(text);
  5356                }
  5357                else {
  5358                  $text.text(text);
  5359                }
  5360              }
  5361            },
  5362            selectedItem: function($item) {
  5363              var
  5364                value      = module.get.choiceValue($item),
  5365                searchText = module.get.choiceText($item, false),
  5366                text       = module.get.choiceText($item, true)
  5367              ;
  5368              module.debug('Setting user selection to item', $item);
  5369              module.remove.activeItem();
  5370              module.set.partialSearch(searchText);
  5371              module.set.activeItem($item);
  5372              module.set.selected(value, $item);
  5373              module.set.text(text);
  5374            },
  5375            selectedLetter: function(letter) {
  5376              var
  5377                $selectedItem         = $item.filter('.' + className.selected),
  5378                alreadySelectedLetter = $selectedItem.length > 0 && module.has.firstLetter($selectedItem, letter),
  5379                $nextValue            = false,
  5380                $nextItem
  5381              ;
  5382              // check next of same letter
  5383              if(alreadySelectedLetter) {
  5384                $nextItem = $selectedItem.nextAll($item).eq(0);
  5385                if( module.has.firstLetter($nextItem, letter) ) {
  5386                  $nextValue  = $nextItem;
  5387                }
  5388              }
  5389              // check all values
  5390              if(!$nextValue) {
  5391                $item
  5392                  .each(function(){
  5393                    if(module.has.firstLetter($(this), letter)) {
  5394                      $nextValue = $(this);
  5395                      return false;
  5396                    }
  5397                  })
  5398                ;
  5399              }
  5400              // set next value
  5401              if($nextValue) {
  5402                module.verbose('Scrolling to next value with letter', letter);
  5403                module.set.scrollPosition($nextValue);
  5404                $selectedItem.removeClass(className.selected);
  5405                $nextValue.addClass(className.selected);
  5406                if(settings.selectOnKeydown && module.is.single()) {
  5407                  module.set.selectedItem($nextValue);
  5408                }
  5409              }
  5410            },
  5411            direction: function($menu) {
  5412              if(settings.direction == 'auto') {
  5413                // reset position, remove upward if it's base menu
  5414                if (!$menu) {
  5415                  module.remove.upward();
  5416                } else if (module.is.upward($menu)) {
  5417                  //we need make sure when make assertion openDownward for $menu, $menu does not have upward class
  5418                  module.remove.upward($menu);
  5419                }
  5420  
  5421                if(module.can.openDownward($menu)) {
  5422                  module.remove.upward($menu);
  5423                }
  5424                else {
  5425                  module.set.upward($menu);
  5426                }
  5427                if(!module.is.leftward($menu) && !module.can.openRightward($menu)) {
  5428                  module.set.leftward($menu);
  5429                }
  5430              }
  5431              else if(settings.direction == 'upward') {
  5432                module.set.upward($menu);
  5433              }
  5434            },
  5435            upward: function($currentMenu) {
  5436              var $element = $currentMenu || $module;
  5437              $element.addClass(className.upward);
  5438            },
  5439            leftward: function($currentMenu) {
  5440              var $element = $currentMenu || $menu;
  5441              $element.addClass(className.leftward);
  5442            },
  5443            value: function(value, text, $selected, preventChangeTrigger) {
  5444              if(value !== undefined && value !== '' && !(Array.isArray(value) && value.length === 0)) {
  5445                $input.removeClass(className.noselection);
  5446              } else {
  5447                $input.addClass(className.noselection);
  5448              }
  5449              var
  5450                escapedValue = module.escape.value(value),
  5451                hasInput     = ($input.length > 0),
  5452                currentValue = module.get.values(),
  5453                stringValue  = (value !== undefined)
  5454                  ? String(value)
  5455                  : value,
  5456                newValue
  5457              ;
  5458              if(hasInput) {
  5459                if(!settings.allowReselection && stringValue == currentValue) {
  5460                  module.verbose('Skipping value update already same value', value, currentValue);
  5461                  if(!module.is.initialLoad()) {
  5462                    return;
  5463                  }
  5464                }
  5465  
  5466                if( module.is.single() && module.has.selectInput() && module.can.extendSelect() ) {
  5467                  module.debug('Adding user option', value);
  5468                  module.add.optionValue(value);
  5469                }
  5470                module.debug('Updating input value', escapedValue, currentValue);
  5471                internalChange = true;
  5472                $input
  5473                  .val(escapedValue)
  5474                ;
  5475                if(settings.fireOnInit === false && module.is.initialLoad()) {
  5476                  module.debug('Input native change event ignored on initial load');
  5477                }
  5478                else if(preventChangeTrigger !== true) {
  5479                  module.trigger.change();
  5480                }
  5481                internalChange = false;
  5482              }
  5483              else {
  5484                module.verbose('Storing value in metadata', escapedValue, $input);
  5485                if(escapedValue !== currentValue) {
  5486                  $module.data(metadata.value, stringValue);
  5487                }
  5488              }
  5489              if(settings.fireOnInit === false && module.is.initialLoad()) {
  5490                module.verbose('No callback on initial load', settings.onChange);
  5491              }
  5492              else if(preventChangeTrigger !== true) {
  5493                settings.onChange.call(element, value, text, $selected);
  5494              }
  5495            },
  5496            active: function() {
  5497              $module
  5498                .addClass(className.active)
  5499              ;
  5500            },
  5501            multiple: function() {
  5502              $module.addClass(className.multiple);
  5503            },
  5504            visible: function() {
  5505              $module.addClass(className.visible);
  5506            },
  5507            exactly: function(value, $selectedItem) {
  5508              module.debug('Setting selected to exact values');
  5509              module.clear();
  5510              module.set.selected(value, $selectedItem);
  5511            },
  5512            selected: function(value, $selectedItem) {
  5513              var
  5514                isMultiple = module.is.multiple()
  5515              ;
  5516              $selectedItem = (settings.allowAdditions)
  5517                ? $selectedItem || module.get.itemWithAdditions(value)
  5518                : $selectedItem || module.get.item(value)
  5519              ;
  5520              if(!$selectedItem) {
  5521                return;
  5522              }
  5523              module.debug('Setting selected menu item to', $selectedItem);
  5524              if(module.is.multiple()) {
  5525                module.remove.searchWidth();
  5526              }
  5527              if(module.is.single()) {
  5528                module.remove.activeItem();
  5529                module.remove.selectedItem();
  5530              }
  5531              else if(settings.useLabels) {
  5532                module.remove.selectedItem();
  5533              }
  5534              // select each item
  5535              $selectedItem
  5536                .each(function() {
  5537                  var
  5538                    $selected      = $(this),
  5539                    selectedText   = module.get.choiceText($selected),
  5540                    selectedValue  = module.get.choiceValue($selected, selectedText),
  5541  
  5542                    isFiltered     = $selected.hasClass(className.filtered),
  5543                    isActive       = $selected.hasClass(className.active),
  5544                    isUserValue    = $selected.hasClass(className.addition),
  5545                    shouldAnimate  = (isMultiple && $selectedItem.length == 1)
  5546                  ;
  5547                  if(isMultiple) {
  5548                    if(!isActive || isUserValue) {
  5549                      if(settings.apiSettings && settings.saveRemoteData) {
  5550                        module.save.remoteData(selectedText, selectedValue);
  5551                      }
  5552                      if(settings.useLabels) {
  5553                        module.add.label(selectedValue, selectedText, shouldAnimate);
  5554                        module.add.value(selectedValue, selectedText, $selected);
  5555                        module.set.activeItem($selected);
  5556                        module.filterActive();
  5557                        module.select.nextAvailable($selectedItem);
  5558                      }
  5559                      else {
  5560                        module.add.value(selectedValue, selectedText, $selected);
  5561                        module.set.text(module.add.variables(message.count));
  5562                        module.set.activeItem($selected);
  5563                      }
  5564                    }
  5565                    else if(!isFiltered && (settings.useLabels || selectActionActive)) {
  5566                      module.debug('Selected active value, removing label');
  5567                      module.remove.selected(selectedValue);
  5568                    }
  5569                  }
  5570                  else {
  5571                    if(settings.apiSettings && settings.saveRemoteData) {
  5572                      module.save.remoteData(selectedText, selectedValue);
  5573                    }
  5574                    module.set.text(selectedText);
  5575                    module.set.value(selectedValue, selectedText, $selected);
  5576                    $selected
  5577                      .addClass(className.active)
  5578                      .addClass(className.selected)
  5579                    ;
  5580                  }
  5581                })
  5582              ;
  5583              module.remove.searchTerm();
  5584            }
  5585          },
  5586  
  5587          add: {
  5588            label: function(value, text, shouldAnimate) {
  5589              var
  5590                $next  = module.is.searchSelection()
  5591                  ? $search
  5592                  : $text,
  5593                escapedValue = module.escape.value(value),
  5594                $label
  5595              ;
  5596              if(settings.ignoreCase) {
  5597                escapedValue = escapedValue.toLowerCase();
  5598              }
  5599              $label =  $('<a />')
  5600                .addClass(className.label)
  5601                .attr('data-' + metadata.value, escapedValue)
  5602                .html(templates.label(escapedValue, text, settings.preserveHTML, settings.className))
  5603              ;
  5604              $label = settings.onLabelCreate.call($label, escapedValue, text);
  5605  
  5606              if(module.has.label(value)) {
  5607                module.debug('User selection already exists, skipping', escapedValue);
  5608                return;
  5609              }
  5610              if(settings.label.variation) {
  5611                $label.addClass(settings.label.variation);
  5612              }
  5613              if(shouldAnimate === true) {
  5614                module.debug('Animating in label', $label);
  5615                $label
  5616                  .addClass(className.hidden)
  5617                  .insertBefore($next)
  5618                  .transition({
  5619                      animation  : settings.label.transition,
  5620                      debug      : settings.debug,
  5621                      verbose    : settings.verbose,
  5622                      duration   : settings.label.duration
  5623                  })
  5624                ;
  5625              }
  5626              else {
  5627                module.debug('Adding selection label', $label);
  5628                $label
  5629                  .insertBefore($next)
  5630                ;
  5631              }
  5632            },
  5633            message: function(message) {
  5634              var
  5635                $message = $menu.children(selector.message),
  5636                html     = settings.templates.message(module.add.variables(message))
  5637              ;
  5638              if($message.length > 0) {
  5639                $message
  5640                  .html(html)
  5641                ;
  5642              }
  5643              else {
  5644                $message = $('<div/>')
  5645                  .html(html)
  5646                  .addClass(className.message)
  5647                  .appendTo($menu)
  5648                ;
  5649              }
  5650            },
  5651            optionValue: function(value) {
  5652              var
  5653                escapedValue = module.escape.value(value),
  5654                $option      = $input.find('option[value="' + module.escape.string(escapedValue) + '"]'),
  5655                hasOption    = ($option.length > 0)
  5656              ;
  5657              if(hasOption) {
  5658                return;
  5659              }
  5660              // temporarily disconnect observer
  5661              module.disconnect.selectObserver();
  5662              if( module.is.single() ) {
  5663                module.verbose('Removing previous user addition');
  5664                $input.find('option.' + className.addition).remove();
  5665              }
  5666              $('<option/>')
  5667                .prop('value', escapedValue)
  5668                .addClass(className.addition)
  5669                .html(value)
  5670                .appendTo($input)
  5671              ;
  5672              module.verbose('Adding user addition as an <option>', value);
  5673              module.observe.select();
  5674            },
  5675            userSuggestion: function(value) {
  5676              var
  5677                $addition         = $menu.children(selector.addition),
  5678                $existingItem     = module.get.item(value),
  5679                alreadyHasValue   = $existingItem && $existingItem.not(selector.addition).length,
  5680                hasUserSuggestion = $addition.length > 0,
  5681                html
  5682              ;
  5683              if(settings.useLabels && module.has.maxSelections()) {
  5684                return;
  5685              }
  5686              if(value === '' || alreadyHasValue) {
  5687                $addition.remove();
  5688                return;
  5689              }
  5690              if(hasUserSuggestion) {
  5691                $addition
  5692                  .data(metadata.value, value)
  5693                  .data(metadata.text, value)
  5694                  .attr('data-' + metadata.value, value)
  5695                  .attr('data-' + metadata.text, value)
  5696                  .removeClass(className.filtered)
  5697                ;
  5698                if(!settings.hideAdditions) {
  5699                  html = settings.templates.addition( module.add.variables(message.addResult, value) );
  5700                  $addition
  5701                    .html(html)
  5702                  ;
  5703                }
  5704                module.verbose('Replacing user suggestion with new value', $addition);
  5705              }
  5706              else {
  5707                $addition = module.create.userChoice(value);
  5708                $addition
  5709                  .prependTo($menu)
  5710                ;
  5711                module.verbose('Adding item choice to menu corresponding with user choice addition', $addition);
  5712              }
  5713              if(!settings.hideAdditions || module.is.allFiltered()) {
  5714                $addition
  5715                  .addClass(className.selected)
  5716                  .siblings()
  5717                  .removeClass(className.selected)
  5718                ;
  5719              }
  5720              module.refreshItems();
  5721            },
  5722            variables: function(message, term) {
  5723              var
  5724                hasCount    = (message.search('{count}') !== -1),
  5725                hasMaxCount = (message.search('{maxCount}') !== -1),
  5726                hasTerm     = (message.search('{term}') !== -1),
  5727                count,
  5728                query
  5729              ;
  5730              module.verbose('Adding templated variables to message', message);
  5731              if(hasCount) {
  5732                count  = module.get.selectionCount();
  5733                message = message.replace('{count}', count);
  5734              }
  5735              if(hasMaxCount) {
  5736                count  = module.get.selectionCount();
  5737                message = message.replace('{maxCount}', settings.maxSelections);
  5738              }
  5739              if(hasTerm) {
  5740                query   = term || module.get.query();
  5741                message = message.replace('{term}', query);
  5742              }
  5743              return message;
  5744            },
  5745            value: function(addedValue, addedText, $selectedItem) {
  5746              var
  5747                currentValue = module.get.values(),
  5748                newValue
  5749              ;
  5750              if(module.has.value(addedValue)) {
  5751                module.debug('Value already selected');
  5752                return;
  5753              }
  5754              if(addedValue === '') {
  5755                module.debug('Cannot select blank values from multiselect');
  5756                return;
  5757              }
  5758              // extend current array
  5759              if(Array.isArray(currentValue)) {
  5760                newValue = currentValue.concat([addedValue]);
  5761                newValue = module.get.uniqueArray(newValue);
  5762              }
  5763              else {
  5764                newValue = [addedValue];
  5765              }
  5766              // add values
  5767              if( module.has.selectInput() ) {
  5768                if(module.can.extendSelect()) {
  5769                  module.debug('Adding value to select', addedValue, newValue, $input);
  5770                  module.add.optionValue(addedValue);
  5771                }
  5772              }
  5773              else {
  5774                newValue = newValue.join(settings.delimiter);
  5775                module.debug('Setting hidden input to delimited value', newValue, $input);
  5776              }
  5777  
  5778              if(settings.fireOnInit === false && module.is.initialLoad()) {
  5779                module.verbose('Skipping onadd callback on initial load', settings.onAdd);
  5780              }
  5781              else {
  5782                settings.onAdd.call(element, addedValue, addedText, $selectedItem);
  5783              }
  5784              module.set.value(newValue, addedText, $selectedItem);
  5785              module.check.maxSelections();
  5786            },
  5787          },
  5788  
  5789          remove: {
  5790            active: function() {
  5791              $module.removeClass(className.active);
  5792            },
  5793            activeLabel: function() {
  5794              $module.find(selector.label).removeClass(className.active);
  5795            },
  5796            empty: function() {
  5797              $module.removeClass(className.empty);
  5798            },
  5799            loading: function() {
  5800              $module.removeClass(className.loading);
  5801            },
  5802            initialLoad: function() {
  5803              initialLoad = false;
  5804            },
  5805            upward: function($currentMenu) {
  5806              var $element = $currentMenu || $module;
  5807              $element.removeClass(className.upward);
  5808            },
  5809            leftward: function($currentMenu) {
  5810              var $element = $currentMenu || $menu;
  5811              $element.removeClass(className.leftward);
  5812            },
  5813            visible: function() {
  5814              $module.removeClass(className.visible);
  5815            },
  5816            activeItem: function() {
  5817              $item.removeClass(className.active);
  5818            },
  5819            filteredItem: function() {
  5820              if(settings.useLabels && module.has.maxSelections() ) {
  5821                return;
  5822              }
  5823              if(settings.useLabels && module.is.multiple()) {
  5824                $item.not('.' + className.active).removeClass(className.filtered);
  5825              }
  5826              else {
  5827                $item.removeClass(className.filtered);
  5828              }
  5829              if(settings.hideDividers) {
  5830                $divider.removeClass(className.hidden);
  5831              }
  5832              module.remove.empty();
  5833            },
  5834            optionValue: function(value) {
  5835              var
  5836                escapedValue = module.escape.value(value),
  5837                $option      = $input.find('option[value="' + module.escape.string(escapedValue) + '"]'),
  5838                hasOption    = ($option.length > 0)
  5839              ;
  5840              if(!hasOption || !$option.hasClass(className.addition)) {
  5841                return;
  5842              }
  5843              // temporarily disconnect observer
  5844              if(selectObserver) {
  5845                selectObserver.disconnect();
  5846                module.verbose('Temporarily disconnecting mutation observer');
  5847              }
  5848              $option.remove();
  5849              module.verbose('Removing user addition as an <option>', escapedValue);
  5850              if(selectObserver) {
  5851                selectObserver.observe($input[0], {
  5852                  childList : true,
  5853                  subtree   : true
  5854                });
  5855              }
  5856            },
  5857            message: function() {
  5858              $menu.children(selector.message).remove();
  5859            },
  5860            searchWidth: function() {
  5861              $search.css('width', '');
  5862            },
  5863            searchTerm: function() {
  5864              module.verbose('Cleared search term');
  5865              $search.val('');
  5866              module.set.filtered();
  5867            },
  5868            userAddition: function() {
  5869              $item.filter(selector.addition).remove();
  5870            },
  5871            selected: function(value, $selectedItem) {
  5872              $selectedItem = (settings.allowAdditions)
  5873                ? $selectedItem || module.get.itemWithAdditions(value)
  5874                : $selectedItem || module.get.item(value)
  5875              ;
  5876  
  5877              if(!$selectedItem) {
  5878                return false;
  5879              }
  5880  
  5881              $selectedItem
  5882                .each(function() {
  5883                  var
  5884                    $selected     = $(this),
  5885                    selectedText  = module.get.choiceText($selected),
  5886                    selectedValue = module.get.choiceValue($selected, selectedText)
  5887                  ;
  5888                  if(module.is.multiple()) {
  5889                    if(settings.useLabels) {
  5890                      module.remove.value(selectedValue, selectedText, $selected);
  5891                      module.remove.label(selectedValue);
  5892                    }
  5893                    else {
  5894                      module.remove.value(selectedValue, selectedText, $selected);
  5895                      if(module.get.selectionCount() === 0) {
  5896                        module.set.placeholderText();
  5897                      }
  5898                      else {
  5899                        module.set.text(module.add.variables(message.count));
  5900                      }
  5901                    }
  5902                  }
  5903                  else {
  5904                    module.remove.value(selectedValue, selectedText, $selected);
  5905                  }
  5906                  $selected
  5907                    .removeClass(className.filtered)
  5908                    .removeClass(className.active)
  5909                  ;
  5910                  if(settings.useLabels) {
  5911                    $selected.removeClass(className.selected);
  5912                  }
  5913                })
  5914              ;
  5915            },
  5916            selectedItem: function() {
  5917              $item.removeClass(className.selected);
  5918            },
  5919            value: function(removedValue, removedText, $removedItem) {
  5920              var
  5921                values = module.get.values(),
  5922                newValue
  5923              ;
  5924              removedValue = module.escape.htmlEntities(removedValue);
  5925              if( module.has.selectInput() ) {
  5926                module.verbose('Input is <select> removing selected option', removedValue);
  5927                newValue = module.remove.arrayValue(removedValue, values);
  5928                module.remove.optionValue(removedValue);
  5929              }
  5930              else {
  5931                module.verbose('Removing from delimited values', removedValue);
  5932                newValue = module.remove.arrayValue(removedValue, values);
  5933                newValue = newValue.join(settings.delimiter);
  5934              }
  5935              if(settings.fireOnInit === false && module.is.initialLoad()) {
  5936                module.verbose('No callback on initial load', settings.onRemove);
  5937              }
  5938              else {
  5939                settings.onRemove.call(element, removedValue, removedText, $removedItem);
  5940              }
  5941              module.set.value(newValue, removedText, $removedItem);
  5942              module.check.maxSelections();
  5943            },
  5944            arrayValue: function(removedValue, values) {
  5945              if( !Array.isArray(values) ) {
  5946                values = [values];
  5947              }
  5948              values = $.grep(values, function(value){
  5949                return (removedValue != value);
  5950              });
  5951              module.verbose('Removed value from delimited string', removedValue, values);
  5952              return values;
  5953            },
  5954            label: function(value, shouldAnimate) {
  5955              var
  5956                $labels       = $module.find(selector.label),
  5957                $removedLabel = $labels.filter('[data-' + metadata.value + '="' + module.escape.string(settings.ignoreCase ? value.toLowerCase() : value) +'"]')
  5958              ;
  5959              module.verbose('Removing label', $removedLabel);
  5960              $removedLabel.remove();
  5961            },
  5962            activeLabels: function($activeLabels) {
  5963              $activeLabels = $activeLabels || $module.find(selector.label).filter('.' + className.active);
  5964              module.verbose('Removing active label selections', $activeLabels);
  5965              module.remove.labels($activeLabels);
  5966            },
  5967            labels: function($labels) {
  5968              $labels = $labels || $module.find(selector.label);
  5969              module.verbose('Removing labels', $labels);
  5970              $labels
  5971                .each(function(){
  5972                  var
  5973                    $label      = $(this),
  5974                    value       = $label.data(metadata.value),
  5975                    stringValue = (value !== undefined)
  5976                      ? String(value)
  5977                      : value,
  5978                    isUserValue = module.is.userValue(stringValue)
  5979                  ;
  5980                  if(settings.onLabelRemove.call($label, value) === false) {
  5981                    module.debug('Label remove callback cancelled removal');
  5982                    return;
  5983                  }
  5984                  module.remove.message();
  5985                  if(isUserValue) {
  5986                    module.remove.value(stringValue);
  5987                    module.remove.label(stringValue);
  5988                  }
  5989                  else {
  5990                    // selected will also remove label
  5991                    module.remove.selected(stringValue);
  5992                  }
  5993                })
  5994              ;
  5995            },
  5996            tabbable: function() {
  5997              if( module.is.searchSelection() ) {
  5998                module.debug('Searchable dropdown initialized');
  5999                $search
  6000                  .removeAttr('tabindex')
  6001                ;
  6002                $menu
  6003                  .removeAttr('tabindex')
  6004                ;
  6005              }
  6006              else {
  6007                module.debug('Simple selection dropdown initialized');
  6008                $module
  6009                  .removeAttr('tabindex')
  6010                ;
  6011                $menu
  6012                  .removeAttr('tabindex')
  6013                ;
  6014              }
  6015            },
  6016            diacritics: function(text) {
  6017              return settings.ignoreDiacritics ?  text.normalize('NFD').replace(/[\u0300-\u036f]/g, '') : text;
  6018            }
  6019          },
  6020  
  6021          has: {
  6022            menuSearch: function() {
  6023              return (module.has.search() && $search.closest($menu).length > 0);
  6024            },
  6025            clearItem: function() {
  6026              return ($clear.length > 0);
  6027            },
  6028            search: function() {
  6029              return ($search.length > 0);
  6030            },
  6031            sizer: function() {
  6032              return ($sizer.length > 0);
  6033            },
  6034            selectInput: function() {
  6035              return ( $input.is('select') );
  6036            },
  6037            minCharacters: function(searchTerm) {
  6038              if(settings.minCharacters && !iconClicked) {
  6039                searchTerm = (searchTerm !== undefined)
  6040                  ? String(searchTerm)
  6041                  : String(module.get.query())
  6042                ;
  6043                return (searchTerm.length >= settings.minCharacters);
  6044              }
  6045              iconClicked=false;
  6046              return true;
  6047            },
  6048            firstLetter: function($item, letter) {
  6049              var
  6050                text,
  6051                firstLetter
  6052              ;
  6053              if(!$item || $item.length === 0 || typeof letter !== 'string') {
  6054                return false;
  6055              }
  6056              text        = module.get.choiceText($item, false);
  6057              letter      = letter.toLowerCase();
  6058              firstLetter = String(text).charAt(0).toLowerCase();
  6059              return (letter == firstLetter);
  6060            },
  6061            input: function() {
  6062              return ($input.length > 0);
  6063            },
  6064            items: function() {
  6065              return ($item.length > 0);
  6066            },
  6067            menu: function() {
  6068              return ($menu.length > 0);
  6069            },
  6070            message: function() {
  6071              return ($menu.children(selector.message).length !== 0);
  6072            },
  6073            label: function(value) {
  6074              var
  6075                escapedValue = module.escape.value(value),
  6076                $labels      = $module.find(selector.label)
  6077              ;
  6078              if(settings.ignoreCase) {
  6079                escapedValue = escapedValue.toLowerCase();
  6080              }
  6081              return ($labels.filter('[data-' + metadata.value + '="' + module.escape.string(escapedValue) +'"]').length > 0);
  6082            },
  6083            maxSelections: function() {
  6084              return (settings.maxSelections && module.get.selectionCount() >= settings.maxSelections);
  6085            },
  6086            allResultsFiltered: function() {
  6087              var
  6088                $normalResults = $item.not(selector.addition)
  6089              ;
  6090              return ($normalResults.filter(selector.unselectable).length === $normalResults.length);
  6091            },
  6092            userSuggestion: function() {
  6093              return ($menu.children(selector.addition).length > 0);
  6094            },
  6095            query: function() {
  6096              return (module.get.query() !== '');
  6097            },
  6098            value: function(value) {
  6099              return (settings.ignoreCase)
  6100                ? module.has.valueIgnoringCase(value)
  6101                : module.has.valueMatchingCase(value)
  6102              ;
  6103            },
  6104            valueMatchingCase: function(value) {
  6105              var
  6106                values   = module.get.values(),
  6107                hasValue = Array.isArray(values)
  6108                 ? values && ($.inArray(value, values) !== -1)
  6109                 : (values == value)
  6110              ;
  6111              return (hasValue)
  6112                ? true
  6113                : false
  6114              ;
  6115            },
  6116            valueIgnoringCase: function(value) {
  6117              var
  6118                values   = module.get.values(),
  6119                hasValue = false
  6120              ;
  6121              if(!Array.isArray(values)) {
  6122                values = [values];
  6123              }
  6124              $.each(values, function(index, existingValue) {
  6125                if(String(value).toLowerCase() == String(existingValue).toLowerCase()) {
  6126                  hasValue = true;
  6127                  return false;
  6128                }
  6129              });
  6130              return hasValue;
  6131            }
  6132          },
  6133  
  6134          is: {
  6135            active: function() {
  6136              return $module.hasClass(className.active);
  6137            },
  6138            animatingInward: function() {
  6139              return $menu.transition('is inward');
  6140            },
  6141            animatingOutward: function() {
  6142              return $menu.transition('is outward');
  6143            },
  6144            bubbledLabelClick: function(event) {
  6145              return $(event.target).is('select, input') && $module.closest('label').length > 0;
  6146            },
  6147            bubbledIconClick: function(event) {
  6148              return $(event.target).closest($icon).length > 0;
  6149            },
  6150            alreadySetup: function() {
  6151              return ($module.is('select') && $module.parent(selector.dropdown).data(moduleNamespace) !== undefined && $module.prev().length === 0);
  6152            },
  6153            animating: function($subMenu) {
  6154              return ($subMenu)
  6155                ? $subMenu.transition && $subMenu.transition('is animating')
  6156                : $menu.transition    && $menu.transition('is animating')
  6157              ;
  6158            },
  6159            leftward: function($subMenu) {
  6160              var $selectedMenu = $subMenu || $menu;
  6161              return $selectedMenu.hasClass(className.leftward);
  6162            },
  6163            clearable: function() {
  6164              return ($module.hasClass(className.clearable) || settings.clearable);
  6165            },
  6166            disabled: function() {
  6167              return $module.hasClass(className.disabled);
  6168            },
  6169            focused: function() {
  6170              return (document.activeElement === $module[0]);
  6171            },
  6172            focusedOnSearch: function() {
  6173              return (document.activeElement === $search[0]);
  6174            },
  6175            allFiltered: function() {
  6176              return( (module.is.multiple() || module.has.search()) && !(settings.hideAdditions == false && module.has.userSuggestion()) && !module.has.message() && module.has.allResultsFiltered() );
  6177            },
  6178            hidden: function($subMenu) {
  6179              return !module.is.visible($subMenu);
  6180            },
  6181            initialLoad: function() {
  6182              return initialLoad;
  6183            },
  6184            inObject: function(needle, object) {
  6185              var
  6186                found = false
  6187              ;
  6188              $.each(object, function(index, property) {
  6189                if(property == needle) {
  6190                  found = true;
  6191                  return true;
  6192                }
  6193              });
  6194              return found;
  6195            },
  6196            multiple: function() {
  6197              return $module.hasClass(className.multiple);
  6198            },
  6199            remote: function() {
  6200              return settings.apiSettings && module.can.useAPI();
  6201            },
  6202            single: function() {
  6203              return !module.is.multiple();
  6204            },
  6205            selectMutation: function(mutations) {
  6206              var
  6207                selectChanged = false
  6208              ;
  6209              $.each(mutations, function(index, mutation) {
  6210                if($(mutation.target).is('select') || $(mutation.addedNodes).is('select')) {
  6211                  selectChanged = true;
  6212                  return false;
  6213                }
  6214              });
  6215              return selectChanged;
  6216            },
  6217            search: function() {
  6218              return $module.hasClass(className.search);
  6219            },
  6220            searchSelection: function() {
  6221              return ( module.has.search() && $search.parent(selector.dropdown).length === 1 );
  6222            },
  6223            selection: function() {
  6224              return $module.hasClass(className.selection);
  6225            },
  6226            userValue: function(value) {
  6227              return ($.inArray(value, module.get.userValues()) !== -1);
  6228            },
  6229            upward: function($menu) {
  6230              var $element = $menu || $module;
  6231              return $element.hasClass(className.upward);
  6232            },
  6233            visible: function($subMenu) {
  6234              return ($subMenu)
  6235                ? $subMenu.hasClass(className.visible)
  6236                : $menu.hasClass(className.visible)
  6237              ;
  6238            },
  6239            verticallyScrollableContext: function() {
  6240              var
  6241                overflowY = ($context.get(0) !== window)
  6242                  ? $context.css('overflow-y')
  6243                  : false
  6244              ;
  6245              return (overflowY == 'auto' || overflowY == 'scroll');
  6246            },
  6247            horizontallyScrollableContext: function() {
  6248              var
  6249                overflowX = ($context.get(0) !== window)
  6250                  ? $context.css('overflow-X')
  6251                  : false
  6252              ;
  6253              return (overflowX == 'auto' || overflowX == 'scroll');
  6254            }
  6255          },
  6256  
  6257          can: {
  6258            activate: function($item) {
  6259              if(settings.useLabels) {
  6260                return true;
  6261              }
  6262              if(!module.has.maxSelections()) {
  6263                return true;
  6264              }
  6265              if(module.has.maxSelections() && $item.hasClass(className.active)) {
  6266                return true;
  6267              }
  6268              return false;
  6269            },
  6270            openDownward: function($subMenu) {
  6271              var
  6272                $currentMenu    = $subMenu || $menu,
  6273                canOpenDownward = true,
  6274                onScreen        = {},
  6275                calculations
  6276              ;
  6277              $currentMenu
  6278                .addClass(className.loading)
  6279              ;
  6280              calculations = {
  6281                context: {
  6282                  offset    : ($context.get(0) === window)
  6283                    ? { top: 0, left: 0}
  6284                    : $context.offset(),
  6285                  scrollTop : $context.scrollTop(),
  6286                  height    : $context.outerHeight()
  6287                },
  6288                menu : {
  6289                  offset: $currentMenu.offset(),
  6290                  height: $currentMenu.outerHeight()
  6291                }
  6292              };
  6293              if(module.is.verticallyScrollableContext()) {
  6294                calculations.menu.offset.top += calculations.context.scrollTop;
  6295              }
  6296              onScreen = {
  6297                above : (calculations.context.scrollTop) <= calculations.menu.offset.top - calculations.context.offset.top - calculations.menu.height,
  6298                below : (calculations.context.scrollTop + calculations.context.height) >= calculations.menu.offset.top - calculations.context.offset.top + calculations.menu.height
  6299              };
  6300              if(onScreen.below) {
  6301                module.verbose('Dropdown can fit in context downward', onScreen);
  6302                canOpenDownward = true;
  6303              }
  6304              else if(!onScreen.below && !onScreen.above) {
  6305                module.verbose('Dropdown cannot fit in either direction, favoring downward', onScreen);
  6306                canOpenDownward = true;
  6307              }
  6308              else {
  6309                module.verbose('Dropdown cannot fit below, opening upward', onScreen);
  6310                canOpenDownward = false;
  6311              }
  6312              $currentMenu.removeClass(className.loading);
  6313              return canOpenDownward;
  6314            },
  6315            openRightward: function($subMenu) {
  6316              var
  6317                $currentMenu     = $subMenu || $menu,
  6318                canOpenRightward = true,
  6319                isOffscreenRight = false,
  6320                calculations
  6321              ;
  6322              $currentMenu
  6323                .addClass(className.loading)
  6324              ;
  6325              calculations = {
  6326                context: {
  6327                  offset     : ($context.get(0) === window)
  6328                    ? { top: 0, left: 0}
  6329                    : $context.offset(),
  6330                  scrollLeft : $context.scrollLeft(),
  6331                  width      : $context.outerWidth()
  6332                },
  6333                menu: {
  6334                  offset : $currentMenu.offset(),
  6335                  width  : $currentMenu.outerWidth()
  6336                }
  6337              };
  6338              if(module.is.horizontallyScrollableContext()) {
  6339                calculations.menu.offset.left += calculations.context.scrollLeft;
  6340              }
  6341              isOffscreenRight = (calculations.menu.offset.left - calculations.context.offset.left + calculations.menu.width >= calculations.context.scrollLeft + calculations.context.width);
  6342              if(isOffscreenRight) {
  6343                module.verbose('Dropdown cannot fit in context rightward', isOffscreenRight);
  6344                canOpenRightward = false;
  6345              }
  6346              $currentMenu.removeClass(className.loading);
  6347              return canOpenRightward;
  6348            },
  6349            click: function() {
  6350              return (hasTouch || settings.on == 'click');
  6351            },
  6352            extendSelect: function() {
  6353              return settings.allowAdditions || settings.apiSettings;
  6354            },
  6355            show: function() {
  6356              return !module.is.disabled() && (module.has.items() || module.has.message());
  6357            },
  6358            useAPI: function() {
  6359              return $.fn.api !== undefined;
  6360            }
  6361          },
  6362  
  6363          animate: {
  6364            show: function(callback, $subMenu) {
  6365              var
  6366                $currentMenu = $subMenu || $menu,
  6367                start = ($subMenu)
  6368                  ? function() {}
  6369                  : function() {
  6370                    module.hideSubMenus();
  6371                    module.hideOthers();
  6372                    module.set.active();
  6373                  },
  6374                transition
  6375              ;
  6376              callback = $.isFunction(callback)
  6377                ? callback
  6378                : function(){}
  6379              ;
  6380              module.verbose('Doing menu show animation', $currentMenu);
  6381              module.set.direction($subMenu);
  6382              transition = module.get.transition($subMenu);
  6383              if( module.is.selection() ) {
  6384                module.set.scrollPosition(module.get.selectedItem(), true);
  6385              }
  6386              if( module.is.hidden($currentMenu) || module.is.animating($currentMenu) ) {
  6387                var displayType = $module.hasClass('column') ? 'flex' : false;
  6388                if(transition == 'none') {
  6389                  start();
  6390                  $currentMenu.transition({
  6391                    displayType: displayType
  6392                  }).transition('show');
  6393                  callback.call(element);
  6394                }
  6395                else if($.fn.transition !== undefined && $module.transition('is supported')) {
  6396                  $currentMenu
  6397                    .transition({
  6398                      animation  : transition + ' in',
  6399                      debug      : settings.debug,
  6400                      verbose    : settings.verbose,
  6401                      duration   : settings.duration,
  6402                      queue      : true,
  6403                      onStart    : start,
  6404                      displayType: displayType,
  6405                      onComplete : function() {
  6406                        callback.call(element);
  6407                      }
  6408                    })
  6409                  ;
  6410                }
  6411                else {
  6412                  module.error(error.noTransition, transition);
  6413                }
  6414              }
  6415            },
  6416            hide: function(callback, $subMenu) {
  6417              var
  6418                $currentMenu = $subMenu || $menu,
  6419                start = ($subMenu)
  6420                  ? function() {}
  6421                  : function() {
  6422                    if( module.can.click() ) {
  6423                      module.unbind.intent();
  6424                    }
  6425                    module.remove.active();
  6426                  },
  6427                transition = module.get.transition($subMenu)
  6428              ;
  6429              callback = $.isFunction(callback)
  6430                ? callback
  6431                : function(){}
  6432              ;
  6433              if( module.is.visible($currentMenu) || module.is.animating($currentMenu) ) {
  6434                module.verbose('Doing menu hide animation', $currentMenu);
  6435  
  6436                if(transition == 'none') {
  6437                  start();
  6438                  $currentMenu.transition('hide');
  6439                  callback.call(element);
  6440                }
  6441                else if($.fn.transition !== undefined && $module.transition('is supported')) {
  6442                  $currentMenu
  6443                    .transition({
  6444                      animation  : transition + ' out',
  6445                      duration   : settings.duration,
  6446                      debug      : settings.debug,
  6447                      verbose    : settings.verbose,
  6448                      queue      : false,
  6449                      onStart    : start,
  6450                      onComplete : function() {
  6451                        callback.call(element);
  6452                      }
  6453                    })
  6454                  ;
  6455                }
  6456                else {
  6457                  module.error(error.transition);
  6458                }
  6459              }
  6460            }
  6461          },
  6462  
  6463          hideAndClear: function() {
  6464            module.remove.searchTerm();
  6465            if( module.has.maxSelections() ) {
  6466              return;
  6467            }
  6468            if(module.has.search()) {
  6469              module.hide(function() {
  6470                module.remove.filteredItem();
  6471              });
  6472            }
  6473            else {
  6474              module.hide();
  6475            }
  6476          },
  6477  
  6478          delay: {
  6479            show: function() {
  6480              module.verbose('Delaying show event to ensure user intent');
  6481              clearTimeout(module.timer);
  6482              module.timer = setTimeout(module.show, settings.delay.show);
  6483            },
  6484            hide: function() {
  6485              module.verbose('Delaying hide event to ensure user intent');
  6486              clearTimeout(module.timer);
  6487              module.timer = setTimeout(module.hide, settings.delay.hide);
  6488            }
  6489          },
  6490  
  6491          escape: {
  6492            value: function(value) {
  6493              var
  6494                multipleValues = Array.isArray(value),
  6495                stringValue    = (typeof value === 'string'),
  6496                isUnparsable   = (!stringValue && !multipleValues),
  6497                hasQuotes      = (stringValue && value.search(regExp.quote) !== -1),
  6498                values         = []
  6499              ;
  6500              if(isUnparsable || !hasQuotes) {
  6501                return value;
  6502              }
  6503              module.debug('Encoding quote values for use in select', value);
  6504              if(multipleValues) {
  6505                $.each(value, function(index, value){
  6506                  values.push(value.replace(regExp.quote, '&quot;'));
  6507                });
  6508                return values;
  6509              }
  6510              return value.replace(regExp.quote, '&quot;');
  6511            },
  6512            string: function(text) {
  6513              text =  String(text);
  6514              return text.replace(regExp.escape, '\\$&');
  6515            },
  6516            htmlEntities: function(string) {
  6517                var
  6518                    badChars     = /[<>"'`]/g,
  6519                    shouldEscape = /[&<>"'`]/,
  6520                    escape       = {
  6521                        "<": "&lt;",
  6522                        ">": "&gt;",
  6523                        '"': "&quot;",
  6524                        "'": "&#x27;",
  6525                        "`": "&#x60;"
  6526                    },
  6527                    escapedChar  = function(chr) {
  6528                        return escape[chr];
  6529                    }
  6530                ;
  6531                if(shouldEscape.test(string)) {
  6532                    string = string.replace(/&(?![a-z0-9#]{1,6};)/, "&amp;");
  6533                    return string.replace(badChars, escapedChar);
  6534                }
  6535                return string;
  6536            }
  6537          },
  6538  
  6539          setting: function(name, value) {
  6540            module.debug('Changing setting', name, value);
  6541            if( $.isPlainObject(name) ) {
  6542              $.extend(true, settings, name);
  6543            }
  6544            else if(value !== undefined) {
  6545              if($.isPlainObject(settings[name])) {
  6546                $.extend(true, settings[name], value);
  6547              }
  6548              else {
  6549                settings[name] = value;
  6550              }
  6551            }
  6552            else {
  6553              return settings[name];
  6554            }
  6555          },
  6556          internal: function(name, value) {
  6557            if( $.isPlainObject(name) ) {
  6558              $.extend(true, module, name);
  6559            }
  6560            else if(value !== undefined) {
  6561              module[name] = value;
  6562            }
  6563            else {
  6564              return module[name];
  6565            }
  6566          },
  6567          debug: function() {
  6568            if(!settings.silent && settings.debug) {
  6569              if(settings.performance) {
  6570                module.performance.log(arguments);
  6571              }
  6572              else {
  6573                module.debug = Function.prototype.bind.call(console.info, console, settings.name + ':');
  6574                module.debug.apply(console, arguments);
  6575              }
  6576            }
  6577          },
  6578          verbose: function() {
  6579            if(!settings.silent && settings.verbose && settings.debug) {
  6580              if(settings.performance) {
  6581                module.performance.log(arguments);
  6582              }
  6583              else {
  6584                module.verbose = Function.prototype.bind.call(console.info, console, settings.name + ':');
  6585                module.verbose.apply(console, arguments);
  6586              }
  6587            }
  6588          },
  6589          error: function() {
  6590            if(!settings.silent) {
  6591              module.error = Function.prototype.bind.call(console.error, console, settings.name + ':');
  6592              module.error.apply(console, arguments);
  6593            }
  6594          },
  6595          performance: {
  6596            log: function(message) {
  6597              var
  6598                currentTime,
  6599                executionTime,
  6600                previousTime
  6601              ;
  6602              if(settings.performance) {
  6603                currentTime   = new Date().getTime();
  6604                previousTime  = time || currentTime;
  6605                executionTime = currentTime - previousTime;
  6606                time          = currentTime;
  6607                performance.push({
  6608                  'Name'           : message[0],
  6609                  'Arguments'      : [].slice.call(message, 1) || '',
  6610                  'Element'        : element,
  6611                  'Execution Time' : executionTime
  6612                });
  6613              }
  6614              clearTimeout(module.performance.timer);
  6615              module.performance.timer = setTimeout(module.performance.display, 500);
  6616            },
  6617            display: function() {
  6618              var
  6619                title = settings.name + ':',
  6620                totalTime = 0
  6621              ;
  6622              time = false;
  6623              clearTimeout(module.performance.timer);
  6624              $.each(performance, function(index, data) {
  6625                totalTime += data['Execution Time'];
  6626              });
  6627              title += ' ' + totalTime + 'ms';
  6628              if(moduleSelector) {
  6629                title += ' \'' + moduleSelector + '\'';
  6630              }
  6631              if( (console.group !== undefined || console.table !== undefined) && performance.length > 0) {
  6632                console.groupCollapsed(title);
  6633                if(console.table) {
  6634                  console.table(performance);
  6635                }
  6636                else {
  6637                  $.each(performance, function(index, data) {
  6638                    console.log(data['Name'] + ': ' + data['Execution Time']+'ms');
  6639                  });
  6640                }
  6641                console.groupEnd();
  6642              }
  6643              performance = [];
  6644            }
  6645          },
  6646          invoke: function(query, passedArguments, context) {
  6647            var
  6648              object = instance,
  6649              maxDepth,
  6650              found,
  6651              response
  6652            ;
  6653            passedArguments = passedArguments || queryArguments;
  6654            context         = element         || context;
  6655            if(typeof query == 'string' && object !== undefined) {
  6656              query    = query.split(/[\. ]/);
  6657              maxDepth = query.length - 1;
  6658              $.each(query, function(depth, value) {
  6659                var camelCaseValue = (depth != maxDepth)
  6660                  ? value + query[depth + 1].charAt(0).toUpperCase() + query[depth + 1].slice(1)
  6661                  : query
  6662                ;
  6663                if( $.isPlainObject( object[camelCaseValue] ) && (depth != maxDepth) ) {
  6664                  object = object[camelCaseValue];
  6665                }
  6666                else if( object[camelCaseValue] !== undefined ) {
  6667                  found = object[camelCaseValue];
  6668                  return false;
  6669                }
  6670                else if( $.isPlainObject( object[value] ) && (depth != maxDepth) ) {
  6671                  object = object[value];
  6672                }
  6673                else if( object[value] !== undefined ) {
  6674                  found = object[value];
  6675                  return false;
  6676                }
  6677                else {
  6678                  module.error(error.method, query);
  6679                  return false;
  6680                }
  6681              });
  6682            }
  6683            if ( $.isFunction( found ) ) {
  6684              response = found.apply(context, passedArguments);
  6685            }
  6686            else if(found !== undefined) {
  6687              response = found;
  6688            }
  6689            if(Array.isArray(returnedValue)) {
  6690              returnedValue.push(response);
  6691            }
  6692            else if(returnedValue !== undefined) {
  6693              returnedValue = [returnedValue, response];
  6694            }
  6695            else if(response !== undefined) {
  6696              returnedValue = response;
  6697            }
  6698            return found;
  6699          }
  6700        };
  6701  
  6702        if(methodInvoked) {
  6703          if(instance === undefined) {
  6704            module.initialize();
  6705          }
  6706          module.invoke(query);
  6707        }
  6708        else {
  6709          if(instance !== undefined) {
  6710            instance.invoke('destroy');
  6711          }
  6712          module.initialize();
  6713        }
  6714      })
  6715    ;
  6716    return (returnedValue !== undefined)
  6717      ? returnedValue
  6718      : $allModules
  6719    ;
  6720  };
  6721  
  6722  $.fn.dropdown.settings = {
  6723  
  6724    silent                 : false,
  6725    debug                  : false,
  6726    verbose                : false,
  6727    performance            : true,
  6728  
  6729    on                     : 'click',    // what event should show menu action on item selection
  6730    action                 : 'activate', // action on item selection (nothing, activate, select, combo, hide, function(){})
  6731  
  6732    values                 : false,      // specify values to use for dropdown
  6733  
  6734    clearable              : false,      // whether the value of the dropdown can be cleared
  6735  
  6736    apiSettings            : false,
  6737    selectOnKeydown        : true,       // Whether selection should occur automatically when keyboard shortcuts used
  6738    minCharacters          : 0,          // Minimum characters required to trigger API call
  6739  
  6740    filterRemoteData       : false,      // Whether API results should be filtered after being returned for query term
  6741    saveRemoteData         : true,       // Whether remote name/value pairs should be stored in sessionStorage to allow remote data to be restored on page refresh
  6742  
  6743    throttle               : 200,        // How long to wait after last user input to search remotely
  6744  
  6745    context                : window,     // Context to use when determining if on screen
  6746    direction              : 'auto',     // Whether dropdown should always open in one direction
  6747    keepOnScreen           : true,       // Whether dropdown should check whether it is on screen before showing
  6748  
  6749    match                  : 'both',     // what to match against with search selection (both, text, or label)
  6750    fullTextSearch         : false,      // search anywhere in value (set to 'exact' to require exact matches)
  6751    ignoreDiacritics       : false,      // match results also if they contain diacritics of the same base character (for example searching for "a" will also match "á" or "â" or "à", etc...)
  6752    hideDividers           : false,      // Whether to hide any divider elements (specified in selector.divider) that are sibling to any items when searched (set to true will hide all dividers, set to 'empty' will hide them when they are not followed by a visible item)
  6753  
  6754    placeholder            : 'auto',     // whether to convert blank <select> values to placeholder text
  6755    preserveHTML           : true,       // preserve html when selecting value
  6756    sortSelect             : false,      // sort selection on init
  6757  
  6758    forceSelection         : true,       // force a choice on blur with search selection
  6759  
  6760    allowAdditions         : false,      // whether multiple select should allow user added values
  6761    ignoreCase             : false,      // whether to consider case sensitivity when creating labels
  6762    ignoreSearchCase       : true,       // whether to consider case sensitivity when filtering items
  6763    hideAdditions          : true,       // whether or not to hide special message prompting a user they can enter a value
  6764  
  6765    maxSelections          : false,      // When set to a number limits the number of selections to this count
  6766    useLabels              : true,       // whether multiple select should filter currently active selections from choices
  6767    delimiter              : ',',        // when multiselect uses normal <input> the values will be delimited with this character
  6768  
  6769    showOnFocus            : true,       // show menu on focus
  6770    allowReselection       : false,      // whether current value should trigger callbacks when reselected
  6771    allowTab               : true,       // add tabindex to element
  6772    allowCategorySelection : false,      // allow elements with sub-menus to be selected
  6773  
  6774    fireOnInit             : false,      // Whether callbacks should fire when initializing dropdown values
  6775  
  6776    transition             : 'auto',     // auto transition will slide down or up based on direction
  6777    duration               : 200,        // duration of transition
  6778  
  6779    glyphWidth             : 1.037,      // widest glyph width in em (W is 1.037 em) used to calculate multiselect input width
  6780  
  6781    headerDivider          : true,       // whether option headers should have an additional divider line underneath when converted from <select> <optgroup>
  6782  
  6783    // label settings on multi-select
  6784    label: {
  6785      transition : 'scale',
  6786      duration   : 200,
  6787      variation  : false
  6788    },
  6789  
  6790    // delay before event
  6791    delay : {
  6792      hide   : 300,
  6793      show   : 200,
  6794      search : 20,
  6795      touch  : 50
  6796    },
  6797  
  6798    /* Callbacks */
  6799    onChange      : function(value, text, $selected){},
  6800    onAdd         : function(value, text, $selected){},
  6801    onRemove      : function(value, text, $selected){},
  6802  
  6803    onLabelSelect : function($selectedLabels){},
  6804    onLabelCreate : function(value, text) { return $(this); },
  6805    onLabelRemove : function(value) { return true; },
  6806    onNoResults   : function(searchTerm) { return true; },
  6807    onShow        : function(){},
  6808    onHide        : function(){},
  6809  
  6810    /* Component */
  6811    name           : 'Dropdown',
  6812    namespace      : 'dropdown',
  6813  
  6814    message: {
  6815      addResult     : 'Add <b>{term}</b>',
  6816      count         : '{count} selected',
  6817      maxSelections : 'Max {maxCount} selections',
  6818      noResults     : 'No results found.',
  6819      serverError   : 'There was an error contacting the server'
  6820    },
  6821  
  6822    error : {
  6823      action          : 'You called a dropdown action that was not defined',
  6824      alreadySetup    : 'Once a select has been initialized behaviors must be called on the created ui dropdown',
  6825      labels          : 'Allowing user additions currently requires the use of labels.',
  6826      missingMultiple : '<select> requires multiple property to be set to correctly preserve multiple values',
  6827      method          : 'The method you called is not defined.',
  6828      noAPI           : 'The API module is required to load resources remotely',
  6829      noStorage       : 'Saving remote data requires session storage',
  6830      noTransition    : 'This module requires ui transitions <https://github.com/Semantic-Org/UI-Transition>',
  6831      noNormalize     : '"ignoreDiacritics" setting will be ignored. Browser does not support String().normalize(). You may consider including <https://cdn.jsdelivr.net/npm/unorm@1.4.1/lib/unorm.min.js> as a polyfill.'
  6832    },
  6833  
  6834    regExp : {
  6835      escape   : /[-[\]{}()*+?.,\\^$|#\s:=@]/g,
  6836      quote    : /"/g
  6837    },
  6838  
  6839    metadata : {
  6840      defaultText     : 'defaultText',
  6841      defaultValue    : 'defaultValue',
  6842      placeholderText : 'placeholder',
  6843      text            : 'text',
  6844      value           : 'value'
  6845    },
  6846  
  6847    // property names for remote query
  6848    fields: {
  6849      remoteValues : 'results',  // grouping for api results
  6850      values       : 'values',   // grouping for all dropdown values
  6851      disabled     : 'disabled', // whether value should be disabled
  6852      name         : 'name',     // displayed dropdown text
  6853      value        : 'value',    // actual dropdown value
  6854      text         : 'text',     // displayed text when selected
  6855      type         : 'type',     // type of dropdown element
  6856      image        : 'image',    // optional image path
  6857      imageClass   : 'imageClass', // optional individual class for image
  6858      icon         : 'icon',     // optional icon name
  6859      iconClass    : 'iconClass', // optional individual class for icon (for example to use flag instead)
  6860      class        : 'class',    // optional individual class for item/header
  6861      divider      : 'divider'   // optional divider append for group headers
  6862    },
  6863  
  6864    keys : {
  6865      backspace  : 8,
  6866      delimiter  : 188, // comma
  6867      deleteKey  : 46,
  6868      enter      : 13,
  6869      escape     : 27,
  6870      pageUp     : 33,
  6871      pageDown   : 34,
  6872      leftArrow  : 37,
  6873      upArrow    : 38,
  6874      rightArrow : 39,
  6875      downArrow  : 40
  6876    },
  6877  
  6878    selector : {
  6879      addition     : '.addition',
  6880      divider      : '.divider, .header',
  6881      dropdown     : '.ui.dropdown',
  6882      hidden       : '.hidden',
  6883      icon         : '> .dropdown.icon',
  6884      input        : '> input[type="hidden"], > select',
  6885      item         : '.item',
  6886      label        : '> .label',
  6887      remove       : '> .label > .delete.icon',
  6888      siblingLabel : '.label',
  6889      menu         : '.menu',
  6890      message      : '.message',
  6891      menuIcon     : '.dropdown.icon',
  6892      search       : 'input.search, .menu > .search > input, .menu input.search',
  6893      sizer        : '> span.sizer',
  6894      text         : '> .text:not(.icon)',
  6895      unselectable : '.disabled, .filtered',
  6896      clearIcon    : '> .remove.icon'
  6897    },
  6898  
  6899    className : {
  6900      active      : 'active',
  6901      addition    : 'addition',
  6902      animating   : 'animating',
  6903      disabled    : 'disabled',
  6904      empty       : 'empty',
  6905      dropdown    : 'ui dropdown',
  6906      filtered    : 'filtered',
  6907      hidden      : 'hidden transition',
  6908      icon        : 'icon',
  6909      image       : 'image',
  6910      item        : 'item',
  6911      label       : 'ui label',
  6912      loading     : 'loading',
  6913      menu        : 'menu',
  6914      message     : 'message',
  6915      multiple    : 'multiple',
  6916      placeholder : 'default',
  6917      sizer       : 'sizer',
  6918      search      : 'search',
  6919      selected    : 'selected',
  6920      selection   : 'selection',
  6921      upward      : 'upward',
  6922      leftward    : 'left',
  6923      visible     : 'visible',
  6924      clearable   : 'clearable',
  6925      noselection : 'noselection',
  6926      delete      : 'delete',
  6927      header      : 'header',
  6928      divider     : 'divider',
  6929      groupIcon   : '',
  6930      unfilterable : 'unfilterable'
  6931    }
  6932  
  6933  };
  6934  
  6935  /* Templates */
  6936  $.fn.dropdown.settings.templates = {
  6937    deQuote: function(string) {
  6938        return String(string).replace(/"/g,"");
  6939    },
  6940    escape: function(string, preserveHTML) {
  6941      if (preserveHTML){
  6942        return string;
  6943      }
  6944      var
  6945          badChars     = /[<>"'`]/g,
  6946          shouldEscape = /[&<>"'`]/,
  6947          escape       = {
  6948            "<": "&lt;",
  6949            ">": "&gt;",
  6950            '"': "&quot;",
  6951            "'": "&#x27;",
  6952            "`": "&#x60;"
  6953          },
  6954          escapedChar  = function(chr) {
  6955            return escape[chr];
  6956          }
  6957      ;
  6958      if(shouldEscape.test(string)) {
  6959        string = string.replace(/&(?![a-z0-9#]{1,6};)/, "&amp;");
  6960        return string.replace(badChars, escapedChar);
  6961      }
  6962      return string;
  6963    },
  6964    // generates dropdown from select values
  6965    dropdown: function(select, fields, preserveHTML, className) {
  6966      var
  6967        placeholder = select.placeholder || false,
  6968        html        = '',
  6969        escape = $.fn.dropdown.settings.templates.escape
  6970      ;
  6971      html +=  '<i class="dropdown icon"></i>';
  6972      if(placeholder) {
  6973        html += '<div class="default text">' + escape(placeholder,preserveHTML) + '</div>';
  6974      }
  6975      else {
  6976        html += '<div class="text"></div>';
  6977      }
  6978      html += '<div class="'+className.menu+'">';
  6979      html += $.fn.dropdown.settings.templates.menu(select, fields, preserveHTML,className);
  6980      html += '</div>';
  6981      return html;
  6982    },
  6983  
  6984    // generates just menu from select
  6985    menu: function(response, fields, preserveHTML, className) {
  6986      var
  6987        values = response[fields.values] || [],
  6988        html   = '',
  6989        escape = $.fn.dropdown.settings.templates.escape,
  6990        deQuote = $.fn.dropdown.settings.templates.deQuote
  6991      ;
  6992      $.each(values, function(index, option) {
  6993        var
  6994          itemType = (option[fields.type])
  6995            ? option[fields.type]
  6996            : 'item'
  6997        ;
  6998  
  6999        if( itemType === 'item' ) {
  7000          var
  7001            maybeText = (option[fields.text])
  7002              ? ' data-text="' + deQuote(option[fields.text]) + '"'
  7003              : '',
  7004            maybeDisabled = (option[fields.disabled])
  7005              ? className.disabled+' '
  7006              : ''
  7007          ;
  7008          html += '<div class="'+ maybeDisabled + (option[fields.class] ? deQuote(option[fields.class]) : className.item)+'" data-value="' + deQuote(option[fields.value]) + '"' + maybeText + '>';
  7009          if(option[fields.image]) {
  7010            html += '<img class="'+(option[fields.imageClass] ? deQuote(option[fields.imageClass]) : className.image)+'" src="' + deQuote(option[fields.image]) + '">';
  7011          }
  7012          if(option[fields.icon]) {
  7013            html += '<i class="'+deQuote(option[fields.icon])+' '+(option[fields.iconClass] ? deQuote(option[fields.iconClass]) : className.icon)+'"></i>';
  7014          }
  7015          html +=   escape(option[fields.name] || '', preserveHTML);
  7016          html += '</div>';
  7017        } else if (itemType === 'header') {
  7018          var groupName = escape(option[fields.name] || '', preserveHTML),
  7019              groupIcon = option[fields.icon] ? deQuote(option[fields.icon]) : className.groupIcon
  7020          ;
  7021          if(groupName !== '' || groupIcon !== '') {
  7022            html += '<div class="' + (option[fields.class] ? deQuote(option[fields.class]) : className.header) + '">';
  7023            if (groupIcon !== '') {
  7024              html += '<i class="' + groupIcon + ' ' + (option[fields.iconClass] ? deQuote(option[fields.iconClass]) : className.icon) + '"></i>';
  7025            }
  7026            html += groupName;
  7027            html += '</div>';
  7028          }
  7029          if(option[fields.divider]){
  7030            html += '<div class="'+className.divider+'"></div>';
  7031          }
  7032        }
  7033      });
  7034      return html;
  7035    },
  7036  
  7037    // generates label for multiselect
  7038    label: function(value, text, preserveHTML, className) {
  7039      var
  7040          escape = $.fn.dropdown.settings.templates.escape;
  7041      return escape(text,preserveHTML) + '<i class="'+className.delete+' icon"></i>';
  7042    },
  7043  
  7044  
  7045    // generates messages like "No results"
  7046    message: function(message) {
  7047      return message;
  7048    },
  7049  
  7050    // generates user addition to selection menu
  7051    addition: function(choice) {
  7052      return choice;
  7053    }
  7054  
  7055  };
  7056  
  7057  })( jQuery, window, document );
  7058  
  7059  /*!
  7060   * # Fomantic-UI - Form Validation
  7061   * http://github.com/fomantic/Fomantic-UI/
  7062   *
  7063   *
  7064   * Released under the MIT license
  7065   * http://opensource.org/licenses/MIT
  7066   *
  7067   */
  7068  
  7069  ;(function ($, window, document, undefined) {
  7070  
  7071  'use strict';
  7072  
  7073  $.isFunction = $.isFunction || function(obj) {
  7074    return typeof obj === "function" && typeof obj.nodeType !== "number";
  7075  };
  7076  
  7077  window = (typeof window != 'undefined' && window.Math == Math)
  7078    ? window
  7079    : (typeof self != 'undefined' && self.Math == Math)
  7080      ? self
  7081      : Function('return this')()
  7082  ;
  7083  
  7084  $.fn.form = function(parameters) {
  7085    var
  7086      $allModules      = $(this),
  7087      moduleSelector   = $allModules.selector || '',
  7088  
  7089      time             = new Date().getTime(),
  7090      performance      = [],
  7091  
  7092      query            = arguments[0],
  7093      legacyParameters = arguments[1],
  7094      methodInvoked    = (typeof query == 'string'),
  7095      queryArguments   = [].slice.call(arguments, 1),
  7096      returnedValue
  7097    ;
  7098    $allModules
  7099      .each(function() {
  7100        var
  7101          $module     = $(this),
  7102          element     = this,
  7103  
  7104          formErrors  = [],
  7105          keyHeldDown = false,
  7106  
  7107          // set at run-time
  7108          $field,
  7109          $group,
  7110          $message,
  7111          $prompt,
  7112          $submit,
  7113          $clear,
  7114          $reset,
  7115  
  7116          settings,
  7117          validation,
  7118  
  7119          metadata,
  7120          selector,
  7121          className,
  7122          regExp,
  7123          error,
  7124  
  7125          namespace,
  7126          moduleNamespace,
  7127          eventNamespace,
  7128  
  7129          submitting = false,
  7130          dirty = false,
  7131          history = ['clean', 'clean'],
  7132  
  7133          instance,
  7134          module
  7135        ;
  7136  
  7137        module      = {
  7138  
  7139          initialize: function() {
  7140  
  7141            // settings grabbed at run time
  7142            module.get.settings();
  7143            if(methodInvoked) {
  7144              if(instance === undefined) {
  7145                module.instantiate();
  7146              }
  7147              module.invoke(query);
  7148            }
  7149            else {
  7150              if(instance !== undefined) {
  7151                instance.invoke('destroy');
  7152              }
  7153              module.verbose('Initializing form validation', $module, settings);
  7154              module.bindEvents();
  7155              module.set.defaults();
  7156              if (settings.autoCheckRequired) {
  7157                module.set.autoCheck();
  7158              }
  7159              module.instantiate();
  7160            }
  7161          },
  7162  
  7163          instantiate: function() {
  7164            module.verbose('Storing instance of module', module);
  7165            instance = module;
  7166            $module
  7167              .data(moduleNamespace, module)
  7168            ;
  7169          },
  7170  
  7171          destroy: function() {
  7172            module.verbose('Destroying previous module', instance);
  7173            module.removeEvents();
  7174            $module
  7175              .removeData(moduleNamespace)
  7176            ;
  7177          },
  7178  
  7179          refresh: function() {
  7180            module.verbose('Refreshing selector cache');
  7181            $field      = $module.find(selector.field);
  7182            $group      = $module.find(selector.group);
  7183            $message    = $module.find(selector.message);
  7184            $prompt     = $module.find(selector.prompt);
  7185  
  7186            $submit     = $module.find(selector.submit);
  7187            $clear      = $module.find(selector.clear);
  7188            $reset      = $module.find(selector.reset);
  7189          },
  7190  
  7191          submit: function() {
  7192            module.verbose('Submitting form', $module);
  7193            submitting = true;
  7194            $module.submit();
  7195          },
  7196  
  7197          attachEvents: function(selector, action) {
  7198            action = action || 'submit';
  7199            $(selector).on('click' + eventNamespace, function(event) {
  7200              module[action]();
  7201              event.preventDefault();
  7202            });
  7203          },
  7204  
  7205          bindEvents: function() {
  7206            module.verbose('Attaching form events');
  7207            $module
  7208              .on('submit' + eventNamespace, module.validate.form)
  7209              .on('blur'   + eventNamespace, selector.field, module.event.field.blur)
  7210              .on('click'  + eventNamespace, selector.submit, module.submit)
  7211              .on('click'  + eventNamespace, selector.reset, module.reset)
  7212              .on('click'  + eventNamespace, selector.clear, module.clear)
  7213            ;
  7214            if(settings.keyboardShortcuts) {
  7215              $module.on('keydown' + eventNamespace, selector.field, module.event.field.keydown);
  7216            }
  7217            $field.each(function(index, el) {
  7218              var
  7219                $input     = $(el),
  7220                type       = $input.prop('type'),
  7221                inputEvent = module.get.changeEvent(type, $input)
  7222              ;
  7223              $input.on(inputEvent + eventNamespace, module.event.field.change);
  7224            });
  7225  
  7226            // Dirty events
  7227            if (settings.preventLeaving) {
  7228              $(window).on('beforeunload' + eventNamespace, module.event.beforeUnload);
  7229            }
  7230  
  7231            $field.on('change click keyup keydown blur', function(e) {
  7232              $(this).triggerHandler(e.type + ".dirty");
  7233            });
  7234  
  7235            $field.on('change.dirty click.dirty keyup.dirty keydown.dirty blur.dirty', module.determine.isDirty);
  7236  
  7237            $module.on('dirty' + eventNamespace, function(e) {
  7238              settings.onDirty.call();
  7239            });
  7240  
  7241            $module.on('clean' + eventNamespace, function(e) {
  7242              settings.onClean.call();
  7243            })
  7244          },
  7245  
  7246          clear: function() {
  7247            $field.each(function (index, el) {
  7248              var
  7249                $field       = $(el),
  7250                $element     = $field.parent(),
  7251                $fieldGroup  = $field.closest($group),
  7252                $prompt      = $fieldGroup.find(selector.prompt),
  7253                $calendar    = $field.closest(selector.uiCalendar),
  7254                defaultValue = $field.data(metadata.defaultValue) || '',
  7255                isCheckbox   = $element.is(selector.uiCheckbox),
  7256                isDropdown   = $element.is(selector.uiDropdown)  && module.can.useElement('dropdown'),
  7257                isCalendar   = ($calendar.length > 0  && module.can.useElement('calendar')),
  7258                isErrored    = $fieldGroup.hasClass(className.error)
  7259              ;
  7260              if(isErrored) {
  7261                module.verbose('Resetting error on field', $fieldGroup);
  7262                $fieldGroup.removeClass(className.error);
  7263                $prompt.remove();
  7264              }
  7265              if(isDropdown) {
  7266                module.verbose('Resetting dropdown value', $element, defaultValue);
  7267                $element.dropdown('clear', true);
  7268              }
  7269              else if(isCheckbox) {
  7270                $field.prop('checked', false);
  7271              }
  7272              else if (isCalendar) {
  7273                $calendar.calendar('clear');
  7274              }
  7275              else {
  7276                module.verbose('Resetting field value', $field, defaultValue);
  7277                $field.val('');
  7278              }
  7279            });
  7280            module.remove.states();
  7281          },
  7282  
  7283          reset: function() {
  7284            $field.each(function (index, el) {
  7285              var
  7286                $field       = $(el),
  7287                $element     = $field.parent(),
  7288                $fieldGroup  = $field.closest($group),
  7289                $calendar    = $field.closest(selector.uiCalendar),
  7290                $prompt      = $fieldGroup.find(selector.prompt),
  7291                defaultValue = $field.data(metadata.defaultValue),
  7292                isCheckbox   = $element.is(selector.uiCheckbox),
  7293                isDropdown   = $element.is(selector.uiDropdown)  && module.can.useElement('dropdown'),
  7294                isCalendar   = ($calendar.length > 0  && module.can.useElement('calendar')),
  7295                isErrored    = $fieldGroup.hasClass(className.error)
  7296              ;
  7297              if(defaultValue === undefined) {
  7298                return;
  7299              }
  7300              if(isErrored) {
  7301                module.verbose('Resetting error on field', $fieldGroup);
  7302                $fieldGroup.removeClass(className.error);
  7303                $prompt.remove();
  7304              }
  7305              if(isDropdown) {
  7306                module.verbose('Resetting dropdown value', $element, defaultValue);
  7307                $element.dropdown('restore defaults', true);
  7308              }
  7309              else if(isCheckbox) {
  7310                module.verbose('Resetting checkbox value', $element, defaultValue);
  7311                $field.prop('checked', defaultValue);
  7312              }
  7313              else if (isCalendar) {
  7314                $calendar.calendar('set date', defaultValue);
  7315              }
  7316              else {
  7317                module.verbose('Resetting field value', $field, defaultValue);
  7318                $field.val(defaultValue);
  7319              }
  7320            });
  7321            module.remove.states();
  7322          },
  7323  
  7324          determine: {
  7325            isValid: function() {
  7326              var
  7327                allValid = true
  7328              ;
  7329              $.each(validation, function(fieldName, field) {
  7330                if( !( module.validate.field(field, fieldName, true) ) ) {
  7331                  allValid = false;
  7332                }
  7333              });
  7334              return allValid;
  7335            },
  7336            isDirty: function(e) {
  7337              var formIsDirty = false;
  7338  
  7339              $field.each(function(index, el) {
  7340                var
  7341                  $el = $(el),
  7342                  isCheckbox = ($el.filter(selector.checkbox).length > 0),
  7343                  isDirty
  7344                ;
  7345  
  7346                if (isCheckbox) {
  7347                  isDirty = module.is.checkboxDirty($el);
  7348                } else {
  7349                  isDirty = module.is.fieldDirty($el);
  7350                }
  7351  
  7352                $el.data(settings.metadata.isDirty, isDirty);
  7353  
  7354                formIsDirty |= isDirty;
  7355              });
  7356  
  7357              if (formIsDirty) {
  7358                module.set.dirty();
  7359              } else {
  7360                module.set.clean();
  7361              }
  7362  
  7363              if (e && e.namespace === 'dirty') {
  7364                e.stopImmediatePropagation();
  7365                e.preventDefault();
  7366              }
  7367            }
  7368          },
  7369  
  7370          is: {
  7371            bracketedRule: function(rule) {
  7372              return (rule.type && rule.type.match(settings.regExp.bracket));
  7373            },
  7374            shorthandFields: function(fields) {
  7375              var
  7376                fieldKeys = Object.keys(fields),
  7377                firstRule = fields[fieldKeys[0]]
  7378              ;
  7379              return module.is.shorthandRules(firstRule);
  7380            },
  7381            // duck type rule test
  7382            shorthandRules: function(rules) {
  7383              return (typeof rules == 'string' || Array.isArray(rules));
  7384            },
  7385            empty: function($field) {
  7386              if(!$field || $field.length === 0) {
  7387                return true;
  7388              }
  7389              else if($field.is(selector.checkbox)) {
  7390                return !$field.is(':checked');
  7391              }
  7392              else {
  7393                return module.is.blank($field);
  7394              }
  7395            },
  7396            blank: function($field) {
  7397              return String($field.val()).trim() === '';
  7398            },
  7399            valid: function(field, showErrors) {
  7400              var
  7401                allValid = true
  7402              ;
  7403              if(field) {
  7404                module.verbose('Checking if field is valid', field);
  7405                return module.validate.field(validation[field], field, !!showErrors);
  7406              }
  7407              else {
  7408                module.verbose('Checking if form is valid');
  7409                $.each(validation, function(fieldName, field) {
  7410                  if( !module.is.valid(fieldName, showErrors) ) {
  7411                    allValid = false;
  7412                  }
  7413                });
  7414                return allValid;
  7415              }
  7416            },
  7417            dirty: function() {
  7418              return dirty;
  7419            },
  7420            clean: function() {
  7421              return !dirty;
  7422            },
  7423            fieldDirty: function($el) {
  7424              var initialValue = $el.data(metadata.defaultValue);
  7425              // Explicitly check for null/undefined here as value may be `false`, so ($el.data(dataInitialValue) || '') would not work
  7426              if (initialValue == null) { initialValue = ''; }
  7427              else if(Array.isArray(initialValue)) {
  7428                initialValue = initialValue.toString();
  7429              }
  7430              var currentValue = $el.val();
  7431              if (currentValue == null) { currentValue = ''; }
  7432              // multiple select values are returned as arrays which are never equal, so do string conversion first
  7433              else if(Array.isArray(currentValue)) {
  7434                currentValue = currentValue.toString();
  7435              }
  7436              // Boolean values can be encoded as "true/false" or "True/False" depending on underlying frameworks so we need a case insensitive comparison
  7437              var boolRegex = /^(true|false)$/i;
  7438              var isBoolValue = boolRegex.test(initialValue) && boolRegex.test(currentValue);
  7439              if (isBoolValue) {
  7440                var regex = new RegExp("^" + initialValue + "$", "i");
  7441                return !regex.test(currentValue);
  7442              }
  7443  
  7444              return currentValue !== initialValue;
  7445            },
  7446            checkboxDirty: function($el) {
  7447              var initialValue = $el.data(metadata.defaultValue);
  7448              var currentValue = $el.is(":checked");
  7449  
  7450              return initialValue !== currentValue;
  7451            },
  7452            justDirty: function() {
  7453              return (history[0] === 'dirty');
  7454            },
  7455            justClean: function() {
  7456              return (history[0] === 'clean');
  7457            }
  7458          },
  7459  
  7460          removeEvents: function() {
  7461            $module.off(eventNamespace);
  7462            $field.off(eventNamespace);
  7463            $submit.off(eventNamespace);
  7464            $field.off(eventNamespace);
  7465          },
  7466  
  7467          event: {
  7468            field: {
  7469              keydown: function(event) {
  7470                var
  7471                  $field       = $(this),
  7472                  key          = event.which,
  7473                  isInput      = $field.is(selector.input),
  7474                  isCheckbox   = $field.is(selector.checkbox),
  7475                  isInDropdown = ($field.closest(selector.uiDropdown).length > 0),
  7476                  keyCode      = {
  7477                    enter  : 13,
  7478                    escape : 27
  7479                  }
  7480                ;
  7481                if( key == keyCode.escape) {
  7482                  module.verbose('Escape key pressed blurring field');
  7483                  $field
  7484                    .blur()
  7485                  ;
  7486                }
  7487                if(!event.ctrlKey && key == keyCode.enter && isInput && !isInDropdown && !isCheckbox) {
  7488                  if(!keyHeldDown) {
  7489                    $field.one('keyup' + eventNamespace, module.event.field.keyup);
  7490                    module.submit();
  7491                    module.debug('Enter pressed on input submitting form');
  7492                  }
  7493                  keyHeldDown = true;
  7494                }
  7495              },
  7496              keyup: function() {
  7497                keyHeldDown = false;
  7498              },
  7499              blur: function(event) {
  7500                var
  7501                  $field          = $(this),
  7502                  $fieldGroup     = $field.closest($group),
  7503                  validationRules = module.get.validation($field)
  7504                ;
  7505                if( $fieldGroup.hasClass(className.error) ) {
  7506                  module.debug('Revalidating field', $field, validationRules);
  7507                  if(validationRules) {
  7508                    module.validate.field( validationRules );
  7509                  }
  7510                }
  7511                else if(settings.on == 'blur') {
  7512                  if(validationRules) {
  7513                    module.validate.field( validationRules );
  7514                  }
  7515                }
  7516              },
  7517              change: function(event) {
  7518                var
  7519                  $field      = $(this),
  7520                  $fieldGroup = $field.closest($group),
  7521                  validationRules = module.get.validation($field)
  7522                ;
  7523                if(validationRules && (settings.on == 'change' || ( $fieldGroup.hasClass(className.error) && settings.revalidate) )) {
  7524                  clearTimeout(module.timer);
  7525                  module.timer = setTimeout(function() {
  7526                    module.debug('Revalidating field', $field,  module.get.validation($field));
  7527                    module.validate.field( validationRules );
  7528                    if(!settings.inline) {
  7529                      module.validate.form(false,true);
  7530                    }
  7531                  }, settings.delay);
  7532                }
  7533              }
  7534            },
  7535            beforeUnload: function(event) {
  7536              if (module.is.dirty() && !submitting) {
  7537                var event = event || window.event;
  7538  
  7539                // For modern browsers
  7540                if (event) {
  7541                  event.returnValue = settings.text.leavingMessage;
  7542                }
  7543  
  7544                // For olders...
  7545                return settings.text.leavingMessage;
  7546              }
  7547            }
  7548  
  7549          },
  7550  
  7551          get: {
  7552            ancillaryValue: function(rule) {
  7553              if(!rule.type || (!rule.value && !module.is.bracketedRule(rule))) {
  7554                return false;
  7555              }
  7556              return (rule.value !== undefined)
  7557                ? rule.value
  7558                : rule.type.match(settings.regExp.bracket)[1] + ''
  7559              ;
  7560            },
  7561            ruleName: function(rule) {
  7562              if( module.is.bracketedRule(rule) ) {
  7563                return rule.type.replace(rule.type.match(settings.regExp.bracket)[0], '');
  7564              }
  7565              return rule.type;
  7566            },
  7567            changeEvent: function(type, $input) {
  7568              if(type == 'checkbox' || type == 'radio' || type == 'hidden' || $input.is('select')) {
  7569                return 'change';
  7570              }
  7571              else {
  7572                return module.get.inputEvent();
  7573              }
  7574            },
  7575            inputEvent: function() {
  7576              return (document.createElement('input').oninput !== undefined)
  7577                ? 'input'
  7578                : (document.createElement('input').onpropertychange !== undefined)
  7579                  ? 'propertychange'
  7580                  : 'keyup'
  7581              ;
  7582            },
  7583            fieldsFromShorthand: function(fields) {
  7584              var
  7585                fullFields = {}
  7586              ;
  7587              $.each(fields, function(name, rules) {
  7588                if(typeof rules == 'string') {
  7589                  rules = [rules];
  7590                }
  7591                fullFields[name] = {
  7592                  rules: []
  7593                };
  7594                $.each(rules, function(index, rule) {
  7595                  fullFields[name].rules.push({ type: rule });
  7596                });
  7597              });
  7598              return fullFields;
  7599            },
  7600            prompt: function(rule, field) {
  7601              var
  7602                ruleName      = module.get.ruleName(rule),
  7603                ancillary     = module.get.ancillaryValue(rule),
  7604                $field        = module.get.field(field.identifier),
  7605                value         = $field.val(),
  7606                prompt        = $.isFunction(rule.prompt)
  7607                  ? rule.prompt(value)
  7608                  : rule.prompt || settings.prompt[ruleName] || settings.text.unspecifiedRule,
  7609                requiresValue = (prompt.search('{value}') !== -1),
  7610                requiresName  = (prompt.search('{name}') !== -1),
  7611                $label,
  7612                name
  7613              ;
  7614              if(requiresValue) {
  7615                prompt = prompt.replace(/\{value\}/g, $field.val());
  7616              }
  7617              if(requiresName) {
  7618                $label = $field.closest(selector.group).find('label').eq(0);
  7619                name = ($label.length == 1)
  7620                  ? $label.text()
  7621                  : $field.prop('placeholder') || settings.text.unspecifiedField
  7622                ;
  7623                prompt = prompt.replace(/\{name\}/g, name);
  7624              }
  7625              prompt = prompt.replace(/\{identifier\}/g, field.identifier);
  7626              prompt = prompt.replace(/\{ruleValue\}/g, ancillary);
  7627              if(!rule.prompt) {
  7628                module.verbose('Using default validation prompt for type', prompt, ruleName);
  7629              }
  7630              return prompt;
  7631            },
  7632            settings: function() {
  7633              if($.isPlainObject(parameters)) {
  7634                var
  7635                  keys     = Object.keys(parameters),
  7636                  isLegacySettings = (keys.length > 0)
  7637                    ? (parameters[keys[0]].identifier !== undefined && parameters[keys[0]].rules !== undefined)
  7638                    : false
  7639                ;
  7640                if(isLegacySettings) {
  7641                  // 1.x (ducktyped)
  7642                  settings   = $.extend(true, {}, $.fn.form.settings, legacyParameters);
  7643                  validation = $.extend({}, $.fn.form.settings.defaults, parameters);
  7644                  module.error(settings.error.oldSyntax, element);
  7645                  module.verbose('Extending settings from legacy parameters', validation, settings);
  7646                }
  7647                else {
  7648                  // 2.x
  7649                  if(parameters.fields && module.is.shorthandFields(parameters.fields)) {
  7650                    parameters.fields = module.get.fieldsFromShorthand(parameters.fields);
  7651                  }
  7652                  settings   = $.extend(true, {}, $.fn.form.settings, parameters);
  7653                  validation = $.extend({}, $.fn.form.settings.defaults, settings.fields);
  7654                  module.verbose('Extending settings', validation, settings);
  7655                }
  7656              }
  7657              else {
  7658                settings   = $.fn.form.settings;
  7659                validation = $.fn.form.settings.defaults;
  7660                module.verbose('Using default form validation', validation, settings);
  7661              }
  7662  
  7663              // shorthand
  7664              namespace       = settings.namespace;
  7665              metadata        = settings.metadata;
  7666              selector        = settings.selector;
  7667              className       = settings.className;
  7668              regExp          = settings.regExp;
  7669              error           = settings.error;
  7670              moduleNamespace = 'module-' + namespace;
  7671              eventNamespace  = '.' + namespace;
  7672  
  7673              // grab instance
  7674              instance = $module.data(moduleNamespace);
  7675  
  7676              // refresh selector cache
  7677              module.refresh();
  7678            },
  7679            field: function(identifier) {
  7680              module.verbose('Finding field with identifier', identifier);
  7681              identifier = module.escape.string(identifier);
  7682              var t;
  7683              if((t=$field.filter('#' + identifier)).length > 0 ) {
  7684                return t;
  7685              }
  7686              if((t=$field.filter('[name="' + identifier +'"]')).length > 0 ) {
  7687                return t;
  7688              }
  7689              if((t=$field.filter('[name="' + identifier +'[]"]')).length > 0 ) {
  7690                return t;
  7691              }
  7692              if((t=$field.filter('[data-' + metadata.validate + '="'+ identifier +'"]')).length > 0 ) {
  7693                return t;
  7694              }
  7695              return $('<input/>');
  7696            },
  7697            fields: function(fields) {
  7698              var
  7699                $fields = $()
  7700              ;
  7701              $.each(fields, function(index, name) {
  7702                $fields = $fields.add( module.get.field(name) );
  7703              });
  7704              return $fields;
  7705            },
  7706            validation: function($field) {
  7707              var
  7708                fieldValidation,
  7709                identifier
  7710              ;
  7711              if(!validation) {
  7712                return false;
  7713              }
  7714              $.each(validation, function(fieldName, field) {
  7715                identifier = field.identifier || fieldName;
  7716                $.each(module.get.field(identifier), function(index, groupField) {
  7717                  if(groupField == $field[0]) {
  7718                    field.identifier = identifier;
  7719                    fieldValidation = field;
  7720                    return false;
  7721                  }
  7722                });
  7723              });
  7724              return fieldValidation || false;
  7725            },
  7726            value: function (field) {
  7727              var
  7728                fields = [],
  7729                results
  7730              ;
  7731              fields.push(field);
  7732              results = module.get.values.call(element, fields);
  7733              return results[field];
  7734            },
  7735            values: function (fields) {
  7736              var
  7737                $fields = Array.isArray(fields)
  7738                  ? module.get.fields(fields)
  7739                  : $field,
  7740                values = {}
  7741              ;
  7742              $fields.each(function(index, field) {
  7743                var
  7744                  $field       = $(field),
  7745                  $calendar    = $field.closest(selector.uiCalendar),
  7746                  name         = $field.prop('name'),
  7747                  value        = $field.val(),
  7748                  isCheckbox   = $field.is(selector.checkbox),
  7749                  isRadio      = $field.is(selector.radio),
  7750                  isMultiple   = (name.indexOf('[]') !== -1),
  7751                  isCalendar   = ($calendar.length > 0  && module.can.useElement('calendar')),
  7752                  isChecked    = (isCheckbox)
  7753                    ? $field.is(':checked')
  7754                    : false
  7755                ;
  7756                if(name) {
  7757                  if(isMultiple) {
  7758                    name = name.replace('[]', '');
  7759                    if(!values[name]) {
  7760                      values[name] = [];
  7761                    }
  7762                    if(isCheckbox) {
  7763                      if(isChecked) {
  7764                        values[name].push(value || true);
  7765                      }
  7766                      else {
  7767                        values[name].push(false);
  7768                      }
  7769                    }
  7770                    else {
  7771                      values[name].push(value);
  7772                    }
  7773                  }
  7774                  else {
  7775                    if(isRadio) {
  7776                      if(values[name] === undefined || values[name] === false) {
  7777                        values[name] = (isChecked)
  7778                          ? value || true
  7779                          : false
  7780                        ;
  7781                      }
  7782                    }
  7783                    else if(isCheckbox) {
  7784                      if(isChecked) {
  7785                        values[name] = value || true;
  7786                      }
  7787                      else {
  7788                        values[name] = false;
  7789                      }
  7790                    }
  7791                    else if(isCalendar) {
  7792                      var date = $calendar.calendar('get date');
  7793  
  7794                      if (date !== null) {
  7795                        if (settings.dateHandling == 'date') {
  7796                          values[name] = date;
  7797                        } else if(settings.dateHandling == 'input') {
  7798                          values[name] = $calendar.calendar('get input date')
  7799                        } else if (settings.dateHandling == 'formatter') {
  7800                          var type = $calendar.calendar('setting', 'type');
  7801  
  7802                          switch(type) {
  7803                            case 'date':
  7804                            values[name] = settings.formatter.date(date);
  7805                            break;
  7806  
  7807                            case 'datetime':
  7808                            values[name] = settings.formatter.datetime(date);
  7809                            break;
  7810  
  7811                            case 'time':
  7812                            values[name] = settings.formatter.time(date);
  7813                            break;
  7814  
  7815                            case 'month':
  7816                            values[name] = settings.formatter.month(date);
  7817                            break;
  7818  
  7819                            case 'year':
  7820                            values[name] = settings.formatter.year(date);
  7821                            break;
  7822  
  7823                            default:
  7824                            module.debug('Wrong calendar mode', $calendar, type);
  7825                            values[name] = '';
  7826                          }
  7827                        }
  7828                      } else {
  7829                        values[name] = '';
  7830                      }
  7831                    } else {
  7832                      values[name] = value;
  7833                    }
  7834                  }
  7835                }
  7836              });
  7837              return values;
  7838            },
  7839            dirtyFields: function() {
  7840              return $field.filter(function(index, e) {
  7841                return $(e).data(metadata.isDirty);
  7842              });
  7843            }
  7844          },
  7845  
  7846          has: {
  7847  
  7848            field: function(identifier) {
  7849              module.verbose('Checking for existence of a field with identifier', identifier);
  7850              identifier = module.escape.string(identifier);
  7851              if(typeof identifier !== 'string') {
  7852                module.error(error.identifier, identifier);
  7853              }
  7854              if($field.filter('#' + identifier).length > 0 ) {
  7855                return true;
  7856              }
  7857              else if( $field.filter('[name="' + identifier +'"]').length > 0 ) {
  7858                return true;
  7859              }
  7860              else if( $field.filter('[data-' + metadata.validate + '="'+ identifier +'"]').length > 0 ) {
  7861                return true;
  7862              }
  7863              return false;
  7864            }
  7865  
  7866          },
  7867  
  7868          can: {
  7869              useElement: function(element){
  7870                 if ($.fn[element] !== undefined) {
  7871                     return true;
  7872                 }
  7873                 module.error(error.noElement.replace('{element}',element));
  7874                 return false;
  7875              }
  7876          },
  7877  
  7878          escape: {
  7879            string: function(text) {
  7880              text =  String(text);
  7881              return text.replace(regExp.escape, '\\$&');
  7882            }
  7883          },
  7884  
  7885          add: {
  7886            // alias
  7887            rule: function(name, rules) {
  7888              module.add.field(name, rules);
  7889            },
  7890            field: function(name, rules) {
  7891              // Validation should have at least a standard format
  7892              if(validation[name] === undefined || validation[name].rules === undefined) {
  7893                validation[name] = {
  7894                  rules: []
  7895                };
  7896              }
  7897              var
  7898                newValidation = {
  7899                  rules: []
  7900                }
  7901              ;
  7902              if(module.is.shorthandRules(rules)) {
  7903                rules = Array.isArray(rules)
  7904                  ? rules
  7905                  : [rules]
  7906                ;
  7907                $.each(rules, function(_index, rule) {
  7908                  newValidation.rules.push({ type: rule });
  7909                });
  7910              }
  7911              else {
  7912                newValidation.rules = rules.rules;
  7913              }
  7914              // For each new rule, check if there's not already one with the same type
  7915              $.each(newValidation.rules, function (_index, rule) {
  7916                if ($.grep(validation[name].rules, function(item){ return item.type == rule.type; }).length == 0) {
  7917                  validation[name].rules.push(rule);
  7918                }
  7919              });
  7920              module.debug('Adding rules', newValidation.rules, validation);
  7921            },
  7922            fields: function(fields) {
  7923              var
  7924                newValidation
  7925              ;
  7926              if(fields && module.is.shorthandFields(fields)) {
  7927                newValidation = module.get.fieldsFromShorthand(fields);
  7928              }
  7929              else {
  7930                newValidation = fields;
  7931              }
  7932              validation = $.extend({}, validation, newValidation);
  7933            },
  7934            prompt: function(identifier, errors, internal) {
  7935              var
  7936                $field       = module.get.field(identifier),
  7937                $fieldGroup  = $field.closest($group),
  7938                $prompt      = $fieldGroup.children(selector.prompt),
  7939                promptExists = ($prompt.length !== 0)
  7940              ;
  7941              errors = (typeof errors == 'string')
  7942                ? [errors]
  7943                : errors
  7944              ;
  7945              module.verbose('Adding field error state', identifier);
  7946              if(!internal) {
  7947                $fieldGroup
  7948                    .addClass(className.error)
  7949                ;
  7950              }
  7951              if(settings.inline) {
  7952                if(!promptExists) {
  7953                  $prompt = settings.templates.prompt(errors, className.label);
  7954                  $prompt
  7955                    .appendTo($fieldGroup)
  7956                  ;
  7957                }
  7958                $prompt
  7959                  .html(errors[0])
  7960                ;
  7961                if(!promptExists) {
  7962                  if(settings.transition && module.can.useElement('transition') && $module.transition('is supported')) {
  7963                    module.verbose('Displaying error with css transition', settings.transition);
  7964                    $prompt.transition(settings.transition + ' in', settings.duration);
  7965                  }
  7966                  else {
  7967                    module.verbose('Displaying error with fallback javascript animation');
  7968                    $prompt
  7969                      .fadeIn(settings.duration)
  7970                    ;
  7971                  }
  7972                }
  7973                else {
  7974                  module.verbose('Inline errors are disabled, no inline error added', identifier);
  7975                }
  7976              }
  7977            },
  7978            errors: function(errors) {
  7979              module.debug('Adding form error messages', errors);
  7980              module.set.error();
  7981              $message
  7982                .html( settings.templates.error(errors) )
  7983              ;
  7984            }
  7985          },
  7986  
  7987          remove: {
  7988            errors: function() {
  7989              module.debug('Removing form error messages');
  7990              $message.empty();
  7991            },
  7992            states: function() {
  7993              $module.removeClass(className.error).removeClass(className.success);
  7994              if(!settings.inline) {
  7995                module.remove.errors();
  7996              }
  7997              module.determine.isDirty();
  7998            },
  7999            rule: function(field, rule) {
  8000              var
  8001                rules = Array.isArray(rule)
  8002                  ? rule
  8003                  : [rule]
  8004              ;
  8005              if(validation[field] === undefined || !Array.isArray(validation[field].rules)) {
  8006                return;
  8007              }
  8008              if(rule === undefined) {
  8009                module.debug('Removed all rules');
  8010                validation[field].rules = [];
  8011                return;
  8012              }
  8013              $.each(validation[field].rules, function(index, rule) {
  8014                if(rule && rules.indexOf(rule.type) !== -1) {
  8015                  module.debug('Removed rule', rule.type);
  8016                  validation[field].rules.splice(index, 1);
  8017                }
  8018              });
  8019            },
  8020            field: function(field) {
  8021              var
  8022                fields = Array.isArray(field)
  8023                  ? field
  8024                  : [field]
  8025              ;
  8026              $.each(fields, function(index, field) {
  8027                module.remove.rule(field);
  8028              });
  8029            },
  8030            // alias
  8031            rules: function(field, rules) {
  8032              if(Array.isArray(field)) {
  8033                $.each(field, function(index, field) {
  8034                  module.remove.rule(field, rules);
  8035                });
  8036              }
  8037              else {
  8038                module.remove.rule(field, rules);
  8039              }
  8040            },
  8041            fields: function(fields) {
  8042              module.remove.field(fields);
  8043            },
  8044            prompt: function(identifier) {
  8045              var
  8046                $field      = module.get.field(identifier),
  8047                $fieldGroup = $field.closest($group),
  8048                $prompt     = $fieldGroup.children(selector.prompt)
  8049              ;
  8050              $fieldGroup
  8051                .removeClass(className.error)
  8052              ;
  8053              if(settings.inline && $prompt.is(':visible')) {
  8054                module.verbose('Removing prompt for field', identifier);
  8055                if(settings.transition  && module.can.useElement('transition') && $module.transition('is supported')) {
  8056                  $prompt.transition(settings.transition + ' out', settings.duration, function() {
  8057                    $prompt.remove();
  8058                  });
  8059                }
  8060                else {
  8061                  $prompt
  8062                    .fadeOut(settings.duration, function(){
  8063                      $prompt.remove();
  8064                    })
  8065                  ;
  8066                }
  8067              }
  8068            }
  8069          },
  8070  
  8071          set: {
  8072            success: function() {
  8073              $module
  8074                .removeClass(className.error)
  8075                .addClass(className.success)
  8076              ;
  8077            },
  8078            defaults: function () {
  8079              $field.each(function (index, el) {
  8080                var
  8081                  $el        = $(el),
  8082                  $parent    = $el.parent(),
  8083                  isCheckbox = ($el.filter(selector.checkbox).length > 0),
  8084                  isDropdown = $parent.is(selector.uiDropdown) && module.can.useElement('dropdown'),
  8085                  $calendar   = $el.closest(selector.uiCalendar),
  8086                  isCalendar  = ($calendar.length > 0  && module.can.useElement('calendar')),
  8087                  value      = (isCheckbox)
  8088                    ? $el.is(':checked')
  8089                    : $el.val()
  8090                ;
  8091                if (isDropdown) {
  8092                  $parent.dropdown('save defaults');
  8093                }
  8094                else if (isCalendar) {
  8095                  $calendar.calendar('refresh');
  8096                }
  8097                $el.data(metadata.defaultValue, value);
  8098                $el.data(metadata.isDirty, false);
  8099              });
  8100            },
  8101            error: function() {
  8102              $module
  8103                .removeClass(className.success)
  8104                .addClass(className.error)
  8105              ;
  8106            },
  8107            value: function (field, value) {
  8108              var
  8109                fields = {}
  8110              ;
  8111              fields[field] = value;
  8112              return module.set.values.call(element, fields);
  8113            },
  8114            values: function (fields) {
  8115              if($.isEmptyObject(fields)) {
  8116                return;
  8117              }
  8118              $.each(fields, function(key, value) {
  8119                var
  8120                  $field      = module.get.field(key),
  8121                  $element    = $field.parent(),
  8122                  $calendar   = $field.closest(selector.uiCalendar),
  8123                  isMultiple  = Array.isArray(value),
  8124                  isCheckbox  = $element.is(selector.uiCheckbox)  && module.can.useElement('checkbox'),
  8125                  isDropdown  = $element.is(selector.uiDropdown) && module.can.useElement('dropdown'),
  8126                  isRadio     = ($field.is(selector.radio) && isCheckbox),
  8127                  isCalendar  = ($calendar.length > 0  && module.can.useElement('calendar')),
  8128                  fieldExists = ($field.length > 0),
  8129                  $multipleField
  8130                ;
  8131                if(fieldExists) {
  8132                  if(isMultiple && isCheckbox) {
  8133                    module.verbose('Selecting multiple', value, $field);
  8134                    $element.checkbox('uncheck');
  8135                    $.each(value, function(index, value) {
  8136                      $multipleField = $field.filter('[value="' + value + '"]');
  8137                      $element       = $multipleField.parent();
  8138                      if($multipleField.length > 0) {
  8139                        $element.checkbox('check');
  8140                      }
  8141                    });
  8142                  }
  8143                  else if(isRadio) {
  8144                    module.verbose('Selecting radio value', value, $field);
  8145                    $field.filter('[value="' + value + '"]')
  8146                      .parent(selector.uiCheckbox)
  8147                        .checkbox('check')
  8148                    ;
  8149                  }
  8150                  else if(isCheckbox) {
  8151                    module.verbose('Setting checkbox value', value, $element);
  8152                    if(value === true || value === 1) {
  8153                      $element.checkbox('check');
  8154                    }
  8155                    else {
  8156                      $element.checkbox('uncheck');
  8157                    }
  8158                  }
  8159                  else if(isDropdown) {
  8160                    module.verbose('Setting dropdown value', value, $element);
  8161                    $element.dropdown('set selected', value);
  8162                  }
  8163                  else if (isCalendar) {
  8164                    $calendar.calendar('set date',value);
  8165                  }
  8166                  else {
  8167                    module.verbose('Setting field value', value, $field);
  8168                    $field.val(value);
  8169                  }
  8170                }
  8171              });
  8172            },
  8173            dirty: function() {
  8174              module.verbose('Setting state dirty');
  8175              dirty = true;
  8176              history[0] = history[1];
  8177              history[1] = 'dirty';
  8178  
  8179              if (module.is.justClean()) {
  8180                $module.trigger('dirty');
  8181              }
  8182            },
  8183            clean: function() {
  8184              module.verbose('Setting state clean');
  8185              dirty = false;
  8186              history[0] = history[1];
  8187              history[1] = 'clean';
  8188  
  8189              if (module.is.justDirty()) {
  8190                $module.trigger('clean');
  8191              }
  8192            },
  8193            asClean: function() {
  8194              module.set.defaults();
  8195              module.set.clean();
  8196            },
  8197            asDirty: function() {
  8198              module.set.defaults();
  8199              module.set.dirty();
  8200            },
  8201            autoCheck: function() {
  8202              module.debug('Enabling auto check on required fields');
  8203              $field.each(function (_index, el) {
  8204                var
  8205                  $el        = $(el),
  8206                  $elGroup   = $(el).closest($group),
  8207                  isCheckbox = ($el.filter(selector.checkbox).length > 0),
  8208                  isRequired = $el.prop('required') || $elGroup.hasClass(className.required) || $elGroup.parent().hasClass(className.required),
  8209                  isDisabled = $el.is(':disabled') || $elGroup.hasClass(className.disabled) || $elGroup.parent().hasClass(className.disabled),
  8210                  validation = module.get.validation($el),
  8211                  hasEmptyRule = validation
  8212                    ? $.grep(validation.rules, function(rule) { return rule.type == "empty" }) !== 0
  8213                    : false,
  8214                  identifier = validation.identifier || $el.attr('id') || $el.attr('name') || $el.data(metadata.validate)
  8215                ;
  8216                if (isRequired && !isDisabled && !hasEmptyRule && identifier !== undefined) {
  8217                  if (isCheckbox) {
  8218                    module.verbose("Adding 'checked' rule on field", identifier);
  8219                    module.add.rule(identifier, "checked");
  8220                  } else {
  8221                    module.verbose("Adding 'empty' rule on field", identifier);
  8222                    module.add.rule(identifier, "empty");
  8223                  }
  8224                }
  8225              });
  8226            }
  8227          },
  8228  
  8229          validate: {
  8230  
  8231            form: function(event, ignoreCallbacks) {
  8232              var values = module.get.values();
  8233  
  8234              // input keydown event will fire submit repeatedly by browser default
  8235              if(keyHeldDown) {
  8236                return false;
  8237              }
  8238  
  8239              // reset errors
  8240              formErrors = [];
  8241              if( module.determine.isValid() ) {
  8242                module.debug('Form has no validation errors, submitting');
  8243                module.set.success();
  8244                if(!settings.inline) {
  8245                  module.remove.errors();
  8246                }
  8247                if(ignoreCallbacks !== true) {
  8248                  return settings.onSuccess.call(element, event, values);
  8249                }
  8250              }
  8251              else {
  8252                module.debug('Form has errors');
  8253                submitting = false;
  8254                module.set.error();
  8255                if(!settings.inline) {
  8256                  module.add.errors(formErrors);
  8257                }
  8258                // prevent ajax submit
  8259                if(event && $module.data('moduleApi') !== undefined) {
  8260                  event.stopImmediatePropagation();
  8261                }
  8262                if(ignoreCallbacks !== true) {
  8263                  return settings.onFailure.call(element, formErrors, values);
  8264                }
  8265              }
  8266            },
  8267  
  8268            // takes a validation object and returns whether field passes validation
  8269            field: function(field, fieldName, showErrors) {
  8270              showErrors = (showErrors !== undefined)
  8271                ? showErrors
  8272                : true
  8273              ;
  8274              if(typeof field == 'string') {
  8275                module.verbose('Validating field', field);
  8276                fieldName = field;
  8277                field     = validation[field];
  8278              }
  8279              var
  8280                identifier    = field.identifier || fieldName,
  8281                $field        = module.get.field(identifier),
  8282                $dependsField = (field.depends)
  8283                  ? module.get.field(field.depends)
  8284                  : false,
  8285                fieldValid  = true,
  8286                fieldErrors = []
  8287              ;
  8288              if(!field.identifier) {
  8289                module.debug('Using field name as identifier', identifier);
  8290                field.identifier = identifier;
  8291              }
  8292              var isDisabled = !$field.filter(':not(:disabled)').length;
  8293              if(isDisabled) {
  8294                module.debug('Field is disabled. Skipping', identifier);
  8295              }
  8296              else if(field.optional && module.is.blank($field)){
  8297                module.debug('Field is optional and blank. Skipping', identifier);
  8298              }
  8299              else if(field.depends && module.is.empty($dependsField)) {
  8300                module.debug('Field depends on another value that is not present or empty. Skipping', $dependsField);
  8301              }
  8302              else if(field.rules !== undefined) {
  8303                if(showErrors) {
  8304                  $field.closest($group).removeClass(className.error);
  8305                }
  8306                $.each(field.rules, function(index, rule) {
  8307                  if( module.has.field(identifier)) {
  8308                    var invalidFields = module.validate.rule(field, rule,true) || [];
  8309                    if (invalidFields.length>0){
  8310                      module.debug('Field is invalid', identifier, rule.type);
  8311                      fieldErrors.push(module.get.prompt(rule, field));
  8312                      fieldValid = false;
  8313                      if(showErrors){
  8314                        $(invalidFields).closest($group).addClass(className.error);
  8315                      }
  8316                    }
  8317                  }
  8318                });
  8319              }
  8320              if(fieldValid) {
  8321                if(showErrors) {
  8322                  module.remove.prompt(identifier, fieldErrors);
  8323                  settings.onValid.call($field);
  8324                }
  8325              }
  8326              else {
  8327                if(showErrors) {
  8328                  formErrors = formErrors.concat(fieldErrors);
  8329                  module.add.prompt(identifier, fieldErrors, true);
  8330                  settings.onInvalid.call($field, fieldErrors);
  8331                }
  8332                return false;
  8333              }
  8334              return true;
  8335            },
  8336  
  8337            // takes validation rule and returns whether field passes rule
  8338            rule: function(field, rule, internal) {
  8339              var
  8340                $field       = module.get.field(field.identifier),
  8341                ancillary    = module.get.ancillaryValue(rule),
  8342                ruleName     = module.get.ruleName(rule),
  8343                ruleFunction = settings.rules[ruleName],
  8344                invalidFields = [],
  8345                isCheckbox = $field.is(selector.checkbox),
  8346                isValid = function(field){
  8347                  var value = (isCheckbox ? $(field).filter(':checked').val() : $(field).val());
  8348                  // cast to string avoiding encoding special values
  8349                  value = (value === undefined || value === '' || value === null)
  8350                      ? ''
  8351                      : (settings.shouldTrim) ? String(value + '').trim() : String(value + '')
  8352                  ;
  8353                  return ruleFunction.call(field, value, ancillary, $module);
  8354                }
  8355              ;
  8356              if( !$.isFunction(ruleFunction) ) {
  8357                module.error(error.noRule, ruleName);
  8358                return;
  8359              }
  8360              if(isCheckbox) {
  8361                if (!isValid($field)) {
  8362                  invalidFields = $field;
  8363                }
  8364              } else {
  8365                $.each($field, function (index, field) {
  8366                  if (!isValid(field)) {
  8367                    invalidFields.push(field);
  8368                  }
  8369                });
  8370              }
  8371              return internal ? invalidFields : !(invalidFields.length>0);
  8372            }
  8373          },
  8374  
  8375          setting: function(name, value) {
  8376            if( $.isPlainObject(name) ) {
  8377              $.extend(true, settings, name);
  8378            }
  8379            else if(value !== undefined) {
  8380              settings[name] = value;
  8381            }
  8382            else {
  8383              return settings[name];
  8384            }
  8385          },
  8386          internal: function(name, value) {
  8387            if( $.isPlainObject(name) ) {
  8388              $.extend(true, module, name);
  8389            }
  8390            else if(value !== undefined) {
  8391              module[name] = value;
  8392            }
  8393            else {
  8394              return module[name];
  8395            }
  8396          },
  8397          debug: function() {
  8398            if(!settings.silent && settings.debug) {
  8399              if(settings.performance) {
  8400                module.performance.log(arguments);
  8401              }
  8402              else {
  8403                module.debug = Function.prototype.bind.call(console.info, console, settings.name + ':');
  8404                module.debug.apply(console, arguments);
  8405              }
  8406            }
  8407          },
  8408          verbose: function() {
  8409            if(!settings.silent && settings.verbose && settings.debug) {
  8410              if(settings.performance) {
  8411                module.performance.log(arguments);
  8412              }
  8413              else {
  8414                module.verbose = Function.prototype.bind.call(console.info, console, settings.name + ':');
  8415                module.verbose.apply(console, arguments);
  8416              }
  8417            }
  8418          },
  8419          error: function() {
  8420            if(!settings.silent) {
  8421              module.error = Function.prototype.bind.call(console.error, console, settings.name + ':');
  8422              module.error.apply(console, arguments);
  8423            }
  8424          },
  8425          performance: {
  8426            log: function(message) {
  8427              var
  8428                currentTime,
  8429                executionTime,
  8430                previousTime
  8431              ;
  8432              if(settings.performance) {
  8433                currentTime   = new Date().getTime();
  8434                previousTime  = time || currentTime;
  8435                executionTime = currentTime - previousTime;
  8436                time          = currentTime;
  8437                performance.push({
  8438                  'Name'           : message[0],
  8439                  'Arguments'      : [].slice.call(message, 1) || '',
  8440                  'Element'        : element,
  8441                  'Execution Time' : executionTime
  8442                });
  8443              }
  8444              clearTimeout(module.performance.timer);
  8445              module.performance.timer = setTimeout(module.performance.display, 500);
  8446            },
  8447            display: function() {
  8448              var
  8449                title = settings.name + ':',
  8450                totalTime = 0
  8451              ;
  8452              time = false;
  8453              clearTimeout(module.performance.timer);
  8454              $.each(performance, function(index, data) {
  8455                totalTime += data['Execution Time'];
  8456              });
  8457              title += ' ' + totalTime + 'ms';
  8458              if(moduleSelector) {
  8459                title += ' \'' + moduleSelector + '\'';
  8460              }
  8461              if($allModules.length > 1) {
  8462                title += ' ' + '(' + $allModules.length + ')';
  8463              }
  8464              if( (console.group !== undefined || console.table !== undefined) && performance.length > 0) {
  8465                console.groupCollapsed(title);
  8466                if(console.table) {
  8467                  console.table(performance);
  8468                }
  8469                else {
  8470                  $.each(performance, function(index, data) {
  8471                    console.log(data['Name'] + ': ' + data['Execution Time']+'ms');
  8472                  });
  8473                }
  8474                console.groupEnd();
  8475              }
  8476              performance = [];
  8477            }
  8478          },
  8479          invoke: function(query, passedArguments, context) {
  8480            var
  8481              object = instance,
  8482              maxDepth,
  8483              found,
  8484              response
  8485            ;
  8486            passedArguments = passedArguments || queryArguments;
  8487            context         = element         || context;
  8488            if(typeof query == 'string' && object !== undefined) {
  8489              query    = query.split(/[\. ]/);
  8490              maxDepth = query.length - 1;
  8491              $.each(query, function(depth, value) {
  8492                var camelCaseValue = (depth != maxDepth)
  8493                  ? value + query[depth + 1].charAt(0).toUpperCase() + query[depth + 1].slice(1)
  8494                  : query
  8495                ;
  8496                if( $.isPlainObject( object[camelCaseValue] ) && (depth != maxDepth) ) {
  8497                  object = object[camelCaseValue];
  8498                }
  8499                else if( object[camelCaseValue] !== undefined ) {
  8500                  found = object[camelCaseValue];
  8501                  return false;
  8502                }
  8503                else if( $.isPlainObject( object[value] ) && (depth != maxDepth) ) {
  8504                  object = object[value];
  8505                }
  8506                else if( object[value] !== undefined ) {
  8507                  found = object[value];
  8508                  return false;
  8509                }
  8510                else {
  8511                  return false;
  8512                }
  8513              });
  8514            }
  8515            if( $.isFunction( found ) ) {
  8516              response = found.apply(context, passedArguments);
  8517            }
  8518            else if(found !== undefined) {
  8519              response = found;
  8520            }
  8521            if(Array.isArray(returnedValue)) {
  8522              returnedValue.push(response);
  8523            }
  8524            else if(returnedValue !== undefined) {
  8525              returnedValue = [returnedValue, response];
  8526            }
  8527            else if(response !== undefined) {
  8528              returnedValue = response;
  8529            }
  8530            return found;
  8531          }
  8532        };
  8533        module.initialize();
  8534      })
  8535    ;
  8536  
  8537    return (returnedValue !== undefined)
  8538      ? returnedValue
  8539      : this
  8540    ;
  8541  };
  8542  
  8543  $.fn.form.settings = {
  8544  
  8545    name              : 'Form',
  8546    namespace         : 'form',
  8547  
  8548    debug             : false,
  8549    verbose           : false,
  8550    performance       : true,
  8551  
  8552    fields            : false,
  8553  
  8554    keyboardShortcuts : true,
  8555    on                : 'submit',
  8556    inline            : false,
  8557  
  8558    delay             : 200,
  8559    revalidate        : true,
  8560    shouldTrim        : true,
  8561  
  8562    transition        : 'scale',
  8563    duration          : 200,
  8564  
  8565    autoCheckRequired : false,
  8566    preventLeaving    : false,
  8567    dateHandling      : 'date', // 'date', 'input', 'formatter'
  8568  
  8569    onValid           : function() {},
  8570    onInvalid         : function() {},
  8571    onSuccess         : function() { return true; },
  8572    onFailure         : function() { return false; },
  8573    onDirty           : function() {},
  8574    onClean           : function() {},
  8575  
  8576    metadata : {
  8577      defaultValue : 'default',
  8578      validate     : 'validate',
  8579      isDirty      : 'isDirty'
  8580    },
  8581  
  8582    regExp: {
  8583      htmlID  : /^[a-zA-Z][\w:.-]*$/g,
  8584      bracket : /\[(.*)\]/i,
  8585      decimal : /^\d+\.?\d*$/,
  8586      email   : /^[a-z0-9!#$%&'*+\/=?^_`{|}~.-]+@[a-z0-9]([a-z0-9-]*[a-z0-9])?(\.[a-z0-9]([a-z0-9-]*[a-z0-9])?)*$/i,
  8587      escape  : /[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|:,=@]/g,
  8588      flags   : /^\/(.*)\/(.*)?/,
  8589      integer : /^\-?\d+$/,
  8590      number  : /^\-?\d*(\.\d+)?$/,
  8591      url     : /(https?:\/\/(?:www\.|(?!www))[^\s\.]+\.[^\s]{2,}|www\.[^\s]+\.[^\s]{2,})/i
  8592    },
  8593  
  8594    text: {
  8595      unspecifiedRule  : 'Please enter a valid value',
  8596      unspecifiedField : 'This field',
  8597      leavingMessage   : 'There are unsaved changes on this page which will be discarded if you continue.'
  8598    },
  8599  
  8600    prompt: {
  8601      empty                : '{name} must have a value',
  8602      checked              : '{name} must be checked',
  8603      email                : '{name} must be a valid e-mail',
  8604      url                  : '{name} must be a valid url',
  8605      regExp               : '{name} is not formatted correctly',
  8606      integer              : '{name} must be an integer',
  8607      decimal              : '{name} must be a decimal number',
  8608      number               : '{name} must be set to a number',
  8609      is                   : '{name} must be "{ruleValue}"',
  8610      isExactly            : '{name} must be exactly "{ruleValue}"',
  8611      not                  : '{name} cannot be set to "{ruleValue}"',
  8612      notExactly           : '{name} cannot be set to exactly "{ruleValue}"',
  8613      contain              : '{name} must contain "{ruleValue}"',
  8614      containExactly       : '{name} must contain exactly "{ruleValue}"',
  8615      doesntContain        : '{name} cannot contain  "{ruleValue}"',
  8616      doesntContainExactly : '{name} cannot contain exactly "{ruleValue}"',
  8617      minLength            : '{name} must be at least {ruleValue} characters',
  8618      length               : '{name} must be at least {ruleValue} characters',
  8619      exactLength          : '{name} must be exactly {ruleValue} characters',
  8620      maxLength            : '{name} cannot be longer than {ruleValue} characters',
  8621      match                : '{name} must match {ruleValue} field',
  8622      different            : '{name} must have a different value than {ruleValue} field',
  8623      creditCard           : '{name} must be a valid credit card number',
  8624      minCount             : '{name} must have at least {ruleValue} choices',
  8625      exactCount           : '{name} must have exactly {ruleValue} choices',
  8626      maxCount             : '{name} must have {ruleValue} or less choices'
  8627    },
  8628  
  8629    selector : {
  8630      checkbox   : 'input[type="checkbox"], input[type="radio"]',
  8631      clear      : '.clear',
  8632      field      : 'input:not(.search), textarea, select',
  8633      group      : '.field',
  8634      input      : 'input',
  8635      message    : '.error.message',
  8636      prompt     : '.prompt.label',
  8637      radio      : 'input[type="radio"]',
  8638      reset      : '.reset:not([type="reset"])',
  8639      submit     : '.submit:not([type="submit"])',
  8640      uiCheckbox : '.ui.checkbox',
  8641      uiDropdown : '.ui.dropdown',
  8642      uiCalendar : '.ui.calendar'
  8643    },
  8644  
  8645    className : {
  8646      error    : 'error',
  8647      label    : 'ui basic red pointing prompt label',
  8648      pressed  : 'down',
  8649      success  : 'success',
  8650      required : 'required',
  8651      disabled : 'disabled'
  8652    },
  8653  
  8654    error: {
  8655      identifier : 'You must specify a string identifier for each field',
  8656      method     : 'The method you called is not defined.',
  8657      noRule     : 'There is no rule matching the one you specified',
  8658      oldSyntax  : 'Starting in 2.0 forms now only take a single settings object. Validation settings converted to new syntax automatically.',
  8659      noElement  : 'This module requires ui {element}'
  8660    },
  8661  
  8662    templates: {
  8663  
  8664      // template that produces error message
  8665      error: function(errors) {
  8666        var
  8667          html = '<ul class="list">'
  8668        ;
  8669        $.each(errors, function(index, value) {
  8670          html += '<li>' + value + '</li>';
  8671        });
  8672        html += '</ul>';
  8673        return $(html);
  8674      },
  8675  
  8676      // template that produces label
  8677      prompt: function(errors, labelClasses) {
  8678        return $('<div/>')
  8679          .addClass(labelClasses)
  8680          .html(errors[0])
  8681        ;
  8682      }
  8683    },
  8684  
  8685    formatter: {
  8686      date: function(date) {
  8687        return Intl.DateTimeFormat('en-GB').format(date);
  8688      },
  8689      datetime: function(date) {
  8690        return Intl.DateTimeFormat('en-GB', {
  8691          year: "numeric",
  8692          month: "2-digit",
  8693          day: "2-digit",
  8694          hour: '2-digit',
  8695          minute: '2-digit',
  8696          second: '2-digit'
  8697        }).format(date);
  8698      },
  8699      time: function(date) {
  8700        return Intl.DateTimeFormat('en-GB', {
  8701          hour: '2-digit',
  8702          minute: '2-digit',
  8703          second: '2-digit'
  8704        }).format(date);
  8705      },
  8706      month: function(date) {
  8707        return Intl.DateTimeFormat('en-GB', {
  8708          month: '2-digit',
  8709          year: 'numeric'
  8710        }).format(date);
  8711      },
  8712      year: function(date) {
  8713        return Intl.DateTimeFormat('en-GB', {
  8714          year: 'numeric'
  8715        }).format(date);
  8716      }
  8717    },
  8718  
  8719    rules: {
  8720  
  8721      // is not empty or blank string
  8722      empty: function(value) {
  8723        return !(value === undefined || '' === value || Array.isArray(value) && value.length === 0);
  8724      },
  8725  
  8726      // checkbox checked
  8727      checked: function() {
  8728        return ($(this).filter(':checked').length > 0);
  8729      },
  8730  
  8731      // is most likely an email
  8732      email: function(value){
  8733        return $.fn.form.settings.regExp.email.test(value);
  8734      },
  8735  
  8736      // value is most likely url
  8737      url: function(value) {
  8738        return $.fn.form.settings.regExp.url.test(value);
  8739      },
  8740  
  8741      // matches specified regExp
  8742      regExp: function(value, regExp) {
  8743        if(regExp instanceof RegExp) {
  8744          return value.match(regExp);
  8745        }
  8746        var
  8747          regExpParts = regExp.match($.fn.form.settings.regExp.flags),
  8748          flags
  8749        ;
  8750        // regular expression specified as /baz/gi (flags)
  8751        if(regExpParts) {
  8752          regExp = (regExpParts.length >= 2)
  8753            ? regExpParts[1]
  8754            : regExp
  8755          ;
  8756          flags = (regExpParts.length >= 3)
  8757            ? regExpParts[2]
  8758            : ''
  8759          ;
  8760        }
  8761        return value.match( new RegExp(regExp, flags) );
  8762      },
  8763  
  8764      // is valid integer or matches range
  8765      integer: function(value, range) {
  8766        var
  8767          intRegExp = $.fn.form.settings.regExp.integer,
  8768          min,
  8769          max,
  8770          parts
  8771        ;
  8772        if( !range || ['', '..'].indexOf(range) !== -1) {
  8773          // do nothing
  8774        }
  8775        else if(range.indexOf('..') == -1) {
  8776          if(intRegExp.test(range)) {
  8777            min = max = range - 0;
  8778          }
  8779        }
  8780        else {
  8781          parts = range.split('..', 2);
  8782          if(intRegExp.test(parts[0])) {
  8783            min = parts[0] - 0;
  8784          }
  8785          if(intRegExp.test(parts[1])) {
  8786            max = parts[1] - 0;
  8787          }
  8788        }
  8789        return (
  8790          intRegExp.test(value) &&
  8791          (min === undefined || value >= min) &&
  8792          (max === undefined || value <= max)
  8793        );
  8794      },
  8795  
  8796      // is valid number (with decimal)
  8797      decimal: function(value) {
  8798        return $.fn.form.settings.regExp.decimal.test(value);
  8799      },
  8800  
  8801      // is valid number
  8802      number: function(value) {
  8803        return $.fn.form.settings.regExp.number.test(value);
  8804      },
  8805  
  8806      // is value (case insensitive)
  8807      is: function(value, text) {
  8808        text = (typeof text == 'string')
  8809          ? text.toLowerCase()
  8810          : text
  8811        ;
  8812        value = (typeof value == 'string')
  8813          ? value.toLowerCase()
  8814          : value
  8815        ;
  8816        return (value == text);
  8817      },
  8818  
  8819      // is value
  8820      isExactly: function(value, text) {
  8821        return (value == text);
  8822      },
  8823  
  8824      // value is not another value (case insensitive)
  8825      not: function(value, notValue) {
  8826        value = (typeof value == 'string')
  8827          ? value.toLowerCase()
  8828          : value
  8829        ;
  8830        notValue = (typeof notValue == 'string')
  8831          ? notValue.toLowerCase()
  8832          : notValue
  8833        ;
  8834        return (value != notValue);
  8835      },
  8836  
  8837      // value is not another value (case sensitive)
  8838      notExactly: function(value, notValue) {
  8839        return (value != notValue);
  8840      },
  8841  
  8842      // value contains text (insensitive)
  8843      contains: function(value, text) {
  8844        // escape regex characters
  8845        text = text.replace($.fn.form.settings.regExp.escape, "\\$&");
  8846        return (value.search( new RegExp(text, 'i') ) !== -1);
  8847      },
  8848  
  8849      // value contains text (case sensitive)
  8850      containsExactly: function(value, text) {
  8851        // escape regex characters
  8852        text = text.replace($.fn.form.settings.regExp.escape, "\\$&");
  8853        return (value.search( new RegExp(text) ) !== -1);
  8854      },
  8855  
  8856      // value contains text (insensitive)
  8857      doesntContain: function(value, text) {
  8858        // escape regex characters
  8859        text = text.replace($.fn.form.settings.regExp.escape, "\\$&");
  8860        return (value.search( new RegExp(text, 'i') ) === -1);
  8861      },
  8862  
  8863      // value contains text (case sensitive)
  8864      doesntContainExactly: function(value, text) {
  8865        // escape regex characters
  8866        text = text.replace($.fn.form.settings.regExp.escape, "\\$&");
  8867        return (value.search( new RegExp(text) ) === -1);
  8868      },
  8869  
  8870      // is at least string length
  8871      minLength: function(value, requiredLength) {
  8872        return (value !== undefined)
  8873          ? (value.length >= requiredLength)
  8874          : false
  8875        ;
  8876      },
  8877  
  8878      // see rls notes for 2.0.6 (this is a duplicate of minLength)
  8879      length: function(value, requiredLength) {
  8880        return (value !== undefined)
  8881          ? (value.length >= requiredLength)
  8882          : false
  8883        ;
  8884      },
  8885  
  8886      // is exactly length
  8887      exactLength: function(value, requiredLength) {
  8888        return (value !== undefined)
  8889          ? (value.length == requiredLength)
  8890          : false
  8891        ;
  8892      },
  8893  
  8894      // is less than length
  8895      maxLength: function(value, maxLength) {
  8896        return (value !== undefined)
  8897          ? (value.length <= maxLength)
  8898          : false
  8899        ;
  8900      },
  8901  
  8902      // matches another field
  8903      match: function(value, identifier, $module) {
  8904        var
  8905          matchingValue,
  8906          matchingElement
  8907        ;
  8908        if((matchingElement = $module.find('[data-validate="'+ identifier +'"]')).length > 0 ) {
  8909          matchingValue = matchingElement.val();
  8910        }
  8911        else if((matchingElement = $module.find('#' + identifier)).length > 0) {
  8912          matchingValue = matchingElement.val();
  8913        }
  8914        else if((matchingElement = $module.find('[name="' + identifier +'"]')).length > 0) {
  8915          matchingValue = matchingElement.val();
  8916        }
  8917        else if((matchingElement = $module.find('[name="' + identifier +'[]"]')).length > 0 ) {
  8918          matchingValue = matchingElement;
  8919        }
  8920        return (matchingValue !== undefined)
  8921          ? ( value.toString() == matchingValue.toString() )
  8922          : false
  8923        ;
  8924      },
  8925  
  8926      // different than another field
  8927      different: function(value, identifier, $module) {
  8928        // use either id or name of field
  8929        var
  8930          matchingValue,
  8931          matchingElement
  8932        ;
  8933        if((matchingElement = $module.find('[data-validate="'+ identifier +'"]')).length > 0 ) {
  8934          matchingValue = matchingElement.val();
  8935        }
  8936        else if((matchingElement = $module.find('#' + identifier)).length > 0) {
  8937          matchingValue = matchingElement.val();
  8938        }
  8939        else if((matchingElement = $module.find('[name="' + identifier +'"]')).length > 0) {
  8940          matchingValue = matchingElement.val();
  8941        }
  8942        else if((matchingElement = $module.find('[name="' + identifier +'[]"]')).length > 0 ) {
  8943          matchingValue = matchingElement;
  8944        }
  8945        return (matchingValue !== undefined)
  8946          ? ( value.toString() !== matchingValue.toString() )
  8947          : false
  8948        ;
  8949      },
  8950  
  8951      creditCard: function(cardNumber, cardTypes) {
  8952        var
  8953          cards = {
  8954            visa: {
  8955              pattern : /^4/,
  8956              length  : [16]
  8957            },
  8958            amex: {
  8959              pattern : /^3[47]/,
  8960              length  : [15]
  8961            },
  8962            mastercard: {
  8963              pattern : /^5[1-5]/,
  8964              length  : [16]
  8965            },
  8966            discover: {
  8967              pattern : /^(6011|622(12[6-9]|1[3-9][0-9]|[2-8][0-9]{2}|9[0-1][0-9]|92[0-5]|64[4-9])|65)/,
  8968              length  : [16]
  8969            },
  8970            unionPay: {
  8971              pattern : /^(62|88)/,
  8972              length  : [16, 17, 18, 19]
  8973            },
  8974            jcb: {
  8975              pattern : /^35(2[89]|[3-8][0-9])/,
  8976              length  : [16]
  8977            },
  8978            maestro: {
  8979              pattern : /^(5018|5020|5038|6304|6759|676[1-3])/,
  8980              length  : [12, 13, 14, 15, 16, 17, 18, 19]
  8981            },
  8982            dinersClub: {
  8983              pattern : /^(30[0-5]|^36)/,
  8984              length  : [14]
  8985            },
  8986            laser: {
  8987              pattern : /^(6304|670[69]|6771)/,
  8988              length  : [16, 17, 18, 19]
  8989            },
  8990            visaElectron: {
  8991              pattern : /^(4026|417500|4508|4844|491(3|7))/,
  8992              length  : [16]
  8993            }
  8994          },
  8995          valid         = {},
  8996          validCard     = false,
  8997          requiredTypes = (typeof cardTypes == 'string')
  8998            ? cardTypes.split(',')
  8999            : false,
  9000          unionPay,
  9001          validation
  9002        ;
  9003  
  9004        if(typeof cardNumber !== 'string' || cardNumber.length === 0) {
  9005          return;
  9006        }
  9007  
  9008        // allow dashes in card
  9009        cardNumber = cardNumber.replace(/[\-]/g, '');
  9010  
  9011        // verify card types
  9012        if(requiredTypes) {
  9013          $.each(requiredTypes, function(index, type){
  9014            // verify each card type
  9015            validation = cards[type];
  9016            if(validation) {
  9017              valid = {
  9018                length  : ($.inArray(cardNumber.length, validation.length) !== -1),
  9019                pattern : (cardNumber.search(validation.pattern) !== -1)
  9020              };
  9021              if(valid.length && valid.pattern) {
  9022                validCard = true;
  9023              }
  9024            }
  9025          });
  9026  
  9027          if(!validCard) {
  9028            return false;
  9029          }
  9030        }
  9031  
  9032        // skip luhn for UnionPay
  9033        unionPay = {
  9034          number  : ($.inArray(cardNumber.length, cards.unionPay.length) !== -1),
  9035          pattern : (cardNumber.search(cards.unionPay.pattern) !== -1)
  9036        };
  9037        if(unionPay.number && unionPay.pattern) {
  9038          return true;
  9039        }
  9040  
  9041        // verify luhn, adapted from  <https://gist.github.com/2134376>
  9042        var
  9043          length        = cardNumber.length,
  9044          multiple      = 0,
  9045          producedValue = [
  9046            [0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
  9047            [0, 2, 4, 6, 8, 1, 3, 5, 7, 9]
  9048          ],
  9049          sum           = 0
  9050        ;
  9051        while (length--) {
  9052          sum += producedValue[multiple][parseInt(cardNumber.charAt(length), 10)];
  9053          multiple ^= 1;
  9054        }
  9055        return (sum % 10 === 0 && sum > 0);
  9056      },
  9057  
  9058      minCount: function(value, minCount) {
  9059        if(minCount == 0) {
  9060          return true;
  9061        }
  9062        if(minCount == 1) {
  9063          return (value !== '');
  9064        }
  9065        return (value.split(',').length >= minCount);
  9066      },
  9067  
  9068      exactCount: function(value, exactCount) {
  9069        if(exactCount == 0) {
  9070          return (value === '');
  9071        }
  9072        if(exactCount == 1) {
  9073          return (value !== '' && value.search(',') === -1);
  9074        }
  9075        return (value.split(',').length == exactCount);
  9076      },
  9077  
  9078      maxCount: function(value, maxCount) {
  9079        if(maxCount == 0) {
  9080          return false;
  9081        }
  9082        if(maxCount == 1) {
  9083          return (value.search(',') === -1);
  9084        }
  9085        return (value.split(',').length <= maxCount);
  9086      }
  9087    }
  9088  
  9089  };
  9090  
  9091  })( jQuery, window, document );
  9092  
  9093  /*!
  9094   * # Fomantic-UI - Modal
  9095   * http://github.com/fomantic/Fomantic-UI/
  9096   *
  9097   *
  9098   * Released under the MIT license
  9099   * http://opensource.org/licenses/MIT
  9100   *
  9101   */
  9102  
  9103  ;(function ($, window, document, undefined) {
  9104  
  9105  'use strict';
  9106  
  9107  $.isFunction = $.isFunction || function(obj) {
  9108    return typeof obj === "function" && typeof obj.nodeType !== "number";
  9109  };
  9110  
  9111  window = (typeof window != 'undefined' && window.Math == Math)
  9112    ? window
  9113    : (typeof self != 'undefined' && self.Math == Math)
  9114      ? self
  9115      : Function('return this')()
  9116  ;
  9117  
  9118  $.fn.modal = function(parameters) {
  9119    var
  9120      $allModules    = $(this),
  9121      $window        = $(window),
  9122      $document      = $(document),
  9123      $body          = $('body'),
  9124  
  9125      moduleSelector = $allModules.selector || '',
  9126  
  9127      time           = new Date().getTime(),
  9128      performance    = [],
  9129  
  9130      query          = arguments[0],
  9131      methodInvoked  = (typeof query == 'string'),
  9132      queryArguments = [].slice.call(arguments, 1),
  9133  
  9134      requestAnimationFrame = window.requestAnimationFrame
  9135        || window.mozRequestAnimationFrame
  9136        || window.webkitRequestAnimationFrame
  9137        || window.msRequestAnimationFrame
  9138        || function(callback) { setTimeout(callback, 0); },
  9139  
  9140      returnedValue
  9141    ;
  9142  
  9143    $allModules
  9144      .each(function() {
  9145        var
  9146          settings    = ( $.isPlainObject(parameters) )
  9147            ? $.extend(true, {}, $.fn.modal.settings, parameters)
  9148            : $.extend({}, $.fn.modal.settings),
  9149  
  9150          selector        = settings.selector,
  9151          className       = settings.className,
  9152          namespace       = settings.namespace,
  9153          error           = settings.error,
  9154  
  9155          eventNamespace  = '.' + namespace,
  9156          moduleNamespace = 'module-' + namespace,
  9157  
  9158          $module         = $(this),
  9159          $context        = $(settings.context),
  9160          $close          = $module.find(selector.close),
  9161  
  9162          $allModals,
  9163          $otherModals,
  9164          $focusedElement,
  9165          $dimmable,
  9166          $dimmer,
  9167  
  9168          element         = this,
  9169          instance        = $module.data(moduleNamespace),
  9170  
  9171          ignoreRepeatedEvents = false,
  9172  
  9173          initialMouseDownInModal,
  9174          initialMouseDownInScrollbar,
  9175          initialBodyMargin = '',
  9176          tempBodyMargin = '',
  9177  
  9178          elementEventNamespace,
  9179          id,
  9180          observer,
  9181          module
  9182        ;
  9183        module  = {
  9184  
  9185          initialize: function() {
  9186            module.cache = {};
  9187            module.verbose('Initializing dimmer', $context);
  9188  
  9189            module.create.id();
  9190            module.create.dimmer();
  9191  
  9192            if ( settings.allowMultiple ) {
  9193              module.create.innerDimmer();
  9194            }
  9195            if (!settings.centered){
  9196              $module.addClass('top aligned');
  9197            }
  9198            module.refreshModals();
  9199  
  9200            module.bind.events();
  9201            if(settings.observeChanges) {
  9202              module.observeChanges();
  9203            }
  9204            module.instantiate();
  9205          },
  9206  
  9207          instantiate: function() {
  9208            module.verbose('Storing instance of modal');
  9209            instance = module;
  9210            $module
  9211              .data(moduleNamespace, instance)
  9212            ;
  9213          },
  9214  
  9215          create: {
  9216            dimmer: function() {
  9217              var
  9218                defaultSettings = {
  9219                  debug      : settings.debug,
  9220                  dimmerName : 'modals'
  9221                },
  9222                dimmerSettings = $.extend(true, defaultSettings, settings.dimmerSettings)
  9223              ;
  9224              if($.fn.dimmer === undefined) {
  9225                module.error(error.dimmer);
  9226                return;
  9227              }
  9228              module.debug('Creating dimmer');
  9229              $dimmable = $context.dimmer(dimmerSettings);
  9230              if(settings.detachable) {
  9231                module.verbose('Modal is detachable, moving content into dimmer');
  9232                $dimmable.dimmer('add content', $module);
  9233              }
  9234              else {
  9235                module.set.undetached();
  9236              }
  9237              $dimmer = $dimmable.dimmer('get dimmer');
  9238            },
  9239            id: function() {
  9240              id = (Math.random().toString(16) + '000000000').substr(2, 8);
  9241              elementEventNamespace = '.' + id;
  9242              module.verbose('Creating unique id for element', id);
  9243            },
  9244            innerDimmer: function() {
  9245              if ( $module.find(selector.dimmer).length == 0 ) {
  9246                $module.prepend('<div class="ui inverted dimmer"></div>');
  9247              }
  9248            }
  9249          },
  9250  
  9251          destroy: function() {
  9252            if (observer) {
  9253              observer.disconnect();
  9254            }
  9255            module.verbose('Destroying previous modal');
  9256            $module
  9257              .removeData(moduleNamespace)
  9258              .off(eventNamespace)
  9259            ;
  9260            $window.off(elementEventNamespace);
  9261            $dimmer.off(elementEventNamespace);
  9262            $close.off(eventNamespace);
  9263            $context.dimmer('destroy');
  9264          },
  9265  
  9266          observeChanges: function() {
  9267            if('MutationObserver' in window) {
  9268              observer = new MutationObserver(function(mutations) {
  9269                module.debug('DOM tree modified, refreshing');
  9270                module.refresh();
  9271              });
  9272              observer.observe(element, {
  9273                childList : true,
  9274                subtree   : true
  9275              });
  9276              module.debug('Setting up mutation observer', observer);
  9277            }
  9278          },
  9279  
  9280          refresh: function() {
  9281            module.remove.scrolling();
  9282            module.cacheSizes();
  9283            if(!module.can.useFlex()) {
  9284              module.set.modalOffset();
  9285            }
  9286            module.set.screenHeight();
  9287            module.set.type();
  9288          },
  9289  
  9290          refreshModals: function() {
  9291            $otherModals = $module.siblings(selector.modal);
  9292            $allModals   = $otherModals.add($module);
  9293          },
  9294  
  9295          attachEvents: function(selector, event) {
  9296            var
  9297              $toggle = $(selector)
  9298            ;
  9299            event = $.isFunction(module[event])
  9300              ? module[event]
  9301              : module.toggle
  9302            ;
  9303            if($toggle.length > 0) {
  9304              module.debug('Attaching modal events to element', selector, event);
  9305              $toggle
  9306                .off(eventNamespace)
  9307                .on('click' + eventNamespace, event)
  9308              ;
  9309            }
  9310            else {
  9311              module.error(error.notFound, selector);
  9312            }
  9313          },
  9314  
  9315          bind: {
  9316            events: function() {
  9317              module.verbose('Attaching events');
  9318              $module
  9319                .on('click' + eventNamespace, selector.close, module.event.close)
  9320                .on('click' + eventNamespace, selector.approve, module.event.approve)
  9321                .on('click' + eventNamespace, selector.deny, module.event.deny)
  9322              ;
  9323              $window
  9324                .on('resize' + elementEventNamespace, module.event.resize)
  9325              ;
  9326            },
  9327            scrollLock: function() {
  9328              // touch events default to passive, due to changes in chrome to optimize mobile perf
  9329              $dimmable.get(0).addEventListener('touchmove', module.event.preventScroll, { passive: false });
  9330            }
  9331          },
  9332  
  9333          unbind: {
  9334            scrollLock: function() {
  9335              $dimmable.get(0).removeEventListener('touchmove', module.event.preventScroll, { passive: false });
  9336            }
  9337          },
  9338  
  9339          get: {
  9340            id: function() {
  9341              return (Math.random().toString(16) + '000000000').substr(2, 8);
  9342            }
  9343          },
  9344  
  9345          event: {
  9346            approve: function() {
  9347              if(ignoreRepeatedEvents || settings.onApprove.call(element, $(this)) === false) {
  9348                module.verbose('Approve callback returned false cancelling hide');
  9349                return;
  9350              }
  9351              ignoreRepeatedEvents = true;
  9352              module.hide(function() {
  9353                ignoreRepeatedEvents = false;
  9354              });
  9355            },
  9356            preventScroll: function(event) {
  9357              if(event.target.className.indexOf('dimmer') !== -1) {
  9358                event.preventDefault();
  9359              }
  9360            },
  9361            deny: function() {
  9362              if(ignoreRepeatedEvents || settings.onDeny.call(element, $(this)) === false) {
  9363                module.verbose('Deny callback returned false cancelling hide');
  9364                return;
  9365              }
  9366              ignoreRepeatedEvents = true;
  9367              module.hide(function() {
  9368                ignoreRepeatedEvents = false;
  9369              });
  9370            },
  9371            close: function() {
  9372              module.hide();
  9373            },
  9374            mousedown: function(event) {
  9375              var
  9376                $target   = $(event.target),
  9377                isRtl = module.is.rtl();
  9378              ;
  9379              initialMouseDownInModal = ($target.closest(selector.modal).length > 0);
  9380              if(initialMouseDownInModal) {
  9381                module.verbose('Mouse down event registered inside the modal');
  9382              }
  9383              initialMouseDownInScrollbar = module.is.scrolling() && ((!isRtl && $(window).outerWidth() - settings.scrollbarWidth <= event.clientX) || (isRtl && settings.scrollbarWidth >= event.clientX));
  9384              if(initialMouseDownInScrollbar) {
  9385                module.verbose('Mouse down event registered inside the scrollbar');
  9386              }
  9387            },
  9388            mouseup: function(event) {
  9389              if(!settings.closable) {
  9390                module.verbose('Dimmer clicked but closable setting is disabled');
  9391                return;
  9392              }
  9393              if(initialMouseDownInModal) {
  9394                module.debug('Dimmer clicked but mouse down was initially registered inside the modal');
  9395                return;
  9396              }
  9397              if(initialMouseDownInScrollbar){
  9398                module.debug('Dimmer clicked but mouse down was initially registered inside the scrollbar');
  9399                return;
  9400              }
  9401              var
  9402                $target   = $(event.target),
  9403                isInModal = ($target.closest(selector.modal).length > 0),
  9404                isInDOM   = $.contains(document.documentElement, event.target)
  9405              ;
  9406              if(!isInModal && isInDOM && module.is.active() && $module.hasClass(className.front) ) {
  9407                module.debug('Dimmer clicked, hiding all modals');
  9408                if(settings.allowMultiple) {
  9409                  if(!module.hideAll()) {
  9410                    return;
  9411                  }
  9412                }
  9413                else if(!module.hide()){
  9414                    return;
  9415                }
  9416                module.remove.clickaway();
  9417              }
  9418            },
  9419            debounce: function(method, delay) {
  9420              clearTimeout(module.timer);
  9421              module.timer = setTimeout(method, delay);
  9422            },
  9423            keyboard: function(event) {
  9424              var
  9425                keyCode   = event.which,
  9426                escapeKey = 27
  9427              ;
  9428              if(keyCode == escapeKey) {
  9429                if(settings.closable) {
  9430                  module.debug('Escape key pressed hiding modal');
  9431                  if ( $module.hasClass(className.front) ) {
  9432                    module.hide();
  9433                  }
  9434                }
  9435                else {
  9436                  module.debug('Escape key pressed, but closable is set to false');
  9437                }
  9438                event.preventDefault();
  9439              }
  9440            },
  9441            resize: function() {
  9442              if( $dimmable.dimmer('is active') && ( module.is.animating() || module.is.active() ) ) {
  9443                requestAnimationFrame(module.refresh);
  9444              }
  9445            }
  9446          },
  9447  
  9448          toggle: function() {
  9449            if( module.is.active() || module.is.animating() ) {
  9450              module.hide();
  9451            }
  9452            else {
  9453              module.show();
  9454            }
  9455          },
  9456  
  9457          show: function(callback) {
  9458            callback = $.isFunction(callback)
  9459              ? callback
  9460              : function(){}
  9461            ;
  9462            module.refreshModals();
  9463            module.set.dimmerSettings();
  9464            module.set.dimmerStyles();
  9465  
  9466            module.showModal(callback);
  9467          },
  9468  
  9469          hide: function(callback) {
  9470            callback = $.isFunction(callback)
  9471              ? callback
  9472              : function(){}
  9473            ;
  9474            module.refreshModals();
  9475            return module.hideModal(callback);
  9476          },
  9477  
  9478          showModal: function(callback) {
  9479            callback = $.isFunction(callback)
  9480              ? callback
  9481              : function(){}
  9482            ;
  9483            if( module.is.animating() || !module.is.active() ) {
  9484              module.showDimmer();
  9485              module.cacheSizes();
  9486              module.set.bodyMargin();
  9487              if(module.can.useFlex()) {
  9488                module.remove.legacy();
  9489              }
  9490              else {
  9491                module.set.legacy();
  9492                module.set.modalOffset();
  9493                module.debug('Using non-flex legacy modal positioning.');
  9494              }
  9495              module.set.screenHeight();
  9496              module.set.type();
  9497              module.set.clickaway();
  9498  
  9499              if( !settings.allowMultiple && module.others.active() ) {
  9500                module.hideOthers(module.showModal);
  9501              }
  9502              else {
  9503                ignoreRepeatedEvents = false;
  9504                if( settings.allowMultiple ) {
  9505                  if ( module.others.active() ) {
  9506                    $otherModals.filter('.' + className.active).find(selector.dimmer).addClass('active');
  9507                  }
  9508  
  9509                  if ( settings.detachable ) {
  9510                    $module.detach().appendTo($dimmer);
  9511                  }
  9512                }
  9513                settings.onShow.call(element);
  9514                if(settings.transition && $.fn.transition !== undefined && $module.transition('is supported')) {
  9515                  module.debug('Showing modal with css animations');
  9516                  $module
  9517                    .transition({
  9518                      debug       : settings.debug,
  9519                      animation   : settings.transition + ' in',
  9520                      queue       : settings.queue,
  9521                      duration    : settings.duration,
  9522                      useFailSafe : true,
  9523                      onComplete : function() {
  9524                        settings.onVisible.apply(element);
  9525                        if(settings.keyboardShortcuts) {
  9526                          module.add.keyboardShortcuts();
  9527                        }
  9528                        module.save.focus();
  9529                        module.set.active();
  9530                        if(settings.autofocus) {
  9531                          module.set.autofocus();
  9532                        }
  9533                        callback();
  9534                      }
  9535                    })
  9536                  ;
  9537                }
  9538                else {
  9539                  module.error(error.noTransition);
  9540                }
  9541              }
  9542            }
  9543            else {
  9544              module.debug('Modal is already visible');
  9545            }
  9546          },
  9547  
  9548          hideModal: function(callback, keepDimmed, hideOthersToo) {
  9549            var
  9550              $previousModal = $otherModals.filter('.' + className.active).last()
  9551            ;
  9552            callback = $.isFunction(callback)
  9553              ? callback
  9554              : function(){}
  9555            ;
  9556            module.debug('Hiding modal');
  9557            if(settings.onHide.call(element, $(this)) === false) {
  9558              module.verbose('Hide callback returned false cancelling hide');
  9559              ignoreRepeatedEvents = false;
  9560              return false;
  9561            }
  9562  
  9563            if( module.is.animating() || module.is.active() ) {
  9564              if(settings.transition && $.fn.transition !== undefined && $module.transition('is supported')) {
  9565                module.remove.active();
  9566                $module
  9567                  .transition({
  9568                    debug       : settings.debug,
  9569                    animation   : settings.transition + ' out',
  9570                    queue       : settings.queue,
  9571                    duration    : settings.duration,
  9572                    useFailSafe : true,
  9573                    onStart     : function() {
  9574                      if(!module.others.active() && !module.others.animating() && !keepDimmed) {
  9575                        module.hideDimmer();
  9576                      }
  9577                      if( settings.keyboardShortcuts && !module.others.active() ) {
  9578                        module.remove.keyboardShortcuts();
  9579                      }
  9580                    },
  9581                    onComplete : function() {
  9582                      module.unbind.scrollLock();
  9583                      if ( settings.allowMultiple ) {
  9584                        $previousModal.addClass(className.front);
  9585                        $module.removeClass(className.front);
  9586  
  9587                        if ( hideOthersToo ) {
  9588                          $allModals.find(selector.dimmer).removeClass('active');
  9589                        }
  9590                        else {
  9591                          $previousModal.find(selector.dimmer).removeClass('active');
  9592                        }
  9593                      }
  9594                      settings.onHidden.call(element);
  9595                      module.remove.dimmerStyles();
  9596                      module.restore.focus();
  9597                      callback();
  9598                    }
  9599                  })
  9600                ;
  9601              }
  9602              else {
  9603                module.error(error.noTransition);
  9604              }
  9605            }
  9606          },
  9607  
  9608          showDimmer: function() {
  9609            if($dimmable.dimmer('is animating') || !$dimmable.dimmer('is active') ) {
  9610              module.save.bodyMargin();
  9611              module.debug('Showing dimmer');
  9612              $dimmable.dimmer('show');
  9613            }
  9614            else {
  9615              module.debug('Dimmer already visible');
  9616            }
  9617          },
  9618  
  9619          hideDimmer: function() {
  9620            if( $dimmable.dimmer('is animating') || ($dimmable.dimmer('is active')) ) {
  9621              module.unbind.scrollLock();
  9622              $dimmable.dimmer('hide', function() {
  9623                module.restore.bodyMargin();
  9624                module.remove.clickaway();
  9625                module.remove.screenHeight();
  9626              });
  9627            }
  9628            else {
  9629              module.debug('Dimmer is not visible cannot hide');
  9630              return;
  9631            }
  9632          },
  9633  
  9634          hideAll: function(callback) {
  9635            var
  9636              $visibleModals = $allModals.filter('.' + className.active + ', .' + className.animating)
  9637            ;
  9638            callback = $.isFunction(callback)
  9639              ? callback
  9640              : function(){}
  9641            ;
  9642            if( $visibleModals.length > 0 ) {
  9643              module.debug('Hiding all visible modals');
  9644              var hideOk = true;
  9645  //check in reverse order trying to hide most top displayed modal first
  9646              $($visibleModals.get().reverse()).each(function(index,element){
  9647                  if(hideOk){
  9648                      hideOk = $(element).modal('hide modal', callback, false, true);
  9649                  }
  9650              });
  9651              if(hideOk) {
  9652                module.hideDimmer();
  9653              }
  9654              return hideOk;
  9655            }
  9656          },
  9657  
  9658          hideOthers: function(callback) {
  9659            var
  9660              $visibleModals = $otherModals.filter('.' + className.active + ', .' + className.animating)
  9661            ;
  9662            callback = $.isFunction(callback)
  9663              ? callback
  9664              : function(){}
  9665            ;
  9666            if( $visibleModals.length > 0 ) {
  9667              module.debug('Hiding other modals', $otherModals);
  9668              $visibleModals
  9669                .modal('hide modal', callback, true)
  9670              ;
  9671            }
  9672          },
  9673  
  9674          others: {
  9675            active: function() {
  9676              return ($otherModals.filter('.' + className.active).length > 0);
  9677            },
  9678            animating: function() {
  9679              return ($otherModals.filter('.' + className.animating).length > 0);
  9680            }
  9681          },
  9682  
  9683  
  9684          add: {
  9685            keyboardShortcuts: function() {
  9686              module.verbose('Adding keyboard shortcuts');
  9687              $document
  9688                .on('keyup' + eventNamespace, module.event.keyboard)
  9689              ;
  9690            }
  9691          },
  9692  
  9693          save: {
  9694            focus: function() {
  9695              var
  9696                $activeElement = $(document.activeElement),
  9697                inCurrentModal = $activeElement.closest($module).length > 0
  9698              ;
  9699              if(!inCurrentModal) {
  9700                $focusedElement = $(document.activeElement).blur();
  9701              }
  9702            },
  9703            bodyMargin: function() {
  9704              initialBodyMargin = $body.css('margin-'+(module.can.leftBodyScrollbar() ? 'left':'right'));
  9705              var bodyMarginRightPixel = parseInt(initialBodyMargin.replace(/[^\d.]/g, '')),
  9706                  bodyScrollbarWidth = window.innerWidth - document.documentElement.clientWidth;
  9707              tempBodyMargin = bodyMarginRightPixel + bodyScrollbarWidth;
  9708            }
  9709          },
  9710  
  9711          restore: {
  9712            focus: function() {
  9713              if($focusedElement && $focusedElement.length > 0 && settings.restoreFocus) {
  9714                $focusedElement.focus();
  9715              }
  9716            },
  9717            bodyMargin: function() {
  9718              var position = module.can.leftBodyScrollbar() ? 'left':'right';
  9719              $body.css('margin-'+position, initialBodyMargin);
  9720              $body.find(selector.bodyFixed.replace('right',position)).css('padding-'+position, initialBodyMargin);
  9721            }
  9722          },
  9723  
  9724          remove: {
  9725            active: function() {
  9726              $module.removeClass(className.active);
  9727            },
  9728            legacy: function() {
  9729              $module.removeClass(className.legacy);
  9730            },
  9731            clickaway: function() {
  9732              if (!settings.detachable) {
  9733                $module
  9734                    .off('mousedown' + elementEventNamespace)
  9735                ;
  9736              }           
  9737              $dimmer
  9738                .off('mousedown' + elementEventNamespace)
  9739              ;
  9740              $dimmer
  9741                .off('mouseup' + elementEventNamespace)
  9742              ;
  9743            },
  9744            dimmerStyles: function() {
  9745              $dimmer.removeClass(className.inverted);
  9746              $dimmable.removeClass(className.blurring);
  9747            },
  9748            bodyStyle: function() {
  9749              if($body.attr('style') === '') {
  9750                module.verbose('Removing style attribute');
  9751                $body.removeAttr('style');
  9752              }
  9753            },
  9754            screenHeight: function() {
  9755              module.debug('Removing page height');
  9756              $body
  9757                .css('height', '')
  9758              ;
  9759            },
  9760            keyboardShortcuts: function() {
  9761              module.verbose('Removing keyboard shortcuts');
  9762              $document
  9763                .off('keyup' + eventNamespace)
  9764              ;
  9765            },
  9766            scrolling: function() {
  9767              $dimmable.removeClass(className.scrolling);
  9768              $module.removeClass(className.scrolling);
  9769            }
  9770          },
  9771  
  9772          cacheSizes: function() {
  9773            $module.addClass(className.loading);
  9774            var
  9775              scrollHeight = $module.prop('scrollHeight'),
  9776              modalWidth   = $module.outerWidth(),
  9777              modalHeight  = $module.outerHeight()
  9778            ;
  9779            if(module.cache.pageHeight === undefined || modalHeight !== 0) {
  9780              $.extend(module.cache, {
  9781                pageHeight    : $(document).outerHeight(),
  9782                width         : modalWidth,
  9783                height        : modalHeight + settings.offset,
  9784                scrollHeight  : scrollHeight + settings.offset,
  9785                contextHeight : (settings.context == 'body')
  9786                  ? $(window).height()
  9787                  : $dimmable.height(),
  9788              });
  9789              module.cache.topOffset = -(module.cache.height / 2);
  9790            }
  9791            $module.removeClass(className.loading);
  9792            module.debug('Caching modal and container sizes', module.cache);
  9793          },
  9794  
  9795          can: {
  9796            leftBodyScrollbar: function(){
  9797              if(module.cache.leftBodyScrollbar === undefined) {
  9798                module.cache.leftBodyScrollbar = module.is.rtl() && ((module.is.iframe && !module.is.firefox()) || module.is.safari() || module.is.edge() || module.is.ie());
  9799              }
  9800              return module.cache.leftBodyScrollbar;
  9801            },
  9802            useFlex: function() {
  9803              if (settings.useFlex === 'auto') {
  9804                return settings.detachable && !module.is.ie();
  9805              }
  9806              if(settings.useFlex && module.is.ie()) {
  9807                module.debug('useFlex true is not supported in IE');
  9808              } else if(settings.useFlex && !settings.detachable) {
  9809                module.debug('useFlex true in combination with detachable false is not supported');
  9810              }
  9811              return settings.useFlex;
  9812            },
  9813            fit: function() {
  9814              var
  9815                contextHeight  = module.cache.contextHeight,
  9816                verticalCenter = module.cache.contextHeight / 2,
  9817                topOffset      = module.cache.topOffset,
  9818                scrollHeight   = module.cache.scrollHeight,
  9819                height         = module.cache.height,
  9820                paddingHeight  = settings.padding,
  9821                startPosition  = (verticalCenter + topOffset)
  9822              ;
  9823              return (scrollHeight > height)
  9824                ? (startPosition + scrollHeight + paddingHeight < contextHeight)
  9825                : (height + (paddingHeight * 2) < contextHeight)
  9826              ;
  9827            }
  9828          },
  9829  
  9830          is: {
  9831            active: function() {
  9832              return $module.hasClass(className.active);
  9833            },
  9834            ie: function() {
  9835              if(module.cache.isIE === undefined) {
  9836                var
  9837                    isIE11 = (!(window.ActiveXObject) && 'ActiveXObject' in window),
  9838                    isIE = ('ActiveXObject' in window)
  9839                ;
  9840                module.cache.isIE = (isIE11 || isIE);
  9841              }
  9842              return module.cache.isIE;
  9843            },
  9844            animating: function() {
  9845              return $module.transition('is supported')
  9846                ? $module.transition('is animating')
  9847                : $module.is(':visible')
  9848              ;
  9849            },
  9850            scrolling: function() {
  9851              return $dimmable.hasClass(className.scrolling);
  9852            },
  9853            modernBrowser: function() {
  9854              // appName for IE11 reports 'Netscape' can no longer use
  9855              return !(window.ActiveXObject || 'ActiveXObject' in window);
  9856            },
  9857            rtl: function() {
  9858              if(module.cache.isRTL === undefined) {
  9859                module.cache.isRTL = $body.attr('dir') === 'rtl' || $body.css('direction') === 'rtl';
  9860              }
  9861              return module.cache.isRTL;
  9862            },
  9863            safari: function() {
  9864              if(module.cache.isSafari === undefined) {
  9865                module.cache.isSafari = /constructor/i.test(window.HTMLElement) || !!window.ApplePaySession;
  9866              }
  9867              return module.cache.isSafari;
  9868            },
  9869            edge: function(){
  9870              if(module.cache.isEdge === undefined) {
  9871                module.cache.isEdge = !!window.setImmediate && !module.is.ie();
  9872              }
  9873              return module.cache.isEdge;
  9874            },
  9875            firefox: function(){
  9876              if(module.cache.isFirefox === undefined) {
  9877                  module.cache.isFirefox = !!window.InstallTrigger;
  9878              }
  9879              return module.cache.isFirefox;
  9880            },
  9881            iframe: function() {
  9882                return !(self === top);
  9883            }
  9884          },
  9885  
  9886          set: {
  9887            autofocus: function() {
  9888              var
  9889                $inputs    = $module.find('[tabindex], :input').filter(':visible').filter(function() {
  9890                  return $(this).closest('.disabled').length === 0;
  9891                }),
  9892                $autofocus = $inputs.filter('[autofocus]'),
  9893                $input     = ($autofocus.length > 0)
  9894                  ? $autofocus.first()
  9895                  : $inputs.first()
  9896              ;
  9897              if($input.length > 0) {
  9898                $input.focus();
  9899              }
  9900            },
  9901            bodyMargin: function() {
  9902              var position = module.can.leftBodyScrollbar() ? 'left':'right';
  9903              if(settings.detachable || module.can.fit()) {
  9904                $body.css('margin-'+position, tempBodyMargin + 'px');
  9905              }
  9906              $body.find(selector.bodyFixed.replace('right',position)).css('padding-'+position, tempBodyMargin + 'px');
  9907            },
  9908            clickaway: function() {
  9909              if (!settings.detachable) {
  9910                $module
  9911                  .on('mousedown' + elementEventNamespace, module.event.mousedown)
  9912                ;
  9913              }
  9914              $dimmer
  9915                .on('mousedown' + elementEventNamespace, module.event.mousedown)
  9916              ;
  9917              $dimmer
  9918                .on('mouseup' + elementEventNamespace, module.event.mouseup)
  9919              ;
  9920            },
  9921            dimmerSettings: function() {
  9922              if($.fn.dimmer === undefined) {
  9923                module.error(error.dimmer);
  9924                return;
  9925              }
  9926              var
  9927                defaultSettings = {
  9928                  debug      : settings.debug,
  9929                  dimmerName : 'modals',
  9930                  closable   : 'auto',
  9931                  useFlex    : module.can.useFlex(),
  9932                  duration   : {
  9933                    show     : settings.duration,
  9934                    hide     : settings.duration
  9935                  }
  9936                },
  9937                dimmerSettings = $.extend(true, defaultSettings, settings.dimmerSettings)
  9938              ;
  9939              if(settings.inverted) {
  9940                dimmerSettings.variation = (dimmerSettings.variation !== undefined)
  9941                  ? dimmerSettings.variation + ' inverted'
  9942                  : 'inverted'
  9943                ;
  9944              }
  9945              $context.dimmer('setting', dimmerSettings);
  9946            },
  9947            dimmerStyles: function() {
  9948              if(settings.inverted) {
  9949                $dimmer.addClass(className.inverted);
  9950              }
  9951              else {
  9952                $dimmer.removeClass(className.inverted);
  9953              }
  9954              if(settings.blurring) {
  9955                $dimmable.addClass(className.blurring);
  9956              }
  9957              else {
  9958                $dimmable.removeClass(className.blurring);
  9959              }
  9960            },
  9961            modalOffset: function() {
  9962              if (!settings.detachable) {
  9963                var canFit = module.can.fit();
  9964                $module
  9965                  .css({
  9966                    top: (!$module.hasClass('aligned') && canFit)
  9967                      ? $(document).scrollTop() + (module.cache.contextHeight - module.cache.height) / 2
  9968                      : !canFit || $module.hasClass('top')
  9969                        ? $(document).scrollTop() + settings.padding
  9970                        : $(document).scrollTop() + (module.cache.contextHeight - module.cache.height - settings.padding),
  9971                    marginLeft: -(module.cache.width / 2)
  9972                  }) 
  9973                ;
  9974              } else {
  9975                $module
  9976                  .css({
  9977                    marginTop: (!$module.hasClass('aligned') && module.can.fit())
  9978                      ? -(module.cache.height / 2)
  9979                      : settings.padding / 2,
  9980                    marginLeft: -(module.cache.width / 2)
  9981                  }) 
  9982                ;
  9983              }
  9984              module.verbose('Setting modal offset for legacy mode');
  9985            },
  9986            screenHeight: function() {
  9987              if( module.can.fit() ) {
  9988                $body.css('height', '');
  9989              }
  9990              else if(!$module.hasClass('bottom')) {
  9991                module.debug('Modal is taller than page content, resizing page height');
  9992                $body
  9993                  .css('height', module.cache.height + (settings.padding * 2) )
  9994                ;
  9995              }
  9996            },
  9997            active: function() {
  9998              $module.addClass(className.active + ' ' + className.front);
  9999              $otherModals.filter('.' + className.active).removeClass(className.front);
 10000            },
 10001            scrolling: function() {
 10002              $dimmable.addClass(className.scrolling);
 10003              $module.addClass(className.scrolling);
 10004              module.unbind.scrollLock();
 10005            },
 10006            legacy: function() {
 10007              $module.addClass(className.legacy);
 10008            },
 10009            type: function() {
 10010              if(module.can.fit()) {
 10011                module.verbose('Modal fits on screen');
 10012                if(!module.others.active() && !module.others.animating()) {
 10013                  module.remove.scrolling();
 10014                  module.bind.scrollLock();
 10015                }
 10016              }
 10017              else if (!$module.hasClass('bottom')){
 10018                module.verbose('Modal cannot fit on screen setting to scrolling');
 10019                module.set.scrolling();
 10020              } else {
 10021                  module.verbose('Bottom aligned modal not fitting on screen is unsupported for scrolling');
 10022              }
 10023            },
 10024            undetached: function() {
 10025              $dimmable.addClass(className.undetached);
 10026            }
 10027          },
 10028  
 10029          setting: function(name, value) {
 10030            module.debug('Changing setting', name, value);
 10031            if( $.isPlainObject(name) ) {
 10032              $.extend(true, settings, name);
 10033            }
 10034            else if(value !== undefined) {
 10035              if($.isPlainObject(settings[name])) {
 10036                $.extend(true, settings[name], value);
 10037              }
 10038              else {
 10039                settings[name] = value;
 10040              }
 10041            }
 10042            else {
 10043              return settings[name];
 10044            }
 10045          },
 10046          internal: function(name, value) {
 10047            if( $.isPlainObject(name) ) {
 10048              $.extend(true, module, name);
 10049            }
 10050            else if(value !== undefined) {
 10051              module[name] = value;
 10052            }
 10053            else {
 10054              return module[name];
 10055            }
 10056          },
 10057          debug: function() {
 10058            if(!settings.silent && settings.debug) {
 10059              if(settings.performance) {
 10060                module.performance.log(arguments);
 10061              }
 10062              else {
 10063                module.debug = Function.prototype.bind.call(console.info, console, settings.name + ':');
 10064                module.debug.apply(console, arguments);
 10065              }
 10066            }
 10067          },
 10068          verbose: function() {
 10069            if(!settings.silent && settings.verbose && settings.debug) {
 10070              if(settings.performance) {
 10071                module.performance.log(arguments);
 10072              }
 10073              else {
 10074                module.verbose = Function.prototype.bind.call(console.info, console, settings.name + ':');
 10075                module.verbose.apply(console, arguments);
 10076              }
 10077            }
 10078          },
 10079          error: function() {
 10080            if(!settings.silent) {
 10081              module.error = Function.prototype.bind.call(console.error, console, settings.name + ':');
 10082              module.error.apply(console, arguments);
 10083            }
 10084          },
 10085          performance: {
 10086            log: function(message) {
 10087              var
 10088                currentTime,
 10089                executionTime,
 10090                previousTime
 10091              ;
 10092              if(settings.performance) {
 10093                currentTime   = new Date().getTime();
 10094                previousTime  = time || currentTime;
 10095                executionTime = currentTime - previousTime;
 10096                time          = currentTime;
 10097                performance.push({
 10098                  'Name'           : message[0],
 10099                  'Arguments'      : [].slice.call(message, 1) || '',
 10100                  'Element'        : element,
 10101                  'Execution Time' : executionTime
 10102                });
 10103              }
 10104              clearTimeout(module.performance.timer);
 10105              module.performance.timer = setTimeout(module.performance.display, 500);
 10106            },
 10107            display: function() {
 10108              var
 10109                title = settings.name + ':',
 10110                totalTime = 0
 10111              ;
 10112              time = false;
 10113              clearTimeout(module.performance.timer);
 10114              $.each(performance, function(index, data) {
 10115                totalTime += data['Execution Time'];
 10116              });
 10117              title += ' ' + totalTime + 'ms';
 10118              if(moduleSelector) {
 10119                title += ' \'' + moduleSelector + '\'';
 10120              }
 10121              if( (console.group !== undefined || console.table !== undefined) && performance.length > 0) {
 10122                console.groupCollapsed(title);
 10123                if(console.table) {
 10124                  console.table(performance);
 10125                }
 10126                else {
 10127                  $.each(performance, function(index, data) {
 10128                    console.log(data['Name'] + ': ' + data['Execution Time']+'ms');
 10129                  });
 10130                }
 10131                console.groupEnd();
 10132              }
 10133              performance = [];
 10134            }
 10135          },
 10136          invoke: function(query, passedArguments, context) {
 10137            var
 10138              object = instance,
 10139              maxDepth,
 10140              found,
 10141              response
 10142            ;
 10143            passedArguments = passedArguments || queryArguments;
 10144            context         = element         || context;
 10145            if(typeof query == 'string' && object !== undefined) {
 10146              query    = query.split(/[\. ]/);
 10147              maxDepth = query.length - 1;
 10148              $.each(query, function(depth, value) {
 10149                var camelCaseValue = (depth != maxDepth)
 10150                  ? value + query[depth + 1].charAt(0).toUpperCase() + query[depth + 1].slice(1)
 10151                  : query
 10152                ;
 10153                if( $.isPlainObject( object[camelCaseValue] ) && (depth != maxDepth) ) {
 10154                  object = object[camelCaseValue];
 10155                }
 10156                else if( object[camelCaseValue] !== undefined ) {
 10157                  found = object[camelCaseValue];
 10158                  return false;
 10159                }
 10160                else if( $.isPlainObject( object[value] ) && (depth != maxDepth) ) {
 10161                  object = object[value];
 10162                }
 10163                else if( object[value] !== undefined ) {
 10164                  found = object[value];
 10165                  return false;
 10166                }
 10167                else {
 10168                  return false;
 10169                }
 10170              });
 10171            }
 10172            if ( $.isFunction( found ) ) {
 10173              response = found.apply(context, passedArguments);
 10174            }
 10175            else if(found !== undefined) {
 10176              response = found;
 10177            }
 10178            if(Array.isArray(returnedValue)) {
 10179              returnedValue.push(response);
 10180            }
 10181            else if(returnedValue !== undefined) {
 10182              returnedValue = [returnedValue, response];
 10183            }
 10184            else if(response !== undefined) {
 10185              returnedValue = response;
 10186            }
 10187            return found;
 10188          }
 10189        };
 10190  
 10191        if(methodInvoked) {
 10192          if(instance === undefined) {
 10193            module.initialize();
 10194          }
 10195          module.invoke(query);
 10196        }
 10197        else {
 10198          if(instance !== undefined) {
 10199            instance.invoke('destroy');
 10200          }
 10201          module.initialize();
 10202        }
 10203      })
 10204    ;
 10205  
 10206    return (returnedValue !== undefined)
 10207      ? returnedValue
 10208      : this
 10209    ;
 10210  };
 10211  
 10212  $.fn.modal.settings = {
 10213  
 10214    name           : 'Modal',
 10215    namespace      : 'modal',
 10216  
 10217    useFlex        : 'auto',
 10218    offset         : 0,
 10219  
 10220    silent         : false,
 10221    debug          : false,
 10222    verbose        : false,
 10223    performance    : true,
 10224  
 10225    observeChanges : false,
 10226  
 10227    allowMultiple  : false,
 10228    detachable     : true,
 10229    closable       : true,
 10230    autofocus      : true,
 10231    restoreFocus   : true,
 10232  
 10233    inverted       : false,
 10234    blurring       : false,
 10235  
 10236    centered       : true,
 10237  
 10238    dimmerSettings : {
 10239      closable : false,
 10240      useCSS   : true
 10241    },
 10242  
 10243    // whether to use keyboard shortcuts
 10244    keyboardShortcuts: true,
 10245  
 10246    context    : 'body',
 10247  
 10248    queue      : false,
 10249    duration   : 500,
 10250    transition : 'scale',
 10251  
 10252    // padding with edge of page
 10253    padding    : 50,
 10254    scrollbarWidth: 10,
 10255  
 10256    // called before show animation
 10257    onShow     : function(){},
 10258  
 10259    // called after show animation
 10260    onVisible  : function(){},
 10261  
 10262    // called before hide animation
 10263    onHide     : function(){ return true; },
 10264  
 10265    // called after hide animation
 10266    onHidden   : function(){},
 10267  
 10268    // called after approve selector match
 10269    onApprove  : function(){ return true; },
 10270  
 10271    // called after deny selector match
 10272    onDeny     : function(){ return true; },
 10273  
 10274    selector    : {
 10275      close    : '> .close',
 10276      approve  : '.actions .positive, .actions .approve, .actions .ok',
 10277      deny     : '.actions .negative, .actions .deny, .actions .cancel',
 10278      modal    : '.ui.modal',
 10279      dimmer   : '> .ui.dimmer',
 10280      bodyFixed: '> .ui.fixed.menu, > .ui.right.toast-container, > .ui.right.sidebar'
 10281    },
 10282    error : {
 10283      dimmer    : 'UI Dimmer, a required component is not included in this page',
 10284      method    : 'The method you called is not defined.',
 10285      notFound  : 'The element you specified could not be found'
 10286    },
 10287    className : {
 10288      active     : 'active',
 10289      animating  : 'animating',
 10290      blurring   : 'blurring',
 10291      inverted   : 'inverted',
 10292      legacy     : 'legacy',
 10293      loading    : 'loading',
 10294      scrolling  : 'scrolling',
 10295      undetached : 'undetached',
 10296      front      : 'front'
 10297    }
 10298  };
 10299  
 10300  
 10301  })( jQuery, window, document );
 10302  
 10303  /*!
 10304   * # Fomantic-UI - Search
 10305   * http://github.com/fomantic/Fomantic-UI/
 10306   *
 10307   *
 10308   * Released under the MIT license
 10309   * http://opensource.org/licenses/MIT
 10310   *
 10311   */
 10312  
 10313  ;(function ($, window, document, undefined) {
 10314  
 10315  'use strict';
 10316  
 10317  $.isFunction = $.isFunction || function(obj) {
 10318    return typeof obj === "function" && typeof obj.nodeType !== "number";
 10319  };
 10320  
 10321  window = (typeof window != 'undefined' && window.Math == Math)
 10322    ? window
 10323    : (typeof self != 'undefined' && self.Math == Math)
 10324      ? self
 10325      : Function('return this')()
 10326  ;
 10327  
 10328  $.fn.search = function(parameters) {
 10329    var
 10330      $allModules     = $(this),
 10331      moduleSelector  = $allModules.selector || '',
 10332  
 10333      time            = new Date().getTime(),
 10334      performance     = [],
 10335  
 10336      query           = arguments[0],
 10337      methodInvoked   = (typeof query == 'string'),
 10338      queryArguments  = [].slice.call(arguments, 1),
 10339      returnedValue
 10340    ;
 10341    $(this)
 10342      .each(function() {
 10343        var
 10344          settings          = ( $.isPlainObject(parameters) )
 10345            ? $.extend(true, {}, $.fn.search.settings, parameters)
 10346            : $.extend({}, $.fn.search.settings),
 10347  
 10348          className        = settings.className,
 10349          metadata         = settings.metadata,
 10350          regExp           = settings.regExp,
 10351          fields           = settings.fields,
 10352          selector         = settings.selector,
 10353          error            = settings.error,
 10354          namespace        = settings.namespace,
 10355  
 10356          eventNamespace   = '.' + namespace,
 10357          moduleNamespace  = namespace + '-module',
 10358  
 10359          $module          = $(this),
 10360          $prompt          = $module.find(selector.prompt),
 10361          $searchButton    = $module.find(selector.searchButton),
 10362          $results         = $module.find(selector.results),
 10363          $result          = $module.find(selector.result),
 10364          $category        = $module.find(selector.category),
 10365  
 10366          element          = this,
 10367          instance         = $module.data(moduleNamespace),
 10368  
 10369          disabledBubbled  = false,
 10370          resultsDismissed = false,
 10371  
 10372          module
 10373        ;
 10374  
 10375        module = {
 10376  
 10377          initialize: function() {
 10378            module.verbose('Initializing module');
 10379            module.get.settings();
 10380            module.determine.searchFields();
 10381            module.bind.events();
 10382            module.set.type();
 10383            module.create.results();
 10384            module.instantiate();
 10385          },
 10386          instantiate: function() {
 10387            module.verbose('Storing instance of module', module);
 10388            instance = module;
 10389            $module
 10390              .data(moduleNamespace, module)
 10391            ;
 10392          },
 10393          destroy: function() {
 10394            module.verbose('Destroying instance');
 10395            $module
 10396              .off(eventNamespace)
 10397              .removeData(moduleNamespace)
 10398            ;
 10399          },
 10400  
 10401          refresh: function() {
 10402            module.debug('Refreshing selector cache');
 10403            $prompt         = $module.find(selector.prompt);
 10404            $searchButton   = $module.find(selector.searchButton);
 10405            $category       = $module.find(selector.category);
 10406            $results        = $module.find(selector.results);
 10407            $result         = $module.find(selector.result);
 10408          },
 10409  
 10410          refreshResults: function() {
 10411            $results = $module.find(selector.results);
 10412            $result  = $module.find(selector.result);
 10413          },
 10414  
 10415          bind: {
 10416            events: function() {
 10417              module.verbose('Binding events to search');
 10418              if(settings.automatic) {
 10419                $module
 10420                  .on(module.get.inputEvent() + eventNamespace, selector.prompt, module.event.input)
 10421                ;
 10422                $prompt
 10423                  .attr('autocomplete', 'off')
 10424                ;
 10425              }
 10426              $module
 10427                // prompt
 10428                .on('focus'     + eventNamespace, selector.prompt, module.event.focus)
 10429                .on('blur'      + eventNamespace, selector.prompt, module.event.blur)
 10430                .on('keydown'   + eventNamespace, selector.prompt, module.handleKeyboard)
 10431                // search button
 10432                .on('click'     + eventNamespace, selector.searchButton, module.query)
 10433                // results
 10434                .on('mousedown' + eventNamespace, selector.results, module.event.result.mousedown)
 10435                .on('mouseup'   + eventNamespace, selector.results, module.event.result.mouseup)
 10436                .on('click'     + eventNamespace, selector.result,  module.event.result.click)
 10437              ;
 10438            }
 10439          },
 10440  
 10441          determine: {
 10442            searchFields: function() {
 10443              // this makes sure $.extend does not add specified search fields to default fields
 10444              // this is the only setting which should not extend defaults
 10445              if(parameters && parameters.searchFields !== undefined) {
 10446                settings.searchFields = parameters.searchFields;
 10447              }
 10448            }
 10449          },
 10450  
 10451          event: {
 10452            input: function() {
 10453              if(settings.searchDelay) {
 10454                clearTimeout(module.timer);
 10455                module.timer = setTimeout(function() {
 10456                  if(module.is.focused()) {
 10457                    module.query();
 10458                  }
 10459                }, settings.searchDelay);
 10460              }
 10461              else {
 10462                module.query();
 10463              }
 10464            },
 10465            focus: function() {
 10466              module.set.focus();
 10467              if(settings.searchOnFocus && module.has.minimumCharacters() ) {
 10468                module.query(function() {
 10469                  if(module.can.show() ) {
 10470                    module.showResults();
 10471                  }
 10472                });
 10473              }
 10474            },
 10475            blur: function(event) {
 10476              var
 10477                pageLostFocus = (document.activeElement === this),
 10478                callback      = function() {
 10479                  module.cancel.query();
 10480                  module.remove.focus();
 10481                  module.timer = setTimeout(module.hideResults, settings.hideDelay);
 10482                }
 10483              ;
 10484              if(pageLostFocus) {
 10485                return;
 10486              }
 10487              resultsDismissed = false;
 10488              if(module.resultsClicked) {
 10489                module.debug('Determining if user action caused search to close');
 10490                $module
 10491                  .one('click.close' + eventNamespace, selector.results, function(event) {
 10492                    if(module.is.inMessage(event) || disabledBubbled) {
 10493                      $prompt.focus();
 10494                      return;
 10495                    }
 10496                    disabledBubbled = false;
 10497                    if( !module.is.animating() && !module.is.hidden()) {
 10498                      callback();
 10499                    }
 10500                  })
 10501                ;
 10502              }
 10503              else {
 10504                module.debug('Input blurred without user action, closing results');
 10505                callback();
 10506              }
 10507            },
 10508            result: {
 10509              mousedown: function() {
 10510                module.resultsClicked = true;
 10511              },
 10512              mouseup: function() {
 10513                module.resultsClicked = false;
 10514              },
 10515              click: function(event) {
 10516                module.debug('Search result selected');
 10517                var
 10518                  $result = $(this),
 10519                  $title  = $result.find(selector.title).eq(0),
 10520                  $link   = $result.is('a[href]')
 10521                    ? $result
 10522                    : $result.find('a[href]').eq(0),
 10523                  href    = $link.attr('href')   || false,
 10524                  target  = $link.attr('target') || false,
 10525                  // title is used for result lookup
 10526                  value   = ($title.length > 0)
 10527                    ? $title.text()
 10528                    : false,
 10529                  results = module.get.results(),
 10530                  result  = $result.data(metadata.result) || module.get.result(value, results)
 10531                ;
 10532                if(value) {
 10533                  module.set.value(value);
 10534                }
 10535                if( $.isFunction(settings.onSelect) ) {
 10536                  if(settings.onSelect.call(element, result, results) === false) {
 10537                    module.debug('Custom onSelect callback cancelled default select action');
 10538                    disabledBubbled = true;
 10539                    return;
 10540                  }
 10541                }
 10542                module.hideResults();
 10543                if(href) {
 10544                  event.preventDefault();
 10545                  module.verbose('Opening search link found in result', $link);
 10546                  if(target == '_blank' || event.ctrlKey) {
 10547                    window.open(href);
 10548                  }
 10549                  else {
 10550                    window.location.href = (href);
 10551                  }
 10552                }
 10553              }
 10554            }
 10555          },
 10556          ensureVisible: function ensureVisible($el) {
 10557            var elTop, elBottom, resultsScrollTop, resultsHeight;
 10558  
 10559            elTop = $el.position().top;
 10560            elBottom = elTop + $el.outerHeight(true);
 10561  
 10562            resultsScrollTop = $results.scrollTop();
 10563            resultsHeight = $results.height()
 10564              parseInt($results.css('paddingTop'), 0) +
 10565              parseInt($results.css('paddingBottom'), 0);
 10566              
 10567            if (elTop < 0) {
 10568              $results.scrollTop(resultsScrollTop + elTop);
 10569            }
 10570  
 10571            else if (resultsHeight < elBottom) {
 10572              $results.scrollTop(resultsScrollTop + (elBottom - resultsHeight));
 10573            }
 10574          },
 10575          handleKeyboard: function(event) {
 10576            var
 10577              // force selector refresh
 10578              $result         = $module.find(selector.result),
 10579              $category       = $module.find(selector.category),
 10580              $activeResult   = $result.filter('.' + className.active),
 10581              currentIndex    = $result.index( $activeResult ),
 10582              resultSize      = $result.length,
 10583              hasActiveResult = $activeResult.length > 0,
 10584  
 10585              keyCode         = event.which,
 10586              keys            = {
 10587                backspace : 8,
 10588                enter     : 13,
 10589                escape    : 27,
 10590                upArrow   : 38,
 10591                downArrow : 40
 10592              },
 10593              newIndex
 10594            ;
 10595            // search shortcuts
 10596            if(keyCode == keys.escape) {
 10597              module.verbose('Escape key pressed, blurring search field');
 10598              module.hideResults();
 10599              resultsDismissed = true;
 10600            }
 10601            if( module.is.visible() ) {
 10602              if(keyCode == keys.enter) {
 10603                module.verbose('Enter key pressed, selecting active result');
 10604                if( $result.filter('.' + className.active).length > 0 ) {
 10605                  module.event.result.click.call($result.filter('.' + className.active), event);
 10606                  event.preventDefault();
 10607                  return false;
 10608                }
 10609              }
 10610              else if(keyCode == keys.upArrow && hasActiveResult) {
 10611                module.verbose('Up key pressed, changing active result');
 10612                newIndex = (currentIndex - 1 < 0)
 10613                  ? currentIndex
 10614                  : currentIndex - 1
 10615                ;
 10616                $category
 10617                  .removeClass(className.active)
 10618                ;
 10619                $result
 10620                  .removeClass(className.active)
 10621                  .eq(newIndex)
 10622                    .addClass(className.active)
 10623                    .closest($category)
 10624                      .addClass(className.active)
 10625                ;
 10626                module.ensureVisible($result.eq(newIndex));
 10627                event.preventDefault();
 10628              }
 10629              else if(keyCode == keys.downArrow) {
 10630                module.verbose('Down key pressed, changing active result');
 10631                newIndex = (currentIndex + 1 >= resultSize)
 10632                  ? currentIndex
 10633                  : currentIndex + 1
 10634                ;
 10635                $category
 10636                  .removeClass(className.active)
 10637                ;
 10638                $result
 10639                  .removeClass(className.active)
 10640                  .eq(newIndex)
 10641                    .addClass(className.active)
 10642                    .closest($category)
 10643                      .addClass(className.active)
 10644                ;
 10645                module.ensureVisible($result.eq(newIndex));
 10646                event.preventDefault();
 10647              }
 10648            }
 10649            else {
 10650              // query shortcuts
 10651              if(keyCode == keys.enter) {
 10652                module.verbose('Enter key pressed, executing query');
 10653                module.query();
 10654                module.set.buttonPressed();
 10655                $prompt.one('keyup', module.remove.buttonFocus);
 10656              }
 10657            }
 10658          },
 10659  
 10660          setup: {
 10661            api: function(searchTerm, callback) {
 10662              var
 10663                apiSettings = {
 10664                  debug             : settings.debug,
 10665                  on                : false,
 10666                  cache             : settings.cache,
 10667                  action            : 'search',
 10668                  urlData           : {
 10669                    query : searchTerm
 10670                  },
 10671                  onSuccess         : function(response) {
 10672                    module.parse.response.call(element, response, searchTerm);
 10673                    callback();
 10674                  },
 10675                  onFailure         : function() {
 10676                    module.displayMessage(error.serverError);
 10677                    callback();
 10678                  },
 10679                  onAbort : function(response) {
 10680                  },
 10681                  onError           : module.error
 10682                }
 10683              ;
 10684              $.extend(true, apiSettings, settings.apiSettings);
 10685              module.verbose('Setting up API request', apiSettings);
 10686              $module.api(apiSettings);
 10687            }
 10688          },
 10689  
 10690          can: {
 10691            useAPI: function() {
 10692              return $.fn.api !== undefined;
 10693            },
 10694            show: function() {
 10695              return module.is.focused() && !module.is.visible() && !module.is.empty();
 10696            },
 10697            transition: function() {
 10698              return settings.transition && $.fn.transition !== undefined && $module.transition('is supported');
 10699            }
 10700          },
 10701  
 10702          is: {
 10703            animating: function() {
 10704              return $results.hasClass(className.animating);
 10705            },
 10706            hidden: function() {
 10707              return $results.hasClass(className.hidden);
 10708            },
 10709            inMessage: function(event) {
 10710              if(!event.target) {
 10711                return;
 10712              }
 10713              var
 10714                $target = $(event.target),
 10715                isInDOM = $.contains(document.documentElement, event.target)
 10716              ;
 10717              return (isInDOM && $target.closest(selector.message).length > 0);
 10718            },
 10719            empty: function() {
 10720              return ($results.html() === '');
 10721            },
 10722            visible: function() {
 10723              return ($results.filter(':visible').length > 0);
 10724            },
 10725            focused: function() {
 10726              return ($prompt.filter(':focus').length > 0);
 10727            }
 10728          },
 10729  
 10730          get: {
 10731            settings: function() {
 10732              if($.isPlainObject(parameters) && parameters.searchFullText) {
 10733                settings.fullTextSearch = parameters.searchFullText;
 10734                module.error(settings.error.oldSearchSyntax, element);
 10735              }
 10736              if (settings.ignoreDiacritics && !String.prototype.normalize) {
 10737                settings.ignoreDiacritics = false;
 10738                module.error(error.noNormalize, element);
 10739              }
 10740            },
 10741            inputEvent: function() {
 10742              var
 10743                prompt = $prompt[0],
 10744                inputEvent   = (prompt !== undefined && prompt.oninput !== undefined)
 10745                  ? 'input'
 10746                  : (prompt !== undefined && prompt.onpropertychange !== undefined)
 10747                    ? 'propertychange'
 10748                    : 'keyup'
 10749              ;
 10750              return inputEvent;
 10751            },
 10752            value: function() {
 10753              return $prompt.val();
 10754            },
 10755            results: function() {
 10756              var
 10757                results = $module.data(metadata.results)
 10758              ;
 10759              return results;
 10760            },
 10761            result: function(value, results) {
 10762              var
 10763                result       = false
 10764              ;
 10765              value = (value !== undefined)
 10766                ? value
 10767                : module.get.value()
 10768              ;
 10769              results = (results !== undefined)
 10770                ? results
 10771                : module.get.results()
 10772              ;
 10773              if(settings.type === 'category') {
 10774                module.debug('Finding result that matches', value);
 10775                $.each(results, function(index, category) {
 10776                  if(Array.isArray(category.results)) {
 10777                    result = module.search.object(value, category.results)[0];
 10778                    // don't continue searching if a result is found
 10779                    if(result) {
 10780                      return false;
 10781                    }
 10782                  }
 10783                });
 10784              }
 10785              else {
 10786                module.debug('Finding result in results object', value);
 10787                result = module.search.object(value, results)[0];
 10788              }
 10789              return result || false;
 10790            },
 10791          },
 10792  
 10793          select: {
 10794            firstResult: function() {
 10795              module.verbose('Selecting first result');
 10796              $result.first().addClass(className.active);
 10797            }
 10798          },
 10799  
 10800          set: {
 10801            focus: function() {
 10802              $module.addClass(className.focus);
 10803            },
 10804            loading: function() {
 10805              $module.addClass(className.loading);
 10806            },
 10807            value: function(value) {
 10808              module.verbose('Setting search input value', value);
 10809              $prompt
 10810                .val(value)
 10811              ;
 10812            },
 10813            type: function(type) {
 10814              type = type || settings.type;
 10815              if(settings.type == 'category') {
 10816                $module.addClass(settings.type);
 10817              }
 10818            },
 10819            buttonPressed: function() {
 10820              $searchButton.addClass(className.pressed);
 10821            }
 10822          },
 10823  
 10824          remove: {
 10825            loading: function() {
 10826              $module.removeClass(className.loading);
 10827            },
 10828            focus: function() {
 10829              $module.removeClass(className.focus);
 10830            },
 10831            buttonPressed: function() {
 10832              $searchButton.removeClass(className.pressed);
 10833            },
 10834            diacritics: function(text) {
 10835              return settings.ignoreDiacritics ?  text.normalize('NFD').replace(/[\u0300-\u036f]/g, '') : text;
 10836            }
 10837          },
 10838  
 10839          query: function(callback) {
 10840            callback = $.isFunction(callback)
 10841              ? callback
 10842              : function(){}
 10843            ;
 10844            var
 10845              searchTerm = module.get.value(),
 10846              cache = module.read.cache(searchTerm)
 10847            ;
 10848            callback = callback || function() {};
 10849            if( module.has.minimumCharacters() )  {
 10850              if(cache) {
 10851                module.debug('Reading result from cache', searchTerm);
 10852                module.save.results(cache.results);
 10853                module.addResults(cache.html);
 10854                module.inject.id(cache.results);
 10855                callback();
 10856              }
 10857              else {
 10858                module.debug('Querying for', searchTerm);
 10859                if($.isPlainObject(settings.source) || Array.isArray(settings.source)) {
 10860                  module.search.local(searchTerm);
 10861                  callback();
 10862                }
 10863                else if( module.can.useAPI() ) {
 10864                  module.search.remote(searchTerm, callback);
 10865                }
 10866                else {
 10867                  module.error(error.source);
 10868                  callback();
 10869                }
 10870              }
 10871              settings.onSearchQuery.call(element, searchTerm);
 10872            }
 10873            else {
 10874              module.hideResults();
 10875            }
 10876          },
 10877  
 10878          search: {
 10879            local: function(searchTerm) {
 10880              var
 10881                results = module.search.object(searchTerm, settings.source),
 10882                searchHTML
 10883              ;
 10884              module.set.loading();
 10885              module.save.results(results);
 10886              module.debug('Returned full local search results', results);
 10887              if(settings.maxResults > 0) {
 10888                module.debug('Using specified max results', results);
 10889                results = results.slice(0, settings.maxResults);
 10890              }
 10891              if(settings.type == 'category') {
 10892                results = module.create.categoryResults(results);
 10893              }
 10894              searchHTML = module.generateResults({
 10895                results: results
 10896              });
 10897              module.remove.loading();
 10898              module.addResults(searchHTML);
 10899              module.inject.id(results);
 10900              module.write.cache(searchTerm, {
 10901                html    : searchHTML,
 10902                results : results
 10903              });
 10904            },
 10905            remote: function(searchTerm, callback) {
 10906              callback = $.isFunction(callback)
 10907                ? callback
 10908                : function(){}
 10909              ;
 10910              if($module.api('is loading')) {
 10911                $module.api('abort');
 10912              }
 10913              module.setup.api(searchTerm, callback);
 10914              $module
 10915                .api('query')
 10916              ;
 10917            },
 10918            object: function(searchTerm, source, searchFields) {
 10919              searchTerm = module.remove.diacritics(String(searchTerm));
 10920              var
 10921                results      = [],
 10922                exactResults = [],
 10923                fuzzyResults = [],
 10924                searchExp    = searchTerm.replace(regExp.escape, '\\$&'),
 10925                matchRegExp  = new RegExp(regExp.beginsWith + searchExp, 'i'),
 10926  
 10927                // avoid duplicates when pushing results
 10928                addResult = function(array, result) {
 10929                  var
 10930                    notResult      = ($.inArray(result, results) == -1),
 10931                    notFuzzyResult = ($.inArray(result, fuzzyResults) == -1),
 10932                    notExactResults = ($.inArray(result, exactResults) == -1)
 10933                  ;
 10934                  if(notResult && notFuzzyResult && notExactResults) {
 10935                    array.push(result);
 10936                  }
 10937                }
 10938              ;
 10939              source = source || settings.source;
 10940              searchFields = (searchFields !== undefined)
 10941                ? searchFields
 10942                : settings.searchFields
 10943              ;
 10944  
 10945              // search fields should be array to loop correctly
 10946              if(!Array.isArray(searchFields)) {
 10947                searchFields = [searchFields];
 10948              }
 10949  
 10950              // exit conditions if no source
 10951              if(source === undefined || source === false) {
 10952                module.error(error.source);
 10953                return [];
 10954              }
 10955              // iterate through search fields looking for matches
 10956              $.each(searchFields, function(index, field) {
 10957                $.each(source, function(label, content) {
 10958                  var
 10959                    fieldExists = (typeof content[field] == 'string') || (typeof content[field] == 'number')
 10960                  ;
 10961                  if(fieldExists) {
 10962                    var text;
 10963                    if (typeof content[field] === 'string'){  
 10964                        text = module.remove.diacritics(content[field]);
 10965                    } else {
 10966                        text = content[field].toString(); 
 10967                    }
 10968                    if( text.search(matchRegExp) !== -1) {
 10969                      // content starts with value (first in results)
 10970                      addResult(results, content);
 10971                    }
 10972                    else if(settings.fullTextSearch === 'exact' && module.exactSearch(searchTerm, text) ) {
 10973                      // content fuzzy matches (last in results)
 10974                      addResult(exactResults, content);
 10975                    }
 10976                    else if(settings.fullTextSearch == true && module.fuzzySearch(searchTerm, text) ) {
 10977                      // content fuzzy matches (last in results)
 10978                      addResult(fuzzyResults, content);
 10979                    }
 10980                  }
 10981                });
 10982              });
 10983              $.merge(exactResults, fuzzyResults);
 10984              $.merge(results, exactResults);
 10985              return results;
 10986            }
 10987          },
 10988          exactSearch: function (query, term) {
 10989            query = query.toLowerCase();
 10990            term  = term.toLowerCase();
 10991            return term.indexOf(query) > -1;
 10992          },
 10993          fuzzySearch: function(query, term) {
 10994            var
 10995              termLength  = term.length,
 10996              queryLength = query.length
 10997            ;
 10998            if(typeof query !== 'string') {
 10999              return false;
 11000            }
 11001            query = query.toLowerCase();
 11002            term  = term.toLowerCase();
 11003            if(queryLength > termLength) {
 11004              return false;
 11005            }
 11006            if(queryLength === termLength) {
 11007              return (query === term);
 11008            }
 11009            search: for (var characterIndex = 0, nextCharacterIndex = 0; characterIndex < queryLength; characterIndex++) {
 11010              var
 11011                queryCharacter = query.charCodeAt(characterIndex)
 11012              ;
 11013              while(nextCharacterIndex < termLength) {
 11014                if(term.charCodeAt(nextCharacterIndex++) === queryCharacter) {
 11015                  continue search;
 11016                }
 11017              }
 11018              return false;
 11019            }
 11020            return true;
 11021          },
 11022  
 11023          parse: {
 11024            response: function(response, searchTerm) {
 11025              if(Array.isArray(response)){
 11026                  var o={};
 11027                  o[fields.results]=response;
 11028                  response = o;
 11029              }
 11030              var
 11031                searchHTML = module.generateResults(response)
 11032              ;
 11033              module.verbose('Parsing server response', response);
 11034              if(response !== undefined) {
 11035                if(searchTerm !== undefined && response[fields.results] !== undefined) {
 11036                  module.addResults(searchHTML);
 11037                  module.inject.id(response[fields.results]);
 11038                  module.write.cache(searchTerm, {
 11039                    html    : searchHTML,
 11040                    results : response[fields.results]
 11041                  });
 11042                  module.save.results(response[fields.results]);
 11043                }
 11044              }
 11045            }
 11046          },
 11047  
 11048          cancel: {
 11049            query: function() {
 11050              if( module.can.useAPI() ) {
 11051                $module.api('abort');
 11052              }
 11053            }
 11054          },
 11055  
 11056          has: {
 11057            minimumCharacters: function() {
 11058              var
 11059                searchTerm    = module.get.value(),
 11060                numCharacters = searchTerm.length
 11061              ;
 11062              return (numCharacters >= settings.minCharacters);
 11063            },
 11064            results: function() {
 11065              if($results.length === 0) {
 11066                return false;
 11067              }
 11068              var
 11069                html = $results.html()
 11070              ;
 11071              return html != '';
 11072            }
 11073          },
 11074  
 11075          clear: {
 11076            cache: function(value) {
 11077              var
 11078                cache = $module.data(metadata.cache)
 11079              ;
 11080              if(!value) {
 11081                module.debug('Clearing cache', value);
 11082                $module.removeData(metadata.cache);
 11083              }
 11084              else if(value && cache && cache[value]) {
 11085                module.debug('Removing value from cache', value);
 11086                delete cache[value];
 11087                $module.data(metadata.cache, cache);
 11088              }
 11089            }
 11090          },
 11091  
 11092          read: {
 11093            cache: function(name) {
 11094              var
 11095                cache = $module.data(metadata.cache)
 11096              ;
 11097              if(settings.cache) {
 11098                module.verbose('Checking cache for generated html for query', name);
 11099                return (typeof cache == 'object') && (cache[name] !== undefined)
 11100                  ? cache[name]
 11101                  : false
 11102                ;
 11103              }
 11104              return false;
 11105            }
 11106          },
 11107  
 11108          create: {
 11109            categoryResults: function(results) {
 11110              var
 11111                categoryResults = {}
 11112              ;
 11113              $.each(results, function(index, result) {
 11114                if(!result.category) {
 11115                  return;
 11116                }
 11117                if(categoryResults[result.category] === undefined) {
 11118                  module.verbose('Creating new category of results', result.category);
 11119                  categoryResults[result.category] = {
 11120                    name    : result.category,
 11121                    results : [result]
 11122                  };
 11123                }
 11124                else {
 11125                  categoryResults[result.category].results.push(result);
 11126                }
 11127              });
 11128              return categoryResults;
 11129            },
 11130            id: function(resultIndex, categoryIndex) {
 11131              var
 11132                resultID      = (resultIndex + 1), // not zero indexed
 11133                letterID,
 11134                id
 11135              ;
 11136              if(categoryIndex !== undefined) {
 11137                // start char code for "A"
 11138                letterID = String.fromCharCode(97 + categoryIndex);
 11139                id          = letterID + resultID;
 11140                module.verbose('Creating category result id', id);
 11141              }
 11142              else {
 11143                id = resultID;
 11144                module.verbose('Creating result id', id);
 11145              }
 11146              return id;
 11147            },
 11148            results: function() {
 11149              if($results.length === 0) {
 11150                $results = $('<div />')
 11151                  .addClass(className.results)
 11152                  .appendTo($module)
 11153                ;
 11154              }
 11155            }
 11156          },
 11157  
 11158          inject: {
 11159            result: function(result, resultIndex, categoryIndex) {
 11160              module.verbose('Injecting result into results');
 11161              var
 11162                $selectedResult = (categoryIndex !== undefined)
 11163                  ? $results
 11164                      .children().eq(categoryIndex)
 11165                        .children(selector.results)
 11166                          .first()
 11167                          .children(selector.result)
 11168                            .eq(resultIndex)
 11169                  : $results
 11170                      .children(selector.result).eq(resultIndex)
 11171              ;
 11172              module.verbose('Injecting results metadata', $selectedResult);
 11173              $selectedResult
 11174                .data(metadata.result, result)
 11175              ;
 11176            },
 11177            id: function(results) {
 11178              module.debug('Injecting unique ids into results');
 11179              var
 11180                // since results may be object, we must use counters
 11181                categoryIndex = 0,
 11182                resultIndex   = 0
 11183              ;
 11184              if(settings.type === 'category') {
 11185                // iterate through each category result
 11186                $.each(results, function(index, category) {
 11187                  if(category.results.length > 0){
 11188                    resultIndex = 0;
 11189                    $.each(category.results, function(index, result) {
 11190                      if(result.id === undefined) {
 11191                        result.id = module.create.id(resultIndex, categoryIndex);
 11192                      }
 11193                      module.inject.result(result, resultIndex, categoryIndex);
 11194                      resultIndex++;
 11195                    });
 11196                    categoryIndex++;
 11197                  }
 11198                });
 11199              }
 11200              else {
 11201                // top level
 11202                $.each(results, function(index, result) {
 11203                  if(result.id === undefined) {
 11204                    result.id = module.create.id(resultIndex);
 11205                  }
 11206                  module.inject.result(result, resultIndex);
 11207                  resultIndex++;
 11208                });
 11209              }
 11210              return results;
 11211            }
 11212          },
 11213  
 11214          save: {
 11215            results: function(results) {
 11216              module.verbose('Saving current search results to metadata', results);
 11217              $module.data(metadata.results, results);
 11218            }
 11219          },
 11220  
 11221          write: {
 11222            cache: function(name, value) {
 11223              var
 11224                cache = ($module.data(metadata.cache) !== undefined)
 11225                  ? $module.data(metadata.cache)
 11226                  : {}
 11227              ;
 11228              if(settings.cache) {
 11229                module.verbose('Writing generated html to cache', name, value);
 11230                cache[name] = value;
 11231                $module
 11232                  .data(metadata.cache, cache)
 11233                ;
 11234              }
 11235            }
 11236          },
 11237  
 11238          addResults: function(html) {
 11239            if( $.isFunction(settings.onResultsAdd) ) {
 11240              if( settings.onResultsAdd.call($results, html) === false ) {
 11241                module.debug('onResultsAdd callback cancelled default action');
 11242                return false;
 11243              }
 11244            }
 11245            if(html) {
 11246              $results
 11247                .html(html)
 11248              ;
 11249              module.refreshResults();
 11250              if(settings.selectFirstResult) {
 11251                module.select.firstResult();
 11252              }
 11253              module.showResults();
 11254            }
 11255            else {
 11256              module.hideResults(function() {
 11257                $results.empty();
 11258              });
 11259            }
 11260          },
 11261  
 11262          showResults: function(callback) {
 11263            callback = $.isFunction(callback)
 11264              ? callback
 11265              : function(){}
 11266            ;
 11267            if(resultsDismissed) {
 11268              return;
 11269            }
 11270            if(!module.is.visible() && module.has.results()) {
 11271              if( module.can.transition() ) {
 11272                module.debug('Showing results with css animations');
 11273                $results
 11274                  .transition({
 11275                    animation  : settings.transition + ' in',
 11276                    debug      : settings.debug,
 11277                    verbose    : settings.verbose,
 11278                    duration   : settings.duration,
 11279                    onShow     : function() {
 11280                      var $firstResult = $module.find(selector.result).eq(0);
 11281                      if($firstResult.length > 0) {
 11282                        module.ensureVisible($firstResult);
 11283                      }
 11284                    },
 11285                    onComplete : function() {
 11286                      callback();
 11287                    },
 11288                    queue      : true
 11289                  })
 11290                ;
 11291              }
 11292              else {
 11293                module.debug('Showing results with javascript');
 11294                $results
 11295                  .stop()
 11296                  .fadeIn(settings.duration, settings.easing)
 11297                ;
 11298              }
 11299              settings.onResultsOpen.call($results);
 11300            }
 11301          },
 11302          hideResults: function(callback) {
 11303            callback = $.isFunction(callback)
 11304              ? callback
 11305              : function(){}
 11306            ;
 11307            if( module.is.visible() ) {
 11308              if( module.can.transition() ) {
 11309                module.debug('Hiding results with css animations');
 11310                $results
 11311                  .transition({
 11312                    animation  : settings.transition + ' out',
 11313                    debug      : settings.debug,
 11314                    verbose    : settings.verbose,
 11315                    duration   : settings.duration,
 11316                    onComplete : function() {
 11317                      callback();
 11318                    },
 11319                    queue      : true
 11320                  })
 11321                ;
 11322              }
 11323              else {
 11324                module.debug('Hiding results with javascript');
 11325                $results
 11326                  .stop()
 11327                  .fadeOut(settings.duration, settings.easing)
 11328                ;
 11329              }
 11330              settings.onResultsClose.call($results);
 11331            }
 11332          },
 11333  
 11334          generateResults: function(response) {
 11335            module.debug('Generating html from response', response);
 11336            var
 11337              template       = settings.templates[settings.type],
 11338              isProperObject = ($.isPlainObject(response[fields.results]) && !$.isEmptyObject(response[fields.results])),
 11339              isProperArray  = (Array.isArray(response[fields.results]) && response[fields.results].length > 0),
 11340              html           = ''
 11341            ;
 11342            if(isProperObject || isProperArray ) {
 11343              if(settings.maxResults > 0) {
 11344                if(isProperObject) {
 11345                  if(settings.type == 'standard') {
 11346                    module.error(error.maxResults);
 11347                  }
 11348                }
 11349                else {
 11350                  response[fields.results] = response[fields.results].slice(0, settings.maxResults);
 11351                }
 11352              }
 11353              if($.isFunction(template)) {
 11354                html = template(response, fields, settings.preserveHTML);
 11355              }
 11356              else {
 11357                module.error(error.noTemplate, false);
 11358              }
 11359            }
 11360            else if(settings.showNoResults) {
 11361              html = module.displayMessage(error.noResults, 'empty', error.noResultsHeader);
 11362            }
 11363            settings.onResults.call(element, response);
 11364            return html;
 11365          },
 11366  
 11367          displayMessage: function(text, type, header) {
 11368            type = type || 'standard';
 11369            module.debug('Displaying message', text, type, header);
 11370            module.addResults( settings.templates.message(text, type, header) );
 11371            return settings.templates.message(text, type, header);
 11372          },
 11373  
 11374          setting: function(name, value) {
 11375            if( $.isPlainObject(name) ) {
 11376              $.extend(true, settings, name);
 11377            }
 11378            else if(value !== undefined) {
 11379              settings[name] = value;
 11380            }
 11381            else {
 11382              return settings[name];
 11383            }
 11384          },
 11385          internal: function(name, value) {
 11386            if( $.isPlainObject(name) ) {
 11387              $.extend(true, module, name);
 11388            }
 11389            else if(value !== undefined) {
 11390              module[name] = value;
 11391            }
 11392            else {
 11393              return module[name];
 11394            }
 11395          },
 11396          debug: function() {
 11397            if(!settings.silent && settings.debug) {
 11398              if(settings.performance) {
 11399                module.performance.log(arguments);
 11400              }
 11401              else {
 11402                module.debug = Function.prototype.bind.call(console.info, console, settings.name + ':');
 11403                module.debug.apply(console, arguments);
 11404              }
 11405            }
 11406          },
 11407          verbose: function() {
 11408            if(!settings.silent && settings.verbose && settings.debug) {
 11409              if(settings.performance) {
 11410                module.performance.log(arguments);
 11411              }
 11412              else {
 11413                module.verbose = Function.prototype.bind.call(console.info, console, settings.name + ':');
 11414                module.verbose.apply(console, arguments);
 11415              }
 11416            }
 11417          },
 11418          error: function() {
 11419            if(!settings.silent) {
 11420              module.error = Function.prototype.bind.call(console.error, console, settings.name + ':');
 11421              module.error.apply(console, arguments);
 11422            }
 11423          },
 11424          performance: {
 11425            log: function(message) {
 11426              var
 11427                currentTime,
 11428                executionTime,
 11429                previousTime
 11430              ;
 11431              if(settings.performance) {
 11432                currentTime   = new Date().getTime();
 11433                previousTime  = time || currentTime;
 11434                executionTime = currentTime - previousTime;
 11435                time          = currentTime;
 11436                performance.push({
 11437                  'Name'           : message[0],
 11438                  'Arguments'      : [].slice.call(message, 1) || '',
 11439                  'Element'        : element,
 11440                  'Execution Time' : executionTime
 11441                });
 11442              }
 11443              clearTimeout(module.performance.timer);
 11444              module.performance.timer = setTimeout(module.performance.display, 500);
 11445            },
 11446            display: function() {
 11447              var
 11448                title = settings.name + ':',
 11449                totalTime = 0
 11450              ;
 11451              time = false;
 11452              clearTimeout(module.performance.timer);
 11453              $.each(performance, function(index, data) {
 11454                totalTime += data['Execution Time'];
 11455              });
 11456              title += ' ' + totalTime + 'ms';
 11457              if(moduleSelector) {
 11458                title += ' \'' + moduleSelector + '\'';
 11459              }
 11460              if($allModules.length > 1) {
 11461                title += ' ' + '(' + $allModules.length + ')';
 11462              }
 11463              if( (console.group !== undefined || console.table !== undefined) && performance.length > 0) {
 11464                console.groupCollapsed(title);
 11465                if(console.table) {
 11466                  console.table(performance);
 11467                }
 11468                else {
 11469                  $.each(performance, function(index, data) {
 11470                    console.log(data['Name'] + ': ' + data['Execution Time']+'ms');
 11471                  });
 11472                }
 11473                console.groupEnd();
 11474              }
 11475              performance = [];
 11476            }
 11477          },
 11478          invoke: function(query, passedArguments, context) {
 11479            var
 11480              object = instance,
 11481              maxDepth,
 11482              found,
 11483              response
 11484            ;
 11485            passedArguments = passedArguments || queryArguments;
 11486            context         = element         || context;
 11487            if(typeof query == 'string' && object !== undefined) {
 11488              query    = query.split(/[\. ]/);
 11489              maxDepth = query.length - 1;
 11490              $.each(query, function(depth, value) {
 11491                var camelCaseValue = (depth != maxDepth)
 11492                  ? value + query[depth + 1].charAt(0).toUpperCase() + query[depth + 1].slice(1)
 11493                  : query
 11494                ;
 11495                if( $.isPlainObject( object[camelCaseValue] ) && (depth != maxDepth) ) {
 11496                  object = object[camelCaseValue];
 11497                }
 11498                else if( object[camelCaseValue] !== undefined ) {
 11499                  found = object[camelCaseValue];
 11500                  return false;
 11501                }
 11502                else if( $.isPlainObject( object[value] ) && (depth != maxDepth) ) {
 11503                  object = object[value];
 11504                }
 11505                else if( object[value] !== undefined ) {
 11506                  found = object[value];
 11507                  return false;
 11508                }
 11509                else {
 11510                  return false;
 11511                }
 11512              });
 11513            }
 11514            if( $.isFunction( found ) ) {
 11515              response = found.apply(context, passedArguments);
 11516            }
 11517            else if(found !== undefined) {
 11518              response = found;
 11519            }
 11520            if(Array.isArray(returnedValue)) {
 11521              returnedValue.push(response);
 11522            }
 11523            else if(returnedValue !== undefined) {
 11524              returnedValue = [returnedValue, response];
 11525            }
 11526            else if(response !== undefined) {
 11527              returnedValue = response;
 11528            }
 11529            return found;
 11530          }
 11531        };
 11532        if(methodInvoked) {
 11533          if(instance === undefined) {
 11534            module.initialize();
 11535          }
 11536          module.invoke(query);
 11537        }
 11538        else {
 11539          if(instance !== undefined) {
 11540            instance.invoke('destroy');
 11541          }
 11542          module.initialize();
 11543        }
 11544  
 11545      })
 11546    ;
 11547  
 11548    return (returnedValue !== undefined)
 11549      ? returnedValue
 11550      : this
 11551    ;
 11552  };
 11553  
 11554  $.fn.search.settings = {
 11555  
 11556    name              : 'Search',
 11557    namespace         : 'search',
 11558  
 11559    silent            : false,
 11560    debug             : false,
 11561    verbose           : false,
 11562    performance       : true,
 11563  
 11564    // template to use (specified in settings.templates)
 11565    type              : 'standard',
 11566  
 11567    // minimum characters required to search
 11568    minCharacters     : 1,
 11569  
 11570    // whether to select first result after searching automatically
 11571    selectFirstResult : false,
 11572  
 11573    // API config
 11574    apiSettings       : false,
 11575  
 11576    // object to search
 11577    source            : false,
 11578  
 11579    // Whether search should query current term on focus
 11580    searchOnFocus     : true,
 11581  
 11582    // fields to search
 11583    searchFields   : [
 11584      'id',
 11585      'title',
 11586      'description'
 11587    ],
 11588  
 11589    // field to display in standard results template
 11590    displayField   : '',
 11591  
 11592    // search anywhere in value (set to 'exact' to require exact matches
 11593    fullTextSearch : 'exact',
 11594  
 11595    // match results also if they contain diacritics of the same base character (for example searching for "a" will also match "á" or "â" or "à", etc...)
 11596    ignoreDiacritics : false,
 11597  
 11598    // whether to add events to prompt automatically
 11599    automatic      : true,
 11600  
 11601    // delay before hiding menu after blur
 11602    hideDelay      : 0,
 11603  
 11604    // delay before searching
 11605    searchDelay    : 200,
 11606  
 11607    // maximum results returned from search
 11608    maxResults     : 7,
 11609  
 11610    // whether to store lookups in local cache
 11611    cache          : true,
 11612  
 11613    // whether no results errors should be shown
 11614    showNoResults  : true,
 11615  
 11616    // preserve possible html of resultset values
 11617    preserveHTML   : true,
 11618  
 11619    // transition settings
 11620    transition     : 'scale',
 11621    duration       : 200,
 11622    easing         : 'easeOutExpo',
 11623  
 11624    // callbacks
 11625    onSelect       : false,
 11626    onResultsAdd   : false,
 11627  
 11628    onSearchQuery  : function(query){},
 11629    onResults      : function(response){},
 11630  
 11631    onResultsOpen  : function(){},
 11632    onResultsClose : function(){},
 11633  
 11634    className: {
 11635      animating : 'animating',
 11636      active    : 'active',
 11637      empty     : 'empty',
 11638      focus     : 'focus',
 11639      hidden    : 'hidden',
 11640      loading   : 'loading',
 11641      results   : 'results',
 11642      pressed   : 'down'
 11643    },
 11644  
 11645    error : {
 11646      source          : 'Cannot search. No source used, and Semantic API module was not included',
 11647      noResultsHeader : 'No Results',
 11648      noResults       : 'Your search returned no results',
 11649      logging         : 'Error in debug logging, exiting.',
 11650      noEndpoint      : 'No search endpoint was specified',
 11651      noTemplate      : 'A valid template name was not specified.',
 11652      oldSearchSyntax : 'searchFullText setting has been renamed fullTextSearch for consistency, please adjust your settings.',
 11653      serverError     : 'There was an issue querying the server.',
 11654      maxResults      : 'Results must be an array to use maxResults setting',
 11655      method          : 'The method you called is not defined.',
 11656      noNormalize     : '"ignoreDiacritics" setting will be ignored. Browser does not support String().normalize(). You may consider including <https://cdn.jsdelivr.net/npm/unorm@1.4.1/lib/unorm.min.js> as a polyfill.'
 11657    },
 11658  
 11659    metadata: {
 11660      cache   : 'cache',
 11661      results : 'results',
 11662      result  : 'result'
 11663    },
 11664  
 11665    regExp: {
 11666      escape     : /[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g,
 11667      beginsWith : '(?:\s|^)'
 11668    },
 11669  
 11670    // maps api response attributes to internal representation
 11671    fields: {
 11672      categories      : 'results',     // array of categories (category view)
 11673      categoryName    : 'name',        // name of category (category view)
 11674      categoryResults : 'results',     // array of results (category view)
 11675      description     : 'description', // result description
 11676      image           : 'image',       // result image
 11677      price           : 'price',       // result price
 11678      results         : 'results',     // array of results (standard)
 11679      title           : 'title',       // result title
 11680      url             : 'url',         // result url
 11681      action          : 'action',      // "view more" object name
 11682      actionText      : 'text',        // "view more" text
 11683      actionURL       : 'url'          // "view more" url
 11684    },
 11685  
 11686    selector : {
 11687      prompt       : '.prompt',
 11688      searchButton : '.search.button',
 11689      results      : '.results',
 11690      message      : '.results > .message',
 11691      category     : '.category',
 11692      result       : '.result',
 11693      title        : '.title, .name'
 11694    },
 11695  
 11696    templates: {
 11697      escape: function(string, preserveHTML) {
 11698        if (preserveHTML){
 11699          return string;
 11700        }
 11701        var
 11702          badChars     = /[<>"'`]/g,
 11703          shouldEscape = /[&<>"'`]/,
 11704          escape       = {
 11705            "<": "&lt;",
 11706            ">": "&gt;",
 11707            '"': "&quot;",
 11708            "'": "&#x27;",
 11709            "`": "&#x60;"
 11710          },
 11711          escapedChar  = function(chr) {
 11712            return escape[chr];
 11713          }
 11714        ;
 11715        if(shouldEscape.test(string)) {
 11716          string = string.replace(/&(?![a-z0-9#]{1,6};)/, "&amp;");
 11717          return string.replace(badChars, escapedChar);
 11718        }
 11719        return string;
 11720      },
 11721      message: function(message, type, header) {
 11722        var
 11723          html = ''
 11724        ;
 11725        if(message !== undefined && type !== undefined) {
 11726          html +=  ''
 11727            + '<div class="message ' + type + '">'
 11728          ;
 11729          if(header) {
 11730            html += ''
 11731            + '<div class="header">' + header + '</div>'
 11732            ;
 11733          }
 11734          html += ' <div class="description">' + message + '</div>';
 11735          html += '</div>';
 11736        }
 11737        return html;
 11738      },
 11739      category: function(response, fields, preserveHTML) {
 11740        var
 11741          html = '',
 11742          escape = $.fn.search.settings.templates.escape
 11743        ;
 11744        if(response[fields.categoryResults] !== undefined) {
 11745  
 11746          // each category
 11747          $.each(response[fields.categoryResults], function(index, category) {
 11748            if(category[fields.results] !== undefined && category.results.length > 0) {
 11749  
 11750              html  += '<div class="category">';
 11751  
 11752              if(category[fields.categoryName] !== undefined) {
 11753                html += '<div class="name">' + escape(category[fields.categoryName], preserveHTML) + '</div>';
 11754              }
 11755  
 11756              // each item inside category
 11757              html += '<div class="results">';
 11758              $.each(category.results, function(index, result) {
 11759                if(result[fields.url]) {
 11760                  html  += '<a class="result" href="' + result[fields.url].replace(/"/g,"") + '">';
 11761                }
 11762                else {
 11763                  html  += '<a class="result">';
 11764                }
 11765                if(result[fields.image] !== undefined) {
 11766                  html += ''
 11767                    + '<div class="image">'
 11768                    + ' <img src="' + result[fields.image].replace(/"/g,"") + '">'
 11769                    + '</div>'
 11770                  ;
 11771                }
 11772                html += '<div class="content">';
 11773                if(result[fields.price] !== undefined) {
 11774                  html += '<div class="price">' + escape(result[fields.price], preserveHTML) + '</div>';
 11775                }
 11776                if(result[fields.title] !== undefined) {
 11777                  html += '<div class="title">' + escape(result[fields.title], preserveHTML) + '</div>';
 11778                }
 11779                if(result[fields.description] !== undefined) {
 11780                  html += '<div class="description">' + escape(result[fields.description], preserveHTML) + '</div>';
 11781                }
 11782                html  += ''
 11783                  + '</div>'
 11784                ;
 11785                html += '</a>';
 11786              });
 11787              html += '</div>';
 11788              html  += ''
 11789                + '</div>'
 11790              ;
 11791            }
 11792          });
 11793          if(response[fields.action]) {
 11794            if(fields.actionURL === false) {
 11795              html += ''
 11796              + '<div class="action">'
 11797              +   escape(response[fields.action][fields.actionText], preserveHTML)
 11798              + '</div>';
 11799            } else {
 11800              html += ''
 11801              + '<a href="' + response[fields.action][fields.actionURL].replace(/"/g,"") + '" class="action">'
 11802              +   escape(response[fields.action][fields.actionText], preserveHTML)
 11803              + '</a>';
 11804            }
 11805          }
 11806          return html;
 11807        }
 11808        return false;
 11809      },
 11810      standard: function(response, fields, preserveHTML) {
 11811        var
 11812          html = '',
 11813          escape = $.fn.search.settings.templates.escape
 11814        ;
 11815        if(response[fields.results] !== undefined) {
 11816  
 11817          // each result
 11818          $.each(response[fields.results], function(index, result) {
 11819            if(result[fields.url]) {
 11820              html  += '<a class="result" href="' + result[fields.url].replace(/"/g,"") + '">';
 11821            }
 11822            else {
 11823              html  += '<a class="result">';
 11824            }
 11825            if(result[fields.image] !== undefined) {
 11826              html += ''
 11827                + '<div class="image">'
 11828                + ' <img src="' + result[fields.image].replace(/"/g,"") + '">'
 11829                + '</div>'
 11830              ;
 11831            }
 11832            html += '<div class="content">';
 11833            if(result[fields.price] !== undefined) {
 11834              html += '<div class="price">' + escape(result[fields.price], preserveHTML) + '</div>';
 11835            }
 11836            if(result[fields.title] !== undefined) {
 11837              html += '<div class="title">' + escape(result[fields.title], preserveHTML) + '</div>';
 11838            }
 11839            if(result[fields.description] !== undefined) {
 11840              html += '<div class="description">' + escape(result[fields.description], preserveHTML) + '</div>';
 11841            }
 11842            html  += ''
 11843              + '</div>'
 11844            ;
 11845            html += '</a>';
 11846          });
 11847          if(response[fields.action]) {
 11848            if(fields.actionURL === false) {
 11849              html += ''
 11850              + '<div class="action">'
 11851              +   escape(response[fields.action][fields.actionText], preserveHTML)
 11852              + '</div>';
 11853            } else {
 11854              html += ''
 11855              + '<a href="' + response[fields.action][fields.actionURL].replace(/"/g,"") + '" class="action">'
 11856              +   escape(response[fields.action][fields.actionText], preserveHTML)
 11857              + '</a>';
 11858            }
 11859          }
 11860          return html;
 11861        }
 11862        return false;
 11863      }
 11864    }
 11865  };
 11866  
 11867  })( jQuery, window, document );
 11868  
 11869  /*!
 11870   * # Fomantic-UI - Site
 11871   * http://github.com/fomantic/Fomantic-UI/
 11872   *
 11873   *
 11874   * Released under the MIT license
 11875   * http://opensource.org/licenses/MIT
 11876   *
 11877   */
 11878  
 11879  ;(function ($, window, document, undefined) {
 11880  
 11881  $.isFunction = $.isFunction || function(obj) {
 11882      return typeof obj === "function" && typeof obj.nodeType !== "number";
 11883  };
 11884  
 11885  $.site = $.fn.site = function(parameters) {
 11886    var
 11887      time           = new Date().getTime(),
 11888      performance    = [],
 11889  
 11890      query          = arguments[0],
 11891      methodInvoked  = (typeof query == 'string'),
 11892      queryArguments = [].slice.call(arguments, 1),
 11893  
 11894      settings        = ( $.isPlainObject(parameters) )
 11895        ? $.extend(true, {}, $.site.settings, parameters)
 11896        : $.extend({}, $.site.settings),
 11897  
 11898      namespace       = settings.namespace,
 11899      error           = settings.error,
 11900  
 11901      moduleNamespace = 'module-' + namespace,
 11902  
 11903      $document       = $(document),
 11904      $module         = $document,
 11905      element         = this,
 11906      instance        = $module.data(moduleNamespace),
 11907  
 11908      module,
 11909      returnedValue
 11910    ;
 11911    module = {
 11912  
 11913      initialize: function() {
 11914        module.instantiate();
 11915      },
 11916  
 11917      instantiate: function() {
 11918        module.verbose('Storing instance of site', module);
 11919        instance = module;
 11920        $module
 11921          .data(moduleNamespace, module)
 11922        ;
 11923      },
 11924  
 11925      normalize: function() {
 11926        module.fix.console();
 11927        module.fix.requestAnimationFrame();
 11928      },
 11929  
 11930      fix: {
 11931        console: function() {
 11932          module.debug('Normalizing window.console');
 11933          if (console === undefined || console.log === undefined) {
 11934            module.verbose('Console not available, normalizing events');
 11935            module.disable.console();
 11936          }
 11937          if (typeof console.group == 'undefined' || typeof console.groupEnd == 'undefined' || typeof console.groupCollapsed == 'undefined') {
 11938            module.verbose('Console group not available, normalizing events');
 11939            window.console.group = function() {};
 11940            window.console.groupEnd = function() {};
 11941            window.console.groupCollapsed = function() {};
 11942          }
 11943          if (typeof console.markTimeline == 'undefined') {
 11944            module.verbose('Mark timeline not available, normalizing events');
 11945            window.console.markTimeline = function() {};
 11946          }
 11947        },
 11948        consoleClear: function() {
 11949          module.debug('Disabling programmatic console clearing');
 11950          window.console.clear = function() {};
 11951        },
 11952        requestAnimationFrame: function() {
 11953          module.debug('Normalizing requestAnimationFrame');
 11954          if(window.requestAnimationFrame === undefined) {
 11955            module.debug('RequestAnimationFrame not available, normalizing event');
 11956            window.requestAnimationFrame = window.requestAnimationFrame
 11957              || window.mozRequestAnimationFrame
 11958              || window.webkitRequestAnimationFrame
 11959              || window.msRequestAnimationFrame
 11960              || function(callback) { setTimeout(callback, 0); }
 11961            ;
 11962          }
 11963        }
 11964      },
 11965  
 11966      moduleExists: function(name) {
 11967        return ($.fn[name] !== undefined && $.fn[name].settings !== undefined);
 11968      },
 11969  
 11970      enabled: {
 11971        modules: function(modules) {
 11972          var
 11973            enabledModules = []
 11974          ;
 11975          modules = modules || settings.modules;
 11976          $.each(modules, function(index, name) {
 11977            if(module.moduleExists(name)) {
 11978              enabledModules.push(name);
 11979            }
 11980          });
 11981          return enabledModules;
 11982        }
 11983      },
 11984  
 11985      disabled: {
 11986        modules: function(modules) {
 11987          var
 11988            disabledModules = []
 11989          ;
 11990          modules = modules || settings.modules;
 11991          $.each(modules, function(index, name) {
 11992            if(!module.moduleExists(name)) {
 11993              disabledModules.push(name);
 11994            }
 11995          });
 11996          return disabledModules;
 11997        }
 11998      },
 11999  
 12000      change: {
 12001        setting: function(setting, value, modules, modifyExisting) {
 12002          modules = (typeof modules === 'string')
 12003            ? (modules === 'all')
 12004              ? settings.modules
 12005              : [modules]
 12006            : modules || settings.modules
 12007          ;
 12008          modifyExisting = (modifyExisting !== undefined)
 12009            ? modifyExisting
 12010            : true
 12011          ;
 12012          $.each(modules, function(index, name) {
 12013            var
 12014              namespace = (module.moduleExists(name))
 12015                ? $.fn[name].settings.namespace || false
 12016                : true,
 12017              $existingModules
 12018            ;
 12019            if(module.moduleExists(name)) {
 12020              module.verbose('Changing default setting', setting, value, name);
 12021              $.fn[name].settings[setting] = value;
 12022              if(modifyExisting && namespace) {
 12023                $existingModules = $(':data(module-' + namespace + ')');
 12024                if($existingModules.length > 0) {
 12025                  module.verbose('Modifying existing settings', $existingModules);
 12026                  $existingModules[name]('setting', setting, value);
 12027                }
 12028              }
 12029            }
 12030          });
 12031        },
 12032        settings: function(newSettings, modules, modifyExisting) {
 12033          modules = (typeof modules === 'string')
 12034            ? [modules]
 12035            : modules || settings.modules
 12036          ;
 12037          modifyExisting = (modifyExisting !== undefined)
 12038            ? modifyExisting
 12039            : true
 12040          ;
 12041          $.each(modules, function(index, name) {
 12042            var
 12043              $existingModules
 12044            ;
 12045            if(module.moduleExists(name)) {
 12046              module.verbose('Changing default setting', newSettings, name);
 12047              $.extend(true, $.fn[name].settings, newSettings);
 12048              if(modifyExisting && namespace) {
 12049                $existingModules = $(':data(module-' + namespace + ')');
 12050                if($existingModules.length > 0) {
 12051                  module.verbose('Modifying existing settings', $existingModules);
 12052                  $existingModules[name]('setting', newSettings);
 12053                }
 12054              }
 12055            }
 12056          });
 12057        }
 12058      },
 12059  
 12060      enable: {
 12061        console: function() {
 12062          module.console(true);
 12063        },
 12064        debug: function(modules, modifyExisting) {
 12065          modules = modules || settings.modules;
 12066          module.debug('Enabling debug for modules', modules);
 12067          module.change.setting('debug', true, modules, modifyExisting);
 12068        },
 12069        verbose: function(modules, modifyExisting) {
 12070          modules = modules || settings.modules;
 12071          module.debug('Enabling verbose debug for modules', modules);
 12072          module.change.setting('verbose', true, modules, modifyExisting);
 12073        }
 12074      },
 12075      disable: {
 12076        console: function() {
 12077          module.console(false);
 12078        },
 12079        debug: function(modules, modifyExisting) {
 12080          modules = modules || settings.modules;
 12081          module.debug('Disabling debug for modules', modules);
 12082          module.change.setting('debug', false, modules, modifyExisting);
 12083        },
 12084        verbose: function(modules, modifyExisting) {
 12085          modules = modules || settings.modules;
 12086          module.debug('Disabling verbose debug for modules', modules);
 12087          module.change.setting('verbose', false, modules, modifyExisting);
 12088        }
 12089      },
 12090  
 12091      console: function(enable) {
 12092        if(enable) {
 12093          if(instance.cache.console === undefined) {
 12094            module.error(error.console);
 12095            return;
 12096          }
 12097          module.debug('Restoring console function');
 12098          window.console = instance.cache.console;
 12099        }
 12100        else {
 12101          module.debug('Disabling console function');
 12102          instance.cache.console = window.console;
 12103          window.console = {
 12104            clear          : function(){},
 12105            error          : function(){},
 12106            group          : function(){},
 12107            groupCollapsed : function(){},
 12108            groupEnd       : function(){},
 12109            info           : function(){},
 12110            log            : function(){},
 12111            markTimeline   : function(){},
 12112            warn           : function(){}
 12113          };
 12114        }
 12115      },
 12116  
 12117      destroy: function() {
 12118        module.verbose('Destroying previous site for', $module);
 12119        $module
 12120          .removeData(moduleNamespace)
 12121        ;
 12122      },
 12123  
 12124      cache: {},
 12125  
 12126      setting: function(name, value) {
 12127        if( $.isPlainObject(name) ) {
 12128          $.extend(true, settings, name);
 12129        }
 12130        else if(value !== undefined) {
 12131          settings[name] = value;
 12132        }
 12133        else {
 12134          return settings[name];
 12135        }
 12136      },
 12137      internal: function(name, value) {
 12138        if( $.isPlainObject(name) ) {
 12139          $.extend(true, module, name);
 12140        }
 12141        else if(value !== undefined) {
 12142          module[name] = value;
 12143        }
 12144        else {
 12145          return module[name];
 12146        }
 12147      },
 12148      debug: function() {
 12149        if(settings.debug) {
 12150          if(settings.performance) {
 12151            module.performance.log(arguments);
 12152          }
 12153          else {
 12154            module.debug = Function.prototype.bind.call(console.info, console, settings.name + ':');
 12155            module.debug.apply(console, arguments);
 12156          }
 12157        }
 12158      },
 12159      verbose: function() {
 12160        if(settings.verbose && settings.debug) {
 12161          if(settings.performance) {
 12162            module.performance.log(arguments);
 12163          }
 12164          else {
 12165            module.verbose = Function.prototype.bind.call(console.info, console, settings.name + ':');
 12166            module.verbose.apply(console, arguments);
 12167          }
 12168        }
 12169      },
 12170      error: function() {
 12171        module.error = Function.prototype.bind.call(console.error, console, settings.name + ':');
 12172        module.error.apply(console, arguments);
 12173      },
 12174      performance: {
 12175        log: function(message) {
 12176          var
 12177            currentTime,
 12178            executionTime,
 12179            previousTime
 12180          ;
 12181          if(settings.performance) {
 12182            currentTime   = new Date().getTime();
 12183            previousTime  = time || currentTime;
 12184            executionTime = currentTime - previousTime;
 12185            time          = currentTime;
 12186            performance.push({
 12187              'Element'        : element,
 12188              'Name'           : message[0],
 12189              'Arguments'      : [].slice.call(message, 1) || '',
 12190              'Execution Time' : executionTime
 12191            });
 12192          }
 12193          clearTimeout(module.performance.timer);
 12194          module.performance.timer = setTimeout(module.performance.display, 500);
 12195        },
 12196        display: function() {
 12197          var
 12198            title = settings.name + ':',
 12199            totalTime = 0
 12200          ;
 12201          time = false;
 12202          clearTimeout(module.performance.timer);
 12203          $.each(performance, function(index, data) {
 12204            totalTime += data['Execution Time'];
 12205          });
 12206          title += ' ' + totalTime + 'ms';
 12207          if( (console.group !== undefined || console.table !== undefined) && performance.length > 0) {
 12208            console.groupCollapsed(title);
 12209            if(console.table) {
 12210              console.table(performance);
 12211            }
 12212            else {
 12213              $.each(performance, function(index, data) {
 12214                console.log(data['Name'] + ': ' + data['Execution Time']+'ms');
 12215              });
 12216            }
 12217            console.groupEnd();
 12218          }
 12219          performance = [];
 12220        }
 12221      },
 12222      invoke: function(query, passedArguments, context) {
 12223        var
 12224          object = instance,
 12225          maxDepth,
 12226          found,
 12227          response
 12228        ;
 12229        passedArguments = passedArguments || queryArguments;
 12230        context         = element         || context;
 12231        if(typeof query == 'string' && object !== undefined) {
 12232          query    = query.split(/[\. ]/);
 12233          maxDepth = query.length - 1;
 12234          $.each(query, function(depth, value) {
 12235            var camelCaseValue = (depth != maxDepth)
 12236              ? value + query[depth + 1].charAt(0).toUpperCase() + query[depth + 1].slice(1)
 12237              : query
 12238            ;
 12239            if( $.isPlainObject( object[camelCaseValue] ) && (depth != maxDepth) ) {
 12240              object = object[camelCaseValue];
 12241            }
 12242            else if( object[camelCaseValue] !== undefined ) {
 12243              found = object[camelCaseValue];
 12244              return false;
 12245            }
 12246            else if( $.isPlainObject( object[value] ) && (depth != maxDepth) ) {
 12247              object = object[value];
 12248            }
 12249            else if( object[value] !== undefined ) {
 12250              found = object[value];
 12251              return false;
 12252            }
 12253            else {
 12254              module.error(error.method, query);
 12255              return false;
 12256            }
 12257          });
 12258        }
 12259        if ( $.isFunction( found ) ) {
 12260          response = found.apply(context, passedArguments);
 12261        }
 12262        else if(found !== undefined) {
 12263          response = found;
 12264        }
 12265        if(Array.isArray(returnedValue)) {
 12266          returnedValue.push(response);
 12267        }
 12268        else if(returnedValue !== undefined) {
 12269          returnedValue = [returnedValue, response];
 12270        }
 12271        else if(response !== undefined) {
 12272          returnedValue = response;
 12273        }
 12274        return found;
 12275      }
 12276    };
 12277  
 12278    if(methodInvoked) {
 12279      if(instance === undefined) {
 12280        module.initialize();
 12281      }
 12282      module.invoke(query);
 12283    }
 12284    else {
 12285      if(instance !== undefined) {
 12286        module.destroy();
 12287      }
 12288      module.initialize();
 12289    }
 12290    return (returnedValue !== undefined)
 12291      ? returnedValue
 12292      : this
 12293    ;
 12294  };
 12295  
 12296  $.site.settings = {
 12297  
 12298    name        : 'Site',
 12299    namespace   : 'site',
 12300  
 12301    error : {
 12302      console : 'Console cannot be restored, most likely it was overwritten outside of module',
 12303      method : 'The method you called is not defined.'
 12304    },
 12305  
 12306    debug       : false,
 12307    verbose     : false,
 12308    performance : true,
 12309  
 12310    modules: [
 12311      'accordion',
 12312      'api',
 12313      'calendar',
 12314      'checkbox',
 12315      'dimmer',
 12316      'dropdown',
 12317      'embed',
 12318      'form',
 12319      'modal',
 12320      'nag',
 12321      'popup',
 12322      'slider',
 12323      'rating',
 12324      'shape',
 12325      'sidebar',
 12326      'state',
 12327      'sticky',
 12328      'tab',
 12329      'toast',
 12330      'transition',
 12331      'visibility',
 12332      'visit'
 12333    ],
 12334  
 12335    siteNamespace   : 'site',
 12336    namespaceStub   : {
 12337      cache     : {},
 12338      config    : {},
 12339      sections  : {},
 12340      section   : {},
 12341      utilities : {}
 12342    }
 12343  
 12344  };
 12345  
 12346  // allows for selection of elements with data attributes
 12347  $.extend($.expr[ ":" ], {
 12348    data: ($.expr.createPseudo)
 12349      ? $.expr.createPseudo(function(dataName) {
 12350          return function(elem) {
 12351            return !!$.data(elem, dataName);
 12352          };
 12353        })
 12354      : function(elem, i, match) {
 12355        // support: jQuery < 1.8
 12356        return !!$.data(elem, match[ 3 ]);
 12357      }
 12358  });
 12359  
 12360  
 12361  })( jQuery, window, document );
 12362  
 12363  /*!
 12364   * # Fomantic-UI - Tab
 12365   * http://github.com/fomantic/Fomantic-UI/
 12366   *
 12367   *
 12368   * Released under the MIT license
 12369   * http://opensource.org/licenses/MIT
 12370   *
 12371   */
 12372  
 12373  ;(function ($, window, document, undefined) {
 12374  
 12375  'use strict';
 12376  
 12377  $.isWindow = $.isWindow || function(obj) {
 12378    return obj != null && obj === obj.window;
 12379  };
 12380  $.isFunction = $.isFunction || function(obj) {
 12381    return typeof obj === "function" && typeof obj.nodeType !== "number";
 12382  };
 12383  
 12384  window = (typeof window != 'undefined' && window.Math == Math)
 12385    ? window
 12386    : (typeof self != 'undefined' && self.Math == Math)
 12387      ? self
 12388      : Function('return this')()
 12389  ;
 12390  
 12391  $.fn.tab = function(parameters) {
 12392  
 12393    var
 12394      // use window context if none specified
 12395      $allModules     = $.isFunction(this)
 12396          ? $(window)
 12397          : $(this),
 12398  
 12399      moduleSelector  = $allModules.selector || '',
 12400      time            = new Date().getTime(),
 12401      performance     = [],
 12402  
 12403      query           = arguments[0],
 12404      methodInvoked   = (typeof query == 'string'),
 12405      queryArguments  = [].slice.call(arguments, 1),
 12406  
 12407      initializedHistory = false,
 12408      returnedValue
 12409    ;
 12410  
 12411    $allModules
 12412      .each(function() {
 12413        var
 12414  
 12415          settings        = ( $.isPlainObject(parameters) )
 12416            ? $.extend(true, {}, $.fn.tab.settings, parameters)
 12417            : $.extend({}, $.fn.tab.settings),
 12418  
 12419          className       = settings.className,
 12420          metadata        = settings.metadata,
 12421          selector        = settings.selector,
 12422          error           = settings.error,
 12423          regExp          = settings.regExp,
 12424  
 12425          eventNamespace  = '.' + settings.namespace,
 12426          moduleNamespace = 'module-' + settings.namespace,
 12427  
 12428          $module         = $(this),
 12429          $context,
 12430          $tabs,
 12431  
 12432          cache           = {},
 12433          firstLoad       = true,
 12434          recursionDepth  = 0,
 12435          element         = this,
 12436          instance        = $module.data(moduleNamespace),
 12437  
 12438          activeTabPath,
 12439          parameterArray,
 12440          module,
 12441  
 12442          historyEvent
 12443  
 12444        ;
 12445  
 12446        module = {
 12447  
 12448          initialize: function() {
 12449            module.debug('Initializing tab menu item', $module);
 12450            module.fix.callbacks();
 12451            module.determineTabs();
 12452  
 12453            module.debug('Determining tabs', settings.context, $tabs);
 12454            // set up automatic routing
 12455            if(settings.auto) {
 12456              module.set.auto();
 12457            }
 12458            module.bind.events();
 12459  
 12460            if(settings.history && !initializedHistory) {
 12461              module.initializeHistory();
 12462              initializedHistory = true;
 12463            }
 12464  
 12465            if(settings.autoTabActivation && instance === undefined && module.determine.activeTab() == null) {
 12466              module.debug('No active tab detected, setting first tab active', module.get.initialPath());
 12467              module.changeTab(settings.autoTabActivation === true ? module.get.initialPath() : settings.autoTabActivation);
 12468            };
 12469  
 12470            module.instantiate();
 12471          },
 12472  
 12473          instantiate: function () {
 12474            module.verbose('Storing instance of module', module);
 12475            instance = module;
 12476            $module
 12477              .data(moduleNamespace, module)
 12478            ;
 12479          },
 12480  
 12481          destroy: function() {
 12482            module.debug('Destroying tabs', $module);
 12483            $module
 12484              .removeData(moduleNamespace)
 12485              .off(eventNamespace)
 12486            ;
 12487          },
 12488  
 12489          bind: {
 12490            events: function() {
 12491              // if using $.tab don't add events
 12492              if( !$.isWindow( element ) ) {
 12493                module.debug('Attaching tab activation events to element', $module);
 12494                $module
 12495                  .on('click' + eventNamespace, module.event.click)
 12496                ;
 12497              }
 12498            }
 12499          },
 12500  
 12501          determineTabs: function() {
 12502            var
 12503              $reference
 12504            ;
 12505  
 12506            // determine tab context
 12507            if(settings.context === 'parent') {
 12508              if($module.closest(selector.ui).length > 0) {
 12509                $reference = $module.closest(selector.ui);
 12510                module.verbose('Using closest UI element as parent', $reference);
 12511              }
 12512              else {
 12513                $reference = $module;
 12514              }
 12515              $context = $reference.parent();
 12516              module.verbose('Determined parent element for creating context', $context);
 12517            }
 12518            else if(settings.context) {
 12519              $context = $(settings.context);
 12520              module.verbose('Using selector for tab context', settings.context, $context);
 12521            }
 12522            else {
 12523              $context = $('body');
 12524            }
 12525            // find tabs
 12526            if(settings.childrenOnly) {
 12527              $tabs = $context.children(selector.tabs);
 12528              module.debug('Searching tab context children for tabs', $context, $tabs);
 12529            }
 12530            else {
 12531              $tabs = $context.find(selector.tabs);
 12532              module.debug('Searching tab context for tabs', $context, $tabs);
 12533            }
 12534          },
 12535  
 12536          fix: {
 12537            callbacks: function() {
 12538              if( $.isPlainObject(parameters) && (parameters.onTabLoad || parameters.onTabInit) ) {
 12539                if(parameters.onTabLoad) {
 12540                  parameters.onLoad = parameters.onTabLoad;
 12541                  delete parameters.onTabLoad;
 12542                  module.error(error.legacyLoad, parameters.onLoad);
 12543                }
 12544                if(parameters.onTabInit) {
 12545                  parameters.onFirstLoad = parameters.onTabInit;
 12546                  delete parameters.onTabInit;
 12547                  module.error(error.legacyInit, parameters.onFirstLoad);
 12548                }
 12549                settings = $.extend(true, {}, $.fn.tab.settings, parameters);
 12550              }
 12551            }
 12552          },
 12553  
 12554          initializeHistory: function() {
 12555            module.debug('Initializing page state');
 12556            if( $.address === undefined ) {
 12557              module.error(error.state);
 12558              return false;
 12559            }
 12560            else {
 12561              if(settings.historyType == 'state') {
 12562                module.debug('Using HTML5 to manage state');
 12563                if(settings.path !== false) {
 12564                  $.address
 12565                    .history(true)
 12566                    .state(settings.path)
 12567                  ;
 12568                }
 12569                else {
 12570                  module.error(error.path);
 12571                  return false;
 12572                }
 12573              }
 12574              $.address
 12575                .bind('change', module.event.history.change)
 12576              ;
 12577            }
 12578          },
 12579  
 12580          event: {
 12581            click: function(event) {
 12582              var
 12583                tabPath = $(this).data(metadata.tab)
 12584              ;
 12585              if(tabPath !== undefined) {
 12586                if(settings.history) {
 12587                  module.verbose('Updating page state', event);
 12588                  $.address.value(tabPath);
 12589                }
 12590                else {
 12591                  module.verbose('Changing tab', event);
 12592                  module.changeTab(tabPath);
 12593                }
 12594                event.preventDefault();
 12595              }
 12596              else {
 12597                module.debug('No tab specified');
 12598              }
 12599            },
 12600            history: {
 12601              change: function(event) {
 12602                var
 12603                  tabPath   = event.pathNames.join('/') || module.get.initialPath(),
 12604                  pageTitle = settings.templates.determineTitle(tabPath) || false
 12605                ;
 12606                module.performance.display();
 12607                module.debug('History change event', tabPath, event);
 12608                historyEvent = event;
 12609                if(tabPath !== undefined) {
 12610                  module.changeTab(tabPath);
 12611                }
 12612                if(pageTitle) {
 12613                  $.address.title(pageTitle);
 12614                }
 12615              }
 12616            }
 12617          },
 12618  
 12619          refresh: function() {
 12620            if(activeTabPath) {
 12621              module.debug('Refreshing tab', activeTabPath);
 12622              module.changeTab(activeTabPath);
 12623            }
 12624          },
 12625  
 12626          cache: {
 12627  
 12628            read: function(cacheKey) {
 12629              return (cacheKey !== undefined)
 12630                ? cache[cacheKey]
 12631                : false
 12632              ;
 12633            },
 12634            add: function(cacheKey, content) {
 12635              cacheKey = cacheKey || activeTabPath;
 12636              module.debug('Adding cached content for', cacheKey);
 12637              cache[cacheKey] = content;
 12638            },
 12639            remove: function(cacheKey) {
 12640              cacheKey = cacheKey || activeTabPath;
 12641              module.debug('Removing cached content for', cacheKey);
 12642              delete cache[cacheKey];
 12643            }
 12644          },
 12645  
 12646          escape: {
 12647            string: function(text) {
 12648              text =  String(text);
 12649              return text.replace(regExp.escape, '\\$&');
 12650            }
 12651          },
 12652  
 12653          set: {
 12654            auto: function() {
 12655              var
 12656                url = (typeof settings.path == 'string')
 12657                  ? settings.path.replace(/\/$/, '') + '/{$tab}'
 12658                  : '/{$tab}'
 12659              ;
 12660              module.verbose('Setting up automatic tab retrieval from server', url);
 12661              if($.isPlainObject(settings.apiSettings)) {
 12662                settings.apiSettings.url = url;
 12663              }
 12664              else {
 12665                settings.apiSettings = {
 12666                  url: url
 12667                };
 12668              }
 12669            },
 12670            loading: function(tabPath) {
 12671              var
 12672                $tab      = module.get.tabElement(tabPath),
 12673                isLoading = $tab.hasClass(className.loading)
 12674              ;
 12675              if(!isLoading) {
 12676                module.verbose('Setting loading state for', $tab);
 12677                $tab
 12678                  .addClass(className.loading)
 12679                  .siblings($tabs)
 12680                    .removeClass(className.active + ' ' + className.loading)
 12681                ;
 12682                if($tab.length > 0) {
 12683                  settings.onRequest.call($tab[0], tabPath);
 12684                }
 12685              }
 12686            },
 12687            state: function(state) {
 12688              $.address.value(state);
 12689            }
 12690          },
 12691  
 12692          changeTab: function(tabPath) {
 12693            var
 12694              pushStateAvailable = (window.history && window.history.pushState),
 12695              shouldIgnoreLoad   = (pushStateAvailable && settings.ignoreFirstLoad && firstLoad),
 12696              remoteContent      = (settings.auto || $.isPlainObject(settings.apiSettings) ),
 12697              // only add default path if not remote content
 12698              pathArray = (remoteContent && !shouldIgnoreLoad)
 12699                ? module.utilities.pathToArray(tabPath)
 12700                : module.get.defaultPathArray(tabPath)
 12701            ;
 12702            tabPath = module.utilities.arrayToPath(pathArray);
 12703            $.each(pathArray, function(index, tab) {
 12704              var
 12705                currentPathArray   = pathArray.slice(0, index + 1),
 12706                currentPath        = module.utilities.arrayToPath(currentPathArray),
 12707  
 12708                isTab              = module.is.tab(currentPath),
 12709                isLastIndex        = (index + 1 == pathArray.length),
 12710  
 12711                $tab               = module.get.tabElement(currentPath),
 12712                $anchor,
 12713                nextPathArray,
 12714                nextPath,
 12715                isLastTab
 12716              ;
 12717              module.verbose('Looking for tab', tab);
 12718              if(isTab) {
 12719                module.verbose('Tab was found', tab);
 12720                // scope up
 12721                activeTabPath  = currentPath;
 12722                parameterArray = module.utilities.filterArray(pathArray, currentPathArray);
 12723  
 12724                if(isLastIndex) {
 12725                  isLastTab = true;
 12726                }
 12727                else {
 12728                  nextPathArray = pathArray.slice(0, index + 2);
 12729                  nextPath      = module.utilities.arrayToPath(nextPathArray);
 12730                  isLastTab     = ( !module.is.tab(nextPath) );
 12731                  if(isLastTab) {
 12732                    module.verbose('Tab parameters found', nextPathArray);
 12733                  }
 12734                }
 12735                if(isLastTab && remoteContent) {
 12736                  if(!shouldIgnoreLoad) {
 12737                    module.activate.navigation(currentPath);
 12738                    module.fetch.content(currentPath, tabPath);
 12739                  }
 12740                  else {
 12741                    module.debug('Ignoring remote content on first tab load', currentPath);
 12742                    firstLoad = false;
 12743                    module.cache.add(tabPath, $tab.html());
 12744                    module.activate.all(currentPath);
 12745                    settings.onFirstLoad.call($tab[0], currentPath, parameterArray, historyEvent);
 12746                    settings.onLoad.call($tab[0], currentPath, parameterArray, historyEvent);
 12747                  }
 12748                  return false;
 12749                }
 12750                else {
 12751                  module.debug('Opened local tab', currentPath);
 12752                  module.activate.all(currentPath);
 12753                  if( !module.cache.read(currentPath) ) {
 12754                    module.cache.add(currentPath, true);
 12755                    module.debug('First time tab loaded calling tab init');
 12756                    settings.onFirstLoad.call($tab[0], currentPath, parameterArray, historyEvent);
 12757                  }
 12758                  settings.onLoad.call($tab[0], currentPath, parameterArray, historyEvent);
 12759                }
 12760  
 12761              }
 12762              else if(tabPath.search('/') == -1 && tabPath !== '') {
 12763                // look for in page anchor
 12764                tabPath = module.escape.string(tabPath);
 12765                $anchor     = $('#' + tabPath + ', a[name="' + tabPath + '"]');
 12766                currentPath = $anchor.closest('[data-tab]').data(metadata.tab);
 12767                $tab        = module.get.tabElement(currentPath);
 12768                // if anchor exists use parent tab
 12769                if($anchor && $anchor.length > 0 && currentPath) {
 12770                  module.debug('Anchor link used, opening parent tab', $tab, $anchor);
 12771                  if( !$tab.hasClass(className.active) ) {
 12772                    setTimeout(function() {
 12773                      module.scrollTo($anchor);
 12774                    }, 0);
 12775                  }
 12776                  module.activate.all(currentPath);
 12777                  if( !module.cache.read(currentPath) ) {
 12778                    module.cache.add(currentPath, true);
 12779                    module.debug('First time tab loaded calling tab init');
 12780                    settings.onFirstLoad.call($tab[0], currentPath, parameterArray, historyEvent);
 12781                  }
 12782                  settings.onLoad.call($tab[0], currentPath, parameterArray, historyEvent);
 12783                  return false;
 12784                }
 12785              }
 12786              else {
 12787                module.error(error.missingTab, $module, $context, currentPath);
 12788                return false;
 12789              }
 12790            });
 12791          },
 12792  
 12793          scrollTo: function($element) {
 12794            var
 12795              scrollOffset = ($element && $element.length > 0)
 12796                ? $element.offset().top
 12797                : false
 12798            ;
 12799            if(scrollOffset !== false) {
 12800              module.debug('Forcing scroll to an in-page link in a hidden tab', scrollOffset, $element);
 12801              $(document).scrollTop(scrollOffset);
 12802            }
 12803          },
 12804  
 12805          update: {
 12806            content: function(tabPath, html, evaluateScripts) {
 12807              var
 12808                $tab = module.get.tabElement(tabPath),
 12809                tab  = $tab[0]
 12810              ;
 12811              evaluateScripts = (evaluateScripts !== undefined)
 12812                ? evaluateScripts
 12813                : settings.evaluateScripts
 12814              ;
 12815              if(typeof settings.cacheType == 'string' && settings.cacheType.toLowerCase() == 'dom' && typeof html !== 'string') {
 12816                $tab
 12817                  .empty()
 12818                  .append($(html).clone(true))
 12819                ;
 12820              }
 12821              else {
 12822                if(evaluateScripts) {
 12823                  module.debug('Updating HTML and evaluating inline scripts', tabPath, html);
 12824                  $tab.html(html);
 12825                }
 12826                else {
 12827                  module.debug('Updating HTML', tabPath, html);
 12828                  tab.innerHTML = html;
 12829                }
 12830              }
 12831            }
 12832          },
 12833  
 12834          fetch: {
 12835  
 12836            content: function(tabPath, fullTabPath) {
 12837              var
 12838                $tab        = module.get.tabElement(tabPath),
 12839                apiSettings = {
 12840                  dataType         : 'html',
 12841                  encodeParameters : false,
 12842                  on               : 'now',
 12843                  cache            : settings.alwaysRefresh,
 12844                  headers          : {
 12845                    'X-Remote': true
 12846                  },
 12847                  onSuccess : function(response) {
 12848                    if(settings.cacheType == 'response') {
 12849                      module.cache.add(fullTabPath, response);
 12850                    }
 12851                    module.update.content(tabPath, response);
 12852                    if(tabPath == activeTabPath) {
 12853                      module.debug('Content loaded', tabPath);
 12854                      module.activate.tab(tabPath);
 12855                    }
 12856                    else {
 12857                      module.debug('Content loaded in background', tabPath);
 12858                    }
 12859                    settings.onFirstLoad.call($tab[0], tabPath, parameterArray, historyEvent);
 12860                    settings.onLoad.call($tab[0], tabPath, parameterArray, historyEvent);
 12861  
 12862                    if(settings.loadOnce) {
 12863                      module.cache.add(fullTabPath, true);
 12864                    }
 12865                    else if(typeof settings.cacheType == 'string' && settings.cacheType.toLowerCase() == 'dom' && $tab.children().length > 0) {
 12866                      setTimeout(function() {
 12867                        var
 12868                          $clone = $tab.children().clone(true)
 12869                        ;
 12870                        $clone = $clone.not('script');
 12871                        module.cache.add(fullTabPath, $clone);
 12872                      }, 0);
 12873                    }
 12874                    else {
 12875                      module.cache.add(fullTabPath, $tab.html());
 12876                    }
 12877                  },
 12878                  urlData: {
 12879                    tab: fullTabPath
 12880                  }
 12881                },
 12882                request         = $tab.api('get request') || false,
 12883                existingRequest = ( request && request.state() === 'pending' ),
 12884                requestSettings,
 12885                cachedContent
 12886              ;
 12887  
 12888              fullTabPath   = fullTabPath || tabPath;
 12889              cachedContent = module.cache.read(fullTabPath);
 12890  
 12891  
 12892              if(settings.cache && cachedContent) {
 12893                module.activate.tab(tabPath);
 12894                module.debug('Adding cached content', fullTabPath);
 12895                if(!settings.loadOnce) {
 12896                  if(settings.evaluateScripts == 'once') {
 12897                    module.update.content(tabPath, cachedContent, false);
 12898                  }
 12899                  else {
 12900                    module.update.content(tabPath, cachedContent);
 12901                  }
 12902                }
 12903                settings.onLoad.call($tab[0], tabPath, parameterArray, historyEvent);
 12904              }
 12905              else if(existingRequest) {
 12906                module.set.loading(tabPath);
 12907                module.debug('Content is already loading', fullTabPath);
 12908              }
 12909              else if($.api !== undefined) {
 12910                requestSettings = $.extend(true, {}, settings.apiSettings, apiSettings);
 12911                module.debug('Retrieving remote content', fullTabPath, requestSettings);
 12912                module.set.loading(tabPath);
 12913                $tab.api(requestSettings);
 12914              }
 12915              else {
 12916                module.error(error.api);
 12917              }
 12918            }
 12919          },
 12920  
 12921          activate: {
 12922            all: function(tabPath) {
 12923              module.activate.tab(tabPath);
 12924              module.activate.navigation(tabPath);
 12925            },
 12926            tab: function(tabPath) {
 12927              var
 12928                $tab          = module.get.tabElement(tabPath),
 12929                $deactiveTabs = (settings.deactivate == 'siblings')
 12930                  ? $tab.siblings($tabs)
 12931                  : $tabs.not($tab),
 12932                isActive      = $tab.hasClass(className.active)
 12933              ;
 12934              module.verbose('Showing tab content for', $tab);
 12935              if(!isActive) {
 12936                $tab
 12937                  .addClass(className.active)
 12938                ;
 12939                $deactiveTabs
 12940                  .removeClass(className.active + ' ' + className.loading)
 12941                ;
 12942                if($tab.length > 0) {
 12943                  settings.onVisible.call($tab[0], tabPath);
 12944                }
 12945              }
 12946            },
 12947            navigation: function(tabPath) {
 12948              var
 12949                $navigation         = module.get.navElement(tabPath),
 12950                $deactiveNavigation = (settings.deactivate == 'siblings')
 12951                  ? $navigation.siblings($allModules)
 12952                  : $allModules.not($navigation),
 12953                isActive    = $navigation.hasClass(className.active)
 12954              ;
 12955              module.verbose('Activating tab navigation for', $navigation, tabPath);
 12956              if(!isActive) {
 12957                $navigation
 12958                  .addClass(className.active)
 12959                ;
 12960                $deactiveNavigation
 12961                  .removeClass(className.active + ' ' + className.loading)
 12962                ;
 12963              }
 12964            }
 12965          },
 12966  
 12967          deactivate: {
 12968            all: function() {
 12969              module.deactivate.navigation();
 12970              module.deactivate.tabs();
 12971            },
 12972            navigation: function() {
 12973              $allModules
 12974                .removeClass(className.active)
 12975              ;
 12976            },
 12977            tabs: function() {
 12978              $tabs
 12979                .removeClass(className.active + ' ' + className.loading)
 12980              ;
 12981            }
 12982          },
 12983  
 12984          is: {
 12985            tab: function(tabName) {
 12986              return (tabName !== undefined)
 12987                ? ( module.get.tabElement(tabName).length > 0 )
 12988                : false
 12989              ;
 12990            }
 12991          },
 12992  
 12993          get: {
 12994            initialPath: function() {
 12995              return $allModules.eq(0).data(metadata.tab) || $tabs.eq(0).data(metadata.tab);
 12996            },
 12997            path: function() {
 12998              return $.address.value();
 12999            },
 13000            // adds default tabs to tab path
 13001            defaultPathArray: function(tabPath) {
 13002              return module.utilities.pathToArray( module.get.defaultPath(tabPath) );
 13003            },
 13004            defaultPath: function(tabPath) {
 13005              var
 13006                $defaultNav = $allModules.filter('[data-' + metadata.tab + '^="' + module.escape.string(tabPath) + '/"]').eq(0),
 13007                defaultTab  = $defaultNav.data(metadata.tab) || false
 13008              ;
 13009              if( defaultTab ) {
 13010                module.debug('Found default tab', defaultTab);
 13011                if(recursionDepth < settings.maxDepth) {
 13012                  recursionDepth++;
 13013                  return module.get.defaultPath(defaultTab);
 13014                }
 13015                module.error(error.recursion);
 13016              }
 13017              else {
 13018                module.debug('No default tabs found for', tabPath, $tabs);
 13019              }
 13020              recursionDepth = 0;
 13021              return tabPath;
 13022            },
 13023            navElement: function(tabPath) {
 13024              tabPath = tabPath || activeTabPath;
 13025              return $allModules.filter('[data-' + metadata.tab + '="' + module.escape.string(tabPath) + '"]');
 13026            },
 13027            tabElement: function(tabPath) {
 13028              var
 13029                $fullPathTab,
 13030                $simplePathTab,
 13031                tabPathArray,
 13032                lastTab
 13033              ;
 13034              tabPath        = tabPath || activeTabPath;
 13035              tabPathArray   = module.utilities.pathToArray(tabPath);
 13036              lastTab        = module.utilities.last(tabPathArray);
 13037              $fullPathTab   = $tabs.filter('[data-' + metadata.tab + '="' + module.escape.string(tabPath) + '"]');
 13038              $simplePathTab = $tabs.filter('[data-' + metadata.tab + '="' + module.escape.string(lastTab) + '"]');
 13039              return ($fullPathTab.length > 0)
 13040                ? $fullPathTab
 13041                : $simplePathTab
 13042              ;
 13043            },
 13044            tab: function() {
 13045              return activeTabPath;
 13046            }
 13047          },
 13048  
 13049          determine: {
 13050            activeTab: function() {
 13051              var activeTab = null;
 13052  
 13053              $tabs.each(function(_index, tab) {
 13054                var $tab = $(tab);
 13055  
 13056                if( $tab.hasClass(className.active) ) {
 13057                  var
 13058                    tabPath = $(this).data(metadata.tab),
 13059                    $anchor = $allModules.filter('[data-' + metadata.tab + '="' + module.escape.string(tabPath) + '"]')
 13060                  ;
 13061  
 13062                  if( $anchor.hasClass(className.active) ) {
 13063                    activeTab = tabPath;
 13064                  }
 13065                }
 13066              });
 13067  
 13068              return activeTab;
 13069            }
 13070          },
 13071  
 13072          utilities: {
 13073            filterArray: function(keepArray, removeArray) {
 13074              return $.grep(keepArray, function(keepValue) {
 13075                return ( $.inArray(keepValue, removeArray) == -1);
 13076              });
 13077            },
 13078            last: function(array) {
 13079              return Array.isArray(array)
 13080                ? array[ array.length - 1]
 13081                : false
 13082              ;
 13083            },
 13084            pathToArray: function(pathName) {
 13085              if(pathName === undefined) {
 13086                pathName = activeTabPath;
 13087              }
 13088              return typeof pathName == 'string'
 13089                ? pathName.split('/')
 13090                : [pathName]
 13091              ;
 13092            },
 13093            arrayToPath: function(pathArray) {
 13094              return Array.isArray(pathArray)
 13095                ? pathArray.join('/')
 13096                : false
 13097              ;
 13098            }
 13099          },
 13100  
 13101          setting: function(name, value) {
 13102            module.debug('Changing setting', name, value);
 13103            if( $.isPlainObject(name) ) {
 13104              $.extend(true, settings, name);
 13105            }
 13106            else if(value !== undefined) {
 13107              if($.isPlainObject(settings[name])) {
 13108                $.extend(true, settings[name], value);
 13109              }
 13110              else {
 13111                settings[name] = value;
 13112              }
 13113            }
 13114            else {
 13115              return settings[name];
 13116            }
 13117          },
 13118          internal: function(name, value) {
 13119            if( $.isPlainObject(name) ) {
 13120              $.extend(true, module, name);
 13121            }
 13122            else if(value !== undefined) {
 13123              module[name] = value;
 13124            }
 13125            else {
 13126              return module[name];
 13127            }
 13128          },
 13129          debug: function() {
 13130            if(!settings.silent && settings.debug) {
 13131              if(settings.performance) {
 13132                module.performance.log(arguments);
 13133              }
 13134              else {
 13135                module.debug = Function.prototype.bind.call(console.info, console, settings.name + ':');
 13136                module.debug.apply(console, arguments);
 13137              }
 13138            }
 13139          },
 13140          verbose: function() {
 13141            if(!settings.silent && settings.verbose && settings.debug) {
 13142              if(settings.performance) {
 13143                module.performance.log(arguments);
 13144              }
 13145              else {
 13146                module.verbose = Function.prototype.bind.call(console.info, console, settings.name + ':');
 13147                module.verbose.apply(console, arguments);
 13148              }
 13149            }
 13150          },
 13151          error: function() {
 13152            if(!settings.silent) {
 13153              module.error = Function.prototype.bind.call(console.error, console, settings.name + ':');
 13154              module.error.apply(console, arguments);
 13155            }
 13156          },
 13157          performance: {
 13158            log: function(message) {
 13159              var
 13160                currentTime,
 13161                executionTime,
 13162                previousTime
 13163              ;
 13164              if(settings.performance) {
 13165                currentTime   = new Date().getTime();
 13166                previousTime  = time || currentTime;
 13167                executionTime = currentTime - previousTime;
 13168                time          = currentTime;
 13169                performance.push({
 13170                  'Name'           : message[0],
 13171                  'Arguments'      : [].slice.call(message, 1) || '',
 13172                  'Element'        : element,
 13173                  'Execution Time' : executionTime
 13174                });
 13175              }
 13176              clearTimeout(module.performance.timer);
 13177              module.performance.timer = setTimeout(module.performance.display, 500);
 13178            },
 13179            display: function() {
 13180              var
 13181                title = settings.name + ':',
 13182                totalTime = 0
 13183              ;
 13184              time = false;
 13185              clearTimeout(module.performance.timer);
 13186              $.each(performance, function(index, data) {
 13187                totalTime += data['Execution Time'];
 13188              });
 13189              title += ' ' + totalTime + 'ms';
 13190              if(moduleSelector) {
 13191                title += ' \'' + moduleSelector + '\'';
 13192              }
 13193              if( (console.group !== undefined || console.table !== undefined) && performance.length > 0) {
 13194                console.groupCollapsed(title);
 13195                if(console.table) {
 13196                  console.table(performance);
 13197                }
 13198                else {
 13199                  $.each(performance, function(index, data) {
 13200                    console.log(data['Name'] + ': ' + data['Execution Time']+'ms');
 13201                  });
 13202                }
 13203                console.groupEnd();
 13204              }
 13205              performance = [];
 13206            }
 13207          },
 13208          invoke: function(query, passedArguments, context) {
 13209            var
 13210              object = instance,
 13211              maxDepth,
 13212              found,
 13213              response
 13214            ;
 13215            passedArguments = passedArguments || queryArguments;
 13216            context         = element         || context;
 13217            if(typeof query == 'string' && object !== undefined) {
 13218              query    = query.split(/[\. ]/);
 13219              maxDepth = query.length - 1;
 13220              $.each(query, function(depth, value) {
 13221                var camelCaseValue = (depth != maxDepth)
 13222                  ? value + query[depth + 1].charAt(0).toUpperCase() + query[depth + 1].slice(1)
 13223                  : query
 13224                ;
 13225                if( $.isPlainObject( object[camelCaseValue] ) && (depth != maxDepth) ) {
 13226                  object = object[camelCaseValue];
 13227                }
 13228                else if( object[camelCaseValue] !== undefined ) {
 13229                  found = object[camelCaseValue];
 13230                  return false;
 13231                }
 13232                else if( $.isPlainObject( object[value] ) && (depth != maxDepth) ) {
 13233                  object = object[value];
 13234                }
 13235                else if( object[value] !== undefined ) {
 13236                  found = object[value];
 13237                  return false;
 13238                }
 13239                else {
 13240                  module.error(error.method, query);
 13241                  return false;
 13242                }
 13243              });
 13244            }
 13245            if ( $.isFunction( found ) ) {
 13246              response = found.apply(context, passedArguments);
 13247            }
 13248            else if(found !== undefined) {
 13249              response = found;
 13250            }
 13251            if(Array.isArray(returnedValue)) {
 13252              returnedValue.push(response);
 13253            }
 13254            else if(returnedValue !== undefined) {
 13255              returnedValue = [returnedValue, response];
 13256            }
 13257            else if(response !== undefined) {
 13258              returnedValue = response;
 13259            }
 13260            return found;
 13261          }
 13262        };
 13263        if(methodInvoked) {
 13264          if(instance === undefined) {
 13265            module.initialize();
 13266          }
 13267          module.invoke(query);
 13268        }
 13269        else {
 13270          if(instance !== undefined) {
 13271            instance.invoke('destroy');
 13272          }
 13273          module.initialize();
 13274        }
 13275      })
 13276    ;
 13277    return (returnedValue !== undefined)
 13278      ? returnedValue
 13279      : this
 13280    ;
 13281  
 13282  };
 13283  
 13284  // shortcut for tabbed content with no defined navigation
 13285  $.tab = function() {
 13286    $(window).tab.apply(this, arguments);
 13287  };
 13288  
 13289  $.fn.tab.settings = {
 13290  
 13291    name            : 'Tab',
 13292    namespace       : 'tab',
 13293  
 13294    silent          : false,
 13295    debug           : false,
 13296    verbose         : false,
 13297    performance     : true,
 13298  
 13299    auto            : false,      // uses pjax style endpoints fetching content from same url with remote-content headers
 13300    history         : false,      // use browser history
 13301    historyType     : 'hash',     // #/ or html5 state
 13302    path            : false,      // base path of url
 13303  
 13304    context         : false,      // specify a context that tabs must appear inside
 13305    childrenOnly    : false,      // use only tabs that are children of context
 13306    maxDepth        : 25,         // max depth a tab can be nested
 13307  
 13308    deactivate      : 'siblings', // whether tabs should deactivate sibling menu elements or all elements initialized together
 13309  
 13310    alwaysRefresh   : false,      // load tab content new every tab click
 13311    cache           : true,       // cache the content requests to pull locally
 13312    loadOnce        : false,      // Whether tab data should only be loaded once when using remote content
 13313    cacheType       : 'response', // Whether to cache exact response, or to html cache contents after scripts execute
 13314    ignoreFirstLoad : false,      // don't load remote content on first load
 13315  
 13316    apiSettings     : false,      // settings for api call
 13317    evaluateScripts : 'once',     // whether inline scripts should be parsed (true/false/once). Once will not re-evaluate on cached content
 13318    autoTabActivation: true,      // whether a non existing active tab will auto activate the first available tab
 13319  
 13320    onFirstLoad : function(tabPath, parameterArray, historyEvent) {}, // called first time loaded
 13321    onLoad      : function(tabPath, parameterArray, historyEvent) {}, // called on every load
 13322    onVisible   : function(tabPath, parameterArray, historyEvent) {}, // called every time tab visible
 13323    onRequest   : function(tabPath, parameterArray, historyEvent) {}, // called ever time a tab beings loading remote content
 13324  
 13325    templates : {
 13326      determineTitle: function(tabArray) {} // returns page title for path
 13327    },
 13328  
 13329    error: {
 13330      api        : 'You attempted to load content without API module',
 13331      method     : 'The method you called is not defined',
 13332      missingTab : 'Activated tab cannot be found. Tabs are case-sensitive.',
 13333      noContent  : 'The tab you specified is missing a content url.',
 13334      path       : 'History enabled, but no path was specified',
 13335      recursion  : 'Max recursive depth reached',
 13336      legacyInit : 'onTabInit has been renamed to onFirstLoad in 2.0, please adjust your code.',
 13337      legacyLoad : 'onTabLoad has been renamed to onLoad in 2.0. Please adjust your code',
 13338      state      : 'History requires Asual\'s Address library <https://github.com/asual/jquery-address>'
 13339    },
 13340  
 13341    regExp : {
 13342      escape   : /[-[\]{}()*+?.,\\^$|#\s:=@]/g
 13343    },
 13344  
 13345    metadata : {
 13346      tab    : 'tab',
 13347      loaded : 'loaded',
 13348      promise: 'promise'
 13349    },
 13350  
 13351    className   : {
 13352      loading : 'loading',
 13353      active  : 'active'
 13354    },
 13355  
 13356    selector    : {
 13357      tabs : '.ui.tab',
 13358      ui   : '.ui'
 13359    }
 13360  
 13361  };
 13362  
 13363  })( jQuery, window, document );