xscroll-master.js 172 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366336733683369337033713372337333743375337633773378337933803381338233833384338533863387338833893390339133923393339433953396339733983399340034013402340334043405340634073408340934103411341234133414341534163417341834193420342134223423342434253426342734283429343034313432343334343435343634373438343934403441344234433444344534463447344834493450345134523453345434553456345734583459346034613462346334643465346634673468346934703471347234733474347534763477347834793480348134823483348434853486348734883489349034913492349334943495349634973498349935003501350235033504350535063507350835093510351135123513351435153516351735183519352035213522352335243525352635273528352935303531353235333534353535363537353835393540354135423543354435453546354735483549355035513552355335543555355635573558355935603561356235633564356535663567356835693570357135723573357435753576357735783579358035813582358335843585358635873588358935903591359235933594359535963597359835993600360136023603360436053606360736083609361036113612361336143615361636173618361936203621362236233624362536263627362836293630363136323633363436353636363736383639364036413642364336443645364636473648364936503651365236533654365536563657365836593660366136623663366436653666366736683669367036713672367336743675367636773678367936803681368236833684368536863687368836893690369136923693369436953696369736983699370037013702370337043705370637073708370937103711371237133714371537163717371837193720372137223723372437253726372737283729373037313732373337343735373637373738373937403741374237433744374537463747374837493750375137523753375437553756375737583759376037613762376337643765376637673768376937703771377237733774377537763777377837793780378137823783378437853786378737883789379037913792379337943795379637973798379938003801380238033804380538063807380838093810381138123813381438153816381738183819382038213822382338243825382638273828382938303831383238333834383538363837383838393840384138423843384438453846384738483849385038513852385338543855385638573858385938603861386238633864386538663867386838693870387138723873387438753876387738783879388038813882388338843885388638873888388938903891389238933894389538963897389838993900390139023903390439053906390739083909391039113912391339143915391639173918391939203921392239233924392539263927392839293930393139323933393439353936393739383939394039413942394339443945394639473948394939503951395239533954395539563957395839593960396139623963396439653966396739683969397039713972397339743975397639773978397939803981398239833984398539863987398839893990399139923993399439953996399739983999400040014002400340044005400640074008400940104011401240134014401540164017401840194020402140224023402440254026402740284029403040314032403340344035403640374038403940404041404240434044404540464047404840494050405140524053405440554056405740584059406040614062406340644065406640674068406940704071407240734074407540764077407840794080408140824083408440854086408740884089409040914092409340944095409640974098409941004101410241034104410541064107410841094110411141124113411441154116411741184119412041214122412341244125412641274128412941304131413241334134413541364137413841394140414141424143414441454146414741484149415041514152415341544155415641574158415941604161416241634164416541664167416841694170417141724173417441754176417741784179418041814182418341844185418641874188418941904191419241934194419541964197419841994200420142024203420442054206420742084209421042114212421342144215421642174218421942204221422242234224422542264227422842294230423142324233423442354236423742384239424042414242424342444245424642474248424942504251425242534254425542564257425842594260426142624263426442654266426742684269427042714272427342744275427642774278427942804281428242834284428542864287428842894290429142924293429442954296429742984299430043014302430343044305430643074308430943104311431243134314431543164317431843194320432143224323432443254326432743284329433043314332433343344335433643374338433943404341434243434344434543464347434843494350435143524353435443554356435743584359436043614362436343644365436643674368436943704371437243734374437543764377437843794380438143824383438443854386438743884389439043914392439343944395439643974398439944004401440244034404440544064407440844094410441144124413441444154416441744184419442044214422442344244425442644274428442944304431443244334434443544364437443844394440444144424443444444454446444744484449445044514452445344544455445644574458445944604461446244634464446544664467446844694470447144724473447444754476447744784479448044814482448344844485448644874488448944904491449244934494449544964497449844994500450145024503450445054506450745084509451045114512451345144515451645174518451945204521452245234524452545264527452845294530453145324533453445354536453745384539454045414542454345444545454645474548454945504551455245534554455545564557455845594560456145624563456445654566456745684569457045714572457345744575457645774578457945804581458245834584458545864587458845894590459145924593459445954596459745984599460046014602460346044605460646074608460946104611461246134614461546164617461846194620462146224623462446254626462746284629463046314632463346344635463646374638463946404641464246434644464546464647464846494650465146524653465446554656465746584659466046614662466346644665466646674668466946704671467246734674467546764677467846794680468146824683468446854686468746884689469046914692469346944695469646974698469947004701470247034704470547064707470847094710471147124713471447154716471747184719472047214722472347244725472647274728472947304731473247334734473547364737473847394740474147424743474447454746474747484749475047514752475347544755475647574758475947604761476247634764476547664767476847694770477147724773477447754776477747784779478047814782478347844785478647874788478947904791479247934794479547964797479847994800480148024803480448054806480748084809481048114812481348144815481648174818481948204821482248234824482548264827482848294830483148324833483448354836483748384839484048414842484348444845484648474848484948504851485248534854485548564857485848594860486148624863486448654866486748684869487048714872487348744875487648774878487948804881488248834884488548864887488848894890489148924893489448954896489748984899490049014902490349044905490649074908490949104911491249134914491549164917491849194920492149224923492449254926492749284929493049314932493349344935493649374938493949404941494249434944494549464947494849494950495149524953495449554956495749584959496049614962496349644965496649674968496949704971497249734974497549764977497849794980498149824983498449854986498749884989499049914992499349944995499649974998499950005001500250035004500550065007500850095010501150125013501450155016501750185019502050215022502350245025502650275028502950305031503250335034503550365037503850395040504150425043504450455046504750485049505050515052505350545055505650575058505950605061506250635064506550665067506850695070507150725073507450755076507750785079508050815082508350845085508650875088508950905091509250935094509550965097509850995100510151025103510451055106510751085109511051115112511351145115511651175118511951205121512251235124512551265127512851295130513151325133513451355136513751385139514051415142514351445145514651475148514951505151515251535154515551565157515851595160516151625163516451655166516751685169517051715172517351745175517651775178517951805181518251835184518551865187518851895190519151925193519451955196519751985199520052015202520352045205520652075208520952105211521252135214521552165217521852195220522152225223522452255226522752285229523052315232523352345235523652375238523952405241524252435244524552465247524852495250525152525253525452555256525752585259526052615262526352645265526652675268526952705271527252735274527552765277527852795280528152825283528452855286528752885289529052915292529352945295529652975298529953005301530253035304530553065307530853095310531153125313531453155316531753185319532053215322532353245325532653275328532953305331533253335334533553365337533853395340534153425343534453455346534753485349535053515352535353545355535653575358535953605361536253635364536553665367536853695370537153725373537453755376537753785379538053815382538353845385538653875388538953905391539253935394539553965397539853995400540154025403540454055406540754085409541054115412541354145415541654175418541954205421542254235424542554265427542854295430543154325433543454355436543754385439544054415442544354445445544654475448544954505451
  1. ;(function() {
  2. var util = {}, events = {}, base = {}, easing = {}, timer = {}, animate = {}, hammer = {}, boundry = {}, components_sticky = {}, components_fixed = {}, core = {}, components_scrollbar = {}, components_controller = {}, simulate_scroll = {}, origin_scroll = {}, xscroll = {}, xscroll_master = {};
  3. util = function (exports) {
  4. var SUBSTITUTE_REG = /\\?\{([^{}]+)\}/g, EMPTY = '';
  5. var RE_TRIM = /^[\s\xa0]+|[\s\xa0]+$/g, trim = String.prototype.trim;
  6. var _trim = trim ? function (str) {
  7. return str == null ? EMPTY : trim.call(str);
  8. } : function (str) {
  9. return str == null ? EMPTY : (str + '').replace(RE_TRIM, EMPTY);
  10. };
  11. function upperCase() {
  12. return arguments[1].toUpperCase();
  13. }
  14. function Empty() {
  15. }
  16. function createObject(proto, constructor) {
  17. var newProto;
  18. if (Object.create) {
  19. newProto = Object.create(proto);
  20. } else {
  21. Empty.prototype = proto;
  22. newProto = new Empty();
  23. }
  24. newProto.constructor = constructor;
  25. return newProto;
  26. }
  27. function getNodes(node, rootNode) {
  28. if (!node)
  29. return;
  30. if (node.nodeType)
  31. return [node];
  32. var rootNode = rootNode && rootNode.nodeType ? rootNode : document;
  33. if (node && typeof node === 'string') {
  34. return rootNode.querySelectorAll(node);
  35. }
  36. return;
  37. }
  38. // Useful for temporary DOM ids.
  39. var idCounter = 0;
  40. var getOffsetTop = function (el) {
  41. var offset = el.offsetTop;
  42. if (el.offsetParent != null)
  43. offset += getOffsetTop(el.offsetParent);
  44. return offset;
  45. };
  46. var getOffsetLeft = function (el) {
  47. var offset = el.offsetLeft;
  48. if (el.offsetParent != null)
  49. offset += getOffsetLeft(el.offsetParent);
  50. return offset;
  51. };
  52. var Util = {
  53. // Is a given variable an object?
  54. isObject: function (obj) {
  55. return obj === Object(obj);
  56. },
  57. isArray: Array.isArray || function (obj) {
  58. return toString.call(obj) == '[object Array]';
  59. },
  60. // Is a given array, string, or object empty?
  61. // An "empty" object has no enumerable own-properties.
  62. isEmpty: function (obj) {
  63. if (obj == null)
  64. return true;
  65. if (this.isArray(obj) || this.isString(obj))
  66. return obj.length === 0;
  67. for (var key in obj)
  68. if (this.has(obj, key))
  69. return false;
  70. return true;
  71. },
  72. mix: function (to, from, deep) {
  73. for (var i in from) {
  74. to[i] = from[i];
  75. }
  76. return to;
  77. },
  78. extend: function (r, s, px, sx) {
  79. if (!s || !r) {
  80. return r;
  81. }
  82. var sp = s.prototype, rp;
  83. // add prototype chain
  84. rp = createObject(sp, r);
  85. r.prototype = this.mix(rp, r.prototype);
  86. r.superclass = createObject(sp, s);
  87. // add prototype overrides
  88. if (px) {
  89. this.mix(rp, px);
  90. }
  91. // add object overrides
  92. if (sx) {
  93. this.mix(r, sx);
  94. }
  95. return r;
  96. },
  97. /**
  98. * test whether a string start with a specified substring
  99. * @param {String} str the whole string
  100. * @param {String} prefix a specified substring
  101. * @return {Boolean} whether str start with prefix
  102. * @member util
  103. */
  104. startsWith: function (str, prefix) {
  105. return str.lastIndexOf(prefix, 0) === 0;
  106. },
  107. /**
  108. * test whether a string end with a specified substring
  109. * @param {String} str the whole string
  110. * @param {String} suffix a specified substring
  111. * @return {Boolean} whether str end with suffix
  112. * @member util
  113. */
  114. endsWith: function (str, suffix) {
  115. var ind = str.length - suffix.length;
  116. return ind >= 0 && str.indexOf(suffix, ind) === ind;
  117. },
  118. /**
  119. * Removes the whitespace from the beginning and end of a string.
  120. * @method
  121. * @member util
  122. */
  123. trim: _trim,
  124. /**
  125. * Substitutes keywords in a string using an object/array.
  126. * Removes undef keywords and ignores escaped keywords.
  127. * @param {String} str template string
  128. * @param {Object} o json data
  129. * @member util
  130. * @param {RegExp} [regexp] to match a piece of template string
  131. */
  132. substitute: function (str, o, regexp) {
  133. if (typeof str !== 'string' || !o) {
  134. return str;
  135. }
  136. return str.replace(regexp || SUBSTITUTE_REG, function (match, name) {
  137. if (match.charAt(0) === '\\') {
  138. return match.slice(1);
  139. }
  140. return o[name] === undefined ? EMPTY : o[name];
  141. });
  142. },
  143. /**
  144. * vendors
  145. * @return { String } webkit|moz|ms|o
  146. * @memberOf Util
  147. */
  148. vendor: function () {
  149. var el = document.createElement('div').style;
  150. var vendors = [
  151. 't',
  152. 'webkitT',
  153. 'MozT',
  154. 'msT',
  155. 'OT'
  156. ], transform, i = 0, l = vendors.length;
  157. for (; i < l; i++) {
  158. transform = vendors[i] + 'ransform';
  159. if (transform in el)
  160. return vendors[i].substr(0, vendors[i].length - 1);
  161. }
  162. return false;
  163. }(),
  164. /**
  165. * add vendor to attribute
  166. * @memberOf Util
  167. * @param {String} attrName name of attribute
  168. * @return { String }
  169. **/
  170. prefixStyle: function (attrName) {
  171. if (this.vendor === false)
  172. return false;
  173. if (this.vendor === '')
  174. return attrName;
  175. return this.vendor + attrName.charAt(0).toUpperCase() + attrName.substr(1);
  176. },
  177. /**
  178. * judge if has class
  179. * @memberOf Util
  180. * @param {HTMLElement} el
  181. * @param {String} className
  182. * @return {Boolean}
  183. */
  184. hasClass: function (el, className) {
  185. return el && el.className && className && el.className.indexOf(className) != -1;
  186. },
  187. /**
  188. * add className for the element
  189. * @memberOf Util
  190. * @param {HTMLElement} el
  191. * @param {String} className
  192. */
  193. addClass: function (el, className) {
  194. if (el && className && !this.hasClass(el, className)) {
  195. el.className += ' ' + className;
  196. }
  197. },
  198. /**
  199. * remove className for the element
  200. * @memberOf Util
  201. * @param {HTMLElement} el
  202. * @param {String} className
  203. */
  204. removeClass: function (el, className) {
  205. if (el && el.className && className) {
  206. el.className = el.className.replace(className, '');
  207. }
  208. },
  209. /**
  210. * remove an element
  211. * @memberOf Util
  212. * @param {HTMLElement} el
  213. */
  214. remove: function (el) {
  215. if (!el || !el.parentNode)
  216. return;
  217. el.parentNode.removeChild(el);
  218. },
  219. /**
  220. * get offset top
  221. * @memberOf Util
  222. * @param {HTMLElement} el
  223. * @return {Number} offsetTop
  224. */
  225. getOffsetTop: getOffsetTop,
  226. /**
  227. * get offset left
  228. * @memberOf Util
  229. * @param {HTMLElement} el
  230. * @return {Number} offsetLeft
  231. */
  232. getOffsetLeft: getOffsetLeft,
  233. /**
  234. * get offset left
  235. * @memberOf Util
  236. * @param {HTMLElement} el
  237. * @param {String} selector
  238. * @param {HTMLElement} rootNode
  239. * @return {HTMLElement} parent element
  240. */
  241. findParentEl: function (el, selector, rootNode) {
  242. var rs = null, parent = null;
  243. var type = /^#/.test(selector) ? 'id' : /^\./.test(selector) ? 'class' : 'tag';
  244. var sel = selector.replace(/\.|#/g, '');
  245. if (rootNode && typeof rootNode === 'string') {
  246. rootNode = document.querySelector(rootNode);
  247. }
  248. rootNode = rootNode || document.body;
  249. if (!el || !selector)
  250. return;
  251. if (type == 'class' && el.className && el.className.match(sel)) {
  252. return el;
  253. } else if (type == 'id' && el.id && _trim(el.id) == sel) {
  254. return el;
  255. } else if (type == 'tag' && el.tagName.toLowerCase() == sel) {
  256. return el;
  257. }
  258. while (!rs) {
  259. if (parent == rootNode)
  260. break;
  261. parent = el.parentNode;
  262. if (!parent)
  263. break;
  264. if (type == 'class' && parent.className && parent.className.match(sel) || type == 'id' && parent.id && _trim(parent.id) == sel || type == 'tag' && parent.tagName && parent.tagName.toLowerCase() == sel) {
  265. rs = parent;
  266. return rs;
  267. break;
  268. } else {
  269. el = parent;
  270. }
  271. }
  272. return null;
  273. },
  274. /**
  275. * Generate a unique integer id (unique within the entire client session).
  276. * @param {String} prefix
  277. * @return {String} guid
  278. */
  279. guid: function (prefix) {
  280. var id = ++idCounter + '';
  281. return prefix ? prefix + id : id;
  282. },
  283. /**
  284. * judge if is an android os
  285. * @return {Boolean} [description]
  286. */
  287. isAndroid: function () {
  288. return /Android /.test(window.navigator.appVersion);
  289. },
  290. /**
  291. * judge if is an android device with low performance
  292. * @return {Boolean}
  293. */
  294. isBadAndroid: function () {
  295. return /Android /.test(window.navigator.appVersion) && !/Chrome\/\d/.test(window.navigator.appVersion);
  296. },
  297. px2Num: function (px) {
  298. return Number(px.replace(/px/, ''));
  299. },
  300. getNodes: getNodes,
  301. getNode: function (node, rootNode) {
  302. var nodes = getNodes(node, rootNode);
  303. return nodes && nodes[0];
  304. },
  305. stringifyStyle: function (style) {
  306. var styleStr = '';
  307. for (var i in style) {
  308. styleStr += [
  309. i,
  310. ':',
  311. style[i],
  312. ';'
  313. ].join('');
  314. }
  315. return styleStr;
  316. }
  317. };
  318. // Add some isType methods: isArguments, isFunction, isString, isNumber, isDate, isRegExp.
  319. var names = [
  320. 'Arguments',
  321. 'Function',
  322. 'String',
  323. 'Number',
  324. 'Date',
  325. 'RegExp'
  326. ];
  327. for (var i = 0; i < names.length; i++) {
  328. Util['is' + names[i]] = function (obj) {
  329. return toString.call(obj) == '[object ' + names[i] + ']';
  330. };
  331. }
  332. if (typeof module == 'object' && module.exports) {
  333. exports = Util;
  334. } /** ignored by jsdoc **/ else {
  335. return Util;
  336. }
  337. return exports;
  338. }(util);
  339. events = function (exports) {
  340. var Util = util;
  341. // Returns a function that will be executed at most one time, no matter how
  342. // often you call it. Useful for lazy initialization.
  343. var _once = function (func) {
  344. var ran = false, memo;
  345. return function () {
  346. if (ran)
  347. return memo;
  348. ran = true;
  349. memo = func.apply(this, arguments);
  350. func = null;
  351. return memo;
  352. };
  353. };
  354. /**
  355. * @discription events
  356. * @mixin
  357. */
  358. var Events = {
  359. // Bind an event to a `callback` function. Passing `"all"` will bind
  360. // the callback to all events fired.
  361. on: function (name, callback, context) {
  362. if (!eventsApi(this, 'on', name, [
  363. callback,
  364. context
  365. ]) || !callback)
  366. return this;
  367. this._events || (this._events = {});
  368. var events = this._events[name] || (this._events[name] = []);
  369. events.push({
  370. callback: callback,
  371. context: context,
  372. ctx: context || this
  373. });
  374. return this;
  375. },
  376. // Bind an event to only be triggered a single time. After the first time
  377. // the callback is invoked, it will be removed.
  378. once: function (name, callback, context) {
  379. if (!eventsApi(this, 'once', name, [
  380. callback,
  381. context
  382. ]) || !callback)
  383. return this;
  384. var self = this;
  385. var once = _once(function () {
  386. self.off(name, once);
  387. callback.apply(this, arguments);
  388. });
  389. once._callback = callback;
  390. return this.on(name, once, context);
  391. },
  392. // Remove one or many callbacks. If `context` is null, removes all
  393. // callbacks with that function. If `callback` is null, removes all
  394. // callbacks for the event. If `name` is null, removes all bound
  395. // callbacks for all events.
  396. off: function (name, callback, context) {
  397. if (!this._events || !eventsApi(this, 'off', name, [
  398. callback,
  399. context
  400. ]))
  401. return this;
  402. // Remove all callbacks for all events.
  403. if (!name && !callback && !context) {
  404. this._events = void 0;
  405. return this;
  406. }
  407. var names = name ? [name] : Object.keys(this._events);
  408. for (var i = 0, length = names.length; i < length; i++) {
  409. name = names[i];
  410. // Bail out if there are no events stored.
  411. var events = this._events[name];
  412. if (!events)
  413. continue;
  414. // Remove all callbacks for this event.
  415. if (!callback && !context) {
  416. delete this._events[name];
  417. continue;
  418. }
  419. // Find any remaining events.
  420. var remaining = [];
  421. for (var j = 0, k = events.length; j < k; j++) {
  422. var event = events[j];
  423. if (callback && callback !== event.callback && callback !== event.callback._callback || context && context !== event.context) {
  424. remaining.push(event);
  425. }
  426. }
  427. // Replace events if there are any remaining. Otherwise, clean up.
  428. if (remaining.length) {
  429. this._events[name] = remaining;
  430. } else {
  431. delete this._events[name];
  432. }
  433. }
  434. return this;
  435. },
  436. // Trigger one or many events, firing all bound callbacks. Callbacks are
  437. // passed the same arguments as `trigger` is, apart from the event name
  438. // (unless you're listening on `"all"`, which will cause your callback to
  439. // receive the true name of the event as the first argument).
  440. trigger: function (name) {
  441. if (!this._events)
  442. return this;
  443. var args = Array.prototype.slice.call(arguments, 1);
  444. if (!eventsApi(this, 'trigger', name, args))
  445. return this;
  446. var events = this._events[name];
  447. var allEvents = this._events.all;
  448. if (events)
  449. triggerEvents(events, args);
  450. if (allEvents)
  451. triggerEvents(allEvents, arguments);
  452. return this;
  453. },
  454. // Inversion-of-control versions of `on` and `once`. Tell *this* object to
  455. // listen to an event in another object ... keeping track of what it's
  456. // listening to.
  457. listenTo: function (obj, name, callback) {
  458. var listeningTo = this._listeningTo || (this._listeningTo = {});
  459. var id = obj._listenId || (obj._listenId = Util.guid('l'));
  460. listeningTo[id] = obj;
  461. if (!callback && typeof name === 'object')
  462. callback = this;
  463. obj.on(name, callback, this);
  464. return this;
  465. },
  466. listenToOnce: function (obj, name, callback) {
  467. if (typeof name === 'object') {
  468. for (var event in name)
  469. this.listenToOnce(obj, event, name[event]);
  470. return this;
  471. }
  472. var cb = _once(function () {
  473. this.stopListening(obj, name, cb);
  474. callback.apply(this, arguments);
  475. });
  476. cb._callback = callback;
  477. return this.listenTo(obj, name, cb);
  478. },
  479. // Tell this object to stop listening to either specific events ... or
  480. // to every object it's currently listening to.
  481. stopListening: function (obj, name, callback) {
  482. var listeningTo = this._listeningTo;
  483. if (!listeningTo)
  484. return this;
  485. var remove = !name && !callback;
  486. if (!callback && typeof name === 'object')
  487. callback = this;
  488. if (obj)
  489. (listeningTo = {})[obj._listenId] = obj;
  490. for (var id in listeningTo) {
  491. obj = listeningTo[id];
  492. obj.off(name, callback, this);
  493. if (remove || Util.isEmpty(obj._events))
  494. delete this._listeningTo[id];
  495. }
  496. return this;
  497. }
  498. };
  499. // Regular expression used to split event strings.
  500. var eventSplitter = /\s+/;
  501. // Implement fancy features of the Events API such as multiple event
  502. // names `"change blur"` and jQuery-style event maps `{change: action}`
  503. // in terms of the existing API.
  504. var eventsApi = function (obj, action, name, rest) {
  505. if (!name)
  506. return true;
  507. // Handle event maps.
  508. if (typeof name === 'object') {
  509. for (var key in name) {
  510. obj[action].apply(obj, [
  511. key,
  512. name[key]
  513. ].concat(rest));
  514. }
  515. return false;
  516. }
  517. // Handle space separated event names.
  518. if (eventSplitter.test(name)) {
  519. var names = name.split(eventSplitter);
  520. for (var i = 0, length = names.length; i < length; i++) {
  521. obj[action].apply(obj, [names[i]].concat(rest));
  522. }
  523. return false;
  524. }
  525. return true;
  526. };
  527. // A difficult-to-believe, but optimized internal dispatch function for
  528. // triggering events. Tries to keep the usual cases speedy (most internal
  529. var triggerEvents = function (events, args) {
  530. var ev, i = -1, l = events.length, a1 = args[0], a2 = args[1], a3 = args[2];
  531. switch (args.length) {
  532. case 0:
  533. while (++i < l)
  534. (ev = events[i]).callback.call(ev.ctx);
  535. return;
  536. case 1:
  537. while (++i < l)
  538. (ev = events[i]).callback.call(ev.ctx, a1);
  539. return;
  540. case 2:
  541. while (++i < l)
  542. (ev = events[i]).callback.call(ev.ctx, a1, a2);
  543. return;
  544. case 3:
  545. while (++i < l)
  546. (ev = events[i]).callback.call(ev.ctx, a1, a2, a3);
  547. return;
  548. default:
  549. while (++i < l)
  550. (ev = events[i]).callback.apply(ev.ctx, args);
  551. return;
  552. }
  553. };
  554. // Aliases for backwards compatibility.
  555. Events.bind = Events.on;
  556. Events.unbind = Events.off;
  557. if (typeof module == 'object' && module.exports) {
  558. exports = Events;
  559. } /** ignored by jsdoc **/ else {
  560. return Events;
  561. }
  562. return exports;
  563. }(events);
  564. base = function (exports) {
  565. var Util = util;
  566. var Events = events;
  567. /**
  568. @constructor
  569. @mixes Events
  570. */
  571. var Base = function () {
  572. };
  573. Util.mix(Base.prototype, Events);
  574. Util.mix(Base.prototype, {
  575. /**
  576. * @memberof Base
  577. * @param {object} plugin plug a plugin
  578. */
  579. plug: function (plugin) {
  580. var self = this;
  581. if (!plugin || !plugin.pluginId)
  582. return;
  583. if (!self.__plugins) {
  584. self.__plugins = [];
  585. }
  586. var __plugin = self.getPlugin(plugin.pluginId);
  587. __plugin && self.unplug(plugin.pluginId);
  588. plugin.pluginInitializer(self);
  589. self.__plugins.push(plugin);
  590. return self;
  591. },
  592. /**
  593. * @memberof Base
  594. * @param {object|string} plugin unplug a plugin by pluginId or plugin instance
  595. */
  596. unplug: function (plugin) {
  597. var self = this;
  598. if (!plugin || !self.__plugins)
  599. return;
  600. var _plugin = typeof plugin == 'string' ? self.getPlugin(plugin) : plugin;
  601. _plugin.pluginDestructor(self);
  602. for (var i = 0, l = self.__plugins.length; i < l; i++) {
  603. if (self.__plugins[i] == _plugin) {
  604. return self.__plugins.splice(i, 1);
  605. }
  606. }
  607. },
  608. /**
  609. * @memberof Base
  610. * @param {object|string} plugin get plugin by pluginId
  611. */
  612. getPlugin: function (pluginId) {
  613. var self = this;
  614. var plugins = [];
  615. if (!self.__plugins)
  616. return;
  617. for (var i = 0, l = self.__plugins.length; i < l; i++) {
  618. if (self.__plugins[i] && self.__plugins[i].pluginId == pluginId) {
  619. plugins.push(self.__plugins[i]);
  620. }
  621. }
  622. return plugins.length > 1 ? plugins : plugins[0] || null;
  623. }
  624. });
  625. if (typeof module == 'object' && module.exports) {
  626. exports = Base;
  627. } /** ignored by jsdoc **/ else {
  628. return Base;
  629. }
  630. return exports;
  631. }(base);
  632. easing = function (exports) {
  633. //easing
  634. var Easing = {
  635. 'linear': [
  636. 0,
  637. 0,
  638. 1,
  639. 1
  640. ],
  641. 'ease': [
  642. 0.25,
  643. 0.1,
  644. 0.25,
  645. 1
  646. ],
  647. 'ease-in': [
  648. 0.42,
  649. 0,
  650. 1,
  651. 1
  652. ],
  653. 'ease-out': [
  654. 0,
  655. 0,
  656. 0.58,
  657. 1
  658. ],
  659. 'ease-in-out': [
  660. 0.42,
  661. 0,
  662. 0.58,
  663. 1
  664. ],
  665. 'quadratic': [
  666. 0.33,
  667. 0.66,
  668. 0.66,
  669. 1
  670. ],
  671. 'circular': [
  672. 0.1,
  673. 0.57,
  674. 0.1,
  675. 1
  676. ],
  677. 'bounce': [
  678. 0.71,
  679. 1.35,
  680. 0.47,
  681. 1.41
  682. ],
  683. format: function (easing) {
  684. if (!easing)
  685. return;
  686. if (typeof easing === 'string' && this[easing]) {
  687. return this[easing] instanceof Array ? [
  688. ' cubic-bezier(',
  689. this[easing],
  690. ') '
  691. ].join('') : this[easing];
  692. }
  693. if (easing instanceof Array) {
  694. return [
  695. ' cubic-bezier(',
  696. easing,
  697. ') '
  698. ].join('');
  699. }
  700. return easing;
  701. }
  702. };
  703. if (typeof module == 'object' && module.exports) {
  704. exports = Easing;
  705. } /** ignored by jsdoc **/ else {
  706. return Easing;
  707. }
  708. return exports;
  709. }(easing);
  710. timer = function (exports) {
  711. var Util = util;
  712. var Base = base;
  713. var Easing = easing;
  714. var RAF = window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || window.oRequestAnimationFrame || window.msRequestAnimationFrame || function (callback) {
  715. window.setTimeout(callback, 1000 / 60);
  716. };
  717. var vendors = [
  718. 'webkit',
  719. 'moz',
  720. 'ms',
  721. 'o'
  722. ];
  723. var cancelRAF = window.cancelAnimationFrame;
  724. if (!cancelRAF) {
  725. for (var i = 0; i < vendors.length; i++) {
  726. if (window[vendors[i] + 'CancelAnimationFrame'] || window[vendors[i] + 'CancelRequestAnimationFrame']) {
  727. cancelRAF = window[vendors[i] + 'CancelAnimationFrame'] || window[vendors[i] + 'CancelRequestAnimationFrame'];
  728. }
  729. }
  730. }
  731. cancelRAF = cancelRAF || window.clearTimeout;
  732. function Bezier(x1, y1, x2, y2, epsilon) {
  733. var curveX = function (t) {
  734. var v = 1 - t;
  735. return 3 * v * v * t * x1 + 3 * v * t * t * x2 + t * t * t;
  736. };
  737. var curveY = function (t) {
  738. var v = 1 - t;
  739. return 3 * v * v * t * y1 + 3 * v * t * t * y2 + t * t * t;
  740. };
  741. var derivativeCurveX = function (t) {
  742. var v = 1 - t;
  743. return 3 * (2 * (t - 1) * t + v * v) * x1 + 3 * (-t * t * t + 2 * v * t) * x2;
  744. };
  745. return function (t) {
  746. var x = t, t0, t1, t2, x2, d2, i;
  747. // First try a few iterations of Newton's method -- normally very fast.
  748. for (t2 = x, i = 0; i < 8; i++) {
  749. x2 = curveX(t2) - x;
  750. if (Math.abs(x2) < epsilon)
  751. return curveY(t2);
  752. d2 = derivativeCurveX(t2);
  753. if (Math.abs(d2) < 0.000001)
  754. break;
  755. t2 = t2 - x2 / d2;
  756. }
  757. t0 = 0, t1 = 1, t2 = x;
  758. if (t2 < t0)
  759. return curveY(t0);
  760. if (t2 > t1)
  761. return curveY(t1);
  762. // Fallback to the bisection method for reliability.
  763. while (t0 < t1) {
  764. x2 = curveX(t2);
  765. if (Math.abs(x2 - x) < epsilon)
  766. return curveY(t2);
  767. if (x > x2)
  768. t0 = t2;
  769. else
  770. t1 = t2;
  771. t2 = (t1 - t0) * 0.5 + t0;
  772. }
  773. // Failure
  774. return curveY(t2);
  775. };
  776. }
  777. function Timer(cfg) {
  778. var self = this;
  779. self.cfg = Util.mix({ easing: 'linear' }, cfg);
  780. }
  781. Timer.MIN_DURATION = 1;
  782. Util.extend(Timer, Base, {
  783. reset: function (cfg) {
  784. var self = this;
  785. Util.mix(self.cfg, cfg);
  786. self.isfinished = false;
  787. self.percent = 0;
  788. self._stop = null;
  789. },
  790. run: function () {
  791. var self = this;
  792. var duration = self.cfg.duration;
  793. if (duration <= Timer.MIN_DURATION) {
  794. self.isfinished = true;
  795. self.trigger('run', { percent: 1 });
  796. self.trigger('end', { percent: 1 });
  797. }
  798. if (self.isfinished)
  799. return;
  800. self._hasFinishedPercent = self._stop && self._stop.percent || 0;
  801. self._stop = null;
  802. self.start = Date.now();
  803. self.percent = 0;
  804. // epsilon determines the precision of the solved values
  805. var epsilon = 1000 / 60 / duration / 4;
  806. var b = Easing[self.cfg.easing];
  807. self.easingFn = Bezier(b[0], b[1], b[2], b[3], epsilon);
  808. self._run();
  809. },
  810. _run: function () {
  811. var self = this;
  812. cancelRAF(self._raf);
  813. self._raf = RAF(function () {
  814. self.now = Date.now();
  815. self.duration = self.now - self.start >= self.cfg.duration ? self.cfg.duration : self.now - self.start;
  816. self.progress = self.easingFn(self.duration / self.cfg.duration);
  817. self.percent = self.duration / self.cfg.duration + self._hasFinishedPercent;
  818. if (self.percent >= 1 || self._stop) {
  819. self.percent = self._stop && self._stop.percent ? self._stop.percent : 1;
  820. self.duration = self._stop && self._stop.duration ? self._stop.duration : self.duration;
  821. var param = { percent: self.percent };
  822. self.trigger('stop', param);
  823. if (self.percent >= 1) {
  824. self.isfinished = true;
  825. self.trigger('end', { percent: 1 });
  826. }
  827. return;
  828. }
  829. self.trigger('run', {
  830. percent: self.progress,
  831. originPercent: self.percent
  832. });
  833. self._run();
  834. });
  835. },
  836. stop: function () {
  837. var self = this;
  838. self._stop = {
  839. percent: self.percent,
  840. now: self.now
  841. };
  842. cancelRAF(self._raf);
  843. }
  844. });
  845. if (typeof module == 'object' && module.exports) {
  846. exports = Timer;
  847. } /** ignored by jsdoc **/ else {
  848. return Timer;
  849. }
  850. return exports;
  851. }(timer);
  852. animate = function (exports) {
  853. var Util = util;
  854. var Timer = timer;
  855. var Easing = easing;
  856. var Base = base;
  857. //transform
  858. var vendorTransform = Util.prefixStyle('transform');
  859. //transition webkitTransition MozTransition OTransition msTtransition
  860. var vendorTransition = Util.prefixStyle('transition');
  861. var vendorTransitionDuration = Util.prefixStyle('transitionDuration');
  862. var vendorTransformOrigin = Util.prefixStyle('transformOrigin');
  863. var vendorTransitionEnd = Util.vendor ? Util.prefixStyle('transitionEnd') : 'transitionend';
  864. var vendorTransformStr = Util.vendor ? [
  865. '-',
  866. Util.vendor,
  867. '-transform'
  868. ].join('') : 'transform';
  869. var translateTpl = 'translateX({translateX}px) translateY({translateY}px) translateZ(0)';
  870. //limit attrs
  871. var animAttrs = {
  872. 'transform': true,
  873. 'opacity': true,
  874. 'scrollTop': true,
  875. 'scrollLeft': true
  876. };
  877. function myParse(v) {
  878. return Math.round(parseFloat(v) * 100000) / 100000;
  879. }
  880. function defaultDecompose() {
  881. return {
  882. translateX: 0,
  883. translateY: 0,
  884. rotate: 0,
  885. skewX: 0,
  886. skewY: 0,
  887. scaleX: 1,
  888. scaleY: 1
  889. };
  890. }
  891. function toMatrixArray(matrix) {
  892. matrix = matrix.split(/,/);
  893. matrix = Array.prototype.map.call(matrix, function (v) {
  894. return myParse(v);
  895. });
  896. return matrix;
  897. }
  898. function decomposeMatrix(matrix) {
  899. matrix = toMatrixArray(matrix);
  900. var scaleX, scaleY, skew, A = matrix[0], B = matrix[1], C = matrix[2], D = matrix[3];
  901. // Make sure matrix is not singular
  902. if (A * D - B * C) {
  903. scaleX = Math.sqrt(A * A + B * B);
  904. skew = (A * C + B * D) / (A * D - C * B);
  905. scaleY = (A * D - B * C) / scaleX;
  906. // step (6)
  907. if (A * D < B * C) {
  908. skew = -skew;
  909. scaleX = -scaleX;
  910. } // matrix is singular and cannot be interpolated
  911. } else {
  912. // In this case the elem shouldn't be rendered, hence scale == 0
  913. scaleX = scaleY = skew = 0;
  914. }
  915. // The recomposition order is very important
  916. // see http://hg.mozilla.org/mozilla-central/file/7cb3e9795d04/layout/style/nsStyleAnimation.cpp#l971
  917. return {
  918. translateX: myParse(matrix[4]),
  919. translateY: myParse(matrix[5]),
  920. rotate: myParse(Math.atan2(B, A) * 180 / Math.PI),
  921. skewX: myParse(Math.atan(skew) * 180 / Math.PI),
  922. skewY: 0,
  923. scaleX: myParse(scaleX),
  924. scaleY: myParse(scaleY)
  925. };
  926. }
  927. function getTransformInfo(transform) {
  928. transform = transform.split(')');
  929. var trim = Util.trim, i = -1, l = transform.length - 1, split, prop, val, ret = defaultDecompose();
  930. // Loop through the transform properties, parse and multiply them
  931. while (++i < l) {
  932. split = transform[i].split('(');
  933. prop = trim(split[0]);
  934. val = split[1];
  935. switch (prop) {
  936. case 'translateX':
  937. case 'translateY':
  938. case 'scaleX':
  939. case 'scaleY':
  940. ret[prop] = myParse(val);
  941. break;
  942. case 'translate':
  943. case 'translate3d':
  944. val = val.split(',');
  945. ret.translateX = myParse(val[0]);
  946. ret.translateY = myParse(val[1] || 0);
  947. break;
  948. case 'scale':
  949. val = val.split(',');
  950. ret.scaleX = myParse(val[0]);
  951. ret.scaleY = myParse(val[1] || val[0]);
  952. break;
  953. case 'matrix':
  954. return decomposeMatrix(val);
  955. }
  956. }
  957. return ret;
  958. }
  959. /**
  960. * animate function
  961. * @constructor
  962. * @param {HTMLElement} el element to animate
  963. * @param {Object} config config for animate
  964. * @param {Object} config.css
  965. * @param {Number} config.duration
  966. * @param {String} config.easing
  967. * @extends {Base}
  968. */
  969. function Animate(el, cfg) {
  970. if (!el || !cfg || !cfg.css)
  971. return;
  972. var self = this;
  973. self.cfg = cfg;
  974. self.el = el;
  975. var duration = cfg.duration || 0, easing = cfg.easing || 'ease', delay = cfg.delay || 0;
  976. //trigger run
  977. if (cfg.run) {
  978. //frame animate
  979. self.timer = self.timer || new Timer({
  980. duration: Math.round(duration),
  981. easing: easing
  982. });
  983. self.timer.on('run', cfg.run);
  984. }
  985. self._bindEvt();
  986. return self;
  987. }
  988. function computeTransform(prevTransform, destTransform) {
  989. var transform = getTransformInfo(prevTransform);
  990. var dest = getTransformInfo(destTransform);
  991. var trans = {};
  992. for (var i in dest) {
  993. trans[i] = {
  994. prevVal: transform[i],
  995. newVal: dest[i]
  996. };
  997. }
  998. return trans;
  999. }
  1000. //for scroll only
  1001. function setStyle(el, styleName, prevVal, newVal, percent) {
  1002. prevVal = isNaN(Number(prevVal)) ? 0 : Number(prevVal);
  1003. var curVal = (newVal - prevVal) * percent + prevVal;
  1004. css(el, styleName, curVal);
  1005. }
  1006. function css(el, styleName, val) {
  1007. switch (styleName) {
  1008. case 'scrollTop':
  1009. case 'scrollLeft':
  1010. el[styleName] = val;
  1011. break;
  1012. case 'transform':
  1013. el.style[vendorTransform] = val;
  1014. case 'opacity':
  1015. el.style[styleName] = val;
  1016. break;
  1017. }
  1018. }
  1019. Util.extend(Animate, Base, {
  1020. /**
  1021. * to start the animation
  1022. * @memberof Animate
  1023. * @return {Animate}
  1024. */
  1025. run: function () {
  1026. var self = this;
  1027. var cfg = self.cfg, el = self.el, duration = cfg.duration || 0, easing = cfg.easing || 'ease', delay = cfg.delay || 0;
  1028. self.__isTransitionEnd = false;
  1029. clearTimeout(self.__itv);
  1030. self.timer && self.timer.run();
  1031. if (duration <= Timer.MIN_DURATION) {
  1032. for (var i in cfg.css) {
  1033. css(el, i, cfg.css[i]);
  1034. }
  1035. self.stop();
  1036. self.__handlers.stop.call(self);
  1037. return;
  1038. }
  1039. if (Util.isBadAndroid()) {
  1040. //use frame animate on bad android device
  1041. cfg.useTransition = false;
  1042. }
  1043. if (cfg.useTransition) {
  1044. //transition
  1045. el.style[vendorTransition] = Util.substitute('all {duration}ms {easing} {delay}ms', {
  1046. duration: Math.round(duration),
  1047. easing: Easing.format(easing),
  1048. delay: delay
  1049. });
  1050. for (var i in cfg.css) {
  1051. //set css
  1052. css(el, i, cfg.css[i]);
  1053. }
  1054. self.__itv = setTimeout(function () {
  1055. if (!self.__isTransitionEnd) {
  1056. self.__isTransitionEnd = true;
  1057. self.trigger('transitionend');
  1058. }
  1059. }, Number(duration) + 60);
  1060. } else {
  1061. self.computeStyle = self.computeStyle || window.getComputedStyle(el);
  1062. //transform
  1063. if (cfg.css.transform && self.timer) {
  1064. var transmap = self.transmap = computeTransform(self.computeStyle[vendorTransform], cfg.css.transform);
  1065. self.timer.off('run', self.__handlers.transRun);
  1066. self.timer.on('run', self.__handlers.transRun, self);
  1067. self.timer.off('end', self.__handlers.transRun);
  1068. self.timer.on('end', self.__handlers.transRun, self);
  1069. }
  1070. }
  1071. return self;
  1072. },
  1073. _transitionEndHandler: function (e) {
  1074. var self = this;
  1075. self.stop();
  1076. self.__handlers.stop.call(self);
  1077. },
  1078. __handlers: {
  1079. transRun: function (e) {
  1080. var self = this;
  1081. var transmap = self.transmap;
  1082. var el = self.el;
  1083. var newTrans = {};
  1084. for (var i in transmap) {
  1085. newTrans[i] = (transmap[i].newVal - transmap[i].prevVal) * e.percent + transmap[i].prevVal;
  1086. }
  1087. var ret = Util.substitute(translateTpl + ' ' + 'scale({scaleX},{scaleY})', newTrans);
  1088. el.style[vendorTransform] = ret;
  1089. },
  1090. stop: function (e) {
  1091. var self = this;
  1092. var cfg = self.cfg;
  1093. cfg.end && cfg.end({ percent: 1 });
  1094. }
  1095. },
  1096. _bindEvt: function () {
  1097. var self = this;
  1098. var cfg = self.cfg;
  1099. var el = self.el;
  1100. self.el.addEventListener(vendorTransitionEnd, function (e) {
  1101. self.__isTransitionEnd = true;
  1102. if (e.target !== e.currentTarget)
  1103. return;
  1104. self.trigger('transitionend', e);
  1105. });
  1106. self.on('transitionend', self._transitionEndHandler, self);
  1107. var cssRun = function (e) {
  1108. self.computeStyle = self.computeStyle || window.getComputedStyle(el);
  1109. for (var i in cfg.css) {
  1110. if (!/transform/.test(i)) {
  1111. setStyle(self.el, i, self.computeStyle[i], cfg.css[i], e.percent);
  1112. }
  1113. }
  1114. };
  1115. self.timer && self.timer.on('run', cssRun);
  1116. self.timer && self.timer.on('stop', self.__handlers.stop, self);
  1117. },
  1118. /**
  1119. * to stop the animation
  1120. * @memberof Animate
  1121. * @return {Animate}
  1122. */
  1123. stop: function () {
  1124. var self = this;
  1125. if (self.cfg.useTransition && self.cfg.duration > Timer.MIN_DURATION) {
  1126. var computeStyle = window.getComputedStyle(this.el);
  1127. for (var i in self.cfg.css) {
  1128. if (animAttrs[i]) {
  1129. var value = /transform/.test(i) ? computeStyle[vendorTransform] : computeStyle[i];
  1130. css(self.el, i, Util.substitute(translateTpl + ' ' + 'scale({scaleX},{scaleY})', getTransformInfo(value)));
  1131. }
  1132. }
  1133. self.el.style[vendorTransition] = 'none';
  1134. }
  1135. self.timer && self.timer.stop() && self.timer.reset();
  1136. self.computeStyle = null;
  1137. return self;
  1138. },
  1139. /**
  1140. * to reset the animation to a new state
  1141. * @memberof Animate
  1142. * @param {object} cfg cfg for new animation
  1143. * @return {Animate}
  1144. */
  1145. reset: function (cfg) {
  1146. var self = this;
  1147. self.computeStyle = null;
  1148. Util.mix(self.cfg, cfg);
  1149. this.timer && self.timer.reset({
  1150. duration: Math.round(self.cfg.duration),
  1151. easing: self.cfg.easing
  1152. });
  1153. return self;
  1154. }
  1155. });
  1156. if (typeof module == 'object' && module.exports) {
  1157. exports = Animate;
  1158. } /** ignored by jsdoc **/ else {
  1159. return Animate;
  1160. }
  1161. return exports;
  1162. }(animate);
  1163. hammer = function (exports) {
  1164. var VENDOR_PREFIXES = [
  1165. '',
  1166. 'webkit',
  1167. 'moz',
  1168. 'MS',
  1169. 'ms',
  1170. 'o'
  1171. ];
  1172. var TEST_ELEMENT = document.createElement('div');
  1173. var TYPE_FUNCTION = 'function';
  1174. var round = Math.round;
  1175. var abs = Math.abs;
  1176. var now = Date.now;
  1177. /**
  1178. * set a timeout with a given scope
  1179. * @param {Function} fn
  1180. * @param {Number} timeout
  1181. * @param {Object} context
  1182. * @returns {number}
  1183. */
  1184. function setTimeoutContext(fn, timeout, context) {
  1185. return setTimeout(bindFn(fn, context), timeout);
  1186. }
  1187. /**
  1188. * if the argument is an array, we want to execute the fn on each entry
  1189. * if it aint an array we don't want to do a thing.
  1190. * this is used by all the methods that accept a single and array argument.
  1191. * @param {*|Array} arg
  1192. * @param {String} fn
  1193. * @param {Object} [context]
  1194. * @returns {Boolean}
  1195. */
  1196. function invokeArrayArg(arg, fn, context) {
  1197. if (Array.isArray(arg)) {
  1198. each(arg, context[fn], context);
  1199. return true;
  1200. }
  1201. return false;
  1202. }
  1203. /**
  1204. * walk objects and arrays
  1205. * @param {Object} obj
  1206. * @param {Function} iterator
  1207. * @param {Object} context
  1208. */
  1209. function each(obj, iterator, context) {
  1210. var i;
  1211. if (!obj) {
  1212. return;
  1213. }
  1214. if (obj.forEach) {
  1215. obj.forEach(iterator, context);
  1216. } else if (obj.length !== undefined) {
  1217. i = 0;
  1218. while (i < obj.length) {
  1219. iterator.call(context, obj[i], i, obj);
  1220. i++;
  1221. }
  1222. } else {
  1223. for (i in obj) {
  1224. obj.hasOwnProperty(i) && iterator.call(context, obj[i], i, obj);
  1225. }
  1226. }
  1227. }
  1228. /**
  1229. * extend object.
  1230. * means that properties in dest will be overwritten by the ones in src.
  1231. * @param {Object} dest
  1232. * @param {Object} src
  1233. * @param {Boolean} [merge]
  1234. * @returns {Object} dest
  1235. */
  1236. function extend(dest, src, merge) {
  1237. var keys = Object.keys(src);
  1238. var i = 0;
  1239. while (i < keys.length) {
  1240. if (!merge || merge && dest[keys[i]] === undefined) {
  1241. dest[keys[i]] = src[keys[i]];
  1242. }
  1243. i++;
  1244. }
  1245. return dest;
  1246. }
  1247. /**
  1248. * merge the values from src in the dest.
  1249. * means that properties that exist in dest will not be overwritten by src
  1250. * @param {Object} dest
  1251. * @param {Object} src
  1252. * @returns {Object} dest
  1253. */
  1254. function merge(dest, src) {
  1255. return extend(dest, src, true);
  1256. }
  1257. /**
  1258. * simple class inheritance
  1259. * @param {Function} child
  1260. * @param {Function} base
  1261. * @param {Object} [properties]
  1262. */
  1263. function inherit(child, base, properties) {
  1264. var baseP = base.prototype, childP;
  1265. childP = child.prototype = Object.create(baseP);
  1266. childP.constructor = child;
  1267. childP._super = baseP;
  1268. if (properties) {
  1269. extend(childP, properties);
  1270. }
  1271. }
  1272. /**
  1273. * simple function bind
  1274. * @param {Function} fn
  1275. * @param {Object} context
  1276. * @returns {Function}
  1277. */
  1278. function bindFn(fn, context) {
  1279. return function boundFn() {
  1280. return fn.apply(context, arguments);
  1281. };
  1282. }
  1283. /**
  1284. * let a boolean value also be a function that must return a boolean
  1285. * this first item in args will be used as the context
  1286. * @param {Boolean|Function} val
  1287. * @param {Array} [args]
  1288. * @returns {Boolean}
  1289. */
  1290. function boolOrFn(val, args) {
  1291. if (typeof val == TYPE_FUNCTION) {
  1292. return val.apply(args ? args[0] || undefined : undefined, args);
  1293. }
  1294. return val;
  1295. }
  1296. /**
  1297. * use the val2 when val1 is undefined
  1298. * @param {*} val1
  1299. * @param {*} val2
  1300. * @returns {*}
  1301. */
  1302. function ifUndefined(val1, val2) {
  1303. return val1 === undefined ? val2 : val1;
  1304. }
  1305. /**
  1306. * addEventListener with multiple events at once
  1307. * @param {EventTarget} target
  1308. * @param {String} types
  1309. * @param {Function} handler
  1310. */
  1311. function addEventListeners(target, types, handler) {
  1312. each(splitStr(types), function (type) {
  1313. target.addEventListener(type, handler, false);
  1314. });
  1315. }
  1316. /**
  1317. * removeEventListener with multiple events at once
  1318. * @param {EventTarget} target
  1319. * @param {String} types
  1320. * @param {Function} handler
  1321. */
  1322. function removeEventListeners(target, types, handler) {
  1323. each(splitStr(types), function (type) {
  1324. target.removeEventListener(type, handler, false);
  1325. });
  1326. }
  1327. /**
  1328. * find if a node is in the given parent
  1329. * @method hasParent
  1330. * @param {HTMLElement} node
  1331. * @param {HTMLElement} parent
  1332. * @return {Boolean} found
  1333. */
  1334. function hasParent(node, parent) {
  1335. while (node) {
  1336. if (node == parent) {
  1337. return true;
  1338. }
  1339. node = node.parentNode;
  1340. }
  1341. return false;
  1342. }
  1343. /**
  1344. * small indexOf wrapper
  1345. * @param {String} str
  1346. * @param {String} find
  1347. * @returns {Boolean} found
  1348. */
  1349. function inStr(str, find) {
  1350. return str.indexOf(find) > -1;
  1351. }
  1352. /**
  1353. * split string on whitespace
  1354. * @param {String} str
  1355. * @returns {Array} words
  1356. */
  1357. function splitStr(str) {
  1358. return str.trim().split(/\s+/g);
  1359. }
  1360. /**
  1361. * find if a array contains the object using indexOf or a simple polyFill
  1362. * @param {Array} src
  1363. * @param {String} find
  1364. * @param {String} [findByKey]
  1365. * @return {Boolean|Number} false when not found, or the index
  1366. */
  1367. function inArray(src, find, findByKey) {
  1368. if (src.indexOf && !findByKey) {
  1369. return src.indexOf(find);
  1370. } else {
  1371. var i = 0;
  1372. while (i < src.length) {
  1373. if (findByKey && src[i][findByKey] == find || !findByKey && src[i] === find) {
  1374. return i;
  1375. }
  1376. i++;
  1377. }
  1378. return -1;
  1379. }
  1380. }
  1381. /**
  1382. * convert array-like objects to real arrays
  1383. * @param {Object} obj
  1384. * @returns {Array}
  1385. */
  1386. function toArray(obj) {
  1387. return Array.prototype.slice.call(obj, 0);
  1388. }
  1389. /**
  1390. * unique array with objects based on a key (like 'id') or just by the array's value
  1391. * @param {Array} src [{id:1},{id:2},{id:1}]
  1392. * @param {String} [key]
  1393. * @param {Boolean} [sort=False]
  1394. * @returns {Array} [{id:1},{id:2}]
  1395. */
  1396. function uniqueArray(src, key, sort) {
  1397. var results = [];
  1398. var values = [];
  1399. var i = 0;
  1400. while (i < src.length) {
  1401. var val = key ? src[i][key] : src[i];
  1402. if (inArray(values, val) < 0) {
  1403. results.push(src[i]);
  1404. }
  1405. values[i] = val;
  1406. i++;
  1407. }
  1408. if (sort) {
  1409. if (!key) {
  1410. results = results.sort();
  1411. } else {
  1412. results = results.sort(function sortUniqueArray(a, b) {
  1413. return a[key] > b[key];
  1414. });
  1415. }
  1416. }
  1417. return results;
  1418. }
  1419. /**
  1420. * get the prefixed property
  1421. * @param {Object} obj
  1422. * @param {String} property
  1423. * @returns {String|Undefined} prefixed
  1424. */
  1425. function prefixed(obj, property) {
  1426. var prefix, prop;
  1427. var camelProp = property[0].toUpperCase() + property.slice(1);
  1428. var i = 0;
  1429. while (i < VENDOR_PREFIXES.length) {
  1430. prefix = VENDOR_PREFIXES[i];
  1431. prop = prefix ? prefix + camelProp : property;
  1432. if (prop in obj) {
  1433. return prop;
  1434. }
  1435. i++;
  1436. }
  1437. return undefined;
  1438. }
  1439. /**
  1440. * get a unique id
  1441. * @returns {number} uniqueId
  1442. */
  1443. var _uniqueId = 1;
  1444. function uniqueId() {
  1445. return _uniqueId++;
  1446. }
  1447. /**
  1448. * get the window object of an element
  1449. * @param {HTMLElement} element
  1450. * @returns {DocumentView|Window}
  1451. */
  1452. function getWindowForElement(element) {
  1453. var doc = element.ownerDocument;
  1454. return doc.defaultView || doc.parentWindow;
  1455. }
  1456. var MOBILE_REGEX = /mobile|tablet|ip(ad|hone|od)|android/i;
  1457. var SUPPORT_TOUCH = 'ontouchstart' in window;
  1458. var SUPPORT_POINTER_EVENTS = prefixed(window, 'PointerEvent') !== undefined;
  1459. var SUPPORT_ONLY_TOUCH = SUPPORT_TOUCH && MOBILE_REGEX.test(navigator.userAgent);
  1460. var INPUT_TYPE_TOUCH = 'touch';
  1461. var INPUT_TYPE_PEN = 'pen';
  1462. var INPUT_TYPE_MOUSE = 'mouse';
  1463. var INPUT_TYPE_KINECT = 'kinect';
  1464. var COMPUTE_INTERVAL = 25;
  1465. var INPUT_START = 1;
  1466. var INPUT_MOVE = 2;
  1467. var INPUT_END = 4;
  1468. var INPUT_CANCEL = 8;
  1469. var DIRECTION_NONE = 1;
  1470. var DIRECTION_LEFT = 2;
  1471. var DIRECTION_RIGHT = 4;
  1472. var DIRECTION_UP = 8;
  1473. var DIRECTION_DOWN = 16;
  1474. var DIRECTION_HORIZONTAL = DIRECTION_LEFT | DIRECTION_RIGHT;
  1475. var DIRECTION_VERTICAL = DIRECTION_UP | DIRECTION_DOWN;
  1476. var DIRECTION_ALL = DIRECTION_HORIZONTAL | DIRECTION_VERTICAL;
  1477. var PROPS_XY = [
  1478. 'x',
  1479. 'y'
  1480. ];
  1481. var PROPS_CLIENT_XY = [
  1482. 'clientX',
  1483. 'clientY'
  1484. ];
  1485. /**
  1486. * create new input type manager
  1487. * @param {Manager} manager
  1488. * @param {Function} callback
  1489. * @returns {Input}
  1490. * @constructor
  1491. */
  1492. function Input(manager, callback) {
  1493. var self = this;
  1494. this.manager = manager;
  1495. this.callback = callback;
  1496. this.element = manager.element;
  1497. this.target = manager.options.inputTarget;
  1498. // smaller wrapper around the handler, for the scope and the enabled state of the manager,
  1499. // so when disabled the input events are completely bypassed.
  1500. this.domHandler = function (ev) {
  1501. if (boolOrFn(manager.options.enable, [manager])) {
  1502. self.handler(ev);
  1503. }
  1504. };
  1505. this.init();
  1506. }
  1507. Input.prototype = {
  1508. /**
  1509. * should handle the inputEvent data and trigger the callback
  1510. * @virtual
  1511. */
  1512. handler: function () {
  1513. },
  1514. /**
  1515. * bind the events
  1516. */
  1517. init: function () {
  1518. this.evEl && addEventListeners(this.element, this.evEl, this.domHandler);
  1519. this.evTarget && addEventListeners(this.target, this.evTarget, this.domHandler);
  1520. this.evWin && addEventListeners(getWindowForElement(this.element), this.evWin, this.domHandler);
  1521. },
  1522. /**
  1523. * unbind the events
  1524. */
  1525. destroy: function () {
  1526. this.evEl && removeEventListeners(this.element, this.evEl, this.domHandler);
  1527. this.evTarget && removeEventListeners(this.target, this.evTarget, this.domHandler);
  1528. this.evWin && removeEventListeners(getWindowForElement(this.element), this.evWin, this.domHandler);
  1529. }
  1530. };
  1531. /**
  1532. * create new input type manager
  1533. * called by the Manager constructor
  1534. * @param {Hammer} manager
  1535. * @returns {Input}
  1536. */
  1537. function createInputInstance(manager) {
  1538. var Type;
  1539. var inputClass = manager.options.inputClass;
  1540. if (inputClass) {
  1541. Type = inputClass;
  1542. } else if (SUPPORT_POINTER_EVENTS) {
  1543. Type = PointerEventInput;
  1544. } else if (SUPPORT_ONLY_TOUCH) {
  1545. Type = TouchInput;
  1546. } else if (!SUPPORT_TOUCH) {
  1547. Type = MouseInput;
  1548. } else {
  1549. Type = TouchMouseInput;
  1550. }
  1551. return new Type(manager, inputHandler);
  1552. }
  1553. /**
  1554. * handle input events
  1555. * @param {Manager} manager
  1556. * @param {String} eventType
  1557. * @param {Object} input
  1558. */
  1559. function inputHandler(manager, eventType, input) {
  1560. var pointersLen = input.pointers.length;
  1561. var changedPointersLen = input.changedPointers.length;
  1562. var isFirst = eventType & INPUT_START && pointersLen - changedPointersLen === 0;
  1563. var isFinal = eventType & (INPUT_END | INPUT_CANCEL) && pointersLen - changedPointersLen === 0;
  1564. input.isFirst = !!isFirst;
  1565. input.isFinal = !!isFinal;
  1566. if (isFirst) {
  1567. manager.session = {};
  1568. }
  1569. // source event is the normalized value of the domEvents
  1570. // like 'touchstart, mouseup, pointerdown'
  1571. input.eventType = eventType;
  1572. // compute scale, rotation etc
  1573. computeInputData(manager, input);
  1574. // emit secret event
  1575. manager.emit('hammer.input', input);
  1576. manager.recognize(input);
  1577. manager.session.prevInput = input;
  1578. }
  1579. /**
  1580. * extend the data with some usable properties like scale, rotate, velocity etc
  1581. * @param {Object} manager
  1582. * @param {Object} input
  1583. */
  1584. function computeInputData(manager, input) {
  1585. var session = manager.session;
  1586. var pointers = input.pointers;
  1587. var pointersLength = pointers.length;
  1588. // store the first input to calculate the distance and direction
  1589. if (!session.firstInput) {
  1590. session.firstInput = simpleCloneInputData(input);
  1591. }
  1592. // to compute scale and rotation we need to store the multiple touches
  1593. if (pointersLength > 1 && !session.firstMultiple) {
  1594. session.firstMultiple = simpleCloneInputData(input);
  1595. } else if (pointersLength === 1) {
  1596. session.firstMultiple = false;
  1597. }
  1598. var firstInput = session.firstInput;
  1599. var firstMultiple = session.firstMultiple;
  1600. var offsetCenter = firstMultiple ? firstMultiple.center : firstInput.center;
  1601. var center = input.center = getCenter(pointers);
  1602. input.timeStamp = now();
  1603. input.deltaTime = input.timeStamp - firstInput.timeStamp;
  1604. input.angle = getAngle(offsetCenter, center);
  1605. input.distance = getDistance(offsetCenter, center);
  1606. computeDeltaXY(session, input);
  1607. input.offsetDirection = getDirection(input.deltaX, input.deltaY);
  1608. input.scale = firstMultiple ? getScale(firstMultiple.pointers, pointers) : 1;
  1609. input.rotation = firstMultiple ? getRotation(firstMultiple.pointers, pointers) : 0;
  1610. computeIntervalInputData(session, input);
  1611. // find the correct target
  1612. var target = manager.element;
  1613. if (hasParent(input.srcEvent.target, target)) {
  1614. target = input.srcEvent.target;
  1615. }
  1616. input.target = target;
  1617. }
  1618. function computeDeltaXY(session, input) {
  1619. var center = input.center;
  1620. var offset = session.offsetDelta || {};
  1621. var prevDelta = session.prevDelta || {};
  1622. var prevInput = session.prevInput || {};
  1623. if (input.eventType === INPUT_START || prevInput.eventType === INPUT_END) {
  1624. prevDelta = session.prevDelta = {
  1625. x: prevInput.deltaX || 0,
  1626. y: prevInput.deltaY || 0
  1627. };
  1628. offset = session.offsetDelta = {
  1629. x: center.x,
  1630. y: center.y
  1631. };
  1632. }
  1633. input.deltaX = prevDelta.x + (center.x - offset.x);
  1634. input.deltaY = prevDelta.y + (center.y - offset.y);
  1635. }
  1636. /**
  1637. * velocity is calculated every x ms
  1638. * @param {Object} session
  1639. * @param {Object} input
  1640. */
  1641. function computeIntervalInputData(session, input) {
  1642. var last = session.lastInterval || input, deltaTime = input.timeStamp - last.timeStamp, velocity, velocityX, velocityY, direction;
  1643. if (input.eventType != INPUT_CANCEL && (deltaTime > COMPUTE_INTERVAL || last.velocity === undefined)) {
  1644. var deltaX = last.deltaX - input.deltaX;
  1645. var deltaY = last.deltaY - input.deltaY;
  1646. var v = getVelocity(deltaTime, deltaX, deltaY);
  1647. velocityX = v.x;
  1648. velocityY = v.y;
  1649. velocity = abs(v.x) > abs(v.y) ? v.x : v.y;
  1650. direction = getDirection(deltaX, deltaY);
  1651. session.lastInterval = input;
  1652. } else {
  1653. // use latest velocity info if it doesn't overtake a minimum period
  1654. velocity = last.velocity;
  1655. velocityX = last.velocityX;
  1656. velocityY = last.velocityY;
  1657. direction = last.direction;
  1658. }
  1659. input.velocity = velocity;
  1660. input.velocityX = velocityX;
  1661. input.velocityY = velocityY;
  1662. input.direction = direction;
  1663. }
  1664. /**
  1665. * create a simple clone from the input used for storage of firstInput and firstMultiple
  1666. * @param {Object} input
  1667. * @returns {Object} clonedInputData
  1668. */
  1669. function simpleCloneInputData(input) {
  1670. // make a simple copy of the pointers because we will get a reference if we don't
  1671. // we only need clientXY for the calculations
  1672. var pointers = [];
  1673. var i = 0;
  1674. while (i < input.pointers.length) {
  1675. pointers[i] = {
  1676. clientX: round(input.pointers[i].clientX),
  1677. clientY: round(input.pointers[i].clientY)
  1678. };
  1679. i++;
  1680. }
  1681. return {
  1682. timeStamp: now(),
  1683. pointers: pointers,
  1684. center: getCenter(pointers),
  1685. deltaX: input.deltaX,
  1686. deltaY: input.deltaY
  1687. };
  1688. }
  1689. /**
  1690. * get the center of all the pointers
  1691. * @param {Array} pointers
  1692. * @return {Object} center contains `x` and `y` properties
  1693. */
  1694. function getCenter(pointers) {
  1695. var pointersLength = pointers.length;
  1696. // no need to loop when only one touch
  1697. if (pointersLength === 1) {
  1698. return {
  1699. x: round(pointers[0].clientX),
  1700. y: round(pointers[0].clientY)
  1701. };
  1702. }
  1703. var x = 0, y = 0, i = 0;
  1704. while (i < pointersLength) {
  1705. x += pointers[i].clientX;
  1706. y += pointers[i].clientY;
  1707. i++;
  1708. }
  1709. return {
  1710. x: round(x / pointersLength),
  1711. y: round(y / pointersLength)
  1712. };
  1713. }
  1714. /**
  1715. * calculate the velocity between two points. unit is in px per ms.
  1716. * @param {Number} deltaTime
  1717. * @param {Number} x
  1718. * @param {Number} y
  1719. * @return {Object} velocity `x` and `y`
  1720. */
  1721. function getVelocity(deltaTime, x, y) {
  1722. return {
  1723. x: x / deltaTime || 0,
  1724. y: y / deltaTime || 0
  1725. };
  1726. }
  1727. /**
  1728. * get the direction between two points
  1729. * @param {Number} x
  1730. * @param {Number} y
  1731. * @return {Number} direction
  1732. */
  1733. function getDirection(x, y) {
  1734. if (x === y) {
  1735. return DIRECTION_NONE;
  1736. }
  1737. if (abs(x) >= abs(y)) {
  1738. return x > 0 ? DIRECTION_LEFT : DIRECTION_RIGHT;
  1739. }
  1740. return y > 0 ? DIRECTION_UP : DIRECTION_DOWN;
  1741. }
  1742. /**
  1743. * calculate the absolute distance between two points
  1744. * @param {Object} p1 {x, y}
  1745. * @param {Object} p2 {x, y}
  1746. * @param {Array} [props] containing x and y keys
  1747. * @return {Number} distance
  1748. */
  1749. function getDistance(p1, p2, props) {
  1750. if (!props) {
  1751. props = PROPS_XY;
  1752. }
  1753. var x = p2[props[0]] - p1[props[0]], y = p2[props[1]] - p1[props[1]];
  1754. return Math.sqrt(x * x + y * y);
  1755. }
  1756. /**
  1757. * calculate the angle between two coordinates
  1758. * @param {Object} p1
  1759. * @param {Object} p2
  1760. * @param {Array} [props] containing x and y keys
  1761. * @return {Number} angle
  1762. */
  1763. function getAngle(p1, p2, props) {
  1764. if (!props) {
  1765. props = PROPS_XY;
  1766. }
  1767. var x = p2[props[0]] - p1[props[0]], y = p2[props[1]] - p1[props[1]];
  1768. return Math.atan2(y, x) * 180 / Math.PI;
  1769. }
  1770. /**
  1771. * calculate the rotation degrees between two pointersets
  1772. * @param {Array} start array of pointers
  1773. * @param {Array} end array of pointers
  1774. * @return {Number} rotation
  1775. */
  1776. function getRotation(start, end) {
  1777. return getAngle(end[1], end[0], PROPS_CLIENT_XY) - getAngle(start[1], start[0], PROPS_CLIENT_XY);
  1778. }
  1779. /**
  1780. * calculate the scale factor between two pointersets
  1781. * no scale is 1, and goes down to 0 when pinched together, and bigger when pinched out
  1782. * @param {Array} start array of pointers
  1783. * @param {Array} end array of pointers
  1784. * @return {Number} scale
  1785. */
  1786. function getScale(start, end) {
  1787. return getDistance(end[0], end[1], PROPS_CLIENT_XY) / getDistance(start[0], start[1], PROPS_CLIENT_XY);
  1788. }
  1789. var MOUSE_INPUT_MAP = {
  1790. mousedown: INPUT_START,
  1791. mousemove: INPUT_MOVE,
  1792. mouseup: INPUT_END
  1793. };
  1794. var MOUSE_ELEMENT_EVENTS = 'mousedown';
  1795. var MOUSE_WINDOW_EVENTS = 'mousemove mouseup';
  1796. /**
  1797. * Mouse events input
  1798. * @constructor
  1799. * @extends Input
  1800. */
  1801. function MouseInput() {
  1802. this.evEl = MOUSE_ELEMENT_EVENTS;
  1803. this.evWin = MOUSE_WINDOW_EVENTS;
  1804. this.allow = true;
  1805. // used by Input.TouchMouse to disable mouse events
  1806. this.pressed = false;
  1807. // mousedown state
  1808. Input.apply(this, arguments);
  1809. }
  1810. inherit(MouseInput, Input, {
  1811. /**
  1812. * handle mouse events
  1813. * @param {Object} ev
  1814. */
  1815. handler: function MEhandler(ev) {
  1816. var eventType = MOUSE_INPUT_MAP[ev.type];
  1817. // on start we want to have the left mouse button down
  1818. if (eventType & INPUT_START && ev.button === 0) {
  1819. this.pressed = true;
  1820. }
  1821. if (eventType & INPUT_MOVE && ev.which !== 1) {
  1822. eventType = INPUT_END;
  1823. }
  1824. // mouse must be down, and mouse events are allowed (see the TouchMouse input)
  1825. if (!this.pressed || !this.allow) {
  1826. return;
  1827. }
  1828. if (eventType & INPUT_END) {
  1829. this.pressed = false;
  1830. }
  1831. this.callback(this.manager, eventType, {
  1832. pointers: [ev],
  1833. changedPointers: [ev],
  1834. pointerType: INPUT_TYPE_MOUSE,
  1835. srcEvent: ev
  1836. });
  1837. }
  1838. });
  1839. var POINTER_INPUT_MAP = {
  1840. pointerdown: INPUT_START,
  1841. pointermove: INPUT_MOVE,
  1842. pointerup: INPUT_END,
  1843. pointercancel: INPUT_CANCEL,
  1844. pointerout: INPUT_CANCEL
  1845. };
  1846. // in IE10 the pointer types is defined as an enum
  1847. var IE10_POINTER_TYPE_ENUM = {
  1848. 2: INPUT_TYPE_TOUCH,
  1849. 3: INPUT_TYPE_PEN,
  1850. 4: INPUT_TYPE_MOUSE,
  1851. 5: INPUT_TYPE_KINECT // see https://twitter.com/jacobrossi/status/480596438489890816
  1852. };
  1853. var POINTER_ELEMENT_EVENTS = 'pointerdown';
  1854. var POINTER_WINDOW_EVENTS = 'pointermove pointerup pointercancel';
  1855. // IE10 has prefixed support, and case-sensitive
  1856. if (window.MSPointerEvent) {
  1857. POINTER_ELEMENT_EVENTS = 'MSPointerDown';
  1858. POINTER_WINDOW_EVENTS = 'MSPointerMove MSPointerUp MSPointerCancel';
  1859. }
  1860. /**
  1861. * Pointer events input
  1862. * @constructor
  1863. * @extends Input
  1864. */
  1865. function PointerEventInput() {
  1866. this.evEl = POINTER_ELEMENT_EVENTS;
  1867. this.evWin = POINTER_WINDOW_EVENTS;
  1868. Input.apply(this, arguments);
  1869. this.store = this.manager.session.pointerEvents = [];
  1870. }
  1871. inherit(PointerEventInput, Input, {
  1872. /**
  1873. * handle mouse events
  1874. * @param {Object} ev
  1875. */
  1876. handler: function PEhandler(ev) {
  1877. var store = this.store;
  1878. var removePointer = false;
  1879. var eventTypeNormalized = ev.type.toLowerCase().replace('ms', '');
  1880. var eventType = POINTER_INPUT_MAP[eventTypeNormalized];
  1881. var pointerType = IE10_POINTER_TYPE_ENUM[ev.pointerType] || ev.pointerType;
  1882. var isTouch = pointerType == INPUT_TYPE_TOUCH;
  1883. // get index of the event in the store
  1884. var storeIndex = inArray(store, ev.pointerId, 'pointerId');
  1885. // start and mouse must be down
  1886. if (eventType & INPUT_START && (ev.button === 0 || isTouch)) {
  1887. if (storeIndex < 0) {
  1888. store.push(ev);
  1889. storeIndex = store.length - 1;
  1890. }
  1891. } else if (eventType & (INPUT_END | INPUT_CANCEL)) {
  1892. removePointer = true;
  1893. }
  1894. // it not found, so the pointer hasn't been down (so it's probably a hover)
  1895. if (storeIndex < 0) {
  1896. return;
  1897. }
  1898. // update the event in the store
  1899. store[storeIndex] = ev;
  1900. this.callback(this.manager, eventType, {
  1901. pointers: store,
  1902. changedPointers: [ev],
  1903. pointerType: pointerType,
  1904. srcEvent: ev
  1905. });
  1906. if (removePointer) {
  1907. // remove from the store
  1908. store.splice(storeIndex, 1);
  1909. }
  1910. }
  1911. });
  1912. var SINGLE_TOUCH_INPUT_MAP = {
  1913. touchstart: INPUT_START,
  1914. touchmove: INPUT_MOVE,
  1915. touchend: INPUT_END,
  1916. touchcancel: INPUT_CANCEL
  1917. };
  1918. var SINGLE_TOUCH_TARGET_EVENTS = 'touchstart';
  1919. var SINGLE_TOUCH_WINDOW_EVENTS = 'touchstart touchmove touchend touchcancel';
  1920. /**
  1921. * Touch events input
  1922. * @constructor
  1923. * @extends Input
  1924. */
  1925. function SingleTouchInput() {
  1926. this.evTarget = SINGLE_TOUCH_TARGET_EVENTS;
  1927. this.evWin = SINGLE_TOUCH_WINDOW_EVENTS;
  1928. this.started = false;
  1929. Input.apply(this, arguments);
  1930. }
  1931. inherit(SingleTouchInput, Input, {
  1932. handler: function TEhandler(ev) {
  1933. var type = SINGLE_TOUCH_INPUT_MAP[ev.type];
  1934. // should we handle the touch events?
  1935. if (type === INPUT_START) {
  1936. this.started = true;
  1937. }
  1938. if (!this.started) {
  1939. return;
  1940. }
  1941. var touches = normalizeSingleTouches.call(this, ev, type);
  1942. // when done, reset the started state
  1943. if (type & (INPUT_END | INPUT_CANCEL) && touches[0].length - touches[1].length === 0) {
  1944. this.started = false;
  1945. }
  1946. this.callback(this.manager, type, {
  1947. pointers: touches[0],
  1948. changedPointers: touches[1],
  1949. pointerType: INPUT_TYPE_TOUCH,
  1950. srcEvent: ev
  1951. });
  1952. }
  1953. });
  1954. /**
  1955. * @this {TouchInput}
  1956. * @param {Object} ev
  1957. * @param {Number} type flag
  1958. * @returns {undefined|Array} [all, changed]
  1959. */
  1960. function normalizeSingleTouches(ev, type) {
  1961. var all = toArray(ev.touches);
  1962. var changed = toArray(ev.changedTouches);
  1963. if (type & (INPUT_END | INPUT_CANCEL)) {
  1964. all = uniqueArray(all.concat(changed), 'identifier', true);
  1965. }
  1966. return [
  1967. all,
  1968. changed
  1969. ];
  1970. }
  1971. var TOUCH_INPUT_MAP = {
  1972. touchstart: INPUT_START,
  1973. touchmove: INPUT_MOVE,
  1974. touchend: INPUT_END,
  1975. touchcancel: INPUT_CANCEL
  1976. };
  1977. var TOUCH_TARGET_EVENTS = 'touchstart touchmove touchend touchcancel';
  1978. /**
  1979. * Multi-user touch events input
  1980. * @constructor
  1981. * @extends Input
  1982. */
  1983. function TouchInput() {
  1984. this.evTarget = TOUCH_TARGET_EVENTS;
  1985. this.targetIds = {};
  1986. Input.apply(this, arguments);
  1987. }
  1988. inherit(TouchInput, Input, {
  1989. handler: function MTEhandler(ev) {
  1990. var type = TOUCH_INPUT_MAP[ev.type];
  1991. var touches = getTouches.call(this, ev, type);
  1992. if (!touches) {
  1993. return;
  1994. }
  1995. this.callback(this.manager, type, {
  1996. pointers: touches[0],
  1997. changedPointers: touches[1],
  1998. pointerType: INPUT_TYPE_TOUCH,
  1999. srcEvent: ev
  2000. });
  2001. }
  2002. });
  2003. /**
  2004. * @this {TouchInput}
  2005. * @param {Object} ev
  2006. * @param {Number} type flag
  2007. * @returns {undefined|Array} [all, changed]
  2008. */
  2009. function getTouches(ev, type) {
  2010. var allTouches = toArray(ev.touches);
  2011. var targetIds = this.targetIds;
  2012. // when there is only one touch, the process can be simplified
  2013. if (type & (INPUT_START | INPUT_MOVE) && allTouches.length === 1) {
  2014. targetIds[allTouches[0].identifier] = true;
  2015. return [
  2016. allTouches,
  2017. allTouches
  2018. ];
  2019. }
  2020. var i, targetTouches, changedTouches = toArray(ev.changedTouches), changedTargetTouches = [], target = this.target;
  2021. // get target touches from touches
  2022. targetTouches = allTouches.filter(function (touch) {
  2023. return hasParent(touch.target, target);
  2024. });
  2025. // collect touches
  2026. if (type === INPUT_START) {
  2027. i = 0;
  2028. while (i < targetTouches.length) {
  2029. targetIds[targetTouches[i].identifier] = true;
  2030. i++;
  2031. }
  2032. }
  2033. // filter changed touches to only contain touches that exist in the collected target ids
  2034. i = 0;
  2035. while (i < changedTouches.length) {
  2036. if (targetIds[changedTouches[i].identifier]) {
  2037. changedTargetTouches.push(changedTouches[i]);
  2038. }
  2039. // cleanup removed touches
  2040. if (type & (INPUT_END | INPUT_CANCEL)) {
  2041. delete targetIds[changedTouches[i].identifier];
  2042. }
  2043. i++;
  2044. }
  2045. if (!changedTargetTouches.length) {
  2046. return;
  2047. }
  2048. return [
  2049. // merge targetTouches with changedTargetTouches so it contains ALL touches, including 'end' and 'cancel'
  2050. uniqueArray(targetTouches.concat(changedTargetTouches), 'identifier', true),
  2051. changedTargetTouches
  2052. ];
  2053. }
  2054. /**
  2055. * Combined touch and mouse input
  2056. *
  2057. * Touch has a higher priority then mouse, and while touching no mouse events are allowed.
  2058. * This because touch devices also emit mouse events while doing a touch.
  2059. *
  2060. * @constructor
  2061. * @extends Input
  2062. */
  2063. function TouchMouseInput() {
  2064. Input.apply(this, arguments);
  2065. var handler = bindFn(this.handler, this);
  2066. this.touch = new TouchInput(this.manager, handler);
  2067. this.mouse = new MouseInput(this.manager, handler);
  2068. }
  2069. inherit(TouchMouseInput, Input, {
  2070. /**
  2071. * handle mouse and touch events
  2072. * @param {Hammer} manager
  2073. * @param {String} inputEvent
  2074. * @param {Object} inputData
  2075. */
  2076. handler: function TMEhandler(manager, inputEvent, inputData) {
  2077. var isTouch = inputData.pointerType == INPUT_TYPE_TOUCH, isMouse = inputData.pointerType == INPUT_TYPE_MOUSE;
  2078. // when we're in a touch event, so block all upcoming mouse events
  2079. // most mobile browser also emit mouseevents, right after touchstart
  2080. if (isTouch) {
  2081. this.mouse.allow = false;
  2082. } else if (isMouse && !this.mouse.allow) {
  2083. return;
  2084. }
  2085. // reset the allowMouse when we're done
  2086. if (inputEvent & (INPUT_END | INPUT_CANCEL)) {
  2087. this.mouse.allow = true;
  2088. }
  2089. this.callback(manager, inputEvent, inputData);
  2090. },
  2091. /**
  2092. * remove the event listeners
  2093. */
  2094. destroy: function destroy() {
  2095. this.touch.destroy();
  2096. this.mouse.destroy();
  2097. }
  2098. });
  2099. var PREFIXED_TOUCH_ACTION = prefixed(TEST_ELEMENT.style, 'touchAction');
  2100. var NATIVE_TOUCH_ACTION = PREFIXED_TOUCH_ACTION !== undefined;
  2101. // magical touchAction value
  2102. var TOUCH_ACTION_COMPUTE = 'compute';
  2103. var TOUCH_ACTION_AUTO = 'auto';
  2104. var TOUCH_ACTION_MANIPULATION = 'manipulation';
  2105. // not implemented
  2106. var TOUCH_ACTION_NONE = 'none';
  2107. var TOUCH_ACTION_PAN_X = 'pan-x';
  2108. var TOUCH_ACTION_PAN_Y = 'pan-y';
  2109. /**
  2110. * Touch Action
  2111. * sets the touchAction property or uses the js alternative
  2112. * @param {Manager} manager
  2113. * @param {String} value
  2114. * @constructor
  2115. */
  2116. function TouchAction(manager, value) {
  2117. this.manager = manager;
  2118. this.set(value);
  2119. }
  2120. TouchAction.prototype = {
  2121. /**
  2122. * set the touchAction value on the element or enable the polyfill
  2123. * @param {String} value
  2124. */
  2125. set: function (value) {
  2126. // find out the touch-action by the event handlers
  2127. if (value == TOUCH_ACTION_COMPUTE) {
  2128. value = this.compute();
  2129. }
  2130. if (NATIVE_TOUCH_ACTION) {
  2131. this.manager.element.style[PREFIXED_TOUCH_ACTION] = value;
  2132. }
  2133. this.actions = value.toLowerCase().trim();
  2134. },
  2135. /**
  2136. * just re-set the touchAction value
  2137. */
  2138. update: function () {
  2139. this.set(this.manager.options.touchAction);
  2140. },
  2141. /**
  2142. * compute the value for the touchAction property based on the recognizer's settings
  2143. * @returns {String} value
  2144. */
  2145. compute: function () {
  2146. var actions = [];
  2147. each(this.manager.recognizers, function (recognizer) {
  2148. if (boolOrFn(recognizer.options.enable, [recognizer])) {
  2149. actions = actions.concat(recognizer.getTouchAction());
  2150. }
  2151. });
  2152. return cleanTouchActions(actions.join(' '));
  2153. },
  2154. /**
  2155. * this method is called on each input cycle and provides the preventing of the browser behavior
  2156. * @param {Object} input
  2157. */
  2158. preventDefaults: function (input) {
  2159. // not needed with native support for the touchAction property
  2160. if (NATIVE_TOUCH_ACTION) {
  2161. return;
  2162. }
  2163. var srcEvent = input.srcEvent;
  2164. var direction = input.offsetDirection;
  2165. // if the touch action did prevented once this session
  2166. if (this.manager.session.prevented) {
  2167. srcEvent.preventDefault();
  2168. return;
  2169. }
  2170. var actions = this.actions;
  2171. var hasNone = inStr(actions, TOUCH_ACTION_NONE);
  2172. var hasPanY = inStr(actions, TOUCH_ACTION_PAN_Y);
  2173. var hasPanX = inStr(actions, TOUCH_ACTION_PAN_X);
  2174. if (hasNone || hasPanY && direction & DIRECTION_HORIZONTAL || hasPanX && direction & DIRECTION_VERTICAL) {
  2175. return this.preventSrc(srcEvent);
  2176. }
  2177. },
  2178. /**
  2179. * call preventDefault to prevent the browser's default behavior (scrolling in most cases)
  2180. * @param {Object} srcEvent
  2181. */
  2182. preventSrc: function (srcEvent) {
  2183. this.manager.session.prevented = true;
  2184. srcEvent.preventDefault();
  2185. }
  2186. };
  2187. /**
  2188. * when the touchActions are collected they are not a valid value, so we need to clean things up. *
  2189. * @param {String} actions
  2190. * @returns {*}
  2191. */
  2192. function cleanTouchActions(actions) {
  2193. // none
  2194. if (inStr(actions, TOUCH_ACTION_NONE)) {
  2195. return TOUCH_ACTION_NONE;
  2196. }
  2197. var hasPanX = inStr(actions, TOUCH_ACTION_PAN_X);
  2198. var hasPanY = inStr(actions, TOUCH_ACTION_PAN_Y);
  2199. // pan-x and pan-y can be combined
  2200. if (hasPanX && hasPanY) {
  2201. return TOUCH_ACTION_PAN_X + ' ' + TOUCH_ACTION_PAN_Y;
  2202. }
  2203. // pan-x OR pan-y
  2204. if (hasPanX || hasPanY) {
  2205. return hasPanX ? TOUCH_ACTION_PAN_X : TOUCH_ACTION_PAN_Y;
  2206. }
  2207. // manipulation
  2208. if (inStr(actions, TOUCH_ACTION_MANIPULATION)) {
  2209. return TOUCH_ACTION_MANIPULATION;
  2210. }
  2211. return TOUCH_ACTION_AUTO;
  2212. }
  2213. /**
  2214. * Recognizer flow explained; *
  2215. * All recognizers have the initial state of POSSIBLE when a input session starts.
  2216. * The definition of a input session is from the first input until the last input, with all it's movement in it. *
  2217. * Example session for mouse-input: mousedown -> mousemove -> mouseup
  2218. *
  2219. * On each recognizing cycle (see Manager.recognize) the .recognize() method is executed
  2220. * which determines with state it should be.
  2221. *
  2222. * If the recognizer has the state FAILED, CANCELLED or RECOGNIZED (equals ENDED), it is reset to
  2223. * POSSIBLE to give it another change on the next cycle.
  2224. *
  2225. * Possible
  2226. * |
  2227. * +-----+---------------+
  2228. * | |
  2229. * +-----+-----+ |
  2230. * | | |
  2231. * Failed Cancelled |
  2232. * +-------+------+
  2233. * | |
  2234. * Recognized Began
  2235. * |
  2236. * Changed
  2237. * |
  2238. * Ended/Recognized
  2239. */
  2240. var STATE_POSSIBLE = 1;
  2241. var STATE_BEGAN = 2;
  2242. var STATE_CHANGED = 4;
  2243. var STATE_ENDED = 8;
  2244. var STATE_RECOGNIZED = STATE_ENDED;
  2245. var STATE_CANCELLED = 16;
  2246. var STATE_FAILED = 32;
  2247. /**
  2248. * Recognizer
  2249. * Every recognizer needs to extend from this class.
  2250. * @constructor
  2251. * @param {Object} options
  2252. */
  2253. function Recognizer(options) {
  2254. this.id = uniqueId();
  2255. this.manager = null;
  2256. this.options = merge(options || {}, this.defaults);
  2257. // default is enable true
  2258. this.options.enable = ifUndefined(this.options.enable, true);
  2259. this.state = STATE_POSSIBLE;
  2260. this.simultaneous = {};
  2261. this.requireFail = [];
  2262. }
  2263. Recognizer.prototype = {
  2264. /**
  2265. * @virtual
  2266. * @type {Object}
  2267. */
  2268. defaults: {},
  2269. /**
  2270. * set options
  2271. * @param {Object} options
  2272. * @return {Recognizer}
  2273. */
  2274. set: function (options) {
  2275. extend(this.options, options);
  2276. // also update the touchAction, in case something changed about the directions/enabled state
  2277. this.manager && this.manager.touchAction.update();
  2278. return this;
  2279. },
  2280. /**
  2281. * recognize simultaneous with an other recognizer.
  2282. * @param {Recognizer} otherRecognizer
  2283. * @returns {Recognizer} this
  2284. */
  2285. recognizeWith: function (otherRecognizer) {
  2286. if (invokeArrayArg(otherRecognizer, 'recognizeWith', this)) {
  2287. return this;
  2288. }
  2289. var simultaneous = this.simultaneous;
  2290. otherRecognizer = getRecognizerByNameIfManager(otherRecognizer, this);
  2291. if (!simultaneous[otherRecognizer.id]) {
  2292. simultaneous[otherRecognizer.id] = otherRecognizer;
  2293. otherRecognizer.recognizeWith(this);
  2294. }
  2295. return this;
  2296. },
  2297. /**
  2298. * drop the simultaneous link. it doesnt remove the link on the other recognizer.
  2299. * @param {Recognizer} otherRecognizer
  2300. * @returns {Recognizer} this
  2301. */
  2302. dropRecognizeWith: function (otherRecognizer) {
  2303. if (invokeArrayArg(otherRecognizer, 'dropRecognizeWith', this)) {
  2304. return this;
  2305. }
  2306. otherRecognizer = getRecognizerByNameIfManager(otherRecognizer, this);
  2307. delete this.simultaneous[otherRecognizer.id];
  2308. return this;
  2309. },
  2310. /**
  2311. * recognizer can only run when an other is failing
  2312. * @param {Recognizer} otherRecognizer
  2313. * @returns {Recognizer} this
  2314. */
  2315. requireFailure: function (otherRecognizer) {
  2316. if (invokeArrayArg(otherRecognizer, 'requireFailure', this)) {
  2317. return this;
  2318. }
  2319. var requireFail = this.requireFail;
  2320. otherRecognizer = getRecognizerByNameIfManager(otherRecognizer, this);
  2321. if (inArray(requireFail, otherRecognizer) === -1) {
  2322. requireFail.push(otherRecognizer);
  2323. otherRecognizer.requireFailure(this);
  2324. }
  2325. return this;
  2326. },
  2327. /**
  2328. * drop the requireFailure link. it does not remove the link on the other recognizer.
  2329. * @param {Recognizer} otherRecognizer
  2330. * @returns {Recognizer} this
  2331. */
  2332. dropRequireFailure: function (otherRecognizer) {
  2333. if (invokeArrayArg(otherRecognizer, 'dropRequireFailure', this)) {
  2334. return this;
  2335. }
  2336. otherRecognizer = getRecognizerByNameIfManager(otherRecognizer, this);
  2337. var index = inArray(this.requireFail, otherRecognizer);
  2338. if (index > -1) {
  2339. this.requireFail.splice(index, 1);
  2340. }
  2341. return this;
  2342. },
  2343. /**
  2344. * has require failures boolean
  2345. * @returns {boolean}
  2346. */
  2347. hasRequireFailures: function () {
  2348. return this.requireFail.length > 0;
  2349. },
  2350. /**
  2351. * if the recognizer can recognize simultaneous with an other recognizer
  2352. * @param {Recognizer} otherRecognizer
  2353. * @returns {Boolean}
  2354. */
  2355. canRecognizeWith: function (otherRecognizer) {
  2356. return !!this.simultaneous[otherRecognizer.id];
  2357. },
  2358. /**
  2359. * You should use `tryEmit` instead of `emit` directly to check
  2360. * that all the needed recognizers has failed before emitting.
  2361. * @param {Object} input
  2362. */
  2363. emit: function (input) {
  2364. var self = this;
  2365. var state = this.state;
  2366. function emit(withState) {
  2367. self.manager.emit(self.options.event + (withState ? stateStr(state) : ''), input);
  2368. }
  2369. // 'panstart' and 'panmove'
  2370. if (state < STATE_ENDED) {
  2371. emit(true);
  2372. }
  2373. emit();
  2374. // simple 'eventName' events
  2375. // panend and pancancel
  2376. if (state >= STATE_ENDED) {
  2377. emit(true);
  2378. }
  2379. },
  2380. /**
  2381. * Check that all the require failure recognizers has failed,
  2382. * if true, it emits a gesture event,
  2383. * otherwise, setup the state to FAILED.
  2384. * @param {Object} input
  2385. */
  2386. tryEmit: function (input) {
  2387. if (this.canEmit()) {
  2388. return this.emit(input);
  2389. }
  2390. // it's failing anyway
  2391. this.state = STATE_FAILED;
  2392. },
  2393. /**
  2394. * can we emit?
  2395. * @returns {boolean}
  2396. */
  2397. canEmit: function () {
  2398. var i = 0;
  2399. while (i < this.requireFail.length) {
  2400. if (!(this.requireFail[i].state & (STATE_FAILED | STATE_POSSIBLE))) {
  2401. return false;
  2402. }
  2403. i++;
  2404. }
  2405. return true;
  2406. },
  2407. /**
  2408. * update the recognizer
  2409. * @param {Object} inputData
  2410. */
  2411. recognize: function (inputData) {
  2412. // make a new copy of the inputData
  2413. // so we can change the inputData without messing up the other recognizers
  2414. var inputDataClone = extend({}, inputData);
  2415. // is is enabled and allow recognizing?
  2416. if (!boolOrFn(this.options.enable, [
  2417. this,
  2418. inputDataClone
  2419. ])) {
  2420. this.reset();
  2421. this.state = STATE_FAILED;
  2422. return;
  2423. }
  2424. // reset when we've reached the end
  2425. if (this.state & (STATE_RECOGNIZED | STATE_CANCELLED | STATE_FAILED)) {
  2426. this.state = STATE_POSSIBLE;
  2427. }
  2428. this.state = this.process(inputDataClone);
  2429. // the recognizer has recognized a gesture
  2430. // so trigger an event
  2431. if (this.state & (STATE_BEGAN | STATE_CHANGED | STATE_ENDED | STATE_CANCELLED)) {
  2432. this.tryEmit(inputDataClone);
  2433. }
  2434. },
  2435. /**
  2436. * return the state of the recognizer
  2437. * the actual recognizing happens in this method
  2438. * @virtual
  2439. * @param {Object} inputData
  2440. * @returns {Const} STATE
  2441. */
  2442. process: function (inputData) {
  2443. },
  2444. // jshint ignore:line
  2445. /**
  2446. * return the preferred touch-action
  2447. * @virtual
  2448. * @returns {Array}
  2449. */
  2450. getTouchAction: function () {
  2451. },
  2452. /**
  2453. * called when the gesture isn't allowed to recognize
  2454. * like when another is being recognized or it is disabled
  2455. * @virtual
  2456. */
  2457. reset: function () {
  2458. }
  2459. };
  2460. /**
  2461. * get a usable string, used as event postfix
  2462. * @param {Const} state
  2463. * @returns {String} state
  2464. */
  2465. function stateStr(state) {
  2466. if (state & STATE_CANCELLED) {
  2467. return 'cancel';
  2468. } else if (state & STATE_ENDED) {
  2469. return 'end';
  2470. } else if (state & STATE_CHANGED) {
  2471. return 'move';
  2472. } else if (state & STATE_BEGAN) {
  2473. return 'start';
  2474. }
  2475. return '';
  2476. }
  2477. /**
  2478. * direction cons to string
  2479. * @param {Const} direction
  2480. * @returns {String}
  2481. */
  2482. function directionStr(direction) {
  2483. if (direction == DIRECTION_DOWN) {
  2484. return 'down';
  2485. } else if (direction == DIRECTION_UP) {
  2486. return 'up';
  2487. } else if (direction == DIRECTION_LEFT) {
  2488. return 'left';
  2489. } else if (direction == DIRECTION_RIGHT) {
  2490. return 'right';
  2491. }
  2492. return '';
  2493. }
  2494. /**
  2495. * get a recognizer by name if it is bound to a manager
  2496. * @param {Recognizer|String} otherRecognizer
  2497. * @param {Recognizer} recognizer
  2498. * @returns {Recognizer}
  2499. */
  2500. function getRecognizerByNameIfManager(otherRecognizer, recognizer) {
  2501. var manager = recognizer.manager;
  2502. if (manager) {
  2503. return manager.get(otherRecognizer);
  2504. }
  2505. return otherRecognizer;
  2506. }
  2507. /**
  2508. * This recognizer is just used as a base for the simple attribute recognizers.
  2509. * @constructor
  2510. * @extends Recognizer
  2511. */
  2512. function AttrRecognizer() {
  2513. Recognizer.apply(this, arguments);
  2514. }
  2515. inherit(AttrRecognizer, Recognizer, {
  2516. /**
  2517. * @namespace
  2518. * @memberof AttrRecognizer
  2519. */
  2520. defaults: {
  2521. /**
  2522. * @type {Number}
  2523. * @default 1
  2524. */
  2525. pointers: 1
  2526. },
  2527. /**
  2528. * Used to check if it the recognizer receives valid input, like input.distance > 10.
  2529. * @memberof AttrRecognizer
  2530. * @param {Object} input
  2531. * @returns {Boolean} recognized
  2532. */
  2533. attrTest: function (input) {
  2534. var optionPointers = this.options.pointers;
  2535. return optionPointers === 0 || input.pointers.length === optionPointers;
  2536. },
  2537. /**
  2538. * Process the input and return the state for the recognizer
  2539. * @memberof AttrRecognizer
  2540. * @param {Object} input
  2541. * @returns {*} State
  2542. */
  2543. process: function (input) {
  2544. var state = this.state;
  2545. var eventType = input.eventType;
  2546. var isRecognized = state & (STATE_BEGAN | STATE_CHANGED);
  2547. var isValid = this.attrTest(input);
  2548. // on cancel input and we've recognized before, return STATE_CANCELLED
  2549. if (isRecognized && (eventType & INPUT_CANCEL || !isValid)) {
  2550. return state | STATE_CANCELLED;
  2551. } else if (isRecognized || isValid) {
  2552. if (eventType & INPUT_END) {
  2553. return state | STATE_ENDED;
  2554. } else if (!(state & STATE_BEGAN)) {
  2555. return STATE_BEGAN;
  2556. }
  2557. return state | STATE_CHANGED;
  2558. }
  2559. return STATE_FAILED;
  2560. }
  2561. });
  2562. /**
  2563. * Pan
  2564. * Recognized when the pointer is down and moved in the allowed direction.
  2565. * @constructor
  2566. * @extends AttrRecognizer
  2567. */
  2568. function PanRecognizer() {
  2569. AttrRecognizer.apply(this, arguments);
  2570. this.pX = null;
  2571. this.pY = null;
  2572. }
  2573. inherit(PanRecognizer, AttrRecognizer, {
  2574. /**
  2575. * @namespace
  2576. * @memberof PanRecognizer
  2577. */
  2578. defaults: {
  2579. event: 'pan',
  2580. threshold: 10,
  2581. pointers: 1,
  2582. direction: DIRECTION_ALL
  2583. },
  2584. getTouchAction: function () {
  2585. var direction = this.options.direction;
  2586. var actions = [];
  2587. if (direction & DIRECTION_HORIZONTAL) {
  2588. actions.push(TOUCH_ACTION_PAN_Y);
  2589. }
  2590. if (direction & DIRECTION_VERTICAL) {
  2591. actions.push(TOUCH_ACTION_PAN_X);
  2592. }
  2593. return actions;
  2594. },
  2595. directionTest: function (input) {
  2596. var options = this.options;
  2597. var hasMoved = true;
  2598. var distance = input.distance;
  2599. var direction = input.direction;
  2600. var x = input.deltaX;
  2601. var y = input.deltaY;
  2602. // lock to axis?
  2603. if (!(direction & options.direction)) {
  2604. if (options.direction & DIRECTION_HORIZONTAL) {
  2605. direction = x === 0 ? DIRECTION_NONE : x < 0 ? DIRECTION_LEFT : DIRECTION_RIGHT;
  2606. hasMoved = x != this.pX;
  2607. distance = Math.abs(input.deltaX);
  2608. } else {
  2609. direction = y === 0 ? DIRECTION_NONE : y < 0 ? DIRECTION_UP : DIRECTION_DOWN;
  2610. hasMoved = y != this.pY;
  2611. distance = Math.abs(input.deltaY);
  2612. }
  2613. }
  2614. input.direction = direction;
  2615. return hasMoved && distance > options.threshold && direction & options.direction;
  2616. },
  2617. attrTest: function (input) {
  2618. return AttrRecognizer.prototype.attrTest.call(this, input) && (this.state & STATE_BEGAN || !(this.state & STATE_BEGAN) && this.directionTest(input));
  2619. },
  2620. emit: function (input) {
  2621. this.pX = input.deltaX;
  2622. this.pY = input.deltaY;
  2623. var direction = directionStr(input.direction);
  2624. if (direction) {
  2625. this.manager.emit(this.options.event + direction, input);
  2626. }
  2627. this._super.emit.call(this, input);
  2628. },
  2629. reset: function () {
  2630. }
  2631. });
  2632. /**
  2633. * Pinch
  2634. * Recognized when two or more pointers are moving toward (zoom-in) or away from each other (zoom-out).
  2635. * @constructor
  2636. * @extends AttrRecognizer
  2637. */
  2638. function PinchRecognizer() {
  2639. AttrRecognizer.apply(this, arguments);
  2640. }
  2641. inherit(PinchRecognizer, AttrRecognizer, {
  2642. /**
  2643. * @namespace
  2644. * @memberof PinchRecognizer
  2645. */
  2646. defaults: {
  2647. event: 'pinch',
  2648. threshold: 0,
  2649. pointers: 2
  2650. },
  2651. getTouchAction: function () {
  2652. return [TOUCH_ACTION_NONE];
  2653. },
  2654. attrTest: function (input) {
  2655. return this._super.attrTest.call(this, input) && (Math.abs(input.scale - 1) > this.options.threshold || this.state & STATE_BEGAN);
  2656. },
  2657. emit: function (input) {
  2658. this._super.emit.call(this, input);
  2659. if (input.scale !== 1) {
  2660. var inOut = input.scale < 1 ? 'in' : 'out';
  2661. this.manager.emit(this.options.event + inOut, input);
  2662. }
  2663. }
  2664. });
  2665. /**
  2666. * Press
  2667. * Recognized when the pointer is down for x ms without any movement.
  2668. * @constructor
  2669. * @extends Recognizer
  2670. */
  2671. function PressRecognizer() {
  2672. Recognizer.apply(this, arguments);
  2673. this._timer = null;
  2674. this._input = null;
  2675. }
  2676. inherit(PressRecognizer, Recognizer, {
  2677. /**
  2678. * @namespace
  2679. * @memberof PressRecognizer
  2680. */
  2681. defaults: {
  2682. event: 'press',
  2683. pointers: 1,
  2684. time: 500,
  2685. // minimal time of the pointer to be pressed
  2686. threshold: 5 // a minimal movement is ok, but keep it low
  2687. },
  2688. getTouchAction: function () {
  2689. return [TOUCH_ACTION_AUTO];
  2690. },
  2691. process: function (input) {
  2692. var options = this.options;
  2693. var validPointers = input.pointers.length === options.pointers;
  2694. var validMovement = input.distance < options.threshold;
  2695. var validTime = input.deltaTime > options.time;
  2696. this._input = input;
  2697. // we only allow little movement
  2698. // and we've reached an end event, so a tap is possible
  2699. if (!validMovement || !validPointers || input.eventType & (INPUT_END | INPUT_CANCEL) && !validTime) {
  2700. this.reset();
  2701. } else if (input.eventType & INPUT_START) {
  2702. this.reset();
  2703. this._timer = setTimeoutContext(function () {
  2704. this.state = STATE_RECOGNIZED;
  2705. this.tryEmit();
  2706. }, options.time, this);
  2707. } else if (input.eventType & INPUT_END) {
  2708. return STATE_RECOGNIZED;
  2709. }
  2710. return STATE_FAILED;
  2711. },
  2712. reset: function () {
  2713. clearTimeout(this._timer);
  2714. },
  2715. emit: function (input) {
  2716. if (this.state !== STATE_RECOGNIZED) {
  2717. return;
  2718. }
  2719. if (input && input.eventType & INPUT_END) {
  2720. this.manager.emit(this.options.event + 'up', input);
  2721. } else {
  2722. this._input.timeStamp = now();
  2723. this.manager.emit(this.options.event, this._input);
  2724. }
  2725. }
  2726. });
  2727. /**
  2728. * Rotate
  2729. * Recognized when two or more pointer are moving in a circular motion.
  2730. * @constructor
  2731. * @extends AttrRecognizer
  2732. */
  2733. function RotateRecognizer() {
  2734. AttrRecognizer.apply(this, arguments);
  2735. }
  2736. inherit(RotateRecognizer, AttrRecognizer, {
  2737. /**
  2738. * @namespace
  2739. * @memberof RotateRecognizer
  2740. */
  2741. defaults: {
  2742. event: 'rotate',
  2743. threshold: 0,
  2744. pointers: 2
  2745. },
  2746. getTouchAction: function () {
  2747. return [TOUCH_ACTION_NONE];
  2748. },
  2749. attrTest: function (input) {
  2750. return this._super.attrTest.call(this, input) && (Math.abs(input.rotation) > this.options.threshold || this.state & STATE_BEGAN);
  2751. }
  2752. });
  2753. /**
  2754. * Swipe
  2755. * Recognized when the pointer is moving fast (velocity), with enough distance in the allowed direction.
  2756. * @constructor
  2757. * @extends AttrRecognizer
  2758. */
  2759. function SwipeRecognizer() {
  2760. AttrRecognizer.apply(this, arguments);
  2761. }
  2762. inherit(SwipeRecognizer, AttrRecognizer, {
  2763. /**
  2764. * @namespace
  2765. * @memberof SwipeRecognizer
  2766. */
  2767. defaults: {
  2768. event: 'swipe',
  2769. threshold: 10,
  2770. velocity: 0.65,
  2771. direction: DIRECTION_HORIZONTAL | DIRECTION_VERTICAL,
  2772. pointers: 1
  2773. },
  2774. getTouchAction: function () {
  2775. return PanRecognizer.prototype.getTouchAction.call(this);
  2776. },
  2777. attrTest: function (input) {
  2778. var direction = this.options.direction;
  2779. var velocity;
  2780. if (direction & (DIRECTION_HORIZONTAL | DIRECTION_VERTICAL)) {
  2781. velocity = input.velocity;
  2782. } else if (direction & DIRECTION_HORIZONTAL) {
  2783. velocity = input.velocityX;
  2784. } else if (direction & DIRECTION_VERTICAL) {
  2785. velocity = input.velocityY;
  2786. }
  2787. return this._super.attrTest.call(this, input) && direction & input.direction && input.distance > this.options.threshold && abs(velocity) > this.options.velocity && input.eventType & INPUT_END;
  2788. },
  2789. emit: function (input) {
  2790. var direction = directionStr(input.direction);
  2791. if (direction) {
  2792. this.manager.emit(this.options.event + direction, input);
  2793. }
  2794. this.manager.emit(this.options.event, input);
  2795. }
  2796. });
  2797. /**
  2798. * A tap is ecognized when the pointer is doing a small tap/click. Multiple taps are recognized if they occur
  2799. * between the given interval and position. The delay option can be used to recognize multi-taps without firing
  2800. * a single tap.
  2801. *
  2802. * The eventData from the emitted event contains the property `tapCount`, which contains the amount of
  2803. * multi-taps being recognized.
  2804. * @constructor
  2805. * @extends Recognizer
  2806. */
  2807. function TapRecognizer() {
  2808. Recognizer.apply(this, arguments);
  2809. // previous time and center,
  2810. // used for tap counting
  2811. this.pTime = false;
  2812. this.pCenter = false;
  2813. this._timer = null;
  2814. this._input = null;
  2815. this.count = 0;
  2816. }
  2817. inherit(TapRecognizer, Recognizer, {
  2818. /**
  2819. * @namespace
  2820. * @memberof PinchRecognizer
  2821. */
  2822. defaults: {
  2823. event: 'tap',
  2824. pointers: 1,
  2825. taps: 1,
  2826. interval: 300,
  2827. // max time between the multi-tap taps
  2828. time: 250,
  2829. // max time of the pointer to be down (like finger on the screen)
  2830. threshold: 10,
  2831. // a minimal movement is ok, but keep it low
  2832. posThreshold: 10 // a multi-tap can be a bit off the initial position
  2833. },
  2834. getTouchAction: function () {
  2835. return [TOUCH_ACTION_MANIPULATION];
  2836. },
  2837. process: function (input) {
  2838. var options = this.options;
  2839. var validPointers = input.pointers.length === options.pointers;
  2840. var validMovement = input.distance < options.threshold;
  2841. var validTouchTime = input.deltaTime < options.time;
  2842. this.reset();
  2843. if (input.eventType & INPUT_START && this.count === 0) {
  2844. return this.failTimeout();
  2845. }
  2846. // we only allow little movement
  2847. // and we've reached an end event, so a tap is possible
  2848. if (validMovement && validTouchTime && validPointers) {
  2849. if (input.eventType != INPUT_END) {
  2850. return this.failTimeout();
  2851. }
  2852. var validInterval = this.pTime ? input.timeStamp - this.pTime < options.interval : true;
  2853. var validMultiTap = !this.pCenter || getDistance(this.pCenter, input.center) < options.posThreshold;
  2854. this.pTime = input.timeStamp;
  2855. this.pCenter = input.center;
  2856. if (!validMultiTap || !validInterval) {
  2857. this.count = 1;
  2858. } else {
  2859. this.count += 1;
  2860. }
  2861. this._input = input;
  2862. // if tap count matches we have recognized it,
  2863. // else it has began recognizing...
  2864. var tapCount = this.count % options.taps;
  2865. if (tapCount === 0) {
  2866. // no failing requirements, immediately trigger the tap event
  2867. // or wait as long as the multitap interval to trigger
  2868. if (!this.hasRequireFailures()) {
  2869. return STATE_RECOGNIZED;
  2870. } else {
  2871. this._timer = setTimeoutContext(function () {
  2872. this.state = STATE_RECOGNIZED;
  2873. this.tryEmit();
  2874. }, options.interval, this);
  2875. return STATE_BEGAN;
  2876. }
  2877. }
  2878. }
  2879. return STATE_FAILED;
  2880. },
  2881. failTimeout: function () {
  2882. this._timer = setTimeoutContext(function () {
  2883. this.state = STATE_FAILED;
  2884. }, this.options.interval, this);
  2885. return STATE_FAILED;
  2886. },
  2887. reset: function () {
  2888. clearTimeout(this._timer);
  2889. },
  2890. emit: function () {
  2891. if (this.state == STATE_RECOGNIZED) {
  2892. this._input.tapCount = this.count;
  2893. this.manager.emit(this.options.event, this._input);
  2894. }
  2895. }
  2896. });
  2897. /**
  2898. * Simple way to create an manager with a default set of recognizers.
  2899. * @param {HTMLElement} element
  2900. * @param {Object} [options]
  2901. * @constructor
  2902. */
  2903. function Hammer(element, options) {
  2904. options = options || {};
  2905. options.recognizers = ifUndefined(options.recognizers, Hammer.defaults.preset);
  2906. return new Manager(element, options);
  2907. }
  2908. /**
  2909. * @const {string}
  2910. */
  2911. Hammer.VERSION = '2.0.4';
  2912. /**
  2913. * default settings
  2914. * @namespace
  2915. */
  2916. Hammer.defaults = {
  2917. /**
  2918. * set if DOM events are being triggered.
  2919. * But this is slower and unused by simple implementations, so disabled by default.
  2920. * @type {Boolean}
  2921. * @default false
  2922. */
  2923. domEvents: false,
  2924. /**
  2925. * The value for the touchAction property/fallback.
  2926. * When set to `compute` it will magically set the correct value based on the added recognizers.
  2927. * @type {String}
  2928. * @default compute
  2929. */
  2930. touchAction: TOUCH_ACTION_COMPUTE,
  2931. /**
  2932. * @type {Boolean}
  2933. * @default true
  2934. */
  2935. enable: true,
  2936. /**
  2937. * EXPERIMENTAL FEATURE -- can be removed/changed
  2938. * Change the parent input target element.
  2939. * If Null, then it is being set the to main element.
  2940. * @type {Null|EventTarget}
  2941. * @default null
  2942. */
  2943. inputTarget: null,
  2944. /**
  2945. * force an input class
  2946. * @type {Null|Function}
  2947. * @default null
  2948. */
  2949. inputClass: null,
  2950. /**
  2951. * Default recognizer setup when calling `Hammer()`
  2952. * When creating a new Manager these will be skipped.
  2953. * @type {Array}
  2954. */
  2955. preset: [
  2956. // RecognizerClass, options, [recognizeWith, ...], [requireFailure, ...]
  2957. [
  2958. RotateRecognizer,
  2959. { enable: false }
  2960. ],
  2961. [
  2962. PinchRecognizer,
  2963. { enable: false },
  2964. ['rotate']
  2965. ],
  2966. [
  2967. SwipeRecognizer,
  2968. { direction: DIRECTION_HORIZONTAL }
  2969. ],
  2970. [
  2971. PanRecognizer,
  2972. { direction: DIRECTION_HORIZONTAL },
  2973. ['swipe']
  2974. ],
  2975. [TapRecognizer],
  2976. [
  2977. TapRecognizer,
  2978. {
  2979. event: 'doubletap',
  2980. taps: 2
  2981. },
  2982. ['tap']
  2983. ],
  2984. [PressRecognizer]
  2985. ],
  2986. /**
  2987. * Some CSS properties can be used to improve the working of Hammer.
  2988. * Add them to this method and they will be set when creating a new Manager.
  2989. * @namespace
  2990. */
  2991. cssProps: {
  2992. /**
  2993. * Disables text selection to improve the dragging gesture. Mainly for desktop browsers.
  2994. * @type {String}
  2995. * @default 'none'
  2996. */
  2997. userSelect: 'none',
  2998. /**
  2999. * Disable the Windows Phone grippers when pressing an element.
  3000. * @type {String}
  3001. * @default 'none'
  3002. */
  3003. touchSelect: 'none',
  3004. /**
  3005. * Disables the default callout shown when you touch and hold a touch target.
  3006. * On iOS, when you touch and hold a touch target such as a link, Safari displays
  3007. * a callout containing information about the link. This property allows you to disable that callout.
  3008. * @type {String}
  3009. * @default 'none'
  3010. */
  3011. touchCallout: 'none',
  3012. /**
  3013. * Specifies whether zooming is enabled. Used by IE10>
  3014. * @type {String}
  3015. * @default 'none'
  3016. */
  3017. contentZooming: 'none',
  3018. /**
  3019. * Specifies that an entire element should be draggable instead of its contents. Mainly for desktop browsers.
  3020. * @type {String}
  3021. * @default 'none'
  3022. */
  3023. userDrag: 'none',
  3024. /**
  3025. * Overrides the highlight color shown when the user taps a link or a JavaScript
  3026. * clickable element in iOS. This property obeys the alpha value, if specified.
  3027. * @type {String}
  3028. * @default 'rgba(0,0,0,0)'
  3029. */
  3030. tapHighlightColor: 'rgba(0,0,0,0)'
  3031. }
  3032. };
  3033. var STOP = 1;
  3034. var FORCED_STOP = 2;
  3035. /**
  3036. * Manager
  3037. * @param {HTMLElement} element
  3038. * @param {Object} [options]
  3039. * @constructor
  3040. */
  3041. function Manager(element, options) {
  3042. options = options || {};
  3043. this.options = merge(options, Hammer.defaults);
  3044. this.options.inputTarget = this.options.inputTarget || element;
  3045. this.handlers = {};
  3046. this.session = {};
  3047. this.recognizers = [];
  3048. this.element = element;
  3049. this.input = createInputInstance(this);
  3050. this.touchAction = new TouchAction(this, this.options.touchAction);
  3051. toggleCssProps(this, true);
  3052. each(options.recognizers, function (item) {
  3053. var recognizer = this.add(new item[0](item[1]));
  3054. item[2] && recognizer.recognizeWith(item[2]);
  3055. item[3] && recognizer.requireFailure(item[3]);
  3056. }, this);
  3057. }
  3058. Manager.prototype = {
  3059. /**
  3060. * set options
  3061. * @param {Object} options
  3062. * @returns {Manager}
  3063. */
  3064. set: function (options) {
  3065. extend(this.options, options);
  3066. // Options that need a little more setup
  3067. if (options.touchAction) {
  3068. this.touchAction.update();
  3069. }
  3070. if (options.inputTarget) {
  3071. // Clean up existing event listeners and reinitialize
  3072. this.input.destroy();
  3073. this.input.target = options.inputTarget;
  3074. this.input.init();
  3075. }
  3076. return this;
  3077. },
  3078. /**
  3079. * stop recognizing for this session.
  3080. * This session will be discarded, when a new [input]start event is fired.
  3081. * When forced, the recognizer cycle is stopped immediately.
  3082. * @param {Boolean} [force]
  3083. */
  3084. stop: function (force) {
  3085. this.session.stopped = force ? FORCED_STOP : STOP;
  3086. },
  3087. /**
  3088. * run the recognizers!
  3089. * called by the inputHandler function on every movement of the pointers (touches)
  3090. * it walks through all the recognizers and tries to detect the gesture that is being made
  3091. * @param {Object} inputData
  3092. */
  3093. recognize: function (inputData) {
  3094. var session = this.session;
  3095. if (session.stopped) {
  3096. return;
  3097. }
  3098. // run the touch-action polyfill
  3099. this.touchAction.preventDefaults(inputData);
  3100. var recognizer;
  3101. var recognizers = this.recognizers;
  3102. // this holds the recognizer that is being recognized.
  3103. // so the recognizer's state needs to be BEGAN, CHANGED, ENDED or RECOGNIZED
  3104. // if no recognizer is detecting a thing, it is set to `null`
  3105. var curRecognizer = session.curRecognizer;
  3106. // reset when the last recognizer is recognized
  3107. // or when we're in a new session
  3108. if (!curRecognizer || curRecognizer && curRecognizer.state & STATE_RECOGNIZED) {
  3109. curRecognizer = session.curRecognizer = null;
  3110. }
  3111. var i = 0;
  3112. while (i < recognizers.length) {
  3113. recognizer = recognizers[i];
  3114. // find out if we are allowed try to recognize the input for this one.
  3115. // 1. allow if the session is NOT forced stopped (see the .stop() method)
  3116. // 2. allow if we still haven't recognized a gesture in this session, or the this recognizer is the one
  3117. // that is being recognized.
  3118. // 3. allow if the recognizer is allowed to run simultaneous with the current recognized recognizer.
  3119. // this can be setup with the `recognizeWith()` method on the recognizer.
  3120. if (session.stopped !== FORCED_STOP && // 1
  3121. (!curRecognizer || recognizer == curRecognizer || // 2
  3122. recognizer.canRecognizeWith(curRecognizer))) {
  3123. // 3
  3124. recognizer.recognize(inputData);
  3125. } else {
  3126. recognizer.reset();
  3127. }
  3128. // if the recognizer has been recognizing the input as a valid gesture, we want to store this one as the
  3129. // current active recognizer. but only if we don't already have an active recognizer
  3130. if (!curRecognizer && recognizer.state & (STATE_BEGAN | STATE_CHANGED | STATE_ENDED)) {
  3131. curRecognizer = session.curRecognizer = recognizer;
  3132. }
  3133. i++;
  3134. }
  3135. },
  3136. /**
  3137. * get a recognizer by its event name.
  3138. * @param {Recognizer|String} recognizer
  3139. * @returns {Recognizer|Null}
  3140. */
  3141. get: function (recognizer) {
  3142. if (recognizer instanceof Recognizer) {
  3143. return recognizer;
  3144. }
  3145. var recognizers = this.recognizers;
  3146. for (var i = 0; i < recognizers.length; i++) {
  3147. if (recognizers[i].options.event == recognizer) {
  3148. return recognizers[i];
  3149. }
  3150. }
  3151. return null;
  3152. },
  3153. /**
  3154. * add a recognizer to the manager
  3155. * existing recognizers with the same event name will be removed
  3156. * @param {Recognizer} recognizer
  3157. * @returns {Recognizer|Manager}
  3158. */
  3159. add: function (recognizer) {
  3160. if (invokeArrayArg(recognizer, 'add', this)) {
  3161. return this;
  3162. }
  3163. // remove existing
  3164. var existing = this.get(recognizer.options.event);
  3165. if (existing) {
  3166. this.remove(existing);
  3167. }
  3168. this.recognizers.push(recognizer);
  3169. recognizer.manager = this;
  3170. this.touchAction.update();
  3171. return recognizer;
  3172. },
  3173. /**
  3174. * remove a recognizer by name or instance
  3175. * @param {Recognizer|String} recognizer
  3176. * @returns {Manager}
  3177. */
  3178. remove: function (recognizer) {
  3179. if (invokeArrayArg(recognizer, 'remove', this)) {
  3180. return this;
  3181. }
  3182. var recognizers = this.recognizers;
  3183. recognizer = this.get(recognizer);
  3184. recognizers.splice(inArray(recognizers, recognizer), 1);
  3185. this.touchAction.update();
  3186. return this;
  3187. },
  3188. /**
  3189. * bind event
  3190. * @param {String} events
  3191. * @param {Function} handler
  3192. * @returns {EventEmitter} this
  3193. */
  3194. on: function (events, handler) {
  3195. var handlers = this.handlers;
  3196. each(splitStr(events), function (event) {
  3197. handlers[event] = handlers[event] || [];
  3198. handlers[event].push(handler);
  3199. });
  3200. return this;
  3201. },
  3202. /**
  3203. * unbind event, leave emit blank to remove all handlers
  3204. * @param {String} events
  3205. * @param {Function} [handler]
  3206. * @returns {EventEmitter} this
  3207. */
  3208. off: function (events, handler) {
  3209. var handlers = this.handlers;
  3210. each(splitStr(events), function (event) {
  3211. if (!handler) {
  3212. delete handlers[event];
  3213. } else {
  3214. handlers[event].splice(inArray(handlers[event], handler), 1);
  3215. }
  3216. });
  3217. return this;
  3218. },
  3219. /**
  3220. * emit event to the listeners
  3221. * @param {String} event
  3222. * @param {Object} data
  3223. */
  3224. emit: function (event, data) {
  3225. // we also want to trigger dom events
  3226. if (this.options.domEvents) {
  3227. triggerDomEvent(event, data);
  3228. }
  3229. // no handlers, so skip it all
  3230. var handlers = this.handlers[event] && this.handlers[event].slice();
  3231. if (!handlers || !handlers.length) {
  3232. return;
  3233. }
  3234. data.type = event;
  3235. data.preventDefault = function () {
  3236. data.srcEvent.preventDefault();
  3237. };
  3238. var i = 0;
  3239. while (i < handlers.length) {
  3240. handlers[i](data);
  3241. i++;
  3242. }
  3243. },
  3244. /**
  3245. * destroy the manager and unbinds all events
  3246. * it doesn't unbind dom events, that is the user own responsibility
  3247. */
  3248. destroy: function () {
  3249. this.element && toggleCssProps(this, false);
  3250. this.handlers = {};
  3251. this.session = {};
  3252. this.input.destroy();
  3253. this.element = null;
  3254. }
  3255. };
  3256. /**
  3257. * add/remove the css properties as defined in manager.options.cssProps
  3258. * @param {Manager} manager
  3259. * @param {Boolean} add
  3260. */
  3261. function toggleCssProps(manager, add) {
  3262. var element = manager.element;
  3263. each(manager.options.cssProps, function (value, name) {
  3264. element.style[prefixed(element.style, name)] = add ? value : '';
  3265. });
  3266. }
  3267. /**
  3268. * trigger dom event
  3269. * @param {String} event
  3270. * @param {Object} data
  3271. */
  3272. function triggerDomEvent(event, data) {
  3273. var gestureEvent = document.createEvent('Event');
  3274. gestureEvent.initEvent(event, true, true);
  3275. gestureEvent.gesture = data;
  3276. data.target.dispatchEvent(gestureEvent);
  3277. }
  3278. extend(Hammer, {
  3279. INPUT_START: INPUT_START,
  3280. INPUT_MOVE: INPUT_MOVE,
  3281. INPUT_END: INPUT_END,
  3282. INPUT_CANCEL: INPUT_CANCEL,
  3283. STATE_POSSIBLE: STATE_POSSIBLE,
  3284. STATE_BEGAN: STATE_BEGAN,
  3285. STATE_CHANGED: STATE_CHANGED,
  3286. STATE_ENDED: STATE_ENDED,
  3287. STATE_RECOGNIZED: STATE_RECOGNIZED,
  3288. STATE_CANCELLED: STATE_CANCELLED,
  3289. STATE_FAILED: STATE_FAILED,
  3290. DIRECTION_NONE: DIRECTION_NONE,
  3291. DIRECTION_LEFT: DIRECTION_LEFT,
  3292. DIRECTION_RIGHT: DIRECTION_RIGHT,
  3293. DIRECTION_UP: DIRECTION_UP,
  3294. DIRECTION_DOWN: DIRECTION_DOWN,
  3295. DIRECTION_HORIZONTAL: DIRECTION_HORIZONTAL,
  3296. DIRECTION_VERTICAL: DIRECTION_VERTICAL,
  3297. DIRECTION_ALL: DIRECTION_ALL,
  3298. Manager: Manager,
  3299. Input: Input,
  3300. TouchAction: TouchAction,
  3301. TouchInput: TouchInput,
  3302. MouseInput: MouseInput,
  3303. PointerEventInput: PointerEventInput,
  3304. TouchMouseInput: TouchMouseInput,
  3305. SingleTouchInput: SingleTouchInput,
  3306. Recognizer: Recognizer,
  3307. AttrRecognizer: AttrRecognizer,
  3308. Tap: TapRecognizer,
  3309. Pan: PanRecognizer,
  3310. Swipe: SwipeRecognizer,
  3311. Pinch: PinchRecognizer,
  3312. Rotate: RotateRecognizer,
  3313. Press: PressRecognizer,
  3314. on: addEventListeners,
  3315. off: removeEventListeners,
  3316. each: each,
  3317. merge: merge,
  3318. extend: extend,
  3319. inherit: inherit,
  3320. bindFn: bindFn,
  3321. prefixed: prefixed
  3322. });
  3323. if (typeof module == 'object' && module.exports) {
  3324. exports = Hammer;
  3325. } /** ignored by jsdoc **/ else {
  3326. return Hammer;
  3327. }
  3328. return exports;
  3329. }(hammer);
  3330. boundry = function (exports) {
  3331. var Util = util;
  3332. function Boundry(cfg) {
  3333. this.cfg = Util.mix({
  3334. width: 0,
  3335. height: 0
  3336. }, cfg);
  3337. this.init();
  3338. }
  3339. Util.mix(Boundry.prototype, {
  3340. init: function () {
  3341. var self = this;
  3342. self._xtop = 0;
  3343. self._xright = 0;
  3344. self._xleft = 0;
  3345. self._xbottom = 0;
  3346. self.refresh({
  3347. width: self.cfg.width,
  3348. height: self.cfg.height
  3349. });
  3350. },
  3351. reset: function () {
  3352. this.resetTop();
  3353. this.resetLeft();
  3354. this.resetBottom();
  3355. this.resetRight();
  3356. return this;
  3357. },
  3358. resetTop: function () {
  3359. this._xtop = 0;
  3360. this.refresh();
  3361. return this;
  3362. },
  3363. resetLeft: function () {
  3364. this._xleft = 0;
  3365. this.refresh();
  3366. return this;
  3367. },
  3368. resetBottom: function () {
  3369. this._xbottom = 0;
  3370. this.refresh();
  3371. return this;
  3372. },
  3373. resetRight: function () {
  3374. this._xright = 0;
  3375. this.refresh();
  3376. return this;
  3377. },
  3378. expandTop: function (top) {
  3379. this._xtop = top;
  3380. this.refresh();
  3381. return this;
  3382. },
  3383. expandLeft: function (left) {
  3384. this._xleft = left;
  3385. this.refresh();
  3386. return this;
  3387. },
  3388. expandRight: function (right) {
  3389. this._xright = right;
  3390. this.refresh();
  3391. return this;
  3392. },
  3393. expandBottom: function (bottom) {
  3394. this._xbottom = bottom;
  3395. this.refresh();
  3396. return this;
  3397. },
  3398. refresh: function (cfg) {
  3399. Util.mix(this.cfg, cfg);
  3400. this.top = this._xtop;
  3401. this.left = this._xleft;
  3402. this.bottom = (cfg && cfg.height || this.cfg.height || 0) - this._xbottom;
  3403. this.right = (cfg && cfg.width || this.cfg.width || 0) - this._xright;
  3404. this.width = this.right - this.left > 0 ? this.right - this.left : 0;
  3405. this.height = this.bottom - this.top > 0 ? this.bottom - this.top : 0;
  3406. return this;
  3407. }
  3408. });
  3409. if (typeof module == 'object' && module.exports) {
  3410. exports = Boundry;
  3411. } /** ignored by jsdoc **/ else {
  3412. return Boundry;
  3413. }
  3414. return exports;
  3415. }(boundry);
  3416. components_sticky = function (exports) {
  3417. var Util = util;
  3418. var Base = base;
  3419. //transform
  3420. var transform = Util.prefixStyle('transform');
  3421. // default render function for position:sticky elements
  3422. var defaultStickyRenderFunc = function (e) {
  3423. var stickyElement = e.stickyElement;
  3424. var curStickyElement = e.curStickyElement;
  3425. var xscroll = e.xscroll;
  3426. var _ = e._;
  3427. var infinite = xscroll.getPlugin('infinite');
  3428. if (infinite) {
  3429. infinite.userConfig.renderHook.call(self, stickyElement, curStickyElement);
  3430. stickyElement.setAttribute('xs-guid', curStickyElement.guid);
  3431. Util.addClass(stickyElement, curStickyElement.className);
  3432. for (var attrName in curStickyElement.style) {
  3433. if (attrName != 'display' && attrName != 'position') {
  3434. //copy styles
  3435. stickyElement.style[attrName] = attrName == _.height ? curStickyElement.style[attrName] + 'px' : curStickyElement.style[attrName];
  3436. }
  3437. }
  3438. } else {
  3439. var style = curStickyElement.getAttribute('style');
  3440. stickyElement.innerHTML = curStickyElement.innerHTML;
  3441. stickyElement.className = curStickyElement.className;
  3442. style && stickyElement.setAttribute('style', style);
  3443. }
  3444. };
  3445. var Sticky = function (cfg) {
  3446. Sticky.superclass.constructor.call(this, cfg);
  3447. this.userConfig = Util.mix({
  3448. stickyRenderTo: undefined,
  3449. forceSticky: true,
  3450. prefix: 'xs-sticky-container',
  3451. stickyRenderFunc: defaultStickyRenderFunc,
  3452. zoomType: 'y'
  3453. }, cfg);
  3454. this.init();
  3455. };
  3456. Util.extend(Sticky, Base, {
  3457. init: function () {
  3458. var self = this, userConfig = self.userConfig, xscroll = self.xscroll = userConfig.xscroll;
  3459. var isY = self.isY = !!(userConfig.zoomType == 'y');
  3460. self._ = {
  3461. top: self.isY ? 'top' : 'left',
  3462. left: self.isY ? 'left' : 'bottom',
  3463. right: self.isY ? 'right' : 'top',
  3464. height: self.isY ? 'height' : 'width',
  3465. width: self.isY ? 'width' : 'height'
  3466. };
  3467. self.stickyRenderTo = Util.getNode(userConfig.stickyRenderTo);
  3468. self._handlers = [];
  3469. return self;
  3470. },
  3471. getStickiesPos: function () {
  3472. var self = this;
  3473. var xscroll = self.xscroll;
  3474. var isInfinite = self.isInfinite;
  3475. var isY = self.isY;
  3476. var _ = self._;
  3477. var stickiesPos = [];
  3478. var getPos = function (sticky) {
  3479. var pos = {};
  3480. if (isInfinite) {
  3481. pos[_.top] = isY ? sticky._top : sticky._left;
  3482. pos[_.height] = isY ? sticky._height : sticky._width;
  3483. } else {
  3484. pos[_.top] = self.isY ? Util.getOffsetTop(sticky) : Util.getOffsetLeft(sticky);
  3485. pos[_.height] = self.isY ? sticky.offsetHeight : sticky.offsetWidth;
  3486. }
  3487. return pos;
  3488. };
  3489. for (var i = 0; i < self.stickiesNum; i++) {
  3490. var pos = getPos(self.stickyElements[i]);
  3491. self._handlers[i] = self._handlers[i] || self.createStickyEl();
  3492. pos.el = self._handlers[i];
  3493. pos.isRender = false;
  3494. stickiesPos.push(pos);
  3495. }
  3496. return stickiesPos;
  3497. },
  3498. getStickyElements: function () {
  3499. var self = this;
  3500. var xscroll = self.xscroll;
  3501. var userConfig = self.userConfig;
  3502. var isInfinite = self.isInfinite;
  3503. var infinite = xscroll.getPlugin('infinite');
  3504. if (infinite) {
  3505. var stickyElements = [], serializedData = infinite.__serializedData;
  3506. for (var i in serializedData) {
  3507. var rowData = serializedData[i];
  3508. if (rowData && rowData.style && 'sticky' == rowData.style.position) {
  3509. stickyElements.push(rowData);
  3510. }
  3511. }
  3512. return stickyElements;
  3513. } else {
  3514. return Util.getNodes(xscroll.userConfig.stickyElements, xscroll.content);
  3515. }
  3516. },
  3517. render: function (force) {
  3518. var self = this;
  3519. var userConfig = self.userConfig;
  3520. var xscroll = self.xscroll;
  3521. self.isInfinite = !!xscroll.getPlugin('infinite');
  3522. var _ = self._;
  3523. self.stickyElements = self.getStickyElements();
  3524. self.stickiesNum = self.stickyElements && self.stickyElements.length;
  3525. if (!self.stickiesNum)
  3526. return;
  3527. if (!self.stickyRenderTo) {
  3528. self.stickyRenderTo = document.createElement('div');
  3529. xscroll.renderTo.appendChild(self.stickyRenderTo);
  3530. }
  3531. self.stickiesPos = self.getStickiesPos();
  3532. var stickyRenderTo = self.stickyRenderTo;
  3533. stickyRenderTo.style[_.top] = 0;
  3534. stickyRenderTo.style[_.left] = 0;
  3535. stickyRenderTo.style[_.right] = 0;
  3536. stickyRenderTo.style.position = xscroll.userConfig.useOriginScroll ? 'fixed' : 'absolute';
  3537. Util.addClass(self.stickyRenderTo, userConfig.prefix);
  3538. self.stickyHandler(force);
  3539. self._bindEvt();
  3540. },
  3541. createStickyEl: function () {
  3542. var self = this;
  3543. var el = document.createElement('div');
  3544. el.style.display = 'none';
  3545. Util.addClass(el, 'xs-sticky-handler');
  3546. self.stickyRenderTo.appendChild(el);
  3547. return el;
  3548. },
  3549. _bindEvt: function () {
  3550. var self = this, xscroll = self.xscroll;
  3551. xscroll.on('scroll', self.stickyHandler, self);
  3552. },
  3553. stickyHandler: function (force) {
  3554. var self = this;
  3555. var xscroll = self.xscroll;
  3556. var userConfig = self.userConfig;
  3557. var scrollTop = self.isY ? xscroll.getScrollTop() : xscroll.getScrollLeft();
  3558. var stickiesPos = self.stickiesPos;
  3559. var _ = self._;
  3560. var indexes = [];
  3561. for (var i = 0, l = stickiesPos.length; i < l; i++) {
  3562. var top = stickiesPos[i][_.top];
  3563. if (scrollTop > top) {
  3564. indexes.push(i);
  3565. }
  3566. }
  3567. if (!indexes.length) {
  3568. if (self.stickyElement) {
  3569. self.stickyElement.style.display = 'none';
  3570. }
  3571. self.curStickyIndex = undefined;
  3572. return;
  3573. }
  3574. var curStickyIndex = Math.max.apply(null, indexes);
  3575. if (self.curStickyIndex != curStickyIndex || force) {
  3576. var prevStickyIndex = self.curStickyIndex;
  3577. self.curStickyIndex = curStickyIndex;
  3578. self.curStickyElement = self.stickyElements[curStickyIndex];
  3579. self.curStickyPos = stickiesPos[curStickyIndex];
  3580. self.stickyElement = self.curStickyPos.el;
  3581. for (var i = 0, l = stickiesPos.length; i < l; i++) {
  3582. stickiesPos[i].el.style.display = 'none';
  3583. }
  3584. var eventsObj = {
  3585. stickyElement: self.stickyElement,
  3586. curStickyIndex: self.curStickyIndex,
  3587. prevStickyIndex: prevStickyIndex,
  3588. curStickyPos: self.curStickyPos,
  3589. isRender: self.curStickyPos.isRender
  3590. };
  3591. xscroll.trigger('beforestickychange', eventsObj);
  3592. self._stickyRenderFunc(self);
  3593. xscroll.trigger('stickychange', eventsObj);
  3594. }
  3595. var trans = 0;
  3596. if (self.stickiesPos[self.curStickyIndex + 1]) {
  3597. var cur = self.stickiesPos[self.curStickyIndex];
  3598. var next = self.stickiesPos[self.curStickyIndex + 1];
  3599. if (scrollTop + cur[_.height] > next[_.top] && scrollTop + cur[_.height] < next[_.top] + cur[_.height]) {
  3600. trans = cur[_.height] + scrollTop - next[_.top];
  3601. } else {
  3602. trans = 0;
  3603. }
  3604. }
  3605. self.stickyElement.style[transform] = self.isY ? 'translateY(-' + trans + 'px) translateZ(0)' : 'translateX(-' + trans + 'px) translateZ(0)';
  3606. },
  3607. _stickyRenderFunc: function (e) {
  3608. var self = this;
  3609. var _ = self._;
  3610. var stickyRenderFunc = self.userConfig.stickyRenderFunc;
  3611. var el = self.curStickyPos.el;
  3612. if (!self.curStickyPos.isRender) {
  3613. el.style[_.left] = 0;
  3614. el.style[_.right] = 0;
  3615. stickyRenderFunc && stickyRenderFunc.call(self, e);
  3616. }
  3617. el.style.display = 'block';
  3618. self.curStickyPos.isRender = true;
  3619. },
  3620. destroy: function () {
  3621. var self = this;
  3622. self.stickyElements = undefined;
  3623. self.stickiesNum = undefined;
  3624. self.stickiesPos = undefined;
  3625. Util.remove(self.stickyElement);
  3626. self.stickyElement = undefined;
  3627. }
  3628. });
  3629. if (typeof module == 'object' && module.exports) {
  3630. exports = Sticky;
  3631. } /** ignored by jsdoc **/ else {
  3632. return Sticky;
  3633. }
  3634. return exports;
  3635. }(components_sticky);
  3636. components_fixed = function (exports) {
  3637. var Util = util;
  3638. var Base = base;
  3639. var transform = Util.prefixStyle('transform');
  3640. var Fixed = function (cfg) {
  3641. Fixed.superclass.constructor.call(this, cfg);
  3642. this.userConfig = Util.mix({
  3643. fixedRenderTo: undefined,
  3644. fixedElements: '.xs-fixed',
  3645. prefix: 'xs-fixed-container',
  3646. zoomType: 'y'
  3647. }, cfg);
  3648. this.init();
  3649. };
  3650. Util.extend(Fixed, Base, {
  3651. fixedElements: [],
  3652. init: function () {
  3653. var self = this, userConfig = self.userConfig, xscroll = self.xscroll = userConfig.xscroll, xscrollConfig = self.xscrollConfig = xscroll.userConfig;
  3654. self.isY = !!(userConfig.zoomType == 'y');
  3655. self._ = self.isY ? {
  3656. top: 'top',
  3657. height: 'height',
  3658. width: 'width',
  3659. offsetTop: 'offsetTop'
  3660. } : {
  3661. top: 'left',
  3662. height: 'width',
  3663. width: 'height',
  3664. offsetTop: 'offsetLeft'
  3665. };
  3666. self.fixedRenderTo = Util.getNode(userConfig.fixedRenderTo);
  3667. return self;
  3668. },
  3669. render: function () {
  3670. var self = this;
  3671. var xscroll = self.xscroll;
  3672. self.infinite = xscroll.getPlugin('infinite');
  3673. if (!self.fixedRenderTo) {
  3674. self.fixedRenderTo = document.createElement('div');
  3675. xscroll.renderTo.appendChild(self.fixedRenderTo);
  3676. }
  3677. Util.addClass(self.fixedRenderTo, self.userConfig.prefix);
  3678. var originalFixedElements = self.originalFixedElements = self.getFixedElements();
  3679. for (var i = 0, l = originalFixedElements.length; i < l; i++) {
  3680. self.renderFixedElement(originalFixedElements[i], i, self.fixedRenderTo);
  3681. }
  3682. return self;
  3683. },
  3684. getFixedElements: function () {
  3685. var self = this;
  3686. var infinite = self.infinite;
  3687. var userConfig = self.userConfig;
  3688. if (infinite) {
  3689. var els = [];
  3690. for (var i in infinite.__serializedData) {
  3691. var data = infinite.__serializedData[i];
  3692. if (data && data.style && data.style.position == 'fixed') {
  3693. els.push(data);
  3694. }
  3695. }
  3696. return els;
  3697. } else {
  3698. return Util.getNodes(userConfig.fixedElements, self.xscroll.content);
  3699. }
  3700. },
  3701. renderFixedElement: function (el, fixedIndex, fixedRenderTo) {
  3702. var self = this;
  3703. var isRender = true;
  3704. var _ = self._;
  3705. var xscroll = self.xscroll;
  3706. var userConfig = self.userConfig;
  3707. var xscrollConfig = self.xscrollConfig;
  3708. var useOriginScroll = xscrollConfig.useOriginScroll;
  3709. var infinite = self.infinite;
  3710. var fixedElement = self.fixedElements[fixedIndex];
  3711. if (!self.fixedElements[fixedIndex]) {
  3712. isRender = false;
  3713. if (useOriginScroll && !infinite) {
  3714. //use original position:fixed stylesheet
  3715. el.style.position = 'fixed';
  3716. el.style.display = 'block';
  3717. } else {
  3718. //deep clone fixed nodes and hide original nodes
  3719. fixedElement = document.createElement('div');
  3720. if (infinite) {
  3721. fixedElement.setAttribute('style', Util.stringifyStyle(Util.mix(el.style, {
  3722. display: 'block',
  3723. width: '100%'
  3724. })));
  3725. fixedElement.style[_.top] = (el.style[_.top] >= 0 ? el.style[_.top] : el._top) + 'px';
  3726. if (el.style[_.height]) {
  3727. fixedElement.style[_.height] = el.style[_.height] + 'px';
  3728. }
  3729. infinite.userConfig.renderHook.call(self, fixedElement, el);
  3730. } else {
  3731. fixedElement.style.display = 'block';
  3732. fixedElement.style.position = 'absolute';
  3733. fixedElement.style[_.width] = '100%';
  3734. fixedElement.innerHTML = el.innerHTML;
  3735. fixedElement.className = el.className;
  3736. fixedElement.setAttribute('style', el.getAttribute('style'));
  3737. fixedElement.style[_.top] = el[_.offsetTop] + 'px';
  3738. el.style.display = 'none';
  3739. }
  3740. fixedRenderTo.appendChild(fixedElement);
  3741. self.fixedElements.push(fixedElement);
  3742. }
  3743. }
  3744. xscroll.trigger('fixedchange', {
  3745. fixedIndex: fixedIndex,
  3746. fixedElement: useOriginScroll ? el : fixedElement,
  3747. originalFixedElement: el,
  3748. isRender: isRender
  3749. });
  3750. },
  3751. destroy: function () {
  3752. var self = this;
  3753. self.fixedElements = undefined;
  3754. }
  3755. });
  3756. if (typeof module == 'object' && module.exports) {
  3757. exports = Fixed;
  3758. } /** ignored by jsdoc **/ else {
  3759. return Fixed;
  3760. }
  3761. return exports;
  3762. }(components_fixed);
  3763. core = function (exports) {
  3764. var Util = util, Base = base, Animate = animate, Boundry = boundry, Hammer = hammer, Sticky = components_sticky, Fixed = components_fixed;
  3765. // boundry checked bounce effect
  3766. var BOUNDRY_CHECK_DURATION = 500;
  3767. var BOUNDRY_CHECK_EASING = 'ease';
  3768. var BOUNDRY_CHECK_ACCELERATION = 0.1;
  3769. /**
  3770. * @constructor
  3771. * @param {object} cfg config for scroll
  3772. * @param {number} cfg.SCROLL_ACCELERATION acceleration for scroll, min value make the scrolling smoothly
  3773. * @param {number} cfg.BOUNDRY_CHECK_DURATION duration for boundry bounce
  3774. * @param {number} cfg.BOUNDRY_CHECK_EASING easing for boundry bounce
  3775. * @param {number} cfg.BOUNDRY_CHECK_ACCELERATION acceleration for boundry bounce
  3776. * @param {boolean} cfg.lockX just like overflow-x:hidden
  3777. * @param {boolean} cfg.lockY just like overflow-y:hidden
  3778. * @param {boolean} cfg.scrollbarX config if the scrollbar-x is visible
  3779. * @param {boolean} cfg.scrollbarY config if the scrollbar-y is visible
  3780. * @param {boolean} cfg.useTransition config if use css3 transition or raf for scroll animation
  3781. * @param {boolean} cfg.useOriginScroll config if use simulate or origin scroll
  3782. * @param {boolean} cfg.bounce config if use has the bounce effect when scrolling outside of the boundry
  3783. * @param {boolean} cfg.boundryCheck config if scrolling inside of the boundry
  3784. * @param {boolean} cfg.preventDefault prevent touchstart
  3785. * @param {boolean} cfg.preventTouchMove prevent touchmove
  3786. * @param {string|HTMLElement} cfg.container config for scroller's container which default value is ".xs-container"
  3787. * @param {string|HTMLElement} cfg.content config for scroller's content which default value is ".xs-content"
  3788. * @param {object} cfg.indicatorInsets config scrollbars position {top: number, left: number, bottom: number, right: number}
  3789. * @param {string} cfg.stickyElements config for sticky-positioned elements
  3790. * @param {string} cfg.fixedElements config for fixed-positioned elements
  3791. * @param {string} cfg.touchAction config for touchAction of the scroller
  3792. * @extends XScroll
  3793. * @example
  3794. * var xscroll = new XScroll({
  3795. * renderTo:"#scroll",
  3796. * lockX:false,
  3797. * scrollbarX:true
  3798. * });
  3799. * xscroll.render();
  3800. */
  3801. function XScroll(cfg) {
  3802. XScroll.superclass.constructor.call(this);
  3803. this.userConfig = cfg;
  3804. this.init();
  3805. }
  3806. Util.extend(XScroll, Base, {
  3807. /**
  3808. * version
  3809. * @memberof XScroll
  3810. * @type {string}
  3811. */
  3812. version: '3.0.13',
  3813. /**
  3814. * init scroll
  3815. * @memberof XScroll
  3816. * @return {XScroll}
  3817. */
  3818. init: function () {
  3819. var self = this;
  3820. var defaultCfg = {
  3821. preventDefault: true,
  3822. bounce: true,
  3823. boundryCheck: true,
  3824. useTransition: true,
  3825. gpuAcceleration: true,
  3826. BOUNDRY_CHECK_EASING: BOUNDRY_CHECK_EASING,
  3827. BOUNDRY_CHECK_DURATION: BOUNDRY_CHECK_DURATION,
  3828. BOUNDRY_CHECK_ACCELERATION: BOUNDRY_CHECK_ACCELERATION,
  3829. useOriginScroll: false,
  3830. zoomType: 'y',
  3831. indicatorInsets: {
  3832. top: 3,
  3833. bottom: 3,
  3834. left: 3,
  3835. right: 3,
  3836. width: 3,
  3837. spacing: 5
  3838. },
  3839. container: '.xs-container',
  3840. content: '.xs-content',
  3841. stickyElements: '.xs-sticky',
  3842. fixedElements: '.xs-fixed',
  3843. touchAction: 'auto'
  3844. };
  3845. //generate guid
  3846. self.guid = Util.guid();
  3847. self.renderTo = Util.getNode(self.userConfig.renderTo);
  3848. //timer for animtion
  3849. self.__timers = {};
  3850. //config attributes on element
  3851. var elCfg = JSON.parse(self.renderTo.getAttribute('xs-cfg'));
  3852. var userConfig = self.userConfig = Util.mix(Util.mix(defaultCfg, elCfg), self.userConfig);
  3853. self.container = Util.getNode(userConfig.container, self.renderTo);
  3854. self.content = Util.getNode(userConfig.content, self.renderTo);
  3855. self.boundry = new Boundry();
  3856. self.boundry.refresh();
  3857. return self;
  3858. },
  3859. /**
  3860. * destroy scroll
  3861. * @memberof XScroll
  3862. * @return {XScroll}
  3863. */
  3864. destroy: function () {
  3865. var self = this;
  3866. self.mc && self.mc.destroy();
  3867. self.sticky && self.sticky.destroy();
  3868. self.fixed && self.fixed.destroy();
  3869. },
  3870. _initContainer: function () {
  3871. },
  3872. /**
  3873. * @memberof XScroll
  3874. * @return {XScroll}
  3875. */
  3876. enableGPUAcceleration: function () {
  3877. this.userConfig.gpuAcceleration = true;
  3878. return this;
  3879. },
  3880. /**
  3881. * @memberof XScroll
  3882. * @return {XScroll}
  3883. */
  3884. disableGPUAcceleration: function () {
  3885. this.userConfig.gpuAcceleration = false;
  3886. return this;
  3887. },
  3888. /**
  3889. * get scroll offset
  3890. * @memberof XScroll
  3891. * @return {Object} {scrollTop:scrollTop,scrollLeft:scrollLeft}
  3892. */
  3893. getScrollPos: function () {
  3894. var self = this;
  3895. return {
  3896. scrollLeft: self.getScrollLeft(),
  3897. scrollTop: self.getScrollTop()
  3898. };
  3899. },
  3900. /**
  3901. * get scroll top value
  3902. * @memberof XScroll
  3903. * @return {number} scrollTop
  3904. */
  3905. getScrollTop: function () {
  3906. },
  3907. /**
  3908. * get scroll left value
  3909. * @memberof XScroll
  3910. * @return {number} scrollLeft
  3911. */
  3912. getScrollLeft: function () {
  3913. },
  3914. /**
  3915. * scroll absolute to the destination
  3916. * @memberof XScroll
  3917. * @param scrollLeft {number} scrollLeft
  3918. * @param scrollTop {number} scrollTop
  3919. * @param duration {number} duration for animte
  3920. * @param easing {string} easing functio for animate : ease-in | ease-in-out | ease | bezier(n,n,n,n)
  3921. **/
  3922. scrollTo: function (scrollLeft, scrollTop, duration, easing, callback) {
  3923. var self = this;
  3924. var scrollLeft = undefined === scrollLeft || isNaN(scrollLeft) ? -self.getScrollLeft() : scrollLeft;
  3925. var scrollTop = undefined === scrollTop || isNaN(scrollTop) ? -self.getScrollTop() : scrollTop;
  3926. self.scrollLeft(scrollLeft, duration, easing, callback);
  3927. self.scrollTop(scrollTop, duration, easing, callback);
  3928. },
  3929. /**
  3930. * scroll relative to the destination
  3931. * @memberof XScroll
  3932. * @param scrollLeft {number} scrollLeft
  3933. * @param scrollTop {number} scrollTop
  3934. * @param duration {number} duration for animte
  3935. * @param easing {string} easing functio for animate : ease-in | ease-in-out | ease | bezier(n,n,n,n)
  3936. **/
  3937. scrollBy: function (scrollLeft, scrollTop, duration, easing, callback) {
  3938. this.scrollByX(scrollLeft, duration, easing, callback);
  3939. this.scrollByY(scrollTop, duration, easing, callback);
  3940. },
  3941. /**
  3942. * horizontal scroll relative to the destination
  3943. * @memberof XScroll
  3944. * @param scrollLeft {number} scrollLeft
  3945. * @param duration {number} duration for animte
  3946. * @param easing {string} easing functio for animate : ease-in | ease-in-out | ease | bezier(n,n,n,n)
  3947. **/
  3948. scrollLeftBy: function (scrollLeft, duration, easing, callback) {
  3949. this.scrollLeft(Number(scrollLeft) + Number(this.getScrollLeft()), duration, easing, callback);
  3950. },
  3951. /**
  3952. * vertical scroll relative to the destination
  3953. * @memberof XScroll
  3954. * @param scrollTop {number} scrollTop
  3955. * @param duration {number} duration for animte
  3956. * @param easing {string} easing functio for animate : ease-in | ease-in-out | ease | bezier(n,n,n,n)
  3957. **/
  3958. scrollTopBy: function (scrollTop, duration, easing, callback) {
  3959. this.scrollTop(Number(scrollTop) + Number(this.getScrollTop()), duration, easing, callback);
  3960. },
  3961. /**
  3962. * horizontal scroll absolute to the destination
  3963. * @memberof XScroll
  3964. * @param scrollLeft {number} scrollLeft
  3965. * @param duration {number} duration for animte
  3966. * @param easing {string} easing functio for animate : ease-in | ease-in-out | ease | bezier(n,n,n,n)
  3967. **/
  3968. scrollLeft: function (scrollLeft, duration, easing, callback) {
  3969. },
  3970. /**
  3971. * vertical scroll absolute to the destination
  3972. * @memberof XScroll
  3973. * @param scrollTop {number} scrollTop
  3974. * @param duration {number} duration for animte
  3975. * @param easing {string} easing functio for animate : ease-in | ease-in-out | ease | bezier(n,n,n,n)
  3976. **/
  3977. scrollTop: function (scrollTop, duration, easing, callback) {
  3978. },
  3979. /**
  3980. * reset the boundry size
  3981. * @memberof XScroll
  3982. * @return {XScroll}
  3983. **/
  3984. resetSize: function () {
  3985. var self = this;
  3986. if (!self.container || !self.content)
  3987. return;
  3988. var userConfig = self.userConfig;
  3989. var renderToStyle = getComputedStyle(self.renderTo);
  3990. var width = self.width = (userConfig.width || self.renderTo.offsetWidth) - Util.px2Num(renderToStyle['padding-left']) - Util.px2Num(renderToStyle['padding-right']);
  3991. var height = self.height = (userConfig.height || self.renderTo.offsetHeight) - Util.px2Num(renderToStyle['padding-top']) - Util.px2Num(renderToStyle['padding-bottom']);
  3992. var containerWidth = userConfig.containerWidth || self.content.offsetWidth;
  3993. var containerHeight = userConfig.containerHeight || self.content.offsetHeight;
  3994. self.containerWidth = containerWidth < self.width ? self.width : containerWidth;
  3995. self.containerHeight = containerHeight < self.height ? self.height : containerHeight;
  3996. self.boundry.refresh({
  3997. width: self.width,
  3998. height: self.height
  3999. });
  4000. return self;
  4001. },
  4002. /**
  4003. * render scroll
  4004. * @memberof XScroll
  4005. * @return {XScroll}
  4006. **/
  4007. render: function () {
  4008. var self = this;
  4009. self.resetSize();
  4010. //init stickies
  4011. self.initSticky();
  4012. //init fixed elements
  4013. self.initFixed();
  4014. self.trigger('afterrender', { type: 'afterrender' });
  4015. self._bindEvt();
  4016. //update touch-action
  4017. self.initTouchAction();
  4018. return self;
  4019. },
  4020. /**
  4021. * init touch action
  4022. * @memberof XScroll
  4023. * @return {XScroll}
  4024. */
  4025. initTouchAction: function () {
  4026. var self = this;
  4027. self.mc.set({ touchAction: self.userConfig.touchAction });
  4028. return self;
  4029. },
  4030. initFixed: function () {
  4031. var self = this, userConfig = self.userConfig;
  4032. self.fixed = self.fixed || new Fixed({
  4033. fixedElements: userConfig.fixedElements,
  4034. xscroll: self,
  4035. fixedRenderTo: userConfig.fixedRenderTo
  4036. });
  4037. self.fixed.render();
  4038. self.resetSize();
  4039. return self;
  4040. },
  4041. initSticky: function () {
  4042. var self = this, userConfig = self.userConfig;
  4043. var sticky = self.sticky = self.sticky || new Sticky({
  4044. xscroll: self,
  4045. zoomType: userConfig.zoomType,
  4046. stickyRenderTo: userConfig.stickyRenderTo
  4047. });
  4048. sticky.render();
  4049. },
  4050. /**
  4051. * bounce to the boundry vertical and horizontal
  4052. * @memberof XScroll
  4053. * @return {XScroll}
  4054. **/
  4055. boundryCheck: function () {
  4056. return this;
  4057. },
  4058. /**
  4059. * bounce to the boundry horizontal
  4060. * @memberof XScroll
  4061. * @return {XScroll}
  4062. **/
  4063. boundryCheckX: function () {
  4064. return this;
  4065. },
  4066. /**
  4067. * bounce to the boundry vertical
  4068. * @memberof XScroll
  4069. * @return {XScroll}
  4070. **/
  4071. boundryCheckY: function () {
  4072. return this;
  4073. },
  4074. _bindEvt: function () {
  4075. var self = this;
  4076. if (self.___isEvtBind)
  4077. return;
  4078. self.___isEvtBind = true;
  4079. var mc = self.mc = new Hammer.Manager(self.renderTo);
  4080. var tap = new Hammer.Tap();
  4081. var pan = new Hammer.Pan();
  4082. var pinch = new Hammer.Pinch();
  4083. mc.add([
  4084. tap,
  4085. pan
  4086. ]);
  4087. //trigger all events
  4088. self.mc.on('panstart pan panend pancancel pinchstart pinchmove pinchend pinchcancel pinchin pinchout', function (e) {
  4089. self.trigger(e.type, e);
  4090. });
  4091. //trigger touch events
  4092. var touchEvents = [
  4093. 'touchstart',
  4094. 'touchmove',
  4095. 'touchend',
  4096. 'touchcancel',
  4097. 'mousedown'
  4098. ];
  4099. for (var i = 0, l = touchEvents.length; i < l; i++) {
  4100. self.renderTo.addEventListener(touchEvents[i], function (e) {
  4101. self.trigger(e.type, e);
  4102. });
  4103. }
  4104. self.mc.on('tap', function (e) {
  4105. if (e.tapCount == 1) {
  4106. e.type = 'tap';
  4107. self.trigger(e.type, e);
  4108. } else if (e.tapCount == 2) {
  4109. e.type = 'doubletap';
  4110. self.trigger('doubletap', e);
  4111. }
  4112. });
  4113. return self;
  4114. },
  4115. _resetLockConfig: function () {
  4116. },
  4117. stop: function () {
  4118. }
  4119. });
  4120. if (typeof module == 'object' && module.exports) {
  4121. exports = XScroll;
  4122. } /** ignored by jsdoc **/ else {
  4123. return XScroll;
  4124. }
  4125. return exports;
  4126. }(core);
  4127. components_scrollbar = function (exports) {
  4128. var Util = util;
  4129. var Animate = animate;
  4130. var MAX_BOUNCE_DISTANCE = 40;
  4131. var MIN_BAR_SCROLLED_SIZE = 10;
  4132. var MIN_BAR_SIZE = 50;
  4133. var transform = Util.prefixStyle('transform');
  4134. var transformStr = Util.vendor ? [
  4135. '-',
  4136. Util.vendor,
  4137. '-transform'
  4138. ].join('') : 'transform';
  4139. var transition = Util.prefixStyle('transition');
  4140. var borderRadius = Util.prefixStyle('borderRadius');
  4141. var transitionDuration = Util.prefixStyle('transitionDuration');
  4142. var ScrollBar = function (cfg) {
  4143. this.userConfig = Util.mix({
  4144. MIN_BAR_SCROLLED_SIZE: MIN_BAR_SCROLLED_SIZE,
  4145. MIN_BAR_SIZE: MIN_BAR_SIZE,
  4146. MAX_BOUNCE_DISTANCE: MAX_BOUNCE_DISTANCE,
  4147. spacing: 5
  4148. }, cfg);
  4149. this.init(cfg.xscroll);
  4150. };
  4151. Util.mix(ScrollBar.prototype, {
  4152. init: function (xscroll) {
  4153. var self = this;
  4154. self.xscroll = xscroll;
  4155. self.type = self.userConfig.type;
  4156. self.isY = self.type == 'y' ? true : false;
  4157. self.scrollTopOrLeft = self.isY ? 'scrollTop' : 'scrollLeft';
  4158. },
  4159. destroy: function () {
  4160. var self = this;
  4161. Util.remove(self.scrollbar);
  4162. self.xscroll.off('scroll', self._scrollHandler, self);
  4163. self.xscroll.off('scrollend', self._scrollEndHandler, self);
  4164. },
  4165. render: function () {
  4166. var self = this;
  4167. var xscroll = self.xscroll;
  4168. var boundry = xscroll.boundry;
  4169. var indicatorInsets = self.xscroll.userConfig.indicatorInsets;
  4170. var translateZ = xscroll.userConfig.gpuAcceleration ? ' translateZ(0) ' : '';
  4171. var transform = translateZ ? transformStr + ':' + translateZ + ';' : '';
  4172. var commonCss = 'opacity:0;position:absolute;z-index:999;overflow:hidden;-webkit-border-radius:3px;-moz-border-radius:3px;-o-border-radius:3px;' + transform;
  4173. indicatorInsets._xright = indicatorInsets.right + indicatorInsets.spacing;
  4174. indicatorInsets._xbottom = indicatorInsets.bottom + indicatorInsets.spacing;
  4175. var css = self.isY ? Util.substitute('width:{width}px;bottom:{_xbottom}px;top:{top}px;right:{right}px;', indicatorInsets) + commonCss : Util.substitute('height:{width}px;left:{left}px;right:{_xright}px;bottom:{bottom}px;', indicatorInsets) + commonCss;
  4176. if (!self.scrollbar) {
  4177. self.scrollbar = document.createElement('div');
  4178. self.indicate = document.createElement('div');
  4179. xscroll.renderTo.appendChild(self.scrollbar);
  4180. self.scrollbar.appendChild(self.indicate);
  4181. }
  4182. self.scrollbar.style.cssText = css;
  4183. var size = self.isY ? 'width:100%;' : 'height:100%;';
  4184. self.indicate.style.cssText = size + 'position:absolute;background:rgba(0,0,0,0.3);-webkit-border-radius:3px;-moz-border-radius:3px;-o-border-radius:3px;';
  4185. self._update();
  4186. self.hide(0);
  4187. self._bindEvt();
  4188. },
  4189. _update: function (pos, duration, easing, callback) {
  4190. var self = this;
  4191. var pos = undefined === pos ? self.isY ? self.xscroll.getScrollTop() : self.xscroll.getScrollLeft() : pos;
  4192. var barInfo = self.computeScrollBar(pos);
  4193. var size = self.isY ? 'height' : 'width';
  4194. self.indicate.style[size] = Math.round(barInfo.size) + 'px';
  4195. if (duration && easing) {
  4196. self.scrollTo(barInfo.pos, duration, easing, callback);
  4197. } else {
  4198. self.moveTo(barInfo.pos);
  4199. }
  4200. },
  4201. //compute the position and size of the scrollbar
  4202. computeScrollBar: function (pos) {
  4203. var self = this;
  4204. var type = self.isY ? 'y' : 'x';
  4205. var spacing = self.userConfig.spacing;
  4206. var xscroll = self.xscroll;
  4207. var boundry = xscroll.boundry;
  4208. var userConfig = self.userConfig;
  4209. var pos = self.isY ? Math.round(pos) + boundry._xtop : Math.round(pos) + boundry._xleft;
  4210. var MIN_BAR_SCROLLED_SIZE = userConfig.MIN_BAR_SCROLLED_SIZE;
  4211. var MIN_BAR_SIZE = userConfig.MIN_BAR_SIZE;
  4212. var MAX_BOUNCE_DISTANCE = userConfig.MAX_BOUNCE_DISTANCE;
  4213. self.containerSize = self.isY ? xscroll.containerHeight + boundry._xtop + boundry._xbottom : self.xscroll.containerWidth + boundry._xright + boundry._xleft;
  4214. self.size = self.isY ? boundry.cfg.height : boundry.cfg.width;
  4215. self.indicateSize = self.isY ? boundry.cfg.height - spacing * 2 : boundry.cfg.width - spacing * 2;
  4216. var indicateSize = self.indicateSize;
  4217. var containerSize = self.containerSize;
  4218. var barPos = indicateSize * pos / containerSize;
  4219. var barSize = Math.round(indicateSize * self.size / containerSize);
  4220. var overTop = self.isY ? xscroll.getBoundryOutTop() : xscroll.getBoundryOutLeft();
  4221. var overBottom = self.isY ? xscroll.getBoundryOutBottom() : xscroll.getBoundryOutRight();
  4222. var barShiftSize = MIN_BAR_SIZE - barSize > 0 ? MIN_BAR_SIZE - barSize : 0;
  4223. barSize = barSize < MIN_BAR_SIZE ? MIN_BAR_SIZE : barSize;
  4224. barPos = (indicateSize - barShiftSize) * pos / containerSize;
  4225. if (overTop >= 0) {
  4226. var pct = overTop / MAX_BOUNCE_DISTANCE;
  4227. pct = pct > 1 ? 1 : pct;
  4228. barPos = -pct * (barSize - MIN_BAR_SCROLLED_SIZE);
  4229. }
  4230. if (overBottom >= 0) {
  4231. var pct = overBottom / MAX_BOUNCE_DISTANCE;
  4232. pct = pct > 1 ? 1 : pct;
  4233. barPos = pct * (barSize - MIN_BAR_SCROLLED_SIZE) + indicateSize - barSize;
  4234. }
  4235. self.barPos = Math.round(barPos);
  4236. return {
  4237. size: Math.round(barSize),
  4238. pos: self.barPos
  4239. };
  4240. },
  4241. scrollTo: function (pos, duration, easing, callback) {
  4242. var self = this;
  4243. self.show();
  4244. var translateZ = self.xscroll.userConfig.gpuAcceleration ? ' translateZ(0) ' : '';
  4245. var config = {
  4246. css: { transform: self.isY ? 'translateY(' + pos + 'px)' + translateZ : 'translateX(' + pos + 'px)' + translateZ },
  4247. duration: duration,
  4248. easing: easing,
  4249. useTransition: self.xscroll.userConfig.useTransition,
  4250. end: callback
  4251. };
  4252. self.__timer = self.__timer || new Animate(self.indicate, config);
  4253. //run
  4254. self.__timer.stop();
  4255. self.__timer.reset(config);
  4256. self.__timer.run();
  4257. },
  4258. moveTo: function (pos) {
  4259. var self = this;
  4260. self.show();
  4261. var translateZ = self.xscroll.userConfig.gpuAcceleration ? ' translateZ(0) ' : '';
  4262. self.isY ? self.indicate.style[transform] = 'translateY(' + pos + 'px) ' + translateZ : self.indicate.style[transform] = 'translateX(' + pos + 'px) ' + translateZ;
  4263. self.indicate.style[transition] = '';
  4264. },
  4265. _scrollHandler: function (e) {
  4266. var self = this;
  4267. self._update(e[self.scrollTopOrLeft]);
  4268. return self;
  4269. },
  4270. isBoundryOut: function () {
  4271. var self = this;
  4272. return !self.isY ? self.xscroll.isBoundryOutLeft() || self.xscroll.isBoundryOutRight() : self.xscroll.isBoundryOutTop() || self.xscroll.isBoundryOutBottom();
  4273. },
  4274. _scrollEndHandler: function (e) {
  4275. var self = this;
  4276. if (!self.isBoundryOut()) {
  4277. self._update(e[self.scrollTopOrLeft]);
  4278. self.hide();
  4279. }
  4280. return self;
  4281. },
  4282. _bindEvt: function () {
  4283. var self = this;
  4284. if (self.__isEvtBind)
  4285. return;
  4286. self.__isEvtBind = true;
  4287. self.xscroll.on('scroll', self._scrollHandler, self);
  4288. self.xscroll.on('scrollend', self._scrollEndHandler, self);
  4289. },
  4290. reset: function () {
  4291. var self = this;
  4292. self.pos = 0;
  4293. self._update();
  4294. },
  4295. hide: function (duration, easing, delay) {
  4296. var self = this;
  4297. var duration = duration >= 0 ? duration : 300;
  4298. var easing = easing || 'ease-out';
  4299. var delay = delay >= 0 ? delay : 100;
  4300. self.scrollbar.style.opacity = 0;
  4301. self.scrollbar.style[transition] = [
  4302. 'opacity ',
  4303. duration,
  4304. 'ms ',
  4305. ' ease-out ',
  4306. delay,
  4307. 'ms'
  4308. ].join('');
  4309. },
  4310. show: function () {
  4311. var self = this;
  4312. self.scrollbar.style.opacity = 1;
  4313. self.scrollbar.style[transition] = '';
  4314. }
  4315. });
  4316. if (typeof module == 'object' && module.exports) {
  4317. exports = ScrollBar;
  4318. } /** ignored by jsdoc **/ else {
  4319. return ScrollBar;
  4320. }
  4321. return exports;
  4322. }(components_scrollbar);
  4323. components_controller = function (exports) {
  4324. var Util = util, Base = base;
  4325. var Controller = function (cfg) {
  4326. Controller.superclass.constructor.call(this, cfg);
  4327. this.userConfig = Util.mix({}, cfg);
  4328. this.init();
  4329. };
  4330. Util.extend(Controller, Base, {
  4331. init: function () {
  4332. var self = this;
  4333. self.xscroll = self.userConfig.xscroll;
  4334. },
  4335. add: function (scroll, cfg) {
  4336. var self = this;
  4337. cfg = Util.extend({
  4338. captureBounce: false,
  4339. stopPropagation: true
  4340. }, cfg);
  4341. if (!self.__scrolls) {
  4342. self.__scrolls = {};
  4343. }
  4344. if (scroll.guid && !self.__scrolls[scroll.guid]) {
  4345. scroll.parentscroll = self.xscroll;
  4346. self._bind(scroll);
  4347. return self.__scrolls[scroll.guid] = scroll;
  4348. }
  4349. return;
  4350. },
  4351. remove: function (scroll) {
  4352. var self = this;
  4353. if (!scroll || !scroll.guid)
  4354. return;
  4355. var subscroll = self.__scrolls[scroll.guid];
  4356. if (subscroll) {
  4357. subscroll.parentscroll = null;
  4358. self._unbind(scroll);
  4359. subscroll = null;
  4360. }
  4361. },
  4362. get: function (guid) {
  4363. if (guid) {
  4364. return this.__scrolls[guid];
  4365. }
  4366. return this.__scrolls;
  4367. },
  4368. _unbind: function (sub) {
  4369. },
  4370. _bind: function (sub) {
  4371. var self = this, xscroll = self.xscroll;
  4372. xscroll.renderTo.addEventListener('touchstart', function () {
  4373. xscroll._resetLockConfig();
  4374. });
  4375. sub.renderTo.addEventListener('touchstart', function () {
  4376. sub._resetLockConfig();
  4377. });
  4378. xscroll.on('panend', xscroll._resetLockConfig);
  4379. sub.on('panend', sub._resetLockConfig);
  4380. sub.on('panstart', function (e) {
  4381. //vertical scroll enabled
  4382. if (!sub.userConfig.lockY && !xscroll.userConfig.lockY) {
  4383. //outside of boundry
  4384. if (sub.isBoundryOut()) {
  4385. xscroll.userConfig.lockY = true;
  4386. return;
  4387. }
  4388. if (e.direction == 16 && sub.getBoundryOutTop() >= 0) {
  4389. sub.userConfig.lockY = true;
  4390. } else if (e.direction == 8 && sub.getBoundryOutTop() >= 0 && sub.getBoundryOutBottom() < 0) {
  4391. xscroll.userConfig.lockY = true;
  4392. }
  4393. if (e.direction == 8 && sub.getBoundryOutBottom() >= 0) {
  4394. sub.userConfig.lockY = true;
  4395. } else if (e.direction == 16 && sub.getBoundryOutBottom() >= 0 && sub.getBoundryOutTop() < 0) {
  4396. xscroll.userConfig.lockY = true;
  4397. }
  4398. if (sub.getBoundryOutTop() < 0 && sub.getBoundryOutBottom() < 0) {
  4399. xscroll.userConfig.lockY = true;
  4400. }
  4401. }
  4402. //horizontal scroll enabled
  4403. if (!sub.userConfig.lockX && !xscroll.userConfig.lockX) {
  4404. if (sub.isBoundryOut()) {
  4405. xscroll.userConfig.lockX = true;
  4406. return;
  4407. }
  4408. if (e.direction == 4 && sub.getBoundryOutLeft() >= 0) {
  4409. sub.userConfig.lockX = true;
  4410. } else if (e.direction == 2 && sub.getBoundryOutLeft() >= 0 && sub.getBoundryOutRight() < 0) {
  4411. xscroll.userConfig.lockX = true;
  4412. }
  4413. if (e.direction == 2 && sub.getBoundryOutRight() >= 0) {
  4414. sub.userConfig.lockX = true;
  4415. } else if (e.direction == 4 && sub.getBoundryOutRight() >= 0 && sub.getBoundryOutLeft() < 0) {
  4416. xscroll.userConfig.lockX = true;
  4417. }
  4418. if (sub.getBoundryOutLeft() < 0 && sub.getBoundryOutRight() < 0) {
  4419. xscroll.userConfig.lockX = true;
  4420. }
  4421. }
  4422. if (!sub.userConfig.lockX && xscroll.userConfig.lockX) {
  4423. //pan x
  4424. if (e.direction == 2 || e.direction == 4) {
  4425. xscroll.userConfig.lockY = true;
  4426. } else {
  4427. sub.userConfig.lockX = true;
  4428. }
  4429. }
  4430. if (!sub.userConfig.lockY && xscroll.userConfig.lockY) {
  4431. //pan y
  4432. if (e.direction == 8 || e.direction == 16) {
  4433. xscroll.userConfig.lockX = true;
  4434. } else {
  4435. sub.userConfig.lockY = true;
  4436. }
  4437. }
  4438. });
  4439. }
  4440. });
  4441. if (typeof module == 'object' && module.exports) {
  4442. exports = Controller;
  4443. } /** ignored by jsdoc **/ else {
  4444. return Controller;
  4445. }
  4446. return exports;
  4447. }(components_controller);
  4448. simulate_scroll = function (exports) {
  4449. var Util = util, Base = base, Core = core, Animate = animate, Hammer = hammer, ScrollBar = components_scrollbar, Controller = components_controller;
  4450. //reduced boundry pan distance
  4451. var PAN_RATE = 1 - 0.618;
  4452. //constant for scrolling acceleration
  4453. var SCROLL_ACCELERATION = 0.0005;
  4454. //constant for outside of boundry acceleration
  4455. var BOUNDRY_ACCELERATION = 0.03;
  4456. //transform-origin
  4457. var transformOrigin = Util.prefixStyle('transformOrigin');
  4458. //transform
  4459. var transform = Util.prefixStyle('transform');
  4460. /**
  4461. * @constructor
  4462. * @param {object} cfg config for scroll
  4463. * @param {number} cfg.SCROLL_ACCELERATION acceleration for scroll, min value make the scrolling smoothly
  4464. * @param {number} cfg.BOUNDRY_CHECK_DURATION duration for boundry bounce
  4465. * @param {number} cfg.BOUNDRY_CHECK_EASING easing for boundry bounce
  4466. * @param {number} cfg.BOUNDRY_CHECK_ACCELERATION acceleration for boundry bounce
  4467. * @param {boolean} cfg.lockX just like overflow-x:hidden
  4468. * @param {boolean} cfg.lockY just like overflow-y:hidden
  4469. * @param {boolean} cfg.scrollbarX config if the scrollbar-x is visible
  4470. * @param {boolean} cfg.scrollbarY config if the scrollbar-y is visible
  4471. * @param {boolean} cfg.useTransition config if use css3 transition or raf for scroll animation
  4472. * @param {boolean} cfg.bounce config if use has the bounce effect when scrolling outside of the boundry
  4473. * @param {boolean} cfg.boundryCheck config if scrolling inside of the boundry
  4474. * @param {boolean} cfg.preventDefault prevent touchstart
  4475. * @param {boolean} cfg.preventTouchMove prevent touchmove
  4476. * @param {string|HTMLElement} cfg.container config for scroller's container which default value is ".xs-container"
  4477. * @param {string|HTMLElement} cfg.content config for scroller's content which default value is ".xs-content"
  4478. * @param {object} cfg.indicatorInsets config scrollbars position {top: number, left: number, bottom: number, right: number}
  4479. * @param {string} cfg.stickyElements config for sticky-positioned elements
  4480. * @param {string} cfg.fixedElements config for fixed-positioned elements
  4481. * @param {string} cfg.touchAction config for touchAction of the scroller
  4482. * @extends XScroll
  4483. * @example
  4484. * var xscroll = new SimuScroll({
  4485. * renderTo:"#scroll",
  4486. * lockX:false,
  4487. * scrollbarX:true
  4488. * });
  4489. * xscroll.render();
  4490. */
  4491. function SimuScroll(cfg) {
  4492. SimuScroll.superclass.constructor.call(this, cfg);
  4493. }
  4494. Util.extend(SimuScroll, Core, {
  4495. /**
  4496. * @memberof SimuScroll
  4497. * @override
  4498. */
  4499. init: function () {
  4500. var self = this;
  4501. var defaultCfg = {
  4502. preventDefault: true,
  4503. preventTouchMove: true
  4504. };
  4505. SimuScroll.superclass.init.call(this);
  4506. self.userConfig = Util.mix(defaultCfg, self.userConfig);
  4507. self.SCROLL_ACCELERATION = self.userConfig.SCROLL_ACCELERATION || SCROLL_ACCELERATION;
  4508. self.BOUNDRY_ACCELERATION = self.userConfig.BOUNDRY_ACCELERATION || BOUNDRY_ACCELERATION;
  4509. self._initContainer();
  4510. self.resetSize();
  4511. //set overflow behaviors
  4512. self._setOverflowBehavior();
  4513. self.defaltConfig = {
  4514. lockY: self.userConfig.lockY,
  4515. lockX: self.userConfig.lockX
  4516. };
  4517. return self;
  4518. },
  4519. destroy: function () {
  4520. var self = this;
  4521. SimuScroll.superclass.destroy.call(this);
  4522. self.renderTo.style.overflow = '';
  4523. self.renderTo.style.touchAction = '';
  4524. self.container.style.transform = '';
  4525. self.container.style.transformOrigin = '';
  4526. self.content.style.transform = '';
  4527. self.content.style.transformOrigin = '';
  4528. self.off('touchstart mousedown', self._ontouchstart);
  4529. self.off('touchmove', self._ontouchmove);
  4530. window.removeEventListener('resize', self.resizeHandler, self);
  4531. self.destroyScrollBars();
  4532. },
  4533. /**
  4534. * set overflow behavior
  4535. * @return {boolean} [description]
  4536. */
  4537. _setOverflowBehavior: function () {
  4538. var self = this;
  4539. var renderTo = self.renderTo;
  4540. var computeStyle = getComputedStyle(renderTo);
  4541. self.userConfig.lockX = undefined === self.userConfig.lockX ? computeStyle['overflow-x'] == 'hidden' || self.width == self.containerWidth ? true : false : self.userConfig.lockX;
  4542. self.userConfig.lockY = undefined === self.userConfig.lockY ? computeStyle['overflow-y'] == 'hidden' || self.height == self.containerHeight ? true : false : self.userConfig.lockY;
  4543. self.userConfig.scrollbarX = undefined === self.userConfig.scrollbarX ? self.userConfig.lockX ? false : true : self.userConfig.scrollbarX;
  4544. self.userConfig.scrollbarY = undefined === self.userConfig.scrollbarY ? self.userConfig.lockY ? false : true : self.userConfig.scrollbarY;
  4545. return self;
  4546. },
  4547. /**
  4548. * reset lockX or lockY config to the default value
  4549. */
  4550. _resetLockConfig: function () {
  4551. var self = this;
  4552. self.userConfig.lockX = self.defaltConfig.lockX;
  4553. self.userConfig.lockY = self.defaltConfig.lockY;
  4554. return self;
  4555. },
  4556. /**
  4557. * init container
  4558. * @override
  4559. * @return {SimuScroll}
  4560. */
  4561. _initContainer: function () {
  4562. var self = this;
  4563. SimuScroll.superclass._initContainer.call(self);
  4564. if (self.__isContainerInited || !self.container || !self.content)
  4565. return;
  4566. self.container.style[transformOrigin] = '0 0';
  4567. self.content.style[transformOrigin] = '0 0';
  4568. self.translate(0, 0);
  4569. self.__isContainerInited = true;
  4570. return self;
  4571. },
  4572. /**
  4573. * get scroll top value
  4574. * @memberof SimuScroll
  4575. * @return {number} scrollTop
  4576. */
  4577. getScrollTop: function () {
  4578. var transY = window.getComputedStyle(this.container)[transform].match(/[-\d\.*\d*]+/g);
  4579. return transY ? Math.round(transY[5]) === 0 ? 0 : -Math.round(transY[5]) : 0;
  4580. },
  4581. /**
  4582. * get scroll left value
  4583. * @memberof SimuScroll
  4584. * @return {number} scrollLeft
  4585. */
  4586. getScrollLeft: function () {
  4587. var transX = window.getComputedStyle(this.content)[transform].match(/[-\d\.*\d*]+/g);
  4588. return transX ? Math.round(transX[4]) === 0 ? 0 : -Math.round(transX[4]) : 0;
  4589. },
  4590. /**
  4591. * horizontal scroll absolute to the destination
  4592. * @memberof SimuScroll
  4593. * @param scrollLeft {number} scrollLeft
  4594. * @param duration {number} duration for animte
  4595. * @param easing {string} easing functio for animate : ease-in | ease-in-out | ease | bezier(n,n,n,n)
  4596. **/
  4597. scrollLeft: function (x, duration, easing, callback) {
  4598. if (this.userConfig.lockX)
  4599. return;
  4600. var translateZ = this.userConfig.gpuAcceleration ? ' translateZ(0) ' : '';
  4601. this.x = undefined === x || isNaN(x) || 0 === x ? 0 : -Math.round(x);
  4602. this._animate('x', 'translateX(' + this.x + 'px) scale(' + this.scale + ')' + translateZ, duration, easing, callback);
  4603. return this;
  4604. },
  4605. /**
  4606. * vertical scroll absolute to the destination
  4607. * @memberof SimuScroll
  4608. * @param scrollTop {number} scrollTop
  4609. * @param duration {number} duration for animte
  4610. * @param easing {string} easing functio for animate : ease-in | ease-in-out | ease | bezier(n,n,n,n)
  4611. **/
  4612. scrollTop: function (y, duration, easing, callback) {
  4613. if (this.userConfig.lockY)
  4614. return;
  4615. var translateZ = this.userConfig.gpuAcceleration ? ' translateZ(0) ' : '';
  4616. this.y = undefined === y || isNaN(y) || 0 === y ? 0 : -Math.round(y);
  4617. this._animate('y', 'translateY(' + this.y + 'px) ' + translateZ, duration, easing, callback);
  4618. return this;
  4619. },
  4620. /**
  4621. * translate the scroller to a new destination includes x , y , scale
  4622. * @memberof SimuScroll
  4623. * @param x {number} x
  4624. * @param y {number} y
  4625. * @param scale {number} scale
  4626. **/
  4627. translate: function (x, y, scale) {
  4628. var translateZ = this.userConfig.gpuAcceleration ? ' translateZ(0) ' : '';
  4629. this.x = x || this.x || 0;
  4630. this.y = y || this.y || 0;
  4631. this.scale = scale || this.scale || 1;
  4632. this.content.style[transform] = 'translate(' + this.x + 'px,0px) scale(' + this.scale + ') ' + translateZ;
  4633. this.container.style[transform] = 'translate(0px,' + this.y + 'px) ' + translateZ;
  4634. return this;
  4635. },
  4636. _animate: function (type, transform, duration, easing, callback) {
  4637. var self = this;
  4638. var duration = duration || 0;
  4639. var easing = easing || 'quadratic';
  4640. var el = type == 'y' ? self.container : self.content;
  4641. var config = {
  4642. css: { transform: transform },
  4643. duration: duration,
  4644. easing: easing,
  4645. run: function (e) {
  4646. /**
  4647. * @event {@link SimuScroll#"scroll"}
  4648. */
  4649. self.trigger('scroll', {
  4650. scrollTop: self.getScrollTop(),
  4651. scrollLeft: self.getScrollLeft(),
  4652. type: 'scroll'
  4653. });
  4654. },
  4655. useTransition: self.userConfig.useTransition,
  4656. end: function (e) {
  4657. callback && callback();
  4658. if ((self['_bounce' + type] === 0 || self['_bounce' + type] === undefined) && easing != 'linear') {
  4659. self['isScrolling' + type.toUpperCase()] = false;
  4660. self['isRealScrolling' + type.toUpperCase()] = false;
  4661. self.trigger('scrollend', {
  4662. type: 'scrollend',
  4663. scrollTop: self.getScrollTop(),
  4664. scrollLeft: self.getScrollLeft(),
  4665. zoomType: type,
  4666. duration: duration,
  4667. easing: easing
  4668. });
  4669. }
  4670. }
  4671. };
  4672. var timer = self.__timers[type] = self.__timers[type] || new Animate(el, config);
  4673. timer.stop();
  4674. timer.reset(config);
  4675. timer.run();
  4676. self.trigger('scrollanimate', {
  4677. type: 'scrollanimate',
  4678. scrollTop: -self.y,
  4679. scrollLeft: -self.x,
  4680. duration: duration,
  4681. easing: easing,
  4682. zoomType: type
  4683. });
  4684. return this;
  4685. },
  4686. _ontap: function (e) {
  4687. var self = this;
  4688. self.boundryCheck();
  4689. // self._unPreventHref(e);
  4690. if (!self.isRealScrollingX && !self.isRealScrollingY) {
  4691. }
  4692. // self._preventHref(e);
  4693. self.isRealScrollingY = false;
  4694. self.isRealScrollingY = false;
  4695. },
  4696. _bindEvt: function () {
  4697. SimuScroll.superclass._bindEvt.call(this);
  4698. var self = this;
  4699. if (self.__isEvtBind)
  4700. return;
  4701. self.__isEvtBind = true;
  4702. var pinch = new Hammer.Pinch();
  4703. self.mc.add(pinch);
  4704. self.on('touchstart mousedown', self._ontouchstart, self);
  4705. self.on('touchmove', self._ontouchmove, self);
  4706. self.on('tap', self._ontap, self);
  4707. self.on('panstart', self._onpanstart, self);
  4708. self.on('pan', self._onpan, self);
  4709. self.on('panend', self._onpanend, self);
  4710. self.resizeHandler = function (e) {
  4711. setTimeout(function () {
  4712. self.resetSize();
  4713. self.boundryCheck(0);
  4714. self.render();
  4715. }, 100);
  4716. };
  4717. //window resize
  4718. window.addEventListener('resize', self.resizeHandler, self);
  4719. return this;
  4720. },
  4721. _ontouchstart: function (e) {
  4722. var self = this;
  4723. if (!/(SELECT|INPUT|TEXTAREA)/i.test(e.target.tagName) && self.userConfig.preventDefault) {
  4724. e.preventDefault();
  4725. }
  4726. self.stop();
  4727. },
  4728. _ontouchmove: function (e) {
  4729. this.userConfig.preventTouchMove && e.preventDefault();
  4730. },
  4731. _onpanstart: function (e) {
  4732. this.userConfig.preventTouchMove && e.preventDefault();
  4733. var self = this;
  4734. var scrollLeft = self.getScrollLeft();
  4735. var scrollTop = self.getScrollTop();
  4736. self.stop();
  4737. self.translate(-scrollLeft, -scrollTop);
  4738. var threshold = self.mc.get('pan').options.threshold;
  4739. self.thresholdY = e.direction == '8' ? threshold : e.direction == '16' ? -threshold : 0;
  4740. self.thresholdX = e.direction == '2' ? threshold : e.direction == '4' ? -threshold : 0;
  4741. return self;
  4742. },
  4743. _onpan: function (e) {
  4744. this.userConfig.preventTouchMove && e.preventDefault();
  4745. var self = this;
  4746. var boundry = self.boundry;
  4747. var userConfig = self.userConfig;
  4748. var boundryCheck = userConfig.boundryCheck;
  4749. var bounce = userConfig.bounce;
  4750. var scrollTop = self.__topstart || (self.__topstart = -self.getScrollTop());
  4751. var scrollLeft = self.__leftstart || (self.__leftstart = -self.getScrollLeft());
  4752. var y = userConfig.lockY ? Number(scrollTop) : Number(scrollTop) + (e.deltaY + self.thresholdY);
  4753. var x = userConfig.lockX ? Number(scrollLeft) : Number(scrollLeft) + (e.deltaX + self.thresholdX);
  4754. var containerWidth = self.containerWidth;
  4755. var containerHeight = self.containerHeight;
  4756. if (boundryCheck) {
  4757. //over top
  4758. y = y > boundry.top ? bounce ? (y - boundry.top) * PAN_RATE + boundry.top : boundry.top : y;
  4759. //over bottom
  4760. y = y < boundry.bottom - containerHeight ? bounce ? y + (boundry.bottom - containerHeight - y) * PAN_RATE : boundry.bottom - containerHeight : y;
  4761. //over left
  4762. x = x > boundry.left ? bounce ? (x - boundry.left) * PAN_RATE + boundry.left : boundry.left : x;
  4763. //over right
  4764. x = x < boundry.right - containerWidth ? bounce ? x + (boundry.right - containerWidth - x) * PAN_RATE : boundry.right - containerWidth : x;
  4765. }
  4766. //move to x,y
  4767. self.translate(x, y);
  4768. //pan trigger the opposite direction
  4769. self.directionX = e.type == 'panleft' ? 'right' : e.type == 'panright' ? 'left' : '';
  4770. self.directionY = e.type == 'panup' ? 'down' : e.type == 'pandown' ? 'up' : '';
  4771. self.trigger('scroll', {
  4772. scrollTop: -y,
  4773. scrollLeft: -x,
  4774. triggerType: 'pan',
  4775. type: 'scroll'
  4776. });
  4777. return self;
  4778. },
  4779. _onpanend: function (e) {
  4780. var self = this;
  4781. var userConfig = self.userConfig;
  4782. var transX = self.computeScroll('x', e.velocityX);
  4783. var transY = self.computeScroll('y', e.velocityY);
  4784. var scrollLeft = transX ? transX.pos : 0;
  4785. var scrollTop = transY ? transY.pos : 0;
  4786. var duration;
  4787. if (transX && transY && transX.status == 'inside' && transY.status == 'inside' && transX.duration && transY.duration) {
  4788. //ensure the same duration
  4789. duration = Math.max(transX.duration, transY.duration);
  4790. }
  4791. transX && self.scrollLeft(scrollLeft, duration || transX.duration, transX.easing, function (e) {
  4792. self.boundryCheckX();
  4793. });
  4794. transY && self.scrollTop(scrollTop, duration || transY.duration, transY.easing, function (e) {
  4795. self.boundryCheckY();
  4796. });
  4797. //judge the direction
  4798. self.directionX = e.velocityX < 0 ? 'left' : 'right';
  4799. self.directionY = e.velocityY < 0 ? 'up' : 'down';
  4800. //clear start
  4801. self.__topstart = null;
  4802. self.__leftstart = null;
  4803. return self;
  4804. },
  4805. /**
  4806. * judge the scroller is out of boundry horizontally and vertically
  4807. * @memberof SimuScroll
  4808. * @return {boolean} isBoundryOut
  4809. **/
  4810. isBoundryOut: function () {
  4811. return this.isBoundryOutLeft() || this.isBoundryOutRight() || this.isBoundryOutTop() || this.isBoundryOutBottom();
  4812. },
  4813. /**
  4814. * judge if the scroller is outsideof left
  4815. * @memberof SimuScroll
  4816. * @return {boolean} isBoundryOut
  4817. **/
  4818. isBoundryOutLeft: function () {
  4819. return this.getBoundryOutLeft() > 0 ? true : false;
  4820. },
  4821. /**
  4822. * judge if the scroller is outsideof right
  4823. * @memberof SimuScroll
  4824. * @return {boolean} isBoundryOut
  4825. **/
  4826. isBoundryOutRight: function () {
  4827. return this.getBoundryOutRight() > 0 ? true : false;
  4828. },
  4829. /**
  4830. * judge if the scroller is outsideof top
  4831. * @memberof SimuScroll
  4832. * @return {boolean} isBoundryOut
  4833. **/
  4834. isBoundryOutTop: function () {
  4835. return this.getBoundryOutTop() > 0 ? true : false;
  4836. },
  4837. /**
  4838. * judge if the scroller is outsideof bottom
  4839. * @memberof SimuScroll
  4840. * @return {boolean} isBoundryOut
  4841. **/
  4842. isBoundryOutBottom: function () {
  4843. return this.getBoundryOutBottom() > 0 ? true : false;
  4844. },
  4845. /**
  4846. * get the offset value outsideof top
  4847. * @memberof SimuScroll
  4848. * @return {number} offset
  4849. **/
  4850. getBoundryOutTop: function () {
  4851. return -this.boundry.top - this.getScrollTop();
  4852. },
  4853. /**
  4854. * get the offset value outsideof left
  4855. * @memberof SimuScroll
  4856. * @return {number} offset
  4857. **/
  4858. getBoundryOutLeft: function () {
  4859. return -this.boundry.left - this.getScrollLeft();
  4860. },
  4861. /**
  4862. * get the offset value outsideof bottom
  4863. * @memberof SimuScroll
  4864. * @return {number} offset
  4865. **/
  4866. getBoundryOutBottom: function () {
  4867. return this.boundry.bottom - this.containerHeight + this.getScrollTop();
  4868. },
  4869. /**
  4870. * get the offset value outsideof right
  4871. * @memberof SimuScroll
  4872. * @return {number} offset
  4873. **/
  4874. getBoundryOutRight: function () {
  4875. return this.boundry.right - this.containerWidth + this.getScrollLeft();
  4876. },
  4877. /**
  4878. * compute scroll transition by zoomType and velocity
  4879. * @memberof SimuScroll
  4880. * @param {string} zoomType zoomType of scrolling
  4881. * @param {number} velocity velocity after panend
  4882. * @example
  4883. * var info = xscroll.computeScroll("x",2);
  4884. * // return {pos:90,easing:"easing",status:"inside",duration:500}
  4885. * @return {Object}
  4886. **/
  4887. computeScroll: function (type, v) {
  4888. var self = this;
  4889. var userConfig = self.userConfig;
  4890. var boundry = self.boundry;
  4891. var pos = type == 'x' ? self.getScrollLeft() : self.getScrollTop();
  4892. var boundryStart = type == 'x' ? boundry.left : boundry.top;
  4893. var boundryEnd = type == 'x' ? boundry.right : boundry.bottom;
  4894. var innerSize = type == 'x' ? self.containerWidth : self.containerHeight;
  4895. var maxSpeed = userConfig.maxSpeed || 2;
  4896. var boundryCheck = userConfig.boundryCheck;
  4897. var bounce = userConfig.bounce;
  4898. var transition = {};
  4899. var status = 'inside';
  4900. if (boundryCheck) {
  4901. if (type == 'x' && (self.isBoundryOutLeft() || self.isBoundryOutRight())) {
  4902. self.boundryCheckX();
  4903. return;
  4904. } else if (type == 'y' && (self.isBoundryOutTop() || self.isBoundryOutBottom())) {
  4905. self.boundryCheckY();
  4906. return;
  4907. }
  4908. }
  4909. if (type == 'x' && self.userConfig.lockX)
  4910. return;
  4911. if (type == 'y' && self.userConfig.lockY)
  4912. return;
  4913. v = v > maxSpeed ? maxSpeed : v < -maxSpeed ? -maxSpeed : v;
  4914. var a = self.SCROLL_ACCELERATION * (v / (Math.abs(v) || 1));
  4915. var a2 = self.BOUNDRY_ACCELERATION;
  4916. var t = isNaN(v / a) ? 0 : v / a;
  4917. var s = Number(pos) + t * v / 2;
  4918. //over top boundry check bounce
  4919. if (s < -boundryStart && boundryCheck) {
  4920. var _s = -boundryStart - pos;
  4921. var _t = (Math.sqrt(-2 * a * _s + v * v) + v) / a;
  4922. var v0 = v - a * _t;
  4923. var _t2 = Math.abs(v0 / a2);
  4924. var s2 = v0 / 2 * _t2;
  4925. t = _t + _t2;
  4926. s = bounce ? -boundryStart + s2 : -boundryStart;
  4927. status = 'outside';
  4928. } else if (s > innerSize - boundryEnd && boundryCheck) {
  4929. var _s = boundryEnd - innerSize + pos;
  4930. var _t = (Math.sqrt(-2 * a * _s + v * v) - v) / a;
  4931. var v0 = v - a * _t;
  4932. var _t2 = Math.abs(v0 / a2);
  4933. var s2 = v0 / 2 * _t2;
  4934. t = _t + _t2;
  4935. s = bounce ? innerSize - boundryEnd + s2 : innerSize - boundryEnd;
  4936. status = 'outside';
  4937. }
  4938. if (isNaN(s) || isNaN(t))
  4939. return;
  4940. transition.pos = s;
  4941. transition.duration = t;
  4942. transition.easing = Math.abs(v) > 2 ? 'circular' : 'quadratic';
  4943. transition.status = status;
  4944. var Type = type.toUpperCase();
  4945. self['isScrolling' + Type] = true;
  4946. self['isRealScrolling' + Type] = true;
  4947. return transition;
  4948. },
  4949. /**
  4950. * bounce to the boundry horizontal
  4951. * @memberof SimuScroll
  4952. * @return {SimuScroll}
  4953. **/
  4954. boundryCheckX: function (duration, easing, callback) {
  4955. var self = this;
  4956. if (!self.userConfig.boundryCheck)
  4957. return;
  4958. if (typeof arguments[0] == 'function') {
  4959. callback = arguments[0];
  4960. duration = self.userConfig.BOUNDRY_CHECK_DURATION;
  4961. easing = self.userConfig.BOUNDRY_CHECK_EASING;
  4962. } else {
  4963. duration = duration === 0 ? 0 : self.userConfig.BOUNDRY_CHECK_DURATION, easing = easing || self.userConfig.BOUNDRY_CHECK_EASING;
  4964. }
  4965. if (!self.userConfig.bounce || self.userConfig.lockX)
  4966. return;
  4967. var boundry = self.boundry;
  4968. if (self.isBoundryOutLeft()) {
  4969. self.scrollLeft(-boundry.left, duration, easing, callback);
  4970. } else if (self.isBoundryOutRight()) {
  4971. self.scrollLeft(self.containerWidth - boundry.right, duration, easing, callback);
  4972. }
  4973. return self;
  4974. },
  4975. /**
  4976. * bounce to the boundry vertical
  4977. * @memberof SimuScroll
  4978. * @return {SimuScroll}
  4979. **/
  4980. boundryCheckY: function (duration, easing, callback) {
  4981. var self = this;
  4982. if (!self.userConfig.boundryCheck)
  4983. return;
  4984. if (typeof arguments[0] == 'function') {
  4985. callback = arguments[0];
  4986. duration = self.userConfig.BOUNDRY_CHECK_DURATION;
  4987. easing = self.userConfig.BOUNDRY_CHECK_EASING;
  4988. } else {
  4989. duration = duration === 0 ? 0 : self.userConfig.BOUNDRY_CHECK_DURATION, easing = easing || self.userConfig.BOUNDRY_CHECK_EASING;
  4990. }
  4991. if (!self.userConfig.boundryCheck || self.userConfig.lockY)
  4992. return;
  4993. var boundry = self.boundry;
  4994. if (self.isBoundryOutTop()) {
  4995. self.scrollTop(-boundry.top, duration, easing, callback);
  4996. } else if (self.isBoundryOutBottom()) {
  4997. self.scrollTop(self.containerHeight - boundry.bottom, duration, easing, callback);
  4998. }
  4999. return self;
  5000. },
  5001. /**
  5002. * bounce to the boundry vertical and horizontal
  5003. * @memberof SimuScroll
  5004. * @return {SimuScroll}
  5005. **/
  5006. boundryCheck: function (duration, easing, callback) {
  5007. this.boundryCheckX(duration, easing, callback);
  5008. this.boundryCheckY(duration, easing, callback);
  5009. return this;
  5010. },
  5011. /**
  5012. * stop scrolling immediatelly
  5013. * @memberof SimuScroll
  5014. * @return {SimuScroll}
  5015. **/
  5016. stop: function () {
  5017. var self = this;
  5018. self.__timers.x && self.__timers.x.stop();
  5019. self.__timers.y && self.__timers.y.stop();
  5020. if (self.isScrollingX || self.isScrollingY) {
  5021. var scrollTop = self.getScrollTop(), scrollLeft = self.getScrollLeft();
  5022. self.trigger('scrollend', {
  5023. scrollTop: scrollTop,
  5024. scrollLeft: scrollLeft
  5025. });
  5026. self.trigger('stop', {
  5027. scrollTop: scrollTop,
  5028. scrollLeft: scrollLeft
  5029. });
  5030. self.isScrollingX = false;
  5031. self.isScrollingY = false;
  5032. }
  5033. return self;
  5034. },
  5035. /**
  5036. * render scroll
  5037. * @memberof SimuScroll
  5038. * @return {SimuScroll}
  5039. **/
  5040. render: function () {
  5041. var self = this;
  5042. SimuScroll.superclass.render.call(this);
  5043. //fixed for scrollbars
  5044. if (getComputedStyle(self.renderTo).position == 'static') {
  5045. self.renderTo.style.position = 'relative';
  5046. }
  5047. self.renderTo.style.overflow = 'hidden';
  5048. self.initScrollBars();
  5049. self.initController();
  5050. return self;
  5051. },
  5052. /**
  5053. * init scrollbars
  5054. * @memberof SimuScroll
  5055. * @return {SimuScroll}
  5056. */
  5057. initScrollBars: function () {
  5058. var self = this;
  5059. if (!self.userConfig.boundryCheck)
  5060. return;
  5061. var indicatorInsets = self.userConfig.indicatorInsets;
  5062. if (self.userConfig.scrollbarX) {
  5063. self.scrollbarX = self.scrollbarX || new ScrollBar({
  5064. xscroll: self,
  5065. type: 'x',
  5066. spacing: indicatorInsets.spacing
  5067. });
  5068. self.scrollbarX.render();
  5069. self.scrollbarX._update();
  5070. self.scrollbarX.hide();
  5071. }
  5072. if (self.userConfig.scrollbarY) {
  5073. self.scrollbarY = self.scrollbarY || new ScrollBar({
  5074. xscroll: self,
  5075. type: 'y',
  5076. spacing: indicatorInsets.spacing
  5077. });
  5078. self.scrollbarY.render();
  5079. self.scrollbarY._update();
  5080. self.scrollbarY.hide();
  5081. }
  5082. return self;
  5083. },
  5084. /**
  5085. * destroy scrollbars
  5086. * @memberof SimuScroll
  5087. * @return {SimuScroll}
  5088. */
  5089. destroyScrollBars: function () {
  5090. this.scrollbarX && this.scrollbarX.destroy();
  5091. this.scrollbarY && this.scrollbarY.destroy();
  5092. return this;
  5093. },
  5094. /**
  5095. * init controller for multi-scrollers
  5096. * @memberof SimuScroll
  5097. * @return {SimuScroll}
  5098. */
  5099. initController: function () {
  5100. var self = this;
  5101. self.controller = self.controller || new Controller({ xscroll: self });
  5102. return self;
  5103. },
  5104. _unPreventHref: function (e) {
  5105. var target = Util.findParentEl(e.target, 'a', this.renderTo);
  5106. if (!target)
  5107. return;
  5108. if (target.tagName.toLowerCase() == 'a') {
  5109. var href = target.getAttribute('data-xs-href');
  5110. if (href) {
  5111. target.setAttribute('href', href);
  5112. }
  5113. }
  5114. },
  5115. _preventHref: function (e) {
  5116. var target = Util.findParentEl(e.target, 'a', this.renderTo);
  5117. if (!target)
  5118. return;
  5119. if (target.tagName.toLowerCase() == 'a') {
  5120. var href = target.getAttribute('href');
  5121. href && target.setAttribute('href', 'javascript:void(0)');
  5122. href && target.setAttribute('data-xs-href', href);
  5123. }
  5124. },
  5125. _triggerClick: function (e) {
  5126. var target = e.target;
  5127. if (!/(SELECT|INPUT|TEXTAREA)/i.test(target.tagName)) {
  5128. var ev = document.createEvent('MouseEvents');
  5129. ev.initMouseEvent('click', true, true, e.view, 1, target.screenX, target.screenY, target.clientX, target.clientY, e.ctrlKey, e.altKey, e.shiftKey, e.metaKey, 0, null);
  5130. target.dispatchEvent(ev);
  5131. }
  5132. }
  5133. });
  5134. if (typeof module == 'object' && module.exports) {
  5135. exports = SimuScroll;
  5136. } /** ignored by jsdoc **/ else {
  5137. return SimuScroll;
  5138. }
  5139. return exports;
  5140. }(simulate_scroll);
  5141. origin_scroll = function (exports) {
  5142. var Util = util, Base = base, Core = core, Animate = animate;
  5143. var transformOrigin = Util.prefixStyle('transformOrigin');
  5144. /**
  5145. * @constructor
  5146. * @param {object} cfg config for scroll
  5147. * @extends XScroll
  5148. * @example
  5149. * var xscroll = new OriginScroll({
  5150. * renderTo:"#scroll"
  5151. * });
  5152. * xscroll.render();
  5153. */
  5154. function OriginScroll(cfg) {
  5155. OriginScroll.superclass.constructor.call(this, cfg);
  5156. }
  5157. Util.extend(OriginScroll, Core, {
  5158. init: function () {
  5159. var self = this;
  5160. OriginScroll.superclass.init.call(this);
  5161. self.resetSize();
  5162. },
  5163. /**
  5164. * get scroll top value
  5165. * @memberof OriginScroll
  5166. * @return {number} scrollTop
  5167. */
  5168. getScrollTop: function () {
  5169. return this.renderTo.scrollTop;
  5170. },
  5171. /**
  5172. * get scroll left value
  5173. * @memberof OriginScroll
  5174. * @return {number} scrollLeft
  5175. */
  5176. getScrollLeft: function () {
  5177. return this.renderTo.scrollLeft;
  5178. },
  5179. /**
  5180. * vertical scroll absolute to the destination
  5181. * @memberof SimuScroll
  5182. * @param scrollTop {number} scrollTop
  5183. * @param duration {number} duration for animte
  5184. * @param easing {string} easing functio for animate : ease-in | ease-in-out | ease | bezier(n,n,n,n)
  5185. **/
  5186. scrollTop: function (y, duration, easing, callback) {
  5187. var self = this;
  5188. var y = Math.round(y);
  5189. if (self.userConfig.lockY)
  5190. return;
  5191. var duration = duration || 0;
  5192. var easing = easing || 'quadratic';
  5193. var config = {
  5194. css: { scrollTop: y },
  5195. duration: duration,
  5196. easing: easing,
  5197. run: function (e) {
  5198. //trigger scroll event
  5199. self.trigger('scroll', {
  5200. scrollTop: self.getScrollTop(),
  5201. scrollLeft: self.getScrollLeft()
  5202. });
  5203. },
  5204. useTransition: false,
  5205. //scrollTop
  5206. end: callback
  5207. };
  5208. self.__timers.y = self.__timers.y || new Animate(self.renderTo, config);
  5209. //run
  5210. self.__timers.y.stop();
  5211. self.__timers.y.reset(config);
  5212. self.__timers.y.run();
  5213. },
  5214. /**
  5215. * horizontal scroll absolute to the destination
  5216. * @memberof SimuScroll
  5217. * @param scrollLeft {number} scrollLeft
  5218. * @param duration {number} duration for animte
  5219. * @param easing {string} easing functio for animate : ease-in | ease-in-out | ease | bezier(n,n,n,n)
  5220. **/
  5221. scrollLeft: function (x, duration, easing, callback) {
  5222. var self = this;
  5223. var x = Math.round(x);
  5224. if (self.userConfig.lockX)
  5225. return;
  5226. var duration = duration || 0;
  5227. var easing = easing || 'quadratic';
  5228. var config = {
  5229. css: { scrollLeft: x },
  5230. duration: duration,
  5231. easing: easing,
  5232. run: function (e) {
  5233. //trigger scroll event
  5234. self.trigger('scroll', {
  5235. scrollTop: self.getScrollTop(),
  5236. scrollLeft: self.getScrollLeft()
  5237. });
  5238. },
  5239. useTransition: false,
  5240. //scrollTop
  5241. end: callback
  5242. };
  5243. self.__timers.x = self.__timers.x || new Animate(self.renderTo, config);
  5244. //run
  5245. self.__timers.x.stop();
  5246. self.__timers.x.reset(config);
  5247. self.__timers.x.run();
  5248. },
  5249. _bindEvt: function () {
  5250. OriginScroll.superclass._bindEvt.call(this);
  5251. var self = this;
  5252. if (self.__isEvtBind)
  5253. return;
  5254. self.__isEvtBind = true;
  5255. self.renderTo.addEventListener('scroll', function (e) {
  5256. self.trigger('scroll', {
  5257. type: 'scroll',
  5258. scrollTop: self.getScrollTop(),
  5259. scrollLeft: self.getScrollLeft()
  5260. });
  5261. }, false);
  5262. }
  5263. });
  5264. if (typeof module == 'object' && module.exports) {
  5265. exports = OriginScroll;
  5266. } /** ignored by jsdoc **/ else {
  5267. return OriginScroll;
  5268. }
  5269. return exports;
  5270. }(origin_scroll);
  5271. xscroll = function (exports) {
  5272. var Util = util, Base = base, Timer = timer, Animate = animate, Hammer = hammer, SimuScroll = simulate_scroll, OriginScroll = origin_scroll;
  5273. var XScroll = function (cfg) {
  5274. var _ = cfg && cfg.useOriginScroll ? OriginScroll : SimuScroll;
  5275. return new _(cfg);
  5276. };
  5277. /**
  5278. * Util
  5279. * @namespace Util
  5280. * @type {Object}
  5281. */
  5282. XScroll.Util = Util;
  5283. /**
  5284. * Base
  5285. * @namespace Base
  5286. * @type {Base}
  5287. */
  5288. XScroll.Base = Base;
  5289. /**
  5290. * Timer
  5291. * @namespace Timer
  5292. * @type {Timer}
  5293. */
  5294. XScroll.Timer = Timer;
  5295. /**
  5296. * Animate
  5297. * @namespace Animate
  5298. * @type {Animate}
  5299. */
  5300. XScroll.Animate = Animate;
  5301. /**
  5302. * Hammer
  5303. * @namespace Hammer
  5304. * @type {Hammer}
  5305. */
  5306. XScroll.Hammer = Hammer;
  5307. /**
  5308. * plugins
  5309. * @namespace Plugins
  5310. * @type {Object}
  5311. */
  5312. XScroll.Plugins = {};
  5313. if (typeof module == 'object' && module.exports) {
  5314. exports = XScroll;
  5315. } /** ignored by jsdoc **/ else {
  5316. return window.XScroll = XScroll;
  5317. }
  5318. return exports;
  5319. }(xscroll);
  5320. xscroll_master = function (exports) {
  5321. var Util = util, Base = base, XScroll = xscroll;
  5322. /**
  5323. * A master for multi-scrollers.
  5324. * @constructor
  5325. * @param {object} cfg - config for master.
  5326. * @extends Base
  5327. */
  5328. var XScrollMaster = function (cfg) {
  5329. XScrollMaster.superclass.constructor.call(this, cfg);
  5330. this.init(cfg);
  5331. };
  5332. Util.extend(XScrollMaster, Base, {
  5333. /**
  5334. * init the master
  5335. * @memberof XScrollMaster
  5336. * @param {object} cfg config for master
  5337. * @param {string} selector xscroll root elements,it will be set to xscroll.renderTo
  5338. * @return {XScrollMaster}
  5339. */
  5340. init: function (cfg) {
  5341. var self = this;
  5342. self.userConfig = Util.mix({ selector: '.xscroll' }, cfg);
  5343. return self;
  5344. },
  5345. /**
  5346. * find xscroll instance
  5347. * @memberof XScrollMaster
  5348. * @param {string} id element id for xscroll instance
  5349. * @return {XScroll} xscroll instance
  5350. */
  5351. get: function (id) {
  5352. var self = this;
  5353. if (!id)
  5354. return;
  5355. for (var i = 0, l = self.xscrolls.length; i < l; i++) {
  5356. if (self.xscrolls[i].renderTo.id === id) {
  5357. return self.xscrolls[i];
  5358. }
  5359. }
  5360. return;
  5361. },
  5362. getElPos: function () {
  5363. var self = this;
  5364. var elpos = [];
  5365. var els = document.querySelectorAll(self.userConfig.selector);
  5366. for (var i = 0; i < els.length; i++) {
  5367. var content = els[i].querySelector('.xs-content');
  5368. elpos.push({
  5369. el: els[i],
  5370. containerWidth: content.offsetWidth,
  5371. containerHeight: content.offsetHeight,
  5372. width: els[i].offsetWidth,
  5373. height: els[i].offsetHeight
  5374. });
  5375. }
  5376. return elpos;
  5377. },
  5378. /**
  5379. * render xscrolls
  5380. * @memberof XScrollMaster
  5381. * @return {XScrollMaster}
  5382. */
  5383. render: function () {
  5384. var self = this;
  5385. var findByEl = function (el, xscrolls) {
  5386. if (!el || !xscrolls)
  5387. return;
  5388. for (var i = 0, l = xscrolls.length; i < l; i++) {
  5389. if (xscrolls[i].renderTo === el) {
  5390. return xscrolls[i];
  5391. }
  5392. }
  5393. };
  5394. var els = document.querySelectorAll(self.userConfig.selector);
  5395. var elpos = self.getElPos();
  5396. for (var i in self.xscrolls) {
  5397. //destroy
  5398. self.xscrolls[i].destroy();
  5399. }
  5400. self.xscrolls = [];
  5401. for (var i = 0; i < els.length; i++) {
  5402. self.xscrolls.push(new XScroll({
  5403. renderTo: els[i],
  5404. containerWidth: elpos[i].containerWidth,
  5405. containerHeight: elpos[i].containerHeight,
  5406. width: elpos[i].width,
  5407. height: elpos[i].height
  5408. }).render());
  5409. }
  5410. for (var i = 0, l = self.xscrolls.length; i < l; i++) {
  5411. var innerEls = self.xscrolls[i].renderTo.querySelectorAll(self.userConfig.selector);
  5412. for (var j = 0; j < innerEls.length; j++) {
  5413. var xscroll = findByEl(innerEls[j], self.xscrolls);
  5414. if (xscroll && self.xscrolls[i].controller) {
  5415. self.xscrolls[i].controller.add(xscroll);
  5416. }
  5417. }
  5418. }
  5419. self._bindEvt();
  5420. return self;
  5421. },
  5422. _bindEvt: function () {
  5423. var self = this;
  5424. window.addEventListener('resize', function (e) {
  5425. setTimeout(function () {
  5426. var elpos = self.getElPos();
  5427. for (var i = 0, l = self.xscrolls.length; i < l; i++) {
  5428. var xscroll = self.xscrolls[i];
  5429. xscroll.userConfig.containerWidth = elpos[i].containerWidth;
  5430. xscroll.userConfig.containerHeight = elpos[i].containerHeight;
  5431. xscroll.userConfig.width = elpos[i].width;
  5432. xscroll.userConfig.height = elpos[i].height;
  5433. xscroll.boundry.refresh({
  5434. width: xscroll.userConfig.width,
  5435. height: xscroll.userConfig.height
  5436. });
  5437. xscroll.boundryCheck(0);
  5438. xscroll.render();
  5439. }
  5440. }, 0);
  5441. }, self);
  5442. }
  5443. });
  5444. if (typeof module == 'object' && module.exports) {
  5445. exports = XScrollMaster;
  5446. } /** ignored by jsdoc **/ else {
  5447. return window.XScrollMaster = XScrollMaster;
  5448. }
  5449. return exports;
  5450. }(xscroll_master);
  5451. }());