text: /*
   * # Semantic - API
   * http://github.com/semantic-org/semantic-ui/
   *
   *
   * Copyright 2014 Contributor
   * Released under the MIT license
   * http://opensource.org/licenses/MIT
   *
   */
  
  ;(function ( $, window, document, undefined ) {
  
  $.api = $.fn.api = function(parameters) {
  
    var
      // use window context if none specified
      $allModules     = $.isFunction(this)
          ? $(window)
          : $(this),
      moduleSelector = $allModules.selector || '',
      time           = new Date().getTime(),
      performance    = [],
  
      query          = arguments[0],
      methodInvoked  = (typeof query == 'string'),
      queryArguments = [].slice.call(arguments, 1),
  
      returnedValue
    ;
  
    $allModules
      .each(function() {
        var
          settings          = ( $.isPlainObject(parameters) )
            ? $.extend(true, {}, $.fn.api.settings, parameters)
            : $.extend({}, $.fn.api.settings),
  
          // internal aliases
          namespace       = settings.namespace,
          metadata        = settings.metadata,
          selector        = settings.selector,
          error           = settings.error,
          className       = settings.className,
  
          // define namespaces for modules
          eventNamespace  = '.' + namespace,
          moduleNamespace = 'module-' + namespace,
  
          // element that creates request
          $module         = $(this),
          $form           = $module.closest(selector.form),
  
          // context used for state
          $context        = (settings.stateContext)
            ? $(settings.stateContext)
            : $module,
  
          // request details
          ajaxSettings,
          requestSettings,
          url,
          data,
  
          // standard module
          element         = this,
          context         = $context.get(),
          instance        = $module.data(moduleNamespace),
          module
        ;
  
        module = {
  
          initialize: function() {
            var
              triggerEvent = module.get.event()
            ;
            // bind events
            if(!methodInvoked) {
              if( triggerEvent ) {
                module.debug('Attaching API events to element', triggerEvent);
                $module
                  .on(triggerEvent + eventNamespace, module.event.trigger)
                ;
              }
              else {
                module.query();
              }
            }
            module.instantiate();
          },
  
          instantiate: function() {
            module.verbose('Storing instance of module', module);
            instance = module;
            $module
              .data(moduleNamespace, instance)
            ;
          },
  
          destroy: function() {
            module.verbose('Destroying previous module for', element);
            $module
              .removeData(moduleNamespace)
              .off(eventNamespace)
            ;
          },
  
          query: function() {
  
            if(module.is.disabled()) {
              module.debug('Element is disabled API request aborted');
              return;
            }
            // determine if an api event already occurred
            if(module.is.loading() && settings.throttle === 0 ) {
              module.debug('Cancelling request, previous request is still pending');
              return;
            }
  
            // pass element metadata to url (value, text)
            if(settings.defaultData) {
              $.extend(true, settings.urlData, module.get.defaultData());
            }
  
            // Add form content
            if(settings.serializeForm !== false || $context.is('form')) {
              if(settings.serializeForm == 'json') {
                $.extend(true, settings.data, module.get.formData());
              }
              else {
                settings.data = module.get.formData();
              }
            }
  
            // call beforesend and get any settings changes
            requestSettings = module.get.settings();
  
            // check if beforesend cancelled request
            if(requestSettings === false) {
              module.error(error.beforeSend);
              return;
            }
  
            if(settings.url) {
              // override with url if specified
              module.debug('Using specified url', url);
              url = module.add.urlData( settings.url );
            }
            else {
              // otherwise find url from api endpoints
              url = module.add.urlData( module.get.templateURL() );
              module.debug('Added URL Data to url', url);
            }
  
            // exit conditions reached, missing url parameters
            if( !url ) {
              if($module.is('form')) {
                module.debug('No url or action specified, defaulting to form action');
                url = $module.attr('action');
              }
              else {
                module.error(error.missingURL, settings.action);
                return;
              }
            }
  
            // add loading state
            module.set.loading();
  
            // look for jQuery ajax parameters in settings
            ajaxSettings = $.extend(true, {}, settings, {
              type       : settings.method || settings.type,
              data       : data,
              url        : settings.base + url,
              beforeSend : settings.beforeXHR,
              success    : function() {},
              failure    : function() {},
              complete   : function() {}
            });
  
            module.verbose('Creating AJAX request with settings', ajaxSettings);
  
            if( !module.is.loading() ) {
              module.request = module.create.request();
              module.xhr = module.create.xhr();
            }
            else {
              // throttle additional requests
              module.timer = setTimeout(function() {
                module.request = module.create.request();
                module.xhr = module.create.xhr();
              }, settings.throttle);
            }
  
          },
  
  
          is: {
            disabled: function() {
              return ($module.filter(settings.filter).size() > 0);
            },
            loading: function() {
              return (module.request && module.request.state() == 'pending');
            }
          },
  
          was: {
            succesful: function() {
              return (module.request && module.request.state() == 'resolved');
            },
            failure: function() {
              return (module.request && module.request.state() == 'rejected');
            },
            complete: function() {
              return (module.request && (module.request.state() == 'resolved' || module.request.state() == 'rejected') );
            }
          },
  
          add: {
            urlData: function(url, urlData) {
              var
                requiredVariables,
                optionalVariables
              ;
              if(url) {
                requiredVariables = url.match(settings.regExp.required);
                optionalVariables = url.match(settings.regExp.optional);
                urlData           = urlData || settings.urlData;
                if(requiredVariables) {
                  module.debug('Looking for required URL variables', requiredVariables);
                  $.each(requiredVariables, function(index, templatedString) {
                    var
                      // allow legacy {$var} style
                      variable = (templatedString.indexOf('$') !== -1)
                        ? templatedString.substr(2, templatedString.length - 3)
                        : templatedString.substr(1, templatedString.length - 2),
                      value   = ($.isPlainObject(urlData) && urlData[variable] !== undefined)
                        ? urlData[variable]
                        : ($module.data(variable) !== undefined)
                          ? $module.data(variable)
                          : ($context.data(variable) !== undefined)
                            ? $context.data(variable)
                            : urlData[variable]
                    ;
                    // remove value
                    if(value === undefined) {
                      module.error(error.requiredParameter, variable, url);
                      url = false;
                      return false;
                    }
                    else {
                      module.verbose('Found required variable', variable, value);
                      url = url.replace(templatedString, value);
                    }
                  });
                }
                if(optionalVariables) {
                  module.debug('Looking for optional URL variables', requiredVariables);
                  $.each(optionalVariables, function(index, templatedString) {
                    var
                      // allow legacy {/$var} style
                      variable = (templatedString.indexOf('$') !== -1)
                        ? templatedString.substr(3, templatedString.length - 4)
                        : templatedString.substr(2, templatedString.length - 3),
                      value   = ($.isPlainObject(urlData) && urlData[variable] !== undefined)
                        ? urlData[variable]
                        : ($module.data(variable) !== undefined)
                          ? $module.data(variable)
                          : ($context.data(variable) !== undefined)
                            ? $context.data(variable)
                            : urlData[variable]
                    ;
                    // optional replacement
                    if(value !== undefined) {
                      module.verbose('Optional variable Found', variable, value);
                      url = url.replace(templatedString, value);
                    }
                    else {
                      module.verbose('Optional variable not found', variable);
                      // remove preceding slash if set
                      if(url.indexOf('/' + templatedString) !== -1) {
                        url = url.replace('/' + templatedString, '');
                      }
                      else {
                        url = url.replace(templatedString, '');
                      }
                    }
                  });
                }
              }
              return url;
            }
          },
  
          event: {
            trigger: function(event) {
              module.query();
              if(event.type == 'submit' || event.type == 'click') {
                event.preventDefault();
              }
            },
            xhr: {
              always: function() {
                // calculate if loading time was below minimum threshold
              },
              done: function(response) {
                var
                  context      = this,
                  elapsedTime  = (new Date().getTime() - time),
                  timeLeft     = (settings.loadingDuration - elapsedTime)
                ;
                timeLeft = (timeLeft > 0)
                  ? timeLeft
                  : 0
                ;
                setTimeout(function() {
                  module.request.resolveWith(context, [response]);
                }, timeLeft);
              },
              fail: function(xhr, status, httpMessage) {
                var
                  context     = this,
                  elapsedTime = (new Date().getTime() - time),
                  timeLeft    = (settings.loadingDuration - elapsedTime)
                ;
                timeLeft = (timeLeft > 0)
                  ? timeLeft
                  : 0
                ;
                // page triggers abort on navigation, dont show error
                setTimeout(function() {
                  if(status !== 'abort') {
                    module.request.rejectWith(context, [xhr, status, httpMessage]);
                  }
                  else {
                    module.reset();
                  }
                }, timeLeft);
              }
            },
            request: {
              complete: function(response) {
                module.remove.loading();
                $.proxy(settings.onComplete, context)(response, $module);
              },
              done: function(response) {
                module.debug('API Response Received', response);
                if(settings.dataType == 'json') {
                  if( $.isFunction(settings.successTest) ) {
                    module.debug('Checking JSON returned success', settings.successTest, response);
                    if( settings.successTest(response) ) {
                      $.proxy(settings.onSuccess, context)(response, $module);
                    }
                    else {
                      module.debug('JSON test specified by user and response failed', response);
                      $.proxy(settings.onFailure, context)(response, $module);
                    }
                  }
                  else {
                    $.proxy(settings.onSuccess, context)(response, $module);
                  }
                }
                else {
                  $.proxy(settings.onSuccess, context)(response, $module);
                }
              },
              error: function(xhr, status, httpMessage) {
                var
                  errorMessage = (settings.error[status] !== undefined)
                    ? settings.error[status]
                    : httpMessage,
                  response
                ;
                // let em know unless request aborted
                if(xhr !== undefined) {
                  // readyState 4 = done, anything less is not really sent
                  if(xhr.readyState !== undefined && xhr.readyState == 4) {
  
                    // if http status code returned and json returned error, look for it
                    if( xhr.status != 200 && httpMessage !== undefined && httpMessage !== '') {
                      module.error(error.statusMessage + httpMessage);
                    }
                    else {
                      if(status == 'error' && settings.dataType == 'json') {
                        try {
                          response = $.parseJSON(xhr.responseText);
                          if(response && response.error !== undefined) {
                            errorMessage = response.error;
                          }
                        }
                        catch(e) {
                          module.error(error.JSONParse);
                        }
                      }
                    }
                    module.remove.loading();
                    module.set.error();
                    // show error state only for duration specified in settings
                    if(settings.errorDuration) {
                      setTimeout(module.remove.error, settings.errorDuration);
                    }
                    module.debug('API Request error:', errorMessage);
                    $.proxy(settings.onError, context)(errorMessage, context);
                  }
                  else {
                    $.proxy(settings.onAbort, context)(errorMessage, context);
                    module.debug('Request Aborted (Most likely caused by page change or CORS Policy)', status, httpMessage);
                  }
                }
              }
            }
          },
  
          create: {
            request: function() {
              return $.Deferred()
                .always(module.event.request.complete)
                .done(module.event.request.done)
                .fail(module.event.request.error)
              ;
            },
            xhr: function() {
              $.ajax(ajaxSettings)
                .always(module.event.xhr.always)
                .done(module.event.xhr.done)
                .fail(module.event.xhr.fail)
              ;
            }
          },
  
          set: {
            error: function() {
              module.verbose('Adding error state to element', $context);
              $context.addClass(className.error);
            },
            loading: function() {
              module.verbose('Adding loading state to element', $context);
              $context.addClass(className.loading);
            }
          },
  
          remove: {
            error: function() {
              module.verbose('Removing error state from element', $context);
              $context.removeClass(className.error);
            },
            loading: function() {
              module.verbose('Removing loading state from element', $context);
              $context.removeClass(className.loading);
            }
          },
  
          get: {
            request: function() {
              return module.request || false;
            },
            xhr: function() {
              return module.xhr || false;
            },
            settings: function() {
              var
                runSettings
              ;
              runSettings = $.proxy(settings.beforeSend, $module)(settings);
              if(runSettings) {
                if(runSettings.success !== undefined) {
                  module.debug('Legacy success callback detected', runSettings);
                  module.error(error.legacyParameters, runSettings.success);
                  runSettings.onSuccess = runSettings.success;
                }
                if(runSettings.failure !== undefined) {
                  module.debug('Legacy failure callback detected', runSettings);
                  module.error(error.legacyParameters, runSettings.failure);
                  runSettings.onFailure = runSettings.failure;
                }
                if(runSettings.complete !== undefined) {
                  module.debug('Legacy complete callback detected', runSettings);
                  module.error(error.legacyParameters, runSettings.complete);
                  runSettings.onComplete = runSettings.complete;
                }
              }
              if(runSettings === undefined) {
                module.error(error.noReturnedValue);
              }
              return (runSettings !== undefined)
                ? runSettings
                : settings
              ;
            },
            defaultData: function() {
              var
                data = {}
              ;
              if( !$.isWindow(element) ) {
                if( $module.is('input') ) {
                  data.value = $module.val();
                }
                else if( $module.is('form') ) {
  
                }
                else {
                  data.text = $module.text();
                }
              }
              return data;
            },
            event: function() {
              if( $.isWindow(element) || settings.on == 'now' ) {
                module.debug('API called without element, no events attached');
                return false;
              }
              else if(settings.on == 'auto') {
                if( $module.is('input') ) {
                  return (element.oninput !== undefined)
                    ? 'input'
                    : (element.onpropertychange !== undefined)
                      ? 'propertychange'
                      : 'keyup'
                  ;
                }
                else if( $module.is('form') ) {
                  return 'submit';
                }
                else {
                  return 'click';
                }
              }
              else {
                return settings.on;
              }
            },
            formData: function() {
              var
                formData
              ;
              if($(this).serializeObject() !== undefined) {
                formData = $form.serializeObject();
              }
              else {
                module.error(error.missingSerialize);
                formData = $form.serialize();
              }
              module.debug('Retrieved form data', formData);
              return formData;
            },
            templateURL: function(action) {
              var
                url
              ;
              action = action || $module.data(settings.metadata.action) || settings.action || false;
              if(action) {
                module.debug('Looking up url for action', action, settings.api);
                if(settings.api[action] !== undefined) {
                  url = settings.api[action];
                  module.debug('Found template url', url);
                }
                else {
                  module.error(error.missingAction, settings.action, settings.api);
                }
              }
              return url;
            }
          },
  
          // reset state
          reset: function() {
            module.remove.error();
            module.remove.loading();
          },
  
          setting: function(name, value) {
            module.debug('Changing setting', name, value);
            if( $.isPlainObject(name) ) {
              $.extend(true, settings, name);
            }
            else if(value !== undefined) {
              settings[name] = value;
            }
            else {
              return settings[name];
            }
          },
          internal: function(name, value) {
            if( $.isPlainObject(name) ) {
              $.extend(true, module, name);
            }
            else if(value !== undefined) {
              module[name] = value;
            }
            else {
              return module[name];
            }
          },
          debug: function() {
            if(settings.debug) {
              if(settings.performance) {
                module.performance.log(arguments);
              }
              else {
                module.debug = Function.prototype.bind.call(console.info, console, settings.name + ':');
                module.debug.apply(console, arguments);
              }
            }
          },
          verbose: function() {
            if(settings.verbose && settings.debug) {
              if(settings.performance) {
                module.performance.log(arguments);
              }
              else {
                module.verbose = Function.prototype.bind.call(console.info, console, settings.name + ':');
                module.verbose.apply(console, arguments);
              }
            }
          },
          error: function() {
            module.error = Function.prototype.bind.call(console.error, console, settings.name + ':');
            module.error.apply(console, arguments);
          },
          performance: {
            log: function(message) {
              var
                currentTime,
                executionTime,
                previousTime
              ;
              if(settings.performance) {
                currentTime   = new Date().getTime();
                previousTime  = time || currentTime;
                executionTime = currentTime - previousTime;
                time          = currentTime;
                performance.push({
                  'Name'           : message[0],
                  'Arguments'      : [].slice.call(message, 1) || '',
                  //'Element'        : element,
                  'Execution Time' : executionTime
                });
              }
              clearTimeout(module.performance.timer);
              module.performance.timer = setTimeout(module.performance.display, 100);
            },
            display: function() {
              var
                title = settings.name + ':',
                totalTime = 0
              ;
              time = false;
              clearTimeout(module.performance.timer);
              $.each(performance, function(index, data) {
                totalTime += data['Execution Time'];
              });
              title += ' ' + totalTime + 'ms';
              if(moduleSelector) {
                title += ' \'' + moduleSelector + '\'';
              }
              if( (console.group !== undefined || console.table !== undefined) && performance.length > 0) {
                console.groupCollapsed(title);
                if(console.table) {
                  console.table(performance);
                }
                else {
                  $.each(performance, function(index, data) {
                    console.log(data['Name'] + ': ' + data['Execution Time']+'ms');
                  });
                }
                console.groupEnd();
              }
              performance = [];
            }
          },
          invoke: function(query, passedArguments, context) {
            var
              object = instance,
              maxDepth,
              found,
              response
            ;
            passedArguments = passedArguments || queryArguments;
            context         = element         || context;
            if(typeof query == 'string' && object !== undefined) {
              query    = query.split(/[\. ]/);
              maxDepth = query.length - 1;
              $.each(query, function(depth, value) {
                var camelCaseValue = (depth != maxDepth)
                  ? value + query[depth + 1].charAt(0).toUpperCase() + query[depth + 1].slice(1)
                  : query
                ;
                if( $.isPlainObject( object[camelCaseValue] ) && (depth != maxDepth) ) {
                  object = object[camelCaseValue];
                }
                else if( object[camelCaseValue] !== undefined ) {
                  found = object[camelCaseValue];
                  return false;
                }
                else if( $.isPlainObject( object[value] ) && (depth != maxDepth) ) {
                  object = object[value];
                }
                else if( object[value] !== undefined ) {
                  found = object[value];
                  return false;
                }
                else {
                  module.error(error.method, query);
                  return false;
                }
              });
            }
            if ( $.isFunction( found ) ) {
              response = found.apply(context, passedArguments);
            }
            else if(found !== undefined) {
              response = found;
            }
            if($.isArray(returnedValue)) {
              returnedValue.push(response);
            }
            else if(returnedValue !== undefined) {
              returnedValue = [returnedValue, response];
            }
            else if(response !== undefined) {
              returnedValue = response;
            }
            return found;
          }
        };
  
        if(methodInvoked) {
          if(instance === undefined) {
            module.initialize();
          }
          module.invoke(query);
        }
        else {
          if(instance !== undefined) {
            module.destroy();
          }
          module.initialize();
        }
      })
    ;
  
    return (returnedValue !== undefined)
      ? returnedValue
      : this
    ;
  };
  
  $.api.settings = {
  
    name            : 'API',
    namespace       : 'api',
  
    debug           : false,
    verbose         : true,
    performance     : true,
  
    // event binding
    on              : 'auto',
    filter          : '.disabled',
    stateContext    : false,
  
    // state
    loadingDuration : 0,
    errorDuration   : 2000,
  
    // templating
    action          : false,
    url             : false,
    base            : '',
  
    // data
    urlData         : {},
  
    // ui
    defaultData     : true,
    serializeForm   : false,
    throttle        : 0,
  
    // jQ ajax
    method          : 'get',
    data            : {},
    dataType        : 'json',
  
    // callbacks
    beforeSend  : function(settings) { return settings; },
    beforeXHR   : function(xhr) {},
  
    onSuccess   : function(response, $module) {},
    onComplete  : function(response, $module) {},
    onFailure   : function(errorMessage, $module) {},
    onError     : function(errorMessage, $module) {},
    onAbort     : function(errorMessage, $module) {},
  
    successTest : false,
  
    // errors
    error : {
      beforeSend        : 'The before send function has aborted the request',
      error             : 'There was an error with your request',
      exitConditions    : 'API Request Aborted. Exit conditions met',
      JSONParse         : 'JSON could not be parsed during error handling',
      legacyParameters  : 'You are using legacy API success callback names',
      missingAction     : 'API action used but no url was defined',
      missingSerialize  : 'Required dependency jquery-serialize-object missing, using basic serialize',
      missingURL        : 'No URL specified for api event',
      noReturnedValue   : 'The beforeSend callback must return a settings object, beforeSend ignored.',
      parseError        : 'There was an error parsing your request',
      requiredParameter : 'Missing a required URL parameter: ',
      statusMessage     : 'Server gave an error: ',
      timeout           : 'Your request timed out'
    },
  
    regExp  : {
      required: /\{\$*[A-z0-9]+\}/g,
      optional: /\{\/\$*[A-z0-9]+\}/g,
    },
  
    className: {
      loading : 'loading',
      error   : 'error'
    },
  
    selector: {
      form: 'form'
    },
  
    metadata: {
      action  : 'action',
      request : 'request',
      xhr     : 'xhr'
    }
  };
  
  
  $.api.settings.api = {};
  
  
  })( jQuery, window , document );