123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366336733683369337033713372337333743375337633773378337933803381338233833384338533863387338833893390339133923393339433953396339733983399340034013402340334043405340634073408340934103411341234133414341534163417341834193420342134223423342434253426342734283429343034313432343334343435343634373438343934403441344234433444344534463447344834493450345134523453345434553456345734583459346034613462346334643465346634673468346934703471347234733474347534763477347834793480348134823483348434853486348734883489349034913492349334943495349634973498349935003501350235033504350535063507350835093510351135123513351435153516351735183519352035213522352335243525352635273528352935303531353235333534353535363537353835393540354135423543354435453546354735483549355035513552355335543555355635573558355935603561356235633564356535663567356835693570357135723573357435753576357735783579358035813582358335843585358635873588358935903591359235933594359535963597359835993600360136023603360436053606360736083609361036113612361336143615361636173618361936203621362236233624362536263627362836293630363136323633363436353636363736383639364036413642364336443645364636473648364936503651365236533654365536563657365836593660366136623663366436653666366736683669367036713672367336743675367636773678367936803681368236833684368536863687368836893690369136923693369436953696369736983699370037013702370337043705370637073708370937103711371237133714371537163717371837193720372137223723372437253726372737283729373037313732373337343735373637373738373937403741374237433744374537463747374837493750375137523753375437553756375737583759376037613762376337643765376637673768376937703771377237733774377537763777377837793780378137823783378437853786378737883789379037913792379337943795379637973798379938003801380238033804380538063807380838093810381138123813381438153816381738183819382038213822382338243825382638273828382938303831383238333834383538363837383838393840384138423843384438453846384738483849385038513852385338543855385638573858385938603861386238633864386538663867386838693870387138723873387438753876387738783879388038813882388338843885388638873888388938903891389238933894389538963897389838993900390139023903390439053906390739083909391039113912391339143915391639173918391939203921392239233924392539263927392839293930393139323933393439353936393739383939394039413942394339443945394639473948394939503951395239533954395539563957395839593960396139623963396439653966396739683969397039713972397339743975397639773978397939803981398239833984398539863987398839893990399139923993399439953996399739983999400040014002400340044005400640074008400940104011401240134014401540164017401840194020402140224023402440254026402740284029403040314032403340344035403640374038403940404041404240434044404540464047404840494050405140524053405440554056405740584059406040614062406340644065406640674068406940704071407240734074407540764077407840794080408140824083408440854086408740884089409040914092409340944095409640974098409941004101410241034104410541064107410841094110411141124113411441154116411741184119412041214122412341244125412641274128412941304131413241334134413541364137413841394140414141424143414441454146414741484149415041514152415341544155415641574158415941604161416241634164416541664167416841694170417141724173417441754176417741784179418041814182418341844185418641874188418941904191419241934194419541964197419841994200420142024203420442054206420742084209421042114212421342144215421642174218421942204221422242234224422542264227422842294230423142324233423442354236423742384239424042414242424342444245424642474248424942504251425242534254425542564257425842594260426142624263426442654266426742684269427042714272427342744275427642774278427942804281428242834284428542864287428842894290429142924293429442954296429742984299430043014302430343044305430643074308430943104311431243134314431543164317431843194320432143224323432443254326432743284329433043314332433343344335433643374338433943404341434243434344434543464347434843494350435143524353435443554356435743584359436043614362436343644365436643674368436943704371437243734374437543764377437843794380438143824383438443854386438743884389439043914392439343944395439643974398439944004401440244034404440544064407440844094410441144124413441444154416441744184419442044214422442344244425442644274428442944304431443244334434443544364437443844394440444144424443444444454446444744484449445044514452445344544455445644574458445944604461446244634464446544664467446844694470447144724473447444754476447744784479448044814482448344844485448644874488448944904491449244934494449544964497449844994500450145024503450445054506450745084509451045114512451345144515451645174518451945204521452245234524452545264527452845294530453145324533453445354536453745384539454045414542454345444545454645474548454945504551455245534554455545564557455845594560456145624563456445654566456745684569457045714572457345744575457645774578457945804581458245834584458545864587458845894590459145924593459445954596459745984599460046014602460346044605460646074608460946104611461246134614461546164617461846194620462146224623462446254626462746284629463046314632463346344635463646374638463946404641464246434644464546464647464846494650465146524653465446554656465746584659466046614662466346644665466646674668466946704671467246734674467546764677467846794680468146824683468446854686468746884689469046914692469346944695469646974698469947004701470247034704470547064707470847094710471147124713471447154716471747184719472047214722472347244725472647274728472947304731473247334734473547364737473847394740474147424743474447454746474747484749475047514752475347544755475647574758475947604761476247634764476547664767476847694770477147724773477447754776477747784779478047814782478347844785478647874788478947904791479247934794479547964797479847994800480148024803480448054806480748084809481048114812481348144815481648174818481948204821482248234824482548264827482848294830483148324833483448354836483748384839484048414842484348444845484648474848484948504851485248534854485548564857485848594860486148624863486448654866486748684869487048714872487348744875487648774878487948804881488248834884488548864887488848894890489148924893489448954896489748984899490049014902490349044905490649074908490949104911491249134914491549164917491849194920492149224923492449254926492749284929493049314932493349344935493649374938493949404941494249434944494549464947494849494950495149524953495449554956495749584959496049614962496349644965496649674968496949704971497249734974497549764977497849794980498149824983498449854986498749884989499049914992499349944995499649974998499950005001500250035004500550065007500850095010501150125013501450155016501750185019502050215022502350245025502650275028502950305031503250335034503550365037503850395040504150425043504450455046504750485049505050515052505350545055505650575058505950605061506250635064506550665067506850695070507150725073507450755076507750785079508050815082508350845085508650875088508950905091509250935094509550965097509850995100510151025103510451055106510751085109511051115112511351145115511651175118511951205121512251235124512551265127512851295130513151325133513451355136513751385139514051415142514351445145514651475148514951505151515251535154515551565157515851595160516151625163516451655166516751685169517051715172517351745175517651775178517951805181518251835184518551865187518851895190519151925193519451955196519751985199520052015202520352045205520652075208520952105211521252135214521552165217521852195220522152225223522452255226522752285229523052315232523352345235523652375238523952405241524252435244524552465247524852495250525152525253525452555256525752585259526052615262526352645265526652675268526952705271527252735274527552765277527852795280528152825283528452855286528752885289529052915292529352945295529652975298529953005301530253035304530553065307530853095310531153125313531453155316531753185319532053215322532353245325532653275328532953305331533253335334533553365337533853395340534153425343534453455346534753485349535053515352535353545355535653575358535953605361536253635364536553665367536853695370537153725373537453755376537753785379538053815382538353845385538653875388538953905391539253935394539553965397539853995400540154025403540454055406540754085409541054115412541354145415541654175418541954205421542254235424542554265427542854295430543154325433543454355436543754385439544054415442544354445445544654475448544954505451545254535454545554565457545854595460546154625463546454655466546754685469547054715472547354745475547654775478547954805481548254835484548554865487548854895490549154925493549454955496549754985499550055015502550355045505550655075508550955105511551255135514551555165517551855195520552155225523552455255526552755285529553055315532553355345535553655375538553955405541554255435544554555465547554855495550555155525553555455555556555755585559556055615562556355645565556655675568556955705571557255735574557555765577557855795580558155825583558455855586558755885589559055915592559355945595559655975598559956005601560256035604560556065607560856095610561156125613561456155616561756185619562056215622562356245625562656275628562956305631563256335634563556365637563856395640564156425643564456455646564756485649565056515652565356545655565656575658565956605661566256635664566556665667566856695670567156725673567456755676567756785679568056815682568356845685568656875688568956905691569256935694569556965697569856995700570157025703570457055706570757085709571057115712571357145715571657175718571957205721572257235724572557265727572857295730573157325733573457355736573757385739574057415742574357445745574657475748574957505751575257535754575557565757575857595760576157625763576457655766576757685769577057715772577357745775577657775778577957805781578257835784578557865787578857895790579157925793579457955796579757985799580058015802580358045805580658075808580958105811581258135814581558165817581858195820582158225823582458255826582758285829583058315832583358345835583658375838583958405841584258435844584558465847584858495850585158525853585458555856585758585859586058615862586358645865586658675868586958705871587258735874587558765877587858795880588158825883588458855886588758885889589058915892589358945895589658975898589959005901590259035904590559065907590859095910591159125913591459155916591759185919592059215922592359245925592659275928592959305931593259335934593559365937593859395940594159425943594459455946594759485949595059515952595359545955595659575958595959605961596259635964596559665967596859695970597159725973597459755976597759785979598059815982598359845985598659875988598959905991599259935994599559965997599859996000600160026003600460056006600760086009601060116012601360146015601660176018601960206021602260236024602560266027602860296030603160326033603460356036603760386039604060416042604360446045604660476048604960506051605260536054605560566057605860596060606160626063606460656066606760686069607060716072607360746075607660776078607960806081608260836084608560866087608860896090609160926093609460956096609760986099610061016102610361046105610661076108610961106111611261136114611561166117611861196120612161226123612461256126612761286129613061316132613361346135613661376138613961406141614261436144614561466147614861496150615161526153615461556156615761586159616061616162616361646165616661676168616961706171617261736174617561766177617861796180618161826183618461856186618761886189619061916192619361946195619661976198619962006201620262036204620562066207620862096210621162126213621462156216621762186219622062216222622362246225622662276228622962306231623262336234623562366237623862396240624162426243624462456246624762486249625062516252625362546255625662576258625962606261626262636264626562666267626862696270627162726273627462756276627762786279628062816282628362846285628662876288628962906291629262936294629562966297629862996300630163026303630463056306630763086309631063116312631363146315631663176318631963206321632263236324632563266327632863296330633163326333633463356336633763386339634063416342634363446345634663476348634963506351635263536354635563566357635863596360636163626363636463656366636763686369637063716372637363746375637663776378637963806381638263836384638563866387638863896390639163926393639463956396639763986399640064016402640364046405640664076408640964106411641264136414641564166417641864196420642164226423642464256426642764286429643064316432643364346435643664376438643964406441644264436444644564466447644864496450645164526453645464556456645764586459646064616462646364646465646664676468646964706471647264736474647564766477647864796480648164826483648464856486648764886489649064916492649364946495649664976498649965006501650265036504650565066507650865096510651165126513651465156516651765186519652065216522652365246525652665276528652965306531653265336534653565366537653865396540654165426543654465456546654765486549655065516552655365546555655665576558655965606561656265636564656565666567656865696570657165726573657465756576657765786579658065816582658365846585658665876588658965906591659265936594659565966597659865996600660166026603660466056606660766086609661066116612661366146615661666176618661966206621662266236624662566266627662866296630663166326633663466356636663766386639664066416642664366446645664666476648664966506651665266536654665566566657665866596660666166626663666466656666666766686669667066716672667366746675667666776678667966806681668266836684668566866687668866896690669166926693669466956696669766986699670067016702670367046705670667076708670967106711671267136714671567166717671867196720672167226723672467256726672767286729673067316732673367346735673667376738673967406741674267436744674567466747674867496750675167526753675467556756675767586759676067616762676367646765676667676768676967706771677267736774677567766777677867796780678167826783678467856786678767886789679067916792679367946795679667976798679968006801680268036804680568066807680868096810681168126813681468156816681768186819682068216822682368246825682668276828682968306831683268336834683568366837683868396840684168426843684468456846684768486849685068516852685368546855685668576858685968606861686268636864686568666867686868696870687168726873687468756876687768786879688068816882688368846885688668876888688968906891689268936894689568966897689868996900690169026903690469056906690769086909691069116912691369146915691669176918691969206921692269236924692569266927692869296930693169326933693469356936693769386939694069416942694369446945694669476948694969506951695269536954695569566957695869596960696169626963696469656966696769686969697069716972697369746975697669776978697969806981698269836984698569866987698869896990699169926993699469956996699769986999700070017002700370047005700670077008700970107011701270137014701570167017701870197020702170227023702470257026702770287029703070317032703370347035703670377038703970407041704270437044704570467047704870497050705170527053705470557056705770587059706070617062706370647065706670677068706970707071707270737074707570767077707870797080708170827083708470857086708770887089709070917092709370947095709670977098709971007101710271037104710571067107710871097110711171127113711471157116711771187119712071217122712371247125712671277128712971307131713271337134713571367137713871397140714171427143714471457146714771487149715071517152715371547155715671577158715971607161716271637164716571667167716871697170717171727173717471757176717771787179718071817182718371847185718671877188718971907191719271937194719571967197719871997200720172027203720472057206720772087209721072117212721372147215721672177218721972207221722272237224722572267227722872297230723172327233723472357236723772387239724072417242724372447245724672477248724972507251725272537254725572567257725872597260726172627263726472657266726772687269727072717272727372747275727672777278727972807281728272837284728572867287728872897290729172927293729472957296729772987299730073017302730373047305730673077308730973107311731273137314731573167317731873197320732173227323732473257326732773287329733073317332733373347335733673377338733973407341734273437344734573467347734873497350735173527353735473557356735773587359736073617362736373647365736673677368736973707371737273737374737573767377737873797380738173827383738473857386738773887389739073917392739373947395739673977398739974007401740274037404740574067407740874097410741174127413741474157416741774187419742074217422742374247425742674277428742974307431743274337434743574367437743874397440744174427443744474457446744774487449745074517452745374547455745674577458745974607461746274637464746574667467746874697470747174727473747474757476747774787479748074817482748374847485748674877488748974907491749274937494749574967497749874997500750175027503750475057506750775087509751075117512751375147515751675177518751975207521752275237524752575267527752875297530753175327533753475357536753775387539754075417542754375447545754675477548754975507551755275537554755575567557755875597560756175627563756475657566756775687569757075717572757375747575757675777578757975807581758275837584758575867587758875897590759175927593759475957596759775987599760076017602760376047605760676077608760976107611761276137614761576167617761876197620762176227623762476257626762776287629763076317632763376347635763676377638763976407641764276437644764576467647764876497650765176527653765476557656765776587659766076617662766376647665766676677668766976707671767276737674767576767677767876797680768176827683768476857686768776887689769076917692769376947695769676977698769977007701770277037704770577067707770877097710771177127713771477157716771777187719772077217722772377247725772677277728772977307731773277337734773577367737773877397740774177427743774477457746774777487749775077517752775377547755775677577758775977607761776277637764776577667767776877697770777177727773777477757776777777787779778077817782778377847785778677877788778977907791779277937794779577967797779877997800780178027803780478057806780778087809781078117812781378147815781678177818781978207821782278237824782578267827782878297830783178327833783478357836783778387839784078417842784378447845784678477848784978507851785278537854785578567857785878597860786178627863786478657866786778687869787078717872787378747875787678777878787978807881788278837884788578867887788878897890789178927893789478957896789778987899790079017902790379047905790679077908790979107911791279137914791579167917791879197920792179227923792479257926792779287929793079317932793379347935793679377938793979407941794279437944794579467947794879497950795179527953795479557956795779587959796079617962796379647965796679677968796979707971797279737974797579767977797879797980798179827983798479857986798779887989799079917992799379947995799679977998799980008001800280038004800580068007800880098010801180128013801480158016801780188019802080218022802380248025802680278028802980308031803280338034803580368037803880398040804180428043804480458046804780488049805080518052805380548055805680578058805980608061806280638064806580668067806880698070807180728073807480758076807780788079808080818082808380848085808680878088808980908091809280938094809580968097809880998100810181028103810481058106810781088109811081118112811381148115811681178118811981208121812281238124812581268127812881298130813181328133813481358136813781388139814081418142814381448145814681478148814981508151815281538154815581568157815881598160816181628163816481658166816781688169817081718172817381748175817681778178817981808181818281838184818581868187818881898190819181928193819481958196819781988199820082018202820382048205820682078208820982108211821282138214821582168217821882198220822182228223822482258226822782288229823082318232823382348235823682378238823982408241824282438244824582468247824882498250825182528253825482558256825782588259826082618262826382648265826682678268826982708271827282738274827582768277827882798280828182828283828482858286828782888289829082918292829382948295829682978298829983008301830283038304830583068307830883098310831183128313831483158316831783188319832083218322832383248325832683278328832983308331833283338334833583368337833883398340834183428343834483458346834783488349835083518352835383548355835683578358835983608361836283638364836583668367836883698370837183728373837483758376837783788379838083818382838383848385838683878388838983908391839283938394839583968397839883998400840184028403840484058406840784088409841084118412841384148415841684178418841984208421842284238424842584268427842884298430843184328433843484358436843784388439844084418442844384448445844684478448844984508451845284538454845584568457845884598460846184628463846484658466846784688469847084718472847384748475847684778478847984808481848284838484848584868487848884898490849184928493849484958496849784988499850085018502850385048505850685078508850985108511851285138514851585168517851885198520852185228523852485258526852785288529853085318532853385348535853685378538853985408541854285438544854585468547854885498550855185528553855485558556855785588559856085618562856385648565856685678568856985708571857285738574857585768577857885798580858185828583858485858586858785888589859085918592859385948595859685978598859986008601860286038604860586068607860886098610861186128613861486158616861786188619862086218622862386248625862686278628862986308631863286338634863586368637863886398640864186428643864486458646864786488649865086518652865386548655865686578658865986608661866286638664866586668667866886698670867186728673867486758676867786788679868086818682868386848685868686878688868986908691869286938694869586968697869886998700870187028703870487058706870787088709871087118712871387148715871687178718871987208721872287238724872587268727872887298730873187328733873487358736873787388739874087418742874387448745874687478748874987508751875287538754875587568757875887598760876187628763876487658766876787688769877087718772877387748775877687778778877987808781878287838784878587868787878887898790879187928793879487958796879787988799880088018802880388048805880688078808880988108811881288138814881588168817881888198820882188228823882488258826882788288829883088318832883388348835883688378838883988408841884288438844884588468847884888498850885188528853885488558856885788588859886088618862886388648865886688678868886988708871887288738874887588768877887888798880888188828883888488858886888788888889889088918892889388948895889688978898889989008901890289038904890589068907890889098910891189128913891489158916891789188919892089218922892389248925892689278928892989308931893289338934893589368937893889398940894189428943894489458946894789488949895089518952895389548955895689578958895989608961896289638964896589668967896889698970897189728973897489758976897789788979898089818982898389848985898689878988898989908991899289938994899589968997899889999000900190029003900490059006900790089009901090119012901390149015901690179018901990209021902290239024902590269027902890299030903190329033903490359036903790389039904090419042904390449045904690479048904990509051905290539054905590569057905890599060906190629063906490659066906790689069907090719072907390749075907690779078907990809081908290839084908590869087908890899090909190929093909490959096909790989099910091019102910391049105910691079108910991109111911291139114911591169117911891199120912191229123912491259126912791289129913091319132913391349135913691379138913991409141914291439144914591469147914891499150915191529153915491559156915791589159916091619162916391649165916691679168916991709171917291739174917591769177917891799180918191829183918491859186918791889189919091919192919391949195919691979198919992009201920292039204920592069207920892099210921192129213921492159216921792189219922092219222922392249225922692279228922992309231923292339234923592369237923892399240924192429243924492459246924792489249925092519252925392549255925692579258925992609261926292639264926592669267926892699270927192729273927492759276927792789279928092819282928392849285928692879288928992909291929292939294929592969297929892999300930193029303930493059306930793089309931093119312931393149315931693179318931993209321932293239324932593269327932893299330933193329333933493359336933793389339934093419342934393449345934693479348934993509351935293539354935593569357935893599360936193629363936493659366936793689369937093719372937393749375937693779378937993809381938293839384938593869387938893899390939193929393939493959396939793989399940094019402940394049405940694079408940994109411941294139414941594169417941894199420942194229423942494259426942794289429943094319432943394349435943694379438943994409441944294439444944594469447944894499450945194529453945494559456945794589459946094619462946394649465946694679468946994709471947294739474947594769477947894799480948194829483948494859486948794889489949094919492949394949495949694979498949995009501950295039504950595069507950895099510951195129513951495159516951795189519952095219522952395249525952695279528952995309531953295339534953595369537953895399540954195429543954495459546954795489549955095519552955395549555955695579558955995609561956295639564956595669567956895699570957195729573957495759576957795789579958095819582958395849585958695879588958995909591959295939594959595969597959895999600960196029603960496059606960796089609961096119612961396149615961696179618961996209621962296239624962596269627962896299630963196329633963496359636963796389639964096419642964396449645964696479648964996509651965296539654965596569657965896599660966196629663966496659666966796689669967096719672967396749675967696779678967996809681968296839684968596869687968896899690969196929693969496959696969796989699970097019702970397049705970697079708970997109711971297139714971597169717971897199720972197229723972497259726972797289729973097319732973397349735973697379738973997409741974297439744974597469747974897499750975197529753975497559756975797589759976097619762976397649765976697679768976997709771977297739774977597769777977897799780978197829783978497859786978797889789979097919792979397949795979697979798979998009801980298039804980598069807980898099810981198129813981498159816981798189819982098219822982398249825982698279828982998309831983298339834983598369837983898399840984198429843984498459846984798489849985098519852985398549855985698579858985998609861986298639864986598669867986898699870987198729873987498759876987798789879988098819882988398849885988698879888988998909891989298939894989598969897989898999900990199029903990499059906990799089909991099119912991399149915991699179918991999209921992299239924992599269927992899299930993199329933993499359936993799389939994099419942994399449945994699479948994999509951995299539954995599569957995899599960996199629963996499659966996799689969997099719972997399749975997699779978997999809981998299839984998599869987998899899990999199929993999499959996999799989999100001000110002100031000410005100061000710008100091001010011100121001310014100151001610017100181001910020100211002210023100241002510026100271002810029100301003110032100331003410035100361003710038100391004010041100421004310044100451004610047100481004910050100511005210053100541005510056100571005810059100601006110062100631006410065100661006710068100691007010071100721007310074100751007610077100781007910080100811008210083100841008510086100871008810089100901009110092100931009410095100961009710098100991010010101101021010310104101051010610107101081010910110101111011210113101141011510116101171011810119101201012110122101231012410125101261012710128101291013010131101321013310134101351013610137101381013910140101411014210143101441014510146101471014810149101501015110152101531015410155101561015710158101591016010161101621016310164101651016610167101681016910170101711017210173101741017510176101771017810179101801018110182101831018410185101861018710188101891019010191101921019310194101951019610197101981019910200102011020210203102041020510206102071020810209102101021110212102131021410215102161021710218102191022010221102221022310224102251022610227102281022910230102311023210233102341023510236102371023810239102401024110242102431024410245102461024710248102491025010251102521025310254102551025610257102581025910260102611026210263102641026510266102671026810269102701027110272102731027410275102761027710278102791028010281102821028310284102851028610287102881028910290102911029210293102941029510296102971029810299103001030110302103031030410305103061030710308103091031010311103121031310314103151031610317103181031910320103211032210323103241032510326103271032810329103301033110332103331033410335103361033710338103391034010341103421034310344103451034610347103481034910350103511035210353103541035510356103571035810359103601036110362103631036410365103661036710368103691037010371103721037310374103751037610377103781037910380103811038210383103841038510386103871038810389103901039110392103931039410395103961039710398103991040010401104021040310404104051040610407104081040910410104111041210413104141041510416104171041810419104201042110422104231042410425104261042710428104291043010431104321043310434104351043610437104381043910440104411044210443104441044510446104471044810449104501045110452104531045410455104561045710458104591046010461104621046310464104651046610467104681046910470104711047210473104741047510476104771047810479104801048110482104831048410485104861048710488104891049010491104921049310494104951049610497104981049910500105011050210503105041050510506105071050810509105101051110512105131051410515105161051710518105191052010521105221052310524105251052610527105281052910530105311053210533105341053510536105371053810539105401054110542105431054410545105461054710548105491055010551105521055310554105551055610557105581055910560105611056210563105641056510566105671056810569105701057110572105731057410575105761057710578105791058010581105821058310584105851058610587105881058910590105911059210593105941059510596105971059810599106001060110602106031060410605106061060710608106091061010611106121061310614106151061610617106181061910620106211062210623106241062510626106271062810629106301063110632106331063410635106361063710638106391064010641106421064310644106451064610647106481064910650106511065210653106541065510656106571065810659 |
- /**!
- * MixItUp v3.3.0
- * A high-performance, dependency-free library for animated filtering, sorting and more
- * Build 0be05511-2264-4384-8e31-c75554304cd0
- *
- * @copyright Copyright 2014-2018 KunkaLabs Limited.
- * @author KunkaLabs Limited.
- * @link https://www.kunkalabs.com/mixitup/
- *
- * @license Commercial use requires a commercial license.
- * https://www.kunkalabs.com/mixitup/licenses/
- *
- * Non-commercial use permitted under same terms as CC BY-NC 3.0 license.
- * http://creativecommons.org/licenses/by-nc/3.0/
- */
- (function(window) {
- 'use strict';
- var mixitup = null,
- h = null;
- (function() {
- var VENDORS = ['webkit', 'moz', 'o', 'ms'],
- canary = window.document.createElement('div'),
- i = -1;
- // window.requestAnimationFrame
- for (i = 0; i < VENDORS.length && !window.requestAnimationFrame; i++) {
- window.requestAnimationFrame = window[VENDORS[i] + 'RequestAnimationFrame'];
- }
- // Element.nextElementSibling
- if (typeof canary.nextElementSibling === 'undefined') {
- Object.defineProperty(window.Element.prototype, 'nextElementSibling', {
- get: function() {
- var el = this.nextSibling;
- while (el) {
- if (el.nodeType === 1) {
- return el;
- }
- el = el.nextSibling;
- }
- return null;
- }
- });
- }
- // Element.matches
- (function(ElementPrototype) {
- ElementPrototype.matches =
- ElementPrototype.matches ||
- ElementPrototype.machesSelector ||
- ElementPrototype.mozMatchesSelector ||
- ElementPrototype.msMatchesSelector ||
- ElementPrototype.oMatchesSelector ||
- ElementPrototype.webkitMatchesSelector ||
- function (selector) {
- return Array.prototype.indexOf.call(this.parentElement.querySelectorAll(selector), this) > -1;
- };
- })(window.Element.prototype);
- // Object.keys
- // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/keys
- if (!Object.keys) {
- Object.keys = (function() {
- var hasOwnProperty = Object.prototype.hasOwnProperty,
- hasDontEnumBug = false,
- dontEnums = [],
- dontEnumsLength = -1;
- hasDontEnumBug = !({
- toString: null
- })
- .propertyIsEnumerable('toString');
- dontEnums = [
- 'toString',
- 'toLocaleString',
- 'valueOf',
- 'hasOwnProperty',
- 'isPrototypeOf',
- 'propertyIsEnumerable',
- 'constructor'
- ];
- dontEnumsLength = dontEnums.length;
- return function(obj) {
- var result = [],
- prop = '',
- i = -1;
- if (typeof obj !== 'object' && (typeof obj !== 'function' || obj === null)) {
- throw new TypeError('Object.keys called on non-object');
- }
- for (prop in obj) {
- if (hasOwnProperty.call(obj, prop)) {
- result.push(prop);
- }
- }
- if (hasDontEnumBug) {
- for (i = 0; i < dontEnumsLength; i++) {
- if (hasOwnProperty.call(obj, dontEnums[i])) {
- result.push(dontEnums[i]);
- }
- }
- }
- return result;
- };
- }());
- }
- // Array.isArray
- // https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Array/isArray
- if (!Array.isArray) {
- Array.isArray = function(arg) {
- return Object.prototype.toString.call(arg) === '[object Array]';
- };
- }
- // Object.create
- // https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Object/create
- if (typeof Object.create !== 'function') {
- Object.create = (function(undefined) {
- var Temp = function() {};
- return function (prototype, propertiesObject) {
- if (prototype !== Object(prototype) && prototype !== null) {
- throw TypeError('Argument must be an object, or null');
- }
- Temp.prototype = prototype || {};
- var result = new Temp();
- Temp.prototype = null;
- if (propertiesObject !== undefined) {
- Object.defineProperties(result, propertiesObject);
- }
- if (prototype === null) {
- /* jshint ignore:start */
- result.__proto__ = null;
- /* jshint ignore:end */
- }
- return result;
- };
- })();
- }
- // String.prototyoe.trim
- if (!String.prototype.trim) {
- String.prototype.trim = function() {
- return this.replace(/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g, '');
- };
- }
- // Array.prototype.indexOf
- // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/indexOf
- if (!Array.prototype.indexOf) {
- Array.prototype.indexOf = function(searchElement) {
- var n, k, t, len;
- if (this === null) {
- throw new TypeError();
- }
- t = Object(this);
- len = t.length >>> 0;
- if (len === 0) {
- return -1;
- }
- n = 0;
- if (arguments.length > 1) {
- n = Number(arguments[1]);
- if (n !== n) {
- n = 0;
- } else if (n !== 0 && n !== Infinity && n !== -Infinity) {
- n = (n > 0 || -1) * Math.floor(Math.abs(n));
- }
- }
- if (n >= len) {
- return -1;
- }
- for (k = n >= 0 ? n : Math.max(len - Math.abs(n), 0); k < len; k++) {
- if (k in t && t[k] === searchElement) {
- return k;
- }
- }
- return -1;
- };
- }
- // Function.prototype.bind
- // https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_objects/Function/bind
- if (!Function.prototype.bind) {
- Function.prototype.bind = function(oThis) {
- var aArgs, self, FNOP, fBound;
- if (typeof this !== 'function') {
- throw new TypeError();
- }
- aArgs = Array.prototype.slice.call(arguments, 1);
- self = this;
- FNOP = function() {};
- fBound = function() {
- return self.apply(this instanceof FNOP ? this : oThis, aArgs.concat(Array.prototype.slice.call(arguments)));
- };
- if (this.prototype) {
- FNOP.prototype = this.prototype;
- }
- fBound.prototype = new FNOP();
- return fBound;
- };
- }
- // Element.prototype.dispatchEvent
- if (!window.Element.prototype.dispatchEvent) {
- window.Element.prototype.dispatchEvent = function(event) {
- try {
- return this.fireEvent('on' + event.type, event);
- } catch (err) {}
- };
- }
- })();
- /**
- * The `mixitup()` "factory" function creates and returns individual instances
- * of MixItUp, known as "mixers", on which API methods can be called.
- *
- * When loading MixItUp via a script tag, the factory function is accessed
- * via the global variable `mixitup`. When using a module loading
- * system (e.g. ES2015, CommonJS, RequireJS), the factory function is
- * exported into your module when you require the MixItUp library.
- *
- * @example
- * mixitup(container [,config] [,foreignDoc])
- *
- * @example <caption>Example 1: Creating a mixer instance with an element reference</caption>
- * var containerEl = document.querySelector('.container');
- *
- * var mixer = mixitup(containerEl);
- *
- * @example <caption>Example 2: Creating a mixer instance with a selector string</caption>
- * var mixer = mixitup('.container');
- *
- * @example <caption>Example 3: Passing a configuration object</caption>
- * var mixer = mixitup(containerEl, {
- * animation: {
- * effects: 'fade scale(0.5)'
- * }
- * });
- *
- * @example <caption>Example 4: Passing an iframe reference</caption>
- * var mixer = mixitup(containerEl, config, foreignDocument);
- *
- * @global
- * @namespace
- * @public
- * @kind function
- * @since 3.0.0
- * @param {(Element|string)} container
- * A DOM element or selector string representing the container(s) on which to instantiate MixItUp.
- * @param {object} [config]
- * An optional "configuration object" used to customize the behavior of the MixItUp instance.
- * @param {object} [foreignDoc]
- * An optional reference to a `document`, which can be used to control a MixItUp instance in an iframe.
- * @return {mixitup.Mixer}
- * A "mixer" object holding the MixItUp instance.
- */
- mixitup = function(container, config, foreignDoc) {
- var el = null,
- returnCollection = false,
- instance = null,
- facade = null,
- doc = null,
- output = null,
- instances = [],
- id = '',
- elements = [],
- i = -1;
- doc = foreignDoc || window.document;
- if (returnCollection = arguments[3]) {
- // A non-documented 4th paramater enabling control of multiple instances
- returnCollection = typeof returnCollection === 'boolean';
- }
- if (typeof container === 'string') {
- elements = doc.querySelectorAll(container);
- } else if (container && typeof container === 'object' && h.isElement(container, doc)) {
- elements = [container];
- } else if (container && typeof container === 'object' && container.length) {
- // Although not documented, the container may also be an array-like list of
- // elements such as a NodeList or jQuery collection, is returnCollection is true
- elements = container;
- } else {
- throw new Error(mixitup.messages.errorFactoryInvalidContainer());
- }
- if (elements.length < 1) {
- throw new Error(mixitup.messages.errorFactoryContainerNotFound());
- }
- for (i = 0; el = elements[i]; i++) {
- if (i > 0 && !returnCollection) break;
- if (!el.id) {
- id = 'MixItUp' + h.randomHex();
- el.id = id;
- } else {
- id = el.id;
- }
- if (mixitup.instances[id] instanceof mixitup.Mixer) {
- instance = mixitup.instances[id];
- if (!config || (config && config.debug && config.debug.showWarnings !== false)) {
- console.warn(mixitup.messages.warningFactoryPreexistingInstance());
- }
- } else {
- instance = new mixitup.Mixer();
- instance.attach(el, doc, id, config);
- mixitup.instances[id] = instance;
- }
- facade = new mixitup.Facade(instance);
- if (config && config.debug && config.debug.enable) {
- instances.push(instance);
- } else {
- instances.push(facade);
- }
- }
- if (returnCollection) {
- output = new mixitup.Collection(instances);
- } else {
- // Return the first instance regardless
- output = instances[0];
- }
- return output;
- };
- /**
- * The `.use()` static method is used to extend the functionality of mixitup with compatible
- * extensions and libraries in an environment with modular scoping e.g. ES2015, CommonJS, or RequireJS.
- *
- * You need only call the `.use()` function once per project, per extension, as module loaders
- * will cache a single reference to MixItUp inclusive of all changes made.
- *
- * @example
- * mixitup.use(extension)
- *
- * @example <caption>Example 1: Extending MixItUp with the Pagination Extension</caption>
- *
- * import mixitup from 'mixitup';
- * import mixitupPagination from 'mixitup-pagination';
- *
- * mixitup.use(mixitupPagination);
- *
- * // All mixers created by the factory function in all modules will now
- * // have pagination functionality
- *
- * var mixer = mixitup('.container');
- *
- * @public
- * @name use
- * @memberof mixitup
- * @kind function
- * @static
- * @since 3.0.0
- * @param {*} extension A reference to the extension or library to be used.
- * @return {void}
- */
- mixitup.use = function(extension) {
- mixitup.Base.prototype.callActions.call(mixitup, 'beforeUse', arguments);
- // Call the extension's factory function, passing
- // the mixitup factory as a paramater
- if (typeof extension === 'function' && extension.TYPE === 'mixitup-extension') {
- // Mixitup extension
- if (typeof mixitup.extensions[extension.NAME] === 'undefined') {
- extension(mixitup);
- mixitup.extensions[extension.NAME] = extension;
- }
- } else if (extension.fn && extension.fn.jquery) {
- // jQuery
- mixitup.libraries.$ = extension;
- }
- mixitup.Base.prototype.callActions.call(mixitup, 'afterUse', arguments);
- };
- mixitup.instances = {};
- mixitup.extensions = {};
- mixitup.libraries = {};
- /**
- * @private
- */
- h = {
- /**
- * @private
- * @param {HTMLElement} el
- * @param {string} cls
- * @return {boolean}
- */
- hasClass: function(el, cls) {
- return !!el.className.match(new RegExp('(\\s|^)' + cls + '(\\s|$)'));
- },
- /**
- * @private
- * @param {HTMLElement} el
- * @param {string} cls
- * @return {void}
- */
- addClass: function(el, cls) {
- if (!this.hasClass(el, cls)) el.className += el.className ? ' ' + cls : cls;
- },
- /**
- * @private
- * @param {HTMLElement} el
- * @param {string} cls
- * @return {void}
- */
- removeClass: function(el, cls) {
- if (this.hasClass(el, cls)) {
- var reg = new RegExp('(\\s|^)' + cls + '(\\s|$)');
- el.className = el.className.replace(reg, ' ').trim();
- }
- },
- /**
- * Merges the properties of the source object onto the
- * target object. Alters the target object.
- *
- * @private
- * @param {object} destination
- * @param {object} source
- * @param {boolean} [deep=false]
- * @param {boolean} [handleErrors=false]
- * @return {void}
- */
- extend: function(destination, source, deep, handleErrors) {
- var sourceKeys = [],
- key = '',
- i = -1;
- deep = deep || false;
- handleErrors = handleErrors || false;
- try {
- if (Array.isArray(source)) {
- for (i = 0; i < source.length; i++) {
- sourceKeys.push(i);
- }
- } else if (source) {
- sourceKeys = Object.keys(source);
- }
- for (i = 0; i < sourceKeys.length; i++) {
- key = sourceKeys[i];
- if (!deep || typeof source[key] !== 'object' || this.isElement(source[key])) {
- // All non-object properties, or all properties if shallow extend
- destination[key] = source[key];
- } else if (Array.isArray(source[key])) {
- // Arrays
- if (!destination[key]) {
- destination[key] = [];
- }
- this.extend(destination[key], source[key], deep, handleErrors);
- } else {
- // Objects
- if (!destination[key]) {
- destination[key] = {};
- }
- this.extend(destination[key], source[key], deep, handleErrors);
- }
- }
- } catch(err) {
- if (handleErrors) {
- this.handleExtendError(err, destination);
- } else {
- throw err;
- }
- }
- return destination;
- },
- /**
- * @private
- * @param {Error} err
- * @param {object} destination
- * @return {void}
- */
- handleExtendError: function(err, destination) {
- var re = /property "?(\w*)"?[,:] object/i,
- matches = null,
- erroneous = '',
- message = '',
- suggestion = '',
- probableMatch = '',
- key = '',
- mostMatchingChars = -1,
- i = -1;
- if (err instanceof TypeError && (matches = re.exec(err.message))) {
- erroneous = matches[1];
- for (key in destination) {
- i = 0;
- while (i < erroneous.length && erroneous.charAt(i) === key.charAt(i)) {
- i++;
- }
- if (i > mostMatchingChars) {
- mostMatchingChars = i;
- probableMatch = key;
- }
- }
- if (mostMatchingChars > 1) {
- suggestion = mixitup.messages.errorConfigInvalidPropertySuggestion({
- probableMatch: probableMatch
- });
- }
- message = mixitup.messages.errorConfigInvalidProperty({
- erroneous: erroneous,
- suggestion: suggestion
- });
- throw new TypeError(message);
- }
- throw err;
- },
- /**
- * @private
- * @param {string} str
- * @return {function}
- */
- template: function(str) {
- var re = /\${([\w]*)}/g,
- dynamics = {},
- matches = null;
- while ((matches = re.exec(str))) {
- dynamics[matches[1]] = new RegExp('\\${' + matches[1] + '}', 'g');
- }
- return function(data) {
- var key = '',
- output = str;
- data = data || {};
- for (key in dynamics) {
- output = output.replace(dynamics[key], typeof data[key] !== 'undefined' ? data[key] : '');
- }
- return output;
- };
- },
- /**
- * @private
- * @param {HTMLElement} el
- * @param {string} type
- * @param {function} fn
- * @param {boolean} useCapture
- * @return {void}
- */
- on: function(el, type, fn, useCapture) {
- if (!el) return;
- if (el.addEventListener) {
- el.addEventListener(type, fn, useCapture);
- } else if (el.attachEvent) {
- el['e' + type + fn] = fn;
- el[type + fn] = function() {
- el['e' + type + fn](window.event);
- };
- el.attachEvent('on' + type, el[type + fn]);
- }
- },
- /**
- * @private
- * @param {HTMLElement} el
- * @param {string} type
- * @param {function} fn
- * @return {void}
- */
- off: function(el, type, fn) {
- if (!el) return;
- if (el.removeEventListener) {
- el.removeEventListener(type, fn, false);
- } else if (el.detachEvent) {
- el.detachEvent('on' + type, el[type + fn]);
- el[type + fn] = null;
- }
- },
- /**
- * @private
- * @param {string} eventType
- * @param {object} detail
- * @param {Document} [doc]
- * @return {CustomEvent}
- */
- getCustomEvent: function(eventType, detail, doc) {
- var event = null;
- doc = doc || window.document;
- if (typeof window.CustomEvent === 'function') {
- event = new window.CustomEvent(eventType, {
- detail: detail,
- bubbles: true,
- cancelable: true
- });
- } else if (typeof doc.createEvent === 'function') {
- event = doc.createEvent('CustomEvent');
- event.initCustomEvent(eventType, true, true, detail);
- } else {
- event = doc.createEventObject(),
- event.type = eventType;
- event.returnValue = false;
- event.cancelBubble = false;
- event.detail = detail;
- }
- return event;
- },
- /**
- * @private
- * @param {Event} e
- * @return {Event}
- */
- getOriginalEvent: function(e) {
- if (e.touches && e.touches.length) {
- return e.touches[0];
- } else if (e.changedTouches && e.changedTouches.length) {
- return e.changedTouches[0];
- } else {
- return e;
- }
- },
- /**
- * @private
- * @param {HTMLElement} el
- * @param {string} selector
- * @return {Number}
- */
- index: function(el, selector) {
- var i = 0;
- while ((el = el.previousElementSibling) !== null) {
- if (!selector || el.matches(selector)) {
- ++i;
- }
- }
- return i;
- },
- /**
- * Converts a dash or snake-case string to camel case.
- *
- * @private
- * @param {string} str
- * @param {boolean} [isPascal]
- * @return {string}
- */
- camelCase: function(str) {
- return str.toLowerCase().replace(/([_-][a-z])/g, function($1) {
- return $1.toUpperCase().replace(/[_-]/, '');
- });
- },
- /**
- * Converts a dash or snake-case string to pascal case.
- *
- * @private
- * @param {string} str
- * @param {boolean} [isPascal]
- * @return {string}
- */
- pascalCase: function(str) {
- return (str = this.camelCase(str)).charAt(0).toUpperCase() + str.slice(1);
- },
- /**
- * Converts a camel or pascal-case string to dash case.
- *
- * @private
- * @param {string} str
- * @return {string}
- */
- dashCase: function(str) {
- return str.replace(/([A-Z])/g, '-$1').replace(/^-/, '').toLowerCase();
- },
- /**
- * @private
- * @param {HTMLElement} el
- * @param {HTMLHtmlElement} [doc]
- * @return {boolean}
- */
- isElement: function(el, doc) {
- doc = doc || window.document;
- if (
- window.HTMLElement &&
- el instanceof window.HTMLElement
- ) {
- return true;
- } else if (
- doc.defaultView &&
- doc.defaultView.HTMLElement &&
- el instanceof doc.defaultView.HTMLElement
- ) {
- return true;
- } else {
- return (
- el !== null &&
- el.nodeType === 1 &&
- typeof el.nodeName === 'string'
- );
- }
- },
- /**
- * @private
- * @param {string} htmlString
- * @param {HTMLHtmlElement} [doc]
- * @return {DocumentFragment}
- */
- createElement: function(htmlString, doc) {
- var frag = null,
- temp = null;
- doc = doc || window.document;
- frag = doc.createDocumentFragment();
- temp = doc.createElement('div');
- temp.innerHTML = htmlString.trim();
- while (temp.firstChild) {
- frag.appendChild(temp.firstChild);
- }
- return frag;
- },
- /**
- * @private
- * @param {Node} node
- * @return {void}
- */
- removeWhitespace: function(node) {
- var deleting;
- while (node && node.nodeName === '#text') {
- deleting = node;
- node = node.previousSibling;
- deleting.parentElement && deleting.parentElement.removeChild(deleting);
- }
- },
- /**
- * @private
- * @param {Array<*>} a
- * @param {Array<*>} b
- * @return {boolean}
- */
- isEqualArray: function(a, b) {
- var i = a.length;
- if (i !== b.length) return false;
- while (i--) {
- if (a[i] !== b[i]) return false;
- }
- return true;
- },
- /**
- * @private
- * @param {object} a
- * @param {object} b
- * @return {boolean}
- */
- deepEquals: function(a, b) {
- var key;
- if (typeof a === 'object' && a && typeof b === 'object' && b) {
- if (Object.keys(a).length !== Object.keys(b).length) return false;
- for (key in a) {
- if (!b.hasOwnProperty(key) || !this.deepEquals(a[key], b[key])) return false;
- }
- } else if (a !== b) {
- return false;
- }
- return true;
- },
- /**
- * @private
- * @param {Array<*>} oldArray
- * @return {Array<*>}
- */
- arrayShuffle: function(oldArray) {
- var newArray = oldArray.slice(),
- len = newArray.length,
- i = len,
- p = -1,
- t = [];
- while (i--) {
- p = ~~(Math.random() * len);
- t = newArray[i];
- newArray[i] = newArray[p];
- newArray[p] = t;
- }
- return newArray;
- },
- /**
- * @private
- * @param {object} list
- */
- arrayFromList: function(list) {
- var output, i;
- try {
- return Array.prototype.slice.call(list);
- } catch(err) {
- output = [];
- for (i = 0; i < list.length; i++) {
- output.push(list[i]);
- }
- return output;
- }
- },
- /**
- * @private
- * @param {function} func
- * @param {Number} wait
- * @param {boolean} immediate
- * @return {function}
- */
- debounce: function(func, wait, immediate) {
- var timeout;
- return function() {
- var self = this,
- args = arguments,
- callNow = immediate && !timeout,
- later = null;
- later = function() {
- timeout = null;
- if (!immediate) {
- func.apply(self, args);
- }
- };
- clearTimeout(timeout);
- timeout = setTimeout(later, wait);
- if (callNow) func.apply(self, args);
- };
- },
- /**
- * @private
- * @param {HTMLElement} element
- * @return {object}
- */
- position: function(element) {
- var xPosition = 0,
- yPosition = 0,
- offsetParent = element;
- while (element) {
- xPosition -= element.scrollLeft;
- yPosition -= element.scrollTop;
- if (element === offsetParent) {
- xPosition += element.offsetLeft;
- yPosition += element.offsetTop;
- offsetParent = element.offsetParent;
- }
- element = element.parentElement;
- }
- return {
- x: xPosition,
- y: yPosition
- };
- },
- /**
- * @private
- * @param {object} node1
- * @param {object} node2
- * @return {Number}
- */
- getHypotenuse: function(node1, node2) {
- var distanceX = node1.x - node2.x,
- distanceY = node1.y - node2.y;
- distanceX = distanceX < 0 ? distanceX * -1 : distanceX,
- distanceY = distanceY < 0 ? distanceY * -1 : distanceY;
- return Math.sqrt(Math.pow(distanceX, 2) + Math.pow(distanceY, 2));
- },
- /**
- * Calcuates the area of intersection between two rectangles and expresses it as
- * a ratio in comparison to the area of the first rectangle.
- *
- * @private
- * @param {Rect} box1
- * @param {Rect} box2
- * @return {number}
- */
- getIntersectionRatio: function(box1, box2) {
- var controlArea = box1.width * box1.height,
- intersectionX = -1,
- intersectionY = -1,
- intersectionArea = -1,
- ratio = -1;
- intersectionX =
- Math.max(0, Math.min(box1.left + box1.width, box2.left + box2.width) - Math.max(box1.left, box2.left));
- intersectionY =
- Math.max(0, Math.min(box1.top + box1.height, box2.top + box2.height) - Math.max(box1.top, box2.top));
- intersectionArea = intersectionY * intersectionX;
- ratio = intersectionArea / controlArea;
- return ratio;
- },
- /**
- * @private
- * @param {object} el
- * @param {string} selector
- * @param {boolean} [includeSelf]
- * @param {HTMLHtmlElement} [doc]
- * @return {Element|null}
- */
- closestParent: function(el, selector, includeSelf, doc) {
- var parent = el.parentNode;
- doc = doc || window.document;
- if (includeSelf && el.matches(selector)) {
- return el;
- }
- while (parent && parent != doc.body) {
- if (parent.matches && parent.matches(selector)) {
- return parent;
- } else if (parent.parentNode) {
- parent = parent.parentNode;
- } else {
- return null;
- }
- }
- return null;
- },
- /**
- * @private
- * @param {HTMLElement} el
- * @param {string} selector
- * @param {HTMLHtmlElement} [doc]
- * @return {NodeList}
- */
- children: function(el, selector, doc) {
- var children = [],
- tempId = '';
- doc = doc || window.doc;
- if (el) {
- if (!el.id) {
- tempId = 'Temp' + this.randomHexKey();
- el.id = tempId;
- }
- children = doc.querySelectorAll('#' + el.id + ' > ' + selector);
- if (tempId) {
- el.removeAttribute('id');
- }
- }
- return children;
- },
- /**
- * Creates a clone of a provided array, with any empty strings removed.
- *
- * @private
- * @param {Array<*>} originalArray
- * @return {Array<*>}
- */
- clean: function(originalArray) {
- var cleanArray = [],
- i = -1;
- for (i = 0; i < originalArray.length; i++) {
- if (originalArray[i] !== '') {
- cleanArray.push(originalArray[i]);
- }
- }
- return cleanArray;
- },
- /**
- * Abstracts an ES6 promise into a q-like deferred interface for storage and deferred resolution.
- *
- * @private
- * @param {object} libraries
- * @return {h.Deferred}
- */
- defer: function(libraries) {
- var deferred = null,
- promiseWrapper = null,
- $ = null;
- promiseWrapper = new this.Deferred();
- if (mixitup.features.has.promises) {
- // ES6 native promise or polyfill
- promiseWrapper.promise = new Promise(function(resolve, reject) {
- promiseWrapper.resolve = resolve;
- promiseWrapper.reject = reject;
- });
- } else if (($ = (window.jQuery || libraries.$)) && typeof $.Deferred === 'function') {
- // jQuery
- deferred = $.Deferred();
- promiseWrapper.promise = deferred.promise();
- promiseWrapper.resolve = deferred.resolve;
- promiseWrapper.reject = deferred.reject;
- } else if (window.console) {
- // No implementation
- console.warn(mixitup.messages.warningNoPromiseImplementation());
- }
- return promiseWrapper;
- },
- /**
- * @private
- * @param {Array<Promise>} tasks
- * @param {object} libraries
- * @return {Promise<Array>}
- */
- all: function(tasks, libraries) {
- var $ = null;
- if (mixitup.features.has.promises) {
- return Promise.all(tasks);
- } else if (($ = (window.jQuery || libraries.$)) && typeof $.when === 'function') {
- return $.when.apply($, tasks)
- .done(function() {
- // jQuery when returns spread arguments rather than an array or resolutions
- return arguments;
- });
- }
- // No implementation
- if (window.console) {
- console.warn(mixitup.messages.warningNoPromiseImplementation());
- }
- return [];
- },
- /**
- * @private
- * @param {HTMLElement} el
- * @param {string} property
- * @param {Array<string>} vendors
- * @return {string}
- */
- getPrefix: function(el, property, vendors) {
- var i = -1,
- prefix = '';
- if (h.dashCase(property) in el.style) return '';
- for (i = 0; prefix = vendors[i]; i++) {
- if (prefix + property in el.style) {
- return prefix.toLowerCase();
- }
- }
- return 'unsupported';
- },
- /**
- * @private
- * @return {string}
- */
- randomHex: function() {
- return ('00000' + (Math.random() * 16777216 << 0).toString(16)).substr(-6).toUpperCase();
- },
- /**
- * @private
- * @param {HTMLDocument} [doc]
- * @return {object}
- */
- getDocumentState: function(doc) {
- doc = typeof doc.body === 'object' ? doc : window.document;
- return {
- scrollTop: window.pageYOffset,
- scrollLeft: window.pageXOffset,
- docHeight: doc.documentElement.scrollHeight,
- docWidth: doc.documentElement.scrollWidth,
- viewportHeight: doc.documentElement.clientHeight,
- viewportWidth: doc.documentElement.clientWidth
- };
- },
- /**
- * @private
- * @param {object} obj
- * @param {function} fn
- * @return {function}
- */
- bind: function(obj, fn) {
- return function() {
- return fn.apply(obj, arguments);
- };
- },
- /**
- * @private
- * @param {HTMLElement} el
- * @return {boolean}
- */
- isVisible: function(el) {
- var styles = null;
- if (el.offsetParent) return true;
- styles = window.getComputedStyle(el);
- if (
- styles.position === 'fixed' &&
- styles.visibility !== 'hidden' &&
- styles.opacity !== '0'
- ) {
- // Fixed elements report no offsetParent,
- // but may still be invisible
- return true;
- }
- return false;
- },
- /**
- * @private
- * @param {object} obj
- */
- seal: function(obj) {
- if (typeof Object.seal === 'function') {
- Object.seal(obj);
- }
- },
- /**
- * @private
- * @param {object} obj
- */
- freeze: function(obj) {
- if (typeof Object.freeze === 'function') {
- Object.freeze(obj);
- }
- },
- /**
- * @private
- * @param {string} control
- * @param {string} specimen
- * @return {boolean}
- */
- compareVersions: function(control, specimen) {
- var controlParts = control.split('.'),
- specimenParts = specimen.split('.'),
- controlPart = -1,
- specimenPart = -1,
- i = -1;
- for (i = 0; i < controlParts.length; i++) {
- controlPart = parseInt(controlParts[i].replace(/[^\d.]/g, ''));
- specimenPart = parseInt(specimenParts[i].replace(/[^\d.]/g, '') || 0);
- if (specimenPart < controlPart) {
- return false;
- } else if (specimenPart > controlPart) {
- return true;
- }
- }
- return true;
- },
- /**
- * @private
- * @constructor
- */
- Deferred: function() {
- this.promise = null;
- this.resolve = null;
- this.reject = null;
- this.id = h.randomHex();
- },
- /**
- * @private
- * @param {object} obj
- * @return {boolean}
- */
- isEmptyObject: function(obj) {
- var key = '';
- if (typeof Object.keys === 'function') {
- return Object.keys(obj).length === 0;
- }
- for (key in obj) {
- if (obj.hasOwnProperty(key)) {
- return false;
- }
- }
- return true;
- },
- /**
- * @param {mixitup.Config.ClassNames} classNames
- * @param {string} elementName
- * @param {string} [modifier]
- * @return {string}
- */
- getClassname: function(classNames, elementName, modifier) {
- var classname = '';
- classname += classNames.block;
- if (classname.length) {
- classname += classNames.delineatorElement;
- }
- classname += classNames['element' + this.pascalCase(elementName)];
- if (!modifier) return classname;
- if (classname.length) {
- classname += classNames.delineatorModifier;
- }
- classname += modifier;
- return classname;
- },
- /**
- * Returns the value of a property on a given object via its string key.
- *
- * @param {object} obj
- * @param {string} stringKey
- * @return {*} value
- */
- getProperty: function(obj, stringKey) {
- var parts = stringKey.split('.'),
- returnCurrent = null,
- current = '',
- i = 0;
- if (!stringKey) {
- return obj;
- }
- returnCurrent = function(obj) {
- if (!obj) {
- return null;
- } else {
- return obj[current];
- }
- };
- while (i < parts.length) {
- current = parts[i];
- obj = returnCurrent(obj);
- i++;
- }
- if (typeof obj !== 'undefined') {
- return obj;
- } else {
- return null;
- }
- }
- };
- mixitup.h = h;
- /**
- * The Base class adds instance methods to all other extensible MixItUp classes,
- * enabling the calling of any registered hooks.
- *
- * @constructor
- * @namespace
- * @memberof mixitup
- * @private
- * @since 3.0.0
- */
- mixitup.Base = function() {};
- mixitup.Base.prototype = {
- constructor: mixitup.Base,
- /**
- * Calls any registered hooks for the provided action.
- *
- * @memberof mixitup.Base
- * @private
- * @instance
- * @since 2.0.0
- * @param {string} actionName
- * @param {Array<*>} args
- * @return {void}
- */
- callActions: function(actionName, args) {
- var self = this,
- hooks = self.constructor.actions[actionName],
- extensionName = '';
- if (!hooks || h.isEmptyObject(hooks)) return;
- for (extensionName in hooks) {
- hooks[extensionName].apply(self, args);
- }
- },
- /**
- * Calls any registered hooks for the provided filter.
- *
- * @memberof mixitup.Base
- * @private
- * @instance
- * @since 2.0.0
- * @param {string} filterName
- * @param {*} input
- * @param {Array<*>} args
- * @return {*}
- */
- callFilters: function(filterName, input, args) {
- var self = this,
- hooks = self.constructor.filters[filterName],
- output = input,
- extensionName = '';
- if (!hooks || h.isEmptyObject(hooks)) return output;
- args = args || [];
- for (extensionName in hooks) {
- args = h.arrayFromList(args);
- args.unshift(output);
- output = hooks[extensionName].apply(self, args);
- }
- return output;
- }
- };
- /**
- * The BaseStatic class holds a set of static methods which are then added to all other
- * extensible MixItUp classes as a means of integrating extensions via the addition of new
- * methods and/or actions and hooks.
- *
- * @constructor
- * @namespace
- * @memberof mixitup
- * @private
- * @since 3.0.0
- */
- mixitup.BaseStatic = function() {
- this.actions = {};
- this.filters = {};
- /**
- * Performs a shallow extend on the class's prototype, adding one or more new members to
- * the class in a single operation.
- *
- * @memberof mixitup.BaseStatic
- * @public
- * @static
- * @since 2.1.0
- * @param {object} extension
- * @return {void}
- */
- this.extend = function(extension) {
- h.extend(this.prototype, extension);
- };
- /**
- * Registers a function to be called on the action hook of the provided name.
- *
- * @memberof mixitup.BaseStatic
- * @public
- * @static
- * @since 2.1.0
- * @param {string} hookName
- * @param {string} extensionName
- * @param {function} func
- * @return {void}
- */
- this.registerAction = function(hookName, extensionName, func) {
- (this.actions[hookName] = this.actions[hookName] || {})[extensionName] = func;
- };
- /**
- * Registers a function to be called on the filter of the provided name.
- *
- * @memberof mixitup.BaseStatic
- * @public
- * @static
- * @since 2.1.0
- * @param {string} hookName
- * @param {string} extensionName
- * @param {function} func
- * @return {void}
- */
- this.registerFilter = function(hookName, extensionName, func) {
- (this.filters[hookName] = this.filters[hookName] || {})[extensionName] = func;
- };
- };
- /**
- * The `mixitup.Features` class performs all feature and CSS prefix detection
- * neccessary for MixItUp to function correctly, as well as storing various
- * string and array constants. All feature decection is on evaluation of the
- * library and stored in a singleton instance for use by other internal classes.
- *
- * @constructor
- * @namespace
- * @memberof mixitup
- * @private
- * @since 3.0.0
- */
- mixitup.Features = function() {
- mixitup.Base.call(this);
- this.callActions('beforeConstruct');
- this.boxSizingPrefix = '';
- this.transformPrefix = '';
- this.transitionPrefix = '';
- this.boxSizingPrefix = '';
- this.transformProp = '';
- this.transformRule = '';
- this.transitionProp = '';
- this.perspectiveProp = '';
- this.perspectiveOriginProp = '';
- this.has = new mixitup.Has();
- this.canary = null;
- this.BOX_SIZING_PROP = 'boxSizing';
- this.TRANSITION_PROP = 'transition';
- this.TRANSFORM_PROP = 'transform';
- this.PERSPECTIVE_PROP = 'perspective';
- this.PERSPECTIVE_ORIGIN_PROP = 'perspectiveOrigin';
- this.VENDORS = ['Webkit', 'moz', 'O', 'ms'];
- this.TWEENABLE = [
- 'opacity',
- 'width', 'height',
- 'marginRight', 'marginBottom',
- 'x', 'y',
- 'scale',
- 'translateX', 'translateY', 'translateZ',
- 'rotateX', 'rotateY', 'rotateZ'
- ];
- this.callActions('afterConstruct');
- };
- mixitup.BaseStatic.call(mixitup.Features);
- mixitup.Features.prototype = Object.create(mixitup.Base.prototype);
- h.extend(mixitup.Features.prototype,
- /** @lends mixitup.Features */
- {
- constructor: mixitup.Features,
- /**
- * @private
- * @return {void}
- */
- init: function() {
- var self = this;
- self.callActions('beforeInit', arguments);
- self.canary = document.createElement('div');
- self.setPrefixes();
- self.runTests();
- self.callActions('beforeInit', arguments);
- },
- /**
- * @private
- * @return {void}
- */
- runTests: function() {
- var self = this;
- self.callActions('beforeRunTests', arguments);
- self.has.promises = typeof window.Promise === 'function';
- self.has.transitions = self.transitionPrefix !== 'unsupported';
- self.callActions('afterRunTests', arguments);
- h.freeze(self.has);
- },
- /**
- * @private
- * @return {void}
- */
- setPrefixes: function() {
- var self = this;
- self.callActions('beforeSetPrefixes', arguments);
- self.transitionPrefix = h.getPrefix(self.canary, 'Transition', self.VENDORS);
- self.transformPrefix = h.getPrefix(self.canary, 'Transform', self.VENDORS);
- self.boxSizingPrefix = h.getPrefix(self.canary, 'BoxSizing', self.VENDORS);
- self.boxSizingProp = self.boxSizingPrefix ?
- self.boxSizingPrefix + h.pascalCase(self.BOX_SIZING_PROP) : self.BOX_SIZING_PROP;
- self.transitionProp = self.transitionPrefix ?
- self.transitionPrefix + h.pascalCase(self.TRANSITION_PROP) : self.TRANSITION_PROP;
- self.transformProp = self.transformPrefix ?
- self.transformPrefix + h.pascalCase(self.TRANSFORM_PROP) : self.TRANSFORM_PROP;
- self.transformRule = self.transformPrefix ?
- '-' + self.transformPrefix + '-' + self.TRANSFORM_PROP : self.TRANSFORM_PROP;
- self.perspectiveProp = self.transformPrefix ?
- self.transformPrefix + h.pascalCase(self.PERSPECTIVE_PROP) : self.PERSPECTIVE_PROP;
- self.perspectiveOriginProp = self.transformPrefix ?
- self.transformPrefix + h.pascalCase(self.PERSPECTIVE_ORIGIN_PROP) :
- self.PERSPECTIVE_ORIGIN_PROP;
- self.callActions('afterSetPrefixes', arguments);
- }
- });
- /**
- * @constructor
- * @memberof mixitup
- * @private
- * @since 3.0.0
- */
- mixitup.Has = function() {
- this.transitions = false;
- this.promises = false;
- h.seal(this);
- };
- // Assign a singleton instance to `mixitup.features` and initialise:
- mixitup.features = new mixitup.Features();
- mixitup.features.init();
- /**
- * A group of properties defining the mixer's animation and effects settings.
- *
- * @constructor
- * @memberof mixitup.Config
- * @name animation
- * @namespace
- * @public
- * @since 2.0.0
- */
- mixitup.ConfigAnimation = function() {
- mixitup.Base.call(this);
- this.callActions('beforeConstruct');
- /**
- * A boolean dictating whether or not animation should be enabled for the MixItUp instance.
- * If `false`, all operations will occur instantly and syncronously, although callback
- * functions and any returned promises will still be fulfilled.
- *
- * @example <caption>Example: Create a mixer with all animations disabled</caption>
- * var mixer = mixitup(containerEl, {
- * animation: {
- * enable: false
- * }
- * });
- *
- * @name enable
- * @memberof mixitup.Config.animation
- * @instance
- * @type {boolean}
- * @default true
- */
- this.enable = true;
- /**
- * A string of one or more space-seperated properties to which transitions will be
- * applied for all filtering animations.
- *
- * Properties can be listed any order or combination, although they will be applied in a specific
- * predefined order to produce consistent results.
- *
- * To learn more about available effects, experiment with our <a href="https://www.kunkalabs.com/mixitup/">
- * sandbox demo</a> and try out the "Export config" button in the Animation options drop down.
- *
- * @example <caption>Example: Apply "fade" and "translateZ" effects to all animations</caption>
- * // As targets are filtered in and out, they will fade between
- * // opacity 1 and 0 and transform between translateZ(-100px) and
- * // translateZ(0).
- *
- * var mixer = mixitup(containerEl, {
- * animation: {
- * effects: 'fade translateZ(-100px)'
- * }
- * });
- *
- * @name effects
- * @memberof mixitup.Config.animation
- * @instance
- * @type {string}
- * @default 'fade scale'
- */
- this.effects = 'fade scale';
- /**
- * A string of one or more space-seperated effects to be applied only to filter-in
- * animations, overriding `config.animation.effects` if set.
- *
- * @example <caption>Example: Apply downwards vertical translate to targets being filtered in</caption>
- *
- * var mixer = mixitup(containerEl, {
- * animation: {
- * effectsIn: 'fade translateY(-100%)'
- * }
- * });
- *
- * @name effectsIn
- * @memberof mixitup.Config.animation
- * @instance
- * @type {string}
- * @default ''
- */
- this.effectsIn = '';
- /**
- * A string of one or more space-seperated effects to be applied only to filter-out
- * animations, overriding `config.animation.effects` if set.
- *
- * @example <caption>Example: Apply upwards vertical translate to targets being filtered out</caption>
- *
- * var mixer = mixitup(containerEl, {
- * animation: {
- * effectsOut: 'fade translateY(-100%)'
- * }
- * });
- *
- * @name effectsOut
- * @memberof mixitup.Config.animation
- * @instance
- * @type {string}
- * @default ''
- */
- this.effectsOut = '';
- /**
- * An integer dictating the duration of all MixItUp animations in milliseconds, not
- * including any additional delay apllied via the `'stagger'` effect.
- *
- * @example <caption>Example: Apply an animation duration of 200ms to all mixitup animations</caption>
- *
- * var mixer = mixitup(containerEl, {
- * animation: {
- * duration: 200
- * }
- * });
- *
- * @name duration
- * @memberof mixitup.Config.animation
- * @instance
- * @type {number}
- * @default 600
- */
- this.duration = 600;
- /**
- * A valid CSS3 transition-timing function or shorthand. For a full list of accepted
- * values, visit <a href="http://easings.net" target="_blank">easings.net</a>.
- *
- * @example <caption>Example 1: Apply "ease-in-out" easing to all animations</caption>
- *
- * var mixer = mixitup(containerEl, {
- * animation: {
- * easing: 'ease-in-out'
- * }
- * });
- *
- * @example <caption>Example 2: Apply a custom "cubic-bezier" easing function to all animations</caption>
- * var mixer = mixitup(containerEl, {
- * animation: {
- * easing: 'cubic-bezier(0.645, 0.045, 0.355, 1)'
- * }
- * });
- *
- * @name easing
- * @memberof mixitup.Config.animation
- * @instance
- * @type {string}
- * @default 'ease'
- */
- this.easing = 'ease';
- /**
- * A boolean dictating whether or not to apply perspective to the MixItUp container
- * during animations. By default, perspective is always applied and creates the
- * illusion of three-dimensional space for effects such as `translateZ`, `rotateX`,
- * and `rotateY`.
- *
- * You may wish to disable this and define your own perspective settings via CSS.
- *
- * @example <caption>Example: Prevent perspective from being applied to any 3D transforms</caption>
- * var mixer = mixitup(containerEl, {
- * animation: {
- * applyPerspective: false
- * }
- * });
- *
- * @name applyPerspective
- * @memberof mixitup.Config.animation
- * @instance
- * @type {bolean}
- * @default true
- */
- this.applyPerspective = true;
- /**
- * The perspective distance value to be applied to the container during animations,
- * affecting any 3D-transform-based effects.
- *
- * @example <caption>Example: Set a perspective distance of 2000px</caption>
- * var mixer = mixitup(containerEl, {
- * animation: {
- * effects: 'rotateY(-25deg)',
- * perspectiveDistance: '2000px'
- * }
- * });
- *
- * @name perspectiveDistance
- * @memberof mixitup.Config.animation
- * @instance
- * @type {string}
- * @default '3000px'
- */
- this.perspectiveDistance = '3000px';
- /**
- * The perspective-origin value to be applied to the container during animations,
- * affecting any 3D-transform-based effects.
- *
- * @example <caption>Example: Set a perspective origin in the top-right of the container</caption>
- * var mixer = mixitup(containerEl, {
- * animation: {
- * effects: 'transateZ(-200px)',
- * perspectiveOrigin: '100% 0'
- * }
- * });
- *
- * @name perspectiveOrigin
- * @memberof mixitup.Config.animation
- * @instance
- * @type {string}
- * @default '50% 50%'
- */
- this.perspectiveOrigin = '50% 50%';
- /**
- * A boolean dictating whether or not to enable the queuing of operations.
- *
- * If `true` (default), and a control is clicked or an API call is made while another
- * operation is progress, the operation will go into the queue and will be automatically exectuted
- * when the previous operaitons is finished.
- *
- * If `false`, any requested operations will be ignored, and the `onMixBusy` callback and `mixBusy`
- * event will be fired. If `debug.showWarnings` is enabled, a console warning will also occur.
- *
- * @example <caption>Example: Disable queuing</caption>
- * var mixer = mixitup(containerEl, {
- * animation: {
- * queue: false
- * }
- * });
- *
- * @name queue
- * @memberof mixitup.Config.animation
- * @instance
- * @type {boolean}
- * @default true
- */
- this.queue = true;
- /**
- * An integer dictacting the maximum number of operations allowed in the queue at
- * any time, when queuing is enabled.
- *
- * @example <caption>Example: Allow a maximum of 5 operations in the queue at any time</caption>
- * var mixer = mixitup(containerEl, {
- * animation: {
- * queueLimit: 5
- * }
- * });
- *
- * @name queueLimit
- * @memberof mixitup.Config.animation
- * @instance
- * @type {number}
- * @default 3
- */
- this.queueLimit = 3;
- /**
- * A boolean dictating whether or not to transition the height and width of the
- * container as elements are filtered in and out. If disabled, the container height
- * will change abruptly.
- *
- * It may be desirable to disable this on mobile devices as the CSS `height` and
- * `width` properties do not receive GPU-acceleration and can therefore cause stuttering.
- *
- * @example <caption>Example 1: Disable the transitioning of the container height and/or width</caption>
- * var mixer = mixitup(containerEl, {
- * animation: {
- * animateResizeContainer: false
- * }
- * });
- *
- * @example <caption>Example 2: Disable the transitioning of the container height and/or width for mobile devices only</caption>
- * var mixer = mixitup(containerEl, {
- * animation: {
- * animateResizeContainer: myFeatureTests.isMobile ? false : true
- * }
- * });
- *
- * @name animateResizeContainer
- * @memberof mixitup.Config.animation
- * @instance
- * @type {boolean}
- * @default true
- */
- this.animateResizeContainer = true;
- /**
- * A boolean dictating whether or not to transition the height and width of target
- * elements as they change throughout the course of an animation.
- *
- * This is often a must for flex-box grid layouts where the size of target elements may change
- * depending on final their position in relation to their siblings, or for `.changeLayout()`
- * operations where the size of targets change between layouts.
- *
- * NB: This feature requires additional calculations and manipulation to non-hardware-accelerated
- * properties which may adversely affect performance on slower devices, and is therefore
- * disabled by default.
- *
- * @example <caption>Example: Enable the transitioning of target widths and heights</caption>
- * var mixer = mixitup(containerEl, {
- * animation: {
- * animateResizeTargets: true
- * }
- * });
- *
- * @name animateResizeTargets
- * @memberof mixitup.Config.animation
- * @instance
- * @type {boolean}
- * @default false
- */
- this.animateResizeTargets = false;
- /**
- * A custom function used to manipulate the order in which the stagger delay is
- * incremented when using the ‘stagger’ effect.
- *
- * When using the 'stagger' effect, the delay applied to each target element is incremented
- * based on its index. You may create a custom function to manipulate the order in which the
- * delay is incremented and create engaging non-linear stagger effects.
- *
- * The function receives the index of the target element as a parameter, and must
- * return an integer which serves as the multiplier for the stagger delay.
- *
- * @example <caption>Example 1: Stagger target elements by column in a 3-column grid</caption>
- * var mixer = mixitup(containerEl, {
- * animation: {
- * effects: 'fade stagger(100ms)',
- * staggerSequence: function(i) {
- * return i % 3;
- * }
- * }
- * });
- *
- * @example <caption>Example 2: Using an algorithm to produce a more complex sequence</caption>
- * var mixer = mixitup(containerEl, {
- * animation: {
- * effects: 'fade stagger(100ms)',
- * staggerSequence: function(i) {
- * return (2*i) - (5*((i/3) - ((1/3) * (i%3))));
- * }
- * }
- * });
- *
- * @name staggerSequence
- * @memberof mixitup.Config.animation
- * @instance
- * @type {function}
- * @default null
- */
- this.staggerSequence = null;
- /**
- * A boolean dictating whether or not to reverse the direction of `translate`
- * and `rotate` transforms for elements being filtered out.
- *
- * It can be used to create carousel-like animations where elements enter and exit
- * from opposite directions. If enabled, the effect `translateX(-100%)` for elements
- * being filtered in would become `translateX(100%)` for targets being filtered out.
- *
- * This functionality can also be achieved by providing seperate effects
- * strings for `config.animation.effectsIn` and `config.animation.effectsOut`.
- *
- * @example <caption>Example: Reverse the desired direction on any translate/rotate effect for targets being filtered out</caption>
- * // Elements being filtered in will be translated from '100%' to '0' while
- * // elements being filtered out will be translated from 0 to '-100%'
- *
- * var mixer = mixitup(containerEl, {
- * animation: {
- * effects: 'fade translateX(100%)',
- * reverseOut: true,
- * nudge: false // Disable nudging to create a carousel-like effect
- * }
- * });
- *
- * @name reverseOut
- * @memberof mixitup.Config.animation
- * @instance
- * @type {boolean}
- * @default false
- */
- this.reverseOut = false;
- /**
- * A boolean dictating whether or not to "nudge" the animation path of targets
- * when they are being filtered in and out simulatenously.
- *
- * This has been the default behavior of MixItUp since version 1, but it
- * may be desirable to disable this effect when filtering directly from
- * one exclusive set of targets to a different exclusive set of targets,
- * to create a carousel-like effect, or a generally more subtle animation.
- *
- * @example <caption>Example: Disable the "nudging" of targets being filtered in and out simulatenously</caption>
- *
- * var mixer = mixitup(containerEl, {
- * animation: {
- * nudge: false
- * }
- * });
- *
- * @name nudge
- * @memberof mixitup.Config.animation
- * @instance
- * @type {boolean}
- * @default true
- */
- this.nudge = true;
- /**
- * A boolean dictating whether or not to clamp the height of the container while MixItUp's
- * geometry tests are carried out before an operation.
- *
- * To prevent scroll-bar flicker, clamping is turned on by default. But in the case where the
- * height of the container might affect its vertical positioning in the viewport
- * (e.g. a vertically-centered container), this should be turned off to ensure accurate
- * test results and a smooth animation.
- *
- * @example <caption>Example: Disable container height-clamping</caption>
- *
- * var mixer = mixitup(containerEl, {
- * animation: {
- * clampHeight: false
- * }
- * });
- *
- * @name clampHeight
- * @memberof mixitup.Config.animation
- * @instance
- * @type {boolean}
- * @default true
- */
- this.clampHeight = true;
- /**
- * A boolean dictating whether or not to clamp the width of the container while MixItUp's
- * geometry tests are carried out before an operation.
- *
- * To prevent scroll-bar flicker, clamping is turned on by default. But in the case where the
- * width of the container might affect its horitzontal positioning in the viewport
- * (e.g. a horizontall-centered container), this should be turned off to ensure accurate
- * test results and a smooth animation.
- *
- * @example <caption>Example: Disable container width-clamping</caption>
- *
- * var mixer = mixitup(containerEl, {
- * animation: {
- * clampWidth: false
- * }
- * });
- *
- * @name clampWidth
- * @memberof mixitup.Config.animation
- * @instance
- * @type {boolean}
- * @default true
- */
- this.clampWidth = true;
- this.callActions('afterConstruct');
- h.seal(this);
- };
- mixitup.BaseStatic.call(mixitup.ConfigAnimation);
- mixitup.ConfigAnimation.prototype = Object.create(mixitup.Base.prototype);
- mixitup.ConfigAnimation.prototype.constructor = mixitup.ConfigAnimation;
- /**
- * A group of properties relating to the behavior of the Mixer.
- *
- * @constructor
- * @memberof mixitup.Config
- * @name behavior
- * @namespace
- * @public
- * @since 3.1.12
- */
- mixitup.ConfigBehavior = function() {
- mixitup.Base.call(this);
- this.callActions('beforeConstruct');
- /**
- * A boolean dictating whether to allow "live" sorting of the mixer.
- *
- * Because of the expensive nature of sorting, MixItUp makes use of several
- * internal optimizations to skip redundant sorting operations, such as when
- * the newly requested sort command is the same as the active one. The caveat
- * to this optimization is that "live" edits to the value of a target's sorting
- * attribute will be ignored when requesting a re-sort by the same attribute.
- *
- * By setting to `behavior.liveSort` to `true`, the mixer will always re-sort
- * regardless of whether or not the sorting attribute and order have changed.
- *
- * @example <caption>Example: Enabling `liveSort` to allow for re-sorting</caption>
- *
- * var mixer = mixitup(containerEl, {
- * behavior: {
- * liveSort: true
- * },
- * load: {
- * sort: 'edited:desc'
- * }
- * });
- *
- * var target = containerEl.children[3];
- *
- * console.log(target.getAttribute('data-edited')); // '2015-04-24'
- *
- * target.setAttribute('data-edited', '2017-08-10'); // Update the target's edited date
- *
- * mixer.sort('edited:desc')
- * .then(function(state) {
- * // The target is now at the top of the list
- *
- * console.log(state.targets[0] === target); // true
- * });
- *
- * @name liveSort
- * @memberof mixitup.Config.behavior
- * @instance
- * @type {boolean}
- * @default false
- */
- this.liveSort = false;
- this.callActions('afterConstruct');
- h.seal(this);
- };
- mixitup.BaseStatic.call(mixitup.ConfigBehavior);
- mixitup.ConfigBehavior.prototype = Object.create(mixitup.Base.prototype);
- mixitup.ConfigBehavior.prototype.constructor = mixitup.ConfigBehavior;
- /**
- * A group of optional callback functions to be invoked at various
- * points within the lifecycle of a mixer operation.
- *
- * Each function is analogous to an event of the same name triggered from the
- * container element, and is invoked immediately after it.
- *
- * All callback functions receive the current `state` object as their first
- * argument, as well as other more specific arguments described below.
- *
- * @constructor
- * @memberof mixitup.Config
- * @name callbacks
- * @namespace
- * @public
- * @since 2.0.0
- */
- mixitup.ConfigCallbacks = function() {
- mixitup.Base.call(this);
- this.callActions('beforeConstruct');
- /**
- * A callback function invoked immediately after any MixItUp operation is requested
- * and before animations have begun.
- *
- * A second `futureState` argument is passed to the function which represents the final
- * state of the mixer once the requested operation has completed.
- *
- * @example <caption>Example: Adding an `onMixStart` callback function</caption>
- * var mixer = mixitup(containerEl, {
- * callbacks: {
- * onMixStart: function(state, futureState) {
- * console.log('Starting operation...');
- * }
- * }
- * });
- *
- * @name onMixStart
- * @memberof mixitup.Config.callbacks
- * @instance
- * @type {function}
- * @default null
- */
- this.onMixStart = null;
- /**
- * A callback function invoked when a MixItUp operation is requested while another
- * operation is in progress, and the animation queue is full, or queueing
- * is disabled.
- *
- * @example <caption>Example: Adding an `onMixBusy` callback function</caption>
- * var mixer = mixitup(containerEl, {
- * callbacks: {
- * onMixBusy: function(state) {
- * console.log('Mixer busy');
- * }
- * }
- * });
- *
- * @name onMixBusy
- * @memberof mixitup.Config.callbacks
- * @instance
- * @type {function}
- * @default null
- */
- this.onMixBusy = null;
- /**
- * A callback function invoked after any MixItUp operation has completed, and the
- * state has been updated.
- *
- * @example <caption>Example: Adding an `onMixEnd` callback function</caption>
- * var mixer = mixitup(containerEl, {
- * callbacks: {
- * onMixEnd: function(state) {
- * console.log('Operation complete');
- * }
- * }
- * });
- *
- * @name onMixEnd
- * @memberof mixitup.Config.callbacks
- * @instance
- * @type {function}
- * @default null
- */
- this.onMixEnd = null;
- /**
- * A callback function invoked whenever an operation "fails", i.e. no targets
- * could be found matching the requested filter.
- *
- * @example <caption>Example: Adding an `onMixFail` callback function</caption>
- * var mixer = mixitup(containerEl, {
- * callbacks: {
- * onMixFail: function(state) {
- * console.log('No items could be found matching the requested filter');
- * }
- * }
- * });
- *
- * @name onMixFail
- * @memberof mixitup.Config.callbacks
- * @instance
- * @type {function}
- * @default null
- */
- this.onMixFail = null;
- /**
- * A callback function invoked whenever a MixItUp control is clicked, and before its
- * respective operation is requested.
- *
- * The clicked element is assigned to the `this` keyword within the function. The original
- * click event is passed to the function as the second argument, which can be useful if
- * using `<a>` tags as controls where the default behavior needs to be prevented.
- *
- * Returning `false` from the callback will prevent the control click from triggering
- * an operation.
- *
- * @example <caption>Example 1: Adding an `onMixClick` callback function</caption>
- * var mixer = mixitup(containerEl, {
- * callbacks: {
- * onMixClick: function(state, originalEvent) {
- * console.log('The control "' + this.innerText + '" was clicked');
- * }
- * }
- * });
- *
- * @example <caption>Example 2: Using `onMixClick` to manipulate the original click event</caption>
- * var mixer = mixitup(containerEl, {
- * callbacks: {
- * onMixClick: function(state, originalEvent) {
- * // Prevent original click event from bubbling up:
- * originalEvent.stopPropagation();
- *
- * // Prevent default behavior of clicked element:
- * originalEvent.preventDefault();
- * }
- * }
- * });
- *
- * @example <caption>Example 3: Using `onMixClick` to conditionally cancel operations</caption>
- * var mixer = mixitup(containerEl, {
- * callbacks: {
- * onMixClick: function(state, originalEvent) {
- * // Perform some conditional check:
- *
- * if (myApp.isLoading) {
- * // By returning false, we can prevent the control click from triggering an operation.
- *
- * return false;
- * }
- * }
- * }
- * });
- *
- * @name onMixClick
- * @memberof mixitup.Config.callbacks
- * @instance
- * @type {function}
- * @default null
- */
- this.onMixClick = null;
- this.callActions('afterConstruct');
- h.seal(this);
- };
- mixitup.BaseStatic.call(mixitup.ConfigCallbacks);
- mixitup.ConfigCallbacks.prototype = Object.create(mixitup.Base.prototype);
- mixitup.ConfigCallbacks.prototype.constructor = mixitup.ConfigCallbacks;
- /**
- * A group of properties relating to clickable control elements.
- *
- * @constructor
- * @memberof mixitup.Config
- * @name controls
- * @namespace
- * @public
- * @since 2.0.0
- */
- mixitup.ConfigControls = function() {
- mixitup.Base.call(this);
- this.callActions('beforeConstruct');
- /**
- * A boolean dictating whether or not controls should be enabled for the mixer instance.
- *
- * If `true` (default behavior), MixItUp will search the DOM for any clickable elements with
- * `data-filter`, `data-sort` or `data-toggle` attributes, and bind them for click events.
- *
- * If `false`, no click handlers will be bound, and all functionality must therefore be performed
- * via the mixer's API methods.
- *
- * If you do not intend to use the default controls, setting this property to `false` will
- * marginally improve the startup time of your mixer instance, and will also prevent any other active
- * mixer instances in the DOM which are bound to controls from controlling the instance.
- *
- * @example <caption>Example: Disabling controls</caption>
- * var mixer = mixitup(containerEl, {
- * controls: {
- * enable: false
- * }
- * });
- *
- * // With the default controls disabled, we can only control
- * // the mixer via its API methods, e.g.:
- *
- * mixer.filter('.cat-1');
- *
- * @name enable
- * @memberof mixitup.Config.controls
- * @instance
- * @type {boolean}
- * @default true
- */
- this.enable = true;
- /**
- * A boolean dictating whether or not to use event delegation when binding click events
- * to the default controls.
- *
- * If `false` (default behavior), each control button in the DOM will be found and
- * individually bound when a mixer is instantiated, with their corresponding actions
- * cached for performance.
- *
- * If `true`, a single click handler will be applied to the `window` (or container element - see
- * `config.controls.scope`), and any click events triggered by elements with `data-filter`,
- * `data-sort` or `data-toggle` attributes present will be handled as they propagate upwards.
- *
- * If you require a user interface where control buttons may be added, removed, or changed during the
- * lifetime of a mixer, `controls.live` should be set to `true`. There is a marginal but unavoidable
- * performance deficit when using live controls, as the value of each control button must be read
- * from the DOM in real time once the click event has propagated.
- *
- * @example <caption>Example: Setting live controls</caption>
- * var mixer = mixitup(containerEl, {
- * controls: {
- * live: true
- * }
- * });
- *
- * // Control buttons can now be added, remove and changed without breaking
- * // the mixer's UI
- *
- * @name live
- * @memberof mixitup.Config.controls
- * @instance
- * @type {boolean}
- * @default true
- */
- this.live = false;
- /**
- * A string dictating the "scope" to use when binding or querying the default controls. The available
- * values are `'global'` or `'local'`.
- *
- * When set to `'global'` (default behavior), MixItUp will query the entire document for control buttons
- * to bind, or delegate click events from (see `config.controls.live`).
- *
- * When set to `'local'`, MixItUp will only query (or bind click events to) its own container element.
- * This may be desireable if you require multiple active mixer instances within the same document, with
- * controls that would otherwise intefere with each other if scoped globally.
- *
- * Conversely, if you wish to control multiple instances with a single UI, you would create one
- * set of controls and keep the controls scope of each mixer set to `global`.
- *
- * @example <caption>Example: Setting 'local' scoped controls</caption>
- * var mixerOne = mixitup(containerOne, {
- * controls: {
- * scope: 'local'
- * }
- * });
- *
- * var mixerTwo = mixitup(containerTwo, {
- * controls: {
- * scope: 'local'
- * }
- * });
- *
- * // Both mixers can now exist within the same document with
- * // isolated controls placed within their container elements.
- *
- * @name scope
- * @memberof mixitup.Config.controls
- * @instance
- * @type {string}
- * @default 'global'
- */
- this.scope = 'global'; // enum: ['local' ,'global']
- /**
- * A string dictating the type of logic to apply when concatenating the filter selectors of
- * active toggle buttons (i.e. any clickable element with a `data-toggle` attribute).
- *
- * If set to `'or'` (default behavior), selectors will be concatenated together as
- * a comma-seperated list. For example:
- *
- * `'.cat-1, .cat-2'` (shows any elements matching `'.cat-1'` OR `'.cat-2'`)
- *
- * If set to `'and'`, selectors will be directly concatenated together. For example:
- *
- * `'.cat-1.cat-2'` (shows any elements which match both `'.cat-1'` AND `'.cat-2'`)
- *
- * @example <caption>Example: Setting "and" toggle logic</caption>
- * var mixer = mixitup(containerEl, {
- * controls: {
- * toggleLogic: 'and'
- * }
- * });
- *
- * @name toggleLogic
- * @memberof mixitup.Config.controls
- * @instance
- * @type {string}
- * @default 'or'
- */
- this.toggleLogic = 'or'; // enum: ['or', 'and']
- /**
- * A string dictating the filter behavior when all toggles are inactive.
- *
- * When set to `'all'` (default behavior), *all* targets will be shown by default
- * when no toggles are active, or at the moment all active toggles are toggled off.
- *
- * When set to `'none'`, no targets will be shown by default when no toggles are
- * active, or at the moment all active toggles are toggled off.
- *
- * @example <caption>Example 1: Setting the default toggle behavior to `'all'`</caption>
- * var mixer = mixitup(containerEl, {
- * controls: {
- * toggleDefault: 'all'
- * }
- * });
- *
- * mixer.toggleOn('.cat-2')
- * .then(function() {
- * // Deactivate all active toggles
- *
- * return mixer.toggleOff('.cat-2')
- * })
- * .then(function(state) {
- * console.log(state.activeFilter.selector); // 'all'
- * console.log(state.totalShow); // 12
- * });
- *
- * @example <caption>Example 2: Setting the default toggle behavior to `'none'`</caption>
- * var mixer = mixitup(containerEl, {
- * controls: {
- * toggleDefault: 'none'
- * }
- * });
- *
- * mixer.toggleOn('.cat-2')
- * .then(function() {
- * // Deactivate all active toggles
- *
- * return mixer.toggleOff('.cat-2')
- * })
- * .then(function(state) {
- * console.log(state.activeFilter.selector); // 'none'
- * console.log(state.totalShow); // 0
- * });
- *
- * @name toggleDefault
- * @memberof mixitup.Config.controls
- * @instance
- * @type {string}
- * @default 'all'
- */
- this.toggleDefault = 'all'; // enum: ['all', 'none']
- this.callActions('afterConstruct');
- h.seal(this);
- };
- mixitup.BaseStatic.call(mixitup.ConfigControls);
- mixitup.ConfigControls.prototype = Object.create(mixitup.Base.prototype);
- mixitup.ConfigControls.prototype.constructor = mixitup.ConfigControls;
- /**
- * A group of properties defining the output and structure of class names programmatically
- * added to controls and containers to reflect the state of the mixer.
- *
- * Most commonly, class names are added to controls by MixItUp to indicate that
- * the control is active so that it can be styled accordingly - `'mixitup-control-active'` by default.
- *
- * Using a "BEM" like structure, each classname is broken into the three parts:
- * a block namespace (`'mixitup'`), an element name (e.g. `'control'`), and an optional modifier
- * name (e.g. `'active'`) reflecting the state of the element.
- *
- * By default, each part of the classname is concatenated together using single hyphens as
- * delineators, but this can be easily customised to match the naming convention and style of
- * your project.
- *
- * @constructor
- * @memberof mixitup.Config
- * @name classNames
- * @namespace
- * @public
- * @since 3.0.0
- */
- mixitup.ConfigClassNames = function() {
- mixitup.Base.call(this);
- this.callActions('beforeConstruct');
- /**
- * The "block" portion, or top-level namespace added to the start of any class names created by MixItUp.
- *
- * @example <caption>Example 1: changing the `config.classNames.block` value</caption>
- * var mixer = mixitup(containerEl, {
- * classNames: {
- * block: 'portfolio'
- * }
- * });
- *
- * // Active control output: "portfolio-control-active"
- *
- * @example <caption>Example 2: Removing `config.classNames.block`</caption>
- * var mixer = mixitup(containerEl, {
- * classNames: {
- * block: ''
- * }
- * });
- *
- * // Active control output: "control-active"
- *
- * @name block
- * @memberof mixitup.Config.classNames
- * @instance
- * @type {string}
- * @default 'mixitup'
- */
- this.block = 'mixitup';
- /**
- * The "element" portion of the class name added to container.
- *
- * @name elementContainer
- * @memberof mixitup.Config.classNames
- * @instance
- * @type {string}
- * @default 'container'
- */
- this.elementContainer = 'container';
- /**
- * The "element" portion of the class name added to filter controls.
- *
- * By default, all filter, sort, multimix and toggle controls take the same element value of `'control'`, but
- * each type's element value can be individually overwritten to match the unique classNames of your controls as needed.
- *
- * @example <caption>Example 1: changing the `config.classNames.elementFilter` value</caption>
- * var mixer = mixitup(containerEl, {
- * classNames: {
- * elementFilter: 'filter'
- * }
- * });
- *
- * // Active filter output: "mixitup-filter-active"
- *
- * @example <caption>Example 2: changing the `config.classNames.block` and `config.classNames.elementFilter` values</caption>
- * var mixer = mixitup(containerEl, {
- * classNames: {
- * block: 'portfolio',
- * elementFilter: 'filter'
- * }
- * });
- *
- * // Active filter output: "portfolio-filter-active"
- *
- * @name elementFilter
- * @memberof mixitup.Config.classNames
- * @instance
- * @type {string}
- * @default 'control'
- */
- this.elementFilter = 'control';
- /**
- * The "element" portion of the class name added to sort controls.
- *
- * By default, all filter, sort, multimix and toggle controls take the same element value of `'control'`, but
- * each type's element value can be individually overwritten to match the unique classNames of your controls as needed.
- *
- * @example <caption>Example 1: changing the `config.classNames.elementSort` value</caption>
- * var mixer = mixitup(containerEl, {
- * classNames: {
- * elementSort: 'sort'
- * }
- * });
- *
- * // Active sort output: "mixitup-sort-active"
- *
- * @example <caption>Example 2: changing the `config.classNames.block` and `config.classNames.elementSort` values</caption>
- * var mixer = mixitup(containerEl, {
- * classNames: {
- * block: 'portfolio',
- * elementSort: 'sort'
- * }
- * });
- *
- * // Active sort output: "portfolio-sort-active"
- *
- * @name elementSort
- * @memberof mixitup.Config.classNames
- * @instance
- * @type {string}
- * @default 'control'
- */
- this.elementSort = 'control';
- /**
- * The "element" portion of the class name added to multimix controls.
- *
- * By default, all filter, sort, multimix and toggle controls take the same element value of `'control'`, but
- * each type's element value can be individually overwritten to match the unique classNames of your controls as needed.
- *
- * @example <caption>Example 1: changing the `config.classNames.elementMultimix` value</caption>
- * var mixer = mixitup(containerEl, {
- * classNames: {
- * elementMultimix: 'multimix'
- * }
- * });
- *
- * // Active multimix output: "mixitup-multimix-active"
- *
- * @example <caption>Example 2: changing the `config.classNames.block` and `config.classNames.elementMultimix` values</caption>
- * var mixer = mixitup(containerEl, {
- * classNames: {
- * block: 'portfolio',
- * elementSort: 'multimix'
- * }
- * });
- *
- * // Active multimix output: "portfolio-multimix-active"
- *
- * @name elementMultimix
- * @memberof mixitup.Config.classNames
- * @instance
- * @type {string}
- * @default 'control'
- */
- this.elementMultimix = 'control';
- /**
- * The "element" portion of the class name added to toggle controls.
- *
- * By default, all filter, sort, multimix and toggle controls take the same element value of `'control'`, but
- * each type's element value can be individually overwritten to match the unique classNames of your controls as needed.
- *
- * @example <caption>Example 1: changing the `config.classNames.elementToggle` value</caption>
- * var mixer = mixitup(containerEl, {
- * classNames: {
- * elementToggle: 'toggle'
- * }
- * });
- *
- * // Active toggle output: "mixitup-toggle-active"
- *
- * @example <caption>Example 2: changing the `config.classNames.block` and `config.classNames.elementToggle` values</caption>
- * var mixer = mixitup(containerEl, {
- * classNames: {
- * block: 'portfolio',
- * elementToggle: 'toggle'
- * }
- * });
- *
- * // Active toggle output: "portfolio-toggle-active"
- *
- * @name elementToggle
- * @memberof mixitup.Config.classNames
- * @instance
- * @type {string}
- * @default 'control'
- */
- this.elementToggle = 'control';
- /**
- * The "modifier" portion of the class name added to active controls.
- * @name modifierActive
- * @memberof mixitup.Config.classNames
- * @instance
- * @type {string}
- * @default 'active'
- */
- this.modifierActive = 'active';
- /**
- * The "modifier" portion of the class name added to disabled controls.
- *
- * @name modifierDisabled
- * @memberof mixitup.Config.classNames
- * @instance
- * @type {string}
- * @default 'disabled'
- */
- this.modifierDisabled = 'disabled';
- /**
- * The "modifier" portion of the class name added to the container when in a "failed" state.
- *
- * @name modifierFailed
- * @memberof mixitup.Config.classNames
- * @instance
- * @type {string}
- * @default 'failed'
- */
- this.modifierFailed = 'failed';
- /**
- * The delineator used between the "block" and "element" portions of any class name added by MixItUp.
- *
- * If the block portion is ommited by setting it to an empty string, no delineator will be added.
- *
- * @example <caption>Example: changing the delineator to match BEM convention</caption>
- * var mixer = mixitup(containerEl, {
- * classNames: {
- * delineatorElement: '__'
- * }
- * });
- *
- * // example active control output: "mixitup__control-active"
- *
- * @name delineatorElement
- * @memberof mixitup.Config.classNames
- * @instance
- * @type {string}
- * @default '-'
- */
- this.delineatorElement = '-';
- /**
- * The delineator used between the "element" and "modifier" portions of any class name added by MixItUp.
- *
- * If the element portion is ommited by setting it to an empty string, no delineator will be added.
- *
- * @example <caption>Example: changing both delineators to match BEM convention</caption>
- * var mixer = mixitup(containerEl, {
- * classNames: {
- * delineatorElement: '__'
- * delineatorModifier: '--'
- * }
- * });
- *
- * // Active control output: "mixitup__control--active"
- *
- * @name delineatorModifier
- * @memberof mixitup.Config.classNames
- * @instance
- * @type {string}
- * @default '-'
- */
- this.delineatorModifier = '-';
- this.callActions('afterConstruct');
- h.seal(this);
- };
- mixitup.BaseStatic.call(mixitup.ConfigClassNames);
- mixitup.ConfigClassNames.prototype = Object.create(mixitup.Base.prototype);
- mixitup.ConfigClassNames.prototype.constructor = mixitup.ConfigClassNames;
- /**
- * A group of properties relating to MixItUp's dataset API.
- *
- * @constructor
- * @memberof mixitup.Config
- * @name data
- * @namespace
- * @public
- * @since 3.0.0
- */
- mixitup.ConfigData = function() {
- mixitup.Base.call(this);
- this.callActions('beforeConstruct');
- /**
- * A string specifying the name of the key containing your data model's unique
- * identifier (UID). To use the dataset API, a UID key must be specified and
- * be present and unique on all objects in the dataset you provide to MixItUp.
- *
- * For example, if your dataset is made up of MongoDB documents, the UID
- * key would be `'id'` or `'_id'`.
- *
- * @example <caption>Example: Setting the UID to `'id'`</caption>
- * var mixer = mixitup(containerEl, {
- * data: {
- * uidKey: 'id'
- * }
- * });
- *
- * @name uidKey
- * @memberof mixitup.Config.data
- * @instance
- * @type {string}
- * @default ''
- */
- this.uidKey = '';
- /**
- * A boolean dictating whether or not MixItUp should "dirty check" each object in
- * your dataset for changes whenever `.dataset()` is called, and re-render any targets
- * for which a change is found.
- *
- * Depending on the complexity of your data model, dirty checking can be expensive
- * and is therefore disabled by default.
- *
- * NB: For changes to be detected, a new immutable instance of the edited model must be
- * provided to mixitup, rather than manipulating properties on the existing instance.
- * If your changes are a result of a DB write and read, you will most likely be calling
- * `.dataset()` with a clean set of objects each time, so this will not be an issue.
- *
- * @example <caption>Example: Enabling dirty checking</caption>
- *
- * var myDataset = [
- * {
- * id: 0,
- * title: "Blog Post Title 0"
- * ...
- * },
- * {
- * id: 1,
- * title: "Blog Post Title 1"
- * ...
- * }
- * ];
- *
- * // Instantiate a mixer with a pre-loaded dataset, and a target renderer
- * // function defined
- *
- * var mixer = mixitup(containerEl, {
- * data: {
- * uidKey: 'id',
- * dirtyCheck: true
- * },
- * load: {
- * dataset: myDataset
- * },
- * render: {
- * target: function() { ... }
- * }
- * });
- *
- * // For illustration, we will clone and edit the second object in the dataset.
- * // NB: this would typically be done server-side in response to a DB update,
- * and then re-queried via an API.
- *
- * myDataset[1] = Object.assign({}, myDataset[1]);
- *
- * myDataset[1].title = 'Blog Post Title 11';
- *
- * mixer.dataset(myDataset)
- * .then(function() {
- * // the target with ID "1", will be re-rendered reflecting its new title
- * });
- *
- * @name dirtyCheck
- * @memberof mixitup.Config.data
- * @instance
- * @type {boolean}
- * @default false
- */
- this.dirtyCheck = false;
- this.callActions('afterConstruct');
- h.seal(this);
- };
- mixitup.BaseStatic.call(mixitup.ConfigData);
- mixitup.ConfigData.prototype = Object.create(mixitup.Base.prototype);
- mixitup.ConfigData.prototype.constructor = mixitup.ConfigData;
- /**
- * A group of properties allowing the toggling of various debug features.
- *
- * @constructor
- * @memberof mixitup.Config
- * @name debug
- * @namespace
- * @public
- * @since 3.0.0
- */
- mixitup.ConfigDebug = function() {
- mixitup.Base.call(this);
- this.callActions('beforeConstruct');
- /**
- * A boolean dictating whether or not the mixer instance returned by the
- * `mixitup()` factory function should expose private properties and methods.
- *
- * By default, mixer instances only expose their public API, but enabling
- * debug mode will give you access to various mixer internals which may aid
- * in debugging, or the authoring of extensions.
- *
- * @example <caption>Example: Enabling debug mode</caption>
- *
- * var mixer = mixitup(containerEl, {
- * debug: {
- * enable: true
- * }
- * });
- *
- * // Private properties and methods will now be visible on the mixer instance:
- *
- * console.log(mixer);
- *
- * @name enable
- * @memberof mixitup.Config.debug
- * @instance
- * @type {boolean}
- * @default false
- */
- this.enable = false;
- /**
- * A boolean dictating whether or not warnings should be shown when various
- * common gotchas occur.
- *
- * Warnings are intended to provide insights during development when something
- * occurs that is not a fatal, but may indicate an issue with your integration,
- * and are therefore turned on by default. However, you may wish to disable
- * them in production.
- *
- * @example <caption>Example 1: Disabling warnings</caption>
- *
- * var mixer = mixitup(containerEl, {
- * debug: {
- * showWarnings: false
- * }
- * });
- *
- * @example <caption>Example 2: Disabling warnings based on environment</caption>
- *
- * var showWarnings = myAppConfig.environment === 'development' ? true : false;
- *
- * var mixer = mixitup(containerEl, {
- * debug: {
- * showWarnings: showWarnings
- * }
- * });
- *
- * @name showWarnings
- * @memberof mixitup.Config.debug
- * @instance
- * @type {boolean}
- * @default true
- */
- this.showWarnings = true;
- /**
- * Used for server-side testing only.
- *
- * @private
- * @name fauxAsync
- * @memberof mixitup.Config.debug
- * @instance
- * @type {boolean}
- * @default false
- */
- this.fauxAsync = false;
- this.callActions('afterConstruct');
- h.seal(this);
- };
- mixitup.BaseStatic.call(mixitup.ConfigDebug);
- mixitup.ConfigDebug.prototype = Object.create(mixitup.Base.prototype);
- mixitup.ConfigDebug.prototype.constructor = mixitup.ConfigDebug;
- /**
- * A group of properties relating to the layout of the container.
- *
- * @constructor
- * @memberof mixitup.Config
- * @name layout
- * @namespace
- * @public
- * @since 3.0.0
- */
- mixitup.ConfigLayout = function() {
- mixitup.Base.call(this);
- this.callActions('beforeConstruct');
- /**
- * A boolean dictating whether or not mixitup should query all descendants
- * of the container for targets, or only immediate children.
- *
- * By default, mixitup will query all descendants matching the
- * `selectors.target` selector when indexing targets upon instantiation.
- * This allows for targets to be nested inside a sub-container which is
- * useful when ring-fencing targets from locally scoped controls in your
- * markup (see `controls.scope`).
- *
- * However, if you are building a more complex UI requiring the nesting
- * of mixers within mixers, you will most likely want to limit targets to
- * immediate children of the container by setting this property to `false`.
- *
- * @example <caption>Example: Restricting targets to immediate children</caption>
- *
- * var mixer = mixitup(containerEl, {
- * layout: {
- * allowNestedTargets: false
- * }
- * });
- *
- * @name allowNestedTargets
- * @memberof mixitup.Config.layout
- * @instance
- * @type {boolean}
- * @default true
- */
- this.allowNestedTargets = true;
- /**
- * A string specifying an optional class name to apply to the container when in
- * its default state.
- *
- * By changing this class name or adding a class name to the container via the
- * `.changeLayout()` API method, the CSS layout of the container can be changed,
- * and MixItUp will attemp to gracefully animate the container and its targets
- * between states.
- *
- * @example <caption>Example 1: Specifying a container class name</caption>
- *
- * var mixer = mixitup(containerEl, {
- * layout: {
- * containerClassName: 'grid'
- * }
- * });
- *
- * @example <caption>Example 2: Changing the default class name with `.changeLayout()`</caption>
- *
- * var mixer = mixitup(containerEl, {
- * layout: {
- * containerClassName: 'grid'
- * }
- * });
- *
- * mixer.changeLayout('list')
- * .then(function(state) {
- * console.log(state.activeContainerClass); // "list"
- * });
- *
- * @name containerClassName
- * @memberof mixitup.Config.layout
- * @instance
- * @type {string}
- * @default ''
- */
- this.containerClassName = '';
- /**
- * A reference to a non-target sibling element after which to insert targets
- * when there are no targets in the container.
- *
- * @example <caption>Example: Setting a `siblingBefore` reference element</caption>
- *
- * var addButton = containerEl.querySelector('button');
- *
- * var mixer = mixitup(containerEl, {
- * layout: {
- * siblingBefore: addButton
- * }
- * });
- *
- * @name siblingBefore
- * @memberof mixitup.Config.layout
- * @instance
- * @type {HTMLElement}
- * @default null
- */
- this.siblingBefore = null;
- /**
- * A reference to a non-target sibling element before which to insert targets
- * when there are no targets in the container.
- *
- * @example <caption>Example: Setting an `siblingAfter` reference element</caption>
- *
- * var gap = containerEl.querySelector('.gap');
- *
- * var mixer = mixitup(containerEl, {
- * layout: {
- * siblingAfter: gap
- * }
- * });
- *
- * @name siblingAfter
- * @memberof mixitup.Config.layout
- * @instance
- * @type {HTMLElement}
- * @default null
- */
- this.siblingAfter = null;
- this.callActions('afterConstruct');
- h.seal(this);
- };
- mixitup.BaseStatic.call(mixitup.ConfigLayout);
- mixitup.ConfigLayout.prototype = Object.create(mixitup.Base.prototype);
- mixitup.ConfigLayout.prototype.constructor = mixitup.ConfigLayout;
- /**
- * A group of properties defining the initial state of the mixer on load (instantiation).
- *
- * @constructor
- * @memberof mixitup.Config
- * @name load
- * @namespace
- * @public
- * @since 2.0.0
- */
- mixitup.ConfigLoad = function() {
- mixitup.Base.call(this);
- this.callActions('beforeConstruct');
- /**
- * A string defining any filtering to be statically applied to the mixer on load.
- * As per the `.filter()` API, this can be any valid selector string, or the
- * values `'all'` or `'none'`.
- *
- * @example <caption>Example 1: Defining an initial filter selector to be applied on load</caption>
- *
- * // The mixer will show only those targets matching '.category-a' on load.
- *
- * var mixer = mixitup(containerEl, {
- * load: {
- * filter: '.category-a'
- * }
- * });
- *
- * @example <caption>Example 2: Hiding all targets on load</caption>
- *
- * // The mixer will show hide all targets on load.
- *
- * var mixer = mixitup(containerEl, {
- * load: {
- * filter: 'none'
- * }
- * });
- *
- * @name filter
- * @memberof mixitup.Config.load
- * @instance
- * @type {string}
- * @default 'all'
- */
- this.filter = 'all';
- /**
- * A string defining any sorting to be statically applied to the mixer on load.
- * As per the `.sort()` API, this should be a valid "sort string" made up of
- * an attribute to sort by (or `'default'`) followed by an optional sorting
- * order, or the value `'random'`;
- *
- * @example <caption>Example: Defining sorting to be applied on load</caption>
- *
- * // The mixer will sort the container by the value of the `data-published-date`
- * // attribute, in descending order.
- *
- * var mixer = mixitup(containerEl, {
- * load: {
- * sort: 'published-date:desc'
- * }
- * });
- *
- * @name sort
- * @memberof mixitup.Config.load
- * @instance
- * @type {string}
- * @default 'default:asc'
- */
- this.sort = 'default:asc';
- /**
- * An array of objects representing the underlying data of any pre-rendered targets,
- * when using the `.dataset()` API.
- *
- * NB: If targets are pre-rendered when the mixer is instantiated, this must be set.
- *
- * @example <caption>Example: Defining the initial underyling dataset</caption>
- *
- * var myDataset = [
- * {
- * id: 0,
- * title: "Blog Post Title 0",
- * ...
- * },
- * {
- * id: 1,
- * title: "Blog Post Title 1",
- * ...
- * }
- * ];
- *
- * var mixer = mixitup(containerEl, {
- * data: {
- * uidKey: 'id'
- * },
- * load: {
- * dataset: myDataset
- * }
- * });
- *
- * @name dataset
- * @memberof mixitup.Config.load
- * @instance
- * @type {Array.<object>}
- * @default null
- */
- this.dataset = null;
- this.callActions('afterConstruct');
- h.seal(this);
- };
- mixitup.BaseStatic.call(mixitup.ConfigLoad);
- mixitup.ConfigLoad.prototype = Object.create(mixitup.Base.prototype);
- mixitup.ConfigLoad.prototype.constructor = mixitup.ConfigLoad;
- /**
- * A group of properties defining the selectors used to query elements within a mixitup container.
- *
- * @constructor
- * @memberof mixitup.Config
- * @name selectors
- * @namespace
- * @public
- * @since 3.0.0
- */
- mixitup.ConfigSelectors = function() {
- mixitup.Base.call(this);
- this.callActions('beforeConstruct');
- /**
- * A selector string used to query and index target elements within the container.
- *
- * By default, the class selector `'.mix'` is used, but this can be changed to an
- * attribute or element selector to match the style of your project.
- *
- * @example <caption>Example 1: Changing the target selector</caption>
- *
- * var mixer = mixitup(containerEl, {
- * selectors: {
- * target: '.portfolio-item'
- * }
- * });
- *
- * @example <caption>Example 2: Using an attribute selector as a target selector</caption>
- *
- * // The mixer will search for any children with the attribute `data-ref="mix"`
- *
- * var mixer = mixitup(containerEl, {
- * selectors: {
- * target: '[data-ref="mix"]'
- * }
- * });
- *
- * @name target
- * @memberof mixitup.Config.selectors
- * @instance
- * @type {string}
- * @default '.mix'
- */
- this.target = '.mix';
- /**
- * A optional selector string used to add further specificity to the querying of control elements,
- * in addition to their mandatory data attribute (e.g. `data-filter`, `data-toggle`, `data-sort`).
- *
- * This can be used if other elements in your document must contain the above attributes
- * (e.g. for use in third-party scripts), and would otherwise interfere with MixItUp. Adding
- * an additional `control` selector of your choice allows MixItUp to restrict event handling
- * to only those elements matching the defined selector.
- *
- * @name control
- * @memberof mixitup.Config.selectors
- * @instance
- * @type {string}
- * @default ''
- *
- * @example <caption>Example 1: Adding a `selectors.control` selector</caption>
- *
- * var mixer = mixitup(containerEl, {
- * selectors: {
- * control: '.mixitup-control'
- * }
- * });
- *
- * // Will not be handled:
- * // <button data-filter=".category-a"></button>
- *
- * // Will be handled:
- * // <button class="mixitup-control" data-filter=".category-a"></button>
- */
- this.control = '';
- this.callActions('afterConstruct');
- h.seal(this);
- };
- mixitup.BaseStatic.call(mixitup.ConfigSelectors);
- mixitup.ConfigSelectors.prototype = Object.create(mixitup.Base.prototype);
- mixitup.ConfigSelectors.prototype.constructor = mixitup.ConfigSelectors;
- /**
- * A group of optional render functions for creating and updating elements.
- *
- * All render functions receive a data object, and should return a valid HTML string.
- *
- * @constructor
- * @memberof mixitup.Config
- * @name render
- * @namespace
- * @public
- * @since 3.0.0
- */
- mixitup.ConfigRender = function() {
- mixitup.Base.call(this);
- this.callActions('beforeConstruct');
- /**
- * A function returning an HTML string representing a target element, or a reference to a
- * single DOM element.
- *
- * The function is invoked as part of the `.dataset()` API, whenever a new item is added
- * to the dataset, or an item in the dataset changes (if `dataset.dirtyCheck` is enabled).
- *
- * The function receives the relevant dataset item as its first parameter.
- *
- * @example <caption>Example 1: Using string concatenation</caption>
- *
- * var mixer = mixitup(containerEl, {
- * render: {
- * target: function(item) {
- * return (
- * '<div class="mix">' +
- * '<h2>' + item.title + '</h2>' +
- * '</div>'
- * );
- * }
- * }
- * });
- *
- * @example <caption>Example 2: Using an ES2015 template literal</caption>
- *
- * var mixer = mixitup(containerEl, {
- * render: {
- * target: function(item) {
- * return (
- * `<div class="mix">
- * <h2>${item.title}</h2>
- * </div>`
- * );
- * }
- * }
- * });
- *
- * @example <caption>Example 3: Using a Handlebars template</caption>
- *
- * var targetTemplate = Handlebars.compile('<div class="mix"><h2>{{title}}</h2></div>');
- *
- * var mixer = mixitup(containerEl, {
- * render: {
- * target: targetTemplate
- * }
- * });
- *
- * @example <caption>Example 4: Returning a DOM element</caption>
- *
- * var mixer = mixitup(containerEl, {
- * render: {
- * target: function(item) {
- * // Create a single element using your framework's built-in renderer
- *
- * var el = ...
- *
- * return el;
- * }
- * }
- * });
- *
- * @name target
- * @memberof mixitup.Config.render
- * @instance
- * @type {function}
- * @default 'null'
- */
- this.target = null;
- this.callActions('afterConstruct');
- h.seal(this);
- };
- mixitup.BaseStatic.call(mixitup.ConfigRender);
- mixitup.ConfigRender.prototype = Object.create(mixitup.Base.prototype);
- mixitup.ConfigRender.prototype.constructor = mixitup.ConfigRender;
- /**
- * @constructor
- * @memberof mixitup
- * @private
- * @since 3.0.0
- */
- mixitup.ConfigTemplates = function() {
- mixitup.Base.call(this);
- this.callActions('beforeConstruct');
- this.callActions('afterConstruct');
- h.seal(this);
- };
- mixitup.BaseStatic.call(mixitup.ConfigTemplates);
- mixitup.ConfigTemplates.prototype = Object.create(mixitup.Base.prototype);
- mixitup.ConfigTemplates.prototype.constructor = mixitup.ConfigTemplates;
- /**
- * `mixitup.Config` is an interface used for customising the functionality of a
- * mixer instance. It is organised into several semantically distinct sub-objects,
- * each one pertaining to a particular aspect of MixItUp functionality.
- *
- * An object literal containing any or all of the available properies,
- * known as the "configuration object", can be passed as the second parameter to
- * the `mixitup` factory function when creating a mixer instance to customise its
- * functionality as needed.
- *
- * If no configuration object is passed, the mixer instance will take on the default
- * configuration values detailed below.
- *
- * @example <caption>Example 1: Creating and passing the configuration object</caption>
- * // Create a configuration object with desired values
- *
- * var config = {
- * animation: {
- * enable: false
- * },
- * selectors: {
- * target: '.item'
- * }
- * };
- *
- * // Pass the configuration object to the mixitup factory function
- *
- * var mixer = mixitup(containerEl, config);
- *
- * @example <caption>Example 2: Passing the configuration object inline</caption>
- * // Typically, the configuration object is passed inline for brevity.
- *
- * var mixer = mixitup(containerEl, {
- * controls: {
- * live: true,
- * toggleLogic: 'and'
- * }
- * });
- *
- *
- * @constructor
- * @memberof mixitup
- * @namespace
- * @public
- * @since 2.0.0
- */
- mixitup.Config = function() {
- mixitup.Base.call(this);
- this.callActions('beforeConstruct');
- this.animation = new mixitup.ConfigAnimation();
- this.behavior = new mixitup.ConfigBehavior();
- this.callbacks = new mixitup.ConfigCallbacks();
- this.controls = new mixitup.ConfigControls();
- this.classNames = new mixitup.ConfigClassNames();
- this.data = new mixitup.ConfigData();
- this.debug = new mixitup.ConfigDebug();
- this.layout = new mixitup.ConfigLayout();
- this.load = new mixitup.ConfigLoad();
- this.selectors = new mixitup.ConfigSelectors();
- this.render = new mixitup.ConfigRender();
- this.templates = new mixitup.ConfigTemplates();
- this.callActions('afterConstruct');
- h.seal(this);
- };
- mixitup.BaseStatic.call(mixitup.Config);
- mixitup.Config.prototype = Object.create(mixitup.Base.prototype);
- mixitup.Config.prototype.constructor = mixitup.Config;
- /**
- * @constructor
- * @memberof mixitup
- * @private
- * @since 3.0.0
- */
- mixitup.MixerDom = function() {
- mixitup.Base.call(this);
- this.callActions('beforeConstruct');
- this.document = null;
- this.body = null;
- this.container = null;
- this.parent = null;
- this.targets = [];
- this.callActions('afterConstruct');
- h.seal(this);
- };
- mixitup.BaseStatic.call(mixitup.MixerDom);
- mixitup.MixerDom.prototype = Object.create(mixitup.Base.prototype);
- mixitup.MixerDom.prototype.constructor = mixitup.MixerDom;
- /**
- * @constructor
- * @memberof mixitup
- * @private
- * @since 3.0.0
- */
- mixitup.UiClassNames = function() {
- mixitup.Base.call(this);
- this.callActions('beforeConstruct');
- this.base = '';
- this.active = '';
- this.disabled = '';
- this.callActions('afterConstruct');
- h.seal(this);
- };
- mixitup.BaseStatic.call(mixitup.UiClassNames);
- mixitup.UiClassNames.prototype = Object.create(mixitup.Base.prototype);
- mixitup.UiClassNames.prototype.constructor = mixitup.UiClassNames;
- /**
- * An object into which all arbitrary arguments sent to '.dataset()' are mapped.
- *
- * @constructor
- * @memberof mixitup
- * @private
- * @since 3.0.0
- */
- mixitup.CommandDataset = function() {
- mixitup.Base.call(this);
- this.callActions('beforeConstruct');
- this.dataset = null;
- this.callActions('afterConstruct');
- h.seal(this);
- };
- mixitup.BaseStatic.call(mixitup.CommandDataset);
- mixitup.CommandDataset.prototype = Object.create(mixitup.Base.prototype);
- mixitup.CommandDataset.prototype.constructor = mixitup.CommandDataset;
- /**
- * An object into which all arbitrary arguments sent to '.multimix()' are mapped.
- *
- * @constructor
- * @memberof mixitup
- * @private
- * @since 3.0.0
- */
- mixitup.CommandMultimix = function() {
- mixitup.Base.call(this);
- this.callActions('beforeConstruct');
- this.filter = null;
- this.sort = null;
- this.insert = null;
- this.remove = null;
- this.changeLayout = null;
- this.callActions('afterConstruct');
- h.seal(this);
- };
- mixitup.BaseStatic.call(mixitup.CommandMultimix);
- mixitup.CommandMultimix.prototype = Object.create(mixitup.Base.prototype);
- mixitup.CommandMultimix.prototype.constructor = mixitup.CommandMultimix;
- /**
- * An object into which all arbitrary arguments sent to '.filter()' are mapped.
- *
- * @constructor
- * @memberof mixitup
- * @private
- * @since 3.0.0
- */
- mixitup.CommandFilter = function() {
- mixitup.Base.call(this);
- this.callActions('beforeConstruct');
- this.selector = '';
- this.collection = null;
- this.action = 'show'; // enum: ['show', 'hide']
- this.callActions('afterConstruct');
- h.seal(this);
- };
- mixitup.BaseStatic.call(mixitup.CommandFilter);
- mixitup.CommandFilter.prototype = Object.create(mixitup.Base.prototype);
- mixitup.CommandFilter.prototype.constructor = mixitup.CommandFilter;
- /**
- * An object into which all arbitrary arguments sent to '.sort()' are mapped.
- *
- * @constructor
- * @memberof mixitup
- * @private
- * @since 3.0.0
- */
- mixitup.CommandSort = function() {
- mixitup.Base.call(this);
- this.callActions('beforeConstruct');
- this.sortString = '';
- this.attribute = '';
- this.order = 'asc';
- this.collection = null;
- this.next = null;
- this.callActions('afterConstruct');
- h.seal(this);
- };
- mixitup.BaseStatic.call(mixitup.CommandSort);
- mixitup.CommandSort.prototype = Object.create(mixitup.Base.prototype);
- mixitup.CommandSort.prototype.constructor = mixitup.CommandSort;
- /**
- * An object into which all arbitrary arguments sent to '.insert()' are mapped.
- *
- * @constructor
- * @memberof mixitup
- * @private
- * @since 3.0.0
- */
- mixitup.CommandInsert = function() {
- mixitup.Base.call(this);
- this.callActions('beforeConstruct');
- this.index = 0;
- this.collection = [];
- this.position = 'before'; // enum: ['before', 'after']
- this.sibling = null;
- this.callActions('afterConstruct');
- h.seal(this);
- };
- mixitup.BaseStatic.call(mixitup.CommandInsert);
- mixitup.CommandInsert.prototype = Object.create(mixitup.Base.prototype);
- mixitup.CommandInsert.prototype.constructor = mixitup.CommandInsert;
- /**
- * An object into which all arbitrary arguments sent to '.remove()' are mapped.
- *
- * @constructor
- * @memberof mixitup
- * @private
- * @since 3.0.0
- */
- mixitup.CommandRemove = function() {
- mixitup.Base.call(this);
- this.callActions('beforeConstruct');
- this.targets = [];
- this.collection = [];
- this.callActions('afterConstruct');
- h.seal(this);
- };
- mixitup.BaseStatic.call(mixitup.CommandRemove);
- mixitup.CommandRemove.prototype = Object.create(mixitup.Base.prototype);
- mixitup.CommandRemove.prototype.constructor = mixitup.CommandRemove;
- /**
- * An object into which all arbitrary arguments sent to '.changeLayout()' are mapped.
- *
- * @constructor
- * @memberof mixitup
- * @private
- * @since 3.0.0
- */
- mixitup.CommandChangeLayout = function() {
- mixitup.Base.call(this);
- this.callActions('beforeConstruct');
- this.containerClassName = '';
- this.callActions('afterConstruct');
- h.seal(this);
- };
- mixitup.BaseStatic.call(mixitup.CommandChangeLayout);
- mixitup.CommandChangeLayout.prototype = Object.create(mixitup.Base.prototype);
- mixitup.CommandChangeLayout.prototype.constructor = mixitup.CommandChangeLayout;
- /**
- * @constructor
- * @memberof mixitup
- * @private
- * @since 3.0.0
- * @param {string} type
- * @param {string} selector
- * @param {boolean} [live]
- * @param {string} [parent]
- * An optional string representing the name of the mixer.dom property containing a reference to a parent element.
- */
- mixitup.ControlDefinition = function(type, selector, live, parent) {
- mixitup.Base.call(this);
- this.callActions('beforeConstruct');
- this.type = type;
- this.selector = selector;
- this.live = live || false;
- this.parent = parent || '';
- this.callActions('afterConstruct');
- h.freeze(this);
- h.seal(this);
- };
- mixitup.BaseStatic.call(mixitup.ControlDefinition);
- mixitup.ControlDefinition.prototype = Object.create(mixitup.Base.prototype);
- mixitup.ControlDefinition.prototype.constructor = mixitup.ControlDefinition;
- mixitup.controlDefinitions = [];
- mixitup.controlDefinitions.push(new mixitup.ControlDefinition('multimix', '[data-filter][data-sort]'));
- mixitup.controlDefinitions.push(new mixitup.ControlDefinition('filter', '[data-filter]'));
- mixitup.controlDefinitions.push(new mixitup.ControlDefinition('sort', '[data-sort]'));
- mixitup.controlDefinitions.push(new mixitup.ControlDefinition('toggle', '[data-toggle]'));
- /**
- * @constructor
- * @memberof mixitup
- * @private
- * @since 3.0.0
- */
- mixitup.Control = function() {
- mixitup.Base.call(this);
- this.callActions('beforeConstruct');
- this.el = null;
- this.selector = '';
- this.bound = [];
- this.pending = -1;
- this.type = '';
- this.status = 'inactive'; // enum: ['inactive', 'active', 'disabled', 'live']
- this.filter = '';
- this.sort = '';
- this.canDisable = false;
- this.handler = null;
- this.classNames = new mixitup.UiClassNames();
- this.callActions('afterConstruct');
- h.seal(this);
- };
- mixitup.BaseStatic.call(mixitup.Control);
- mixitup.Control.prototype = Object.create(mixitup.Base.prototype);
- h.extend(mixitup.Control.prototype,
- /** @lends mixitup.Control */
- {
- constructor: mixitup.Control,
- /**
- * @private
- * @param {HTMLElement} el
- * @param {string} type
- * @param {string} selector
- */
- init: function(el, type, selector) {
- var self = this;
- this.callActions('beforeInit', arguments);
- self.el = el;
- self.type = type;
- self.selector = selector;
- if (self.selector) {
- self.status = 'live';
- } else {
- self.canDisable = typeof self.el.disable === 'boolean';
- switch (self.type) {
- case 'filter':
- self.filter = self.el.getAttribute('data-filter');
- break;
- case 'toggle':
- self.filter = self.el.getAttribute('data-toggle');
- break;
- case 'sort':
- self.sort = self.el.getAttribute('data-sort');
- break;
- case 'multimix':
- self.filter = self.el.getAttribute('data-filter');
- self.sort = self.el.getAttribute('data-sort');
- break;
- }
- }
- self.bindClick();
- mixitup.controls.push(self);
- this.callActions('afterInit', arguments);
- },
- /**
- * @private
- * @param {mixitup.Mixer} mixer
- * @return {boolean}
- */
- isBound: function(mixer) {
- var self = this,
- isBound = false;
- this.callActions('beforeIsBound', arguments);
- isBound = self.bound.indexOf(mixer) > -1;
- return self.callFilters('afterIsBound', isBound, arguments);
- },
- /**
- * @private
- * @param {mixitup.Mixer} mixer
- * @return {void}
- */
- addBinding: function(mixer) {
- var self = this;
- this.callActions('beforeAddBinding', arguments);
- if (!self.isBound()) {
- self.bound.push(mixer);
- }
- this.callActions('afterAddBinding', arguments);
- },
- /**
- * @private
- * @param {mixitup.Mixer} mixer
- * @return {void}
- */
- removeBinding: function(mixer) {
- var self = this,
- removeIndex = -1;
- this.callActions('beforeRemoveBinding', arguments);
- if ((removeIndex = self.bound.indexOf(mixer)) > -1) {
- self.bound.splice(removeIndex, 1);
- }
- if (self.bound.length < 1) {
- // No bindings exist, unbind event click handlers
- self.unbindClick();
- // Remove from `mixitup.controls` list
- removeIndex = mixitup.controls.indexOf(self);
- mixitup.controls.splice(removeIndex, 1);
- if (self.status === 'active') {
- self.renderStatus(self.el, 'inactive');
- }
- }
- this.callActions('afterRemoveBinding', arguments);
- },
- /**
- * @private
- * @return {void}
- */
- bindClick: function() {
- var self = this;
- this.callActions('beforeBindClick', arguments);
- self.handler = function(e) {
- self.handleClick(e);
- };
- h.on(self.el, 'click', self.handler);
- this.callActions('afterBindClick', arguments);
- },
- /**
- * @private
- * @return {void}
- */
- unbindClick: function() {
- var self = this;
- this.callActions('beforeUnbindClick', arguments);
- h.off(self.el, 'click', self.handler);
- self.handler = null;
- this.callActions('afterUnbindClick', arguments);
- },
- /**
- * @private
- * @param {MouseEvent} e
- * @return {void}
- */
- handleClick: function(e) {
- var self = this,
- button = null,
- mixer = null,
- isActive = false,
- returnValue = void(0),
- command = {},
- clone = null,
- commands = [],
- i = -1;
- this.callActions('beforeHandleClick', arguments);
- this.pending = 0;
- mixer = self.bound[0];
- if (!self.selector) {
- button = self.el;
- } else {
- button = h.closestParent(e.target, mixer.config.selectors.control + self.selector, true, mixer.dom.document);
- }
- if (!button) {
- self.callActions('afterHandleClick', arguments);
- return;
- }
- switch (self.type) {
- case 'filter':
- command.filter = self.filter || button.getAttribute('data-filter');
- break;
- case 'sort':
- command.sort = self.sort || button.getAttribute('data-sort');
- break;
- case 'multimix':
- command.filter = self.filter || button.getAttribute('data-filter');
- command.sort = self.sort || button.getAttribute('data-sort');
- break;
- case 'toggle':
- command.filter = self.filter || button.getAttribute('data-toggle');
- if (self.status === 'live') {
- isActive = h.hasClass(button, self.classNames.active);
- } else {
- isActive = self.status === 'active';
- }
- break;
- }
- for (i = 0; i < self.bound.length; i++) {
- // Create a clone of the command for each bound mixer instance
- clone = new mixitup.CommandMultimix();
- h.extend(clone, command);
- commands.push(clone);
- }
- commands = self.callFilters('commandsHandleClick', commands, arguments);
- self.pending = self.bound.length;
- for (i = 0; mixer = self.bound[i]; i++) {
- command = commands[i];
- if (!command) {
- // An extension may set a command null to indicate that the click should not be handled
- continue;
- }
- if (!mixer.lastClicked) {
- mixer.lastClicked = button;
- }
- mixitup.events.fire('mixClick', mixer.dom.container, {
- state: mixer.state,
- instance: mixer,
- originalEvent: e,
- control: mixer.lastClicked
- }, mixer.dom.document);
- if (typeof mixer.config.callbacks.onMixClick === 'function') {
- returnValue = mixer.config.callbacks.onMixClick.call(mixer.lastClicked, mixer.state, e, mixer);
- if (returnValue === false) {
- // User has returned `false` from the callback, so do not handle click
- continue;
- }
- }
- if (self.type === 'toggle') {
- isActive ? mixer.toggleOff(command.filter) : mixer.toggleOn(command.filter);
- } else {
- mixer.multimix(command);
- }
- }
- this.callActions('afterHandleClick', arguments);
- },
- /**
- * @param {object} command
- * @param {Array<string>} toggleArray
- * @return {void}
- */
- update: function(command, toggleArray) {
- var self = this,
- actions = new mixitup.CommandMultimix();
- self.callActions('beforeUpdate', arguments);
- self.pending--;
- self.pending = Math.max(0, self.pending);
- if (self.pending > 0) return;
- if (self.status === 'live') {
- // Live control (status unknown)
- self.updateLive(command, toggleArray);
- } else {
- // Static control
- actions.sort = self.sort;
- actions.filter = self.filter;
- self.callFilters('actionsUpdate', actions, arguments);
- self.parseStatusChange(self.el, command, actions, toggleArray);
- }
- self.callActions('afterUpdate', arguments);
- },
- /**
- * @param {mixitup.CommandMultimix} command
- * @param {Array<string>} toggleArray
- * @return {void}
- */
- updateLive: function(command, toggleArray) {
- var self = this,
- controlButtons = null,
- actions = null,
- button = null,
- i = -1;
- self.callActions('beforeUpdateLive', arguments);
- if (!self.el) return;
- controlButtons = self.el.querySelectorAll(self.selector);
- for (i = 0; button = controlButtons[i]; i++) {
- actions = new mixitup.CommandMultimix();
- switch (self.type) {
- case 'filter':
- actions.filter = button.getAttribute('data-filter');
- break;
- case 'sort':
- actions.sort = button.getAttribute('data-sort');
- break;
- case 'multimix':
- actions.filter = button.getAttribute('data-filter');
- actions.sort = button.getAttribute('data-sort');
- break;
- case 'toggle':
- actions.filter = button.getAttribute('data-toggle');
- break;
- }
- actions = self.callFilters('actionsUpdateLive', actions, arguments);
- self.parseStatusChange(button, command, actions, toggleArray);
- }
- self.callActions('afterUpdateLive', arguments);
- },
- /**
- * @param {HTMLElement} button
- * @param {mixitup.CommandMultimix} command
- * @param {mixitup.CommandMultimix} actions
- * @param {Array<string>} toggleArray
- * @return {void}
- */
- parseStatusChange: function(button, command, actions, toggleArray) {
- var self = this,
- alias = '',
- toggle = '',
- i = -1;
- self.callActions('beforeParseStatusChange', arguments);
- switch (self.type) {
- case 'filter':
- if (command.filter === actions.filter) {
- self.renderStatus(button, 'active');
- } else {
- self.renderStatus(button, 'inactive');
- }
- break;
- case 'multimix':
- if (command.sort === actions.sort && command.filter === actions.filter) {
- self.renderStatus(button, 'active');
- } else {
- self.renderStatus(button, 'inactive');
- }
- break;
- case 'sort':
- if (command.sort.match(/:asc/g)) {
- alias = command.sort.replace(/:asc/g, '');
- }
- if (command.sort === actions.sort || alias === actions.sort) {
- self.renderStatus(button, 'active');
- } else {
- self.renderStatus(button, 'inactive');
- }
- break;
- case 'toggle':
- if (toggleArray.length < 1) self.renderStatus(button, 'inactive');
- if (command.filter === actions.filter) {
- self.renderStatus(button, 'active');
- }
- for (i = 0; i < toggleArray.length; i++) {
- toggle = toggleArray[i];
- if (toggle === actions.filter) {
- // Button matches one active toggle
- self.renderStatus(button, 'active');
- break;
- }
- self.renderStatus(button, 'inactive');
- }
- break;
- }
- self.callActions('afterParseStatusChange', arguments);
- },
- /**
- * @param {HTMLElement} button
- * @param {string} status
- * @return {void}
- */
- renderStatus: function(button, status) {
- var self = this;
- self.callActions('beforeRenderStatus', arguments);
- switch (status) {
- case 'active':
- h.addClass(button, self.classNames.active);
- h.removeClass(button, self.classNames.disabled);
- if (self.canDisable) self.el.disabled = false;
- break;
- case 'inactive':
- h.removeClass(button, self.classNames.active);
- h.removeClass(button, self.classNames.disabled);
- if (self.canDisable) self.el.disabled = false;
- break;
- case 'disabled':
- if (self.canDisable) self.el.disabled = true;
- h.addClass(button, self.classNames.disabled);
- h.removeClass(button, self.classNames.active);
- break;
- }
- if (self.status !== 'live') {
- // Update the control's status propery if not live
- self.status = status;
- }
- self.callActions('afterRenderStatus', arguments);
- }
- });
- mixitup.controls = [];
- /**
- * @constructor
- * @memberof mixitup
- * @private
- * @since 3.0.0
- */
- mixitup.StyleData = function() {
- mixitup.Base.call(this);
- this.callActions('beforeConstruct');
- this.x = 0;
- this.y = 0;
- this.top = 0;
- this.right = 0;
- this.bottom = 0;
- this.left = 0;
- this.width = 0;
- this.height = 0;
- this.marginRight = 0;
- this.marginBottom = 0;
- this.opacity = 0;
- this.scale = new mixitup.TransformData();
- this.translateX = new mixitup.TransformData();
- this.translateY = new mixitup.TransformData();
- this.translateZ = new mixitup.TransformData();
- this.rotateX = new mixitup.TransformData();
- this.rotateY = new mixitup.TransformData();
- this.rotateZ = new mixitup.TransformData();
- this.callActions('afterConstruct');
- h.seal(this);
- };
- mixitup.BaseStatic.call(mixitup.StyleData);
- mixitup.StyleData.prototype = Object.create(mixitup.Base.prototype);
- mixitup.StyleData.prototype.constructor = mixitup.StyleData;
- /**
- * @constructor
- * @memberof mixitup
- * @private
- * @since 3.0.0
- */
- mixitup.TransformData = function() {
- mixitup.Base.call(this);
- this.callActions('beforeConstruct');
- this.value = 0;
- this.unit = '';
- this.callActions('afterConstruct');
- h.seal(this);
- };
- mixitup.BaseStatic.call(mixitup.TransformData);
- mixitup.TransformData.prototype = Object.create(mixitup.Base.prototype);
- mixitup.TransformData.prototype.constructor = mixitup.TransformData;
- /**
- * @constructor
- * @memberof mixitup
- * @private
- * @since 3.0.0
- */
- mixitup.TransformDefaults = function() {
- mixitup.StyleData.apply(this);
- this.callActions('beforeConstruct');
- this.scale.value = 0.01;
- this.scale.unit = '';
- this.translateX.value = 20;
- this.translateX.unit = 'px';
- this.translateY.value = 20;
- this.translateY.unit = 'px';
- this.translateZ.value = 20;
- this.translateZ.unit = 'px';
- this.rotateX.value = 90;
- this.rotateX.unit = 'deg';
- this.rotateY.value = 90;
- this.rotateY.unit = 'deg';
- this.rotateX.value = 90;
- this.rotateX.unit = 'deg';
- this.rotateZ.value = 180;
- this.rotateZ.unit = 'deg';
- this.callActions('afterConstruct');
- h.seal(this);
- };
- mixitup.BaseStatic.call(mixitup.TransformDefaults);
- mixitup.TransformDefaults.prototype = Object.create(mixitup.StyleData.prototype);
- mixitup.TransformDefaults.prototype.constructor = mixitup.TransformDefaults;
- /**
- * @private
- * @static
- * @since 3.0.0
- * @type {mixitup.TransformDefaults}
- */
- mixitup.transformDefaults = new mixitup.TransformDefaults();
- /**
- * @constructor
- * @memberof mixitup
- * @private
- * @since 3.0.0
- */
- mixitup.EventDetail = function() {
- this.state = null;
- this.futureState = null;
- this.instance = null;
- this.originalEvent = null;
- };
- /**
- * The `mixitup.Events` class contains all custom events dispatched by MixItUp at various
- * points within the lifecycle of a mixer operation.
- *
- * Each event is analogous to the callback function of the same name defined in
- * the `callbacks` configuration object, and is triggered immediately before it.
- *
- * Events are always triggered from the container element on which MixItUp is instantiated
- * upon.
- *
- * As with any event, registered event handlers receive the event object as a parameter
- * which includes a `detail` property containting references to the current `state`,
- * the `mixer` instance, and other event-specific properties described below.
- *
- * @constructor
- * @namespace
- * @memberof mixitup
- * @public
- * @since 3.0.0
- */
- mixitup.Events = function() {
- mixitup.Base.call(this);
- this.callActions('beforeConstruct');
- /**
- * A custom event triggered immediately after any MixItUp operation is requested
- * and before animations have begun.
- *
- * The `mixStart` event also exposes a `futureState` property via the
- * `event.detail` object, which represents the final state of the mixer once
- * the requested operation has completed.
- *
- * @name mixStart
- * @memberof mixitup.Events
- * @static
- * @type {CustomEvent}
- */
- this.mixStart = null;
- /**
- * A custom event triggered when a MixItUp operation is requested while another
- * operation is in progress, and the animation queue is full, or queueing
- * is disabled.
- *
- * @name mixBusy
- * @memberof mixitup.Events
- * @static
- * @type {CustomEvent}
- */
- this.mixBusy = null;
- /**
- * A custom event triggered after any MixItUp operation has completed, and the
- * state has been updated.
- *
- * @name mixEnd
- * @memberof mixitup.Events
- * @static
- * @type {CustomEvent}
- */
- this.mixEnd = null;
- /**
- * A custom event triggered whenever a filter operation "fails", i.e. no targets
- * could be found matching the requested filter.
- *
- * @name mixFail
- * @memberof mixitup.Events
- * @static
- * @type {CustomEvent}
- */
- this.mixFail = null;
- /**
- * A custom event triggered whenever a MixItUp control is clicked, and before its
- * respective operation is requested.
- *
- * This event also exposes an `originalEvent` property via the `event.detail`
- * object, which holds a reference to the original click event.
- *
- * @name mixClick
- * @memberof mixitup.Events
- * @static
- * @type {CustomEvent}
- */
- this.mixClick = null;
- this.callActions('afterConstruct');
- h.seal(this);
- };
- mixitup.BaseStatic.call(mixitup.Events);
- mixitup.Events.prototype = Object.create(mixitup.Base.prototype);
- mixitup.Events.prototype.constructor = mixitup.Events;
- /**
- * @private
- * @param {string} eventType
- * @param {Element} el
- * @param {object} detail
- * @param {Document} [doc]
- */
- mixitup.Events.prototype.fire = function(eventType, el, detail, doc) {
- var self = this,
- event = null,
- eventDetail = new mixitup.EventDetail();
- self.callActions('beforeFire', arguments);
- if (typeof self[eventType] === 'undefined') {
- throw new Error('Event type "' + eventType + '" not found.');
- }
- eventDetail.state = new mixitup.State();
- h.extend(eventDetail.state, detail.state);
- if (detail.futureState) {
- eventDetail.futureState = new mixitup.State();
- h.extend(eventDetail.futureState, detail.futureState);
- }
- eventDetail.instance = detail.instance;
- if (detail.originalEvent) {
- eventDetail.originalEvent = detail.originalEvent;
- }
- event = h.getCustomEvent(eventType, eventDetail, doc);
- self.callFilters('eventFire', event, arguments);
- el.dispatchEvent(event);
- };
- // Asign a singleton instance to `mixitup.events`:
- mixitup.events = new mixitup.Events();
- /**
- * @constructor
- * @memberof mixitup
- * @private
- * @since 3.0.0
- */
- mixitup.QueueItem = function() {
- mixitup.Base.call(this);
- this.callActions('beforeConstruct');
- this.args = [];
- this.instruction = null;
- this.triggerElement = null;
- this.deferred = null;
- this.isToggling = false;
- this.callActions('afterConstruct');
- h.seal(this);
- };
- mixitup.BaseStatic.call(mixitup.QueueItem);
- mixitup.QueueItem.prototype = Object.create(mixitup.Base.prototype);
- mixitup.QueueItem.prototype.constructor = mixitup.QueueItem;
- /**
- * The `mixitup.Mixer` class is used to hold discreet, user-configured
- * instances of MixItUp on a provided container element.
- *
- * Mixer instances are returned whenever the `mixitup()` factory function is called,
- * which expose a range of methods enabling API-based filtering, sorting,
- * insertion, removal and more.
- *
- * @constructor
- * @namespace
- * @memberof mixitup
- * @public
- * @since 3.0.0
- */
- mixitup.Mixer = function() {
- mixitup.Base.call(this);
- this.callActions('beforeConstruct');
- this.config = new mixitup.Config();
- this.id = '';
- this.isBusy = false;
- this.isToggling = false;
- this.incPadding = true;
- this.controls = [];
- this.targets = [];
- this.origOrder = [];
- this.cache = {};
- this.toggleArray = [];
- this.targetsMoved = 0;
- this.targetsImmovable = 0;
- this.targetsBound = 0;
- this.targetsDone = 0;
- this.staggerDuration = 0;
- this.effectsIn = null;
- this.effectsOut = null;
- this.transformIn = [];
- this.transformOut = [];
- this.queue = [];
- this.state = null;
- this.lastOperation = null;
- this.lastClicked = null;
- this.userCallback = null;
- this.userDeferred = null;
- this.dom = new mixitup.MixerDom();
- this.callActions('afterConstruct');
- h.seal(this);
- };
- mixitup.BaseStatic.call(mixitup.Mixer);
- mixitup.Mixer.prototype = Object.create(mixitup.Base.prototype);
- h.extend(mixitup.Mixer.prototype,
- /** @lends mixitup.Mixer */
- {
- constructor: mixitup.Mixer,
- /**
- * @private
- * @instance
- * @since 3.0.0
- * @param {HTMLElement} container
- * @param {HTMLElement} document
- * @param {string} id
- * @param {object} [config]
- */
- attach: function(container, document, id, config) {
- var self = this,
- target = null,
- i = -1;
- self.callActions('beforeAttach', arguments);
- self.id = id;
- if (config) {
- h.extend(self.config, config, true, true);
- }
- self.sanitizeConfig();
- self.cacheDom(container, document);
- if (self.config.layout.containerClassName) {
- h.addClass(self.dom.container, self.config.layout.containerClassName);
- }
- if (!mixitup.features.has.transitions) {
- self.config.animation.enable = false;
- }
- if (typeof window.console === 'undefined') {
- self.config.debug.showWarnings = false;
- }
- if (self.config.data.uidKey) {
- // If the dataset API is in use, force disable controls
- self.config.controls.enable = false;
- }
- self.indexTargets();
- self.state = self.getInitialState();
- for (i = 0; target = self.lastOperation.toHide[i]; i++) {
- target.hide();
- }
- if (self.config.controls.enable) {
- self.initControls();
- self.buildToggleArray(null, self.state);
- self.updateControls({
- filter: self.state.activeFilter,
- sort: self.state.activeSort
- });
- }
- self.parseEffects();
- self.callActions('afterAttach', arguments);
- },
- /**
- * @private
- * @instance
- * @since 3.0.0
- * @return {void}
- */
- sanitizeConfig: function() {
- var self = this;
- self.callActions('beforeSanitizeConfig', arguments);
- // Sanitize enum/string config options
- self.config.controls.scope = self.config.controls.scope.toLowerCase().trim();
- self.config.controls.toggleLogic = self.config.controls.toggleLogic.toLowerCase().trim();
- self.config.controls.toggleDefault = self.config.controls.toggleDefault.toLowerCase().trim();
- self.config.animation.effects = self.config.animation.effects.trim();
- self.callActions('afterSanitizeConfig', arguments);
- },
- /**
- * @private
- * @instance
- * @since 3.0.0
- * @return {mixitup.State}
- */
- getInitialState: function() {
- var self = this,
- state = new mixitup.State(),
- operation = new mixitup.Operation();
- self.callActions('beforeGetInitialState', arguments);
- // Map initial values into a mock state object in order to construct an operation
- state.activeContainerClassName = self.config.layout.containerClassName;
- if (self.config.load.dataset) {
- // Dataset API
- if (!self.config.data.uidKey || typeof self.config.data.uidKey !== 'string') {
- throw new TypeError(mixitup.messages.errorConfigDataUidKeyNotSet());
- }
- operation.startDataset = operation.newDataset = state.activeDataset = self.config.load.dataset.slice();
- operation.startContainerClassName = operation.newContainerClassName = state.activeContainerClassName;
- operation.show = self.targets.slice();
- state = self.callFilters('stateGetInitialState', state, arguments);
- } else {
- // DOM API
- state.activeFilter = self.parseFilterArgs([self.config.load.filter]).command;
- state.activeSort = self.parseSortArgs([self.config.load.sort]).command;
- state.totalTargets = self.targets.length;
- state = self.callFilters('stateGetInitialState', state, arguments);
- if (
- state.activeSort.collection || state.activeSort.attribute ||
- state.activeSort.order === 'random' || state.activeSort.order === 'desc'
- ) {
- // Sorting on load
- operation.newSort = state.activeSort;
- self.sortOperation(operation);
- self.printSort(false, operation);
- self.targets = operation.newOrder;
- } else {
- operation.startOrder = operation.newOrder = self.targets;
- }
- operation.startFilter = operation.newFilter = state.activeFilter;
- operation.startSort = operation.newSort = state.activeSort;
- operation.startContainerClassName = operation.newContainerClassName = state.activeContainerClassName;
- if (operation.newFilter.selector === 'all') {
- operation.newFilter.selector = self.config.selectors.target;
- } else if (operation.newFilter.selector === 'none') {
- operation.newFilter.selector = '';
- }
- }
- operation = self.callFilters('operationGetInitialState', operation, [state]);
- self.lastOperation = operation;
- if (operation.newFilter) {
- self.filterOperation(operation);
- }
- state = self.buildState(operation);
- return state;
- },
- /**
- * Caches references of DOM elements neccessary for the mixer's functionality.
- *
- * @private
- * @instance
- * @since 3.0.0
- * @param {HTMLElement} el
- * @param {HTMLHtmlElement} document
- * @return {void}
- */
- cacheDom: function(el, document) {
- var self = this;
- self.callActions('beforeCacheDom', arguments);
- self.dom.document = document;
- self.dom.body = self.dom.document.querySelector('body');
- self.dom.container = el;
- self.dom.parent = el;
- self.callActions('afterCacheDom', arguments);
- },
- /**
- * Indexes all child elements of the mixer matching the `selectors.target`
- * selector, instantiating a mixitup.Target for each one.
- *
- * @private
- * @instance
- * @since 3.0.0
- * @return {void}
- */
- indexTargets: function() {
- var self = this,
- target = null,
- el = null,
- dataset = null,
- i = -1;
- self.callActions('beforeIndexTargets', arguments);
- self.dom.targets = self.config.layout.allowNestedTargets ?
- self.dom.container.querySelectorAll(self.config.selectors.target) :
- h.children(self.dom.container, self.config.selectors.target, self.dom.document);
- self.dom.targets = h.arrayFromList(self.dom.targets);
- self.targets = [];
- if ((dataset = self.config.load.dataset) && dataset.length !== self.dom.targets.length) {
- throw new Error(mixitup.messages.errorDatasetPrerenderedMismatch());
- }
- if (self.dom.targets.length) {
- for (i = 0; el = self.dom.targets[i]; i++) {
- target = new mixitup.Target();
- target.init(el, self, dataset ? dataset[i] : void(0));
- target.isInDom = true;
- self.targets.push(target);
- }
- self.dom.parent = self.dom.targets[0].parentElement === self.dom.container ?
- self.dom.container :
- self.dom.targets[0].parentElement;
- }
- self.origOrder = self.targets;
- self.callActions('afterIndexTargets', arguments);
- },
- initControls: function() {
- var self = this,
- definition = '',
- controlElements = null,
- el = null,
- parent = null,
- delagators = null,
- control = null,
- i = -1,
- j = -1;
- self.callActions('beforeInitControls', arguments);
- switch (self.config.controls.scope) {
- case 'local':
- parent = self.dom.container;
- break;
- case 'global':
- parent = self.dom.document;
- break;
- default:
- throw new Error(mixitup.messages.errorConfigInvalidControlsScope());
- }
- for (i = 0; definition = mixitup.controlDefinitions[i]; i++) {
- if (self.config.controls.live || definition.live) {
- if (definition.parent) {
- delagators = self.dom[definition.parent];
- if (!delagators || delagators.length < 0) continue;
- if (typeof delagators.length !== 'number') {
- delagators = [delagators];
- }
- } else {
- delagators = [parent];
- }
- for (j = 0; (el = delagators[j]); j++) {
- control = self.getControl(el, definition.type, definition.selector);
- self.controls.push(control);
- }
- } else {
- controlElements = parent.querySelectorAll(self.config.selectors.control + definition.selector);
- for (j = 0; (el = controlElements[j]); j++) {
- control = self.getControl(el, definition.type, '');
- if (!control) continue;
- self.controls.push(control);
- }
- }
- }
- self.callActions('afterInitControls', arguments);
- },
- /**
- * @private
- * @instance
- * @since 3.0.0
- * @param {HTMLElement} el
- * @param {string} type
- * @param {string} selector
- * @return {mixitup.Control|null}
- */
- getControl: function(el, type, selector) {
- var self = this,
- control = null,
- i = -1;
- self.callActions('beforeGetControl', arguments);
- if (!selector) {
- // Static controls only
- for (i = 0; control = mixitup.controls[i]; i++) {
- if (control.el === el && control.isBound(self)) {
- // Control already bound to this mixer (as another type).
- // NB: This prevents duplicate controls from being registered where a selector
- // might collide, eg: "[data-filter]" and "[data-filter][data-sort]"
- return self.callFilters('controlGetControl', null, arguments);
- } else if (control.el === el && control.type === type && control.selector === selector) {
- // Another mixer is already using this control, add this mixer as a binding
- control.addBinding(self);
- return self.callFilters('controlGetControl', control, arguments);
- }
- }
- }
- // Create new control
- control = new mixitup.Control();
- control.init(el, type, selector);
- control.classNames.base = h.getClassname(self.config.classNames, type);
- control.classNames.active = h.getClassname(self.config.classNames, type, self.config.classNames.modifierActive);
- control.classNames.disabled = h.getClassname(self.config.classNames, type, self.config.classNames.modifierDisabled);
- // Add a reference to this mixer as a binding
- control.addBinding(self);
- return self.callFilters('controlGetControl', control, arguments);
- },
- /**
- * Creates a compound selector by joining the `toggleArray` value as per the
- * defined toggle logic.
- *
- * @private
- * @instance
- * @since 3.0.0
- * @return {string}
- */
- getToggleSelector: function() {
- var self = this,
- delineator = self.config.controls.toggleLogic === 'or' ? ', ' : '',
- toggleSelector = '';
- self.callActions('beforeGetToggleSelector', arguments);
- self.toggleArray = h.clean(self.toggleArray);
- toggleSelector = self.toggleArray.join(delineator);
- if (toggleSelector === '') {
- toggleSelector = self.config.controls.toggleDefault;
- }
- return self.callFilters('selectorGetToggleSelector', toggleSelector, arguments);
- },
- /**
- * Breaks compound selector strings in an array of discreet selectors,
- * as per the active `controls.toggleLogic` configuration option. Accepts
- * either a dynamic command object, or a state object.
- *
- * @private
- * @instance
- * @since 2.0.0
- * @param {object} [command]
- * @param {mixitup.State} [state]
- * @return {void}
- */
- buildToggleArray: function(command, state) {
- var self = this,
- activeFilterSelector = '';
- self.callActions('beforeBuildToggleArray', arguments);
- if (command && command.filter) {
- activeFilterSelector = command.filter.selector.replace(/\s/g, '');
- } else if (state) {
- activeFilterSelector = state.activeFilter.selector.replace(/\s/g, '');
- } else {
- return;
- }
- if (activeFilterSelector === self.config.selectors.target || activeFilterSelector === 'all') {
- activeFilterSelector = '';
- }
- if (self.config.controls.toggleLogic === 'or') {
- self.toggleArray = activeFilterSelector.split(',');
- } else {
- self.toggleArray = self.splitCompoundSelector(activeFilterSelector);
- }
- self.toggleArray = h.clean(self.toggleArray);
- self.callActions('afterBuildToggleArray', arguments);
- },
- /**
- * Takes a compound selector (e.g. `.cat-1.cat-2`, `[data-cat="1"][data-cat="2"]`)
- * and breaks into its individual selectors.
- *
- * @private
- * @instance
- * @since 3.0.0
- * @param {string} compoundSelector
- * @return {string[]}
- */
- splitCompoundSelector: function(compoundSelector) {
- // Break at a `.` or `[`, capturing the delineator
- var partials = compoundSelector.split(/([\.\[])/g),
- toggleArray = [],
- selector = '',
- i = -1;
- if (partials[0] === '') {
- partials.shift();
- }
- for (i = 0; i < partials.length; i++) {
- if (i % 2 === 0) {
- selector = '';
- }
- selector += partials[i];
- if (i % 2 !== 0) {
- toggleArray.push(selector);
- }
- }
- return toggleArray;
- },
- /**
- * Updates controls to their active/inactive state based on the command or
- * current state of the mixer.
- *
- * @private
- * @instance
- * @since 2.0.0
- * @param {object} command
- * @return {void}
- */
- updateControls: function(command) {
- var self = this,
- control = null,
- output = new mixitup.CommandMultimix(),
- i = -1;
- self.callActions('beforeUpdateControls', arguments);
- // Sanitise to defaults
- if (command.filter) {
- output.filter = command.filter.selector;
- } else {
- output.filter = self.state.activeFilter.selector;
- }
- if (command.sort) {
- output.sort = self.buildSortString(command.sort);
- } else {
- output.sort = self.buildSortString(self.state.activeSort);
- }
- if (output.filter === self.config.selectors.target) {
- output.filter = 'all';
- }
- if (output.filter === '') {
- output.filter = 'none';
- }
- h.freeze(output);
- for (i = 0; control = self.controls[i]; i++) {
- control.update(output, self.toggleArray);
- }
- self.callActions('afterUpdateControls', arguments);
- },
- /**
- * @private
- * @instance
- * @since 3.0.0
- * @param {mixitup.CommandSort} command
- * @return {string}
- */
- buildSortString: function(command) {
- var self = this;
- var output = '';
- output += command.sortString;
- if (command.next) {
- output += ' ' + self.buildSortString(command.next);
- }
- return output;
- },
- /**
- * @private
- * @instance
- * @since 3.0.0
- * @param {object} command
- * @param {Operation} operation
- * @return {Promise.<mixitup.State>}
- */
- insertTargets: function(command, operation) {
- var self = this,
- nextSibling = null,
- insertionIndex = -1,
- frag = null,
- target = null,
- el = null,
- i = -1;
- self.callActions('beforeInsertTargets', arguments);
- if (typeof command.index === 'undefined') command.index = 0;
- nextSibling = self.getNextSibling(command.index, command.sibling, command.position);
- frag = self.dom.document.createDocumentFragment();
- if (nextSibling) {
- insertionIndex = h.index(nextSibling, self.config.selectors.target);
- } else {
- insertionIndex = self.targets.length;
- }
- if (command.collection) {
- for (i = 0; el = command.collection[i]; i++) {
- if (self.dom.targets.indexOf(el) > -1) {
- throw new Error(mixitup.messages.errorInsertPreexistingElement());
- }
- // Ensure elements are hidden when they are added to the DOM, so they can
- // be animated in gracefully
- el.style.display = 'none';
- frag.appendChild(el);
- frag.appendChild(self.dom.document.createTextNode(' '));
- if (!h.isElement(el, self.dom.document) || !el.matches(self.config.selectors.target)) continue;
- target = new mixitup.Target();
- target.init(el, self);
- target.isInDom = true;
- self.targets.splice(insertionIndex, 0, target);
- insertionIndex++;
- }
- self.dom.parent.insertBefore(frag, nextSibling);
- }
- // Since targets have been added, the original order must be updated
- operation.startOrder = self.origOrder = self.targets;
- self.callActions('afterInsertTargets', arguments);
- },
- /**
- * @private
- * @instance
- * @since 3.0.0
- * @param {Number} [index]
- * @param {Element} [sibling]
- * @param {string} [position]
- * @return {Element}
- */
- getNextSibling: function(index, sibling, position) {
- var self = this,
- element = null;
- index = Math.max(index, 0);
- if (sibling && position === 'before') {
- // Explicit sibling
- element = sibling;
- } else if (sibling && position === 'after') {
- // Explicit sibling
- element = sibling.nextElementSibling || null;
- } else if (self.targets.length > 0 && typeof index !== 'undefined') {
- // Index and targets exist
- element = (index < self.targets.length || !self.targets.length) ?
- self.targets[index].dom.el :
- self.targets[self.targets.length - 1].dom.el.nextElementSibling;
- } else if (self.targets.length === 0 && self.dom.parent.children.length > 0) {
- // No targets but other siblings
- if (self.config.layout.siblingAfter) {
- element = self.config.layout.siblingAfter;
- } else if (self.config.layout.siblingBefore) {
- element = self.config.layout.siblingBefore.nextElementSibling;
- } else {
- self.dom.parent.children[0];
- }
- } else {
- element === null;
- }
- return self.callFilters('elementGetNextSibling', element, arguments);
- },
- /**
- * @private
- * @instance
- * @since 2.0.0
- * @param {Operation} operation
- * @return {void}
- */
- filterOperation: function(operation) {
- var self = this,
- testResult = false,
- index = -1,
- action = '',
- target = null,
- i = -1;
- self.callActions('beforeFilterOperation', arguments);
- action = operation.newFilter.action;
- for (i = 0; target = operation.newOrder[i]; i++) {
- if (operation.newFilter.collection) {
- // show via collection
- testResult = operation.newFilter.collection.indexOf(target.dom.el) > -1;
- } else {
- // show via selector
- if (operation.newFilter.selector === '') {
- testResult = false;
- } else {
- testResult = target.dom.el.matches(operation.newFilter.selector);
- }
- }
- self.evaluateHideShow(testResult, target, action, operation);
- }
- if (operation.toRemove.length) {
- for (i = 0; target = operation.show[i]; i++) {
- if (operation.toRemove.indexOf(target) > -1) {
- // If any shown targets should be removed, move them into the toHide array
- operation.show.splice(i, 1);
- if ((index = operation.toShow.indexOf(target)) > -1) {
- operation.toShow.splice(index, 1);
- }
- operation.toHide.push(target);
- operation.hide.push(target);
- i--;
- }
- }
- }
- operation.matching = operation.show.slice();
- if (operation.show.length === 0 && operation.newFilter.selector !== '' && self.targets.length !== 0) {
- operation.hasFailed = true;
- }
- self.callActions('afterFilterOperation', arguments);
- },
- /**
- * @private
- * @instance
- * @since 3.0.0
- * @param {boolean} testResult
- * @param {Element} target
- * @param {string} action
- * @param {Operation} operation
- * @return {void}
- */
- evaluateHideShow: function(testResult, target, action, operation) {
- var self = this,
- filteredTestResult = false,
- args = Array.prototype.slice.call(arguments, 1);
- filteredTestResult = self.callFilters('testResultEvaluateHideShow', testResult, args);
- self.callActions('beforeEvaluateHideShow', arguments);
- if (
- filteredTestResult === true && action === 'show' ||
- filteredTestResult === false && action === 'hide'
- ) {
- operation.show.push(target);
- !target.isShown && operation.toShow.push(target);
- } else {
- operation.hide.push(target);
- target.isShown && operation.toHide.push(target);
- }
- self.callActions('afterEvaluateHideShow', arguments);
- },
- /**
- * @private
- * @instance
- * @since 2.0.0
- * @param {Operation} operation
- * @return {void}
- */
- sortOperation: function(operation) {
- var self = this;
- self.callActions('beforeSortOperation', arguments);
- operation.startOrder = self.targets;
- if (operation.newSort.collection) {
- // Sort by collection
- operation.newOrder = operation.newSort.collection;
- } else if (operation.newSort.order === 'random') {
- // Sort random
- operation.newOrder = h.arrayShuffle(operation.startOrder);
- } else if (operation.newSort.attribute === '') {
- // Sort by default
- operation.newOrder = self.origOrder.slice();
- if (operation.newSort.order === 'desc') {
- operation.newOrder.reverse();
- }
- } else {
- // Sort by attribute
- operation.newOrder = operation.startOrder.slice();
- operation.newOrder.sort(function(a, b) {
- return self.compare(a, b, operation.newSort);
- });
- }
- if (h.isEqualArray(operation.newOrder, operation.startOrder)) {
- operation.willSort = false;
- }
- self.callActions('afterSortOperation', arguments);
- },
- /**
- * @private
- * @instance
- * @since 2.0.0
- * @param {mixitup.Target} a
- * @param {mixitup.Target} b
- * @param {mixitup.CommandSort} command
- * @return {Number}
- */
- compare: function(a, b, command) {
- var self = this,
- order = command.order,
- attrA = self.getAttributeValue(a, command.attribute),
- attrB = self.getAttributeValue(b, command.attribute);
- if (isNaN(attrA * 1) || isNaN(attrB * 1)) {
- attrA = attrA.toLowerCase();
- attrB = attrB.toLowerCase();
- } else {
- attrA = attrA * 1;
- attrB = attrB * 1;
- }
- if (attrA < attrB) {
- return order === 'asc' ? -1 : 1;
- }
- if (attrA > attrB) {
- return order === 'asc' ? 1 : -1;
- }
- if (attrA === attrB && command.next) {
- return self.compare(a, b, command.next);
- }
- return 0;
- },
- /**
- * Reads the values of any data attributes present the provided target element
- * which match the current sort command.
- *
- * @private
- * @instance
- * @since 3.0.0
- * @param {mixitup.Target} target
- * @param {string} [attribute]
- * @return {(String|Number)}
- */
- getAttributeValue: function(target, attribute) {
- var self = this,
- value = '';
- value = target.dom.el.getAttribute('data-' + attribute);
- if (value === null) {
- if (self.config.debug.showWarnings) {
- // Encourage users to assign values to all targets to avoid erroneous sorting
- // when types are mixed
- console.warn(mixitup.messages.warningInconsistentSortingAttributes({
- attribute: 'data-' + attribute
- }));
- }
- }
- // If an attribute is not present, return 0 as a safety value
- return self.callFilters('valueGetAttributeValue', value || 0, arguments);
- },
- /**
- * Inserts elements into the DOM in the appropriate
- * order using a document fragment for minimal
- * DOM thrashing
- *
- * @private
- * @instance
- * @since 2.0.0
- * @param {boolean} isResetting
- * @param {Operation} operation
- * @return {void}
- */
- printSort: function(isResetting, operation) {
- var self = this,
- startOrder = isResetting ? operation.newOrder : operation.startOrder,
- newOrder = isResetting ? operation.startOrder : operation.newOrder,
- nextSibling = startOrder.length ? startOrder[startOrder.length - 1].dom.el.nextElementSibling : null,
- frag = window.document.createDocumentFragment(),
- whitespace = null,
- target = null,
- el = null,
- i = -1;
- self.callActions('beforePrintSort', arguments);
- // Empty the container
- for (i = 0; target = startOrder[i]; i++) {
- el = target.dom.el;
- if (el.style.position === 'absolute') continue;
- h.removeWhitespace(el.previousSibling);
- el.parentElement.removeChild(el);
- }
- whitespace = nextSibling ? nextSibling.previousSibling : self.dom.parent.lastChild;
- if (whitespace && whitespace.nodeName === '#text') {
- h.removeWhitespace(whitespace);
- }
- for (i = 0; target = newOrder[i]; i++) {
- // Add targets into a document fragment
- el = target.dom.el;
- if (h.isElement(frag.lastChild)) {
- frag.appendChild(window.document.createTextNode(' '));
- }
- frag.appendChild(el);
- }
- // Insert the document fragment into the container
- // before any other non-target elements
- if (self.dom.parent.firstChild && self.dom.parent.firstChild !== nextSibling) {
- frag.insertBefore(window.document.createTextNode(' '), frag.childNodes[0]);
- }
- if (nextSibling) {
- frag.appendChild(window.document.createTextNode(' '));
- self.dom.parent.insertBefore(frag, nextSibling);
- } else {
- self.dom.parent.appendChild(frag);
- }
- self.callActions('afterPrintSort', arguments);
- },
- /**
- * Parses user-defined sort strings (i.e. `default:asc`) into sort commands objects.
- *
- * @private
- * @instance
- * @since 3.0.0
- * @param {string} sortString
- * @param {mixitup.CommandSort} command
- * @return {mixitup.CommandSort}
- */
- parseSortString: function(sortString, command) {
- var self = this,
- rules = sortString.split(' '),
- current = command,
- rule = [],
- i = -1;
- // command.sortString = sortString;
- for (i = 0; i < rules.length; i++) {
- rule = rules[i].split(':');
- current.sortString = rules[i];
- current.attribute = h.dashCase(rule[0]);
- current.order = rule[1] || 'asc';
- switch (current.attribute) {
- case 'default':
- // treat "default" as sorting by no attribute
- current.attribute = '';
- break;
- case 'random':
- // treat "random" as an order not an attribute
- current.attribute = '';
- current.order = 'random';
- break;
- }
- if (!current.attribute || current.order === 'random') break;
- if (i < rules.length - 1) {
- // Embed reference to the next command
- current.next = new mixitup.CommandSort();
- h.freeze(current);
- current = current.next;
- }
- }
- return self.callFilters('commandsParseSort', command, arguments);
- },
- /**
- * Parses all effects out of the user-defined `animation.effects` string into
- * their respective properties and units.
- *
- * @private
- * @instance
- * @since 2.0.0
- * @return {void}
- */
- parseEffects: function() {
- var self = this,
- transformName = '',
- effectsIn = self.config.animation.effectsIn || self.config.animation.effects,
- effectsOut = self.config.animation.effectsOut || self.config.animation.effects;
- self.callActions('beforeParseEffects', arguments);
- self.effectsIn = new mixitup.StyleData();
- self.effectsOut = new mixitup.StyleData();
- self.transformIn = [];
- self.transformOut = [];
- self.effectsIn.opacity = self.effectsOut.opacity = 1;
- self.parseEffect('fade', effectsIn, self.effectsIn, self.transformIn);
- self.parseEffect('fade', effectsOut, self.effectsOut, self.transformOut, true);
- for (transformName in mixitup.transformDefaults) {
- if (!(mixitup.transformDefaults[transformName] instanceof mixitup.TransformData)) {
- continue;
- }
- self.parseEffect(transformName, effectsIn, self.effectsIn, self.transformIn);
- self.parseEffect(transformName, effectsOut, self.effectsOut, self.transformOut, true);
- }
- self.parseEffect('stagger', effectsIn, self.effectsIn, self.transformIn);
- self.parseEffect('stagger', effectsOut, self.effectsOut, self.transformOut, true);
- self.callActions('afterParseEffects', arguments);
- },
- /**
- * @private
- * @instance
- * @since 2.0.0
- * @param {string} effectName
- * @param {string} effectString
- * @param {StyleData} effects
- * @param {String[]} transform
- * @param {boolean} [isOut]
- */
- parseEffect: function(effectName, effectString, effects, transform, isOut) {
- var self = this,
- re = /\(([^)]+)\)/,
- propIndex = -1,
- str = '',
- match = [],
- val = '',
- units = ['%', 'px', 'em', 'rem', 'vh', 'vw', 'deg'],
- unit = '',
- i = -1;
- self.callActions('beforeParseEffect', arguments);
- if (typeof effectString !== 'string') {
- throw new TypeError(mixitup.messages.errorConfigInvalidAnimationEffects());
- }
- if (effectString.indexOf(effectName) < 0) {
- // The effect is not present in the effects string
- if (effectName === 'stagger') {
- // Reset stagger to 0
- self.staggerDuration = 0;
- }
- return;
- }
- // The effect is present
- propIndex = effectString.indexOf(effectName + '(');
- if (propIndex > -1) {
- // The effect has a user defined value in parentheses
- // Extract from the first parenthesis to the end of string
- str = effectString.substring(propIndex);
- // Match any number of characters between "(" and ")"
- match = re.exec(str);
- val = match[1];
- }
- switch (effectName) {
- case 'fade':
- effects.opacity = val ? parseFloat(val) : 0;
- break;
- case 'stagger':
- self.staggerDuration = val ? parseFloat(val) : 100;
- // TODO: Currently stagger must be applied globally, but
- // if seperate values are specified for in/out, this should
- // be respected
- break;
- default:
- // All other effects are transforms following the same structure
- if (isOut && self.config.animation.reverseOut && effectName !== 'scale') {
- effects[effectName].value =
- (val ? parseFloat(val) : mixitup.transformDefaults[effectName].value) * -1;
- } else {
- effects[effectName].value =
- (val ? parseFloat(val) : mixitup.transformDefaults[effectName].value);
- }
- if (val) {
- for (i = 0; unit = units[i]; i++) {
- if (val.indexOf(unit) > -1) {
- effects[effectName].unit = unit;
- break;
- }
- }
- } else {
- effects[effectName].unit = mixitup.transformDefaults[effectName].unit;
- }
- transform.push(
- effectName +
- '(' +
- effects[effectName].value +
- effects[effectName].unit +
- ')'
- );
- }
- self.callActions('afterParseEffect', arguments);
- },
- /**
- * @private
- * @instance
- * @since 2.0.0
- * @param {Operation} operation
- * @return {State}
- */
- buildState: function(operation) {
- var self = this,
- state = new mixitup.State(),
- target = null,
- i = -1;
- self.callActions('beforeBuildState', arguments);
- // Map target elements into state arrays.
- // the real target objects should never be exposed
- for (i = 0; target = self.targets[i]; i++) {
- if (!operation.toRemove.length || operation.toRemove.indexOf(target) < 0) {
- state.targets.push(target.dom.el);
- }
- }
- for (i = 0; target = operation.matching[i]; i++) {
- state.matching.push(target.dom.el);
- }
- for (i = 0; target = operation.show[i]; i++) {
- state.show.push(target.dom.el);
- }
- for (i = 0; target = operation.hide[i]; i++) {
- if (!operation.toRemove.length || operation.toRemove.indexOf(target) < 0) {
- state.hide.push(target.dom.el);
- }
- }
- state.id = self.id;
- state.container = self.dom.container;
- state.activeFilter = operation.newFilter;
- state.activeSort = operation.newSort;
- state.activeDataset = operation.newDataset;
- state.activeContainerClassName = operation.newContainerClassName;
- state.hasFailed = operation.hasFailed;
- state.totalTargets = self.targets.length;
- state.totalShow = operation.show.length;
- state.totalHide = operation.hide.length;
- state.totalMatching = operation.matching.length;
- state.triggerElement = operation.triggerElement;
- return self.callFilters('stateBuildState', state, arguments);
- },
- /**
- * @private
- * @instance
- * @since 2.0.0
- * @param {boolean} shouldAnimate
- * @param {Operation} operation
- * @return {void}
- */
- goMix: function(shouldAnimate, operation) {
- var self = this,
- deferred = null;
- self.callActions('beforeGoMix', arguments);
- // If the animation duration is set to 0ms,
- // or no effects specified,
- // or the container is hidden
- // then abort animation
- if (
- !self.config.animation.duration || !self.config.animation.effects || !h.isVisible(self.dom.container)
- ) {
- shouldAnimate = false;
- }
- if (
- !operation.toShow.length &&
- !operation.toHide.length &&
- !operation.willSort &&
- !operation.willChangeLayout
- ) {
- // If nothing to show or hide, and not sorting or
- // changing layout
- shouldAnimate = false;
- }
- if (
- !operation.startState.show.length &&
- !operation.show.length
- ) {
- // If nothing currently shown, nothing to show
- shouldAnimate = false;
- }
- mixitup.events.fire('mixStart', self.dom.container, {
- state: operation.startState,
- futureState: operation.newState,
- instance: self
- }, self.dom.document);
- if (typeof self.config.callbacks.onMixStart === 'function') {
- self.config.callbacks.onMixStart.call(
- self.dom.container,
- operation.startState,
- operation.newState,
- self
- );
- }
- h.removeClass(self.dom.container, h.getClassname(self.config.classNames, 'container', self.config.classNames.modifierFailed));
- if (!self.userDeferred) {
- // Queue empty, no pending operations
- deferred = self.userDeferred = h.defer(mixitup.libraries);
- } else {
- // Use existing deferred
- deferred = self.userDeferred;
- }
- self.isBusy = true;
- if (!shouldAnimate || !mixitup.features.has.transitions) {
- // Abort
- if (self.config.debug.fauxAsync) {
- setTimeout(function() {
- self.cleanUp(operation);
- }, self.config.animation.duration);
- } else {
- self.cleanUp(operation);
- }
- return self.callFilters('promiseGoMix', deferred.promise, arguments);
- }
- // If we should animate and the platform supports transitions, go for it
- if (window.pageYOffset !== operation.docState.scrollTop) {
- window.scrollTo(operation.docState.scrollLeft, operation.docState.scrollTop);
- }
- if (self.config.animation.applyPerspective) {
- self.dom.parent.style[mixitup.features.perspectiveProp] =
- self.config.animation.perspectiveDistance;
- self.dom.parent.style[mixitup.features.perspectiveOriginProp] =
- self.config.animation.perspectiveOrigin;
- }
- if (
- self.config.animation.animateResizeContainer &&
- operation.startHeight !== operation.newHeight &&
- operation.viewportDeltaY !== operation.startHeight - operation.newHeight
- ) {
- self.dom.parent.style.height = operation.startHeight + 'px';
- }
- if (
- self.config.animation.animateResizeContainer &&
- operation.startWidth !== operation.newWidth &&
- operation.viewportDeltaX !== operation.startWidth - operation.newWidth
- ) {
- self.dom.parent.style.width = operation.startWidth + 'px';
- }
- if (operation.startHeight === operation.newHeight) {
- self.dom.parent.style.height = operation.startHeight + 'px';
- }
- if (operation.startWidth === operation.newWidth) {
- self.dom.parent.style.width = operation.startWidth + 'px';
- }
- if (operation.startHeight === operation.newHeight && operation.startWidth === operation.newWidth) {
- self.dom.parent.style.overflow = 'hidden';
- }
- requestAnimationFrame(function() {
- self.moveTargets(operation);
- });
- return self.callFilters('promiseGoMix', deferred.promise, arguments);
- },
- /**
- * @private
- * @instance
- * @since 2.0.0
- * @param {Operation} operation
- * @return {void}
- */
- getStartMixData: function(operation) {
- var self = this,
- parentStyle = window.getComputedStyle(self.dom.parent),
- parentRect = self.dom.parent.getBoundingClientRect(),
- target = null,
- data = {},
- i = -1,
- boxSizing = parentStyle[mixitup.features.boxSizingProp];
- self.incPadding = (boxSizing === 'border-box');
- self.callActions('beforeGetStartMixData', arguments);
- for (i = 0; target = operation.show[i]; i++) {
- data = target.getPosData();
- operation.showPosData[i] = {
- startPosData: data
- };
- }
- for (i = 0; target = operation.toHide[i]; i++) {
- data = target.getPosData();
- operation.toHidePosData[i] = {
- startPosData: data
- };
- }
- operation.startX = parentRect.left;
- operation.startY = parentRect.top;
- operation.startHeight = self.incPadding ?
- parentRect.height :
- parentRect.height -
- parseFloat(parentStyle.paddingTop) -
- parseFloat(parentStyle.paddingBottom) -
- parseFloat(parentStyle.borderTop) -
- parseFloat(parentStyle.borderBottom);
- operation.startWidth = self.incPadding ?
- parentRect.width :
- parentRect.width -
- parseFloat(parentStyle.paddingLeft) -
- parseFloat(parentStyle.paddingRight) -
- parseFloat(parentStyle.borderLeft) -
- parseFloat(parentStyle.borderRight);
- self.callActions('afterGetStartMixData', arguments);
- },
- /**
- * @private
- * @instance
- * @since 2.0.0
- * @param {Operation} operation
- * @return {void}
- */
- setInter: function(operation) {
- var self = this,
- target = null,
- i = -1;
- self.callActions('beforeSetInter', arguments);
- // Prevent scrollbar flicker on non-inertial scroll platforms by clamping height/width
- if (self.config.animation.clampHeight) {
- self.dom.parent.style.height = operation.startHeight + 'px';
- self.dom.parent.style.overflow = 'hidden';
- }
- if (self.config.animation.clampWidth) {
- self.dom.parent.style.width = operation.startWidth + 'px';
- self.dom.parent.style.overflow = 'hidden';
- }
- for (i = 0; target = operation.toShow[i]; i++) {
- target.show();
- }
- if (operation.willChangeLayout) {
- h.removeClass(self.dom.container, operation.startContainerClassName);
- h.addClass(self.dom.container, operation.newContainerClassName);
- }
- self.callActions('afterSetInter', arguments);
- },
- /**
- * @private
- * @instance
- * @since 2.0.0
- * @param {Operation} operation
- * @return {void}
- */
- getInterMixData: function(operation) {
- var self = this,
- target = null,
- i = -1;
- self.callActions('beforeGetInterMixData', arguments);
- for (i = 0; target = operation.show[i]; i++) {
- operation.showPosData[i].interPosData = target.getPosData();
- }
- for (i = 0; target = operation.toHide[i]; i++) {
- operation.toHidePosData[i].interPosData = target.getPosData();
- }
- self.callActions('afterGetInterMixData', arguments);
- },
- /**
- * @private
- * @instance
- * @since 2.0.0
- * @param {Operation} operation
- * @return {void}
- */
- setFinal: function(operation) {
- var self = this,
- target = null,
- i = -1;
- self.callActions('beforeSetFinal', arguments);
- operation.willSort && self.printSort(false, operation);
- for (i = 0; target = operation.toHide[i]; i++) {
- target.hide();
- }
- self.callActions('afterSetFinal', arguments);
- },
- /**
- * @private
- * @instance
- * @since 2.0.0
- * @param {Operation} operation
- * @return {void}
- */
- getFinalMixData: function(operation) {
- var self = this,
- parentStyle = null,
- parentRect = null,
- target = null,
- i = -1;
- self.callActions('beforeGetFinalMixData', arguments);
- for (i = 0; target = operation.show[i]; i++) {
- operation.showPosData[i].finalPosData = target.getPosData();
- }
- for (i = 0; target = operation.toHide[i]; i++) {
- operation.toHidePosData[i].finalPosData = target.getPosData();
- }
- // Remove clamping
- if (self.config.animation.clampHeight || self.config.animation.clampWidth) {
- self.dom.parent.style.height =
- self.dom.parent.style.width =
- self.dom.parent.style.overflow = '';
- }
- if (!self.incPadding) {
- parentStyle = window.getComputedStyle(self.dom.parent);
- }
- parentRect = self.dom.parent.getBoundingClientRect();
- operation.newX = parentRect.left;
- operation.newY = parentRect.top;
- operation.newHeight = self.incPadding ?
- parentRect.height :
- parentRect.height -
- parseFloat(parentStyle.paddingTop) -
- parseFloat(parentStyle.paddingBottom) -
- parseFloat(parentStyle.borderTop) -
- parseFloat(parentStyle.borderBottom);
- operation.newWidth = self.incPadding ?
- parentRect.width :
- parentRect.width -
- parseFloat(parentStyle.paddingLeft) -
- parseFloat(parentStyle.paddingRight) -
- parseFloat(parentStyle.borderLeft) -
- parseFloat(parentStyle.borderRight);
- operation.viewportDeltaX = operation.docState.viewportWidth - this.dom.document.documentElement.clientWidth;
- operation.viewportDeltaY = operation.docState.viewportHeight - this.dom.document.documentElement.clientHeight;
- if (operation.willSort) {
- self.printSort(true, operation);
- }
- for (i = 0; target = operation.toShow[i]; i++) {
- target.hide();
- }
- for (i = 0; target = operation.toHide[i]; i++) {
- target.show();
- }
- if (operation.willChangeLayout) {
- h.removeClass(self.dom.container, operation.newContainerClassName);
- h.addClass(self.dom.container, self.config.layout.containerClassName);
- }
- self.callActions('afterGetFinalMixData', arguments);
- },
- /**
- * @private
- * @instance
- * @since 3.0.0
- * @param {Operation} operation
- */
- getTweenData: function(operation) {
- var self = this,
- target = null,
- posData = null,
- effectNames = Object.getOwnPropertyNames(self.effectsIn),
- effectName = '',
- effect = null,
- widthChange = -1,
- heightChange = -1,
- i = -1,
- j = -1;
- self.callActions('beforeGetTweenData', arguments);
- for (i = 0; target = operation.show[i]; i++) {
- posData = operation.showPosData[i];
- posData.posIn = new mixitup.StyleData();
- posData.posOut = new mixitup.StyleData();
- posData.tweenData = new mixitup.StyleData();
- // Process x and y
- if (target.isShown) {
- posData.posIn.x = posData.startPosData.x - posData.interPosData.x;
- posData.posIn.y = posData.startPosData.y - posData.interPosData.y;
- } else {
- posData.posIn.x = posData.posIn.y = 0;
- }
- posData.posOut.x = posData.finalPosData.x - posData.interPosData.x;
- posData.posOut.y = posData.finalPosData.y - posData.interPosData.y;
- // Process opacity
- posData.posIn.opacity = target.isShown ? 1 : self.effectsIn.opacity;
- posData.posOut.opacity = 1;
- posData.tweenData.opacity = posData.posOut.opacity - posData.posIn.opacity;
- // Adjust x and y if not nudging
- if (!target.isShown && !self.config.animation.nudge) {
- posData.posIn.x = posData.posOut.x;
- posData.posIn.y = posData.posOut.y;
- }
- posData.tweenData.x = posData.posOut.x - posData.posIn.x;
- posData.tweenData.y = posData.posOut.y - posData.posIn.y;
- // Process width, height, and margins
- if (self.config.animation.animateResizeTargets) {
- posData.posIn.width = posData.startPosData.width;
- posData.posIn.height = posData.startPosData.height;
- // "||" Prevents width/height change from including 0 width/height if hiding or showing
- widthChange = (posData.startPosData.width || posData.finalPosData.width) - posData.interPosData.width;
- posData.posIn.marginRight = posData.startPosData.marginRight - widthChange;
- heightChange = (posData.startPosData.height || posData.finalPosData.height) - posData.interPosData.height;
- posData.posIn.marginBottom = posData.startPosData.marginBottom - heightChange;
- posData.posOut.width = posData.finalPosData.width;
- posData.posOut.height = posData.finalPosData.height;
- widthChange = (posData.finalPosData.width || posData.startPosData.width) - posData.interPosData.width;
- posData.posOut.marginRight = posData.finalPosData.marginRight - widthChange;
- heightChange = (posData.finalPosData.height || posData.startPosData.height) - posData.interPosData.height;
- posData.posOut.marginBottom = posData.finalPosData.marginBottom - heightChange;
- posData.tweenData.width = posData.posOut.width - posData.posIn.width;
- posData.tweenData.height = posData.posOut.height - posData.posIn.height;
- posData.tweenData.marginRight = posData.posOut.marginRight - posData.posIn.marginRight;
- posData.tweenData.marginBottom = posData.posOut.marginBottom - posData.posIn.marginBottom;
- }
- // Process transforms
- for (j = 0; effectName = effectNames[j]; j++) {
- effect = self.effectsIn[effectName];
- if (!(effect instanceof mixitup.TransformData) || !effect.value) continue;
- posData.posIn[effectName].value = effect.value;
- posData.posOut[effectName].value = 0;
- posData.tweenData[effectName].value =
- posData.posOut[effectName].value - posData.posIn[effectName].value;
- posData.posIn[effectName].unit =
- posData.posOut[effectName].unit =
- posData.tweenData[effectName].unit =
- effect.unit;
- }
- }
- for (i = 0; target = operation.toHide[i]; i++) {
- posData = operation.toHidePosData[i];
- posData.posIn = new mixitup.StyleData();
- posData.posOut = new mixitup.StyleData();
- posData.tweenData = new mixitup.StyleData();
- // Process x and y
- posData.posIn.x = target.isShown ? posData.startPosData.x - posData.interPosData.x : 0;
- posData.posIn.y = target.isShown ? posData.startPosData.y - posData.interPosData.y : 0;
- posData.posOut.x = self.config.animation.nudge ? 0 : posData.posIn.x;
- posData.posOut.y = self.config.animation.nudge ? 0 : posData.posIn.y;
- posData.tweenData.x = posData.posOut.x - posData.posIn.x;
- posData.tweenData.y = posData.posOut.y - posData.posIn.y;
- // Process width, height, and margins
- if (self.config.animation.animateResizeTargets) {
- posData.posIn.width = posData.startPosData.width;
- posData.posIn.height = posData.startPosData.height;
- widthChange = posData.startPosData.width - posData.interPosData.width;
- posData.posIn.marginRight = posData.startPosData.marginRight - widthChange;
- heightChange = posData.startPosData.height - posData.interPosData.height;
- posData.posIn.marginBottom = posData.startPosData.marginBottom - heightChange;
- }
- // Process opacity
- posData.posIn.opacity = 1;
- posData.posOut.opacity = self.effectsOut.opacity;
- posData.tweenData.opacity = posData.posOut.opacity - posData.posIn.opacity;
- // Process transforms
- for (j = 0; effectName = effectNames[j]; j++) {
- effect = self.effectsOut[effectName];
- if (!(effect instanceof mixitup.TransformData) || !effect.value) continue;
- posData.posIn[effectName].value = 0;
- posData.posOut[effectName].value = effect.value;
- posData.tweenData[effectName].value =
- posData.posOut[effectName].value - posData.posIn[effectName].value;
- posData.posIn[effectName].unit =
- posData.posOut[effectName].unit =
- posData.tweenData[effectName].unit =
- effect.unit;
- }
- }
- self.callActions('afterGetTweenData', arguments);
- },
- /**
- * @private
- * @instance
- * @since 3.0.0
- * @param {Operation} operation
- * @return {void}
- */
- moveTargets: function(operation) {
- var self = this,
- target = null,
- moveData = null,
- posData = null,
- statusChange = '',
- willTransition = false,
- staggerIndex = -1,
- i = -1,
- checkProgress = self.checkProgress.bind(self);
- self.callActions('beforeMoveTargets', arguments);
- // TODO: this is an extra loop in addition to the calcs
- // done in getOperation, could some of this be done there?
- for (i = 0; target = operation.show[i]; i++) {
- moveData = new mixitup.IMoveData();
- posData = operation.showPosData[i];
- statusChange = target.isShown ? 'none' : 'show';
- willTransition = self.willTransition(
- statusChange,
- operation.hasEffect,
- posData.posIn,
- posData.posOut
- );
- if (willTransition) {
- // Prevent non-transitioning targets from incrementing the staggerIndex
- staggerIndex++;
- }
- target.show();
- moveData.posIn = posData.posIn;
- moveData.posOut = posData.posOut;
- moveData.statusChange = statusChange;
- moveData.staggerIndex = staggerIndex;
- moveData.operation = operation;
- moveData.callback = willTransition ? checkProgress : null;
- target.move(moveData);
- }
- for (i = 0; target = operation.toHide[i]; i++) {
- posData = operation.toHidePosData[i];
- moveData = new mixitup.IMoveData();
- statusChange = 'hide';
- willTransition = self.willTransition(statusChange, posData.posIn, posData.posOut);
- moveData.posIn = posData.posIn;
- moveData.posOut = posData.posOut;
- moveData.statusChange = statusChange;
- moveData.staggerIndex = i;
- moveData.operation = operation;
- moveData.callback = willTransition ? checkProgress : null;
- target.move(moveData);
- }
- if (self.config.animation.animateResizeContainer) {
- self.dom.parent.style[mixitup.features.transitionProp] =
- 'height ' + self.config.animation.duration + 'ms ease, ' +
- 'width ' + self.config.animation.duration + 'ms ease ';
- requestAnimationFrame(function() {
- if (
- operation.startHeight !== operation.newHeight &&
- operation.viewportDeltaY !== operation.startHeight - operation.newHeight
- ) {
- self.dom.parent.style.height = operation.newHeight + 'px';
- }
- if (
- operation.startWidth !== operation.newWidth &&
- operation.viewportDeltaX !== operation.startWidth - operation.newWidth
- ) {
- self.dom.parent.style.width = operation.newWidth + 'px';
- }
- });
- }
- if (operation.willChangeLayout) {
- h.removeClass(self.dom.container, self.config.layout.ContainerClassName);
- h.addClass(self.dom.container, operation.newContainerClassName);
- }
- self.callActions('afterMoveTargets', arguments);
- },
- /**
- * @private
- * @instance
- * @return {boolean}
- */
- hasEffect: function() {
- var self = this,
- EFFECTABLES = [
- 'scale',
- 'translateX', 'translateY', 'translateZ',
- 'rotateX', 'rotateY', 'rotateZ'
- ],
- effectName = '',
- effect = null,
- result = false,
- value = -1,
- i = -1;
- if (self.effectsIn.opacity !== 1) {
- return self.callFilters('resultHasEffect', true, arguments);
- }
- for (i = 0; effectName = EFFECTABLES[i]; i++) {
- effect = self.effectsIn[effectName];
- value = (typeof effect && effect.value !== 'undefined') ?
- effect.value : effect;
- if (value !== 0) {
- result = true;
- break;
- }
- }
- return self.callFilters('resultHasEffect', result, arguments);
- },
- /**
- * Determines if a target element will transition in
- * some fasion and therefore requires binding of
- * transitionEnd
- *
- * @private
- * @instance
- * @since 3.0.0
- * @param {string} statusChange
- * @param {boolean} hasEffect
- * @param {StyleData} posIn
- * @param {StyleData} posOut
- * @return {boolean}
- */
- willTransition: function(statusChange, hasEffect, posIn, posOut) {
- var self = this,
- result = false;
- if (!h.isVisible(self.dom.container)) {
- // If the container is not visible, the transitionEnd
- // event will not occur and MixItUp will hang
- result = false;
- } else if (
- (statusChange !== 'none' && hasEffect) ||
- posIn.x !== posOut.x ||
- posIn.y !== posOut.y
- ) {
- // If opacity and/or translate will change
- result = true;
- } else if (self.config.animation.animateResizeTargets) {
- // Check if width, height or margins will change
- result = (
- posIn.width !== posOut.width ||
- posIn.height !== posOut.height ||
- posIn.marginRight !== posOut.marginRight ||
- posIn.marginTop !== posOut.marginTop
- );
- } else {
- result = false;
- }
- return self.callFilters('resultWillTransition', result, arguments);
- },
- /**
- * @private
- * @instance
- * @since 2.0.0
- * @param {Operation} operation
- * @return {void}
- */
- checkProgress: function(operation) {
- var self = this;
- self.targetsDone++;
- if (self.targetsBound === self.targetsDone) {
- self.cleanUp(operation);
- }
- },
- /**
- * @private
- * @instance
- * @since 2.0.0
- * @param {Operation} operation
- * @return {void}
- */
- cleanUp: function(operation) {
- var self = this,
- target = null,
- whitespaceBefore = null,
- whitespaceAfter = null,
- nextInQueue = null,
- i = -1;
- self.callActions('beforeCleanUp', arguments);
- self.targetsMoved =
- self.targetsImmovable =
- self.targetsBound =
- self.targetsDone = 0;
- for (i = 0; target = operation.show[i]; i++) {
- target.cleanUp();
- target.show();
- }
- for (i = 0; target = operation.toHide[i]; i++) {
- target.cleanUp();
- target.hide();
- }
- if (operation.willSort) {
- self.printSort(false, operation);
- }
- // Remove any styles applied to the parent container
- self.dom.parent.style[mixitup.features.transitionProp] =
- self.dom.parent.style.height =
- self.dom.parent.style.width =
- self.dom.parent.style.overflow =
- self.dom.parent.style[mixitup.features.perspectiveProp] =
- self.dom.parent.style[mixitup.features.perspectiveOriginProp] = '';
- if (operation.willChangeLayout) {
- h.removeClass(self.dom.container, operation.startContainerClassName);
- h.addClass(self.dom.container, operation.newContainerClassName);
- }
- if (operation.toRemove.length) {
- for (i = 0; target = self.targets[i]; i++) {
- if (operation.toRemove.indexOf(target) > -1) {
- if (
- (whitespaceBefore = target.dom.el.previousSibling) && whitespaceBefore.nodeName === '#text' &&
- (whitespaceAfter = target.dom.el.nextSibling) && whitespaceAfter.nodeName === '#text'
- ) {
- h.removeWhitespace(whitespaceBefore);
- }
- if (!operation.willSort) {
- // NB: Sorting will remove targets as a bi-product of `printSort()`
- self.dom.parent.removeChild(target.dom.el);
- }
- self.targets.splice(i, 1);
- target.isInDom = false;
- i--;
- }
- }
- // Since targets have been removed, the original order must be updated
- self.origOrder = self.targets;
- }
- if (operation.willSort) {
- self.targets = operation.newOrder;
- }
- self.state = operation.newState;
- self.lastOperation = operation;
- self.dom.targets = self.state.targets;
- // mixEnd
- mixitup.events.fire('mixEnd', self.dom.container, {
- state: self.state,
- instance: self
- }, self.dom.document);
- if (typeof self.config.callbacks.onMixEnd === 'function') {
- self.config.callbacks.onMixEnd.call(self.dom.container, self.state, self);
- }
- if (operation.hasFailed) {
- // mixFail
- mixitup.events.fire('mixFail', self.dom.container, {
- state: self.state,
- instance: self
- }, self.dom.document);
- if (typeof self.config.callbacks.onMixFail === 'function') {
- self.config.callbacks.onMixFail.call(self.dom.container, self.state, self);
- }
- h.addClass(self.dom.container, h.getClassname(self.config.classNames, 'container', self.config.classNames.modifierFailed));
- }
- // User-defined callback function
- if (typeof self.userCallback === 'function') {
- self.userCallback.call(self.dom.container, self.state, self);
- }
- if (typeof self.userDeferred.resolve === 'function') {
- self.userDeferred.resolve(self.state);
- }
- self.userCallback = null;
- self.userDeferred = null;
- self.lastClicked = null;
- self.isToggling = false;
- self.isBusy = false;
- if (self.queue.length) {
- self.callActions('beforeReadQueueCleanUp', arguments);
- nextInQueue = self.queue.shift();
- // Update non-public API properties stored in queue
- self.userDeferred = nextInQueue.deferred;
- self.isToggling = nextInQueue.isToggling;
- self.lastClicked = nextInQueue.triggerElement;
- if (nextInQueue.instruction.command instanceof mixitup.CommandMultimix) {
- self.multimix.apply(self, nextInQueue.args);
- } else {
- self.dataset.apply(self, nextInQueue.args);
- }
- }
- self.callActions('afterCleanUp', arguments);
- },
- /**
- * @private
- * @instance
- * @since 2.0.0
- * @param {Array<*>} args
- * @return {mixitup.UserInstruction}
- */
- parseMultimixArgs: function(args) {
- var self = this,
- instruction = new mixitup.UserInstruction(),
- arg = null,
- i = -1;
- instruction.animate = self.config.animation.enable;
- instruction.command = new mixitup.CommandMultimix();
- for (i = 0; i < args.length; i++) {
- arg = args[i];
- if (arg === null) continue;
- if (typeof arg === 'object') {
- h.extend(instruction.command, arg);
- } else if (typeof arg === 'boolean') {
- instruction.animate = arg;
- } else if (typeof arg === 'function') {
- instruction.callback = arg;
- }
- }
- // Coerce arbitrary command arguments into typed command objects
- if (instruction.command.insert && !(instruction.command.insert instanceof mixitup.CommandInsert)) {
- instruction.command.insert = self.parseInsertArgs([instruction.command.insert]).command;
- }
- if (instruction.command.remove && !(instruction.command.remove instanceof mixitup.CommandRemove)) {
- instruction.command.remove = self.parseRemoveArgs([instruction.command.remove]).command;
- }
- if (instruction.command.filter && !(instruction.command.filter instanceof mixitup.CommandFilter)) {
- instruction.command.filter = self.parseFilterArgs([instruction.command.filter]).command;
- }
- if (instruction.command.sort && !(instruction.command.sort instanceof mixitup.CommandSort)) {
- instruction.command.sort = self.parseSortArgs([instruction.command.sort]).command;
- }
- if (instruction.command.changeLayout && !(instruction.command.changeLayout instanceof mixitup.CommandChangeLayout)) {
- instruction.command.changeLayout = self.parseChangeLayoutArgs([instruction.command.changeLayout]).command;
- }
- instruction = self.callFilters('instructionParseMultimixArgs', instruction, arguments);
- h.freeze(instruction);
- return instruction;
- },
- /**
- * @private
- * @instance
- * @since 2.0.0
- * @param {Array<*>} args
- * @return {mixitup.UserInstruction}
- */
- parseFilterArgs: function(args) {
- var self = this,
- instruction = new mixitup.UserInstruction(),
- arg = null,
- i = -1;
- instruction.animate = self.config.animation.enable;
- instruction.command = new mixitup.CommandFilter();
- for (i = 0; i < args.length; i++) {
- arg = args[i];
- if (typeof arg === 'string') {
- // Selector
- instruction.command.selector = arg;
- } else if (arg === null) {
- instruction.command.collection = [];
- } else if (typeof arg === 'object' && h.isElement(arg, self.dom.document)) {
- // Single element
- instruction.command.collection = [arg];
- } else if (typeof arg === 'object' && typeof arg.length !== 'undefined') {
- // Multiple elements in array, NodeList or jQuery collection
- instruction.command.collection = h.arrayFromList(arg);
- } else if (typeof arg === 'object') {
- // Filter command
- h.extend(instruction.command, arg);
- } else if (typeof arg === 'boolean') {
- instruction.animate = arg;
- } else if (typeof arg === 'function') {
- instruction.callback = arg;
- }
- }
- if (instruction.command.selector && instruction.command.collection) {
- throw new Error(mixitup.messages.errorFilterInvalidArguments());
- }
- instruction = self.callFilters('instructionParseFilterArgs', instruction, arguments);
- h.freeze(instruction);
- return instruction;
- },
- parseSortArgs: function(args) {
- var self = this,
- instruction = new mixitup.UserInstruction(),
- arg = null,
- sortString = '',
- i = -1;
- instruction.animate = self.config.animation.enable;
- instruction.command = new mixitup.CommandSort();
- for (i = 0; i < args.length; i++) {
- arg = args[i];
- if (arg === null) continue;
- switch (typeof arg) {
- case 'string':
- // Sort string
- sortString = arg;
- break;
- case 'object':
- // Array of element references
- if (arg.length) {
- instruction.command.collection = h.arrayFromList(arg);
- }
- break;
- case 'boolean':
- instruction.animate = arg;
- break;
- case 'function':
- instruction.callback = arg;
- break;
- }
- }
- if (sortString) {
- instruction.command = self.parseSortString(sortString, instruction.command);
- }
- instruction = self.callFilters('instructionParseSortArgs', instruction, arguments);
- h.freeze(instruction);
- return instruction;
- },
- /**
- * @private
- * @instance
- * @since 2.0.0
- * @param {Array<*>} args
- * @return {mixitup.UserInstruction}
- */
- parseInsertArgs: function(args) {
- var self = this,
- instruction = new mixitup.UserInstruction(),
- arg = null,
- i = -1;
- instruction.animate = self.config.animation.enable;
- instruction.command = new mixitup.CommandInsert();
- for (i = 0; i < args.length; i++) {
- arg = args[i];
- if (arg === null) continue;
- if (typeof arg === 'number') {
- // Insert index
- instruction.command.index = arg;
- } else if (typeof arg === 'string' && ['before', 'after'].indexOf(arg) > -1) {
- // 'before'/'after'
- instruction.command.position = arg;
- } else if (typeof arg === 'string') {
- // Markup
- instruction.command.collection =
- h.arrayFromList(h.createElement(arg).childNodes);
- } else if (typeof arg === 'object' && h.isElement(arg, self.dom.document)) {
- // Single element
- !instruction.command.collection.length ?
- (instruction.command.collection = [arg]) :
- (instruction.command.sibling = arg);
- } else if (typeof arg === 'object' && arg.length) {
- // Multiple elements in array or jQuery collection
- !instruction.command.collection.length ?
- (instruction.command.collection = arg) :
- instruction.command.sibling = arg[0];
- } else if (typeof arg === 'object' && arg.childNodes && arg.childNodes.length) {
- // Document fragment
- !instruction.command.collection.length ?
- instruction.command.collection = h.arrayFromList(arg.childNodes) :
- instruction.command.sibling = arg.childNodes[0];
- } else if (typeof arg === 'object') {
- // Insert command
- h.extend(instruction.command, arg);
- } else if (typeof arg === 'boolean') {
- instruction.animate = arg;
- } else if (typeof arg === 'function') {
- instruction.callback = arg;
- }
- }
- if (instruction.command.index && instruction.command.sibling) {
- throw new Error(mixitup.messages.errorInsertInvalidArguments());
- }
- if (!instruction.command.collection.length && self.config.debug.showWarnings) {
- console.warn(mixitup.messages.warningInsertNoElements());
- }
- instruction = self.callFilters('instructionParseInsertArgs', instruction, arguments);
- h.freeze(instruction);
- return instruction;
- },
- /**
- * @private
- * @instance
- * @since 3.0.0
- * @param {Array<*>} args
- * @return {mixitup.UserInstruction}
- */
- parseRemoveArgs: function(args) {
- var self = this,
- instruction = new mixitup.UserInstruction(),
- target = null,
- arg = null,
- i = -1;
- instruction.animate = self.config.animation.enable;
- instruction.command = new mixitup.CommandRemove();
- for (i = 0; i < args.length; i++) {
- arg = args[i];
- if (arg === null) continue;
- switch (typeof arg) {
- case 'number':
- if (self.targets[arg]) {
- instruction.command.targets[0] = self.targets[arg];
- }
- break;
- case 'string':
- instruction.command.collection = h.arrayFromList(self.dom.parent.querySelectorAll(arg));
- break;
- case 'object':
- if (arg && arg.length) {
- instruction.command.collection = arg;
- } else if (h.isElement(arg, self.dom.document)) {
- instruction.command.collection = [arg];
- } else {
- // Remove command
- h.extend(instruction.command, arg);
- }
- break;
- case 'boolean':
- instruction.animate = arg;
- break;
- case 'function':
- instruction.callback = arg;
- break;
- }
- }
- if (instruction.command.collection.length) {
- for (i = 0; target = self.targets[i]; i++) {
- if (instruction.command.collection.indexOf(target.dom.el) > -1) {
- instruction.command.targets.push(target);
- }
- }
- }
- if (!instruction.command.targets.length && self.config.debug.showWarnings) {
- console.warn(mixitup.messages.warningRemoveNoElements());
- }
- h.freeze(instruction);
- return instruction;
- },
- /**
- * @private
- * @instance
- * @since 3.0.0
- * @param {Array<*>} args
- * @return {mixitup.UserInstruction}
- */
- parseDatasetArgs: function(args) {
- var self = this,
- instruction = new mixitup.UserInstruction(),
- arg = null,
- i = -1;
- instruction.animate = self.config.animation.enable;
- instruction.command = new mixitup.CommandDataset();
- for (i = 0; i < args.length; i++) {
- arg = args[i];
- if (arg === null) continue;
- switch (typeof arg) {
- case 'object':
- if (Array.isArray(arg) || typeof arg.length === 'number') {
- instruction.command.dataset = arg;
- } else {
- // Change layout command
- h.extend(instruction.command, arg);
- }
- break;
- case 'boolean':
- instruction.animate = arg;
- break;
- case 'function':
- instruction.callback = arg;
- break;
- }
- }
- h.freeze(instruction);
- return instruction;
- },
- /**
- * @private
- * @instance
- * @since 3.0.0
- * @param {Array<*>} args
- * @return {mixitup.UserInstruction}
- */
- parseChangeLayoutArgs: function(args) {
- var self = this,
- instruction = new mixitup.UserInstruction(),
- arg = null,
- i = -1;
- instruction.animate = self.config.animation.enable;
- instruction.command = new mixitup.CommandChangeLayout();
- for (i = 0; i < args.length; i++) {
- arg = args[i];
- if (arg === null) continue;
- switch (typeof arg) {
- case 'string':
- instruction.command.containerClassName = arg;
- break;
- case 'object':
- // Change layout command
- h.extend(instruction.command, arg);
- break;
- case 'boolean':
- instruction.animate = arg;
- break;
- case 'function':
- instruction.callback = arg;
- break;
- }
- }
- h.freeze(instruction);
- return instruction;
- },
- /**
- * @private
- * @instance
- * @since 3.0.0
- * @param {mixitup.QueueItem} queueItem
- * @return {Promise.<mixitup.State>}
- */
- queueMix: function(queueItem) {
- var self = this,
- deferred = null,
- toggleSelector = '';
- self.callActions('beforeQueueMix', arguments);
- deferred = h.defer(mixitup.libraries);
- if (self.config.animation.queue && self.queue.length < self.config.animation.queueLimit) {
- queueItem.deferred = deferred;
- self.queue.push(queueItem);
- // Keep controls in sync with user interactions. Mixer will catch up as it drains the queue.
- if (self.config.controls.enable) {
- if (self.isToggling) {
- self.buildToggleArray(queueItem.instruction.command);
- toggleSelector = self.getToggleSelector();
- self.updateControls({
- filter: {
- selector: toggleSelector
- }
- });
- } else {
- self.updateControls(queueItem.instruction.command);
- }
- }
- } else {
- if (self.config.debug.showWarnings) {
- console.warn(mixitup.messages.warningMultimixInstanceQueueFull());
- }
- deferred.resolve(self.state);
- mixitup.events.fire('mixBusy', self.dom.container, {
- state: self.state,
- instance: self
- }, self.dom.document);
- if (typeof self.config.callbacks.onMixBusy === 'function') {
- self.config.callbacks.onMixBusy.call(self.dom.container, self.state, self);
- }
- }
- return self.callFilters('promiseQueueMix', deferred.promise, arguments);
- },
- /**
- * @private
- * @instance
- * @since 3.0.0
- * @param {Array.<object>} newDataset
- * @return {Operation}
- */
- getDataOperation: function(newDataset) {
- var self = this,
- operation = new mixitup.Operation(),
- startDataset = [];
- operation = self.callFilters('operationUnmappedGetDataOperation', operation, arguments);
- if (self.dom.targets.length && !(startDataset = (self.state.activeDataset || [])).length) {
- throw new Error(mixitup.messages.errorDatasetNotSet());
- }
- operation.id = h.randomHex();
- operation.startState = self.state;
- operation.startDataset = startDataset;
- operation.newDataset = newDataset.slice();
- self.diffDatasets(operation);
- operation.startOrder = self.targets;
- operation.newOrder = operation.show;
- if (self.config.animation.enable) {
- self.getStartMixData(operation);
- self.setInter(operation);
- operation.docState = h.getDocumentState(self.dom.document);
- self.getInterMixData(operation);
- self.setFinal(operation);
- self.getFinalMixData(operation);
- self.parseEffects();
- operation.hasEffect = self.hasEffect();
- self.getTweenData(operation);
- }
- self.targets = operation.show.slice();
- operation.newState = self.buildState(operation);
- // NB: Targets to be removed must be included in `self.targets` for removal during clean up,
- // but are added after state is built so that state is accurate
- Array.prototype.push.apply(self.targets, operation.toRemove);
- operation = self.callFilters('operationMappedGetDataOperation', operation, arguments);
- return operation;
- },
- /**
- * @private
- * @instance
- * @since 3.0.0
- * @param {mixitup.Operation} operation
- * @return {void}
- */
- diffDatasets: function(operation) {
- var self = this,
- persistantStartIds = [],
- persistantNewIds = [],
- insertedTargets = [],
- data = null,
- target = null,
- el = null,
- frag = null,
- nextEl = null,
- uids = {},
- id = '',
- i = -1;
- self.callActions('beforeDiffDatasets', arguments);
- for (i = 0; data = operation.newDataset[i]; i++) {
- if (typeof (id = data[self.config.data.uidKey]) === 'undefined' || id.toString().length < 1) {
- throw new TypeError(mixitup.messages.errorDatasetInvalidUidKey({
- uidKey: self.config.data.uidKey
- }));
- }
- if (!uids[id]) {
- uids[id] = true;
- } else {
- throw new Error(mixitup.messages.errorDatasetDuplicateUid({
- uid: id
- }));
- }
- if ((target = self.cache[id]) instanceof mixitup.Target) {
- // Already in cache
- if (self.config.data.dirtyCheck && !h.deepEquals(data, target.data)) {
- // change detected
- el = target.render(data);
- target.data = data;
- if (el !== target.dom.el) {
- // Update target element reference
- if (target.isInDom) {
- target.unbindEvents();
- self.dom.parent.replaceChild(el, target.dom.el);
- }
- if (!target.isShown) {
- el.style.display = 'none';
- }
- target.dom.el = el;
- if (target.isInDom) {
- target.bindEvents();
- }
- }
- }
- el = target.dom.el;
- } else {
- // New target
- target = new mixitup.Target();
- target.init(null, self, data);
- target.hide();
- }
- if (!target.isInDom) {
- // Adding to DOM
- if (!frag) {
- // Open frag
- frag = self.dom.document.createDocumentFragment();
- }
- if (frag.lastElementChild) {
- frag.appendChild(self.dom.document.createTextNode(' '));
- }
- frag.appendChild(target.dom.el);
- target.isInDom = true;
- target.unbindEvents();
- target.bindEvents();
- target.hide();
- operation.toShow.push(target);
- insertedTargets.push(target);
- } else {
- // Already in DOM
- nextEl = target.dom.el.nextElementSibling;
- persistantNewIds.push(id);
- if (frag) {
- // Close and insert previously opened frag
- if (frag.lastElementChild) {
- frag.appendChild(self.dom.document.createTextNode(' '));
- }
- self.insertDatasetFrag(frag, target.dom.el, insertedTargets);
- frag = null;
- }
- }
- operation.show.push(target);
- }
- if (frag) {
- // Unclosed frag remaining
- nextEl = nextEl || self.config.layout.siblingAfter;
- if (nextEl) {
- frag.appendChild(self.dom.document.createTextNode(' '));
- }
- self.insertDatasetFrag(frag, nextEl, insertedTargets);
- }
- for (i = 0; data = operation.startDataset[i]; i++) {
- id = data[self.config.data.uidKey];
- target = self.cache[id];
- if (operation.show.indexOf(target) < 0) {
- // Previously shown but now absent
- operation.hide.push(target);
- operation.toHide.push(target);
- operation.toRemove.push(target);
- } else {
- persistantStartIds.push(id);
- }
- }
- if (!h.isEqualArray(persistantStartIds, persistantNewIds)) {
- operation.willSort = true;
- }
- self.callActions('afterDiffDatasets', arguments);
- },
- /**
- * @private
- * @instance
- * @since 3.1.5
- * @param {DocumentFragment} frag
- * @param {(HTMLElement|null)} nextEl
- * @param {Array.<mixitup.Target>} targets
- * @return {void}
- */
- insertDatasetFrag: function(frag, nextEl, targets) {
- var self = this;
- var insertAt = nextEl ? Array.from(self.dom.parent.children).indexOf(nextEl) : self.targets.length;
- self.dom.parent.insertBefore(frag, nextEl);
- while (targets.length) {
- self.targets.splice(insertAt, 0, targets.shift());
- insertAt++;
- }
- },
- /**
- * @private
- * @instance
- * @since 3.0.0
- * @param {mixitup.CommandSort} sortCommandA
- * @param {mixitup.CommandSort} sortCommandB
- * @return {boolean}
- */
- willSort: function(sortCommandA, sortCommandB) {
- var self = this,
- result = false;
- if (
- self.config.behavior.liveSort ||
- sortCommandA.order === 'random' ||
- sortCommandA.attribute !== sortCommandB.attribute ||
- sortCommandA.order !== sortCommandB.order ||
- sortCommandA.collection !== sortCommandB.collection ||
- (sortCommandA.next === null && sortCommandB.next) ||
- (sortCommandA.next && sortCommandB.next === null)
- ) {
- result = true;
- } else if (sortCommandA.next && sortCommandB.next) {
- result = self.willSort(sortCommandA.next, sortCommandB.next);
- } else {
- result = false;
- }
- return self.callFilters('resultWillSort', result, arguments);
- },
- /**
- * A shorthand method for `.filter('all')`. Shows all targets in the container.
- *
- * @example
- *
- * .show()
- *
- * @example <caption>Example: Showing all targets</caption>
- *
- * mixer.show()
- * .then(function(state) {
- * console.log(state.totalShow === state.totalTargets); // true
- * });
- *
- * @public
- * @instance
- * @since 3.0.0
- * @return {Promise.<mixitup.State>}
- */
- show: function() {
- var self = this;
- return self.filter('all');
- },
- /**
- * A shorthand method for `.filter('none')`. Hides all targets in the container.
- *
- * @example
- *
- * .hide()
- *
- * @example <caption>Example: Hiding all targets</caption>
- *
- * mixer.hide()
- * .then(function(state) {
- * console.log(state.totalShow === 0); // true
- * console.log(state.totalHide === state.totalTargets); // true
- * });
- *
- * @public
- * @instance
- * @since 3.0.0
- * @return {Promise.<mixitup.State>}
- */
- hide: function() {
- var self = this;
- return self.filter('none');
- },
- /**
- * Returns a boolean indicating whether or not a MixItUp operation is
- * currently in progress.
- *
- * @example
- *
- * .isMixing()
- *
- * @example <caption>Example: Checking the status of a mixer</caption>
- *
- * mixer.sort('random', function() {
- * console.log(mixer.isMixing()) // false
- * });
- *
- * console.log(mixer.isMixing()) // true
- *
- * @public
- * @instance
- * @since 2.0.0
- * @return {boolean}
- */
- isMixing: function() {
- var self = this;
- return self.isBusy;
- },
- /**
- * Filters all targets in the container by a provided selector string, or the values `'all'`
- * or `'none'`. Only targets matching the selector will be shown.
- *
- * @example
- *
- * .filter(selector [, animate] [, callback])
- *
- * @example <caption>Example 1: Filtering targets by a class selector</caption>
- *
- * mixer.filter('.category-a')
- * .then(function(state) {
- * console.log(state.totalShow === containerEl.querySelectorAll('.category-a').length); // true
- * });
- *
- * @example <caption>Example 2: Filtering targets by an attribute selector</caption>
- *
- * mixer.filter('[data-category~="a"]')
- * .then(function(state) {
- * console.log(state.totalShow === containerEl.querySelectorAll('[data-category~="a"]').length); // true
- * });
- *
- * @example <caption>Example 3: Filtering targets by a compound selector</caption>
- *
- * // Show only those targets with the classes 'category-a' AND 'category-b'
- *
- * mixer.filter('.category-a.category-c')
- * .then(function(state) {
- * console.log(state.totalShow === containerEl.querySelectorAll('.category-a.category-c').length); // true
- * });
- *
- * @example <caption>Example 4: Filtering via an element collection</caption>
- *
- * var collection = Array.from(container.querySelectorAll('.mix'));
- *
- * console.log(collection.length); // 34
- *
- * // Filter the collection manually using Array.prototype.filter
- *
- * var filtered = collection.filter(function(target) {
- * return parseInt(target.getAttribute('data-price')) > 10;
- * });
- *
- * console.log(filtered.length); // 22
- *
- * // Pass the filtered collection to MixItUp
- *
- * mixer.filter(filtered)
- * .then(function(state) {
- * console.log(state.activeFilter.collection.length === 22); // true
- * });
- *
- * @public
- * @instance
- * @since 2.0.0
- * @param {(string|HTMLElement|Array.<HTMLElement>)} selector
- * Any valid CSS selector (i.e. `'.category-a'`), or the values `'all'` or `'none'`. The filter method also accepts a reference to single target element or a collection of target elements to show.
- * @param {boolean} [animate=true]
- * An optional boolean dictating whether the operation should animate, or occur syncronously with no animation. `true` by default.
- * @param {function} [callback=null]
- * An optional callback function to be invoked after the operation has completed.
- * @return {Promise.<mixitup.State>}
- * A promise resolving with the current state object.
- */
- filter: function() {
- var self = this,
- instruction = self.parseFilterArgs(arguments);
- return self.multimix({
- filter: instruction.command
- }, instruction.animate, instruction.callback);
- },
- /**
- * Adds an additional selector to the currently active filter selector, concatenating
- * as per the logic defined in `controls.toggleLogic`.
- *
- * @example
- *
- * .toggleOn(selector [, animate] [, callback])
- *
- * @example <caption>Example: Toggling on a filter selector</caption>
- *
- * console.log(mixer.getState().activeFilter.selector); // '.category-a'
- *
- * mixer.toggleOn('.category-b')
- * .then(function(state) {
- * console.log(state.activeFilter.selector); // '.category-a, .category-b'
- * });
- *
- * @public
- * @instance
- * @since 3.0.0
- * @param {string} selector
- * Any valid CSS selector (i.e. `'.category-a'`)
- * @param {boolean} [animate=true]
- * An optional boolean dictating whether the operation should animate, or occur syncronously with no animation. `true` by default.
- * @param {function} [callback=null]
- * An optional callback function to be invoked after the operation has completed.
- * @return {Promise.<mixitup.State>}
- * A promise resolving with the current state object.
- */
- toggleOn: function() {
- var self = this,
- instruction = self.parseFilterArgs(arguments),
- selector = instruction.command.selector,
- toggleSelector = '';
- self.isToggling = true;
- if (self.toggleArray.indexOf(selector) < 0) {
- self.toggleArray.push(selector);
- }
- toggleSelector = self.getToggleSelector();
- return self.multimix({
- filter: toggleSelector
- }, instruction.animate, instruction.callback);
- },
- /**
- * Removes a selector from the active filter selector.
- *
- * @example
- *
- * .toggleOff(selector [, animate] [, callback])
- *
- * @example <caption>Example: Toggling off a filter selector</caption>
- *
- * console.log(mixer.getState().activeFilter.selector); // '.category-a, .category-b'
- *
- * mixer.toggleOff('.category-b')
- * .then(function(state) {
- * console.log(state.activeFilter.selector); // '.category-a'
- * });
- *
- * @public
- * @instance
- * @since 3.0.0
- * @param {string} selector
- * Any valid CSS selector (i.e. `'.category-a'`)
- * @param {boolean} [animate=true]
- * An optional boolean dictating whether the operation should animate, or occur syncronously with no animation. `true` by default.
- * @param {function} [callback=null]
- * An optional callback function to be invoked after the operation has completed.
- * @return {Promise.<mixitup.State>}
- * A promise resolving with the current state object.
- */
- toggleOff: function() {
- var self = this,
- instruction = self.parseFilterArgs(arguments),
- selector = instruction.command.selector,
- selectorIndex = self.toggleArray.indexOf(selector),
- toggleSelector = '';
- self.isToggling = true;
- if (selectorIndex > -1) {
- self.toggleArray.splice(selectorIndex, 1);
- }
- toggleSelector = self.getToggleSelector();
- return self.multimix({
- filter: toggleSelector
- }, instruction.animate, instruction.callback);
- },
- /**
- * Sorts all targets in the container according to a provided sort string.
- *
- * @example
- *
- * .sort(sortString [, animate] [, callback])
- *
- * @example <caption>Example 1: Sorting by the default DOM order</caption>
- *
- * // Reverse the default order of the targets
- *
- * mixer.sort('default:desc')
- * .then(function(state) {
- * console.log(state.activeSort.attribute === 'default'); // true
- * console.log(state.activeSort.order === 'desc'); // true
- * });
- *
- * @example <caption>Example 2: Sorting by a custom data-attribute</caption>
- *
- * // Sort the targets by the value of a `data-published-date` attribute
- *
- * mixer.sort('published-date:asc')
- * .then(function(state) {
- * console.log(state.activeSort.attribute === 'published-date'); // true
- * console.log(state.activeSort.order === 'asc'); // true
- * });
- *
- * @example <caption>Example 3: Sorting by multiple attributes</caption>
- *
- * // Sort the targets by the value of a `data-published-date` attribute, then by `data-title`
- *
- * mixer.sort('published-date:desc data-title:asc')
- * .then(function(state) {
- * console.log(state.activeSort.attribute === 'published-date'); // true
- * console.log(state.activeSort.order === 'desc'); // true
- *
- * console.log(state.activeSort.next.attribute === 'title'); // true
- * console.log(state.activeSort.next.order === 'asc'); // true
- * });
- *
- * @example <caption>Example 4: Sorting by random</caption>
- *
- * mixer.sort('random')
- * .then(function(state) {
- * console.log(state.activeSort.order === 'random') // true
- * });
- *
- * @example <caption>Example 5: Sorting via an element collection</caption>
- *
- * var collection = Array.from(container.querySelectorAll('.mix'));
- *
- * // Swap the position of two elements in the collection:
- *
- * var temp = collection[1];
- *
- * collection[1] = collection[0];
- * collection[0] = temp;
- *
- * // Pass the sorted collection to MixItUp
- *
- * mixer.sort(collection)
- * .then(function(state) {
- * console.log(state.targets[0] === collection[0]); // true
- * });
- *
- * @public
- * @instance
- * @since 2.0.0
- * @param {(string|Array.<HTMLElement>)} sortString
- * A valid sort string (e.g. `'default'`, `'published-date:asc'`, or `'random'`). The sort method also accepts an array of all target elements in a user-defined order.
- * @param {boolean} [animate=true]
- * An optional boolean dictating whether the operation should animate, or occur syncronously with no animation. `true` by default.
- * @param {function} [callback=null]
- * An optional callback function to be invoked after the operation has completed.
- * @return {Promise.<mixitup.State>}
- * A promise resolving with the current state object.
- */
- sort: function() {
- var self = this,
- instruction = self.parseSortArgs(arguments);
- return self.multimix({
- sort: instruction.command
- }, instruction.animate, instruction.callback);
- },
- /**
- * Changes the layout of the container by adding, removing or updating a
- * layout-specific class name. If `animation.animateResizetargets` is
- * enabled, MixItUp will attempt to gracefully animate the width, height,
- * and position of targets between layout states.
- *
- * @example
- *
- * .changeLayout(containerClassName [, animate] [, callback])
- *
- * @example <caption>Example 1: Adding a new class name to the container</caption>
- *
- * mixer.changeLayout('container-list')
- * .then(function(state) {
- * console.log(state.activeContainerClass === 'container-list'); // true
- * });
- *
- * @example <caption>Example 2: Removing a previously added class name from the container</caption>
- *
- * mixer.changeLayout('')
- * .then(function(state) {
- * console.log(state.activeContainerClass === ''); // true
- * });
- *
- * @public
- * @instance
- * @since 2.0.0
- * @param {string} containerClassName
- * A layout-specific class name to add to the container.
- * @param {boolean} [animate=true]
- * An optional boolean dictating whether the operation should animate, or occur syncronously with no animation. `true` by default.
- * @param {function} [callback=null]
- * An optional callback function to be invoked after the operation has completed.
- * @return {Promise.<mixitup.State>}
- * A promise resolving with the current state object.
- */
- changeLayout: function() {
- var self = this,
- instruction = self.parseChangeLayoutArgs(arguments);
- return self.multimix({
- changeLayout: instruction.command
- }, instruction.animate, instruction.callback);
- },
- /**
- * Updates the contents and order of the container to reflect the provided dataset,
- * if the dataset API is in use.
- *
- * The dataset API is designed for use in API-driven JavaScript applications, and
- * can be used instead of DOM-based methods such as `.filter()`, `.sort()`,
- * `.insert()`, etc. When used, insertion, removal, sorting and pagination can be
- * achieved purely via changes to your data model, without the uglyness of having
- * to interact with or query the DOM directly.
- *
- * @example
- *
- * .dataset(dataset [, animate] [, callback])
- *
- * @example <caption>Example 1: Rendering a dataset</caption>
- *
- * var myDataset = [
- * {id: 1, ...},
- * {id: 2, ...},
- * {id: 3, ...}
- * ];
- *
- * mixer.dataset(myDataset)
- * .then(function(state) {
- * console.log(state.totalShow === 3); // true
- * });
- *
- * @example <caption>Example 2: Sorting a dataset</caption>
- *
- * // Create a new dataset in reverse order
- *
- * var newDataset = myDataset.slice().reverse();
- *
- * mixer.dataset(newDataset)
- * .then(function(state) {
- * console.log(state.activeDataset[0] === myDataset[2]); // true
- * });
- *
- * @example <caption>Example 3: Removing an item from the dataset</caption>
- *
- * console.log(myDataset.length); // 3
- *
- * // Create a new dataset with the last item removed.
- *
- * var newDataset = myDataset.slice().pop();
- *
- * mixer.dataset(newDataset)
- * .then(function(state) {
- * console.log(state.totalShow === 2); // true
- * });
- *
- * @public
- * @instance
- * @since 3.0.0
- * @param {Array.<object>} dataset
- * An array of objects, each one representing the underlying data model of a target to be rendered.
- * @param {boolean} [animate=true]
- * An optional boolean dictating whether the operation should animate, or occur syncronously with no animation. `true` by default.
- * @param {function} [callback=null]
- * An optional callback function to be invoked after the operation has completed.
- * @return {Promise.<mixitup.State>}
- * A promise resolving with the current state object.
- */
- dataset: function() {
- var self = this,
- instruction = self.parseDatasetArgs(arguments),
- operation = null,
- queueItem = null,
- animate = false;
- self.callActions('beforeDataset', arguments);
- if (!self.isBusy) {
- if (instruction.callback) self.userCallback = instruction.callback;
- animate = (instruction.animate ^ self.config.animation.enable) ? instruction.animate : self.config.animation.enable;
- operation = self.getDataOperation(instruction.command.dataset);
- return self.goMix(animate, operation);
- } else {
- queueItem = new mixitup.QueueItem();
- queueItem.args = arguments;
- queueItem.instruction = instruction;
- return self.queueMix(queueItem);
- }
- },
- /**
- * Performs simultaneous `filter`, `sort`, `insert`, `remove` and `changeLayout`
- * operations as requested.
- *
- * @example
- *
- * .multimix(multimixCommand [, animate] [, callback])
- *
- * @example <caption>Example 1: Performing simultaneous filtering and sorting</caption>
- *
- * mixer.multimix({
- * filter: '.category-b',
- * sort: 'published-date:desc'
- * })
- * .then(function(state) {
- * console.log(state.activeFilter.selector === '.category-b'); // true
- * console.log(state.activeSort.attribute === 'published-date'); // true
- * });
- *
- * @example <caption>Example 2: Performing simultaneous sorting, insertion, and removal</caption>
- *
- * console.log(mixer.getState().totalShow); // 6
- *
- * // NB: When inserting via `multimix()`, an object should be provided as the value
- * // for the `insert` portion of the command, allowing for a collection of elements
- * // and an insertion index to be specified.
- *
- * mixer.multimix({
- * sort: 'published-date:desc', // Sort the container, including any new elements
- * insert: {
- * collection: [newElementReferenceA, newElementReferenceB], // Add 2 new elements at index 5
- * index: 5
- * },
- * remove: existingElementReference // Remove 1 existing element
- * })
- * .then(function(state) {
- * console.log(state.activeSort.attribute === 'published-date'); // true
- * console.log(state.totalShow === 7); // true
- * });
- *
- * @public
- * @instance
- * @since 2.0.0
- * @param {object} multimixCommand
- * An object containing one or more things to do
- * @param {boolean} [animate=true]
- * An optional boolean dictating whether the operation should animate, or occur syncronously with no animation. `true` by default.
- * @param {function} [callback=null]
- * An optional callback function to be invoked after the operation has completed.
- * @return {Promise.<mixitup.State>}
- * A promise resolving with the current state object.
- */
- multimix: function() {
- var self = this,
- operation = null,
- animate = false,
- queueItem = null,
- instruction = self.parseMultimixArgs(arguments);
- self.callActions('beforeMultimix', arguments);
- if (!self.isBusy) {
- operation = self.getOperation(instruction.command);
- if (self.config.controls.enable) {
- // Update controls for API calls
- if (instruction.command.filter && !self.isToggling) {
- // As we are not toggling, reset the toggle array
- // so new filter overrides existing toggles
- self.toggleArray.length = 0;
- self.buildToggleArray(operation.command);
- }
- if (self.queue.length < 1) {
- self.updateControls(operation.command);
- }
- }
- if (instruction.callback) self.userCallback = instruction.callback;
- // Always allow the instruction to override the instance setting
- animate = (instruction.animate ^ self.config.animation.enable) ?
- instruction.animate :
- self.config.animation.enable;
- self.callFilters('operationMultimix', operation, arguments);
- return self.goMix(animate, operation);
- } else {
- queueItem = new mixitup.QueueItem();
- queueItem.args = arguments;
- queueItem.instruction = instruction;
- queueItem.triggerElement = self.lastClicked;
- queueItem.isToggling = self.isToggling;
- return self.queueMix(queueItem);
- }
- },
- /**
- * @private
- * @instance
- * @since 3.0.0
- * @param {object} multimixCommand
- * @param {boolean} [isPreFetch]
- * An optional boolean indicating that the operation is being pre-fetched for execution at a later time.
- * @return {Operation|null}
- */
- getOperation: function(multimixCommand) {
- var self = this,
- sortCommand = multimixCommand.sort,
- filterCommand = multimixCommand.filter,
- changeLayoutCommand = multimixCommand.changeLayout,
- removeCommand = multimixCommand.remove,
- insertCommand = multimixCommand.insert,
- operation = new mixitup.Operation();
- operation = self.callFilters('operationUnmappedGetOperation', operation, arguments);
- operation.id = h.randomHex();
- operation.command = multimixCommand;
- operation.startState = self.state;
- operation.triggerElement = self.lastClicked;
- if (self.isBusy) {
- if (self.config.debug.showWarnings) {
- console.warn(mixitup.messages.warningGetOperationInstanceBusy());
- }
- return null;
- }
- if (insertCommand) {
- self.insertTargets(insertCommand, operation);
- }
- if (removeCommand) {
- operation.toRemove = removeCommand.targets;
- }
- operation.startSort = operation.newSort = operation.startState.activeSort;
- operation.startOrder = operation.newOrder = self.targets;
- if (sortCommand) {
- operation.startSort = operation.startState.activeSort;
- operation.newSort = sortCommand;
- operation.willSort = self.willSort(sortCommand, operation.startState.activeSort);
- if (operation.willSort) {
- self.sortOperation(operation);
- }
- }
- operation.startFilter = operation.startState.activeFilter;
- if (filterCommand) {
- operation.newFilter = filterCommand;
- } else {
- operation.newFilter = h.extend(new mixitup.CommandFilter(), operation.startFilter);
- }
- if (operation.newFilter.selector === 'all') {
- operation.newFilter.selector = self.config.selectors.target;
- } else if (operation.newFilter.selector === 'none') {
- operation.newFilter.selector = '';
- }
- self.filterOperation(operation);
- operation.startContainerClassName = operation.startState.activeContainerClassName;
- if (changeLayoutCommand) {
- operation.newContainerClassName = changeLayoutCommand.containerClassName;
- if (operation.newContainerClassName !== operation.startContainerClassName) {
- operation.willChangeLayout = true;
- }
- } else {
- operation.newContainerClassName = operation.startContainerClassName;
- }
- if (self.config.animation.enable) {
- // Populate the operation's position data
- self.getStartMixData(operation);
- self.setInter(operation);
- operation.docState = h.getDocumentState(self.dom.document);
- self.getInterMixData(operation);
- self.setFinal(operation);
- self.getFinalMixData(operation);
- self.parseEffects();
- operation.hasEffect = self.hasEffect();
- self.getTweenData(operation);
- }
- if (operation.willSort) {
- self.targets = operation.newOrder;
- }
- operation.newState = self.buildState(operation);
- return self.callFilters('operationMappedGetOperation', operation, arguments);
- },
- /**
- * Renders a previously created operation at a specific point in its path, as
- * determined by a multiplier between 0 and 1.
- *
- * @example
- * .tween(operation, multiplier)
- *
- * @private
- * @instance
- * @since 3.0.0
- * @param {mixitup.Operation} operation
- * An operation object created via the `getOperation` method
- *
- * @param {Float} multiplier
- * Any number between 0 and 1 representing the percentage complete of the operation
- * @return {void}
- */
- tween: function(operation, multiplier) {
- var target = null,
- posData = null,
- toHideIndex = -1,
- i = -1;
- multiplier = Math.min(multiplier, 1);
- multiplier = Math.max(multiplier, 0);
- for (i = 0; target = operation.show[i]; i++) {
- posData = operation.showPosData[i];
- target.applyTween(posData, multiplier);
- }
- for (i = 0; target = operation.hide[i]; i++) {
- if (target.isShown) {
- target.hide();
- }
- if ((toHideIndex = operation.toHide.indexOf(target)) > -1) {
- posData = operation.toHidePosData[toHideIndex];
- if (!target.isShown) {
- target.show();
- }
- target.applyTween(posData, multiplier);
- }
- }
- },
- /**
- * Inserts one or more new target elements into the container at a specified
- * index.
- *
- * To be indexed as targets, new elements must match the `selectors.target`
- * selector (`'.mix'` by default).
- *
- * @example
- *
- * .insert(newElements [, index] [, animate], [, callback])
- *
- * @example <caption>Example 1: Inserting a single element via reference</caption>
- *
- * console.log(mixer.getState().totalShow); // 0
- *
- * // Create a new element
- *
- * var newElement = document.createElement('div');
- * newElement.classList.add('mix');
- *
- * mixer.insert(newElement)
- * .then(function(state) {
- * console.log(state.totalShow === 1); // true
- * });
- *
- * @example <caption>Example 2: Inserting a single element via HTML string</caption>
- *
- * console.log(mixer.getState().totalShow); // 1
- *
- * // Create a new element via reference
- *
- * var newElementHtml = '<div class="mix"></div>';
- *
- * // Create and insert the new element at index 1
- *
- * mixer.insert(newElementHtml, 1)
- * .then(function(state) {
- * console.log(state.totalShow === 2); // true
- * console.log(state.show[1].outerHTML === newElementHtml); // true
- * });
- *
- * @example <caption>Example 3: Inserting multiple elements via reference</caption>
- *
- * console.log(mixer.getState().totalShow); // 2
- *
- * // Create an array of new elements to insert.
- *
- * var newElement1 = document.createElement('div');
- * var newElement2 = document.createElement('div');
- *
- * newElement1.classList.add('mix');
- * newElement2.classList.add('mix');
- *
- * var newElementsCollection = [newElement1, newElement2];
- *
- * // Insert the new elements starting at index 1
- *
- * mixer.insert(newElementsCollection, 1)
- * .then(function(state) {
- * console.log(state.totalShow === 4); // true
- * console.log(state.show[1] === newElement1); // true
- * console.log(state.show[2] === newElement2); // true
- * });
- *
- * @example <caption>Example 4: Inserting a jQuery collection object containing one or more elements</caption>
- *
- * console.log(mixer.getState().totalShow); // 4
- *
- * var $newElement = $('<div class="mix"></div>');
- *
- * // Insert the new elements starting at index 3
- *
- * mixer.insert($newElement, 3)
- * .then(function(state) {
- * console.log(state.totalShow === 5); // true
- * console.log(state.show[3] === $newElement[0]); // true
- * });
- *
- * @public
- * @instance
- * @since 2.0.0
- * @param {(HTMLElement|Array.<HTMLElement>|string)} newElements
- * A reference to a single element to insert, an array-like collection of elements, or an HTML string representing a single element.
- * @param {number} index=0
- * The index at which to insert the new element(s). `0` by default.
- * @param {boolean} [animate=true]
- * An optional boolean dictating whether the operation should animate, or occur syncronously with no animation. `true` by default.
- * @param {function} [callback=null]
- * An optional callback function to be invoked after the operation has completed.
- * @return {Promise.<mixitup.State>}
- * A promise resolving with the current state object.
- */
- insert: function() {
- var self = this,
- args = self.parseInsertArgs(arguments);
- return self.multimix({
- insert: args.command
- }, args.animate, args.callback);
- },
- /**
- * Inserts one or more new elements before a provided reference element.
- *
- * @example
- *
- * .insertBefore(newElements, referenceElement [, animate] [, callback])
- *
- * @example <caption>Example: Inserting a new element before a reference element</caption>
- *
- * // An existing reference element is chosen at index 2
- *
- * var referenceElement = mixer.getState().show[2];
- *
- * // Create a new element
- *
- * var newElement = document.createElement('div');
- * newElement.classList.add('mix');
- *
- * mixer.insertBefore(newElement, referenceElement)
- * .then(function(state) {
- * // The new element is inserted into the container at index 2, before the reference element
- *
- * console.log(state.show[2] === newElement); // true
- *
- * // The reference element is now at index 3
- *
- * console.log(state.show[3] === referenceElement); // true
- * });
- *
- * @public
- * @instance
- * @since 3.0.0
- * @param {(HTMLElement|Array.<HTMLElement>|string)} newElements
- * A reference to a single element to insert, an array-like collection of elements, or an HTML string representing a single element.
- * @param {HTMLElement} referenceElement
- * A reference to an existing element in the container to insert new elements before.
- *@param {boolean} [animate=true]
- * An optional boolean dictating whether the operation should animate, or occur syncronously with no animation. `true` by default.
- * @param {function} [callback=null]
- * An optional callback function to be invoked after the operation has completed.
- * @return {Promise.<mixitup.State>}
- * A promise resolving with the current state object.
- */
- insertBefore: function() {
- var self = this,
- args = self.parseInsertArgs(arguments);
- return self.insert(args.command.collection, 'before', args.command.sibling, args.animate, args.callback);
- },
- /**
- * Inserts one or more new elements after a provided reference element.
- *
- * @example
- *
- * .insertAfter(newElements, referenceElement [, animate] [, callback])
- *
- * @example <caption>Example: Inserting a new element after a reference element</caption>
- *
- * // An existing reference element is chosen at index 2
- *
- * var referenceElement = mixer.getState().show[2];
- *
- * // Create a new element
- *
- * var newElement = document.createElement('div');
- * newElement.classList.add('mix');
- *
- * mixer.insertAfter(newElement, referenceElement)
- * .then(function(state) {
- * // The new element is inserted into the container at index 3, after the reference element
- *
- * console.log(state.show[3] === newElement); // true
- * });
- *
- * @public
- * @instance
- * @since 3.0.0
- * @param {(HTMLElement|Array.<HTMLElement>|string)} newElements
- * A reference to a single element to insert, an array-like collection of elements, or an HTML string representing a single element.
- * @param {HTMLElement} referenceElement
- * A reference to an existing element in the container to insert new elements after.
- * @param {boolean} [animate=true]
- * An optional boolean dictating whether the operation should animate, or occur syncronously with no animation. `true` by default.
- * @param {function} [callback=null]
- * An optional callback function to be invoked after the operation has completed.
- * @return {Promise.<mixitup.State>}
- * A promise resolving with the current state object.
- */
- insertAfter: function() {
- var self = this,
- args = self.parseInsertArgs(arguments);
- return self.insert(args.command.collection, 'after', args.command.sibling, args.animate, args.callback);
- },
- /**
- * Inserts one or more new elements into the container before all existing targets.
- *
- * @example
- *
- * .prepend(newElements [,animate] [,callback])
- *
- * @example <caption>Example: Prepending a new element</caption>
- *
- * // Create a new element
- *
- * var newElement = document.createElement('div');
- * newElement.classList.add('mix');
- *
- * // Insert the element into the container
- *
- * mixer.prepend(newElement)
- * .then(function(state) {
- * console.log(state.show[0] === newElement); // true
- * });
- *
- * @public
- * @instance
- * @since 3.0.0
- * @param {(HTMLElement|Array.<HTMLElement>|string)} newElements
- * A reference to a single element to insert, an array-like collection of elements, or an HTML string representing a single element.
- * @param {boolean} [animate=true]
- * An optional boolean dictating whether the operation should animate, or occur syncronously with no animation. `true` by default.
- * @param {function} [callback=null]
- * An optional callback function to be invoked after the operation has completed.
- * @return {Promise.<mixitup.State>}
- * A promise resolving with the current state object.
- */
- prepend: function() {
- var self = this,
- args = self.parseInsertArgs(arguments);
- return self.insert(0, args.command.collection, args.animate, args.callback);
- },
- /**
- * Inserts one or more new elements into the container after all existing targets.
- *
- * @example
- *
- * .append(newElements [,animate] [,callback])
- *
- * @example <caption>Example: Appending a new element</caption>
- *
- * // Create a new element
- *
- * var newElement = document.createElement('div');
- * newElement.classList.add('mix');
- *
- * // Insert the element into the container
- *
- * mixer.append(newElement)
- * .then(function(state) {
- * console.log(state.show[state.show.length - 1] === newElement); // true
- * });
- *
- * @public
- * @instance
- * @since 3.0.0
- * @param {(HTMLElement|Array.<HTMLElement>|string)} newElements
- * A reference to a single element to insert, an array-like collection of elements, or an HTML string representing a single element.
- * @param {boolean} [animate=true]
- * An optional boolean dictating whether the operation should animate, or occur syncronously with no animation. `true` by default.
- * @param {function} [callback=null]
- * An optional callback function to be invoked after the operation has completed.
- * @return {Promise.<mixitup.State>}
- * A promise resolving with the current state object.
- */
- append: function() {
- var self = this,
- args = self.parseInsertArgs(arguments);
- return self.insert(self.state.totalTargets, args.command.collection, args.animate, args.callback);
- },
- /**
- * Removes one or more existing target elements from the container.
- *
- * @example
- *
- * .remove(elements [, animate] [, callback])
- *
- * @example <caption>Example 1: Removing an element by reference</caption>
- *
- * var elementToRemove = containerEl.firstElementChild;
- *
- * mixer.remove(elementToRemove)
- * .then(function(state) {
- * console.log(state.targets.indexOf(elementToRemove) === -1); // true
- * });
- *
- * @example <caption>Example 2: Removing a collection of elements by reference</caption>
- *
- * var elementsToRemove = containerEl.querySelectorAll('.category-a');
- *
- * console.log(elementsToRemove.length) // 3
- *
- * mixer.remove(elementsToRemove)
- * .then(function() {
- * console.log(containerEl.querySelectorAll('.category-a').length); // 0
- * });
- *
- * @example <caption>Example 3: Removing one or more elements by selector</caption>
- *
- * mixer.remove('.category-a')
- * .then(function() {
- * console.log(containerEl.querySelectorAll('.category-a').length); // 0
- * });
- *
- * @example <caption>Example 4: Removing an element by index</caption>
- *
- * console.log(mixer.getState.totalShow); // 4
- *
- * // Remove the element at index 3
- *
- * mixer.remove(3)
- * .then(function(state) {
- * console.log(state.totalShow); // 3
- * console.log(state.show[3]); // undefined
- * });
- *
- *
- * @public
- * @instance
- * @since 3.0.0
- * @param {(HTMLElement|Array.<HTMLElement>|string|number)} elements
- * A reference to a single element to remove, an array-like collection of elements, a selector string, or the index of an element to remove.
- * @param {boolean} [animate=true]
- * An optional boolean dictating whether the operation should animate, or occur syncronously with no animation. `true` by default.
- * @param {function} [callback=null]
- * An optional callback function to be invoked after the operation has completed.
- * @return {Promise.<mixitup.State>}
- * A promise resolving with the current state object.
- */
- remove: function() {
- var self = this,
- args = self.parseRemoveArgs(arguments);
- return self.multimix({
- remove: args.command
- }, args.animate, args.callback);
- },
- /**
- * Retrieves the the value of any property or sub-object within the current
- * mixitup configuration, or the whole configuration object.
- *
- * @example
- *
- * .getConfig([stringKey])
- *
- * @example <caption>Example 1: retrieve the entire configuration object</caption>
- *
- * var config = mixer.getConfig(); // Config { ... }
- *
- * @example <caption>Example 2: retrieve a named sub-object of configuration object</caption>
- *
- * var animation = mixer.getConfig('animation'); // ConfigAnimation { ... }
- *
- * @example <caption>Example 3: retrieve a value of configuration object via a dot-notation string key</caption>
- *
- * var effects = mixer.getConfig('animation.effects'); // 'fade scale'
- *
- * @public
- * @instance
- * @since 2.0.0
- * @param {string} [stringKey] A "dot-notation" string key
- * @return {*}
- */
- getConfig: function(stringKey) {
- var self = this,
- value = null;
- if (!stringKey) {
- value = self.config;
- } else {
- value = h.getProperty(self.config, stringKey);
- }
- return self.callFilters('valueGetConfig', value, arguments);
- },
- /**
- * Updates the configuration of the mixer, after it has been instantiated.
- *
- * See the Configuration Object documentation for a full list of avilable
- * configuration options.
- *
- * @example
- *
- * .configure(config)
- *
- * @example <caption>Example 1: Updating animation options</caption>
- *
- * mixer.configure({
- * animation: {
- * effects: 'fade translateX(-100%)',
- * duration: 300
- * }
- * });
- *
- * @example <caption>Example 2: Removing a callback after it has been set</caption>
- *
- * var mixer;
- *
- * function handleMixEndOnce() {
- * // Do something ..
- *
- * // Then nullify the callback
- *
- * mixer.configure({
- * callbacks: {
- * onMixEnd: null
- * }
- * });
- * };
- *
- * // Instantiate a mixer with a callback defined
- *
- * mixer = mixitup(containerEl, {
- * callbacks: {
- * onMixEnd: handleMixEndOnce
- * }
- * });
- *
- * @public
- * @instance
- * @since 3.0.0
- * @param {object} config
- * An object containing one of more configuration options.
- * @return {void}
- */
- configure: function(config) {
- var self = this;
- self.callActions('beforeConfigure', arguments);
- h.extend(self.config, config, true, true);
- self.callActions('afterConfigure', arguments);
- },
- /**
- * Returns an object containing information about the current state of the
- * mixer. See the State Object documentation for more information.
- *
- * NB: State objects are immutable and should therefore be regenerated
- * after any operation.
- *
- * @example
- *
- * .getState();
- *
- * @example <caption>Example: Retrieving a state object</caption>
- *
- * var state = mixer.getState();
- *
- * console.log(state.totalShow + 'targets are currently shown');
- *
- * @public
- * @instance
- * @since 2.0.0
- * @return {mixitup.State} An object reflecting the current state of the mixer.
- */
- getState: function() {
- var self = this,
- state = null;
- state = new mixitup.State();
- h.extend(state, self.state);
- h.freeze(state);
- return self.callFilters('stateGetState', state, arguments);
- },
- /**
- * Forces the re-indexing all targets within the container.
- *
- * This should only be used if some other piece of code in your application
- * has manipulated the contents of your container, which should be avoided.
- *
- * If you need to add or remove target elements from the container, use
- * the built-in `.insert()` or `.remove()` methods, and MixItUp will keep
- * itself up to date.
- *
- * @example
- *
- * .forceRefresh()
- *
- * @example <caption>Example: Force refreshing the mixer after external DOM manipulation</caption>
- *
- * console.log(mixer.getState().totalShow); // 3
- *
- * // An element is removed from the container via some external DOM manipulation code:
- *
- * containerEl.removeChild(containerEl.firstElementChild);
- *
- * // The mixer does not know that the number of targets has changed:
- *
- * console.log(mixer.getState().totalShow); // 3
- *
- * mixer.forceRefresh();
- *
- * // After forceRefresh, the mixer is in sync again:
- *
- * console.log(mixer.getState().totalShow); // 2
- *
- * @public
- * @instance
- * @since 2.1.2
- * @return {void}
- */
- forceRefresh: function() {
- var self = this;
- self.indexTargets();
- },
- /**
- * Forces the re-rendering of all targets when using the Dataset API.
- *
- * By default, targets are only re-rendered when `data.dirtyCheck` is
- * enabled, and an item's data has changed when `dataset()` is called.
- *
- * The `forceRender()` method allows for the re-rendering of all targets
- * in response to some arbitrary event, such as the changing of the target
- * render function.
- *
- * Targets are rendered against their existing data.
- *
- * @example
- *
- * .forceRender()
- *
- * @example <caption>Example: Force render targets after changing the target render function</caption>
- *
- * console.log(container.innerHTML); // ... <span class="mix">Foo</span> ...
- *
- * mixer.configure({
- * render: {
- * target: (item) => `<a href="/${item.slug}/" class="mix">${item.title}</a>`
- * }
- * });
- *
- * mixer.forceRender();
- *
- * console.log(container.innerHTML); // ... <a href="/foo/" class="mix">Foo</a> ...
- *
- * @public
- * @instance
- * @since 3.2.1
- * @return {void}
- */
- forceRender: function() {
- var self = this,
- target = null,
- el = null,
- id = '';
- for (id in self.cache) {
- target = self.cache[id];
- el = target.render(target.data);
- if (el !== target.dom.el) {
- // Update target element reference
- if (target.isInDom) {
- target.unbindEvents();
- self.dom.parent.replaceChild(el, target.dom.el);
- }
- if (!target.isShown) {
- el.style.display = 'none';
- }
- target.dom.el = el;
- if (target.isInDom) {
- target.bindEvents();
- }
- }
- }
- self.state = self.buildState(self.lastOperation);
- },
- /**
- * Removes mixitup functionality from the container, unbinds all control
- * event handlers, and deletes the mixer instance from MixItUp's internal
- * cache.
- *
- * This should be performed whenever a mixer's container is removed from
- * the DOM, such as during a page change in a single page application,
- * or React's `componentWillUnmount()`.
- *
- * @example
- *
- * .destroy([cleanUp])
- *
- * @example <caption>Example: Destroying the mixer before removing its container element</caption>
- *
- * mixer.destroy();
- *
- * containerEl.parentElement.removeChild(containerEl);
- *
- * @public
- * @instance
- * @since 2.0.0
- * @param {boolean} [cleanUp=false]
- * An optional boolean dictating whether or not to clean up any inline `display: none;` styling applied to hidden targets.
- * @return {void}
- */
- destroy: function(cleanUp) {
- var self = this,
- control = null,
- target = null,
- i = 0;
- self.callActions('beforeDestroy', arguments);
- for (i = 0; control = self.controls[i]; i++) {
- control.removeBinding(self);
- }
- for (i = 0; target = self.targets[i]; i++) {
- if (cleanUp) {
- target.show();
- }
- target.unbindEvents();
- }
- if (self.dom.container.id.match(/^MixItUp/)) {
- self.dom.container.removeAttribute('id');
- }
- delete mixitup.instances[self.id];
- self.callActions('afterDestroy', arguments);
- }
- });
- /**
- * @constructor
- * @memberof mixitup
- * @private
- * @since 3.0.0
- */
- mixitup.IMoveData = function() {
- mixitup.Base.call(this);
- this.callActions('beforeConstruct');
- this.posIn = null;
- this.posOut = null;
- this.operation = null;
- this.callback = null;
- this.statusChange = '';
- this.duration = -1;
- this.staggerIndex = -1;
- this.callActions('afterConstruct');
- h.seal(this);
- };
- mixitup.BaseStatic.call(mixitup.IMoveData);
- mixitup.IMoveData.prototype = Object.create(mixitup.Base.prototype);
- mixitup.IMoveData.prototype.constructor = mixitup.IMoveData;
- /**
- * @constructor
- * @memberof mixitup
- * @private
- * @since 3.0.0
- */
- mixitup.TargetDom = function() {
- mixitup.Base.call(this);
- this.callActions('beforeConstruct');
- this.el = null;
- this.callActions('afterConstruct');
- h.seal(this);
- };
- mixitup.BaseStatic.call(mixitup.TargetDom);
- mixitup.TargetDom.prototype = Object.create(mixitup.Base.prototype);
- mixitup.TargetDom.prototype.constructor = mixitup.TargetDom;
- /**
- * @constructor
- * @namespace
- * @memberof mixitup
- * @private
- * @since 3.0.0
- */
- mixitup.Target = function() {
- mixitup.Base.call(this);
- this.callActions('beforeConstruct');
- this.id = '';
- this.sortString = '';
- this.mixer = null;
- this.callback = null;
- this.isShown = false;
- this.isBound = false;
- this.isExcluded = false;
- this.isInDom = false;
- this.handler = null;
- this.operation = null;
- this.data = null;
- this.dom = new mixitup.TargetDom();
- this.callActions('afterConstruct');
- h.seal(this);
- };
- mixitup.BaseStatic.call(mixitup.Target);
- mixitup.Target.prototype = Object.create(mixitup.Base.prototype);
- h.extend(mixitup.Target.prototype, {
- constructor: mixitup.Target,
- /**
- * Initialises a newly instantiated Target.
- *
- * @private
- * @instance
- * @since 3.0.0
- * @param {(Element|null)} el
- * @param {object} mixer
- * @param {object} [data]
- * @return {void}
- */
- init: function(el, mixer, data) {
- var self = this,
- id = '';
- self.callActions('beforeInit', arguments);
- self.mixer = mixer;
- if (!el) {
- // If no element is provided, render it
- el = self.render(data);
- }
- self.cacheDom(el);
- self.bindEvents();
- if (self.dom.el.style.display !== 'none') {
- self.isShown = true;
- }
- if (data && mixer.config.data.uidKey) {
- if (typeof (id = data[mixer.config.data.uidKey]) === 'undefined' || id.toString().length < 1) {
- throw new TypeError(mixitup.messages.errorDatasetInvalidUidKey({
- uidKey: mixer.config.data.uidKey
- }));
- }
- self.id = id;
- self.data = data;
- mixer.cache[id] = self;
- }
- self.callActions('afterInit', arguments);
- },
- /**
- * Renders the target element using a user-defined renderer function.
- *
- * @private
- * @instance
- * @since 3.1.4
- * @param {object} data
- * @return {void}
- */
- render: function(data) {
- var self = this,
- render = null,
- el = null,
- temp = null,
- output = '';
- self.callActions('beforeRender', arguments);
- render = self.callFilters('renderRender', self.mixer.config.render.target, arguments);
- if (typeof render !== 'function') {
- throw new TypeError(mixitup.messages.errorDatasetRendererNotSet());
- }
- output = render(data);
- if (output && typeof output === 'object' && h.isElement(output)) {
- el = output;
- } else if (typeof output === 'string') {
- temp = document.createElement('div');
- temp.innerHTML = output;
- el = temp.firstElementChild;
- }
- return self.callFilters('elRender', el, arguments);
- },
- /**
- * Caches references of DOM elements neccessary for the target's functionality.
- *
- * @private
- * @instance
- * @since 3.0.0
- * @param {Element} el
- * @return {void}
- */
- cacheDom: function(el) {
- var self = this;
- self.callActions('beforeCacheDom', arguments);
- self.dom.el = el;
- self.callActions('afterCacheDom', arguments);
- },
- /**
- * @private
- * @instance
- * @since 3.0.0
- * @param {string} attributeName
- * @return {void}
- */
- getSortString: function(attributeName) {
- var self = this,
- value = self.dom.el.getAttribute('data-' + attributeName) || '';
- self.callActions('beforeGetSortString', arguments);
- value = isNaN(value * 1) ?
- value.toLowerCase() :
- value * 1;
- self.sortString = value;
- self.callActions('afterGetSortString', arguments);
- },
- /**
- * @private
- * @instance
- * @since 3.0.0
- * @return {void}
- */
- show: function() {
- var self = this;
- self.callActions('beforeShow', arguments);
- if (!self.isShown) {
- self.dom.el.style.display = '';
- self.isShown = true;
- }
- self.callActions('afterShow', arguments);
- },
- /**
- * @private
- * @instance
- * @since 3.0.0
- * @return {void}
- */
- hide: function() {
- var self = this;
- self.callActions('beforeHide', arguments);
- if (self.isShown) {
- self.dom.el.style.display = 'none';
- self.isShown = false;
- }
- self.callActions('afterHide', arguments);
- },
- /**
- * @private
- * @instance
- * @since 3.0.0
- * @param {mixitup.IMoveData} moveData
- * @return {void}
- */
- move: function(moveData) {
- var self = this;
- self.callActions('beforeMove', arguments);
- if (!self.isExcluded) {
- self.mixer.targetsMoved++;
- }
- self.applyStylesIn(moveData);
- requestAnimationFrame(function() {
- self.applyStylesOut(moveData);
- });
- self.callActions('afterMove', arguments);
- },
- /**
- * @private
- * @instance
- * @since 3.0.0
- * @param {object} posData
- * @param {number} multiplier
- * @return {void}
- */
- applyTween: function(posData, multiplier) {
- var self = this,
- propertyName = '',
- tweenData = null,
- posIn = posData.posIn,
- currentTransformValues = [],
- currentValues = new mixitup.StyleData(),
- i = -1;
- self.callActions('beforeApplyTween', arguments);
- currentValues.x = posIn.x;
- currentValues.y = posIn.y;
- if (multiplier === 0) {
- self.hide();
- } else if (!self.isShown) {
- self.show();
- }
- for (i = 0; propertyName = mixitup.features.TWEENABLE[i]; i++) {
- tweenData = posData.tweenData[propertyName];
- if (propertyName === 'x') {
- if (!tweenData) continue;
- currentValues.x = posIn.x + (tweenData * multiplier);
- } else if (propertyName === 'y') {
- if (!tweenData) continue;
- currentValues.y = posIn.y + (tweenData * multiplier);
- } else if (tweenData instanceof mixitup.TransformData) {
- if (!tweenData.value) continue;
- currentValues[propertyName].value =
- posIn[propertyName].value + (tweenData.value * multiplier);
- currentValues[propertyName].unit = tweenData.unit;
- currentTransformValues.push(
- propertyName + '(' + currentValues[propertyName].value + tweenData.unit + ')'
- );
- } else {
- if (!tweenData) continue;
- currentValues[propertyName] = posIn[propertyName] + (tweenData * multiplier);
- self.dom.el.style[propertyName] = currentValues[propertyName];
- }
- }
- if (currentValues.x || currentValues.y) {
- currentTransformValues.unshift('translate(' + currentValues.x + 'px, ' + currentValues.y + 'px)');
- }
- if (currentTransformValues.length) {
- self.dom.el.style[mixitup.features.transformProp] = currentTransformValues.join(' ');
- }
- self.callActions('afterApplyTween', arguments);
- },
- /**
- * Applies the initial styling to a target element before any transition
- * is applied.
- *
- * @private
- * @instance
- * @param {mixitup.IMoveData} moveData
- * @return {void}
- */
- applyStylesIn: function(moveData) {
- var self = this,
- posIn = moveData.posIn,
- isFading = self.mixer.effectsIn.opacity !== 1,
- transformValues = [];
- self.callActions('beforeApplyStylesIn', arguments);
- transformValues.push('translate(' + posIn.x + 'px, ' + posIn.y + 'px)');
- if (self.mixer.config.animation.animateResizeTargets) {
- if (moveData.statusChange !== 'show') {
- // Don't apply posIn width or height or showing, as will be 0
- self.dom.el.style.width = posIn.width + 'px';
- self.dom.el.style.height = posIn.height + 'px';
- }
- self.dom.el.style.marginRight = posIn.marginRight + 'px';
- self.dom.el.style.marginBottom = posIn.marginBottom + 'px';
- }
- isFading && (self.dom.el.style.opacity = posIn.opacity);
- if (moveData.statusChange === 'show') {
- transformValues = transformValues.concat(self.mixer.transformIn);
- }
- self.dom.el.style[mixitup.features.transformProp] = transformValues.join(' ');
- self.callActions('afterApplyStylesIn', arguments);
- },
- /**
- * Applies a transition followed by the final styles for the element to
- * transition towards.
- *
- * @private
- * @instance
- * @param {mixitup.IMoveData} moveData
- * @return {void}
- */
- applyStylesOut: function(moveData) {
- var self = this,
- transitionRules = [],
- transformValues = [],
- isResizing = self.mixer.config.animation.animateResizeTargets,
- isFading = typeof self.mixer.effectsIn.opacity !== 'undefined';
- self.callActions('beforeApplyStylesOut', arguments);
- // Build the transition rules
- transitionRules.push(self.writeTransitionRule(
- mixitup.features.transformRule,
- moveData.staggerIndex
- ));
- if (moveData.statusChange !== 'none') {
- transitionRules.push(self.writeTransitionRule(
- 'opacity',
- moveData.staggerIndex,
- moveData.duration
- ));
- }
- if (isResizing) {
- transitionRules.push(self.writeTransitionRule(
- 'width',
- moveData.staggerIndex,
- moveData.duration
- ));
- transitionRules.push(self.writeTransitionRule(
- 'height',
- moveData.staggerIndex,
- moveData.duration
- ));
- transitionRules.push(self.writeTransitionRule(
- 'margin',
- moveData.staggerIndex,
- moveData.duration
- ));
- }
- // If no callback was provided, the element will
- // not transition in any way so tag it as "immovable"
- if (!moveData.callback) {
- self.mixer.targetsImmovable++;
- if (self.mixer.targetsMoved === self.mixer.targetsImmovable) {
- // If the total targets moved is equal to the
- // number of immovable targets, the operation
- // should be considered finished
- self.mixer.cleanUp(moveData.operation);
- }
- return;
- }
- // If the target will transition in some fasion,
- // assign a callback function
- self.operation = moveData.operation;
- self.callback = moveData.callback;
- // As long as the target is not excluded, increment
- // the total number of targets bound
- !self.isExcluded && self.mixer.targetsBound++;
- // Tag the target as bound to differentiate from transitionEnd
- // events that may come from stylesheet driven effects
- self.isBound = true;
- // Apply the transition
- self.applyTransition(transitionRules);
- // Apply width, height and margin negation
- if (isResizing && moveData.posOut.width > 0 && moveData.posOut.height > 0) {
- self.dom.el.style.width = moveData.posOut.width + 'px';
- self.dom.el.style.height = moveData.posOut.height + 'px';
- self.dom.el.style.marginRight = moveData.posOut.marginRight + 'px';
- self.dom.el.style.marginBottom = moveData.posOut.marginBottom + 'px';
- }
- if (!self.mixer.config.animation.nudge && moveData.statusChange === 'hide') {
- // If we're not nudging, the translation should be
- // applied before any other transforms to prevent
- // lateral movement
- transformValues.push('translate(' + moveData.posOut.x + 'px, ' + moveData.posOut.y + 'px)');
- }
- // Apply fade
- switch (moveData.statusChange) {
- case 'hide':
- isFading && (self.dom.el.style.opacity = self.mixer.effectsOut.opacity);
- transformValues = transformValues.concat(self.mixer.transformOut);
- break;
- case 'show':
- isFading && (self.dom.el.style.opacity = 1);
- }
- if (
- self.mixer.config.animation.nudge ||
- (!self.mixer.config.animation.nudge && moveData.statusChange !== 'hide')
- ) {
- // Opposite of above - apply translate after
- // other transform
- transformValues.push('translate(' + moveData.posOut.x + 'px, ' + moveData.posOut.y + 'px)');
- }
- // Apply transforms
- self.dom.el.style[mixitup.features.transformProp] = transformValues.join(' ');
- self.callActions('afterApplyStylesOut', arguments);
- },
- /**
- * Combines the name of a CSS property with the appropriate duration and delay
- * values to created a valid transition rule.
- *
- * @private
- * @instance
- * @since 3.0.0
- * @param {string} property
- * @param {number} staggerIndex
- * @param {number} duration
- * @return {string}
- */
- writeTransitionRule: function(property, staggerIndex, duration) {
- var self = this,
- delay = self.getDelay(staggerIndex),
- rule = '';
- rule = property + ' ' +
- (duration > 0 ? duration : self.mixer.config.animation.duration) + 'ms ' +
- delay + 'ms ' +
- (property === 'opacity' ? 'linear' : self.mixer.config.animation.easing);
- return self.callFilters('ruleWriteTransitionRule', rule, arguments);
- },
- /**
- * Calculates the transition delay for each target element based on its index, if
- * staggering is applied. If defined, A custom `animation.staggerSeqeuence`
- * function can be used to manipulate the order of indices to produce custom
- * stagger effects (e.g. for use in a grid with irregular row lengths).
- *
- * @private
- * @instance
- * @since 2.0.0
- * @param {number} index
- * @return {number}
- */
- getDelay: function(index) {
- var self = this,
- delay = -1;
- if (typeof self.mixer.config.animation.staggerSequence === 'function') {
- index = self.mixer.config.animation.staggerSequence.call(self, index, self.state);
- }
- delay = !!self.mixer.staggerDuration ? index * self.mixer.staggerDuration : 0;
- return self.callFilters('delayGetDelay', delay, arguments);
- },
- /**
- * @private
- * @instance
- * @since 3.0.0
- * @param {string[]} rules
- * @return {void}
- */
- applyTransition: function(rules) {
- var self = this,
- transitionString = rules.join(', ');
- self.callActions('beforeApplyTransition', arguments);
- self.dom.el.style[mixitup.features.transitionProp] = transitionString;
- self.callActions('afterApplyTransition', arguments);
- },
- /**
- * @private
- * @instance
- * @since 3.0.0
- * @param {Event} e
- * @return {void}
- */
- handleTransitionEnd: function(e) {
- var self = this,
- propName = e.propertyName,
- canResize = self.mixer.config.animation.animateResizeTargets;
- self.callActions('beforeHandleTransitionEnd', arguments);
- if (
- self.isBound &&
- e.target.matches(self.mixer.config.selectors.target) &&
- (
- propName.indexOf('transform') > -1 ||
- propName.indexOf('opacity') > -1 ||
- canResize && propName.indexOf('height') > -1 ||
- canResize && propName.indexOf('width') > -1 ||
- canResize && propName.indexOf('margin') > -1
- )
- ) {
- self.callback.call(self, self.operation);
- self.isBound = false;
- self.callback = null;
- self.operation = null;
- }
- self.callActions('afterHandleTransitionEnd', arguments);
- },
- /**
- * @private
- * @instance
- * @since 3.0.0
- * @param {Event} e
- * @return {void}
- */
- eventBus: function(e) {
- var self = this;
- self.callActions('beforeEventBus', arguments);
- switch (e.type) {
- case 'webkitTransitionEnd':
- case 'transitionend':
- self.handleTransitionEnd(e);
- }
- self.callActions('afterEventBus', arguments);
- },
- /**
- * @private
- * @instance
- * @since 3.0.0
- * @return {void}
- */
- unbindEvents: function() {
- var self = this;
- self.callActions('beforeUnbindEvents', arguments);
- h.off(self.dom.el, 'webkitTransitionEnd', self.handler);
- h.off(self.dom.el, 'transitionend', self.handler);
- self.callActions('afterUnbindEvents', arguments);
- },
- /**
- * @private
- * @instance
- * @since 3.0.0
- * @return {void}
- */
- bindEvents: function() {
- var self = this,
- transitionEndEvent = '';
- self.callActions('beforeBindEvents', arguments);
- transitionEndEvent = mixitup.features.transitionPrefix === 'webkit' ? 'webkitTransitionEnd' : 'transitionend';
- self.handler = function(e) {
- return self.eventBus(e);
- };
- h.on(self.dom.el, transitionEndEvent, self.handler);
- self.callActions('afterBindEvents', arguments);
- },
- /**
- * @private
- * @instance
- * @since 3.0.0
- * @param {boolean} [getBox]
- * @return {PosData}
- */
- getPosData: function(getBox) {
- var self = this,
- styles = {},
- rect = null,
- posData = new mixitup.StyleData();
- self.callActions('beforeGetPosData', arguments);
- posData.x = self.dom.el.offsetLeft;
- posData.y = self.dom.el.offsetTop;
- if (self.mixer.config.animation.animateResizeTargets || getBox) {
- rect = self.dom.el.getBoundingClientRect();
- posData.top = rect.top;
- posData.right = rect.right;
- posData.bottom = rect.bottom;
- posData.left = rect.left;
- posData.width = rect.width;
- posData.height = rect.height;
- }
- if (self.mixer.config.animation.animateResizeTargets) {
- styles = window.getComputedStyle(self.dom.el);
- posData.marginBottom = parseFloat(styles.marginBottom);
- posData.marginRight = parseFloat(styles.marginRight);
- }
- return self.callFilters('posDataGetPosData', posData, arguments);
- },
- /**
- * @private
- * @instance
- * @since 3.0.0
- * @return {void}
- */
- cleanUp: function() {
- var self = this;
- self.callActions('beforeCleanUp', arguments);
- self.dom.el.style[mixitup.features.transformProp] = '';
- self.dom.el.style[mixitup.features.transitionProp] = '';
- self.dom.el.style.opacity = '';
- if (self.mixer.config.animation.animateResizeTargets) {
- self.dom.el.style.width = '';
- self.dom.el.style.height = '';
- self.dom.el.style.marginRight = '';
- self.dom.el.style.marginBottom = '';
- }
- self.callActions('afterCleanUp', arguments);
- }
- });
- /**
- * A jQuery-collection-like wrapper around one or more `mixitup.Mixer` instances
- * allowing simultaneous control of said instances similar to the MixItUp 2 API.
- *
- * @example
- * new mixitup.Collection(instances)
- *
- * @constructor
- * @namespace
- * @memberof mixitup
- * @private
- * @since 3.0.0
- * @param {mixitup.Mixer[]} instances
- */
- mixitup.Collection = function(instances) {
- var instance = null,
- i = -1;
- this.callActions('beforeConstruct');
- for (i = 0; instance = instances[i]; i++) {
- this[i] = instance;
- }
- this.length = instances.length;
- this.callActions('afterConstruct');
- h.freeze(this);
- };
- mixitup.BaseStatic.call(mixitup.Collection);
- mixitup.Collection.prototype = Object.create(mixitup.Base.prototype);
- h.extend(mixitup.Collection.prototype,
- /** @lends mixitup.Collection */
- {
- constructor: mixitup.Collection,
- /**
- * Calls a method on all instances in the collection by passing the method
- * name as a string followed by any applicable parameters to be curried into
- * to the method.
- *
- * @example
- * .mixitup(methodName[,arg1][,arg2..]);
- *
- * @example
- * var collection = new Collection([mixer1, mixer2]);
- *
- * return collection.mixitup('filter', '.category-a')
- * .then(function(states) {
- * state.forEach(function(state) {
- * console.log(state.activeFilter.selector); // .category-a
- * });
- * });
- *
- * @public
- * @instance
- * @since 3.0.0
- * @param {string} methodName
- * @return {Promise<Array<mixitup.State>>}
- */
- mixitup: function(methodName) {
- var self = this,
- instance = null,
- args = Array.prototype.slice.call(arguments),
- tasks = [],
- i = -1;
- this.callActions('beforeMixitup');
- args.shift();
- for (i = 0; instance = self[i]; i++) {
- tasks.push(instance[methodName].apply(instance, args));
- }
- return self.callFilters('promiseMixitup', h.all(tasks, mixitup.libraries), arguments);
- }
- });
- /**
- * `mixitup.Operation` objects contain all data neccessary to describe the full
- * lifecycle of any MixItUp operation. They can be used to compute and store an
- * operation for use at a later time (e.g. programmatic tweening).
- *
- * @constructor
- * @namespace
- * @memberof mixitup
- * @private
- * @since 3.0.0
- */
- mixitup.Operation = function() {
- mixitup.Base.call(this);
- this.callActions('beforeConstruct');
- this.id = '';
- this.args = [];
- this.command = null;
- this.showPosData = [];
- this.toHidePosData = [];
- this.startState = null;
- this.newState = null;
- this.docState = null;
- this.willSort = false;
- this.willChangeLayout = false;
- this.hasEffect = false;
- this.hasFailed = false;
- this.triggerElement = null;
- this.show = [];
- this.hide = [];
- this.matching = [];
- this.toShow = [];
- this.toHide = [];
- this.toMove = [];
- this.toRemove = [];
- this.startOrder = [];
- this.newOrder = [];
- this.startSort = null;
- this.newSort = null;
- this.startFilter = null;
- this.newFilter = null;
- this.startDataset = null;
- this.newDataset = null;
- this.viewportDeltaX = 0;
- this.viewportDeltaY = 0;
- this.startX = 0;
- this.startY = 0;
- this.startHeight = 0;
- this.startWidth = 0;
- this.newX = 0;
- this.newY = 0;
- this.newHeight = 0;
- this.newWidth = 0;
- this.startContainerClassName = '';
- this.startDisplay = '';
- this.newContainerClassName = '';
- this.newDisplay = '';
- this.callActions('afterConstruct');
- h.seal(this);
- };
- mixitup.BaseStatic.call(mixitup.Operation);
- mixitup.Operation.prototype = Object.create(mixitup.Base.prototype);
- mixitup.Operation.prototype.constructor = mixitup.Operation;
- /**
- * `mixitup.State` objects expose various pieces of data detailing the state of
- * a MixItUp instance. They are provided at the start and end of any operation via
- * callbacks and events, with the most recent state stored between operations
- * for retrieval at any time via the API.
- *
- * @constructor
- * @namespace
- * @memberof mixitup
- * @public
- * @since 3.0.0
- */
- mixitup.State = function() {
- mixitup.Base.call(this);
- this.callActions('beforeConstruct');
- /**
- * The ID of the mixer instance.
- *
- * @name id
- * @memberof mixitup.State
- * @instance
- * @type {string}
- * @default ''
- */
- this.id = '';
- /**
- * The currently active filter command as set by a control click or API call.
- *
- * @name activeFilter
- * @memberof mixitup.State
- * @instance
- * @type {mixitup.CommandFilter}
- * @default null
- */
- this.activeFilter = null;
- /**
- * The currently active sort command as set by a control click or API call.
- *
- * @name activeSort
- * @memberof mixitup.State
- * @instance
- * @type {mixitup.CommandSort}
- * @default null
- */
- this.activeSort = null;
- /**
- * The current layout-specific container class name, if applied.
- *
- * @name activeContainerClassName
- * @memberof mixitup.State
- * @instance
- * @type {string}
- * @default ''
- */
- this.activeContainerClassName = '';
- /**
- * A reference to the container element that the mixer is instantiated on.
- *
- * @name container
- * @memberof mixitup.State
- * @instance
- * @type {Element}
- * @default null
- */
- this.container = null;
- /**
- * An array of all target elements indexed by the mixer.
- *
- * @name targets
- * @memberof mixitup.State
- * @instance
- * @type {Array.<Element>}
- * @default []
- */
- this.targets = [];
- /**
- * An array of all target elements not matching the current filter.
- *
- * @name hide
- * @memberof mixitup.State
- * @instance
- * @type {Array.<Element>}
- * @default []
- */
- this.hide = [];
- /**
- * An array of all target elements matching the current filter and any additional
- * limits applied such as pagination.
- *
- * @name show
- * @memberof mixitup.State
- * @instance
- * @type {Array.<Element>}
- * @default []
- */
- this.show = [];
- /**
- * An array of all target elements matching the current filter irrespective of
- * any additional limits applied such as pagination.
- *
- * @name matching
- * @memberof mixitup.State
- * @instance
- * @type {Array.<Element>}
- * @default []
- */
- this.matching = [];
- /**
- * An integer representing the total number of target elements indexed by the
- * mixer. Equivalent to `state.targets.length`.
- *
- * @name totalTargets
- * @memberof mixitup.State
- * @instance
- * @type {number}
- * @default -1
- */
- this.totalTargets = -1;
- /**
- * An integer representing the total number of target elements matching the
- * current filter and any additional limits applied such as pagination.
- * Equivalent to `state.show.length`.
- *
- * @name totalShow
- * @memberof mixitup.State
- * @instance
- * @type {number}
- * @default -1
- */
- this.totalShow = -1;
- /**
- * An integer representing the total number of target elements not matching
- * the current filter. Equivalent to `state.hide.length`.
- *
- * @name totalHide
- * @memberof mixitup.State
- * @instance
- * @type {number}
- * @default -1
- */
- this.totalHide = -1;
- /**
- * An integer representing the total number of target elements matching the
- * current filter irrespective of any other limits applied such as pagination.
- * Equivalent to `state.matching.length`.
- *
- * @name totalMatching
- * @memberof mixitup.State
- * @instance
- * @type {number}
- * @default -1
- */
- this.totalMatching = -1;
- /**
- * A boolean indicating whether the last operation "failed", i.e. no targets
- * could be found matching the filter.
- *
- * @name hasFailed
- * @memberof mixitup.State
- * @instance
- * @type {boolean}
- * @default false
- */
- this.hasFailed = false;
- /**
- * The DOM element that was clicked if the last operation was triggered by the
- * clicking of a control and not an API call.
- *
- * @name triggerElement
- * @memberof mixitup.State
- * @instance
- * @type {Element|null}
- * @default null
- */
- this.triggerElement = null;
- /**
- * The currently active dataset underlying the rendered targets, if the
- * dataset API is in use.
- *
- * @name activeDataset
- * @memberof mixitup.State
- * @instance
- * @type {Array.<object>}
- * @default null
- */
- this.activeDataset = null;
- this.callActions('afterConstruct');
- h.seal(this);
- };
- mixitup.BaseStatic.call(mixitup.State);
- mixitup.State.prototype = Object.create(mixitup.Base.prototype);
- mixitup.State.prototype.constructor = mixitup.State;
- /**
- * @constructor
- * @memberof mixitup
- * @private
- * @since 3.0.0
- */
- mixitup.UserInstruction = function() {
- mixitup.Base.call(this);
- this.callActions('beforeConstruct');
- this.command = {};
- this.animate = false;
- this.callback = null;
- this.callActions('afterConstruct');
- h.seal(this);
- };
- mixitup.BaseStatic.call(mixitup.UserInstruction);
- mixitup.UserInstruction.prototype = Object.create(mixitup.Base.prototype);
- mixitup.UserInstruction.prototype.constructor = mixitup.UserInstruction;
- /**
- * @constructor
- * @memberof mixitup
- * @private
- * @since 3.0.0
- */
- mixitup.Messages = function() {
- mixitup.Base.call(this);
- this.callActions('beforeConstruct');
- /* Errors
- ----------------------------------------------------------------------------- */
- this.ERROR_FACTORY_INVALID_CONTAINER =
- '[MixItUp] An invalid selector or element reference was passed to the mixitup factory function';
- this.ERROR_FACTORY_CONTAINER_NOT_FOUND =
- '[MixItUp] The provided selector yielded no container element';
- this.ERROR_CONFIG_INVALID_ANIMATION_EFFECTS =
- '[MixItUp] Invalid value for `animation.effects`';
- this.ERROR_CONFIG_INVALID_CONTROLS_SCOPE =
- '[MixItUp] Invalid value for `controls.scope`';
- this.ERROR_CONFIG_INVALID_PROPERTY =
- '[MixitUp] Invalid configuration object property "${erroneous}"${suggestion}';
- this.ERROR_CONFIG_INVALID_PROPERTY_SUGGESTION =
- '. Did you mean "${probableMatch}"?';
- this.ERROR_CONFIG_DATA_UID_KEY_NOT_SET =
- '[MixItUp] To use the dataset API, a UID key must be specified using `data.uidKey`';
- this.ERROR_DATASET_INVALID_UID_KEY =
- '[MixItUp] The specified UID key "${uidKey}" is not present on one or more dataset items';
- this.ERROR_DATASET_DUPLICATE_UID =
- '[MixItUp] The UID "${uid}" was found on two or more dataset items. UIDs must be unique.';
- this.ERROR_INSERT_INVALID_ARGUMENTS =
- '[MixItUp] Please provider either an index or a sibling and position to insert, not both';
- this.ERROR_INSERT_PREEXISTING_ELEMENT =
- '[MixItUp] An element to be inserted already exists in the container';
- this.ERROR_FILTER_INVALID_ARGUMENTS =
- '[MixItUp] Please provide either a selector or collection `.filter()`, not both';
- this.ERROR_DATASET_NOT_SET =
- '[MixItUp] To use the dataset API with pre-rendered targets, a starting dataset must be set using `load.dataset`';
- this.ERROR_DATASET_PRERENDERED_MISMATCH =
- '[MixItUp] `load.dataset` does not match pre-rendered targets';
- this.ERROR_DATASET_RENDERER_NOT_SET =
- '[MixItUp] To insert an element via the dataset API, a target renderer function must be provided to `render.target`';
- /* Warnings
- ----------------------------------------------------------------------------- */
- this.WARNING_FACTORY_PREEXISTING_INSTANCE =
- '[MixItUp] WARNING: This element already has an active MixItUp instance. The provided configuration object will be ignored.' +
- ' If you wish to perform additional methods on this instance, please create a reference.';
- this.WARNING_INSERT_NO_ELEMENTS =
- '[MixItUp] WARNING: No valid elements were passed to `.insert()`';
- this.WARNING_REMOVE_NO_ELEMENTS =
- '[MixItUp] WARNING: No valid elements were passed to `.remove()`';
- this.WARNING_MULTIMIX_INSTANCE_QUEUE_FULL =
- '[MixItUp] WARNING: An operation was requested but the MixItUp instance was busy. The operation was rejected because the ' +
- 'queue is full or queuing is disabled.';
- this.WARNING_GET_OPERATION_INSTANCE_BUSY =
- '[MixItUp] WARNING: Operations can be be created while the MixItUp instance is busy.';
- this.WARNING_NO_PROMISE_IMPLEMENTATION =
- '[MixItUp] WARNING: No Promise implementations could be found. If you wish to use promises with MixItUp please install' +
- ' an ES6 Promise polyfill.';
- this.WARNING_INCONSISTENT_SORTING_ATTRIBUTES =
- '[MixItUp] WARNING: The requested sorting data attribute "${attribute}" was not present on one or more target elements' +
- ' which may product unexpected sort output';
- this.callActions('afterConstruct');
- this.compileTemplates();
- h.seal(this);
- };
- mixitup.BaseStatic.call(mixitup.Messages);
- mixitup.Messages.prototype = Object.create(mixitup.Base.prototype);
- mixitup.Messages.prototype.constructor = mixitup.Messages;
- /**
- * @return {void}
- */
- mixitup.Messages.prototype.compileTemplates = function() {
- var errorKey = '';
- var errorMessage = '';
- for (errorKey in this) {
- if (typeof (errorMessage = this[errorKey]) !== 'string') continue;
- this[h.camelCase(errorKey)] = h.template(errorMessage);
- }
- };
- mixitup.messages = new mixitup.Messages();
- /**
- * @constructor
- * @memberof mixitup
- * @private
- * @since 3.0.0
- * @param {mixitup.Mixer} mixer
- */
- mixitup.Facade = function Mixer(mixer) {
- mixitup.Base.call(this);
- this.callActions('beforeConstruct', arguments);
- this.configure = mixer.configure.bind(mixer);
- this.show = mixer.show.bind(mixer);
- this.hide = mixer.hide.bind(mixer);
- this.filter = mixer.filter.bind(mixer);
- this.toggleOn = mixer.toggleOn.bind(mixer);
- this.toggleOff = mixer.toggleOff.bind(mixer);
- this.sort = mixer.sort.bind(mixer);
- this.changeLayout = mixer.changeLayout.bind(mixer);
- this.multimix = mixer.multimix.bind(mixer);
- this.dataset = mixer.dataset.bind(mixer);
- this.tween = mixer.tween.bind(mixer);
- this.insert = mixer.insert.bind(mixer);
- this.insertBefore = mixer.insertBefore.bind(mixer);
- this.insertAfter = mixer.insertAfter.bind(mixer);
- this.prepend = mixer.prepend.bind(mixer);
- this.append = mixer.append.bind(mixer);
- this.remove = mixer.remove.bind(mixer);
- this.destroy = mixer.destroy.bind(mixer);
- this.forceRefresh = mixer.forceRefresh.bind(mixer);
- this.forceRender = mixer.forceRender.bind(mixer);
- this.isMixing = mixer.isMixing.bind(mixer);
- this.getOperation = mixer.getOperation.bind(mixer);
- this.getConfig = mixer.getConfig.bind(mixer);
- this.getState = mixer.getState.bind(mixer);
- this.callActions('afterConstruct', arguments);
- h.freeze(this);
- h.seal(this);
- };
- mixitup.BaseStatic.call(mixitup.Facade);
- mixitup.Facade.prototype = Object.create(mixitup.Base.prototype);
- mixitup.Facade.prototype.constructor = mixitup.Facade;
- if (typeof exports === 'object' && typeof module === 'object') {
- module.exports = mixitup;
- } else if (typeof define === 'function' && define.amd) {
- define(function() {
- return mixitup;
- });
- } else if (typeof window.mixitup === 'undefined' || typeof window.mixitup !== 'function') {
- window.mixitup = mixitup;
- }
- mixitup.BaseStatic.call(mixitup.constructor);
- mixitup.NAME = 'mixitup';
- mixitup.CORE_VERSION = '3.3.0';
- })(window);
|