outlayer.js 22 KB

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