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

     1  /*!
     2   * # Semantic UI x.x - Popup
     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.popup = function(parameters) {
    17    var
    18      $allModules    = $(this),
    19      $document      = $(document),
    20  
    21      moduleSelector = $allModules.selector || '',
    22  
    23      hasTouch       = ('ontouchstart' in document.documentElement),
    24      time           = new Date().getTime(),
    25      performance    = [],
    26  
    27      query          = arguments[0],
    28      methodInvoked  = (typeof query == 'string'),
    29      queryArguments = [].slice.call(arguments, 1),
    30  
    31      returnedValue
    32    ;
    33    $allModules
    34      .each(function() {
    35        var
    36          settings        = ( $.isPlainObject(parameters) )
    37            ? $.extend(true, {}, $.fn.popup.settings, parameters)
    38            : $.extend({}, $.fn.popup.settings),
    39  
    40          selector           = settings.selector,
    41          className          = settings.className,
    42          error              = settings.error,
    43          metadata           = settings.metadata,
    44          namespace          = settings.namespace,
    45  
    46          eventNamespace     = '.' + settings.namespace,
    47          moduleNamespace    = 'module-' + namespace,
    48  
    49          $module            = $(this),
    50          $context           = $(settings.context),
    51          $target            = (settings.target)
    52            ? $(settings.target)
    53            : $module,
    54  
    55          $window            = $(window),
    56          $body              = $('body'),
    57          $popup,
    58          $offsetParent,
    59  
    60          searchDepth        = 0,
    61          triedPositions     = false,
    62  
    63          element            = this,
    64          instance           = $module.data(moduleNamespace),
    65  
    66          elementNamespace,
    67          id,
    68          module
    69        ;
    70  
    71        module = {
    72  
    73          // binds events
    74          initialize: function() {
    75            module.debug('Initializing', $module);
    76            module.createID();
    77            module.bind.events();
    78            if( !module.exists() && settings.preserve) {
    79              module.create();
    80            }
    81            module.instantiate();
    82          },
    83  
    84          instantiate: function() {
    85            module.verbose('Storing instance', module);
    86            instance = module;
    87            $module
    88              .data(moduleNamespace, instance)
    89            ;
    90          },
    91  
    92          refresh: function() {
    93            if(settings.popup) {
    94              $popup = $(settings.popup).eq(0);
    95            }
    96            else {
    97              if(settings.inline) {
    98                $popup = $target.next(selector.popup).eq(0);
    99              }
   100            }
   101            if(settings.popup) {
   102              $popup.addClass(className.loading);
   103              $offsetParent = module.get.offsetParent();
   104              $popup.removeClass(className.loading);
   105              if(settings.movePopup && module.has.popup() && module.get.offsetParent($popup)[0] !== $offsetParent[0]) {
   106                module.debug('Moving popup to the same offset parent as activating element');
   107                $popup
   108                  .detach()
   109                  .appendTo($offsetParent)
   110                ;
   111              }
   112            }
   113            else {
   114              $offsetParent = (settings.inline)
   115                ? module.get.offsetParent($target)
   116                : module.has.popup()
   117                  ? module.get.offsetParent($popup)
   118                  : $body
   119              ;
   120            }
   121            if( $offsetParent.is('html') ) {
   122              module.debug('Setting page as offset parent');
   123              $offsetParent = $body;
   124            }
   125          },
   126  
   127          reposition: function() {
   128            module.refresh();
   129            module.set.position();
   130          },
   131  
   132          destroy: function() {
   133            module.debug('Destroying previous module');
   134            // remove element only if was created dynamically
   135            if($popup && !settings.preserve) {
   136              module.removePopup();
   137            }
   138            // clear all timeouts
   139            clearTimeout(module.hideTimer);
   140            clearTimeout(module.showTimer);
   141            // remove events
   142            $window.off(elementNamespace);
   143            $module
   144              .off(eventNamespace)
   145              .removeData(moduleNamespace)
   146            ;
   147          },
   148  
   149          event: {
   150            start:  function(event) {
   151              var
   152                delay = ($.isPlainObject(settings.delay))
   153                  ? settings.delay.show
   154                  : settings.delay
   155              ;
   156              clearTimeout(module.hideTimer);
   157              module.showTimer = setTimeout(function() {
   158                if(module.is.hidden() && !( module.is.active() && module.is.dropdown()) ) {
   159                  module.show();
   160                }
   161              }, delay);
   162            },
   163            end:  function() {
   164              var
   165                delay = ($.isPlainObject(settings.delay))
   166                  ? settings.delay.hide
   167                  : settings.delay
   168              ;
   169              clearTimeout(module.showTimer);
   170              module.hideTimer = setTimeout(function() {
   171                if(module.is.visible() ) {
   172                  module.hide();
   173                }
   174              }, delay);
   175            },
   176            resize: function() {
   177              if( module.is.visible() ) {
   178                module.set.position();
   179              }
   180            }
   181          },
   182  
   183          // generates popup html from metadata
   184          create: function() {
   185            var
   186              html      = $module.data(metadata.html)      || settings.html,
   187              variation = $module.data(metadata.variation) || settings.variation,
   188              title     = $module.data(metadata.title)     || settings.title,
   189              content   = $module.data(metadata.content)   || $module.attr('title') || settings.content
   190            ;
   191            if(html || content || title) {
   192              module.debug('Creating pop-up html');
   193              if(!html) {
   194                html = settings.templates.popup({
   195                  title   : title,
   196                  content : content
   197                });
   198              }
   199              $popup = $('<div/>')
   200                .addClass(className.popup)
   201                .addClass(variation)
   202                .data(metadata.activator, $module)
   203                .html(html)
   204              ;
   205              if(variation) {
   206                $popup
   207                  .addClass(variation)
   208                ;
   209              }
   210              if(settings.inline) {
   211                module.verbose('Inserting popup element inline', $popup);
   212                $popup
   213                  .insertAfter($module)
   214                ;
   215              }
   216              else {
   217                module.verbose('Appending popup element to body', $popup);
   218                $popup
   219                  .appendTo( $context )
   220                ;
   221              }
   222              module.refresh();
   223              if(settings.hoverable) {
   224                module.bind.popup();
   225              }
   226              settings.onCreate.call($popup, element);
   227            }
   228            else if($target.next(selector.popup).length !== 0) {
   229              module.verbose('Pre-existing popup found');
   230              settings.inline = true;
   231              settings.popup  = $target.next(selector.popup).data(metadata.activator, $module);
   232              module.refresh();
   233              if(settings.hoverable) {
   234                module.bind.popup();
   235              }
   236            }
   237            else if(settings.popup) {
   238              settings.popup.data(metadata.activator, $module);
   239              module.verbose('Used popup specified in settings');
   240              module.refresh();
   241              if(settings.hoverable) {
   242                module.bind.popup();
   243              }
   244            }
   245            else {
   246              module.debug('No content specified skipping display', element);
   247            }
   248          },
   249  
   250          createID: function() {
   251            id = (Math.random().toString(16) + '000000000').substr(2,8);
   252            elementNamespace = '.' + id;
   253            module.verbose('Creating unique id for element', id);
   254          },
   255  
   256          // determines popup state
   257          toggle: function() {
   258            module.debug('Toggling pop-up');
   259            if( module.is.hidden() ) {
   260              module.debug('Popup is hidden, showing pop-up');
   261              module.unbind.close();
   262              module.show();
   263            }
   264            else {
   265              module.debug('Popup is visible, hiding pop-up');
   266              module.hide();
   267            }
   268          },
   269  
   270          show: function(callback) {
   271            callback = $.isFunction(callback) ? callback : function(){};
   272            module.debug('Showing pop-up', settings.transition);
   273            if( !module.exists() ) {
   274              module.create();
   275            }
   276            else if(!settings.preserve && !settings.popup) {
   277              module.refresh();
   278            }
   279            if( $popup && module.set.position() ) {
   280              module.save.conditions();
   281              if(settings.exclusive) {
   282                module.hideAll();
   283              }
   284              module.animate.show(callback);
   285            }
   286          },
   287  
   288  
   289          hide: function(callback) {
   290            callback = $.isFunction(callback) ? callback : function(){};
   291            module.remove.visible();
   292            module.unbind.close();
   293            if( module.is.visible() ) {
   294              module.restore.conditions();
   295              module.animate.hide(callback);
   296            }
   297          },
   298  
   299          hideAll: function() {
   300            $(selector.popup)
   301              .filter('.' + className.visible)
   302              .each(function() {
   303                $(this)
   304                  .data(metadata.activator)
   305                  .popup('hide')
   306                ;
   307              })
   308            ;
   309          },
   310  
   311          hideGracefully: function(event) {
   312            // don't close on clicks inside popup
   313            if(event && $(event.target).closest(selector.popup).length === 0) {
   314              module.debug('Click occurred outside popup hiding popup');
   315              module.hide();
   316            }
   317            else {
   318              module.debug('Click was inside popup, keeping popup open');
   319            }
   320          },
   321  
   322          exists: function() {
   323            if(!$popup) {
   324              return false;
   325            }
   326            if(settings.inline || settings.popup) {
   327              return ( module.has.popup() );
   328            }
   329            else {
   330              return ( $popup.closest($context).length >= 1 )
   331                ? true
   332                : false
   333              ;
   334            }
   335          },
   336  
   337          removePopup: function() {
   338            module.debug('Removing popup', $popup);
   339            if( module.has.popup() && !settings.popup) {
   340              $popup.remove();
   341              $popup = undefined;
   342            }
   343            settings.onRemove.call($popup, element);
   344          },
   345  
   346          save: {
   347            conditions: function() {
   348              module.cache = {
   349                title: $module.attr('title')
   350              };
   351              if (module.cache.title) {
   352                $module.removeAttr('title');
   353              }
   354              module.verbose('Saving original attributes', module.cache.title);
   355            }
   356          },
   357          restore: {
   358            conditions: function() {
   359              if(module.cache && module.cache.title) {
   360                $module.attr('title', module.cache.title);
   361                module.verbose('Restoring original attributes', module.cache.title);
   362              }
   363              return true;
   364            }
   365          },
   366          animate: {
   367            show: function(callback) {
   368              callback = $.isFunction(callback) ? callback : function(){};
   369              if(settings.transition && $.fn.transition !== undefined && $module.transition('is supported')) {
   370                module.set.visible();
   371                $popup
   372                  .transition({
   373                    animation  : settings.transition + ' in',
   374                    queue      : false,
   375                    debug      : settings.debug,
   376                    verbose    : settings.verbose,
   377                    duration   : settings.duration,
   378                    onComplete : function() {
   379                      module.bind.close();
   380                      callback.call($popup, element);
   381                      settings.onVisible.call($popup, element);
   382                    }
   383                  })
   384                ;
   385              }
   386              else {
   387                module.set.visible();
   388                $popup
   389                  .stop()
   390                  .fadeIn(settings.duration, settings.easing, function() {
   391                    module.bind.close();
   392                    callback.call($popup, element);
   393                    settings.onVisible.call($popup, element);
   394                  })
   395                ;
   396              }
   397              settings.onShow.call($popup, element);
   398            },
   399            hide: function(callback) {
   400              callback = $.isFunction(callback) ? callback : function(){};
   401              module.debug('Hiding pop-up');
   402              if(settings.transition && $.fn.transition !== undefined && $module.transition('is supported')) {
   403                $popup
   404                  .transition({
   405                    animation  : settings.transition + ' out',
   406                    queue      : false,
   407                    duration   : settings.duration,
   408                    debug      : settings.debug,
   409                    verbose    : settings.verbose,
   410                    onComplete : function() {
   411                      module.reset();
   412                      callback.call($popup, element);
   413                      settings.onHidden.call($popup, element);
   414                    }
   415                  })
   416                ;
   417              }
   418              else {
   419                $popup
   420                  .stop()
   421                  .fadeOut(settings.duration, settings.easing, function() {
   422                    module.reset();
   423                    callback.call($popup, element);
   424                    settings.onHidden.call($popup, element);
   425                  })
   426                ;
   427              }
   428              settings.onHide.call($popup, element);
   429            }
   430          },
   431  
   432          get: {
   433            id: function() {
   434              return id;
   435            },
   436            startEvent: function() {
   437              if(settings.on == 'hover') {
   438                return (hasTouch)
   439                  ? 'touchstart mouseenter'
   440                  : 'mouseenter'
   441                ;
   442              }
   443              else if(settings.on == 'focus') {
   444                return 'focus';
   445              }
   446              return false;
   447            },
   448            scrollEvent: function() {
   449              return (hasTouch)
   450                ? 'touchmove scroll'
   451                : 'scroll'
   452              ;
   453            },
   454            endEvent: function() {
   455              if(settings.on == 'hover') {
   456                return 'mouseleave';
   457              }
   458              else if(settings.on == 'focus') {
   459                return 'blur';
   460              }
   461              return false;
   462            },
   463            offsetParent: function($target) {
   464              var
   465                element = ($target !== undefined)
   466                  ? $target[0]
   467                  : $module[0],
   468                parentNode = element.parentNode,
   469                $node    = $(parentNode)
   470              ;
   471              if(parentNode) {
   472                var
   473                  is2D     = ($node.css('transform') === 'none'),
   474                  isStatic = ($node.css('position') === 'static'),
   475                  isHTML   = $node.is('html')
   476                ;
   477                while(parentNode && !isHTML && isStatic && is2D) {
   478                  parentNode = parentNode.parentNode;
   479                  $node    = $(parentNode);
   480                  is2D     = ($node.css('transform') === 'none');
   481                  isStatic = ($node.css('position') === 'static');
   482                  isHTML   = $node.is('html');
   483                }
   484              }
   485              return ($node && $node.length > 0)
   486                ? $node
   487                : $()
   488              ;
   489            },
   490            offstagePosition: function(position) {
   491              var
   492                boundary  = {
   493                  top    : $(window).scrollTop(),
   494                  bottom : $(window).scrollTop() + $(window).height(),
   495                  left   : 0,
   496                  right  : $(window).width()
   497                },
   498                popup     = {
   499                  width  : $popup.width(),
   500                  height : $popup.height(),
   501                  offset : $popup.offset()
   502                },
   503                offstage  = {},
   504                offstagePositions = []
   505              ;
   506              position = position || false;
   507              if(popup.offset && position) {
   508                module.verbose('Checking if outside viewable area', popup.offset);
   509                offstage = {
   510                  top    : (popup.offset.top < boundary.top),
   511                  bottom : (popup.offset.top + popup.height > boundary.bottom),
   512                  right  : (popup.offset.left + popup.width > boundary.right),
   513                  left   : (popup.offset.left < boundary.left)
   514                };
   515              }
   516              // return only boundaries that have been surpassed
   517              $.each(offstage, function(direction, isOffstage) {
   518                if(isOffstage) {
   519                  offstagePositions.push(direction);
   520                }
   521              });
   522              return (offstagePositions.length > 0)
   523                ? offstagePositions.join(' ')
   524                : false
   525              ;
   526            },
   527            positions: function() {
   528              return {
   529                'top left'      : false,
   530                'top center'    : false,
   531                'top right'     : false,
   532                'bottom left'   : false,
   533                'bottom center' : false,
   534                'bottom right'  : false,
   535                'left center'   : false,
   536                'right center'  : false
   537              };
   538            },
   539            nextPosition: function(position) {
   540              var
   541                positions          = position.split(' '),
   542                verticalPosition   = positions[0],
   543                horizontalPosition = positions[1],
   544                opposite = {
   545                  top    : 'bottom',
   546                  bottom : 'top',
   547                  left   : 'right',
   548                  right  : 'left'
   549                },
   550                adjacent = {
   551                  left   : 'center',
   552                  center : 'right',
   553                  right  : 'left'
   554                },
   555                backup = {
   556                  'top left'      : 'top center',
   557                  'top center'    : 'top right',
   558                  'top right'     : 'right center',
   559                  'right center'  : 'bottom right',
   560                  'bottom right'  : 'bottom center',
   561                  'bottom center' : 'bottom left',
   562                  'bottom left'   : 'left center',
   563                  'left center'   : 'top left'
   564                },
   565                adjacentsAvailable = (verticalPosition == 'top' || verticalPosition == 'bottom'),
   566                oppositeTried = false,
   567                adjacentTried = false,
   568                nextPosition  = false
   569              ;
   570              if(!triedPositions) {
   571                module.verbose('All available positions available');
   572                triedPositions = module.get.positions();
   573              }
   574  
   575              module.debug('Recording last position tried', position);
   576              triedPositions[position] = true;
   577  
   578              if(settings.prefer === 'opposite') {
   579                nextPosition  = [opposite[verticalPosition], horizontalPosition];
   580                nextPosition  = nextPosition.join(' ');
   581                oppositeTried = (triedPositions[nextPosition] === true);
   582                module.debug('Trying opposite strategy', nextPosition);
   583              }
   584              if((settings.prefer === 'adjacent') && adjacentsAvailable ) {
   585                nextPosition  = [verticalPosition, adjacent[horizontalPosition]];
   586                nextPosition  = nextPosition.join(' ');
   587                adjacentTried = (triedPositions[nextPosition] === true);
   588                module.debug('Trying adjacent strategy', nextPosition);
   589              }
   590              if(adjacentTried || oppositeTried) {
   591                module.debug('Using backup position', nextPosition);
   592                nextPosition = backup[position];
   593              }
   594              return nextPosition;
   595            }
   596          },
   597  
   598          set: {
   599            position: function(position, arrowOffset) {
   600              var
   601                windowWidth   = $(window).width(),
   602                windowHeight  = $(window).height(),
   603  
   604                targetWidth   = $target.outerWidth(),
   605                targetHeight  = $target.outerHeight(),
   606  
   607                popupWidth    = $popup.outerWidth(),
   608                popupHeight   = $popup.outerHeight(),
   609  
   610                parentWidth   = $offsetParent.outerWidth(),
   611                parentHeight  = $offsetParent.outerHeight(),
   612  
   613                distanceAway  = settings.distanceAway,
   614  
   615                targetElement = $target[0],
   616  
   617                marginTop     = (settings.inline)
   618                  ? parseInt( window.getComputedStyle(targetElement).getPropertyValue('margin-top'), 10)
   619                  : 0,
   620                marginLeft    = (settings.inline)
   621                  ? parseInt( window.getComputedStyle(targetElement).getPropertyValue(module.is.rtl() ? 'margin-right' : 'margin-left'), 10)
   622                  : 0,
   623  
   624                target        = (settings.inline || settings.popup)
   625                  ? $target.position()
   626                  : $target.offset(),
   627  
   628                computedPosition,
   629                positioning,
   630                offstagePosition
   631              ;
   632              position    = position    || $module.data(metadata.position)    || settings.position;
   633              arrowOffset = arrowOffset || $module.data(metadata.offset)      || settings.offset;
   634  
   635              if(searchDepth == settings.maxSearchDepth && settings.lastResort) {
   636                module.debug('Using last resort position to display', settings.lastResort);
   637                position = settings.lastResort;
   638              }
   639  
   640              if(settings.inline) {
   641                module.debug('Adding targets margin to calculation');
   642                if(position == 'left center' || position == 'right center') {
   643                  arrowOffset  += marginTop;
   644                  distanceAway += -marginLeft;
   645                }
   646                else if (position == 'top left' || position == 'top center' || position == 'top right') {
   647                  arrowOffset  += marginLeft;
   648                  distanceAway -= marginTop;
   649                }
   650                else {
   651                  arrowOffset  += marginLeft;
   652                  distanceAway += marginTop;
   653                }
   654              }
   655              module.debug('Calculating popup positioning', position);
   656  
   657              computedPosition = position;
   658              if (module.is.rtl()) {
   659                computedPosition = computedPosition.replace(/left|right/g, function (match) {
   660                  return (match == 'left')
   661                    ? 'right'
   662                    : 'left'
   663                  ;
   664                });
   665                module.debug('RTL: Popup positioning updated', computedPosition);
   666              }
   667              switch (computedPosition) {
   668                case 'top left':
   669                  positioning = {
   670                    top    : 'auto',
   671                    bottom : parentHeight - target.top + distanceAway,
   672                    left   : target.left + arrowOffset,
   673                    right  : 'auto'
   674                  };
   675                break;
   676                case 'top center':
   677                  positioning = {
   678                    bottom : parentHeight - target.top + distanceAway,
   679                    left   : target.left + (targetWidth / 2) - (popupWidth / 2) + arrowOffset,
   680                    top    : 'auto',
   681                    right  : 'auto'
   682                  };
   683                break;
   684                case 'top right':
   685                  positioning = {
   686                    bottom :  parentHeight - target.top + distanceAway,
   687                    right  :  parentWidth - target.left - targetWidth - arrowOffset,
   688                    top    : 'auto',
   689                    left   : 'auto'
   690                  };
   691                break;
   692                case 'left center':
   693                  positioning = {
   694                    top    : target.top + (targetHeight / 2) - (popupHeight / 2) + arrowOffset,
   695                    right  : parentWidth - target.left + distanceAway,
   696                    left   : 'auto',
   697                    bottom : 'auto'
   698                  };
   699                break;
   700                case 'right center':
   701                  positioning = {
   702                    top    : target.top + (targetHeight / 2) - (popupHeight / 2) + arrowOffset,
   703                    left   : target.left + targetWidth + distanceAway,
   704                    bottom : 'auto',
   705                    right  : 'auto'
   706                  };
   707                break;
   708                case 'bottom left':
   709                  positioning = {
   710                    top    : target.top + targetHeight + distanceAway,
   711                    left   : target.left + arrowOffset,
   712                    bottom : 'auto',
   713                    right  : 'auto'
   714                  };
   715                break;
   716                case 'bottom center':
   717                  positioning = {
   718                    top    : target.top + targetHeight + distanceAway,
   719                    left   : target.left + (targetWidth / 2) - (popupWidth / 2) + arrowOffset,
   720                    bottom : 'auto',
   721                    right  : 'auto'
   722                  };
   723                break;
   724                case 'bottom right':
   725                  positioning = {
   726                    top    : target.top + targetHeight + distanceAway,
   727                    right  : parentWidth - target.left  - targetWidth - arrowOffset,
   728                    left   : 'auto',
   729                    bottom : 'auto'
   730                  };
   731                break;
   732              }
   733              if(positioning === undefined) {
   734                module.error(error.invalidPosition, position);
   735              }
   736  
   737              module.debug('Calculated popup positioning values', positioning);
   738  
   739              // tentatively place on stage
   740              $popup
   741                .css(positioning)
   742                .removeClass(className.position)
   743                .addClass(position)
   744                .addClass(className.loading)
   745              ;
   746              // check if is offstage
   747              offstagePosition = module.get.offstagePosition(position);
   748  
   749              // recursively find new positioning
   750              if(offstagePosition) {
   751                module.debug('Popup cant fit into viewport', offstagePosition);
   752                if(searchDepth < settings.maxSearchDepth) {
   753                  searchDepth++;
   754                  position = module.get.nextPosition(position);
   755                  module.debug('Trying new position', position);
   756                  return ($popup)
   757                    ? module.set.position(position)
   758                    : false
   759                  ;
   760                }
   761                else if(!settings.lastResort) {
   762                  module.debug('Popup could not find a position in view', $popup);
   763                  module.error(error.cannotPlace, element);
   764                  module.remove.attempts();
   765                  module.remove.loading();
   766                  module.reset();
   767                  return false;
   768                }
   769              }
   770  
   771              module.debug('Position is on stage', position);
   772              module.remove.attempts();
   773              module.set.fluidWidth();
   774              module.remove.loading();
   775              return true;
   776            },
   777  
   778            fluidWidth: function() {
   779              if( settings.setFluidWidth && $popup.hasClass(className.fluid) ) {
   780                $popup.css('width', $offsetParent.width());
   781              }
   782            },
   783  
   784            visible: function() {
   785              $module.addClass(className.visible);
   786            }
   787          },
   788  
   789          remove: {
   790            loading: function() {
   791              $popup.removeClass(className.loading);
   792            },
   793            visible: function() {
   794              $module.removeClass(className.visible);
   795            },
   796            attempts: function() {
   797              module.verbose('Resetting all searched positions');
   798              searchDepth    = 0;
   799              triedPositions = false;
   800            }
   801          },
   802  
   803          bind: {
   804            events: function() {
   805              module.debug('Binding popup events to module');
   806              if(settings.on == 'click') {
   807                $module
   808                  .on('click' + eventNamespace, module.toggle)
   809                ;
   810              }
   811              else if( module.get.startEvent() ) {
   812                $module
   813                  .on(module.get.startEvent() + eventNamespace, module.event.start)
   814                  .on(module.get.endEvent() + eventNamespace, module.event.end)
   815                ;
   816              }
   817              if(settings.target) {
   818                module.debug('Target set to element', $target);
   819              }
   820              $window.on('resize' + elementNamespace, module.event.resize);
   821            },
   822            popup: function() {
   823              module.verbose('Allowing hover events on popup to prevent closing');
   824              if( $popup && module.has.popup() ) {
   825                $popup
   826                  .on('mouseenter' + eventNamespace, module.event.start)
   827                  .on('mouseleave' + eventNamespace, module.event.end)
   828                ;
   829              }
   830            },
   831            close:function() {
   832              if(settings.hideOnScroll === true || settings.hideOnScroll == 'auto' && settings.on != 'click') {
   833                $document
   834                  .one(module.get.scrollEvent() + elementNamespace, module.hideGracefully)
   835                ;
   836                $context
   837                  .one(module.get.scrollEvent() + elementNamespace, module.hideGracefully)
   838                ;
   839              }
   840              if(settings.on == 'click' && settings.closable) {
   841                module.verbose('Binding popup close event to document');
   842                $document
   843                  .on('click' + elementNamespace, function(event) {
   844                    module.verbose('Pop-up clickaway intent detected');
   845                    module.hideGracefully.call(element, event);
   846                  })
   847                ;
   848              }
   849            }
   850          },
   851  
   852          unbind: {
   853            close: function() {
   854              if(settings.hideOnScroll === true || settings.hideOnScroll == 'auto' && settings.on != 'click') {
   855                $document
   856                  .off('scroll' + elementNamespace, module.hide)
   857                ;
   858                $context
   859                  .off('scroll' + elementNamespace, module.hide)
   860                ;
   861              }
   862              if(settings.on == 'click' && settings.closable) {
   863                module.verbose('Removing close event from document');
   864                $document
   865                  .off('click' + elementNamespace)
   866                ;
   867              }
   868            }
   869          },
   870  
   871          has: {
   872            popup: function() {
   873              return ($popup && $popup.length > 0);
   874            }
   875          },
   876  
   877          is: {
   878            active: function() {
   879              return $module.hasClass(className.active);
   880            },
   881            animating: function() {
   882              return ( $popup && $popup.is(':animated') || $popup.hasClass(className.animating) );
   883            },
   884            visible: function() {
   885              return $popup && $popup.is(':visible');
   886            },
   887            dropdown: function() {
   888              return $module.hasClass(className.dropdown);
   889            },
   890            hidden: function() {
   891              return !module.is.visible();
   892            },
   893            rtl: function () {
   894              return $module.css('direction') == 'rtl';
   895            }
   896          },
   897  
   898          reset: function() {
   899            module.remove.visible();
   900            if(settings.preserve) {
   901              if($.fn.transition !== undefined) {
   902                $popup
   903                  .transition('remove transition')
   904                ;
   905              }
   906            }
   907            else {
   908              module.removePopup();
   909            }
   910          },
   911  
   912          setting: function(name, value) {
   913            if( $.isPlainObject(name) ) {
   914              $.extend(true, settings, name);
   915            }
   916            else if(value !== undefined) {
   917              settings[name] = value;
   918            }
   919            else {
   920              return settings[name];
   921            }
   922          },
   923          internal: function(name, value) {
   924            if( $.isPlainObject(name) ) {
   925              $.extend(true, module, name);
   926            }
   927            else if(value !== undefined) {
   928              module[name] = value;
   929            }
   930            else {
   931              return module[name];
   932            }
   933          },
   934          debug: function() {
   935            if(settings.debug) {
   936              if(settings.performance) {
   937                module.performance.log(arguments);
   938              }
   939              else {
   940                module.debug = Function.prototype.bind.call(console.info, console, settings.name + ':');
   941                module.debug.apply(console, arguments);
   942              }
   943            }
   944          },
   945          verbose: function() {
   946            if(settings.verbose && settings.debug) {
   947              if(settings.performance) {
   948                module.performance.log(arguments);
   949              }
   950              else {
   951                module.verbose = Function.prototype.bind.call(console.info, console, settings.name + ':');
   952                module.verbose.apply(console, arguments);
   953              }
   954            }
   955          },
   956          error: function() {
   957            module.error = Function.prototype.bind.call(console.error, console, settings.name + ':');
   958            module.error.apply(console, arguments);
   959          },
   960          performance: {
   961            log: function(message) {
   962              var
   963                currentTime,
   964                executionTime,
   965                previousTime
   966              ;
   967              if(settings.performance) {
   968                currentTime   = new Date().getTime();
   969                previousTime  = time || currentTime;
   970                executionTime = currentTime - previousTime;
   971                time          = currentTime;
   972                performance.push({
   973                  'Name'           : message[0],
   974                  'Arguments'      : [].slice.call(message, 1) || '',
   975                  'Element'        : element,
   976                  'Execution Time' : executionTime
   977                });
   978              }
   979              clearTimeout(module.performance.timer);
   980              module.performance.timer = setTimeout(module.performance.display, 100);
   981            },
   982            display: function() {
   983              var
   984                title = settings.name + ':',
   985                totalTime = 0
   986              ;
   987              time = false;
   988              clearTimeout(module.performance.timer);
   989              $.each(performance, function(index, data) {
   990                totalTime += data['Execution Time'];
   991              });
   992              title += ' ' + totalTime + 'ms';
   993              if(moduleSelector) {
   994                title += ' \'' + moduleSelector + '\'';
   995              }
   996              if( (console.group !== undefined || console.table !== undefined) && performance.length > 0) {
   997                console.groupCollapsed(title);
   998                if(console.table) {
   999                  console.table(performance);
  1000                }
  1001                else {
  1002                  $.each(performance, function(index, data) {
  1003                    console.log(data['Name'] + ': ' + data['Execution Time']+'ms');
  1004                  });
  1005                }
  1006                console.groupEnd();
  1007              }
  1008              performance = [];
  1009            }
  1010          },
  1011          invoke: function(query, passedArguments, context) {
  1012            var
  1013              object = instance,
  1014              maxDepth,
  1015              found,
  1016              response
  1017            ;
  1018            passedArguments = passedArguments || queryArguments;
  1019            context         = element         || context;
  1020            if(typeof query == 'string' && object !== undefined) {
  1021              query    = query.split(/[\. ]/);
  1022              maxDepth = query.length - 1;
  1023              $.each(query, function(depth, value) {
  1024                var camelCaseValue = (depth != maxDepth)
  1025                  ? value + query[depth + 1].charAt(0).toUpperCase() + query[depth + 1].slice(1)
  1026                  : query
  1027                ;
  1028                if( $.isPlainObject( object[camelCaseValue] ) && (depth != maxDepth) ) {
  1029                  object = object[camelCaseValue];
  1030                }
  1031                else if( object[camelCaseValue] !== undefined ) {
  1032                  found = object[camelCaseValue];
  1033                  return false;
  1034                }
  1035                else if( $.isPlainObject( object[value] ) && (depth != maxDepth) ) {
  1036                  object = object[value];
  1037                }
  1038                else if( object[value] !== undefined ) {
  1039                  found = object[value];
  1040                  return false;
  1041                }
  1042                else {
  1043                  return false;
  1044                }
  1045              });
  1046            }
  1047            if ( $.isFunction( found ) ) {
  1048              response = found.apply(context, passedArguments);
  1049            }
  1050            else if(found !== undefined) {
  1051              response = found;
  1052            }
  1053            if($.isArray(returnedValue)) {
  1054              returnedValue.push(response);
  1055            }
  1056            else if(returnedValue !== undefined) {
  1057              returnedValue = [returnedValue, response];
  1058            }
  1059            else if(response !== undefined) {
  1060              returnedValue = response;
  1061            }
  1062            return found;
  1063          }
  1064        };
  1065  
  1066        if(methodInvoked) {
  1067          if(instance === undefined) {
  1068            module.initialize();
  1069          }
  1070          module.invoke(query);
  1071        }
  1072        else {
  1073          if(instance !== undefined) {
  1074            instance.invoke('destroy');
  1075          }
  1076          module.initialize();
  1077        }
  1078      })
  1079    ;
  1080  
  1081    return (returnedValue !== undefined)
  1082      ? returnedValue
  1083      : this
  1084    ;
  1085  };
  1086  
  1087  $.fn.popup.settings = {
  1088  
  1089    name         : 'Popup',
  1090  
  1091    debug        : false,
  1092    verbose      : true,
  1093    performance  : true,
  1094    namespace    : 'popup',
  1095  
  1096    onCreate     : function(){},
  1097    onRemove     : function(){},
  1098  
  1099    onShow       : function(){},
  1100    onVisible    : function(){},
  1101    onHide       : function(){},
  1102    onHidden     : function(){},
  1103  
  1104    variation    : '',
  1105    content      : false,
  1106    html         : false,
  1107    title        : false,
  1108  
  1109    on           : 'hover',
  1110    closable     : true,
  1111    hideOnScroll : 'auto',
  1112    exclusive    : true,
  1113  
  1114    context      : 'body',
  1115  
  1116    position     : 'top left',
  1117    prefer       : 'opposite',
  1118    lastResort   : false,
  1119  
  1120    delay        : {
  1121      show : 30,
  1122      hide : 0
  1123    },
  1124  
  1125    setFluidWidth  : true,
  1126    movePopup      : true,
  1127  
  1128    target         : false,
  1129    popup          : false,
  1130    inline         : false,
  1131    preserve       : false,
  1132    hoverable      : false,
  1133  
  1134    duration       : 200,
  1135    easing         : 'easeOutQuint',
  1136    transition     : 'scale',
  1137  
  1138    distanceAway   : 0,
  1139    offset         : 0,
  1140    maxSearchDepth : 20,
  1141  
  1142    error: {
  1143      invalidPosition : 'The position you specified is not a valid position',
  1144      cannotPlace     : 'No visible position could be found for the popup',
  1145      method          : 'The method you called is not defined.'
  1146    },
  1147  
  1148    metadata: {
  1149      activator : 'activator',
  1150      content   : 'content',
  1151      html      : 'html',
  1152      offset    : 'offset',
  1153      position  : 'position',
  1154      title     : 'title',
  1155      variation : 'variation'
  1156    },
  1157  
  1158    className   : {
  1159      active    : 'active',
  1160      animating : 'animating',
  1161      dropdown  : 'dropdown',
  1162      fluid     : 'fluid',
  1163      loading   : 'loading',
  1164      popup     : 'ui popup',
  1165      position  : 'top left center bottom right',
  1166      visible   : 'visible'
  1167    },
  1168  
  1169    selector    : {
  1170      popup    : '.ui.popup'
  1171    },
  1172  
  1173    templates: {
  1174      escape: function(string) {
  1175        var
  1176          badChars     = /[&<>"'`]/g,
  1177          shouldEscape = /[&<>"'`]/,
  1178          escape       = {
  1179            "&": "&amp;",
  1180            "<": "&lt;",
  1181            ">": "&gt;",
  1182            '"': "&quot;",
  1183            "'": "&#x27;",
  1184            "`": "&#x60;"
  1185          },
  1186          escapedChar  = function(chr) {
  1187            return escape[chr];
  1188          }
  1189        ;
  1190        if(shouldEscape.test(string)) {
  1191          return string.replace(badChars, escapedChar);
  1192        }
  1193        return string;
  1194      },
  1195      popup: function(text) {
  1196        var
  1197          html   = '',
  1198          escape = $.fn.popup.settings.templates.escape
  1199        ;
  1200        if(typeof text !== undefined) {
  1201          if(typeof text.title !== undefined && text.title) {
  1202            text.title = escape(text.title);
  1203            html += '<div class="header">' + text.title + '</div>';
  1204          }
  1205          if(typeof text.content !== undefined && text.content) {
  1206            text.content = escape(text.content);
  1207            html += '<div class="content">' + text.content + '</div>';
  1208          }
  1209        }
  1210        return html;
  1211      }
  1212    }
  1213  
  1214  };
  1215  
  1216  // Adds easing
  1217  $.extend( $.easing, {
  1218    easeOutQuad: function (x, t, b, c, d) {
  1219      return -c *(t/=d)*(t-2) + b;
  1220    }
  1221  });
  1222  
  1223  
  1224  })( jQuery, window , document );