JAVASCRIPT   28

outlayer js

Guest on 13th July 2022 06:52:33 AM

  1. /*!
  2.  * Outlayer v2.1.1
  3.  * the brains and guts of a layout library
  4.  * MIT license
  5.  */
  6.  
  7. ( function( window, factory ) {
  8.   'use strict';
  9.   // universal module definition
  10.   /* jshint strict: false */ /* globals define, module, require */
  11.   if ( typeof define == 'function' && define.amd ) {
  12.     // AMD - RequireJS
  13.     define( [
  14.         'ev-emitter/ev-emitter',
  15.         'get-size/get-size',
  16.         'fizzy-ui-utils/utils',
  17.         './item'
  18.       ],
  19.       function( EvEmitter, getSize, utils, Item ) {
  20.         return factory( window, EvEmitter, getSize, utils, Item);
  21.       }
  22.     );
  23.   } else if ( typeof module == 'object' && module.exports ) {
  24.     // CommonJS - Browserify, Webpack
  25.     module.exports = factory(
  26.       window,
  27.       require('ev-emitter'),
  28.       require('get-size'),
  29.       require('fizzy-ui-utils'),
  30.       require('./item')
  31.     );
  32.   } else {
  33.     // browser global
  34.     window.Outlayer = factory(
  35.       window,
  36.       window.EvEmitter,
  37.       window.getSize,
  38.       window.fizzyUIUtils,
  39.       window.Outlayer.Item
  40.     );
  41.   }
  42.  
  43. }( window, function factory( window, EvEmitter, getSize, utils, Item ) {
  44. 'use strict';
  45.  
  46. // ----- vars ----- //
  47.  
  48. var console = window.console;
  49. var jQuery = window.jQuery;
  50. var noop = function() {};
  51.  
  52. // -------------------------- Outlayer -------------------------- //
  53.  
  54. // globally unique identifiers
  55. var GUID = 0;
  56. // internal store of all Outlayer intances
  57. var instances = {};
  58.  
  59.  
  60. /**
  61.  * @param {Element, String} element
  62.  * @param {Object} options
  63.  * @constructor
  64.  */
  65. function Outlayer( element, options ) {
  66.   var queryElement = utils.getQueryElement( element );
  67.   if ( !queryElement ) {
  68.     if ( console ) {
  69.       console.error( 'Bad element for ' + this.constructor.namespace +
  70.         ': ' + ( queryElement || element ) );
  71.     }
  72.     return;
  73.   }
  74.   this.element = queryElement;
  75.   // add jQuery
  76.   if ( jQuery ) {
  77.     this.$element = jQuery( this.element );
  78.   }
  79.  
  80.   // options
  81.   this.options = utils.extend( {}, this.constructor.defaults );
  82.   this.option( options );
  83.  
  84.   // add id for Outlayer.getFromElement
  85.   var id = ++GUID;
  86.   this.element.outlayerGUID = id; // expando
  87.   instances[ id ] = this; // associate via id
  88.  
  89.   // kick it off
  90.   this._create();
  91.  
  92.   var isInitLayout = this._getOption('initLayout');
  93.   if ( isInitLayout ) {
  94.     this.layout();
  95.   }
  96. }
  97.  
  98. // settings are for internal use only
  99. Outlayer.namespace = 'outlayer';
  100. Outlayer.Item = Item;
  101.  
  102. // default options
  103. Outlayer.defaults = {
  104.   containerStyle: {
  105.     position: 'relative'
  106.   },
  107.   initLayout: true,
  108.   originLeft: true,
  109.   originTop: true,
  110.   resize: true,
  111.   resizeContainer: true,
  112.   // item options
  113.   transitionDuration: '0.4s',
  114.   hiddenStyle: {
  115.     opacity: 0,
  116.     transform: 'scale(0.001)'
  117.   },
  118.   visibleStyle: {
  119.     opacity: 1,
  120.     transform: 'scale(1)'
  121.   }
  122. };
  123.  
  124. var proto = Outlayer.prototype;
  125. // inherit EvEmitter
  126. utils.extend( proto, EvEmitter.prototype );
  127.  
  128. /**
  129.  * set options
  130.  * @param {Object} opts
  131.  */
  132. proto.option = function( opts ) {
  133.   utils.extend( this.options, opts );
  134. };
  135.  
  136. /**
  137.  * get backwards compatible option value, check old name
  138.  */
  139. proto._getOption = function( option ) {
  140.   var oldOption = this.constructor.compatOptions[ option ];
  141.   return oldOption && this.options[ oldOption ] !== undefined ?
  142.     this.options[ oldOption ] : this.options[ option ];
  143. };
  144.  
  145. Outlayer.compatOptions = {
  146.   // currentName: oldName
  147.   initLayout: 'isInitLayout',
  148.   horizontal: 'isHorizontal',
  149.   layoutInstant: 'isLayoutInstant',
  150.   originLeft: 'isOriginLeft',
  151.   originTop: 'isOriginTop',
  152.   resize: 'isResizeBound',
  153.   resizeContainer: 'isResizingContainer'
  154. };
  155.  
  156. proto._create = function() {
  157.   // get items from children
  158.   this.reloadItems();
  159.   // elements that affect layout, but are not laid out
  160.   this.stamps = [];
  161.   this.stamp( this.options.stamp );
  162.   // set container style
  163.   utils.extend( this.element.style, this.options.containerStyle );
  164.  
  165.   // bind resize method
  166.   var canBindResize = this._getOption('resize');
  167.   if ( canBindResize ) {
  168.     this.bindResize();
  169.   }
  170. };
  171.  
  172. // goes through all children again and gets bricks in proper order
  173. proto.reloadItems = function() {
  174.   // collection of item elements
  175.   this.items = this._itemize( this.element.children );
  176. };
  177.  
  178.  
  179. /**
  180.  * turn elements into Outlayer.Items to be used in layout
  181.  * @param {Array or NodeList or HTMLElement} elems
  182.  * @returns {Array} items - collection of new Outlayer Items
  183.  */
  184. proto._itemize = function( elems ) {
  185.  
  186.   var itemElems = this._filterFindItemElements( elems );
  187.   var Item = this.constructor.Item;
  188.  
  189.   // create new Outlayer Items for collection
  190.   var items = [];
  191.   for ( var i=0; i < itemElems.length; i++ ) {
  192.     var elem = itemElems[i];
  193.     var item = new Item( elem, this );
  194.     items.push( item );
  195.   }
  196.  
  197.   return items;
  198. };
  199.  
  200. /**
  201.  * get item elements to be used in layout
  202.  * @param {Array or NodeList or HTMLElement} elems
  203.  * @returns {Array} items - item elements
  204.  */
  205. proto._filterFindItemElements = function( elems ) {
  206.   return utils.filterFindElements( elems, this.options.itemSelector );
  207. };
  208.  
  209. /**
  210.  * getter method for getting item elements
  211.  * @returns {Array} elems - collection of item elements
  212.  */
  213. proto.getItemElements = function() {
  214.   return this.items.map( function( item ) {
  215.     return item.element;
  216.   });
  217. };
  218.  
  219. // ----- init & layout ----- //
  220.  
  221. /**
  222.  * lays out all items
  223.  */
  224. proto.layout = function() {
  225.   this._resetLayout();
  226.   this._manageStamps();
  227.  
  228.   // don't animate first layout
  229.   var layoutInstant = this._getOption('layoutInstant');
  230.   var isInstant = layoutInstant !== undefined ?
  231.     layoutInstant : !this._isLayoutInited;
  232.   this.layoutItems( this.items, isInstant );
  233.  
  234.   // flag for initalized
  235.   this._isLayoutInited = true;
  236. };
  237.  
  238. // _init is alias for layout
  239. proto._init = proto.layout;
  240.  
  241. /**
  242.  * logic before any new layout
  243.  */
  244. proto._resetLayout = function() {
  245.   this.getSize();
  246. };
  247.  
  248.  
  249. proto.getSize = function() {
  250.   this.size = getSize( this.element );
  251. };
  252.  
  253. /**
  254.  * get measurement from option, for columnWidth, rowHeight, gutter
  255.  * if option is String -> get element from selector string, & get size of element
  256.  * if option is Element -> get size of element
  257.  * else use option as a number
  258.  *
  259.  * @param {String} measurement
  260.  * @param {String} size - width or height
  261.  * @private
  262.  */
  263. proto._getMeasurement = function( measurement, size ) {
  264.   var option = this.options[ measurement ];
  265.   var elem;
  266.   if ( !option ) {
  267.     // default to 0
  268.     this[ measurement ] = 0;
  269.   } else {
  270.     // use option as an element
  271.     if ( typeof option == 'string' ) {
  272.       elem = this.element.querySelector( option );
  273.     } else if ( option instanceof HTMLElement ) {
  274.       elem = option;
  275.     }
  276.     // use size of element, if element
  277.     this[ measurement ] = elem ? getSize( elem )[ size ] : option;
  278.   }
  279. };
  280.  
  281. /**
  282.  * layout a collection of item elements
  283.  * @api public
  284.  */
  285. proto.layoutItems = function( items, isInstant ) {
  286.   items = this._getItemsForLayout( items );
  287.  
  288.   this._layoutItems( items, isInstant );
  289.  
  290.   this._postLayout();
  291. };
  292.  
  293. /**
  294.  * get the items to be laid out
  295.  * you may want to skip over some items
  296.  * @param {Array} items
  297.  * @returns {Array} items
  298.  */
  299. proto._getItemsForLayout = function( items ) {
  300.   return items.filter( function( item ) {
  301.     return !item.isIgnored;
  302.   });
  303. };
  304.  
  305. /**
  306.  * layout items
  307.  * @param {Array} items
  308.  * @param {Boolean} isInstant
  309.  */
  310. proto._layoutItems = function( items, isInstant ) {
  311.   this._emitCompleteOnItems( 'layout', items );
  312.  
  313.   if ( !items || !items.length ) {
  314.     // no items, emit event with empty array
  315.     return;
  316.   }
  317.  
  318.   var queue = [];
  319.  
  320.   items.forEach( function( item ) {
  321.     // get x/y object from method
  322.     var position = this._getItemLayoutPosition( item );
  323.     // enqueue
  324.     position.item = item;
  325.     position.isInstant = isInstant || item.isLayoutInstant;
  326.     queue.push( position );
  327.   }, this );
  328.  
  329.   this._processLayoutQueue( queue );
  330. };
  331.  
  332. /**
  333.  * get item layout position
  334.  * @param {Outlayer.Item} item
  335.  * @returns {Object} x and y position
  336.  */
  337. proto._getItemLayoutPosition = function( /* item */ ) {
  338.   return {
  339.     x: 0,
  340.     y: 0
  341.   };
  342. };
  343.  
  344. /**
  345.  * iterate over array and position each item
  346.  * Reason being - separating this logic prevents 'layout invalidation'
  347.  * thx @paul_irish
  348.  * @param {Array} queue
  349.  */
  350. proto._processLayoutQueue = function( queue ) {
  351.   this.updateStagger();
  352.   queue.forEach( function( obj, i ) {
  353.     this._positionItem( obj.item, obj.x, obj.y, obj.isInstant, i );
  354.   }, this );
  355. };
  356.  
  357. // set stagger from option in milliseconds number
  358. proto.updateStagger = function() {
  359.   var stagger = this.options.stagger;
  360.   if ( stagger === null || stagger === undefined ) {
  361.     this.stagger = 0;
  362.     return;
  363.   }
  364.   this.stagger = getMilliseconds( stagger );
  365.   return this.stagger;
  366. };
  367.  
  368. /**
  369.  * Sets position of item in DOM
  370.  * @param {Outlayer.Item} item
  371.  * @param {Number} x - horizontal position
  372.  * @param {Number} y - vertical position
  373.  * @param {Boolean} isInstant - disables transitions
  374.  */
  375. proto._positionItem = function( item, x, y, isInstant, i ) {
  376.   if ( isInstant ) {
  377.     // if not transition, just set CSS
  378.     item.goTo( x, y );
  379.   } else {
  380.     item.stagger( i * this.stagger );
  381.     item.moveTo( x, y );
  382.   }
  383. };
  384.  
  385. /**
  386.  * Any logic you want to do after each layout,
  387.  * i.e. size the container
  388.  */
  389. proto._postLayout = function() {
  390.   this.resizeContainer();
  391. };
  392.  
  393. proto.resizeContainer = function() {
  394.   var isResizingContainer = this._getOption('resizeContainer');
  395.   if ( !isResizingContainer ) {
  396.     return;
  397.   }
  398.   var size = this._getContainerSize();
  399.   if ( size ) {
  400.     this._setContainerMeasure( size.width, true );
  401.     this._setContainerMeasure( size.height, false );
  402.   }
  403. };
  404.  
  405. /**
  406.  * Sets width or height of container if returned
  407.  * @returns {Object} size
  408.  *   @param {Number} width
  409.  *   @param {Number} height
  410.  */
  411. proto._getContainerSize = noop;
  412.  
  413. /**
  414.  * @param {Number} measure - size of width or height
  415.  * @param {Boolean} isWidth
  416.  */
  417. proto._setContainerMeasure = function( measure, isWidth ) {
  418.   if ( measure === undefined ) {
  419.     return;
  420.   }
  421.  
  422.   var elemSize = this.size;
  423.   // add padding and border width if border box
  424.   if ( elemSize.isBorderBox ) {
  425.     measure += isWidth ? elemSize.paddingLeft + elemSize.paddingRight +
  426.       elemSize.borderLeftWidth + elemSize.borderRightWidth :
  427.       elemSize.paddingBottom + elemSize.paddingTop +
  428.       elemSize.borderTopWidth + elemSize.borderBottomWidth;
  429.   }
  430.  
  431.   measure = Math.max( measure, 0 );
  432.   this.element.style[ isWidth ? 'width' : 'height' ] = measure + 'px';
  433. };
  434.  
  435. /**
  436.  * emit eventComplete on a collection of items events
  437.  * @param {String} eventName
  438.  * @param {Array} items - Outlayer.Items
  439.  */
  440. proto._emitCompleteOnItems = function( eventName, items ) {
  441.   var _this = this;
  442.   function onComplete() {
  443.     _this.dispatchEvent( eventName + 'Complete', null, [ items ] );
  444.   }
  445.  
  446.   var count = items.length;
  447.   if ( !items || !count ) {
  448.     onComplete();
  449.     return;
  450.   }
  451.  
  452.   var doneCount = 0;
  453.   function tick() {
  454.     doneCount++;
  455.     if ( doneCount == count ) {
  456.       onComplete();
  457.     }
  458.   }
  459.  
  460.   // bind callback
  461.   items.forEach( function( item ) {
  462.     item.once( eventName, tick );
  463.   });
  464. };
  465.  
  466. /**
  467.  * emits events via EvEmitter and jQuery events
  468.  * @param {String} type - name of event
  469.  * @param {Event} event - original event
  470.  * @param {Array} args - extra arguments
  471.  */
  472. proto.dispatchEvent = function( type, event, args ) {
  473.   // add original event to arguments
  474.   var emitArgs = event ? [ event ].concat( args ) : args;
  475.   this.emitEvent( type, emitArgs );
  476.  
  477.   if ( jQuery ) {
  478.     // set this.$element
  479.     this.$element = this.$element || jQuery( this.element );
  480.     if ( event ) {
  481.       // create jQuery event
  482.       var $event = jQuery.Event( event );
  483.       $event.type = type;
  484.       this.$element.trigger( $event, args );
  485.     } else {
  486.       // just trigger with type if no event available
  487.       this.$element.trigger( type, args );
  488.     }
  489.   }
  490. };
  491.  
  492. // -------------------------- ignore & stamps -------------------------- //
  493.  
  494.  
  495. /**
  496.  * keep item in collection, but do not lay it out
  497.  * ignored items do not get skipped in layout
  498.  * @param {Element} elem
  499.  */
  500. proto.ignore = function( elem ) {
  501.   var item = this.getItem( elem );
  502.   if ( item ) {
  503.     item.isIgnored = true;
  504.   }
  505. };
  506.  
  507. /**
  508.  * return item to layout collection
  509.  * @param {Element} elem
  510.  */
  511. proto.unignore = function( elem ) {
  512.   var item = this.getItem( elem );
  513.   if ( item ) {
  514.     delete item.isIgnored;
  515.   }
  516. };
  517.  
  518. /**
  519.  * adds elements to stamps
  520.  * @param {NodeList, Array, Element, or String} elems
  521.  */
  522. proto.stamp = function( elems ) {
  523.   elems = this._find( elems );
  524.   if ( !elems ) {
  525.     return;
  526.   }
  527.  
  528.   this.stamps = this.stamps.concat( elems );
  529.   // ignore
  530.   elems.forEach( this.ignore, this );
  531. };
  532.  
  533. /**
  534.  * removes elements to stamps
  535.  * @param {NodeList, Array, or Element} elems
  536.  */
  537. proto.unstamp = function( elems ) {
  538.   elems = this._find( elems );
  539.   if ( !elems ){
  540.     return;
  541.   }
  542.  
  543.   elems.forEach( function( elem ) {
  544.     // filter out removed stamp elements
  545.     utils.removeFrom( this.stamps, elem );
  546.     this.unignore( elem );
  547.   }, this );
  548. };
  549.  
  550. /**
  551.  * finds child elements
  552.  * @param {NodeList, Array, Element, or String} elems
  553.  * @returns {Array} elems
  554.  */
  555. proto._find = function( elems ) {
  556.   if ( !elems ) {
  557.     return;
  558.   }
  559.   // if string, use argument as selector string
  560.   if ( typeof elems == 'string' ) {
  561.     elems = this.element.querySelectorAll( elems );
  562.   }
  563.   elems = utils.makeArray( elems );
  564.   return elems;
  565. };
  566.  
  567. proto._manageStamps = function() {
  568.   if ( !this.stamps || !this.stamps.length ) {
  569.     return;
  570.   }
  571.  
  572.   this._getBoundingRect();
  573.  
  574.   this.stamps.forEach( this._manageStamp, this );
  575. };
  576.  
  577. // update boundingLeft / Top
  578. proto._getBoundingRect = function() {
  579.   // get bounding rect for container element
  580.   var boundingRect = this.element.getBoundingClientRect();
  581.   var size = this.size;
  582.   this._boundingRect = {
  583.     left: boundingRect.left + size.paddingLeft + size.borderLeftWidth,
  584.     top: boundingRect.top + size.paddingTop + size.borderTopWidth,
  585.     right: boundingRect.right - ( size.paddingRight + size.borderRightWidth ),
  586.     bottom: boundingRect.bottom - ( size.paddingBottom + size.borderBottomWidth )
  587.   };
  588. };
  589.  
  590. /**
  591.  * @param {Element} stamp
  592. **/
  593. proto._manageStamp = noop;
  594.  
  595. /**
  596.  * get x/y position of element relative to container element
  597.  * @param {Element} elem
  598.  * @returns {Object} offset - has left, top, right, bottom
  599.  */
  600. proto._getElementOffset = function( elem ) {
  601.   var boundingRect = elem.getBoundingClientRect();
  602.   var thisRect = this._boundingRect;
  603.   var size = getSize( elem );
  604.   var offset = {
  605.     left: boundingRect.left - thisRect.left - size.marginLeft,
  606.     top: boundingRect.top - thisRect.top - size.marginTop,
  607.     right: thisRect.right - boundingRect.right - size.marginRight,
  608.     bottom: thisRect.bottom - boundingRect.bottom - size.marginBottom
  609.   };
  610.   return offset;
  611. };
  612.  
  613. // -------------------------- resize -------------------------- //
  614.  
  615. // enable event handlers for listeners
  616. // i.e. resize -> onresize
  617. proto.handleEvent = utils.handleEvent;
  618.  
  619. /**
  620.  * Bind layout to window resizing
  621.  */
  622. proto.bindResize = function() {
  623.   window.addEventListener( 'resize', this );
  624.   this.isResizeBound = true;
  625. };
  626.  
  627. /**
  628.  * Unbind layout to window resizing
  629.  */
  630. proto.unbindResize = function() {
  631.   window.removeEventListener( 'resize', this );
  632.   this.isResizeBound = false;
  633. };
  634.  
  635. proto.onresize = function() {
  636.   this.resize();
  637. };
  638.  
  639. utils.debounceMethod( Outlayer, 'onresize', 100 );
  640.  
  641. proto.resize = function() {
  642.   // don't trigger if size did not change
  643.   // or if resize was unbound. See #9
  644.   if ( !this.isResizeBound || !this.needsResizeLayout() ) {
  645.     return;
  646.   }
  647.  
  648.   this.layout();
  649. };
  650.  
  651. /**
  652.  * check if layout is needed post layout
  653.  * @returns Boolean
  654.  */
  655. proto.needsResizeLayout = function() {
  656.   var size = getSize( this.element );
  657.   // check that this.size and size are there
  658.   // IE8 triggers resize on body size change, so they might not be
  659.   var hasSizes = this.size && size;
  660.   return hasSizes && size.innerWidth !== this.size.innerWidth;
  661. };
  662.  
  663. // -------------------------- methods -------------------------- //
  664.  
  665. /**
  666.  * add items to Outlayer instance
  667.  * @param {Array or NodeList or Element} elems
  668.  * @returns {Array} items - Outlayer.Items
  669. **/
  670. proto.addItems = function( elems ) {
  671.   var items = this._itemize( elems );
  672.   // add items to collection
  673.   if ( items.length ) {
  674.     this.items = this.items.concat( items );
  675.   }
  676.   return items;
  677. };
  678.  
  679. /**
  680.  * Layout newly-appended item elements
  681.  * @param {Array or NodeList or Element} elems
  682.  */
  683. proto.appended = function( elems ) {
  684.   var items = this.addItems( elems );
  685.   if ( !items.length ) {
  686.     return;
  687.   }
  688.   // layout and reveal just the new items
  689.   this.layoutItems( items, true );
  690.   this.reveal( items );
  691. };
  692.  
  693. /**
  694.  * Layout prepended elements
  695.  * @param {Array or NodeList or Element} elems
  696.  */
  697. proto.prepended = function( elems ) {
  698.   var items = this._itemize( elems );
  699.   if ( !items.length ) {
  700.     return;
  701.   }
  702.   // add items to beginning of collection
  703.   var previousItems = this.items.slice(0);
  704.   this.items = items.concat( previousItems );
  705.   // start new layout
  706.   this._resetLayout();
  707.   this._manageStamps();
  708.   // layout new stuff without transition
  709.   this.layoutItems( items, true );
  710.   this.reveal( items );
  711.   // layout previous items
  712.   this.layoutItems( previousItems );
  713. };
  714.  
  715. /**
  716.  * reveal a collection of items
  717.  * @param {Array of Outlayer.Items} items
  718.  */
  719. proto.reveal = function( items ) {
  720.   this._emitCompleteOnItems( 'reveal', items );
  721.   if ( !items || !items.length ) {
  722.     return;
  723.   }
  724.   var stagger = this.updateStagger();
  725.   items.forEach( function( item, i ) {
  726.     item.stagger( i * stagger );
  727.     item.reveal();
  728.   });
  729. };
  730.  
  731. /**
  732.  * hide a collection of items
  733.  * @param {Array of Outlayer.Items} items
  734.  */
  735. proto.hide = function( items ) {
  736.   this._emitCompleteOnItems( 'hide', items );
  737.   if ( !items || !items.length ) {
  738.     return;
  739.   }
  740.   var stagger = this.updateStagger();
  741.   items.forEach( function( item, i ) {
  742.     item.stagger( i * stagger );
  743.     item.hide();
  744.   });
  745. };
  746.  
  747. /**
  748.  * reveal item elements
  749.  * @param {Array}, {Element}, {NodeList} items
  750.  */
  751. proto.revealItemElements = function( elems ) {
  752.   var items = this.getItems( elems );
  753.   this.reveal( items );
  754. };
  755.  
  756. /**
  757.  * hide item elements
  758.  * @param {Array}, {Element}, {NodeList} items
  759.  */
  760. proto.hideItemElements = function( elems ) {
  761.   var items = this.getItems( elems );
  762.   this.hide( items );
  763. };
  764.  
  765. /**
  766.  * get Outlayer.Item, given an Element
  767.  * @param {Element} elem
  768.  * @param {Function} callback
  769.  * @returns {Outlayer.Item} item
  770.  */
  771. proto.getItem = function( elem ) {
  772.   // loop through items to get the one that matches
  773.   for ( var i=0; i < this.items.length; i++ ) {
  774.     var item = this.items[i];
  775.     if ( item.element == elem ) {
  776.       // return item
  777.       return item;
  778.     }
  779.   }
  780. };
  781.  
  782. /**
  783.  * get collection of Outlayer.Items, given Elements
  784.  * @param {Array} elems
  785.  * @returns {Array} items - Outlayer.Items
  786.  */
  787. proto.getItems = function( elems ) {
  788.   elems = utils.makeArray( elems );
  789.   var items = [];
  790.   elems.forEach( function( elem ) {
  791.     var item = this.getItem( elem );
  792.     if ( item ) {
  793.       items.push( item );
  794.     }
  795.   }, this );
  796.  
  797.   return items;
  798. };
  799.  
  800. /**
  801.  * remove element(s) from instance and DOM
  802.  * @param {Array or NodeList or Element} elems
  803.  */
  804. proto.remove = function( elems ) {
  805.   var removeItems = this.getItems( elems );
  806.  
  807.   this._emitCompleteOnItems( 'remove', removeItems );
  808.  
  809.   // bail if no items to remove
  810.   if ( !removeItems || !removeItems.length ) {
  811.     return;
  812.   }
  813.  
  814.   removeItems.forEach( function( item ) {
  815.     item.remove();
  816.     // remove item from collection
  817.     utils.removeFrom( this.items, item );
  818.   }, this );
  819. };
  820.  
  821. // ----- destroy ----- //
  822.  
  823. // remove and disable Outlayer instance
  824. proto.destroy = function() {
  825.   // clean up dynamic styles
  826.   var style = this.element.style;
  827.   style.height = '';
  828.   style.position = '';
  829.   style.width = '';
  830.   // destroy items
  831.   this.items.forEach( function( item ) {
  832.     item.destroy();
  833.   });
  834.  
  835.   this.unbindResize();
  836.  
  837.   var id = this.element.outlayerGUID;
  838.   delete instances[ id ]; // remove reference to instance by id
  839.   delete this.element.outlayerGUID;
  840.   // remove data for jQuery
  841.   if ( jQuery ) {
  842.     jQuery.removeData( this.element, this.constructor.namespace );
  843.   }
  844.  
  845. };
  846.  
  847. // -------------------------- data -------------------------- //
  848.  
  849. /**
  850.  * get Outlayer instance from element
  851.  * @param {Element} elem
  852.  * @returns {Outlayer}
  853.  */
  854. Outlayer.data = function( elem ) {
  855.   elem = utils.getQueryElement( elem );
  856.   var id = elem && elem.outlayerGUID;
  857.   return id && instances[ id ];
  858. };
  859.  
  860.  
  861. // -------------------------- create Outlayer class -------------------------- //
  862.  
  863. /**
  864.  * create a layout class
  865.  * @param {String} namespace
  866.  */
  867. Outlayer.create = function( namespace, options ) {
  868.   // sub-class Outlayer
  869.   var Layout = subclass( Outlayer );
  870.   // apply new options and compatOptions
  871.   Layout.defaults = utils.extend( {}, Outlayer.defaults );
  872.   utils.extend( Layout.defaults, options );
  873.   Layout.compatOptions = utils.extend( {}, Outlayer.compatOptions  );
  874.  
  875.   Layout.namespace = namespace;
  876.  
  877.   Layout.data = Outlayer.data;
  878.  
  879.   // sub-class Item
  880.   Layout.Item = subclass( Item );
  881.  
  882.   // -------------------------- declarative -------------------------- //
  883.  
  884.   utils.htmlInit( Layout, namespace );
  885.  
  886.   // -------------------------- jQuery bridge -------------------------- //
  887.  
  888.   // make into jQuery plugin
  889.   if ( jQuery && jQuery.bridget ) {
  890.     jQuery.bridget( namespace, Layout );
  891.   }
  892.  
  893.   return Layout;
  894. };
  895.  
  896. function subclass( Parent ) {
  897.   function SubClass() {
  898.     Parent.apply( this, arguments );
  899.   }
  900.  
  901.   SubClass.prototype = Object.create( Parent.prototype );
  902.   SubClass.prototype.constructor = SubClass;
  903.  
  904.   return SubClass;
  905. }
  906.  
  907. // ----- helpers ----- //
  908.  
  909. // how many milliseconds are in each unit
  910. var msUnits = {
  911.   ms: 1,
  912.   s: 1000
  913. };
  914.  
  915. // munge time-like parameter into millisecond number
  916. // '0.4s' -> 40
  917. function getMilliseconds( time ) {
  918.   if ( typeof time == 'number' ) {
  919.     return time;
  920.   }
  921.   var matches = time.match( /(^\d*\.?\d*)(\w*)/ );
  922.   var num = matches && matches[1];
  923.   var unit = matches && matches[2];
  924.   if ( !num.length ) {
  925.     return 0;
  926.   }
  927.   num = parseFloat( num );
  928.   var mult = msUnits[ unit ] || 1;
  929.   return num * mult;
  930. }
  931.  
  932. // ----- fin ----- //
  933.  
  934. // back in global
  935. Outlayer.Item = Item;
  936.  
  937. return Outlayer;
  938.  
  939. }));

Raw Paste


Login or Register to edit or fork this paste. It's free.