isotope.js 126 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366336733683369337033713372337333743375337633773378337933803381338233833384338533863387338833893390339133923393339433953396339733983399340034013402340334043405340634073408340934103411341234133414341534163417341834193420342134223423342434253426342734283429343034313432343334343435343634373438343934403441344234433444344534463447344834493450345134523453345434553456345734583459346034613462346334643465346634673468346934703471347234733474347534763477347834793480348134823483348434853486348734883489349034913492349334943495349634973498349935003501350235033504350535063507350835093510351135123513351435153516351735183519352035213522352335243525352635273528352935303531353235333534353535363537353835393540354135423543354435453546354735483549355035513552355335543555355635573558355935603561356235633564356535663567356835693570357135723573357435753576357735783579358035813582358335843585358635873588358935903591359235933594359535963597359835993600360136023603360436053606360736083609361036113612361336143615361636173618361936203621362236233624362536263627362836293630363136323633363436353636363736383639364036413642364336443645364636473648364936503651365236533654365536563657365836593660366136623663366436653666366736683669367036713672367336743675367636773678367936803681368236833684368536863687368836893690369136923693369436953696369736983699370037013702370337043705370637073708370937103711371237133714371537163717371837193720372137223723372437253726372737283729373037313732373337343735373637373738373937403741374237433744374537463747374837493750375137523753375437553756375737583759376037613762376337643765376637673768376937703771377237733774377537763777377837793780378137823783378437853786378737883789379037913792379337943795379637973798379938003801380238033804380538063807380838093810381138123813381438153816381738183819382038213822382338243825382638273828382938303831383238333834383538363837383838393840384138423843384438453846384738483849385038513852385338543855385638573858385938603861386238633864386538663867386838693870387138723873387438753876387738783879388038813882388338843885388638873888388938903891389238933894389538963897389838993900390139023903390439053906390739083909391039113912391339143915391639173918391939203921392239233924
  1. /*!
  2. * Isotope PACKAGED v2.0.1
  3. * Filter & sort magical layouts
  4. * http://isotope.metafizzy.co
  5. */
  6. /**
  7. * Bridget makes jQuery widgets
  8. * v1.0.1
  9. */
  10. ( function( window ) {
  11. // -------------------------- utils -------------------------- //
  12. var slice = Array.prototype.slice;
  13. function noop() {}
  14. // -------------------------- definition -------------------------- //
  15. function defineBridget( $ ) {
  16. // bail if no jQuery
  17. if ( !$ ) {
  18. return;
  19. }
  20. // -------------------------- addOptionMethod -------------------------- //
  21. /**
  22. * adds option method -> $().plugin('option', {...})
  23. * @param {Function} PluginClass - constructor class
  24. */
  25. function addOptionMethod( PluginClass ) {
  26. // don't overwrite original option method
  27. if ( PluginClass.prototype.option ) {
  28. return;
  29. }
  30. // option setter
  31. PluginClass.prototype.option = function( opts ) {
  32. // bail out if not an object
  33. if ( !$.isPlainObject( opts ) ){
  34. return;
  35. }
  36. this.options = $.extend( true, this.options, opts );
  37. };
  38. }
  39. // -------------------------- plugin bridge -------------------------- //
  40. // helper function for logging errors
  41. // $.error breaks jQuery chaining
  42. var logError = typeof console === 'undefined' ? noop :
  43. function( message ) {
  44. console.error( message );
  45. };
  46. /**
  47. * jQuery plugin bridge, access methods like $elem.plugin('method')
  48. * @param {String} namespace - plugin name
  49. * @param {Function} PluginClass - constructor class
  50. */
  51. function bridge( namespace, PluginClass ) {
  52. // add to jQuery fn namespace
  53. $.fn[ namespace ] = function( options ) {
  54. if ( typeof options === 'string' ) {
  55. // call plugin method when first argument is a string
  56. // get arguments for method
  57. var args = slice.call( arguments, 1 );
  58. for ( var i=0, len = this.length; i < len; i++ ) {
  59. var elem = this[i];
  60. var instance = $.data( elem, namespace );
  61. if ( !instance ) {
  62. logError( "cannot call methods on " + namespace + " prior to initialization; " +
  63. "attempted to call '" + options + "'" );
  64. continue;
  65. }
  66. if ( !$.isFunction( instance[options] ) || options.charAt(0) === '_' ) {
  67. logError( "no such method '" + options + "' for " + namespace + " instance" );
  68. continue;
  69. }
  70. // trigger method with arguments
  71. var returnValue = instance[ options ].apply( instance, args );
  72. // break look and return first value if provided
  73. if ( returnValue !== undefined ) {
  74. return returnValue;
  75. }
  76. }
  77. // return this if no return value
  78. return this;
  79. } else {
  80. return this.each( function() {
  81. var instance = $.data( this, namespace );
  82. if ( instance ) {
  83. // apply options & init
  84. instance.option( options );
  85. instance._init();
  86. } else {
  87. // initialize new instance
  88. instance = new PluginClass( this, options );
  89. $.data( this, namespace, instance );
  90. }
  91. });
  92. }
  93. };
  94. }
  95. // -------------------------- bridget -------------------------- //
  96. /**
  97. * converts a Prototypical class into a proper jQuery plugin
  98. * the class must have a ._init method
  99. * @param {String} namespace - plugin name, used in $().pluginName
  100. * @param {Function} PluginClass - constructor class
  101. */
  102. $.bridget = function( namespace, PluginClass ) {
  103. addOptionMethod( PluginClass );
  104. bridge( namespace, PluginClass );
  105. };
  106. return $.bridget;
  107. }
  108. // transport
  109. if ( typeof define === 'function' && define.amd ) {
  110. // AMD
  111. define( 'jquery-bridget/jquery.bridget',[ 'jquery' ], defineBridget );
  112. } else {
  113. // get jquery from browser global
  114. defineBridget( window.jQuery );
  115. }
  116. })( window );
  117. /*!
  118. * eventie v1.0.5
  119. * event binding helper
  120. * eventie.bind( elem, 'click', myFn )
  121. * eventie.unbind( elem, 'click', myFn )
  122. * MIT license
  123. */
  124. /*jshint browser: true, undef: true, unused: true */
  125. /*global define: false, module: false */
  126. ( function( window ) {
  127. var docElem = document.documentElement;
  128. var bind = function() {};
  129. function getIEEvent( obj ) {
  130. var event = window.event;
  131. // add event.target
  132. event.target = event.target || event.srcElement || obj;
  133. return event;
  134. }
  135. if ( docElem.addEventListener ) {
  136. bind = function( obj, type, fn ) {
  137. obj.addEventListener( type, fn, false );
  138. };
  139. } else if ( docElem.attachEvent ) {
  140. bind = function( obj, type, fn ) {
  141. obj[ type + fn ] = fn.handleEvent ?
  142. function() {
  143. var event = getIEEvent( obj );
  144. fn.handleEvent.call( fn, event );
  145. } :
  146. function() {
  147. var event = getIEEvent( obj );
  148. fn.call( obj, event );
  149. };
  150. obj.attachEvent( "on" + type, obj[ type + fn ] );
  151. };
  152. }
  153. var unbind = function() {};
  154. if ( docElem.removeEventListener ) {
  155. unbind = function( obj, type, fn ) {
  156. obj.removeEventListener( type, fn, false );
  157. };
  158. } else if ( docElem.detachEvent ) {
  159. unbind = function( obj, type, fn ) {
  160. obj.detachEvent( "on" + type, obj[ type + fn ] );
  161. try {
  162. delete obj[ type + fn ];
  163. } catch ( err ) {
  164. // can't delete window object properties
  165. obj[ type + fn ] = undefined;
  166. }
  167. };
  168. }
  169. var eventie = {
  170. bind: bind,
  171. unbind: unbind
  172. };
  173. // ----- module definition ----- //
  174. if ( typeof define === 'function' && define.amd ) {
  175. // AMD
  176. define( 'eventie/eventie',eventie );
  177. } else if ( typeof exports === 'object' ) {
  178. // CommonJS
  179. module.exports = eventie;
  180. } else {
  181. // browser global
  182. window.eventie = eventie;
  183. }
  184. })( this );
  185. /*!
  186. * docReady
  187. * Cross browser DOMContentLoaded event emitter
  188. */
  189. /*jshint browser: true, strict: true, undef: true, unused: true*/
  190. /*global define: false */
  191. ( function( window ) {
  192. var document = window.document;
  193. // collection of functions to be triggered on ready
  194. var queue = [];
  195. function docReady( fn ) {
  196. // throw out non-functions
  197. if ( typeof fn !== 'function' ) {
  198. return;
  199. }
  200. if ( docReady.isReady ) {
  201. // ready now, hit it
  202. fn();
  203. } else {
  204. // queue function when ready
  205. queue.push( fn );
  206. }
  207. }
  208. docReady.isReady = false;
  209. // triggered on various doc ready events
  210. function init( event ) {
  211. // bail if IE8 document is not ready just yet
  212. var isIE8NotReady = event.type === 'readystatechange' && document.readyState !== 'complete';
  213. if ( docReady.isReady || isIE8NotReady ) {
  214. return;
  215. }
  216. docReady.isReady = true;
  217. // process queue
  218. for ( var i=0, len = queue.length; i < len; i++ ) {
  219. var fn = queue[i];
  220. fn();
  221. }
  222. }
  223. function defineDocReady( eventie ) {
  224. eventie.bind( document, 'DOMContentLoaded', init );
  225. eventie.bind( document, 'readystatechange', init );
  226. eventie.bind( window, 'load', init );
  227. return docReady;
  228. }
  229. // transport
  230. if ( typeof define === 'function' && define.amd ) {
  231. // AMD
  232. // if RequireJS, then doc is already ready
  233. docReady.isReady = typeof requirejs === 'function';
  234. define( 'doc-ready/doc-ready',[ 'eventie/eventie' ], defineDocReady );
  235. } else {
  236. // browser global
  237. window.docReady = defineDocReady( window.eventie );
  238. }
  239. })( this );
  240. /*!
  241. * EventEmitter v4.2.7 - git.io/ee
  242. * Oliver Caldwell
  243. * MIT license
  244. * @preserve
  245. */
  246. (function () {
  247. /**
  248. * Class for managing events.
  249. * Can be extended to provide event functionality in other classes.
  250. *
  251. * @class EventEmitter Manages event registering and emitting.
  252. */
  253. function EventEmitter() {}
  254. // Shortcuts to improve speed and size
  255. var proto = EventEmitter.prototype;
  256. var exports = this;
  257. var originalGlobalValue = exports.EventEmitter;
  258. /**
  259. * Finds the index of the listener for the event in it's storage array.
  260. *
  261. * @param {Function[]} listeners Array of listeners to search through.
  262. * @param {Function} listener Method to look for.
  263. * @return {Number} Index of the specified listener, -1 if not found
  264. * @api private
  265. */
  266. function indexOfListener(listeners, listener) {
  267. var i = listeners.length;
  268. while (i--) {
  269. if (listeners[i].listener === listener) {
  270. return i;
  271. }
  272. }
  273. return -1;
  274. }
  275. /**
  276. * Alias a method while keeping the context correct, to allow for overwriting of target method.
  277. *
  278. * @param {String} name The name of the target method.
  279. * @return {Function} The aliased method
  280. * @api private
  281. */
  282. function alias(name) {
  283. return function aliasClosure() {
  284. return this[name].apply(this, arguments);
  285. };
  286. }
  287. /**
  288. * Returns the listener array for the specified event.
  289. * Will initialise the event object and listener arrays if required.
  290. * Will return an object if you use a regex search. The object contains keys for each matched event. So /ba[rz]/ might return an object containing bar and baz. But only if you have either defined them with defineEvent or added some listeners to them.
  291. * Each property in the object response is an array of listener functions.
  292. *
  293. * @param {String|RegExp} evt Name of the event to return the listeners from.
  294. * @return {Function[]|Object} All listener functions for the event.
  295. */
  296. proto.getListeners = function getListeners(evt) {
  297. var events = this._getEvents();
  298. var response;
  299. var key;
  300. // Return a concatenated array of all matching events if
  301. // the selector is a regular expression.
  302. if (evt instanceof RegExp) {
  303. response = {};
  304. for (key in events) {
  305. if (events.hasOwnProperty(key) && evt.test(key)) {
  306. response[key] = events[key];
  307. }
  308. }
  309. }
  310. else {
  311. response = events[evt] || (events[evt] = []);
  312. }
  313. return response;
  314. };
  315. /**
  316. * Takes a list of listener objects and flattens it into a list of listener functions.
  317. *
  318. * @param {Object[]} listeners Raw listener objects.
  319. * @return {Function[]} Just the listener functions.
  320. */
  321. proto.flattenListeners = function flattenListeners(listeners) {
  322. var flatListeners = [];
  323. var i;
  324. for (i = 0; i < listeners.length; i += 1) {
  325. flatListeners.push(listeners[i].listener);
  326. }
  327. return flatListeners;
  328. };
  329. /**
  330. * Fetches the requested listeners via getListeners but will always return the results inside an object. This is mainly for internal use but others may find it useful.
  331. *
  332. * @param {String|RegExp} evt Name of the event to return the listeners from.
  333. * @return {Object} All listener functions for an event in an object.
  334. */
  335. proto.getListenersAsObject = function getListenersAsObject(evt) {
  336. var listeners = this.getListeners(evt);
  337. var response;
  338. if (listeners instanceof Array) {
  339. response = {};
  340. response[evt] = listeners;
  341. }
  342. return response || listeners;
  343. };
  344. /**
  345. * Adds a listener function to the specified event.
  346. * The listener will not be added if it is a duplicate.
  347. * If the listener returns true then it will be removed after it is called.
  348. * If you pass a regular expression as the event name then the listener will be added to all events that match it.
  349. *
  350. * @param {String|RegExp} evt Name of the event to attach the listener to.
  351. * @param {Function} listener Method to be called when the event is emitted. If the function returns true then it will be removed after calling.
  352. * @return {Object} Current instance of EventEmitter for chaining.
  353. */
  354. proto.addListener = function addListener(evt, listener) {
  355. var listeners = this.getListenersAsObject(evt);
  356. var listenerIsWrapped = typeof listener === 'object';
  357. var key;
  358. for (key in listeners) {
  359. if (listeners.hasOwnProperty(key) && indexOfListener(listeners[key], listener) === -1) {
  360. listeners[key].push(listenerIsWrapped ? listener : {
  361. listener: listener,
  362. once: false
  363. });
  364. }
  365. }
  366. return this;
  367. };
  368. /**
  369. * Alias of addListener
  370. */
  371. proto.on = alias('addListener');
  372. /**
  373. * Semi-alias of addListener. It will add a listener that will be
  374. * automatically removed after it's first execution.
  375. *
  376. * @param {String|RegExp} evt Name of the event to attach the listener to.
  377. * @param {Function} listener Method to be called when the event is emitted. If the function returns true then it will be removed after calling.
  378. * @return {Object} Current instance of EventEmitter for chaining.
  379. */
  380. proto.addOnceListener = function addOnceListener(evt, listener) {
  381. return this.addListener(evt, {
  382. listener: listener,
  383. once: true
  384. });
  385. };
  386. /**
  387. * Alias of addOnceListener.
  388. */
  389. proto.once = alias('addOnceListener');
  390. /**
  391. * Defines an event name. This is required if you want to use a regex to add a listener to multiple events at once. If you don't do this then how do you expect it to know what event to add to? Should it just add to every possible match for a regex? No. That is scary and bad.
  392. * You need to tell it what event names should be matched by a regex.
  393. *
  394. * @param {String} evt Name of the event to create.
  395. * @return {Object} Current instance of EventEmitter for chaining.
  396. */
  397. proto.defineEvent = function defineEvent(evt) {
  398. this.getListeners(evt);
  399. return this;
  400. };
  401. /**
  402. * Uses defineEvent to define multiple events.
  403. *
  404. * @param {String[]} evts An array of event names to define.
  405. * @return {Object} Current instance of EventEmitter for chaining.
  406. */
  407. proto.defineEvents = function defineEvents(evts) {
  408. for (var i = 0; i < evts.length; i += 1) {
  409. this.defineEvent(evts[i]);
  410. }
  411. return this;
  412. };
  413. /**
  414. * Removes a listener function from the specified event.
  415. * When passed a regular expression as the event name, it will remove the listener from all events that match it.
  416. *
  417. * @param {String|RegExp} evt Name of the event to remove the listener from.
  418. * @param {Function} listener Method to remove from the event.
  419. * @return {Object} Current instance of EventEmitter for chaining.
  420. */
  421. proto.removeListener = function removeListener(evt, listener) {
  422. var listeners = this.getListenersAsObject(evt);
  423. var index;
  424. var key;
  425. for (key in listeners) {
  426. if (listeners.hasOwnProperty(key)) {
  427. index = indexOfListener(listeners[key], listener);
  428. if (index !== -1) {
  429. listeners[key].splice(index, 1);
  430. }
  431. }
  432. }
  433. return this;
  434. };
  435. /**
  436. * Alias of removeListener
  437. */
  438. proto.off = alias('removeListener');
  439. /**
  440. * Adds listeners in bulk using the manipulateListeners method.
  441. * If you pass an object as the second argument you can add to multiple events at once. The object should contain key value pairs of events and listeners or listener arrays. You can also pass it an event name and an array of listeners to be added.
  442. * You can also pass it a regular expression to add the array of listeners to all events that match it.
  443. * Yeah, this function does quite a bit. That's probably a bad thing.
  444. *
  445. * @param {String|Object|RegExp} evt An event name if you will pass an array of listeners next. An object if you wish to add to multiple events at once.
  446. * @param {Function[]} [listeners] An optional array of listener functions to add.
  447. * @return {Object} Current instance of EventEmitter for chaining.
  448. */
  449. proto.addListeners = function addListeners(evt, listeners) {
  450. // Pass through to manipulateListeners
  451. return this.manipulateListeners(false, evt, listeners);
  452. };
  453. /**
  454. * Removes listeners in bulk using the manipulateListeners method.
  455. * If you pass an object as the second argument you can remove from multiple events at once. The object should contain key value pairs of events and listeners or listener arrays.
  456. * You can also pass it an event name and an array of listeners to be removed.
  457. * You can also pass it a regular expression to remove the listeners from all events that match it.
  458. *
  459. * @param {String|Object|RegExp} evt An event name if you will pass an array of listeners next. An object if you wish to remove from multiple events at once.
  460. * @param {Function[]} [listeners] An optional array of listener functions to remove.
  461. * @return {Object} Current instance of EventEmitter for chaining.
  462. */
  463. proto.removeListeners = function removeListeners(evt, listeners) {
  464. // Pass through to manipulateListeners
  465. return this.manipulateListeners(true, evt, listeners);
  466. };
  467. /**
  468. * Edits listeners in bulk. The addListeners and removeListeners methods both use this to do their job. You should really use those instead, this is a little lower level.
  469. * The first argument will determine if the listeners are removed (true) or added (false).
  470. * If you pass an object as the second argument you can add/remove from multiple events at once. The object should contain key value pairs of events and listeners or listener arrays.
  471. * You can also pass it an event name and an array of listeners to be added/removed.
  472. * You can also pass it a regular expression to manipulate the listeners of all events that match it.
  473. *
  474. * @param {Boolean} remove True if you want to remove listeners, false if you want to add.
  475. * @param {String|Object|RegExp} evt An event name if you will pass an array of listeners next. An object if you wish to add/remove from multiple events at once.
  476. * @param {Function[]} [listeners] An optional array of listener functions to add/remove.
  477. * @return {Object} Current instance of EventEmitter for chaining.
  478. */
  479. proto.manipulateListeners = function manipulateListeners(remove, evt, listeners) {
  480. var i;
  481. var value;
  482. var single = remove ? this.removeListener : this.addListener;
  483. var multiple = remove ? this.removeListeners : this.addListeners;
  484. // If evt is an object then pass each of it's properties to this method
  485. if (typeof evt === 'object' && !(evt instanceof RegExp)) {
  486. for (i in evt) {
  487. if (evt.hasOwnProperty(i) && (value = evt[i])) {
  488. // Pass the single listener straight through to the singular method
  489. if (typeof value === 'function') {
  490. single.call(this, i, value);
  491. }
  492. else {
  493. // Otherwise pass back to the multiple function
  494. multiple.call(this, i, value);
  495. }
  496. }
  497. }
  498. }
  499. else {
  500. // So evt must be a string
  501. // And listeners must be an array of listeners
  502. // Loop over it and pass each one to the multiple method
  503. i = listeners.length;
  504. while (i--) {
  505. single.call(this, evt, listeners[i]);
  506. }
  507. }
  508. return this;
  509. };
  510. /**
  511. * Removes all listeners from a specified event.
  512. * If you do not specify an event then all listeners will be removed.
  513. * That means every event will be emptied.
  514. * You can also pass a regex to remove all events that match it.
  515. *
  516. * @param {String|RegExp} [evt] Optional name of the event to remove all listeners for. Will remove from every event if not passed.
  517. * @return {Object} Current instance of EventEmitter for chaining.
  518. */
  519. proto.removeEvent = function removeEvent(evt) {
  520. var type = typeof evt;
  521. var events = this._getEvents();
  522. var key;
  523. // Remove different things depending on the state of evt
  524. if (type === 'string') {
  525. // Remove all listeners for the specified event
  526. delete events[evt];
  527. }
  528. else if (evt instanceof RegExp) {
  529. // Remove all events matching the regex.
  530. for (key in events) {
  531. if (events.hasOwnProperty(key) && evt.test(key)) {
  532. delete events[key];
  533. }
  534. }
  535. }
  536. else {
  537. // Remove all listeners in all events
  538. delete this._events;
  539. }
  540. return this;
  541. };
  542. /**
  543. * Alias of removeEvent.
  544. *
  545. * Added to mirror the node API.
  546. */
  547. proto.removeAllListeners = alias('removeEvent');
  548. /**
  549. * Emits an event of your choice.
  550. * When emitted, every listener attached to that event will be executed.
  551. * If you pass the optional argument array then those arguments will be passed to every listener upon execution.
  552. * Because it uses `apply`, your array of arguments will be passed as if you wrote them out separately.
  553. * So they will not arrive within the array on the other side, they will be separate.
  554. * You can also pass a regular expression to emit to all events that match it.
  555. *
  556. * @param {String|RegExp} evt Name of the event to emit and execute listeners for.
  557. * @param {Array} [args] Optional array of arguments to be passed to each listener.
  558. * @return {Object} Current instance of EventEmitter for chaining.
  559. */
  560. proto.emitEvent = function emitEvent(evt, args) {
  561. var listeners = this.getListenersAsObject(evt);
  562. var listener;
  563. var i;
  564. var key;
  565. var response;
  566. for (key in listeners) {
  567. if (listeners.hasOwnProperty(key)) {
  568. i = listeners[key].length;
  569. while (i--) {
  570. // If the listener returns true then it shall be removed from the event
  571. // The function is executed either with a basic call or an apply if there is an args array
  572. listener = listeners[key][i];
  573. if (listener.once === true) {
  574. this.removeListener(evt, listener.listener);
  575. }
  576. response = listener.listener.apply(this, args || []);
  577. if (response === this._getOnceReturnValue()) {
  578. this.removeListener(evt, listener.listener);
  579. }
  580. }
  581. }
  582. }
  583. return this;
  584. };
  585. /**
  586. * Alias of emitEvent
  587. */
  588. proto.trigger = alias('emitEvent');
  589. /**
  590. * Subtly different from emitEvent in that it will pass its arguments on to the listeners, as opposed to taking a single array of arguments to pass on.
  591. * As with emitEvent, you can pass a regex in place of the event name to emit to all events that match it.
  592. *
  593. * @param {String|RegExp} evt Name of the event to emit and execute listeners for.
  594. * @param {...*} Optional additional arguments to be passed to each listener.
  595. * @return {Object} Current instance of EventEmitter for chaining.
  596. */
  597. proto.emit = function emit(evt) {
  598. var args = Array.prototype.slice.call(arguments, 1);
  599. return this.emitEvent(evt, args);
  600. };
  601. /**
  602. * Sets the current value to check against when executing listeners. If a
  603. * listeners return value matches the one set here then it will be removed
  604. * after execution. This value defaults to true.
  605. *
  606. * @param {*} value The new value to check for when executing listeners.
  607. * @return {Object} Current instance of EventEmitter for chaining.
  608. */
  609. proto.setOnceReturnValue = function setOnceReturnValue(value) {
  610. this._onceReturnValue = value;
  611. return this;
  612. };
  613. /**
  614. * Fetches the current value to check against when executing listeners. If
  615. * the listeners return value matches this one then it should be removed
  616. * automatically. It will return true by default.
  617. *
  618. * @return {*|Boolean} The current value to check for or the default, true.
  619. * @api private
  620. */
  621. proto._getOnceReturnValue = function _getOnceReturnValue() {
  622. if (this.hasOwnProperty('_onceReturnValue')) {
  623. return this._onceReturnValue;
  624. }
  625. else {
  626. return true;
  627. }
  628. };
  629. /**
  630. * Fetches the events object and creates one if required.
  631. *
  632. * @return {Object} The events storage object.
  633. * @api private
  634. */
  635. proto._getEvents = function _getEvents() {
  636. return this._events || (this._events = {});
  637. };
  638. /**
  639. * Reverts the global {@link EventEmitter} to its previous value and returns a reference to this version.
  640. *
  641. * @return {Function} Non conflicting EventEmitter class.
  642. */
  643. EventEmitter.noConflict = function noConflict() {
  644. exports.EventEmitter = originalGlobalValue;
  645. return EventEmitter;
  646. };
  647. // Expose the class either via AMD, CommonJS or the global object
  648. if (typeof define === 'function' && define.amd) {
  649. define('eventEmitter/EventEmitter',[],function () {
  650. return EventEmitter;
  651. });
  652. }
  653. else if (typeof module === 'object' && module.exports){
  654. module.exports = EventEmitter;
  655. }
  656. else {
  657. this.EventEmitter = EventEmitter;
  658. }
  659. }.call(this));
  660. /*!
  661. * getStyleProperty v1.0.3
  662. * original by kangax
  663. * http://perfectionkills.com/feature-testing-css-properties/
  664. */
  665. /*jshint browser: true, strict: true, undef: true */
  666. /*global define: false, exports: false, module: false */
  667. ( function( window ) {
  668. var prefixes = 'Webkit Moz ms Ms O'.split(' ');
  669. var docElemStyle = document.documentElement.style;
  670. function getStyleProperty( propName ) {
  671. if ( !propName ) {
  672. return;
  673. }
  674. // test standard property first
  675. if ( typeof docElemStyle[ propName ] === 'string' ) {
  676. return propName;
  677. }
  678. // capitalize
  679. propName = propName.charAt(0).toUpperCase() + propName.slice(1);
  680. // test vendor specific properties
  681. var prefixed;
  682. for ( var i=0, len = prefixes.length; i < len; i++ ) {
  683. prefixed = prefixes[i] + propName;
  684. if ( typeof docElemStyle[ prefixed ] === 'string' ) {
  685. return prefixed;
  686. }
  687. }
  688. }
  689. // transport
  690. if ( typeof define === 'function' && define.amd ) {
  691. // AMD
  692. define( 'get-style-property/get-style-property',[],function() {
  693. return getStyleProperty;
  694. });
  695. } else if ( typeof exports === 'object' ) {
  696. // CommonJS for Component
  697. module.exports = getStyleProperty;
  698. } else {
  699. // browser global
  700. window.getStyleProperty = getStyleProperty;
  701. }
  702. })( window );
  703. /**
  704. * getSize v1.1.7
  705. * measure size of elements
  706. */
  707. /*jshint browser: true, strict: true, undef: true, unused: true */
  708. /*global define: false, exports: false, require: false, module: false */
  709. ( function( window, undefined ) {
  710. // -------------------------- helpers -------------------------- //
  711. var getComputedStyle = window.getComputedStyle;
  712. var getStyle = getComputedStyle ?
  713. function( elem ) {
  714. return getComputedStyle( elem, null );
  715. } :
  716. function( elem ) {
  717. return elem.currentStyle;
  718. };
  719. // get a number from a string, not a percentage
  720. function getStyleSize( value ) {
  721. var num = parseFloat( value );
  722. // not a percent like '100%', and a number
  723. var isValid = value.indexOf('%') === -1 && !isNaN( num );
  724. return isValid && num;
  725. }
  726. // -------------------------- measurements -------------------------- //
  727. var measurements = [
  728. 'paddingLeft',
  729. 'paddingRight',
  730. 'paddingTop',
  731. 'paddingBottom',
  732. 'marginLeft',
  733. 'marginRight',
  734. 'marginTop',
  735. 'marginBottom',
  736. 'borderLeftWidth',
  737. 'borderRightWidth',
  738. 'borderTopWidth',
  739. 'borderBottomWidth'
  740. ];
  741. function getZeroSize() {
  742. var size = {
  743. width: 0,
  744. height: 0,
  745. innerWidth: 0,
  746. innerHeight: 0,
  747. outerWidth: 0,
  748. outerHeight: 0
  749. };
  750. for ( var i=0, len = measurements.length; i < len; i++ ) {
  751. var measurement = measurements[i];
  752. size[ measurement ] = 0;
  753. }
  754. return size;
  755. }
  756. function defineGetSize( getStyleProperty ) {
  757. // -------------------------- box sizing -------------------------- //
  758. var boxSizingProp = getStyleProperty('boxSizing');
  759. var isBoxSizeOuter;
  760. /**
  761. * WebKit measures the outer-width on style.width on border-box elems
  762. * IE & Firefox measures the inner-width
  763. */
  764. ( function() {
  765. if ( !boxSizingProp ) {
  766. return;
  767. }
  768. var div = document.createElement('div');
  769. div.style.width = '200px';
  770. div.style.padding = '1px 2px 3px 4px';
  771. div.style.borderStyle = 'solid';
  772. div.style.borderWidth = '1px 2px 3px 4px';
  773. div.style[ boxSizingProp ] = 'border-box';
  774. var body = document.body || document.documentElement;
  775. body.appendChild( div );
  776. var style = getStyle( div );
  777. isBoxSizeOuter = getStyleSize( style.width ) === 200;
  778. body.removeChild( div );
  779. })();
  780. // -------------------------- getSize -------------------------- //
  781. function getSize( elem ) {
  782. // use querySeletor if elem is string
  783. if ( typeof elem === 'string' ) {
  784. elem = document.querySelector( elem );
  785. }
  786. // do not proceed on non-objects
  787. if ( !elem || typeof elem !== 'object' || !elem.nodeType ) {
  788. return;
  789. }
  790. var style = getStyle( elem );
  791. // if hidden, everything is 0
  792. if ( style.display === 'none' ) {
  793. return getZeroSize();
  794. }
  795. var size = {};
  796. size.width = elem.offsetWidth;
  797. size.height = elem.offsetHeight;
  798. var isBorderBox = size.isBorderBox = !!( boxSizingProp &&
  799. style[ boxSizingProp ] && style[ boxSizingProp ] === 'border-box' );
  800. // get all measurements
  801. for ( var i=0, len = measurements.length; i < len; i++ ) {
  802. var measurement = measurements[i];
  803. var value = style[ measurement ];
  804. value = mungeNonPixel( elem, value );
  805. var num = parseFloat( value );
  806. // any 'auto', 'medium' value will be 0
  807. size[ measurement ] = !isNaN( num ) ? num : 0;
  808. }
  809. var paddingWidth = size.paddingLeft + size.paddingRight;
  810. var paddingHeight = size.paddingTop + size.paddingBottom;
  811. var marginWidth = size.marginLeft + size.marginRight;
  812. var marginHeight = size.marginTop + size.marginBottom;
  813. var borderWidth = size.borderLeftWidth + size.borderRightWidth;
  814. var borderHeight = size.borderTopWidth + size.borderBottomWidth;
  815. var isBorderBoxSizeOuter = isBorderBox && isBoxSizeOuter;
  816. // overwrite width and height if we can get it from style
  817. var styleWidth = getStyleSize( style.width );
  818. if ( styleWidth !== false ) {
  819. size.width = styleWidth +
  820. // add padding and border unless it's already including it
  821. ( isBorderBoxSizeOuter ? 0 : paddingWidth + borderWidth );
  822. }
  823. var styleHeight = getStyleSize( style.height );
  824. if ( styleHeight !== false ) {
  825. size.height = styleHeight +
  826. // add padding and border unless it's already including it
  827. ( isBorderBoxSizeOuter ? 0 : paddingHeight + borderHeight );
  828. }
  829. size.innerWidth = size.width - ( paddingWidth + borderWidth );
  830. size.innerHeight = size.height - ( paddingHeight + borderHeight );
  831. size.outerWidth = size.width + marginWidth;
  832. size.outerHeight = size.height + marginHeight;
  833. return size;
  834. }
  835. // IE8 returns percent values, not pixels
  836. // taken from jQuery's curCSS
  837. function mungeNonPixel( elem, value ) {
  838. // IE8 and has percent value
  839. if ( getComputedStyle || value.indexOf('%') === -1 ) {
  840. return value;
  841. }
  842. var style = elem.style;
  843. // Remember the original values
  844. var left = style.left;
  845. var rs = elem.runtimeStyle;
  846. var rsLeft = rs && rs.left;
  847. // Put in the new values to get a computed value out
  848. if ( rsLeft ) {
  849. rs.left = elem.currentStyle.left;
  850. }
  851. style.left = value;
  852. value = style.pixelLeft;
  853. // Revert the changed values
  854. style.left = left;
  855. if ( rsLeft ) {
  856. rs.left = rsLeft;
  857. }
  858. return value;
  859. }
  860. return getSize;
  861. }
  862. // transport
  863. if ( typeof define === 'function' && define.amd ) {
  864. // AMD for RequireJS
  865. define( 'get-size/get-size',[ 'get-style-property/get-style-property' ], defineGetSize );
  866. } else if ( typeof exports === 'object' ) {
  867. // CommonJS for Component
  868. module.exports = defineGetSize( require('get-style-property') );
  869. } else {
  870. // browser global
  871. window.getSize = defineGetSize( window.getStyleProperty );
  872. }
  873. })( window );
  874. /**
  875. * matchesSelector helper v1.0.1
  876. *
  877. * @name matchesSelector
  878. * @param {Element} elem
  879. * @param {String} selector
  880. */
  881. /*jshint browser: true, strict: true, undef: true, unused: true */
  882. /*global define: false */
  883. ( function( global, ElemProto ) {
  884. var matchesMethod = ( function() {
  885. // check un-prefixed
  886. if ( ElemProto.matchesSelector ) {
  887. return 'matchesSelector';
  888. }
  889. // check vendor prefixes
  890. var prefixes = [ 'webkit', 'moz', 'ms', 'o' ];
  891. for ( var i=0, len = prefixes.length; i < len; i++ ) {
  892. var prefix = prefixes[i];
  893. var method = prefix + 'MatchesSelector';
  894. if ( ElemProto[ method ] ) {
  895. return method;
  896. }
  897. }
  898. })();
  899. // ----- match ----- //
  900. function match( elem, selector ) {
  901. return elem[ matchesMethod ]( selector );
  902. }
  903. // ----- appendToFragment ----- //
  904. function checkParent( elem ) {
  905. // not needed if already has parent
  906. if ( elem.parentNode ) {
  907. return;
  908. }
  909. var fragment = document.createDocumentFragment();
  910. fragment.appendChild( elem );
  911. }
  912. // ----- query ----- //
  913. // fall back to using QSA
  914. // thx @jonathantneal https://gist.github.com/3062955
  915. function query( elem, selector ) {
  916. // append to fragment if no parent
  917. checkParent( elem );
  918. // match elem with all selected elems of parent
  919. var elems = elem.parentNode.querySelectorAll( selector );
  920. for ( var i=0, len = elems.length; i < len; i++ ) {
  921. // return true if match
  922. if ( elems[i] === elem ) {
  923. return true;
  924. }
  925. }
  926. // otherwise return false
  927. return false;
  928. }
  929. // ----- matchChild ----- //
  930. function matchChild( elem, selector ) {
  931. checkParent( elem );
  932. return match( elem, selector );
  933. }
  934. // ----- matchesSelector ----- //
  935. var matchesSelector;
  936. if ( matchesMethod ) {
  937. // IE9 supports matchesSelector, but doesn't work on orphaned elems
  938. // check for that
  939. var div = document.createElement('div');
  940. var supportsOrphans = match( div, 'div' );
  941. matchesSelector = supportsOrphans ? match : matchChild;
  942. } else {
  943. matchesSelector = query;
  944. }
  945. // transport
  946. if ( typeof define === 'function' && define.amd ) {
  947. // AMD
  948. define( 'matches-selector/matches-selector',[],function() {
  949. return matchesSelector;
  950. });
  951. } else {
  952. // browser global
  953. window.matchesSelector = matchesSelector;
  954. }
  955. })( this, Element.prototype );
  956. /**
  957. * Outlayer Item
  958. */
  959. ( function( window ) {
  960. // ----- get style ----- //
  961. var getComputedStyle = window.getComputedStyle;
  962. var getStyle = getComputedStyle ?
  963. function( elem ) {
  964. return getComputedStyle( elem, null );
  965. } :
  966. function( elem ) {
  967. return elem.currentStyle;
  968. };
  969. // extend objects
  970. function extend( a, b ) {
  971. for ( var prop in b ) {
  972. a[ prop ] = b[ prop ];
  973. }
  974. return a;
  975. }
  976. function isEmptyObj( obj ) {
  977. for ( var prop in obj ) {
  978. return false;
  979. }
  980. prop = null;
  981. return true;
  982. }
  983. // http://jamesroberts.name/blog/2010/02/22/string-functions-for-javascript-trim-to-camel-case-to-dashed-and-to-underscore/
  984. function toDash( str ) {
  985. return str.replace( /([A-Z])/g, function( $1 ){
  986. return '-' + $1.toLowerCase();
  987. });
  988. }
  989. // -------------------------- Outlayer definition -------------------------- //
  990. function outlayerItemDefinition( EventEmitter, getSize, getStyleProperty ) {
  991. // -------------------------- CSS3 support -------------------------- //
  992. var transitionProperty = getStyleProperty('transition');
  993. var transformProperty = getStyleProperty('transform');
  994. var supportsCSS3 = transitionProperty && transformProperty;
  995. var is3d = !!getStyleProperty('perspective');
  996. var transitionEndEvent = {
  997. WebkitTransition: 'webkitTransitionEnd',
  998. MozTransition: 'transitionend',
  999. OTransition: 'otransitionend',
  1000. transition: 'transitionend'
  1001. }[ transitionProperty ];
  1002. // properties that could have vendor prefix
  1003. var prefixableProperties = [
  1004. 'transform',
  1005. 'transition',
  1006. 'transitionDuration',
  1007. 'transitionProperty'
  1008. ];
  1009. // cache all vendor properties
  1010. var vendorProperties = ( function() {
  1011. var cache = {};
  1012. for ( var i=0, len = prefixableProperties.length; i < len; i++ ) {
  1013. var prop = prefixableProperties[i];
  1014. var supportedProp = getStyleProperty( prop );
  1015. if ( supportedProp && supportedProp !== prop ) {
  1016. cache[ prop ] = supportedProp;
  1017. }
  1018. }
  1019. return cache;
  1020. })();
  1021. // -------------------------- Item -------------------------- //
  1022. function Item( element, layout ) {
  1023. if ( !element ) {
  1024. return;
  1025. }
  1026. this.element = element;
  1027. // parent layout class, i.e. Masonry, Isotope, or Packery
  1028. this.layout = layout;
  1029. this.position = {
  1030. x: 0,
  1031. y: 0
  1032. };
  1033. this._create();
  1034. }
  1035. // inherit EventEmitter
  1036. extend( Item.prototype, EventEmitter.prototype );
  1037. Item.prototype._create = function() {
  1038. // transition objects
  1039. this._transn = {
  1040. ingProperties: {},
  1041. clean: {},
  1042. onEnd: {}
  1043. };
  1044. this.css({
  1045. position: 'absolute'
  1046. });
  1047. };
  1048. // trigger specified handler for event type
  1049. Item.prototype.handleEvent = function( event ) {
  1050. var method = 'on' + event.type;
  1051. if ( this[ method ] ) {
  1052. this[ method ]( event );
  1053. }
  1054. };
  1055. Item.prototype.getSize = function() {
  1056. this.size = getSize( this.element );
  1057. };
  1058. /**
  1059. * apply CSS styles to element
  1060. * @param {Object} style
  1061. */
  1062. Item.prototype.css = function( style ) {
  1063. var elemStyle = this.element.style;
  1064. for ( var prop in style ) {
  1065. // use vendor property if available
  1066. var supportedProp = vendorProperties[ prop ] || prop;
  1067. elemStyle[ supportedProp ] = style[ prop ];
  1068. }
  1069. };
  1070. // measure position, and sets it
  1071. Item.prototype.getPosition = function() {
  1072. var style = getStyle( this.element );
  1073. var layoutOptions = this.layout.options;
  1074. var isOriginLeft = layoutOptions.isOriginLeft;
  1075. var isOriginTop = layoutOptions.isOriginTop;
  1076. var x = parseInt( style[ isOriginLeft ? 'left' : 'right' ], 10 );
  1077. var y = parseInt( style[ isOriginTop ? 'top' : 'bottom' ], 10 );
  1078. // clean up 'auto' or other non-integer values
  1079. x = isNaN( x ) ? 0 : x;
  1080. y = isNaN( y ) ? 0 : y;
  1081. // remove padding from measurement
  1082. var layoutSize = this.layout.size;
  1083. x -= isOriginLeft ? layoutSize.paddingLeft : layoutSize.paddingRight;
  1084. y -= isOriginTop ? layoutSize.paddingTop : layoutSize.paddingBottom;
  1085. this.position.x = x;
  1086. this.position.y = y;
  1087. };
  1088. // set settled position, apply padding
  1089. Item.prototype.layoutPosition = function() {
  1090. var layoutSize = this.layout.size;
  1091. var layoutOptions = this.layout.options;
  1092. var style = {};
  1093. if ( layoutOptions.isOriginLeft ) {
  1094. style.left = ( this.position.x + layoutSize.paddingLeft ) + 'px';
  1095. // reset other property
  1096. style.right = '';
  1097. } else {
  1098. style.right = ( this.position.x + layoutSize.paddingRight ) + 'px';
  1099. style.left = '';
  1100. }
  1101. if ( layoutOptions.isOriginTop ) {
  1102. style.top = ( this.position.y + layoutSize.paddingTop ) + 'px';
  1103. style.bottom = '';
  1104. } else {
  1105. style.bottom = ( this.position.y + layoutSize.paddingBottom ) + 'px';
  1106. style.top = '';
  1107. }
  1108. this.css( style );
  1109. this.emitEvent( 'layout', [ this ] );
  1110. };
  1111. // transform translate function
  1112. var translate = is3d ?
  1113. function( x, y ) {
  1114. return 'translate3d(' + x + 'px, ' + y + 'px, 0)';
  1115. } :
  1116. function( x, y ) {
  1117. return 'translate(' + x + 'px, ' + y + 'px)';
  1118. };
  1119. Item.prototype._transitionTo = function( x, y ) {
  1120. this.getPosition();
  1121. // get current x & y from top/left
  1122. var curX = this.position.x;
  1123. var curY = this.position.y;
  1124. var compareX = parseInt( x, 10 );
  1125. var compareY = parseInt( y, 10 );
  1126. var didNotMove = compareX === this.position.x && compareY === this.position.y;
  1127. // save end position
  1128. this.setPosition( x, y );
  1129. // if did not move and not transitioning, just go to layout
  1130. if ( didNotMove && !this.isTransitioning ) {
  1131. this.layoutPosition();
  1132. return;
  1133. }
  1134. var transX = x - curX;
  1135. var transY = y - curY;
  1136. var transitionStyle = {};
  1137. // flip cooridinates if origin on right or bottom
  1138. var layoutOptions = this.layout.options;
  1139. transX = layoutOptions.isOriginLeft ? transX : -transX;
  1140. transY = layoutOptions.isOriginTop ? transY : -transY;
  1141. transitionStyle.transform = translate( transX, transY );
  1142. this.transition({
  1143. to: transitionStyle,
  1144. onTransitionEnd: {
  1145. transform: this.layoutPosition
  1146. },
  1147. isCleaning: true
  1148. });
  1149. };
  1150. // non transition + transform support
  1151. Item.prototype.goTo = function( x, y ) {
  1152. this.setPosition( x, y );
  1153. this.layoutPosition();
  1154. };
  1155. // use transition and transforms if supported
  1156. Item.prototype.moveTo = supportsCSS3 ?
  1157. Item.prototype._transitionTo : Item.prototype.goTo;
  1158. Item.prototype.setPosition = function( x, y ) {
  1159. this.position.x = parseInt( x, 10 );
  1160. this.position.y = parseInt( y, 10 );
  1161. };
  1162. // ----- transition ----- //
  1163. /**
  1164. * @param {Object} style - CSS
  1165. * @param {Function} onTransitionEnd
  1166. */
  1167. // non transition, just trigger callback
  1168. Item.prototype._nonTransition = function( args ) {
  1169. this.css( args.to );
  1170. if ( args.isCleaning ) {
  1171. this._removeStyles( args.to );
  1172. }
  1173. for ( var prop in args.onTransitionEnd ) {
  1174. args.onTransitionEnd[ prop ].call( this );
  1175. }
  1176. };
  1177. /**
  1178. * proper transition
  1179. * @param {Object} args - arguments
  1180. * @param {Object} to - style to transition to
  1181. * @param {Object} from - style to start transition from
  1182. * @param {Boolean} isCleaning - removes transition styles after transition
  1183. * @param {Function} onTransitionEnd - callback
  1184. */
  1185. Item.prototype._transition = function( args ) {
  1186. // redirect to nonTransition if no transition duration
  1187. if ( !parseFloat( this.layout.options.transitionDuration ) ) {
  1188. this._nonTransition( args );
  1189. return;
  1190. }
  1191. var _transition = this._transn;
  1192. // keep track of onTransitionEnd callback by css property
  1193. for ( var prop in args.onTransitionEnd ) {
  1194. _transition.onEnd[ prop ] = args.onTransitionEnd[ prop ];
  1195. }
  1196. // keep track of properties that are transitioning
  1197. for ( prop in args.to ) {
  1198. _transition.ingProperties[ prop ] = true;
  1199. // keep track of properties to clean up when transition is done
  1200. if ( args.isCleaning ) {
  1201. _transition.clean[ prop ] = true;
  1202. }
  1203. }
  1204. // set from styles
  1205. if ( args.from ) {
  1206. this.css( args.from );
  1207. // force redraw. http://blog.alexmaccaw.com/css-transitions
  1208. var h = this.element.offsetHeight;
  1209. // hack for JSHint to hush about unused var
  1210. h = null;
  1211. }
  1212. // enable transition
  1213. this.enableTransition( args.to );
  1214. // set styles that are transitioning
  1215. this.css( args.to );
  1216. this.isTransitioning = true;
  1217. };
  1218. var itemTransitionProperties = transformProperty && ( toDash( transformProperty ) +
  1219. ',opacity' );
  1220. Item.prototype.enableTransition = function(/* style */) {
  1221. // only enable if not already transitioning
  1222. // bug in IE10 were re-setting transition style will prevent
  1223. // transitionend event from triggering
  1224. if ( this.isTransitioning ) {
  1225. return;
  1226. }
  1227. // make transition: foo, bar, baz from style object
  1228. // TODO uncomment this bit when IE10 bug is resolved
  1229. // var transitionValue = [];
  1230. // for ( var prop in style ) {
  1231. // // dash-ify camelCased properties like WebkitTransition
  1232. // transitionValue.push( toDash( prop ) );
  1233. // }
  1234. // enable transition styles
  1235. // HACK always enable transform,opacity for IE10
  1236. this.css({
  1237. transitionProperty: itemTransitionProperties,
  1238. transitionDuration: this.layout.options.transitionDuration
  1239. });
  1240. // listen for transition end event
  1241. this.element.addEventListener( transitionEndEvent, this, false );
  1242. };
  1243. Item.prototype.transition = Item.prototype[ transitionProperty ? '_transition' : '_nonTransition' ];
  1244. // ----- events ----- //
  1245. Item.prototype.onwebkitTransitionEnd = function( event ) {
  1246. this.ontransitionend( event );
  1247. };
  1248. Item.prototype.onotransitionend = function( event ) {
  1249. this.ontransitionend( event );
  1250. };
  1251. // properties that I munge to make my life easier
  1252. var dashedVendorProperties = {
  1253. '-webkit-transform': 'transform',
  1254. '-moz-transform': 'transform',
  1255. '-o-transform': 'transform'
  1256. };
  1257. Item.prototype.ontransitionend = function( event ) {
  1258. // disregard bubbled events from children
  1259. if ( event.target !== this.element ) {
  1260. return;
  1261. }
  1262. var _transition = this._transn;
  1263. // get property name of transitioned property, convert to prefix-free
  1264. var propertyName = dashedVendorProperties[ event.propertyName ] || event.propertyName;
  1265. // remove property that has completed transitioning
  1266. delete _transition.ingProperties[ propertyName ];
  1267. // check if any properties are still transitioning
  1268. if ( isEmptyObj( _transition.ingProperties ) ) {
  1269. // all properties have completed transitioning
  1270. this.disableTransition();
  1271. }
  1272. // clean style
  1273. if ( propertyName in _transition.clean ) {
  1274. // clean up style
  1275. this.element.style[ event.propertyName ] = '';
  1276. delete _transition.clean[ propertyName ];
  1277. }
  1278. // trigger onTransitionEnd callback
  1279. if ( propertyName in _transition.onEnd ) {
  1280. var onTransitionEnd = _transition.onEnd[ propertyName ];
  1281. onTransitionEnd.call( this );
  1282. delete _transition.onEnd[ propertyName ];
  1283. }
  1284. this.emitEvent( 'transitionEnd', [ this ] );
  1285. };
  1286. Item.prototype.disableTransition = function() {
  1287. this.removeTransitionStyles();
  1288. this.element.removeEventListener( transitionEndEvent, this, false );
  1289. this.isTransitioning = false;
  1290. };
  1291. /**
  1292. * removes style property from element
  1293. * @param {Object} style
  1294. **/
  1295. Item.prototype._removeStyles = function( style ) {
  1296. // clean up transition styles
  1297. var cleanStyle = {};
  1298. for ( var prop in style ) {
  1299. cleanStyle[ prop ] = '';
  1300. }
  1301. this.css( cleanStyle );
  1302. };
  1303. var cleanTransitionStyle = {
  1304. transitionProperty: '',
  1305. transitionDuration: ''
  1306. };
  1307. Item.prototype.removeTransitionStyles = function() {
  1308. // remove transition
  1309. this.css( cleanTransitionStyle );
  1310. };
  1311. // ----- show/hide/remove ----- //
  1312. // remove element from DOM
  1313. Item.prototype.removeElem = function() {
  1314. this.element.parentNode.removeChild( this.element );
  1315. this.emitEvent( 'remove', [ this ] );
  1316. };
  1317. Item.prototype.remove = function() {
  1318. // just remove element if no transition support or no transition
  1319. if ( !transitionProperty || !parseFloat( this.layout.options.transitionDuration ) ) {
  1320. this.removeElem();
  1321. return;
  1322. }
  1323. // start transition
  1324. var _this = this;
  1325. this.on( 'transitionEnd', function() {
  1326. _this.removeElem();
  1327. return true; // bind once
  1328. });
  1329. this.hide();
  1330. };
  1331. Item.prototype.reveal = function() {
  1332. delete this.isHidden;
  1333. // remove display: none
  1334. this.css({ display: '' });
  1335. var options = this.layout.options;
  1336. this.transition({
  1337. from: options.hiddenStyle,
  1338. to: options.visibleStyle,
  1339. isCleaning: true
  1340. });
  1341. };
  1342. Item.prototype.hide = function() {
  1343. // set flag
  1344. this.isHidden = true;
  1345. // remove display: none
  1346. this.css({ display: '' });
  1347. var options = this.layout.options;
  1348. this.transition({
  1349. from: options.visibleStyle,
  1350. to: options.hiddenStyle,
  1351. // keep hidden stuff hidden
  1352. isCleaning: true,
  1353. onTransitionEnd: {
  1354. opacity: function() {
  1355. // check if still hidden
  1356. // during transition, item may have been un-hidden
  1357. if ( this.isHidden ) {
  1358. this.css({ display: 'none' });
  1359. }
  1360. }
  1361. }
  1362. });
  1363. };
  1364. Item.prototype.destroy = function() {
  1365. this.css({
  1366. position: '',
  1367. left: '',
  1368. right: '',
  1369. top: '',
  1370. bottom: '',
  1371. transition: '',
  1372. transform: ''
  1373. });
  1374. };
  1375. return Item;
  1376. }
  1377. // -------------------------- transport -------------------------- //
  1378. if ( typeof define === 'function' && define.amd ) {
  1379. // AMD
  1380. define( 'outlayer/item',[
  1381. 'eventEmitter/EventEmitter',
  1382. 'get-size/get-size',
  1383. 'get-style-property/get-style-property'
  1384. ],
  1385. outlayerItemDefinition );
  1386. } else {
  1387. // browser global
  1388. window.Outlayer = {};
  1389. window.Outlayer.Item = outlayerItemDefinition(
  1390. window.EventEmitter,
  1391. window.getSize,
  1392. window.getStyleProperty
  1393. );
  1394. }
  1395. })( window );
  1396. /*!
  1397. * Outlayer v1.2.0
  1398. * the brains and guts of a layout library
  1399. * MIT license
  1400. */
  1401. ( function( window ) {
  1402. // ----- vars ----- //
  1403. var document = window.document;
  1404. var console = window.console;
  1405. var jQuery = window.jQuery;
  1406. var noop = function() {};
  1407. // -------------------------- helpers -------------------------- //
  1408. // extend objects
  1409. function extend( a, b ) {
  1410. for ( var prop in b ) {
  1411. a[ prop ] = b[ prop ];
  1412. }
  1413. return a;
  1414. }
  1415. var objToString = Object.prototype.toString;
  1416. function isArray( obj ) {
  1417. return objToString.call( obj ) === '[object Array]';
  1418. }
  1419. // turn element or nodeList into an array
  1420. function makeArray( obj ) {
  1421. var ary = [];
  1422. if ( isArray( obj ) ) {
  1423. // use object if already an array
  1424. ary = obj;
  1425. } else if ( obj && typeof obj.length === 'number' ) {
  1426. // convert nodeList to array
  1427. for ( var i=0, len = obj.length; i < len; i++ ) {
  1428. ary.push( obj[i] );
  1429. }
  1430. } else {
  1431. // array of single index
  1432. ary.push( obj );
  1433. }
  1434. return ary;
  1435. }
  1436. // http://stackoverflow.com/a/384380/182183
  1437. var isElement = ( typeof HTMLElement === 'object' ) ?
  1438. function isElementDOM2( obj ) {
  1439. return obj instanceof HTMLElement;
  1440. } :
  1441. function isElementQuirky( obj ) {
  1442. return obj && typeof obj === 'object' &&
  1443. obj.nodeType === 1 && typeof obj.nodeName === 'string';
  1444. };
  1445. // index of helper cause IE8
  1446. var indexOf = Array.prototype.indexOf ? function( ary, obj ) {
  1447. return ary.indexOf( obj );
  1448. } : function( ary, obj ) {
  1449. for ( var i=0, len = ary.length; i < len; i++ ) {
  1450. if ( ary[i] === obj ) {
  1451. return i;
  1452. }
  1453. }
  1454. return -1;
  1455. };
  1456. function removeFrom( obj, ary ) {
  1457. var index = indexOf( ary, obj );
  1458. if ( index !== -1 ) {
  1459. ary.splice( index, 1 );
  1460. }
  1461. }
  1462. // http://jamesroberts.name/blog/2010/02/22/string-functions-for-javascript-trim-to-camel-case-to-dashed-and-to-underscore/
  1463. function toDashed( str ) {
  1464. return str.replace( /(.)([A-Z])/g, function( match, $1, $2 ) {
  1465. return $1 + '-' + $2;
  1466. }).toLowerCase();
  1467. }
  1468. function outlayerDefinition( eventie, docReady, EventEmitter, getSize, matchesSelector, Item ) {
  1469. // -------------------------- Outlayer -------------------------- //
  1470. // globally unique identifiers
  1471. var GUID = 0;
  1472. // internal store of all Outlayer intances
  1473. var instances = {};
  1474. /**
  1475. * @param {Element, String} element
  1476. * @param {Object} options
  1477. * @constructor
  1478. */
  1479. function Outlayer( element, options ) {
  1480. // use element as selector string
  1481. if ( typeof element === 'string' ) {
  1482. element = document.querySelector( element );
  1483. }
  1484. // bail out if not proper element
  1485. if ( !element || !isElement( element ) ) {
  1486. if ( console ) {
  1487. console.error( 'Bad ' + this.constructor.namespace + ' element: ' + element );
  1488. }
  1489. return;
  1490. }
  1491. this.element = element;
  1492. // options
  1493. this.options = extend( {}, this.constructor.defaults );
  1494. this.option( options );
  1495. // add id for Outlayer.getFromElement
  1496. var id = ++GUID;
  1497. this.element.outlayerGUID = id; // expando
  1498. instances[ id ] = this; // associate via id
  1499. // kick it off
  1500. this._create();
  1501. if ( this.options.isInitLayout ) {
  1502. this.layout();
  1503. }
  1504. }
  1505. // settings are for internal use only
  1506. Outlayer.namespace = 'outlayer';
  1507. Outlayer.Item = Item;
  1508. // default options
  1509. Outlayer.defaults = {
  1510. containerStyle: {
  1511. position: 'relative'
  1512. },
  1513. isInitLayout: true,
  1514. isOriginLeft: true,
  1515. isOriginTop: true,
  1516. isResizeBound: true,
  1517. isResizingContainer: true,
  1518. // item options
  1519. transitionDuration: '0.4s',
  1520. hiddenStyle: {
  1521. opacity: 0,
  1522. transform: 'scale(0.001)'
  1523. },
  1524. visibleStyle: {
  1525. opacity: 1,
  1526. transform: 'scale(1)'
  1527. }
  1528. };
  1529. // inherit EventEmitter
  1530. extend( Outlayer.prototype, EventEmitter.prototype );
  1531. /**
  1532. * set options
  1533. * @param {Object} opts
  1534. */
  1535. Outlayer.prototype.option = function( opts ) {
  1536. extend( this.options, opts );
  1537. };
  1538. Outlayer.prototype._create = function() {
  1539. // get items from children
  1540. this.reloadItems();
  1541. // elements that affect layout, but are not laid out
  1542. this.stamps = [];
  1543. this.stamp( this.options.stamp );
  1544. // set container style
  1545. extend( this.element.style, this.options.containerStyle );
  1546. // bind resize method
  1547. if ( this.options.isResizeBound ) {
  1548. this.bindResize();
  1549. }
  1550. };
  1551. // goes through all children again and gets bricks in proper order
  1552. Outlayer.prototype.reloadItems = function() {
  1553. // collection of item elements
  1554. this.items = this._itemize( this.element.children );
  1555. };
  1556. /**
  1557. * turn elements into Outlayer.Items to be used in layout
  1558. * @param {Array or NodeList or HTMLElement} elems
  1559. * @returns {Array} items - collection of new Outlayer Items
  1560. */
  1561. Outlayer.prototype._itemize = function( elems ) {
  1562. var itemElems = this._filterFindItemElements( elems );
  1563. var Item = this.constructor.Item;
  1564. // create new Outlayer Items for collection
  1565. var items = [];
  1566. for ( var i=0, len = itemElems.length; i < len; i++ ) {
  1567. var elem = itemElems[i];
  1568. var item = new Item( elem, this );
  1569. items.push( item );
  1570. }
  1571. return items;
  1572. };
  1573. /**
  1574. * get item elements to be used in layout
  1575. * @param {Array or NodeList or HTMLElement} elems
  1576. * @returns {Array} items - item elements
  1577. */
  1578. Outlayer.prototype._filterFindItemElements = function( elems ) {
  1579. // make array of elems
  1580. elems = makeArray( elems );
  1581. var itemSelector = this.options.itemSelector;
  1582. var itemElems = [];
  1583. for ( var i=0, len = elems.length; i < len; i++ ) {
  1584. var elem = elems[i];
  1585. // check that elem is an actual element
  1586. if ( !isElement( elem ) ) {
  1587. continue;
  1588. }
  1589. // filter & find items if we have an item selector
  1590. if ( itemSelector ) {
  1591. // filter siblings
  1592. if ( matchesSelector( elem, itemSelector ) ) {
  1593. itemElems.push( elem );
  1594. }
  1595. // find children
  1596. var childElems = elem.querySelectorAll( itemSelector );
  1597. // concat childElems to filterFound array
  1598. for ( var j=0, jLen = childElems.length; j < jLen; j++ ) {
  1599. itemElems.push( childElems[j] );
  1600. }
  1601. } else {
  1602. itemElems.push( elem );
  1603. }
  1604. }
  1605. return itemElems;
  1606. };
  1607. /**
  1608. * getter method for getting item elements
  1609. * @returns {Array} elems - collection of item elements
  1610. */
  1611. Outlayer.prototype.getItemElements = function() {
  1612. var elems = [];
  1613. for ( var i=0, len = this.items.length; i < len; i++ ) {
  1614. elems.push( this.items[i].element );
  1615. }
  1616. return elems;
  1617. };
  1618. // ----- init & layout ----- //
  1619. /**
  1620. * lays out all items
  1621. */
  1622. Outlayer.prototype.layout = function() {
  1623. this._resetLayout();
  1624. this._manageStamps();
  1625. // don't animate first layout
  1626. var isInstant = this.options.isLayoutInstant !== undefined ?
  1627. this.options.isLayoutInstant : !this._isLayoutInited;
  1628. this.layoutItems( this.items, isInstant );
  1629. // flag for initalized
  1630. this._isLayoutInited = true;
  1631. };
  1632. // _init is alias for layout
  1633. Outlayer.prototype._init = Outlayer.prototype.layout;
  1634. /**
  1635. * logic before any new layout
  1636. */
  1637. Outlayer.prototype._resetLayout = function() {
  1638. this.getSize();
  1639. };
  1640. Outlayer.prototype.getSize = function() {
  1641. this.size = getSize( this.element );
  1642. };
  1643. /**
  1644. * get measurement from option, for columnWidth, rowHeight, gutter
  1645. * if option is String -> get element from selector string, & get size of element
  1646. * if option is Element -> get size of element
  1647. * else use option as a number
  1648. *
  1649. * @param {String} measurement
  1650. * @param {String} size - width or height
  1651. * @private
  1652. */
  1653. Outlayer.prototype._getMeasurement = function( measurement, size ) {
  1654. var option = this.options[ measurement ];
  1655. var elem;
  1656. if ( !option ) {
  1657. // default to 0
  1658. this[ measurement ] = 0;
  1659. } else {
  1660. // use option as an element
  1661. if ( typeof option === 'string' ) {
  1662. elem = this.element.querySelector( option );
  1663. } else if ( isElement( option ) ) {
  1664. elem = option;
  1665. }
  1666. // use size of element, if element
  1667. this[ measurement ] = elem ? getSize( elem )[ size ] : option;
  1668. }
  1669. };
  1670. /**
  1671. * layout a collection of item elements
  1672. * @api public
  1673. */
  1674. Outlayer.prototype.layoutItems = function( items, isInstant ) {
  1675. items = this._getItemsForLayout( items );
  1676. this._layoutItems( items, isInstant );
  1677. this._postLayout();
  1678. };
  1679. /**
  1680. * get the items to be laid out
  1681. * you may want to skip over some items
  1682. * @param {Array} items
  1683. * @returns {Array} items
  1684. */
  1685. Outlayer.prototype._getItemsForLayout = function( items ) {
  1686. var layoutItems = [];
  1687. for ( var i=0, len = items.length; i < len; i++ ) {
  1688. var item = items[i];
  1689. if ( !item.isIgnored ) {
  1690. layoutItems.push( item );
  1691. }
  1692. }
  1693. return layoutItems;
  1694. };
  1695. /**
  1696. * layout items
  1697. * @param {Array} items
  1698. * @param {Boolean} isInstant
  1699. */
  1700. Outlayer.prototype._layoutItems = function( items, isInstant ) {
  1701. var _this = this;
  1702. function onItemsLayout() {
  1703. _this.emitEvent( 'layoutComplete', [ _this, items ] );
  1704. }
  1705. if ( !items || !items.length ) {
  1706. // no items, emit event with empty array
  1707. onItemsLayout();
  1708. return;
  1709. }
  1710. // emit layoutComplete when done
  1711. this._itemsOn( items, 'layout', onItemsLayout );
  1712. var queue = [];
  1713. for ( var i=0, len = items.length; i < len; i++ ) {
  1714. var item = items[i];
  1715. // get x/y object from method
  1716. var position = this._getItemLayoutPosition( item );
  1717. // enqueue
  1718. position.item = item;
  1719. position.isInstant = isInstant || item.isLayoutInstant;
  1720. queue.push( position );
  1721. }
  1722. this._processLayoutQueue( queue );
  1723. };
  1724. /**
  1725. * get item layout position
  1726. * @param {Outlayer.Item} item
  1727. * @returns {Object} x and y position
  1728. */
  1729. Outlayer.prototype._getItemLayoutPosition = function( /* item */ ) {
  1730. return {
  1731. x: 0,
  1732. y: 0
  1733. };
  1734. };
  1735. /**
  1736. * iterate over array and position each item
  1737. * Reason being - separating this logic prevents 'layout invalidation'
  1738. * thx @paul_irish
  1739. * @param {Array} queue
  1740. */
  1741. Outlayer.prototype._processLayoutQueue = function( queue ) {
  1742. for ( var i=0, len = queue.length; i < len; i++ ) {
  1743. var obj = queue[i];
  1744. this._positionItem( obj.item, obj.x, obj.y, obj.isInstant );
  1745. }
  1746. };
  1747. /**
  1748. * Sets position of item in DOM
  1749. * @param {Outlayer.Item} item
  1750. * @param {Number} x - horizontal position
  1751. * @param {Number} y - vertical position
  1752. * @param {Boolean} isInstant - disables transitions
  1753. */
  1754. Outlayer.prototype._positionItem = function( item, x, y, isInstant ) {
  1755. if ( isInstant ) {
  1756. // if not transition, just set CSS
  1757. item.goTo( x, y );
  1758. } else {
  1759. item.moveTo( x, y );
  1760. }
  1761. };
  1762. /**
  1763. * Any logic you want to do after each layout,
  1764. * i.e. size the container
  1765. */
  1766. Outlayer.prototype._postLayout = function() {
  1767. this.resizeContainer();
  1768. };
  1769. Outlayer.prototype.resizeContainer = function() {
  1770. if ( !this.options.isResizingContainer ) {
  1771. return;
  1772. }
  1773. var size = this._getContainerSize();
  1774. if ( size ) {
  1775. this._setContainerMeasure( size.width, true );
  1776. this._setContainerMeasure( size.height, false );
  1777. }
  1778. };
  1779. /**
  1780. * Sets width or height of container if returned
  1781. * @returns {Object} size
  1782. * @param {Number} width
  1783. * @param {Number} height
  1784. */
  1785. Outlayer.prototype._getContainerSize = noop;
  1786. /**
  1787. * @param {Number} measure - size of width or height
  1788. * @param {Boolean} isWidth
  1789. */
  1790. Outlayer.prototype._setContainerMeasure = function( measure, isWidth ) {
  1791. if ( measure === undefined ) {
  1792. return;
  1793. }
  1794. var elemSize = this.size;
  1795. // add padding and border width if border box
  1796. if ( elemSize.isBorderBox ) {
  1797. measure += isWidth ? elemSize.paddingLeft + elemSize.paddingRight +
  1798. elemSize.borderLeftWidth + elemSize.borderRightWidth :
  1799. elemSize.paddingBottom + elemSize.paddingTop +
  1800. elemSize.borderTopWidth + elemSize.borderBottomWidth;
  1801. }
  1802. measure = Math.max( measure, 0 );
  1803. this.element.style[ isWidth ? 'width' : 'height' ] = measure + 'px';
  1804. };
  1805. /**
  1806. * trigger a callback for a collection of items events
  1807. * @param {Array} items - Outlayer.Items
  1808. * @param {String} eventName
  1809. * @param {Function} callback
  1810. */
  1811. Outlayer.prototype._itemsOn = function( items, eventName, callback ) {
  1812. var doneCount = 0;
  1813. var count = items.length;
  1814. // event callback
  1815. var _this = this;
  1816. function tick() {
  1817. doneCount++;
  1818. if ( doneCount === count ) {
  1819. callback.call( _this );
  1820. }
  1821. return true; // bind once
  1822. }
  1823. // bind callback
  1824. for ( var i=0, len = items.length; i < len; i++ ) {
  1825. var item = items[i];
  1826. item.on( eventName, tick );
  1827. }
  1828. };
  1829. // -------------------------- ignore & stamps -------------------------- //
  1830. /**
  1831. * keep item in collection, but do not lay it out
  1832. * ignored items do not get skipped in layout
  1833. * @param {Element} elem
  1834. */
  1835. Outlayer.prototype.ignore = function( elem ) {
  1836. var item = this.getItem( elem );
  1837. if ( item ) {
  1838. item.isIgnored = true;
  1839. }
  1840. };
  1841. /**
  1842. * return item to layout collection
  1843. * @param {Element} elem
  1844. */
  1845. Outlayer.prototype.unignore = function( elem ) {
  1846. var item = this.getItem( elem );
  1847. if ( item ) {
  1848. delete item.isIgnored;
  1849. }
  1850. };
  1851. /**
  1852. * adds elements to stamps
  1853. * @param {NodeList, Array, Element, or String} elems
  1854. */
  1855. Outlayer.prototype.stamp = function( elems ) {
  1856. elems = this._find( elems );
  1857. if ( !elems ) {
  1858. return;
  1859. }
  1860. this.stamps = this.stamps.concat( elems );
  1861. // ignore
  1862. for ( var i=0, len = elems.length; i < len; i++ ) {
  1863. var elem = elems[i];
  1864. this.ignore( elem );
  1865. }
  1866. };
  1867. /**
  1868. * removes elements to stamps
  1869. * @param {NodeList, Array, or Element} elems
  1870. */
  1871. Outlayer.prototype.unstamp = function( elems ) {
  1872. elems = this._find( elems );
  1873. if ( !elems ){
  1874. return;
  1875. }
  1876. for ( var i=0, len = elems.length; i < len; i++ ) {
  1877. var elem = elems[i];
  1878. // filter out removed stamp elements
  1879. removeFrom( elem, this.stamps );
  1880. this.unignore( elem );
  1881. }
  1882. };
  1883. /**
  1884. * finds child elements
  1885. * @param {NodeList, Array, Element, or String} elems
  1886. * @returns {Array} elems
  1887. */
  1888. Outlayer.prototype._find = function( elems ) {
  1889. if ( !elems ) {
  1890. return;
  1891. }
  1892. // if string, use argument as selector string
  1893. if ( typeof elems === 'string' ) {
  1894. elems = this.element.querySelectorAll( elems );
  1895. }
  1896. elems = makeArray( elems );
  1897. return elems;
  1898. };
  1899. Outlayer.prototype._manageStamps = function() {
  1900. if ( !this.stamps || !this.stamps.length ) {
  1901. return;
  1902. }
  1903. this._getBoundingRect();
  1904. for ( var i=0, len = this.stamps.length; i < len; i++ ) {
  1905. var stamp = this.stamps[i];
  1906. this._manageStamp( stamp );
  1907. }
  1908. };
  1909. // update boundingLeft / Top
  1910. Outlayer.prototype._getBoundingRect = function() {
  1911. // get bounding rect for container element
  1912. var boundingRect = this.element.getBoundingClientRect();
  1913. var size = this.size;
  1914. this._boundingRect = {
  1915. left: boundingRect.left + size.paddingLeft + size.borderLeftWidth,
  1916. top: boundingRect.top + size.paddingTop + size.borderTopWidth,
  1917. right: boundingRect.right - ( size.paddingRight + size.borderRightWidth ),
  1918. bottom: boundingRect.bottom - ( size.paddingBottom + size.borderBottomWidth )
  1919. };
  1920. };
  1921. /**
  1922. * @param {Element} stamp
  1923. **/
  1924. Outlayer.prototype._manageStamp = noop;
  1925. /**
  1926. * get x/y position of element relative to container element
  1927. * @param {Element} elem
  1928. * @returns {Object} offset - has left, top, right, bottom
  1929. */
  1930. Outlayer.prototype._getElementOffset = function( elem ) {
  1931. var boundingRect = elem.getBoundingClientRect();
  1932. var thisRect = this._boundingRect;
  1933. var size = getSize( elem );
  1934. var offset = {
  1935. left: boundingRect.left - thisRect.left - size.marginLeft,
  1936. top: boundingRect.top - thisRect.top - size.marginTop,
  1937. right: thisRect.right - boundingRect.right - size.marginRight,
  1938. bottom: thisRect.bottom - boundingRect.bottom - size.marginBottom
  1939. };
  1940. return offset;
  1941. };
  1942. // -------------------------- resize -------------------------- //
  1943. // enable event handlers for listeners
  1944. // i.e. resize -> onresize
  1945. Outlayer.prototype.handleEvent = function( event ) {
  1946. var method = 'on' + event.type;
  1947. if ( this[ method ] ) {
  1948. this[ method ]( event );
  1949. }
  1950. };
  1951. /**
  1952. * Bind layout to window resizing
  1953. */
  1954. Outlayer.prototype.bindResize = function() {
  1955. // bind just one listener
  1956. if ( this.isResizeBound ) {
  1957. return;
  1958. }
  1959. eventie.bind( window, 'resize', this );
  1960. this.isResizeBound = true;
  1961. };
  1962. /**
  1963. * Unbind layout to window resizing
  1964. */
  1965. Outlayer.prototype.unbindResize = function() {
  1966. if ( this.isResizeBound ) {
  1967. eventie.unbind( window, 'resize', this );
  1968. }
  1969. this.isResizeBound = false;
  1970. };
  1971. // original debounce by John Hann
  1972. // http://unscriptable.com/index.php/2009/03/20/debouncing-javascript-methods/
  1973. // this fires every resize
  1974. Outlayer.prototype.onresize = function() {
  1975. if ( this.resizeTimeout ) {
  1976. clearTimeout( this.resizeTimeout );
  1977. }
  1978. var _this = this;
  1979. function delayed() {
  1980. _this.resize();
  1981. delete _this.resizeTimeout;
  1982. }
  1983. this.resizeTimeout = setTimeout( delayed, 100 );
  1984. };
  1985. // debounced, layout on resize
  1986. Outlayer.prototype.resize = function() {
  1987. // don't trigger if size did not change
  1988. // or if resize was unbound. See #9
  1989. if ( !this.isResizeBound || !this.needsResizeLayout() ) {
  1990. return;
  1991. }
  1992. this.layout();
  1993. };
  1994. /**
  1995. * check if layout is needed post layout
  1996. * @returns Boolean
  1997. */
  1998. Outlayer.prototype.needsResizeLayout = function() {
  1999. var size = getSize( this.element );
  2000. // check that this.size and size are there
  2001. // IE8 triggers resize on body size change, so they might not be
  2002. var hasSizes = this.size && size;
  2003. return hasSizes && size.innerWidth !== this.size.innerWidth;
  2004. };
  2005. // -------------------------- methods -------------------------- //
  2006. /**
  2007. * add items to Outlayer instance
  2008. * @param {Array or NodeList or Element} elems
  2009. * @returns {Array} items - Outlayer.Items
  2010. **/
  2011. Outlayer.prototype.addItems = function( elems ) {
  2012. var items = this._itemize( elems );
  2013. // add items to collection
  2014. if ( items.length ) {
  2015. this.items = this.items.concat( items );
  2016. }
  2017. return items;
  2018. };
  2019. /**
  2020. * Layout newly-appended item elements
  2021. * @param {Array or NodeList or Element} elems
  2022. */
  2023. Outlayer.prototype.appended = function( elems ) {
  2024. var items = this.addItems( elems );
  2025. if ( !items.length ) {
  2026. return;
  2027. }
  2028. // layout and reveal just the new items
  2029. this.layoutItems( items, true );
  2030. this.reveal( items );
  2031. };
  2032. /**
  2033. * Layout prepended elements
  2034. * @param {Array or NodeList or Element} elems
  2035. */
  2036. Outlayer.prototype.prepended = function( elems ) {
  2037. var items = this._itemize( elems );
  2038. if ( !items.length ) {
  2039. return;
  2040. }
  2041. // add items to beginning of collection
  2042. var previousItems = this.items.slice(0);
  2043. this.items = items.concat( previousItems );
  2044. // start new layout
  2045. this._resetLayout();
  2046. this._manageStamps();
  2047. // layout new stuff without transition
  2048. this.layoutItems( items, true );
  2049. this.reveal( items );
  2050. // layout previous items
  2051. this.layoutItems( previousItems );
  2052. };
  2053. /**
  2054. * reveal a collection of items
  2055. * @param {Array of Outlayer.Items} items
  2056. */
  2057. Outlayer.prototype.reveal = function( items ) {
  2058. var len = items && items.length;
  2059. if ( !len ) {
  2060. return;
  2061. }
  2062. for ( var i=0; i < len; i++ ) {
  2063. var item = items[i];
  2064. item.reveal();
  2065. }
  2066. };
  2067. /**
  2068. * hide a collection of items
  2069. * @param {Array of Outlayer.Items} items
  2070. */
  2071. Outlayer.prototype.hide = function( items ) {
  2072. var len = items && items.length;
  2073. if ( !len ) {
  2074. return;
  2075. }
  2076. for ( var i=0; i < len; i++ ) {
  2077. var item = items[i];
  2078. item.hide();
  2079. }
  2080. };
  2081. /**
  2082. * get Outlayer.Item, given an Element
  2083. * @param {Element} elem
  2084. * @param {Function} callback
  2085. * @returns {Outlayer.Item} item
  2086. */
  2087. Outlayer.prototype.getItem = function( elem ) {
  2088. // loop through items to get the one that matches
  2089. for ( var i=0, len = this.items.length; i < len; i++ ) {
  2090. var item = this.items[i];
  2091. if ( item.element === elem ) {
  2092. // return item
  2093. return item;
  2094. }
  2095. }
  2096. };
  2097. /**
  2098. * get collection of Outlayer.Items, given Elements
  2099. * @param {Array} elems
  2100. * @returns {Array} items - Outlayer.Items
  2101. */
  2102. Outlayer.prototype.getItems = function( elems ) {
  2103. if ( !elems || !elems.length ) {
  2104. return;
  2105. }
  2106. var items = [];
  2107. for ( var i=0, len = elems.length; i < len; i++ ) {
  2108. var elem = elems[i];
  2109. var item = this.getItem( elem );
  2110. if ( item ) {
  2111. items.push( item );
  2112. }
  2113. }
  2114. return items;
  2115. };
  2116. /**
  2117. * remove element(s) from instance and DOM
  2118. * @param {Array or NodeList or Element} elems
  2119. */
  2120. Outlayer.prototype.remove = function( elems ) {
  2121. elems = makeArray( elems );
  2122. var removeItems = this.getItems( elems );
  2123. // bail if no items to remove
  2124. if ( !removeItems || !removeItems.length ) {
  2125. return;
  2126. }
  2127. this._itemsOn( removeItems, 'remove', function() {
  2128. this.emitEvent( 'removeComplete', [ this, removeItems ] );
  2129. });
  2130. for ( var i=0, len = removeItems.length; i < len; i++ ) {
  2131. var item = removeItems[i];
  2132. item.remove();
  2133. // remove item from collection
  2134. removeFrom( item, this.items );
  2135. }
  2136. };
  2137. // ----- destroy ----- //
  2138. // remove and disable Outlayer instance
  2139. Outlayer.prototype.destroy = function() {
  2140. // clean up dynamic styles
  2141. var style = this.element.style;
  2142. style.height = '';
  2143. style.position = '';
  2144. style.width = '';
  2145. // destroy items
  2146. for ( var i=0, len = this.items.length; i < len; i++ ) {
  2147. var item = this.items[i];
  2148. item.destroy();
  2149. }
  2150. this.unbindResize();
  2151. delete this.element.outlayerGUID;
  2152. // remove data for jQuery
  2153. if ( jQuery ) {
  2154. jQuery.removeData( this.element, this.constructor.namespace );
  2155. }
  2156. };
  2157. // -------------------------- data -------------------------- //
  2158. /**
  2159. * get Outlayer instance from element
  2160. * @param {Element} elem
  2161. * @returns {Outlayer}
  2162. */
  2163. Outlayer.data = function( elem ) {
  2164. var id = elem && elem.outlayerGUID;
  2165. return id && instances[ id ];
  2166. };
  2167. // -------------------------- create Outlayer class -------------------------- //
  2168. /**
  2169. * create a layout class
  2170. * @param {String} namespace
  2171. */
  2172. Outlayer.create = function( namespace, options ) {
  2173. // sub-class Outlayer
  2174. function Layout() {
  2175. Outlayer.apply( this, arguments );
  2176. }
  2177. // inherit Outlayer prototype, use Object.create if there
  2178. if ( Object.create ) {
  2179. Layout.prototype = Object.create( Outlayer.prototype );
  2180. } else {
  2181. extend( Layout.prototype, Outlayer.prototype );
  2182. }
  2183. // set contructor, used for namespace and Item
  2184. Layout.prototype.constructor = Layout;
  2185. Layout.defaults = extend( {}, Outlayer.defaults );
  2186. // apply new options
  2187. extend( Layout.defaults, options );
  2188. // keep prototype.settings for backwards compatibility (Packery v1.2.0)
  2189. Layout.prototype.settings = {};
  2190. Layout.namespace = namespace;
  2191. Layout.data = Outlayer.data;
  2192. // sub-class Item
  2193. Layout.Item = function LayoutItem() {
  2194. Item.apply( this, arguments );
  2195. };
  2196. Layout.Item.prototype = new Item();
  2197. // -------------------------- declarative -------------------------- //
  2198. /**
  2199. * allow user to initialize Outlayer via .js-namespace class
  2200. * options are parsed from data-namespace-option attribute
  2201. */
  2202. docReady( function() {
  2203. var dashedNamespace = toDashed( namespace );
  2204. var elems = document.querySelectorAll( '.js-' + dashedNamespace );
  2205. var dataAttr = 'data-' + dashedNamespace + '-options';
  2206. for ( var i=0, len = elems.length; i < len; i++ ) {
  2207. var elem = elems[i];
  2208. var attr = elem.getAttribute( dataAttr );
  2209. var options;
  2210. try {
  2211. options = attr && JSON.parse( attr );
  2212. } catch ( error ) {
  2213. // log error, do not initialize
  2214. if ( console ) {
  2215. console.error( 'Error parsing ' + dataAttr + ' on ' +
  2216. elem.nodeName.toLowerCase() + ( elem.id ? '#' + elem.id : '' ) + ': ' +
  2217. error );
  2218. }
  2219. continue;
  2220. }
  2221. // initialize
  2222. var instance = new Layout( elem, options );
  2223. // make available via $().data('layoutname')
  2224. if ( jQuery ) {
  2225. jQuery.data( elem, namespace, instance );
  2226. }
  2227. }
  2228. });
  2229. // -------------------------- jQuery bridge -------------------------- //
  2230. // make into jQuery plugin
  2231. if ( jQuery && jQuery.bridget ) {
  2232. jQuery.bridget( namespace, Layout );
  2233. }
  2234. return Layout;
  2235. };
  2236. // ----- fin ----- //
  2237. // back in global
  2238. Outlayer.Item = Item;
  2239. return Outlayer;
  2240. }
  2241. // -------------------------- transport -------------------------- //
  2242. if ( typeof define === 'function' && define.amd ) {
  2243. // AMD
  2244. define( 'outlayer/outlayer',[
  2245. 'eventie/eventie',
  2246. 'doc-ready/doc-ready',
  2247. 'eventEmitter/EventEmitter',
  2248. 'get-size/get-size',
  2249. 'matches-selector/matches-selector',
  2250. './item'
  2251. ],
  2252. outlayerDefinition );
  2253. } else {
  2254. // browser global
  2255. window.Outlayer = outlayerDefinition(
  2256. window.eventie,
  2257. window.docReady,
  2258. window.EventEmitter,
  2259. window.getSize,
  2260. window.matchesSelector,
  2261. window.Outlayer.Item
  2262. );
  2263. }
  2264. })( window );
  2265. /**
  2266. * Isotope Item
  2267. **/
  2268. ( function( window ) {
  2269. // -------------------------- Item -------------------------- //
  2270. function itemDefinition( Outlayer ) {
  2271. // sub-class Outlayer Item
  2272. function Item() {
  2273. Outlayer.Item.apply( this, arguments );
  2274. }
  2275. Item.prototype = new Outlayer.Item();
  2276. Item.prototype._create = function() {
  2277. // assign id, used for original-order sorting
  2278. this.id = this.layout.itemGUID++;
  2279. Outlayer.Item.prototype._create.call( this );
  2280. this.sortData = {};
  2281. };
  2282. Item.prototype.updateSortData = function() {
  2283. if ( this.isIgnored ) {
  2284. return;
  2285. }
  2286. // default sorters
  2287. this.sortData.id = this.id;
  2288. // for backward compatibility
  2289. this.sortData['original-order'] = this.id;
  2290. this.sortData.random = Math.random();
  2291. // go thru getSortData obj and apply the sorters
  2292. var getSortData = this.layout.options.getSortData;
  2293. var sorters = this.layout._sorters;
  2294. for ( var key in getSortData ) {
  2295. var sorter = sorters[ key ];
  2296. this.sortData[ key ] = sorter( this.element, this );
  2297. }
  2298. };
  2299. var _destroy = Item.prototype.destroy;
  2300. Item.prototype.destroy = function() {
  2301. // call super
  2302. _destroy.apply( this, arguments );
  2303. // reset display, #741
  2304. this.css({
  2305. display: ''
  2306. });
  2307. };
  2308. return Item;
  2309. }
  2310. // -------------------------- transport -------------------------- //
  2311. if ( typeof define === 'function' && define.amd ) {
  2312. // AMD
  2313. define( 'isotope/js/item',[
  2314. 'outlayer/outlayer'
  2315. ],
  2316. itemDefinition );
  2317. } else {
  2318. // browser global
  2319. window.Isotope = window.Isotope || {};
  2320. window.Isotope.Item = itemDefinition(
  2321. window.Outlayer
  2322. );
  2323. }
  2324. })( window );
  2325. ( function( window ) {
  2326. // -------------------------- -------------------------- //
  2327. function layoutModeDefinition( getSize, Outlayer ) {
  2328. // layout mode class
  2329. function LayoutMode( isotope ) {
  2330. this.isotope = isotope;
  2331. // link properties
  2332. if ( isotope ) {
  2333. this.options = isotope.options[ this.namespace ];
  2334. this.element = isotope.element;
  2335. this.items = isotope.filteredItems;
  2336. this.size = isotope.size;
  2337. }
  2338. }
  2339. /**
  2340. * some methods should just defer to default Outlayer method
  2341. * and reference the Isotope instance as `this`
  2342. **/
  2343. ( function() {
  2344. var facadeMethods = [
  2345. '_resetLayout',
  2346. '_getItemLayoutPosition',
  2347. '_manageStamp',
  2348. '_getContainerSize',
  2349. '_getElementOffset',
  2350. 'needsResizeLayout'
  2351. ];
  2352. for ( var i=0, len = facadeMethods.length; i < len; i++ ) {
  2353. var methodName = facadeMethods[i];
  2354. LayoutMode.prototype[ methodName ] = getOutlayerMethod( methodName );
  2355. }
  2356. function getOutlayerMethod( methodName ) {
  2357. return function() {
  2358. return Outlayer.prototype[ methodName ].apply( this.isotope, arguments );
  2359. };
  2360. }
  2361. })();
  2362. // ----- ----- //
  2363. // for horizontal layout modes, check vertical size
  2364. LayoutMode.prototype.needsVerticalResizeLayout = function() {
  2365. // don't trigger if size did not change
  2366. var size = getSize( this.isotope.element );
  2367. // check that this.size and size are there
  2368. // IE8 triggers resize on body size change, so they might not be
  2369. var hasSizes = this.isotope.size && size;
  2370. return hasSizes && size.innerHeight !== this.isotope.size.innerHeight;
  2371. };
  2372. // ----- measurements ----- //
  2373. LayoutMode.prototype._getMeasurement = function() {
  2374. this.isotope._getMeasurement.apply( this, arguments );
  2375. };
  2376. LayoutMode.prototype.getColumnWidth = function() {
  2377. this.getSegmentSize( 'column', 'Width' );
  2378. };
  2379. LayoutMode.prototype.getRowHeight = function() {
  2380. this.getSegmentSize( 'row', 'Height' );
  2381. };
  2382. /**
  2383. * get columnWidth or rowHeight
  2384. * segment: 'column' or 'row'
  2385. * size 'Width' or 'Height'
  2386. **/
  2387. LayoutMode.prototype.getSegmentSize = function( segment, size ) {
  2388. var segmentName = segment + size;
  2389. var outerSize = 'outer' + size;
  2390. // columnWidth / outerWidth // rowHeight / outerHeight
  2391. this._getMeasurement( segmentName, outerSize );
  2392. // got rowHeight or columnWidth, we can chill
  2393. if ( this[ segmentName ] ) {
  2394. return;
  2395. }
  2396. // fall back to item of first element
  2397. var firstItemSize = this.getFirstItemSize();
  2398. this[ segmentName ] = firstItemSize && firstItemSize[ outerSize ] ||
  2399. // or size of container
  2400. this.isotope.size[ 'inner' + size ];
  2401. };
  2402. LayoutMode.prototype.getFirstItemSize = function() {
  2403. var firstItem = this.isotope.filteredItems[0];
  2404. return firstItem && firstItem.element && getSize( firstItem.element );
  2405. };
  2406. // ----- methods that should reference isotope ----- //
  2407. LayoutMode.prototype.layout = function() {
  2408. this.isotope.layout.apply( this.isotope, arguments );
  2409. };
  2410. LayoutMode.prototype.getSize = function() {
  2411. this.isotope.getSize();
  2412. this.size = this.isotope.size;
  2413. };
  2414. // -------------------------- create -------------------------- //
  2415. LayoutMode.modes = {};
  2416. LayoutMode.create = function( namespace, options ) {
  2417. function Mode() {
  2418. LayoutMode.apply( this, arguments );
  2419. }
  2420. Mode.prototype = new LayoutMode();
  2421. // default options
  2422. if ( options ) {
  2423. Mode.options = options;
  2424. }
  2425. Mode.prototype.namespace = namespace;
  2426. // register in Isotope
  2427. LayoutMode.modes[ namespace ] = Mode;
  2428. return Mode;
  2429. };
  2430. return LayoutMode;
  2431. }
  2432. if ( typeof define === 'function' && define.amd ) {
  2433. // AMD
  2434. define( 'isotope/js/layout-mode',[
  2435. 'get-size/get-size',
  2436. 'outlayer/outlayer'
  2437. ],
  2438. layoutModeDefinition );
  2439. } else {
  2440. // browser global
  2441. window.Isotope = window.Isotope || {};
  2442. window.Isotope.LayoutMode = layoutModeDefinition(
  2443. window.getSize,
  2444. window.Outlayer
  2445. );
  2446. }
  2447. })( window );
  2448. /*!
  2449. * Masonry v3.1.5
  2450. * Cascading grid layout library
  2451. * http://masonry.desandro.com
  2452. * MIT License
  2453. * by David DeSandro
  2454. */
  2455. ( function( window ) {
  2456. // -------------------------- helpers -------------------------- //
  2457. var indexOf = Array.prototype.indexOf ?
  2458. function( items, value ) {
  2459. return items.indexOf( value );
  2460. } :
  2461. function ( items, value ) {
  2462. for ( var i=0, len = items.length; i < len; i++ ) {
  2463. var item = items[i];
  2464. if ( item === value ) {
  2465. return i;
  2466. }
  2467. }
  2468. return -1;
  2469. };
  2470. // -------------------------- masonryDefinition -------------------------- //
  2471. // used for AMD definition and requires
  2472. function masonryDefinition( Outlayer, getSize ) {
  2473. // create an Outlayer layout class
  2474. var Masonry = Outlayer.create('masonry');
  2475. Masonry.prototype._resetLayout = function() {
  2476. this.getSize();
  2477. this._getMeasurement( 'columnWidth', 'outerWidth' );
  2478. this._getMeasurement( 'gutter', 'outerWidth' );
  2479. this.measureColumns();
  2480. // reset column Y
  2481. var i = this.cols;
  2482. this.colYs = [];
  2483. while (i--) {
  2484. this.colYs.push( 0 );
  2485. }
  2486. this.maxY = 0;
  2487. };
  2488. Masonry.prototype.measureColumns = function() {
  2489. this.getContainerWidth();
  2490. // if columnWidth is 0, default to outerWidth of first item
  2491. if ( !this.columnWidth ) {
  2492. var firstItem = this.items[0];
  2493. var firstItemElem = firstItem && firstItem.element;
  2494. // columnWidth fall back to item of first element
  2495. this.columnWidth = firstItemElem && getSize( firstItemElem ).outerWidth ||
  2496. // if first elem has no width, default to size of container
  2497. this.containerWidth;
  2498. }
  2499. this.columnWidth += this.gutter;
  2500. this.cols = Math.floor( ( this.containerWidth + this.gutter ) / this.columnWidth );
  2501. this.cols = Math.max( this.cols, 1 );
  2502. };
  2503. Masonry.prototype.getContainerWidth = function() {
  2504. // container is parent if fit width
  2505. var container = this.options.isFitWidth ? this.element.parentNode : this.element;
  2506. // check that this.size and size are there
  2507. // IE8 triggers resize on body size change, so they might not be
  2508. var size = getSize( container );
  2509. this.containerWidth = size && size.innerWidth;
  2510. };
  2511. Masonry.prototype._getItemLayoutPosition = function( item ) {
  2512. item.getSize();
  2513. // how many columns does this brick span
  2514. var remainder = item.size.outerWidth % this.columnWidth;
  2515. var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
  2516. // round if off by 1 pixel, otherwise use ceil
  2517. var colSpan = Math[ mathMethod ]( item.size.outerWidth / this.columnWidth );
  2518. colSpan = Math.min( colSpan, this.cols );
  2519. var colGroup = this._getColGroup( colSpan );
  2520. // get the minimum Y value from the columns
  2521. var minimumY = Math.min.apply( Math, colGroup );
  2522. var shortColIndex = indexOf( colGroup, minimumY );
  2523. // position the brick
  2524. var position = {
  2525. x: this.columnWidth * shortColIndex,
  2526. y: minimumY
  2527. };
  2528. // apply setHeight to necessary columns
  2529. var setHeight = minimumY + item.size.outerHeight;
  2530. var setSpan = this.cols + 1 - colGroup.length;
  2531. for ( var i = 0; i < setSpan; i++ ) {
  2532. this.colYs[ shortColIndex + i ] = setHeight;
  2533. }
  2534. return position;
  2535. };
  2536. /**
  2537. * @param {Number} colSpan - number of columns the element spans
  2538. * @returns {Array} colGroup
  2539. */
  2540. Masonry.prototype._getColGroup = function( colSpan ) {
  2541. if ( colSpan < 2 ) {
  2542. // if brick spans only one column, use all the column Ys
  2543. return this.colYs;
  2544. }
  2545. var colGroup = [];
  2546. // how many different places could this brick fit horizontally
  2547. var groupCount = this.cols + 1 - colSpan;
  2548. // for each group potential horizontal position
  2549. for ( var i = 0; i < groupCount; i++ ) {
  2550. // make an array of colY values for that one group
  2551. var groupColYs = this.colYs.slice( i, i + colSpan );
  2552. // and get the max value of the array
  2553. colGroup[i] = Math.max.apply( Math, groupColYs );
  2554. }
  2555. return colGroup;
  2556. };
  2557. Masonry.prototype._manageStamp = function( stamp ) {
  2558. var stampSize = getSize( stamp );
  2559. var offset = this._getElementOffset( stamp );
  2560. // get the columns that this stamp affects
  2561. var firstX = this.options.isOriginLeft ? offset.left : offset.right;
  2562. var lastX = firstX + stampSize.outerWidth;
  2563. var firstCol = Math.floor( firstX / this.columnWidth );
  2564. firstCol = Math.max( 0, firstCol );
  2565. var lastCol = Math.floor( lastX / this.columnWidth );
  2566. // lastCol should not go over if multiple of columnWidth #425
  2567. lastCol -= lastX % this.columnWidth ? 0 : 1;
  2568. lastCol = Math.min( this.cols - 1, lastCol );
  2569. // set colYs to bottom of the stamp
  2570. var stampMaxY = ( this.options.isOriginTop ? offset.top : offset.bottom ) +
  2571. stampSize.outerHeight;
  2572. for ( var i = firstCol; i <= lastCol; i++ ) {
  2573. this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
  2574. }
  2575. };
  2576. Masonry.prototype._getContainerSize = function() {
  2577. this.maxY = Math.max.apply( Math, this.colYs );
  2578. var size = {
  2579. height: this.maxY
  2580. };
  2581. if ( this.options.isFitWidth ) {
  2582. size.width = this._getContainerFitWidth();
  2583. }
  2584. return size;
  2585. };
  2586. Masonry.prototype._getContainerFitWidth = function() {
  2587. var unusedCols = 0;
  2588. // count unused columns
  2589. var i = this.cols;
  2590. while ( --i ) {
  2591. if ( this.colYs[i] !== 0 ) {
  2592. break;
  2593. }
  2594. unusedCols++;
  2595. }
  2596. // fit container to columns that have been used
  2597. return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
  2598. };
  2599. Masonry.prototype.needsResizeLayout = function() {
  2600. var previousWidth = this.containerWidth;
  2601. this.getContainerWidth();
  2602. return previousWidth !== this.containerWidth;
  2603. };
  2604. return Masonry;
  2605. }
  2606. // -------------------------- transport -------------------------- //
  2607. if ( typeof define === 'function' && define.amd ) {
  2608. // AMD
  2609. define( 'masonry/masonry',[
  2610. 'outlayer/outlayer',
  2611. 'get-size/get-size'
  2612. ],
  2613. masonryDefinition );
  2614. } else {
  2615. // browser global
  2616. window.Masonry = masonryDefinition(
  2617. window.Outlayer,
  2618. window.getSize
  2619. );
  2620. }
  2621. })( window );
  2622. /*!
  2623. * Masonry layout mode
  2624. * sub-classes Masonry
  2625. * http://masonry.desandro.com
  2626. */
  2627. ( function( window ) {
  2628. // -------------------------- helpers -------------------------- //
  2629. // extend objects
  2630. function extend( a, b ) {
  2631. for ( var prop in b ) {
  2632. a[ prop ] = b[ prop ];
  2633. }
  2634. return a;
  2635. }
  2636. // -------------------------- masonryDefinition -------------------------- //
  2637. // used for AMD definition and requires
  2638. function masonryDefinition( LayoutMode, Masonry ) {
  2639. // create an Outlayer layout class
  2640. var MasonryMode = LayoutMode.create('masonry');
  2641. // save on to these methods
  2642. var _getElementOffset = MasonryMode.prototype._getElementOffset;
  2643. var layout = MasonryMode.prototype.layout;
  2644. var _getMeasurement = MasonryMode.prototype._getMeasurement;
  2645. // sub-class Masonry
  2646. extend( MasonryMode.prototype, Masonry.prototype );
  2647. // set back, as it was overwritten by Masonry
  2648. MasonryMode.prototype._getElementOffset = _getElementOffset;
  2649. MasonryMode.prototype.layout = layout;
  2650. MasonryMode.prototype._getMeasurement = _getMeasurement;
  2651. var measureColumns = MasonryMode.prototype.measureColumns;
  2652. MasonryMode.prototype.measureColumns = function() {
  2653. // set items, used if measuring first item
  2654. this.items = this.isotope.filteredItems;
  2655. measureColumns.call( this );
  2656. };
  2657. // HACK copy over isOriginLeft/Top options
  2658. var _manageStamp = MasonryMode.prototype._manageStamp;
  2659. MasonryMode.prototype._manageStamp = function() {
  2660. this.options.isOriginLeft = this.isotope.options.isOriginLeft;
  2661. this.options.isOriginTop = this.isotope.options.isOriginTop;
  2662. _manageStamp.apply( this, arguments );
  2663. };
  2664. return MasonryMode;
  2665. }
  2666. // -------------------------- transport -------------------------- //
  2667. if ( typeof define === 'function' && define.amd ) {
  2668. // AMD
  2669. define( 'isotope/js/layout-modes/masonry',[
  2670. '../layout-mode',
  2671. 'masonry/masonry'
  2672. ],
  2673. masonryDefinition );
  2674. } else {
  2675. // browser global
  2676. masonryDefinition(
  2677. window.Isotope.LayoutMode,
  2678. window.Masonry
  2679. );
  2680. }
  2681. })( window );
  2682. ( function( window ) {
  2683. function fitRowsDefinition( LayoutMode ) {
  2684. var FitRows = LayoutMode.create('fitRows');
  2685. FitRows.prototype._resetLayout = function() {
  2686. this.x = 0;
  2687. this.y = 0;
  2688. this.maxY = 0;
  2689. };
  2690. FitRows.prototype._getItemLayoutPosition = function( item ) {
  2691. item.getSize();
  2692. // if this element cannot fit in the current row
  2693. if ( this.x !== 0 && item.size.outerWidth + this.x > this.isotope.size.innerWidth ) {
  2694. this.x = 0;
  2695. this.y = this.maxY;
  2696. }
  2697. var position = {
  2698. x: this.x,
  2699. y: this.y
  2700. };
  2701. this.maxY = Math.max( this.maxY, this.y + item.size.outerHeight );
  2702. this.x += item.size.outerWidth;
  2703. return position;
  2704. };
  2705. FitRows.prototype._getContainerSize = function() {
  2706. return { height: this.maxY };
  2707. };
  2708. return FitRows;
  2709. }
  2710. if ( typeof define === 'function' && define.amd ) {
  2711. // AMD
  2712. define( 'isotope/js/layout-modes/fit-rows',[
  2713. '../layout-mode'
  2714. ],
  2715. fitRowsDefinition );
  2716. } else {
  2717. // browser global
  2718. fitRowsDefinition(
  2719. window.Isotope.LayoutMode
  2720. );
  2721. }
  2722. })( window );
  2723. ( function( window ) {
  2724. function verticalDefinition( LayoutMode ) {
  2725. var Vertical = LayoutMode.create( 'vertical', {
  2726. horizontalAlignment: 0
  2727. });
  2728. Vertical.prototype._resetLayout = function() {
  2729. this.y = 0;
  2730. };
  2731. Vertical.prototype._getItemLayoutPosition = function( item ) {
  2732. item.getSize();
  2733. var x = ( this.isotope.size.innerWidth - item.size.outerWidth ) *
  2734. this.options.horizontalAlignment;
  2735. var y = this.y;
  2736. this.y += item.size.outerHeight;
  2737. return { x: x, y: y };
  2738. };
  2739. Vertical.prototype._getContainerSize = function() {
  2740. return { height: this.y };
  2741. };
  2742. return Vertical;
  2743. }
  2744. if ( typeof define === 'function' && define.amd ) {
  2745. // AMD
  2746. define( 'isotope/js/layout-modes/vertical',[
  2747. '../layout-mode'
  2748. ],
  2749. verticalDefinition );
  2750. } else {
  2751. // browser global
  2752. verticalDefinition(
  2753. window.Isotope.LayoutMode
  2754. );
  2755. }
  2756. })( window );
  2757. /*!
  2758. * Isotope v2.0.1
  2759. * Filter & sort magical layouts
  2760. * http://isotope.metafizzy.co
  2761. */
  2762. ( function( window ) {
  2763. // -------------------------- vars -------------------------- //
  2764. var jQuery = window.jQuery;
  2765. // -------------------------- helpers -------------------------- //
  2766. // extend objects
  2767. function extend( a, b ) {
  2768. for ( var prop in b ) {
  2769. a[ prop ] = b[ prop ];
  2770. }
  2771. return a;
  2772. }
  2773. var trim = String.prototype.trim ?
  2774. function( str ) {
  2775. return str.trim();
  2776. } :
  2777. function( str ) {
  2778. return str.replace( /^\s+|\s+$/g, '' );
  2779. };
  2780. var docElem = document.documentElement;
  2781. var getText = docElem.textContent ?
  2782. function( elem ) {
  2783. return elem.textContent;
  2784. } :
  2785. function( elem ) {
  2786. return elem.innerText;
  2787. };
  2788. var objToString = Object.prototype.toString;
  2789. function isArray( obj ) {
  2790. return objToString.call( obj ) === '[object Array]';
  2791. }
  2792. // index of helper cause IE8
  2793. var indexOf = Array.prototype.indexOf ? function( ary, obj ) {
  2794. return ary.indexOf( obj );
  2795. } : function( ary, obj ) {
  2796. for ( var i=0, len = ary.length; i < len; i++ ) {
  2797. if ( ary[i] === obj ) {
  2798. return i;
  2799. }
  2800. }
  2801. return -1;
  2802. };
  2803. // turn element or nodeList into an array
  2804. function makeArray( obj ) {
  2805. var ary = [];
  2806. if ( isArray( obj ) ) {
  2807. // use object if already an array
  2808. ary = obj;
  2809. } else if ( obj && typeof obj.length === 'number' ) {
  2810. // convert nodeList to array
  2811. for ( var i=0, len = obj.length; i < len; i++ ) {
  2812. ary.push( obj[i] );
  2813. }
  2814. } else {
  2815. // array of single index
  2816. ary.push( obj );
  2817. }
  2818. return ary;
  2819. }
  2820. function removeFrom( obj, ary ) {
  2821. var index = indexOf( ary, obj );
  2822. if ( index !== -1 ) {
  2823. ary.splice( index, 1 );
  2824. }
  2825. }
  2826. // -------------------------- isotopeDefinition -------------------------- //
  2827. // used for AMD definition and requires
  2828. function isotopeDefinition( Outlayer, getSize, matchesSelector, Item, LayoutMode ) {
  2829. // create an Outlayer layout class
  2830. var Isotope = Outlayer.create( 'isotope', {
  2831. layoutMode: "masonry",
  2832. isJQueryFiltering: true,
  2833. sortAscending: true
  2834. });
  2835. Isotope.Item = Item;
  2836. Isotope.LayoutMode = LayoutMode;
  2837. Isotope.prototype._create = function() {
  2838. this.itemGUID = 0;
  2839. // functions that sort items
  2840. this._sorters = {};
  2841. this._getSorters();
  2842. // call super
  2843. Outlayer.prototype._create.call( this );
  2844. // create layout modes
  2845. this.modes = {};
  2846. // start filteredItems with all items
  2847. this.filteredItems = this.items;
  2848. // keep of track of sortBys
  2849. this.sortHistory = [ 'original-order' ];
  2850. // create from registered layout modes
  2851. for ( var name in LayoutMode.modes ) {
  2852. this._initLayoutMode( name );
  2853. }
  2854. };
  2855. Isotope.prototype.reloadItems = function() {
  2856. // reset item ID counter
  2857. this.itemGUID = 0;
  2858. // call super
  2859. Outlayer.prototype.reloadItems.call( this );
  2860. };
  2861. Isotope.prototype._itemize = function() {
  2862. var items = Outlayer.prototype._itemize.apply( this, arguments );
  2863. // assign ID for original-order
  2864. for ( var i=0, len = items.length; i < len; i++ ) {
  2865. var item = items[i];
  2866. item.id = this.itemGUID++;
  2867. }
  2868. this._updateItemsSortData( items );
  2869. return items;
  2870. };
  2871. // -------------------------- layout -------------------------- //
  2872. Isotope.prototype._initLayoutMode = function( name ) {
  2873. var Mode = LayoutMode.modes[ name ];
  2874. // set mode options
  2875. // HACK extend initial options, back-fill in default options
  2876. var initialOpts = this.options[ name ] || {};
  2877. this.options[ name ] = Mode.options ?
  2878. extend( Mode.options, initialOpts ) : initialOpts;
  2879. // init layout mode instance
  2880. this.modes[ name ] = new Mode( this );
  2881. };
  2882. Isotope.prototype.layout = function() {
  2883. // if first time doing layout, do all magic
  2884. if ( !this._isLayoutInited && this.options.isInitLayout ) {
  2885. this.arrange();
  2886. return;
  2887. }
  2888. this._layout();
  2889. };
  2890. // private method to be used in layout() & magic()
  2891. Isotope.prototype._layout = function() {
  2892. // don't animate first layout
  2893. var isInstant = this._getIsInstant();
  2894. // layout flow
  2895. this._resetLayout();
  2896. this._manageStamps();
  2897. this.layoutItems( this.filteredItems, isInstant );
  2898. // flag for initalized
  2899. this._isLayoutInited = true;
  2900. };
  2901. // filter + sort + layout
  2902. Isotope.prototype.arrange = function( opts ) {
  2903. // set any options pass
  2904. this.option( opts );
  2905. this._getIsInstant();
  2906. // filter, sort, and layout
  2907. this.filteredItems = this._filter( this.items );
  2908. this._sort();
  2909. this._layout();
  2910. };
  2911. // alias to _init for main plugin method
  2912. Isotope.prototype._init = Isotope.prototype.arrange;
  2913. // HACK
  2914. // Don't animate/transition first layout
  2915. // Or don't animate/transition other layouts
  2916. Isotope.prototype._getIsInstant = function() {
  2917. var isInstant = this.options.isLayoutInstant !== undefined ?
  2918. this.options.isLayoutInstant : !this._isLayoutInited;
  2919. this._isInstant = isInstant;
  2920. return isInstant;
  2921. };
  2922. // -------------------------- filter -------------------------- //
  2923. Isotope.prototype._filter = function( items ) {
  2924. var filter = this.options.filter;
  2925. filter = filter || '*';
  2926. var matches = [];
  2927. var hiddenMatched = [];
  2928. var visibleUnmatched = [];
  2929. var test = this._getFilterTest( filter );
  2930. // test each item
  2931. for ( var i=0, len = items.length; i < len; i++ ) {
  2932. var item = items[i];
  2933. if ( item.isIgnored ) {
  2934. continue;
  2935. }
  2936. // add item to either matched or unmatched group
  2937. var isMatched = test( item );
  2938. // item.isFilterMatched = isMatched;
  2939. // add to matches if its a match
  2940. if ( isMatched ) {
  2941. matches.push( item );
  2942. }
  2943. // add to additional group if item needs to be hidden or revealed
  2944. if ( isMatched && item.isHidden ) {
  2945. hiddenMatched.push( item );
  2946. } else if ( !isMatched && !item.isHidden ) {
  2947. visibleUnmatched.push( item );
  2948. }
  2949. }
  2950. var _this = this;
  2951. function hideReveal() {
  2952. _this.reveal( hiddenMatched );
  2953. _this.hide( visibleUnmatched );
  2954. }
  2955. if ( this._isInstant ) {
  2956. this._noTransition( hideReveal );
  2957. } else {
  2958. hideReveal();
  2959. }
  2960. return matches;
  2961. };
  2962. // get a jQuery, function, or a matchesSelector test given the filter
  2963. Isotope.prototype._getFilterTest = function( filter ) {
  2964. if ( jQuery && this.options.isJQueryFiltering ) {
  2965. // use jQuery
  2966. return function( item ) {
  2967. return jQuery( item.element ).is( filter );
  2968. };
  2969. }
  2970. if ( typeof filter === 'function' ) {
  2971. // use filter as function
  2972. return function( item ) {
  2973. return filter( item.element );
  2974. };
  2975. }
  2976. // default, use filter as selector string
  2977. return function( item ) {
  2978. return matchesSelector( item.element, filter );
  2979. };
  2980. };
  2981. // -------------------------- sorting -------------------------- //
  2982. /**
  2983. * @params {Array} elems
  2984. * @public
  2985. */
  2986. Isotope.prototype.updateSortData = function( elems ) {
  2987. this._getSorters();
  2988. // update item sort data
  2989. // default to all items if none are passed in
  2990. elems = makeArray( elems );
  2991. var items = this.getItems( elems );
  2992. // if no items found, update all items
  2993. items = items.length ? items : this.items;
  2994. this._updateItemsSortData( items );
  2995. };
  2996. Isotope.prototype._getSorters = function() {
  2997. var getSortData = this.options.getSortData;
  2998. for ( var key in getSortData ) {
  2999. var sorter = getSortData[ key ];
  3000. this._sorters[ key ] = mungeSorter( sorter );
  3001. }
  3002. };
  3003. /**
  3004. * @params {Array} items - of Isotope.Items
  3005. * @private
  3006. */
  3007. Isotope.prototype._updateItemsSortData = function( items ) {
  3008. for ( var i=0, len = items.length; i < len; i++ ) {
  3009. var item = items[i];
  3010. item.updateSortData();
  3011. }
  3012. };
  3013. // ----- munge sorter ----- //
  3014. // encapsulate this, as we just need mungeSorter
  3015. // other functions in here are just for munging
  3016. var mungeSorter = ( function() {
  3017. // add a magic layer to sorters for convienent shorthands
  3018. // `.foo-bar` will use the text of .foo-bar querySelector
  3019. // `[foo-bar]` will use attribute
  3020. // you can also add parser
  3021. // `.foo-bar parseInt` will parse that as a number
  3022. function mungeSorter( sorter ) {
  3023. // if not a string, return function or whatever it is
  3024. if ( typeof sorter !== 'string' ) {
  3025. return sorter;
  3026. }
  3027. // parse the sorter string
  3028. var args = trim( sorter ).split(' ');
  3029. var query = args[0];
  3030. // check if query looks like [an-attribute]
  3031. var attrMatch = query.match( /^\[(.+)\]$/ );
  3032. var attr = attrMatch && attrMatch[1];
  3033. var getValue = getValueGetter( attr, query );
  3034. // use second argument as a parser
  3035. var parser = Isotope.sortDataParsers[ args[1] ];
  3036. // parse the value, if there was a parser
  3037. sorter = parser ? function( elem ) {
  3038. return elem && parser( getValue( elem ) );
  3039. } :
  3040. // otherwise just return value
  3041. function( elem ) {
  3042. return elem && getValue( elem );
  3043. };
  3044. return sorter;
  3045. }
  3046. // get an attribute getter, or get text of the querySelector
  3047. function getValueGetter( attr, query ) {
  3048. var getValue;
  3049. // if query looks like [foo-bar], get attribute
  3050. if ( attr ) {
  3051. getValue = function( elem ) {
  3052. return elem.getAttribute( attr );
  3053. };
  3054. } else {
  3055. // otherwise, assume its a querySelector, and get its text
  3056. getValue = function( elem ) {
  3057. var child = elem.querySelector( query );
  3058. return child && getText( child );
  3059. };
  3060. }
  3061. return getValue;
  3062. }
  3063. return mungeSorter;
  3064. })();
  3065. // parsers used in getSortData shortcut strings
  3066. Isotope.sortDataParsers = {
  3067. 'parseInt': function( val ) {
  3068. return parseInt( val, 10 );
  3069. },
  3070. 'parseFloat': function( val ) {
  3071. return parseFloat( val );
  3072. }
  3073. };
  3074. // ----- sort method ----- //
  3075. // sort filteredItem order
  3076. Isotope.prototype._sort = function() {
  3077. var sortByOpt = this.options.sortBy;
  3078. if ( !sortByOpt ) {
  3079. return;
  3080. }
  3081. // concat all sortBy and sortHistory
  3082. var sortBys = [].concat.apply( sortByOpt, this.sortHistory );
  3083. // sort magic
  3084. var itemSorter = getItemSorter( sortBys, this.options.sortAscending );
  3085. this.filteredItems.sort( itemSorter );
  3086. // keep track of sortBy History
  3087. if ( sortByOpt !== this.sortHistory[0] ) {
  3088. // add to front, oldest goes in last
  3089. this.sortHistory.unshift( sortByOpt );
  3090. }
  3091. };
  3092. // returns a function used for sorting
  3093. function getItemSorter( sortBys, sortAsc ) {
  3094. return function sorter( itemA, itemB ) {
  3095. // cycle through all sortKeys
  3096. for ( var i = 0, len = sortBys.length; i < len; i++ ) {
  3097. var sortBy = sortBys[i];
  3098. var a = itemA.sortData[ sortBy ];
  3099. var b = itemB.sortData[ sortBy ];
  3100. if ( a > b || a < b ) {
  3101. // if sortAsc is an object, use the value given the sortBy key
  3102. var isAscending = sortAsc[ sortBy ] !== undefined ? sortAsc[ sortBy ] : sortAsc;
  3103. var direction = isAscending ? 1 : -1;
  3104. return ( a > b ? 1 : -1 ) * direction;
  3105. }
  3106. }
  3107. return 0;
  3108. };
  3109. }
  3110. // -------------------------- methods -------------------------- //
  3111. // get layout mode
  3112. Isotope.prototype._mode = function() {
  3113. var layoutMode = this.options.layoutMode;
  3114. var mode = this.modes[ layoutMode ];
  3115. if ( !mode ) {
  3116. // TODO console.error
  3117. throw new Error( 'No layout mode: ' + layoutMode );
  3118. }
  3119. // HACK sync mode's options
  3120. // any options set after init for layout mode need to be synced
  3121. mode.options = this.options[ layoutMode ];
  3122. return mode;
  3123. };
  3124. Isotope.prototype._resetLayout = function() {
  3125. // trigger original reset layout
  3126. Outlayer.prototype._resetLayout.call( this );
  3127. this._mode()._resetLayout();
  3128. };
  3129. Isotope.prototype._getItemLayoutPosition = function( item ) {
  3130. return this._mode()._getItemLayoutPosition( item );
  3131. };
  3132. Isotope.prototype._manageStamp = function( stamp ) {
  3133. this._mode()._manageStamp( stamp );
  3134. };
  3135. Isotope.prototype._getContainerSize = function() {
  3136. return this._mode()._getContainerSize();
  3137. };
  3138. Isotope.prototype.needsResizeLayout = function() {
  3139. return this._mode().needsResizeLayout();
  3140. };
  3141. // -------------------------- adding & removing -------------------------- //
  3142. // HEADS UP overwrites default Outlayer appended
  3143. Isotope.prototype.appended = function( elems ) {
  3144. var items = this.addItems( elems );
  3145. if ( !items.length ) {
  3146. return;
  3147. }
  3148. var filteredItems = this._filterRevealAdded( items );
  3149. // add to filteredItems
  3150. this.filteredItems = this.filteredItems.concat( filteredItems );
  3151. };
  3152. // HEADS UP overwrites default Outlayer prepended
  3153. Isotope.prototype.prepended = function( elems ) {
  3154. var items = this._itemize( elems );
  3155. if ( !items.length ) {
  3156. return;
  3157. }
  3158. // add items to beginning of collection
  3159. var previousItems = this.items.slice(0);
  3160. this.items = items.concat( previousItems );
  3161. // start new layout
  3162. this._resetLayout();
  3163. this._manageStamps();
  3164. // layout new stuff without transition
  3165. var filteredItems = this._filterRevealAdded( items );
  3166. // layout previous items
  3167. this.layoutItems( previousItems );
  3168. // add to filteredItems
  3169. this.filteredItems = filteredItems.concat( this.filteredItems );
  3170. };
  3171. Isotope.prototype._filterRevealAdded = function( items ) {
  3172. var filteredItems = this._noTransition( function() {
  3173. return this._filter( items );
  3174. });
  3175. // layout and reveal just the new items
  3176. this.layoutItems( filteredItems, true );
  3177. this.reveal( filteredItems );
  3178. return items;
  3179. };
  3180. /**
  3181. * Filter, sort, and layout newly-appended item elements
  3182. * @param {Array or NodeList or Element} elems
  3183. */
  3184. Isotope.prototype.insert = function( elems ) {
  3185. var items = this.addItems( elems );
  3186. if ( !items.length ) {
  3187. return;
  3188. }
  3189. // append item elements
  3190. var i, item;
  3191. var len = items.length;
  3192. for ( i=0; i < len; i++ ) {
  3193. item = items[i];
  3194. this.element.appendChild( item.element );
  3195. }
  3196. // filter new stuff
  3197. /*
  3198. // this way adds hides new filtered items with NO transition
  3199. // so user can't see if new hidden items have been inserted
  3200. var filteredInsertItems;
  3201. this._noTransition( function() {
  3202. filteredInsertItems = this._filter( items );
  3203. // hide all new items
  3204. this.hide( filteredInsertItems );
  3205. });
  3206. // */
  3207. // this way hides new filtered items with transition
  3208. // so user at least sees that something has been added
  3209. var filteredInsertItems = this._filter( items );
  3210. // hide all newitems
  3211. this._noTransition( function() {
  3212. this.hide( filteredInsertItems );
  3213. });
  3214. // */
  3215. // set flag
  3216. for ( i=0; i < len; i++ ) {
  3217. items[i].isLayoutInstant = true;
  3218. }
  3219. this.arrange();
  3220. // reset flag
  3221. for ( i=0; i < len; i++ ) {
  3222. delete items[i].isLayoutInstant;
  3223. }
  3224. this.reveal( filteredInsertItems );
  3225. };
  3226. var _remove = Isotope.prototype.remove;
  3227. Isotope.prototype.remove = function( elems ) {
  3228. elems = makeArray( elems );
  3229. var removeItems = this.getItems( elems );
  3230. // do regular thing
  3231. _remove.call( this, elems );
  3232. // bail if no items to remove
  3233. if ( !removeItems || !removeItems.length ) {
  3234. return;
  3235. }
  3236. // remove elems from filteredItems
  3237. for ( var i=0, len = removeItems.length; i < len; i++ ) {
  3238. var item = removeItems[i];
  3239. // remove item from collection
  3240. removeFrom( item, this.filteredItems );
  3241. }
  3242. };
  3243. Isotope.prototype.shuffle = function() {
  3244. // update random sortData
  3245. for ( var i=0, len = this.items.length; i < len; i++ ) {
  3246. var item = this.items[i];
  3247. item.sortData.random = Math.random();
  3248. }
  3249. this.options.sortBy = 'random';
  3250. this._sort();
  3251. this._layout();
  3252. };
  3253. /**
  3254. * trigger fn without transition
  3255. * kind of hacky to have this in the first place
  3256. * @param {Function} fn
  3257. * @returns ret
  3258. * @private
  3259. */
  3260. Isotope.prototype._noTransition = function( fn ) {
  3261. // save transitionDuration before disabling
  3262. var transitionDuration = this.options.transitionDuration;
  3263. // disable transition
  3264. this.options.transitionDuration = 0;
  3265. // do it
  3266. var returnValue = fn.call( this );
  3267. // re-enable transition for reveal
  3268. this.options.transitionDuration = transitionDuration;
  3269. return returnValue;
  3270. };
  3271. // ----- helper methods ----- //
  3272. /**
  3273. * getter method for getting filtered item elements
  3274. * @returns {Array} elems - collection of item elements
  3275. */
  3276. Isotope.prototype.getFilteredItemElements = function() {
  3277. var elems = [];
  3278. for ( var i=0, len = this.filteredItems.length; i < len; i++ ) {
  3279. elems.push( this.filteredItems[i].element );
  3280. }
  3281. return elems;
  3282. };
  3283. // ----- ----- //
  3284. return Isotope;
  3285. }
  3286. // -------------------------- transport -------------------------- //
  3287. if ( typeof define === 'function' && define.amd ) {
  3288. // AMD
  3289. define( [
  3290. 'outlayer/outlayer',
  3291. 'get-size/get-size',
  3292. 'matches-selector/matches-selector',
  3293. 'isotope/js/item',
  3294. 'isotope/js/layout-mode',
  3295. // include default layout modes
  3296. 'isotope/js/layout-modes/masonry',
  3297. 'isotope/js/layout-modes/fit-rows',
  3298. 'isotope/js/layout-modes/vertical'
  3299. ],
  3300. isotopeDefinition );
  3301. } else {
  3302. // browser global
  3303. window.Isotope = isotopeDefinition(
  3304. window.Outlayer,
  3305. window.getSize,
  3306. window.matchesSelector,
  3307. window.Isotope.Item,
  3308. window.Isotope.LayoutMode
  3309. );
  3310. }
  3311. })( window );