github.com/fanux/shipyard@v0.0.0-20161009071005-6515ce223235/controller/static/semantic/src/definitions/modules/modal.js (about)

     1  /*!
     2   * # Semantic UI - Modal
     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.modal = function(parameters) {
    17    var
    18      $allModules    = $(this),
    19      $window        = $(window),
    20      $document      = $(document),
    21      $body          = $('body'),
    22  
    23      moduleSelector = $allModules.selector || '',
    24  
    25      time           = new Date().getTime(),
    26      performance    = [],
    27  
    28      query          = arguments[0],
    29      methodInvoked  = (typeof query == 'string'),
    30      queryArguments = [].slice.call(arguments, 1),
    31  
    32      requestAnimationFrame = window.requestAnimationFrame
    33        || window.mozRequestAnimationFrame
    34        || window.webkitRequestAnimationFrame
    35        || window.msRequestAnimationFrame
    36        || function(callback) { setTimeout(callback, 0); },
    37  
    38      returnedValue
    39    ;
    40  
    41    $allModules
    42      .each(function() {
    43        var
    44          settings    = ( $.isPlainObject(parameters) )
    45            ? $.extend(true, {}, $.fn.modal.settings, parameters)
    46            : $.extend({}, $.fn.modal.settings),
    47  
    48          selector        = settings.selector,
    49          className       = settings.className,
    50          namespace       = settings.namespace,
    51          error           = settings.error,
    52  
    53          eventNamespace  = '.' + namespace,
    54          moduleNamespace = 'module-' + namespace,
    55  
    56          $module         = $(this),
    57          $context        = $(settings.context),
    58          $close          = $module.find(selector.close),
    59  
    60          $allModals,
    61          $otherModals,
    62          $focusedElement,
    63          $dimmable,
    64          $dimmer,
    65  
    66          element         = this,
    67          instance        = $module.data(moduleNamespace),
    68  
    69          elementNamespace,
    70          id,
    71          observer,
    72          module
    73        ;
    74        module  = {
    75  
    76          initialize: function() {
    77            module.verbose('Initializing dimmer', $context);
    78  
    79            module.create.id();
    80            module.create.dimmer();
    81            module.refreshModals();
    82  
    83            module.verbose('Attaching close events', $close);
    84            module.bind.events();
    85            module.observeChanges();
    86            module.instantiate();
    87          },
    88  
    89          instantiate: function() {
    90            module.verbose('Storing instance of modal');
    91            instance = module;
    92            $module
    93              .data(moduleNamespace, instance)
    94            ;
    95          },
    96  
    97          create: {
    98            dimmer: function() {
    99              var
   100                defaultSettings = {
   101                  debug      : settings.debug,
   102                  dimmerName : 'modals',
   103                  duration   : {
   104                    show     : settings.duration,
   105                    hide     : settings.duration
   106                  }
   107                },
   108                dimmerSettings = $.extend(true, defaultSettings, settings.dimmerSettings)
   109              ;
   110              if($.fn.dimmer === undefined) {
   111                module.error(error.dimmer);
   112                return;
   113              }
   114              module.debug('Creating dimmer with settings', dimmerSettings);
   115              $dimmable = $context.dimmer(dimmerSettings);
   116              if(settings.detachable) {
   117                module.verbose('Modal is detachable, moving content into dimmer');
   118                $dimmable.dimmer('add content', $module);
   119              }
   120              $dimmer = $dimmable.dimmer('get dimmer');
   121            },
   122            id: function() {
   123              id = (Math.random().toString(16) + '000000000').substr(2,8);
   124              elementNamespace = '.' + id;
   125              module.verbose('Creating unique id for element', id);
   126            }
   127          },
   128  
   129          destroy: function() {
   130            module.verbose('Destroying previous modal');
   131            $module
   132              .removeData(moduleNamespace)
   133              .off(eventNamespace)
   134            ;
   135            $window.off(elementNamespace);
   136            $close.off(eventNamespace);
   137            $context.dimmer('destroy');
   138          },
   139  
   140          observeChanges: function() {
   141            if('MutationObserver' in window) {
   142              observer = new MutationObserver(function(mutations) {
   143                module.debug('DOM tree modified, refreshing');
   144                module.refresh();
   145              });
   146              observer.observe(element, {
   147                childList : true,
   148                subtree   : true
   149              });
   150              module.debug('Setting up mutation observer', observer);
   151            }
   152          },
   153  
   154          refresh: function() {
   155            module.remove.scrolling();
   156            module.cacheSizes();
   157            module.set.screenHeight();
   158            module.set.type();
   159            module.set.position();
   160          },
   161  
   162          refreshModals: function() {
   163            $otherModals = $module.siblings(selector.modal);
   164            $allModals   = $otherModals.add($module);
   165          },
   166  
   167          attachEvents: function(selector, event) {
   168            var
   169              $toggle = $(selector)
   170            ;
   171            event = $.isFunction(module[event])
   172              ? module[event]
   173              : module.toggle
   174            ;
   175            if($toggle.length > 0) {
   176              module.debug('Attaching modal events to element', selector, event);
   177              $toggle
   178                .off(eventNamespace)
   179                .on('click' + eventNamespace, event)
   180              ;
   181            }
   182            else {
   183              module.error(error.notFound, selector);
   184            }
   185          },
   186  
   187          bind: {
   188            events: function() {
   189              $close.on('click' + eventNamespace, module.event.close);
   190              $window.on('resize' + elementNamespace, module.event.resize);
   191            }
   192          },
   193  
   194          get: {
   195            id: function() {
   196              return (Math.random().toString(16) + '000000000').substr(2,8);
   197            }
   198          },
   199  
   200          event: {
   201            close: function() {
   202              module.verbose('Closing element pressed');
   203              if( $(this).is(selector.approve) ) {
   204                if(settings.onApprove.call(element) !== false) {
   205                  module.hide();
   206                }
   207                else {
   208                  module.verbose('Approve callback returned false cancelling hide');
   209                }
   210              }
   211              else if( $(this).is(selector.deny) ) {
   212                if(settings.onDeny.call(element) !== false) {
   213                  module.hide();
   214                }
   215                else {
   216                  module.verbose('Deny callback returned false cancelling hide');
   217                }
   218              }
   219              else {
   220                module.hide();
   221              }
   222            },
   223            click: function(event) {
   224              if( $(event.target).closest($module).length === 0 ) {
   225                module.debug('Dimmer clicked, hiding all modals');
   226                if( module.is.active() ) {
   227                  module.remove.clickaway();
   228                  if(settings.allowMultiple) {
   229                    module.hide();
   230                  }
   231                  else {
   232                    module.hideAll();
   233                  }
   234                }
   235              }
   236            },
   237            debounce: function(method, delay) {
   238              clearTimeout(module.timer);
   239              module.timer = setTimeout(method, delay);
   240            },
   241            keyboard: function(event) {
   242              var
   243                keyCode   = event.which,
   244                escapeKey = 27
   245              ;
   246              if(keyCode == escapeKey) {
   247                if(settings.closable) {
   248                  module.debug('Escape key pressed hiding modal');
   249                  module.hide();
   250                }
   251                else {
   252                  module.debug('Escape key pressed, but closable is set to false');
   253                }
   254                event.preventDefault();
   255              }
   256            },
   257            resize: function() {
   258              if( $dimmable.dimmer('is active') ) {
   259                requestAnimationFrame(module.refresh);
   260              }
   261            }
   262          },
   263  
   264          toggle: function() {
   265            if( module.is.active() || module.is.animating() ) {
   266              module.hide();
   267            }
   268            else {
   269              module.show();
   270            }
   271          },
   272  
   273          show: function(callback) {
   274            callback = $.isFunction(callback)
   275              ? callback
   276              : function(){}
   277            ;
   278            module.refreshModals();
   279            module.showModal(callback);
   280          },
   281  
   282          hide: function(callback) {
   283            callback = $.isFunction(callback)
   284              ? callback
   285              : function(){}
   286            ;
   287            module.refreshModals();
   288            module.hideModal(callback);
   289          },
   290  
   291          showModal: function(callback) {
   292            callback = $.isFunction(callback)
   293              ? callback
   294              : function(){}
   295            ;
   296            if( module.is.animating() || !module.is.active() ) {
   297  
   298              module.showDimmer();
   299              module.cacheSizes();
   300              module.set.position();
   301              module.set.screenHeight();
   302              module.set.type();
   303              module.set.clickaway();
   304  
   305              if( !settings.allowMultiple && $otherModals.filter('.' + className.active).length > 0) {
   306                module.debug('Other modals visible, queueing show animation');
   307                module.hideOthers(module.showModal);
   308              }
   309              else {
   310                settings.onShow.call(element);
   311                if(settings.transition && $.fn.transition !== undefined && $module.transition('is supported')) {
   312                  module.debug('Showing modal with css animations');
   313                  $module
   314                    .transition({
   315                      debug       : settings.debug,
   316                      animation   : settings.transition + ' in',
   317                      queue       : settings.queue,
   318                      duration    : settings.duration,
   319                      useFailSafe : true,
   320                      onComplete : function() {
   321                        settings.onVisible.apply(element);
   322                        module.add.keyboardShortcuts();
   323                        module.save.focus();
   324                        module.set.active();
   325                        module.set.autofocus();
   326                        callback();
   327                      }
   328                    })
   329                  ;
   330                }
   331                else {
   332                  module.debug('Showing modal with javascript');
   333                  $module
   334                    .fadeIn(settings.duration, settings.easing, function() {
   335                      settings.onVisible.apply(element);
   336                      module.add.keyboardShortcuts();
   337                      module.save.focus();
   338                      module.set.active();
   339                      callback();
   340                    })
   341                  ;
   342                }
   343              }
   344            }
   345            else {
   346              module.debug('Modal is already visible');
   347            }
   348          },
   349  
   350          hideModal: function(callback, keepDimmed) {
   351            callback = $.isFunction(callback)
   352              ? callback
   353              : function(){}
   354            ;
   355            module.debug('Hiding modal');
   356            settings.onHide.call(element);
   357  
   358            if( module.is.animating() || module.is.active() ) {
   359              if(settings.transition && $.fn.transition !== undefined && $module.transition('is supported')) {
   360                module.remove.active();
   361                $module
   362                  .transition({
   363                    debug       : settings.debug,
   364                    animation   : settings.transition + ' out',
   365                    queue       : settings.queue,
   366                    duration    : settings.duration,
   367                    useFailSafe : true,
   368                    onStart     : function() {
   369                      if(!module.othersActive() && !keepDimmed) {
   370                        module.hideDimmer();
   371                      }
   372                      module.remove.keyboardShortcuts();
   373                    },
   374                    onComplete : function() {
   375                      settings.onHidden.call(element);
   376                      module.restore.focus();
   377                      callback();
   378                    }
   379                  })
   380                ;
   381              }
   382              else {
   383                module.remove.active();
   384                if( !module.othersActive() ) {
   385                  module.hideDimmer();
   386                }
   387                module.remove.keyboardShortcuts();
   388                $module
   389                  .fadeOut(settings.duration, settings.easing, function() {
   390                    settings.onHidden.call(element);
   391                    module.restore.focus();
   392                    callback();
   393                  })
   394                ;
   395              }
   396            }
   397          },
   398  
   399          showDimmer: function() {
   400            if($dimmable.dimmer('is animating') || !$dimmable.dimmer('is active') ) {
   401              module.debug('Showing dimmer');
   402              $dimmable.dimmer('show');
   403            }
   404            else {
   405              module.debug('Dimmer already visible');
   406            }
   407          },
   408  
   409          hideDimmer: function() {
   410            if( $dimmable.dimmer('is animating') || ($dimmable.dimmer('is active')) ) {
   411              $dimmable.dimmer('hide', function() {
   412                if(settings.transition && $.fn.transition !== undefined && $module.transition('is supported')) {
   413                  module.remove.clickaway();
   414                  module.remove.screenHeight();
   415                }
   416              });
   417            }
   418            else {
   419              module.debug('Dimmer is not visible cannot hide');
   420              return;
   421            }
   422          },
   423  
   424          hideAll: function(callback) {
   425            var
   426              $visibleModals = $allModals.filter(':visible')
   427            ;
   428            callback = $.isFunction(callback)
   429              ? callback
   430              : function(){}
   431            ;
   432            if( $visibleModals.length > 0 ) {
   433              module.debug('Hiding all visible modals');
   434              module.hideDimmer();
   435              $visibleModals
   436                .modal('hide modal', callback)
   437              ;
   438            }
   439          },
   440  
   441          hideOthers: function(callback) {
   442            var
   443              $visibleModals = $otherModals.filter(':visible')
   444            ;
   445            callback = $.isFunction(callback)
   446              ? callback
   447              : function(){}
   448            ;
   449            if( $visibleModals.length > 0 ) {
   450              module.debug('Hiding other modals', $otherModals);
   451              $visibleModals
   452                .modal('hide modal', callback, true)
   453              ;
   454            }
   455          },
   456  
   457          othersActive: function() {
   458            return ($otherModals.filter('.' + className.active).length > 0);
   459          },
   460  
   461          add: {
   462            keyboardShortcuts: function() {
   463              module.verbose('Adding keyboard shortcuts');
   464              $document
   465                .on('keyup' + eventNamespace, module.event.keyboard)
   466              ;
   467            }
   468          },
   469  
   470          save: {
   471            focus: function() {
   472              $focusedElement = $(document.activeElement).blur();
   473            }
   474          },
   475  
   476          restore: {
   477            focus: function() {
   478              if($focusedElement && $focusedElement.length > 0) {
   479                $focusedElement.focus();
   480              }
   481            }
   482          },
   483  
   484          remove: {
   485            active: function() {
   486              $module.removeClass(className.active);
   487            },
   488            clickaway: function() {
   489              if(settings.closable) {
   490                $dimmer
   491                  .off('click' + elementNamespace)
   492                ;
   493              }
   494            },
   495            screenHeight: function() {
   496              if(module.cache.height > module.cache.pageHeight) {
   497                module.debug('Removing page height');
   498                $body
   499                  .css('height', '')
   500                ;
   501              }
   502            },
   503            keyboardShortcuts: function() {
   504              module.verbose('Removing keyboard shortcuts');
   505              $document
   506                .off('keyup' + eventNamespace)
   507              ;
   508            },
   509            scrolling: function() {
   510              $dimmable.removeClass(className.scrolling);
   511              $module.removeClass(className.scrolling);
   512            }
   513          },
   514  
   515          cacheSizes: function() {
   516            var
   517              modalHeight = $module.outerHeight()
   518            ;
   519            if(module.cache === undefined || modalHeight !== 0) {
   520              module.cache = {
   521                pageHeight    : $(document).outerHeight(),
   522                height        : modalHeight + settings.offset,
   523                contextHeight : (settings.context == 'body')
   524                  ? $(window).height()
   525                  : $dimmable.height()
   526              };
   527            }
   528            module.debug('Caching modal and container sizes', module.cache);
   529          },
   530  
   531          can: {
   532            fit: function() {
   533              return ( ( module.cache.height + (settings.padding * 2) ) < module.cache.contextHeight);
   534            }
   535          },
   536  
   537          is: {
   538            active: function() {
   539              return $module.hasClass(className.active);
   540            },
   541            animating: function() {
   542              return $module.transition('is supported')
   543                ? $module.transition('is animating')
   544                : $module.is(':visible')
   545              ;
   546            },
   547            scrolling: function() {
   548              return $dimmable.hasClass(className.scrolling);
   549            },
   550            modernBrowser: function() {
   551              // appName for IE11 reports 'Netscape' can no longer use
   552              return !(window.ActiveXObject || "ActiveXObject" in window);
   553            }
   554          },
   555  
   556          set: {
   557            autofocus: function() {
   558              if(settings.autofocus) {
   559                var
   560                  $inputs    = $module.find(':input:visible'),
   561                  $autofocus = $inputs.filter('[autofocus]'),
   562                  $input     = ($autofocus.length > 0)
   563                    ? $autofocus
   564                    : $inputs
   565                ;
   566                $input.first().focus();
   567              }
   568            },
   569            clickaway: function() {
   570              if(settings.closable) {
   571                $dimmer
   572                  .on('click' + elementNamespace, module.event.click)
   573                ;
   574              }
   575            },
   576            screenHeight: function() {
   577              if( module.can.fit() ) {
   578                $body.css('height', '');
   579              }
   580              else {
   581                module.debug('Modal is taller than page content, resizing page height');
   582                $body
   583                  .css('height', module.cache.height + (settings.padding / 2) )
   584                ;
   585              }
   586            },
   587            active: function() {
   588              $module.addClass(className.active);
   589            },
   590            scrolling: function() {
   591              $dimmable.addClass(className.scrolling);
   592              $module.addClass(className.scrolling);
   593            },
   594            type: function() {
   595              if(module.can.fit()) {
   596                module.verbose('Modal fits on screen');
   597                if(!module.othersActive) {
   598                  module.remove.scrolling();
   599                }
   600              }
   601              else {
   602                module.verbose('Modal cannot fit on screen setting to scrolling');
   603                module.set.scrolling();
   604              }
   605            },
   606            position: function() {
   607              module.verbose('Centering modal on page', module.cache);
   608              if(module.can.fit()) {
   609                $module
   610                  .css({
   611                    top: '',
   612                    marginTop: -(module.cache.height / 2)
   613                  })
   614                ;
   615              }
   616              else {
   617                $module
   618                  .css({
   619                    marginTop : '',
   620                    top       : $document.scrollTop()
   621                  })
   622                ;
   623              }
   624            }
   625          },
   626  
   627          setting: function(name, value) {
   628            module.debug('Changing setting', name, value);
   629            if( $.isPlainObject(name) ) {
   630              $.extend(true, settings, name);
   631            }
   632            else if(value !== undefined) {
   633              settings[name] = value;
   634            }
   635            else {
   636              return settings[name];
   637            }
   638          },
   639          internal: function(name, value) {
   640            if( $.isPlainObject(name) ) {
   641              $.extend(true, module, name);
   642            }
   643            else if(value !== undefined) {
   644              module[name] = value;
   645            }
   646            else {
   647              return module[name];
   648            }
   649          },
   650          debug: function() {
   651            if(settings.debug) {
   652              if(settings.performance) {
   653                module.performance.log(arguments);
   654              }
   655              else {
   656                module.debug = Function.prototype.bind.call(console.info, console, settings.name + ':');
   657                module.debug.apply(console, arguments);
   658              }
   659            }
   660          },
   661          verbose: function() {
   662            if(settings.verbose && settings.debug) {
   663              if(settings.performance) {
   664                module.performance.log(arguments);
   665              }
   666              else {
   667                module.verbose = Function.prototype.bind.call(console.info, console, settings.name + ':');
   668                module.verbose.apply(console, arguments);
   669              }
   670            }
   671          },
   672          error: function() {
   673            module.error = Function.prototype.bind.call(console.error, console, settings.name + ':');
   674            module.error.apply(console, arguments);
   675          },
   676          performance: {
   677            log: function(message) {
   678              var
   679                currentTime,
   680                executionTime,
   681                previousTime
   682              ;
   683              if(settings.performance) {
   684                currentTime   = new Date().getTime();
   685                previousTime  = time || currentTime;
   686                executionTime = currentTime - previousTime;
   687                time          = currentTime;
   688                performance.push({
   689                  'Name'           : message[0],
   690                  'Arguments'      : [].slice.call(message, 1) || '',
   691                  'Element'        : element,
   692                  'Execution Time' : executionTime
   693                });
   694              }
   695              clearTimeout(module.performance.timer);
   696              module.performance.timer = setTimeout(module.performance.display, 100);
   697            },
   698            display: function() {
   699              var
   700                title = settings.name + ':',
   701                totalTime = 0
   702              ;
   703              time = false;
   704              clearTimeout(module.performance.timer);
   705              $.each(performance, function(index, data) {
   706                totalTime += data['Execution Time'];
   707              });
   708              title += ' ' + totalTime + 'ms';
   709              if(moduleSelector) {
   710                title += ' \'' + moduleSelector + '\'';
   711              }
   712              if( (console.group !== undefined || console.table !== undefined) && performance.length > 0) {
   713                console.groupCollapsed(title);
   714                if(console.table) {
   715                  console.table(performance);
   716                }
   717                else {
   718                  $.each(performance, function(index, data) {
   719                    console.log(data['Name'] + ': ' + data['Execution Time']+'ms');
   720                  });
   721                }
   722                console.groupEnd();
   723              }
   724              performance = [];
   725            }
   726          },
   727          invoke: function(query, passedArguments, context) {
   728            var
   729              object = instance,
   730              maxDepth,
   731              found,
   732              response
   733            ;
   734            passedArguments = passedArguments || queryArguments;
   735            context         = element         || context;
   736            if(typeof query == 'string' && object !== undefined) {
   737              query    = query.split(/[\. ]/);
   738              maxDepth = query.length - 1;
   739              $.each(query, function(depth, value) {
   740                var camelCaseValue = (depth != maxDepth)
   741                  ? value + query[depth + 1].charAt(0).toUpperCase() + query[depth + 1].slice(1)
   742                  : query
   743                ;
   744                if( $.isPlainObject( object[camelCaseValue] ) && (depth != maxDepth) ) {
   745                  object = object[camelCaseValue];
   746                }
   747                else if( object[camelCaseValue] !== undefined ) {
   748                  found = object[camelCaseValue];
   749                  return false;
   750                }
   751                else if( $.isPlainObject( object[value] ) && (depth != maxDepth) ) {
   752                  object = object[value];
   753                }
   754                else if( object[value] !== undefined ) {
   755                  found = object[value];
   756                  return false;
   757                }
   758                else {
   759                  return false;
   760                }
   761              });
   762            }
   763            if ( $.isFunction( found ) ) {
   764              response = found.apply(context, passedArguments);
   765            }
   766            else if(found !== undefined) {
   767              response = found;
   768            }
   769            if($.isArray(returnedValue)) {
   770              returnedValue.push(response);
   771            }
   772            else if(returnedValue !== undefined) {
   773              returnedValue = [returnedValue, response];
   774            }
   775            else if(response !== undefined) {
   776              returnedValue = response;
   777            }
   778            return found;
   779          }
   780        };
   781  
   782        if(methodInvoked) {
   783          if(instance === undefined) {
   784            module.initialize();
   785          }
   786          module.invoke(query);
   787        }
   788        else {
   789          if(instance !== undefined) {
   790            instance.invoke('destroy');
   791          }
   792          module.initialize();
   793        }
   794      })
   795    ;
   796  
   797    return (returnedValue !== undefined)
   798      ? returnedValue
   799      : this
   800    ;
   801  };
   802  
   803  $.fn.modal.settings = {
   804  
   805    name           : 'Modal',
   806    namespace      : 'modal',
   807  
   808    debug          : false,
   809    verbose        : true,
   810    performance    : true,
   811  
   812    allowMultiple  : false,
   813    detachable     : true,
   814    closable       : true,
   815    autofocus      : true,
   816  
   817    dimmerSettings : {
   818      closable : false,
   819      useCSS   : true
   820    },
   821  
   822    context        : 'body',
   823  
   824    queue          : false,
   825    duration       : 500,
   826    easing         : 'easeOutExpo',
   827    offset         : 0,
   828    transition     : 'scale',
   829  
   830    padding        : 50,
   831  
   832    onShow         : function(){},
   833    onHide         : function(){},
   834  
   835    onVisible      : function(){},
   836    onHidden       : function(){},
   837  
   838    onApprove      : function(){ return true; },
   839    onDeny         : function(){ return true; },
   840  
   841    selector    : {
   842      close    : '.close, .actions .button',
   843      approve  : '.actions .positive, .actions .approve, .actions .ok',
   844      deny     : '.actions .negative, .actions .deny, .actions .cancel',
   845      modal    : '.ui.modal'
   846    },
   847    error : {
   848      dimmer    : 'UI Dimmer, a required component is not included in this page',
   849      method    : 'The method you called is not defined.',
   850      notFound  : 'The element you specified could not be found'
   851    },
   852    className : {
   853      active    : 'active',
   854      animating : 'animating',
   855      scrolling : 'scrolling'
   856    }
   857  };
   858  
   859  
   860  })( jQuery, window , document );