jquery-weui.js 202 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366336733683369337033713372337333743375337633773378337933803381338233833384338533863387338833893390339133923393339433953396339733983399340034013402340334043405340634073408340934103411341234133414341534163417341834193420342134223423342434253426342734283429343034313432343334343435343634373438343934403441344234433444344534463447344834493450345134523453345434553456345734583459346034613462346334643465346634673468346934703471347234733474347534763477347834793480348134823483348434853486348734883489349034913492349334943495349634973498349935003501350235033504350535063507350835093510351135123513351435153516351735183519352035213522352335243525352635273528352935303531353235333534353535363537353835393540354135423543354435453546354735483549355035513552355335543555355635573558355935603561356235633564356535663567356835693570357135723573357435753576357735783579358035813582358335843585358635873588358935903591359235933594359535963597359835993600360136023603360436053606360736083609361036113612361336143615361636173618361936203621362236233624362536263627362836293630363136323633363436353636363736383639364036413642364336443645364636473648364936503651365236533654365536563657365836593660366136623663366436653666366736683669367036713672367336743675367636773678367936803681368236833684368536863687368836893690369136923693369436953696369736983699370037013702370337043705370637073708370937103711371237133714371537163717371837193720372137223723372437253726372737283729373037313732373337343735373637373738373937403741374237433744374537463747374837493750375137523753375437553756375737583759376037613762376337643765376637673768376937703771377237733774377537763777377837793780378137823783378437853786378737883789379037913792379337943795379637973798379938003801380238033804380538063807380838093810381138123813381438153816381738183819382038213822382338243825382638273828382938303831383238333834383538363837383838393840384138423843384438453846384738483849385038513852385338543855385638573858385938603861386238633864386538663867386838693870387138723873387438753876387738783879388038813882388338843885388638873888388938903891389238933894389538963897389838993900390139023903390439053906390739083909391039113912391339143915391639173918391939203921392239233924392539263927392839293930393139323933393439353936393739383939394039413942394339443945394639473948394939503951395239533954395539563957395839593960396139623963396439653966396739683969397039713972397339743975397639773978397939803981398239833984398539863987398839893990399139923993399439953996399739983999400040014002400340044005400640074008400940104011401240134014401540164017401840194020402140224023402440254026402740284029403040314032403340344035403640374038403940404041404240434044404540464047404840494050405140524053405440554056405740584059406040614062406340644065406640674068406940704071407240734074407540764077407840794080408140824083408440854086408740884089409040914092409340944095409640974098409941004101410241034104410541064107410841094110411141124113411441154116411741184119412041214122412341244125412641274128412941304131413241334134413541364137413841394140414141424143414441454146414741484149415041514152415341544155415641574158415941604161416241634164416541664167416841694170417141724173417441754176417741784179418041814182418341844185418641874188418941904191419241934194419541964197419841994200420142024203420442054206420742084209421042114212421342144215421642174218421942204221422242234224422542264227422842294230423142324233423442354236423742384239424042414242424342444245424642474248424942504251425242534254425542564257425842594260426142624263426442654266426742684269427042714272427342744275427642774278427942804281428242834284428542864287428842894290429142924293429442954296429742984299430043014302430343044305430643074308430943104311431243134314431543164317431843194320432143224323432443254326432743284329433043314332433343344335433643374338433943404341434243434344434543464347434843494350435143524353435443554356435743584359436043614362436343644365436643674368436943704371437243734374437543764377437843794380438143824383438443854386438743884389439043914392439343944395439643974398439944004401440244034404440544064407440844094410441144124413441444154416441744184419442044214422442344244425442644274428442944304431443244334434443544364437443844394440444144424443444444454446444744484449445044514452445344544455445644574458445944604461446244634464446544664467446844694470447144724473447444754476447744784479448044814482448344844485448644874488448944904491449244934494449544964497449844994500450145024503450445054506450745084509451045114512451345144515451645174518451945204521452245234524452545264527452845294530453145324533453445354536453745384539454045414542454345444545454645474548454945504551455245534554455545564557455845594560456145624563456445654566456745684569457045714572457345744575457645774578457945804581458245834584458545864587458845894590459145924593459445954596459745984599460046014602460346044605460646074608460946104611461246134614461546164617461846194620462146224623462446254626462746284629463046314632463346344635463646374638463946404641464246434644464546464647464846494650465146524653465446554656465746584659466046614662466346644665466646674668466946704671467246734674467546764677467846794680468146824683468446854686468746884689469046914692469346944695469646974698469947004701470247034704470547064707470847094710471147124713471447154716471747184719472047214722472347244725472647274728472947304731473247334734473547364737473847394740474147424743474447454746474747484749475047514752475347544755475647574758475947604761476247634764476547664767476847694770477147724773477447754776477747784779478047814782478347844785478647874788478947904791479247934794479547964797479847994800480148024803480448054806480748084809481048114812481348144815481648174818481948204821482248234824482548264827482848294830483148324833483448354836483748384839484048414842484348444845484648474848484948504851485248534854485548564857485848594860486148624863486448654866486748684869487048714872487348744875487648774878487948804881488248834884488548864887488848894890489148924893489448954896489748984899490049014902490349044905490649074908490949104911491249134914491549164917491849194920492149224923492449254926492749284929493049314932493349344935493649374938493949404941494249434944494549464947494849494950495149524953495449554956495749584959496049614962496349644965496649674968496949704971497249734974497549764977497849794980498149824983498449854986498749884989499049914992499349944995499649974998499950005001500250035004500550065007500850095010501150125013501450155016501750185019502050215022502350245025502650275028502950305031503250335034503550365037503850395040504150425043504450455046504750485049505050515052505350545055505650575058505950605061506250635064506550665067506850695070507150725073507450755076507750785079508050815082508350845085508650875088508950905091509250935094509550965097509850995100510151025103510451055106510751085109511051115112511351145115511651175118511951205121512251235124512551265127512851295130513151325133513451355136513751385139514051415142514351445145514651475148514951505151515251535154515551565157515851595160516151625163516451655166516751685169517051715172517351745175517651775178517951805181518251835184518551865187518851895190519151925193519451955196519751985199520052015202520352045205520652075208520952105211521252135214521552165217521852195220522152225223522452255226522752285229523052315232523352345235523652375238523952405241524252435244524552465247524852495250525152525253525452555256525752585259526052615262526352645265526652675268526952705271527252735274527552765277527852795280528152825283528452855286528752885289529052915292529352945295529652975298529953005301530253035304530553065307530853095310531153125313531453155316531753185319532053215322532353245325532653275328532953305331533253335334533553365337533853395340534153425343534453455346534753485349535053515352535353545355535653575358535953605361536253635364536553665367536853695370537153725373537453755376537753785379538053815382538353845385538653875388538953905391539253935394539553965397539853995400540154025403540454055406540754085409541054115412541354145415541654175418541954205421542254235424542554265427542854295430543154325433543454355436543754385439544054415442544354445445544654475448544954505451545254535454545554565457545854595460546154625463546454655466546754685469547054715472547354745475547654775478547954805481548254835484548554865487548854895490549154925493549454955496549754985499550055015502550355045505550655075508550955105511551255135514551555165517551855195520552155225523552455255526552755285529553055315532553355345535553655375538553955405541554255435544554555465547554855495550555155525553555455555556555755585559556055615562556355645565556655675568556955705571557255735574557555765577557855795580558155825583558455855586558755885589559055915592559355945595559655975598559956005601560256035604560556065607560856095610561156125613561456155616561756185619562056215622562356245625562656275628562956305631563256335634563556365637563856395640564156425643564456455646564756485649565056515652565356545655565656575658565956605661566256635664566556665667566856695670567156725673567456755676567756785679568056815682568356845685568656875688568956905691569256935694569556965697569856995700570157025703570457055706570757085709571057115712571357145715571657175718571957205721572257235724572557265727572857295730573157325733573457355736573757385739574057415742574357445745574657475748574957505751575257535754575557565757575857595760576157625763576457655766576757685769577057715772577357745775577657775778577957805781578257835784578557865787578857895790579157925793579457955796579757985799580058015802580358045805580658075808580958105811581258135814581558165817581858195820582158225823582458255826582758285829583058315832583358345835583658375838583958405841584258435844584558465847584858495850585158525853585458555856585758585859586058615862586358645865586658675868586958705871587258735874587558765877587858795880588158825883588458855886588758885889589058915892589358945895589658975898589959005901590259035904590559065907590859095910591159125913591459155916591759185919592059215922592359245925592659275928592959305931593259335934593559365937593859395940594159425943594459455946594759485949595059515952595359545955595659575958595959605961596259635964596559665967596859695970597159725973597459755976597759785979598059815982598359845985598659875988598959905991599259935994599559965997599859996000600160026003600460056006600760086009601060116012601360146015601660176018601960206021602260236024602560266027602860296030603160326033603460356036603760386039604060416042604360446045604660476048604960506051605260536054605560566057605860596060606160626063606460656066606760686069607060716072607360746075607660776078607960806081608260836084608560866087608860896090609160926093609460956096609760986099610061016102610361046105610661076108610961106111611261136114611561166117611861196120612161226123612461256126612761286129613061316132613361346135613661376138613961406141614261436144614561466147614861496150615161526153615461556156615761586159616061616162616361646165616661676168616961706171617261736174617561766177617861796180618161826183618461856186618761886189619061916192619361946195619661976198619962006201620262036204620562066207620862096210621162126213621462156216621762186219622062216222622362246225622662276228622962306231623262336234623562366237623862396240624162426243624462456246624762486249625062516252625362546255625662576258625962606261626262636264626562666267626862696270627162726273627462756276627762786279628062816282628362846285628662876288628962906291629262936294629562966297629862996300630163026303630463056306630763086309631063116312631363146315631663176318631963206321632263236324632563266327632863296330633163326333633463356336633763386339634063416342634363446345634663476348634963506351635263536354635563566357635863596360636163626363636463656366636763686369637063716372637363746375637663776378637963806381638263836384638563866387638863896390639163926393639463956396639763986399640064016402640364046405640664076408640964106411641264136414641564166417641864196420642164226423642464256426642764286429643064316432643364346435643664376438643964406441644264436444644564466447644864496450645164526453645464556456645764586459646064616462646364646465
  1. /**
  2. * jQuery WeUI V1.2.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.addClass("weui-toast--visible");
  3077. dialog.show();
  3078. };
  3079. var hide = function(callback) {
  3080. $(".weui-mask_transparent").remove();
  3081. var done = false;
  3082. var $el = $(".weui-toast--visible").removeClass("weui-toast--visible").transitionEnd(function() {
  3083. var $this = $(this);
  3084. $this.remove();
  3085. callback && callback();
  3086. done = true
  3087. });
  3088. setTimeout(function () {
  3089. if (!done) {
  3090. $el.remove()
  3091. callback && callback();
  3092. }
  3093. }, 1000)
  3094. }
  3095. $.toast = function(text, style, callback) {
  3096. if(typeof style === "function") {
  3097. callback = style;
  3098. }
  3099. var className, iconClassName = 'weui-icon-success-no-circle';
  3100. var duration = toastDefaults.duration;
  3101. if(style == "cancel") {
  3102. className = "weui-toast_cancel";
  3103. iconClassName = 'weui-icon-cancel'
  3104. } else if(style == "forbidden") {
  3105. className = "weui-toast--forbidden";
  3106. iconClassName = 'weui-icon-warn'
  3107. } else if(style == "text") {
  3108. className = "weui-toast--text";
  3109. } else if(typeof style === typeof 1) {
  3110. duration = style
  3111. }
  3112. show('<i class="' + iconClassName + ' weui-icon_toast"></i><p class="weui-toast_content">' + (text || "已经完成") + '</p>', className);
  3113. setTimeout(function() {
  3114. hide(callback);
  3115. }, duration);
  3116. }
  3117. $.showLoading = function(text) {
  3118. var html = '<div class="weui_loading">';
  3119. html += '<i class="weui-loading weui-icon_toast"></i>';
  3120. html += '</div>';
  3121. html += '<p class="weui-toast_content">' + (text || "数据加载中") + '</p>';
  3122. show(html, 'weui_loading_toast');
  3123. }
  3124. $.hideLoading = function() {
  3125. hide();
  3126. }
  3127. //自己添加的,用于上传图片
  3128. $.showUpLoading = function(text) {
  3129. var html = '<div class="weui_loading">';
  3130. html += '<i class="weui-loading weui-icon_toast"></i>';
  3131. html += '</div>';
  3132. html += '<p class="weui-toast_content">' + (text || "文件上传中") + '</p>';
  3133. show(html, 'weui_loading_toast');
  3134. }
  3135. //自己添加的,用于上传图片
  3136. $.hideUpLoading = function() {
  3137. hide();
  3138. }
  3139. var toastDefaults = $.toast.prototype.defaults = {
  3140. duration: 2500
  3141. }
  3142. }($);
  3143. + function($) {
  3144. "use strict";
  3145. var defaults;
  3146. var show = function(params) {
  3147. var mask = $("<div class='weui-mask weui-actions_mask'></div>").appendTo(document.body);
  3148. var actions = params.actions || [];
  3149. var actionsHtml = actions.map(function(d, i) {
  3150. return '<div class="weui-actionsheet__cell ' + (d.className || "") + '">' + d.text + '</div>';
  3151. }).join("");
  3152. var titleHtml = "";
  3153. if (params.title) {
  3154. titleHtml = '<div class="weui-actionsheet__title"><p class="weui-actionsheet__title-text">' + params.title + '</p></div>';
  3155. }
  3156. var tpl = '<div class="weui-actionsheet " id="weui-actionsheet">'+
  3157. titleHtml +
  3158. '<div class="weui-actionsheet__menu">'+
  3159. actionsHtml +
  3160. '</div>'+
  3161. '<div class="weui-actionsheet__action">'+
  3162. '<div class="weui-actionsheet__cell weui-actionsheet_cancel">取消</div>'+
  3163. '</div>'+
  3164. '</div>';
  3165. var dialog = $(tpl).appendTo(document.body);
  3166. dialog.find(".weui-actionsheet__menu .weui-actionsheet__cell, .weui-actionsheet__action .weui-actionsheet__cell").each(function(i, e) {
  3167. $(e).click(function() {
  3168. $.closeActions();
  3169. params.onClose && params.onClose();
  3170. if(actions[i] && actions[i].onClick) {
  3171. actions[i].onClick();
  3172. }
  3173. })
  3174. });
  3175. mask.show();
  3176. dialog.show();
  3177. mask.addClass("weui-mask--visible");
  3178. dialog.addClass("weui-actionsheet_toggle");
  3179. };
  3180. var hide = function() {
  3181. $(".weui-mask").removeClass("weui-mask--visible").transitionEnd(function() {
  3182. $(this).remove();
  3183. });
  3184. $(".weui-actionsheet").removeClass("weui-actionsheet_toggle").transitionEnd(function() {
  3185. $(this).remove();
  3186. });
  3187. }
  3188. $.actions = function(params) {
  3189. params = $.extend({}, defaults, params);
  3190. show(params);
  3191. }
  3192. $.closeActions = function() {
  3193. hide();
  3194. }
  3195. $(document).on("click", ".weui-actions_mask", function() {
  3196. $.closeActions();
  3197. });
  3198. var defaults = $.actions.prototype.defaults = {
  3199. title: undefined,
  3200. onClose: undefined,
  3201. /*actions: [{
  3202. text: "菜单",
  3203. className: "color-danger",
  3204. onClick: function() {
  3205. console.log(1);
  3206. }
  3207. },{
  3208. text: "菜单2",
  3209. className: "color-success",
  3210. onClick: function() {
  3211. console.log(2);
  3212. }
  3213. }]*/
  3214. }
  3215. }($);
  3216. /* ===============================================================================
  3217. ************ Pull to refreh ************
  3218. =============================================================================== */
  3219. /* global $:true */
  3220. +function ($) {
  3221. "use strict";
  3222. var PTR = function(el, opt) {
  3223. if (typeof opt === typeof function () {}) {
  3224. opt = {
  3225. onRefresh: opt
  3226. }
  3227. }
  3228. if (typeof opt === typeof 'a') {
  3229. opt = undefined
  3230. }
  3231. this.opt = $.extend(PTR.defaults, opt || {});
  3232. this.container = $(el);
  3233. this.attachEvents();
  3234. }
  3235. PTR.defaults = {
  3236. distance: 50,
  3237. onRefresh: undefined,
  3238. onPull: undefined
  3239. }
  3240. PTR.prototype.touchStart = function(e) {
  3241. if(this.container.hasClass("refreshing")) return;
  3242. var p = $.getTouchPosition(e);
  3243. this.start = p;
  3244. this.diffX = this.diffY = 0;
  3245. };
  3246. PTR.prototype.touchMove= function(e) {
  3247. if(this.container.hasClass("refreshing")) return;
  3248. if(!this.start) return false;
  3249. if(this.container.scrollTop() > 0) return;
  3250. var p = $.getTouchPosition(e);
  3251. this.diffX = p.x - this.start.x;
  3252. this.diffY = p.y - this.start.y;
  3253. if (Math.abs(this.diffX) > Math.abs(this.diffY)) return true; // 说明是左右方向的拖动
  3254. if(this.diffY < 0) return;
  3255. this.container.addClass("touching");
  3256. e.preventDefault();
  3257. e.stopPropagation();
  3258. this.diffY = Math.pow(this.diffY, 0.75);
  3259. this.container.css("transform", "translate3d(0, "+this.diffY+"px, 0)");
  3260. this.triggerPull(this.diffY)
  3261. };
  3262. PTR.prototype.touchEnd = function() {
  3263. this.start = false;
  3264. if(this.diffY <= 0 || this.container.hasClass("refreshing")) return;
  3265. this.container.removeClass("touching");
  3266. this.container.removeClass("pull-down pull-up");
  3267. this.container.css("transform", "");
  3268. if(Math.abs(this.diffY) <= this.opt.distance) {
  3269. } else {
  3270. this.triggerPullToRefresh();
  3271. }
  3272. };
  3273. PTR.prototype.triggerPullToRefresh = function() {
  3274. this.triggerPull(this.opt.distance)
  3275. this.container.removeClass('pull-up').addClass("refreshing");
  3276. if (this.opt.onRefresh) {
  3277. this.opt.onRefresh.call(this)
  3278. }
  3279. this.container.trigger("pull-to-refresh");
  3280. }
  3281. PTR.prototype.triggerPull = function(diffY) {
  3282. if(diffY < this.opt.distance) {
  3283. this.container.removeClass("pull-up").addClass("pull-down");
  3284. } else {
  3285. this.container.removeClass("pull-down").addClass("pull-up");
  3286. }
  3287. if (this.opt.onPull) {
  3288. this.opt.onPull.call(this, Math.floor(diffY / this.opt.distance * 100))
  3289. }
  3290. this.container.trigger("pull");
  3291. }
  3292. PTR.prototype.pullToRefreshDone = function() {
  3293. this.container.removeClass("refreshing");
  3294. }
  3295. PTR.prototype.attachEvents = function() {
  3296. var el = this.container;
  3297. el.addClass("weui-pull-to-refresh");
  3298. el.on($.touchEvents.start, $.proxy(this.touchStart, this));
  3299. el.on($.touchEvents.move, $.proxy(this.touchMove, this));
  3300. el.on($.touchEvents.end, $.proxy(this.touchEnd, this));
  3301. };
  3302. var pullToRefreshDone = function(el) {
  3303. $(el).removeClass("refreshing");
  3304. }
  3305. $.fn.pullToRefresh = function(opt) {
  3306. return this.each(function() {
  3307. var $this = $(this)
  3308. var ptr = $this.data('ptr')
  3309. if (!ptr) $this.data('ptr', ptr = new PTR(this, opt))
  3310. if (typeof opt === typeof 'a') {
  3311. ptr[opt].call(ptr)
  3312. }
  3313. });
  3314. }
  3315. $.fn.pullToRefreshDone = function() {
  3316. return this.each(function() {
  3317. pullToRefreshDone(this);
  3318. });
  3319. }
  3320. }($);
  3321. /* ===============================================================================
  3322. ************ Infinite ************
  3323. =============================================================================== */
  3324. /* global $:true */
  3325. +function ($) {
  3326. "use strict";
  3327. // fix https://github.com/lihongxun945/jquery-weui/issues/442
  3328. // chrome will always return 0, when use document.body.scrollTop
  3329. // https://stackoverflow.com/questions/43717316/google-chrome-document-body-scrolltop-always-returns-0
  3330. var getOffset = function (container) {
  3331. var tagName = container[0].tagName.toUpperCase()
  3332. var scrollTop
  3333. if (tagName === 'BODY' || tagName === 'HTML') {
  3334. scrollTop = container.scrollTop() || $(window).scrollTop()
  3335. } else {
  3336. scrollTop = container.scrollTop()
  3337. }
  3338. var offset = container.scrollHeight() - ($(window).height() + scrollTop)
  3339. return offset
  3340. }
  3341. var Infinite = function(el, distance) {
  3342. this.container = $(el);
  3343. this.container.data("infinite", this);
  3344. this.distance = distance || 50;
  3345. this.attachEvents();
  3346. }
  3347. Infinite.prototype.scroll = function() {
  3348. var container = this.container;
  3349. this._check();
  3350. }
  3351. Infinite.prototype.attachEvents = function(off) {
  3352. var el = this.container;
  3353. var scrollContainer = (el[0].tagName.toUpperCase() === "BODY" ? $(document) : el);
  3354. scrollContainer[off ? "off" : "on"]("scroll", $.proxy(this.scroll, this));
  3355. };
  3356. Infinite.prototype.detachEvents = function(off) {
  3357. this.attachEvents(true);
  3358. }
  3359. Infinite.prototype._check = function() {
  3360. var offset = getOffset(this.container);
  3361. if(Math.abs(offset) <= this.distance) {
  3362. this.container.trigger("infinite");
  3363. }
  3364. }
  3365. var infinite = function(el) {
  3366. attachEvents(el);
  3367. }
  3368. $.fn.infinite = function(distance) {
  3369. return this.each(function() {
  3370. new Infinite(this, distance);
  3371. });
  3372. }
  3373. $.fn.destroyInfinite = function() {
  3374. return this.each(function() {
  3375. var infinite = $(this).data("infinite");
  3376. if(infinite && infinite.detachEvents) infinite.detachEvents();
  3377. });
  3378. }
  3379. }($);
  3380. /* global $:true */
  3381. +function ($) {
  3382. "use strict";
  3383. var ITEM_ON = "weui-bar__item--on";
  3384. var showTab = function(a) {
  3385. var $a = $(a);
  3386. if($a.hasClass(ITEM_ON)) return;
  3387. var href = $a.attr("href");
  3388. if(!/^#/.test(href)) return ;
  3389. $a.parent().find("."+ITEM_ON).removeClass(ITEM_ON);
  3390. $a.addClass(ITEM_ON);
  3391. var bd = $a.parents(".weui-tab").find(".weui-tab__bd");
  3392. bd.find(".weui-tab__bd-item--active").removeClass("weui-tab__bd-item--active");
  3393. $(href).addClass("weui-tab__bd-item--active");
  3394. }
  3395. $.showTab = showTab;
  3396. $(document).on("click", ".weui-navbar__item, .weui-tabbar__item", function(e) {
  3397. var $a = $(e.currentTarget);
  3398. var href = $a.attr("href");
  3399. if($a.hasClass(ITEM_ON)) return;
  3400. if(!/^#/.test(href)) return;
  3401. e.preventDefault();
  3402. showTab($a);
  3403. });
  3404. }($);
  3405. /* global $:true */
  3406. + function($) {
  3407. "use strict";
  3408. $(document).on("click touchstart", ".weui-search-bar__label", function(e) {
  3409. $(e.target).parents(".weui-search-bar").addClass("weui-search-bar_focusing").find('input').focus();
  3410. })
  3411. /*
  3412. .on("blur", ".weui-search-bar__input", function(e) {
  3413. var $input = $(e.target);
  3414. if(!$input.val()) $input.parents(".weui-search-bar").removeClass("weui-search-bar_focusing");
  3415. })
  3416. */
  3417. .on("click", ".weui-search-bar__cancel-btn", function(e) {
  3418. var $input = $(e.target).parents(".weui-search-bar").removeClass("weui-search-bar_focusing").find(".weui-search-bar__input").val("").blur();
  3419. })
  3420. .on("click", ".weui-icon-clear", function(e) {
  3421. var $input = $(e.target).parents(".weui-search-bar").find(".weui-search-bar__input").val("").focus();
  3422. });
  3423. }($);
  3424. /*===========================
  3425. Device/OS Detection
  3426. ===========================*/
  3427. /* global $:true */
  3428. ;(function ($) {
  3429. "use strict";
  3430. var device = {};
  3431. var ua = navigator.userAgent;
  3432. var android = ua.match(/(Android);?[\s\/]+([\d.]+)?/);
  3433. var ipad = ua.match(/(iPad).*OS\s([\d_]+)/);
  3434. var ipod = ua.match(/(iPod)(.*OS\s([\d_]+))?/);
  3435. var iphone = !ipad && ua.match(/(iPhone\sOS)\s([\d_]+)/);
  3436. device.ios = device.android = device.iphone = device.ipad = device.androidChrome = false;
  3437. // Android
  3438. if (android) {
  3439. device.os = 'android';
  3440. device.osVersion = android[2];
  3441. device.android = true;
  3442. device.androidChrome = ua.toLowerCase().indexOf('chrome') >= 0;
  3443. }
  3444. if (ipad || iphone || ipod) {
  3445. device.os = 'ios';
  3446. device.ios = true;
  3447. }
  3448. // iOS
  3449. if (iphone && !ipod) {
  3450. device.osVersion = iphone[2].replace(/_/g, '.');
  3451. device.iphone = true;
  3452. }
  3453. if (ipad) {
  3454. device.osVersion = ipad[2].replace(/_/g, '.');
  3455. device.ipad = true;
  3456. }
  3457. if (ipod) {
  3458. device.osVersion = ipod[3] ? ipod[3].replace(/_/g, '.') : null;
  3459. device.iphone = true;
  3460. }
  3461. // iOS 8+ changed UA
  3462. if (device.ios && device.osVersion && ua.indexOf('Version/') >= 0) {
  3463. if (device.osVersion.split('.')[0] === '10') {
  3464. device.osVersion = ua.toLowerCase().split('version/')[1].split(' ')[0];
  3465. }
  3466. }
  3467. // Webview
  3468. device.webView = (iphone || ipad || ipod) && ua.match(/.*AppleWebKit(?!.*Safari)/i);
  3469. // Minimal UI
  3470. if (device.os && device.os === 'ios') {
  3471. var osVersionArr = device.osVersion.split('.');
  3472. device.minimalUi = !device.webView &&
  3473. (ipod || iphone) &&
  3474. (osVersionArr[0] * 1 === 7 ? osVersionArr[1] * 1 >= 1 : osVersionArr[0] * 1 > 7) &&
  3475. $('meta[name="viewport"]').length > 0 && $('meta[name="viewport"]').attr('content').indexOf('minimal-ui') >= 0;
  3476. }
  3477. // Check for status bar and fullscreen app mode
  3478. var windowWidth = $(window).width();
  3479. var windowHeight = $(window).height();
  3480. device.statusBar = false;
  3481. if (device.webView && (windowWidth * windowHeight === screen.width * screen.height)) {
  3482. device.statusBar = true;
  3483. }
  3484. else {
  3485. device.statusBar = false;
  3486. }
  3487. // Classes
  3488. var classNames = [];
  3489. // Pixel Ratio
  3490. device.pixelRatio = window.devicePixelRatio || 1;
  3491. classNames.push('pixel-ratio-' + Math.floor(device.pixelRatio));
  3492. if (device.pixelRatio >= 2) {
  3493. classNames.push('retina');
  3494. }
  3495. // OS classes
  3496. if (device.os) {
  3497. classNames.push(device.os, device.os + '-' + device.osVersion.split('.')[0], device.os + '-' + device.osVersion.replace(/\./g, '-'));
  3498. if (device.os === 'ios') {
  3499. var major = parseInt(device.osVersion.split('.')[0], 10);
  3500. for (var i = major - 1; i >= 6; i--) {
  3501. classNames.push('ios-gt-' + i);
  3502. }
  3503. }
  3504. }
  3505. // Status bar classes
  3506. if (device.statusBar) {
  3507. classNames.push('with-statusbar-overlay');
  3508. }
  3509. else {
  3510. $('html').removeClass('with-statusbar-overlay');
  3511. }
  3512. // Add html classes
  3513. if (classNames.length > 0) $('html').addClass(classNames.join(' '));
  3514. $.device = device;
  3515. })($);
  3516. /*======================================================
  3517. ************ Picker ************
  3518. ======================================================*/
  3519. /* global $:true */
  3520. /* jshint unused:false */
  3521. /* jshint multistr:true */
  3522. + function($) {
  3523. "use strict";
  3524. var Picker = function (params) {
  3525. var p = this;
  3526. var defaults = {
  3527. updateValuesOnMomentum: false,
  3528. updateValuesOnTouchmove: true,
  3529. rotateEffect: false,
  3530. momentumRatio: 7,
  3531. freeMode: false,
  3532. // Common settings
  3533. scrollToInput: true,
  3534. inputReadOnly: true,
  3535. toolbar: true,
  3536. toolbarCloseText: '完成',
  3537. title: '请选择',
  3538. toolbarTemplate: '<div class="toolbar">\
  3539. <div class="toolbar-inner">\
  3540. <a href="javascript:;" class="picker-button close-picker">{{closeText}}</a>\
  3541. <h1 class="title">{{title}}</h1>\
  3542. </div>\
  3543. </div>',
  3544. };
  3545. params = params || {};
  3546. for (var def in defaults) {
  3547. if (typeof params[def] === 'undefined') {
  3548. params[def] = defaults[def];
  3549. }
  3550. }
  3551. p.params = params;
  3552. p.cols = [];
  3553. p.initialized = false;
  3554. // Inline flag
  3555. p.inline = p.params.container ? true : false;
  3556. // 3D Transforms origin bug, only on safari
  3557. var originBug = $.device.ios || (navigator.userAgent.toLowerCase().indexOf('safari') >= 0 && navigator.userAgent.toLowerCase().indexOf('chrome') < 0) && !$.device.android;
  3558. // Should be converted to popover
  3559. function isPopover() {
  3560. var toPopover = false;
  3561. if (!p.params.convertToPopover && !p.params.onlyInPopover) return toPopover;
  3562. if (!p.inline && p.params.input) {
  3563. if (p.params.onlyInPopover) toPopover = true;
  3564. else {
  3565. if ($.device.ios) {
  3566. toPopover = $.device.ipad ? true : false;
  3567. }
  3568. else {
  3569. if ($(window).width() >= 768) toPopover = true;
  3570. }
  3571. }
  3572. }
  3573. return toPopover;
  3574. }
  3575. function inPopover() {
  3576. if (p.opened && p.container && p.container.length > 0 && p.container.parents('.popover').length > 0) return true;
  3577. else return false;
  3578. }
  3579. // Value
  3580. p.setValue = function (arrValues, transition) {
  3581. var valueIndex = 0;
  3582. for (var i = 0; i < p.cols.length; i++) {
  3583. if (p.cols[i] && !p.cols[i].divider) {
  3584. p.cols[i].setValue(arrValues[valueIndex], transition);
  3585. valueIndex++;
  3586. }
  3587. }
  3588. };
  3589. p.updateValue = function () {
  3590. var newValue = [];
  3591. var newDisplayValue = [];
  3592. for (var i = 0; i < p.cols.length; i++) {
  3593. if (!p.cols[i].divider) {
  3594. newValue.push(p.cols[i].value);
  3595. newDisplayValue.push(p.cols[i].displayValue);
  3596. }
  3597. }
  3598. if (newValue.indexOf(undefined) >= 0) {
  3599. return;
  3600. }
  3601. p.value = newValue;
  3602. p.displayValue = newDisplayValue;
  3603. if (p.params.onChange) {
  3604. p.params.onChange(p, p.value, p.displayValue);
  3605. }
  3606. if (p.input && p.input.length > 0) {
  3607. $(p.input).val(p.params.formatValue ? p.params.formatValue(p, p.value, p.displayValue) : p.value.join(' '));
  3608. $(p.input).trigger('change');
  3609. }
  3610. };
  3611. // Columns Handlers
  3612. p.initPickerCol = function (colElement, updateItems) {
  3613. var colContainer = $(colElement);
  3614. var colIndex = colContainer.index();
  3615. var col = p.cols[colIndex];
  3616. if (col.divider) return;
  3617. col.container = colContainer;
  3618. col.wrapper = col.container.find('.picker-items-col-wrapper');
  3619. col.items = col.wrapper.find('.picker-item');
  3620. var i, j;
  3621. var wrapperHeight, itemHeight, itemsHeight, minTranslate, maxTranslate;
  3622. col.replaceValues = function (values, displayValues) {
  3623. col.destroyEvents();
  3624. col.values = values;
  3625. col.displayValues = displayValues;
  3626. var newItemsHTML = p.columnHTML(col, true);
  3627. col.wrapper.html(newItemsHTML);
  3628. col.items = col.wrapper.find('.picker-item');
  3629. col.calcSize();
  3630. col.setValue(col.values[0] || '', 0, true);
  3631. col.initEvents();
  3632. };
  3633. col.calcSize = function () {
  3634. if (!col.values.length) return;
  3635. if (p.params.rotateEffect) {
  3636. col.container.removeClass('picker-items-col-absolute');
  3637. if (!col.width) col.container.css({width:''});
  3638. }
  3639. var colWidth, colHeight;
  3640. colWidth = 0;
  3641. colHeight = col.container[0].offsetHeight;
  3642. wrapperHeight = col.wrapper[0].offsetHeight;
  3643. itemHeight = col.items[0].offsetHeight;
  3644. itemsHeight = itemHeight * col.items.length;
  3645. minTranslate = colHeight / 2 - itemsHeight + itemHeight / 2;
  3646. maxTranslate = colHeight / 2 - itemHeight / 2;
  3647. if (col.width) {
  3648. colWidth = col.width;
  3649. if (parseInt(colWidth, 10) === colWidth) colWidth = colWidth + 'px';
  3650. col.container.css({width: colWidth});
  3651. }
  3652. if (p.params.rotateEffect) {
  3653. if (!col.width) {
  3654. col.items.each(function () {
  3655. var item = $(this);
  3656. item.css({width:'auto'});
  3657. colWidth = Math.max(colWidth, item[0].offsetWidth);
  3658. item.css({width:''});
  3659. });
  3660. col.container.css({width: (colWidth + 2) + 'px'});
  3661. }
  3662. col.container.addClass('picker-items-col-absolute');
  3663. }
  3664. };
  3665. col.calcSize();
  3666. col.wrapper.transform('translate3d(0,' + maxTranslate + 'px,0)').transition(0);
  3667. var activeIndex = 0;
  3668. var animationFrameId;
  3669. // Set Value Function
  3670. col.setValue = function (newValue, transition, valueCallbacks) {
  3671. if (typeof transition === 'undefined') transition = '';
  3672. var newActiveIndex = col.wrapper.find('.picker-item[data-picker-value="' + newValue + '"]').index();
  3673. if(typeof newActiveIndex === 'undefined' || newActiveIndex === -1) {
  3674. col.value = col.displayValue = newValue;
  3675. return;
  3676. }
  3677. var newTranslate = -newActiveIndex * itemHeight + maxTranslate;
  3678. // Update wrapper
  3679. col.wrapper.transition(transition);
  3680. col.wrapper.transform('translate3d(0,' + (newTranslate) + 'px,0)');
  3681. // Watch items
  3682. if (p.params.updateValuesOnMomentum && col.activeIndex && col.activeIndex !== newActiveIndex ) {
  3683. $.cancelAnimationFrame(animationFrameId);
  3684. col.wrapper.transitionEnd(function(){
  3685. $.cancelAnimationFrame(animationFrameId);
  3686. });
  3687. updateDuringScroll();
  3688. }
  3689. // Update items
  3690. col.updateItems(newActiveIndex, newTranslate, transition, valueCallbacks);
  3691. };
  3692. col.updateItems = function (activeIndex, translate, transition, valueCallbacks) {
  3693. if (typeof translate === 'undefined') {
  3694. translate = $.getTranslate(col.wrapper[0], 'y');
  3695. }
  3696. if(typeof activeIndex === 'undefined') activeIndex = -Math.round((translate - maxTranslate)/itemHeight);
  3697. if (activeIndex < 0) activeIndex = 0;
  3698. if (activeIndex >= col.items.length) activeIndex = col.items.length - 1;
  3699. var previousActiveIndex = col.activeIndex;
  3700. col.activeIndex = activeIndex;
  3701. /*
  3702. col.wrapper.find('.picker-selected, .picker-after-selected, .picker-before-selected').removeClass('picker-selected picker-after-selected picker-before-selected');
  3703. col.items.transition(transition);
  3704. var selectedItem = col.items.eq(activeIndex).addClass('picker-selected').transform('');
  3705. var prevItems = selectedItem.prevAll().addClass('picker-before-selected');
  3706. var nextItems = selectedItem.nextAll().addClass('picker-after-selected');
  3707. */
  3708. //去掉 .picker-after-selected, .picker-before-selected 以提高性能
  3709. col.wrapper.find('.picker-selected').removeClass('picker-selected');
  3710. if (p.params.rotateEffect) {
  3711. col.items.transition(transition);
  3712. }
  3713. var selectedItem = col.items.eq(activeIndex).addClass('picker-selected').transform('');
  3714. if (valueCallbacks || typeof valueCallbacks === 'undefined') {
  3715. // Update values
  3716. col.value = selectedItem.attr('data-picker-value');
  3717. col.displayValue = col.displayValues ? col.displayValues[activeIndex] : col.value;
  3718. // On change callback
  3719. if (previousActiveIndex !== activeIndex) {
  3720. if (col.onChange) {
  3721. col.onChange(p, col.value, col.displayValue);
  3722. }
  3723. p.updateValue();
  3724. }
  3725. }
  3726. // Set 3D rotate effect
  3727. if (!p.params.rotateEffect) {
  3728. return;
  3729. }
  3730. var percentage = (translate - (Math.floor((translate - maxTranslate)/itemHeight) * itemHeight + maxTranslate)) / itemHeight;
  3731. col.items.each(function () {
  3732. var item = $(this);
  3733. var itemOffsetTop = item.index() * itemHeight;
  3734. var translateOffset = maxTranslate - translate;
  3735. var itemOffset = itemOffsetTop - translateOffset;
  3736. var percentage = itemOffset / itemHeight;
  3737. var itemsFit = Math.ceil(col.height / itemHeight / 2) + 1;
  3738. var angle = (-18*percentage);
  3739. if (angle > 180) angle = 180;
  3740. if (angle < -180) angle = -180;
  3741. // Far class
  3742. if (Math.abs(percentage) > itemsFit) item.addClass('picker-item-far');
  3743. else item.removeClass('picker-item-far');
  3744. // Set transform
  3745. item.transform('translate3d(0, ' + (-translate + maxTranslate) + 'px, ' + (originBug ? -110 : 0) + 'px) rotateX(' + angle + 'deg)');
  3746. });
  3747. };
  3748. function updateDuringScroll() {
  3749. animationFrameId = $.requestAnimationFrame(function () {
  3750. col.updateItems(undefined, undefined, 0);
  3751. updateDuringScroll();
  3752. });
  3753. }
  3754. // Update items on init
  3755. if (updateItems) col.updateItems(0, maxTranslate, 0);
  3756. var allowItemClick = true;
  3757. var isTouched, isMoved, touchStartY, touchCurrentY, touchStartTime, touchEndTime, startTranslate, returnTo, currentTranslate, prevTranslate, velocityTranslate, velocityTime;
  3758. function handleTouchStart (e) {
  3759. if (isMoved || isTouched) return;
  3760. e.preventDefault();
  3761. isTouched = true;
  3762. var position = $.getTouchPosition(e);
  3763. touchStartY = touchCurrentY = position.y;
  3764. touchStartTime = (new Date()).getTime();
  3765. allowItemClick = true;
  3766. startTranslate = currentTranslate = $.getTranslate(col.wrapper[0], 'y');
  3767. }
  3768. function handleTouchMove (e) {
  3769. if (!isTouched) return;
  3770. e.preventDefault();
  3771. allowItemClick = false;
  3772. var position = $.getTouchPosition(e);
  3773. touchCurrentY = position.y;
  3774. if (!isMoved) {
  3775. // First move
  3776. $.cancelAnimationFrame(animationFrameId);
  3777. isMoved = true;
  3778. startTranslate = currentTranslate = $.getTranslate(col.wrapper[0], 'y');
  3779. col.wrapper.transition(0);
  3780. }
  3781. e.preventDefault();
  3782. var diff = touchCurrentY - touchStartY;
  3783. currentTranslate = startTranslate + diff;
  3784. returnTo = undefined;
  3785. // Normalize translate
  3786. if (currentTranslate < minTranslate) {
  3787. currentTranslate = minTranslate - Math.pow(minTranslate - currentTranslate, 0.8);
  3788. returnTo = 'min';
  3789. }
  3790. if (currentTranslate > maxTranslate) {
  3791. currentTranslate = maxTranslate + Math.pow(currentTranslate - maxTranslate, 0.8);
  3792. returnTo = 'max';
  3793. }
  3794. // Transform wrapper
  3795. col.wrapper.transform('translate3d(0,' + currentTranslate + 'px,0)');
  3796. // Update items
  3797. col.updateItems(undefined, currentTranslate, 0, p.params.updateValuesOnTouchmove);
  3798. // Calc velocity
  3799. velocityTranslate = currentTranslate - prevTranslate || currentTranslate;
  3800. velocityTime = (new Date()).getTime();
  3801. prevTranslate = currentTranslate;
  3802. }
  3803. function handleTouchEnd (e) {
  3804. if (!isTouched || !isMoved) {
  3805. isTouched = isMoved = false;
  3806. return;
  3807. }
  3808. isTouched = isMoved = false;
  3809. col.wrapper.transition('');
  3810. if (returnTo) {
  3811. if (returnTo === 'min') {
  3812. col.wrapper.transform('translate3d(0,' + minTranslate + 'px,0)');
  3813. }
  3814. else col.wrapper.transform('translate3d(0,' + maxTranslate + 'px,0)');
  3815. }
  3816. touchEndTime = new Date().getTime();
  3817. var velocity, newTranslate;
  3818. if (touchEndTime - touchStartTime > 300) {
  3819. newTranslate = currentTranslate;
  3820. }
  3821. else {
  3822. velocity = Math.abs(velocityTranslate / (touchEndTime - velocityTime));
  3823. newTranslate = currentTranslate + velocityTranslate * p.params.momentumRatio;
  3824. }
  3825. newTranslate = Math.max(Math.min(newTranslate, maxTranslate), minTranslate);
  3826. // Active Index
  3827. var activeIndex = -Math.floor((newTranslate - maxTranslate)/itemHeight);
  3828. // Normalize translate
  3829. if (!p.params.freeMode) newTranslate = -activeIndex * itemHeight + maxTranslate;
  3830. // Transform wrapper
  3831. col.wrapper.transform('translate3d(0,' + (parseInt(newTranslate,10)) + 'px,0)');
  3832. // Update items
  3833. col.updateItems(activeIndex, newTranslate, '', true);
  3834. // Watch items
  3835. if (p.params.updateValuesOnMomentum) {
  3836. updateDuringScroll();
  3837. col.wrapper.transitionEnd(function(){
  3838. $.cancelAnimationFrame(animationFrameId);
  3839. });
  3840. }
  3841. // Allow click
  3842. setTimeout(function () {
  3843. allowItemClick = true;
  3844. }, 100);
  3845. }
  3846. function handleClick(e) {
  3847. if (!allowItemClick) return;
  3848. $.cancelAnimationFrame(animationFrameId);
  3849. /*jshint validthis:true */
  3850. var value = $(this).attr('data-picker-value');
  3851. col.setValue(value);
  3852. }
  3853. col.initEvents = function (detach) {
  3854. var method = detach ? 'off' : 'on';
  3855. col.container[method]($.touchEvents.start, handleTouchStart);
  3856. col.container[method]($.touchEvents.move, handleTouchMove);
  3857. col.container[method]($.touchEvents.end, handleTouchEnd);
  3858. col.items[method]('click', handleClick);
  3859. };
  3860. col.destroyEvents = function () {
  3861. col.initEvents(true);
  3862. };
  3863. col.container[0].f7DestroyPickerCol = function () {
  3864. col.destroyEvents();
  3865. };
  3866. col.initEvents();
  3867. };
  3868. p.destroyPickerCol = function (colContainer) {
  3869. colContainer = $(colContainer);
  3870. if ('f7DestroyPickerCol' in colContainer[0]) colContainer[0].f7DestroyPickerCol();
  3871. };
  3872. // Resize cols
  3873. function resizeCols() {
  3874. if (!p.opened) return;
  3875. for (var i = 0; i < p.cols.length; i++) {
  3876. if (!p.cols[i].divider) {
  3877. p.cols[i].calcSize();
  3878. p.cols[i].setValue(p.cols[i].value, 0, false);
  3879. }
  3880. }
  3881. }
  3882. $(window).on('resize', resizeCols);
  3883. // HTML Layout
  3884. p.columnHTML = function (col, onlyItems) {
  3885. var columnItemsHTML = '';
  3886. var columnHTML = '';
  3887. if (col.divider) {
  3888. columnHTML += '<div class="picker-items-col picker-items-col-divider ' + (col.textAlign ? 'picker-items-col-' + col.textAlign : '') + ' ' + (col.cssClass || '') + '">' + col.content + '</div>';
  3889. }
  3890. else {
  3891. for (var j = 0; j < col.values.length; j++) {
  3892. columnItemsHTML += '<div class="picker-item" data-picker-value="' + col.values[j] + '">' + (col.displayValues ? col.displayValues[j] : col.values[j]) + '</div>';
  3893. }
  3894. columnHTML += '<div class="picker-items-col ' + (col.textAlign ? 'picker-items-col-' + col.textAlign : '') + ' ' + (col.cssClass || '') + '"><div class="picker-items-col-wrapper">' + columnItemsHTML + '</div></div>';
  3895. }
  3896. return onlyItems ? columnItemsHTML : columnHTML;
  3897. };
  3898. p.layout = function () {
  3899. var pickerHTML = '';
  3900. var pickerClass = '';
  3901. var i;
  3902. p.cols = [];
  3903. var colsHTML = '';
  3904. for (i = 0; i < p.params.cols.length; i++) {
  3905. var col = p.params.cols[i];
  3906. colsHTML += p.columnHTML(p.params.cols[i]);
  3907. p.cols.push(col);
  3908. }
  3909. pickerClass = 'weui-picker-modal picker-columns ' + (p.params.cssClass || '') + (p.params.rotateEffect ? ' picker-3d' : '') + (p.params.cols.length === 1 ? ' picker-columns-single' : '');
  3910. pickerHTML =
  3911. '<div class="' + (pickerClass) + '">' +
  3912. (p.params.toolbar ? p.params.toolbarTemplate.replace(/{{closeText}}/g, p.params.toolbarCloseText).replace(/{{title}}/g, p.params.title) : '') +
  3913. '<div class="picker-modal-inner picker-items">' +
  3914. colsHTML +
  3915. '<div class="picker-center-highlight"></div>' +
  3916. '</div>' +
  3917. '</div>';
  3918. p.pickerHTML = pickerHTML;
  3919. };
  3920. // Input Events
  3921. function openOnInput(e) {
  3922. e.preventDefault();
  3923. if (p.opened) return;
  3924. p.open();
  3925. if (p.params.scrollToInput && !isPopover()) {
  3926. var pageContent = p.input.parents('.content');
  3927. if (pageContent.length === 0) return;
  3928. var paddingTop = parseInt(pageContent.css('padding-top'), 10),
  3929. paddingBottom = parseInt(pageContent.css('padding-bottom'), 10),
  3930. pageHeight = pageContent[0].offsetHeight - paddingTop - p.container.height(),
  3931. pageScrollHeight = pageContent[0].scrollHeight - paddingTop - p.container.height(),
  3932. newPaddingBottom;
  3933. var inputTop = p.input.offset().top - paddingTop + p.input[0].offsetHeight;
  3934. if (inputTop > pageHeight) {
  3935. var scrollTop = pageContent.scrollTop() + inputTop - pageHeight;
  3936. if (scrollTop + pageHeight > pageScrollHeight) {
  3937. newPaddingBottom = scrollTop + pageHeight - pageScrollHeight + paddingBottom;
  3938. if (pageHeight === pageScrollHeight) {
  3939. newPaddingBottom = p.container.height();
  3940. }
  3941. pageContent.css({'padding-bottom': (newPaddingBottom) + 'px'});
  3942. }
  3943. pageContent.scrollTop(scrollTop, 300);
  3944. }
  3945. }
  3946. }
  3947. function closeOnHTMLClick(e) {
  3948. if (inPopover()) return;
  3949. if (p.input && p.input.length > 0) {
  3950. if (e.target !== p.input[0] && $(e.target).parents('.weui-picker-modal').length === 0) p.close();
  3951. }
  3952. else {
  3953. if ($(e.target).parents('.weui-picker-modal').length === 0) p.close();
  3954. }
  3955. }
  3956. if (p.params.input) {
  3957. p.input = $(p.params.input);
  3958. if (p.input.length > 0) {
  3959. if (p.params.inputReadOnly) p.input.prop('readOnly', true);
  3960. if (!p.inline) {
  3961. p.input.on('click', openOnInput);
  3962. }
  3963. if (p.params.inputReadOnly) {
  3964. p.input.on('focus mousedown', function (e) {
  3965. e.preventDefault();
  3966. });
  3967. }
  3968. }
  3969. }
  3970. if (!p.inline) $('html').on('click', closeOnHTMLClick);
  3971. // Open
  3972. function onPickerClose() {
  3973. p.opened = false;
  3974. if (p.input && p.input.length > 0) p.input.parents('.page-content').css({'padding-bottom': ''});
  3975. if (p.params.onClose) p.params.onClose(p);
  3976. // Destroy events
  3977. p.container.find('.picker-items-col').each(function () {
  3978. p.destroyPickerCol(this);
  3979. });
  3980. }
  3981. p.opened = false;
  3982. p.open = function () {
  3983. var toPopover = isPopover();
  3984. if (!p.opened) {
  3985. // Layout
  3986. p.layout();
  3987. // Append
  3988. if (toPopover) {
  3989. p.pickerHTML = '<div class="popover popover-picker-columns"><div class="popover-inner">' + p.pickerHTML + '</div></div>';
  3990. p.popover = $.popover(p.pickerHTML, p.params.input, true);
  3991. p.container = $(p.popover).find('.weui-picker-modal');
  3992. $(p.popover).on('close', function () {
  3993. onPickerClose();
  3994. });
  3995. }
  3996. else if (p.inline) {
  3997. p.container = $(p.pickerHTML);
  3998. p.container.addClass('picker-modal-inline');
  3999. $(p.params.container).append(p.container);
  4000. }
  4001. else {
  4002. p.container = $($.openPicker(p.pickerHTML));
  4003. $(p.container)
  4004. .on('close', function () {
  4005. onPickerClose();
  4006. });
  4007. }
  4008. // Store picker instance
  4009. p.container[0].f7Picker = p;
  4010. // Init Events
  4011. p.container.find('.picker-items-col').each(function () {
  4012. var updateItems = true;
  4013. if ((!p.initialized && p.params.value) || (p.initialized && p.value)) updateItems = false;
  4014. p.initPickerCol(this, updateItems);
  4015. });
  4016. // Set value
  4017. if (!p.initialized) {
  4018. if (p.params.value) {
  4019. p.setValue(p.params.value, 0);
  4020. }
  4021. }
  4022. else {
  4023. if (p.value) p.setValue(p.value, 0);
  4024. }
  4025. }
  4026. // Set flag
  4027. p.opened = true;
  4028. p.initialized = true;
  4029. if (p.params.onOpen) p.params.onOpen(p);
  4030. };
  4031. // Close
  4032. p.close = function (force) {
  4033. if (!p.opened || p.inline) return;
  4034. if (inPopover()) {
  4035. $.closePicker(p.popover);
  4036. return;
  4037. }
  4038. else {
  4039. $.closePicker(p.container);
  4040. return;
  4041. }
  4042. };
  4043. // Destroy
  4044. p.destroy = function () {
  4045. p.close();
  4046. if (p.params.input && p.input.length > 0) {
  4047. p.input.off('click focus', openOnInput);
  4048. $(p.input).data('picker', null);
  4049. }
  4050. $('html').off('click', closeOnHTMLClick);
  4051. $(window).off('resize', resizeCols);
  4052. };
  4053. if (p.inline) {
  4054. p.open();
  4055. }
  4056. return p;
  4057. };
  4058. $(document).on("click", ".close-picker", function() {
  4059. var pickerToClose = $('.weui-picker-modal.weui-picker-modal-visible');
  4060. if (pickerToClose.length > 0) {
  4061. $.closePicker(pickerToClose);
  4062. }
  4063. });
  4064. //修复picker会滚动页面的bug
  4065. $(document).on($.touchEvents.move, ".picker-modal-inner", function(e) {
  4066. e.preventDefault();
  4067. });
  4068. $.openPicker = function(tpl, className, callback) {
  4069. if(typeof className === "function") {
  4070. callback = className;
  4071. className = undefined;
  4072. }
  4073. $.closePicker();
  4074. var container = $("<div class='weui-picker-container "+ (className || "") + "'></div>").appendTo(document.body);
  4075. container.show();
  4076. container.addClass("weui-picker-container-visible");
  4077. //关于布局的问题,如果直接放在body上,则做动画的时候会撑开body高度而导致滚动条变化。
  4078. var dialog = $(tpl).appendTo(container);
  4079. dialog.width(); //通过取一次CSS值,强制浏览器不能把上下两行代码合并执行,因为合并之后会导致无法出现动画。
  4080. dialog.addClass("weui-picker-modal-visible");
  4081. callback && container.on("close", callback);
  4082. return dialog;
  4083. }
  4084. $.updatePicker = function(tpl) {
  4085. var container = $(".weui-picker-container-visible");
  4086. if(!container[0]) return false;
  4087. container.html("");
  4088. var dialog = $(tpl).appendTo(container);
  4089. dialog.addClass("weui-picker-modal-visible");
  4090. return dialog;
  4091. }
  4092. $.closePicker = function(container, callback) {
  4093. console.log(1);
  4094. if(typeof container === "function") callback = container;
  4095. $(".weui-picker-modal-visible").removeClass("weui-picker-modal-visible").transitionEnd(function() {
  4096. $(this).parent().remove();
  4097. callback && callback();
  4098. }).trigger("close");
  4099. };
  4100. $.fn.picker = function(params) {
  4101. var args = arguments;
  4102. return this.each(function() {
  4103. if(!this) return;
  4104. var $this = $(this);
  4105. var picker = $this.data("picker");
  4106. if(!picker) {
  4107. params = $.extend({ input: this }, params || {}) // https://github.com/lihongxun945/jquery-weui/issues/432
  4108. var inputValue = $this.val();
  4109. if(params.value === undefined && inputValue !== "") {
  4110. params.value = (params.cols && params.cols.length > 1) ? inputValue.split(" ") : [inputValue];
  4111. }
  4112. var p = $.extend({input: this}, params);
  4113. picker = new Picker(p);
  4114. $this.data("picker", picker);
  4115. }
  4116. if(typeof params === typeof "a") {
  4117. picker[params].apply(picker, Array.prototype.slice.call(args, 1));
  4118. }
  4119. });
  4120. };
  4121. }($);
  4122. /* global $:true */
  4123. + function($) {
  4124. "use strict";
  4125. var defaults;
  4126. var selects = [];
  4127. var Select = function(input, config) {
  4128. var self = this;
  4129. this.config = config;
  4130. //init empty data
  4131. this.data = {
  4132. values: '',
  4133. titles: '',
  4134. origins: [],
  4135. length: 0
  4136. };
  4137. this.$input = $(input);
  4138. this.$input.prop("readOnly", true);
  4139. this.initConfig();
  4140. config = this.config;
  4141. this.$input.click($.proxy(this.open, this));
  4142. selects.push(this)
  4143. }
  4144. Select.prototype.initConfig = function() {
  4145. this.config = $.extend({}, defaults, this.config);
  4146. var config = this.config;
  4147. if(!config.items || !config.items.length) return;
  4148. config.items = config.items.map(function(d, i) {
  4149. if(typeof d == typeof "a") {
  4150. return {
  4151. title: d,
  4152. value: d
  4153. };
  4154. }
  4155. return d;
  4156. });
  4157. this.tpl = $.t7.compile("<div class='weui-picker-modal weui-select-modal'>" + config.toolbarTemplate + (config.multi ? config.checkboxTemplate : config.radioTemplate) + "</div>");
  4158. if(config.input !== undefined) this.$input.val(config.input);
  4159. this.parseInitValue();
  4160. this._init = true;
  4161. }
  4162. Select.prototype.updateInputValue = function(values, titles) {
  4163. var v, t;
  4164. if(this.config.multi) {
  4165. v = values.join(this.config.split);
  4166. t = titles.join(this.config.split);
  4167. } else {
  4168. v = values[0];
  4169. t = titles[0];
  4170. }
  4171. //caculate origin data
  4172. var origins = [];
  4173. this.config.items.forEach(function(d) {
  4174. values.each(function(i, dd) {
  4175. if(d.value == dd) origins.push(d);
  4176. });
  4177. });
  4178. this.$input.val(t).data("values", v);
  4179. this.$input.attr("value", t).attr("data-values", v);
  4180. var data = {
  4181. values: v,
  4182. titles: t,
  4183. valuesArray: values,
  4184. titlesArray: titles,
  4185. origins: origins,
  4186. length: origins.length
  4187. };
  4188. this.data = data;
  4189. this.$input.trigger("change", data);
  4190. this.config.onChange && this.config.onChange.call(this, data);
  4191. }
  4192. Select.prototype.parseInitValue = function() {
  4193. var value = this.$input.val();
  4194. var items = this.config.items;
  4195. //如果input为空,只有在第一次初始化的时候才保留默认选择。因为后来就是用户自己取消了全部选择,不能再为他选中默认值。
  4196. if( !this._init && (value === undefined || value == null || value === "")) return;
  4197. var titles = this.config.multi ? value.split(this.config.split) : [value];
  4198. for(var i=0;i<items.length;i++) {
  4199. items[i].checked = false;
  4200. for(var j=0;j<titles.length;j++) {
  4201. if(items[i].title === titles[j]) {
  4202. items[i].checked = true;
  4203. }
  4204. }
  4205. }
  4206. }
  4207. Select.prototype._bind = function(dialog) {
  4208. var self = this,
  4209. config = this.config;
  4210. dialog.on("change", function(e) {
  4211. var checked = dialog.find("input:checked");
  4212. var values = checked.map(function() {
  4213. return $(this).val();
  4214. });
  4215. var titles = checked.map(function() {
  4216. return $(this).data("title");
  4217. });
  4218. self.updateInputValue(values, titles);
  4219. if(config.autoClose && !config.multi) self.close();
  4220. })
  4221. .trigger('change')
  4222. .on("click", ".close-select", function() {
  4223. self.close();
  4224. });
  4225. }
  4226. //更新数据
  4227. Select.prototype.update = function(config) {
  4228. this.config = $.extend({}, this.config, config);
  4229. this.initConfig();
  4230. if(this._open) {
  4231. this._bind($.updatePicker(this.getHTML()));
  4232. }
  4233. }
  4234. Select.prototype.open = function(values, titles) {
  4235. if(this._open) return;
  4236. // open picker 会默认关掉其他的,但是 onClose 不会被调用,所以这里先关掉其他select
  4237. for (var i = 0; i < selects.length; i++ ) {
  4238. var s = selects[i];
  4239. if (s === this) continue;
  4240. if (s._open) {
  4241. if(!s.close()) return false; // 其他的select由于某些条件限制关闭失败。
  4242. }
  4243. }
  4244. this.parseInitValue();
  4245. var config = this.config;
  4246. var dialog = this.dialog = $.openPicker(this.getHTML());
  4247. this._bind(dialog);
  4248. this._open = true;
  4249. if(config.onOpen) config.onOpen(this);
  4250. }
  4251. Select.prototype.close = function(callback, force) {
  4252. if (!this._open) return false;
  4253. var self = this,
  4254. beforeClose = this.config.beforeClose;
  4255. if(typeof callback === typeof true) {
  4256. force === callback;
  4257. }
  4258. if(!force) {
  4259. if(beforeClose && typeof beforeClose === 'function' && beforeClose.call(this, this.data.values, this.data.titles) === false) {
  4260. return false
  4261. }
  4262. if(this.config.multi) {
  4263. if(this.config.min !== undefined && this.data.length < this.config.min) {
  4264. $.toast("请至少选择"+this.config.min+"个", "text");
  4265. return false
  4266. }
  4267. if(this.config.max !== undefined && this.data.length > this.config.max) {
  4268. $.toast("最多只能选择"+this.config.max+"个", "text");
  4269. return false
  4270. }
  4271. }
  4272. }
  4273. $.closePicker(function() {
  4274. self.onClose();
  4275. callback && callback();
  4276. });
  4277. return true
  4278. }
  4279. Select.prototype.onClose = function() {
  4280. this._open = false;
  4281. if(this.config.onClose) this.config.onClose(this);
  4282. }
  4283. Select.prototype.getHTML = function(callback) {
  4284. var config = this.config;
  4285. return this.tpl({
  4286. items: config.items,
  4287. title: config.title,
  4288. closeText: config.closeText
  4289. })
  4290. }
  4291. $.fn.select = function(params, args) {
  4292. return this.each(function() {
  4293. var $this = $(this);
  4294. if(!$this.data("weui-select")) $this.data("weui-select", new Select(this, params));
  4295. var select = $this.data("weui-select");
  4296. if(typeof params === typeof "a") select[params].call(select, args);
  4297. return select;
  4298. });
  4299. }
  4300. defaults = $.fn.select.prototype.defaults = {
  4301. items: [],
  4302. input: undefined, //输入框的初始值
  4303. title: "请选择",
  4304. multi: false,
  4305. closeText: "确定",
  4306. autoClose: true, //是否选择完成后自动关闭,只有单选模式下才有效
  4307. onChange: undefined, //function
  4308. beforeClose: undefined, // function 关闭之前,如果返回false则阻止关闭
  4309. onClose: undefined, //function
  4310. onOpen: undefined, //function
  4311. split: ",", //多选模式下的分隔符
  4312. min: undefined, //多选模式下可用,最少选择数
  4313. max: undefined, //单选模式下可用,最多选择数
  4314. toolbarTemplate: '<div class="toolbar">\
  4315. <div class="toolbar-inner">\
  4316. <a href="javascript:;" class="picker-button close-select">{{closeText}}</a>\
  4317. <h1 class="title">{{title}}</h1>\
  4318. </div>\
  4319. </div>',
  4320. radioTemplate:
  4321. '<div class="weui-cells weui-cells_radio">\
  4322. {{#items}}\
  4323. <label class="weui-cell weui-check_label" for="weui-select-id-{{this.title}}">\
  4324. <div class="weui-cell__bd weui-cell_primary">\
  4325. <p>{{this.title}}</p>\
  4326. </div>\
  4327. <div class="weui-cell__ft">\
  4328. <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}}">\
  4329. <span class="weui-icon-checked"></span>\
  4330. </div>\
  4331. </label>\
  4332. {{/items}}\
  4333. </div>',
  4334. checkboxTemplate:
  4335. '<div class="weui-cells weui-cells_checkbox">\
  4336. {{#items}}\
  4337. <label class="weui-cell weui-check_label" for="weui-select-id-{{this.title}}">\
  4338. <div class="weui-cell__bd weui-cell_primary">\
  4339. <p>{{this.title}}</p>\
  4340. </div>\
  4341. <div class="weui-cell__ft">\
  4342. <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}}" >\
  4343. <span class="weui-icon-checked"></span>\
  4344. </div>\
  4345. </label>\
  4346. {{/items}}\
  4347. </div>',
  4348. }
  4349. }($);
  4350. /*======================================================
  4351. ************ Calendar ************
  4352. ======================================================*/
  4353. /* global $:true */
  4354. /*jshint unused: false*/
  4355. +function ($) {
  4356. "use strict";
  4357. var rtl = false;
  4358. var defaults;
  4359. var isSameDate = function (a, b) {
  4360. var a = new Date(a),
  4361. b = new Date(b);
  4362. return a.getFullYear() === b.getFullYear() && a.getMonth() === b.getMonth() && a.getDate() === b.getDate()
  4363. }
  4364. var Calendar = function (params) {
  4365. var p = this;
  4366. params = params || {};
  4367. for (var def in defaults) {
  4368. if (typeof params[def] === 'undefined') {
  4369. params[def] = defaults[def];
  4370. }
  4371. }
  4372. p.params = params;
  4373. p.initialized = false;
  4374. // Inline flag
  4375. p.inline = p.params.container ? true : false;
  4376. // Is horizontal
  4377. p.isH = p.params.direction === 'horizontal';
  4378. // RTL inverter
  4379. var inverter = p.isH ? (rtl ? -1 : 1) : 1;
  4380. // Animating flag
  4381. p.animating = false;
  4382. // Should be converted to popover
  4383. function isPopover() {
  4384. var toPopover = false;
  4385. if (!p.params.convertToPopover && !p.params.onlyInPopover) return toPopover;
  4386. if (!p.inline && p.params.input) {
  4387. if (p.params.onlyInPopover) toPopover = true;
  4388. else {
  4389. if ($.device.ios) {
  4390. toPopover = $.device.ipad ? true : false;
  4391. }
  4392. else {
  4393. if ($(window).width() >= 768) toPopover = true;
  4394. }
  4395. }
  4396. }
  4397. return toPopover;
  4398. }
  4399. function inPopover() {
  4400. if (p.opened && p.container && p.container.length > 0 && p.container.parents('.popover').length > 0) return true;
  4401. else return false;
  4402. }
  4403. // Format date
  4404. function formatDate(date) {
  4405. date = new Date(date);
  4406. var year = date.getFullYear();
  4407. var month = date.getMonth();
  4408. var month1 = month + 1;
  4409. var day = date.getDate();
  4410. var weekDay = date.getDay();
  4411. return p.params.dateFormat
  4412. .replace(/yyyy/g, year)
  4413. .replace(/yy/g, (year + '').substring(2))
  4414. .replace(/mm/g, month1 < 10 ? '0' + month1 : month1)
  4415. .replace(/m/g, month1)
  4416. .replace(/MM/g, p.params.monthNames[month])
  4417. .replace(/M/g, p.params.monthNamesShort[month])
  4418. .replace(/dd/g, day < 10 ? '0' + day : day)
  4419. .replace(/d/g, day)
  4420. .replace(/DD/g, p.params.dayNames[weekDay])
  4421. .replace(/D/g, p.params.dayNamesShort[weekDay]);
  4422. }
  4423. // Value
  4424. p.addValue = function (value) {
  4425. if (p.params.multiple) {
  4426. if (!p.value) p.value = [];
  4427. var inValuesIndex;
  4428. for (var i = 0; i < p.value.length; i++) {
  4429. if (isSameDate(value, p.value[i])) {
  4430. inValuesIndex = i;
  4431. }
  4432. }
  4433. if (typeof inValuesIndex === 'undefined') {
  4434. p.value.push(value);
  4435. }
  4436. else {
  4437. p.value.splice(inValuesIndex, 1);
  4438. }
  4439. p.updateValue();
  4440. }
  4441. else {
  4442. p.value = [value];
  4443. p.updateValue();
  4444. }
  4445. };
  4446. p.setValue = function (arrValues) {
  4447. var date = new Date(arrValues[0]);
  4448. p.setYearMonth(date.getFullYear(), date.getMonth());
  4449. p.addValue(+ date);
  4450. };
  4451. p.updateValue = function () {
  4452. p.wrapper.find('.picker-calendar-day-selected').removeClass('picker-calendar-day-selected');
  4453. var i, inputValue;
  4454. for (i = 0; i < p.value.length; i++) {
  4455. var valueDate = new Date(p.value[i]);
  4456. p.wrapper.find('.picker-calendar-day[data-date="' + valueDate.getFullYear() + '-' + valueDate.getMonth() + '-' + valueDate.getDate() + '"]').addClass('picker-calendar-day-selected');
  4457. }
  4458. if (p.params.onChange) {
  4459. p.params.onChange(p, p.value.map(formatDate), p.value.map(function (d) {
  4460. return + new Date(typeof d === typeof 'a' ? d.split(/\D/).filter(function (a) { return !!a; }).join("-") : d);
  4461. }));
  4462. }
  4463. if (p.input && p.input.length > 0) {
  4464. if (p.params.formatValue) inputValue = p.params.formatValue(p, p.value);
  4465. else {
  4466. inputValue = [];
  4467. for (i = 0; i < p.value.length; i++) {
  4468. inputValue.push(formatDate(p.value[i]));
  4469. }
  4470. inputValue = inputValue.join(', ');
  4471. }
  4472. $(p.input).val(inputValue);
  4473. $(p.input).trigger('change');
  4474. }
  4475. };
  4476. // Columns Handlers
  4477. p.initCalendarEvents = function () {
  4478. var col;
  4479. var allowItemClick = true;
  4480. var isTouched, isMoved, touchStartX, touchStartY, touchCurrentX, touchCurrentY, touchStartTime, touchEndTime, startTranslate, currentTranslate, wrapperWidth, wrapperHeight, percentage, touchesDiff, isScrolling;
  4481. function handleTouchStart (e) {
  4482. if (isMoved || isTouched) return;
  4483. // e.preventDefault();
  4484. isTouched = true;
  4485. var position = $.getTouchPosition(e);
  4486. touchStartX = touchCurrentY = position.x;
  4487. touchStartY = touchCurrentY = position.y;
  4488. touchStartTime = (new Date()).getTime();
  4489. percentage = 0;
  4490. allowItemClick = true;
  4491. isScrolling = undefined;
  4492. startTranslate = currentTranslate = p.monthsTranslate;
  4493. }
  4494. function handleTouchMove (e) {
  4495. if (!isTouched) return;
  4496. var position = $.getTouchPosition(e);
  4497. touchCurrentX = position.x;
  4498. touchCurrentY = position.y;
  4499. if (typeof isScrolling === 'undefined') {
  4500. isScrolling = !!(isScrolling || Math.abs(touchCurrentY - touchStartY) > Math.abs(touchCurrentX - touchStartX));
  4501. }
  4502. if (p.isH && isScrolling) {
  4503. isTouched = false;
  4504. return;
  4505. }
  4506. e.preventDefault();
  4507. if (p.animating) {
  4508. isTouched = false;
  4509. return;
  4510. }
  4511. allowItemClick = false;
  4512. if (!isMoved) {
  4513. // First move
  4514. isMoved = true;
  4515. wrapperWidth = p.wrapper[0].offsetWidth;
  4516. wrapperHeight = p.wrapper[0].offsetHeight;
  4517. p.wrapper.transition(0);
  4518. }
  4519. e.preventDefault();
  4520. touchesDiff = p.isH ? touchCurrentX - touchStartX : touchCurrentY - touchStartY;
  4521. percentage = touchesDiff/(p.isH ? wrapperWidth : wrapperHeight);
  4522. currentTranslate = (p.monthsTranslate * inverter + percentage) * 100;
  4523. // Transform wrapper
  4524. p.wrapper.transform('translate3d(' + (p.isH ? currentTranslate : 0) + '%, ' + (p.isH ? 0 : currentTranslate) + '%, 0)');
  4525. }
  4526. function handleTouchEnd (e) {
  4527. if (!isTouched || !isMoved) {
  4528. isTouched = isMoved = false;
  4529. return;
  4530. }
  4531. isTouched = isMoved = false;
  4532. touchEndTime = new Date().getTime();
  4533. if (touchEndTime - touchStartTime < 300) {
  4534. if (Math.abs(touchesDiff) < 10) {
  4535. p.resetMonth();
  4536. }
  4537. else if (touchesDiff >= 10) {
  4538. if (rtl) p.nextMonth();
  4539. else p.prevMonth();
  4540. }
  4541. else {
  4542. if (rtl) p.prevMonth();
  4543. else p.nextMonth();
  4544. }
  4545. }
  4546. else {
  4547. if (percentage <= -0.5) {
  4548. if (rtl) p.prevMonth();
  4549. else p.nextMonth();
  4550. }
  4551. else if (percentage >= 0.5) {
  4552. if (rtl) p.nextMonth();
  4553. else p.prevMonth();
  4554. }
  4555. else {
  4556. p.resetMonth();
  4557. }
  4558. }
  4559. // Allow click
  4560. setTimeout(function () {
  4561. allowItemClick = true;
  4562. }, 100);
  4563. }
  4564. function handleDayClick(e) {
  4565. if (!allowItemClick) return;
  4566. var day = $(e.target).parents('.picker-calendar-day');
  4567. if (day.length === 0 && $(e.target).hasClass('picker-calendar-day')) {
  4568. day = $(e.target);
  4569. }
  4570. if (day.length === 0) return;
  4571. // if (day.hasClass('picker-calendar-day-selected') && !p.params.multiple) return;
  4572. if (day.hasClass('picker-calendar-day-disabled')) return;
  4573. if (day.hasClass('picker-calendar-day-next')) p.nextMonth();
  4574. if (day.hasClass('picker-calendar-day-prev')) p.prevMonth();
  4575. var dateYear = day.attr('data-year');
  4576. var dateMonth = day.attr('data-month');
  4577. var dateDay = day.attr('data-day');
  4578. if (p.params.onDayClick) {
  4579. p.params.onDayClick(p, day[0], dateYear, dateMonth, dateDay);
  4580. }
  4581. p.addValue(new Date(dateYear, dateMonth, dateDay).getTime());
  4582. if (p.params.closeOnSelect && !p.params.multiple) p.close();
  4583. }
  4584. p.container.find('.picker-calendar-prev-month').on('click', p.prevMonth);
  4585. p.container.find('.picker-calendar-next-month').on('click', p.nextMonth);
  4586. p.container.find('.picker-calendar-prev-year').on('click', p.prevYear);
  4587. p.container.find('.picker-calendar-next-year').on('click', p.nextYear);
  4588. p.wrapper.on('click', handleDayClick);
  4589. if (p.params.touchMove) {
  4590. p.wrapper.on($.touchEvents.start, handleTouchStart);
  4591. p.wrapper.on($.touchEvents.move, handleTouchMove);
  4592. p.wrapper.on($.touchEvents.end, handleTouchEnd);
  4593. }
  4594. p.container[0].f7DestroyCalendarEvents = function () {
  4595. p.container.find('.picker-calendar-prev-month').off('click', p.prevMonth);
  4596. p.container.find('.picker-calendar-next-month').off('click', p.nextMonth);
  4597. p.container.find('.picker-calendar-prev-year').off('click', p.prevYear);
  4598. p.container.find('.picker-calendar-next-year').off('click', p.nextYear);
  4599. p.wrapper.off('click', handleDayClick);
  4600. if (p.params.touchMove) {
  4601. p.wrapper.off($.touchEvents.start, handleTouchStart);
  4602. p.wrapper.off($.touchEvents.move, handleTouchMove);
  4603. p.wrapper.off($.touchEvents.end, handleTouchEnd);
  4604. }
  4605. };
  4606. };
  4607. p.destroyCalendarEvents = function (colContainer) {
  4608. if ('f7DestroyCalendarEvents' in p.container[0]) p.container[0].f7DestroyCalendarEvents();
  4609. };
  4610. // Calendar Methods
  4611. p.daysInMonth = function (date) {
  4612. var d = new Date(date);
  4613. return new Date(d.getFullYear(), d.getMonth() + 1, 0).getDate();
  4614. };
  4615. p.monthHTML = function (date, offset) {
  4616. date = new Date(date);
  4617. var year = date.getFullYear(),
  4618. month = date.getMonth(),
  4619. day = date.getDate();
  4620. if (offset === 'next') {
  4621. if (month === 11) date = new Date(year + 1, 0);
  4622. else date = new Date(year, month + 1, 1);
  4623. }
  4624. if (offset === 'prev') {
  4625. if (month === 0) date = new Date(year - 1, 11);
  4626. else date = new Date(year, month - 1, 1);
  4627. }
  4628. if (offset === 'next' || offset === 'prev') {
  4629. month = date.getMonth();
  4630. year = date.getFullYear();
  4631. }
  4632. var daysInPrevMonth = p.daysInMonth(new Date(date.getFullYear(), date.getMonth()).getTime() - 10 * 24 * 60 * 60 * 1000),
  4633. daysInMonth = p.daysInMonth(date),
  4634. firstDayOfMonthIndex = new Date(date.getFullYear(), date.getMonth()).getDay();
  4635. if (firstDayOfMonthIndex === 0) firstDayOfMonthIndex = 7;
  4636. var dayDate, currentValues = [], i, j,
  4637. rows = 6, cols = 7,
  4638. monthHTML = '',
  4639. dayIndex = 0 + (p.params.firstDay - 1),
  4640. today = new Date().setHours(0,0,0,0),
  4641. minDate = p.params.minDate ? new Date(p.params.minDate).getTime() : null,
  4642. maxDate = p.params.maxDate ? new Date(p.params.maxDate).getTime() : null;
  4643. if (p.value && p.value.length) {
  4644. for (i = 0; i < p.value.length; i++) {
  4645. currentValues.push(new Date(p.value[i]).setHours(0,0,0,0));
  4646. }
  4647. }
  4648. for (i = 1; i <= rows; i++) {
  4649. var rowHTML = '';
  4650. var row = i;
  4651. for (j = 1; j <= cols; j++) {
  4652. var col = j;
  4653. dayIndex ++;
  4654. var dayNumber = dayIndex - firstDayOfMonthIndex;
  4655. var addClass = '';
  4656. if (dayNumber < 0) {
  4657. dayNumber = daysInPrevMonth + dayNumber + 1;
  4658. addClass += ' picker-calendar-day-prev';
  4659. dayDate = new Date(month - 1 < 0 ? year - 1 : year, month - 1 < 0 ? 11 : month - 1, dayNumber).getTime();
  4660. }
  4661. else {
  4662. dayNumber = dayNumber + 1;
  4663. if (dayNumber > daysInMonth) {
  4664. dayNumber = dayNumber - daysInMonth;
  4665. addClass += ' picker-calendar-day-next';
  4666. dayDate = new Date(month + 1 > 11 ? year + 1 : year, month + 1 > 11 ? 0 : month + 1, dayNumber).getTime();
  4667. }
  4668. else {
  4669. dayDate = new Date(year, month, dayNumber).getTime();
  4670. }
  4671. }
  4672. // Today
  4673. if (dayDate === today) addClass += ' picker-calendar-day-today';
  4674. // Selected
  4675. if (currentValues.indexOf(dayDate) >= 0) addClass += ' picker-calendar-day-selected';
  4676. // Weekend
  4677. if (p.params.weekendDays.indexOf(col - 1) >= 0) {
  4678. addClass += ' picker-calendar-day-weekend';
  4679. }
  4680. // Disabled
  4681. if ((minDate && dayDate < minDate) || (maxDate && dayDate > maxDate)) {
  4682. addClass += ' picker-calendar-day-disabled';
  4683. }
  4684. dayDate = new Date(dayDate);
  4685. var dayYear = dayDate.getFullYear();
  4686. var dayMonth = dayDate.getMonth();
  4687. 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>';
  4688. }
  4689. monthHTML += '<div class="picker-calendar-row">' + rowHTML + '</div>';
  4690. }
  4691. monthHTML = '<div class="picker-calendar-month" data-year="' + year + '" data-month="' + month + '">' + monthHTML + '</div>';
  4692. return monthHTML;
  4693. };
  4694. p.animating = false;
  4695. p.updateCurrentMonthYear = function (dir) {
  4696. if (typeof dir === 'undefined') {
  4697. p.currentMonth = parseInt(p.months.eq(1).attr('data-month'), 10);
  4698. p.currentYear = parseInt(p.months.eq(1).attr('data-year'), 10);
  4699. }
  4700. else {
  4701. p.currentMonth = parseInt(p.months.eq(dir === 'next' ? (p.months.length - 1) : 0).attr('data-month'), 10);
  4702. p.currentYear = parseInt(p.months.eq(dir === 'next' ? (p.months.length - 1) : 0).attr('data-year'), 10);
  4703. }
  4704. p.container.find('.current-month-value').text(p.params.monthNames[p.currentMonth]);
  4705. p.container.find('.current-year-value').text(p.currentYear);
  4706. };
  4707. p.onMonthChangeStart = function (dir) {
  4708. p.updateCurrentMonthYear(dir);
  4709. p.months.removeClass('picker-calendar-month-current picker-calendar-month-prev picker-calendar-month-next');
  4710. var currentIndex = dir === 'next' ? p.months.length - 1 : 0;
  4711. p.months.eq(currentIndex).addClass('picker-calendar-month-current');
  4712. p.months.eq(dir === 'next' ? currentIndex - 1 : currentIndex + 1).addClass(dir === 'next' ? 'picker-calendar-month-prev' : 'picker-calendar-month-next');
  4713. if (p.params.onMonthYearChangeStart) {
  4714. p.params.onMonthYearChangeStart(p, p.currentYear, p.currentMonth);
  4715. }
  4716. };
  4717. p.onMonthChangeEnd = function (dir, rebuildBoth) {
  4718. p.animating = false;
  4719. var nextMonthHTML, prevMonthHTML, newMonthHTML;
  4720. p.wrapper.find('.picker-calendar-month:not(.picker-calendar-month-prev):not(.picker-calendar-month-current):not(.picker-calendar-month-next)').remove();
  4721. if (typeof dir === 'undefined') {
  4722. dir = 'next';
  4723. rebuildBoth = true;
  4724. }
  4725. if (!rebuildBoth) {
  4726. newMonthHTML = p.monthHTML(new Date(p.currentYear, p.currentMonth), dir);
  4727. }
  4728. else {
  4729. p.wrapper.find('.picker-calendar-month-next, .picker-calendar-month-prev').remove();
  4730. prevMonthHTML = p.monthHTML(new Date(p.currentYear, p.currentMonth), 'prev');
  4731. nextMonthHTML = p.monthHTML(new Date(p.currentYear, p.currentMonth), 'next');
  4732. }
  4733. if (dir === 'next' || rebuildBoth) {
  4734. p.wrapper.append(newMonthHTML || nextMonthHTML);
  4735. }
  4736. if (dir === 'prev' || rebuildBoth) {
  4737. p.wrapper.prepend(newMonthHTML || prevMonthHTML);
  4738. }
  4739. p.months = p.wrapper.find('.picker-calendar-month');
  4740. p.setMonthsTranslate(p.monthsTranslate);
  4741. if (p.params.onMonthAdd) {
  4742. p.params.onMonthAdd(p, dir === 'next' ? p.months.eq(p.months.length - 1)[0] : p.months.eq(0)[0]);
  4743. }
  4744. if (p.params.onMonthYearChangeEnd) {
  4745. p.params.onMonthYearChangeEnd(p, p.currentYear, p.currentMonth);
  4746. }
  4747. };
  4748. p.setMonthsTranslate = function (translate) {
  4749. translate = translate || p.monthsTranslate || 0;
  4750. if (typeof p.monthsTranslate === 'undefined') p.monthsTranslate = translate;
  4751. p.months.removeClass('picker-calendar-month-current picker-calendar-month-prev picker-calendar-month-next');
  4752. var prevMonthTranslate = -(translate + 1) * 100 * inverter;
  4753. var currentMonthTranslate = -translate * 100 * inverter;
  4754. var nextMonthTranslate = -(translate - 1) * 100 * inverter;
  4755. p.months.eq(0).transform('translate3d(' + (p.isH ? prevMonthTranslate : 0) + '%, ' + (p.isH ? 0 : prevMonthTranslate) + '%, 0)').addClass('picker-calendar-month-prev');
  4756. p.months.eq(1).transform('translate3d(' + (p.isH ? currentMonthTranslate : 0) + '%, ' + (p.isH ? 0 : currentMonthTranslate) + '%, 0)').addClass('picker-calendar-month-current');
  4757. p.months.eq(2).transform('translate3d(' + (p.isH ? nextMonthTranslate : 0) + '%, ' + (p.isH ? 0 : nextMonthTranslate) + '%, 0)').addClass('picker-calendar-month-next');
  4758. };
  4759. p.nextMonth = function (transition) {
  4760. if (typeof transition === 'undefined' || typeof transition === 'object') {
  4761. transition = '';
  4762. if (!p.params.animate) transition = 0;
  4763. }
  4764. var nextMonth = parseInt(p.months.eq(p.months.length - 1).attr('data-month'), 10);
  4765. var nextYear = parseInt(p.months.eq(p.months.length - 1).attr('data-year'), 10);
  4766. var nextDate = new Date(nextYear, nextMonth);
  4767. var nextDateTime = nextDate.getTime();
  4768. var transitionEndCallback = p.animating ? false : true;
  4769. if (p.params.maxDate) {
  4770. if (nextDateTime > new Date(p.params.maxDate).getTime()) {
  4771. return p.resetMonth();
  4772. }
  4773. }
  4774. p.monthsTranslate --;
  4775. if (nextMonth === p.currentMonth) {
  4776. var nextMonthTranslate = -(p.monthsTranslate) * 100 * inverter;
  4777. var nextMonthHTML = $(p.monthHTML(nextDateTime, 'next')).transform('translate3d(' + (p.isH ? nextMonthTranslate : 0) + '%, ' + (p.isH ? 0 : nextMonthTranslate) + '%, 0)').addClass('picker-calendar-month-next');
  4778. p.wrapper.append(nextMonthHTML[0]);
  4779. p.months = p.wrapper.find('.picker-calendar-month');
  4780. if (p.params.onMonthAdd) {
  4781. p.params.onMonthAdd(p, p.months.eq(p.months.length - 1)[0]);
  4782. }
  4783. }
  4784. p.animating = true;
  4785. p.onMonthChangeStart('next');
  4786. var translate = (p.monthsTranslate * 100) * inverter;
  4787. p.wrapper.transition(transition).transform('translate3d(' + (p.isH ? translate : 0) + '%, ' + (p.isH ? 0 : translate) + '%, 0)');
  4788. if (transitionEndCallback) {
  4789. p.wrapper.transitionEnd(function () {
  4790. p.onMonthChangeEnd('next');
  4791. });
  4792. }
  4793. if (!p.params.animate) {
  4794. p.onMonthChangeEnd('next');
  4795. }
  4796. };
  4797. p.prevMonth = function (transition) {
  4798. if (typeof transition === 'undefined' || typeof transition === 'object') {
  4799. transition = '';
  4800. if (!p.params.animate) transition = 0;
  4801. }
  4802. var prevMonth = parseInt(p.months.eq(0).attr('data-month'), 10);
  4803. var prevYear = parseInt(p.months.eq(0).attr('data-year'), 10);
  4804. var prevDate = new Date(prevYear, prevMonth + 1, -1);
  4805. var prevDateTime = prevDate.getTime();
  4806. var transitionEndCallback = p.animating ? false : true;
  4807. if (p.params.minDate) {
  4808. if (prevDateTime < new Date(p.params.minDate).getTime()) {
  4809. return p.resetMonth();
  4810. }
  4811. }
  4812. p.monthsTranslate ++;
  4813. if (prevMonth === p.currentMonth) {
  4814. var prevMonthTranslate = -(p.monthsTranslate) * 100 * inverter;
  4815. var prevMonthHTML = $(p.monthHTML(prevDateTime, 'prev')).transform('translate3d(' + (p.isH ? prevMonthTranslate : 0) + '%, ' + (p.isH ? 0 : prevMonthTranslate) + '%, 0)').addClass('picker-calendar-month-prev');
  4816. p.wrapper.prepend(prevMonthHTML[0]);
  4817. p.months = p.wrapper.find('.picker-calendar-month');
  4818. if (p.params.onMonthAdd) {
  4819. p.params.onMonthAdd(p, p.months.eq(0)[0]);
  4820. }
  4821. }
  4822. p.animating = true;
  4823. p.onMonthChangeStart('prev');
  4824. var translate = (p.monthsTranslate * 100) * inverter;
  4825. p.wrapper.transition(transition).transform('translate3d(' + (p.isH ? translate : 0) + '%, ' + (p.isH ? 0 : translate) + '%, 0)');
  4826. if (transitionEndCallback) {
  4827. p.wrapper.transitionEnd(function () {
  4828. p.onMonthChangeEnd('prev');
  4829. });
  4830. }
  4831. if (!p.params.animate) {
  4832. p.onMonthChangeEnd('prev');
  4833. }
  4834. };
  4835. p.resetMonth = function (transition) {
  4836. if (typeof transition === 'undefined') transition = '';
  4837. var translate = (p.monthsTranslate * 100) * inverter;
  4838. p.wrapper.transition(transition).transform('translate3d(' + (p.isH ? translate : 0) + '%, ' + (p.isH ? 0 : translate) + '%, 0)');
  4839. };
  4840. p.setYearMonth = function (year, month, transition) {
  4841. if (typeof year === 'undefined') year = p.currentYear;
  4842. if (typeof month === 'undefined') month = p.currentMonth;
  4843. if (typeof transition === 'undefined' || typeof transition === 'object') {
  4844. transition = '';
  4845. if (!p.params.animate) transition = 0;
  4846. }
  4847. var targetDate;
  4848. if (year < p.currentYear) {
  4849. targetDate = new Date(year, month + 1, -1).getTime();
  4850. }
  4851. else {
  4852. targetDate = new Date(year, month).getTime();
  4853. }
  4854. if (p.params.maxDate && targetDate > new Date(p.params.maxDate).getTime()) {
  4855. return false;
  4856. }
  4857. if (p.params.minDate && targetDate < new Date(p.params.minDate).getTime()) {
  4858. return false;
  4859. }
  4860. var currentDate = new Date(p.currentYear, p.currentMonth).getTime();
  4861. var dir = targetDate > currentDate ? 'next' : 'prev';
  4862. var newMonthHTML = p.monthHTML(new Date(year, month));
  4863. p.monthsTranslate = p.monthsTranslate || 0;
  4864. var prevTranslate = p.monthsTranslate;
  4865. var monthTranslate, wrapperTranslate;
  4866. var transitionEndCallback = p.animating ? false : true;
  4867. if (targetDate > currentDate) {
  4868. // To next
  4869. p.monthsTranslate --;
  4870. if (!p.animating) p.months.eq(p.months.length - 1).remove();
  4871. p.wrapper.append(newMonthHTML);
  4872. p.months = p.wrapper.find('.picker-calendar-month');
  4873. monthTranslate = -(prevTranslate - 1) * 100 * inverter;
  4874. p.months.eq(p.months.length - 1).transform('translate3d(' + (p.isH ? monthTranslate : 0) + '%, ' + (p.isH ? 0 : monthTranslate) + '%, 0)').addClass('picker-calendar-month-next');
  4875. }
  4876. else {
  4877. // To prev
  4878. p.monthsTranslate ++;
  4879. if (!p.animating) p.months.eq(0).remove();
  4880. p.wrapper.prepend(newMonthHTML);
  4881. p.months = p.wrapper.find('.picker-calendar-month');
  4882. monthTranslate = -(prevTranslate + 1) * 100 * inverter;
  4883. p.months.eq(0).transform('translate3d(' + (p.isH ? monthTranslate : 0) + '%, ' + (p.isH ? 0 : monthTranslate) + '%, 0)').addClass('picker-calendar-month-prev');
  4884. }
  4885. if (p.params.onMonthAdd) {
  4886. p.params.onMonthAdd(p, dir === 'next' ? p.months.eq(p.months.length - 1)[0] : p.months.eq(0)[0]);
  4887. }
  4888. p.animating = true;
  4889. p.onMonthChangeStart(dir);
  4890. wrapperTranslate = (p.monthsTranslate * 100) * inverter;
  4891. p.wrapper.transition(transition).transform('translate3d(' + (p.isH ? wrapperTranslate : 0) + '%, ' + (p.isH ? 0 : wrapperTranslate) + '%, 0)');
  4892. if (transitionEndCallback) {
  4893. p.wrapper.transitionEnd(function () {
  4894. p.onMonthChangeEnd(dir, true);
  4895. });
  4896. }
  4897. if (!p.params.animate) {
  4898. p.onMonthChangeEnd(dir);
  4899. }
  4900. };
  4901. p.nextYear = function () {
  4902. p.setYearMonth(p.currentYear + 1);
  4903. };
  4904. p.prevYear = function () {
  4905. p.setYearMonth(p.currentYear - 1);
  4906. };
  4907. // HTML Layout
  4908. p.layout = function () {
  4909. var pickerHTML = '';
  4910. var pickerClass = '';
  4911. var i;
  4912. var layoutDate = p.value && p.value.length ? p.value[0] : new Date().setHours(0,0,0,0);
  4913. var prevMonthHTML = p.monthHTML(layoutDate, 'prev');
  4914. var currentMonthHTML = p.monthHTML(layoutDate);
  4915. var nextMonthHTML = p.monthHTML(layoutDate, 'next');
  4916. var monthsHTML = '<div class="picker-calendar-months"><div class="picker-calendar-months-wrapper">' + (prevMonthHTML + currentMonthHTML + nextMonthHTML) + '</div></div>';
  4917. // Week days header
  4918. var weekHeaderHTML = '';
  4919. if (p.params.weekHeader) {
  4920. for (i = 0; i < 7; i++) {
  4921. var weekDayIndex = (i + p.params.firstDay > 6) ? (i - 7 + p.params.firstDay) : (i + p.params.firstDay);
  4922. var dayName = p.params.dayNamesShort[weekDayIndex];
  4923. weekHeaderHTML += '<div class="picker-calendar-week-day ' + ((p.params.weekendDays.indexOf(weekDayIndex) >= 0) ? 'picker-calendar-week-day-weekend' : '') + '"> ' + dayName + '</div>';
  4924. }
  4925. weekHeaderHTML = '<div class="picker-calendar-week-days">' + weekHeaderHTML + '</div>';
  4926. }
  4927. pickerClass = 'weui-picker-calendar ' + (p.params.cssClass || '');
  4928. if(!p.inline) pickerClass = 'weui-picker-modal ' + pickerClass;
  4929. var toolbarHTML = p.params.toolbar ? p.params.toolbarTemplate.replace(/{{closeText}}/g, p.params.toolbarCloseText) : '';
  4930. if (p.params.toolbar) {
  4931. toolbarHTML = p.params.toolbarTemplate
  4932. .replace(/{{closeText}}/g, p.params.toolbarCloseText)
  4933. .replace(/{{monthPicker}}/g, (p.params.monthPicker ? p.params.monthPickerTemplate : ''))
  4934. .replace(/{{yearPicker}}/g, (p.params.yearPicker ? p.params.yearPickerTemplate : ''));
  4935. }
  4936. pickerHTML =
  4937. '<div class="' + (pickerClass) + '">' +
  4938. toolbarHTML +
  4939. '<div class="picker-modal-inner">' +
  4940. weekHeaderHTML +
  4941. monthsHTML +
  4942. '</div>' +
  4943. '</div>';
  4944. p.pickerHTML = pickerHTML;
  4945. };
  4946. // Input Events
  4947. function openOnInput(e) {
  4948. e.preventDefault();
  4949. if (p.opened) return;
  4950. p.open();
  4951. if (p.params.scrollToInput && !isPopover()) {
  4952. var pageContent = p.input.parents('.page-content');
  4953. if (pageContent.length === 0) return;
  4954. var paddingTop = parseInt(pageContent.css('padding-top'), 10),
  4955. paddingBottom = parseInt(pageContent.css('padding-bottom'), 10),
  4956. pageHeight = pageContent[0].offsetHeight - paddingTop - p.container.height(),
  4957. pageScrollHeight = pageContent[0].scrollHeight - paddingTop - p.container.height(),
  4958. newPaddingBottom;
  4959. var inputTop = p.input.offset().top - paddingTop + p.input[0].offsetHeight;
  4960. if (inputTop > pageHeight) {
  4961. var scrollTop = pageContent.scrollTop() + inputTop - pageHeight;
  4962. if (scrollTop + pageHeight > pageScrollHeight) {
  4963. newPaddingBottom = scrollTop + pageHeight - pageScrollHeight + paddingBottom;
  4964. if (pageHeight === pageScrollHeight) {
  4965. newPaddingBottom = p.container.height();
  4966. }
  4967. pageContent.css({'padding-bottom': (newPaddingBottom) + 'px'});
  4968. }
  4969. pageContent.scrollTop(scrollTop, 300);
  4970. }
  4971. }
  4972. }
  4973. function closeOnHTMLClick(e) {
  4974. if (inPopover()) return;
  4975. if (p.input && p.input.length > 0) {
  4976. if (e.target !== p.input[0] && $(e.target).parents('.weui-picker-modal').length === 0) p.close();
  4977. }
  4978. else {
  4979. if ($(e.target).parents('.weui-picker-modal').length === 0) p.close();
  4980. }
  4981. }
  4982. if (p.params.input) {
  4983. p.input = $(p.params.input);
  4984. if (p.input.length > 0) {
  4985. if (p.params.inputReadOnly) p.input.prop('readOnly', true);
  4986. if (!p.inline) {
  4987. p.input.on('click', openOnInput);
  4988. }
  4989. if (p.params.inputReadOnly) {
  4990. p.input.on('focus mousedown', function (e) {
  4991. e.preventDefault();
  4992. });
  4993. }
  4994. }
  4995. }
  4996. //iphone 上无法正确触发 click,会导致点击外面无法关闭
  4997. if (!p.inline) $(document).on('click touchend', closeOnHTMLClick);
  4998. // Open
  4999. function onPickerClose() {
  5000. p.opened = false;
  5001. if (p.input && p.input.length > 0) p.input.parents('.page-content').css({'padding-bottom': ''});
  5002. if (p.params.onClose) p.params.onClose(p);
  5003. // Destroy events
  5004. p.destroyCalendarEvents();
  5005. }
  5006. p.opened = false;
  5007. p.open = function () {
  5008. var toPopover = isPopover() && false;
  5009. var updateValue = false;
  5010. if (!p.opened) {
  5011. // Set date value
  5012. if (!p.value) {
  5013. if (p.params.value) {
  5014. p.value = p.params.value;
  5015. updateValue = true;
  5016. }
  5017. }
  5018. // Layout
  5019. p.layout();
  5020. // Append
  5021. if (toPopover) {
  5022. p.pickerHTML = '<div class="popover popover-picker-calendar"><div class="popover-inner">' + p.pickerHTML + '</div></div>';
  5023. p.popover = $.popover(p.pickerHTML, p.params.input, true);
  5024. p.container = $(p.popover).find('.weui-picker-modal');
  5025. $(p.popover).on('close', function () {
  5026. onPickerClose();
  5027. });
  5028. }
  5029. else if (p.inline) {
  5030. p.container = $(p.pickerHTML);
  5031. p.container.addClass('picker-modal-inline');
  5032. $(p.params.container).append(p.container);
  5033. }
  5034. else {
  5035. p.container = $($.openPicker(p.pickerHTML));
  5036. $(p.container)
  5037. .on('close', function () {
  5038. onPickerClose();
  5039. });
  5040. }
  5041. // Store calendar instance
  5042. p.container[0].f7Calendar = p;
  5043. p.wrapper = p.container.find('.picker-calendar-months-wrapper');
  5044. // Months
  5045. p.months = p.wrapper.find('.picker-calendar-month');
  5046. // Update current month and year
  5047. p.updateCurrentMonthYear();
  5048. // Set initial translate
  5049. p.monthsTranslate = 0;
  5050. p.setMonthsTranslate();
  5051. // Init events
  5052. p.initCalendarEvents();
  5053. // Update input value
  5054. if (updateValue) p.updateValue();
  5055. }
  5056. // Set flag
  5057. p.opened = true;
  5058. p.initialized = true;
  5059. if (p.params.onMonthAdd) {
  5060. p.months.each(function () {
  5061. p.params.onMonthAdd(p, this);
  5062. });
  5063. }
  5064. if (p.params.onOpen) p.params.onOpen(p);
  5065. };
  5066. // Close
  5067. p.close = function () {
  5068. if (!p.opened || p.inline) return;
  5069. p.animating = false; //有可能还有动画没做完,因此animating设置还没改。
  5070. if (inPopover()) {
  5071. $.closePicker(p.popover);
  5072. return;
  5073. }
  5074. else {
  5075. $.closePicker(p.container);
  5076. return;
  5077. }
  5078. };
  5079. // Destroy
  5080. p.destroy = function () {
  5081. p.close();
  5082. if (p.params.input && p.input.length > 0) {
  5083. p.input.off('click focus', openOnInput);
  5084. p.input.data("calendar", null);
  5085. }
  5086. $('html').off('click', closeOnHTMLClick);
  5087. };
  5088. if (p.inline) {
  5089. p.open();
  5090. }
  5091. return p;
  5092. };
  5093. var format = function(d) {
  5094. return d < 10 ? "0"+d : d;
  5095. }
  5096. $.fn.calendar = function (params, args) {
  5097. params = params || {};
  5098. return this.each(function() {
  5099. var $this = $(this);
  5100. if(!$this[0]) return;
  5101. var p = {};
  5102. if($this[0].tagName.toUpperCase() === "INPUT") {
  5103. p.input = $this;
  5104. } else {
  5105. p.container = $this;
  5106. }
  5107. var calendar = $this.data("calendar");
  5108. if(!calendar) {
  5109. if(typeof params === typeof "a") {
  5110. } else {
  5111. if(!params.value && $this.val()) params.value = [$this.val()];
  5112. //默认显示今天
  5113. if(!params.value) {
  5114. var today = new Date();
  5115. params.value = [today.getFullYear() + "/" + format(today.getMonth() + 1) + "/" + format(today.getDate())];
  5116. }
  5117. calendar = $this.data("calendar", new Calendar($.extend(p, params)));
  5118. }
  5119. }
  5120. if(typeof params === typeof "a") {
  5121. calendar[params].call(calendar, args);
  5122. }
  5123. });
  5124. };
  5125. defaults = $.fn.calendar.prototype.defaults = {
  5126. value: undefined, // 通过JS赋值,注意是数组
  5127. monthNames: ['一月', '二月', '三月', '四月', '五月', '六月', '七月', '八月', '九月', '十月', '十一月', '十二月'],
  5128. monthNamesShort: ['一月', '二月', '三月', '四月', '五月', '六月', '七月', '八月', '九月', '十月', '十一月', '十二月'],
  5129. dayNames: ['周日', '周一', '周二', '周三', '周四', '周五', '周六'],
  5130. dayNamesShort: ['周日', '周一', '周二', '周三', '周四', '周五', '周六'],
  5131. firstDay: 1, // First day of the week, Monday
  5132. weekendDays: [0, 6], // Sunday and Saturday
  5133. multiple: false,
  5134. dateFormat: 'yyyy-mm-dd',
  5135. direction: 'horizontal', // or 'vertical'
  5136. minDate: null,
  5137. maxDate: null,
  5138. touchMove: true,
  5139. animate: true,
  5140. closeOnSelect: true,
  5141. monthPicker: true,
  5142. monthPickerTemplate:
  5143. '<div class="picker-calendar-month-picker">' +
  5144. '<a href="javascript:;" class="link icon-only picker-calendar-prev-month"><i class="icon icon-prev"></i></a>' +
  5145. '<div class="current-month-value"></div>' +
  5146. '<a href="javascript:;" class="link icon-only picker-calendar-next-month"><i class="icon icon-next"></i></a>' +
  5147. '</div>',
  5148. yearPicker: true,
  5149. yearPickerTemplate:
  5150. '<div class="picker-calendar-year-picker">' +
  5151. '<a href="javascript:;" class="link icon-only picker-calendar-prev-year"><i class="icon icon-prev"></i></a>' +
  5152. '<span class="current-year-value"></span>' +
  5153. '<a href="javascript:;" class="link icon-only picker-calendar-next-year"><i class="icon icon-next"></i></a>' +
  5154. '</div>',
  5155. weekHeader: true,
  5156. // Common settings
  5157. scrollToInput: true,
  5158. inputReadOnly: true,
  5159. convertToPopover: true,
  5160. onlyInPopover: false,
  5161. toolbar: true,
  5162. toolbarCloseText: 'Done',
  5163. toolbarTemplate:
  5164. '<div class="toolbar">' +
  5165. '<div class="toolbar-inner">' +
  5166. '{{yearPicker}}' +
  5167. '{{monthPicker}}' +
  5168. // '<a href="#" class="link close-picker">{{closeText}}</a>' +
  5169. '</div>' +
  5170. '</div>',
  5171. /* Callbacks
  5172. onMonthAdd
  5173. onChange
  5174. onOpen
  5175. onClose
  5176. onDayClick
  5177. onMonthYearChangeStart
  5178. onMonthYearChangeEnd
  5179. */
  5180. };
  5181. }($);
  5182. /* global $:true */
  5183. /* jshint unused:false*/
  5184. + function($) {
  5185. "use strict";
  5186. var defaults;
  5187. var formatNumber = function (n) {
  5188. return n < 10 ? "0" + n : n;
  5189. }
  5190. var Datetime = function(input, params) {
  5191. this.input = $(input);
  5192. this.params = params || {};
  5193. this.initMonthes = params.monthes
  5194. this.initYears = params.years
  5195. var p = $.extend({}, params, this.getConfig());
  5196. $(this.input).picker(p);
  5197. }
  5198. Datetime.prototype = {
  5199. getDays : function(max) {
  5200. var days = [];
  5201. for(var i=1; i<= (max||31);i++) {
  5202. days.push(i < 10 ? "0"+i : i);
  5203. }
  5204. return days;
  5205. },
  5206. getDaysByMonthAndYear : function(month, year) {
  5207. var int_d = new Date(year, parseInt(month)+1-1, 1);
  5208. var d = new Date(int_d - 1);
  5209. return this.getDays(d.getDate());
  5210. },
  5211. getConfig: function() {
  5212. var today = new Date(),
  5213. params = this.params,
  5214. self = this,
  5215. lastValidValues;
  5216. var config = {
  5217. rotateEffect: false, //为了性能
  5218. cssClass: 'datetime-picker',
  5219. value: [today.getFullYear(), formatNumber(today.getMonth()+1), formatNumber(today.getDate()), formatNumber(today.getHours()), (formatNumber(today.getMinutes()))],
  5220. onChange: function (picker, values, displayValues) {
  5221. var cols = picker.cols;
  5222. var days = self.getDaysByMonthAndYear(values[1], values[0]);
  5223. var currentValue = values[2];
  5224. if(currentValue > days.length) currentValue = days.length;
  5225. picker.cols[4].setValue(currentValue);
  5226. //check min and max
  5227. var current = new Date(values[0]+'-'+values[1]+'-'+values[2]);
  5228. var valid = true;
  5229. if(params.min) {
  5230. var min = new Date(typeof params.min === "function" ? params.min() : params.min);
  5231. if(current < +min) {
  5232. picker.setValue(lastValidValues);
  5233. valid = false;
  5234. }
  5235. }
  5236. if(params.max) {
  5237. var max = new Date(typeof params.max === "function" ? params.max() : params.max);
  5238. if(current > +max) {
  5239. picker.setValue(lastValidValues);
  5240. valid = false;
  5241. }
  5242. }
  5243. valid && (lastValidValues = values);
  5244. if (self.params.onChange) {
  5245. self.params.onChange.apply(this, arguments);
  5246. }
  5247. },
  5248. formatValue: function (p, values, displayValues) {
  5249. return self.params.format(p, values, displayValues);
  5250. },
  5251. cols: [
  5252. {
  5253. values: this.initYears
  5254. },
  5255. {
  5256. divider: true, // 这是一个分隔符
  5257. content: params.yearSplit
  5258. },
  5259. {
  5260. values: this.initMonthes
  5261. },
  5262. {
  5263. divider: true, // 这是一个分隔符
  5264. content: params.monthSplit
  5265. },
  5266. {
  5267. values: (function () {
  5268. var dates = [];
  5269. for (var i=1; i<=31; i++) dates.push(formatNumber(i));
  5270. return dates;
  5271. })()
  5272. },
  5273. ]
  5274. }
  5275. if (params.dateSplit) {
  5276. config.cols.push({
  5277. divider: true,
  5278. content: params.dateSplit
  5279. })
  5280. }
  5281. config.cols.push({
  5282. divider: true,
  5283. content: params.datetimeSplit
  5284. })
  5285. var times = self.params.times();
  5286. if (times && times.length) {
  5287. config.cols = config.cols.concat(times);
  5288. }
  5289. var inputValue = this.input.val();
  5290. if(inputValue) config.value = params.parse(inputValue);
  5291. if(this.params.value) {
  5292. this.input.val(this.params.value);
  5293. config.value = params.parse(this.params.value);
  5294. }
  5295. return config;
  5296. }
  5297. }
  5298. $.fn.datetimePicker = function(params) {
  5299. params = $.extend({}, defaults, params);
  5300. return this.each(function() {
  5301. if(!this) return;
  5302. var $this = $(this);
  5303. var datetime = $this.data("datetime");
  5304. if(!datetime) $this.data("datetime", new Datetime(this, params));
  5305. return datetime;
  5306. });
  5307. };
  5308. defaults = $.fn.datetimePicker.prototype.defaults = {
  5309. input: undefined, // 默认值
  5310. min: undefined, // YYYY-MM-DD 最大最小值只比较年月日,不比较时分秒
  5311. max: undefined, // YYYY-MM-DD
  5312. yearSplit: '-',
  5313. monthSplit: '-',
  5314. dateSplit: '', // 默认为空
  5315. datetimeSplit: ' ', // 日期和时间之间的分隔符,不可为空
  5316. monthes: ('01 02 03 04 05 06 07 08 09 10 11 12').split(' '),
  5317. years: (function () {
  5318. var arr = [];
  5319. for (var i = 1950; i <= 2030; i++) { arr.push(i); }
  5320. return arr;
  5321. })(),
  5322. times: function () {
  5323. return [ // 自定义的时间
  5324. {
  5325. values: (function () {
  5326. var hours = [];
  5327. for (var i=0; i<24; i++) hours.push(formatNumber(i));
  5328. return hours;
  5329. })()
  5330. },
  5331. {
  5332. divider: true, // 这是一个分隔符
  5333. content: ':'
  5334. },
  5335. {
  5336. values: (function () {
  5337. var minutes = [];
  5338. for (var i=0; i<60; i++) minutes.push(formatNumber(i));
  5339. return minutes;
  5340. })()
  5341. }
  5342. ];
  5343. },
  5344. format: function (p, values) { // 数组转换成字符串
  5345. return p.cols.map(function (col) {
  5346. return col.value || col.content;
  5347. }).join('');
  5348. },
  5349. parse: function (str) {
  5350. // 把字符串转换成数组,用来解析初始值
  5351. // 如果你的定制的初始值格式无法被这个默认函数解析,请自定义这个函数。比如你的时间是 '子时' 那么默认情况这个'时'会被当做分隔符而导致错误,所以你需要自己定义parse函数
  5352. // 默认兼容的分隔符
  5353. var t = str.split(this.datetimeSplit);
  5354. return t[0].split(/\D/).concat(t[1].split(/:|时|分|秒/)).filter(function (d) {
  5355. return !!d;
  5356. })
  5357. }
  5358. }
  5359. }($);
  5360. /*======================================================
  5361. ************ Picker ************
  5362. ======================================================*/
  5363. /* global $:true */
  5364. + function($) {
  5365. "use strict";
  5366. //Popup 和 picker 之类的不要共用一个弹出方法,因为这样会导致 在 popup 中再弹出 picker 的时候会有问题。
  5367. $.openPopup = function(popup, className) {
  5368. $.closePopup();
  5369. popup = $(popup);
  5370. popup.show();
  5371. popup.width();
  5372. popup.addClass("weui-popup__container--visible");
  5373. var modal = popup.find(".weui-popup__modal");
  5374. modal.width();
  5375. modal.transitionEnd(function() {
  5376. modal.trigger("open");
  5377. });
  5378. }
  5379. $.closePopup = function(container, remove) {
  5380. container = $(container || ".weui-popup__container--visible");
  5381. container.find('.weui-popup__modal').transitionEnd(function() {
  5382. var $this = $(this);
  5383. $this.trigger("close");
  5384. container.hide();
  5385. remove && container.remove();
  5386. })
  5387. container.removeClass("weui-popup__container--visible")
  5388. };
  5389. $(document).on("click", ".close-popup, .weui-popup__overlay", function() {
  5390. $.closePopup();
  5391. })
  5392. .on("click", ".open-popup", function() {
  5393. $($(this).data("target")).popup();
  5394. })
  5395. .on("click", ".weui-popup__container", function(e) {
  5396. if($(e.target).hasClass("weui-popup__container")) $.closePopup();
  5397. })
  5398. $.fn.popup = function() {
  5399. return this.each(function() {
  5400. $.openPopup(this);
  5401. });
  5402. };
  5403. }($);
  5404. /* ===============================================================================
  5405. ************ Notification ************
  5406. =============================================================================== */
  5407. /* global $:true */
  5408. +function ($) {
  5409. "use strict";
  5410. var noti, defaults, timeout, start, diffX, diffY;
  5411. var touchStart = function(e) {
  5412. var p = $.getTouchPosition(e);
  5413. start = p;
  5414. diffX = diffY = 0;
  5415. noti.addClass("touching");
  5416. };
  5417. var touchMove = function(e) {
  5418. if(!start) return false;
  5419. e.preventDefault();
  5420. e.stopPropagation();
  5421. var p = $.getTouchPosition(e);
  5422. diffX = p.x - start.x;
  5423. diffY = p.y - start.y;
  5424. if(diffY > 0) {
  5425. diffY = Math.sqrt(diffY);
  5426. }
  5427. noti.css("transform", "translate3d(0, "+diffY+"px, 0)");
  5428. };
  5429. var touchEnd = function() {
  5430. noti.removeClass("touching");
  5431. noti.attr("style", "");
  5432. if(diffY < 0 && (Math.abs(diffY) > noti.height()*0.38)) {
  5433. $.closeNotification();
  5434. }
  5435. if(Math.abs(diffX) <= 1 && Math.abs(diffY) <= 1) {
  5436. noti.trigger("noti-click");
  5437. }
  5438. start = false;
  5439. };
  5440. var attachEvents = function(el) {
  5441. el.on($.touchEvents.start, touchStart);
  5442. el.on($.touchEvents.move, touchMove);
  5443. el.on($.touchEvents.end, touchEnd);
  5444. };
  5445. $.notification = $.noti = function(params) {
  5446. params = $.extend({}, defaults, params);
  5447. noti = $(".weui-notification");
  5448. if(!noti[0]) { // create a new notification
  5449. noti = $('<div class="weui-notification"></div>').appendTo(document.body);
  5450. attachEvents(noti);
  5451. }
  5452. noti.off("noti-click"); //the click event is not correct sometime: it will trigger when user is draging.
  5453. if(params.onClick) noti.on("noti-click", function() {
  5454. params.onClick(params.data);
  5455. });
  5456. noti.html($.t7.compile(params.tpl)(params));
  5457. noti.show();
  5458. noti.addClass("weui-notification--in");
  5459. noti.data("params", params);
  5460. var startTimeout = function() {
  5461. if(timeout) {
  5462. clearTimeout(timeout);
  5463. timeout = null;
  5464. }
  5465. timeout = setTimeout(function() {
  5466. if(noti.hasClass("weui-notification--touching")) {
  5467. startTimeout();
  5468. } else {
  5469. $.closeNotification();
  5470. }
  5471. }, params.time);
  5472. };
  5473. startTimeout();
  5474. };
  5475. $.closeNotification = function() {
  5476. timeout && clearTimeout(timeout);
  5477. timeout = null;
  5478. var noti = $(".weui-notification").removeClass("weui-notification--in").transitionEnd(function() {
  5479. $(this).remove();
  5480. });
  5481. if(noti[0]) {
  5482. var params = $(".weui-notification").data("params");
  5483. if(params && params.onClose) {
  5484. params.onClose(params.data);
  5485. }
  5486. }
  5487. };
  5488. defaults = $.noti.prototype.defaults = {
  5489. title: undefined,
  5490. text: undefined,
  5491. media: undefined,
  5492. time: 4000,
  5493. onClick: undefined,
  5494. onClose: undefined,
  5495. data: undefined,
  5496. tpl: '<div class="weui-notification__inner">' +
  5497. '{{#if media}}<div class="weui-notification__media">{{media}}</div>{{/if}}' +
  5498. '<div class="weui-notification__content">' +
  5499. '{{#if title}}<div class="weui-notification__title">{{title}}</div>{{/if}}' +
  5500. '{{#if text}}<div class="weui-notification__text">{{text}}</div>{{/if}}' +
  5501. '</div>' +
  5502. '<div class="weui-notification__handle-bar"></div>' +
  5503. '</div>'
  5504. };
  5505. }($);
  5506. + function($) {
  5507. "use strict";
  5508. var timeout;
  5509. $.toptip = function(text, duration, type) {
  5510. if(!text) return;
  5511. if(typeof duration === typeof "a") {
  5512. type = duration;
  5513. duration = undefined;
  5514. }
  5515. duration = duration || 3000;
  5516. var className = type ? 'bg-' + type : 'bg-danger';
  5517. var $t = $('.weui-toptips').remove();
  5518. $t = $('<div class="weui-toptips"></div>').appendTo(document.body);
  5519. $t.html(text);
  5520. $t[0].className = 'weui-toptips ' + className
  5521. clearTimeout(timeout);
  5522. if(!$t.hasClass('weui-toptips_visible')) {
  5523. $t.show().width();
  5524. $t.addClass('weui-toptips_visible');
  5525. }
  5526. timeout = setTimeout(function() {
  5527. $t.removeClass('weui-toptips_visible').transitionEnd(function() {
  5528. $t.remove();
  5529. });
  5530. }, duration);
  5531. }
  5532. }($);
  5533. /* global $:true */
  5534. + function($) {
  5535. "use strict";
  5536. var Slider = function (container, arg) {
  5537. this.container = $(container);
  5538. this.handler = this.container.find('.weui-slider__handler')
  5539. this.track = this.container.find('.weui-slider__track')
  5540. this.value = this.container.find('.weui-slider-box__value')
  5541. this.bind()
  5542. if (typeof arg === 'function') {
  5543. this.callback = arg
  5544. }
  5545. }
  5546. Slider.prototype.bind = function () {
  5547. this.container
  5548. .on($.touchEvents.start, $.proxy(this.touchStart, this))
  5549. .on($.touchEvents.end, $.proxy(this.touchEnd, this));
  5550. $(document.body).on($.touchEvents.move, $.proxy(this.touchMove, this)) // move even outside container
  5551. }
  5552. Slider.prototype.touchStart = function (e) {
  5553. e.preventDefault()
  5554. this.start = $.getTouchPosition(e)
  5555. this.width = this.container.find('.weui-slider__inner').width()
  5556. this.left = parseInt(this.container.find('.weui-slider__handler').css('left'))
  5557. this.touching = true
  5558. }
  5559. Slider.prototype.touchMove = function (e) {
  5560. if (!this.touching) return true
  5561. var p = $.getTouchPosition(e)
  5562. var distance = p.x - this.start.x
  5563. var left = distance + this.left
  5564. var per = parseInt(left / this.width * 100)
  5565. if (per < 0) per = 0
  5566. if (per > 100) per = 100
  5567. this.handler.css('left', per + '%')
  5568. this.track.css('width', per + '%')
  5569. this.value.text(per)
  5570. this.callback && this.callback.call(this, per)
  5571. this.container.trigger('change', per)
  5572. }
  5573. Slider.prototype.touchEnd = function (e) {
  5574. this.touching = false
  5575. }
  5576. $.fn.slider = function (arg) {
  5577. this.each(function () {
  5578. var $this = $(this)
  5579. var slider = $this.data('slider')
  5580. if (slider) return slider;
  5581. else $this.data('slider', new Slider(this, arg))
  5582. })
  5583. }
  5584. }($);
  5585. /* ===============================================================================
  5586. ************ Swipeout ************
  5587. =============================================================================== */
  5588. /* global $:true */
  5589. +function ($) {
  5590. "use strict";
  5591. var cache = [];
  5592. var TOUCHING = 'swipeout-touching'
  5593. var Swipeout = function(el) {
  5594. this.container = $(el);
  5595. this.mover = this.container.find('>.weui-cell__bd')
  5596. this.attachEvents();
  5597. cache.push(this)
  5598. }
  5599. Swipeout.prototype.touchStart = function(e) {
  5600. var p = $.getTouchPosition(e);
  5601. this.container.addClass(TOUCHING);
  5602. this.start = p;
  5603. this.startX = 0;
  5604. this.startTime = + new Date;
  5605. var transform = this.mover.css('transform').match(/-?[\d\.]+/g)
  5606. if (transform && transform.length) this.startX = parseInt(transform[4])
  5607. this.diffX = this.diffY = 0;
  5608. this._closeOthers()
  5609. this.limit = this.container.find('>.weui-cell__ft').width() || 68; // 因为有的时候初始化的时候元素是隐藏的(比如在对话框内),所以在touchstart的时候计算宽度而不是初始化的时候
  5610. };
  5611. Swipeout.prototype.touchMove= function(e) {
  5612. if(!this.start) return true;
  5613. var p = $.getTouchPosition(e);
  5614. this.diffX = p.x - this.start.x;
  5615. this.diffY = p.y - this.start.y;
  5616. if (Math.abs(this.diffX) < Math.abs(this.diffY)) { // 说明是上下方向在拖动
  5617. this.close()
  5618. this.start = false
  5619. return true;
  5620. }
  5621. e.preventDefault();
  5622. e.stopPropagation();
  5623. var x = this.diffX + this.startX
  5624. if (x > 0) x = 0;
  5625. if (Math.abs(x) > this.limit) x = - (Math.pow(-(x+this.limit), .7) + this.limit)
  5626. this.mover.css("transform", "translate3d("+x+"px, 0, 0)");
  5627. };
  5628. Swipeout.prototype.touchEnd = function() {
  5629. if (!this.start) return true;
  5630. this.start = false;
  5631. var x = this.diffX + this.startX
  5632. var t = new Date - this.startTime;
  5633. if (this.diffX < -5 && t < 200) { // 向左快速滑动,则打开
  5634. this.open()
  5635. } else if (this.diffX >= 0 && t < 200) { // 向右快速滑动,或者单击,则关闭
  5636. this.close()
  5637. } else if (x > 0 || -x <= this.limit / 2) {
  5638. this.close()
  5639. } else {
  5640. this.open()
  5641. }
  5642. };
  5643. Swipeout.prototype.close = function() {
  5644. this.container.removeClass(TOUCHING);
  5645. this.mover.css("transform", "translate3d(0, 0, 0)");
  5646. this.container.trigger('swipeout-close');
  5647. }
  5648. Swipeout.prototype.open = function() {
  5649. this.container.removeClass(TOUCHING);
  5650. this._closeOthers()
  5651. this.mover.css("transform", "translate3d(" + (-this.limit) + "px, 0, 0)");
  5652. this.container.trigger('swipeout-open');
  5653. }
  5654. Swipeout.prototype.attachEvents = function() {
  5655. var el = this.mover;
  5656. el.on($.touchEvents.start, $.proxy(this.touchStart, this));
  5657. el.on($.touchEvents.move, $.proxy(this.touchMove, this));
  5658. el.on($.touchEvents.end, $.proxy(this.touchEnd, this));
  5659. }
  5660. Swipeout.prototype._closeOthers = function() {
  5661. //close others
  5662. var self = this
  5663. cache.forEach(function (s) {
  5664. if (s !== self) s.close()
  5665. })
  5666. }
  5667. var swipeout = function(el) {
  5668. return new Swipeout(el);
  5669. };
  5670. $.fn.swipeout = function (arg) {
  5671. return this.each(function() {
  5672. var $this = $(this)
  5673. var s = $this.data('swipeout') || swipeout(this);
  5674. $this.data('swipeout', s);
  5675. if (typeof arg === typeof 'a') {
  5676. s[arg]()
  5677. }
  5678. });
  5679. }
  5680. $('.weui-cell_swiped').swipeout() // auto init
  5681. }($);