jquery-weui.js 196 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366336733683369337033713372337333743375337633773378337933803381338233833384338533863387338833893390339133923393339433953396339733983399340034013402340334043405340634073408340934103411341234133414341534163417341834193420342134223423342434253426342734283429343034313432343334343435343634373438343934403441344234433444344534463447344834493450345134523453345434553456345734583459346034613462346334643465346634673468346934703471347234733474347534763477347834793480348134823483348434853486348734883489349034913492349334943495349634973498349935003501350235033504350535063507350835093510351135123513351435153516351735183519352035213522352335243525352635273528352935303531353235333534353535363537353835393540354135423543354435453546354735483549355035513552355335543555355635573558355935603561356235633564356535663567356835693570357135723573357435753576357735783579358035813582358335843585358635873588358935903591359235933594359535963597359835993600360136023603360436053606360736083609361036113612361336143615361636173618361936203621362236233624362536263627362836293630363136323633363436353636363736383639364036413642364336443645364636473648364936503651365236533654365536563657365836593660366136623663366436653666366736683669367036713672367336743675367636773678367936803681368236833684368536863687368836893690369136923693369436953696369736983699370037013702370337043705370637073708370937103711371237133714371537163717371837193720372137223723372437253726372737283729373037313732373337343735373637373738373937403741374237433744374537463747374837493750375137523753375437553756375737583759376037613762376337643765376637673768376937703771377237733774377537763777377837793780378137823783378437853786378737883789379037913792379337943795379637973798379938003801380238033804380538063807380838093810381138123813381438153816381738183819382038213822382338243825382638273828382938303831383238333834383538363837383838393840384138423843384438453846384738483849385038513852385338543855385638573858385938603861386238633864386538663867386838693870387138723873387438753876387738783879388038813882388338843885388638873888388938903891389238933894389538963897389838993900390139023903390439053906390739083909391039113912391339143915391639173918391939203921392239233924392539263927392839293930393139323933393439353936393739383939394039413942394339443945394639473948394939503951395239533954395539563957395839593960396139623963396439653966396739683969397039713972397339743975397639773978397939803981398239833984398539863987398839893990399139923993399439953996399739983999400040014002400340044005400640074008400940104011401240134014401540164017401840194020402140224023402440254026402740284029403040314032403340344035403640374038403940404041404240434044404540464047404840494050405140524053405440554056405740584059406040614062406340644065406640674068406940704071407240734074407540764077407840794080408140824083408440854086408740884089409040914092409340944095409640974098409941004101410241034104410541064107410841094110411141124113411441154116411741184119412041214122412341244125412641274128412941304131413241334134413541364137413841394140414141424143414441454146414741484149415041514152415341544155415641574158415941604161416241634164416541664167416841694170417141724173417441754176417741784179418041814182418341844185418641874188418941904191419241934194419541964197419841994200420142024203420442054206420742084209421042114212421342144215421642174218421942204221422242234224422542264227422842294230423142324233423442354236423742384239424042414242424342444245424642474248424942504251425242534254425542564257425842594260426142624263426442654266426742684269427042714272427342744275427642774278427942804281428242834284428542864287428842894290429142924293429442954296429742984299430043014302430343044305430643074308430943104311431243134314431543164317431843194320432143224323432443254326432743284329433043314332433343344335433643374338433943404341434243434344434543464347434843494350435143524353435443554356435743584359436043614362436343644365436643674368436943704371437243734374437543764377437843794380438143824383438443854386438743884389439043914392439343944395439643974398439944004401440244034404440544064407440844094410441144124413441444154416441744184419442044214422442344244425442644274428442944304431443244334434443544364437443844394440444144424443444444454446444744484449445044514452445344544455445644574458445944604461446244634464446544664467446844694470447144724473447444754476447744784479448044814482448344844485448644874488448944904491449244934494449544964497449844994500450145024503450445054506450745084509451045114512451345144515451645174518451945204521452245234524452545264527452845294530453145324533453445354536453745384539454045414542454345444545454645474548454945504551455245534554455545564557455845594560456145624563456445654566456745684569457045714572457345744575457645774578457945804581458245834584458545864587458845894590459145924593459445954596459745984599460046014602460346044605460646074608460946104611461246134614461546164617461846194620462146224623462446254626462746284629463046314632463346344635463646374638463946404641464246434644464546464647464846494650465146524653465446554656465746584659466046614662466346644665466646674668466946704671467246734674467546764677467846794680468146824683468446854686468746884689469046914692469346944695469646974698469947004701470247034704470547064707470847094710471147124713471447154716471747184719472047214722472347244725472647274728472947304731473247334734473547364737473847394740474147424743474447454746474747484749475047514752475347544755475647574758475947604761476247634764476547664767476847694770477147724773477447754776477747784779478047814782478347844785478647874788478947904791479247934794479547964797479847994800480148024803480448054806480748084809481048114812481348144815481648174818481948204821482248234824482548264827482848294830483148324833483448354836483748384839484048414842484348444845484648474848484948504851485248534854485548564857485848594860486148624863486448654866486748684869487048714872487348744875487648774878487948804881488248834884488548864887488848894890489148924893489448954896489748984899490049014902490349044905490649074908490949104911491249134914491549164917491849194920492149224923492449254926492749284929493049314932493349344935493649374938493949404941494249434944494549464947494849494950495149524953495449554956495749584959496049614962496349644965496649674968496949704971497249734974497549764977497849794980498149824983498449854986498749884989499049914992499349944995499649974998499950005001500250035004500550065007500850095010501150125013501450155016501750185019502050215022502350245025502650275028502950305031503250335034503550365037503850395040504150425043504450455046504750485049505050515052505350545055505650575058505950605061506250635064506550665067506850695070507150725073507450755076507750785079508050815082508350845085508650875088508950905091509250935094509550965097509850995100510151025103510451055106510751085109511051115112511351145115511651175118511951205121512251235124512551265127512851295130513151325133513451355136513751385139514051415142514351445145514651475148514951505151515251535154515551565157515851595160516151625163516451655166516751685169517051715172517351745175517651775178517951805181518251835184518551865187518851895190519151925193519451955196519751985199520052015202520352045205520652075208520952105211521252135214521552165217521852195220522152225223522452255226522752285229523052315232523352345235523652375238523952405241524252435244524552465247524852495250525152525253525452555256525752585259526052615262526352645265526652675268526952705271527252735274527552765277527852795280528152825283528452855286528752885289529052915292529352945295529652975298529953005301530253035304530553065307530853095310531153125313531453155316531753185319532053215322532353245325532653275328532953305331533253335334533553365337533853395340534153425343534453455346534753485349535053515352535353545355535653575358535953605361536253635364536553665367536853695370537153725373537453755376537753785379538053815382538353845385538653875388538953905391539253935394539553965397539853995400540154025403540454055406540754085409541054115412541354145415541654175418541954205421542254235424542554265427542854295430543154325433543454355436543754385439544054415442544354445445544654475448544954505451545254535454545554565457545854595460546154625463546454655466546754685469547054715472547354745475547654775478547954805481548254835484548554865487548854895490549154925493549454955496549754985499550055015502550355045505550655075508550955105511551255135514551555165517551855195520552155225523552455255526552755285529553055315532553355345535553655375538553955405541554255435544554555465547554855495550555155525553555455555556555755585559556055615562556355645565556655675568556955705571557255735574557555765577557855795580558155825583558455855586558755885589559055915592559355945595559655975598559956005601560256035604560556065607560856095610561156125613561456155616561756185619562056215622562356245625562656275628562956305631563256335634563556365637563856395640564156425643564456455646564756485649565056515652565356545655565656575658565956605661566256635664566556665667566856695670567156725673567456755676567756785679568056815682568356845685568656875688568956905691569256935694569556965697569856995700570157025703570457055706570757085709571057115712571357145715571657175718571957205721572257235724572557265727572857295730573157325733573457355736573757385739574057415742574357445745574657475748574957505751575257535754575557565757575857595760576157625763576457655766576757685769577057715772577357745775577657775778577957805781578257835784578557865787578857895790579157925793579457955796579757985799580058015802580358045805580658075808580958105811581258135814581558165817581858195820582158225823582458255826582758285829583058315832583358345835583658375838583958405841584258435844584558465847584858495850585158525853585458555856585758585859586058615862586358645865586658675868586958705871587258735874587558765877587858795880588158825883588458855886588758885889589058915892589358945895589658975898589959005901590259035904590559065907590859095910591159125913591459155916591759185919592059215922592359245925592659275928592959305931593259335934593559365937593859395940594159425943594459455946594759485949595059515952595359545955595659575958595959605961596259635964596559665967596859695970597159725973597459755976597759785979598059815982598359845985598659875988598959905991599259935994599559965997599859996000600160026003600460056006600760086009601060116012601360146015601660176018601960206021602260236024602560266027602860296030603160326033603460356036603760386039604060416042604360446045604660476048604960506051605260536054605560566057605860596060606160626063606460656066606760686069607060716072607360746075607660776078607960806081608260836084608560866087608860896090609160926093609460956096609760986099610061016102610361046105610661076108610961106111611261136114611561166117611861196120612161226123612461256126612761286129613061316132613361346135613661376138613961406141614261436144614561466147614861496150615161526153615461556156615761586159616061616162616361646165616661676168616961706171617261736174617561766177617861796180618161826183618461856186618761886189619061916192619361946195619661976198619962006201620262036204620562066207620862096210621162126213621462156216621762186219622062216222622362246225622662276228622962306231623262336234623562366237623862396240624162426243624462456246624762486249625062516252625362546255625662576258625962606261626262636264626562666267626862696270627162726273627462756276
  1. /**
  2. * jQuery WeUI V1.0.1
  3. * By 言川
  4. * http://lihongxun945.github.io/jquery-weui/
  5. */
  6. /* global $:true */
  7. /* global WebKitCSSMatrix:true */
  8. (function($) {
  9. "use strict";
  10. $.fn.transitionEnd = function(callback) {
  11. var events = ['webkitTransitionEnd', 'transitionend', 'oTransitionEnd', 'MSTransitionEnd', 'msTransitionEnd'],
  12. i, dom = this;
  13. function fireCallBack(e) {
  14. /*jshint validthis:true */
  15. if (e.target !== this) return;
  16. callback.call(this, e);
  17. for (i = 0; i < events.length; i++) {
  18. dom.off(events[i], fireCallBack);
  19. }
  20. }
  21. if (callback) {
  22. for (i = 0; i < events.length; i++) {
  23. dom.on(events[i], fireCallBack);
  24. }
  25. }
  26. return this;
  27. };
  28. $.support = (function() {
  29. var support = {
  30. touch: !!(('ontouchstart' in window) || window.DocumentTouch && document instanceof window.DocumentTouch)
  31. };
  32. return support;
  33. })();
  34. $.touchEvents = {
  35. start: $.support.touch ? 'touchstart' : 'mousedown',
  36. move: $.support.touch ? 'touchmove' : 'mousemove',
  37. end: $.support.touch ? 'touchend' : 'mouseup'
  38. };
  39. $.getTouchPosition = function(e) {
  40. e = e.originalEvent || e; //jquery wrap the originevent
  41. if(e.type === 'touchstart' || e.type === 'touchmove' || e.type === 'touchend') {
  42. return {
  43. x: e.targetTouches[0].pageX,
  44. y: e.targetTouches[0].pageY
  45. };
  46. } else {
  47. return {
  48. x: e.pageX,
  49. y: e.pageY
  50. };
  51. }
  52. };
  53. $.fn.scrollHeight = function() {
  54. return this[0].scrollHeight;
  55. };
  56. $.fn.transform = function(transform) {
  57. for (var i = 0; i < this.length; i++) {
  58. var elStyle = this[i].style;
  59. elStyle.webkitTransform = elStyle.MsTransform = elStyle.msTransform = elStyle.MozTransform = elStyle.OTransform = elStyle.transform = transform;
  60. }
  61. return this;
  62. };
  63. $.fn.transition = function(duration) {
  64. if (typeof duration !== 'string') {
  65. duration = duration + 'ms';
  66. }
  67. for (var i = 0; i < this.length; i++) {
  68. var elStyle = this[i].style;
  69. elStyle.webkitTransitionDuration = elStyle.MsTransitionDuration = elStyle.msTransitionDuration = elStyle.MozTransitionDuration = elStyle.OTransitionDuration = elStyle.transitionDuration = duration;
  70. }
  71. return this;
  72. };
  73. $.getTranslate = function (el, axis) {
  74. var matrix, curTransform, curStyle, transformMatrix;
  75. // automatic axis detection
  76. if (typeof axis === 'undefined') {
  77. axis = 'x';
  78. }
  79. curStyle = window.getComputedStyle(el, null);
  80. if (window.WebKitCSSMatrix) {
  81. // Some old versions of Webkit choke when 'none' is passed; pass
  82. // empty string instead in this case
  83. transformMatrix = new WebKitCSSMatrix(curStyle.webkitTransform === 'none' ? '' : curStyle.webkitTransform);
  84. }
  85. else {
  86. transformMatrix = curStyle.MozTransform || curStyle.OTransform || curStyle.MsTransform || curStyle.msTransform || curStyle.transform || curStyle.getPropertyValue('transform').replace('translate(', 'matrix(1, 0, 0, 1,');
  87. matrix = transformMatrix.toString().split(',');
  88. }
  89. if (axis === 'x') {
  90. //Latest Chrome and webkits Fix
  91. if (window.WebKitCSSMatrix)
  92. curTransform = transformMatrix.m41;
  93. //Crazy IE10 Matrix
  94. else if (matrix.length === 16)
  95. curTransform = parseFloat(matrix[12]);
  96. //Normal Browsers
  97. else
  98. curTransform = parseFloat(matrix[4]);
  99. }
  100. if (axis === 'y') {
  101. //Latest Chrome and webkits Fix
  102. if (window.WebKitCSSMatrix)
  103. curTransform = transformMatrix.m42;
  104. //Crazy IE10 Matrix
  105. else if (matrix.length === 16)
  106. curTransform = parseFloat(matrix[13]);
  107. //Normal Browsers
  108. else
  109. curTransform = parseFloat(matrix[5]);
  110. }
  111. return curTransform || 0;
  112. };
  113. $.requestAnimationFrame = function (callback) {
  114. if (window.requestAnimationFrame) return window.requestAnimationFrame(callback);
  115. else if (window.webkitRequestAnimationFrame) return window.webkitRequestAnimationFrame(callback);
  116. else if (window.mozRequestAnimationFrame) return window.mozRequestAnimationFrame(callback);
  117. else {
  118. return window.setTimeout(callback, 1000 / 60);
  119. }
  120. };
  121. $.cancelAnimationFrame = function (id) {
  122. if (window.cancelAnimationFrame) return window.cancelAnimationFrame(id);
  123. else if (window.webkitCancelAnimationFrame) return window.webkitCancelAnimationFrame(id);
  124. else if (window.mozCancelAnimationFrame) return window.mozCancelAnimationFrame(id);
  125. else {
  126. return window.clearTimeout(id);
  127. }
  128. };
  129. $.fn.join = function(arg) {
  130. return this.toArray().join(arg);
  131. }
  132. })($);
  133. /*===========================
  134. Template7 Template engine
  135. ===========================*/
  136. /* global $:true */
  137. /* jshint unused:false */
  138. /* jshint forin:false */
  139. +function ($) {
  140. "use strict";
  141. $.Template7 = $.t7 = (function () {
  142. function isArray(arr) {
  143. return Object.prototype.toString.apply(arr) === '[object Array]';
  144. }
  145. function isObject(obj) {
  146. return obj instanceof Object;
  147. }
  148. function isFunction(func) {
  149. return typeof func === 'function';
  150. }
  151. var cache = {};
  152. function helperToSlices(string) {
  153. var helperParts = string.replace(/[{}#}]/g, '').split(' ');
  154. var slices = [];
  155. var shiftIndex, i, j;
  156. for (i = 0; i < helperParts.length; i++) {
  157. var part = helperParts[i];
  158. if (i === 0) slices.push(part);
  159. else {
  160. if (part.indexOf('"') === 0) {
  161. // Plain String
  162. if (part.match(/"/g).length === 2) {
  163. // One word string
  164. slices.push(part);
  165. }
  166. else {
  167. // Find closed Index
  168. shiftIndex = 0;
  169. for (j = i + 1; j < helperParts.length; j++) {
  170. part += ' ' + helperParts[j];
  171. if (helperParts[j].indexOf('"') >= 0) {
  172. shiftIndex = j;
  173. slices.push(part);
  174. break;
  175. }
  176. }
  177. if (shiftIndex) i = shiftIndex;
  178. }
  179. }
  180. else {
  181. if (part.indexOf('=') > 0) {
  182. // Hash
  183. var hashParts = part.split('=');
  184. var hashName = hashParts[0];
  185. var hashContent = hashParts[1];
  186. if (hashContent.match(/"/g).length !== 2) {
  187. shiftIndex = 0;
  188. for (j = i + 1; j < helperParts.length; j++) {
  189. hashContent += ' ' + helperParts[j];
  190. if (helperParts[j].indexOf('"') >= 0) {
  191. shiftIndex = j;
  192. break;
  193. }
  194. }
  195. if (shiftIndex) i = shiftIndex;
  196. }
  197. var hash = [hashName, hashContent.replace(/"/g,'')];
  198. slices.push(hash);
  199. }
  200. else {
  201. // Plain variable
  202. slices.push(part);
  203. }
  204. }
  205. }
  206. }
  207. return slices;
  208. }
  209. function stringToBlocks(string) {
  210. var blocks = [], i, j, k;
  211. if (!string) return [];
  212. var _blocks = string.split(/({{[^{^}]*}})/);
  213. for (i = 0; i < _blocks.length; i++) {
  214. var block = _blocks[i];
  215. if (block === '') continue;
  216. if (block.indexOf('{{') < 0) {
  217. blocks.push({
  218. type: 'plain',
  219. content: block
  220. });
  221. }
  222. else {
  223. if (block.indexOf('{/') >= 0) {
  224. continue;
  225. }
  226. if (block.indexOf('{#') < 0 && block.indexOf(' ') < 0 && block.indexOf('else') < 0) {
  227. // Simple variable
  228. blocks.push({
  229. type: 'variable',
  230. contextName: block.replace(/[{}]/g, '')
  231. });
  232. continue;
  233. }
  234. // Helpers
  235. var helperSlices = helperToSlices(block);
  236. var helperName = helperSlices[0];
  237. var helperContext = [];
  238. var helperHash = {};
  239. for (j = 1; j < helperSlices.length; j++) {
  240. var slice = helperSlices[j];
  241. if (isArray(slice)) {
  242. // Hash
  243. helperHash[slice[0]] = slice[1] === 'false' ? false : slice[1];
  244. }
  245. else {
  246. helperContext.push(slice);
  247. }
  248. }
  249. if (block.indexOf('{#') >= 0) {
  250. // Condition/Helper
  251. var helperStartIndex = i;
  252. var helperContent = '';
  253. var elseContent = '';
  254. var toSkip = 0;
  255. var shiftIndex;
  256. var foundClosed = false, foundElse = false, foundClosedElse = false, depth = 0;
  257. for (j = i + 1; j < _blocks.length; j++) {
  258. if (_blocks[j].indexOf('{{#') >= 0) {
  259. depth ++;
  260. }
  261. if (_blocks[j].indexOf('{{/') >= 0) {
  262. depth --;
  263. }
  264. if (_blocks[j].indexOf('{{#' + helperName) >= 0) {
  265. helperContent += _blocks[j];
  266. if (foundElse) elseContent += _blocks[j];
  267. toSkip ++;
  268. }
  269. else if (_blocks[j].indexOf('{{/' + helperName) >= 0) {
  270. if (toSkip > 0) {
  271. toSkip--;
  272. helperContent += _blocks[j];
  273. if (foundElse) elseContent += _blocks[j];
  274. }
  275. else {
  276. shiftIndex = j;
  277. foundClosed = true;
  278. break;
  279. }
  280. }
  281. else if (_blocks[j].indexOf('else') >= 0 && depth === 0) {
  282. foundElse = true;
  283. }
  284. else {
  285. if (!foundElse) helperContent += _blocks[j];
  286. if (foundElse) elseContent += _blocks[j];
  287. }
  288. }
  289. if (foundClosed) {
  290. if (shiftIndex) i = shiftIndex;
  291. blocks.push({
  292. type: 'helper',
  293. helperName: helperName,
  294. contextName: helperContext,
  295. content: helperContent,
  296. inverseContent: elseContent,
  297. hash: helperHash
  298. });
  299. }
  300. }
  301. else if (block.indexOf(' ') > 0) {
  302. blocks.push({
  303. type: 'helper',
  304. helperName: helperName,
  305. contextName: helperContext,
  306. hash: helperHash
  307. });
  308. }
  309. }
  310. }
  311. return blocks;
  312. }
  313. var Template7 = function (template) {
  314. var t = this;
  315. t.template = template;
  316. function getCompileFn(block, depth) {
  317. if (block.content) return compile(block.content, depth);
  318. else return function () {return ''; };
  319. }
  320. function getCompileInverse(block, depth) {
  321. if (block.inverseContent) return compile(block.inverseContent, depth);
  322. else return function () {return ''; };
  323. }
  324. function getCompileVar(name, ctx) {
  325. var variable, parts, levelsUp = 0, initialCtx = ctx;
  326. if (name.indexOf('../') === 0) {
  327. levelsUp = name.split('../').length - 1;
  328. var newDepth = ctx.split('_')[1] - levelsUp;
  329. ctx = 'ctx_' + (newDepth >= 1 ? newDepth : 1);
  330. parts = name.split('../')[levelsUp].split('.');
  331. }
  332. else if (name.indexOf('@global') === 0) {
  333. ctx = '$.Template7.global';
  334. parts = name.split('@global.')[1].split('.');
  335. }
  336. else if (name.indexOf('@root') === 0) {
  337. ctx = 'ctx_1';
  338. parts = name.split('@root.')[1].split('.');
  339. }
  340. else {
  341. parts = name.split('.');
  342. }
  343. variable = ctx;
  344. for (var i = 0; i < parts.length; i++) {
  345. var part = parts[i];
  346. if (part.indexOf('@') === 0) {
  347. if (i > 0) {
  348. variable += '[(data && data.' + part.replace('@', '') + ')]';
  349. }
  350. else {
  351. variable = '(data && data.' + name.replace('@', '') + ')';
  352. }
  353. }
  354. else {
  355. if (isFinite(part)) {
  356. variable += '[' + part + ']';
  357. }
  358. else {
  359. if (part.indexOf('this') === 0) {
  360. variable = part.replace('this', ctx);
  361. }
  362. else {
  363. variable += '.' + part;
  364. }
  365. }
  366. }
  367. }
  368. return variable;
  369. }
  370. function getCompiledArguments(contextArray, ctx) {
  371. var arr = [];
  372. for (var i = 0; i < contextArray.length; i++) {
  373. if (contextArray[i].indexOf('"') === 0) arr.push(contextArray[i]);
  374. else {
  375. arr.push(getCompileVar(contextArray[i], ctx));
  376. }
  377. }
  378. return arr.join(', ');
  379. }
  380. function compile(template, depth) {
  381. depth = depth || 1;
  382. template = template || t.template;
  383. if (typeof template !== 'string') {
  384. throw new Error('Template7: Template must be a string');
  385. }
  386. var blocks = stringToBlocks(template);
  387. if (blocks.length === 0) {
  388. return function () { return ''; };
  389. }
  390. var ctx = 'ctx_' + depth;
  391. var resultString = '(function (' + ctx + ', data) {\n';
  392. if (depth === 1) {
  393. resultString += 'function isArray(arr){return Object.prototype.toString.apply(arr) === \'[object Array]\';}\n';
  394. resultString += 'function isFunction(func){return (typeof func === \'function\');}\n';
  395. resultString += 'function c(val, ctx) {if (typeof val !== "undefined") {if (isFunction(val)) {return val.call(ctx);} else return val;} else return "";}\n';
  396. }
  397. resultString += 'var r = \'\';\n';
  398. var i, j, context;
  399. for (i = 0; i < blocks.length; i++) {
  400. var block = blocks[i];
  401. // Plain block
  402. if (block.type === 'plain') {
  403. resultString += 'r +=\'' + (block.content).replace(/\r/g, '\\r').replace(/\n/g, '\\n').replace(/'/g, '\\' + '\'') + '\';';
  404. continue;
  405. }
  406. var variable, compiledArguments;
  407. // Variable block
  408. if (block.type === 'variable') {
  409. variable = getCompileVar(block.contextName, ctx);
  410. resultString += 'r += c(' + variable + ', ' + ctx + ');';
  411. }
  412. // Helpers block
  413. if (block.type === 'helper') {
  414. if (block.helperName in t.helpers) {
  415. compiledArguments = getCompiledArguments(block.contextName, ctx);
  416. resultString += 'r += ($.Template7.helpers.' + block.helperName + ').call(' + ctx + ', ' + (compiledArguments && (compiledArguments + ', ')) +'{hash:' + JSON.stringify(block.hash) + ', data: data || {}, fn: ' + getCompileFn(block, depth+1) + ', inverse: ' + getCompileInverse(block, depth+1) + ', root: ctx_1});';
  417. }
  418. else {
  419. if (block.contextName.length > 0) {
  420. throw new Error('Template7: Missing helper: "' + block.helperName + '"');
  421. }
  422. else {
  423. variable = getCompileVar(block.helperName, ctx);
  424. resultString += 'if (' + variable + ') {';
  425. resultString += 'if (isArray(' + variable + ')) {';
  426. resultString += 'r += ($.Template7.helpers.each).call(' + ctx + ', ' + variable + ', {hash:' + JSON.stringify(block.hash) + ', data: data || {}, fn: ' + getCompileFn(block, depth+1) + ', inverse: ' + getCompileInverse(block, depth+1) + ', root: ctx_1});';
  427. resultString += '}else {';
  428. resultString += 'r += ($.Template7.helpers.with).call(' + ctx + ', ' + variable + ', {hash:' + JSON.stringify(block.hash) + ', data: data || {}, fn: ' + getCompileFn(block, depth+1) + ', inverse: ' + getCompileInverse(block, depth+1) + ', root: ctx_1});';
  429. resultString += '}}';
  430. }
  431. }
  432. }
  433. }
  434. resultString += '\nreturn r;})';
  435. return eval.call(window, resultString);
  436. }
  437. t.compile = function (template) {
  438. if (!t.compiled) {
  439. t.compiled = compile(template);
  440. }
  441. return t.compiled;
  442. };
  443. };
  444. Template7.prototype = {
  445. options: {},
  446. helpers: {
  447. 'if': function (context, options) {
  448. if (isFunction(context)) { context = context.call(this); }
  449. if (context) {
  450. return options.fn(this, options.data);
  451. }
  452. else {
  453. return options.inverse(this, options.data);
  454. }
  455. },
  456. 'unless': function (context, options) {
  457. if (isFunction(context)) { context = context.call(this); }
  458. if (!context) {
  459. return options.fn(this, options.data);
  460. }
  461. else {
  462. return options.inverse(this, options.data);
  463. }
  464. },
  465. 'each': function (context, options) {
  466. var ret = '', i = 0;
  467. if (isFunction(context)) { context = context.call(this); }
  468. if (isArray(context)) {
  469. if (options.hash.reverse) {
  470. context = context.reverse();
  471. }
  472. for (i = 0; i < context.length; i++) {
  473. ret += options.fn(context[i], {first: i === 0, last: i === context.length - 1, index: i});
  474. }
  475. if (options.hash.reverse) {
  476. context = context.reverse();
  477. }
  478. }
  479. else {
  480. for (var key in context) {
  481. i++;
  482. ret += options.fn(context[key], {key: key});
  483. }
  484. }
  485. if (i > 0) return ret;
  486. else return options.inverse(this);
  487. },
  488. 'with': function (context, options) {
  489. if (isFunction(context)) { context = context.call(this); }
  490. return options.fn(context);
  491. },
  492. 'join': function (context, options) {
  493. if (isFunction(context)) { context = context.call(this); }
  494. return context.join(options.hash.delimiter || options.hash.delimeter);
  495. },
  496. 'js': function (expression, options) {
  497. var func;
  498. if (expression.indexOf('return')>=0) {
  499. func = '(function(){'+expression+'})';
  500. }
  501. else {
  502. func = '(function(){return ('+expression+')})';
  503. }
  504. return eval.call(this, func).call(this);
  505. },
  506. 'js_compare': function (expression, options) {
  507. var func;
  508. if (expression.indexOf('return')>=0) {
  509. func = '(function(){'+expression+'})';
  510. }
  511. else {
  512. func = '(function(){return ('+expression+')})';
  513. }
  514. var condition = eval.call(this, func).call(this);
  515. if (condition) {
  516. return options.fn(this, options.data);
  517. }
  518. else {
  519. return options.inverse(this, options.data);
  520. }
  521. }
  522. }
  523. };
  524. var t7 = function (template, data) {
  525. if (arguments.length === 2) {
  526. var instance = new Template7(template);
  527. var rendered = instance.compile()(data);
  528. instance = null;
  529. return (rendered);
  530. }
  531. else return new Template7(template);
  532. };
  533. t7.registerHelper = function (name, fn) {
  534. Template7.prototype.helpers[name] = fn;
  535. };
  536. t7.unregisterHelper = function (name) {
  537. Template7.prototype.helpers[name] = undefined;
  538. delete Template7.prototype.helpers[name];
  539. };
  540. t7.compile = function (template, options) {
  541. var instance = new Template7(template, options);
  542. return instance.compile();
  543. };
  544. t7.options = Template7.prototype.options;
  545. t7.helpers = Template7.prototype.helpers;
  546. return t7;
  547. })();
  548. }($);
  549. /*! Hammer.JS - v2.0.8 - 2016-04-23
  550. * http://hammerjs.github.io/
  551. *
  552. * Copyright (c) 2016 Jorik Tangelder;
  553. * Licensed under the MIT license */
  554. (function(window, document, exportName, undefined) {
  555. 'use strict';
  556. var VENDOR_PREFIXES = ['', 'webkit', 'Moz', 'MS', 'ms', 'o'];
  557. var TEST_ELEMENT = document.createElement('div');
  558. var TYPE_FUNCTION = 'function';
  559. var round = Math.round;
  560. var abs = Math.abs;
  561. var now = Date.now;
  562. /**
  563. * set a timeout with a given scope
  564. * @param {Function} fn
  565. * @param {Number} timeout
  566. * @param {Object} context
  567. * @returns {number}
  568. */
  569. function setTimeoutContext(fn, timeout, context) {
  570. return setTimeout(bindFn(fn, context), timeout);
  571. }
  572. /**
  573. * if the argument is an array, we want to execute the fn on each entry
  574. * if it aint an array we don't want to do a thing.
  575. * this is used by all the methods that accept a single and array argument.
  576. * @param {*|Array} arg
  577. * @param {String} fn
  578. * @param {Object} [context]
  579. * @returns {Boolean}
  580. */
  581. function invokeArrayArg(arg, fn, context) {
  582. if (Array.isArray(arg)) {
  583. each(arg, context[fn], context);
  584. return true;
  585. }
  586. return false;
  587. }
  588. /**
  589. * walk objects and arrays
  590. * @param {Object} obj
  591. * @param {Function} iterator
  592. * @param {Object} context
  593. */
  594. function each(obj, iterator, context) {
  595. var i;
  596. if (!obj) {
  597. return;
  598. }
  599. if (obj.forEach) {
  600. obj.forEach(iterator, context);
  601. } else if (obj.length !== undefined) {
  602. i = 0;
  603. while (i < obj.length) {
  604. iterator.call(context, obj[i], i, obj);
  605. i++;
  606. }
  607. } else {
  608. for (i in obj) {
  609. obj.hasOwnProperty(i) && iterator.call(context, obj[i], i, obj);
  610. }
  611. }
  612. }
  613. /**
  614. * wrap a method with a deprecation warning and stack trace
  615. * @param {Function} method
  616. * @param {String} name
  617. * @param {String} message
  618. * @returns {Function} A new function wrapping the supplied method.
  619. */
  620. function deprecate(method, name, message) {
  621. var deprecationMessage = 'DEPRECATED METHOD: ' + name + '\n' + message + ' AT \n';
  622. return function() {
  623. var e = new Error('get-stack-trace');
  624. var stack = e && e.stack ? e.stack.replace(/^[^\(]+?[\n$]/gm, '')
  625. .replace(/^\s+at\s+/gm, '')
  626. .replace(/^Object.<anonymous>\s*\(/gm, '{anonymous}()@') : 'Unknown Stack Trace';
  627. var log = window.console && (window.console.warn || window.console.log);
  628. if (log) {
  629. log.call(window.console, deprecationMessage, stack);
  630. }
  631. return method.apply(this, arguments);
  632. };
  633. }
  634. /**
  635. * extend object.
  636. * means that properties in dest will be overwritten by the ones in src.
  637. * @param {Object} target
  638. * @param {...Object} objects_to_assign
  639. * @returns {Object} target
  640. */
  641. var assign;
  642. if (typeof Object.assign !== 'function') {
  643. assign = function assign(target) {
  644. if (target === undefined || target === null) {
  645. throw new TypeError('Cannot convert undefined or null to object');
  646. }
  647. var output = Object(target);
  648. for (var index = 1; index < arguments.length; index++) {
  649. var source = arguments[index];
  650. if (source !== undefined && source !== null) {
  651. for (var nextKey in source) {
  652. if (source.hasOwnProperty(nextKey)) {
  653. output[nextKey] = source[nextKey];
  654. }
  655. }
  656. }
  657. }
  658. return output;
  659. };
  660. } else {
  661. assign = Object.assign;
  662. }
  663. /**
  664. * extend object.
  665. * means that properties in dest will be overwritten by the ones in src.
  666. * @param {Object} dest
  667. * @param {Object} src
  668. * @param {Boolean} [merge=false]
  669. * @returns {Object} dest
  670. */
  671. var extend = deprecate(function extend(dest, src, merge) {
  672. var keys = Object.keys(src);
  673. var i = 0;
  674. while (i < keys.length) {
  675. if (!merge || (merge && dest[keys[i]] === undefined)) {
  676. dest[keys[i]] = src[keys[i]];
  677. }
  678. i++;
  679. }
  680. return dest;
  681. }, 'extend', 'Use `assign`.');
  682. /**
  683. * merge the values from src in the dest.
  684. * means that properties that exist in dest will not be overwritten by src
  685. * @param {Object} dest
  686. * @param {Object} src
  687. * @returns {Object} dest
  688. */
  689. var merge = deprecate(function merge(dest, src) {
  690. return extend(dest, src, true);
  691. }, 'merge', 'Use `assign`.');
  692. /**
  693. * simple class inheritance
  694. * @param {Function} child
  695. * @param {Function} base
  696. * @param {Object} [properties]
  697. */
  698. function inherit(child, base, properties) {
  699. var baseP = base.prototype,
  700. childP;
  701. childP = child.prototype = Object.create(baseP);
  702. childP.constructor = child;
  703. childP._super = baseP;
  704. if (properties) {
  705. assign(childP, properties);
  706. }
  707. }
  708. /**
  709. * simple function bind
  710. * @param {Function} fn
  711. * @param {Object} context
  712. * @returns {Function}
  713. */
  714. function bindFn(fn, context) {
  715. return function boundFn() {
  716. return fn.apply(context, arguments);
  717. };
  718. }
  719. /**
  720. * let a boolean value also be a function that must return a boolean
  721. * this first item in args will be used as the context
  722. * @param {Boolean|Function} val
  723. * @param {Array} [args]
  724. * @returns {Boolean}
  725. */
  726. function boolOrFn(val, args) {
  727. if (typeof val == TYPE_FUNCTION) {
  728. return val.apply(args ? args[0] || undefined : undefined, args);
  729. }
  730. return val;
  731. }
  732. /**
  733. * use the val2 when val1 is undefined
  734. * @param {*} val1
  735. * @param {*} val2
  736. * @returns {*}
  737. */
  738. function ifUndefined(val1, val2) {
  739. return (val1 === undefined) ? val2 : val1;
  740. }
  741. /**
  742. * addEventListener with multiple events at once
  743. * @param {EventTarget} target
  744. * @param {String} types
  745. * @param {Function} handler
  746. */
  747. function addEventListeners(target, types, handler) {
  748. each(splitStr(types), function(type) {
  749. target.addEventListener(type, handler, false);
  750. });
  751. }
  752. /**
  753. * removeEventListener with multiple events at once
  754. * @param {EventTarget} target
  755. * @param {String} types
  756. * @param {Function} handler
  757. */
  758. function removeEventListeners(target, types, handler) {
  759. each(splitStr(types), function(type) {
  760. target.removeEventListener(type, handler, false);
  761. });
  762. }
  763. /**
  764. * find if a node is in the given parent
  765. * @method hasParent
  766. * @param {HTMLElement} node
  767. * @param {HTMLElement} parent
  768. * @return {Boolean} found
  769. */
  770. function hasParent(node, parent) {
  771. while (node) {
  772. if (node == parent) {
  773. return true;
  774. }
  775. node = node.parentNode;
  776. }
  777. return false;
  778. }
  779. /**
  780. * small indexOf wrapper
  781. * @param {String} str
  782. * @param {String} find
  783. * @returns {Boolean} found
  784. */
  785. function inStr(str, find) {
  786. return str.indexOf(find) > -1;
  787. }
  788. /**
  789. * split string on whitespace
  790. * @param {String} str
  791. * @returns {Array} words
  792. */
  793. function splitStr(str) {
  794. return str.trim().split(/\s+/g);
  795. }
  796. /**
  797. * find if a array contains the object using indexOf or a simple polyFill
  798. * @param {Array} src
  799. * @param {String} find
  800. * @param {String} [findByKey]
  801. * @return {Boolean|Number} false when not found, or the index
  802. */
  803. function inArray(src, find, findByKey) {
  804. if (src.indexOf && !findByKey) {
  805. return src.indexOf(find);
  806. } else {
  807. var i = 0;
  808. while (i < src.length) {
  809. if ((findByKey && src[i][findByKey] == find) || (!findByKey && src[i] === find)) {
  810. return i;
  811. }
  812. i++;
  813. }
  814. return -1;
  815. }
  816. }
  817. /**
  818. * convert array-like objects to real arrays
  819. * @param {Object} obj
  820. * @returns {Array}
  821. */
  822. function toArray(obj) {
  823. return Array.prototype.slice.call(obj, 0);
  824. }
  825. /**
  826. * unique array with objects based on a key (like 'id') or just by the array's value
  827. * @param {Array} src [{id:1},{id:2},{id:1}]
  828. * @param {String} [key]
  829. * @param {Boolean} [sort=False]
  830. * @returns {Array} [{id:1},{id:2}]
  831. */
  832. function uniqueArray(src, key, sort) {
  833. var results = [];
  834. var values = [];
  835. var i = 0;
  836. while (i < src.length) {
  837. var val = key ? src[i][key] : src[i];
  838. if (inArray(values, val) < 0) {
  839. results.push(src[i]);
  840. }
  841. values[i] = val;
  842. i++;
  843. }
  844. if (sort) {
  845. if (!key) {
  846. results = results.sort();
  847. } else {
  848. results = results.sort(function sortUniqueArray(a, b) {
  849. return a[key] > b[key];
  850. });
  851. }
  852. }
  853. return results;
  854. }
  855. /**
  856. * get the prefixed property
  857. * @param {Object} obj
  858. * @param {String} property
  859. * @returns {String|Undefined} prefixed
  860. */
  861. function prefixed(obj, property) {
  862. var prefix, prop;
  863. var camelProp = property[0].toUpperCase() + property.slice(1);
  864. var i = 0;
  865. while (i < VENDOR_PREFIXES.length) {
  866. prefix = VENDOR_PREFIXES[i];
  867. prop = (prefix) ? prefix + camelProp : property;
  868. if (prop in obj) {
  869. return prop;
  870. }
  871. i++;
  872. }
  873. return undefined;
  874. }
  875. /**
  876. * get a unique id
  877. * @returns {number} uniqueId
  878. */
  879. var _uniqueId = 1;
  880. function uniqueId() {
  881. return _uniqueId++;
  882. }
  883. /**
  884. * get the window object of an element
  885. * @param {HTMLElement} element
  886. * @returns {DocumentView|Window}
  887. */
  888. function getWindowForElement(element) {
  889. var doc = element.ownerDocument || element;
  890. return (doc.defaultView || doc.parentWindow || window);
  891. }
  892. var MOBILE_REGEX = /mobile|tablet|ip(ad|hone|od)|android/i;
  893. var SUPPORT_TOUCH = ('ontouchstart' in window);
  894. var SUPPORT_POINTER_EVENTS = prefixed(window, 'PointerEvent') !== undefined;
  895. var SUPPORT_ONLY_TOUCH = SUPPORT_TOUCH && MOBILE_REGEX.test(navigator.userAgent);
  896. var INPUT_TYPE_TOUCH = 'touch';
  897. var INPUT_TYPE_PEN = 'pen';
  898. var INPUT_TYPE_MOUSE = 'mouse';
  899. var INPUT_TYPE_KINECT = 'kinect';
  900. var COMPUTE_INTERVAL = 25;
  901. var INPUT_START = 1;
  902. var INPUT_MOVE = 2;
  903. var INPUT_END = 4;
  904. var INPUT_CANCEL = 8;
  905. var DIRECTION_NONE = 1;
  906. var DIRECTION_LEFT = 2;
  907. var DIRECTION_RIGHT = 4;
  908. var DIRECTION_UP = 8;
  909. var DIRECTION_DOWN = 16;
  910. var DIRECTION_HORIZONTAL = DIRECTION_LEFT | DIRECTION_RIGHT;
  911. var DIRECTION_VERTICAL = DIRECTION_UP | DIRECTION_DOWN;
  912. var DIRECTION_ALL = DIRECTION_HORIZONTAL | DIRECTION_VERTICAL;
  913. var PROPS_XY = ['x', 'y'];
  914. var PROPS_CLIENT_XY = ['clientX', 'clientY'];
  915. /**
  916. * create new input type manager
  917. * @param {Manager} manager
  918. * @param {Function} callback
  919. * @returns {Input}
  920. * @constructor
  921. */
  922. function Input(manager, callback) {
  923. var self = this;
  924. this.manager = manager;
  925. this.callback = callback;
  926. this.element = manager.element;
  927. this.target = manager.options.inputTarget;
  928. // smaller wrapper around the handler, for the scope and the enabled state of the manager,
  929. // so when disabled the input events are completely bypassed.
  930. this.domHandler = function(ev) {
  931. if (boolOrFn(manager.options.enable, [manager])) {
  932. self.handler(ev);
  933. }
  934. };
  935. this.init();
  936. }
  937. Input.prototype = {
  938. /**
  939. * should handle the inputEvent data and trigger the callback
  940. * @virtual
  941. */
  942. handler: function() { },
  943. /**
  944. * bind the events
  945. */
  946. init: function() {
  947. this.evEl && addEventListeners(this.element, this.evEl, this.domHandler);
  948. this.evTarget && addEventListeners(this.target, this.evTarget, this.domHandler);
  949. this.evWin && addEventListeners(getWindowForElement(this.element), this.evWin, this.domHandler);
  950. },
  951. /**
  952. * unbind the events
  953. */
  954. destroy: function() {
  955. this.evEl && removeEventListeners(this.element, this.evEl, this.domHandler);
  956. this.evTarget && removeEventListeners(this.target, this.evTarget, this.domHandler);
  957. this.evWin && removeEventListeners(getWindowForElement(this.element), this.evWin, this.domHandler);
  958. }
  959. };
  960. /**
  961. * create new input type manager
  962. * called by the Manager constructor
  963. * @param {Hammer} manager
  964. * @returns {Input}
  965. */
  966. function createInputInstance(manager) {
  967. var Type;
  968. var inputClass = manager.options.inputClass;
  969. if (inputClass) {
  970. Type = inputClass;
  971. } else if (SUPPORT_POINTER_EVENTS) {
  972. Type = PointerEventInput;
  973. } else if (SUPPORT_ONLY_TOUCH) {
  974. Type = TouchInput;
  975. } else if (!SUPPORT_TOUCH) {
  976. Type = MouseInput;
  977. } else {
  978. Type = TouchMouseInput;
  979. }
  980. return new (Type)(manager, inputHandler);
  981. }
  982. /**
  983. * handle input events
  984. * @param {Manager} manager
  985. * @param {String} eventType
  986. * @param {Object} input
  987. */
  988. function inputHandler(manager, eventType, input) {
  989. var pointersLen = input.pointers.length;
  990. var changedPointersLen = input.changedPointers.length;
  991. var isFirst = (eventType & INPUT_START && (pointersLen - changedPointersLen === 0));
  992. var isFinal = (eventType & (INPUT_END | INPUT_CANCEL) && (pointersLen - changedPointersLen === 0));
  993. input.isFirst = !!isFirst;
  994. input.isFinal = !!isFinal;
  995. if (isFirst) {
  996. manager.session = {};
  997. }
  998. // source event is the normalized value of the domEvents
  999. // like 'touchstart, mouseup, pointerdown'
  1000. input.eventType = eventType;
  1001. // compute scale, rotation etc
  1002. computeInputData(manager, input);
  1003. // emit secret event
  1004. manager.emit('hammer.input', input);
  1005. manager.recognize(input);
  1006. manager.session.prevInput = input;
  1007. }
  1008. /**
  1009. * extend the data with some usable properties like scale, rotate, velocity etc
  1010. * @param {Object} manager
  1011. * @param {Object} input
  1012. */
  1013. function computeInputData(manager, input) {
  1014. var session = manager.session;
  1015. var pointers = input.pointers;
  1016. var pointersLength = pointers.length;
  1017. // store the first input to calculate the distance and direction
  1018. if (!session.firstInput) {
  1019. session.firstInput = simpleCloneInputData(input);
  1020. }
  1021. // to compute scale and rotation we need to store the multiple touches
  1022. if (pointersLength > 1 && !session.firstMultiple) {
  1023. session.firstMultiple = simpleCloneInputData(input);
  1024. } else if (pointersLength === 1) {
  1025. session.firstMultiple = false;
  1026. }
  1027. var firstInput = session.firstInput;
  1028. var firstMultiple = session.firstMultiple;
  1029. var offsetCenter = firstMultiple ? firstMultiple.center : firstInput.center;
  1030. var center = input.center = getCenter(pointers);
  1031. input.timeStamp = now();
  1032. input.deltaTime = input.timeStamp - firstInput.timeStamp;
  1033. input.angle = getAngle(offsetCenter, center);
  1034. input.distance = getDistance(offsetCenter, center);
  1035. computeDeltaXY(session, input);
  1036. input.offsetDirection = getDirection(input.deltaX, input.deltaY);
  1037. var overallVelocity = getVelocity(input.deltaTime, input.deltaX, input.deltaY);
  1038. input.overallVelocityX = overallVelocity.x;
  1039. input.overallVelocityY = overallVelocity.y;
  1040. input.overallVelocity = (abs(overallVelocity.x) > abs(overallVelocity.y)) ? overallVelocity.x : overallVelocity.y;
  1041. input.scale = firstMultiple ? getScale(firstMultiple.pointers, pointers) : 1;
  1042. input.rotation = firstMultiple ? getRotation(firstMultiple.pointers, pointers) : 0;
  1043. input.maxPointers = !session.prevInput ? input.pointers.length : ((input.pointers.length >
  1044. session.prevInput.maxPointers) ? input.pointers.length : session.prevInput.maxPointers);
  1045. computeIntervalInputData(session, input);
  1046. // find the correct target
  1047. var target = manager.element;
  1048. if (hasParent(input.srcEvent.target, target)) {
  1049. target = input.srcEvent.target;
  1050. }
  1051. input.target = target;
  1052. }
  1053. function computeDeltaXY(session, input) {
  1054. var center = input.center;
  1055. var offset = session.offsetDelta || {};
  1056. var prevDelta = session.prevDelta || {};
  1057. var prevInput = session.prevInput || {};
  1058. if (input.eventType === INPUT_START || prevInput.eventType === INPUT_END) {
  1059. prevDelta = session.prevDelta = {
  1060. x: prevInput.deltaX || 0,
  1061. y: prevInput.deltaY || 0
  1062. };
  1063. offset = session.offsetDelta = {
  1064. x: center.x,
  1065. y: center.y
  1066. };
  1067. }
  1068. input.deltaX = prevDelta.x + (center.x - offset.x);
  1069. input.deltaY = prevDelta.y + (center.y - offset.y);
  1070. }
  1071. /**
  1072. * velocity is calculated every x ms
  1073. * @param {Object} session
  1074. * @param {Object} input
  1075. */
  1076. function computeIntervalInputData(session, input) {
  1077. var last = session.lastInterval || input,
  1078. deltaTime = input.timeStamp - last.timeStamp,
  1079. velocity, velocityX, velocityY, direction;
  1080. if (input.eventType != INPUT_CANCEL && (deltaTime > COMPUTE_INTERVAL || last.velocity === undefined)) {
  1081. var deltaX = input.deltaX - last.deltaX;
  1082. var deltaY = input.deltaY - last.deltaY;
  1083. var v = getVelocity(deltaTime, deltaX, deltaY);
  1084. velocityX = v.x;
  1085. velocityY = v.y;
  1086. velocity = (abs(v.x) > abs(v.y)) ? v.x : v.y;
  1087. direction = getDirection(deltaX, deltaY);
  1088. session.lastInterval = input;
  1089. } else {
  1090. // use latest velocity info if it doesn't overtake a minimum period
  1091. velocity = last.velocity;
  1092. velocityX = last.velocityX;
  1093. velocityY = last.velocityY;
  1094. direction = last.direction;
  1095. }
  1096. input.velocity = velocity;
  1097. input.velocityX = velocityX;
  1098. input.velocityY = velocityY;
  1099. input.direction = direction;
  1100. }
  1101. /**
  1102. * create a simple clone from the input used for storage of firstInput and firstMultiple
  1103. * @param {Object} input
  1104. * @returns {Object} clonedInputData
  1105. */
  1106. function simpleCloneInputData(input) {
  1107. // make a simple copy of the pointers because we will get a reference if we don't
  1108. // we only need clientXY for the calculations
  1109. var pointers = [];
  1110. var i = 0;
  1111. while (i < input.pointers.length) {
  1112. pointers[i] = {
  1113. clientX: round(input.pointers[i].clientX),
  1114. clientY: round(input.pointers[i].clientY)
  1115. };
  1116. i++;
  1117. }
  1118. return {
  1119. timeStamp: now(),
  1120. pointers: pointers,
  1121. center: getCenter(pointers),
  1122. deltaX: input.deltaX,
  1123. deltaY: input.deltaY
  1124. };
  1125. }
  1126. /**
  1127. * get the center of all the pointers
  1128. * @param {Array} pointers
  1129. * @return {Object} center contains `x` and `y` properties
  1130. */
  1131. function getCenter(pointers) {
  1132. var pointersLength = pointers.length;
  1133. // no need to loop when only one touch
  1134. if (pointersLength === 1) {
  1135. return {
  1136. x: round(pointers[0].clientX),
  1137. y: round(pointers[0].clientY)
  1138. };
  1139. }
  1140. var x = 0, y = 0, i = 0;
  1141. while (i < pointersLength) {
  1142. x += pointers[i].clientX;
  1143. y += pointers[i].clientY;
  1144. i++;
  1145. }
  1146. return {
  1147. x: round(x / pointersLength),
  1148. y: round(y / pointersLength)
  1149. };
  1150. }
  1151. /**
  1152. * calculate the velocity between two points. unit is in px per ms.
  1153. * @param {Number} deltaTime
  1154. * @param {Number} x
  1155. * @param {Number} y
  1156. * @return {Object} velocity `x` and `y`
  1157. */
  1158. function getVelocity(deltaTime, x, y) {
  1159. return {
  1160. x: x / deltaTime || 0,
  1161. y: y / deltaTime || 0
  1162. };
  1163. }
  1164. /**
  1165. * get the direction between two points
  1166. * @param {Number} x
  1167. * @param {Number} y
  1168. * @return {Number} direction
  1169. */
  1170. function getDirection(x, y) {
  1171. if (x === y) {
  1172. return DIRECTION_NONE;
  1173. }
  1174. if (abs(x) >= abs(y)) {
  1175. return x < 0 ? DIRECTION_LEFT : DIRECTION_RIGHT;
  1176. }
  1177. return y < 0 ? DIRECTION_UP : DIRECTION_DOWN;
  1178. }
  1179. /**
  1180. * calculate the absolute distance between two points
  1181. * @param {Object} p1 {x, y}
  1182. * @param {Object} p2 {x, y}
  1183. * @param {Array} [props] containing x and y keys
  1184. * @return {Number} distance
  1185. */
  1186. function getDistance(p1, p2, props) {
  1187. if (!props) {
  1188. props = PROPS_XY;
  1189. }
  1190. var x = p2[props[0]] - p1[props[0]],
  1191. y = p2[props[1]] - p1[props[1]];
  1192. return Math.sqrt((x * x) + (y * y));
  1193. }
  1194. /**
  1195. * calculate the angle between two coordinates
  1196. * @param {Object} p1
  1197. * @param {Object} p2
  1198. * @param {Array} [props] containing x and y keys
  1199. * @return {Number} angle
  1200. */
  1201. function getAngle(p1, p2, props) {
  1202. if (!props) {
  1203. props = PROPS_XY;
  1204. }
  1205. var x = p2[props[0]] - p1[props[0]],
  1206. y = p2[props[1]] - p1[props[1]];
  1207. return Math.atan2(y, x) * 180 / Math.PI;
  1208. }
  1209. /**
  1210. * calculate the rotation degrees between two pointersets
  1211. * @param {Array} start array of pointers
  1212. * @param {Array} end array of pointers
  1213. * @return {Number} rotation
  1214. */
  1215. function getRotation(start, end) {
  1216. return getAngle(end[1], end[0], PROPS_CLIENT_XY) + getAngle(start[1], start[0], PROPS_CLIENT_XY);
  1217. }
  1218. /**
  1219. * calculate the scale factor between two pointersets
  1220. * no scale is 1, and goes down to 0 when pinched together, and bigger when pinched out
  1221. * @param {Array} start array of pointers
  1222. * @param {Array} end array of pointers
  1223. * @return {Number} scale
  1224. */
  1225. function getScale(start, end) {
  1226. return getDistance(end[0], end[1], PROPS_CLIENT_XY) / getDistance(start[0], start[1], PROPS_CLIENT_XY);
  1227. }
  1228. var MOUSE_INPUT_MAP = {
  1229. mousedown: INPUT_START,
  1230. mousemove: INPUT_MOVE,
  1231. mouseup: INPUT_END
  1232. };
  1233. var MOUSE_ELEMENT_EVENTS = 'mousedown';
  1234. var MOUSE_WINDOW_EVENTS = 'mousemove mouseup';
  1235. /**
  1236. * Mouse events input
  1237. * @constructor
  1238. * @extends Input
  1239. */
  1240. function MouseInput() {
  1241. this.evEl = MOUSE_ELEMENT_EVENTS;
  1242. this.evWin = MOUSE_WINDOW_EVENTS;
  1243. this.pressed = false; // mousedown state
  1244. Input.apply(this, arguments);
  1245. }
  1246. inherit(MouseInput, Input, {
  1247. /**
  1248. * handle mouse events
  1249. * @param {Object} ev
  1250. */
  1251. handler: function MEhandler(ev) {
  1252. var eventType = MOUSE_INPUT_MAP[ev.type];
  1253. // on start we want to have the left mouse button down
  1254. if (eventType & INPUT_START && ev.button === 0) {
  1255. this.pressed = true;
  1256. }
  1257. if (eventType & INPUT_MOVE && ev.which !== 1) {
  1258. eventType = INPUT_END;
  1259. }
  1260. // mouse must be down
  1261. if (!this.pressed) {
  1262. return;
  1263. }
  1264. if (eventType & INPUT_END) {
  1265. this.pressed = false;
  1266. }
  1267. this.callback(this.manager, eventType, {
  1268. pointers: [ev],
  1269. changedPointers: [ev],
  1270. pointerType: INPUT_TYPE_MOUSE,
  1271. srcEvent: ev
  1272. });
  1273. }
  1274. });
  1275. var POINTER_INPUT_MAP = {
  1276. pointerdown: INPUT_START,
  1277. pointermove: INPUT_MOVE,
  1278. pointerup: INPUT_END,
  1279. pointercancel: INPUT_CANCEL,
  1280. pointerout: INPUT_CANCEL
  1281. };
  1282. // in IE10 the pointer types is defined as an enum
  1283. var IE10_POINTER_TYPE_ENUM = {
  1284. 2: INPUT_TYPE_TOUCH,
  1285. 3: INPUT_TYPE_PEN,
  1286. 4: INPUT_TYPE_MOUSE,
  1287. 5: INPUT_TYPE_KINECT // see https://twitter.com/jacobrossi/status/480596438489890816
  1288. };
  1289. var POINTER_ELEMENT_EVENTS = 'pointerdown';
  1290. var POINTER_WINDOW_EVENTS = 'pointermove pointerup pointercancel';
  1291. // IE10 has prefixed support, and case-sensitive
  1292. if (window.MSPointerEvent && !window.PointerEvent) {
  1293. POINTER_ELEMENT_EVENTS = 'MSPointerDown';
  1294. POINTER_WINDOW_EVENTS = 'MSPointerMove MSPointerUp MSPointerCancel';
  1295. }
  1296. /**
  1297. * Pointer events input
  1298. * @constructor
  1299. * @extends Input
  1300. */
  1301. function PointerEventInput() {
  1302. this.evEl = POINTER_ELEMENT_EVENTS;
  1303. this.evWin = POINTER_WINDOW_EVENTS;
  1304. Input.apply(this, arguments);
  1305. this.store = (this.manager.session.pointerEvents = []);
  1306. }
  1307. inherit(PointerEventInput, Input, {
  1308. /**
  1309. * handle mouse events
  1310. * @param {Object} ev
  1311. */
  1312. handler: function PEhandler(ev) {
  1313. var store = this.store;
  1314. var removePointer = false;
  1315. var eventTypeNormalized = ev.type.toLowerCase().replace('ms', '');
  1316. var eventType = POINTER_INPUT_MAP[eventTypeNormalized];
  1317. var pointerType = IE10_POINTER_TYPE_ENUM[ev.pointerType] || ev.pointerType;
  1318. var isTouch = (pointerType == INPUT_TYPE_TOUCH);
  1319. // get index of the event in the store
  1320. var storeIndex = inArray(store, ev.pointerId, 'pointerId');
  1321. // start and mouse must be down
  1322. if (eventType & INPUT_START && (ev.button === 0 || isTouch)) {
  1323. if (storeIndex < 0) {
  1324. store.push(ev);
  1325. storeIndex = store.length - 1;
  1326. }
  1327. } else if (eventType & (INPUT_END | INPUT_CANCEL)) {
  1328. removePointer = true;
  1329. }
  1330. // it not found, so the pointer hasn't been down (so it's probably a hover)
  1331. if (storeIndex < 0) {
  1332. return;
  1333. }
  1334. // update the event in the store
  1335. store[storeIndex] = ev;
  1336. this.callback(this.manager, eventType, {
  1337. pointers: store,
  1338. changedPointers: [ev],
  1339. pointerType: pointerType,
  1340. srcEvent: ev
  1341. });
  1342. if (removePointer) {
  1343. // remove from the store
  1344. store.splice(storeIndex, 1);
  1345. }
  1346. }
  1347. });
  1348. var SINGLE_TOUCH_INPUT_MAP = {
  1349. touchstart: INPUT_START,
  1350. touchmove: INPUT_MOVE,
  1351. touchend: INPUT_END,
  1352. touchcancel: INPUT_CANCEL
  1353. };
  1354. var SINGLE_TOUCH_TARGET_EVENTS = 'touchstart';
  1355. var SINGLE_TOUCH_WINDOW_EVENTS = 'touchstart touchmove touchend touchcancel';
  1356. /**
  1357. * Touch events input
  1358. * @constructor
  1359. * @extends Input
  1360. */
  1361. function SingleTouchInput() {
  1362. this.evTarget = SINGLE_TOUCH_TARGET_EVENTS;
  1363. this.evWin = SINGLE_TOUCH_WINDOW_EVENTS;
  1364. this.started = false;
  1365. Input.apply(this, arguments);
  1366. }
  1367. inherit(SingleTouchInput, Input, {
  1368. handler: function TEhandler(ev) {
  1369. var type = SINGLE_TOUCH_INPUT_MAP[ev.type];
  1370. // should we handle the touch events?
  1371. if (type === INPUT_START) {
  1372. this.started = true;
  1373. }
  1374. if (!this.started) {
  1375. return;
  1376. }
  1377. var touches = normalizeSingleTouches.call(this, ev, type);
  1378. // when done, reset the started state
  1379. if (type & (INPUT_END | INPUT_CANCEL) && touches[0].length - touches[1].length === 0) {
  1380. this.started = false;
  1381. }
  1382. this.callback(this.manager, type, {
  1383. pointers: touches[0],
  1384. changedPointers: touches[1],
  1385. pointerType: INPUT_TYPE_TOUCH,
  1386. srcEvent: ev
  1387. });
  1388. }
  1389. });
  1390. /**
  1391. * @this {TouchInput}
  1392. * @param {Object} ev
  1393. * @param {Number} type flag
  1394. * @returns {undefined|Array} [all, changed]
  1395. */
  1396. function normalizeSingleTouches(ev, type) {
  1397. var all = toArray(ev.touches);
  1398. var changed = toArray(ev.changedTouches);
  1399. if (type & (INPUT_END | INPUT_CANCEL)) {
  1400. all = uniqueArray(all.concat(changed), 'identifier', true);
  1401. }
  1402. return [all, changed];
  1403. }
  1404. var TOUCH_INPUT_MAP = {
  1405. touchstart: INPUT_START,
  1406. touchmove: INPUT_MOVE,
  1407. touchend: INPUT_END,
  1408. touchcancel: INPUT_CANCEL
  1409. };
  1410. var TOUCH_TARGET_EVENTS = 'touchstart touchmove touchend touchcancel';
  1411. /**
  1412. * Multi-user touch events input
  1413. * @constructor
  1414. * @extends Input
  1415. */
  1416. function TouchInput() {
  1417. this.evTarget = TOUCH_TARGET_EVENTS;
  1418. this.targetIds = {};
  1419. Input.apply(this, arguments);
  1420. }
  1421. inherit(TouchInput, Input, {
  1422. handler: function MTEhandler(ev) {
  1423. var type = TOUCH_INPUT_MAP[ev.type];
  1424. var touches = getTouches.call(this, ev, type);
  1425. if (!touches) {
  1426. return;
  1427. }
  1428. this.callback(this.manager, type, {
  1429. pointers: touches[0],
  1430. changedPointers: touches[1],
  1431. pointerType: INPUT_TYPE_TOUCH,
  1432. srcEvent: ev
  1433. });
  1434. }
  1435. });
  1436. /**
  1437. * @this {TouchInput}
  1438. * @param {Object} ev
  1439. * @param {Number} type flag
  1440. * @returns {undefined|Array} [all, changed]
  1441. */
  1442. function getTouches(ev, type) {
  1443. var allTouches = toArray(ev.touches);
  1444. var targetIds = this.targetIds;
  1445. // when there is only one touch, the process can be simplified
  1446. if (type & (INPUT_START | INPUT_MOVE) && allTouches.length === 1) {
  1447. targetIds[allTouches[0].identifier] = true;
  1448. return [allTouches, allTouches];
  1449. }
  1450. var i,
  1451. targetTouches,
  1452. changedTouches = toArray(ev.changedTouches),
  1453. changedTargetTouches = [],
  1454. target = this.target;
  1455. // get target touches from touches
  1456. targetTouches = allTouches.filter(function(touch) {
  1457. return hasParent(touch.target, target);
  1458. });
  1459. // collect touches
  1460. if (type === INPUT_START) {
  1461. i = 0;
  1462. while (i < targetTouches.length) {
  1463. targetIds[targetTouches[i].identifier] = true;
  1464. i++;
  1465. }
  1466. }
  1467. // filter changed touches to only contain touches that exist in the collected target ids
  1468. i = 0;
  1469. while (i < changedTouches.length) {
  1470. if (targetIds[changedTouches[i].identifier]) {
  1471. changedTargetTouches.push(changedTouches[i]);
  1472. }
  1473. // cleanup removed touches
  1474. if (type & (INPUT_END | INPUT_CANCEL)) {
  1475. delete targetIds[changedTouches[i].identifier];
  1476. }
  1477. i++;
  1478. }
  1479. if (!changedTargetTouches.length) {
  1480. return;
  1481. }
  1482. return [
  1483. // merge targetTouches with changedTargetTouches so it contains ALL touches, including 'end' and 'cancel'
  1484. uniqueArray(targetTouches.concat(changedTargetTouches), 'identifier', true),
  1485. changedTargetTouches
  1486. ];
  1487. }
  1488. /**
  1489. * Combined touch and mouse input
  1490. *
  1491. * Touch has a higher priority then mouse, and while touching no mouse events are allowed.
  1492. * This because touch devices also emit mouse events while doing a touch.
  1493. *
  1494. * @constructor
  1495. * @extends Input
  1496. */
  1497. var DEDUP_TIMEOUT = 2500;
  1498. var DEDUP_DISTANCE = 25;
  1499. function TouchMouseInput() {
  1500. Input.apply(this, arguments);
  1501. var handler = bindFn(this.handler, this);
  1502. this.touch = new TouchInput(this.manager, handler);
  1503. this.mouse = new MouseInput(this.manager, handler);
  1504. this.primaryTouch = null;
  1505. this.lastTouches = [];
  1506. }
  1507. inherit(TouchMouseInput, Input, {
  1508. /**
  1509. * handle mouse and touch events
  1510. * @param {Hammer} manager
  1511. * @param {String} inputEvent
  1512. * @param {Object} inputData
  1513. */
  1514. handler: function TMEhandler(manager, inputEvent, inputData) {
  1515. var isTouch = (inputData.pointerType == INPUT_TYPE_TOUCH),
  1516. isMouse = (inputData.pointerType == INPUT_TYPE_MOUSE);
  1517. if (isMouse && inputData.sourceCapabilities && inputData.sourceCapabilities.firesTouchEvents) {
  1518. return;
  1519. }
  1520. // when we're in a touch event, record touches to de-dupe synthetic mouse event
  1521. if (isTouch) {
  1522. recordTouches.call(this, inputEvent, inputData);
  1523. } else if (isMouse && isSyntheticEvent.call(this, inputData)) {
  1524. return;
  1525. }
  1526. this.callback(manager, inputEvent, inputData);
  1527. },
  1528. /**
  1529. * remove the event listeners
  1530. */
  1531. destroy: function destroy() {
  1532. this.touch.destroy();
  1533. this.mouse.destroy();
  1534. }
  1535. });
  1536. function recordTouches(eventType, eventData) {
  1537. if (eventType & INPUT_START) {
  1538. this.primaryTouch = eventData.changedPointers[0].identifier;
  1539. setLastTouch.call(this, eventData);
  1540. } else if (eventType & (INPUT_END | INPUT_CANCEL)) {
  1541. setLastTouch.call(this, eventData);
  1542. }
  1543. }
  1544. function setLastTouch(eventData) {
  1545. var touch = eventData.changedPointers[0];
  1546. if (touch.identifier === this.primaryTouch) {
  1547. var lastTouch = {x: touch.clientX, y: touch.clientY};
  1548. this.lastTouches.push(lastTouch);
  1549. var lts = this.lastTouches;
  1550. var removeLastTouch = function() {
  1551. var i = lts.indexOf(lastTouch);
  1552. if (i > -1) {
  1553. lts.splice(i, 1);
  1554. }
  1555. };
  1556. setTimeout(removeLastTouch, DEDUP_TIMEOUT);
  1557. }
  1558. }
  1559. function isSyntheticEvent(eventData) {
  1560. var x = eventData.srcEvent.clientX, y = eventData.srcEvent.clientY;
  1561. for (var i = 0; i < this.lastTouches.length; i++) {
  1562. var t = this.lastTouches[i];
  1563. var dx = Math.abs(x - t.x), dy = Math.abs(y - t.y);
  1564. if (dx <= DEDUP_DISTANCE && dy <= DEDUP_DISTANCE) {
  1565. return true;
  1566. }
  1567. }
  1568. return false;
  1569. }
  1570. var PREFIXED_TOUCH_ACTION = prefixed(TEST_ELEMENT.style, 'touchAction');
  1571. var NATIVE_TOUCH_ACTION = PREFIXED_TOUCH_ACTION !== undefined;
  1572. // magical touchAction value
  1573. var TOUCH_ACTION_COMPUTE = 'compute';
  1574. var TOUCH_ACTION_AUTO = 'auto';
  1575. var TOUCH_ACTION_MANIPULATION = 'manipulation'; // not implemented
  1576. var TOUCH_ACTION_NONE = 'none';
  1577. var TOUCH_ACTION_PAN_X = 'pan-x';
  1578. var TOUCH_ACTION_PAN_Y = 'pan-y';
  1579. var TOUCH_ACTION_MAP = getTouchActionProps();
  1580. /**
  1581. * Touch Action
  1582. * sets the touchAction property or uses the js alternative
  1583. * @param {Manager} manager
  1584. * @param {String} value
  1585. * @constructor
  1586. */
  1587. function TouchAction(manager, value) {
  1588. this.manager = manager;
  1589. this.set(value);
  1590. }
  1591. TouchAction.prototype = {
  1592. /**
  1593. * set the touchAction value on the element or enable the polyfill
  1594. * @param {String} value
  1595. */
  1596. set: function(value) {
  1597. // find out the touch-action by the event handlers
  1598. if (value == TOUCH_ACTION_COMPUTE) {
  1599. value = this.compute();
  1600. }
  1601. if (NATIVE_TOUCH_ACTION && this.manager.element.style && TOUCH_ACTION_MAP[value]) {
  1602. this.manager.element.style[PREFIXED_TOUCH_ACTION] = value;
  1603. }
  1604. this.actions = value.toLowerCase().trim();
  1605. },
  1606. /**
  1607. * just re-set the touchAction value
  1608. */
  1609. update: function() {
  1610. this.set(this.manager.options.touchAction);
  1611. },
  1612. /**
  1613. * compute the value for the touchAction property based on the recognizer's settings
  1614. * @returns {String} value
  1615. */
  1616. compute: function() {
  1617. var actions = [];
  1618. each(this.manager.recognizers, function(recognizer) {
  1619. if (boolOrFn(recognizer.options.enable, [recognizer])) {
  1620. actions = actions.concat(recognizer.getTouchAction());
  1621. }
  1622. });
  1623. return cleanTouchActions(actions.join(' '));
  1624. },
  1625. /**
  1626. * this method is called on each input cycle and provides the preventing of the browser behavior
  1627. * @param {Object} input
  1628. */
  1629. preventDefaults: function(input) {
  1630. var srcEvent = input.srcEvent;
  1631. var direction = input.offsetDirection;
  1632. // if the touch action did prevented once this session
  1633. if (this.manager.session.prevented) {
  1634. srcEvent.preventDefault();
  1635. return;
  1636. }
  1637. var actions = this.actions;
  1638. var hasNone = inStr(actions, TOUCH_ACTION_NONE) && !TOUCH_ACTION_MAP[TOUCH_ACTION_NONE];
  1639. var hasPanY = inStr(actions, TOUCH_ACTION_PAN_Y) && !TOUCH_ACTION_MAP[TOUCH_ACTION_PAN_Y];
  1640. var hasPanX = inStr(actions, TOUCH_ACTION_PAN_X) && !TOUCH_ACTION_MAP[TOUCH_ACTION_PAN_X];
  1641. if (hasNone) {
  1642. //do not prevent defaults if this is a tap gesture
  1643. var isTapPointer = input.pointers.length === 1;
  1644. var isTapMovement = input.distance < 2;
  1645. var isTapTouchTime = input.deltaTime < 250;
  1646. if (isTapPointer && isTapMovement && isTapTouchTime) {
  1647. return;
  1648. }
  1649. }
  1650. if (hasPanX && hasPanY) {
  1651. // `pan-x pan-y` means browser handles all scrolling/panning, do not prevent
  1652. return;
  1653. }
  1654. if (hasNone ||
  1655. (hasPanY && direction & DIRECTION_HORIZONTAL) ||
  1656. (hasPanX && direction & DIRECTION_VERTICAL)) {
  1657. return this.preventSrc(srcEvent);
  1658. }
  1659. },
  1660. /**
  1661. * call preventDefault to prevent the browser's default behavior (scrolling in most cases)
  1662. * @param {Object} srcEvent
  1663. */
  1664. preventSrc: function(srcEvent) {
  1665. this.manager.session.prevented = true;
  1666. srcEvent.preventDefault();
  1667. }
  1668. };
  1669. /**
  1670. * when the touchActions are collected they are not a valid value, so we need to clean things up. *
  1671. * @param {String} actions
  1672. * @returns {*}
  1673. */
  1674. function cleanTouchActions(actions) {
  1675. // none
  1676. if (inStr(actions, TOUCH_ACTION_NONE)) {
  1677. return TOUCH_ACTION_NONE;
  1678. }
  1679. var hasPanX = inStr(actions, TOUCH_ACTION_PAN_X);
  1680. var hasPanY = inStr(actions, TOUCH_ACTION_PAN_Y);
  1681. // if both pan-x and pan-y are set (different recognizers
  1682. // for different directions, e.g. horizontal pan but vertical swipe?)
  1683. // we need none (as otherwise with pan-x pan-y combined none of these
  1684. // recognizers will work, since the browser would handle all panning
  1685. if (hasPanX && hasPanY) {
  1686. return TOUCH_ACTION_NONE;
  1687. }
  1688. // pan-x OR pan-y
  1689. if (hasPanX || hasPanY) {
  1690. return hasPanX ? TOUCH_ACTION_PAN_X : TOUCH_ACTION_PAN_Y;
  1691. }
  1692. // manipulation
  1693. if (inStr(actions, TOUCH_ACTION_MANIPULATION)) {
  1694. return TOUCH_ACTION_MANIPULATION;
  1695. }
  1696. return TOUCH_ACTION_AUTO;
  1697. }
  1698. function getTouchActionProps() {
  1699. if (!NATIVE_TOUCH_ACTION) {
  1700. return false;
  1701. }
  1702. var touchMap = {};
  1703. var cssSupports = window.CSS && window.CSS.supports;
  1704. ['auto', 'manipulation', 'pan-y', 'pan-x', 'pan-x pan-y', 'none'].forEach(function(val) {
  1705. // If css.supports is not supported but there is native touch-action assume it supports
  1706. // all values. This is the case for IE 10 and 11.
  1707. touchMap[val] = cssSupports ? window.CSS.supports('touch-action', val) : true;
  1708. });
  1709. return touchMap;
  1710. }
  1711. /**
  1712. * Recognizer flow explained; *
  1713. * All recognizers have the initial state of POSSIBLE when a input session starts.
  1714. * The definition of a input session is from the first input until the last input, with all it's movement in it. *
  1715. * Example session for mouse-input: mousedown -> mousemove -> mouseup
  1716. *
  1717. * On each recognizing cycle (see Manager.recognize) the .recognize() method is executed
  1718. * which determines with state it should be.
  1719. *
  1720. * If the recognizer has the state FAILED, CANCELLED or RECOGNIZED (equals ENDED), it is reset to
  1721. * POSSIBLE to give it another change on the next cycle.
  1722. *
  1723. * Possible
  1724. * |
  1725. * +-----+---------------+
  1726. * | |
  1727. * +-----+-----+ |
  1728. * | | |
  1729. * Failed Cancelled |
  1730. * +-------+------+
  1731. * | |
  1732. * Recognized Began
  1733. * |
  1734. * Changed
  1735. * |
  1736. * Ended/Recognized
  1737. */
  1738. var STATE_POSSIBLE = 1;
  1739. var STATE_BEGAN = 2;
  1740. var STATE_CHANGED = 4;
  1741. var STATE_ENDED = 8;
  1742. var STATE_RECOGNIZED = STATE_ENDED;
  1743. var STATE_CANCELLED = 16;
  1744. var STATE_FAILED = 32;
  1745. /**
  1746. * Recognizer
  1747. * Every recognizer needs to extend from this class.
  1748. * @constructor
  1749. * @param {Object} options
  1750. */
  1751. function Recognizer(options) {
  1752. this.options = assign({}, this.defaults, options || {});
  1753. this.id = uniqueId();
  1754. this.manager = null;
  1755. // default is enable true
  1756. this.options.enable = ifUndefined(this.options.enable, true);
  1757. this.state = STATE_POSSIBLE;
  1758. this.simultaneous = {};
  1759. this.requireFail = [];
  1760. }
  1761. Recognizer.prototype = {
  1762. /**
  1763. * @virtual
  1764. * @type {Object}
  1765. */
  1766. defaults: {},
  1767. /**
  1768. * set options
  1769. * @param {Object} options
  1770. * @return {Recognizer}
  1771. */
  1772. set: function(options) {
  1773. assign(this.options, options);
  1774. // also update the touchAction, in case something changed about the directions/enabled state
  1775. this.manager && this.manager.touchAction.update();
  1776. return this;
  1777. },
  1778. /**
  1779. * recognize simultaneous with an other recognizer.
  1780. * @param {Recognizer} otherRecognizer
  1781. * @returns {Recognizer} this
  1782. */
  1783. recognizeWith: function(otherRecognizer) {
  1784. if (invokeArrayArg(otherRecognizer, 'recognizeWith', this)) {
  1785. return this;
  1786. }
  1787. var simultaneous = this.simultaneous;
  1788. otherRecognizer = getRecognizerByNameIfManager(otherRecognizer, this);
  1789. if (!simultaneous[otherRecognizer.id]) {
  1790. simultaneous[otherRecognizer.id] = otherRecognizer;
  1791. otherRecognizer.recognizeWith(this);
  1792. }
  1793. return this;
  1794. },
  1795. /**
  1796. * drop the simultaneous link. it doesnt remove the link on the other recognizer.
  1797. * @param {Recognizer} otherRecognizer
  1798. * @returns {Recognizer} this
  1799. */
  1800. dropRecognizeWith: function(otherRecognizer) {
  1801. if (invokeArrayArg(otherRecognizer, 'dropRecognizeWith', this)) {
  1802. return this;
  1803. }
  1804. otherRecognizer = getRecognizerByNameIfManager(otherRecognizer, this);
  1805. delete this.simultaneous[otherRecognizer.id];
  1806. return this;
  1807. },
  1808. /**
  1809. * recognizer can only run when an other is failing
  1810. * @param {Recognizer} otherRecognizer
  1811. * @returns {Recognizer} this
  1812. */
  1813. requireFailure: function(otherRecognizer) {
  1814. if (invokeArrayArg(otherRecognizer, 'requireFailure', this)) {
  1815. return this;
  1816. }
  1817. var requireFail = this.requireFail;
  1818. otherRecognizer = getRecognizerByNameIfManager(otherRecognizer, this);
  1819. if (inArray(requireFail, otherRecognizer) === -1) {
  1820. requireFail.push(otherRecognizer);
  1821. otherRecognizer.requireFailure(this);
  1822. }
  1823. return this;
  1824. },
  1825. /**
  1826. * drop the requireFailure link. it does not remove the link on the other recognizer.
  1827. * @param {Recognizer} otherRecognizer
  1828. * @returns {Recognizer} this
  1829. */
  1830. dropRequireFailure: function(otherRecognizer) {
  1831. if (invokeArrayArg(otherRecognizer, 'dropRequireFailure', this)) {
  1832. return this;
  1833. }
  1834. otherRecognizer = getRecognizerByNameIfManager(otherRecognizer, this);
  1835. var index = inArray(this.requireFail, otherRecognizer);
  1836. if (index > -1) {
  1837. this.requireFail.splice(index, 1);
  1838. }
  1839. return this;
  1840. },
  1841. /**
  1842. * has require failures boolean
  1843. * @returns {boolean}
  1844. */
  1845. hasRequireFailures: function() {
  1846. return this.requireFail.length > 0;
  1847. },
  1848. /**
  1849. * if the recognizer can recognize simultaneous with an other recognizer
  1850. * @param {Recognizer} otherRecognizer
  1851. * @returns {Boolean}
  1852. */
  1853. canRecognizeWith: function(otherRecognizer) {
  1854. return !!this.simultaneous[otherRecognizer.id];
  1855. },
  1856. /**
  1857. * You should use `tryEmit` instead of `emit` directly to check
  1858. * that all the needed recognizers has failed before emitting.
  1859. * @param {Object} input
  1860. */
  1861. emit: function(input) {
  1862. var self = this;
  1863. var state = this.state;
  1864. function emit(event) {
  1865. self.manager.emit(event, input);
  1866. }
  1867. // 'panstart' and 'panmove'
  1868. if (state < STATE_ENDED) {
  1869. emit(self.options.event + stateStr(state));
  1870. }
  1871. emit(self.options.event); // simple 'eventName' events
  1872. if (input.additionalEvent) { // additional event(panleft, panright, pinchin, pinchout...)
  1873. emit(input.additionalEvent);
  1874. }
  1875. // panend and pancancel
  1876. if (state >= STATE_ENDED) {
  1877. emit(self.options.event + stateStr(state));
  1878. }
  1879. },
  1880. /**
  1881. * Check that all the require failure recognizers has failed,
  1882. * if true, it emits a gesture event,
  1883. * otherwise, setup the state to FAILED.
  1884. * @param {Object} input
  1885. */
  1886. tryEmit: function(input) {
  1887. if (this.canEmit()) {
  1888. return this.emit(input);
  1889. }
  1890. // it's failing anyway
  1891. this.state = STATE_FAILED;
  1892. },
  1893. /**
  1894. * can we emit?
  1895. * @returns {boolean}
  1896. */
  1897. canEmit: function() {
  1898. var i = 0;
  1899. while (i < this.requireFail.length) {
  1900. if (!(this.requireFail[i].state & (STATE_FAILED | STATE_POSSIBLE))) {
  1901. return false;
  1902. }
  1903. i++;
  1904. }
  1905. return true;
  1906. },
  1907. /**
  1908. * update the recognizer
  1909. * @param {Object} inputData
  1910. */
  1911. recognize: function(inputData) {
  1912. // make a new copy of the inputData
  1913. // so we can change the inputData without messing up the other recognizers
  1914. var inputDataClone = assign({}, inputData);
  1915. // is is enabled and allow recognizing?
  1916. if (!boolOrFn(this.options.enable, [this, inputDataClone])) {
  1917. this.reset();
  1918. this.state = STATE_FAILED;
  1919. return;
  1920. }
  1921. // reset when we've reached the end
  1922. if (this.state & (STATE_RECOGNIZED | STATE_CANCELLED | STATE_FAILED)) {
  1923. this.state = STATE_POSSIBLE;
  1924. }
  1925. this.state = this.process(inputDataClone);
  1926. // the recognizer has recognized a gesture
  1927. // so trigger an event
  1928. if (this.state & (STATE_BEGAN | STATE_CHANGED | STATE_ENDED | STATE_CANCELLED)) {
  1929. this.tryEmit(inputDataClone);
  1930. }
  1931. },
  1932. /**
  1933. * return the state of the recognizer
  1934. * the actual recognizing happens in this method
  1935. * @virtual
  1936. * @param {Object} inputData
  1937. * @returns {Const} STATE
  1938. */
  1939. process: function(inputData) { }, // jshint ignore:line
  1940. /**
  1941. * return the preferred touch-action
  1942. * @virtual
  1943. * @returns {Array}
  1944. */
  1945. getTouchAction: function() { },
  1946. /**
  1947. * called when the gesture isn't allowed to recognize
  1948. * like when another is being recognized or it is disabled
  1949. * @virtual
  1950. */
  1951. reset: function() { }
  1952. };
  1953. /**
  1954. * get a usable string, used as event postfix
  1955. * @param {Const} state
  1956. * @returns {String} state
  1957. */
  1958. function stateStr(state) {
  1959. if (state & STATE_CANCELLED) {
  1960. return 'cancel';
  1961. } else if (state & STATE_ENDED) {
  1962. return 'end';
  1963. } else if (state & STATE_CHANGED) {
  1964. return 'move';
  1965. } else if (state & STATE_BEGAN) {
  1966. return 'start';
  1967. }
  1968. return '';
  1969. }
  1970. /**
  1971. * direction cons to string
  1972. * @param {Const} direction
  1973. * @returns {String}
  1974. */
  1975. function directionStr(direction) {
  1976. if (direction == DIRECTION_DOWN) {
  1977. return 'down';
  1978. } else if (direction == DIRECTION_UP) {
  1979. return 'up';
  1980. } else if (direction == DIRECTION_LEFT) {
  1981. return 'left';
  1982. } else if (direction == DIRECTION_RIGHT) {
  1983. return 'right';
  1984. }
  1985. return '';
  1986. }
  1987. /**
  1988. * get a recognizer by name if it is bound to a manager
  1989. * @param {Recognizer|String} otherRecognizer
  1990. * @param {Recognizer} recognizer
  1991. * @returns {Recognizer}
  1992. */
  1993. function getRecognizerByNameIfManager(otherRecognizer, recognizer) {
  1994. var manager = recognizer.manager;
  1995. if (manager) {
  1996. return manager.get(otherRecognizer);
  1997. }
  1998. return otherRecognizer;
  1999. }
  2000. /**
  2001. * This recognizer is just used as a base for the simple attribute recognizers.
  2002. * @constructor
  2003. * @extends Recognizer
  2004. */
  2005. function AttrRecognizer() {
  2006. Recognizer.apply(this, arguments);
  2007. }
  2008. inherit(AttrRecognizer, Recognizer, {
  2009. /**
  2010. * @namespace
  2011. * @memberof AttrRecognizer
  2012. */
  2013. defaults: {
  2014. /**
  2015. * @type {Number}
  2016. * @default 1
  2017. */
  2018. pointers: 1
  2019. },
  2020. /**
  2021. * Used to check if it the recognizer receives valid input, like input.distance > 10.
  2022. * @memberof AttrRecognizer
  2023. * @param {Object} input
  2024. * @returns {Boolean} recognized
  2025. */
  2026. attrTest: function(input) {
  2027. var optionPointers = this.options.pointers;
  2028. return optionPointers === 0 || input.pointers.length === optionPointers;
  2029. },
  2030. /**
  2031. * Process the input and return the state for the recognizer
  2032. * @memberof AttrRecognizer
  2033. * @param {Object} input
  2034. * @returns {*} State
  2035. */
  2036. process: function(input) {
  2037. var state = this.state;
  2038. var eventType = input.eventType;
  2039. var isRecognized = state & (STATE_BEGAN | STATE_CHANGED);
  2040. var isValid = this.attrTest(input);
  2041. // on cancel input and we've recognized before, return STATE_CANCELLED
  2042. if (isRecognized && (eventType & INPUT_CANCEL || !isValid)) {
  2043. return state | STATE_CANCELLED;
  2044. } else if (isRecognized || isValid) {
  2045. if (eventType & INPUT_END) {
  2046. return state | STATE_ENDED;
  2047. } else if (!(state & STATE_BEGAN)) {
  2048. return STATE_BEGAN;
  2049. }
  2050. return state | STATE_CHANGED;
  2051. }
  2052. return STATE_FAILED;
  2053. }
  2054. });
  2055. /**
  2056. * Pan
  2057. * Recognized when the pointer is down and moved in the allowed direction.
  2058. * @constructor
  2059. * @extends AttrRecognizer
  2060. */
  2061. function PanRecognizer() {
  2062. AttrRecognizer.apply(this, arguments);
  2063. this.pX = null;
  2064. this.pY = null;
  2065. }
  2066. inherit(PanRecognizer, AttrRecognizer, {
  2067. /**
  2068. * @namespace
  2069. * @memberof PanRecognizer
  2070. */
  2071. defaults: {
  2072. event: 'pan',
  2073. threshold: 10,
  2074. pointers: 1,
  2075. direction: DIRECTION_ALL
  2076. },
  2077. getTouchAction: function() {
  2078. var direction = this.options.direction;
  2079. var actions = [];
  2080. if (direction & DIRECTION_HORIZONTAL) {
  2081. actions.push(TOUCH_ACTION_PAN_Y);
  2082. }
  2083. if (direction & DIRECTION_VERTICAL) {
  2084. actions.push(TOUCH_ACTION_PAN_X);
  2085. }
  2086. return actions;
  2087. },
  2088. directionTest: function(input) {
  2089. var options = this.options;
  2090. var hasMoved = true;
  2091. var distance = input.distance;
  2092. var direction = input.direction;
  2093. var x = input.deltaX;
  2094. var y = input.deltaY;
  2095. // lock to axis?
  2096. if (!(direction & options.direction)) {
  2097. if (options.direction & DIRECTION_HORIZONTAL) {
  2098. direction = (x === 0) ? DIRECTION_NONE : (x < 0) ? DIRECTION_LEFT : DIRECTION_RIGHT;
  2099. hasMoved = x != this.pX;
  2100. distance = Math.abs(input.deltaX);
  2101. } else {
  2102. direction = (y === 0) ? DIRECTION_NONE : (y < 0) ? DIRECTION_UP : DIRECTION_DOWN;
  2103. hasMoved = y != this.pY;
  2104. distance = Math.abs(input.deltaY);
  2105. }
  2106. }
  2107. input.direction = direction;
  2108. return hasMoved && distance > options.threshold && direction & options.direction;
  2109. },
  2110. attrTest: function(input) {
  2111. return AttrRecognizer.prototype.attrTest.call(this, input) &&
  2112. (this.state & STATE_BEGAN || (!(this.state & STATE_BEGAN) && this.directionTest(input)));
  2113. },
  2114. emit: function(input) {
  2115. this.pX = input.deltaX;
  2116. this.pY = input.deltaY;
  2117. var direction = directionStr(input.direction);
  2118. if (direction) {
  2119. input.additionalEvent = this.options.event + direction;
  2120. }
  2121. this._super.emit.call(this, input);
  2122. }
  2123. });
  2124. /**
  2125. * Pinch
  2126. * Recognized when two or more pointers are moving toward (zoom-in) or away from each other (zoom-out).
  2127. * @constructor
  2128. * @extends AttrRecognizer
  2129. */
  2130. function PinchRecognizer() {
  2131. AttrRecognizer.apply(this, arguments);
  2132. }
  2133. inherit(PinchRecognizer, AttrRecognizer, {
  2134. /**
  2135. * @namespace
  2136. * @memberof PinchRecognizer
  2137. */
  2138. defaults: {
  2139. event: 'pinch',
  2140. threshold: 0,
  2141. pointers: 2
  2142. },
  2143. getTouchAction: function() {
  2144. return [TOUCH_ACTION_NONE];
  2145. },
  2146. attrTest: function(input) {
  2147. return this._super.attrTest.call(this, input) &&
  2148. (Math.abs(input.scale - 1) > this.options.threshold || this.state & STATE_BEGAN);
  2149. },
  2150. emit: function(input) {
  2151. if (input.scale !== 1) {
  2152. var inOut = input.scale < 1 ? 'in' : 'out';
  2153. input.additionalEvent = this.options.event + inOut;
  2154. }
  2155. this._super.emit.call(this, input);
  2156. }
  2157. });
  2158. /**
  2159. * Press
  2160. * Recognized when the pointer is down for x ms without any movement.
  2161. * @constructor
  2162. * @extends Recognizer
  2163. */
  2164. function PressRecognizer() {
  2165. Recognizer.apply(this, arguments);
  2166. this._timer = null;
  2167. this._input = null;
  2168. }
  2169. inherit(PressRecognizer, Recognizer, {
  2170. /**
  2171. * @namespace
  2172. * @memberof PressRecognizer
  2173. */
  2174. defaults: {
  2175. event: 'press',
  2176. pointers: 1,
  2177. time: 251, // minimal time of the pointer to be pressed
  2178. threshold: 9 // a minimal movement is ok, but keep it low
  2179. },
  2180. getTouchAction: function() {
  2181. return [TOUCH_ACTION_AUTO];
  2182. },
  2183. process: function(input) {
  2184. var options = this.options;
  2185. var validPointers = input.pointers.length === options.pointers;
  2186. var validMovement = input.distance < options.threshold;
  2187. var validTime = input.deltaTime > options.time;
  2188. this._input = input;
  2189. // we only allow little movement
  2190. // and we've reached an end event, so a tap is possible
  2191. if (!validMovement || !validPointers || (input.eventType & (INPUT_END | INPUT_CANCEL) && !validTime)) {
  2192. this.reset();
  2193. } else if (input.eventType & INPUT_START) {
  2194. this.reset();
  2195. this._timer = setTimeoutContext(function() {
  2196. this.state = STATE_RECOGNIZED;
  2197. this.tryEmit();
  2198. }, options.time, this);
  2199. } else if (input.eventType & INPUT_END) {
  2200. return STATE_RECOGNIZED;
  2201. }
  2202. return STATE_FAILED;
  2203. },
  2204. reset: function() {
  2205. clearTimeout(this._timer);
  2206. },
  2207. emit: function(input) {
  2208. if (this.state !== STATE_RECOGNIZED) {
  2209. return;
  2210. }
  2211. if (input && (input.eventType & INPUT_END)) {
  2212. this.manager.emit(this.options.event + 'up', input);
  2213. } else {
  2214. this._input.timeStamp = now();
  2215. this.manager.emit(this.options.event, this._input);
  2216. }
  2217. }
  2218. });
  2219. /**
  2220. * Rotate
  2221. * Recognized when two or more pointer are moving in a circular motion.
  2222. * @constructor
  2223. * @extends AttrRecognizer
  2224. */
  2225. function RotateRecognizer() {
  2226. AttrRecognizer.apply(this, arguments);
  2227. }
  2228. inherit(RotateRecognizer, AttrRecognizer, {
  2229. /**
  2230. * @namespace
  2231. * @memberof RotateRecognizer
  2232. */
  2233. defaults: {
  2234. event: 'rotate',
  2235. threshold: 0,
  2236. pointers: 2
  2237. },
  2238. getTouchAction: function() {
  2239. return [TOUCH_ACTION_NONE];
  2240. },
  2241. attrTest: function(input) {
  2242. return this._super.attrTest.call(this, input) &&
  2243. (Math.abs(input.rotation) > this.options.threshold || this.state & STATE_BEGAN);
  2244. }
  2245. });
  2246. /**
  2247. * Swipe
  2248. * Recognized when the pointer is moving fast (velocity), with enough distance in the allowed direction.
  2249. * @constructor
  2250. * @extends AttrRecognizer
  2251. */
  2252. function SwipeRecognizer() {
  2253. AttrRecognizer.apply(this, arguments);
  2254. }
  2255. inherit(SwipeRecognizer, AttrRecognizer, {
  2256. /**
  2257. * @namespace
  2258. * @memberof SwipeRecognizer
  2259. */
  2260. defaults: {
  2261. event: 'swipe',
  2262. threshold: 10,
  2263. velocity: 0.3,
  2264. direction: DIRECTION_HORIZONTAL | DIRECTION_VERTICAL,
  2265. pointers: 1
  2266. },
  2267. getTouchAction: function() {
  2268. return PanRecognizer.prototype.getTouchAction.call(this);
  2269. },
  2270. attrTest: function(input) {
  2271. var direction = this.options.direction;
  2272. var velocity;
  2273. if (direction & (DIRECTION_HORIZONTAL | DIRECTION_VERTICAL)) {
  2274. velocity = input.overallVelocity;
  2275. } else if (direction & DIRECTION_HORIZONTAL) {
  2276. velocity = input.overallVelocityX;
  2277. } else if (direction & DIRECTION_VERTICAL) {
  2278. velocity = input.overallVelocityY;
  2279. }
  2280. return this._super.attrTest.call(this, input) &&
  2281. direction & input.offsetDirection &&
  2282. input.distance > this.options.threshold &&
  2283. input.maxPointers == this.options.pointers &&
  2284. abs(velocity) > this.options.velocity && input.eventType & INPUT_END;
  2285. },
  2286. emit: function(input) {
  2287. var direction = directionStr(input.offsetDirection);
  2288. if (direction) {
  2289. this.manager.emit(this.options.event + direction, input);
  2290. }
  2291. this.manager.emit(this.options.event, input);
  2292. }
  2293. });
  2294. /**
  2295. * A tap is ecognized when the pointer is doing a small tap/click. Multiple taps are recognized if they occur
  2296. * between the given interval and position. The delay option can be used to recognize multi-taps without firing
  2297. * a single tap.
  2298. *
  2299. * The eventData from the emitted event contains the property `tapCount`, which contains the amount of
  2300. * multi-taps being recognized.
  2301. * @constructor
  2302. * @extends Recognizer
  2303. */
  2304. function TapRecognizer() {
  2305. Recognizer.apply(this, arguments);
  2306. // previous time and center,
  2307. // used for tap counting
  2308. this.pTime = false;
  2309. this.pCenter = false;
  2310. this._timer = null;
  2311. this._input = null;
  2312. this.count = 0;
  2313. }
  2314. inherit(TapRecognizer, Recognizer, {
  2315. /**
  2316. * @namespace
  2317. * @memberof PinchRecognizer
  2318. */
  2319. defaults: {
  2320. event: 'tap',
  2321. pointers: 1,
  2322. taps: 1,
  2323. interval: 300, // max time between the multi-tap taps
  2324. time: 250, // max time of the pointer to be down (like finger on the screen)
  2325. threshold: 9, // a minimal movement is ok, but keep it low
  2326. posThreshold: 10 // a multi-tap can be a bit off the initial position
  2327. },
  2328. getTouchAction: function() {
  2329. return [TOUCH_ACTION_MANIPULATION];
  2330. },
  2331. process: function(input) {
  2332. var options = this.options;
  2333. var validPointers = input.pointers.length === options.pointers;
  2334. var validMovement = input.distance < options.threshold;
  2335. var validTouchTime = input.deltaTime < options.time;
  2336. this.reset();
  2337. if ((input.eventType & INPUT_START) && (this.count === 0)) {
  2338. return this.failTimeout();
  2339. }
  2340. // we only allow little movement
  2341. // and we've reached an end event, so a tap is possible
  2342. if (validMovement && validTouchTime && validPointers) {
  2343. if (input.eventType != INPUT_END) {
  2344. return this.failTimeout();
  2345. }
  2346. var validInterval = this.pTime ? (input.timeStamp - this.pTime < options.interval) : true;
  2347. var validMultiTap = !this.pCenter || getDistance(this.pCenter, input.center) < options.posThreshold;
  2348. this.pTime = input.timeStamp;
  2349. this.pCenter = input.center;
  2350. if (!validMultiTap || !validInterval) {
  2351. this.count = 1;
  2352. } else {
  2353. this.count += 1;
  2354. }
  2355. this._input = input;
  2356. // if tap count matches we have recognized it,
  2357. // else it has began recognizing...
  2358. var tapCount = this.count % options.taps;
  2359. if (tapCount === 0) {
  2360. // no failing requirements, immediately trigger the tap event
  2361. // or wait as long as the multitap interval to trigger
  2362. if (!this.hasRequireFailures()) {
  2363. return STATE_RECOGNIZED;
  2364. } else {
  2365. this._timer = setTimeoutContext(function() {
  2366. this.state = STATE_RECOGNIZED;
  2367. this.tryEmit();
  2368. }, options.interval, this);
  2369. return STATE_BEGAN;
  2370. }
  2371. }
  2372. }
  2373. return STATE_FAILED;
  2374. },
  2375. failTimeout: function() {
  2376. this._timer = setTimeoutContext(function() {
  2377. this.state = STATE_FAILED;
  2378. }, this.options.interval, this);
  2379. return STATE_FAILED;
  2380. },
  2381. reset: function() {
  2382. clearTimeout(this._timer);
  2383. },
  2384. emit: function() {
  2385. if (this.state == STATE_RECOGNIZED) {
  2386. this._input.tapCount = this.count;
  2387. this.manager.emit(this.options.event, this._input);
  2388. }
  2389. }
  2390. });
  2391. /**
  2392. * Simple way to create a manager with a default set of recognizers.
  2393. * @param {HTMLElement} element
  2394. * @param {Object} [options]
  2395. * @constructor
  2396. */
  2397. function Hammer(element, options) {
  2398. options = options || {};
  2399. options.recognizers = ifUndefined(options.recognizers, Hammer.defaults.preset);
  2400. return new Manager(element, options);
  2401. }
  2402. /**
  2403. * @const {string}
  2404. */
  2405. Hammer.VERSION = '2.0.8';
  2406. /**
  2407. * default settings
  2408. * @namespace
  2409. */
  2410. Hammer.defaults = {
  2411. /**
  2412. * set if DOM events are being triggered.
  2413. * But this is slower and unused by simple implementations, so disabled by default.
  2414. * @type {Boolean}
  2415. * @default false
  2416. */
  2417. domEvents: false,
  2418. /**
  2419. * The value for the touchAction property/fallback.
  2420. * When set to `compute` it will magically set the correct value based on the added recognizers.
  2421. * @type {String}
  2422. * @default compute
  2423. */
  2424. touchAction: TOUCH_ACTION_COMPUTE,
  2425. /**
  2426. * @type {Boolean}
  2427. * @default true
  2428. */
  2429. enable: true,
  2430. /**
  2431. * EXPERIMENTAL FEATURE -- can be removed/changed
  2432. * Change the parent input target element.
  2433. * If Null, then it is being set the to main element.
  2434. * @type {Null|EventTarget}
  2435. * @default null
  2436. */
  2437. inputTarget: null,
  2438. /**
  2439. * force an input class
  2440. * @type {Null|Function}
  2441. * @default null
  2442. */
  2443. inputClass: null,
  2444. /**
  2445. * Default recognizer setup when calling `Hammer()`
  2446. * When creating a new Manager these will be skipped.
  2447. * @type {Array}
  2448. */
  2449. preset: [
  2450. // RecognizerClass, options, [recognizeWith, ...], [requireFailure, ...]
  2451. [RotateRecognizer, {enable: false}],
  2452. [PinchRecognizer, {enable: false}, ['rotate']],
  2453. [SwipeRecognizer, {direction: DIRECTION_HORIZONTAL}],
  2454. [PanRecognizer, {direction: DIRECTION_HORIZONTAL}, ['swipe']],
  2455. [TapRecognizer],
  2456. [TapRecognizer, {event: 'doubletap', taps: 2}, ['tap']],
  2457. [PressRecognizer]
  2458. ],
  2459. /**
  2460. * Some CSS properties can be used to improve the working of Hammer.
  2461. * Add them to this method and they will be set when creating a new Manager.
  2462. * @namespace
  2463. */
  2464. cssProps: {
  2465. /**
  2466. * Disables text selection to improve the dragging gesture. Mainly for desktop browsers.
  2467. * @type {String}
  2468. * @default 'none'
  2469. */
  2470. userSelect: 'none',
  2471. /**
  2472. * Disable the Windows Phone grippers when pressing an element.
  2473. * @type {String}
  2474. * @default 'none'
  2475. */
  2476. touchSelect: 'none',
  2477. /**
  2478. * Disables the default callout shown when you touch and hold a touch target.
  2479. * On iOS, when you touch and hold a touch target such as a link, Safari displays
  2480. * a callout containing information about the link. This property allows you to disable that callout.
  2481. * @type {String}
  2482. * @default 'none'
  2483. */
  2484. touchCallout: 'none',
  2485. /**
  2486. * Specifies whether zooming is enabled. Used by IE10>
  2487. * @type {String}
  2488. * @default 'none'
  2489. */
  2490. contentZooming: 'none',
  2491. /**
  2492. * Specifies that an entire element should be draggable instead of its contents. Mainly for desktop browsers.
  2493. * @type {String}
  2494. * @default 'none'
  2495. */
  2496. userDrag: 'none',
  2497. /**
  2498. * Overrides the highlight color shown when the user taps a link or a JavaScript
  2499. * clickable element in iOS. This property obeys the alpha value, if specified.
  2500. * @type {String}
  2501. * @default 'rgba(0,0,0,0)'
  2502. */
  2503. tapHighlightColor: 'rgba(0,0,0,0)'
  2504. }
  2505. };
  2506. var STOP = 1;
  2507. var FORCED_STOP = 2;
  2508. /**
  2509. * Manager
  2510. * @param {HTMLElement} element
  2511. * @param {Object} [options]
  2512. * @constructor
  2513. */
  2514. function Manager(element, options) {
  2515. this.options = assign({}, Hammer.defaults, options || {});
  2516. this.options.inputTarget = this.options.inputTarget || element;
  2517. this.handlers = {};
  2518. this.session = {};
  2519. this.recognizers = [];
  2520. this.oldCssProps = {};
  2521. this.element = element;
  2522. this.input = createInputInstance(this);
  2523. this.touchAction = new TouchAction(this, this.options.touchAction);
  2524. toggleCssProps(this, true);
  2525. each(this.options.recognizers, function(item) {
  2526. var recognizer = this.add(new (item[0])(item[1]));
  2527. item[2] && recognizer.recognizeWith(item[2]);
  2528. item[3] && recognizer.requireFailure(item[3]);
  2529. }, this);
  2530. }
  2531. Manager.prototype = {
  2532. /**
  2533. * set options
  2534. * @param {Object} options
  2535. * @returns {Manager}
  2536. */
  2537. set: function(options) {
  2538. assign(this.options, options);
  2539. // Options that need a little more setup
  2540. if (options.touchAction) {
  2541. this.touchAction.update();
  2542. }
  2543. if (options.inputTarget) {
  2544. // Clean up existing event listeners and reinitialize
  2545. this.input.destroy();
  2546. this.input.target = options.inputTarget;
  2547. this.input.init();
  2548. }
  2549. return this;
  2550. },
  2551. /**
  2552. * stop recognizing for this session.
  2553. * This session will be discarded, when a new [input]start event is fired.
  2554. * When forced, the recognizer cycle is stopped immediately.
  2555. * @param {Boolean} [force]
  2556. */
  2557. stop: function(force) {
  2558. this.session.stopped = force ? FORCED_STOP : STOP;
  2559. },
  2560. /**
  2561. * run the recognizers!
  2562. * called by the inputHandler function on every movement of the pointers (touches)
  2563. * it walks through all the recognizers and tries to detect the gesture that is being made
  2564. * @param {Object} inputData
  2565. */
  2566. recognize: function(inputData) {
  2567. var session = this.session;
  2568. if (session.stopped) {
  2569. return;
  2570. }
  2571. // run the touch-action polyfill
  2572. this.touchAction.preventDefaults(inputData);
  2573. var recognizer;
  2574. var recognizers = this.recognizers;
  2575. // this holds the recognizer that is being recognized.
  2576. // so the recognizer's state needs to be BEGAN, CHANGED, ENDED or RECOGNIZED
  2577. // if no recognizer is detecting a thing, it is set to `null`
  2578. var curRecognizer = session.curRecognizer;
  2579. // reset when the last recognizer is recognized
  2580. // or when we're in a new session
  2581. if (!curRecognizer || (curRecognizer && curRecognizer.state & STATE_RECOGNIZED)) {
  2582. curRecognizer = session.curRecognizer = null;
  2583. }
  2584. var i = 0;
  2585. while (i < recognizers.length) {
  2586. recognizer = recognizers[i];
  2587. // find out if we are allowed try to recognize the input for this one.
  2588. // 1. allow if the session is NOT forced stopped (see the .stop() method)
  2589. // 2. allow if we still haven't recognized a gesture in this session, or the this recognizer is the one
  2590. // that is being recognized.
  2591. // 3. allow if the recognizer is allowed to run simultaneous with the current recognized recognizer.
  2592. // this can be setup with the `recognizeWith()` method on the recognizer.
  2593. if (session.stopped !== FORCED_STOP && ( // 1
  2594. !curRecognizer || recognizer == curRecognizer || // 2
  2595. recognizer.canRecognizeWith(curRecognizer))) { // 3
  2596. recognizer.recognize(inputData);
  2597. } else {
  2598. recognizer.reset();
  2599. }
  2600. // if the recognizer has been recognizing the input as a valid gesture, we want to store this one as the
  2601. // current active recognizer. but only if we don't already have an active recognizer
  2602. if (!curRecognizer && recognizer.state & (STATE_BEGAN | STATE_CHANGED | STATE_ENDED)) {
  2603. curRecognizer = session.curRecognizer = recognizer;
  2604. }
  2605. i++;
  2606. }
  2607. },
  2608. /**
  2609. * get a recognizer by its event name.
  2610. * @param {Recognizer|String} recognizer
  2611. * @returns {Recognizer|Null}
  2612. */
  2613. get: function(recognizer) {
  2614. if (recognizer instanceof Recognizer) {
  2615. return recognizer;
  2616. }
  2617. var recognizers = this.recognizers;
  2618. for (var i = 0; i < recognizers.length; i++) {
  2619. if (recognizers[i].options.event == recognizer) {
  2620. return recognizers[i];
  2621. }
  2622. }
  2623. return null;
  2624. },
  2625. /**
  2626. * add a recognizer to the manager
  2627. * existing recognizers with the same event name will be removed
  2628. * @param {Recognizer} recognizer
  2629. * @returns {Recognizer|Manager}
  2630. */
  2631. add: function(recognizer) {
  2632. if (invokeArrayArg(recognizer, 'add', this)) {
  2633. return this;
  2634. }
  2635. // remove existing
  2636. var existing = this.get(recognizer.options.event);
  2637. if (existing) {
  2638. this.remove(existing);
  2639. }
  2640. this.recognizers.push(recognizer);
  2641. recognizer.manager = this;
  2642. this.touchAction.update();
  2643. return recognizer;
  2644. },
  2645. /**
  2646. * remove a recognizer by name or instance
  2647. * @param {Recognizer|String} recognizer
  2648. * @returns {Manager}
  2649. */
  2650. remove: function(recognizer) {
  2651. if (invokeArrayArg(recognizer, 'remove', this)) {
  2652. return this;
  2653. }
  2654. recognizer = this.get(recognizer);
  2655. // let's make sure this recognizer exists
  2656. if (recognizer) {
  2657. var recognizers = this.recognizers;
  2658. var index = inArray(recognizers, recognizer);
  2659. if (index !== -1) {
  2660. recognizers.splice(index, 1);
  2661. this.touchAction.update();
  2662. }
  2663. }
  2664. return this;
  2665. },
  2666. /**
  2667. * bind event
  2668. * @param {String} events
  2669. * @param {Function} handler
  2670. * @returns {EventEmitter} this
  2671. */
  2672. on: function(events, handler) {
  2673. if (events === undefined) {
  2674. return;
  2675. }
  2676. if (handler === undefined) {
  2677. return;
  2678. }
  2679. var handlers = this.handlers;
  2680. each(splitStr(events), function(event) {
  2681. handlers[event] = handlers[event] || [];
  2682. handlers[event].push(handler);
  2683. });
  2684. return this;
  2685. },
  2686. /**
  2687. * unbind event, leave emit blank to remove all handlers
  2688. * @param {String} events
  2689. * @param {Function} [handler]
  2690. * @returns {EventEmitter} this
  2691. */
  2692. off: function(events, handler) {
  2693. if (events === undefined) {
  2694. return;
  2695. }
  2696. var handlers = this.handlers;
  2697. each(splitStr(events), function(event) {
  2698. if (!handler) {
  2699. delete handlers[event];
  2700. } else {
  2701. handlers[event] && handlers[event].splice(inArray(handlers[event], handler), 1);
  2702. }
  2703. });
  2704. return this;
  2705. },
  2706. /**
  2707. * emit event to the listeners
  2708. * @param {String} event
  2709. * @param {Object} data
  2710. */
  2711. emit: function(event, data) {
  2712. // we also want to trigger dom events
  2713. if (this.options.domEvents) {
  2714. triggerDomEvent(event, data);
  2715. }
  2716. // no handlers, so skip it all
  2717. var handlers = this.handlers[event] && this.handlers[event].slice();
  2718. if (!handlers || !handlers.length) {
  2719. return;
  2720. }
  2721. data.type = event;
  2722. data.preventDefault = function() {
  2723. data.srcEvent.preventDefault();
  2724. };
  2725. var i = 0;
  2726. while (i < handlers.length) {
  2727. handlers[i](data);
  2728. i++;
  2729. }
  2730. },
  2731. /**
  2732. * destroy the manager and unbinds all events
  2733. * it doesn't unbind dom events, that is the user own responsibility
  2734. */
  2735. destroy: function() {
  2736. this.element && toggleCssProps(this, false);
  2737. this.handlers = {};
  2738. this.session = {};
  2739. this.input.destroy();
  2740. this.element = null;
  2741. }
  2742. };
  2743. /**
  2744. * add/remove the css properties as defined in manager.options.cssProps
  2745. * @param {Manager} manager
  2746. * @param {Boolean} add
  2747. */
  2748. function toggleCssProps(manager, add) {
  2749. var element = manager.element;
  2750. if (!element.style) {
  2751. return;
  2752. }
  2753. var prop;
  2754. each(manager.options.cssProps, function(value, name) {
  2755. prop = prefixed(element.style, name);
  2756. if (add) {
  2757. manager.oldCssProps[prop] = element.style[prop];
  2758. element.style[prop] = value;
  2759. } else {
  2760. element.style[prop] = manager.oldCssProps[prop] || '';
  2761. }
  2762. });
  2763. if (!add) {
  2764. manager.oldCssProps = {};
  2765. }
  2766. }
  2767. /**
  2768. * trigger dom event
  2769. * @param {String} event
  2770. * @param {Object} data
  2771. */
  2772. function triggerDomEvent(event, data) {
  2773. var gestureEvent = document.createEvent('Event');
  2774. gestureEvent.initEvent(event, true, true);
  2775. gestureEvent.gesture = data;
  2776. data.target.dispatchEvent(gestureEvent);
  2777. }
  2778. assign(Hammer, {
  2779. INPUT_START: INPUT_START,
  2780. INPUT_MOVE: INPUT_MOVE,
  2781. INPUT_END: INPUT_END,
  2782. INPUT_CANCEL: INPUT_CANCEL,
  2783. STATE_POSSIBLE: STATE_POSSIBLE,
  2784. STATE_BEGAN: STATE_BEGAN,
  2785. STATE_CHANGED: STATE_CHANGED,
  2786. STATE_ENDED: STATE_ENDED,
  2787. STATE_RECOGNIZED: STATE_RECOGNIZED,
  2788. STATE_CANCELLED: STATE_CANCELLED,
  2789. STATE_FAILED: STATE_FAILED,
  2790. DIRECTION_NONE: DIRECTION_NONE,
  2791. DIRECTION_LEFT: DIRECTION_LEFT,
  2792. DIRECTION_RIGHT: DIRECTION_RIGHT,
  2793. DIRECTION_UP: DIRECTION_UP,
  2794. DIRECTION_DOWN: DIRECTION_DOWN,
  2795. DIRECTION_HORIZONTAL: DIRECTION_HORIZONTAL,
  2796. DIRECTION_VERTICAL: DIRECTION_VERTICAL,
  2797. DIRECTION_ALL: DIRECTION_ALL,
  2798. Manager: Manager,
  2799. Input: Input,
  2800. TouchAction: TouchAction,
  2801. TouchInput: TouchInput,
  2802. MouseInput: MouseInput,
  2803. PointerEventInput: PointerEventInput,
  2804. TouchMouseInput: TouchMouseInput,
  2805. SingleTouchInput: SingleTouchInput,
  2806. Recognizer: Recognizer,
  2807. AttrRecognizer: AttrRecognizer,
  2808. Tap: TapRecognizer,
  2809. Pan: PanRecognizer,
  2810. Swipe: SwipeRecognizer,
  2811. Pinch: PinchRecognizer,
  2812. Rotate: RotateRecognizer,
  2813. Press: PressRecognizer,
  2814. on: addEventListeners,
  2815. off: removeEventListeners,
  2816. each: each,
  2817. merge: merge,
  2818. extend: extend,
  2819. assign: assign,
  2820. inherit: inherit,
  2821. bindFn: bindFn,
  2822. prefixed: prefixed
  2823. });
  2824. // this prevents errors when Hammer is loaded in the presence of an AMD
  2825. // style loader but by script tag, not by the loader.
  2826. var freeGlobal = (typeof window !== 'undefined' ? window : (typeof self !== 'undefined' ? self : {})); // jshint ignore:line
  2827. freeGlobal.Hammer = Hammer;
  2828. if (typeof define === 'function' && define.amd) {
  2829. define(function() {
  2830. return Hammer;
  2831. });
  2832. } else if (typeof module != 'undefined' && module.exports) {
  2833. module.exports = Hammer;
  2834. } else {
  2835. window[exportName] = Hammer;
  2836. }
  2837. })(window, document, 'Hammer');
  2838. + function($) {
  2839. "use strict";
  2840. var defaults;
  2841. $.modal = function(params, onOpen) {
  2842. params = $.extend({}, defaults, params);
  2843. var buttons = params.buttons;
  2844. var buttonsHtml = buttons.map(function(d, i) {
  2845. return '<a href="javascript:;" class="weui-dialog__btn ' + (d.className || "") + '">' + d.text + '</a>';
  2846. }).join("");
  2847. var tpl = '<div class="weui-dialog">' +
  2848. '<div class="weui-dialog__hd"><strong class="weui-dialog__title">' + params.title + '</strong></div>' +
  2849. ( params.text ? '<div class="weui-dialog__bd">'+params.text+'</div>' : '')+
  2850. '<div class="weui-dialog__ft">' + buttonsHtml + '</div>' +
  2851. '</div>';
  2852. var dialog = $.openModal(tpl, onOpen);
  2853. dialog.find(".weui-dialog__btn").each(function(i, e) {
  2854. var el = $(e);
  2855. el.click(function() {
  2856. //先关闭对话框,再调用回调函数
  2857. if(params.autoClose) $.closeModal();
  2858. if(buttons[i].onClick) {
  2859. buttons[i].onClick.call(dialog);
  2860. }
  2861. });
  2862. });
  2863. return dialog;
  2864. };
  2865. $.openModal = function(tpl, onOpen) {
  2866. var mask = $("<div class='weui-mask'></div>").appendTo(document.body);
  2867. mask.show();
  2868. var dialog = $(tpl).appendTo(document.body);
  2869. if (onOpen) {
  2870. dialog.transitionEnd(function () {
  2871. onOpen.call(dialog);
  2872. });
  2873. }
  2874. dialog.show();
  2875. mask.addClass("weui-mask--visible");
  2876. dialog.addClass("weui-dialog--visible");
  2877. return dialog;
  2878. }
  2879. $.closeModal = function() {
  2880. $(".weui-mask--visible").removeClass("weui-mask--visible").transitionEnd(function() {
  2881. $(this).remove();
  2882. });
  2883. $(".weui-dialog--visible").removeClass("weui-dialog--visible").transitionEnd(function() {
  2884. $(this).remove();
  2885. });
  2886. };
  2887. $.alert = function(text, title, onOK) {
  2888. var config;
  2889. if (typeof text === 'object') {
  2890. config = text;
  2891. } else {
  2892. if (typeof title === 'function') {
  2893. onOK = arguments[1];
  2894. title = undefined;
  2895. }
  2896. config = {
  2897. text: text,
  2898. title: title,
  2899. onOK: onOK
  2900. }
  2901. }
  2902. return $.modal({
  2903. text: config.text,
  2904. title: config.title,
  2905. buttons: [{
  2906. text: defaults.buttonOK,
  2907. className: "primary",
  2908. onClick: config.onOK
  2909. }]
  2910. });
  2911. }
  2912. $.confirm = function(text, title, onOK, onCancel) {
  2913. var config;
  2914. if (typeof text === 'object') {
  2915. config = text
  2916. } else {
  2917. if (typeof title === 'function') {
  2918. onCancel = arguments[2];
  2919. onOK = arguments[1];
  2920. title = undefined;
  2921. }
  2922. config = {
  2923. text: text,
  2924. title: title,
  2925. onOK: onOK,
  2926. onCancel: onCancel
  2927. }
  2928. }
  2929. return $.modal({
  2930. text: config.text,
  2931. title: config.title,
  2932. buttons: [
  2933. {
  2934. text: defaults.buttonCancel,
  2935. className: "default",
  2936. onClick: config.onCancel
  2937. },
  2938. {
  2939. text: defaults.buttonOK,
  2940. className: "primary",
  2941. onClick: config.onOK
  2942. }]
  2943. });
  2944. };
  2945. //如果参数过多,建议通过 config 对象进行配置,而不是传入多个参数。
  2946. $.prompt = function(text, title, onOK, onCancel, input) {
  2947. var config;
  2948. if (typeof text === 'object') {
  2949. config = text;
  2950. } else {
  2951. if (typeof title === 'function') {
  2952. input = arguments[3];
  2953. onCancel = arguments[2];
  2954. onOK = arguments[1];
  2955. title = undefined;
  2956. }
  2957. config = {
  2958. text: text,
  2959. title: title,
  2960. input: input,
  2961. onOK: onOK,
  2962. onCancel: onCancel,
  2963. empty: false //allow empty
  2964. }
  2965. }
  2966. var modal = $.modal({
  2967. text: '<p class="weui-prompt-text">'+(config.text || '')+'</p><input type="text" class="weui-input weui-prompt-input" id="weui-prompt-input" value="' + (config.input || '') + '" />',
  2968. title: config.title,
  2969. autoClose: false,
  2970. buttons: [
  2971. {
  2972. text: defaults.buttonCancel,
  2973. className: "default",
  2974. onClick: function () {
  2975. $.closeModal();
  2976. config.onCancel && config.onCancel.call(modal);
  2977. }
  2978. },
  2979. {
  2980. text: defaults.buttonOK,
  2981. className: "primary",
  2982. onClick: function() {
  2983. var input = $("#weui-prompt-input").val();
  2984. if (!config.empty && (input === "" || input === null)) {
  2985. modal.find('.weui-prompt-input').focus()[0].select();
  2986. return false;
  2987. }
  2988. $.closeModal();
  2989. config.onOK && config.onOK.call(modal, input);
  2990. }
  2991. }]
  2992. }, function () {
  2993. this.find('.weui-prompt-input').focus()[0].select();
  2994. });
  2995. return modal;
  2996. };
  2997. //如果参数过多,建议通过 config 对象进行配置,而不是传入多个参数。
  2998. $.login = function(text, title, onOK, onCancel, username, password) {
  2999. var config;
  3000. if (typeof text === 'object') {
  3001. config = text;
  3002. } else {
  3003. if (typeof title === 'function') {
  3004. password = arguments[4];
  3005. username = arguments[3];
  3006. onCancel = arguments[2];
  3007. onOK = arguments[1];
  3008. title = undefined;
  3009. }
  3010. config = {
  3011. text: text,
  3012. title: title,
  3013. username: username,
  3014. password: password,
  3015. onOK: onOK,
  3016. onCancel: onCancel
  3017. }
  3018. }
  3019. var modal = $.modal({
  3020. text: '<p class="weui-prompt-text">'+(config.text || '')+'</p>' +
  3021. '<input type="text" class="weui-input weui-prompt-input" id="weui-prompt-username" value="' + (config.username || '') + '" placeholder="输入用户名" />' +
  3022. '<input type="password" class="weui-input weui-prompt-input" id="weui-prompt-password" value="' + (config.password || '') + '" placeholder="输入密码" />',
  3023. title: config.title,
  3024. autoClose: false,
  3025. buttons: [
  3026. {
  3027. text: defaults.buttonCancel,
  3028. className: "default",
  3029. onClick: function () {
  3030. $.closeModal();
  3031. config.onCancel && config.onCancel.call(modal);
  3032. }
  3033. }, {
  3034. text: defaults.buttonOK,
  3035. className: "primary",
  3036. onClick: function() {
  3037. var username = $("#weui-prompt-username").val();
  3038. var password = $("#weui-prompt-password").val();
  3039. if (!config.empty && (username === "" || username === null)) {
  3040. modal.find('#weui-prompt-username').focus()[0].select();
  3041. return false;
  3042. }
  3043. if (!config.empty && (password === "" || password === null)) {
  3044. modal.find('#weui-prompt-password').focus()[0].select();
  3045. return false;
  3046. }
  3047. $.closeModal();
  3048. config.onOK && config.onOK.call(modal, username, password);
  3049. }
  3050. }]
  3051. }, function () {
  3052. this.find('#weui-prompt-username').focus()[0].select();
  3053. });
  3054. return modal;
  3055. };
  3056. defaults = $.modal.prototype.defaults = {
  3057. title: "提示",
  3058. text: undefined,
  3059. buttonOK: "确定",
  3060. buttonCancel: "取消",
  3061. buttons: [{
  3062. text: "确定",
  3063. className: "primary"
  3064. }],
  3065. autoClose: true //点击按钮自动关闭对话框,如果你不希望点击按钮就关闭对话框,可以把这个设置为false
  3066. };
  3067. }($);
  3068. + function($) {
  3069. "use strict";
  3070. var defaults;
  3071. var show = function(html, className) {
  3072. className = className || "";
  3073. var mask = $("<div class='weui-mask_transparent'></div>").appendTo(document.body);
  3074. var tpl = '<div class="weui-toast ' + className + '">' + html + '</div>';
  3075. var dialog = $(tpl).appendTo(document.body);
  3076. dialog.show();
  3077. dialog.addClass("weui-toast--visible");
  3078. };
  3079. var hide = function(callback) {
  3080. $(".weui-mask_transparent").remove();
  3081. $(".weui-toast--visible").removeClass("weui-toast--visible").transitionEnd(function() {
  3082. var $this = $(this);
  3083. $this.remove();
  3084. callback && callback($this);
  3085. });
  3086. }
  3087. $.toast = function(text, style, callback) {
  3088. if(typeof style === "function") {
  3089. callback = style;
  3090. }
  3091. var className, iconClassName = 'weui-icon-success-no-circle';
  3092. var duration = toastDefaults.duration;
  3093. if(style == "cancel") {
  3094. className = "weui-toast_cancel";
  3095. iconClassName = 'weui-icon-cancel'
  3096. } else if(style == "forbidden") {
  3097. className = "weui-toast--forbidden";
  3098. iconClassName = 'weui-icon-warn'
  3099. } else if(style == "text") {
  3100. className = "weui-toast--text";
  3101. } else if(typeof style === typeof 1) {
  3102. duration = style
  3103. }
  3104. show('<i class="' + iconClassName + ' weui-icon_toast"></i><p class="weui-toast_content">' + (text || "已经完成") + '</p>', className);
  3105. setTimeout(function() {
  3106. hide(callback);
  3107. }, duration);
  3108. }
  3109. $.showLoading = function(text) {
  3110. var html = '<div class="weui_loading">';
  3111. html += '<i class="weui-loading weui-icon_toast"></i>';
  3112. html += '</div>';
  3113. html += '<p class="weui-toast_content">' + (text || "数据加载中") + '</p>';
  3114. show(html, 'weui_loading_toast');
  3115. }
  3116. $.hideLoading = function() {
  3117. hide();
  3118. }
  3119. var toastDefaults = $.toast.prototype.defaults = {
  3120. duration: 2500
  3121. }
  3122. }($);
  3123. + function($) {
  3124. "use strict";
  3125. var defaults;
  3126. var show = function(params) {
  3127. var mask = $("<div class='weui-mask weui-actions_mask'></div>").appendTo(document.body);
  3128. var actions = params.actions || [];
  3129. var actionsHtml = actions.map(function(d, i) {
  3130. return '<div class="weui-actionsheet__cell ' + (d.className || "") + '">' + d.text + '</div>';
  3131. }).join("");
  3132. var titleHtml = "";
  3133. if (params.title) {
  3134. titleHtml = '<div class="weui-actionsheet__title">' + params.title + '</div>';
  3135. }
  3136. var tpl = '<div class="weui-actionsheet " id="weui-actionsheet">'+
  3137. titleHtml +
  3138. '<div class="weui-actionsheet__menu">'+
  3139. actionsHtml +
  3140. '</div>'+
  3141. '<div class="weui-actionsheet__action">'+
  3142. '<div class="weui-actionsheet__cell weui-actionsheet_cancel">取消</div>'+
  3143. '</div>'+
  3144. '</div>';
  3145. var dialog = $(tpl).appendTo(document.body);
  3146. dialog.find(".weui-actionsheet__menu .weui-actionsheet__cell, .weui-actionsheet__action .weui-actionsheet__cell").each(function(i, e) {
  3147. $(e).click(function() {
  3148. $.closeActions();
  3149. params.onClose && params.onClose();
  3150. if(actions[i] && actions[i].onClick) {
  3151. actions[i].onClick();
  3152. }
  3153. })
  3154. });
  3155. mask.show();
  3156. dialog.show();
  3157. mask.addClass("weui-mask--visible");
  3158. dialog.addClass("weui-actionsheet_toggle");
  3159. };
  3160. var hide = function() {
  3161. $(".weui-mask").removeClass("weui-mask--visible").transitionEnd(function() {
  3162. $(this).remove();
  3163. });
  3164. $(".weui-actionsheet").removeClass("weui-actionsheet_toggle").transitionEnd(function() {
  3165. $(this).remove();
  3166. });
  3167. }
  3168. $.actions = function(params) {
  3169. params = $.extend({}, defaults, params);
  3170. show(params);
  3171. }
  3172. $.closeActions = function() {
  3173. hide();
  3174. }
  3175. $(document).on("click", ".weui-actions_mask", function() {
  3176. $.closeActions();
  3177. });
  3178. var defaults = $.actions.prototype.defaults = {
  3179. title: undefined,
  3180. onClose: undefined,
  3181. /*actions: [{
  3182. text: "菜单",
  3183. className: "color-danger",
  3184. onClick: function() {
  3185. console.log(1);
  3186. }
  3187. },{
  3188. text: "菜单2",
  3189. className: "color-success",
  3190. onClick: function() {
  3191. console.log(2);
  3192. }
  3193. }]*/
  3194. }
  3195. }($);
  3196. /* ===============================================================================
  3197. ************ Pull to refreh ************
  3198. =============================================================================== */
  3199. /* global $:true */
  3200. +function ($) {
  3201. "use strict";
  3202. var PTR = function(el) {
  3203. this.container = $(el);
  3204. this.distance = 50;
  3205. this.attachEvents();
  3206. }
  3207. PTR.prototype.touchStart = function(e) {
  3208. if(this.container.hasClass("refreshing")) return;
  3209. var p = $.getTouchPosition(e);
  3210. this.start = p;
  3211. this.diffX = this.diffY = 0;
  3212. };
  3213. PTR.prototype.touchMove= function(e) {
  3214. if(this.container.hasClass("refreshing")) return;
  3215. if(!this.start) return false;
  3216. if(this.container.scrollTop() > 0) return;
  3217. var p = $.getTouchPosition(e);
  3218. this.diffX = p.x - this.start.x;
  3219. this.diffY = p.y - this.start.y;
  3220. if(this.diffY < 0) return;
  3221. this.container.addClass("touching");
  3222. e.preventDefault();
  3223. e.stopPropagation();
  3224. this.diffY = Math.pow(this.diffY, 0.8);
  3225. this.container.css("transform", "translate3d(0, "+this.diffY+"px, 0)");
  3226. if(this.diffY < this.distance) {
  3227. this.container.removeClass("pull-up").addClass("pull-down");
  3228. } else {
  3229. this.container.removeClass("pull-down").addClass("pull-up");
  3230. }
  3231. };
  3232. PTR.prototype.touchEnd = function() {
  3233. this.start = false;
  3234. if(this.diffY <= 0 || this.container.hasClass("refreshing")) return;
  3235. this.container.removeClass("touching");
  3236. this.container.removeClass("pull-down pull-up");
  3237. this.container.css("transform", "");
  3238. if(Math.abs(this.diffY) <= this.distance) {
  3239. } else {
  3240. this.container.addClass("refreshing");
  3241. this.container.trigger("pull-to-refresh");
  3242. }
  3243. };
  3244. PTR.prototype.attachEvents = function() {
  3245. var el = this.container;
  3246. el.addClass("weui-pull-to-refresh");
  3247. el.on($.touchEvents.start, $.proxy(this.touchStart, this));
  3248. el.on($.touchEvents.move, $.proxy(this.touchMove, this));
  3249. el.on($.touchEvents.end, $.proxy(this.touchEnd, this));
  3250. };
  3251. var pullToRefresh = function(el) {
  3252. new PTR(el);
  3253. };
  3254. var pullToRefreshDone = function(el) {
  3255. $(el).removeClass("refreshing");
  3256. }
  3257. $.fn.pullToRefresh = function() {
  3258. return this.each(function() {
  3259. pullToRefresh(this);
  3260. });
  3261. }
  3262. $.fn.pullToRefreshDone = function() {
  3263. return this.each(function() {
  3264. pullToRefreshDone(this);
  3265. });
  3266. }
  3267. }($);
  3268. /* ===============================================================================
  3269. ************ Infinite ************
  3270. =============================================================================== */
  3271. /* global $:true */
  3272. +function ($) {
  3273. "use strict";
  3274. var Infinite = function(el, distance) {
  3275. this.container = $(el);
  3276. this.container.data("infinite", this);
  3277. this.distance = distance || 50;
  3278. this.attachEvents();
  3279. }
  3280. Infinite.prototype.scroll = function() {
  3281. var container = this.container;
  3282. var offset = container.scrollHeight() - ($(window).height() + container.scrollTop());
  3283. if(offset <= this.distance) {
  3284. container.trigger("infinite");
  3285. }
  3286. }
  3287. Infinite.prototype.attachEvents = function(off) {
  3288. var el = this.container;
  3289. var scrollContainer = (el[0].tagName.toUpperCase() === "BODY" ? $(document) : el);
  3290. scrollContainer[off ? "off" : "on"]("scroll", $.proxy(this.scroll, this));
  3291. };
  3292. Infinite.prototype.detachEvents = function(off) {
  3293. this.attachEvents(true);
  3294. }
  3295. var infinite = function(el) {
  3296. attachEvents(el);
  3297. }
  3298. $.fn.infinite = function(distance) {
  3299. return this.each(function() {
  3300. new Infinite(this, distance);
  3301. });
  3302. }
  3303. $.fn.destroyInfinite = function() {
  3304. return this.each(function() {
  3305. var infinite = $(this).data("infinite");
  3306. if(infinite && infinite.detachEvents) infinite.detachEvents();
  3307. });
  3308. }
  3309. }($);
  3310. /* global $:true */
  3311. +function ($) {
  3312. "use strict";
  3313. var ITEM_ON = "weui-bar__item--on";
  3314. var showTab = function(a) {
  3315. var $a = $(a);
  3316. if($a.hasClass(ITEM_ON)) return;
  3317. var href = $a.attr("href");
  3318. if(!/^#/.test(href)) return ;
  3319. $a.parent().find("."+ITEM_ON).removeClass(ITEM_ON);
  3320. $a.addClass(ITEM_ON);
  3321. var bd = $a.parents(".weui-tab").find(".weui-tab__bd");
  3322. bd.find(".weui-tab__bd-item--active").removeClass("weui-tab__bd-item--active");
  3323. $(href).addClass("weui-tab__bd-item--active");
  3324. }
  3325. $.showTab = showTab;
  3326. $(document).on("click", ".weui-navbar__item, .weui-tabbar__item", function(e) {
  3327. var $a = $(e.currentTarget);
  3328. var href = $a.attr("href");
  3329. if($a.hasClass(ITEM_ON)) return;
  3330. if(!/^#/.test(href)) return;
  3331. e.preventDefault();
  3332. showTab($a);
  3333. });
  3334. }($);
  3335. /* global $:true */
  3336. + function($) {
  3337. "use strict";
  3338. $(document).on("click touchstart", ".weui-search-bar__label", function(e) {
  3339. $(e.target).parents(".weui-search-bar").addClass("weui-search-bar_focusing").find('input').focus();
  3340. })
  3341. /*
  3342. .on("blur", ".weui-search-bar__input", function(e) {
  3343. var $input = $(e.target);
  3344. if(!$input.val()) $input.parents(".weui-search-bar").removeClass("weui-search-bar_focusing");
  3345. })
  3346. */
  3347. .on("click", ".weui-search-bar__cancel-btn", function(e) {
  3348. var $input = $(e.target).parents(".weui-search-bar").removeClass("weui-search-bar_focusing").find(".weui-search-bar__input").val("").blur();
  3349. })
  3350. .on("click", ".weui-icon-clear", function(e) {
  3351. var $input = $(e.target).parents(".weui-search-bar").find(".weui-search-bar__input").val("").focus();
  3352. });
  3353. }($);
  3354. /*===========================
  3355. Device/OS Detection
  3356. ===========================*/
  3357. /* global $:true */
  3358. ;(function ($) {
  3359. "use strict";
  3360. var device = {};
  3361. var ua = navigator.userAgent;
  3362. var android = ua.match(/(Android);?[\s\/]+([\d.]+)?/);
  3363. var ipad = ua.match(/(iPad).*OS\s([\d_]+)/);
  3364. var ipod = ua.match(/(iPod)(.*OS\s([\d_]+))?/);
  3365. var iphone = !ipad && ua.match(/(iPhone\sOS)\s([\d_]+)/);
  3366. device.ios = device.android = device.iphone = device.ipad = device.androidChrome = false;
  3367. // Android
  3368. if (android) {
  3369. device.os = 'android';
  3370. device.osVersion = android[2];
  3371. device.android = true;
  3372. device.androidChrome = ua.toLowerCase().indexOf('chrome') >= 0;
  3373. }
  3374. if (ipad || iphone || ipod) {
  3375. device.os = 'ios';
  3376. device.ios = true;
  3377. }
  3378. // iOS
  3379. if (iphone && !ipod) {
  3380. device.osVersion = iphone[2].replace(/_/g, '.');
  3381. device.iphone = true;
  3382. }
  3383. if (ipad) {
  3384. device.osVersion = ipad[2].replace(/_/g, '.');
  3385. device.ipad = true;
  3386. }
  3387. if (ipod) {
  3388. device.osVersion = ipod[3] ? ipod[3].replace(/_/g, '.') : null;
  3389. device.iphone = true;
  3390. }
  3391. // iOS 8+ changed UA
  3392. if (device.ios && device.osVersion && ua.indexOf('Version/') >= 0) {
  3393. if (device.osVersion.split('.')[0] === '10') {
  3394. device.osVersion = ua.toLowerCase().split('version/')[1].split(' ')[0];
  3395. }
  3396. }
  3397. // Webview
  3398. device.webView = (iphone || ipad || ipod) && ua.match(/.*AppleWebKit(?!.*Safari)/i);
  3399. // Minimal UI
  3400. if (device.os && device.os === 'ios') {
  3401. var osVersionArr = device.osVersion.split('.');
  3402. device.minimalUi = !device.webView &&
  3403. (ipod || iphone) &&
  3404. (osVersionArr[0] * 1 === 7 ? osVersionArr[1] * 1 >= 1 : osVersionArr[0] * 1 > 7) &&
  3405. $('meta[name="viewport"]').length > 0 && $('meta[name="viewport"]').attr('content').indexOf('minimal-ui') >= 0;
  3406. }
  3407. // Check for status bar and fullscreen app mode
  3408. var windowWidth = $(window).width();
  3409. var windowHeight = $(window).height();
  3410. device.statusBar = false;
  3411. if (device.webView && (windowWidth * windowHeight === screen.width * screen.height)) {
  3412. device.statusBar = true;
  3413. }
  3414. else {
  3415. device.statusBar = false;
  3416. }
  3417. // Classes
  3418. var classNames = [];
  3419. // Pixel Ratio
  3420. device.pixelRatio = window.devicePixelRatio || 1;
  3421. classNames.push('pixel-ratio-' + Math.floor(device.pixelRatio));
  3422. if (device.pixelRatio >= 2) {
  3423. classNames.push('retina');
  3424. }
  3425. // OS classes
  3426. if (device.os) {
  3427. classNames.push(device.os, device.os + '-' + device.osVersion.split('.')[0], device.os + '-' + device.osVersion.replace(/\./g, '-'));
  3428. if (device.os === 'ios') {
  3429. var major = parseInt(device.osVersion.split('.')[0], 10);
  3430. for (var i = major - 1; i >= 6; i--) {
  3431. classNames.push('ios-gt-' + i);
  3432. }
  3433. }
  3434. }
  3435. // Status bar classes
  3436. if (device.statusBar) {
  3437. classNames.push('with-statusbar-overlay');
  3438. }
  3439. else {
  3440. $('html').removeClass('with-statusbar-overlay');
  3441. }
  3442. // Add html classes
  3443. if (classNames.length > 0) $('html').addClass(classNames.join(' '));
  3444. $.device = device;
  3445. })($);
  3446. /*======================================================
  3447. ************ Picker ************
  3448. ======================================================*/
  3449. /* global $:true */
  3450. /* jshint unused:false */
  3451. /* jshint multistr:true */
  3452. + function($) {
  3453. "use strict";
  3454. var Picker = function (params) {
  3455. var p = this;
  3456. var defaults = {
  3457. updateValuesOnMomentum: false,
  3458. updateValuesOnTouchmove: true,
  3459. rotateEffect: false,
  3460. momentumRatio: 7,
  3461. freeMode: false,
  3462. // Common settings
  3463. scrollToInput: true,
  3464. inputReadOnly: true,
  3465. toolbar: true,
  3466. toolbarCloseText: '完成',
  3467. title: '请选择',
  3468. toolbarTemplate: '<div class="toolbar">\
  3469. <div class="toolbar-inner">\
  3470. <a href="javascript:;" class="picker-button close-picker">{{closeText}}</a>\
  3471. <h1 class="title">{{title}}</h1>\
  3472. </div>\
  3473. </div>',
  3474. };
  3475. params = params || {};
  3476. for (var def in defaults) {
  3477. if (typeof params[def] === 'undefined') {
  3478. params[def] = defaults[def];
  3479. }
  3480. }
  3481. p.params = params;
  3482. p.cols = [];
  3483. p.initialized = false;
  3484. // Inline flag
  3485. p.inline = p.params.container ? true : false;
  3486. // 3D Transforms origin bug, only on safari
  3487. var originBug = $.device.ios || (navigator.userAgent.toLowerCase().indexOf('safari') >= 0 && navigator.userAgent.toLowerCase().indexOf('chrome') < 0) && !$.device.android;
  3488. // Should be converted to popover
  3489. function isPopover() {
  3490. var toPopover = false;
  3491. if (!p.params.convertToPopover && !p.params.onlyInPopover) return toPopover;
  3492. if (!p.inline && p.params.input) {
  3493. if (p.params.onlyInPopover) toPopover = true;
  3494. else {
  3495. if ($.device.ios) {
  3496. toPopover = $.device.ipad ? true : false;
  3497. }
  3498. else {
  3499. if ($(window).width() >= 768) toPopover = true;
  3500. }
  3501. }
  3502. }
  3503. return toPopover;
  3504. }
  3505. function inPopover() {
  3506. if (p.opened && p.container && p.container.length > 0 && p.container.parents('.popover').length > 0) return true;
  3507. else return false;
  3508. }
  3509. // Value
  3510. p.setValue = function (arrValues, transition) {
  3511. var valueIndex = 0;
  3512. for (var i = 0; i < p.cols.length; i++) {
  3513. if (p.cols[i] && !p.cols[i].divider) {
  3514. p.cols[i].setValue(arrValues[valueIndex], transition);
  3515. valueIndex++;
  3516. }
  3517. }
  3518. };
  3519. p.updateValue = function () {
  3520. var newValue = [];
  3521. var newDisplayValue = [];
  3522. for (var i = 0; i < p.cols.length; i++) {
  3523. if (!p.cols[i].divider) {
  3524. newValue.push(p.cols[i].value);
  3525. newDisplayValue.push(p.cols[i].displayValue);
  3526. }
  3527. }
  3528. if (newValue.indexOf(undefined) >= 0) {
  3529. return;
  3530. }
  3531. p.value = newValue;
  3532. p.displayValue = newDisplayValue;
  3533. if (p.params.onChange) {
  3534. p.params.onChange(p, p.value, p.displayValue);
  3535. }
  3536. if (p.input && p.input.length > 0) {
  3537. $(p.input).val(p.params.formatValue ? p.params.formatValue(p, p.value, p.displayValue) : p.value.join(' '));
  3538. $(p.input).trigger('change');
  3539. }
  3540. };
  3541. // Columns Handlers
  3542. p.initPickerCol = function (colElement, updateItems) {
  3543. var colContainer = $(colElement);
  3544. var colIndex = colContainer.index();
  3545. var col = p.cols[colIndex];
  3546. if (col.divider) return;
  3547. col.container = colContainer;
  3548. col.wrapper = col.container.find('.picker-items-col-wrapper');
  3549. col.items = col.wrapper.find('.picker-item');
  3550. var i, j;
  3551. var wrapperHeight, itemHeight, itemsHeight, minTranslate, maxTranslate;
  3552. col.replaceValues = function (values, displayValues) {
  3553. col.destroyEvents();
  3554. col.values = values;
  3555. col.displayValues = displayValues;
  3556. var newItemsHTML = p.columnHTML(col, true);
  3557. col.wrapper.html(newItemsHTML);
  3558. col.items = col.wrapper.find('.picker-item');
  3559. col.calcSize();
  3560. col.setValue(col.values[0] || '', 0, true);
  3561. col.initEvents();
  3562. };
  3563. col.calcSize = function () {
  3564. if (!col.values.length) return;
  3565. if (p.params.rotateEffect) {
  3566. col.container.removeClass('picker-items-col-absolute');
  3567. if (!col.width) col.container.css({width:''});
  3568. }
  3569. var colWidth, colHeight;
  3570. colWidth = 0;
  3571. colHeight = col.container[0].offsetHeight;
  3572. wrapperHeight = col.wrapper[0].offsetHeight;
  3573. itemHeight = col.items[0].offsetHeight;
  3574. itemsHeight = itemHeight * col.items.length;
  3575. minTranslate = colHeight / 2 - itemsHeight + itemHeight / 2;
  3576. maxTranslate = colHeight / 2 - itemHeight / 2;
  3577. if (col.width) {
  3578. colWidth = col.width;
  3579. if (parseInt(colWidth, 10) === colWidth) colWidth = colWidth + 'px';
  3580. col.container.css({width: colWidth});
  3581. }
  3582. if (p.params.rotateEffect) {
  3583. if (!col.width) {
  3584. col.items.each(function () {
  3585. var item = $(this);
  3586. item.css({width:'auto'});
  3587. colWidth = Math.max(colWidth, item[0].offsetWidth);
  3588. item.css({width:''});
  3589. });
  3590. col.container.css({width: (colWidth + 2) + 'px'});
  3591. }
  3592. col.container.addClass('picker-items-col-absolute');
  3593. }
  3594. };
  3595. col.calcSize();
  3596. col.wrapper.transform('translate3d(0,' + maxTranslate + 'px,0)').transition(0);
  3597. var activeIndex = 0;
  3598. var animationFrameId;
  3599. // Set Value Function
  3600. col.setValue = function (newValue, transition, valueCallbacks) {
  3601. if (typeof transition === 'undefined') transition = '';
  3602. var newActiveIndex = col.wrapper.find('.picker-item[data-picker-value="' + newValue + '"]').index();
  3603. if(typeof newActiveIndex === 'undefined' || newActiveIndex === -1) {
  3604. col.value = col.displayValue = newValue;
  3605. return;
  3606. }
  3607. var newTranslate = -newActiveIndex * itemHeight + maxTranslate;
  3608. // Update wrapper
  3609. col.wrapper.transition(transition);
  3610. col.wrapper.transform('translate3d(0,' + (newTranslate) + 'px,0)');
  3611. // Watch items
  3612. if (p.params.updateValuesOnMomentum && col.activeIndex && col.activeIndex !== newActiveIndex ) {
  3613. $.cancelAnimationFrame(animationFrameId);
  3614. col.wrapper.transitionEnd(function(){
  3615. $.cancelAnimationFrame(animationFrameId);
  3616. });
  3617. updateDuringScroll();
  3618. }
  3619. // Update items
  3620. col.updateItems(newActiveIndex, newTranslate, transition, valueCallbacks);
  3621. };
  3622. col.updateItems = function (activeIndex, translate, transition, valueCallbacks) {
  3623. if (typeof translate === 'undefined') {
  3624. translate = $.getTranslate(col.wrapper[0], 'y');
  3625. }
  3626. if(typeof activeIndex === 'undefined') activeIndex = -Math.round((translate - maxTranslate)/itemHeight);
  3627. if (activeIndex < 0) activeIndex = 0;
  3628. if (activeIndex >= col.items.length) activeIndex = col.items.length - 1;
  3629. var previousActiveIndex = col.activeIndex;
  3630. col.activeIndex = activeIndex;
  3631. /*
  3632. col.wrapper.find('.picker-selected, .picker-after-selected, .picker-before-selected').removeClass('picker-selected picker-after-selected picker-before-selected');
  3633. col.items.transition(transition);
  3634. var selectedItem = col.items.eq(activeIndex).addClass('picker-selected').transform('');
  3635. var prevItems = selectedItem.prevAll().addClass('picker-before-selected');
  3636. var nextItems = selectedItem.nextAll().addClass('picker-after-selected');
  3637. */
  3638. //去掉 .picker-after-selected, .picker-before-selected 以提高性能
  3639. col.wrapper.find('.picker-selected').removeClass('picker-selected');
  3640. if (p.params.rotateEffect) {
  3641. col.items.transition(transition);
  3642. }
  3643. var selectedItem = col.items.eq(activeIndex).addClass('picker-selected').transform('');
  3644. if (valueCallbacks || typeof valueCallbacks === 'undefined') {
  3645. // Update values
  3646. col.value = selectedItem.attr('data-picker-value');
  3647. col.displayValue = col.displayValues ? col.displayValues[activeIndex] : col.value;
  3648. // On change callback
  3649. if (previousActiveIndex !== activeIndex) {
  3650. if (col.onChange) {
  3651. col.onChange(p, col.value, col.displayValue);
  3652. }
  3653. p.updateValue();
  3654. }
  3655. }
  3656. // Set 3D rotate effect
  3657. if (!p.params.rotateEffect) {
  3658. return;
  3659. }
  3660. var percentage = (translate - (Math.floor((translate - maxTranslate)/itemHeight) * itemHeight + maxTranslate)) / itemHeight;
  3661. col.items.each(function () {
  3662. var item = $(this);
  3663. var itemOffsetTop = item.index() * itemHeight;
  3664. var translateOffset = maxTranslate - translate;
  3665. var itemOffset = itemOffsetTop - translateOffset;
  3666. var percentage = itemOffset / itemHeight;
  3667. var itemsFit = Math.ceil(col.height / itemHeight / 2) + 1;
  3668. var angle = (-18*percentage);
  3669. if (angle > 180) angle = 180;
  3670. if (angle < -180) angle = -180;
  3671. // Far class
  3672. if (Math.abs(percentage) > itemsFit) item.addClass('picker-item-far');
  3673. else item.removeClass('picker-item-far');
  3674. // Set transform
  3675. item.transform('translate3d(0, ' + (-translate + maxTranslate) + 'px, ' + (originBug ? -110 : 0) + 'px) rotateX(' + angle + 'deg)');
  3676. });
  3677. };
  3678. function updateDuringScroll() {
  3679. animationFrameId = $.requestAnimationFrame(function () {
  3680. col.updateItems(undefined, undefined, 0);
  3681. updateDuringScroll();
  3682. });
  3683. }
  3684. // Update items on init
  3685. if (updateItems) col.updateItems(0, maxTranslate, 0);
  3686. var allowItemClick = true;
  3687. var isTouched, isMoved, touchStartY, touchCurrentY, touchStartTime, touchEndTime, startTranslate, returnTo, currentTranslate, prevTranslate, velocityTranslate, velocityTime;
  3688. function handleTouchStart (e) {
  3689. if (isMoved || isTouched) return;
  3690. e.preventDefault();
  3691. isTouched = true;
  3692. var position = $.getTouchPosition(e);
  3693. touchStartY = touchCurrentY = position.y;
  3694. touchStartTime = (new Date()).getTime();
  3695. allowItemClick = true;
  3696. startTranslate = currentTranslate = $.getTranslate(col.wrapper[0], 'y');
  3697. }
  3698. function handleTouchMove (e) {
  3699. if (!isTouched) return;
  3700. e.preventDefault();
  3701. allowItemClick = false;
  3702. var position = $.getTouchPosition(e);
  3703. touchCurrentY = position.y;
  3704. if (!isMoved) {
  3705. // First move
  3706. $.cancelAnimationFrame(animationFrameId);
  3707. isMoved = true;
  3708. startTranslate = currentTranslate = $.getTranslate(col.wrapper[0], 'y');
  3709. col.wrapper.transition(0);
  3710. }
  3711. e.preventDefault();
  3712. var diff = touchCurrentY - touchStartY;
  3713. currentTranslate = startTranslate + diff;
  3714. returnTo = undefined;
  3715. // Normalize translate
  3716. if (currentTranslate < minTranslate) {
  3717. currentTranslate = minTranslate - Math.pow(minTranslate - currentTranslate, 0.8);
  3718. returnTo = 'min';
  3719. }
  3720. if (currentTranslate > maxTranslate) {
  3721. currentTranslate = maxTranslate + Math.pow(currentTranslate - maxTranslate, 0.8);
  3722. returnTo = 'max';
  3723. }
  3724. // Transform wrapper
  3725. col.wrapper.transform('translate3d(0,' + currentTranslate + 'px,0)');
  3726. // Update items
  3727. col.updateItems(undefined, currentTranslate, 0, p.params.updateValuesOnTouchmove);
  3728. // Calc velocity
  3729. velocityTranslate = currentTranslate - prevTranslate || currentTranslate;
  3730. velocityTime = (new Date()).getTime();
  3731. prevTranslate = currentTranslate;
  3732. }
  3733. function handleTouchEnd (e) {
  3734. if (!isTouched || !isMoved) {
  3735. isTouched = isMoved = false;
  3736. return;
  3737. }
  3738. isTouched = isMoved = false;
  3739. col.wrapper.transition('');
  3740. if (returnTo) {
  3741. if (returnTo === 'min') {
  3742. col.wrapper.transform('translate3d(0,' + minTranslate + 'px,0)');
  3743. }
  3744. else col.wrapper.transform('translate3d(0,' + maxTranslate + 'px,0)');
  3745. }
  3746. touchEndTime = new Date().getTime();
  3747. var velocity, newTranslate;
  3748. if (touchEndTime - touchStartTime > 300) {
  3749. newTranslate = currentTranslate;
  3750. }
  3751. else {
  3752. velocity = Math.abs(velocityTranslate / (touchEndTime - velocityTime));
  3753. newTranslate = currentTranslate + velocityTranslate * p.params.momentumRatio;
  3754. }
  3755. newTranslate = Math.max(Math.min(newTranslate, maxTranslate), minTranslate);
  3756. // Active Index
  3757. var activeIndex = -Math.floor((newTranslate - maxTranslate)/itemHeight);
  3758. // Normalize translate
  3759. if (!p.params.freeMode) newTranslate = -activeIndex * itemHeight + maxTranslate;
  3760. // Transform wrapper
  3761. col.wrapper.transform('translate3d(0,' + (parseInt(newTranslate,10)) + 'px,0)');
  3762. // Update items
  3763. col.updateItems(activeIndex, newTranslate, '', true);
  3764. // Watch items
  3765. if (p.params.updateValuesOnMomentum) {
  3766. updateDuringScroll();
  3767. col.wrapper.transitionEnd(function(){
  3768. $.cancelAnimationFrame(animationFrameId);
  3769. });
  3770. }
  3771. // Allow click
  3772. setTimeout(function () {
  3773. allowItemClick = true;
  3774. }, 100);
  3775. }
  3776. function handleClick(e) {
  3777. if (!allowItemClick) return;
  3778. $.cancelAnimationFrame(animationFrameId);
  3779. /*jshint validthis:true */
  3780. var value = $(this).attr('data-picker-value');
  3781. col.setValue(value);
  3782. }
  3783. col.initEvents = function (detach) {
  3784. var method = detach ? 'off' : 'on';
  3785. col.container[method]($.touchEvents.start, handleTouchStart);
  3786. col.container[method]($.touchEvents.move, handleTouchMove);
  3787. col.container[method]($.touchEvents.end, handleTouchEnd);
  3788. col.items[method]('click', handleClick);
  3789. };
  3790. col.destroyEvents = function () {
  3791. col.initEvents(true);
  3792. };
  3793. col.container[0].f7DestroyPickerCol = function () {
  3794. col.destroyEvents();
  3795. };
  3796. col.initEvents();
  3797. };
  3798. p.destroyPickerCol = function (colContainer) {
  3799. colContainer = $(colContainer);
  3800. if ('f7DestroyPickerCol' in colContainer[0]) colContainer[0].f7DestroyPickerCol();
  3801. };
  3802. // Resize cols
  3803. function resizeCols() {
  3804. if (!p.opened) return;
  3805. for (var i = 0; i < p.cols.length; i++) {
  3806. if (!p.cols[i].divider) {
  3807. p.cols[i].calcSize();
  3808. p.cols[i].setValue(p.cols[i].value, 0, false);
  3809. }
  3810. }
  3811. }
  3812. $(window).on('resize', resizeCols);
  3813. // HTML Layout
  3814. p.columnHTML = function (col, onlyItems) {
  3815. var columnItemsHTML = '';
  3816. var columnHTML = '';
  3817. if (col.divider) {
  3818. columnHTML += '<div class="picker-items-col picker-items-col-divider ' + (col.textAlign ? 'picker-items-col-' + col.textAlign : '') + ' ' + (col.cssClass || '') + '">' + col.content + '</div>';
  3819. }
  3820. else {
  3821. for (var j = 0; j < col.values.length; j++) {
  3822. columnItemsHTML += '<div class="picker-item" data-picker-value="' + col.values[j] + '">' + (col.displayValues ? col.displayValues[j] : col.values[j]) + '</div>';
  3823. }
  3824. columnHTML += '<div class="picker-items-col ' + (col.textAlign ? 'picker-items-col-' + col.textAlign : '') + ' ' + (col.cssClass || '') + '"><div class="picker-items-col-wrapper">' + columnItemsHTML + '</div></div>';
  3825. }
  3826. return onlyItems ? columnItemsHTML : columnHTML;
  3827. };
  3828. p.layout = function () {
  3829. var pickerHTML = '';
  3830. var pickerClass = '';
  3831. var i;
  3832. p.cols = [];
  3833. var colsHTML = '';
  3834. for (i = 0; i < p.params.cols.length; i++) {
  3835. var col = p.params.cols[i];
  3836. colsHTML += p.columnHTML(p.params.cols[i]);
  3837. p.cols.push(col);
  3838. }
  3839. pickerClass = 'weui-picker-modal picker-columns ' + (p.params.cssClass || '') + (p.params.rotateEffect ? ' picker-3d' : '') + (p.params.cols.length === 1 ? ' picker-columns-single' : '');
  3840. pickerHTML =
  3841. '<div class="' + (pickerClass) + '">' +
  3842. (p.params.toolbar ? p.params.toolbarTemplate.replace(/{{closeText}}/g, p.params.toolbarCloseText).replace(/{{title}}/g, p.params.title) : '') +
  3843. '<div class="picker-modal-inner picker-items">' +
  3844. colsHTML +
  3845. '<div class="picker-center-highlight"></div>' +
  3846. '</div>' +
  3847. '</div>';
  3848. p.pickerHTML = pickerHTML;
  3849. };
  3850. // Input Events
  3851. function openOnInput(e) {
  3852. e.preventDefault();
  3853. if (p.opened) return;
  3854. p.open();
  3855. if (p.params.scrollToInput && !isPopover()) {
  3856. var pageContent = p.input.parents('.content');
  3857. if (pageContent.length === 0) return;
  3858. var paddingTop = parseInt(pageContent.css('padding-top'), 10),
  3859. paddingBottom = parseInt(pageContent.css('padding-bottom'), 10),
  3860. pageHeight = pageContent[0].offsetHeight - paddingTop - p.container.height(),
  3861. pageScrollHeight = pageContent[0].scrollHeight - paddingTop - p.container.height(),
  3862. newPaddingBottom;
  3863. var inputTop = p.input.offset().top - paddingTop + p.input[0].offsetHeight;
  3864. if (inputTop > pageHeight) {
  3865. var scrollTop = pageContent.scrollTop() + inputTop - pageHeight;
  3866. if (scrollTop + pageHeight > pageScrollHeight) {
  3867. newPaddingBottom = scrollTop + pageHeight - pageScrollHeight + paddingBottom;
  3868. if (pageHeight === pageScrollHeight) {
  3869. newPaddingBottom = p.container.height();
  3870. }
  3871. pageContent.css({'padding-bottom': (newPaddingBottom) + 'px'});
  3872. }
  3873. pageContent.scrollTop(scrollTop, 300);
  3874. }
  3875. }
  3876. }
  3877. function closeOnHTMLClick(e) {
  3878. if (inPopover()) return;
  3879. if (p.input && p.input.length > 0) {
  3880. if (e.target !== p.input[0] && $(e.target).parents('.weui-picker-modal').length === 0) p.close();
  3881. }
  3882. else {
  3883. if ($(e.target).parents('.weui-picker-modal').length === 0) p.close();
  3884. }
  3885. }
  3886. if (p.params.input) {
  3887. p.input = $(p.params.input);
  3888. if (p.input.length > 0) {
  3889. if (p.params.inputReadOnly) p.input.prop('readOnly', true);
  3890. if (!p.inline) {
  3891. p.input.on('click', openOnInput);
  3892. }
  3893. if (p.params.inputReadOnly) {
  3894. p.input.on('focus mousedown', function (e) {
  3895. e.preventDefault();
  3896. });
  3897. }
  3898. }
  3899. }
  3900. if (!p.inline) $('html').on('click', closeOnHTMLClick);
  3901. // Open
  3902. function onPickerClose() {
  3903. p.opened = false;
  3904. if (p.input && p.input.length > 0) p.input.parents('.page-content').css({'padding-bottom': ''});
  3905. if (p.params.onClose) p.params.onClose(p);
  3906. // Destroy events
  3907. p.container.find('.picker-items-col').each(function () {
  3908. p.destroyPickerCol(this);
  3909. });
  3910. }
  3911. p.opened = false;
  3912. p.open = function () {
  3913. var toPopover = isPopover();
  3914. if (!p.opened) {
  3915. // Layout
  3916. p.layout();
  3917. // Append
  3918. if (toPopover) {
  3919. p.pickerHTML = '<div class="popover popover-picker-columns"><div class="popover-inner">' + p.pickerHTML + '</div></div>';
  3920. p.popover = $.popover(p.pickerHTML, p.params.input, true);
  3921. p.container = $(p.popover).find('.weui-picker-modal');
  3922. $(p.popover).on('close', function () {
  3923. onPickerClose();
  3924. });
  3925. }
  3926. else if (p.inline) {
  3927. p.container = $(p.pickerHTML);
  3928. p.container.addClass('picker-modal-inline');
  3929. $(p.params.container).append(p.container);
  3930. }
  3931. else {
  3932. p.container = $($.openPicker(p.pickerHTML));
  3933. $(p.container)
  3934. .on('close', function () {
  3935. onPickerClose();
  3936. });
  3937. }
  3938. // Store picker instance
  3939. p.container[0].f7Picker = p;
  3940. // Init Events
  3941. p.container.find('.picker-items-col').each(function () {
  3942. var updateItems = true;
  3943. if ((!p.initialized && p.params.value) || (p.initialized && p.value)) updateItems = false;
  3944. p.initPickerCol(this, updateItems);
  3945. });
  3946. // Set value
  3947. if (!p.initialized) {
  3948. if (p.params.value) {
  3949. p.setValue(p.params.value, 0);
  3950. }
  3951. }
  3952. else {
  3953. if (p.value) p.setValue(p.value, 0);
  3954. }
  3955. }
  3956. // Set flag
  3957. p.opened = true;
  3958. p.initialized = true;
  3959. if (p.params.onOpen) p.params.onOpen(p);
  3960. };
  3961. // Close
  3962. p.close = function (force) {
  3963. if (!p.opened || p.inline) return;
  3964. if (inPopover()) {
  3965. $.closePicker(p.popover);
  3966. return;
  3967. }
  3968. else {
  3969. $.closePicker(p.container);
  3970. return;
  3971. }
  3972. };
  3973. // Destroy
  3974. p.destroy = function () {
  3975. p.close();
  3976. if (p.params.input && p.input.length > 0) {
  3977. p.input.off('click focus', openOnInput);
  3978. $(p.input).data('picker', null);
  3979. }
  3980. $('html').off('click', closeOnHTMLClick);
  3981. $(window).off('resize', resizeCols);
  3982. };
  3983. if (p.inline) {
  3984. p.open();
  3985. }
  3986. return p;
  3987. };
  3988. $(document).on("click", ".close-picker", function() {
  3989. var pickerToClose = $('.weui-picker-modal.weui-picker-modal-visible');
  3990. if (pickerToClose.length > 0) {
  3991. $.closePicker(pickerToClose);
  3992. }
  3993. });
  3994. //修复picker会滚动页面的bug
  3995. $(document).on($.touchEvents.move, ".picker-modal-inner", function(e) {
  3996. e.preventDefault();
  3997. });
  3998. $.openPicker = function(tpl, className, callback) {
  3999. if(typeof className === "function") {
  4000. callback = className;
  4001. className = undefined;
  4002. }
  4003. $.closePicker();
  4004. var container = $("<div class='weui-picker-container "+ (className || "") + "'></div>").appendTo(document.body);
  4005. container.show();
  4006. container.addClass("weui-picker-container-visible");
  4007. //关于布局的问题,如果直接放在body上,则做动画的时候会撑开body高度而导致滚动条变化。
  4008. var dialog = $(tpl).appendTo(container);
  4009. dialog.width(); //通过取一次CSS值,强制浏览器不能把上下两行代码合并执行,因为合并之后会导致无法出现动画。
  4010. dialog.addClass("weui-picker-modal-visible");
  4011. callback && container.on("close", callback);
  4012. return dialog;
  4013. }
  4014. $.updatePicker = function(tpl) {
  4015. var container = $(".weui-picker-container-visible");
  4016. if(!container[0]) return false;
  4017. container.html("");
  4018. var dialog = $(tpl).appendTo(container);
  4019. dialog.addClass("weui-picker-modal-visible");
  4020. return dialog;
  4021. }
  4022. $.closePicker = function(container, callback) {
  4023. if(typeof container === "function") callback = container;
  4024. $(".weui-picker-modal-visible").removeClass("weui-picker-modal-visible").transitionEnd(function() {
  4025. $(this).parent().remove();
  4026. callback && callback();
  4027. }).trigger("close");
  4028. };
  4029. $.fn.picker = function(params) {
  4030. var args = arguments;
  4031. return this.each(function() {
  4032. if(!this) return;
  4033. var $this = $(this);
  4034. var picker = $this.data("picker");
  4035. if(!picker) {
  4036. params = params || {};
  4037. var inputValue = $this.val();
  4038. if(params.value === undefined && inputValue !== "") {
  4039. params.value = (params.cols && params.cols.length > 1) ? inputValue.split(" ") : [inputValue];
  4040. }
  4041. var p = $.extend({input: this}, params);
  4042. picker = new Picker(p);
  4043. $this.data("picker", picker);
  4044. }
  4045. if(typeof params === typeof "a") {
  4046. picker[params].apply(picker, Array.prototype.slice.call(args, 1));
  4047. }
  4048. });
  4049. };
  4050. }($);
  4051. /* global $:true */
  4052. + function($) {
  4053. "use strict";
  4054. var defaults;
  4055. var selects = [];
  4056. var Select = function(input, config) {
  4057. var self = this;
  4058. this.config = config;
  4059. //init empty data
  4060. this.data = {
  4061. values: '',
  4062. titles: '',
  4063. origins: [],
  4064. length: 0
  4065. };
  4066. this.$input = $(input);
  4067. this.$input.prop("readOnly", true);
  4068. this.initConfig();
  4069. config = this.config;
  4070. this.$input.click($.proxy(this.open, this));
  4071. selects.push(this)
  4072. }
  4073. Select.prototype.initConfig = function() {
  4074. this.config = $.extend({}, defaults, this.config);
  4075. var config = this.config;
  4076. if(!config.items || !config.items.length) return;
  4077. config.items = config.items.map(function(d, i) {
  4078. if(typeof d == typeof "a") {
  4079. return {
  4080. title: d,
  4081. value: d
  4082. };
  4083. }
  4084. return d;
  4085. });
  4086. this.tpl = $.t7.compile("<div class='weui-picker-modal weui-select-modal'>" + config.toolbarTemplate + (config.multi ? config.checkboxTemplate : config.radioTemplate) + "</div>");
  4087. if(config.input !== undefined) this.$input.val(config.input);
  4088. this.parseInitValue();
  4089. this._init = true;
  4090. }
  4091. Select.prototype.updateInputValue = function(values, titles) {
  4092. var v, t;
  4093. if(this.config.multi) {
  4094. v = values.join(this.config.split);
  4095. t = titles.join(this.config.split);
  4096. } else {
  4097. v = values[0];
  4098. t = titles[0];
  4099. }
  4100. //caculate origin data
  4101. var origins = [];
  4102. this.config.items.forEach(function(d) {
  4103. values.each(function(i, dd) {
  4104. if(d.value == dd) origins.push(d);
  4105. });
  4106. });
  4107. this.$input.val(t).data("values", v);
  4108. this.$input.attr("value", t).attr("data-values", v);
  4109. var data = {
  4110. values: v,
  4111. titles: t,
  4112. valuesArray: values,
  4113. titlesArray: titles,
  4114. origins: origins,
  4115. length: origins.length
  4116. };
  4117. this.data = data;
  4118. this.$input.trigger("change", data);
  4119. this.config.onChange && this.config.onChange.call(this, data);
  4120. }
  4121. Select.prototype.parseInitValue = function() {
  4122. var value = this.$input.val();
  4123. var items = this.config.items;
  4124. //如果input为空,只有在第一次初始化的时候才保留默认选择。因为后来就是用户自己取消了全部选择,不能再为他选中默认值。
  4125. if( !this._init && (value === undefined || value == null || value === "")) return;
  4126. var titles = this.config.multi ? value.split(this.config.split) : [value];
  4127. for(var i=0;i<items.length;i++) {
  4128. items[i].checked = false;
  4129. for(var j=0;j<titles.length;j++) {
  4130. if(items[i].title === titles[j]) {
  4131. items[i].checked = true;
  4132. }
  4133. }
  4134. }
  4135. }
  4136. Select.prototype._bind = function(dialog) {
  4137. var self = this,
  4138. config = this.config;
  4139. dialog.on("change", function(e) {
  4140. var checked = dialog.find("input:checked");
  4141. var values = checked.map(function() {
  4142. return $(this).val();
  4143. });
  4144. var titles = checked.map(function() {
  4145. return $(this).data("title");
  4146. });
  4147. self.updateInputValue(values, titles);
  4148. if(config.autoClose && !config.multi) self.close();
  4149. })
  4150. .on("click", ".close-select", function() {
  4151. self.close();
  4152. });
  4153. }
  4154. //更新数据
  4155. Select.prototype.update = function(config) {
  4156. this.config = $.extend({}, this.config, config);
  4157. this.initConfig();
  4158. if(this._open) {
  4159. this._bind($.updatePicker(this.getHTML()));
  4160. }
  4161. }
  4162. Select.prototype.open = function(values, titles) {
  4163. if(this._open) return;
  4164. // open picker 会默认关掉其他的,但是 onClose 不会被调用,所以这里先关掉其他select
  4165. for (var i = 0; i < selects.length; i++ ) {
  4166. var s = selects[i];
  4167. if (s === this) continue;
  4168. if (s._open) {
  4169. if(!s.close()) return false; // 其他的select由于某些条件限制关闭失败。
  4170. }
  4171. }
  4172. this.parseInitValue();
  4173. var config = this.config;
  4174. var dialog = this.dialog = $.openPicker(this.getHTML());
  4175. this._bind(dialog);
  4176. this._open = true;
  4177. if(config.onOpen) config.onOpen(this);
  4178. }
  4179. Select.prototype.close = function(callback, force) {
  4180. if (!this._open) return false;
  4181. var self = this,
  4182. beforeClose = this.config.beforeClose;
  4183. if(typeof callback === typeof true) {
  4184. force === callback;
  4185. }
  4186. if(!force) {
  4187. if(beforeClose && typeof beforeClose === 'function' && beforeClose.call(this, this.data.values, this.data.titles) === false) {
  4188. return false
  4189. }
  4190. if(this.config.multi) {
  4191. if(this.config.min !== undefined && this.data.length < this.config.min) {
  4192. $.toast("请至少选择"+this.config.min+"个", "text");
  4193. return false
  4194. }
  4195. if(this.config.max !== undefined && this.data.length > this.config.max) {
  4196. $.toast("最多只能选择"+this.config.max+"个", "text");
  4197. return false
  4198. }
  4199. }
  4200. }
  4201. $.closePicker(function() {
  4202. self.onClose();
  4203. callback && callback();
  4204. });
  4205. return true
  4206. }
  4207. Select.prototype.onClose = function() {
  4208. this._open = false;
  4209. if(this.config.onClose) this.config.onClose(this);
  4210. }
  4211. Select.prototype.getHTML = function(callback) {
  4212. var config = this.config;
  4213. return this.tpl({
  4214. items: config.items,
  4215. title: config.title,
  4216. closeText: config.closeText
  4217. })
  4218. }
  4219. $.fn.select = function(params, args) {
  4220. return this.each(function() {
  4221. var $this = $(this);
  4222. if(!$this.data("weui-select")) $this.data("weui-select", new Select(this, params));
  4223. var select = $this.data("weui-select");
  4224. if(typeof params === typeof "a") select[params].call(select, args);
  4225. return select;
  4226. });
  4227. }
  4228. defaults = $.fn.select.prototype.defaults = {
  4229. items: [],
  4230. input: undefined, //输入框的初始值
  4231. title: "请选择",
  4232. multi: false,
  4233. closeText: "确定",
  4234. autoClose: true, //是否选择完成后自动关闭,只有单选模式下才有效
  4235. onChange: undefined, //function
  4236. beforeClose: undefined, // function 关闭之前,如果返回false则阻止关闭
  4237. onClose: undefined, //function
  4238. onOpen: undefined, //function
  4239. split: ",", //多选模式下的分隔符
  4240. min: undefined, //多选模式下可用,最少选择数
  4241. max: undefined, //单选模式下可用,最多选择数
  4242. toolbarTemplate: '<div class="toolbar">\
  4243. <div class="toolbar-inner">\
  4244. <a href="javascript:;" class="picker-button close-select">{{closeText}}</a>\
  4245. <h1 class="title">{{title}}</h1>\
  4246. </div>\
  4247. </div>',
  4248. radioTemplate:
  4249. '<div class="weui-cells weui-cells_radio">\
  4250. {{#items}}\
  4251. <label class="weui-cell weui-check_label" for="weui-select-id-{{this.title}}">\
  4252. <div class="weui-cell__bd weui-cell_primary">\
  4253. <p>{{this.title}}</p>\
  4254. </div>\
  4255. <div class="weui-cell__ft">\
  4256. <input type="radio" class="weui-check" name="weui-select" id="weui-select-id-{{this.title}}" value="{{this.value}}" {{#if this.checked}}checked="checked"{{/if}} data-title="{{this.title}}">\
  4257. <span class="weui-icon-checked"></span>\
  4258. </div>\
  4259. </label>\
  4260. {{/items}}\
  4261. </div>',
  4262. checkboxTemplate:
  4263. '<div class="weui-cells weui-cells_checkbox">\
  4264. {{#items}}\
  4265. <label class="weui-cell weui-check_label" for="weui-select-id-{{this.title}}">\
  4266. <div class="weui-cell__bd weui-cell_primary">\
  4267. <p>{{this.title}}</p>\
  4268. </div>\
  4269. <div class="weui-cell__ft">\
  4270. <input type="checkbox" class="weui-check" name="weui-select" id="weui-select-id-{{this.title}}" value="{{this.value}}" {{#if this.checked}}checked="checked"{{/if}} data-title="{{this.title}}" >\
  4271. <span class="weui-icon-checked"></span>\
  4272. </div>\
  4273. </label>\
  4274. {{/items}}\
  4275. </div>',
  4276. }
  4277. }($);
  4278. /*======================================================
  4279. ************ Calendar ************
  4280. ======================================================*/
  4281. /* global $:true */
  4282. /*jshint unused: false*/
  4283. +function ($) {
  4284. "use strict";
  4285. var rtl = false;
  4286. var defaults;
  4287. var isSameDate = function (a, b) {
  4288. var a = new Date(a),
  4289. b = new Date(b);
  4290. return a.getFullYear() === b.getFullYear() && a.getMonth() === b.getMonth() && a.getDate() === b.getDate()
  4291. }
  4292. var Calendar = function (params) {
  4293. var p = this;
  4294. params = params || {};
  4295. for (var def in defaults) {
  4296. if (typeof params[def] === 'undefined') {
  4297. params[def] = defaults[def];
  4298. }
  4299. }
  4300. p.params = params;
  4301. p.initialized = false;
  4302. // Inline flag
  4303. p.inline = p.params.container ? true : false;
  4304. // Is horizontal
  4305. p.isH = p.params.direction === 'horizontal';
  4306. // RTL inverter
  4307. var inverter = p.isH ? (rtl ? -1 : 1) : 1;
  4308. // Animating flag
  4309. p.animating = false;
  4310. // Should be converted to popover
  4311. function isPopover() {
  4312. var toPopover = false;
  4313. if (!p.params.convertToPopover && !p.params.onlyInPopover) return toPopover;
  4314. if (!p.inline && p.params.input) {
  4315. if (p.params.onlyInPopover) toPopover = true;
  4316. else {
  4317. if ($.device.ios) {
  4318. toPopover = $.device.ipad ? true : false;
  4319. }
  4320. else {
  4321. if ($(window).width() >= 768) toPopover = true;
  4322. }
  4323. }
  4324. }
  4325. return toPopover;
  4326. }
  4327. function inPopover() {
  4328. if (p.opened && p.container && p.container.length > 0 && p.container.parents('.popover').length > 0) return true;
  4329. else return false;
  4330. }
  4331. // Format date
  4332. function formatDate(date) {
  4333. date = new Date(date);
  4334. var year = date.getFullYear();
  4335. var month = date.getMonth();
  4336. var month1 = month + 1;
  4337. var day = date.getDate();
  4338. var weekDay = date.getDay();
  4339. return p.params.dateFormat
  4340. .replace(/yyyy/g, year)
  4341. .replace(/yy/g, (year + '').substring(2))
  4342. .replace(/mm/g, month1 < 10 ? '0' + month1 : month1)
  4343. .replace(/m/g, month1)
  4344. .replace(/MM/g, p.params.monthNames[month])
  4345. .replace(/M/g, p.params.monthNamesShort[month])
  4346. .replace(/dd/g, day < 10 ? '0' + day : day)
  4347. .replace(/d/g, day)
  4348. .replace(/DD/g, p.params.dayNames[weekDay])
  4349. .replace(/D/g, p.params.dayNamesShort[weekDay]);
  4350. }
  4351. // Value
  4352. p.addValue = function (value) {
  4353. if (p.params.multiple) {
  4354. if (!p.value) p.value = [];
  4355. var inValuesIndex;
  4356. for (var i = 0; i < p.value.length; i++) {
  4357. if (isSameDate(value, p.value[i])) {
  4358. inValuesIndex = i;
  4359. }
  4360. }
  4361. if (typeof inValuesIndex === 'undefined') {
  4362. p.value.push(value);
  4363. }
  4364. else {
  4365. p.value.splice(inValuesIndex, 1);
  4366. }
  4367. p.updateValue();
  4368. }
  4369. else {
  4370. p.value = [value];
  4371. p.updateValue();
  4372. }
  4373. };
  4374. p.setValue = function (arrValues) {
  4375. var date = new Date(arrValues[0]);
  4376. p.setYearMonth(date.getFullYear(), date.getMonth());
  4377. p.addValue(+ date);
  4378. };
  4379. p.updateValue = function () {
  4380. p.wrapper.find('.picker-calendar-day-selected').removeClass('picker-calendar-day-selected');
  4381. var i, inputValue;
  4382. for (i = 0; i < p.value.length; i++) {
  4383. var valueDate = new Date(p.value[i]);
  4384. p.wrapper.find('.picker-calendar-day[data-date="' + valueDate.getFullYear() + '-' + valueDate.getMonth() + '-' + valueDate.getDate() + '"]').addClass('picker-calendar-day-selected');
  4385. }
  4386. if (p.params.onChange) {
  4387. p.params.onChange(p, p.value.map(formatDate), p.value.map(function (d) {
  4388. return + new Date(typeof d === typeof 'a' ? d.split(/\D/).filter(function (a) { return !!a; }).join("-") : d);
  4389. }));
  4390. }
  4391. if (p.input && p.input.length > 0) {
  4392. if (p.params.formatValue) inputValue = p.params.formatValue(p, p.value);
  4393. else {
  4394. inputValue = [];
  4395. for (i = 0; i < p.value.length; i++) {
  4396. inputValue.push(formatDate(p.value[i]));
  4397. }
  4398. inputValue = inputValue.join(', ');
  4399. }
  4400. $(p.input).val(inputValue);
  4401. $(p.input).trigger('change');
  4402. }
  4403. };
  4404. // Columns Handlers
  4405. p.initCalendarEvents = function () {
  4406. var col;
  4407. var allowItemClick = true;
  4408. var isTouched, isMoved, touchStartX, touchStartY, touchCurrentX, touchCurrentY, touchStartTime, touchEndTime, startTranslate, currentTranslate, wrapperWidth, wrapperHeight, percentage, touchesDiff, isScrolling;
  4409. function handleTouchStart (e) {
  4410. if (isMoved || isTouched) return;
  4411. // e.preventDefault();
  4412. isTouched = true;
  4413. var position = $.getTouchPosition(e);
  4414. touchStartX = touchCurrentY = position.x;
  4415. touchStartY = touchCurrentY = position.y;
  4416. touchStartTime = (new Date()).getTime();
  4417. percentage = 0;
  4418. allowItemClick = true;
  4419. isScrolling = undefined;
  4420. startTranslate = currentTranslate = p.monthsTranslate;
  4421. }
  4422. function handleTouchMove (e) {
  4423. if (!isTouched) return;
  4424. var position = $.getTouchPosition(e);
  4425. touchCurrentX = position.x;
  4426. touchCurrentY = position.y;
  4427. if (typeof isScrolling === 'undefined') {
  4428. isScrolling = !!(isScrolling || Math.abs(touchCurrentY - touchStartY) > Math.abs(touchCurrentX - touchStartX));
  4429. }
  4430. if (p.isH && isScrolling) {
  4431. isTouched = false;
  4432. return;
  4433. }
  4434. e.preventDefault();
  4435. if (p.animating) {
  4436. isTouched = false;
  4437. return;
  4438. }
  4439. allowItemClick = false;
  4440. if (!isMoved) {
  4441. // First move
  4442. isMoved = true;
  4443. wrapperWidth = p.wrapper[0].offsetWidth;
  4444. wrapperHeight = p.wrapper[0].offsetHeight;
  4445. p.wrapper.transition(0);
  4446. }
  4447. e.preventDefault();
  4448. touchesDiff = p.isH ? touchCurrentX - touchStartX : touchCurrentY - touchStartY;
  4449. percentage = touchesDiff/(p.isH ? wrapperWidth : wrapperHeight);
  4450. currentTranslate = (p.monthsTranslate * inverter + percentage) * 100;
  4451. // Transform wrapper
  4452. p.wrapper.transform('translate3d(' + (p.isH ? currentTranslate : 0) + '%, ' + (p.isH ? 0 : currentTranslate) + '%, 0)');
  4453. }
  4454. function handleTouchEnd (e) {
  4455. if (!isTouched || !isMoved) {
  4456. isTouched = isMoved = false;
  4457. return;
  4458. }
  4459. isTouched = isMoved = false;
  4460. touchEndTime = new Date().getTime();
  4461. if (touchEndTime - touchStartTime < 300) {
  4462. if (Math.abs(touchesDiff) < 10) {
  4463. p.resetMonth();
  4464. }
  4465. else if (touchesDiff >= 10) {
  4466. if (rtl) p.nextMonth();
  4467. else p.prevMonth();
  4468. }
  4469. else {
  4470. if (rtl) p.prevMonth();
  4471. else p.nextMonth();
  4472. }
  4473. }
  4474. else {
  4475. if (percentage <= -0.5) {
  4476. if (rtl) p.prevMonth();
  4477. else p.nextMonth();
  4478. }
  4479. else if (percentage >= 0.5) {
  4480. if (rtl) p.nextMonth();
  4481. else p.prevMonth();
  4482. }
  4483. else {
  4484. p.resetMonth();
  4485. }
  4486. }
  4487. // Allow click
  4488. setTimeout(function () {
  4489. allowItemClick = true;
  4490. }, 100);
  4491. }
  4492. function handleDayClick(e) {
  4493. if (!allowItemClick) return;
  4494. var day = $(e.target).parents('.picker-calendar-day');
  4495. if (day.length === 0 && $(e.target).hasClass('picker-calendar-day')) {
  4496. day = $(e.target);
  4497. }
  4498. if (day.length === 0) return;
  4499. // if (day.hasClass('picker-calendar-day-selected') && !p.params.multiple) return;
  4500. if (day.hasClass('picker-calendar-day-disabled')) return;
  4501. if (day.hasClass('picker-calendar-day-next')) p.nextMonth();
  4502. if (day.hasClass('picker-calendar-day-prev')) p.prevMonth();
  4503. var dateYear = day.attr('data-year');
  4504. var dateMonth = day.attr('data-month');
  4505. var dateDay = day.attr('data-day');
  4506. if (p.params.onDayClick) {
  4507. p.params.onDayClick(p, day[0], dateYear, dateMonth, dateDay);
  4508. }
  4509. p.addValue(new Date(dateYear, dateMonth, dateDay).getTime());
  4510. if (p.params.closeOnSelect && !p.params.multiple) p.close();
  4511. }
  4512. p.container.find('.picker-calendar-prev-month').on('click', p.prevMonth);
  4513. p.container.find('.picker-calendar-next-month').on('click', p.nextMonth);
  4514. p.container.find('.picker-calendar-prev-year').on('click', p.prevYear);
  4515. p.container.find('.picker-calendar-next-year').on('click', p.nextYear);
  4516. p.wrapper.on('click', handleDayClick);
  4517. if (p.params.touchMove) {
  4518. p.wrapper.on($.touchEvents.start, handleTouchStart);
  4519. p.wrapper.on($.touchEvents.move, handleTouchMove);
  4520. p.wrapper.on($.touchEvents.end, handleTouchEnd);
  4521. }
  4522. p.container[0].f7DestroyCalendarEvents = function () {
  4523. p.container.find('.picker-calendar-prev-month').off('click', p.prevMonth);
  4524. p.container.find('.picker-calendar-next-month').off('click', p.nextMonth);
  4525. p.container.find('.picker-calendar-prev-year').off('click', p.prevYear);
  4526. p.container.find('.picker-calendar-next-year').off('click', p.nextYear);
  4527. p.wrapper.off('click', handleDayClick);
  4528. if (p.params.touchMove) {
  4529. p.wrapper.off($.touchEvents.start, handleTouchStart);
  4530. p.wrapper.off($.touchEvents.move, handleTouchMove);
  4531. p.wrapper.off($.touchEvents.end, handleTouchEnd);
  4532. }
  4533. };
  4534. };
  4535. p.destroyCalendarEvents = function (colContainer) {
  4536. if ('f7DestroyCalendarEvents' in p.container[0]) p.container[0].f7DestroyCalendarEvents();
  4537. };
  4538. // Calendar Methods
  4539. p.daysInMonth = function (date) {
  4540. var d = new Date(date);
  4541. return new Date(d.getFullYear(), d.getMonth() + 1, 0).getDate();
  4542. };
  4543. p.monthHTML = function (date, offset) {
  4544. date = new Date(date);
  4545. var year = date.getFullYear(),
  4546. month = date.getMonth(),
  4547. day = date.getDate();
  4548. if (offset === 'next') {
  4549. if (month === 11) date = new Date(year + 1, 0);
  4550. else date = new Date(year, month + 1, 1);
  4551. }
  4552. if (offset === 'prev') {
  4553. if (month === 0) date = new Date(year - 1, 11);
  4554. else date = new Date(year, month - 1, 1);
  4555. }
  4556. if (offset === 'next' || offset === 'prev') {
  4557. month = date.getMonth();
  4558. year = date.getFullYear();
  4559. }
  4560. var daysInPrevMonth = p.daysInMonth(new Date(date.getFullYear(), date.getMonth()).getTime() - 10 * 24 * 60 * 60 * 1000),
  4561. daysInMonth = p.daysInMonth(date),
  4562. firstDayOfMonthIndex = new Date(date.getFullYear(), date.getMonth()).getDay();
  4563. if (firstDayOfMonthIndex === 0) firstDayOfMonthIndex = 7;
  4564. var dayDate, currentValues = [], i, j,
  4565. rows = 6, cols = 7,
  4566. monthHTML = '',
  4567. dayIndex = 0 + (p.params.firstDay - 1),
  4568. today = new Date().setHours(0,0,0,0),
  4569. minDate = p.params.minDate ? new Date(p.params.minDate).getTime() : null,
  4570. maxDate = p.params.maxDate ? new Date(p.params.maxDate).getTime() : null;
  4571. if (p.value && p.value.length) {
  4572. for (i = 0; i < p.value.length; i++) {
  4573. currentValues.push(new Date(p.value[i]).setHours(0,0,0,0));
  4574. }
  4575. }
  4576. for (i = 1; i <= rows; i++) {
  4577. var rowHTML = '';
  4578. var row = i;
  4579. for (j = 1; j <= cols; j++) {
  4580. var col = j;
  4581. dayIndex ++;
  4582. var dayNumber = dayIndex - firstDayOfMonthIndex;
  4583. var addClass = '';
  4584. if (dayNumber < 0) {
  4585. dayNumber = daysInPrevMonth + dayNumber + 1;
  4586. addClass += ' picker-calendar-day-prev';
  4587. dayDate = new Date(month - 1 < 0 ? year - 1 : year, month - 1 < 0 ? 11 : month - 1, dayNumber).getTime();
  4588. }
  4589. else {
  4590. dayNumber = dayNumber + 1;
  4591. if (dayNumber > daysInMonth) {
  4592. dayNumber = dayNumber - daysInMonth;
  4593. addClass += ' picker-calendar-day-next';
  4594. dayDate = new Date(month + 1 > 11 ? year + 1 : year, month + 1 > 11 ? 0 : month + 1, dayNumber).getTime();
  4595. }
  4596. else {
  4597. dayDate = new Date(year, month, dayNumber).getTime();
  4598. }
  4599. }
  4600. // Today
  4601. if (dayDate === today) addClass += ' picker-calendar-day-today';
  4602. // Selected
  4603. if (currentValues.indexOf(dayDate) >= 0) addClass += ' picker-calendar-day-selected';
  4604. // Weekend
  4605. if (p.params.weekendDays.indexOf(col - 1) >= 0) {
  4606. addClass += ' picker-calendar-day-weekend';
  4607. }
  4608. // Disabled
  4609. if ((minDate && dayDate < minDate) || (maxDate && dayDate > maxDate)) {
  4610. addClass += ' picker-calendar-day-disabled';
  4611. }
  4612. dayDate = new Date(dayDate);
  4613. var dayYear = dayDate.getFullYear();
  4614. var dayMonth = dayDate.getMonth();
  4615. rowHTML += '<div data-year="' + dayYear + '" data-month="' + dayMonth + '" data-day="' + dayNumber + '" class="picker-calendar-day' + (addClass) + '" data-date="' + (dayYear + '-' + dayMonth + '-' + dayNumber) + '"><span>'+dayNumber+'</span></div>';
  4616. }
  4617. monthHTML += '<div class="picker-calendar-row">' + rowHTML + '</div>';
  4618. }
  4619. monthHTML = '<div class="picker-calendar-month" data-year="' + year + '" data-month="' + month + '">' + monthHTML + '</div>';
  4620. return monthHTML;
  4621. };
  4622. p.animating = false;
  4623. p.updateCurrentMonthYear = function (dir) {
  4624. if (typeof dir === 'undefined') {
  4625. p.currentMonth = parseInt(p.months.eq(1).attr('data-month'), 10);
  4626. p.currentYear = parseInt(p.months.eq(1).attr('data-year'), 10);
  4627. }
  4628. else {
  4629. p.currentMonth = parseInt(p.months.eq(dir === 'next' ? (p.months.length - 1) : 0).attr('data-month'), 10);
  4630. p.currentYear = parseInt(p.months.eq(dir === 'next' ? (p.months.length - 1) : 0).attr('data-year'), 10);
  4631. }
  4632. p.container.find('.current-month-value').text(p.params.monthNames[p.currentMonth]);
  4633. p.container.find('.current-year-value').text(p.currentYear);
  4634. };
  4635. p.onMonthChangeStart = function (dir) {
  4636. p.updateCurrentMonthYear(dir);
  4637. p.months.removeClass('picker-calendar-month-current picker-calendar-month-prev picker-calendar-month-next');
  4638. var currentIndex = dir === 'next' ? p.months.length - 1 : 0;
  4639. p.months.eq(currentIndex).addClass('picker-calendar-month-current');
  4640. p.months.eq(dir === 'next' ? currentIndex - 1 : currentIndex + 1).addClass(dir === 'next' ? 'picker-calendar-month-prev' : 'picker-calendar-month-next');
  4641. if (p.params.onMonthYearChangeStart) {
  4642. p.params.onMonthYearChangeStart(p, p.currentYear, p.currentMonth);
  4643. }
  4644. };
  4645. p.onMonthChangeEnd = function (dir, rebuildBoth) {
  4646. p.animating = false;
  4647. var nextMonthHTML, prevMonthHTML, newMonthHTML;
  4648. p.wrapper.find('.picker-calendar-month:not(.picker-calendar-month-prev):not(.picker-calendar-month-current):not(.picker-calendar-month-next)').remove();
  4649. if (typeof dir === 'undefined') {
  4650. dir = 'next';
  4651. rebuildBoth = true;
  4652. }
  4653. if (!rebuildBoth) {
  4654. newMonthHTML = p.monthHTML(new Date(p.currentYear, p.currentMonth), dir);
  4655. }
  4656. else {
  4657. p.wrapper.find('.picker-calendar-month-next, .picker-calendar-month-prev').remove();
  4658. prevMonthHTML = p.monthHTML(new Date(p.currentYear, p.currentMonth), 'prev');
  4659. nextMonthHTML = p.monthHTML(new Date(p.currentYear, p.currentMonth), 'next');
  4660. }
  4661. if (dir === 'next' || rebuildBoth) {
  4662. p.wrapper.append(newMonthHTML || nextMonthHTML);
  4663. }
  4664. if (dir === 'prev' || rebuildBoth) {
  4665. p.wrapper.prepend(newMonthHTML || prevMonthHTML);
  4666. }
  4667. p.months = p.wrapper.find('.picker-calendar-month');
  4668. p.setMonthsTranslate(p.monthsTranslate);
  4669. if (p.params.onMonthAdd) {
  4670. p.params.onMonthAdd(p, dir === 'next' ? p.months.eq(p.months.length - 1)[0] : p.months.eq(0)[0]);
  4671. }
  4672. if (p.params.onMonthYearChangeEnd) {
  4673. p.params.onMonthYearChangeEnd(p, p.currentYear, p.currentMonth);
  4674. }
  4675. };
  4676. p.setMonthsTranslate = function (translate) {
  4677. translate = translate || p.monthsTranslate || 0;
  4678. if (typeof p.monthsTranslate === 'undefined') p.monthsTranslate = translate;
  4679. p.months.removeClass('picker-calendar-month-current picker-calendar-month-prev picker-calendar-month-next');
  4680. var prevMonthTranslate = -(translate + 1) * 100 * inverter;
  4681. var currentMonthTranslate = -translate * 100 * inverter;
  4682. var nextMonthTranslate = -(translate - 1) * 100 * inverter;
  4683. p.months.eq(0).transform('translate3d(' + (p.isH ? prevMonthTranslate : 0) + '%, ' + (p.isH ? 0 : prevMonthTranslate) + '%, 0)').addClass('picker-calendar-month-prev');
  4684. p.months.eq(1).transform('translate3d(' + (p.isH ? currentMonthTranslate : 0) + '%, ' + (p.isH ? 0 : currentMonthTranslate) + '%, 0)').addClass('picker-calendar-month-current');
  4685. p.months.eq(2).transform('translate3d(' + (p.isH ? nextMonthTranslate : 0) + '%, ' + (p.isH ? 0 : nextMonthTranslate) + '%, 0)').addClass('picker-calendar-month-next');
  4686. };
  4687. p.nextMonth = function (transition) {
  4688. if (typeof transition === 'undefined' || typeof transition === 'object') {
  4689. transition = '';
  4690. if (!p.params.animate) transition = 0;
  4691. }
  4692. var nextMonth = parseInt(p.months.eq(p.months.length - 1).attr('data-month'), 10);
  4693. var nextYear = parseInt(p.months.eq(p.months.length - 1).attr('data-year'), 10);
  4694. var nextDate = new Date(nextYear, nextMonth);
  4695. var nextDateTime = nextDate.getTime();
  4696. var transitionEndCallback = p.animating ? false : true;
  4697. if (p.params.maxDate) {
  4698. if (nextDateTime > new Date(p.params.maxDate).getTime()) {
  4699. return p.resetMonth();
  4700. }
  4701. }
  4702. p.monthsTranslate --;
  4703. if (nextMonth === p.currentMonth) {
  4704. var nextMonthTranslate = -(p.monthsTranslate) * 100 * inverter;
  4705. var nextMonthHTML = $(p.monthHTML(nextDateTime, 'next')).transform('translate3d(' + (p.isH ? nextMonthTranslate : 0) + '%, ' + (p.isH ? 0 : nextMonthTranslate) + '%, 0)').addClass('picker-calendar-month-next');
  4706. p.wrapper.append(nextMonthHTML[0]);
  4707. p.months = p.wrapper.find('.picker-calendar-month');
  4708. if (p.params.onMonthAdd) {
  4709. p.params.onMonthAdd(p, p.months.eq(p.months.length - 1)[0]);
  4710. }
  4711. }
  4712. p.animating = true;
  4713. p.onMonthChangeStart('next');
  4714. var translate = (p.monthsTranslate * 100) * inverter;
  4715. p.wrapper.transition(transition).transform('translate3d(' + (p.isH ? translate : 0) + '%, ' + (p.isH ? 0 : translate) + '%, 0)');
  4716. if (transitionEndCallback) {
  4717. p.wrapper.transitionEnd(function () {
  4718. p.onMonthChangeEnd('next');
  4719. });
  4720. }
  4721. if (!p.params.animate) {
  4722. p.onMonthChangeEnd('next');
  4723. }
  4724. };
  4725. p.prevMonth = function (transition) {
  4726. if (typeof transition === 'undefined' || typeof transition === 'object') {
  4727. transition = '';
  4728. if (!p.params.animate) transition = 0;
  4729. }
  4730. var prevMonth = parseInt(p.months.eq(0).attr('data-month'), 10);
  4731. var prevYear = parseInt(p.months.eq(0).attr('data-year'), 10);
  4732. var prevDate = new Date(prevYear, prevMonth + 1, -1);
  4733. var prevDateTime = prevDate.getTime();
  4734. var transitionEndCallback = p.animating ? false : true;
  4735. if (p.params.minDate) {
  4736. if (prevDateTime < new Date(p.params.minDate).getTime()) {
  4737. return p.resetMonth();
  4738. }
  4739. }
  4740. p.monthsTranslate ++;
  4741. if (prevMonth === p.currentMonth) {
  4742. var prevMonthTranslate = -(p.monthsTranslate) * 100 * inverter;
  4743. var prevMonthHTML = $(p.monthHTML(prevDateTime, 'prev')).transform('translate3d(' + (p.isH ? prevMonthTranslate : 0) + '%, ' + (p.isH ? 0 : prevMonthTranslate) + '%, 0)').addClass('picker-calendar-month-prev');
  4744. p.wrapper.prepend(prevMonthHTML[0]);
  4745. p.months = p.wrapper.find('.picker-calendar-month');
  4746. if (p.params.onMonthAdd) {
  4747. p.params.onMonthAdd(p, p.months.eq(0)[0]);
  4748. }
  4749. }
  4750. p.animating = true;
  4751. p.onMonthChangeStart('prev');
  4752. var translate = (p.monthsTranslate * 100) * inverter;
  4753. p.wrapper.transition(transition).transform('translate3d(' + (p.isH ? translate : 0) + '%, ' + (p.isH ? 0 : translate) + '%, 0)');
  4754. if (transitionEndCallback) {
  4755. p.wrapper.transitionEnd(function () {
  4756. p.onMonthChangeEnd('prev');
  4757. });
  4758. }
  4759. if (!p.params.animate) {
  4760. p.onMonthChangeEnd('prev');
  4761. }
  4762. };
  4763. p.resetMonth = function (transition) {
  4764. if (typeof transition === 'undefined') transition = '';
  4765. var translate = (p.monthsTranslate * 100) * inverter;
  4766. p.wrapper.transition(transition).transform('translate3d(' + (p.isH ? translate : 0) + '%, ' + (p.isH ? 0 : translate) + '%, 0)');
  4767. };
  4768. p.setYearMonth = function (year, month, transition) {
  4769. if (typeof year === 'undefined') year = p.currentYear;
  4770. if (typeof month === 'undefined') month = p.currentMonth;
  4771. if (typeof transition === 'undefined' || typeof transition === 'object') {
  4772. transition = '';
  4773. if (!p.params.animate) transition = 0;
  4774. }
  4775. var targetDate;
  4776. if (year < p.currentYear) {
  4777. targetDate = new Date(year, month + 1, -1).getTime();
  4778. }
  4779. else {
  4780. targetDate = new Date(year, month).getTime();
  4781. }
  4782. if (p.params.maxDate && targetDate > new Date(p.params.maxDate).getTime()) {
  4783. return false;
  4784. }
  4785. if (p.params.minDate && targetDate < new Date(p.params.minDate).getTime()) {
  4786. return false;
  4787. }
  4788. var currentDate = new Date(p.currentYear, p.currentMonth).getTime();
  4789. var dir = targetDate > currentDate ? 'next' : 'prev';
  4790. var newMonthHTML = p.monthHTML(new Date(year, month));
  4791. p.monthsTranslate = p.monthsTranslate || 0;
  4792. var prevTranslate = p.monthsTranslate;
  4793. var monthTranslate, wrapperTranslate;
  4794. var transitionEndCallback = p.animating ? false : true;
  4795. if (targetDate > currentDate) {
  4796. // To next
  4797. p.monthsTranslate --;
  4798. if (!p.animating) p.months.eq(p.months.length - 1).remove();
  4799. p.wrapper.append(newMonthHTML);
  4800. p.months = p.wrapper.find('.picker-calendar-month');
  4801. monthTranslate = -(prevTranslate - 1) * 100 * inverter;
  4802. p.months.eq(p.months.length - 1).transform('translate3d(' + (p.isH ? monthTranslate : 0) + '%, ' + (p.isH ? 0 : monthTranslate) + '%, 0)').addClass('picker-calendar-month-next');
  4803. }
  4804. else {
  4805. // To prev
  4806. p.monthsTranslate ++;
  4807. if (!p.animating) p.months.eq(0).remove();
  4808. p.wrapper.prepend(newMonthHTML);
  4809. p.months = p.wrapper.find('.picker-calendar-month');
  4810. monthTranslate = -(prevTranslate + 1) * 100 * inverter;
  4811. p.months.eq(0).transform('translate3d(' + (p.isH ? monthTranslate : 0) + '%, ' + (p.isH ? 0 : monthTranslate) + '%, 0)').addClass('picker-calendar-month-prev');
  4812. }
  4813. if (p.params.onMonthAdd) {
  4814. p.params.onMonthAdd(p, dir === 'next' ? p.months.eq(p.months.length - 1)[0] : p.months.eq(0)[0]);
  4815. }
  4816. p.animating = true;
  4817. p.onMonthChangeStart(dir);
  4818. wrapperTranslate = (p.monthsTranslate * 100) * inverter;
  4819. p.wrapper.transition(transition).transform('translate3d(' + (p.isH ? wrapperTranslate : 0) + '%, ' + (p.isH ? 0 : wrapperTranslate) + '%, 0)');
  4820. if (transitionEndCallback) {
  4821. p.wrapper.transitionEnd(function () {
  4822. p.onMonthChangeEnd(dir, true);
  4823. });
  4824. }
  4825. if (!p.params.animate) {
  4826. p.onMonthChangeEnd(dir);
  4827. }
  4828. };
  4829. p.nextYear = function () {
  4830. p.setYearMonth(p.currentYear + 1);
  4831. };
  4832. p.prevYear = function () {
  4833. p.setYearMonth(p.currentYear - 1);
  4834. };
  4835. // HTML Layout
  4836. p.layout = function () {
  4837. var pickerHTML = '';
  4838. var pickerClass = '';
  4839. var i;
  4840. var layoutDate = p.value && p.value.length ? p.value[0] : new Date().setHours(0,0,0,0);
  4841. var prevMonthHTML = p.monthHTML(layoutDate, 'prev');
  4842. var currentMonthHTML = p.monthHTML(layoutDate);
  4843. var nextMonthHTML = p.monthHTML(layoutDate, 'next');
  4844. var monthsHTML = '<div class="picker-calendar-months"><div class="picker-calendar-months-wrapper">' + (prevMonthHTML + currentMonthHTML + nextMonthHTML) + '</div></div>';
  4845. // Week days header
  4846. var weekHeaderHTML = '';
  4847. if (p.params.weekHeader) {
  4848. for (i = 0; i < 7; i++) {
  4849. var weekDayIndex = (i + p.params.firstDay > 6) ? (i - 7 + p.params.firstDay) : (i + p.params.firstDay);
  4850. var dayName = p.params.dayNamesShort[weekDayIndex];
  4851. weekHeaderHTML += '<div class="picker-calendar-week-day ' + ((p.params.weekendDays.indexOf(weekDayIndex) >= 0) ? 'picker-calendar-week-day-weekend' : '') + '"> ' + dayName + '</div>';
  4852. }
  4853. weekHeaderHTML = '<div class="picker-calendar-week-days">' + weekHeaderHTML + '</div>';
  4854. }
  4855. pickerClass = 'weui-picker-calendar ' + (p.params.cssClass || '');
  4856. if(!p.inline) pickerClass = 'weui-picker-modal ' + pickerClass;
  4857. var toolbarHTML = p.params.toolbar ? p.params.toolbarTemplate.replace(/{{closeText}}/g, p.params.toolbarCloseText) : '';
  4858. if (p.params.toolbar) {
  4859. toolbarHTML = p.params.toolbarTemplate
  4860. .replace(/{{closeText}}/g, p.params.toolbarCloseText)
  4861. .replace(/{{monthPicker}}/g, (p.params.monthPicker ? p.params.monthPickerTemplate : ''))
  4862. .replace(/{{yearPicker}}/g, (p.params.yearPicker ? p.params.yearPickerTemplate : ''));
  4863. }
  4864. pickerHTML =
  4865. '<div class="' + (pickerClass) + '">' +
  4866. toolbarHTML +
  4867. '<div class="picker-modal-inner">' +
  4868. weekHeaderHTML +
  4869. monthsHTML +
  4870. '</div>' +
  4871. '</div>';
  4872. p.pickerHTML = pickerHTML;
  4873. };
  4874. // Input Events
  4875. function openOnInput(e) {
  4876. e.preventDefault();
  4877. if (p.opened) return;
  4878. p.open();
  4879. if (p.params.scrollToInput && !isPopover()) {
  4880. var pageContent = p.input.parents('.page-content');
  4881. if (pageContent.length === 0) return;
  4882. var paddingTop = parseInt(pageContent.css('padding-top'), 10),
  4883. paddingBottom = parseInt(pageContent.css('padding-bottom'), 10),
  4884. pageHeight = pageContent[0].offsetHeight - paddingTop - p.container.height(),
  4885. pageScrollHeight = pageContent[0].scrollHeight - paddingTop - p.container.height(),
  4886. newPaddingBottom;
  4887. var inputTop = p.input.offset().top - paddingTop + p.input[0].offsetHeight;
  4888. if (inputTop > pageHeight) {
  4889. var scrollTop = pageContent.scrollTop() + inputTop - pageHeight;
  4890. if (scrollTop + pageHeight > pageScrollHeight) {
  4891. newPaddingBottom = scrollTop + pageHeight - pageScrollHeight + paddingBottom;
  4892. if (pageHeight === pageScrollHeight) {
  4893. newPaddingBottom = p.container.height();
  4894. }
  4895. pageContent.css({'padding-bottom': (newPaddingBottom) + 'px'});
  4896. }
  4897. pageContent.scrollTop(scrollTop, 300);
  4898. }
  4899. }
  4900. }
  4901. function closeOnHTMLClick(e) {
  4902. if (inPopover()) return;
  4903. if (p.input && p.input.length > 0) {
  4904. if (e.target !== p.input[0] && $(e.target).parents('.weui-picker-modal').length === 0) p.close();
  4905. }
  4906. else {
  4907. if ($(e.target).parents('.weui-picker-modal').length === 0) p.close();
  4908. }
  4909. }
  4910. if (p.params.input) {
  4911. p.input = $(p.params.input);
  4912. if (p.input.length > 0) {
  4913. if (p.params.inputReadOnly) p.input.prop('readOnly', true);
  4914. if (!p.inline) {
  4915. p.input.on('click', openOnInput);
  4916. }
  4917. if (p.params.inputReadOnly) {
  4918. p.input.on('focus mousedown', function (e) {
  4919. e.preventDefault();
  4920. });
  4921. }
  4922. }
  4923. }
  4924. //iphone 上无法正确触发 click,会导致点击外面无法关闭
  4925. if (!p.inline) $(document).on('click touchend', closeOnHTMLClick);
  4926. // Open
  4927. function onPickerClose() {
  4928. p.opened = false;
  4929. if (p.input && p.input.length > 0) p.input.parents('.page-content').css({'padding-bottom': ''});
  4930. if (p.params.onClose) p.params.onClose(p);
  4931. // Destroy events
  4932. p.destroyCalendarEvents();
  4933. }
  4934. p.opened = false;
  4935. p.open = function () {
  4936. var toPopover = isPopover() && false;
  4937. var updateValue = false;
  4938. if (!p.opened) {
  4939. // Set date value
  4940. if (!p.value) {
  4941. if (p.params.value) {
  4942. p.value = p.params.value;
  4943. updateValue = true;
  4944. }
  4945. }
  4946. // Layout
  4947. p.layout();
  4948. // Append
  4949. if (toPopover) {
  4950. p.pickerHTML = '<div class="popover popover-picker-calendar"><div class="popover-inner">' + p.pickerHTML + '</div></div>';
  4951. p.popover = $.popover(p.pickerHTML, p.params.input, true);
  4952. p.container = $(p.popover).find('.weui-picker-modal');
  4953. $(p.popover).on('close', function () {
  4954. onPickerClose();
  4955. });
  4956. }
  4957. else if (p.inline) {
  4958. p.container = $(p.pickerHTML);
  4959. p.container.addClass('picker-modal-inline');
  4960. $(p.params.container).append(p.container);
  4961. }
  4962. else {
  4963. p.container = $($.openPicker(p.pickerHTML));
  4964. $(p.container)
  4965. .on('close', function () {
  4966. onPickerClose();
  4967. });
  4968. }
  4969. // Store calendar instance
  4970. p.container[0].f7Calendar = p;
  4971. p.wrapper = p.container.find('.picker-calendar-months-wrapper');
  4972. // Months
  4973. p.months = p.wrapper.find('.picker-calendar-month');
  4974. // Update current month and year
  4975. p.updateCurrentMonthYear();
  4976. // Set initial translate
  4977. p.monthsTranslate = 0;
  4978. p.setMonthsTranslate();
  4979. // Init events
  4980. p.initCalendarEvents();
  4981. // Update input value
  4982. if (updateValue) p.updateValue();
  4983. }
  4984. // Set flag
  4985. p.opened = true;
  4986. p.initialized = true;
  4987. if (p.params.onMonthAdd) {
  4988. p.months.each(function () {
  4989. p.params.onMonthAdd(p, this);
  4990. });
  4991. }
  4992. if (p.params.onOpen) p.params.onOpen(p);
  4993. };
  4994. // Close
  4995. p.close = function () {
  4996. if (!p.opened || p.inline) return;
  4997. p.animating = false; //有可能还有动画没做完,因此animating设置还没改。
  4998. if (inPopover()) {
  4999. $.closePicker(p.popover);
  5000. return;
  5001. }
  5002. else {
  5003. $.closePicker(p.container);
  5004. return;
  5005. }
  5006. };
  5007. // Destroy
  5008. p.destroy = function () {
  5009. p.close();
  5010. if (p.params.input && p.input.length > 0) {
  5011. p.input.off('click focus', openOnInput);
  5012. p.input.data("calendar", null);
  5013. }
  5014. $('html').off('click', closeOnHTMLClick);
  5015. };
  5016. if (p.inline) {
  5017. p.open();
  5018. }
  5019. return p;
  5020. };
  5021. var format = function(d) {
  5022. return d < 10 ? "0"+d : d;
  5023. }
  5024. $.fn.calendar = function (params, args) {
  5025. params = params || {};
  5026. return this.each(function() {
  5027. var $this = $(this);
  5028. if(!$this[0]) return;
  5029. var p = {};
  5030. if($this[0].tagName.toUpperCase() === "INPUT") {
  5031. p.input = $this;
  5032. } else {
  5033. p.container = $this;
  5034. }
  5035. var calendar = $this.data("calendar");
  5036. if(!calendar) {
  5037. if(typeof params === typeof "a") {
  5038. } else {
  5039. if(!params.value && $this.val()) params.value = [$this.val()];
  5040. //默认显示今天
  5041. if(!params.value) {
  5042. var today = new Date();
  5043. params.value = [today.getFullYear() + "-" + format(today.getMonth() + 1) + "-" + format(today.getDate())];
  5044. }
  5045. calendar = $this.data("calendar", new Calendar($.extend(p, params)));
  5046. }
  5047. }
  5048. if(typeof params === typeof "a") {
  5049. calendar[params].call(calendar, args);
  5050. }
  5051. });
  5052. };
  5053. defaults = $.fn.calendar.prototype.defaults = {
  5054. value: undefined, // 通过JS赋值,注意是数组
  5055. monthNames: ['一月', '二月', '三月', '四月', '五月', '六月', '七月', '八月', '九月', '十月', '十一月', '十二月'],
  5056. monthNamesShort: ['一月', '二月', '三月', '四月', '五月', '六月', '七月', '八月', '九月', '十月', '十一月', '十二月'],
  5057. dayNames: ['周日', '周一', '周二', '周三', '周四', '周五', '周六'],
  5058. dayNamesShort: ['周日', '周一', '周二', '周三', '周四', '周五', '周六'],
  5059. firstDay: 1, // First day of the week, Monday
  5060. weekendDays: [0, 6], // Sunday and Saturday
  5061. multiple: false,
  5062. dateFormat: 'yyyy-mm-dd',
  5063. direction: 'horizontal', // or 'vertical'
  5064. minDate: null,
  5065. maxDate: null,
  5066. touchMove: true,
  5067. animate: true,
  5068. closeOnSelect: true,
  5069. monthPicker: true,
  5070. monthPickerTemplate:
  5071. '<div class="picker-calendar-month-picker">' +
  5072. '<a href="javascript:;" class="link icon-only picker-calendar-prev-month"><i class="icon icon-prev"></i></a>' +
  5073. '<div class="current-month-value"></div>' +
  5074. '<a href="javascript:;" class="link icon-only picker-calendar-next-month"><i class="icon icon-next"></i></a>' +
  5075. '</div>',
  5076. yearPicker: true,
  5077. yearPickerTemplate:
  5078. '<div class="picker-calendar-year-picker">' +
  5079. '<a href="javascript:;" class="link icon-only picker-calendar-prev-year"><i class="icon icon-prev"></i></a>' +
  5080. '<span class="current-year-value"></span>' +
  5081. '<a href="javascript:;" class="link icon-only picker-calendar-next-year"><i class="icon icon-next"></i></a>' +
  5082. '</div>',
  5083. weekHeader: true,
  5084. // Common settings
  5085. scrollToInput: true,
  5086. inputReadOnly: true,
  5087. convertToPopover: true,
  5088. onlyInPopover: false,
  5089. toolbar: true,
  5090. toolbarCloseText: 'Done',
  5091. toolbarTemplate:
  5092. '<div class="toolbar">' +
  5093. '<div class="toolbar-inner">' +
  5094. '{{yearPicker}}' +
  5095. '{{monthPicker}}' +
  5096. // '<a href="#" class="link close-picker">{{closeText}}</a>' +
  5097. '</div>' +
  5098. '</div>',
  5099. /* Callbacks
  5100. onMonthAdd
  5101. onChange
  5102. onOpen
  5103. onClose
  5104. onDayClick
  5105. onMonthYearChangeStart
  5106. onMonthYearChangeEnd
  5107. */
  5108. };
  5109. }($);
  5110. /* global $:true */
  5111. /* jshint unused:false*/
  5112. + function($) {
  5113. "use strict";
  5114. var defaults;
  5115. var formatNumber = function (n) {
  5116. return n < 10 ? "0" + n : n;
  5117. }
  5118. var Datetime = function(input, params) {
  5119. this.input = $(input);
  5120. this.params = params;
  5121. this.initMonthes = ('01 02 03 04 05 06 07 08 09 10 11 12').split(' ');
  5122. this.initYears = (function () {
  5123. var arr = [];
  5124. for (var i = 1950; i <= 2030; i++) { arr.push(i); }
  5125. return arr;
  5126. })();
  5127. var p = $.extend({}, params, this.getConfig());
  5128. $(this.input).picker(p);
  5129. }
  5130. Datetime.prototype = {
  5131. getDays : function(max) {
  5132. var days = [];
  5133. for(var i=1; i<= (max||31);i++) {
  5134. days.push(i < 10 ? "0"+i : i);
  5135. }
  5136. return days;
  5137. },
  5138. getDaysByMonthAndYear : function(month, year) {
  5139. var int_d = new Date(year, parseInt(month)+1-1, 1);
  5140. var d = new Date(int_d - 1);
  5141. return this.getDays(d.getDate());
  5142. },
  5143. getConfig: function() {
  5144. var today = new Date(),
  5145. params = this.params,
  5146. self = this,
  5147. lastValidValues;
  5148. var config = {
  5149. rotateEffect: false, //为了性能
  5150. cssClass: 'datetime-picker',
  5151. value: [today.getFullYear(), formatNumber(today.getMonth()+1), formatNumber(today.getDate()), formatNumber(today.getHours()), (formatNumber(today.getMinutes()))],
  5152. onChange: function (picker, values, displayValues) {
  5153. var cols = picker.cols;
  5154. var days = self.getDaysByMonthAndYear(values[1], values[0]);
  5155. var currentValue = values[2];
  5156. if(currentValue > days.length) currentValue = days.length;
  5157. picker.cols[4].setValue(currentValue);
  5158. //check min and max
  5159. var current = new Date(values[0]+'-'+values[1]+'-'+values[2]);
  5160. var valid = true;
  5161. if(params.min) {
  5162. var min = new Date(typeof params.min === "function" ? params.min() : params.min);
  5163. if(current < +min) {
  5164. picker.setValue(lastValidValues);
  5165. valid = false;
  5166. }
  5167. }
  5168. if(params.max) {
  5169. var max = new Date(typeof params.max === "function" ? params.max() : params.max);
  5170. if(current > +max) {
  5171. picker.setValue(lastValidValues);
  5172. valid = false;
  5173. }
  5174. }
  5175. valid && (lastValidValues = values);
  5176. if (self.params.onChange) {
  5177. self.params.onChange.apply(this, arguments);
  5178. }
  5179. },
  5180. formatValue: function (p, values, displayValues) {
  5181. return self.params.format(p, values, displayValues);
  5182. },
  5183. cols: [
  5184. {
  5185. values: (function () {
  5186. var years = [];
  5187. for (var i=1950; i<=2050; i++) years.push(i);
  5188. return years;
  5189. })()
  5190. },
  5191. {
  5192. divider: true, // 这是一个分隔符
  5193. content: params.yearSplit
  5194. },
  5195. {
  5196. values: ['01', '02', '03', '04', '05', '06', '07', '08', '09', '10', '11', '12']
  5197. },
  5198. {
  5199. divider: true, // 这是一个分隔符
  5200. content: params.monthSplit
  5201. },
  5202. {
  5203. values: (function () {
  5204. var dates = [];
  5205. for (var i=1; i<=31; i++) dates.push(formatNumber(i));
  5206. return dates;
  5207. })()
  5208. },
  5209. ]
  5210. }
  5211. if (params.dateSplit) {
  5212. config.cols.push({
  5213. divider: true,
  5214. content: params.dateSplit
  5215. })
  5216. }
  5217. config.cols.push({
  5218. divider: true,
  5219. content: params.datetimeSplit
  5220. })
  5221. var times = self.params.times();
  5222. if (times && times.length) {
  5223. config.cols = config.cols.concat(times);
  5224. }
  5225. var inputValue = this.input.val();
  5226. if(inputValue) config.value = params.parse(inputValue);
  5227. if(this.params.value) {
  5228. this.input.val(this.params.value);
  5229. config.value = params.parse(this.params.value);
  5230. }
  5231. return config;
  5232. }
  5233. }
  5234. $.fn.datetimePicker = function(params) {
  5235. params = $.extend({}, defaults, params);
  5236. return this.each(function() {
  5237. if(!this) return;
  5238. var $this = $(this);
  5239. var datetime = $this.data("datetime");
  5240. if(!datetime) $this.data("datetime", new Datetime(this, params));
  5241. return datetime;
  5242. });
  5243. };
  5244. defaults = $.fn.datetimePicker.prototype.defaults = {
  5245. input: undefined, // 默认值
  5246. min: undefined, // YYYY-MM-DD 最大最小值只比较年月日,不比较时分秒
  5247. max: undefined, // YYYY-MM-DD
  5248. yearSplit: '-',
  5249. monthSplit: '-',
  5250. dateSplit: '', // 默认为空
  5251. datetimeSplit: ' ', // 日期和时间之间的分隔符,不可为空
  5252. times: function () {
  5253. return [ // 自定义的时间
  5254. {
  5255. values: (function () {
  5256. var hours = [];
  5257. for (var i=0; i<24; i++) hours.push(formatNumber(i));
  5258. return hours;
  5259. })()
  5260. },
  5261. {
  5262. divider: true, // 这是一个分隔符
  5263. content: ':'
  5264. },
  5265. {
  5266. values: (function () {
  5267. var minutes = [];
  5268. for (var i=0; i<60; i++) minutes.push(formatNumber(i));
  5269. return minutes;
  5270. })()
  5271. }
  5272. ];
  5273. },
  5274. format: function (p, values) { // 数组转换成字符串
  5275. return p.cols.map(function (col) {
  5276. return col.value || col.content;
  5277. }).join('');
  5278. },
  5279. parse: function (str) {
  5280. // 把字符串转换成数组,用来解析初始值
  5281. // 如果你的定制的初始值格式无法被这个默认函数解析,请自定义这个函数。比如你的时间是 '子时' 那么默认情况这个'时'会被当做分隔符而导致错误,所以你需要自己定义parse函数
  5282. // 默认兼容的分隔符
  5283. var t = str.split(this.datetimeSplit);
  5284. return t[0].split(/\D/).concat(t[1].split(/:|时|分|秒/)).filter(function (d) {
  5285. return !!d;
  5286. })
  5287. }
  5288. }
  5289. }($);
  5290. /*======================================================
  5291. ************ Picker ************
  5292. ======================================================*/
  5293. /* global $:true */
  5294. + function($) {
  5295. "use strict";
  5296. //Popup 和 picker 之类的不要共用一个弹出方法,因为这样会导致 在 popup 中再弹出 picker 的时候会有问题。
  5297. $.openPopup = function(popup, className) {
  5298. $.closePopup();
  5299. popup = $(popup);
  5300. popup.show();
  5301. popup.width();
  5302. popup.addClass("weui-popup__container--visible");
  5303. var modal = popup.find(".weui-popup__modal");
  5304. modal.width();
  5305. modal.transitionEnd(function() {
  5306. modal.trigger("open");
  5307. });
  5308. }
  5309. $.closePopup = function(container, remove) {
  5310. container = $(container || ".weui-popup__container--visible");
  5311. container.find('.weui-popup__modal').transitionEnd(function() {
  5312. var $this = $(this);
  5313. $this.trigger("close");
  5314. container.hide();
  5315. remove && container.remove();
  5316. })
  5317. container.removeClass("weui-popup__container--visible")
  5318. };
  5319. $(document).on("click", ".close-popup, .weui-popup__overlay", function() {
  5320. $.closePopup();
  5321. })
  5322. .on("click", ".open-popup", function() {
  5323. $($(this).data("target")).popup();
  5324. })
  5325. .on("click", ".weui-popup__container", function(e) {
  5326. if($(e.target).hasClass("weui-popup__container")) $.closePopup();
  5327. })
  5328. $.fn.popup = function() {
  5329. return this.each(function() {
  5330. $.openPopup(this);
  5331. });
  5332. };
  5333. }($);
  5334. /* ===============================================================================
  5335. ************ Notification ************
  5336. =============================================================================== */
  5337. /* global $:true */
  5338. +function ($) {
  5339. "use strict";
  5340. var noti, defaults, timeout, start, diffX, diffY;
  5341. var touchStart = function(e) {
  5342. var p = $.getTouchPosition(e);
  5343. start = p;
  5344. diffX = diffY = 0;
  5345. noti.addClass("touching");
  5346. };
  5347. var touchMove = function(e) {
  5348. if(!start) return false;
  5349. e.preventDefault();
  5350. e.stopPropagation();
  5351. var p = $.getTouchPosition(e);
  5352. diffX = p.x - start.x;
  5353. diffY = p.y - start.y;
  5354. if(diffY > 0) {
  5355. diffY = Math.sqrt(diffY);
  5356. }
  5357. noti.css("transform", "translate3d(0, "+diffY+"px, 0)");
  5358. };
  5359. var touchEnd = function() {
  5360. noti.removeClass("touching");
  5361. noti.attr("style", "");
  5362. if(diffY < 0 && (Math.abs(diffY) > noti.height()*0.38)) {
  5363. $.closeNotification();
  5364. }
  5365. if(Math.abs(diffX) <= 1 && Math.abs(diffY) <= 1) {
  5366. noti.trigger("noti-click");
  5367. }
  5368. start = false;
  5369. };
  5370. var attachEvents = function(el) {
  5371. el.on($.touchEvents.start, touchStart);
  5372. el.on($.touchEvents.move, touchMove);
  5373. el.on($.touchEvents.end, touchEnd);
  5374. };
  5375. $.notification = $.noti = function(params) {
  5376. params = $.extend({}, defaults, params);
  5377. noti = $(".weui-notification");
  5378. if(!noti[0]) { // create a new notification
  5379. noti = $('<div class="weui-notification"></div>').appendTo(document.body);
  5380. attachEvents(noti);
  5381. }
  5382. noti.off("noti-click"); //the click event is not correct sometime: it will trigger when user is draging.
  5383. if(params.onClick) noti.on("noti-click", function() {
  5384. params.onClick(params.data);
  5385. });
  5386. noti.html($.t7.compile(params.tpl)(params));
  5387. noti.show();
  5388. noti.addClass("weui-notification--in");
  5389. noti.data("params", params);
  5390. var startTimeout = function() {
  5391. if(timeout) {
  5392. clearTimeout(timeout);
  5393. timeout = null;
  5394. }
  5395. timeout = setTimeout(function() {
  5396. if(noti.hasClass("weui-notification--touching")) {
  5397. startTimeout();
  5398. } else {
  5399. $.closeNotification();
  5400. }
  5401. }, params.time);
  5402. };
  5403. startTimeout();
  5404. };
  5405. $.closeNotification = function() {
  5406. timeout && clearTimeout(timeout);
  5407. timeout = null;
  5408. var noti = $(".weui-notification").removeClass("weui-notification--in").transitionEnd(function() {
  5409. $(this).remove();
  5410. });
  5411. if(noti[0]) {
  5412. var params = $(".weui-notification").data("params");
  5413. if(params && params.onClose) {
  5414. params.onClose(params.data);
  5415. }
  5416. }
  5417. };
  5418. defaults = $.noti.prototype.defaults = {
  5419. title: undefined,
  5420. text: undefined,
  5421. media: undefined,
  5422. time: 4000,
  5423. onClick: undefined,
  5424. onClose: undefined,
  5425. data: undefined,
  5426. tpl: '<div class="weui-notification__inner">' +
  5427. '{{#if media}}<div class="weui-notification__media">{{media}}</div>{{/if}}' +
  5428. '<div class="weui-notification__content">' +
  5429. '{{#if title}}<div class="weui-notification__title">{{title}}</div>{{/if}}' +
  5430. '{{#if text}}<div class="weui-notification__text">{{text}}</div>{{/if}}' +
  5431. '</div>' +
  5432. '<div class="weui-notification__handle-bar"></div>' +
  5433. '</div>'
  5434. };
  5435. }($);
  5436. + function($) {
  5437. "use strict";
  5438. var timeout;
  5439. $.toptip = function(text, duration, type) {
  5440. if(!text) return;
  5441. if(typeof duration === typeof "a") {
  5442. type = duration;
  5443. duration = undefined;
  5444. }
  5445. duration = duration || 3000;
  5446. var className = type ? 'bg-' + type : 'bg-danger';
  5447. var $t = $('.weui-toptips').remove();
  5448. $t = $('<div class="weui-toptips"></div>').appendTo(document.body);
  5449. $t.html(text);
  5450. $t[0].className = 'weui-toptips ' + className
  5451. clearTimeout(timeout);
  5452. if(!$t.hasClass('weui-toptips_visible')) {
  5453. $t.show().width();
  5454. $t.addClass('weui-toptips_visible');
  5455. }
  5456. timeout = setTimeout(function() {
  5457. $t.removeClass('weui-toptips_visible').transitionEnd(function() {
  5458. $t.remove();
  5459. });
  5460. }, duration);
  5461. }
  5462. }($);
  5463. /* global $:true */
  5464. + function($) {
  5465. "use strict";
  5466. var Slider = function (container, arg) {
  5467. this.container = $(container);
  5468. this.handler = this.container.find('.weui-slider__handler')
  5469. this.track = this.container.find('.weui-slider__track')
  5470. this.value = this.container.find('.weui-slider-box__value')
  5471. this.bind()
  5472. if (typeof arg === 'function') {
  5473. this.callback = arg
  5474. }
  5475. }
  5476. Slider.prototype.bind = function () {
  5477. this.container
  5478. .on($.touchEvents.start, $.proxy(this.touchStart, this))
  5479. .on($.touchEvents.end, $.proxy(this.touchEnd, this));
  5480. $(document.body).on($.touchEvents.move, $.proxy(this.touchMove, this)) // move even outside container
  5481. }
  5482. Slider.prototype.touchStart = function (e) {
  5483. e.preventDefault()
  5484. this.start = $.getTouchPosition(e)
  5485. this.width = this.container.find('.weui-slider__inner').width()
  5486. this.left = parseInt(this.container.find('.weui-slider__handler').css('left'))
  5487. this.touching = true
  5488. }
  5489. Slider.prototype.touchMove = function (e) {
  5490. if (!this.touching) return true
  5491. var p = $.getTouchPosition(e)
  5492. var distance = p.x - this.start.x
  5493. var left = distance + this.left
  5494. var per = parseInt(left / this.width * 100)
  5495. if (per < 0) per = 0
  5496. if (per > 100) per = 100
  5497. this.handler.css('left', per + '%')
  5498. this.track.css('width', per + '%')
  5499. this.value.text(per)
  5500. this.callback && this.callback.call(this, per)
  5501. this.container.trigger('change', per)
  5502. }
  5503. Slider.prototype.touchEnd = function (e) {
  5504. this.touching = false
  5505. }
  5506. $.fn.slider = function (arg) {
  5507. this.each(function () {
  5508. var $this = $(this)
  5509. var slider = $this.data('slider')
  5510. if (slider) return slider;
  5511. else $this.data('slider', new Slider(this, arg))
  5512. })
  5513. }
  5514. }($);