github.com/fanux/shipyard@v0.0.0-20161009071005-6515ce223235/controller/static/semantic/dist/components/state.js (about)

     1  /*!
     2   * # Semantic UI x.x - State
     3   * http://github.com/semantic-org/semantic-ui/
     4   *
     5   *
     6   * Copyright 2014 Contributors
     7   * Released under the MIT license
     8   * http://opensource.org/licenses/MIT
     9   *
    10   */
    11  
    12  ;(function ( $, window, document, undefined ) {
    13  
    14  "use strict";
    15  
    16  $.fn.state = function(parameters) {
    17    var
    18      $allModules     = $(this),
    19  
    20      moduleSelector  = $allModules.selector || '',
    21  
    22      hasTouch        = ('ontouchstart' in document.documentElement),
    23      time            = new Date().getTime(),
    24      performance     = [],
    25  
    26      query           = arguments[0],
    27      methodInvoked   = (typeof query == 'string'),
    28      queryArguments  = [].slice.call(arguments, 1),
    29  
    30      returnedValue
    31    ;
    32    $allModules
    33      .each(function() {
    34        var
    35          settings          = ( $.isPlainObject(parameters) )
    36            ? $.extend(true, {}, $.fn.state.settings, parameters)
    37            : $.extend({}, $.fn.state.settings),
    38  
    39          error           = settings.error,
    40          metadata        = settings.metadata,
    41          className       = settings.className,
    42          namespace       = settings.namespace,
    43          states          = settings.states,
    44          text            = settings.text,
    45  
    46          eventNamespace  = '.' + namespace,
    47          moduleNamespace = namespace + '-module',
    48  
    49          $module         = $(this),
    50  
    51          element         = this,
    52          instance        = $module.data(moduleNamespace),
    53  
    54          module
    55        ;
    56        module = {
    57  
    58          initialize: function() {
    59            module.verbose('Initializing module');
    60  
    61            // allow module to guess desired state based on element
    62            if(settings.automatic) {
    63              module.add.defaults();
    64            }
    65  
    66            // bind events with delegated events
    67            if(settings.context && moduleSelector !== '') {
    68              $(settings.context)
    69                .on(moduleSelector, 'mouseenter' + eventNamespace, module.change.text)
    70                .on(moduleSelector, 'mouseleave' + eventNamespace, module.reset.text)
    71                .on(moduleSelector, 'click'      + eventNamespace, module.toggle.state)
    72              ;
    73            }
    74            else {
    75              $module
    76                .on('mouseenter' + eventNamespace, module.change.text)
    77                .on('mouseleave' + eventNamespace, module.reset.text)
    78                .on('click'      + eventNamespace, module.toggle.state)
    79              ;
    80            }
    81            module.instantiate();
    82          },
    83  
    84          instantiate: function() {
    85            module.verbose('Storing instance of module', module);
    86            instance = module;
    87            $module
    88              .data(moduleNamespace, module)
    89            ;
    90          },
    91  
    92          destroy: function() {
    93            module.verbose('Destroying previous module', instance);
    94            $module
    95              .off(eventNamespace)
    96              .removeData(moduleNamespace)
    97            ;
    98          },
    99  
   100          refresh: function() {
   101            module.verbose('Refreshing selector cache');
   102            $module = $(element);
   103          },
   104  
   105          add: {
   106            defaults: function() {
   107              var
   108                userStates = parameters && $.isPlainObject(parameters.states)
   109                  ? parameters.states
   110                  : {}
   111              ;
   112              $.each(settings.defaults, function(type, typeStates) {
   113                if( module.is[type] !== undefined && module.is[type]() ) {
   114                  module.verbose('Adding default states', type, element);
   115                  $.extend(settings.states, typeStates, userStates);
   116                }
   117              });
   118            }
   119          },
   120  
   121          is: {
   122  
   123            active: function() {
   124              return $module.hasClass(className.active);
   125            },
   126            loading: function() {
   127              return $module.hasClass(className.loading);
   128            },
   129            inactive: function() {
   130              return !( $module.hasClass(className.active) );
   131            },
   132            state: function(state) {
   133              if(className[state] === undefined) {
   134                return false;
   135              }
   136              return $module.hasClass( className[state] );
   137            },
   138  
   139            enabled: function() {
   140              return !( $module.is(settings.filter.active) );
   141            },
   142            disabled: function() {
   143              return ( $module.is(settings.filter.active) );
   144            },
   145            textEnabled: function() {
   146              return !( $module.is(settings.filter.text) );
   147            },
   148  
   149            // definitions for automatic type detection
   150            button: function() {
   151              return $module.is('.button:not(a, .submit)');
   152            },
   153            input: function() {
   154              return $module.is('input');
   155            },
   156            progress: function() {
   157              return $module.is('.ui.progress');
   158            }
   159          },
   160  
   161          allow: function(state) {
   162            module.debug('Now allowing state', state);
   163            states[state] = true;
   164          },
   165          disallow: function(state) {
   166            module.debug('No longer allowing', state);
   167            states[state] = false;
   168          },
   169  
   170          allows: function(state) {
   171            return states[state] || false;
   172          },
   173  
   174          enable: function() {
   175            $module.removeClass(className.disabled);
   176          },
   177  
   178          disable: function() {
   179            $module.addClass(className.disabled);
   180          },
   181  
   182          setState: function(state) {
   183            if(module.allows(state)) {
   184              $module.addClass( className[state] );
   185            }
   186          },
   187  
   188          removeState: function(state) {
   189            if(module.allows(state)) {
   190              $module.removeClass( className[state] );
   191            }
   192          },
   193  
   194          toggle: {
   195            state: function() {
   196              var
   197                apiRequest,
   198                requestCancelled
   199              ;
   200              if( module.allows('active') && module.is.enabled() ) {
   201                module.refresh();
   202                if($.fn.api !== undefined) {
   203                  apiRequest       = $module.api('get request');
   204                  requestCancelled = $module.api('was cancelled');
   205                  if( requestCancelled ) {
   206                    module.debug('API Request cancelled by beforesend');
   207                    settings.activateTest   = function(){ return false; };
   208                    settings.deactivateTest = function(){ return false; };
   209                  }
   210                  else if(apiRequest) {
   211                    module.listenTo(apiRequest);
   212                    return;
   213                  }
   214                }
   215                module.change.state();
   216              }
   217            }
   218          },
   219  
   220          listenTo: function(apiRequest) {
   221            module.debug('API request detected, waiting for state signal', apiRequest);
   222            if(apiRequest) {
   223              if(text.loading) {
   224                module.update.text(text.loading);
   225              }
   226              $.when(apiRequest)
   227                .then(function() {
   228                  if(apiRequest.state() == 'resolved') {
   229                    module.debug('API request succeeded');
   230                    settings.activateTest   = function(){ return true; };
   231                    settings.deactivateTest = function(){ return true; };
   232                  }
   233                  else {
   234                    module.debug('API request failed');
   235                    settings.activateTest   = function(){ return false; };
   236                    settings.deactivateTest = function(){ return false; };
   237                  }
   238                  module.change.state();
   239                })
   240              ;
   241            }
   242          },
   243  
   244          // checks whether active/inactive state can be given
   245          change: {
   246  
   247            state: function() {
   248              module.debug('Determining state change direction');
   249              // inactive to active change
   250              if( module.is.inactive() ) {
   251                module.activate();
   252              }
   253              else {
   254                module.deactivate();
   255              }
   256              if(settings.sync) {
   257                module.sync();
   258              }
   259              settings.onChange.call(element);
   260            },
   261  
   262            text: function() {
   263              if( module.is.textEnabled() ) {
   264                if(module.is.disabled() ) {
   265                  module.verbose('Changing text to disabled text', text.hover);
   266                  module.update.text(text.disabled);
   267                }
   268                else if( module.is.active() ) {
   269                  if(text.hover) {
   270                    module.verbose('Changing text to hover text', text.hover);
   271                    module.update.text(text.hover);
   272                  }
   273                  else if(text.deactivate) {
   274                    module.verbose('Changing text to deactivating text', text.deactivate);
   275                    module.update.text(text.deactivate);
   276                  }
   277                }
   278                else {
   279                  if(text.hover) {
   280                    module.verbose('Changing text to hover text', text.hover);
   281                    module.update.text(text.hover);
   282                  }
   283                  else if(text.activate){
   284                    module.verbose('Changing text to activating text', text.activate);
   285                    module.update.text(text.activate);
   286                  }
   287                }
   288              }
   289            }
   290  
   291          },
   292  
   293          activate: function() {
   294            if( settings.activateTest.call(element) ) {
   295              module.debug('Setting state to active');
   296              $module
   297                .addClass(className.active)
   298              ;
   299              module.update.text(text.active);
   300              settings.onActivate.call(element);
   301            }
   302          },
   303  
   304          deactivate: function() {
   305            if( settings.deactivateTest.call(element) ) {
   306              module.debug('Setting state to inactive');
   307              $module
   308                .removeClass(className.active)
   309              ;
   310              module.update.text(text.inactive);
   311              settings.onDeactivate.call(element);
   312            }
   313          },
   314  
   315          sync: function() {
   316            module.verbose('Syncing other buttons to current state');
   317            if( module.is.active() ) {
   318              $allModules
   319                .not($module)
   320                  .state('activate');
   321            }
   322            else {
   323              $allModules
   324                .not($module)
   325                  .state('deactivate')
   326              ;
   327            }
   328          },
   329  
   330          get: {
   331            text: function() {
   332              return (settings.selector.text)
   333                ? $module.find(settings.selector.text).text()
   334                : $module.html()
   335              ;
   336            },
   337            textFor: function(state) {
   338              return text[state] || false;
   339            }
   340          },
   341  
   342          flash: {
   343            text: function(text, duration, callback) {
   344              var
   345                previousText = module.get.text()
   346              ;
   347              module.debug('Flashing text message', text, duration);
   348              text     = text     || settings.text.flash;
   349              duration = duration || settings.flashDuration;
   350              callback = callback || function() {};
   351              module.update.text(text);
   352              setTimeout(function(){
   353                module.update.text(previousText);
   354                callback.call(element);
   355              }, duration);
   356            }
   357          },
   358  
   359          reset: {
   360            // on mouseout sets text to previous value
   361            text: function() {
   362              var
   363                activeText   = text.active   || $module.data(metadata.storedText),
   364                inactiveText = text.inactive || $module.data(metadata.storedText)
   365              ;
   366              if( module.is.textEnabled() ) {
   367                if( module.is.active() && activeText) {
   368                  module.verbose('Resetting active text', activeText);
   369                  module.update.text(activeText);
   370                }
   371                else if(inactiveText) {
   372                  module.verbose('Resetting inactive text', activeText);
   373                  module.update.text(inactiveText);
   374                }
   375              }
   376            }
   377          },
   378  
   379          update: {
   380            text: function(text) {
   381              var
   382                currentText = module.get.text()
   383              ;
   384              if(text && text !== currentText) {
   385                module.debug('Updating text', text);
   386                if(settings.selector.text) {
   387                  $module
   388                    .data(metadata.storedText, text)
   389                    .find(settings.selector.text)
   390                      .text(text)
   391                  ;
   392                }
   393                else {
   394                  $module
   395                    .data(metadata.storedText, text)
   396                    .html(text)
   397                  ;
   398                }
   399              }
   400              else {
   401                module.debug('Text is already set, ignoring update', text);
   402              }
   403            }
   404          },
   405  
   406          setting: function(name, value) {
   407            module.debug('Changing setting', name, value);
   408            if( $.isPlainObject(name) ) {
   409              $.extend(true, settings, name);
   410            }
   411            else if(value !== undefined) {
   412              settings[name] = value;
   413            }
   414            else {
   415              return settings[name];
   416            }
   417          },
   418          internal: function(name, value) {
   419            if( $.isPlainObject(name) ) {
   420              $.extend(true, module, name);
   421            }
   422            else if(value !== undefined) {
   423              module[name] = value;
   424            }
   425            else {
   426              return module[name];
   427            }
   428          },
   429          debug: function() {
   430            if(settings.debug) {
   431              if(settings.performance) {
   432                module.performance.log(arguments);
   433              }
   434              else {
   435                module.debug = Function.prototype.bind.call(console.info, console, settings.name + ':');
   436                module.debug.apply(console, arguments);
   437              }
   438            }
   439          },
   440          verbose: function() {
   441            if(settings.verbose && settings.debug) {
   442              if(settings.performance) {
   443                module.performance.log(arguments);
   444              }
   445              else {
   446                module.verbose = Function.prototype.bind.call(console.info, console, settings.name + ':');
   447                module.verbose.apply(console, arguments);
   448              }
   449            }
   450          },
   451          error: function() {
   452            module.error = Function.prototype.bind.call(console.error, console, settings.name + ':');
   453            module.error.apply(console, arguments);
   454          },
   455          performance: {
   456            log: function(message) {
   457              var
   458                currentTime,
   459                executionTime,
   460                previousTime
   461              ;
   462              if(settings.performance) {
   463                currentTime   = new Date().getTime();
   464                previousTime  = time || currentTime;
   465                executionTime = currentTime - previousTime;
   466                time          = currentTime;
   467                performance.push({
   468                  'Name'           : message[0],
   469                  'Arguments'      : [].slice.call(message, 1) || '',
   470                  'Element'        : element,
   471                  'Execution Time' : executionTime
   472                });
   473              }
   474              clearTimeout(module.performance.timer);
   475              module.performance.timer = setTimeout(module.performance.display, 100);
   476            },
   477            display: function() {
   478              var
   479                title = settings.name + ':',
   480                totalTime = 0
   481              ;
   482              time = false;
   483              clearTimeout(module.performance.timer);
   484              $.each(performance, function(index, data) {
   485                totalTime += data['Execution Time'];
   486              });
   487              title += ' ' + totalTime + 'ms';
   488              if(moduleSelector) {
   489                title += ' \'' + moduleSelector + '\'';
   490              }
   491              if( (console.group !== undefined || console.table !== undefined) && performance.length > 0) {
   492                console.groupCollapsed(title);
   493                if(console.table) {
   494                  console.table(performance);
   495                }
   496                else {
   497                  $.each(performance, function(index, data) {
   498                    console.log(data['Name'] + ': ' + data['Execution Time']+'ms');
   499                  });
   500                }
   501                console.groupEnd();
   502              }
   503              performance = [];
   504            }
   505          },
   506          invoke: function(query, passedArguments, context) {
   507            var
   508              object = instance,
   509              maxDepth,
   510              found,
   511              response
   512            ;
   513            passedArguments = passedArguments || queryArguments;
   514            context         = element         || context;
   515            if(typeof query == 'string' && object !== undefined) {
   516              query    = query.split(/[\. ]/);
   517              maxDepth = query.length - 1;
   518              $.each(query, function(depth, value) {
   519                var camelCaseValue = (depth != maxDepth)
   520                  ? value + query[depth + 1].charAt(0).toUpperCase() + query[depth + 1].slice(1)
   521                  : query
   522                ;
   523                if( $.isPlainObject( object[camelCaseValue] ) && (depth != maxDepth) ) {
   524                  object = object[camelCaseValue];
   525                }
   526                else if( object[camelCaseValue] !== undefined ) {
   527                  found = object[camelCaseValue];
   528                  return false;
   529                }
   530                else if( $.isPlainObject( object[value] ) && (depth != maxDepth) ) {
   531                  object = object[value];
   532                }
   533                else if( object[value] !== undefined ) {
   534                  found = object[value];
   535                  return false;
   536                }
   537                else {
   538                  module.error(error.method, query);
   539                  return false;
   540                }
   541              });
   542            }
   543            if ( $.isFunction( found ) ) {
   544              response = found.apply(context, passedArguments);
   545            }
   546            else if(found !== undefined) {
   547              response = found;
   548            }
   549            if($.isArray(returnedValue)) {
   550              returnedValue.push(response);
   551            }
   552            else if(returnedValue !== undefined) {
   553              returnedValue = [returnedValue, response];
   554            }
   555            else if(response !== undefined) {
   556              returnedValue = response;
   557            }
   558            return found;
   559          }
   560        };
   561  
   562        if(methodInvoked) {
   563          if(instance === undefined) {
   564            module.initialize();
   565          }
   566          module.invoke(query);
   567        }
   568        else {
   569          if(instance !== undefined) {
   570            instance.invoke('destroy');
   571          }
   572          module.initialize();
   573        }
   574      })
   575    ;
   576  
   577    return (returnedValue !== undefined)
   578      ? returnedValue
   579      : this
   580    ;
   581  };
   582  
   583  $.fn.state.settings = {
   584  
   585    // module info
   586    name           : 'State',
   587  
   588    // debug output
   589    debug          : false,
   590  
   591    // verbose debug output
   592    verbose        : true,
   593  
   594    // namespace for events
   595    namespace      : 'state',
   596  
   597    // debug data includes performance
   598    performance    : true,
   599  
   600    // callback occurs on state change
   601    onActivate     : function() {},
   602    onDeactivate   : function() {},
   603    onChange       : function() {},
   604  
   605    // state test functions
   606    activateTest   : function() { return true; },
   607    deactivateTest : function() { return true; },
   608  
   609    // whether to automatically map default states
   610    automatic      : true,
   611  
   612    // activate / deactivate changes all elements instantiated at same time
   613    sync           : false,
   614  
   615    // default flash text duration, used for temporarily changing text of an element
   616    flashDuration  : 1000,
   617  
   618    // selector filter
   619    filter     : {
   620      text   : '.loading, .disabled',
   621      active : '.disabled'
   622    },
   623  
   624    context    : false,
   625  
   626    // error
   627    error: {
   628      beforeSend : 'The before send function has cancelled state change',
   629      method     : 'The method you called is not defined.'
   630    },
   631  
   632    // metadata
   633    metadata: {
   634      promise    : 'promise',
   635      storedText : 'stored-text'
   636    },
   637  
   638    // change class on state
   639    className: {
   640      active   : 'active',
   641      disabled : 'disabled',
   642      error    : 'error',
   643      loading  : 'loading',
   644      success  : 'success',
   645      warning  : 'warning'
   646    },
   647  
   648    selector: {
   649      // selector for text node
   650      text: false
   651    },
   652  
   653    defaults : {
   654      input: {
   655        disabled : true,
   656        loading  : true,
   657        active   : true
   658      },
   659      button: {
   660        disabled : true,
   661        loading  : true,
   662        active   : true,
   663      },
   664      progress: {
   665        active   : true,
   666        success  : true,
   667        warning  : true,
   668        error    : true
   669      }
   670    },
   671  
   672    states     : {
   673      active   : true,
   674      disabled : true,
   675      error    : true,
   676      loading  : true,
   677      success  : true,
   678      warning  : true
   679    },
   680  
   681    text     : {
   682      disabled   : false,
   683      flash      : false,
   684      hover      : false,
   685      active     : false,
   686      inactive   : false,
   687      activate   : false,
   688      deactivate : false
   689    }
   690  
   691  };
   692  
   693  
   694  
   695  })( jQuery, window , document );