sm.js 299 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768276927702771277227732774277527762777277827792780278127822783278427852786278727882789279027912792279327942795279627972798279928002801280228032804280528062807280828092810281128122813281428152816281728182819282028212822282328242825282628272828282928302831283228332834283528362837283828392840284128422843284428452846284728482849285028512852285328542855285628572858285928602861286228632864286528662867286828692870287128722873287428752876287728782879288028812882288328842885288628872888288928902891289228932894289528962897289828992900290129022903290429052906290729082909291029112912291329142915291629172918291929202921292229232924292529262927292829292930293129322933293429352936293729382939294029412942294329442945294629472948294929502951295229532954295529562957295829592960296129622963296429652966296729682969297029712972297329742975297629772978297929802981298229832984298529862987298829892990299129922993299429952996299729982999300030013002300330043005300630073008300930103011301230133014301530163017301830193020302130223023302430253026302730283029303030313032303330343035303630373038303930403041304230433044304530463047304830493050305130523053305430553056305730583059306030613062306330643065306630673068306930703071307230733074307530763077307830793080308130823083308430853086308730883089309030913092309330943095309630973098309931003101310231033104310531063107310831093110311131123113311431153116311731183119312031213122312331243125312631273128312931303131313231333134313531363137313831393140314131423143314431453146314731483149315031513152315331543155315631573158315931603161316231633164316531663167316831693170317131723173317431753176317731783179318031813182318331843185318631873188318931903191319231933194319531963197319831993200320132023203320432053206320732083209321032113212321332143215321632173218321932203221322232233224322532263227322832293230323132323233323432353236323732383239324032413242324332443245324632473248324932503251325232533254325532563257325832593260326132623263326432653266326732683269327032713272327332743275327632773278327932803281328232833284328532863287328832893290329132923293329432953296329732983299330033013302330333043305330633073308330933103311331233133314331533163317331833193320332133223323332433253326332733283329333033313332333333343335333633373338333933403341334233433344334533463347334833493350335133523353335433553356335733583359336033613362336333643365336633673368336933703371337233733374337533763377337833793380338133823383338433853386338733883389339033913392339333943395339633973398339934003401340234033404340534063407340834093410341134123413341434153416341734183419342034213422342334243425342634273428342934303431343234333434343534363437343834393440344134423443344434453446344734483449345034513452345334543455345634573458345934603461346234633464346534663467346834693470347134723473347434753476347734783479348034813482348334843485348634873488348934903491349234933494349534963497349834993500350135023503350435053506350735083509351035113512351335143515351635173518351935203521352235233524352535263527352835293530353135323533353435353536353735383539354035413542354335443545354635473548354935503551355235533554355535563557355835593560356135623563356435653566356735683569357035713572357335743575357635773578357935803581358235833584358535863587358835893590359135923593359435953596359735983599360036013602360336043605360636073608360936103611361236133614361536163617361836193620362136223623362436253626362736283629363036313632363336343635363636373638363936403641364236433644364536463647364836493650365136523653365436553656365736583659366036613662366336643665366636673668366936703671367236733674367536763677367836793680368136823683368436853686368736883689369036913692369336943695369636973698369937003701370237033704370537063707370837093710371137123713371437153716371737183719372037213722372337243725372637273728372937303731373237333734373537363737373837393740374137423743374437453746374737483749375037513752375337543755375637573758375937603761376237633764376537663767376837693770377137723773377437753776377737783779378037813782378337843785378637873788378937903791379237933794379537963797379837993800380138023803380438053806380738083809381038113812381338143815381638173818381938203821382238233824382538263827382838293830383138323833383438353836383738383839384038413842384338443845384638473848384938503851385238533854385538563857385838593860386138623863386438653866386738683869387038713872387338743875387638773878387938803881388238833884388538863887388838893890389138923893389438953896389738983899390039013902390339043905390639073908390939103911391239133914391539163917391839193920392139223923392439253926392739283929393039313932393339343935393639373938393939403941394239433944394539463947394839493950395139523953395439553956395739583959396039613962396339643965396639673968396939703971397239733974397539763977397839793980398139823983398439853986398739883989399039913992399339943995399639973998399940004001400240034004400540064007400840094010401140124013401440154016401740184019402040214022402340244025402640274028402940304031403240334034403540364037403840394040404140424043404440454046404740484049405040514052405340544055405640574058405940604061406240634064406540664067406840694070407140724073407440754076407740784079408040814082408340844085408640874088408940904091409240934094409540964097409840994100410141024103410441054106410741084109411041114112411341144115411641174118411941204121412241234124412541264127412841294130413141324133413441354136413741384139414041414142414341444145414641474148414941504151415241534154415541564157415841594160416141624163416441654166416741684169417041714172417341744175417641774178417941804181418241834184418541864187418841894190419141924193419441954196419741984199420042014202420342044205420642074208420942104211421242134214421542164217421842194220422142224223422442254226422742284229423042314232423342344235423642374238423942404241424242434244424542464247424842494250425142524253425442554256425742584259426042614262426342644265426642674268426942704271427242734274427542764277427842794280428142824283428442854286428742884289429042914292429342944295429642974298429943004301430243034304430543064307430843094310431143124313431443154316431743184319432043214322432343244325432643274328432943304331433243334334433543364337433843394340434143424343434443454346434743484349435043514352435343544355435643574358435943604361436243634364436543664367436843694370437143724373437443754376437743784379438043814382438343844385438643874388438943904391439243934394439543964397439843994400440144024403440444054406440744084409441044114412441344144415441644174418441944204421442244234424442544264427442844294430443144324433443444354436443744384439444044414442444344444445444644474448444944504451445244534454445544564457445844594460446144624463446444654466446744684469447044714472447344744475447644774478447944804481448244834484448544864487448844894490449144924493449444954496449744984499450045014502450345044505450645074508450945104511451245134514451545164517451845194520452145224523452445254526452745284529453045314532453345344535453645374538453945404541454245434544454545464547454845494550455145524553455445554556455745584559456045614562456345644565456645674568456945704571457245734574457545764577457845794580458145824583458445854586458745884589459045914592459345944595459645974598459946004601460246034604460546064607460846094610461146124613461446154616461746184619462046214622462346244625462646274628462946304631463246334634463546364637463846394640464146424643464446454646464746484649465046514652465346544655465646574658465946604661466246634664466546664667466846694670467146724673467446754676467746784679468046814682468346844685468646874688468946904691469246934694469546964697469846994700470147024703470447054706470747084709471047114712471347144715471647174718471947204721472247234724472547264727472847294730473147324733473447354736473747384739474047414742474347444745474647474748474947504751475247534754475547564757475847594760476147624763476447654766476747684769477047714772477347744775477647774778477947804781478247834784478547864787478847894790479147924793479447954796479747984799480048014802480348044805480648074808480948104811481248134814481548164817481848194820482148224823482448254826482748284829483048314832483348344835483648374838483948404841484248434844484548464847484848494850485148524853485448554856485748584859486048614862486348644865486648674868486948704871487248734874487548764877487848794880488148824883488448854886488748884889489048914892489348944895489648974898489949004901490249034904490549064907490849094910491149124913491449154916491749184919492049214922492349244925492649274928492949304931493249334934493549364937493849394940494149424943494449454946494749484949495049514952495349544955495649574958495949604961496249634964496549664967496849694970497149724973497449754976497749784979498049814982498349844985498649874988498949904991499249934994499549964997499849995000500150025003500450055006500750085009501050115012501350145015501650175018501950205021502250235024502550265027502850295030503150325033503450355036503750385039504050415042504350445045504650475048504950505051505250535054505550565057505850595060506150625063506450655066506750685069507050715072507350745075507650775078507950805081508250835084508550865087508850895090509150925093509450955096509750985099510051015102510351045105510651075108510951105111511251135114511551165117511851195120512151225123512451255126512751285129513051315132513351345135513651375138513951405141514251435144514551465147514851495150515151525153515451555156515751585159516051615162516351645165516651675168516951705171517251735174517551765177517851795180518151825183518451855186518751885189519051915192519351945195519651975198519952005201520252035204520552065207520852095210521152125213521452155216521752185219522052215222522352245225522652275228522952305231523252335234523552365237523852395240524152425243524452455246524752485249525052515252525352545255525652575258525952605261526252635264526552665267526852695270527152725273527452755276527752785279528052815282528352845285528652875288528952905291529252935294529552965297529852995300530153025303530453055306530753085309531053115312531353145315531653175318531953205321532253235324532553265327532853295330533153325333533453355336533753385339534053415342534353445345534653475348534953505351535253535354535553565357535853595360536153625363536453655366536753685369537053715372537353745375537653775378537953805381538253835384538553865387538853895390539153925393539453955396539753985399540054015402540354045405540654075408540954105411541254135414541554165417541854195420542154225423542454255426542754285429543054315432543354345435543654375438543954405441544254435444544554465447544854495450545154525453545454555456545754585459546054615462546354645465546654675468546954705471547254735474547554765477547854795480548154825483548454855486548754885489549054915492549354945495549654975498549955005501550255035504550555065507550855095510551155125513551455155516551755185519552055215522552355245525552655275528552955305531553255335534553555365537553855395540554155425543554455455546554755485549555055515552555355545555555655575558555955605561556255635564556555665567556855695570557155725573557455755576557755785579558055815582558355845585558655875588558955905591559255935594559555965597559855995600560156025603560456055606560756085609561056115612561356145615561656175618561956205621562256235624562556265627562856295630563156325633563456355636563756385639564056415642564356445645564656475648564956505651565256535654565556565657565856595660566156625663566456655666566756685669567056715672567356745675567656775678567956805681568256835684568556865687568856895690569156925693569456955696569756985699570057015702570357045705570657075708570957105711571257135714571557165717571857195720572157225723572457255726572757285729573057315732573357345735573657375738573957405741574257435744574557465747574857495750575157525753575457555756575757585759576057615762576357645765576657675768576957705771577257735774577557765777577857795780578157825783578457855786578757885789579057915792579357945795579657975798579958005801580258035804580558065807580858095810581158125813581458155816581758185819582058215822582358245825582658275828582958305831583258335834583558365837583858395840584158425843584458455846584758485849585058515852585358545855585658575858585958605861586258635864586558665867586858695870587158725873587458755876587758785879588058815882588358845885588658875888588958905891589258935894589558965897589858995900590159025903590459055906590759085909591059115912591359145915591659175918591959205921592259235924592559265927592859295930593159325933593459355936593759385939594059415942594359445945594659475948594959505951595259535954595559565957595859595960596159625963596459655966596759685969597059715972597359745975597659775978597959805981598259835984598559865987598859895990599159925993599459955996599759985999600060016002600360046005600660076008600960106011601260136014601560166017601860196020602160226023602460256026602760286029603060316032603360346035603660376038603960406041604260436044604560466047604860496050605160526053605460556056605760586059606060616062606360646065606660676068606960706071607260736074607560766077607860796080608160826083608460856086608760886089609060916092609360946095609660976098609961006101610261036104610561066107610861096110611161126113611461156116611761186119612061216122612361246125612661276128612961306131613261336134613561366137613861396140614161426143614461456146614761486149615061516152615361546155615661576158615961606161616261636164616561666167616861696170617161726173617461756176617761786179618061816182618361846185618661876188618961906191619261936194619561966197619861996200620162026203620462056206620762086209621062116212621362146215621662176218621962206221622262236224622562266227622862296230623162326233623462356236623762386239624062416242624362446245624662476248624962506251625262536254625562566257625862596260626162626263626462656266626762686269627062716272627362746275627662776278627962806281628262836284628562866287628862896290629162926293629462956296629762986299630063016302630363046305630663076308630963106311631263136314631563166317631863196320632163226323632463256326632763286329633063316332633363346335633663376338633963406341634263436344634563466347634863496350635163526353635463556356635763586359636063616362636363646365636663676368636963706371637263736374637563766377637863796380638163826383638463856386638763886389639063916392639363946395639663976398639964006401640264036404640564066407640864096410641164126413641464156416641764186419642064216422642364246425642664276428642964306431643264336434643564366437643864396440644164426443644464456446644764486449645064516452645364546455645664576458645964606461646264636464646564666467646864696470647164726473647464756476647764786479648064816482648364846485648664876488648964906491649264936494649564966497649864996500650165026503650465056506650765086509651065116512651365146515651665176518651965206521652265236524652565266527652865296530653165326533653465356536653765386539654065416542654365446545654665476548654965506551655265536554655565566557655865596560656165626563656465656566656765686569657065716572657365746575657665776578657965806581658265836584658565866587658865896590659165926593659465956596659765986599660066016602660366046605660666076608660966106611661266136614661566166617661866196620662166226623662466256626662766286629663066316632663366346635663666376638663966406641664266436644664566466647664866496650665166526653665466556656665766586659666066616662666366646665666666676668666966706671667266736674667566766677667866796680668166826683668466856686668766886689669066916692669366946695669666976698669967006701670267036704670567066707670867096710671167126713671467156716671767186719672067216722672367246725672667276728672967306731673267336734673567366737673867396740674167426743674467456746674767486749675067516752675367546755675667576758675967606761676267636764676567666767676867696770677167726773677467756776677767786779678067816782678367846785678667876788678967906791679267936794679567966797679867996800680168026803680468056806680768086809681068116812681368146815681668176818681968206821682268236824682568266827682868296830683168326833683468356836683768386839684068416842684368446845684668476848684968506851685268536854685568566857685868596860686168626863686468656866686768686869687068716872687368746875687668776878687968806881688268836884688568866887688868896890689168926893689468956896689768986899690069016902690369046905690669076908690969106911691269136914691569166917691869196920692169226923692469256926692769286929693069316932693369346935693669376938693969406941694269436944694569466947694869496950695169526953695469556956695769586959696069616962696369646965696669676968696969706971697269736974697569766977697869796980698169826983698469856986698769886989699069916992699369946995699669976998699970007001700270037004700570067007700870097010701170127013701470157016701770187019702070217022702370247025702670277028702970307031703270337034703570367037703870397040704170427043704470457046704770487049705070517052705370547055705670577058705970607061706270637064706570667067706870697070707170727073707470757076707770787079708070817082708370847085708670877088708970907091709270937094709570967097709870997100710171027103710471057106710771087109711071117112711371147115711671177118711971207121712271237124712571267127712871297130713171327133713471357136713771387139714071417142714371447145714671477148714971507151715271537154715571567157715871597160716171627163716471657166716771687169717071717172717371747175717671777178717971807181718271837184718571867187718871897190719171927193719471957196719771987199720072017202720372047205720672077208720972107211721272137214721572167217721872197220722172227223722472257226722772287229723072317232723372347235723672377238723972407241724272437244724572467247724872497250725172527253725472557256725772587259726072617262726372647265726672677268726972707271727272737274727572767277727872797280728172827283728472857286728772887289729072917292729372947295729672977298729973007301730273037304730573067307730873097310731173127313731473157316731773187319732073217322732373247325732673277328732973307331733273337334733573367337733873397340734173427343734473457346734773487349735073517352735373547355735673577358735973607361736273637364736573667367736873697370737173727373737473757376737773787379738073817382738373847385738673877388738973907391739273937394739573967397739873997400740174027403740474057406740774087409741074117412741374147415741674177418741974207421742274237424742574267427742874297430743174327433743474357436743774387439744074417442744374447445744674477448744974507451745274537454745574567457745874597460746174627463746474657466746774687469747074717472747374747475747674777478747974807481748274837484748574867487748874897490749174927493749474957496749774987499750075017502750375047505750675077508750975107511751275137514751575167517751875197520752175227523752475257526752775287529753075317532753375347535753675377538753975407541754275437544754575467547754875497550755175527553755475557556755775587559756075617562756375647565756675677568756975707571757275737574757575767577757875797580758175827583758475857586758775887589759075917592759375947595759675977598759976007601760276037604760576067607760876097610761176127613761476157616761776187619762076217622762376247625762676277628762976307631763276337634763576367637763876397640764176427643764476457646764776487649765076517652765376547655765676577658765976607661766276637664766576667667766876697670767176727673767476757676767776787679768076817682768376847685768676877688768976907691769276937694769576967697769876997700770177027703770477057706770777087709771077117712771377147715771677177718771977207721772277237724772577267727772877297730773177327733773477357736773777387739774077417742774377447745774677477748774977507751775277537754775577567757775877597760776177627763776477657766776777687769777077717772777377747775777677777778777977807781778277837784778577867787778877897790779177927793779477957796779777987799780078017802780378047805780678077808780978107811781278137814781578167817781878197820782178227823782478257826782778287829783078317832783378347835783678377838783978407841784278437844784578467847784878497850785178527853785478557856785778587859786078617862786378647865786678677868786978707871787278737874787578767877787878797880788178827883788478857886788778887889789078917892789378947895789678977898789979007901790279037904790579067907790879097910791179127913791479157916791779187919792079217922792379247925792679277928792979307931793279337934793579367937793879397940794179427943794479457946794779487949795079517952795379547955795679577958795979607961796279637964796579667967796879697970797179727973797479757976797779787979798079817982798379847985798679877988798979907991799279937994799579967997799879998000800180028003800480058006800780088009801080118012801380148015801680178018801980208021802280238024802580268027802880298030803180328033803480358036803780388039804080418042804380448045804680478048804980508051805280538054805580568057805880598060806180628063806480658066806780688069807080718072807380748075807680778078807980808081808280838084808580868087808880898090809180928093809480958096809780988099810081018102
  1. /*!
  2. * =====================================================
  3. * SUI Mobile - http://m.sui.taobao.org/
  4. *
  5. * =====================================================
  6. */
  7. $.smVersion = "0.6.2";+function ($) {
  8. "use strict";
  9. //全局配置
  10. var defaults = {
  11. autoInit: false, //自动初始化页面
  12. showPageLoadingIndicator: true, //push.js加载页面的时候显示一个加载提示
  13. router: true, //默认使用router
  14. swipePanel: "left", //滑动打开侧栏
  15. swipePanelOnlyClose: true //只允许滑动关闭,不允许滑动打开侧栏
  16. };
  17. $.smConfig = $.extend(defaults, $.config);
  18. }(Zepto);
  19. + function($) {
  20. "use strict";
  21. //比较一个字符串版本号
  22. //a > b === 1
  23. //a = b === 0
  24. //a < b === -1
  25. $.compareVersion = function(a, b) {
  26. var as = a.split('.');
  27. var bs = b.split('.');
  28. if (a === b) return 0;
  29. for (var i = 0; i < as.length; i++) {
  30. var x = parseInt(as[i]);
  31. if (!bs[i]) return 1;
  32. var y = parseInt(bs[i]);
  33. if (x < y) return -1;
  34. if (x > y) return 1;
  35. }
  36. return -1;
  37. };
  38. $.getCurrentPage = function() {
  39. return $(".page-current")[0] || $(".page")[0] || document.body;
  40. };
  41. }(Zepto);
  42. /* global WebKitCSSMatrix:true */
  43. (function($) {
  44. "use strict";
  45. ['width', 'height'].forEach(function(dimension) {
  46. var Dimension = dimension.replace(/./, function(m) {
  47. return m[0].toUpperCase();
  48. });
  49. $.fn['outer' + Dimension] = function(margin) {
  50. var elem = this;
  51. if (elem) {
  52. var size = elem[dimension]();
  53. var sides = {
  54. 'width': ['left', 'right'],
  55. 'height': ['top', 'bottom']
  56. };
  57. sides[dimension].forEach(function(side) {
  58. if (margin) size += parseInt(elem.css('margin-' + side), 10);
  59. });
  60. return size;
  61. } else {
  62. return null;
  63. }
  64. };
  65. });
  66. //support
  67. $.support = (function() {
  68. var support = {
  69. touch: !!(('ontouchstart' in window) || window.DocumentTouch && document instanceof window.DocumentTouch)
  70. };
  71. return support;
  72. })();
  73. $.touchEvents = {
  74. start: $.support.touch ? 'touchstart' : 'mousedown',
  75. move: $.support.touch ? 'touchmove' : 'mousemove',
  76. end: $.support.touch ? 'touchend' : 'mouseup'
  77. };
  78. $.getTranslate = function (el, axis) {
  79. var matrix, curTransform, curStyle, transformMatrix;
  80. // automatic axis detection
  81. if (typeof axis === 'undefined') {
  82. axis = 'x';
  83. }
  84. curStyle = window.getComputedStyle(el, null);
  85. if (window.WebKitCSSMatrix) {
  86. // Some old versions of Webkit choke when 'none' is passed; pass
  87. // empty string instead in this case
  88. transformMatrix = new WebKitCSSMatrix(curStyle.webkitTransform === 'none' ? '' : curStyle.webkitTransform);
  89. }
  90. else {
  91. transformMatrix = curStyle.MozTransform || curStyle.transform || curStyle.getPropertyValue('transform').replace('translate(', 'matrix(1, 0, 0, 1,');
  92. matrix = transformMatrix.toString().split(',');
  93. }
  94. if (axis === 'x') {
  95. //Latest Chrome and webkits Fix
  96. if (window.WebKitCSSMatrix)
  97. curTransform = transformMatrix.m41;
  98. //Crazy IE10 Matrix
  99. else if (matrix.length === 16)
  100. curTransform = parseFloat(matrix[12]);
  101. //Normal Browsers
  102. else
  103. curTransform = parseFloat(matrix[4]);
  104. }
  105. if (axis === 'y') {
  106. //Latest Chrome and webkits Fix
  107. if (window.WebKitCSSMatrix)
  108. curTransform = transformMatrix.m42;
  109. //Crazy IE10 Matrix
  110. else if (matrix.length === 16)
  111. curTransform = parseFloat(matrix[13]);
  112. //Normal Browsers
  113. else
  114. curTransform = parseFloat(matrix[5]);
  115. }
  116. return curTransform || 0;
  117. };
  118. /* jshint ignore:start */
  119. $.requestAnimationFrame = function (callback) {
  120. if (window.requestAnimationFrame) return window.requestAnimationFrame(callback);
  121. else if (window.webkitRequestAnimationFrame) return window.webkitRequestAnimationFrame(callback);
  122. else if (window.mozRequestAnimationFrame) return window.mozRequestAnimationFrame(callback);
  123. else {
  124. return window.setTimeout(callback, 1000 / 60);
  125. }
  126. };
  127. $.cancelAnimationFrame = function (id) {
  128. if (window.cancelAnimationFrame) return window.cancelAnimationFrame(id);
  129. else if (window.webkitCancelAnimationFrame) return window.webkitCancelAnimationFrame(id);
  130. else if (window.mozCancelAnimationFrame) return window.mozCancelAnimationFrame(id);
  131. else {
  132. return window.clearTimeout(id);
  133. }
  134. };
  135. /* jshint ignore:end */
  136. $.fn.dataset = function() {
  137. var dataset = {},
  138. ds = this[0].dataset;
  139. for (var key in ds) { // jshint ignore:line
  140. var item = (dataset[key] = ds[key]);
  141. if (item === 'false') dataset[key] = false;
  142. else if (item === 'true') dataset[key] = true;
  143. else if (parseFloat(item) === item * 1) dataset[key] = item * 1;
  144. }
  145. // mixin dataset and __eleData
  146. return $.extend({}, dataset, this[0].__eleData);
  147. };
  148. $.fn.data = function(key, value) {
  149. var tmpData = $(this).dataset();
  150. if (!key) {
  151. return tmpData;
  152. }
  153. // value may be 0, false, null
  154. if (typeof value === 'undefined') {
  155. // Get value
  156. var dataVal = tmpData[key],
  157. __eD = this[0].__eleData;
  158. //if (dataVal !== undefined) {
  159. if (__eD && (key in __eD)) {
  160. return __eD[key];
  161. } else {
  162. return dataVal;
  163. }
  164. } else {
  165. // Set value,uniformly set in extra ```__eleData```
  166. for (var i = 0; i < this.length; i++) {
  167. var el = this[i];
  168. // delete multiple data in dataset
  169. if (key in tmpData) delete el.dataset[key];
  170. if (!el.__eleData) el.__eleData = {};
  171. el.__eleData[key] = value;
  172. }
  173. return this;
  174. }
  175. };
  176. function __dealCssEvent(eventNameArr, callback) {
  177. var events = eventNameArr,
  178. i, dom = this;// jshint ignore:line
  179. function fireCallBack(e) {
  180. /*jshint validthis:true */
  181. if (e.target !== this) return;
  182. callback.call(this, e);
  183. for (i = 0; i < events.length; i++) {
  184. dom.off(events[i], fireCallBack);
  185. }
  186. }
  187. if (callback) {
  188. for (i = 0; i < events.length; i++) {
  189. dom.on(events[i], fireCallBack);
  190. }
  191. }
  192. }
  193. $.fn.animationEnd = function(callback) {
  194. __dealCssEvent.call(this, ['webkitAnimationEnd', 'animationend'], callback);
  195. return this;
  196. };
  197. $.fn.transitionEnd = function(callback) {
  198. __dealCssEvent.call(this, ['webkitTransitionEnd', 'transitionend'], callback);
  199. return this;
  200. };
  201. $.fn.transition = function(duration) {
  202. if (typeof duration !== 'string') {
  203. duration = duration + 'ms';
  204. }
  205. for (var i = 0; i < this.length; i++) {
  206. var elStyle = this[i].style;
  207. elStyle.webkitTransitionDuration = elStyle.MozTransitionDuration = elStyle.transitionDuration = duration;
  208. }
  209. return this;
  210. };
  211. $.fn.transform = function(transform) {
  212. for (var i = 0; i < this.length; i++) {
  213. var elStyle = this[i].style;
  214. elStyle.webkitTransform = elStyle.MozTransform = elStyle.transform = transform;
  215. }
  216. return this;
  217. };
  218. $.fn.prevAll = function (selector) {
  219. var prevEls = [];
  220. var el = this[0];
  221. if (!el) return $([]);
  222. while (el.previousElementSibling) {
  223. var prev = el.previousElementSibling;
  224. if (selector) {
  225. if($(prev).is(selector)) prevEls.push(prev);
  226. }
  227. else prevEls.push(prev);
  228. el = prev;
  229. }
  230. return $(prevEls);
  231. };
  232. $.fn.nextAll = function (selector) {
  233. var nextEls = [];
  234. var el = this[0];
  235. if (!el) return $([]);
  236. while (el.nextElementSibling) {
  237. var next = el.nextElementSibling;
  238. if (selector) {
  239. if($(next).is(selector)) nextEls.push(next);
  240. }
  241. else nextEls.push(next);
  242. el = next;
  243. }
  244. return $(nextEls);
  245. };
  246. //重置zepto的show方法,防止有些人引用的版本中 show 方法操作 opacity 属性影响动画执行
  247. $.fn.show = function(){
  248. var elementDisplay = {};
  249. function defaultDisplay(nodeName) {
  250. var element, display;
  251. if (!elementDisplay[nodeName]) {
  252. element = document.createElement(nodeName);
  253. document.body.appendChild(element);
  254. display = getComputedStyle(element, '').getPropertyValue("display");
  255. element.parentNode.removeChild(element);
  256. display === "none" && (display = "block");
  257. elementDisplay[nodeName] = display;
  258. }
  259. return elementDisplay[nodeName];
  260. }
  261. return this.each(function(){
  262. this.style.display === "none" && (this.style.display = '');
  263. if (getComputedStyle(this, '').getPropertyValue("display") === "none");
  264. this.style.display = defaultDisplay(this.nodeName);
  265. });
  266. };
  267. })(Zepto);
  268. /*===========================
  269. Device/OS Detection
  270. ===========================*/
  271. ;(function ($) {
  272. "use strict";
  273. var device = {};
  274. var ua = navigator.userAgent;
  275. var android = ua.match(/(Android);?[\s\/]+([\d.]+)?/);
  276. var ipad = ua.match(/(iPad).*OS\s([\d_]+)/);
  277. var ipod = ua.match(/(iPod)(.*OS\s([\d_]+))?/);
  278. var iphone = !ipad && ua.match(/(iPhone\sOS)\s([\d_]+)/);
  279. device.ios = device.android = device.iphone = device.ipad = device.androidChrome = false;
  280. // Android
  281. if (android) {
  282. device.os = 'android';
  283. device.osVersion = android[2];
  284. device.android = true;
  285. device.androidChrome = ua.toLowerCase().indexOf('chrome') >= 0;
  286. }
  287. if (ipad || iphone || ipod) {
  288. device.os = 'ios';
  289. device.ios = true;
  290. }
  291. // iOS
  292. if (iphone && !ipod) {
  293. device.osVersion = iphone[2].replace(/_/g, '.');
  294. device.iphone = true;
  295. }
  296. if (ipad) {
  297. device.osVersion = ipad[2].replace(/_/g, '.');
  298. device.ipad = true;
  299. }
  300. if (ipod) {
  301. device.osVersion = ipod[3] ? ipod[3].replace(/_/g, '.') : null;
  302. device.iphone = true;
  303. }
  304. // iOS 8+ changed UA
  305. if (device.ios && device.osVersion && ua.indexOf('Version/') >= 0) {
  306. if (device.osVersion.split('.')[0] === '10') {
  307. device.osVersion = ua.toLowerCase().split('version/')[1].split(' ')[0];
  308. }
  309. }
  310. // Webview
  311. device.webView = (iphone || ipad || ipod) && ua.match(/.*AppleWebKit(?!.*Safari)/i);
  312. // Minimal UI
  313. if (device.os && device.os === 'ios') {
  314. var osVersionArr = device.osVersion.split('.');
  315. device.minimalUi = !device.webView &&
  316. (ipod || iphone) &&
  317. (osVersionArr[0] * 1 === 7 ? osVersionArr[1] * 1 >= 1 : osVersionArr[0] * 1 > 7) &&
  318. $('meta[name="viewport"]').length > 0 && $('meta[name="viewport"]').attr('content').indexOf('minimal-ui') >= 0;
  319. }
  320. // Check for status bar and fullscreen app mode
  321. var windowWidth = $(window).width();
  322. var windowHeight = $(window).height();
  323. device.statusBar = false;
  324. if (device.webView && (windowWidth * windowHeight === screen.width * screen.height)) {
  325. device.statusBar = true;
  326. }
  327. else {
  328. device.statusBar = false;
  329. }
  330. // Classes
  331. var classNames = [];
  332. // Pixel Ratio
  333. device.pixelRatio = window.devicePixelRatio || 1;
  334. classNames.push('pixel-ratio-' + Math.floor(device.pixelRatio));
  335. if (device.pixelRatio >= 2) {
  336. classNames.push('retina');
  337. }
  338. // OS classes
  339. if (device.os) {
  340. classNames.push(device.os, device.os + '-' + device.osVersion.split('.')[0], device.os + '-' + device.osVersion.replace(/\./g, '-'));
  341. if (device.os === 'ios') {
  342. var major = parseInt(device.osVersion.split('.')[0], 10);
  343. for (var i = major - 1; i >= 6; i--) {
  344. classNames.push('ios-gt-' + i);
  345. }
  346. }
  347. }
  348. // Status bar classes
  349. if (device.statusBar) {
  350. classNames.push('with-statusbar-overlay');
  351. }
  352. else {
  353. $('html').removeClass('with-statusbar-overlay');
  354. }
  355. // Add html classes
  356. if (classNames.length > 0) $('html').addClass(classNames.join(' '));
  357. // keng..
  358. device.isWeixin = /MicroMessenger/i.test(ua);
  359. $.device = device;
  360. })(Zepto);
  361. ;(function () {
  362. 'use strict';
  363. /**
  364. * @preserve FastClick: polyfill to remove click delays on browsers with touch UIs.
  365. *
  366. * @codingstandard ftlabs-jsv2
  367. * @copyright The Financial Times Limited [All Rights Reserved]
  368. * @license MIT License (see LICENSE.txt)
  369. */
  370. /*jslint browser:true, node:true, elision:true*/
  371. /*global Event, Node*/
  372. /**
  373. * Instantiate fast-clicking listeners on the specified layer.
  374. *
  375. * @constructor
  376. * @param {Element} layer The layer to listen on
  377. * @param {Object} [options={}] The options to override the defaults
  378. */
  379. function FastClick(layer, options) {
  380. var oldOnClick;
  381. options = options || {};
  382. /**
  383. * Whether a click is currently being tracked.
  384. *
  385. * @type boolean
  386. */
  387. this.trackingClick = false;
  388. /**
  389. * Timestamp for when click tracking started.
  390. *
  391. * @type number
  392. */
  393. this.trackingClickStart = 0;
  394. /**
  395. * The element being tracked for a click.
  396. *
  397. * @type EventTarget
  398. */
  399. this.targetElement = null;
  400. /**
  401. * X-coordinate of touch start event.
  402. *
  403. * @type number
  404. */
  405. this.touchStartX = 0;
  406. /**
  407. * Y-coordinate of touch start event.
  408. *
  409. * @type number
  410. */
  411. this.touchStartY = 0;
  412. /**
  413. * ID of the last touch, retrieved from Touch.identifier.
  414. *
  415. * @type number
  416. */
  417. this.lastTouchIdentifier = 0;
  418. /**
  419. * Touchmove boundary, beyond which a click will be cancelled.
  420. *
  421. * @type number
  422. */
  423. this.touchBoundary = options.touchBoundary || 10;
  424. /**
  425. * The FastClick layer.
  426. *
  427. * @type Element
  428. */
  429. this.layer = layer;
  430. /**
  431. * The minimum time between tap(touchstart and touchend) events
  432. *
  433. * @type number
  434. */
  435. this.tapDelay = options.tapDelay || 200;
  436. /**
  437. * The maximum time for a tap
  438. *
  439. * @type number
  440. */
  441. this.tapTimeout = options.tapTimeout || 700;
  442. if (FastClick.notNeeded(layer)) {
  443. return;
  444. }
  445. // Some old versions of Android don't have Function.prototype.bind
  446. function bind(method, context) {
  447. return function() { return method.apply(context, arguments); };
  448. }
  449. var methods = ['onMouse', 'onClick', 'onTouchStart', 'onTouchMove', 'onTouchEnd', 'onTouchCancel'];
  450. var context = this;
  451. for (var i = 0, l = methods.length; i < l; i++) {
  452. context[methods[i]] = bind(context[methods[i]], context);
  453. }
  454. // Set up event handlers as required
  455. if (deviceIsAndroid) {
  456. layer.addEventListener('mouseover', this.onMouse, true);
  457. layer.addEventListener('mousedown', this.onMouse, true);
  458. layer.addEventListener('mouseup', this.onMouse, true);
  459. }
  460. layer.addEventListener('click', this.onClick, true);
  461. layer.addEventListener('touchstart', this.onTouchStart, false);
  462. layer.addEventListener('touchmove', this.onTouchMove, false);
  463. layer.addEventListener('touchend', this.onTouchEnd, false);
  464. layer.addEventListener('touchcancel', this.onTouchCancel, false);
  465. // Hack is required for browsers that don't support Event#stopImmediatePropagation (e.g. Android 2)
  466. // which is how FastClick normally stops click events bubbling to callbacks registered on the FastClick
  467. // layer when they are cancelled.
  468. if (!Event.prototype.stopImmediatePropagation) {
  469. layer.removeEventListener = function(type, callback, capture) {
  470. var rmv = Node.prototype.removeEventListener;
  471. if (type === 'click') {
  472. rmv.call(layer, type, callback.hijacked || callback, capture);
  473. } else {
  474. rmv.call(layer, type, callback, capture);
  475. }
  476. };
  477. layer.addEventListener = function(type, callback, capture) {
  478. var adv = Node.prototype.addEventListener;
  479. if (type === 'click') {
  480. adv.call(layer, type, callback.hijacked || (callback.hijacked = function(event) {
  481. if (!event.propagationStopped) {
  482. callback(event);
  483. }
  484. }), capture);
  485. } else {
  486. adv.call(layer, type, callback, capture);
  487. }
  488. };
  489. }
  490. // If a handler is already declared in the element's onclick attribute, it will be fired before
  491. // FastClick's onClick handler. Fix this by pulling out the user-defined handler function and
  492. // adding it as listener.
  493. if (typeof layer.onclick === 'function') {
  494. // Android browser on at least 3.2 requires a new reference to the function in layer.onclick
  495. // - the old one won't work if passed to addEventListener directly.
  496. oldOnClick = layer.onclick;
  497. layer.addEventListener('click', function(event) {
  498. oldOnClick(event);
  499. }, false);
  500. layer.onclick = null;
  501. }
  502. }
  503. /**
  504. * Windows Phone 8.1 fakes user agent string to look like Android and iPhone.
  505. *
  506. * @type boolean
  507. */
  508. var deviceIsWindowsPhone = navigator.userAgent.indexOf("Windows Phone") >= 0;
  509. /**
  510. * Android requires exceptions.
  511. *
  512. * @type boolean
  513. */
  514. var deviceIsAndroid = navigator.userAgent.indexOf('Android') > 0 && !deviceIsWindowsPhone;
  515. /**
  516. * iOS requires exceptions.
  517. *
  518. * @type boolean
  519. */
  520. var deviceIsIOS = /iP(ad|hone|od)/.test(navigator.userAgent) && !deviceIsWindowsPhone;
  521. /**
  522. * iOS 4 requires an exception for select elements.
  523. *
  524. * @type boolean
  525. */
  526. var deviceIsIOS4 = deviceIsIOS && (/OS 4_\d(_\d)?/).test(navigator.userAgent);
  527. /**
  528. * iOS 6.0-7.* requires the target element to be manually derived
  529. *
  530. * @type boolean
  531. */
  532. var deviceIsIOSWithBadTarget = deviceIsIOS && (/OS [6-7]_\d/).test(navigator.userAgent);
  533. /**
  534. * BlackBerry requires exceptions.
  535. *
  536. * @type boolean
  537. */
  538. var deviceIsBlackBerry10 = navigator.userAgent.indexOf('BB10') > 0;
  539. /**
  540. * 判断是否组合型label
  541. * @type {Boolean}
  542. */
  543. var isCompositeLabel = false;
  544. /**
  545. * Determine whether a given element requires a native click.
  546. *
  547. * @param {EventTarget|Element} target Target DOM element
  548. * @returns {boolean} Returns true if the element needs a native click
  549. */
  550. FastClick.prototype.needsClick = function(target) {
  551. // 修复bug: 如果父元素中有 label
  552. // 如果label上有needsclick这个类,则用原生的点击,否则,用模拟点击
  553. var parent = target;
  554. while(parent && (parent.tagName.toUpperCase() !== "BODY")) {
  555. if (parent.tagName.toUpperCase() === "LABEL") {
  556. isCompositeLabel = true;
  557. if ((/\bneedsclick\b/).test(parent.className)) return true;
  558. }
  559. parent = parent.parentNode;
  560. }
  561. switch (target.nodeName.toLowerCase()) {
  562. // Don't send a synthetic click to disabled inputs (issue #62)
  563. case 'button':
  564. case 'select':
  565. case 'textarea':
  566. if (target.disabled) {
  567. return true;
  568. }
  569. break;
  570. case 'input':
  571. // File inputs need real clicks on iOS 6 due to a browser bug (issue #68)
  572. if ((deviceIsIOS && target.type === 'file') || target.disabled) {
  573. return true;
  574. }
  575. break;
  576. case 'label':
  577. case 'iframe': // iOS8 homescreen apps can prevent events bubbling into frames
  578. case 'video':
  579. return true;
  580. }
  581. return (/\bneedsclick\b/).test(target.className);
  582. };
  583. /**
  584. * Determine whether a given element requires a call to focus to simulate click into element.
  585. *
  586. * @param {EventTarget|Element} target Target DOM element
  587. * @returns {boolean} Returns true if the element requires a call to focus to simulate native click.
  588. */
  589. FastClick.prototype.needsFocus = function(target) {
  590. switch (target.nodeName.toLowerCase()) {
  591. case 'textarea':
  592. return true;
  593. case 'select':
  594. return !deviceIsAndroid;
  595. case 'input':
  596. switch (target.type) {
  597. case 'button':
  598. case 'checkbox':
  599. case 'file':
  600. case 'image':
  601. case 'radio':
  602. case 'submit':
  603. return false;
  604. }
  605. // No point in attempting to focus disabled inputs
  606. return !target.disabled && !target.readOnly;
  607. default:
  608. return (/\bneedsfocus\b/).test(target.className);
  609. }
  610. };
  611. /**
  612. * Send a click event to the specified element.
  613. *
  614. * @param {EventTarget|Element} targetElement
  615. * @param {Event} event
  616. */
  617. FastClick.prototype.sendClick = function(targetElement, event) {
  618. var clickEvent, touch;
  619. // On some Android devices activeElement needs to be blurred otherwise the synthetic click will have no effect (#24)
  620. if (document.activeElement && document.activeElement !== targetElement) {
  621. document.activeElement.blur();
  622. }
  623. touch = event.changedTouches[0];
  624. // Synthesise a click event, with an extra attribute so it can be tracked
  625. clickEvent = document.createEvent('MouseEvents');
  626. clickEvent.initMouseEvent(this.determineEventType(targetElement), true, true, window, 1, touch.screenX, touch.screenY, touch.clientX, touch.clientY, false, false, false, false, 0, null);
  627. clickEvent.forwardedTouchEvent = true;
  628. targetElement.dispatchEvent(clickEvent);
  629. };
  630. FastClick.prototype.determineEventType = function(targetElement) {
  631. //Issue #159: Android Chrome Select Box does not open with a synthetic click event
  632. if (deviceIsAndroid && targetElement.tagName.toLowerCase() === 'select') {
  633. return 'mousedown';
  634. }
  635. return 'click';
  636. };
  637. /**
  638. * @param {EventTarget|Element} targetElement
  639. */
  640. FastClick.prototype.focus = function(targetElement) {
  641. var length;
  642. // Issue #160: on iOS 7, some input elements (e.g. date datetime month) throw a vague TypeError on setSelectionRange. These elements don't have an integer value for the selectionStart and selectionEnd properties, but unfortunately that can't be used for detection because accessing the properties also throws a TypeError. Just check the type instead. Filed as Apple bug #15122724.
  643. var unsupportedType = ['date', 'time', 'month', 'number', 'email'];
  644. if (deviceIsIOS && targetElement.setSelectionRange && unsupportedType.indexOf(targetElement.type) === -1) {
  645. length = targetElement.value.length;
  646. targetElement.setSelectionRange(length, length);
  647. } else {
  648. targetElement.focus();
  649. }
  650. };
  651. /**
  652. * Check whether the given target element is a child of a scrollable layer and if so, set a flag on it.
  653. *
  654. * @param {EventTarget|Element} targetElement
  655. */
  656. FastClick.prototype.updateScrollParent = function(targetElement) {
  657. var scrollParent, parentElement;
  658. scrollParent = targetElement.fastClickScrollParent;
  659. // Attempt to discover whether the target element is contained within a scrollable layer. Re-check if the
  660. // target element was moved to another parent.
  661. if (!scrollParent || !scrollParent.contains(targetElement)) {
  662. parentElement = targetElement;
  663. do {
  664. if (parentElement.scrollHeight > parentElement.offsetHeight) {
  665. scrollParent = parentElement;
  666. targetElement.fastClickScrollParent = parentElement;
  667. break;
  668. }
  669. parentElement = parentElement.parentElement;
  670. } while (parentElement);
  671. }
  672. // Always update the scroll top tracker if possible.
  673. if (scrollParent) {
  674. scrollParent.fastClickLastScrollTop = scrollParent.scrollTop;
  675. }
  676. };
  677. /**
  678. * @param {EventTarget} targetElement
  679. * @returns {Element|EventTarget}
  680. */
  681. FastClick.prototype.getTargetElementFromEventTarget = function(eventTarget) {
  682. // On some older browsers (notably Safari on iOS 4.1 - see issue #56) the event target may be a text node.
  683. if (eventTarget.nodeType === Node.TEXT_NODE) {
  684. return eventTarget.parentNode;
  685. }
  686. return eventTarget;
  687. };
  688. /**
  689. * On touch start, record the position and scroll offset.
  690. *
  691. * @param {Event} event
  692. * @returns {boolean}
  693. */
  694. FastClick.prototype.onTouchStart = function(event) {
  695. var targetElement, touch, selection;
  696. // Ignore multiple touches, otherwise pinch-to-zoom is prevented if both fingers are on the FastClick element (issue #111).
  697. if (event.targetTouches.length > 1) {
  698. return true;
  699. }
  700. targetElement = this.getTargetElementFromEventTarget(event.target);
  701. touch = event.targetTouches[0];
  702. if (deviceIsIOS) {
  703. // Only trusted events will deselect text on iOS (issue #49)
  704. selection = window.getSelection();
  705. if (selection.rangeCount && !selection.isCollapsed) {
  706. return true;
  707. }
  708. if (!deviceIsIOS4) {
  709. // Weird things happen on iOS when an alert or confirm dialog is opened from a click event callback (issue #23):
  710. // when the user next taps anywhere else on the page, new touchstart and touchend events are dispatched
  711. // with the same identifier as the touch event that previously triggered the click that triggered the alert.
  712. // Sadly, there is an issue on iOS 4 that causes some normal touch events to have the same identifier as an
  713. // immediately preceeding touch event (issue #52), so this fix is unavailable on that platform.
  714. // Issue 120: touch.identifier is 0 when Chrome dev tools 'Emulate touch events' is set with an iOS device UA string,
  715. // which causes all touch events to be ignored. As this block only applies to iOS, and iOS identifiers are always long,
  716. // random integers, it's safe to to continue if the identifier is 0 here.
  717. if (touch.identifier && touch.identifier === this.lastTouchIdentifier) {
  718. event.preventDefault();
  719. return false;
  720. }
  721. this.lastTouchIdentifier = touch.identifier;
  722. // If the target element is a child of a scrollable layer (using -webkit-overflow-scrolling: touch) and:
  723. // 1) the user does a fling scroll on the scrollable layer
  724. // 2) the user stops the fling scroll with another tap
  725. // then the event.target of the last 'touchend' event will be the element that was under the user's finger
  726. // when the fling scroll was started, causing FastClick to send a click event to that layer - unless a check
  727. // is made to ensure that a parent layer was not scrolled before sending a synthetic click (issue #42).
  728. this.updateScrollParent(targetElement);
  729. }
  730. }
  731. this.trackingClick = true;
  732. this.trackingClickStart = event.timeStamp;
  733. this.targetElement = targetElement;
  734. this.touchStartX = touch.pageX;
  735. this.touchStartY = touch.pageY;
  736. // Prevent phantom clicks on fast double-tap (issue #36)
  737. if ((event.timeStamp - this.lastClickTime) < this.tapDelay) {
  738. event.preventDefault();
  739. }
  740. return true;
  741. };
  742. /**
  743. * Based on a touchmove event object, check whether the touch has moved past a boundary since it started.
  744. *
  745. * @param {Event} event
  746. * @returns {boolean}
  747. */
  748. FastClick.prototype.touchHasMoved = function(event) {
  749. var touch = event.changedTouches[0], boundary = this.touchBoundary;
  750. if (Math.abs(touch.pageX - this.touchStartX) > boundary || Math.abs(touch.pageY - this.touchStartY) > boundary) {
  751. return true;
  752. }
  753. return false;
  754. };
  755. /**
  756. * Update the last position.
  757. *
  758. * @param {Event} event
  759. * @returns {boolean}
  760. */
  761. FastClick.prototype.onTouchMove = function(event) {
  762. if (!this.trackingClick) {
  763. return true;
  764. }
  765. // If the touch has moved, cancel the click tracking
  766. if (this.targetElement !== this.getTargetElementFromEventTarget(event.target) || this.touchHasMoved(event)) {
  767. this.trackingClick = false;
  768. this.targetElement = null;
  769. }
  770. return true;
  771. };
  772. /**
  773. * Attempt to find the labelled control for the given label element.
  774. *
  775. * @param {EventTarget|HTMLLabelElement} labelElement
  776. * @returns {Element|null}
  777. */
  778. FastClick.prototype.findControl = function(labelElement) {
  779. // Fast path for newer browsers supporting the HTML5 control attribute
  780. if (labelElement.control !== undefined) {
  781. return labelElement.control;
  782. }
  783. // All browsers under test that support touch events also support the HTML5 htmlFor attribute
  784. if (labelElement.htmlFor) {
  785. return document.getElementById(labelElement.htmlFor);
  786. }
  787. // If no for attribute exists, attempt to retrieve the first labellable descendant element
  788. // the list of which is defined here: http://www.w3.org/TR/html5/forms.html#category-label
  789. return labelElement.querySelector('button, input:not([type=hidden]), keygen, meter, output, progress, select, textarea');
  790. };
  791. /**
  792. * On touch end, determine whether to send a click event at once.
  793. *
  794. * @param {Event} event
  795. * @returns {boolean}
  796. */
  797. FastClick.prototype.onTouchEnd = function(event) {
  798. var forElement, trackingClickStart, targetTagName, scrollParent, touch, targetElement = this.targetElement;
  799. if (!this.trackingClick) {
  800. return true;
  801. }
  802. // Prevent phantom clicks on fast double-tap (issue #36)
  803. if ((event.timeStamp - this.lastClickTime) < this.tapDelay) {
  804. this.cancelNextClick = true;
  805. return true;
  806. }
  807. if ((event.timeStamp - this.trackingClickStart) > this.tapTimeout) {
  808. return true;
  809. }
  810. //修复安卓微信下,input type="date" 的bug,经测试date,time,month已没问题
  811. var unsupportedType = ['date', 'time', 'month'];
  812. if(unsupportedType.indexOf(event.target.type) !== -1){
  813.     return false;
  814.   }
  815. // Reset to prevent wrong click cancel on input (issue #156).
  816. this.cancelNextClick = false;
  817. this.lastClickTime = event.timeStamp;
  818. trackingClickStart = this.trackingClickStart;
  819. this.trackingClick = false;
  820. this.trackingClickStart = 0;
  821. // On some iOS devices, the targetElement supplied with the event is invalid if the layer
  822. // is performing a transition or scroll, and has to be re-detected manually. Note that
  823. // for this to function correctly, it must be called *after* the event target is checked!
  824. // See issue #57; also filed as rdar://13048589 .
  825. if (deviceIsIOSWithBadTarget) {
  826. touch = event.changedTouches[0];
  827. // In certain cases arguments of elementFromPoint can be negative, so prevent setting targetElement to null
  828. targetElement = document.elementFromPoint(touch.pageX - window.pageXOffset, touch.pageY - window.pageYOffset) || targetElement;
  829. targetElement.fastClickScrollParent = this.targetElement.fastClickScrollParent;
  830. }
  831. targetTagName = targetElement.tagName.toLowerCase();
  832. if (targetTagName === 'label') {
  833. forElement = this.findControl(targetElement);
  834. if (forElement) {
  835. this.focus(targetElement);
  836. if (deviceIsAndroid) {
  837. return false;
  838. }
  839. targetElement = forElement;
  840. }
  841. } else if (this.needsFocus(targetElement)) {
  842. // Case 1: If the touch started a while ago (best guess is 100ms based on tests for issue #36) then focus will be triggered anyway. Return early and unset the target element reference so that the subsequent click will be allowed through.
  843. // Case 2: Without this exception for input elements tapped when the document is contained in an iframe, then any inputted text won't be visible even though the value attribute is updated as the user types (issue #37).
  844. if ((event.timeStamp - trackingClickStart) > 100 || (deviceIsIOS && window.top !== window && targetTagName === 'input')) {
  845. this.targetElement = null;
  846. return false;
  847. }
  848. this.focus(targetElement);
  849. this.sendClick(targetElement, event);
  850. // Select elements need the event to go through on iOS 4, otherwise the selector menu won't open.
  851. // Also this breaks opening selects when VoiceOver is active on iOS6, iOS7 (and possibly others)
  852. if (!deviceIsIOS || targetTagName !== 'select') {
  853. this.targetElement = null;
  854. event.preventDefault();
  855. }
  856. return false;
  857. }
  858. if (deviceIsIOS && !deviceIsIOS4) {
  859. // Don't send a synthetic click event if the target element is contained within a parent layer that was scrolled
  860. // and this tap is being used to stop the scrolling (usually initiated by a fling - issue #42).
  861. scrollParent = targetElement.fastClickScrollParent;
  862. if (scrollParent && scrollParent.fastClickLastScrollTop !== scrollParent.scrollTop) {
  863. return true;
  864. }
  865. }
  866. // Prevent the actual click from going though - unless the target node is marked as requiring
  867. // real clicks or if it is in the whitelist in which case only non-programmatic clicks are permitted.
  868. if (!this.needsClick(targetElement)) {
  869. event.preventDefault();
  870. this.sendClick(targetElement, event);
  871. }
  872. return false;
  873. };
  874. /**
  875. * On touch cancel, stop tracking the click.
  876. *
  877. * @returns {void}
  878. */
  879. FastClick.prototype.onTouchCancel = function() {
  880. this.trackingClick = false;
  881. this.targetElement = null;
  882. };
  883. /**
  884. * Determine mouse events which should be permitted.
  885. *
  886. * @param {Event} event
  887. * @returns {boolean}
  888. */
  889. FastClick.prototype.onMouse = function(event) {
  890. // If a target element was never set (because a touch event was never fired) allow the event
  891. if (!this.targetElement) {
  892. return true;
  893. }
  894. if (event.forwardedTouchEvent) {
  895. return true;
  896. }
  897. // Programmatically generated events targeting a specific element should be permitted
  898. if (!event.cancelable) {
  899. return true;
  900. }
  901. // Derive and check the target element to see whether the mouse event needs to be permitted;
  902. // unless explicitly enabled, prevent non-touch click events from triggering actions,
  903. // to prevent ghost/doubleclicks.
  904. if (!this.needsClick(this.targetElement) || this.cancelNextClick) {
  905. // Prevent any user-added listeners declared on FastClick element from being fired.
  906. if (event.stopImmediatePropagation) {
  907. event.stopImmediatePropagation();
  908. } else {
  909. // Part of the hack for browsers that don't support Event#stopImmediatePropagation (e.g. Android 2)
  910. event.propagationStopped = true;
  911. }
  912. // Cancel the event
  913. event.stopPropagation();
  914. // 允许组合型label冒泡
  915. if (!isCompositeLabel) {
  916. event.preventDefault();
  917. }
  918. // 允许组合型label冒泡
  919. return false;
  920. }
  921. // If the mouse event is permitted, return true for the action to go through.
  922. return true;
  923. };
  924. /**
  925. * On actual clicks, determine whether this is a touch-generated click, a click action occurring
  926. * naturally after a delay after a touch (which needs to be cancelled to avoid duplication), or
  927. * an actual click which should be permitted.
  928. *
  929. * @param {Event} event
  930. * @returns {boolean}
  931. */
  932. FastClick.prototype.onClick = function(event) {
  933. var permitted;
  934. // It's possible for another FastClick-like library delivered with third-party code to fire a click event before FastClick does (issue #44). In that case, set the click-tracking flag back to false and return early. This will cause onTouchEnd to return early.
  935. if (this.trackingClick) {
  936. this.targetElement = null;
  937. this.trackingClick = false;
  938. return true;
  939. }
  940. // Very odd behaviour on iOS (issue #18): if a submit element is present inside a form and the user hits enter in the iOS simulator or clicks the Go button on the pop-up OS keyboard the a kind of 'fake' click event will be triggered with the submit-type input element as the target.
  941. if (event.target.type === 'submit' && event.detail === 0) {
  942. return true;
  943. }
  944. permitted = this.onMouse(event);
  945. // Only unset targetElement if the click is not permitted. This will ensure that the check for !targetElement in onMouse fails and the browser's click doesn't go through.
  946. if (!permitted) {
  947. this.targetElement = null;
  948. }
  949. // If clicks are permitted, return true for the action to go through.
  950. return permitted;
  951. };
  952. /**
  953. * Remove all FastClick's event listeners.
  954. *
  955. * @returns {void}
  956. */
  957. FastClick.prototype.destroy = function() {
  958. var layer = this.layer;
  959. if (deviceIsAndroid) {
  960. layer.removeEventListener('mouseover', this.onMouse, true);
  961. layer.removeEventListener('mousedown', this.onMouse, true);
  962. layer.removeEventListener('mouseup', this.onMouse, true);
  963. }
  964. layer.removeEventListener('click', this.onClick, true);
  965. layer.removeEventListener('touchstart', this.onTouchStart, false);
  966. layer.removeEventListener('touchmove', this.onTouchMove, false);
  967. layer.removeEventListener('touchend', this.onTouchEnd, false);
  968. layer.removeEventListener('touchcancel', this.onTouchCancel, false);
  969. };
  970. /**
  971. * Check whether FastClick is needed.
  972. *
  973. * @param {Element} layer The layer to listen on
  974. */
  975. FastClick.notNeeded = function(layer) {
  976. var metaViewport;
  977. var chromeVersion;
  978. var blackberryVersion;
  979. var firefoxVersion;
  980. // Devices that don't support touch don't need FastClick
  981. if (typeof window.ontouchstart === 'undefined') {
  982. return true;
  983. }
  984. // Chrome version - zero for other browsers
  985. chromeVersion = +(/Chrome\/([0-9]+)/.exec(navigator.userAgent) || [,0])[1];
  986. if (chromeVersion) {
  987. if (deviceIsAndroid) {
  988. metaViewport = document.querySelector('meta[name=viewport]');
  989. if (metaViewport) {
  990. // Chrome on Android with user-scalable="no" doesn't need FastClick (issue #89)
  991. if (metaViewport.content.indexOf('user-scalable=no') !== -1) {
  992. return true;
  993. }
  994. // Chrome 32 and above with width=device-width or less don't need FastClick
  995. if (chromeVersion > 31 && document.documentElement.scrollWidth <= window.outerWidth) {
  996. return true;
  997. }
  998. }
  999. // Chrome desktop doesn't need FastClick (issue #15)
  1000. } else {
  1001. return true;
  1002. }
  1003. }
  1004. if (deviceIsBlackBerry10) {
  1005. blackberryVersion = navigator.userAgent.match(/Version\/([0-9]*)\.([0-9]*)/);
  1006. // BlackBerry 10.3+ does not require Fastclick library.
  1007. // https://github.com/ftlabs/fastclick/issues/251
  1008. if (blackberryVersion[1] >= 10 && blackberryVersion[2] >= 3) {
  1009. metaViewport = document.querySelector('meta[name=viewport]');
  1010. if (metaViewport) {
  1011. // user-scalable=no eliminates click delay.
  1012. if (metaViewport.content.indexOf('user-scalable=no') !== -1) {
  1013. return true;
  1014. }
  1015. // width=device-width (or less than device-width) eliminates click delay.
  1016. if (document.documentElement.scrollWidth <= window.outerWidth) {
  1017. return true;
  1018. }
  1019. }
  1020. }
  1021. }
  1022. // IE10 with -ms-touch-action: none or manipulation, which disables double-tap-to-zoom (issue #97)
  1023. if (layer.style.msTouchAction === 'none' || layer.style.touchAction === 'manipulation') {
  1024. return true;
  1025. }
  1026. // Firefox version - zero for other browsers
  1027. firefoxVersion = +(/Firefox\/([0-9]+)/.exec(navigator.userAgent) || [,0])[1];
  1028. if (firefoxVersion >= 27) {
  1029. // Firefox 27+ does not have tap delay if the content is not zoomable - https://bugzilla.mozilla.org/show_bug.cgi?id=922896
  1030. metaViewport = document.querySelector('meta[name=viewport]');
  1031. if (metaViewport && (metaViewport.content.indexOf('user-scalable=no') !== -1 || document.documentElement.scrollWidth <= window.outerWidth)) {
  1032. return true;
  1033. }
  1034. }
  1035. // IE11: prefixed -ms-touch-action is no longer supported and it's recomended to use non-prefixed version
  1036. // http://msdn.microsoft.com/en-us/library/windows/apps/Hh767313.aspx
  1037. if (layer.style.touchAction === 'none' || layer.style.touchAction === 'manipulation') {
  1038. return true;
  1039. }
  1040. return false;
  1041. };
  1042. /**
  1043. * Factory method for creating a FastClick object
  1044. *
  1045. * @param {Element} layer The layer to listen on
  1046. * @param {Object} [options={}] The options to override the defaults
  1047. */
  1048. FastClick.attach = function(layer, options) {
  1049. return new FastClick(layer, options);
  1050. };
  1051. window.FastClick = FastClick;
  1052. }());
  1053. /*======================================================
  1054. ************ Modals ************
  1055. ======================================================*/
  1056. /*jshint unused: false*/
  1057. +function ($) {
  1058. "use strict";
  1059. var _modalTemplateTempDiv = document.createElement('div');
  1060. $.modalStack = [];
  1061. $.modalStackClearQueue = function () {
  1062. if ($.modalStack.length) {
  1063. ($.modalStack.shift())();
  1064. }
  1065. };
  1066. $.modal = function (params) {
  1067. params = params || {};
  1068. var modalHTML = '';
  1069. var buttonsHTML = '';
  1070. if (params.buttons && params.buttons.length > 0) {
  1071. for (var i = 0; i < params.buttons.length; i++) {
  1072. buttonsHTML += '<span class="modal-button' + (params.buttons[i].bold ? ' modal-button-bold' : '') + '">' + params.buttons[i].text + '</span>';
  1073. }
  1074. }
  1075. var extraClass = params.extraClass || '';
  1076. var titleHTML = params.title ? '<div class="modal-title">' + params.title + '</div>' : '';
  1077. var textHTML = params.text ? '<div class="modal-text">' + params.text + '</div>' : '';
  1078. var afterTextHTML = params.afterText ? params.afterText : '';
  1079. var noButtons = !params.buttons || params.buttons.length === 0 ? 'modal-no-buttons' : '';
  1080. var verticalButtons = params.verticalButtons ? 'modal-buttons-vertical' : '';
  1081. modalHTML = '<div class="modal ' + extraClass + ' ' + noButtons + '"><div class="modal-inner">' + (titleHTML + textHTML + afterTextHTML) + '</div><div class="modal-buttons ' + verticalButtons + '">' + buttonsHTML + '</div></div>';
  1082. _modalTemplateTempDiv.innerHTML = modalHTML;
  1083. var modal = $(_modalTemplateTempDiv).children();
  1084. $(defaults.modalContainer).append(modal[0]);
  1085. // Add events on buttons
  1086. modal.find('.modal-button').each(function (index, el) {
  1087. $(el).on('click', function (e) {
  1088. if (params.buttons[index].close !== false) $.closeModal(modal);
  1089. if (params.buttons[index].onClick) params.buttons[index].onClick(modal, e);
  1090. if (params.onClick) params.onClick(modal, index);
  1091. });
  1092. });
  1093. $.openModal(modal);
  1094. return modal[0];
  1095. };
  1096. $.alert = function (text, title, callbackOk) {
  1097. if (typeof title === 'function') {
  1098. callbackOk = arguments[1];
  1099. title = undefined;
  1100. }
  1101. return $.modal({
  1102. text: text || '',
  1103. title: typeof title === 'undefined' ? defaults.modalTitle : title,
  1104. buttons: [ {text: defaults.modalButtonOk, bold: true, onClick: callbackOk} ]
  1105. });
  1106. };
  1107. $.confirm = function (text, title, callbackOk, callbackCancel) {
  1108. if (typeof title === 'function') {
  1109. callbackCancel = arguments[2];
  1110. callbackOk = arguments[1];
  1111. title = undefined;
  1112. }
  1113. return $.modal({
  1114. text: text || '',
  1115. title: typeof title === 'undefined' ? defaults.modalTitle : title,
  1116. buttons: [
  1117. {text: defaults.modalButtonCancel, onClick: callbackCancel},
  1118. {text: defaults.modalButtonOk, bold: true, onClick: callbackOk}
  1119. ]
  1120. });
  1121. };
  1122. $.prompt = function (text, title, callbackOk, callbackCancel) {
  1123. if (typeof title === 'function') {
  1124. callbackCancel = arguments[2];
  1125. callbackOk = arguments[1];
  1126. title = undefined;
  1127. }
  1128. return $.modal({
  1129. text: text || '',
  1130. title: typeof title === 'undefined' ? defaults.modalTitle : title,
  1131. afterText: '<input type="text" class="modal-text-input">',
  1132. buttons: [
  1133. {
  1134. text: defaults.modalButtonCancel
  1135. },
  1136. {
  1137. text: defaults.modalButtonOk,
  1138. bold: true
  1139. }
  1140. ],
  1141. onClick: function (modal, index) {
  1142. if (index === 0 && callbackCancel) callbackCancel($(modal).find('.modal-text-input').val());
  1143. if (index === 1 && callbackOk) callbackOk($(modal).find('.modal-text-input').val());
  1144. }
  1145. });
  1146. };
  1147. $.modalLogin = function (text, title, callbackOk, callbackCancel) {
  1148. if (typeof title === 'function') {
  1149. callbackCancel = arguments[2];
  1150. callbackOk = arguments[1];
  1151. title = undefined;
  1152. }
  1153. return $.modal({
  1154. text: text || '',
  1155. title: typeof title === 'undefined' ? defaults.modalTitle : title,
  1156. afterText: '<input type="text" name="modal-username" placeholder="' + defaults.modalUsernamePlaceholder + '" class="modal-text-input modal-text-input-double"><input type="password" name="modal-password" placeholder="' + defaults.modalPasswordPlaceholder + '" class="modal-text-input modal-text-input-double">',
  1157. buttons: [
  1158. {
  1159. text: defaults.modalButtonCancel
  1160. },
  1161. {
  1162. text: defaults.modalButtonOk,
  1163. bold: true
  1164. }
  1165. ],
  1166. onClick: function (modal, index) {
  1167. var username = $(modal).find('.modal-text-input[name="modal-username"]').val();
  1168. var password = $(modal).find('.modal-text-input[name="modal-password"]').val();
  1169. if (index === 0 && callbackCancel) callbackCancel(username, password);
  1170. if (index === 1 && callbackOk) callbackOk(username, password);
  1171. }
  1172. });
  1173. };
  1174. $.modalPassword = function (text, title, callbackOk, callbackCancel) {
  1175. if (typeof title === 'function') {
  1176. callbackCancel = arguments[2];
  1177. callbackOk = arguments[1];
  1178. title = undefined;
  1179. }
  1180. return $.modal({
  1181. text: text || '',
  1182. title: typeof title === 'undefined' ? defaults.modalTitle : title,
  1183. afterText: '<input type="password" name="modal-password" placeholder="' + defaults.modalPasswordPlaceholder + '" class="modal-text-input">',
  1184. buttons: [
  1185. {
  1186. text: defaults.modalButtonCancel
  1187. },
  1188. {
  1189. text: defaults.modalButtonOk,
  1190. bold: true
  1191. }
  1192. ],
  1193. onClick: function (modal, index) {
  1194. var password = $(modal).find('.modal-text-input[name="modal-password"]').val();
  1195. if (index === 0 && callbackCancel) callbackCancel(password);
  1196. if (index === 1 && callbackOk) callbackOk(password);
  1197. }
  1198. });
  1199. };
  1200. $.showPreloader = function (title) {
  1201. $.hidePreloader();
  1202. $.showPreloader.preloaderModal = $.modal({
  1203. title: title || defaults.modalPreloaderTitle,
  1204. text: '<div class="preloader"></div>'
  1205. });
  1206. return $.showPreloader.preloaderModal;
  1207. };
  1208. $.hidePreloader = function () {
  1209. $.showPreloader.preloaderModal && $.closeModal($.showPreloader.preloaderModal);
  1210. };
  1211. $.showIndicator = function () {
  1212. if ($('.preloader-indicator-modal')[0]) return;
  1213. $(defaults.modalContainer).append('<div class="preloader-indicator-overlay"></div><div class="preloader-indicator-modal"><span class="preloader preloader-white"></span></div>');
  1214. };
  1215. $.hideIndicator = function () {
  1216. $('.preloader-indicator-overlay, .preloader-indicator-modal').remove();
  1217. };
  1218. // Action Sheet
  1219. $.actions = function (params) {
  1220. var modal, groupSelector, buttonSelector;
  1221. params = params || [];
  1222. if (params.length > 0 && !$.isArray(params[0])) {
  1223. params = [params];
  1224. }
  1225. var modalHTML;
  1226. var buttonsHTML = '';
  1227. for (var i = 0; i < params.length; i++) {
  1228. for (var j = 0; j < params[i].length; j++) {
  1229. if (j === 0) buttonsHTML += '<div class="actions-modal-group">';
  1230. var button = params[i][j];
  1231. var buttonClass = button.label ? 'actions-modal-label' : 'actions-modal-button';
  1232. if (button.bold) buttonClass += ' actions-modal-button-bold';
  1233. if (button.color) buttonClass += ' color-' + button.color;
  1234. if (button.bg) buttonClass += ' bg-' + button.bg;
  1235. if (button.disabled) buttonClass += ' disabled';
  1236. buttonsHTML += '<span class="' + buttonClass + '">' + button.text + '</span>';
  1237. if (j === params[i].length - 1) buttonsHTML += '</div>';
  1238. }
  1239. }
  1240. modalHTML = '<div class="actions-modal">' + buttonsHTML + '</div>';
  1241. _modalTemplateTempDiv.innerHTML = modalHTML;
  1242. modal = $(_modalTemplateTempDiv).children();
  1243. $(defaults.modalContainer).append(modal[0]);
  1244. groupSelector = '.actions-modal-group';
  1245. buttonSelector = '.actions-modal-button';
  1246. var groups = modal.find(groupSelector);
  1247. groups.each(function (index, el) {
  1248. var groupIndex = index;
  1249. $(el).children().each(function (index, el) {
  1250. var buttonIndex = index;
  1251. var buttonParams = params[groupIndex][buttonIndex];
  1252. var clickTarget;
  1253. if ($(el).is(buttonSelector)) clickTarget = $(el);
  1254. // if (toPopover && $(el).find(buttonSelector).length > 0) clickTarget = $(el).find(buttonSelector);
  1255. if (clickTarget) {
  1256. clickTarget.on('click', function (e) {
  1257. if (buttonParams.close !== false) $.closeModal(modal);
  1258. if (buttonParams.onClick) buttonParams.onClick(modal, e);
  1259. });
  1260. }
  1261. });
  1262. });
  1263. $.openModal(modal);
  1264. return modal[0];
  1265. };
  1266. $.popup = function (modal, removeOnClose) {
  1267. if (typeof removeOnClose === 'undefined') removeOnClose = true;
  1268. if (typeof modal === 'string' && modal.indexOf('<') >= 0) {
  1269. var _modal = document.createElement('div');
  1270. _modal.innerHTML = modal.trim();
  1271. if (_modal.childNodes.length > 0) {
  1272. modal = _modal.childNodes[0];
  1273. if (removeOnClose) modal.classList.add('remove-on-close');
  1274. $(defaults.modalContainer).append(modal);
  1275. }
  1276. else return false; //nothing found
  1277. }
  1278. modal = $(modal);
  1279. if (modal.length === 0) return false;
  1280. modal.show();
  1281. modal.find(".content").scroller("refresh");
  1282. if (modal.find('.' + defaults.viewClass).length > 0) {
  1283. $.sizeNavbars(modal.find('.' + defaults.viewClass)[0]);
  1284. }
  1285. $.openModal(modal);
  1286. return modal[0];
  1287. };
  1288. $.pickerModal = function (pickerModal, removeOnClose) {
  1289. if (typeof removeOnClose === 'undefined') removeOnClose = true;
  1290. if (typeof pickerModal === 'string' && pickerModal.indexOf('<') >= 0) {
  1291. pickerModal = $(pickerModal);
  1292. if (pickerModal.length > 0) {
  1293. if (removeOnClose) pickerModal.addClass('remove-on-close');
  1294. $(defaults.modalContainer).append(pickerModal[0]);
  1295. }
  1296. else return false; //nothing found
  1297. }
  1298. pickerModal = $(pickerModal);
  1299. if (pickerModal.length === 0) return false;
  1300. pickerModal.show();
  1301. $.openModal(pickerModal);
  1302. return pickerModal[0];
  1303. };
  1304. $.loginScreen = function (modal) {
  1305. if (!modal) modal = '.login-screen';
  1306. modal = $(modal);
  1307. if (modal.length === 0) return false;
  1308. modal.show();
  1309. if (modal.find('.' + defaults.viewClass).length > 0) {
  1310. $.sizeNavbars(modal.find('.' + defaults.viewClass)[0]);
  1311. }
  1312. $.openModal(modal);
  1313. return modal[0];
  1314. };
  1315. //显示一个消息,会在2秒钟后自动消失
  1316. $.toast = function(msg, duration, extraclass) {
  1317. var $toast = $('<div class="modal toast ' + (extraclass || '') + '">' + msg + '</div>').appendTo(document.body);
  1318. $.openModal($toast, function(){
  1319. setTimeout(function() {
  1320. $.closeModal($toast);
  1321. }, duration || 2000);
  1322. });
  1323. };
  1324. $.openModal = function (modal, cb) {
  1325. modal = $(modal);
  1326. var isModal = modal.hasClass('modal'),
  1327. isNotToast = !modal.hasClass('toast');
  1328. if ($('.modal.modal-in:not(.modal-out)').length && defaults.modalStack && isModal && isNotToast) {
  1329. $.modalStack.push(function () {
  1330. $.openModal(modal, cb);
  1331. });
  1332. return;
  1333. }
  1334. var isPopup = modal.hasClass('popup');
  1335. var isLoginScreen = modal.hasClass('login-screen');
  1336. var isPickerModal = modal.hasClass('picker-modal');
  1337. var isToast = modal.hasClass('toast');
  1338. if (isModal) {
  1339. modal.show();
  1340. modal.css({
  1341. marginTop: - Math.round(modal.outerHeight() / 2) + 'px'
  1342. });
  1343. }
  1344. if (isToast) {
  1345. modal.css({
  1346. marginLeft: - Math.round(modal.outerWidth() / 2 / 1.185) + 'px' //1.185 是初始化时候的放大效果
  1347. });
  1348. }
  1349. var overlay;
  1350. if (!isLoginScreen && !isPickerModal && !isToast) {
  1351. if ($('.modal-overlay').length === 0 && !isPopup) {
  1352. $(defaults.modalContainer).append('<div class="modal-overlay"></div>');
  1353. }
  1354. if ($('.popup-overlay').length === 0 && isPopup) {
  1355. $(defaults.modalContainer).append('<div class="popup-overlay"></div>');
  1356. }
  1357. overlay = isPopup ? $('.popup-overlay') : $('.modal-overlay');
  1358. }
  1359. //Make sure that styles are applied, trigger relayout;
  1360. var clientLeft = modal[0].clientLeft;
  1361. // Trugger open event
  1362. modal.trigger('open');
  1363. // Picker modal body class
  1364. if (isPickerModal) {
  1365. $(defaults.modalContainer).addClass('with-picker-modal');
  1366. }
  1367. // Classes for transition in
  1368. if (!isLoginScreen && !isPickerModal && !isToast) overlay.addClass('modal-overlay-visible');
  1369. modal.removeClass('modal-out').addClass('modal-in').transitionEnd(function (e) {
  1370. if (modal.hasClass('modal-out')) modal.trigger('closed');
  1371. else modal.trigger('opened');
  1372. });
  1373. // excute callback
  1374. if (typeof cb === 'function') {
  1375. cb.call(this);
  1376. }
  1377. return true;
  1378. };
  1379. $.closeModal = function (modal) {
  1380. modal = $(modal || '.modal-in');
  1381. if (typeof modal !== 'undefined' && modal.length === 0) {
  1382. return;
  1383. }
  1384. var isModal = modal.hasClass('modal'),
  1385. isPopup = modal.hasClass('popup'),
  1386. isToast = modal.hasClass('toast'),
  1387. isLoginScreen = modal.hasClass('login-screen'),
  1388. isPickerModal = modal.hasClass('picker-modal'),
  1389. removeOnClose = modal.hasClass('remove-on-close'),
  1390. overlay = isPopup ? $('.popup-overlay') : $('.modal-overlay');
  1391. if (isPopup){
  1392. if (modal.length === $('.popup.modal-in').length) {
  1393. overlay.removeClass('modal-overlay-visible');
  1394. }
  1395. }
  1396. else if (!(isPickerModal || isToast)) {
  1397. overlay.removeClass('modal-overlay-visible');
  1398. }
  1399. modal.trigger('close');
  1400. // Picker modal body class
  1401. if (isPickerModal) {
  1402. $(defaults.modalContainer).removeClass('with-picker-modal');
  1403. $(defaults.modalContainer).addClass('picker-modal-closing');
  1404. }
  1405. modal.removeClass('modal-in').addClass('modal-out').transitionEnd(function (e) {
  1406. if (modal.hasClass('modal-out')) modal.trigger('closed');
  1407. else modal.trigger('opened');
  1408. if (isPickerModal) {
  1409. $(defaults.modalContainer).removeClass('picker-modal-closing');
  1410. }
  1411. if (isPopup || isLoginScreen || isPickerModal) {
  1412. modal.removeClass('modal-out').hide();
  1413. if (removeOnClose && modal.length > 0) {
  1414. modal.remove();
  1415. }
  1416. }
  1417. else {
  1418. modal.remove();
  1419. }
  1420. });
  1421. if (isModal && defaults.modalStack ) {
  1422. $.modalStackClearQueue();
  1423. }
  1424. return true;
  1425. };
  1426. function handleClicks(e) {
  1427. /*jshint validthis:true */
  1428. var clicked = $(this);
  1429. var url = clicked.attr('href');
  1430. //Collect Clicked data- attributes
  1431. var clickedData = clicked.dataset();
  1432. // Popup
  1433. var popup;
  1434. if (clicked.hasClass('open-popup')) {
  1435. if (clickedData.popup) {
  1436. popup = clickedData.popup;
  1437. }
  1438. else popup = '.popup';
  1439. $.popup(popup);
  1440. }
  1441. if (clicked.hasClass('close-popup')) {
  1442. if (clickedData.popup) {
  1443. popup = clickedData.popup;
  1444. }
  1445. else popup = '.popup.modal-in';
  1446. $.closeModal(popup);
  1447. }
  1448. // Close Modal
  1449. if (clicked.hasClass('modal-overlay')) {
  1450. if ($('.modal.modal-in').length > 0 && defaults.modalCloseByOutside)
  1451. $.closeModal('.modal.modal-in');
  1452. if ($('.actions-modal.modal-in').length > 0 && defaults.actionsCloseByOutside)
  1453. $.closeModal('.actions-modal.modal-in');
  1454. }
  1455. if (clicked.hasClass('popup-overlay')) {
  1456. if ($('.popup.modal-in').length > 0 && defaults.popupCloseByOutside)
  1457. $.closeModal('.popup.modal-in');
  1458. }
  1459. }
  1460. $(document).on('click', ' .modal-overlay, .popup-overlay, .close-popup, .open-popup, .close-picker', handleClicks);
  1461. var defaults = $.modal.prototype.defaults = {
  1462. modalStack: true,
  1463. modalButtonOk: '确定',
  1464. modalButtonCancel: '取消',
  1465. modalPreloaderTitle: '加载中',
  1466. modalContainer : document.body ? document.body : 'body'
  1467. };
  1468. }(Zepto);
  1469. /*======================================================
  1470. ************ Calendar ************
  1471. ======================================================*/
  1472. /*jshint unused: false*/
  1473. +function ($) {
  1474. "use strict";
  1475. var rtl = false;
  1476. var Calendar = function (params) {
  1477. var p = this;
  1478. var defaults = {
  1479. monthNames: ['一月', '二月', '三月', '四月', '五月', '六月', '七月', '八月' , '九月' , '十月', '十一月', '十二月'],
  1480. monthNamesShort: ['一月', '二月', '三月', '四月', '五月', '六月', '七月', '八月' , '九月' , '十月', '十一月', '十二月'],
  1481. dayNames: ['周日', '周一', '周二', '周三', '周四', '周五', '周六'],
  1482. dayNamesShort: ['周日', '周一', '周二', '周三', '周四', '周五', '周六'],
  1483. firstDay: 1, // First day of the week, Monday
  1484. weekendDays: [0, 6], // Sunday and Saturday
  1485. multiple: false,
  1486. dateFormat: 'yyyy-mm-dd',
  1487. direction: 'horizontal', // or 'vertical'
  1488. minDate: null,
  1489. maxDate: null,
  1490. touchMove: true,
  1491. animate: true,
  1492. closeOnSelect: true,
  1493. monthPicker: true,
  1494. monthPickerTemplate:
  1495. '<div class="picker-calendar-month-picker">' +
  1496. '<a href="#" class="link icon-only picker-calendar-prev-month"><i class="icon icon-prev"></i></a>' +
  1497. '<div class="current-month-value"></div>' +
  1498. '<a href="#" class="link icon-only picker-calendar-next-month"><i class="icon icon-next"></i></a>' +
  1499. '</div>',
  1500. yearPicker: true,
  1501. yearPickerTemplate:
  1502. '<div class="picker-calendar-year-picker">' +
  1503. '<a href="#" class="link icon-only picker-calendar-prev-year"><i class="icon icon-prev"></i></a>' +
  1504. '<span class="current-year-value"></span>' +
  1505. '<a href="#" class="link icon-only picker-calendar-next-year"><i class="icon icon-next"></i></a>' +
  1506. '</div>',
  1507. weekHeader: true,
  1508. // Common settings
  1509. scrollToInput: true,
  1510. inputReadOnly: true,
  1511. toolbar: true,
  1512. toolbarCloseText: 'Done',
  1513. toolbarTemplate:
  1514. '<div class="toolbar">' +
  1515. '<div class="toolbar-inner">' +
  1516. '{{monthPicker}}' +
  1517. '{{yearPicker}}' +
  1518. // '<a href="#" class="link close-picker">{{closeText}}</a>' +
  1519. '</div>' +
  1520. '</div>',
  1521. /* Callbacks
  1522. onMonthAdd
  1523. onChange
  1524. onOpen
  1525. onClose
  1526. onDayClick
  1527. onMonthYearChangeStart
  1528. onMonthYearChangeEnd
  1529. */
  1530. };
  1531. params = params || {};
  1532. for (var def in defaults) {
  1533. if (typeof params[def] === 'undefined') {
  1534. params[def] = defaults[def];
  1535. }
  1536. }
  1537. p.params = params;
  1538. p.initialized = false;
  1539. // Inline flag
  1540. p.inline = p.params.container ? true : false;
  1541. // Is horizontal
  1542. p.isH = p.params.direction === 'horizontal';
  1543. // RTL inverter
  1544. var inverter = p.isH ? (rtl ? -1 : 1) : 1;
  1545. // Animating flag
  1546. p.animating = false;
  1547. // Format date
  1548. function formatDate(date) {
  1549. date = new Date(date);
  1550. var year = date.getFullYear();
  1551. var month = date.getMonth();
  1552. var month1 = month + 1;
  1553. var day = date.getDate();
  1554. var weekDay = date.getDay();
  1555. return p.params.dateFormat
  1556. .replace(/yyyy/g, year)
  1557. .replace(/yy/g, (year + '').substring(2))
  1558. .replace(/mm/g, month1 < 10 ? '0' + month1 : month1)
  1559. .replace(/m/g, month1)
  1560. .replace(/MM/g, p.params.monthNames[month])
  1561. .replace(/M/g, p.params.monthNamesShort[month])
  1562. .replace(/dd/g, day < 10 ? '0' + day : day)
  1563. .replace(/d/g, day)
  1564. .replace(/DD/g, p.params.dayNames[weekDay])
  1565. .replace(/D/g, p.params.dayNamesShort[weekDay]);
  1566. }
  1567. // Value
  1568. p.addValue = function (value) {
  1569. if (p.params.multiple) {
  1570. if (!p.value) p.value = [];
  1571. var inValuesIndex;
  1572. for (var i = 0; i < p.value.length; i++) {
  1573. if (new Date(value).getTime() === new Date(p.value[i]).getTime()) {
  1574. inValuesIndex = i;
  1575. }
  1576. }
  1577. if (typeof inValuesIndex === 'undefined') {
  1578. p.value.push(value);
  1579. }
  1580. else {
  1581. p.value.splice(inValuesIndex, 1);
  1582. }
  1583. p.updateValue();
  1584. }
  1585. else {
  1586. p.value = [value];
  1587. p.updateValue();
  1588. }
  1589. };
  1590. p.setValue = function (arrValues) {
  1591. p.value = arrValues;
  1592. p.updateValue();
  1593. };
  1594. p.updateValue = function () {
  1595. p.wrapper.find('.picker-calendar-day-selected').removeClass('picker-calendar-day-selected');
  1596. var i, inputValue;
  1597. for (i = 0; i < p.value.length; i++) {
  1598. var valueDate = new Date(p.value[i]);
  1599. p.wrapper.find('.picker-calendar-day[data-date="' + valueDate.getFullYear() + '-' + valueDate.getMonth() + '-' + valueDate.getDate() + '"]').addClass('picker-calendar-day-selected');
  1600. }
  1601. if (p.params.onChange) {
  1602. p.params.onChange(p, p.value, p.value.map(formatDate));
  1603. }
  1604. if (p.input && p.input.length > 0) {
  1605. if (p.params.formatValue) inputValue = p.params.formatValue(p, p.value);
  1606. else {
  1607. inputValue = [];
  1608. for (i = 0; i < p.value.length; i++) {
  1609. inputValue.push(formatDate(p.value[i]));
  1610. }
  1611. inputValue = inputValue.join(', ');
  1612. }
  1613. $(p.input).val(inputValue);
  1614. $(p.input).trigger('change');
  1615. }
  1616. };
  1617. // Columns Handlers
  1618. p.initCalendarEvents = function () {
  1619. var col;
  1620. var allowItemClick = true;
  1621. var isTouched, isMoved, touchStartX, touchStartY, touchCurrentX, touchCurrentY, touchStartTime, touchEndTime, startTranslate, currentTranslate, wrapperWidth, wrapperHeight, percentage, touchesDiff, isScrolling;
  1622. function handleTouchStart (e) {
  1623. if (isMoved || isTouched) return;
  1624. // e.preventDefault();
  1625. isTouched = true;
  1626. touchStartX = touchCurrentY = e.type === 'touchstart' ? e.targetTouches[0].pageX : e.pageX;
  1627. touchStartY = touchCurrentY = e.type === 'touchstart' ? e.targetTouches[0].pageY : e.pageY;
  1628. touchStartTime = (new Date()).getTime();
  1629. percentage = 0;
  1630. allowItemClick = true;
  1631. isScrolling = undefined;
  1632. startTranslate = currentTranslate = p.monthsTranslate;
  1633. }
  1634. function handleTouchMove (e) {
  1635. if (!isTouched) return;
  1636. touchCurrentX = e.type === 'touchmove' ? e.targetTouches[0].pageX : e.pageX;
  1637. touchCurrentY = e.type === 'touchmove' ? e.targetTouches[0].pageY : e.pageY;
  1638. if (typeof isScrolling === 'undefined') {
  1639. isScrolling = !!(isScrolling || Math.abs(touchCurrentY - touchStartY) > Math.abs(touchCurrentX - touchStartX));
  1640. }
  1641. if (p.isH && isScrolling) {
  1642. isTouched = false;
  1643. return;
  1644. }
  1645. e.preventDefault();
  1646. if (p.animating) {
  1647. isTouched = false;
  1648. return;
  1649. }
  1650. allowItemClick = false;
  1651. if (!isMoved) {
  1652. // First move
  1653. isMoved = true;
  1654. wrapperWidth = p.wrapper[0].offsetWidth;
  1655. wrapperHeight = p.wrapper[0].offsetHeight;
  1656. p.wrapper.transition(0);
  1657. }
  1658. e.preventDefault();
  1659. touchesDiff = p.isH ? touchCurrentX - touchStartX : touchCurrentY - touchStartY;
  1660. percentage = touchesDiff/(p.isH ? wrapperWidth : wrapperHeight);
  1661. currentTranslate = (p.monthsTranslate * inverter + percentage) * 100;
  1662. // Transform wrapper
  1663. p.wrapper.transform('translate3d(' + (p.isH ? currentTranslate : 0) + '%, ' + (p.isH ? 0 : currentTranslate) + '%, 0)');
  1664. }
  1665. function handleTouchEnd (e) {
  1666. if (!isTouched || !isMoved) {
  1667. isTouched = isMoved = false;
  1668. return;
  1669. }
  1670. isTouched = isMoved = false;
  1671. touchEndTime = new Date().getTime();
  1672. if (touchEndTime - touchStartTime < 300) {
  1673. if (Math.abs(touchesDiff) < 10) {
  1674. p.resetMonth();
  1675. }
  1676. else if (touchesDiff >= 10) {
  1677. if (rtl) p.nextMonth();
  1678. else p.prevMonth();
  1679. }
  1680. else {
  1681. if (rtl) p.prevMonth();
  1682. else p.nextMonth();
  1683. }
  1684. }
  1685. else {
  1686. if (percentage <= -0.5) {
  1687. if (rtl) p.prevMonth();
  1688. else p.nextMonth();
  1689. }
  1690. else if (percentage >= 0.5) {
  1691. if (rtl) p.nextMonth();
  1692. else p.prevMonth();
  1693. }
  1694. else {
  1695. p.resetMonth();
  1696. }
  1697. }
  1698. // Allow click
  1699. setTimeout(function () {
  1700. allowItemClick = true;
  1701. }, 100);
  1702. }
  1703. function handleDayClick(e) {
  1704. if (!allowItemClick) return;
  1705. var day = $(e.target).parents('.picker-calendar-day');
  1706. if (day.length === 0 && $(e.target).hasClass('picker-calendar-day')) {
  1707. day = $(e.target);
  1708. }
  1709. if (day.length === 0) return;
  1710. if (day.hasClass('picker-calendar-day-selected') && !p.params.multiple) return;
  1711. if (day.hasClass('picker-calendar-day-disabled')) return;
  1712. if (day.hasClass('picker-calendar-day-next')) p.nextMonth();
  1713. if (day.hasClass('picker-calendar-day-prev')) p.prevMonth();
  1714. var dateYear = day.attr('data-year');
  1715. var dateMonth = day.attr('data-month');
  1716. var dateDay = day.attr('data-day');
  1717. if (p.params.onDayClick) {
  1718. p.params.onDayClick(p, day[0], dateYear, dateMonth, dateDay);
  1719. }
  1720. p.addValue(new Date(dateYear, dateMonth, dateDay).getTime());
  1721. if (p.params.closeOnSelect) p.close();
  1722. }
  1723. p.container.find('.picker-calendar-prev-month').on('click', p.prevMonth);
  1724. p.container.find('.picker-calendar-next-month').on('click', p.nextMonth);
  1725. p.container.find('.picker-calendar-prev-year').on('click', p.prevYear);
  1726. p.container.find('.picker-calendar-next-year').on('click', p.nextYear);
  1727. /**
  1728. * 处理选择年份时的手势操作事件
  1729. *
  1730. * Start - edit by JSoon
  1731. */
  1732. function handleYearTouchStart (e) {
  1733. if (isMoved || isTouched) return;
  1734. // e.preventDefault();
  1735. isTouched = true;
  1736. touchStartX = touchCurrentY = e.type === 'touchstart' ? e.targetTouches[0].pageX : e.pageX;
  1737. touchStartY = touchCurrentY = e.type === 'touchstart' ? e.targetTouches[0].pageY : e.pageY;
  1738. touchStartTime = (new Date()).getTime();
  1739. percentage = 0;
  1740. allowItemClick = true;
  1741. isScrolling = undefined;
  1742. startTranslate = currentTranslate = p.yearsTranslate;
  1743. }
  1744. function handleYearTouchMove (e) {
  1745. if (!isTouched) return;
  1746. touchCurrentX = e.type === 'touchmove' ? e.targetTouches[0].pageX : e.pageX;
  1747. touchCurrentY = e.type === 'touchmove' ? e.targetTouches[0].pageY : e.pageY;
  1748. if (typeof isScrolling === 'undefined') {
  1749. isScrolling = !!(isScrolling || Math.abs(touchCurrentY - touchStartY) > Math.abs(touchCurrentX - touchStartX));
  1750. }
  1751. if (p.isH && isScrolling) {
  1752. isTouched = false;
  1753. return;
  1754. }
  1755. e.preventDefault();
  1756. if (p.animating) {
  1757. isTouched = false;
  1758. return;
  1759. }
  1760. allowItemClick = false;
  1761. if (!isMoved) {
  1762. // First move
  1763. isMoved = true;
  1764. wrapperWidth = p.yearsPickerWrapper[0].offsetWidth;
  1765. wrapperHeight = p.yearsPickerWrapper[0].offsetHeight;
  1766. p.yearsPickerWrapper.transition(0);
  1767. }
  1768. e.preventDefault();
  1769. touchesDiff = p.isH ? touchCurrentX - touchStartX : touchCurrentY - touchStartY;
  1770. percentage = touchesDiff/(p.isH ? wrapperWidth : wrapperHeight);
  1771. currentTranslate = (p.yearsTranslate * inverter + percentage) * 100;
  1772. // Transform wrapper
  1773. p.yearsPickerWrapper.transform('translate3d(' + (p.isH ? currentTranslate : 0) + '%, ' + (p.isH ? 0 : currentTranslate) + '%, 0)');
  1774. }
  1775. function handleYearTouchEnd (e) {
  1776. if (!isTouched || !isMoved) {
  1777. isTouched = isMoved = false;
  1778. return;
  1779. }
  1780. isTouched = isMoved = false;
  1781. touchEndTime = new Date().getTime();
  1782. if (touchEndTime - touchStartTime < 300) {
  1783. if (Math.abs(touchesDiff) < 10) {
  1784. p.resetYearsGroup();
  1785. }
  1786. else if (touchesDiff >= 10) {
  1787. if (rtl) p.nextYearsGroup();
  1788. else p.prevYearsGroup();
  1789. }
  1790. else {
  1791. if (rtl) p.prevYearsGroup();
  1792. else p.nextYearsGroup();
  1793. }
  1794. }
  1795. else {
  1796. if (percentage <= -0.5) {
  1797. if (rtl) p.prevYearsGroup();
  1798. else p.nextYearsGroup();
  1799. }
  1800. else if (percentage >= 0.5) {
  1801. if (rtl) p.nextYearsGroup();
  1802. else p.prevYearsGroup();
  1803. }
  1804. else {
  1805. p.resetYearsGroup();
  1806. }
  1807. }
  1808. // Allow click
  1809. setTimeout(function () {
  1810. allowItemClick = true;
  1811. }, 100);
  1812. }
  1813. function handleYearSelector() {
  1814. var curYear = $(this).text(),
  1815. yearsPicker = p.container.find('.picker-calendar-years-picker');
  1816. yearsPicker.show().transform('translate3d(0, 0, 0)');
  1817. p.updateSelectedInPickers();
  1818. yearsPicker.on('click', '.picker-calendar-year-unit', p.pickYear);
  1819. }
  1820. function handleMonthSelector() {
  1821. var monthsPicker = p.container.find('.picker-calendar-months-picker');
  1822. monthsPicker.show().transform('translate3d(0, 0, 0)');
  1823. p.updateSelectedInPickers();
  1824. monthsPicker.on('click', '.picker-calendar-month-unit', p.pickMonth);
  1825. }
  1826. // 选择年份
  1827. p.container.find('.current-year-value').on('click', handleYearSelector);
  1828. // 选择月份
  1829. p.container.find('.current-month-value').on('click', handleMonthSelector);
  1830. /**
  1831. * End - edit by JSoon
  1832. */
  1833. p.wrapper.on('click', handleDayClick);
  1834. if (p.params.touchMove) {
  1835. /**
  1836. * 给年份选择器绑定手势操作事件
  1837. *
  1838. * Start - edit by JSoon
  1839. */
  1840. p.yearsPickerWrapper.on($.touchEvents.start, handleYearTouchStart);
  1841. p.yearsPickerWrapper.on($.touchEvents.move, handleYearTouchMove);
  1842. p.yearsPickerWrapper.on($.touchEvents.end, handleYearTouchEnd);
  1843. /**
  1844. * Start - edit by JSoon
  1845. */
  1846. p.wrapper.on($.touchEvents.start, handleTouchStart);
  1847. p.wrapper.on($.touchEvents.move, handleTouchMove);
  1848. p.wrapper.on($.touchEvents.end, handleTouchEnd);
  1849. }
  1850. p.container[0].f7DestroyCalendarEvents = function () {
  1851. p.container.find('.picker-calendar-prev-month').off('click', p.prevMonth);
  1852. p.container.find('.picker-calendar-next-month').off('click', p.nextMonth);
  1853. p.container.find('.picker-calendar-prev-year').off('click', p.prevYear);
  1854. p.container.find('.picker-calendar-next-year').off('click', p.nextYear);
  1855. p.wrapper.off('click', handleDayClick);
  1856. if (p.params.touchMove) {
  1857. p.wrapper.off($.touchEvents.start, handleTouchStart);
  1858. p.wrapper.off($.touchEvents.move, handleTouchMove);
  1859. p.wrapper.off($.touchEvents.end, handleTouchEnd);
  1860. }
  1861. };
  1862. };
  1863. p.destroyCalendarEvents = function (colContainer) {
  1864. if ('f7DestroyCalendarEvents' in p.container[0]) p.container[0].f7DestroyCalendarEvents();
  1865. };
  1866. // Calendar Methods
  1867. /**
  1868. * 1. 生成年份和月份选择器DOM结构
  1869. * 2. 年份选择和月份选择的pick事件函数
  1870. * 3. 年份选择手势操作结束后,更新年分组DOM结构
  1871. *
  1872. * Start - edit by JSoon
  1873. */
  1874. p.yearsGroupHTML = function(date, offset) {
  1875. date = new Date(date);
  1876. var curYear = date.getFullYear(), // 日历上的当前年份
  1877. trueYear = new Date().getFullYear(), // 当前真实年份
  1878. yearNum = 25, // 年份面板年份总数量
  1879. firstYear = curYear - Math.floor(yearNum/2), // 年份面板第一格年份
  1880. yearsHTML = '';
  1881. if (offset === 'next') {
  1882. firstYear = firstYear + yearNum;
  1883. }
  1884. if (offset === 'prev') {
  1885. firstYear = firstYear - yearNum;
  1886. }
  1887. for (var i = 0; i < 5; i += 1) {
  1888. var rowHTML = '';
  1889. var row = i;
  1890. rowHTML += '<div class="picker-calendar-row">';
  1891. for (var j = 0; j < 5; j += 1) {
  1892. if (firstYear === trueYear) {
  1893. rowHTML += '<div class="picker-calendar-year-unit current-calendar-year-unit" data-year="'+ firstYear +'"><span>' + firstYear + '</span></div>';
  1894. } else if (firstYear === curYear) {
  1895. rowHTML += '<div class="picker-calendar-year-unit picker-calendar-year-unit-selected" data-year="'+ firstYear +'"><span>' + firstYear + '</span></div>';
  1896. } else {
  1897. rowHTML += '<div class="picker-calendar-year-unit" data-year="'+ firstYear +'"><span>' + firstYear + '</span></div>';
  1898. }
  1899. firstYear += 1;
  1900. }
  1901. rowHTML += '</div>';
  1902. yearsHTML += rowHTML;
  1903. }
  1904. yearsHTML = '<div class="picker-calendar-years-group">' + yearsHTML + '</div>';
  1905. return yearsHTML;
  1906. };
  1907. p.pickYear = function() {
  1908. var year = $(this).text(),
  1909. curYear = p.wrapper.find('.picker-calendar-month-current').attr('data-year');
  1910. p.yearsPickerWrapper.find('.picker-calendar-year-unit').removeClass('picker-calendar-year-unit-selected');
  1911. $(this).addClass('picker-calendar-year-unit-selected');
  1912. if (curYear !== year) {
  1913. p.setYearMonth(year);
  1914. p.container.find('.picker-calendar-years-picker').hide().transform('translate3d(0, 100%, 0)');
  1915. } else {
  1916. p.container.find('.picker-calendar-years-picker').transform('translate3d(0, 100%, 0)');
  1917. }
  1918. };
  1919. p.onYearsChangeEnd = function (dir) {
  1920. p.animating = false;
  1921. var nextYearsHTML, prevYearsHTML, newCurFirstYear;
  1922. var yearNum = p.yearsPickerWrapper.children('.picker-calendar-years-next').find('.picker-calendar-year-unit').length;
  1923. if (dir === 'next') {
  1924. var newCurFirstYear = parseInt(p.yearsPickerWrapper.children('.picker-calendar-years-next').find('.picker-calendar-year-unit').eq(Math.floor(yearNum/2)).text());
  1925. nextYearsHTML = p.yearsGroupHTML(new Date(newCurFirstYear, p.currentMonth), 'next');
  1926. p.yearsPickerWrapper.append(nextYearsHTML);
  1927. p.yearsPickerWrapper.children().first().remove();
  1928. p.yearsGroups = p.container.find('.picker-calendar-years-group');
  1929. }
  1930. if (dir === 'prev') {
  1931. var newCurFirstYear = parseInt(p.yearsPickerWrapper.children('.picker-calendar-years-prev').find('.picker-calendar-year-unit').eq(Math.floor(yearNum/2)).text());
  1932. prevYearsHTML = p.yearsGroupHTML(new Date(newCurFirstYear, p.currentMonth), 'prev');
  1933. p.yearsPickerWrapper.prepend(prevYearsHTML);
  1934. p.yearsPickerWrapper.children().last().remove();
  1935. p.yearsGroups = p.container.find('.picker-calendar-years-group');
  1936. }
  1937. p.setYearsTranslate(p.yearsTranslate);
  1938. };
  1939. p.monthsGroupHTML = function(date) {
  1940. date = new Date(date);
  1941. var curMonth = date.getMonth() + 1, // 日历上的当前月份
  1942. trueMonth = new Date().getMonth() + 1, // 当前真实月份
  1943. monthNum = 12, // 月份面板月份总数量
  1944. firstMonth = 1,
  1945. monthsHTML = '';
  1946. for (var i = 0; i < 3; i += 1) {
  1947. var rowHTML = '';
  1948. var row = i;
  1949. rowHTML += '<div class="picker-calendar-row">';
  1950. for (var j = 0; j < 4; j += 1) {
  1951. if (firstMonth === trueMonth) {
  1952. rowHTML += '<div class="picker-calendar-month-unit current-calendar-month-unit" data-month="'+ (firstMonth-1) +'"><span>' + p.params.monthNames[firstMonth-1] + '</span></div>';
  1953. } else if (firstMonth === curMonth) {
  1954. rowHTML += '<div class="picker-calendar-month-unit picker-calendar-month-selected" data-month="'+ (firstMonth-1) +'"><span>' + p.params.monthNames[firstMonth-1] + '</span></div>';
  1955. } else {
  1956. rowHTML += '<div class="picker-calendar-month-unit" data-month="'+ (firstMonth-1) +'"><span>' + p.params.monthNames[firstMonth-1] + '</span></div>';
  1957. }
  1958. firstMonth += 1;
  1959. }
  1960. rowHTML += '</div>';
  1961. monthsHTML += rowHTML;
  1962. }
  1963. monthsHTML = '<div class="picker-calendar-months-group">' + monthsHTML + '</div>';
  1964. return monthsHTML;
  1965. };
  1966. p.pickMonth = function() {
  1967. var month = $(this).attr('data-month'),
  1968. curYear = p.wrapper.find('.picker-calendar-month-current').attr('data-year'),
  1969. curMonth = p.wrapper.find('.picker-calendar-month-current').attr('data-month');
  1970. p.monthsPickerWrapper.find('.picker-calendar-month-unit').removeClass('picker-calendar-month-unit-selected');
  1971. $(this).addClass('picker-calendar-month-unit-selected');
  1972. if (curMonth !== month) {
  1973. p.setYearMonth(curYear, month);
  1974. p.container.find('.picker-calendar-months-picker').hide().transform('translate3d(0, 100%, 0)');
  1975. } else {
  1976. p.container.find('.picker-calendar-months-picker').transform('translate3d(0, 100%, 0)');
  1977. }
  1978. };
  1979. /**
  1980. * End - edit by JSoon
  1981. */
  1982. p.daysInMonth = function (date) {
  1983. var d = new Date(date);
  1984. return new Date(d.getFullYear(), d.getMonth() + 1, 0).getDate();
  1985. };
  1986. p.monthHTML = function (date, offset) {
  1987. date = new Date(date);
  1988. var year = date.getFullYear(),
  1989. month = date.getMonth(),
  1990. day = date.getDate();
  1991. if (offset === 'next') {
  1992. if (month === 11) date = new Date(year + 1, 0);
  1993. else date = new Date(year, month + 1, 1);
  1994. }
  1995. if (offset === 'prev') {
  1996. if (month === 0) date = new Date(year - 1, 11);
  1997. else date = new Date(year, month - 1, 1);
  1998. }
  1999. if (offset === 'next' || offset === 'prev') {
  2000. month = date.getMonth();
  2001. year = date.getFullYear();
  2002. }
  2003. var daysInPrevMonth = p.daysInMonth(new Date(date.getFullYear(), date.getMonth()).getTime() - 10 * 24 * 60 * 60 * 1000),
  2004. daysInMonth = p.daysInMonth(date),
  2005. firstDayOfMonthIndex = new Date(date.getFullYear(), date.getMonth()).getDay();
  2006. if (firstDayOfMonthIndex === 0) firstDayOfMonthIndex = 7;
  2007. var dayDate, currentValues = [], i, j,
  2008. rows = 6, cols = 7,
  2009. monthHTML = '',
  2010. dayIndex = 0 + (p.params.firstDay - 1),
  2011. today = new Date().setHours(0,0,0,0),
  2012. minDate = p.params.minDate ? new Date(p.params.minDate).getTime() : null,
  2013. maxDate = p.params.maxDate ? new Date(p.params.maxDate).getTime() : null;
  2014. if (p.value && p.value.length) {
  2015. for (i = 0; i < p.value.length; i++) {
  2016. currentValues.push(new Date(p.value[i]).setHours(0,0,0,0));
  2017. }
  2018. }
  2019. for (i = 1; i <= rows; i++) {
  2020. var rowHTML = '';
  2021. var row = i;
  2022. for (j = 1; j <= cols; j++) {
  2023. var col = j;
  2024. dayIndex ++;
  2025. var dayNumber = dayIndex - firstDayOfMonthIndex;
  2026. var addClass = '';
  2027. if (dayNumber < 0) {
  2028. dayNumber = daysInPrevMonth + dayNumber + 1;
  2029. addClass += ' picker-calendar-day-prev';
  2030. dayDate = new Date(month - 1 < 0 ? year - 1 : year, month - 1 < 0 ? 11 : month - 1, dayNumber).getTime();
  2031. }
  2032. else {
  2033. dayNumber = dayNumber + 1;
  2034. if (dayNumber > daysInMonth) {
  2035. dayNumber = dayNumber - daysInMonth;
  2036. addClass += ' picker-calendar-day-next';
  2037. dayDate = new Date(month + 1 > 11 ? year + 1 : year, month + 1 > 11 ? 0 : month + 1, dayNumber).getTime();
  2038. }
  2039. else {
  2040. dayDate = new Date(year, month, dayNumber).getTime();
  2041. }
  2042. }
  2043. // Today
  2044. if (dayDate === today) addClass += ' picker-calendar-day-today';
  2045. // Selected
  2046. if (currentValues.indexOf(dayDate) >= 0) addClass += ' picker-calendar-day-selected';
  2047. // Weekend
  2048. if (p.params.weekendDays.indexOf(col - 1) >= 0) {
  2049. addClass += ' picker-calendar-day-weekend';
  2050. }
  2051. // Disabled
  2052. if ((minDate && dayDate < minDate) || (maxDate && dayDate > maxDate)) {
  2053. addClass += ' picker-calendar-day-disabled';
  2054. }
  2055. dayDate = new Date(dayDate);
  2056. var dayYear = dayDate.getFullYear();
  2057. var dayMonth = dayDate.getMonth();
  2058. 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>';
  2059. }
  2060. monthHTML += '<div class="picker-calendar-row">' + rowHTML + '</div>';
  2061. }
  2062. monthHTML = '<div class="picker-calendar-month" data-year="' + year + '" data-month="' + month + '">' + monthHTML + '</div>';
  2063. return monthHTML;
  2064. };
  2065. p.animating = false;
  2066. p.updateCurrentMonthYear = function (dir) {
  2067. if (typeof dir === 'undefined') {
  2068. p.currentMonth = parseInt(p.months.eq(1).attr('data-month'), 10);
  2069. p.currentYear = parseInt(p.months.eq(1).attr('data-year'), 10);
  2070. }
  2071. else {
  2072. p.currentMonth = parseInt(p.months.eq(dir === 'next' ? (p.months.length - 1) : 0).attr('data-month'), 10);
  2073. p.currentYear = parseInt(p.months.eq(dir === 'next' ? (p.months.length - 1) : 0).attr('data-year'), 10);
  2074. }
  2075. p.container.find('.current-month-value').text(p.params.monthNames[p.currentMonth]);
  2076. p.container.find('.current-year-value').text(p.currentYear);
  2077. };
  2078. p.onMonthChangeStart = function (dir) {
  2079. p.updateCurrentMonthYear(dir);
  2080. p.months.removeClass('picker-calendar-month-current picker-calendar-month-prev picker-calendar-month-next');
  2081. var currentIndex = dir === 'next' ? p.months.length - 1 : 0;
  2082. p.months.eq(currentIndex).addClass('picker-calendar-month-current');
  2083. p.months.eq(dir === 'next' ? currentIndex - 1 : currentIndex + 1).addClass(dir === 'next' ? 'picker-calendar-month-prev' : 'picker-calendar-month-next');
  2084. if (p.params.onMonthYearChangeStart) {
  2085. p.params.onMonthYearChangeStart(p, p.currentYear, p.currentMonth);
  2086. }
  2087. };
  2088. p.onMonthChangeEnd = function (dir, rebuildBoth) {
  2089. p.animating = false;
  2090. var nextMonthHTML, prevMonthHTML, newMonthHTML;
  2091. p.wrapper.find('.picker-calendar-month:not(.picker-calendar-month-prev):not(.picker-calendar-month-current):not(.picker-calendar-month-next)').remove();
  2092. if (typeof dir === 'undefined') {
  2093. dir = 'next';
  2094. rebuildBoth = true;
  2095. }
  2096. if (!rebuildBoth) {
  2097. newMonthHTML = p.monthHTML(new Date(p.currentYear, p.currentMonth), dir);
  2098. }
  2099. else {
  2100. p.wrapper.find('.picker-calendar-month-next, .picker-calendar-month-prev').remove();
  2101. prevMonthHTML = p.monthHTML(new Date(p.currentYear, p.currentMonth), 'prev');
  2102. nextMonthHTML = p.monthHTML(new Date(p.currentYear, p.currentMonth), 'next');
  2103. }
  2104. if (dir === 'next' || rebuildBoth) {
  2105. p.wrapper.append(newMonthHTML || nextMonthHTML);
  2106. }
  2107. if (dir === 'prev' || rebuildBoth) {
  2108. p.wrapper.prepend(newMonthHTML || prevMonthHTML);
  2109. }
  2110. p.months = p.wrapper.find('.picker-calendar-month');
  2111. p.setMonthsTranslate(p.monthsTranslate);
  2112. if (p.params.onMonthAdd) {
  2113. p.params.onMonthAdd(p, dir === 'next' ? p.months.eq(p.months.length - 1)[0] : p.months.eq(0)[0]);
  2114. }
  2115. if (p.params.onMonthYearChangeEnd) {
  2116. p.params.onMonthYearChangeEnd(p, p.currentYear, p.currentMonth);
  2117. }
  2118. /**
  2119. * 月历面板结束手势操作后,更新年份/月份选择器中的选中高亮状态
  2120. *
  2121. * Start - edit by JSoon
  2122. */
  2123. p.updateSelectedInPickers();
  2124. /**
  2125. * End - edit by JSoon
  2126. */
  2127. };
  2128. /**
  2129. * 1. 更新年份/月份选择器中的选中高亮状态函数
  2130. * 2. 年份选择器过渡动画函数
  2131. * 3. 下一个/上一个/当前年分组手势操作函数
  2132. *
  2133. * Start - edit by JSoon
  2134. */
  2135. p.updateSelectedInPickers = function() {
  2136. var curYear = parseInt(p.wrapper.find('.picker-calendar-month-current').attr('data-year'), 10),
  2137. trueYear = new Date().getFullYear(),
  2138. curMonth = parseInt(p.wrapper.find('.picker-calendar-month-current').attr('data-month'), 10),
  2139. trueMonth = new Date().getMonth(),
  2140. selectedYear = parseInt(p.yearsPickerWrapper.find('.picker-calendar-year-unit-selected').attr('data-year'), 10),
  2141. selectedMonth = parseInt(p.monthsPickerWrapper.find('.picker-calendar-month-unit-selected').attr('data-month'), 10);
  2142. if (selectedYear !== curYear) {
  2143. p.yearsPickerWrapper.find('.picker-calendar-year-unit').removeClass('picker-calendar-year-unit-selected');
  2144. p.yearsPickerWrapper.find('.picker-calendar-year-unit[data-year="' + curYear + '"]').addClass('picker-calendar-year-unit-selected');
  2145. }
  2146. if (selectedMonth !== curMonth) {
  2147. p.monthsPickerWrapper.find('.picker-calendar-month-unit').removeClass('picker-calendar-month-unit-selected');
  2148. p.monthsPickerWrapper.find('.picker-calendar-month-unit[data-month="' + curMonth + '"]').addClass('picker-calendar-month-unit-selected');
  2149. }
  2150. if (trueYear !== curYear) {
  2151. p.monthsPickerWrapper.find('.picker-calendar-month-unit').removeClass('current-calendar-month-unit');
  2152. } else {
  2153. p.monthsPickerWrapper.find('.picker-calendar-month-unit[data-month="' + trueMonth + '"]').addClass('current-calendar-month-unit');
  2154. }
  2155. };
  2156. p.setYearsTranslate = function (translate) {
  2157. translate = translate || p.yearsTranslate || 0;
  2158. if (typeof p.yearsTranslate === 'undefined') p.yearsTranslate = translate;
  2159. p.yearsGroups.removeClass('picker-calendar-years-current picker-calendar-years-prev picker-calendar-years-next');
  2160. var prevYearTranslate = -(translate + 1) * 100 * inverter;
  2161. var currentYearTranslate = -translate * 100 * inverter;
  2162. var nextYearTranslate = -(translate - 1) * 100 * inverter;
  2163. p.yearsGroups.eq(0).transform('translate3d(' + (p.isH ? prevYearTranslate : 0) + '%, ' + (p.isH ? 0 : prevYearTranslate) + '%, 0)').addClass('picker-calendar-years-prev');
  2164. p.yearsGroups.eq(1).transform('translate3d(' + (p.isH ? currentYearTranslate : 0) + '%, ' + (p.isH ? 0 : currentYearTranslate) + '%, 0)').addClass('picker-calendar-years-current');
  2165. p.yearsGroups.eq(2).transform('translate3d(' + (p.isH ? nextYearTranslate : 0) + '%, ' + (p.isH ? 0 : nextYearTranslate) + '%, 0)').addClass('picker-calendar-years-next');
  2166. };
  2167. p.nextYearsGroup = function (transition) {
  2168. if (typeof transition === 'undefined' || typeof transition === 'object') {
  2169. transition = '';
  2170. if (!p.params.animate) transition = 0;
  2171. }
  2172. var transitionEndCallback = p.animating ? false : true;
  2173. p.yearsTranslate --;
  2174. p.animating = true;
  2175. var translate = (p.yearsTranslate * 100) * inverter;
  2176. p.yearsPickerWrapper.transition(transition).transform('translate3d(' + (p.isH ? translate : 0) + '%, ' + (p.isH ? 0 : translate) + '%, 0)');
  2177. if (transitionEndCallback) {
  2178. p.yearsPickerWrapper.transitionEnd(function () {
  2179. p.onYearsChangeEnd('next');
  2180. });
  2181. }
  2182. if (!p.params.animate) {
  2183. p.onYearsChangeEnd('next');
  2184. }
  2185. };
  2186. p.prevYearsGroup = function (transition) {
  2187. if (typeof transition === 'undefined' || typeof transition === 'object') {
  2188. transition = '';
  2189. if (!p.params.animate) transition = 0;
  2190. }
  2191. var transitionEndCallback = p.animating ? false : true;
  2192. p.yearsTranslate ++;
  2193. p.animating = true;
  2194. var translate = (p.yearsTranslate * 100) * inverter;
  2195. p.yearsPickerWrapper.transition(transition).transform('translate3d(' + (p.isH ? translate : 0) + '%, ' + (p.isH ? 0 : translate) + '%, 0)');
  2196. if (transitionEndCallback) {
  2197. p.yearsPickerWrapper.transitionEnd(function () {
  2198. p.onYearsChangeEnd('prev');
  2199. });
  2200. }
  2201. if (!p.params.animate) {
  2202. p.onYearsChangeEnd('prev');
  2203. }
  2204. };
  2205. p.resetYearsGroup = function (transition) {
  2206. if (typeof transition === 'undefined') transition = '';
  2207. var translate = (p.yearsTranslate * 100) * inverter;
  2208. p.yearsPickerWrapper.transition(transition).transform('translate3d(' + (p.isH ? translate : 0) + '%, ' + (p.isH ? 0 : translate) + '%, 0)');
  2209. };
  2210. /**
  2211. * End - edit by JSoon
  2212. */
  2213. p.setMonthsTranslate = function (translate) {
  2214. translate = translate || p.monthsTranslate || 0;
  2215. if (typeof p.monthsTranslate === 'undefined') p.monthsTranslate = translate;
  2216. p.months.removeClass('picker-calendar-month-current picker-calendar-month-prev picker-calendar-month-next');
  2217. var prevMonthTranslate = -(translate + 1) * 100 * inverter;
  2218. var currentMonthTranslate = -translate * 100 * inverter;
  2219. var nextMonthTranslate = -(translate - 1) * 100 * inverter;
  2220. p.months.eq(0).transform('translate3d(' + (p.isH ? prevMonthTranslate : 0) + '%, ' + (p.isH ? 0 : prevMonthTranslate) + '%, 0)').addClass('picker-calendar-month-prev');
  2221. p.months.eq(1).transform('translate3d(' + (p.isH ? currentMonthTranslate : 0) + '%, ' + (p.isH ? 0 : currentMonthTranslate) + '%, 0)').addClass('picker-calendar-month-current');
  2222. p.months.eq(2).transform('translate3d(' + (p.isH ? nextMonthTranslate : 0) + '%, ' + (p.isH ? 0 : nextMonthTranslate) + '%, 0)').addClass('picker-calendar-month-next');
  2223. };
  2224. p.nextMonth = function (transition) {
  2225. if (typeof transition === 'undefined' || typeof transition === 'object') {
  2226. transition = '';
  2227. if (!p.params.animate) transition = 0;
  2228. }
  2229. var nextMonth = parseInt(p.months.eq(p.months.length - 1).attr('data-month'), 10);
  2230. var nextYear = parseInt(p.months.eq(p.months.length - 1).attr('data-year'), 10);
  2231. var nextDate = new Date(nextYear, nextMonth);
  2232. var nextDateTime = nextDate.getTime();
  2233. var transitionEndCallback = p.animating ? false : true;
  2234. if (p.params.maxDate) {
  2235. if (nextDateTime > new Date(p.params.maxDate).getTime()) {
  2236. return p.resetMonth();
  2237. }
  2238. }
  2239. p.monthsTranslate --;
  2240. if (nextMonth === p.currentMonth) {
  2241. var nextMonthTranslate = -(p.monthsTranslate) * 100 * inverter;
  2242. var nextMonthHTML = $(p.monthHTML(nextDateTime, 'next')).transform('translate3d(' + (p.isH ? nextMonthTranslate : 0) + '%, ' + (p.isH ? 0 : nextMonthTranslate) + '%, 0)').addClass('picker-calendar-month-next');
  2243. p.wrapper.append(nextMonthHTML[0]);
  2244. p.months = p.wrapper.find('.picker-calendar-month');
  2245. if (p.params.onMonthAdd) {
  2246. p.params.onMonthAdd(p, p.months.eq(p.months.length - 1)[0]);
  2247. }
  2248. }
  2249. p.animating = true;
  2250. p.onMonthChangeStart('next');
  2251. var translate = (p.monthsTranslate * 100) * inverter;
  2252. p.wrapper.transition(transition).transform('translate3d(' + (p.isH ? translate : 0) + '%, ' + (p.isH ? 0 : translate) + '%, 0)');
  2253. if (transitionEndCallback) {
  2254. p.wrapper.transitionEnd(function () {
  2255. p.onMonthChangeEnd('next');
  2256. });
  2257. }
  2258. if (!p.params.animate) {
  2259. p.onMonthChangeEnd('next');
  2260. }
  2261. };
  2262. p.prevMonth = function (transition) {
  2263. if (typeof transition === 'undefined' || typeof transition === 'object') {
  2264. transition = '';
  2265. if (!p.params.animate) transition = 0;
  2266. }
  2267. var prevMonth = parseInt(p.months.eq(0).attr('data-month'), 10);
  2268. var prevYear = parseInt(p.months.eq(0).attr('data-year'), 10);
  2269. var prevDate = new Date(prevYear, prevMonth + 1, -1);
  2270. var prevDateTime = prevDate.getTime();
  2271. var transitionEndCallback = p.animating ? false : true;
  2272. if (p.params.minDate) {
  2273. if (prevDateTime < new Date(p.params.minDate).getTime()) {
  2274. return p.resetMonth();
  2275. }
  2276. }
  2277. p.monthsTranslate ++;
  2278. if (prevMonth === p.currentMonth) {
  2279. var prevMonthTranslate = -(p.monthsTranslate) * 100 * inverter;
  2280. var prevMonthHTML = $(p.monthHTML(prevDateTime, 'prev')).transform('translate3d(' + (p.isH ? prevMonthTranslate : 0) + '%, ' + (p.isH ? 0 : prevMonthTranslate) + '%, 0)').addClass('picker-calendar-month-prev');
  2281. p.wrapper.prepend(prevMonthHTML[0]);
  2282. p.months = p.wrapper.find('.picker-calendar-month');
  2283. if (p.params.onMonthAdd) {
  2284. p.params.onMonthAdd(p, p.months.eq(0)[0]);
  2285. }
  2286. }
  2287. p.animating = true;
  2288. p.onMonthChangeStart('prev');
  2289. var translate = (p.monthsTranslate * 100) * inverter;
  2290. p.wrapper.transition(transition).transform('translate3d(' + (p.isH ? translate : 0) + '%, ' + (p.isH ? 0 : translate) + '%, 0)');
  2291. if (transitionEndCallback) {
  2292. p.wrapper.transitionEnd(function () {
  2293. p.onMonthChangeEnd('prev');
  2294. });
  2295. }
  2296. if (!p.params.animate) {
  2297. p.onMonthChangeEnd('prev');
  2298. }
  2299. };
  2300. p.resetMonth = function (transition) {
  2301. if (typeof transition === 'undefined') transition = '';
  2302. var translate = (p.monthsTranslate * 100) * inverter;
  2303. p.wrapper.transition(transition).transform('translate3d(' + (p.isH ? translate : 0) + '%, ' + (p.isH ? 0 : translate) + '%, 0)');
  2304. };
  2305. p.setYearMonth = function (year, month, transition) {
  2306. if (typeof year === 'undefined') year = p.currentYear;
  2307. if (typeof month === 'undefined') month = p.currentMonth;
  2308. if (typeof transition === 'undefined' || typeof transition === 'object') {
  2309. transition = '';
  2310. if (!p.params.animate) transition = 0;
  2311. }
  2312. var targetDate;
  2313. if (year < p.currentYear) {
  2314. targetDate = new Date(year, month + 1, -1).getTime();
  2315. } else {
  2316. targetDate = new Date(year, month).getTime();
  2317. }
  2318. if (p.params.maxDate && targetDate > new Date(p.params.maxDate).getTime()) {
  2319. return false;
  2320. }
  2321. if (p.params.minDate && targetDate < new Date(p.params.minDate).getTime()) {
  2322. return false;
  2323. }
  2324. var currentDate = new Date(p.currentYear, p.currentMonth).getTime();
  2325. var dir = targetDate > currentDate ? 'next' : 'prev';
  2326. var newMonthHTML = p.monthHTML(new Date(year, month));
  2327. p.monthsTranslate = p.monthsTranslate || 0;
  2328. var prevTranslate = p.monthsTranslate;
  2329. var monthTranslate, wrapperTranslate;
  2330. var transitionEndCallback = p.animating ? false : true;
  2331. if (targetDate > currentDate) {
  2332. // To next
  2333. p.monthsTranslate --;
  2334. if (!p.animating) p.months.eq(p.months.length - 1).remove();
  2335. p.wrapper.append(newMonthHTML);
  2336. p.months = p.wrapper.find('.picker-calendar-month');
  2337. monthTranslate = -(prevTranslate - 1) * 100 * inverter;
  2338. p.months.eq(p.months.length - 1).transform('translate3d(' + (p.isH ? monthTranslate : 0) + '%, ' + (p.isH ? 0 : monthTranslate) + '%, 0)').addClass('picker-calendar-month-next');
  2339. }
  2340. else {
  2341. // To prev
  2342. p.monthsTranslate ++;
  2343. if (!p.animating) p.months.eq(0).remove();
  2344. p.wrapper.prepend(newMonthHTML);
  2345. p.months = p.wrapper.find('.picker-calendar-month');
  2346. monthTranslate = -(prevTranslate + 1) * 100 * inverter;
  2347. p.months.eq(0).transform('translate3d(' + (p.isH ? monthTranslate : 0) + '%, ' + (p.isH ? 0 : monthTranslate) + '%, 0)').addClass('picker-calendar-month-prev');
  2348. }
  2349. if (p.params.onMonthAdd) {
  2350. p.params.onMonthAdd(p, dir === 'next' ? p.months.eq(p.months.length - 1)[0] : p.months.eq(0)[0]);
  2351. }
  2352. p.animating = true;
  2353. p.onMonthChangeStart(dir);
  2354. wrapperTranslate = (p.monthsTranslate * 100) * inverter;
  2355. p.wrapper.transition(transition).transform('translate3d(' + (p.isH ? wrapperTranslate : 0) + '%, ' + (p.isH ? 0 : wrapperTranslate) + '%, 0)');
  2356. if (transitionEndCallback) {
  2357. p.wrapper.transitionEnd(function () {
  2358. p.onMonthChangeEnd(dir, true);
  2359. });
  2360. }
  2361. if (!p.params.animate) {
  2362. p.onMonthChangeEnd(dir);
  2363. }
  2364. };
  2365. p.nextYear = function () {
  2366. p.setYearMonth(p.currentYear + 1);
  2367. };
  2368. p.prevYear = function () {
  2369. p.setYearMonth(p.currentYear - 1);
  2370. };
  2371. // HTML Layout
  2372. p.layout = function () {
  2373. var pickerHTML = '';
  2374. var pickerClass = '';
  2375. var i;
  2376. var layoutDate = p.value && p.value.length ? p.value[0] : new Date().setHours(0,0,0,0);
  2377. /**
  2378. * 生成年份组和月份组DOM
  2379. *
  2380. * Start - edit by JSoon
  2381. */
  2382. var prevYearsHTML = p.yearsGroupHTML(layoutDate, 'prev');
  2383. var currentYearsHTML = p.yearsGroupHTML(layoutDate);
  2384. var nextYearsHTML = p.yearsGroupHTML(layoutDate, 'next');
  2385. var yearsGroupHTML = '<div class="picker-calendar-years-picker"><div class="picker-calendar-years-picker-wrapper">' + (prevYearsHTML + currentYearsHTML + nextYearsHTML) + '</div></div>';
  2386. var monthsGroupHTML = '<div class="picker-calendar-months-picker"><div class="picker-calendar-months-picker-wrapper">' + p.monthsGroupHTML(layoutDate) + '</div></div>';
  2387. /**
  2388. * End - edit by JSoon
  2389. */
  2390. var prevMonthHTML = p.monthHTML(layoutDate, 'prev');
  2391. var currentMonthHTML = p.monthHTML(layoutDate);
  2392. var nextMonthHTML = p.monthHTML(layoutDate, 'next');
  2393. var monthsHTML = '<div class="picker-calendar-months"><div class="picker-calendar-months-wrapper">' + (prevMonthHTML + currentMonthHTML + nextMonthHTML) + '</div></div>';
  2394. // Week days header
  2395. var weekHeaderHTML = '';
  2396. if (p.params.weekHeader) {
  2397. for (i = 0; i < 7; i++) {
  2398. var weekDayIndex = (i + p.params.firstDay > 6) ? (i - 7 + p.params.firstDay) : (i + p.params.firstDay);
  2399. var dayName = p.params.dayNamesShort[weekDayIndex];
  2400. weekHeaderHTML += '<div class="picker-calendar-week-day ' + ((p.params.weekendDays.indexOf(weekDayIndex) >= 0) ? 'picker-calendar-week-day-weekend' : '') + '"> ' + dayName + '</div>';
  2401. }
  2402. weekHeaderHTML = '<div class="picker-calendar-week-days">' + weekHeaderHTML + '</div>';
  2403. }
  2404. pickerClass = 'picker-modal picker-calendar ' + (p.params.cssClass || '');
  2405. var toolbarHTML = p.params.toolbar ? p.params.toolbarTemplate.replace(/{{closeText}}/g, p.params.toolbarCloseText) : '';
  2406. if (p.params.toolbar) {
  2407. toolbarHTML = p.params.toolbarTemplate
  2408. .replace(/{{closeText}}/g, p.params.toolbarCloseText)
  2409. .replace(/{{monthPicker}}/g, (p.params.monthPicker ? p.params.monthPickerTemplate : ''))
  2410. .replace(/{{yearPicker}}/g, (p.params.yearPicker ? p.params.yearPickerTemplate : ''));
  2411. }
  2412. /**
  2413. * 将年份组/月份组DOM添加document中
  2414. *
  2415. * Start - edit by JSoon
  2416. */
  2417. pickerHTML =
  2418. '<div class="' + (pickerClass) + '">' +
  2419. toolbarHTML +
  2420. '<div class="picker-modal-inner">' +
  2421. weekHeaderHTML +
  2422. monthsHTML +
  2423. '</div>' +
  2424. monthsGroupHTML +
  2425. yearsGroupHTML +
  2426. '</div>';
  2427. /**
  2428. * End - edit by JSoon
  2429. */
  2430. p.pickerHTML = pickerHTML;
  2431. };
  2432. // Input Events
  2433. function openOnInput(e) {
  2434. e.preventDefault();
  2435. // 安卓微信webviewreadonly的input依然弹出软键盘问题修复
  2436. if ($.device.isWeixin && $.device.android && p.params.inputReadOnly) {
  2437. /*jshint validthis:true */
  2438. this.focus();
  2439. this.blur();
  2440. }
  2441. if (p.opened) return;
  2442. p.open();
  2443. if (p.params.scrollToInput) {
  2444. var pageContent = p.input.parents('.content');
  2445. if (pageContent.length === 0) return;
  2446. var paddingTop = parseInt(pageContent.css('padding-top'), 10),
  2447. paddingBottom = parseInt(pageContent.css('padding-bottom'), 10),
  2448. pageHeight = pageContent[0].offsetHeight - paddingTop - p.container.height(),
  2449. pageScrollHeight = pageContent[0].scrollHeight - paddingTop - p.container.height(),
  2450. newPaddingBottom;
  2451. var inputTop = p.input.offset().top - paddingTop + p.input[0].offsetHeight;
  2452. if (inputTop > pageHeight) {
  2453. var scrollTop = pageContent.scrollTop() + inputTop - pageHeight;
  2454. if (scrollTop + pageHeight > pageScrollHeight) {
  2455. newPaddingBottom = scrollTop + pageHeight - pageScrollHeight + paddingBottom;
  2456. if (pageHeight === pageScrollHeight) {
  2457. newPaddingBottom = p.container.height();
  2458. }
  2459. pageContent.css({'padding-bottom': (newPaddingBottom) + 'px'});
  2460. }
  2461. pageContent.scrollTop(scrollTop, 300);
  2462. }
  2463. }
  2464. }
  2465. function closeOnHTMLClick(e) {
  2466. if (p.input && p.input.length > 0) {
  2467. if (e.target !== p.input[0] && $(e.target).parents('.picker-modal').length === 0) p.close();
  2468. }
  2469. else {
  2470. if ($(e.target).parents('.picker-modal').length === 0) p.close();
  2471. }
  2472. }
  2473. if (p.params.input) {
  2474. p.input = $(p.params.input);
  2475. if (p.input.length > 0) {
  2476. if (p.params.inputReadOnly) p.input.prop('readOnly', true);
  2477. if (!p.inline) {
  2478. p.input.on('click', openOnInput);
  2479. }
  2480. /**
  2481. * 修复[#308](https://github.com/sdc-alibaba/SUI-Mobile/issues/308)
  2482. * 场景:内联页面中存在日历控件的input
  2483. * 问题:因未在关闭时unbind click openOnInput事件导致多次调用p.open()而生成多个日历
  2484. *
  2485. * Start - edit by JSoon
  2486. */
  2487. $(document).on('beforePageSwitch', function() {
  2488. p.input.off('click', openOnInput);
  2489. $(document).off('beforePageSwitch');
  2490. });
  2491. /**
  2492. * End - edit by JSoon
  2493. */
  2494. }
  2495. }
  2496. if (!p.inline) $('html').on('click', closeOnHTMLClick);
  2497. // Open
  2498. function onPickerClose() {
  2499. p.opened = false;
  2500. if (p.input && p.input.length > 0) p.input.parents('.content').css({'padding-bottom': ''});
  2501. if (p.params.onClose) p.params.onClose(p);
  2502. // Destroy events
  2503. p.destroyCalendarEvents();
  2504. }
  2505. p.opened = false;
  2506. p.open = function () {
  2507. var updateValue = false;
  2508. if (!p.opened) {
  2509. // Set date value
  2510. if (!p.value) {
  2511. if (p.params.value) {
  2512. p.value = p.params.value;
  2513. updateValue = true;
  2514. }
  2515. }
  2516. // Layout
  2517. p.layout();
  2518. // Append
  2519. if (p.inline) {
  2520. p.container = $(p.pickerHTML);
  2521. p.container.addClass('picker-modal-inline');
  2522. $(p.params.container).append(p.container);
  2523. }
  2524. else {
  2525. p.container = $($.pickerModal(p.pickerHTML));
  2526. $(p.container)
  2527. .on('close', function () {
  2528. onPickerClose();
  2529. });
  2530. }
  2531. // Store calendar instance
  2532. p.container[0].f7Calendar = p;
  2533. p.wrapper = p.container.find('.picker-calendar-months-wrapper');
  2534. /**
  2535. * 获取全局年份组及其wrapper的zepto对象
  2536. * 获取全局月份组wrapper的zepto对象
  2537. *
  2538. * Start - edit by JSoon
  2539. */
  2540. p.yearsPickerWrapper = p.container.find('.picker-calendar-years-picker-wrapper');
  2541. p.yearsGroups = p.yearsPickerWrapper.find('.picker-calendar-years-group');
  2542. p.monthsPickerWrapper = p.container.find('.picker-calendar-months-picker-wrapper');
  2543. /**
  2544. * End - edit by JSoon
  2545. */
  2546. // Months
  2547. p.months = p.wrapper.find('.picker-calendar-month');
  2548. // Update current month and year
  2549. p.updateCurrentMonthYear();
  2550. // Set initial translate
  2551. /**
  2552. * 初始化年份组过渡动画位置
  2553. *
  2554. * Start - edit by JSoon
  2555. */
  2556. p.yearsTranslate = 0;
  2557. p.setYearsTranslate();
  2558. /**
  2559. * End - edit by JSoon
  2560. */
  2561. p.monthsTranslate = 0;
  2562. p.setMonthsTranslate();
  2563. // Init events
  2564. p.initCalendarEvents();
  2565. // Update input value
  2566. if (updateValue) p.updateValue();
  2567. }
  2568. // Set flag
  2569. p.opened = true;
  2570. p.initialized = true;
  2571. if (p.params.onMonthAdd) {
  2572. p.months.each(function () {
  2573. p.params.onMonthAdd(p, this);
  2574. });
  2575. }
  2576. if (p.params.onOpen) p.params.onOpen(p);
  2577. };
  2578. // Close
  2579. p.close = function () {
  2580. if (!p.opened || p.inline) return;
  2581. $.closeModal(p.container);
  2582. return;
  2583. };
  2584. // Destroy
  2585. p.destroy = function () {
  2586. p.close();
  2587. if (p.params.input && p.input.length > 0) {
  2588. p.input.off('click', openOnInput);
  2589. }
  2590. $('html').off('click', closeOnHTMLClick);
  2591. };
  2592. if (p.inline) {
  2593. p.open();
  2594. }
  2595. return p;
  2596. };
  2597. $.fn.calendar = function (params) {
  2598. return this.each(function() {
  2599. var $this = $(this);
  2600. if(!$this[0]) return;
  2601. var p = {};
  2602. if($this[0].tagName.toUpperCase() === "INPUT") {
  2603. p.input = $this;
  2604. } else {
  2605. p.container = $this;
  2606. }
  2607. new Calendar($.extend(p, params));
  2608. });
  2609. };
  2610. $.initCalendar = function(content) {
  2611. var $content = content ? $(content) : $(document.body);
  2612. $content.find("[data-toggle='date']").each(function() {
  2613. $(this).calendar();
  2614. });
  2615. };
  2616. }(Zepto);
  2617. /*======================================================
  2618. ************ Picker ************
  2619. ======================================================*/
  2620. /* jshint unused:false */
  2621. /* jshint multistr:true */
  2622. + function($) {
  2623. "use strict";
  2624. var Picker = function (params) {
  2625. var p = this;
  2626. var defaults = {
  2627. updateValuesOnMomentum: false,
  2628. updateValuesOnTouchmove: true,
  2629. rotateEffect: false,
  2630. momentumRatio: 7,
  2631. freeMode: false,
  2632. // Common settings
  2633. scrollToInput: true,
  2634. inputReadOnly: true,
  2635. toolbar: true,
  2636. toolbarCloseText: '确定',
  2637. toolbarTemplate: '<header class="bar bar-nav">\
  2638. <button class="button button-link pull-right close-picker">确定</button>\
  2639. <h1 class="title">请选择</h1>\
  2640. </header>',
  2641. };
  2642. params = params || {};
  2643. for (var def in defaults) {
  2644. if (typeof params[def] === 'undefined') {
  2645. params[def] = defaults[def];
  2646. }
  2647. }
  2648. p.params = params;
  2649. p.cols = [];
  2650. p.initialized = false;
  2651. // Inline flag
  2652. p.inline = p.params.container ? true : false;
  2653. // 3D Transforms origin bug, only on safari
  2654. var originBug = $.device.ios || (navigator.userAgent.toLowerCase().indexOf('safari') >= 0 && navigator.userAgent.toLowerCase().indexOf('chrome') < 0) && !$.device.android;
  2655. // Value
  2656. p.setValue = function (arrValues, transition) {
  2657. var valueIndex = 0;
  2658. for (var i = 0; i < p.cols.length; i++) {
  2659. if (p.cols[i] && !p.cols[i].divider) {
  2660. p.cols[i].setValue(arrValues[valueIndex], transition);
  2661. valueIndex++;
  2662. }
  2663. }
  2664. };
  2665. p.updateValue = function () {
  2666. var newValue = [];
  2667. var newDisplayValue = [];
  2668. for (var i = 0; i < p.cols.length; i++) {
  2669. if (!p.cols[i].divider) {
  2670. newValue.push(p.cols[i].value);
  2671. newDisplayValue.push(p.cols[i].displayValue);
  2672. }
  2673. }
  2674. if (newValue.indexOf(undefined) >= 0) {
  2675. return;
  2676. }
  2677. p.value = newValue;
  2678. p.displayValue = newDisplayValue;
  2679. if (p.params.onChange) {
  2680. p.params.onChange(p, p.value, p.displayValue);
  2681. }
  2682. if (p.input && p.input.length > 0) {
  2683. $(p.input).val(p.params.formatValue ? p.params.formatValue(p, p.value, p.displayValue) : p.value.join(' '));
  2684. $(p.input).trigger('change');
  2685. }
  2686. };
  2687. // Columns Handlers
  2688. p.initPickerCol = function (colElement, updateItems) {
  2689. var colContainer = $(colElement);
  2690. var colIndex = colContainer.index();
  2691. var col = p.cols[colIndex];
  2692. if (col.divider) return;
  2693. col.container = colContainer;
  2694. col.wrapper = col.container.find('.picker-items-col-wrapper');
  2695. col.items = col.wrapper.find('.picker-item');
  2696. var i, j;
  2697. var wrapperHeight, itemHeight, itemsHeight, minTranslate, maxTranslate;
  2698. col.replaceValues = function (values, displayValues) {
  2699. col.destroyEvents();
  2700. col.values = values;
  2701. col.displayValues = displayValues;
  2702. var newItemsHTML = p.columnHTML(col, true);
  2703. col.wrapper.html(newItemsHTML);
  2704. col.items = col.wrapper.find('.picker-item');
  2705. col.calcSize();
  2706. col.setValue(col.values[0], 0, true);
  2707. col.initEvents();
  2708. };
  2709. col.calcSize = function () {
  2710. if (p.params.rotateEffect) {
  2711. col.container.removeClass('picker-items-col-absolute');
  2712. if (!col.width) col.container.css({width:''});
  2713. }
  2714. var colWidth, colHeight;
  2715. colWidth = 0;
  2716. colHeight = col.container[0].offsetHeight;
  2717. wrapperHeight = col.wrapper[0].offsetHeight;
  2718. itemHeight = col.items[0].offsetHeight;
  2719. itemsHeight = itemHeight * col.items.length;
  2720. minTranslate = colHeight / 2 - itemsHeight + itemHeight / 2;
  2721. maxTranslate = colHeight / 2 - itemHeight / 2;
  2722. if (col.width) {
  2723. colWidth = col.width;
  2724. if (parseInt(colWidth, 10) === colWidth) colWidth = colWidth + 'px';
  2725. col.container.css({width: colWidth});
  2726. }
  2727. if (p.params.rotateEffect) {
  2728. if (!col.width) {
  2729. col.items.each(function () {
  2730. var item = $(this);
  2731. item.css({width:'auto'});
  2732. colWidth = Math.max(colWidth, item[0].offsetWidth);
  2733. item.css({width:''});
  2734. });
  2735. col.container.css({width: (colWidth + 2) + 'px'});
  2736. }
  2737. col.container.addClass('picker-items-col-absolute');
  2738. }
  2739. };
  2740. col.calcSize();
  2741. col.wrapper.transform('translate3d(0,' + maxTranslate + 'px,0)').transition(0);
  2742. var activeIndex = 0;
  2743. var animationFrameId;
  2744. // Set Value Function
  2745. col.setValue = function (newValue, transition, valueCallbacks) {
  2746. if (typeof transition === 'undefined') transition = '';
  2747. var newActiveIndex = col.wrapper.find('.picker-item[data-picker-value="' + newValue + '"]').index();
  2748. if(typeof newActiveIndex === 'undefined' || newActiveIndex === -1) {
  2749. return;
  2750. }
  2751. var newTranslate = -newActiveIndex * itemHeight + maxTranslate;
  2752. // Update wrapper
  2753. col.wrapper.transition(transition);
  2754. col.wrapper.transform('translate3d(0,' + (newTranslate) + 'px,0)');
  2755. // Watch items
  2756. if (p.params.updateValuesOnMomentum && col.activeIndex && col.activeIndex !== newActiveIndex ) {
  2757. $.cancelAnimationFrame(animationFrameId);
  2758. col.wrapper.transitionEnd(function(){
  2759. $.cancelAnimationFrame(animationFrameId);
  2760. });
  2761. updateDuringScroll();
  2762. }
  2763. // Update items
  2764. col.updateItems(newActiveIndex, newTranslate, transition, valueCallbacks);
  2765. };
  2766. col.updateItems = function (activeIndex, translate, transition, valueCallbacks) {
  2767. if (typeof translate === 'undefined') {
  2768. translate = $.getTranslate(col.wrapper[0], 'y');
  2769. }
  2770. if(typeof activeIndex === 'undefined') activeIndex = -Math.round((translate - maxTranslate)/itemHeight);
  2771. if (activeIndex < 0) activeIndex = 0;
  2772. if (activeIndex >= col.items.length) activeIndex = col.items.length - 1;
  2773. var previousActiveIndex = col.activeIndex;
  2774. col.activeIndex = activeIndex;
  2775. /*
  2776. col.wrapper.find('.picker-selected, .picker-after-selected, .picker-before-selected').removeClass('picker-selected picker-after-selected picker-before-selected');
  2777. col.items.transition(transition);
  2778. var selectedItem = col.items.eq(activeIndex).addClass('picker-selected').transform('');
  2779. var prevItems = selectedItem.prevAll().addClass('picker-before-selected');
  2780. var nextItems = selectedItem.nextAll().addClass('picker-after-selected');
  2781. */
  2782. //去掉 .picker-after-selected, .picker-before-selected 以提高性能
  2783. col.wrapper.find('.picker-selected').removeClass('picker-selected');
  2784. if (p.params.rotateEffect) {
  2785. col.items.transition(transition);
  2786. }
  2787. var selectedItem = col.items.eq(activeIndex).addClass('picker-selected').transform('');
  2788. if (valueCallbacks || typeof valueCallbacks === 'undefined') {
  2789. // Update values
  2790. col.value = selectedItem.attr('data-picker-value');
  2791. col.displayValue = col.displayValues ? col.displayValues[activeIndex] : col.value;
  2792. // On change callback
  2793. if (previousActiveIndex !== activeIndex) {
  2794. if (col.onChange) {
  2795. col.onChange(p, col.value, col.displayValue);
  2796. }
  2797. p.updateValue();
  2798. }
  2799. }
  2800. // Set 3D rotate effect
  2801. if (!p.params.rotateEffect) {
  2802. return;
  2803. }
  2804. var percentage = (translate - (Math.floor((translate - maxTranslate)/itemHeight) * itemHeight + maxTranslate)) / itemHeight;
  2805. col.items.each(function () {
  2806. var item = $(this);
  2807. var itemOffsetTop = item.index() * itemHeight;
  2808. var translateOffset = maxTranslate - translate;
  2809. var itemOffset = itemOffsetTop - translateOffset;
  2810. var percentage = itemOffset / itemHeight;
  2811. var itemsFit = Math.ceil(col.height / itemHeight / 2) + 1;
  2812. var angle = (-18*percentage);
  2813. if (angle > 180) angle = 180;
  2814. if (angle < -180) angle = -180;
  2815. // Far class
  2816. if (Math.abs(percentage) > itemsFit) item.addClass('picker-item-far');
  2817. else item.removeClass('picker-item-far');
  2818. // Set transform
  2819. item.transform('translate3d(0, ' + (-translate + maxTranslate) + 'px, ' + (originBug ? -110 : 0) + 'px) rotateX(' + angle + 'deg)');
  2820. });
  2821. };
  2822. function updateDuringScroll() {
  2823. animationFrameId = $.requestAnimationFrame(function () {
  2824. col.updateItems(undefined, undefined, 0);
  2825. updateDuringScroll();
  2826. });
  2827. }
  2828. // Update items on init
  2829. if (updateItems) col.updateItems(0, maxTranslate, 0);
  2830. var allowItemClick = true;
  2831. var isTouched, isMoved, touchStartY, touchCurrentY, touchStartTime, touchEndTime, startTranslate, returnTo, currentTranslate, prevTranslate, velocityTranslate, velocityTime;
  2832. function handleTouchStart (e) {
  2833. if (isMoved || isTouched) return;
  2834. e.preventDefault();
  2835. isTouched = true;
  2836. touchStartY = touchCurrentY = e.type === 'touchstart' ? e.targetTouches[0].pageY : e.pageY;
  2837. touchStartTime = (new Date()).getTime();
  2838. allowItemClick = true;
  2839. startTranslate = currentTranslate = $.getTranslate(col.wrapper[0], 'y');
  2840. }
  2841. function handleTouchMove (e) {
  2842. if (!isTouched) return;
  2843. e.preventDefault();
  2844. allowItemClick = false;
  2845. touchCurrentY = e.type === 'touchmove' ? e.targetTouches[0].pageY : e.pageY;
  2846. if (!isMoved) {
  2847. // First move
  2848. $.cancelAnimationFrame(animationFrameId);
  2849. isMoved = true;
  2850. startTranslate = currentTranslate = $.getTranslate(col.wrapper[0], 'y');
  2851. col.wrapper.transition(0);
  2852. }
  2853. e.preventDefault();
  2854. var diff = touchCurrentY - touchStartY;
  2855. currentTranslate = startTranslate + diff;
  2856. returnTo = undefined;
  2857. // Normalize translate
  2858. if (currentTranslate < minTranslate) {
  2859. currentTranslate = minTranslate - Math.pow(minTranslate - currentTranslate, 0.8);
  2860. returnTo = 'min';
  2861. }
  2862. if (currentTranslate > maxTranslate) {
  2863. currentTranslate = maxTranslate + Math.pow(currentTranslate - maxTranslate, 0.8);
  2864. returnTo = 'max';
  2865. }
  2866. // Transform wrapper
  2867. col.wrapper.transform('translate3d(0,' + currentTranslate + 'px,0)');
  2868. // Update items
  2869. col.updateItems(undefined, currentTranslate, 0, p.params.updateValuesOnTouchmove);
  2870. // Calc velocity
  2871. velocityTranslate = currentTranslate - prevTranslate || currentTranslate;
  2872. velocityTime = (new Date()).getTime();
  2873. prevTranslate = currentTranslate;
  2874. }
  2875. function handleTouchEnd (e) {
  2876. if (!isTouched || !isMoved) {
  2877. isTouched = isMoved = false;
  2878. return;
  2879. }
  2880. isTouched = isMoved = false;
  2881. col.wrapper.transition('');
  2882. if (returnTo) {
  2883. if (returnTo === 'min') {
  2884. col.wrapper.transform('translate3d(0,' + minTranslate + 'px,0)');
  2885. }
  2886. else col.wrapper.transform('translate3d(0,' + maxTranslate + 'px,0)');
  2887. }
  2888. touchEndTime = new Date().getTime();
  2889. var velocity, newTranslate;
  2890. if (touchEndTime - touchStartTime > 300) {
  2891. newTranslate = currentTranslate;
  2892. }
  2893. else {
  2894. velocity = Math.abs(velocityTranslate / (touchEndTime - velocityTime));
  2895. newTranslate = currentTranslate + velocityTranslate * p.params.momentumRatio;
  2896. }
  2897. newTranslate = Math.max(Math.min(newTranslate, maxTranslate), minTranslate);
  2898. // Active Index
  2899. var activeIndex = -Math.floor((newTranslate - maxTranslate)/itemHeight);
  2900. // Normalize translate
  2901. if (!p.params.freeMode) newTranslate = -activeIndex * itemHeight + maxTranslate;
  2902. // Transform wrapper
  2903. col.wrapper.transform('translate3d(0,' + (parseInt(newTranslate,10)) + 'px,0)');
  2904. // Update items
  2905. col.updateItems(activeIndex, newTranslate, '', true);
  2906. // Watch items
  2907. if (p.params.updateValuesOnMomentum) {
  2908. updateDuringScroll();
  2909. col.wrapper.transitionEnd(function(){
  2910. $.cancelAnimationFrame(animationFrameId);
  2911. });
  2912. }
  2913. // Allow click
  2914. setTimeout(function () {
  2915. allowItemClick = true;
  2916. }, 100);
  2917. }
  2918. function handleClick(e) {
  2919. if (!allowItemClick) return;
  2920. $.cancelAnimationFrame(animationFrameId);
  2921. /*jshint validthis:true */
  2922. var value = $(this).attr('data-picker-value');
  2923. col.setValue(value);
  2924. }
  2925. col.initEvents = function (detach) {
  2926. var method = detach ? 'off' : 'on';
  2927. col.container[method]($.touchEvents.start, handleTouchStart);
  2928. col.container[method]($.touchEvents.move, handleTouchMove);
  2929. col.container[method]($.touchEvents.end, handleTouchEnd);
  2930. col.items[method]('click', handleClick);
  2931. };
  2932. col.destroyEvents = function () {
  2933. col.initEvents(true);
  2934. };
  2935. col.container[0].f7DestroyPickerCol = function () {
  2936. col.destroyEvents();
  2937. };
  2938. col.initEvents();
  2939. };
  2940. p.destroyPickerCol = function (colContainer) {
  2941. colContainer = $(colContainer);
  2942. if ('f7DestroyPickerCol' in colContainer[0]) colContainer[0].f7DestroyPickerCol();
  2943. };
  2944. // Resize cols
  2945. function resizeCols() {
  2946. if (!p.opened) return;
  2947. for (var i = 0; i < p.cols.length; i++) {
  2948. if (!p.cols[i].divider) {
  2949. p.cols[i].calcSize();
  2950. p.cols[i].setValue(p.cols[i].value, 0, false);
  2951. }
  2952. }
  2953. }
  2954. $(window).on('resize', resizeCols);
  2955. // HTML Layout
  2956. p.columnHTML = function (col, onlyItems) {
  2957. var columnItemsHTML = '';
  2958. var columnHTML = '';
  2959. if (col.divider) {
  2960. columnHTML += '<div class="picker-items-col picker-items-col-divider ' + (col.textAlign ? 'picker-items-col-' + col.textAlign : '') + ' ' + (col.cssClass || '') + '">' + col.content + '</div>';
  2961. }
  2962. else {
  2963. for (var j = 0; j < col.values.length; j++) {
  2964. columnItemsHTML += '<div class="picker-item" data-picker-value="' + col.values[j] + '">' + (col.displayValues ? col.displayValues[j] : col.values[j]) + '</div>';
  2965. }
  2966. columnHTML += '<div class="picker-items-col ' + (col.textAlign ? 'picker-items-col-' + col.textAlign : '') + ' ' + (col.cssClass || '') + '"><div class="picker-items-col-wrapper">' + columnItemsHTML + '</div></div>';
  2967. }
  2968. return onlyItems ? columnItemsHTML : columnHTML;
  2969. };
  2970. p.layout = function () {
  2971. var pickerHTML = '';
  2972. var pickerClass = '';
  2973. var i;
  2974. p.cols = [];
  2975. var colsHTML = '';
  2976. for (i = 0; i < p.params.cols.length; i++) {
  2977. var col = p.params.cols[i];
  2978. colsHTML += p.columnHTML(p.params.cols[i]);
  2979. p.cols.push(col);
  2980. }
  2981. pickerClass = 'picker-modal picker-columns ' + (p.params.cssClass || '') + (p.params.rotateEffect ? ' picker-3d' : '');
  2982. pickerHTML =
  2983. '<div class="' + (pickerClass) + '">' +
  2984. (p.params.toolbar ? p.params.toolbarTemplate.replace(/{{closeText}}/g, p.params.toolbarCloseText) : '') +
  2985. '<div class="picker-modal-inner picker-items">' +
  2986. colsHTML +
  2987. '<div class="picker-center-highlight"></div>' +
  2988. '</div>' +
  2989. '</div>';
  2990. p.pickerHTML = pickerHTML;
  2991. };
  2992. // Input Events
  2993. function openOnInput(e) {
  2994. e.preventDefault();
  2995. // 安卓微信webviewreadonly的input依然弹出软键盘问题修复
  2996. if ($.device.isWeixin && $.device.android && p.params.inputReadOnly) {
  2997. /*jshint validthis:true */
  2998. this.focus();
  2999. this.blur();
  3000. }
  3001. if (p.opened) return;
  3002. //关闭其他picker
  3003. $.closeModal($('.picker-modal'));
  3004. p.open();
  3005. if (p.params.scrollToInput) {
  3006. var pageContent = p.input.parents('.content');
  3007. if (pageContent.length === 0) return;
  3008. var paddingTop = parseInt(pageContent.css('padding-top'), 10),
  3009. paddingBottom = parseInt(pageContent.css('padding-bottom'), 10),
  3010. pageHeight = pageContent[0].offsetHeight - paddingTop - p.container.height(),
  3011. pageScrollHeight = pageContent[0].scrollHeight - paddingTop - p.container.height(),
  3012. newPaddingBottom;
  3013. var inputTop = p.input.offset().top - paddingTop + p.input[0].offsetHeight;
  3014. if (inputTop > pageHeight) {
  3015. var scrollTop = pageContent.scrollTop() + inputTop - pageHeight;
  3016. if (scrollTop + pageHeight > pageScrollHeight) {
  3017. newPaddingBottom = scrollTop + pageHeight - pageScrollHeight + paddingBottom;
  3018. if (pageHeight === pageScrollHeight) {
  3019. newPaddingBottom = p.container.height();
  3020. }
  3021. pageContent.css({'padding-bottom': (newPaddingBottom) + 'px'});
  3022. }
  3023. pageContent.scrollTop(scrollTop, 300);
  3024. }
  3025. }
  3026. //停止事件冒泡,主动处理
  3027. e.stopPropagation();
  3028. }
  3029. function closeOnHTMLClick(e) {
  3030. if (!p.opened) return;
  3031. if (p.input && p.input.length > 0) {
  3032. if (e.target !== p.input[0] && $(e.target).parents('.picker-modal').length === 0) p.close();
  3033. }
  3034. else {
  3035. if ($(e.target).parents('.picker-modal').length === 0) p.close();
  3036. }
  3037. }
  3038. if (p.params.input) {
  3039. p.input = $(p.params.input);
  3040. if (p.input.length > 0) {
  3041. if (p.params.inputReadOnly) p.input.prop('readOnly', true);
  3042. if (!p.inline) {
  3043. p.input.on('click', openOnInput);
  3044. }
  3045. }
  3046. }
  3047. if (!p.inline) $('html').on('click', closeOnHTMLClick);
  3048. // Open
  3049. function onPickerClose() {
  3050. p.opened = false;
  3051. if (p.input && p.input.length > 0) p.input.parents('.content').css({'padding-bottom': ''});
  3052. if (p.params.onClose) p.params.onClose(p);
  3053. // Destroy events
  3054. p.container.find('.picker-items-col').each(function () {
  3055. p.destroyPickerCol(this);
  3056. });
  3057. }
  3058. p.opened = false;
  3059. p.open = function () {
  3060. if (!p.opened) {
  3061. // Layout
  3062. p.layout();
  3063. p.opened = true;
  3064. // Append
  3065. if (p.inline) {
  3066. p.container = $(p.pickerHTML);
  3067. p.container.addClass('picker-modal-inline');
  3068. $(p.params.container).append(p.container);
  3069. }
  3070. else {
  3071. p.container = $($.pickerModal(p.pickerHTML));
  3072. $(p.container)
  3073. .on('close', function () {
  3074. onPickerClose();
  3075. });
  3076. }
  3077. // Store picker instance
  3078. p.container[0].f7Picker = p;
  3079. // Init Events
  3080. p.container.find('.picker-items-col').each(function () {
  3081. var updateItems = true;
  3082. if ((!p.initialized && p.params.value) || (p.initialized && p.value)) updateItems = false;
  3083. p.initPickerCol(this, updateItems);
  3084. });
  3085. // Set value
  3086. if (!p.initialized) {
  3087. if (p.params.value) {
  3088. p.setValue(p.params.value, 0);
  3089. }
  3090. }
  3091. else {
  3092. if (p.value) p.setValue(p.value, 0);
  3093. }
  3094. }
  3095. // Set flag
  3096. p.initialized = true;
  3097. if (p.params.onOpen) p.params.onOpen(p);
  3098. };
  3099. // Close
  3100. p.close = function () {
  3101. if (!p.opened || p.inline) return;
  3102. $.closeModal(p.container);
  3103. return;
  3104. };
  3105. // Destroy
  3106. p.destroy = function () {
  3107. p.close();
  3108. if (p.params.input && p.input.length > 0) {
  3109. p.input.off('click', openOnInput);
  3110. }
  3111. $('html').off('click', closeOnHTMLClick);
  3112. $(window).off('resize', resizeCols);
  3113. };
  3114. if (p.inline) {
  3115. p.open();
  3116. }
  3117. return p;
  3118. };
  3119. $(document).on("click", ".close-picker", function() {
  3120. var pickerToClose = $('.picker-modal.modal-in');
  3121. $.closeModal(pickerToClose);
  3122. });
  3123. $.fn.picker = function(params) {
  3124. var args = arguments;
  3125. return this.each(function() {
  3126. if(!this) return;
  3127. var $this = $(this);
  3128. var picker = $this.data("picker");
  3129. if(!picker) {
  3130. var p = $.extend({
  3131. input: this,
  3132. value: $this.val() ? $this.val().split(' ') : ''
  3133. }, params);
  3134. picker = new Picker(p);
  3135. $this.data("picker", picker);
  3136. }
  3137. if(typeof params === typeof "a") {
  3138. picker[params].apply(picker, Array.prototype.slice.call(args, 1));
  3139. }
  3140. });
  3141. };
  3142. }(Zepto);
  3143. /* jshint unused:false*/
  3144. + function($) {
  3145. "use strict";
  3146. var today = new Date();
  3147. var getDays = function(max) {
  3148. var days = [];
  3149. for(var i=1; i<= (max||31);i++) {
  3150. days.push(i < 10 ? "0"+i : i);
  3151. }
  3152. return days;
  3153. };
  3154. var getDaysByMonthAndYear = function(month, year) {
  3155. var int_d = new Date(year, parseInt(month)+1-1, 1);
  3156. var d = new Date(int_d - 1);
  3157. return getDays(d.getDate());
  3158. };
  3159. var formatNumber = function (n) {
  3160. return n < 10 ? "0" + n : n;
  3161. };
  3162. var initMonthes = ('01 02 03 04 05 06 07 08 09 10 11 12').split(' ');
  3163. var initYears = (function () {
  3164. var arr = [];
  3165. for (var i = 1950; i <= 2030; i++) { arr.push(i); }
  3166. return arr;
  3167. })();
  3168. var defaults = {
  3169. rotateEffect: false, //为了性能
  3170. value: [today.getFullYear(), formatNumber(today.getMonth()+1), formatNumber(today.getDate()), today.getHours(), formatNumber(today.getMinutes())],
  3171. onChange: function (picker, values, displayValues) {
  3172. var days = getDaysByMonthAndYear(picker.cols[1].value, picker.cols[0].value);
  3173. var currentValue = picker.cols[2].value;
  3174. if(currentValue > days.length) currentValue = days.length;
  3175. picker.cols[2].setValue(currentValue);
  3176. },
  3177. formatValue: function (p, values, displayValues) {
  3178. return displayValues[0] + '-' + values[1] + '-' + values[2] + ' ' + values[3] + ':' + values[4];
  3179. },
  3180. cols: [
  3181. // Years
  3182. {
  3183. values: initYears
  3184. },
  3185. // Months
  3186. {
  3187. values: initMonthes
  3188. },
  3189. // Days
  3190. {
  3191. values: getDays()
  3192. },
  3193. // Space divider
  3194. {
  3195. divider: true,
  3196. content: ' '
  3197. },
  3198. // Hours
  3199. {
  3200. values: (function () {
  3201. var arr = [];
  3202. for (var i = 0; i <= 23; i++) { arr.push(i); }
  3203. return arr;
  3204. })(),
  3205. },
  3206. // Divider
  3207. {
  3208. divider: true,
  3209. content: ':'
  3210. },
  3211. // Minutes
  3212. {
  3213. values: (function () {
  3214. var arr = [];
  3215. for (var i = 0; i <= 59; i++) { arr.push(i < 10 ? '0' + i : i); }
  3216. return arr;
  3217. })(),
  3218. }
  3219. ]
  3220. };
  3221. $.fn.datetimePicker = function(params) {
  3222. return this.each(function() {
  3223. if(!this) return;
  3224. var p = $.extend(defaults, params);
  3225. $(this).picker(p);
  3226. if (params.value) $(this).val(p.formatValue(p, p.value, p.value));
  3227. });
  3228. };
  3229. }(Zepto);
  3230. + function(window) {
  3231. "use strict";
  3232. var rAF = window.requestAnimationFrame ||
  3233. window.webkitRequestAnimationFrame ||
  3234. window.mozRequestAnimationFrame ||
  3235. window.oRequestAnimationFrame ||
  3236. window.msRequestAnimationFrame ||
  3237. function(callback) {
  3238. window.setTimeout(callback, 1000 / 60);
  3239. };
  3240. /*var cRAF = window.cancelRequestAnimationFrame ||
  3241. window.webkitCancelRequestAnimationFrame ||
  3242. window.mozCancelRequestAnimationFrame ||
  3243. window.oCancelRequestAnimationFrame ||
  3244. window.msCancelRequestAnimationFrame;*/
  3245. var utils = (function() {
  3246. var me = {};
  3247. var _elementStyle = document.createElement('div').style;
  3248. var _vendor = (function() {
  3249. var vendors = ['t', 'webkitT', 'MozT', 'msT', 'OT'],
  3250. transform,
  3251. i = 0,
  3252. l = vendors.length;
  3253. for (; i < l; i++) {
  3254. transform = vendors[i] + 'ransform';
  3255. if (transform in _elementStyle) return vendors[i].substr(0, vendors[i].length - 1);
  3256. }
  3257. return false;
  3258. })();
  3259. function _prefixStyle(style) {
  3260. if (_vendor === false) return false;
  3261. if (_vendor === '') return style;
  3262. return _vendor + style.charAt(0).toUpperCase() + style.substr(1);
  3263. }
  3264. me.getTime = Date.now || function getTime() {
  3265. return new Date().getTime();
  3266. };
  3267. me.extend = function(target, obj) {
  3268. for (var i in obj) { // jshint ignore:line
  3269. target[i] = obj[i];
  3270. }
  3271. };
  3272. me.addEvent = function(el, type, fn, capture) {
  3273. el.addEventListener(type, fn, !!capture);
  3274. };
  3275. me.removeEvent = function(el, type, fn, capture) {
  3276. el.removeEventListener(type, fn, !!capture);
  3277. };
  3278. me.prefixPointerEvent = function(pointerEvent) {
  3279. return window.MSPointerEvent ?
  3280. 'MSPointer' + pointerEvent.charAt(9).toUpperCase() + pointerEvent.substr(10) :
  3281. pointerEvent;
  3282. };
  3283. me.momentum = function(current, start, time, lowerMargin, wrapperSize, deceleration, self) {
  3284. var distance = current - start,
  3285. speed = Math.abs(distance) / time,
  3286. destination,
  3287. duration;
  3288. // var absDistance = Math.abs(distance);
  3289. speed = speed / 2; //slowdown
  3290. speed = speed > 1.5 ? 1.5 : speed; //set max speed to 1
  3291. deceleration = deceleration === undefined ? 0.0006 : deceleration;
  3292. destination = current + (speed * speed) / (2 * deceleration) * (distance < 0 ? -1 : 1);
  3293. duration = speed / deceleration;
  3294. if (destination < lowerMargin) {
  3295. destination = wrapperSize ? lowerMargin - (wrapperSize / 2.5 * (speed / 8)) : lowerMargin;
  3296. distance = Math.abs(destination - current);
  3297. duration = distance / speed;
  3298. } else if (destination > 0) {
  3299. destination = wrapperSize ? wrapperSize / 2.5 * (speed / 8) : 0;
  3300. distance = Math.abs(current) + destination;
  3301. duration = distance / speed;
  3302. }
  3303. //simple trigger, every 50ms
  3304. var t = +new Date();
  3305. var l = t;
  3306. function eventTrigger() {
  3307. if (+new Date() - l > 50) {
  3308. self._execEvent('scroll');
  3309. l = +new Date();
  3310. }
  3311. if (+new Date() - t < duration) {
  3312. rAF(eventTrigger);
  3313. }
  3314. }
  3315. rAF(eventTrigger);
  3316. return {
  3317. destination: Math.round(destination),
  3318. duration: duration
  3319. };
  3320. };
  3321. var _transform = _prefixStyle('transform');
  3322. me.extend(me, {
  3323. hasTransform: _transform !== false,
  3324. hasPerspective: _prefixStyle('perspective') in _elementStyle,
  3325. hasTouch: 'ontouchstart' in window,
  3326. hasPointer: window.PointerEvent || window.MSPointerEvent, // IE10 is prefixed
  3327. hasTransition: _prefixStyle('transition') in _elementStyle
  3328. });
  3329. // This should find all Android browsers lower than build 535.19 (both stock browser and webview)
  3330. me.isBadAndroid = /Android /.test(window.navigator.appVersion) && !(/Chrome\/\d/.test(window.navigator.appVersion)) && false; //this will cause many android device scroll flash; so set it to false!
  3331. me.extend(me.style = {}, {
  3332. transform: _transform,
  3333. transitionTimingFunction: _prefixStyle('transitionTimingFunction'),
  3334. transitionDuration: _prefixStyle('transitionDuration'),
  3335. transitionDelay: _prefixStyle('transitionDelay'),
  3336. transformOrigin: _prefixStyle('transformOrigin')
  3337. });
  3338. me.hasClass = function(e, c) {
  3339. var re = new RegExp('(^|\\s)' + c + '(\\s|$)');
  3340. return re.test(e.className);
  3341. };
  3342. me.addClass = function(e, c) {
  3343. if (me.hasClass(e, c)) {
  3344. return;
  3345. }
  3346. var newclass = e.className.split(' ');
  3347. newclass.push(c);
  3348. e.className = newclass.join(' ');
  3349. };
  3350. me.removeClass = function(e, c) {
  3351. if (!me.hasClass(e, c)) {
  3352. return;
  3353. }
  3354. var re = new RegExp('(^|\\s)' + c + '(\\s|$)', 'g');
  3355. e.className = e.className.replace(re, ' ');
  3356. };
  3357. me.offset = function(el) {
  3358. var left = -el.offsetLeft,
  3359. top = -el.offsetTop;
  3360. // jshint -W084
  3361. while (el = el.offsetParent) {
  3362. left -= el.offsetLeft;
  3363. top -= el.offsetTop;
  3364. }
  3365. // jshint +W084
  3366. return {
  3367. left: left,
  3368. top: top
  3369. };
  3370. };
  3371. me.preventDefaultException = function(el, exceptions) {
  3372. for (var i in exceptions) {
  3373. if (exceptions[i].test(el[i])) {
  3374. return true;
  3375. }
  3376. }
  3377. return false;
  3378. };
  3379. me.extend(me.eventType = {}, {
  3380. touchstart: 1,
  3381. touchmove: 1,
  3382. touchend: 1,
  3383. mousedown: 2,
  3384. mousemove: 2,
  3385. mouseup: 2,
  3386. pointerdown: 3,
  3387. pointermove: 3,
  3388. pointerup: 3,
  3389. MSPointerDown: 3,
  3390. MSPointerMove: 3,
  3391. MSPointerUp: 3
  3392. });
  3393. me.extend(me.ease = {}, {
  3394. quadratic: {
  3395. style: 'cubic-bezier(0.25, 0.46, 0.45, 0.94)',
  3396. fn: function(k) {
  3397. return k * (2 - k);
  3398. }
  3399. },
  3400. circular: {
  3401. style: 'cubic-bezier(0.1, 0.57, 0.1, 1)', // Not properly 'circular' but this looks better, it should be (0.075, 0.82, 0.165, 1)
  3402. fn: function(k) {
  3403. return Math.sqrt(1 - (--k * k));
  3404. }
  3405. },
  3406. back: {
  3407. style: 'cubic-bezier(0.175, 0.885, 0.32, 1.275)',
  3408. fn: function(k) {
  3409. var b = 4;
  3410. return (k = k - 1) * k * ((b + 1) * k + b) + 1;
  3411. }
  3412. },
  3413. bounce: {
  3414. style: '',
  3415. fn: function(k) {
  3416. if ((k /= 1) < (1 / 2.75)) {
  3417. return 7.5625 * k * k;
  3418. } else if (k < (2 / 2.75)) {
  3419. return 7.5625 * (k -= (1.5 / 2.75)) * k + 0.75;
  3420. } else if (k < (2.5 / 2.75)) {
  3421. return 7.5625 * (k -= (2.25 / 2.75)) * k + 0.9375;
  3422. } else {
  3423. return 7.5625 * (k -= (2.625 / 2.75)) * k + 0.984375;
  3424. }
  3425. }
  3426. },
  3427. elastic: {
  3428. style: '',
  3429. fn: function(k) {
  3430. var f = 0.22,
  3431. e = 0.4;
  3432. if (k === 0) {
  3433. return 0;
  3434. }
  3435. if (k === 1) {
  3436. return 1;
  3437. }
  3438. return (e * Math.pow(2, -10 * k) * Math.sin((k - f / 4) * (2 * Math.PI) / f) + 1);
  3439. }
  3440. }
  3441. });
  3442. me.tap = function(e, eventName) {
  3443. var ev = document.createEvent('Event');
  3444. ev.initEvent(eventName, true, true);
  3445. ev.pageX = e.pageX;
  3446. ev.pageY = e.pageY;
  3447. e.target.dispatchEvent(ev);
  3448. };
  3449. me.click = function(e) {
  3450. var target = e.target,
  3451. ev;
  3452. if (!(/(SELECT|INPUT|TEXTAREA)/i).test(target.tagName)) {
  3453. ev = document.createEvent('MouseEvents');
  3454. ev.initMouseEvent('click', true, true, e.view, 1,
  3455. target.screenX, target.screenY, target.clientX, target.clientY,
  3456. e.ctrlKey, e.altKey, e.shiftKey, e.metaKey,
  3457. 0, null);
  3458. ev._constructed = true;
  3459. target.dispatchEvent(ev);
  3460. }
  3461. };
  3462. return me;
  3463. })();
  3464. function IScroll(el, options) {
  3465. this.wrapper = typeof el === 'string' ? document.querySelector(el) : el;
  3466. this.scroller = $(this.wrapper).find('.content-inner')[0]; // jshint ignore:line
  3467. this.scrollerStyle = this.scroller&&this.scroller.style; // cache style for better performance
  3468. this.options = {
  3469. resizeScrollbars: true,
  3470. mouseWheelSpeed: 20,
  3471. snapThreshold: 0.334,
  3472. // INSERT POINT: OPTIONS
  3473. startX: 0,
  3474. startY: 0,
  3475. scrollY: true,
  3476. directionLockThreshold: 5,
  3477. momentum: true,
  3478. bounce: true,
  3479. bounceTime: 600,
  3480. bounceEasing: '',
  3481. preventDefault: true,
  3482. preventDefaultException: {
  3483. tagName: /^(INPUT|TEXTAREA|BUTTON|SELECT)$/
  3484. },
  3485. HWCompositing: true,
  3486. useTransition: true,
  3487. useTransform: true,
  3488. //other options
  3489. eventPassthrough: undefined, //if you want to use native scroll, you can set to: true or horizontal
  3490. };
  3491. for (var i in options) {
  3492. this.options[i] = options[i];
  3493. }
  3494. // Normalize options
  3495. this.translateZ = this.options.HWCompositing && utils.hasPerspective ? ' translateZ(0)' : '';
  3496. this.options.useTransition = utils.hasTransition && this.options.useTransition;
  3497. this.options.useTransform = utils.hasTransform && this.options.useTransform;
  3498. this.options.eventPassthrough = this.options.eventPassthrough === true ? 'vertical' : this.options.eventPassthrough;
  3499. this.options.preventDefault = !this.options.eventPassthrough && this.options.preventDefault;
  3500. // If you want eventPassthrough I have to lock one of the axes
  3501. this.options.scrollY = this.options.eventPassthrough === 'vertical' ? false : this.options.scrollY;
  3502. this.options.scrollX = this.options.eventPassthrough === 'horizontal' ? false : this.options.scrollX;
  3503. // With eventPassthrough we also need lockDirection mechanism
  3504. this.options.freeScroll = this.options.freeScroll && !this.options.eventPassthrough;
  3505. this.options.directionLockThreshold = this.options.eventPassthrough ? 0 : this.options.directionLockThreshold;
  3506. this.options.bounceEasing = typeof this.options.bounceEasing === 'string' ? utils.ease[this.options.bounceEasing] || utils.ease.circular : this.options.bounceEasing;
  3507. this.options.resizePolling = this.options.resizePolling === undefined ? 60 : this.options.resizePolling;
  3508. if (this.options.tap === true) {
  3509. this.options.tap = 'tap';
  3510. }
  3511. if (this.options.shrinkScrollbars === 'scale') {
  3512. this.options.useTransition = false;
  3513. }
  3514. this.options.invertWheelDirection = this.options.invertWheelDirection ? -1 : 1;
  3515. if (this.options.probeType === 3) {
  3516. this.options.useTransition = false;
  3517. }
  3518. // INSERT POINT: NORMALIZATION
  3519. // Some defaults
  3520. this.x = 0;
  3521. this.y = 0;
  3522. this.directionX = 0;
  3523. this.directionY = 0;
  3524. this._events = {};
  3525. // INSERT POINT: DEFAULTS
  3526. this._init();
  3527. this.refresh();
  3528. this.scrollTo(this.options.startX, this.options.startY);
  3529. this.enable();
  3530. }
  3531. IScroll.prototype = {
  3532. version: '5.1.3',
  3533. _init: function() {
  3534. this._initEvents();
  3535. if (this.options.scrollbars || this.options.indicators) {
  3536. this._initIndicators();
  3537. }
  3538. if (this.options.mouseWheel) {
  3539. this._initWheel();
  3540. }
  3541. if (this.options.snap) {
  3542. this._initSnap();
  3543. }
  3544. if (this.options.keyBindings) {
  3545. this._initKeys();
  3546. }
  3547. // INSERT POINT: _init
  3548. },
  3549. destroy: function() {
  3550. this._initEvents(true);
  3551. this._execEvent('destroy');
  3552. },
  3553. _transitionEnd: function(e) {
  3554. if (e.target !== this.scroller || !this.isInTransition) {
  3555. return;
  3556. }
  3557. this._transitionTime();
  3558. if (!this.resetPosition(this.options.bounceTime)) {
  3559. this.isInTransition = false;
  3560. this._execEvent('scrollEnd');
  3561. }
  3562. },
  3563. _start: function(e) {
  3564. // React to left mouse button only
  3565. if (utils.eventType[e.type] !== 1) {
  3566. if (e.button !== 0) {
  3567. return;
  3568. }
  3569. }
  3570. if (!this.enabled || (this.initiated && utils.eventType[e.type] !== this.initiated)) {
  3571. return;
  3572. }
  3573. if (this.options.preventDefault && !utils.isBadAndroid && !utils.preventDefaultException(e.target, this.options.preventDefaultException)) {
  3574. e.preventDefault();
  3575. }
  3576. var point = e.touches ? e.touches[0] : e,
  3577. pos;
  3578. this.initiated = utils.eventType[e.type];
  3579. this.moved = false;
  3580. this.distX = 0;
  3581. this.distY = 0;
  3582. this.directionX = 0;
  3583. this.directionY = 0;
  3584. this.directionLocked = 0;
  3585. this._transitionTime();
  3586. this.startTime = utils.getTime();
  3587. if (this.options.useTransition && this.isInTransition) {
  3588. this.isInTransition = false;
  3589. pos = this.getComputedPosition();
  3590. this._translate(Math.round(pos.x), Math.round(pos.y));
  3591. this._execEvent('scrollEnd');
  3592. } else if (!this.options.useTransition && this.isAnimating) {
  3593. this.isAnimating = false;
  3594. this._execEvent('scrollEnd');
  3595. }
  3596. this.startX = this.x;
  3597. this.startY = this.y;
  3598. this.absStartX = this.x;
  3599. this.absStartY = this.y;
  3600. this.pointX = point.pageX;
  3601. this.pointY = point.pageY;
  3602. this._execEvent('beforeScrollStart');
  3603. },
  3604. _move: function(e) {
  3605. if (!this.enabled || utils.eventType[e.type] !== this.initiated) {
  3606. return;
  3607. }
  3608. if (this.options.preventDefault) { // increases performance on Android? TODO: check!
  3609. e.preventDefault();
  3610. }
  3611. var point = e.touches ? e.touches[0] : e,
  3612. deltaX = point.pageX - this.pointX,
  3613. deltaY = point.pageY - this.pointY,
  3614. timestamp = utils.getTime(),
  3615. newX, newY,
  3616. absDistX, absDistY;
  3617. this.pointX = point.pageX;
  3618. this.pointY = point.pageY;
  3619. this.distX += deltaX;
  3620. this.distY += deltaY;
  3621. absDistX = Math.abs(this.distX);
  3622. absDistY = Math.abs(this.distY);
  3623. // We need to move at least 10 pixels for the scrolling to initiate
  3624. if (timestamp - this.endTime > 300 && (absDistX < 10 && absDistY < 10)) {
  3625. return;
  3626. }
  3627. // If you are scrolling in one direction lock the other
  3628. if (!this.directionLocked && !this.options.freeScroll) {
  3629. if (absDistX > absDistY + this.options.directionLockThreshold) {
  3630. this.directionLocked = 'h'; // lock horizontally
  3631. } else if (absDistY >= absDistX + this.options.directionLockThreshold) {
  3632. this.directionLocked = 'v'; // lock vertically
  3633. } else {
  3634. this.directionLocked = 'n'; // no lock
  3635. }
  3636. }
  3637. if (this.directionLocked === 'h') {
  3638. if (this.options.eventPassthrough === 'vertical') {
  3639. e.preventDefault();
  3640. } else if (this.options.eventPassthrough === 'horizontal') {
  3641. this.initiated = false;
  3642. return;
  3643. }
  3644. deltaY = 0;
  3645. } else if (this.directionLocked === 'v') {
  3646. if (this.options.eventPassthrough === 'horizontal') {
  3647. e.preventDefault();
  3648. } else if (this.options.eventPassthrough === 'vertical') {
  3649. this.initiated = false;
  3650. return;
  3651. }
  3652. deltaX = 0;
  3653. }
  3654. deltaX = this.hasHorizontalScroll ? deltaX : 0;
  3655. deltaY = this.hasVerticalScroll ? deltaY : 0;
  3656. newX = this.x + deltaX;
  3657. newY = this.y + deltaY;
  3658. // Slow down if outside of the boundaries
  3659. if (newX > 0 || newX < this.maxScrollX) {
  3660. newX = this.options.bounce ? this.x + deltaX / 3 : newX > 0 ? 0 : this.maxScrollX;
  3661. }
  3662. if (newY > 0 || newY < this.maxScrollY) {
  3663. newY = this.options.bounce ? this.y + deltaY / 3 : newY > 0 ? 0 : this.maxScrollY;
  3664. }
  3665. this.directionX = deltaX > 0 ? -1 : deltaX < 0 ? 1 : 0;
  3666. this.directionY = deltaY > 0 ? -1 : deltaY < 0 ? 1 : 0;
  3667. if (!this.moved) {
  3668. this._execEvent('scrollStart');
  3669. }
  3670. this.moved = true;
  3671. this._translate(newX, newY);
  3672. /* REPLACE START: _move */
  3673. if (timestamp - this.startTime > 300) {
  3674. this.startTime = timestamp;
  3675. this.startX = this.x;
  3676. this.startY = this.y;
  3677. if (this.options.probeType === 1) {
  3678. this._execEvent('scroll');
  3679. }
  3680. }
  3681. if (this.options.probeType > 1) {
  3682. this._execEvent('scroll');
  3683. }
  3684. /* REPLACE END: _move */
  3685. },
  3686. _end: function(e) {
  3687. if (!this.enabled || utils.eventType[e.type] !== this.initiated) {
  3688. return;
  3689. }
  3690. if (this.options.preventDefault && !utils.preventDefaultException(e.target, this.options.preventDefaultException)) {
  3691. e.preventDefault();
  3692. }
  3693. var /*point = e.changedTouches ? e.changedTouches[0] : e,*/
  3694. momentumX,
  3695. momentumY,
  3696. duration = utils.getTime() - this.startTime,
  3697. newX = Math.round(this.x),
  3698. newY = Math.round(this.y),
  3699. distanceX = Math.abs(newX - this.startX),
  3700. distanceY = Math.abs(newY - this.startY),
  3701. time = 0,
  3702. easing = '';
  3703. this.isInTransition = 0;
  3704. this.initiated = 0;
  3705. this.endTime = utils.getTime();
  3706. // reset if we are outside of the boundaries
  3707. if (this.resetPosition(this.options.bounceTime)) {
  3708. return;
  3709. }
  3710. this.scrollTo(newX, newY); // ensures that the last position is rounded
  3711. // we scrolled less than 10 pixels
  3712. if (!this.moved) {
  3713. if (this.options.tap) {
  3714. utils.tap(e, this.options.tap);
  3715. }
  3716. if (this.options.click) {
  3717. utils.click(e);
  3718. }
  3719. this._execEvent('scrollCancel');
  3720. return;
  3721. }
  3722. if (this._events.flick && duration < 200 && distanceX < 100 && distanceY < 100) {
  3723. this._execEvent('flick');
  3724. return;
  3725. }
  3726. // start momentum animation if needed
  3727. if (this.options.momentum && duration < 300) {
  3728. momentumX = this.hasHorizontalScroll ? utils.momentum(this.x, this.startX, duration, this.maxScrollX, this.options.bounce ? this.wrapperWidth : 0, this.options.deceleration, this) : {
  3729. destination: newX,
  3730. duration: 0
  3731. };
  3732. momentumY = this.hasVerticalScroll ? utils.momentum(this.y, this.startY, duration, this.maxScrollY, this.options.bounce ? this.wrapperHeight : 0, this.options.deceleration, this) : {
  3733. destination: newY,
  3734. duration: 0
  3735. };
  3736. newX = momentumX.destination;
  3737. newY = momentumY.destination;
  3738. time = Math.max(momentumX.duration, momentumY.duration);
  3739. this.isInTransition = 1;
  3740. }
  3741. if (this.options.snap) {
  3742. var snap = this._nearestSnap(newX, newY);
  3743. this.currentPage = snap;
  3744. time = this.options.snapSpeed || Math.max(
  3745. Math.max(
  3746. Math.min(Math.abs(newX - snap.x), 1000),
  3747. Math.min(Math.abs(newY - snap.y), 1000)
  3748. ), 300);
  3749. newX = snap.x;
  3750. newY = snap.y;
  3751. this.directionX = 0;
  3752. this.directionY = 0;
  3753. easing = this.options.bounceEasing;
  3754. }
  3755. // INSERT POINT: _end
  3756. if (newX !== this.x || newY !== this.y) {
  3757. // change easing function when scroller goes out of the boundaries
  3758. if (newX > 0 || newX < this.maxScrollX || newY > 0 || newY < this.maxScrollY) {
  3759. easing = utils.ease.quadratic;
  3760. }
  3761. this.scrollTo(newX, newY, time, easing);
  3762. return;
  3763. }
  3764. this._execEvent('scrollEnd');
  3765. },
  3766. _resize: function() {
  3767. var that = this;
  3768. clearTimeout(this.resizeTimeout);
  3769. this.resizeTimeout = setTimeout(function() {
  3770. that.refresh();
  3771. }, this.options.resizePolling);
  3772. },
  3773. resetPosition: function(time) {
  3774. var x = this.x,
  3775. y = this.y;
  3776. time = time || 0;
  3777. if (!this.hasHorizontalScroll || this.x > 0) {
  3778. x = 0;
  3779. } else if (this.x < this.maxScrollX) {
  3780. x = this.maxScrollX;
  3781. }
  3782. if (!this.hasVerticalScroll || this.y > 0) {
  3783. y = 0;
  3784. } else if (this.y < this.maxScrollY) {
  3785. y = this.maxScrollY;
  3786. }
  3787. if (x === this.x && y === this.y) {
  3788. return false;
  3789. }
  3790. if (this.options.ptr && this.y > 44 && this.startY * -1 < $(window).height() && !this.ptrLock) {// jshint ignore:line
  3791. // not trigger ptr when user want to scroll to top
  3792. y = this.options.ptrOffset || 44;
  3793. this._execEvent('ptr');
  3794. // 防止返回的过程中再次触发了 ptr ,导致被定位到 44px(因为可能done事件触发很快,在返回到44px以前就触发done
  3795. this.ptrLock = true;
  3796. var self = this;
  3797. setTimeout(function() {
  3798. self.ptrLock = false;
  3799. }, 500);
  3800. }
  3801. this.scrollTo(x, y, time, this.options.bounceEasing);
  3802. return true;
  3803. },
  3804. disable: function() {
  3805. this.enabled = false;
  3806. },
  3807. enable: function() {
  3808. this.enabled = true;
  3809. },
  3810. refresh: function() {
  3811. // var rf = this.wrapper.offsetHeight; // Force reflow
  3812. this.wrapperWidth = this.wrapper.clientWidth;
  3813. this.wrapperHeight = this.wrapper.clientHeight;
  3814. /* REPLACE START: refresh */
  3815. this.scrollerWidth = this.scroller.offsetWidth;
  3816. this.scrollerHeight = this.scroller.offsetHeight;
  3817. this.maxScrollX = this.wrapperWidth - this.scrollerWidth;
  3818. this.maxScrollY = this.wrapperHeight - this.scrollerHeight;
  3819. /* REPLACE END: refresh */
  3820. this.hasHorizontalScroll = this.options.scrollX && this.maxScrollX < 0;
  3821. this.hasVerticalScroll = this.options.scrollY && this.maxScrollY < 0;
  3822. if (!this.hasHorizontalScroll) {
  3823. this.maxScrollX = 0;
  3824. this.scrollerWidth = this.wrapperWidth;
  3825. }
  3826. if (!this.hasVerticalScroll) {
  3827. this.maxScrollY = 0;
  3828. this.scrollerHeight = this.wrapperHeight;
  3829. }
  3830. this.endTime = 0;
  3831. this.directionX = 0;
  3832. this.directionY = 0;
  3833. this.wrapperOffset = utils.offset(this.wrapper);
  3834. this._execEvent('refresh');
  3835. this.resetPosition();
  3836. // INSERT POINT: _refresh
  3837. },
  3838. on: function(type, fn) {
  3839. if (!this._events[type]) {
  3840. this._events[type] = [];
  3841. }
  3842. this._events[type].push(fn);
  3843. },
  3844. off: function(type, fn) {
  3845. if (!this._events[type]) {
  3846. return;
  3847. }
  3848. var index = this._events[type].indexOf(fn);
  3849. if (index > -1) {
  3850. this._events[type].splice(index, 1);
  3851. }
  3852. },
  3853. _execEvent: function(type) {
  3854. if (!this._events[type]) {
  3855. return;
  3856. }
  3857. var i = 0,
  3858. l = this._events[type].length;
  3859. if (!l) {
  3860. return;
  3861. }
  3862. for (; i < l; i++) {
  3863. this._events[type][i].apply(this, [].slice.call(arguments, 1));
  3864. }
  3865. },
  3866. scrollBy: function(x, y, time, easing) {
  3867. x = this.x + x;
  3868. y = this.y + y;
  3869. time = time || 0;
  3870. this.scrollTo(x, y, time, easing);
  3871. },
  3872. scrollTo: function(x, y, time, easing) {
  3873. easing = easing || utils.ease.circular;
  3874. this.isInTransition = this.options.useTransition && time > 0;
  3875. if (!time || (this.options.useTransition && easing.style)) {
  3876. this._transitionTimingFunction(easing.style);
  3877. this._transitionTime(time);
  3878. this._translate(x, y);
  3879. } else {
  3880. this._animate(x, y, time, easing.fn);
  3881. }
  3882. },
  3883. scrollToElement: function(el, time, offsetX, offsetY, easing) {
  3884. el = el.nodeType ? el : this.scroller.querySelector(el);
  3885. if (!el) {
  3886. return;
  3887. }
  3888. var pos = utils.offset(el);
  3889. pos.left -= this.wrapperOffset.left;
  3890. pos.top -= this.wrapperOffset.top;
  3891. // if offsetX/Y are true we center the element to the screen
  3892. if (offsetX === true) {
  3893. offsetX = Math.round(el.offsetWidth / 2 - this.wrapper.offsetWidth / 2);
  3894. }
  3895. if (offsetY === true) {
  3896. offsetY = Math.round(el.offsetHeight / 2 - this.wrapper.offsetHeight / 2);
  3897. }
  3898. pos.left -= offsetX || 0;
  3899. pos.top -= offsetY || 0;
  3900. pos.left = pos.left > 0 ? 0 : pos.left < this.maxScrollX ? this.maxScrollX : pos.left;
  3901. pos.top = pos.top > 0 ? 0 : pos.top < this.maxScrollY ? this.maxScrollY : pos.top;
  3902. time = time === undefined || time === null || time === 'auto' ? Math.max(Math.abs(this.x - pos.left), Math.abs(this.y - pos.top)) : time;
  3903. this.scrollTo(pos.left, pos.top, time, easing);
  3904. },
  3905. _transitionTime: function(time) {
  3906. time = time || 0;
  3907. this.scrollerStyle[utils.style.transitionDuration] = time + 'ms';
  3908. if (!time && utils.isBadAndroid) {
  3909. this.scrollerStyle[utils.style.transitionDuration] = '0.001s';
  3910. }
  3911. if (this.indicators) {
  3912. for (var i = this.indicators.length; i--;) {
  3913. this.indicators[i].transitionTime(time);
  3914. }
  3915. }
  3916. // INSERT POINT: _transitionTime
  3917. },
  3918. _transitionTimingFunction: function(easing) {
  3919. this.scrollerStyle[utils.style.transitionTimingFunction] = easing;
  3920. if (this.indicators) {
  3921. for (var i = this.indicators.length; i--;) {
  3922. this.indicators[i].transitionTimingFunction(easing);
  3923. }
  3924. }
  3925. // INSERT POINT: _transitionTimingFunction
  3926. },
  3927. _translate: function(x, y) {
  3928. if (this.options.useTransform) {
  3929. /* REPLACE START: _translate */
  3930. this.scrollerStyle[utils.style.transform] = 'translate(' + x + 'px,' + y + 'px)' + this.translateZ;
  3931. /* REPLACE END: _translate */
  3932. } else {
  3933. x = Math.round(x);
  3934. y = Math.round(y);
  3935. this.scrollerStyle.left = x + 'px';
  3936. this.scrollerStyle.top = y + 'px';
  3937. }
  3938. this.x = x;
  3939. this.y = y;
  3940. if (this.indicators) {
  3941. for (var i = this.indicators.length; i--;) {
  3942. this.indicators[i].updatePosition();
  3943. }
  3944. }
  3945. // INSERT POINT: _translate
  3946. },
  3947. _initEvents: function(remove) {
  3948. var eventType = remove ? utils.removeEvent : utils.addEvent,
  3949. target = this.options.bindToWrapper ? this.wrapper : window;
  3950. eventType(window, 'orientationchange', this);
  3951. eventType(window, 'resize', this);
  3952. if (this.options.click) {
  3953. eventType(this.wrapper, 'click', this, true);
  3954. }
  3955. if (!this.options.disableMouse) {
  3956. eventType(this.wrapper, 'mousedown', this);
  3957. eventType(target, 'mousemove', this);
  3958. eventType(target, 'mousecancel', this);
  3959. eventType(target, 'mouseup', this);
  3960. }
  3961. if (utils.hasPointer && !this.options.disablePointer) {
  3962. eventType(this.wrapper, utils.prefixPointerEvent('pointerdown'), this);
  3963. eventType(target, utils.prefixPointerEvent('pointermove'), this);
  3964. eventType(target, utils.prefixPointerEvent('pointercancel'), this);
  3965. eventType(target, utils.prefixPointerEvent('pointerup'), this);
  3966. }
  3967. if (utils.hasTouch && !this.options.disableTouch) {
  3968. eventType(this.wrapper, 'touchstart', this);
  3969. eventType(target, 'touchmove', this);
  3970. eventType(target, 'touchcancel', this);
  3971. eventType(target, 'touchend', this);
  3972. }
  3973. eventType(this.scroller, 'transitionend', this);
  3974. eventType(this.scroller, 'webkitTransitionEnd', this);
  3975. eventType(this.scroller, 'oTransitionEnd', this);
  3976. eventType(this.scroller, 'MSTransitionEnd', this);
  3977. },
  3978. getComputedPosition: function() {
  3979. var matrix = window.getComputedStyle(this.scroller, null),
  3980. x, y;
  3981. if (this.options.useTransform) {
  3982. matrix = matrix[utils.style.transform].split(')')[0].split(', ');
  3983. x = +(matrix[12] || matrix[4]);
  3984. y = +(matrix[13] || matrix[5]);
  3985. } else {
  3986. x = +matrix.left.replace(/[^-\d.]/g, '');
  3987. y = +matrix.top.replace(/[^-\d.]/g, '');
  3988. }
  3989. return {
  3990. x: x,
  3991. y: y
  3992. };
  3993. },
  3994. _initIndicators: function() {
  3995. var interactive = this.options.interactiveScrollbars,
  3996. customStyle = typeof this.options.scrollbars !== 'string',
  3997. indicators = [],
  3998. indicator;
  3999. var that = this;
  4000. this.indicators = [];
  4001. if (this.options.scrollbars) {
  4002. // Vertical scrollbar
  4003. if (this.options.scrollY) {
  4004. indicator = {
  4005. el: createDefaultScrollbar('v', interactive, this.options.scrollbars),
  4006. interactive: interactive,
  4007. defaultScrollbars: true,
  4008. customStyle: customStyle,
  4009. resize: this.options.resizeScrollbars,
  4010. shrink: this.options.shrinkScrollbars,
  4011. fade: this.options.fadeScrollbars,
  4012. listenX: false
  4013. };
  4014. this.wrapper.appendChild(indicator.el);
  4015. indicators.push(indicator);
  4016. }
  4017. // Horizontal scrollbar
  4018. if (this.options.scrollX) {
  4019. indicator = {
  4020. el: createDefaultScrollbar('h', interactive, this.options.scrollbars),
  4021. interactive: interactive,
  4022. defaultScrollbars: true,
  4023. customStyle: customStyle,
  4024. resize: this.options.resizeScrollbars,
  4025. shrink: this.options.shrinkScrollbars,
  4026. fade: this.options.fadeScrollbars,
  4027. listenY: false
  4028. };
  4029. this.wrapper.appendChild(indicator.el);
  4030. indicators.push(indicator);
  4031. }
  4032. }
  4033. if (this.options.indicators) {
  4034. // TODO: check concat compatibility
  4035. indicators = indicators.concat(this.options.indicators);
  4036. }
  4037. for (var i = indicators.length; i--;) {
  4038. this.indicators.push(new Indicator(this, indicators[i]));
  4039. }
  4040. // TODO: check if we can use array.map (wide compatibility and performance issues)
  4041. function _indicatorsMap(fn) {
  4042. for (var i = that.indicators.length; i--;) {
  4043. fn.call(that.indicators[i]);
  4044. }
  4045. }
  4046. if (this.options.fadeScrollbars) {
  4047. this.on('scrollEnd', function() {
  4048. _indicatorsMap(function() {
  4049. this.fade();
  4050. });
  4051. });
  4052. this.on('scrollCancel', function() {
  4053. _indicatorsMap(function() {
  4054. this.fade();
  4055. });
  4056. });
  4057. this.on('scrollStart', function() {
  4058. _indicatorsMap(function() {
  4059. this.fade(1);
  4060. });
  4061. });
  4062. this.on('beforeScrollStart', function() {
  4063. _indicatorsMap(function() {
  4064. this.fade(1, true);
  4065. });
  4066. });
  4067. }
  4068. this.on('refresh', function() {
  4069. _indicatorsMap(function() {
  4070. this.refresh();
  4071. });
  4072. });
  4073. this.on('destroy', function() {
  4074. _indicatorsMap(function() {
  4075. this.destroy();
  4076. });
  4077. delete this.indicators;
  4078. });
  4079. },
  4080. _initWheel: function() {
  4081. utils.addEvent(this.wrapper, 'wheel', this);
  4082. utils.addEvent(this.wrapper, 'mousewheel', this);
  4083. utils.addEvent(this.wrapper, 'DOMMouseScroll', this);
  4084. this.on('destroy', function() {
  4085. utils.removeEvent(this.wrapper, 'wheel', this);
  4086. utils.removeEvent(this.wrapper, 'mousewheel', this);
  4087. utils.removeEvent(this.wrapper, 'DOMMouseScroll', this);
  4088. });
  4089. },
  4090. _wheel: function(e) {
  4091. if (!this.enabled) {
  4092. return;
  4093. }
  4094. e.preventDefault();
  4095. e.stopPropagation();
  4096. var wheelDeltaX, wheelDeltaY,
  4097. newX, newY,
  4098. that = this;
  4099. if (this.wheelTimeout === undefined) {
  4100. that._execEvent('scrollStart');
  4101. }
  4102. // Execute the scrollEnd event after 400ms the wheel stopped scrolling
  4103. clearTimeout(this.wheelTimeout);
  4104. this.wheelTimeout = setTimeout(function() {
  4105. that._execEvent('scrollEnd');
  4106. that.wheelTimeout = undefined;
  4107. }, 400);
  4108. if ('deltaX' in e) {
  4109. if (e.deltaMode === 1) {
  4110. wheelDeltaX = -e.deltaX * this.options.mouseWheelSpeed;
  4111. wheelDeltaY = -e.deltaY * this.options.mouseWheelSpeed;
  4112. } else {
  4113. wheelDeltaX = -e.deltaX;
  4114. wheelDeltaY = -e.deltaY;
  4115. }
  4116. } else if ('wheelDeltaX' in e) {
  4117. wheelDeltaX = e.wheelDeltaX / 120 * this.options.mouseWheelSpeed;
  4118. wheelDeltaY = e.wheelDeltaY / 120 * this.options.mouseWheelSpeed;
  4119. } else if ('wheelDelta' in e) {
  4120. wheelDeltaX = wheelDeltaY = e.wheelDelta / 120 * this.options.mouseWheelSpeed;
  4121. } else if ('detail' in e) {
  4122. wheelDeltaX = wheelDeltaY = -e.detail / 3 * this.options.mouseWheelSpeed;
  4123. } else {
  4124. return;
  4125. }
  4126. wheelDeltaX *= this.options.invertWheelDirection;
  4127. wheelDeltaY *= this.options.invertWheelDirection;
  4128. if (!this.hasVerticalScroll) {
  4129. wheelDeltaX = wheelDeltaY;
  4130. wheelDeltaY = 0;
  4131. }
  4132. if (this.options.snap) {
  4133. newX = this.currentPage.pageX;
  4134. newY = this.currentPage.pageY;
  4135. if (wheelDeltaX > 0) {
  4136. newX--;
  4137. } else if (wheelDeltaX < 0) {
  4138. newX++;
  4139. }
  4140. if (wheelDeltaY > 0) {
  4141. newY--;
  4142. } else if (wheelDeltaY < 0) {
  4143. newY++;
  4144. }
  4145. this.goToPage(newX, newY);
  4146. return;
  4147. }
  4148. newX = this.x + Math.round(this.hasHorizontalScroll ? wheelDeltaX : 0);
  4149. newY = this.y + Math.round(this.hasVerticalScroll ? wheelDeltaY : 0);
  4150. if (newX > 0) {
  4151. newX = 0;
  4152. } else if (newX < this.maxScrollX) {
  4153. newX = this.maxScrollX;
  4154. }
  4155. if (newY > 0) {
  4156. newY = 0;
  4157. } else if (newY < this.maxScrollY) {
  4158. newY = this.maxScrollY;
  4159. }
  4160. this.scrollTo(newX, newY, 0);
  4161. this._execEvent('scroll');
  4162. // INSERT POINT: _wheel
  4163. },
  4164. _initSnap: function() {
  4165. this.currentPage = {};
  4166. if (typeof this.options.snap === 'string') {
  4167. this.options.snap = this.scroller.querySelectorAll(this.options.snap);
  4168. }
  4169. this.on('refresh', function() {
  4170. var i = 0,
  4171. l,
  4172. m = 0,
  4173. n,
  4174. cx, cy,
  4175. x = 0,
  4176. y,
  4177. stepX = this.options.snapStepX || this.wrapperWidth,
  4178. stepY = this.options.snapStepY || this.wrapperHeight,
  4179. el;
  4180. this.pages = [];
  4181. if (!this.wrapperWidth || !this.wrapperHeight || !this.scrollerWidth || !this.scrollerHeight) {
  4182. return;
  4183. }
  4184. if (this.options.snap === true) {
  4185. cx = Math.round(stepX / 2);
  4186. cy = Math.round(stepY / 2);
  4187. while (x > -this.scrollerWidth) {
  4188. this.pages[i] = [];
  4189. l = 0;
  4190. y = 0;
  4191. while (y > -this.scrollerHeight) {
  4192. this.pages[i][l] = {
  4193. x: Math.max(x, this.maxScrollX),
  4194. y: Math.max(y, this.maxScrollY),
  4195. width: stepX,
  4196. height: stepY,
  4197. cx: x - cx,
  4198. cy: y - cy
  4199. };
  4200. y -= stepY;
  4201. l++;
  4202. }
  4203. x -= stepX;
  4204. i++;
  4205. }
  4206. } else {
  4207. el = this.options.snap;
  4208. l = el.length;
  4209. n = -1;
  4210. for (; i < l; i++) {
  4211. if (i === 0 || el[i].offsetLeft <= el[i - 1].offsetLeft) {
  4212. m = 0;
  4213. n++;
  4214. }
  4215. if (!this.pages[m]) {
  4216. this.pages[m] = [];
  4217. }
  4218. x = Math.max(-el[i].offsetLeft, this.maxScrollX);
  4219. y = Math.max(-el[i].offsetTop, this.maxScrollY);
  4220. cx = x - Math.round(el[i].offsetWidth / 2);
  4221. cy = y - Math.round(el[i].offsetHeight / 2);
  4222. this.pages[m][n] = {
  4223. x: x,
  4224. y: y,
  4225. width: el[i].offsetWidth,
  4226. height: el[i].offsetHeight,
  4227. cx: cx,
  4228. cy: cy
  4229. };
  4230. if (x > this.maxScrollX) {
  4231. m++;
  4232. }
  4233. }
  4234. }
  4235. this.goToPage(this.currentPage.pageX || 0, this.currentPage.pageY || 0, 0);
  4236. // Update snap threshold if needed
  4237. if (this.options.snapThreshold % 1 === 0) {
  4238. this.snapThresholdX = this.options.snapThreshold;
  4239. this.snapThresholdY = this.options.snapThreshold;
  4240. } else {
  4241. this.snapThresholdX = Math.round(this.pages[this.currentPage.pageX][this.currentPage.pageY].width * this.options.snapThreshold);
  4242. this.snapThresholdY = Math.round(this.pages[this.currentPage.pageX][this.currentPage.pageY].height * this.options.snapThreshold);
  4243. }
  4244. });
  4245. this.on('flick', function() {
  4246. var time = this.options.snapSpeed || Math.max(
  4247. Math.max(
  4248. Math.min(Math.abs(this.x - this.startX), 1000),
  4249. Math.min(Math.abs(this.y - this.startY), 1000)
  4250. ), 300);
  4251. this.goToPage(
  4252. this.currentPage.pageX + this.directionX,
  4253. this.currentPage.pageY + this.directionY,
  4254. time
  4255. );
  4256. });
  4257. },
  4258. _nearestSnap: function(x, y) {
  4259. if (!this.pages.length) {
  4260. return {
  4261. x: 0,
  4262. y: 0,
  4263. pageX: 0,
  4264. pageY: 0
  4265. };
  4266. }
  4267. var i = 0,
  4268. l = this.pages.length,
  4269. m = 0;
  4270. // Check if we exceeded the snap threshold
  4271. if (Math.abs(x - this.absStartX) < this.snapThresholdX &&
  4272. Math.abs(y - this.absStartY) < this.snapThresholdY) {
  4273. return this.currentPage;
  4274. }
  4275. if (x > 0) {
  4276. x = 0;
  4277. } else if (x < this.maxScrollX) {
  4278. x = this.maxScrollX;
  4279. }
  4280. if (y > 0) {
  4281. y = 0;
  4282. } else if (y < this.maxScrollY) {
  4283. y = this.maxScrollY;
  4284. }
  4285. for (; i < l; i++) {
  4286. if (x >= this.pages[i][0].cx) {
  4287. x = this.pages[i][0].x;
  4288. break;
  4289. }
  4290. }
  4291. l = this.pages[i].length;
  4292. for (; m < l; m++) {
  4293. if (y >= this.pages[0][m].cy) {
  4294. y = this.pages[0][m].y;
  4295. break;
  4296. }
  4297. }
  4298. if (i === this.currentPage.pageX) {
  4299. i += this.directionX;
  4300. if (i < 0) {
  4301. i = 0;
  4302. } else if (i >= this.pages.length) {
  4303. i = this.pages.length - 1;
  4304. }
  4305. x = this.pages[i][0].x;
  4306. }
  4307. if (m === this.currentPage.pageY) {
  4308. m += this.directionY;
  4309. if (m < 0) {
  4310. m = 0;
  4311. } else if (m >= this.pages[0].length) {
  4312. m = this.pages[0].length - 1;
  4313. }
  4314. y = this.pages[0][m].y;
  4315. }
  4316. return {
  4317. x: x,
  4318. y: y,
  4319. pageX: i,
  4320. pageY: m
  4321. };
  4322. },
  4323. goToPage: function(x, y, time, easing) {
  4324. easing = easing || this.options.bounceEasing;
  4325. if (x >= this.pages.length) {
  4326. x = this.pages.length - 1;
  4327. } else if (x < 0) {
  4328. x = 0;
  4329. }
  4330. if (y >= this.pages[x].length) {
  4331. y = this.pages[x].length - 1;
  4332. } else if (y < 0) {
  4333. y = 0;
  4334. }
  4335. var posX = this.pages[x][y].x,
  4336. posY = this.pages[x][y].y;
  4337. time = time === undefined ? this.options.snapSpeed || Math.max(
  4338. Math.max(
  4339. Math.min(Math.abs(posX - this.x), 1000),
  4340. Math.min(Math.abs(posY - this.y), 1000)
  4341. ), 300) : time;
  4342. this.currentPage = {
  4343. x: posX,
  4344. y: posY,
  4345. pageX: x,
  4346. pageY: y
  4347. };
  4348. this.scrollTo(posX, posY, time, easing);
  4349. },
  4350. next: function(time, easing) {
  4351. var x = this.currentPage.pageX,
  4352. y = this.currentPage.pageY;
  4353. x++;
  4354. if (x >= this.pages.length && this.hasVerticalScroll) {
  4355. x = 0;
  4356. y++;
  4357. }
  4358. this.goToPage(x, y, time, easing);
  4359. },
  4360. prev: function(time, easing) {
  4361. var x = this.currentPage.pageX,
  4362. y = this.currentPage.pageY;
  4363. x--;
  4364. if (x < 0 && this.hasVerticalScroll) {
  4365. x = 0;
  4366. y--;
  4367. }
  4368. this.goToPage(x, y, time, easing);
  4369. },
  4370. _initKeys: function() {
  4371. // default key bindings
  4372. var keys = {
  4373. pageUp: 33,
  4374. pageDown: 34,
  4375. end: 35,
  4376. home: 36,
  4377. left: 37,
  4378. up: 38,
  4379. right: 39,
  4380. down: 40
  4381. };
  4382. var i;
  4383. // if you give me characters I give you keycode
  4384. if (typeof this.options.keyBindings === 'object') {
  4385. for (i in this.options.keyBindings) {
  4386. if (typeof this.options.keyBindings[i] === 'string') {
  4387. this.options.keyBindings[i] = this.options.keyBindings[i].toUpperCase().charCodeAt(0);
  4388. }
  4389. }
  4390. } else {
  4391. this.options.keyBindings = {};
  4392. }
  4393. for (i in keys) { // jshint ignore:line
  4394. this.options.keyBindings[i] = this.options.keyBindings[i] || keys[i];
  4395. }
  4396. utils.addEvent(window, 'keydown', this);
  4397. this.on('destroy', function() {
  4398. utils.removeEvent(window, 'keydown', this);
  4399. });
  4400. },
  4401. _key: function(e) {
  4402. if (!this.enabled) {
  4403. return;
  4404. }
  4405. var snap = this.options.snap, // we are using this alot, better to cache it
  4406. newX = snap ? this.currentPage.pageX : this.x,
  4407. newY = snap ? this.currentPage.pageY : this.y,
  4408. now = utils.getTime(),
  4409. prevTime = this.keyTime || 0,
  4410. acceleration = 0.250,
  4411. pos;
  4412. if (this.options.useTransition && this.isInTransition) {
  4413. pos = this.getComputedPosition();
  4414. this._translate(Math.round(pos.x), Math.round(pos.y));
  4415. this.isInTransition = false;
  4416. }
  4417. this.keyAcceleration = now - prevTime < 200 ? Math.min(this.keyAcceleration + acceleration, 50) : 0;
  4418. switch (e.keyCode) {
  4419. case this.options.keyBindings.pageUp:
  4420. if (this.hasHorizontalScroll && !this.hasVerticalScroll) {
  4421. newX += snap ? 1 : this.wrapperWidth;
  4422. } else {
  4423. newY += snap ? 1 : this.wrapperHeight;
  4424. }
  4425. break;
  4426. case this.options.keyBindings.pageDown:
  4427. if (this.hasHorizontalScroll && !this.hasVerticalScroll) {
  4428. newX -= snap ? 1 : this.wrapperWidth;
  4429. } else {
  4430. newY -= snap ? 1 : this.wrapperHeight;
  4431. }
  4432. break;
  4433. case this.options.keyBindings.end:
  4434. newX = snap ? this.pages.length - 1 : this.maxScrollX;
  4435. newY = snap ? this.pages[0].length - 1 : this.maxScrollY;
  4436. break;
  4437. case this.options.keyBindings.home:
  4438. newX = 0;
  4439. newY = 0;
  4440. break;
  4441. case this.options.keyBindings.left:
  4442. newX += snap ? -1 : 5 + this.keyAcceleration >> 0; // jshint ignore:line
  4443. break;
  4444. case this.options.keyBindings.up:
  4445. newY += snap ? 1 : 5 + this.keyAcceleration >> 0; // jshint ignore:line
  4446. break;
  4447. case this.options.keyBindings.right:
  4448. newX -= snap ? -1 : 5 + this.keyAcceleration >> 0; // jshint ignore:line
  4449. break;
  4450. case this.options.keyBindings.down:
  4451. newY -= snap ? 1 : 5 + this.keyAcceleration >> 0; // jshint ignore:line
  4452. break;
  4453. default:
  4454. return;
  4455. }
  4456. if (snap) {
  4457. this.goToPage(newX, newY);
  4458. return;
  4459. }
  4460. if (newX > 0) {
  4461. newX = 0;
  4462. this.keyAcceleration = 0;
  4463. } else if (newX < this.maxScrollX) {
  4464. newX = this.maxScrollX;
  4465. this.keyAcceleration = 0;
  4466. }
  4467. if (newY > 0) {
  4468. newY = 0;
  4469. this.keyAcceleration = 0;
  4470. } else if (newY < this.maxScrollY) {
  4471. newY = this.maxScrollY;
  4472. this.keyAcceleration = 0;
  4473. }
  4474. this.scrollTo(newX, newY, 0);
  4475. this.keyTime = now;
  4476. },
  4477. _animate: function(destX, destY, duration, easingFn) {
  4478. var that = this,
  4479. startX = this.x,
  4480. startY = this.y,
  4481. startTime = utils.getTime(),
  4482. destTime = startTime + duration;
  4483. function step() {
  4484. var now = utils.getTime(),
  4485. newX, newY,
  4486. easing;
  4487. if (now >= destTime) {
  4488. that.isAnimating = false;
  4489. that._translate(destX, destY);
  4490. if (!that.resetPosition(that.options.bounceTime)) {
  4491. that._execEvent('scrollEnd');
  4492. }
  4493. return;
  4494. }
  4495. now = (now - startTime) / duration;
  4496. easing = easingFn(now);
  4497. newX = (destX - startX) * easing + startX;
  4498. newY = (destY - startY) * easing + startY;
  4499. that._translate(newX, newY);
  4500. if (that.isAnimating) {
  4501. rAF(step);
  4502. }
  4503. if (that.options.probeType === 3) {
  4504. that._execEvent('scroll');
  4505. }
  4506. }
  4507. this.isAnimating = true;
  4508. step();
  4509. },
  4510. handleEvent: function(e) {
  4511. switch (e.type) {
  4512. case 'touchstart':
  4513. case 'pointerdown':
  4514. case 'MSPointerDown':
  4515. case 'mousedown':
  4516. this._start(e);
  4517. break;
  4518. case 'touchmove':
  4519. case 'pointermove':
  4520. case 'MSPointerMove':
  4521. case 'mousemove':
  4522. this._move(e);
  4523. break;
  4524. case 'touchend':
  4525. case 'pointerup':
  4526. case 'MSPointerUp':
  4527. case 'mouseup':
  4528. case 'touchcancel':
  4529. case 'pointercancel':
  4530. case 'MSPointerCancel':
  4531. case 'mousecancel':
  4532. this._end(e);
  4533. break;
  4534. case 'orientationchange':
  4535. case 'resize':
  4536. this._resize();
  4537. break;
  4538. case 'transitionend':
  4539. case 'webkitTransitionEnd':
  4540. case 'oTransitionEnd':
  4541. case 'MSTransitionEnd':
  4542. this._transitionEnd(e);
  4543. break;
  4544. case 'wheel':
  4545. case 'DOMMouseScroll':
  4546. case 'mousewheel':
  4547. this._wheel(e);
  4548. break;
  4549. case 'keydown':
  4550. this._key(e);
  4551. break;
  4552. case 'click':
  4553. if (!e._constructed) {
  4554. e.preventDefault();
  4555. e.stopPropagation();
  4556. }
  4557. break;
  4558. }
  4559. }
  4560. };
  4561. function createDefaultScrollbar(direction, interactive, type) {
  4562. var scrollbar = document.createElement('div'),
  4563. indicator = document.createElement('div');
  4564. if (type === true) {
  4565. scrollbar.style.cssText = 'position:absolute;z-index:9999';
  4566. indicator.style.cssText = '-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;position:absolute;background:rgba(0,0,0,0.5);border:1px solid rgba(255,255,255,0.9);border-radius:3px';
  4567. }
  4568. indicator.className = 'iScrollIndicator';
  4569. if (direction === 'h') {
  4570. if (type === true) {
  4571. scrollbar.style.cssText += ';height:5px;left:2px;right:2px;bottom:0';
  4572. indicator.style.height = '100%';
  4573. }
  4574. scrollbar.className = 'iScrollHorizontalScrollbar';
  4575. } else {
  4576. if (type === true) {
  4577. scrollbar.style.cssText += ';width:5px;bottom:2px;top:2px;right:1px';
  4578. indicator.style.width = '100%';
  4579. }
  4580. scrollbar.className = 'iScrollVerticalScrollbar';
  4581. }
  4582. scrollbar.style.cssText += ';overflow:hidden';
  4583. if (!interactive) {
  4584. scrollbar.style.pointerEvents = 'none';
  4585. }
  4586. scrollbar.appendChild(indicator);
  4587. return scrollbar;
  4588. }
  4589. function Indicator(scroller, options) {
  4590. this.wrapper = typeof options.el === 'string' ? document.querySelector(options.el) : options.el;
  4591. this.wrapperStyle = this.wrapper.style;
  4592. this.indicator = this.wrapper.children[0];
  4593. this.indicatorStyle = this.indicator.style;
  4594. this.scroller = scroller;
  4595. this.options = {
  4596. listenX: true,
  4597. listenY: true,
  4598. interactive: false,
  4599. resize: true,
  4600. defaultScrollbars: false,
  4601. shrink: false,
  4602. fade: false,
  4603. speedRatioX: 0,
  4604. speedRatioY: 0
  4605. };
  4606. for (var i in options) { // jshint ignore:line
  4607. this.options[i] = options[i];
  4608. }
  4609. this.sizeRatioX = 1;
  4610. this.sizeRatioY = 1;
  4611. this.maxPosX = 0;
  4612. this.maxPosY = 0;
  4613. if (this.options.interactive) {
  4614. if (!this.options.disableTouch) {
  4615. utils.addEvent(this.indicator, 'touchstart', this);
  4616. utils.addEvent(window, 'touchend', this);
  4617. }
  4618. if (!this.options.disablePointer) {
  4619. utils.addEvent(this.indicator, utils.prefixPointerEvent('pointerdown'), this);
  4620. utils.addEvent(window, utils.prefixPointerEvent('pointerup'), this);
  4621. }
  4622. if (!this.options.disableMouse) {
  4623. utils.addEvent(this.indicator, 'mousedown', this);
  4624. utils.addEvent(window, 'mouseup', this);
  4625. }
  4626. }
  4627. if (this.options.fade) {
  4628. this.wrapperStyle[utils.style.transform] = this.scroller.translateZ;
  4629. this.wrapperStyle[utils.style.transitionDuration] = utils.isBadAndroid ? '0.001s' : '0ms';
  4630. this.wrapperStyle.opacity = '0';
  4631. }
  4632. }
  4633. Indicator.prototype = {
  4634. handleEvent: function(e) {
  4635. switch (e.type) {
  4636. case 'touchstart':
  4637. case 'pointerdown':
  4638. case 'MSPointerDown':
  4639. case 'mousedown':
  4640. this._start(e);
  4641. break;
  4642. case 'touchmove':
  4643. case 'pointermove':
  4644. case 'MSPointerMove':
  4645. case 'mousemove':
  4646. this._move(e);
  4647. break;
  4648. case 'touchend':
  4649. case 'pointerup':
  4650. case 'MSPointerUp':
  4651. case 'mouseup':
  4652. case 'touchcancel':
  4653. case 'pointercancel':
  4654. case 'MSPointerCancel':
  4655. case 'mousecancel':
  4656. this._end(e);
  4657. break;
  4658. }
  4659. },
  4660. destroy: function() {
  4661. if (this.options.interactive) {
  4662. utils.removeEvent(this.indicator, 'touchstart', this);
  4663. utils.removeEvent(this.indicator, utils.prefixPointerEvent('pointerdown'), this);
  4664. utils.removeEvent(this.indicator, 'mousedown', this);
  4665. utils.removeEvent(window, 'touchmove', this);
  4666. utils.removeEvent(window, utils.prefixPointerEvent('pointermove'), this);
  4667. utils.removeEvent(window, 'mousemove', this);
  4668. utils.removeEvent(window, 'touchend', this);
  4669. utils.removeEvent(window, utils.prefixPointerEvent('pointerup'), this);
  4670. utils.removeEvent(window, 'mouseup', this);
  4671. }
  4672. if (this.options.defaultScrollbars) {
  4673. this.wrapper.parentNode.removeChild(this.wrapper);
  4674. }
  4675. },
  4676. _start: function(e) {
  4677. var point = e.touches ? e.touches[0] : e;
  4678. e.preventDefault();
  4679. e.stopPropagation();
  4680. this.transitionTime();
  4681. this.initiated = true;
  4682. this.moved = false;
  4683. this.lastPointX = point.pageX;
  4684. this.lastPointY = point.pageY;
  4685. this.startTime = utils.getTime();
  4686. if (!this.options.disableTouch) {
  4687. utils.addEvent(window, 'touchmove', this);
  4688. }
  4689. if (!this.options.disablePointer) {
  4690. utils.addEvent(window, utils.prefixPointerEvent('pointermove'), this);
  4691. }
  4692. if (!this.options.disableMouse) {
  4693. utils.addEvent(window, 'mousemove', this);
  4694. }
  4695. this.scroller._execEvent('beforeScrollStart');
  4696. },
  4697. _move: function(e) {
  4698. var point = e.touches ? e.touches[0] : e,
  4699. deltaX, deltaY,
  4700. newX, newY,
  4701. timestamp = utils.getTime();
  4702. if (!this.moved) {
  4703. this.scroller._execEvent('scrollStart');
  4704. }
  4705. this.moved = true;
  4706. deltaX = point.pageX - this.lastPointX;
  4707. this.lastPointX = point.pageX;
  4708. deltaY = point.pageY - this.lastPointY;
  4709. this.lastPointY = point.pageY;
  4710. newX = this.x + deltaX;
  4711. newY = this.y + deltaY;
  4712. this._pos(newX, newY);
  4713. if (this.scroller.options.probeType === 1 && timestamp - this.startTime > 300) {
  4714. this.startTime = timestamp;
  4715. this.scroller._execEvent('scroll');
  4716. } else if (this.scroller.options.probeType > 1) {
  4717. this.scroller._execEvent('scroll');
  4718. }
  4719. // INSERT POINT: indicator._move
  4720. e.preventDefault();
  4721. e.stopPropagation();
  4722. },
  4723. _end: function(e) {
  4724. if (!this.initiated) {
  4725. return;
  4726. }
  4727. this.initiated = false;
  4728. e.preventDefault();
  4729. e.stopPropagation();
  4730. utils.removeEvent(window, 'touchmove', this);
  4731. utils.removeEvent(window, utils.prefixPointerEvent('pointermove'), this);
  4732. utils.removeEvent(window, 'mousemove', this);
  4733. if (this.scroller.options.snap) {
  4734. var snap = this.scroller._nearestSnap(this.scroller.x, this.scroller.y);
  4735. var time = this.options.snapSpeed || Math.max(
  4736. Math.max(
  4737. Math.min(Math.abs(this.scroller.x - snap.x), 1000),
  4738. Math.min(Math.abs(this.scroller.y - snap.y), 1000)
  4739. ), 300);
  4740. if (this.scroller.x !== snap.x || this.scroller.y !== snap.y) {
  4741. this.scroller.directionX = 0;
  4742. this.scroller.directionY = 0;
  4743. this.scroller.currentPage = snap;
  4744. this.scroller.scrollTo(snap.x, snap.y, time, this.scroller.options.bounceEasing);
  4745. }
  4746. }
  4747. if (this.moved) {
  4748. this.scroller._execEvent('scrollEnd');
  4749. }
  4750. },
  4751. transitionTime: function(time) {
  4752. time = time || 0;
  4753. this.indicatorStyle[utils.style.transitionDuration] = time + 'ms';
  4754. if (!time && utils.isBadAndroid) {
  4755. this.indicatorStyle[utils.style.transitionDuration] = '0.001s';
  4756. }
  4757. },
  4758. transitionTimingFunction: function(easing) {
  4759. this.indicatorStyle[utils.style.transitionTimingFunction] = easing;
  4760. },
  4761. refresh: function() {
  4762. this.transitionTime();
  4763. if (this.options.listenX && !this.options.listenY) {
  4764. this.indicatorStyle.display = this.scroller.hasHorizontalScroll ? 'block' : 'none';
  4765. } else if (this.options.listenY && !this.options.listenX) {
  4766. this.indicatorStyle.display = this.scroller.hasVerticalScroll ? 'block' : 'none';
  4767. } else {
  4768. this.indicatorStyle.display = this.scroller.hasHorizontalScroll || this.scroller.hasVerticalScroll ? 'block' : 'none';
  4769. }
  4770. if (this.scroller.hasHorizontalScroll && this.scroller.hasVerticalScroll) {
  4771. utils.addClass(this.wrapper, 'iScrollBothScrollbars');
  4772. utils.removeClass(this.wrapper, 'iScrollLoneScrollbar');
  4773. if (this.options.defaultScrollbars && this.options.customStyle) {
  4774. if (this.options.listenX) {
  4775. this.wrapper.style.right = '8px';
  4776. } else {
  4777. this.wrapper.style.bottom = '8px';
  4778. }
  4779. }
  4780. } else {
  4781. utils.removeClass(this.wrapper, 'iScrollBothScrollbars');
  4782. utils.addClass(this.wrapper, 'iScrollLoneScrollbar');
  4783. if (this.options.defaultScrollbars && this.options.customStyle) {
  4784. if (this.options.listenX) {
  4785. this.wrapper.style.right = '2px';
  4786. } else {
  4787. this.wrapper.style.bottom = '2px';
  4788. }
  4789. }
  4790. }
  4791. // var r = this.wrapper.offsetHeight; // force refresh
  4792. if (this.options.listenX) {
  4793. this.wrapperWidth = this.wrapper.clientWidth;
  4794. if (this.options.resize) {
  4795. this.indicatorWidth = Math.max(Math.round(this.wrapperWidth * this.wrapperWidth / (this.scroller.scrollerWidth || this.wrapperWidth || 1)), 8);
  4796. this.indicatorStyle.width = this.indicatorWidth + 'px';
  4797. } else {
  4798. this.indicatorWidth = this.indicator.clientWidth;
  4799. }
  4800. this.maxPosX = this.wrapperWidth - this.indicatorWidth;
  4801. if (this.options.shrink === 'clip') {
  4802. this.minBoundaryX = -this.indicatorWidth + 8;
  4803. this.maxBoundaryX = this.wrapperWidth - 8;
  4804. } else {
  4805. this.minBoundaryX = 0;
  4806. this.maxBoundaryX = this.maxPosX;
  4807. }
  4808. this.sizeRatioX = this.options.speedRatioX || (this.scroller.maxScrollX && (this.maxPosX / this.scroller.maxScrollX));
  4809. }
  4810. if (this.options.listenY) {
  4811. this.wrapperHeight = this.wrapper.clientHeight;
  4812. if (this.options.resize) {
  4813. this.indicatorHeight = Math.max(Math.round(this.wrapperHeight * this.wrapperHeight / (this.scroller.scrollerHeight || this.wrapperHeight || 1)), 8);
  4814. this.indicatorStyle.height = this.indicatorHeight + 'px';
  4815. } else {
  4816. this.indicatorHeight = this.indicator.clientHeight;
  4817. }
  4818. this.maxPosY = this.wrapperHeight - this.indicatorHeight;
  4819. if (this.options.shrink === 'clip') {
  4820. this.minBoundaryY = -this.indicatorHeight + 8;
  4821. this.maxBoundaryY = this.wrapperHeight - 8;
  4822. } else {
  4823. this.minBoundaryY = 0;
  4824. this.maxBoundaryY = this.maxPosY;
  4825. }
  4826. this.maxPosY = this.wrapperHeight - this.indicatorHeight;
  4827. this.sizeRatioY = this.options.speedRatioY || (this.scroller.maxScrollY && (this.maxPosY / this.scroller.maxScrollY));
  4828. }
  4829. this.updatePosition();
  4830. },
  4831. updatePosition: function() {
  4832. var x = this.options.listenX && Math.round(this.sizeRatioX * this.scroller.x) || 0,
  4833. y = this.options.listenY && Math.round(this.sizeRatioY * this.scroller.y) || 0;
  4834. if (!this.options.ignoreBoundaries) {
  4835. if (x < this.minBoundaryX) {
  4836. if (this.options.shrink === 'scale') {
  4837. this.width = Math.max(this.indicatorWidth + x, 8);
  4838. this.indicatorStyle.width = this.width + 'px';
  4839. }
  4840. x = this.minBoundaryX;
  4841. } else if (x > this.maxBoundaryX) {
  4842. if (this.options.shrink === 'scale') {
  4843. this.width = Math.max(this.indicatorWidth - (x - this.maxPosX), 8);
  4844. this.indicatorStyle.width = this.width + 'px';
  4845. x = this.maxPosX + this.indicatorWidth - this.width;
  4846. } else {
  4847. x = this.maxBoundaryX;
  4848. }
  4849. } else if (this.options.shrink === 'scale' && this.width !== this.indicatorWidth) {
  4850. this.width = this.indicatorWidth;
  4851. this.indicatorStyle.width = this.width + 'px';
  4852. }
  4853. if (y < this.minBoundaryY) {
  4854. if (this.options.shrink === 'scale') {
  4855. this.height = Math.max(this.indicatorHeight + y * 3, 8);
  4856. this.indicatorStyle.height = this.height + 'px';
  4857. }
  4858. y = this.minBoundaryY;
  4859. } else if (y > this.maxBoundaryY) {
  4860. if (this.options.shrink === 'scale') {
  4861. this.height = Math.max(this.indicatorHeight - (y - this.maxPosY) * 3, 8);
  4862. this.indicatorStyle.height = this.height + 'px';
  4863. y = this.maxPosY + this.indicatorHeight - this.height;
  4864. } else {
  4865. y = this.maxBoundaryY;
  4866. }
  4867. } else if (this.options.shrink === 'scale' && this.height !== this.indicatorHeight) {
  4868. this.height = this.indicatorHeight;
  4869. this.indicatorStyle.height = this.height + 'px';
  4870. }
  4871. }
  4872. this.x = x;
  4873. this.y = y;
  4874. if (this.scroller.options.useTransform) {
  4875. this.indicatorStyle[utils.style.transform] = 'translate(' + x + 'px,' + y + 'px)' + this.scroller.translateZ;
  4876. } else {
  4877. this.indicatorStyle.left = x + 'px';
  4878. this.indicatorStyle.top = y + 'px';
  4879. }
  4880. },
  4881. _pos: function(x, y) {
  4882. if (x < 0) {
  4883. x = 0;
  4884. } else if (x > this.maxPosX) {
  4885. x = this.maxPosX;
  4886. }
  4887. if (y < 0) {
  4888. y = 0;
  4889. } else if (y > this.maxPosY) {
  4890. y = this.maxPosY;
  4891. }
  4892. x = this.options.listenX ? Math.round(x / this.sizeRatioX) : this.scroller.x;
  4893. y = this.options.listenY ? Math.round(y / this.sizeRatioY) : this.scroller.y;
  4894. this.scroller.scrollTo(x, y);
  4895. },
  4896. fade: function(val, hold) {
  4897. if (hold && !this.visible) {
  4898. return;
  4899. }
  4900. clearTimeout(this.fadeTimeout);
  4901. this.fadeTimeout = null;
  4902. var time = val ? 250 : 500,
  4903. delay = val ? 0 : 300;
  4904. val = val ? '1' : '0';
  4905. this.wrapperStyle[utils.style.transitionDuration] = time + 'ms';
  4906. this.fadeTimeout = setTimeout((function(val) {
  4907. this.wrapperStyle.opacity = val;
  4908. this.visible = +val;
  4909. }).bind(this, val), delay);
  4910. }
  4911. };
  4912. IScroll.utils = utils;
  4913. window.IScroll = IScroll;
  4914. }(window);
  4915. /* ===============================================================================
  4916. ************ scroller ************
  4917. =============================================================================== */
  4918. + function($) {
  4919. "use strict";
  4920. //重置zepto自带的滚动条
  4921. var _zeptoMethodCache = {
  4922. "scrollTop": $.fn.scrollTop,
  4923. "scrollLeft": $.fn.scrollLeft
  4924. };
  4925. //重置scrollLeft和scrollRight
  4926. (function() {
  4927. $.extend($.fn, {
  4928. scrollTop: function(top, dur) {
  4929. if (!this.length) return;
  4930. var scroller = this.data('scroller');
  4931. if (scroller && scroller.scroller) { //js滚动
  4932. return scroller.scrollTop(top, dur);
  4933. } else {
  4934. return _zeptoMethodCache.scrollTop.apply(this, arguments);
  4935. }
  4936. }
  4937. });
  4938. $.extend($.fn, {
  4939. scrollLeft: function(left, dur) {
  4940. if (!this.length) return;
  4941. var scroller = this.data('scroller');
  4942. if (scroller && scroller.scroller) { //js滚动
  4943. return scroller.scrollLeft(left, dur);
  4944. } else {
  4945. return _zeptoMethodCache.scrollLeft.apply(this, arguments);
  4946. }
  4947. }
  4948. });
  4949. })();
  4950. //自定义的滚动条
  4951. var Scroller = function(pageContent, _options) {
  4952. var $pageContent = this.$pageContent = $(pageContent);
  4953. this.options = $.extend({}, this._defaults, _options);
  4954. var type = this.options.type;
  4955. //auto的type,系统版本的小于4.4.0的安卓设备和系统版本小于6.0.0的ios设备,启用js版的iscoll
  4956. var useJSScroller = (type === 'js') || (type === 'auto' && ($.device.android && $.compareVersion('4.4.0', $.device.osVersion) > -1) || (type === 'auto' && ($.device.ios && $.compareVersion('6.0.0', $.device.osVersion) > -1)));
  4957. if (useJSScroller) {
  4958. var $pageContentInner = $pageContent.find('.content-inner');
  4959. //如果滚动内容没有被包裹,自动添加wrap
  4960. if (!$pageContentInner[0]) {
  4961. // $pageContent.html('<div class="content-inner">' + $pageContent.html() + '</div>');
  4962. var children = $pageContent.children();
  4963. if (children.length < 1) {
  4964. $pageContent.children().wrapAll('<div class="content-inner"></div>');
  4965. } else {
  4966. $pageContent.html('<div class="content-inner">' + $pageContent.html() + '</div>');
  4967. }
  4968. }
  4969. if ($pageContent.hasClass('pull-to-refresh-content')) {
  4970. //因为iscroll 当页面高度不足 100% 时无法滑动,所以无法触发下拉动作,这里改动一下高度
  4971. //区分是否有.bar容器,如有,则content的top:0,无则content的top:-2.2rem,这里取2.2rem的最大值,近60
  4972. var minHeight = $(window).height() + ($pageContent.prev().hasClass(".bar") ? 1 : 61);
  4973. $pageContent.find('.content-inner').css('min-height', minHeight + 'px');
  4974. }
  4975. var ptr = $(pageContent).hasClass('pull-to-refresh-content');
  4976. //js滚动模式,用transform移动内容区位置,会导致fixed失效,表现类似absolute。因此禁用transform模式
  4977. var useTransform = $pageContent.find('.fixed-tab').length === 0;
  4978. var options = {
  4979. probeType: 1,
  4980. mouseWheel: true,
  4981. //解决安卓js模式下,刷新滚动条后绑定的事件不响应,对chrome内核浏览器设置click:true
  4982. click: $.device.androidChrome,
  4983. useTransform: useTransform,
  4984. //js模式下允许滚动条横向滚动,但是需要注意,滚动容易宽度必须大于屏幕宽度滚动才生效
  4985. scrollX: true
  4986. };
  4987. if (ptr) {
  4988. options.ptr = true;
  4989. options.ptrOffset = 44;
  4990. }
  4991. //如果用js滚动条,用transform计算内容区位置,position:fixed将实效。若有.fixed-tab,强制使用native滚动条;备选方案,略粗暴
  4992. // if($(pageContent).find('.fixed-tab').length>0){
  4993. // $pageContent.addClass('native-scroll');
  4994. // return;
  4995. // }
  4996. this.scroller = new IScroll(pageContent, options); // jshint ignore:line
  4997. //和native滚动统一起来
  4998. this._bindEventToDomWhenJs();
  4999. $.initPullToRefresh = $._pullToRefreshJSScroll.initPullToRefresh;
  5000. $.pullToRefreshDone = $._pullToRefreshJSScroll.pullToRefreshDone;
  5001. $.pullToRefreshTrigger = $._pullToRefreshJSScroll.pullToRefreshTrigger;
  5002. $.destroyToRefresh = $._pullToRefreshJSScroll.destroyToRefresh;
  5003. $pageContent.addClass('javascript-scroll');
  5004. if (!useTransform) {
  5005. $pageContent.find('.content-inner').css({
  5006. width: '100%',
  5007. position: 'absolute'
  5008. });
  5009. }
  5010. //如果页面本身已经进行了原生滚动,那么把这个滚动换成JS的滚动
  5011. var nativeScrollTop = this.$pageContent[0].scrollTop;
  5012. if(nativeScrollTop) {
  5013. this.$pageContent[0].scrollTop = 0;
  5014. this.scrollTop(nativeScrollTop);
  5015. }
  5016. } else {
  5017. $pageContent.addClass('native-scroll');
  5018. }
  5019. };
  5020. Scroller.prototype = {
  5021. _defaults: {
  5022. type: 'native',
  5023. },
  5024. _bindEventToDomWhenJs: function() {
  5025. //"scrollStart", //the scroll started.
  5026. //"scroll", //the content is scrolling. Available only in scroll-probe.js edition. See onScroll event.
  5027. //"scrollEnd", //content stopped scrolling.
  5028. if (this.scroller) {
  5029. var self = this;
  5030. this.scroller.on('scrollStart', function() {
  5031. self.$pageContent.trigger('scrollstart');
  5032. });
  5033. this.scroller.on('scroll', function() {
  5034. self.$pageContent.trigger('scroll');
  5035. });
  5036. this.scroller.on('scrollEnd', function() {
  5037. self.$pageContent.trigger('scrollend');
  5038. });
  5039. } else {
  5040. //TODO: 实现native的scrollStart和scrollEnd
  5041. }
  5042. },
  5043. scrollTop: function(top, dur) {
  5044. if (this.scroller) {
  5045. if (top !== undefined) {
  5046. this.scroller.scrollTo(0, -1 * top, dur);
  5047. } else {
  5048. return this.scroller.getComputedPosition().y * -1;
  5049. }
  5050. } else {
  5051. return this.$pageContent.scrollTop(top, dur);
  5052. }
  5053. return this;
  5054. },
  5055. scrollLeft: function(left, dur) {
  5056. if (this.scroller) {
  5057. if (left !== undefined) {
  5058. this.scroller.scrollTo(-1 * left, 0);
  5059. } else {
  5060. return this.scroller.getComputedPosition().x * -1;
  5061. }
  5062. } else {
  5063. return this.$pageContent.scrollTop(left, dur);
  5064. }
  5065. return this;
  5066. },
  5067. on: function(event, callback) {
  5068. if (this.scroller) {
  5069. this.scroller.on(event, function() {
  5070. callback.call(this.wrapper);
  5071. });
  5072. } else {
  5073. this.$pageContent.on(event, callback);
  5074. }
  5075. return this;
  5076. },
  5077. off: function(event, callback) {
  5078. if (this.scroller) {
  5079. this.scroller.off(event, callback);
  5080. } else {
  5081. this.$pageContent.off(event, callback);
  5082. }
  5083. return this;
  5084. },
  5085. refresh: function() {
  5086. if (this.scroller) this.scroller.refresh();
  5087. return this;
  5088. },
  5089. scrollHeight: function() {
  5090. if (this.scroller) {
  5091. return this.scroller.scrollerHeight;
  5092. } else {
  5093. return this.$pageContent[0].scrollHeight;
  5094. }
  5095. }
  5096. };
  5097. //Scroller PLUGIN DEFINITION
  5098. // =======================
  5099. function Plugin(option) {
  5100. var args = Array.apply(null, arguments);
  5101. args.shift();
  5102. var internal_return;
  5103. this.each(function() {
  5104. var $this = $(this);
  5105. var options = $.extend({}, $this.dataset(), typeof option === 'object' && option);
  5106. var data = $this.data('scroller');
  5107. //如果 scroller 没有被初始化,对scroller 进行初始化r
  5108. if (!data) {
  5109. //获取data-api的
  5110. $this.data('scroller', (data = new Scroller(this, options)));
  5111. }
  5112. if (typeof option === 'string' && typeof data[option] === 'function') {
  5113. internal_return = data[option].apply(data, args);
  5114. if (internal_return !== undefined)
  5115. return false;
  5116. }
  5117. });
  5118. if (internal_return !== undefined)
  5119. return internal_return;
  5120. else
  5121. return this;
  5122. }
  5123. var old = $.fn.scroller;
  5124. $.fn.scroller = Plugin;
  5125. $.fn.scroller.Constructor = Scroller;
  5126. // Scroll NO CONFLICT
  5127. // =================
  5128. $.fn.scroller.noConflict = function() {
  5129. $.fn.scroller = old;
  5130. return this;
  5131. };
  5132. //添加data-api
  5133. $(function() {
  5134. $('[data-toggle="scroller"]').scroller();
  5135. });
  5136. //统一的接口,带有 .javascript-scroll 的content 进行刷新
  5137. $.refreshScroller = function(content) {
  5138. if (content) {
  5139. $(content).scroller('refresh');
  5140. } else {
  5141. $('.javascript-scroll').each(function() {
  5142. $(this).scroller('refresh');
  5143. });
  5144. }
  5145. };
  5146. //全局初始化方法,会对页面上的 [data-toggle="scroller"],.content. 进行滚动条初始化
  5147. $.initScroller = function(option) {
  5148. this.options = $.extend({}, typeof option === 'object' && option);
  5149. $('[data-toggle="scroller"],.content').scroller(option);
  5150. };
  5151. //获取scroller对象
  5152. $.getScroller = function(content) {
  5153. //以前默认只能有一个无限滚动,因此infinitescroll都是加在content上,现在允许里面有多个,因此要判断父元素是否有content
  5154. content = content.hasClass('content') ? content : content.parents('.content');
  5155. if (content) {
  5156. return $(content).data('scroller');
  5157. } else {
  5158. return $('.content.javascript-scroll').data('scroller');
  5159. }
  5160. };
  5161. //检测滚动类型,
  5162. //‘js’: javascript 滚动条
  5163. //‘native’: 原生滚动条
  5164. $.detectScrollerType = function(content) {
  5165. if (content) {
  5166. if ($(content).data('scroller') && $(content).data('scroller').scroller) {
  5167. return 'js';
  5168. } else {
  5169. return 'native';
  5170. }
  5171. }
  5172. };
  5173. }(Zepto);
  5174. /* ===============================================================================
  5175. ************ Tabs ************
  5176. =============================================================================== */
  5177. +function ($) {
  5178. "use strict";
  5179. var showTab = function (tab, tabLink, force) {
  5180. var newTab = $(tab);
  5181. if (arguments.length === 2) {
  5182. if (typeof tabLink === 'boolean') {
  5183. force = tabLink;
  5184. }
  5185. }
  5186. if (newTab.length === 0) return false;
  5187. if (newTab.hasClass('active')) {
  5188. if (force) newTab.trigger('show');
  5189. return false;
  5190. }
  5191. var tabs = newTab.parent('.tabs');
  5192. if (tabs.length === 0) return false;
  5193. // Animated tabs
  5194. /*var isAnimatedTabs = tabs.parent().hasClass('tabs-animated-wrap');
  5195. if (isAnimatedTabs) {
  5196. tabs.transform('translate3d(' + -newTab.index() * 100 + '%,0,0)');
  5197. }*/
  5198. // Remove active class from old tabs
  5199. var oldTab = tabs.children('.tab.active').removeClass('active');
  5200. // Add active class to new tab
  5201. newTab.addClass('active');
  5202. // Trigger 'show' event on new tab
  5203. newTab.trigger('show');
  5204. // Update navbars in new tab
  5205. /*if (!isAnimatedTabs && newTab.find('.navbar').length > 0) {
  5206. // Find tab's view
  5207. var viewContainer;
  5208. if (newTab.hasClass(app.params.viewClass)) viewContainer = newTab[0];
  5209. else viewContainer = newTab.parents('.' + app.params.viewClass)[0];
  5210. app.sizeNavbars(viewContainer);
  5211. }*/
  5212. // Find related link for new tab
  5213. if (tabLink) tabLink = $(tabLink);
  5214. else {
  5215. // Search by id
  5216. if (typeof tab === 'string') tabLink = $('.tab-link[href="' + tab + '"]');
  5217. else tabLink = $('.tab-link[href="#' + newTab.attr('id') + '"]');
  5218. // Search by data-tab
  5219. if (!tabLink || tabLink && tabLink.length === 0) {
  5220. $('[data-tab]').each(function () {
  5221. if (newTab.is($(this).attr('data-tab'))) tabLink = $(this);
  5222. });
  5223. }
  5224. }
  5225. if (tabLink.length === 0) return;
  5226. // Find related link for old tab
  5227. var oldTabLink;
  5228. if (oldTab && oldTab.length > 0) {
  5229. // Search by id
  5230. var oldTabId = oldTab.attr('id');
  5231. if (oldTabId) oldTabLink = $('.tab-link[href="#' + oldTabId + '"]');
  5232. // Search by data-tab
  5233. if (!oldTabLink || oldTabLink && oldTabLink.length === 0) {
  5234. $('[data-tab]').each(function () {
  5235. if (oldTab.is($(this).attr('data-tab'))) oldTabLink = $(this);
  5236. });
  5237. }
  5238. }
  5239. // Update links' classes
  5240. if (tabLink && tabLink.length > 0) tabLink.addClass('active');
  5241. if (oldTabLink && oldTabLink.length > 0) oldTabLink.removeClass('active');
  5242. tabLink.trigger('active');
  5243. //app.refreshScroller();
  5244. return true;
  5245. };
  5246. var old = $.showTab;
  5247. $.showTab = showTab;
  5248. $.showTab.noConflict = function () {
  5249. $.showTab = old;
  5250. return this;
  5251. };
  5252. //a标签上的click事件,在iscroll下响应有问题
  5253. $(document).on("click", ".tab-link", function(e) {
  5254. e.preventDefault();
  5255. var clicked = $(this);
  5256. showTab(clicked.data("tab") || clicked.attr('href'), clicked);
  5257. });
  5258. }(Zepto);
  5259. /* ===============================================================================
  5260. ************ Tabs ************
  5261. =============================================================================== */
  5262. +function ($) {
  5263. "use strict";
  5264. $.initFixedTab = function(){
  5265. var $fixedTab = $('.fixed-tab');
  5266. if ($fixedTab.length === 0) return;
  5267. $('.fixed-tab').fixedTab();//默认{offset: 0}
  5268. };
  5269. var FixedTab = function(pageContent, _options) {
  5270. var $pageContent = this.$pageContent = $(pageContent);
  5271. var shadow = $pageContent.clone();
  5272. var fixedTop = $pageContent[0].getBoundingClientRect().top;
  5273. shadow.css('visibility', 'hidden');
  5274. this.options = $.extend({}, this._defaults, {
  5275. fixedTop: fixedTop,
  5276. shadow: shadow,
  5277. offset: 0
  5278. }, _options);
  5279. this._bindEvents();
  5280. };
  5281. FixedTab.prototype = {
  5282. _defaults: {
  5283. offset: 0,
  5284. },
  5285. _bindEvents: function() {
  5286. this.$pageContent.parents('.content').on('scroll', this._scrollHandler.bind(this));
  5287. this.$pageContent.on('active', '.tab-link', this._tabLinkHandler.bind(this));
  5288. },
  5289. _tabLinkHandler: function(ev) {
  5290. var isFixed = $(ev.target).parents('.buttons-fixed').length > 0;
  5291. var fixedTop = this.options.fixedTop;
  5292. var offset = this.options.offset;
  5293. $.refreshScroller();
  5294. if (!isFixed) return;
  5295. this.$pageContent.parents('.content').scrollTop(fixedTop - offset);
  5296. },
  5297. // 滚动核心代码
  5298. _scrollHandler: function(ev) {
  5299. var $scroller = $(ev.target);
  5300. var $pageContent = this.$pageContent;
  5301. var shadow = this.options.shadow;
  5302. var offset = this.options.offset;
  5303. var fixedTop = this.options.fixedTop;
  5304. var scrollTop = $scroller.scrollTop();
  5305. var isFixed = scrollTop >= fixedTop - offset;
  5306. if (isFixed) {
  5307. shadow.insertAfter($pageContent);
  5308. $pageContent.addClass('buttons-fixed').css('top', offset);
  5309. } else {
  5310. shadow.remove();
  5311. $pageContent.removeClass('buttons-fixed').css('top', 0);
  5312. }
  5313. }
  5314. };
  5315. //FixedTab PLUGIN DEFINITION
  5316. // =======================
  5317. function Plugin(option) {
  5318. var args = Array.apply(null, arguments);
  5319. args.shift();
  5320. this.each(function() {
  5321. var $this = $(this);
  5322. var options = $.extend({}, $this.dataset(), typeof option === 'object' && option);
  5323. var data = $this.data('fixedtab');
  5324. if (!data) {
  5325. //获取data-api的
  5326. $this.data('fixedtab', (data = new FixedTab(this, options)));
  5327. }
  5328. });
  5329. }
  5330. $.fn.fixedTab = Plugin;
  5331. $.fn.fixedTab.Constructor = FixedTab;
  5332. $(document).on('pageInit',function(){
  5333. $.initFixedTab();
  5334. });
  5335. }(Zepto);
  5336. + function($) {
  5337. "use strict";
  5338. //这里实在js滚动时使用的下拉刷新代码。
  5339. var refreshTime = 0;
  5340. var initPullToRefreshJS = function(pageContainer) {
  5341. var eventsTarget = $(pageContainer);
  5342. if (!eventsTarget.hasClass('pull-to-refresh-content')) {
  5343. eventsTarget = eventsTarget.find('.pull-to-refresh-content');
  5344. }
  5345. if (!eventsTarget || eventsTarget.length === 0) return;
  5346. var page = eventsTarget.hasClass('content') ? eventsTarget : eventsTarget.parents('.content');
  5347. var scroller = $.getScroller(page[0]);
  5348. if(!scroller) return;
  5349. var container = eventsTarget;
  5350. function handleScroll() {
  5351. if (container.hasClass('refreshing')) return;
  5352. if (scroller.scrollTop() * -1 >= 44) {
  5353. container.removeClass('pull-down').addClass('pull-up');
  5354. } else {
  5355. container.removeClass('pull-up').addClass('pull-down');
  5356. }
  5357. }
  5358. function handleRefresh() {
  5359. if (container.hasClass('refreshing')) return;
  5360. container.removeClass('pull-down pull-up');
  5361. container.addClass('refreshing transitioning');
  5362. container.trigger('refresh');
  5363. refreshTime = +new Date();
  5364. }
  5365. scroller.on('scroll', handleScroll);
  5366. scroller.scroller.on('ptr', handleRefresh);
  5367. // Detach Events on page remove
  5368. function destroyPullToRefresh() {
  5369. scroller.off('scroll', handleScroll);
  5370. scroller.scroller.off('ptr', handleRefresh);
  5371. }
  5372. eventsTarget[0].destroyPullToRefresh = destroyPullToRefresh;
  5373. };
  5374. var pullToRefreshDoneJS = function(container) {
  5375. container = $(container);
  5376. if (container.length === 0) container = $('.pull-to-refresh-content.refreshing');
  5377. if (container.length === 0) return;
  5378. var interval = (+new Date()) - refreshTime;
  5379. var timeOut = interval > 1000 ? 0 : 1000 - interval; //long than bounce time
  5380. var scroller = $.getScroller(container);
  5381. setTimeout(function() {
  5382. scroller.refresh();
  5383. container.removeClass('refreshing');
  5384. container.transitionEnd(function() {
  5385. container.removeClass("transitioning");
  5386. });
  5387. }, timeOut);
  5388. };
  5389. var pullToRefreshTriggerJS = function(container) {
  5390. container = $(container);
  5391. if (container.length === 0) container = $('.pull-to-refresh-content');
  5392. if (container.hasClass('refreshing')) return;
  5393. container.addClass('refreshing');
  5394. var scroller = $.getScroller(container);
  5395. scroller.scrollTop(44 + 1, 200);
  5396. container.trigger('refresh');
  5397. };
  5398. var destroyPullToRefreshJS = function(pageContainer) {
  5399. pageContainer = $(pageContainer);
  5400. var pullToRefreshContent = pageContainer.hasClass('pull-to-refresh-content') ? pageContainer : pageContainer.find('.pull-to-refresh-content');
  5401. if (pullToRefreshContent.length === 0) return;
  5402. if (pullToRefreshContent[0].destroyPullToRefresh) pullToRefreshContent[0].destroyPullToRefresh();
  5403. };
  5404. $._pullToRefreshJSScroll = {
  5405. "initPullToRefresh": initPullToRefreshJS,
  5406. "pullToRefreshDone": pullToRefreshDoneJS,
  5407. "pullToRefreshTrigger": pullToRefreshTriggerJS,
  5408. "destroyPullToRefresh": destroyPullToRefreshJS,
  5409. };
  5410. }(Zepto); // jshint ignore:line
  5411. + function($) {
  5412. 'use strict';
  5413. $.initPullToRefresh = function(pageContainer) {
  5414. var eventsTarget = $(pageContainer);
  5415. if (!eventsTarget.hasClass('pull-to-refresh-content')) {
  5416. eventsTarget = eventsTarget.find('.pull-to-refresh-content');
  5417. }
  5418. if (!eventsTarget || eventsTarget.length === 0) return;
  5419. var isTouched, isMoved, touchesStart = {},
  5420. isScrolling, touchesDiff, touchStartTime, container, refresh = false,
  5421. useTranslate = false,
  5422. startTranslate = 0,
  5423. translate, scrollTop, wasScrolled, triggerDistance, dynamicTriggerDistance;
  5424. container = eventsTarget;
  5425. // Define trigger distance
  5426. if (container.attr('data-ptr-distance')) {
  5427. dynamicTriggerDistance = true;
  5428. } else {
  5429. triggerDistance = 44;
  5430. }
  5431. function handleTouchStart(e) {
  5432. if (isTouched) {
  5433. if ($.device.android) {
  5434. if ('targetTouches' in e && e.targetTouches.length > 1) return;
  5435. } else return;
  5436. }
  5437. isMoved = false;
  5438. isTouched = true;
  5439. isScrolling = undefined;
  5440. wasScrolled = undefined;
  5441. touchesStart.x = e.type === 'touchstart' ? e.targetTouches[0].pageX : e.pageX;
  5442. touchesStart.y = e.type === 'touchstart' ? e.targetTouches[0].pageY : e.pageY;
  5443. touchStartTime = (new Date()).getTime();
  5444. /*jshint validthis:true */
  5445. container = $(this);
  5446. }
  5447. function handleTouchMove(e) {
  5448. if (!isTouched) return;
  5449. var pageX = e.type === 'touchmove' ? e.targetTouches[0].pageX : e.pageX;
  5450. var pageY = e.type === 'touchmove' ? e.targetTouches[0].pageY : e.pageY;
  5451. if (typeof isScrolling === 'undefined') {
  5452. isScrolling = !!(isScrolling || Math.abs(pageY - touchesStart.y) > Math.abs(pageX - touchesStart.x));
  5453. }
  5454. if (!isScrolling) {
  5455. isTouched = false;
  5456. return;
  5457. }
  5458. scrollTop = container[0].scrollTop;
  5459. if (typeof wasScrolled === 'undefined' && scrollTop !== 0) wasScrolled = true;
  5460. if (!isMoved) {
  5461. /*jshint validthis:true */
  5462. container.removeClass('transitioning');
  5463. if (scrollTop > container[0].offsetHeight) {
  5464. isTouched = false;
  5465. return;
  5466. }
  5467. if (dynamicTriggerDistance) {
  5468. triggerDistance = container.attr('data-ptr-distance');
  5469. if (triggerDistance.indexOf('%') >= 0) triggerDistance = container[0].offsetHeight * parseInt(triggerDistance, 10) / 100;
  5470. }
  5471. startTranslate = container.hasClass('refreshing') ? triggerDistance : 0;
  5472. if (container[0].scrollHeight === container[0].offsetHeight || !$.device.ios) {
  5473. useTranslate = true;
  5474. } else {
  5475. useTranslate = false;
  5476. }
  5477. useTranslate = true;
  5478. }
  5479. isMoved = true;
  5480. touchesDiff = pageY - touchesStart.y;
  5481. if (touchesDiff > 0 && scrollTop <= 0 || scrollTop < 0) {
  5482. // iOS 8 fix
  5483. if ($.device.ios && parseInt($.device.osVersion.split('.')[0], 10) > 7 && scrollTop === 0 && !wasScrolled) useTranslate = true;
  5484. if (useTranslate) {
  5485. e.preventDefault();
  5486. translate = (Math.pow(touchesDiff, 0.85) + startTranslate);
  5487. container.transform('translate3d(0,' + translate + 'px,0)');
  5488. } else {}
  5489. if ((useTranslate && Math.pow(touchesDiff, 0.85) > triggerDistance) || (!useTranslate && touchesDiff >= triggerDistance * 2)) {
  5490. refresh = true;
  5491. container.addClass('pull-up').removeClass('pull-down');
  5492. } else {
  5493. refresh = false;
  5494. container.removeClass('pull-up').addClass('pull-down');
  5495. }
  5496. } else {
  5497. container.removeClass('pull-up pull-down');
  5498. refresh = false;
  5499. return;
  5500. }
  5501. }
  5502. function handleTouchEnd() {
  5503. if (!isTouched || !isMoved) {
  5504. isTouched = false;
  5505. isMoved = false;
  5506. return;
  5507. }
  5508. if (translate) {
  5509. container.addClass('transitioning');
  5510. translate = 0;
  5511. }
  5512. container.transform('');
  5513. if (refresh) {
  5514. //防止二次触发
  5515. if(container.hasClass('refreshing')) return;
  5516. container.addClass('refreshing');
  5517. container.trigger('refresh');
  5518. } else {
  5519. container.removeClass('pull-down');
  5520. }
  5521. isTouched = false;
  5522. isMoved = false;
  5523. }
  5524. // Attach Events
  5525. eventsTarget.on($.touchEvents.start, handleTouchStart);
  5526. eventsTarget.on($.touchEvents.move, handleTouchMove);
  5527. eventsTarget.on($.touchEvents.end, handleTouchEnd);
  5528. function destroyPullToRefresh() {
  5529. eventsTarget.off($.touchEvents.start, handleTouchStart);
  5530. eventsTarget.off($.touchEvents.move, handleTouchMove);
  5531. eventsTarget.off($.touchEvents.end, handleTouchEnd);
  5532. }
  5533. eventsTarget[0].destroyPullToRefresh = destroyPullToRefresh;
  5534. };
  5535. $.pullToRefreshDone = function(container) {
  5536. $(window).scrollTop(0);//解决微信下拉刷新顶部消失的问题
  5537. container = $(container);
  5538. if (container.length === 0) container = $('.pull-to-refresh-content.refreshing');
  5539. container.removeClass('refreshing').addClass('transitioning');
  5540. container.transitionEnd(function() {
  5541. container.removeClass('transitioning pull-up pull-down');
  5542. });
  5543. };
  5544. $.pullToRefreshTrigger = function(container) {
  5545. container = $(container);
  5546. if (container.length === 0) container = $('.pull-to-refresh-content');
  5547. if (container.hasClass('refreshing')) return;
  5548. container.addClass('transitioning refreshing');
  5549. container.trigger('refresh');
  5550. };
  5551. $.destroyPullToRefresh = function(pageContainer) {
  5552. pageContainer = $(pageContainer);
  5553. var pullToRefreshContent = pageContainer.hasClass('pull-to-refresh-content') ? pageContainer : pageContainer.find('.pull-to-refresh-content');
  5554. if (pullToRefreshContent.length === 0) return;
  5555. if (pullToRefreshContent[0].destroyPullToRefresh) pullToRefreshContent[0].destroyPullToRefresh();
  5556. };
  5557. //这里是否需要写到 scroller 中去?
  5558. /* $.initPullToRefresh = function(pageContainer) {
  5559. var $pageContainer = $(pageContainer);
  5560. $pageContainer.each(function(index, item) {
  5561. if ($.detectScrollerType(item) === 'js') {
  5562. $._pullToRefreshJSScroll.initPullToRefresh(item);
  5563. } else {
  5564. initPullToRefresh(item);
  5565. }
  5566. });
  5567. };
  5568. $.pullToRefreshDone = function(pageContainer) {
  5569. var $pageContainer = $(pageContainer);
  5570. $pageContainer.each(function(index, item) {
  5571. if ($.detectScrollerType(item) === 'js') {
  5572. $._pullToRefreshJSScroll.pullToRefreshDone(item);
  5573. } else {
  5574. pullToRefreshDone(item);
  5575. }
  5576. });
  5577. };
  5578. $.pullToRefreshTrigger = function(pageContainer) {
  5579. var $pageContainer = $(pageContainer);
  5580. $pageContainer.each(function(index, item) {
  5581. if ($.detectScrollerType(item) === 'js') {
  5582. $._pullToRefreshJSScroll.pullToRefreshTrigger(item);
  5583. } else {
  5584. pullToRefreshTrigger(item);
  5585. }
  5586. });
  5587. };
  5588. $.destroyPullToRefresh = function(pageContainer) {
  5589. var $pageContainer = $(pageContainer);
  5590. $pageContainer.each(function(index, item) {
  5591. if ($.detectScrollerType(item) === 'js') {
  5592. $._pullToRefreshJSScroll.destroyPullToRefresh(item);
  5593. } else {
  5594. destroyPullToRefresh(item);
  5595. }
  5596. });
  5597. };
  5598. */
  5599. }(Zepto); //jshint ignore:line
  5600. + function($) {
  5601. 'use strict';
  5602. function handleInfiniteScroll() {
  5603. /*jshint validthis:true */
  5604. var inf = $(this);
  5605. var scroller = $.getScroller(inf);
  5606. var scrollTop = scroller.scrollTop();
  5607. var scrollHeight = scroller.scrollHeight();
  5608. var height = inf[0].offsetHeight;
  5609. var distance = inf[0].getAttribute('data-distance');
  5610. var virtualListContainer = inf.find('.virtual-list');
  5611. var virtualList;
  5612. var onTop = inf.hasClass('infinite-scroll-top');
  5613. if (!distance) distance = 50;
  5614. if (typeof distance === 'string' && distance.indexOf('%') >= 0) {
  5615. distance = parseInt(distance, 10) / 100 * height;
  5616. }
  5617. if (distance > height) distance = height;
  5618. if (onTop) {
  5619. if (scrollTop < distance) {
  5620. inf.trigger('infinite');
  5621. }
  5622. } else {
  5623. if (scrollTop + height >= scrollHeight - distance) {
  5624. if (virtualListContainer.length > 0) {
  5625. virtualList = virtualListContainer[0].f7VirtualList;
  5626. if (virtualList && !virtualList.reachEnd) return;
  5627. }
  5628. inf.trigger('infinite');
  5629. }
  5630. }
  5631. }
  5632. $.attachInfiniteScroll = function(infiniteContent) {
  5633. $.getScroller(infiniteContent).on('scroll', handleInfiniteScroll);
  5634. };
  5635. $.detachInfiniteScroll = function(infiniteContent) {
  5636. $.getScroller(infiniteContent).off('scroll', handleInfiniteScroll);
  5637. };
  5638. $.initInfiniteScroll = function(pageContainer) {
  5639. pageContainer = $(pageContainer);
  5640. var infiniteContent = pageContainer.hasClass('infinite-scroll')?pageContainer:pageContainer.find('.infinite-scroll');
  5641. if (infiniteContent.length === 0) return;
  5642. $.attachInfiniteScroll(infiniteContent);
  5643. //如果是顶部无限刷新,要将滚动条初始化于最下端
  5644. pageContainer.forEach(function(v){
  5645. if($(v).hasClass('infinite-scroll-top')){
  5646. var height = v.scrollHeight - v.clientHeight;
  5647. $(v).scrollTop(height);
  5648. }
  5649. });
  5650. function detachEvents() {
  5651. $.detachInfiniteScroll(infiniteContent);
  5652. pageContainer.off('pageBeforeRemove', detachEvents);
  5653. }
  5654. pageContainer.on('pageBeforeRemove', detachEvents);
  5655. };
  5656. }(Zepto);
  5657. +function ($) {
  5658. "use strict";
  5659. $(function() {
  5660. $(document).on("focus", ".searchbar input", function(e) {
  5661. var $input = $(e.target);
  5662. $input.parents(".searchbar").addClass("searchbar-active");
  5663. });
  5664. $(document).on("click", ".searchbar-cancel", function(e) {
  5665. var $btn = $(e.target);
  5666. $btn.parents(".searchbar").removeClass("searchbar-active");
  5667. });
  5668. $(document).on("blur", ".searchbar input", function(e) {
  5669. var $input = $(e.target);
  5670. $input.parents(".searchbar").removeClass("searchbar-active");
  5671. });
  5672. });
  5673. }(Zepto);
  5674. /*======================================================
  5675. ************ Panels ************
  5676. ======================================================*/
  5677. /*jshint unused: false*/
  5678. +function ($) {
  5679. "use strict";
  5680. $.allowPanelOpen = true;
  5681. $.openPanel = function (panel) {
  5682. if (!$.allowPanelOpen) return false;
  5683. if(panel === 'left' || panel === 'right') panel = ".panel-" + panel; //可以传入一个方向
  5684. panel = panel ? $(panel) : $(".panel").eq(0);
  5685. var direction = panel.hasClass("panel-right") ? "right" : "left";
  5686. if (panel.length === 0 || panel.hasClass('active')) return false;
  5687. $.closePanel(); // Close if some panel is opened
  5688. $.allowPanelOpen = false;
  5689. var effect = panel.hasClass('panel-reveal') ? 'reveal' : 'cover';
  5690. panel.css({display: 'block'}).addClass('active');
  5691. panel.trigger('open');
  5692. // Trigger reLayout
  5693. var clientLeft = panel[0].clientLeft;
  5694. // Transition End;
  5695. var transitionEndTarget = effect === 'reveal' ? $($.getCurrentPage()) : panel;
  5696. var openedTriggered = false;
  5697. function panelTransitionEnd() {
  5698. transitionEndTarget.transitionEnd(function (e) {
  5699. if (e.target === transitionEndTarget[0]) {
  5700. if (panel.hasClass('active')) {
  5701. panel.trigger('opened');
  5702. }
  5703. else {
  5704. panel.trigger('closed');
  5705. }
  5706. $.allowPanelOpen = true;
  5707. }
  5708. else panelTransitionEnd();
  5709. });
  5710. }
  5711. panelTransitionEnd();
  5712. $(document.body).addClass('with-panel-' + direction + '-' + effect);
  5713. return true;
  5714. };
  5715. $.closePanel = function () {
  5716. var activePanel = $('.panel.active');
  5717. if (activePanel.length === 0) return false;
  5718. var effect = activePanel.hasClass('panel-reveal') ? 'reveal' : 'cover';
  5719. var panelPosition = activePanel.hasClass('panel-left') ? 'left' : 'right';
  5720. activePanel.removeClass('active');
  5721. var transitionEndTarget = effect === 'reveal' ? $('.page') : activePanel;
  5722. activePanel.trigger('close');
  5723. $.allowPanelOpen = false;
  5724. transitionEndTarget.transitionEnd(function () {
  5725. if (activePanel.hasClass('active')) return;
  5726. activePanel.css({display: ''});
  5727. activePanel.trigger('closed');
  5728. $('body').removeClass('panel-closing');
  5729. $.allowPanelOpen = true;
  5730. });
  5731. $('body').addClass('panel-closing').removeClass('with-panel-' + panelPosition + '-' + effect);
  5732. };
  5733. $(document).on("click", ".open-panel", function(e) {
  5734. var panel = $(e.target).data('panel');
  5735. $.openPanel(panel);
  5736. });
  5737. $(document).on("click", ".close-panel, .panel-overlay", function(e) {
  5738. $.closePanel();
  5739. });
  5740. /*======================================================
  5741. ************ Swipe panels ************
  5742. ======================================================*/
  5743. $.initSwipePanels = function () {
  5744. var panel, side;
  5745. var swipePanel = $.smConfig.swipePanel;
  5746. var swipePanelOnlyClose = $.smConfig.swipePanelOnlyClose;
  5747. var swipePanelCloseOpposite = true;
  5748. var swipePanelActiveArea = false;
  5749. var swipePanelThreshold = 2;
  5750. var swipePanelNoFollow = false;
  5751. if(!(swipePanel || swipePanelOnlyClose)) return;
  5752. var panelOverlay = $('.panel-overlay');
  5753. var isTouched, isMoved, isScrolling, touchesStart = {}, touchStartTime, touchesDiff, translate, opened, panelWidth, effect, direction;
  5754. var views = $('.page');
  5755. function handleTouchStart(e) {
  5756. if (!$.allowPanelOpen || (!swipePanel && !swipePanelOnlyClose) || isTouched) return;
  5757. if ($('.modal-in, .photo-browser-in').length > 0) return;
  5758. if (!(swipePanelCloseOpposite || swipePanelOnlyClose)) {
  5759. if ($('.panel.active').length > 0 && !panel.hasClass('active')) return;
  5760. }
  5761. touchesStart.x = e.type === 'touchstart' ? e.targetTouches[0].pageX : e.pageX;
  5762. touchesStart.y = e.type === 'touchstart' ? e.targetTouches[0].pageY : e.pageY;
  5763. if (swipePanelCloseOpposite || swipePanelOnlyClose) {
  5764. if ($('.panel.active').length > 0) {
  5765. side = $('.panel.active').hasClass('panel-left') ? 'left' : 'right';
  5766. }
  5767. else {
  5768. if (swipePanelOnlyClose) return;
  5769. side = swipePanel;
  5770. }
  5771. if (!side) return;
  5772. }
  5773. panel = $('.panel.panel-' + side);
  5774. if(!panel[0]) return;
  5775. opened = panel.hasClass('active');
  5776. if (swipePanelActiveArea && !opened) {
  5777. if (side === 'left') {
  5778. if (touchesStart.x > swipePanelActiveArea) return;
  5779. }
  5780. if (side === 'right') {
  5781. if (touchesStart.x < window.innerWidth - swipePanelActiveArea) return;
  5782. }
  5783. }
  5784. isMoved = false;
  5785. isTouched = true;
  5786. isScrolling = undefined;
  5787. touchStartTime = (new Date()).getTime();
  5788. direction = undefined;
  5789. }
  5790. function handleTouchMove(e) {
  5791. if (!isTouched) return;
  5792. if(!panel[0]) return;
  5793. if (e.f7PreventPanelSwipe) return;
  5794. var pageX = e.type === 'touchmove' ? e.targetTouches[0].pageX : e.pageX;
  5795. var pageY = e.type === 'touchmove' ? e.targetTouches[0].pageY : e.pageY;
  5796. if (typeof isScrolling === 'undefined') {
  5797. isScrolling = !!(isScrolling || Math.abs(pageY - touchesStart.y) > Math.abs(pageX - touchesStart.x));
  5798. }
  5799. if (isScrolling) {
  5800. isTouched = false;
  5801. return;
  5802. }
  5803. if (!direction) {
  5804. if (pageX > touchesStart.x) {
  5805. direction = 'to-right';
  5806. }
  5807. else {
  5808. direction = 'to-left';
  5809. }
  5810. if (
  5811. side === 'left' &&
  5812. (
  5813. direction === 'to-left' && !panel.hasClass('active')
  5814. ) ||
  5815. side === 'right' &&
  5816. (
  5817. direction === 'to-right' && !panel.hasClass('active')
  5818. )
  5819. )
  5820. {
  5821. isTouched = false;
  5822. return;
  5823. }
  5824. }
  5825. if (swipePanelNoFollow) {
  5826. var timeDiff = (new Date()).getTime() - touchStartTime;
  5827. if (timeDiff < 300) {
  5828. if (direction === 'to-left') {
  5829. if (side === 'right') $.openPanel(side);
  5830. if (side === 'left' && panel.hasClass('active')) $.closePanel();
  5831. }
  5832. if (direction === 'to-right') {
  5833. if (side === 'left') $.openPanel(side);
  5834. if (side === 'right' && panel.hasClass('active')) $.closePanel();
  5835. }
  5836. }
  5837. isTouched = false;
  5838. console.log(3);
  5839. isMoved = false;
  5840. return;
  5841. }
  5842. if (!isMoved) {
  5843. effect = panel.hasClass('panel-cover') ? 'cover' : 'reveal';
  5844. if (!opened) {
  5845. panel.show();
  5846. panelOverlay.show();
  5847. }
  5848. panelWidth = panel[0].offsetWidth;
  5849. panel.transition(0);
  5850. /*
  5851. if (panel.find('.' + app.params.viewClass).length > 0) {
  5852. if (app.sizeNavbars) app.sizeNavbars(panel.find('.' + app.params.viewClass)[0]);
  5853. }
  5854. */
  5855. }
  5856. isMoved = true;
  5857. e.preventDefault();
  5858. var threshold = opened ? 0 : -swipePanelThreshold;
  5859. if (side === 'right') threshold = -threshold;
  5860. touchesDiff = pageX - touchesStart.x + threshold;
  5861. if (side === 'right') {
  5862. translate = touchesDiff - (opened ? panelWidth : 0);
  5863. if (translate > 0) translate = 0;
  5864. if (translate < -panelWidth) {
  5865. translate = -panelWidth;
  5866. }
  5867. }
  5868. else {
  5869. translate = touchesDiff + (opened ? panelWidth : 0);
  5870. if (translate < 0) translate = 0;
  5871. if (translate > panelWidth) {
  5872. translate = panelWidth;
  5873. }
  5874. }
  5875. if (effect === 'reveal') {
  5876. views.transform('translate3d(' + translate + 'px,0,0)').transition(0);
  5877. panelOverlay.transform('translate3d(' + translate + 'px,0,0)');
  5878. //app.pluginHook('swipePanelSetTransform', views[0], panel[0], Math.abs(translate / panelWidth));
  5879. }
  5880. else {
  5881. panel.transform('translate3d(' + translate + 'px,0,0)').transition(0);
  5882. //app.pluginHook('swipePanelSetTransform', views[0], panel[0], Math.abs(translate / panelWidth));
  5883. }
  5884. }
  5885. function handleTouchEnd(e) {
  5886. if (!isTouched || !isMoved) {
  5887. isTouched = false;
  5888. isMoved = false;
  5889. return;
  5890. }
  5891. isTouched = false;
  5892. isMoved = false;
  5893. var timeDiff = (new Date()).getTime() - touchStartTime;
  5894. var action;
  5895. var edge = (translate === 0 || Math.abs(translate) === panelWidth);
  5896. if (!opened) {
  5897. if (translate === 0) {
  5898. action = 'reset';
  5899. }
  5900. else if (
  5901. timeDiff < 300 && Math.abs(translate) > 0 ||
  5902. timeDiff >= 300 && (Math.abs(translate) >= panelWidth / 2)
  5903. ) {
  5904. action = 'swap';
  5905. }
  5906. else {
  5907. action = 'reset';
  5908. }
  5909. }
  5910. else {
  5911. if (translate === -panelWidth) {
  5912. action = 'reset';
  5913. }
  5914. else if (
  5915. timeDiff < 300 && Math.abs(translate) >= 0 ||
  5916. timeDiff >= 300 && (Math.abs(translate) <= panelWidth / 2)
  5917. ) {
  5918. if (side === 'left' && translate === panelWidth) action = 'reset';
  5919. else action = 'swap';
  5920. }
  5921. else {
  5922. action = 'reset';
  5923. }
  5924. }
  5925. if (action === 'swap') {
  5926. $.allowPanelOpen = true;
  5927. if (opened) {
  5928. $.closePanel();
  5929. if (edge) {
  5930. panel.css({display: ''});
  5931. $('body').removeClass('panel-closing');
  5932. }
  5933. }
  5934. else {
  5935. $.openPanel(side);
  5936. }
  5937. if (edge) $.allowPanelOpen = true;
  5938. }
  5939. if (action === 'reset') {
  5940. if (opened) {
  5941. $.allowPanelOpen = true;
  5942. $.openPanel(side);
  5943. }
  5944. else {
  5945. $.closePanel();
  5946. if (edge) {
  5947. $.allowPanelOpen = true;
  5948. panel.css({display: ''});
  5949. }
  5950. else {
  5951. var target = effect === 'reveal' ? views : panel;
  5952. $('body').addClass('panel-closing');
  5953. target.transitionEnd(function () {
  5954. $.allowPanelOpen = true;
  5955. panel.css({display: ''});
  5956. $('body').removeClass('panel-closing');
  5957. });
  5958. }
  5959. }
  5960. }
  5961. if (effect === 'reveal') {
  5962. views.transition('');
  5963. views.transform('');
  5964. }
  5965. panel.transition('').transform('');
  5966. panelOverlay.css({display: ''}).transform('');
  5967. }
  5968. $(document).on($.touchEvents.start, handleTouchStart);
  5969. $(document).on($.touchEvents.move, handleTouchMove);
  5970. $(document).on($.touchEvents.end, handleTouchEnd);
  5971. };
  5972. $.initSwipePanels();
  5973. }(Zepto);
  5974. /**
  5975. * 路由
  5976. *
  5977. * 路由功能将接管页面的链接点击行为,最后达到动画切换的效果,具体如下:
  5978. * 1. 链接对应的是另一个页面,那么则尝试 ajax 加载,然后把新页面里的符合约定的结构提取出来,然后做动画切换;如果没法 ajax 或结构不符合,那么则回退为普通的页面跳转
  5979. * 2. 链接是当前页面的锚点,并且该锚点对应的元素存在且符合路由约定,那么则把该元素做动画切入
  5980. * 3. 浏览器前进后退(history.forward/history.back)时,也使用动画效果
  5981. * 4. 如果链接有 back 这个 class,那么则忽略一切,直接调用 history.back() 来后退
  5982. *
  5983. * 路由功能默认开启,如果需要关闭路由功能,那么在 zepto 之后,msui 脚本之前设置 $.config.router = false 即可(intro.js 中会 extend 到 $.smConfig 中)。
  5984. *
  5985. * 可以设置 $.config.routerFilter 函数来设置当前点击链接是否使用路由功能,实参是 a 链接的 zepto 对象;返回 false 表示不使用 router 功能。
  5986. *
  5987. * ajax 载入新的文档时,并不会执行里面的 js。到目前为止,在开启路由功能时,建议的做法是:
  5988. * 把所有页面的 js 都放到同一个脚本里,js 里面的事件绑定使用委托而不是直接的绑定在元素上(因为动态加载的页面元素还不存在),然后所有页面都引用相同的 js 脚本。非事件类可以通过监控 pageInit 事件,根据里面的 pageId 来做对应区别处理。
  5989. *
  5990. * 如果有需要
  5991. *
  5992. * 对外暴露的方法
  5993. * - load (原 loadPage 效果一致,但后者已标记为待移除)
  5994. * - forward
  5995. * - back
  5996. *
  5997. * 事件
  5998. * pageLoad* 系列在发生 ajax 加载时才会触发;当是块切换或已缓存的情况下,不会发送这些事件
  5999. * - pageLoadCancel: 如果前一个还没加载完,那么取消并发送该事件
  6000. * - pageLoadStart: 开始加载
  6001. * - pageLodComplete: ajax complete 完成
  6002. * - pageLoadError: ajax 发生 error
  6003. * - pageAnimationStart: 执行动画切换前,实参是 event,sectionId 和 $section
  6004. * - pageAnimationEnd: 执行动画完毕,实参是 event,sectionId 和 $section
  6005. * - beforePageRemove: 新 document 载入且动画切换完毕,旧的 document remove 之前在 window 上触发,实参是 event 和 $pageContainer
  6006. * - pageRemoved: 新的 document 载入且动画切换完毕,旧的 document remove 之后在 window 上触发
  6007. * - beforePageSwitch: page 切换前,在 pageAnimationStart 前,beforePageSwitch 之后会做一些额外的处理才触发 pageAnimationStart
  6008. * - pageInitInternal: (经 init.js 处理后,对外是 pageInit)紧跟着动画完成的事件,实参是 event,sectionId 和 $section
  6009. *
  6010. * 术语
  6011. * - 文档(document),不带 hash 的 url 关联着的应答 html 结构
  6012. * - 块(section),一个文档内有指定块标识的元素
  6013. *
  6014. * 路由实现约定
  6015. * - 每个文档的需要展示的内容必需位于指定的标识(routerConfig.sectionGroupClass)的元素里面,默认是: div.page-group (注意,如果改变这个需要同时改变 less 中的命名)
  6016. * - 每个块必需带有指定的块标识(routerConfig.pageClass),默认是 .page
  6017. *
  6018. * 即,使用路由功能的每一个文档应当是下面这样的结构(省略 <body> 等):
  6019. * <div class="page-group">
  6020. * <div class="page">xxx</div>
  6021. * <div class="page">yyy</div>
  6022. * </div>
  6023. *
  6024. * 另,每一个块都应当有一个唯一的 ID,这样才能通过 #the-id 的形式来切换定位。
  6025. * 当一个块没有 id 时,如果是第一个的默认的需要展示的块,那么会给其添加一个随机的 id;否则,没有 id 的块将不会被切换展示。
  6026. *
  6027. * 通过 history.state/history.pushState 以及用 sessionStorage 来记录当前 state 以及最大的 state id 来辅助前进后退的切换效果,所以在不支持 sessionStorage 的情况下,将不开启路由功能。
  6028. *
  6029. * 为了解决 ajax 载入页面导致重复 ID 以及重复 popup 等功能,上面约定了使用路由功能的所有可展示内容都必需位于指定元素内。从而可以在进行文档间切换时可以进行两个文档的整体移动,切换完毕后再把前一个文档的内容从页面之间移除。
  6030. *
  6031. * 默认地过滤了部分协议的链接,包括 tel:, javascript:, mailto:,这些链接将不会使用路由功能。如果有更多的自定义控制需求,可以在 $.config.routerFilter 实现
  6032. *
  6033. * 注: 以 _ 开头的函数标明用于此处内部使用,可根据需要随时重构变更,不对外确保兼容性。
  6034. *
  6035. */
  6036. +function($) {
  6037. 'use strict';
  6038. if (!window.CustomEvent) {
  6039. window.CustomEvent = function(type, config) {
  6040. config = config || { bubbles: false, cancelable: false, detail: undefined};
  6041. var e = document.createEvent('CustomEvent');
  6042. e.initCustomEvent(type, config.bubbles, config.cancelable, config.detail);
  6043. return e;
  6044. };
  6045. window.CustomEvent.prototype = window.Event.prototype;
  6046. }
  6047. var EVENTS = {
  6048. pageLoadStart: 'pageLoadStart', // ajax 开始加载新页面前
  6049. pageLoadCancel: 'pageLoadCancel', // 取消前一个 ajax 加载动作后
  6050. pageLoadError: 'pageLoadError', // ajax 加载页面失败后
  6051. pageLoadComplete: 'pageLoadComplete', // ajax 加载页面完成后(不论成功与否)
  6052. pageAnimationStart: 'pageAnimationStart', // 动画切换 page 前
  6053. pageAnimationEnd: 'pageAnimationEnd', // 动画切换 page 结束后
  6054. beforePageRemove: 'beforePageRemove', // 移除旧 document 前(适用于非内联 page 切换)
  6055. pageRemoved: 'pageRemoved', // 移除旧 document 后(适用于非内联 page 切换)
  6056. beforePageSwitch: 'beforePageSwitch', // page 切换前,在 pageAnimationStart 前,beforePageSwitch 之后会做一些额外的处理才触发 pageAnimationStart
  6057. pageInit: 'pageInitInternal' // 目前是定义为一个 page 加载完毕后(实际和 pageAnimationEnd 等同)
  6058. };
  6059. var Util = {
  6060. /**
  6061. * 获取 url 的 fragment(即 hash 中去掉 # 的剩余部分)
  6062. *
  6063. * 如果没有则返回空字符串
  6064. * 如: http://example.com/path/?query=d#123 => 123
  6065. *
  6066. * @param {String} url url
  6067. * @returns {String}
  6068. */
  6069. getUrlFragment: function(url) {
  6070. var hashIndex = url.indexOf('#');
  6071. return hashIndex === -1 ? '' : url.slice(hashIndex + 1);
  6072. },
  6073. /**
  6074. * 获取一个链接相对于当前页面的绝对地址形式
  6075. *
  6076. * 假设当前页面是 http://a.com/b/c
  6077. * 那么有以下情况:
  6078. * d => http://a.com/b/d
  6079. * /e => http://a.com/e
  6080. * #1 => http://a.com/b/c#1
  6081. * http://b.com/f => http://b.com/f
  6082. *
  6083. * @param {String} url url
  6084. * @returns {String}
  6085. */
  6086. getAbsoluteUrl: function(url) {
  6087. var link = document.createElement('a');
  6088. link.setAttribute('href', url);
  6089. var absoluteUrl = link.href;
  6090. link = null;
  6091. return absoluteUrl;
  6092. },
  6093. /**
  6094. * 获取一个 url 的基本部分,即不包括 hash
  6095. *
  6096. * @param {String} url url
  6097. * @returns {String}
  6098. */
  6099. getBaseUrl: function(url) {
  6100. var hashIndex = url.indexOf('#');
  6101. return hashIndex === -1 ? url.slice(0) : url.slice(0, hashIndex);
  6102. },
  6103. /**
  6104. * 把一个字符串的 url 转为一个可获取其 base 和 fragment 等的对象
  6105. *
  6106. * @param {String} url url
  6107. * @returns {UrlObject}
  6108. */
  6109. toUrlObject: function(url) {
  6110. var fullUrl = this.getAbsoluteUrl(url),
  6111. baseUrl = this.getBaseUrl(fullUrl),
  6112. fragment = this.getUrlFragment(url);
  6113. return {
  6114. base: baseUrl,
  6115. full: fullUrl,
  6116. original: url,
  6117. fragment: fragment
  6118. };
  6119. },
  6120. /**
  6121. * 判断浏览器是否支持 sessionStorage,支持返回 true,否则返回 false
  6122. * @returns {Boolean}
  6123. */
  6124. supportStorage: function() {
  6125. var mod = 'sm.router.storage.ability';
  6126. try {
  6127. sessionStorage.setItem(mod, mod);
  6128. sessionStorage.removeItem(mod);
  6129. return true;
  6130. } catch(e) {
  6131. return false;
  6132. }
  6133. }
  6134. };
  6135. var routerConfig = {
  6136. sectionGroupClass: 'page-group',
  6137. // 表示是当前 page 的 class
  6138. curPageClass: 'page-current',
  6139. // 用来辅助切换时表示 page 是 visible 的,
  6140. // 之所以不用 curPageClass,是因为 page-current 已被赋予了「当前 page」这一含义而不仅仅是 display: block
  6141. // 并且,别的地方已经使用了,所以不方便做变更,故新增一个
  6142. visiblePageClass: 'page-visible',
  6143. // 表示是 page 的 class,注意,仅是标志 class,而不是所有的 class
  6144. pageClass: 'page'
  6145. };
  6146. var DIRECTION = {
  6147. leftToRight: 'from-left-to-right',
  6148. rightToLeft: 'from-right-to-left'
  6149. };
  6150. var theHistory = window.history;
  6151. var Router = function() {
  6152. this.sessionNames = {
  6153. currentState: 'sm.router.currentState',
  6154. maxStateId: 'sm.router.maxStateId'
  6155. };
  6156. this._init();
  6157. this.xhr = null;
  6158. window.addEventListener('popstate', this._onPopState.bind(this));
  6159. };
  6160. /**
  6161. * 初始化
  6162. *
  6163. * - 把当前文档内容缓存起来
  6164. * - 查找默认展示的块内容,查找顺序如下
  6165. * 1. id 是 url 中的 fragment 的元素
  6166. * 2. 有当前块 class 标识的第一个元素
  6167. * 3. 第一个块
  6168. * - 初始页面 state 处理
  6169. *
  6170. * @private
  6171. */
  6172. Router.prototype._init = function() {
  6173. this.$view = $('body');
  6174. // 用来保存 document 的 map
  6175. this.cache = {};
  6176. var $doc = $(document);
  6177. var currentUrl = location.href;
  6178. this._saveDocumentIntoCache($doc, currentUrl);
  6179. var curPageId;
  6180. var currentUrlObj = Util.toUrlObject(currentUrl);
  6181. var $allSection = $doc.find('.' + routerConfig.pageClass);
  6182. var $visibleSection = $doc.find('.' + routerConfig.curPageClass);
  6183. var $curVisibleSection = $visibleSection.eq(0);
  6184. var $hashSection;
  6185. if (currentUrlObj.fragment) {
  6186. $hashSection = $doc.find('#' + currentUrlObj.fragment);
  6187. }
  6188. if ($hashSection && $hashSection.length) {
  6189. $visibleSection = $hashSection.eq(0);
  6190. } else if (!$visibleSection.length) {
  6191. $visibleSection = $allSection.eq(0);
  6192. }
  6193. if (!$visibleSection.attr('id')) {
  6194. $visibleSection.attr('id', this._generateRandomId());
  6195. }
  6196. if ($curVisibleSection.length &&
  6197. ($curVisibleSection.attr('id') !== $visibleSection.attr('id'))) {
  6198. // 在 router 到 inner page 的情况下,刷新(或者直接访问该链接)
  6199. // 直接切换 class 会有「闪」的现象,或许可以采用 animateSection 来减缓一下
  6200. $curVisibleSection.removeClass(routerConfig.curPageClass);
  6201. $visibleSection.addClass(routerConfig.curPageClass);
  6202. } else {
  6203. $visibleSection.addClass(routerConfig.curPageClass);
  6204. }
  6205. curPageId = $visibleSection.attr('id');
  6206. // 新进入一个使用 history.state 相关技术的页面时,如果第一个 state 不 push/replace,
  6207. // 那么在后退回该页面时,将不触发 popState 事件
  6208. if (theHistory.state === null) {
  6209. var curState = {
  6210. id: this._getNextStateId(),
  6211. url: Util.toUrlObject(currentUrl),
  6212. pageId: curPageId
  6213. };
  6214. theHistory.replaceState(curState, '', currentUrl);
  6215. this._saveAsCurrentState(curState);
  6216. this._incMaxStateId();
  6217. }
  6218. };
  6219. /**
  6220. * 切换到 url 指定的块或文档
  6221. *
  6222. * 如果 url 指向的是当前页面,那么认为是切换块;
  6223. * 否则是切换文档
  6224. *
  6225. * @param {String} url url
  6226. * @param {Boolean=} ignoreCache 是否强制请求不使用缓存,对 document 生效,默认是 false
  6227. */
  6228. Router.prototype.load = function(url, ignoreCache) {
  6229. if (ignoreCache === undefined) {
  6230. ignoreCache = false;
  6231. }
  6232. if (this._isTheSameDocument(location.href, url)) {
  6233. this._switchToSection(Util.getUrlFragment(url));
  6234. } else {
  6235. this._saveDocumentIntoCache($(document), location.href);
  6236. this._switchToDocument(url, ignoreCache);
  6237. }
  6238. };
  6239. /**
  6240. * 调用 history.forward()
  6241. */
  6242. Router.prototype.forward = function() {
  6243. theHistory.forward();
  6244. };
  6245. /**
  6246. * 调用 history.back()
  6247. */
  6248. Router.prototype.back = function() {
  6249. theHistory.back();
  6250. };
  6251. //noinspection JSUnusedGlobalSymbols
  6252. /**
  6253. * @deprecated
  6254. */
  6255. Router.prototype.loadPage = Router.prototype.load;
  6256. /**
  6257. * 切换显示当前文档另一个块
  6258. *
  6259. * 把新块从右边切入展示,同时会把新的块的记录用 history.pushState 来保存起来
  6260. *
  6261. * 如果已经是当前显示的块,那么不做任何处理;
  6262. * 如果没对应的块,那么忽略。
  6263. *
  6264. * @param {String} sectionId 待切换显示的块的 id
  6265. * @private
  6266. */
  6267. Router.prototype._switchToSection = function(sectionId) {
  6268. if (!sectionId) {
  6269. return;
  6270. }
  6271. var $curPage = this._getCurrentSection(),
  6272. $newPage = $('#' + sectionId);
  6273. // 如果已经是当前页,不做任何处理
  6274. if ($curPage === $newPage) {
  6275. return;
  6276. }
  6277. this._animateSection($curPage, $newPage, DIRECTION.rightToLeft);
  6278. this._pushNewState('#' + sectionId, sectionId);
  6279. };
  6280. /**
  6281. * 载入显示一个新的文档
  6282. *
  6283. * - 如果有缓存,那么直接利用缓存来切换
  6284. * - 否则,先把页面加载过来缓存,然后再切换
  6285. * - 如果解析失败,那么用 location.href 的方式来跳转
  6286. *
  6287. * 注意:不能在这里以及其之后用 location.href 来 **读取** 切换前的页面的 url,
  6288. * 因为如果是 popState 时的调用,那么此时 location 已经是 pop 出来的 state 的了
  6289. *
  6290. * @param {String} url 新的文档的 url
  6291. * @param {Boolean=} ignoreCache 是否不使用缓存强制加载页面
  6292. * @param {Boolean=} isPushState 是否需要 pushState
  6293. * @param {String=} direction 新文档切入的方向
  6294. * @private
  6295. */
  6296. Router.prototype._switchToDocument = function(url, ignoreCache, isPushState, direction) {
  6297. var baseUrl = Util.toUrlObject(url).base;
  6298. if (ignoreCache) {
  6299. delete this.cache[baseUrl];
  6300. }
  6301. var cacheDocument = this.cache[baseUrl];
  6302. var context = this;
  6303. if (cacheDocument) {
  6304. this._doSwitchDocument(url, isPushState, direction);
  6305. } else {
  6306. this._loadDocument(url, {
  6307. success: function($doc) {
  6308. try {
  6309. context._parseDocument(url, $doc);
  6310. context._doSwitchDocument(url, isPushState, direction);
  6311. } catch (e) {
  6312. location.href = url;
  6313. }
  6314. },
  6315. error: function() {
  6316. location.href = url;
  6317. }
  6318. });
  6319. }
  6320. };
  6321. /**
  6322. * 利用缓存来做具体的切换文档操作
  6323. *
  6324. * - 确定待切入的文档的默认展示 section
  6325. * - 把新文档 append 到 view 中
  6326. * - 动画切换文档
  6327. * - 如果需要 pushState,那么把最新的状态 push 进去并把当前状态更新为该状态
  6328. *
  6329. * @param {String} url 待切换的文档的 url
  6330. * @param {Boolean} isPushState 加载页面后是否需要 pushState,默认是 true
  6331. * @param {String} direction 动画切换方向,默认是 DIRECTION.rightToLeft
  6332. * @private
  6333. */
  6334. Router.prototype._doSwitchDocument = function(url, isPushState, direction) {
  6335. if (typeof isPushState === 'undefined') {
  6336. isPushState = true;
  6337. }
  6338. var urlObj = Util.toUrlObject(url);
  6339. var $currentDoc = this.$view.find('.' + routerConfig.sectionGroupClass);
  6340. var $newDoc = $($('<div></div>').append(this.cache[urlObj.base].$content).html());
  6341. // 确定一个 document 展示 section 的顺序
  6342. // 1. 与 hash 关联的 element
  6343. // 2. 默认的标识为 current 的 element
  6344. // 3. 第一个 section
  6345. var $allSection = $newDoc.find('.' + routerConfig.pageClass);
  6346. var $visibleSection = $newDoc.find('.' + routerConfig.curPageClass);
  6347. var $hashSection;
  6348. if (urlObj.fragment) {
  6349. $hashSection = $newDoc.find('#' + urlObj.fragment);
  6350. }
  6351. if ($hashSection && $hashSection.length) {
  6352. $visibleSection = $hashSection.eq(0);
  6353. } else if (!$visibleSection.length) {
  6354. $visibleSection = $allSection.eq(0);
  6355. }
  6356. if (!$visibleSection.attr('id')) {
  6357. $visibleSection.attr('id', this._generateRandomId());
  6358. }
  6359. var $currentSection = this._getCurrentSection();
  6360. $currentSection.trigger(EVENTS.beforePageSwitch, [$currentSection.attr('id'), $currentSection]);
  6361. $allSection.removeClass(routerConfig.curPageClass);
  6362. $visibleSection.addClass(routerConfig.curPageClass);
  6363. // prepend 而不 append 的目的是避免 append 进去新的 document 在后面,
  6364. // 其里面的默认展示的(.page-current) 的页面直接就覆盖了原显示的页面(因为都是 absolute)
  6365. this.$view.prepend($newDoc);
  6366. this._animateDocument($currentDoc, $newDoc, $visibleSection, direction);
  6367. if (isPushState) {
  6368. this._pushNewState(url, $visibleSection.attr('id'));
  6369. }
  6370. };
  6371. /**
  6372. * 判断两个 url 指向的页面是否是同一个
  6373. *
  6374. * 判断方式: 如果两个 url 的 base 形式(不带 hash 的绝对形式)相同,那么认为是同一个页面
  6375. *
  6376. * @param {String} url
  6377. * @param {String} anotherUrl
  6378. * @returns {Boolean}
  6379. * @private
  6380. */
  6381. Router.prototype._isTheSameDocument = function(url, anotherUrl) {
  6382. return Util.toUrlObject(url).base === Util.toUrlObject(anotherUrl).base;
  6383. };
  6384. /**
  6385. * ajax 加载 url 指定的页面内容
  6386. *
  6387. * 加载过程中会发出以下事件
  6388. * pageLoadCancel: 如果前一个还没加载完,那么取消并发送该事件
  6389. * pageLoadStart: 开始加载
  6390. * pageLodComplete: ajax complete 完成
  6391. * pageLoadError: ajax 发生 error
  6392. *
  6393. *
  6394. * @param {String} url url
  6395. * @param {Object=} callback 回调函数配置,可选,可以配置 success\error 和 complete
  6396. * 所有回调函数的 this 都是 null,各自实参如下:
  6397. * success: $doc, status, xhr
  6398. * error: xhr, status, err
  6399. * complete: xhr, status
  6400. *
  6401. * @private
  6402. */
  6403. Router.prototype._loadDocument = function(url, callback) {
  6404. if (this.xhr && this.xhr.readyState < 4) {
  6405. this.xhr.onreadystatechange = function() {
  6406. };
  6407. this.xhr.abort();
  6408. this.dispatch(EVENTS.pageLoadCancel);
  6409. }
  6410. this.dispatch(EVENTS.pageLoadStart);
  6411. callback = callback || {};
  6412. var self = this;
  6413. this.xhr = $.ajax({
  6414. url: url,
  6415. success: $.proxy(function(data, status, xhr) {
  6416. // 给包一层 <html/>,从而可以拿到完整的结构
  6417. var $doc = $('<html></html>');
  6418. $doc.append(data);
  6419. callback.success && callback.success.call(null, $doc, status, xhr);
  6420. }, this),
  6421. error: function(xhr, status, err) {
  6422. callback.error && callback.error.call(null, xhr, status, err);
  6423. self.dispatch(EVENTS.pageLoadError);
  6424. },
  6425. complete: function(xhr, status) {
  6426. callback.complete && callback.complete.call(null, xhr, status);
  6427. self.dispatch(EVENTS.pageLoadComplete);
  6428. }
  6429. });
  6430. };
  6431. /**
  6432. * 对于 ajax 加载进来的页面,把其缓存起来
  6433. *
  6434. * @param {String} url url
  6435. * @param $doc ajax 载入的页面的 jq 对象,可以看做是该页面的 $(document)
  6436. * @private
  6437. */
  6438. Router.prototype._parseDocument = function(url, $doc) {
  6439. var $innerView = $doc.find('.' + routerConfig.sectionGroupClass);
  6440. if (!$innerView.length) {
  6441. throw new Error('missing router view mark: ' + routerConfig.sectionGroupClass);
  6442. }
  6443. this._saveDocumentIntoCache($doc, url);
  6444. };
  6445. /**
  6446. * 把一个页面的相关信息保存到 this.cache 中
  6447. *
  6448. * 以页面的 baseUrl 为 key,而 value 则是一个 DocumentCache
  6449. *
  6450. * @param {*} doc doc
  6451. * @param {String} url url
  6452. * @private
  6453. */
  6454. Router.prototype._saveDocumentIntoCache = function(doc, url) {
  6455. var urlAsKey = Util.toUrlObject(url).base;
  6456. var $doc = $(doc);
  6457. this.cache[urlAsKey] = {
  6458. $doc: $doc,
  6459. $content: $doc.find('.' + routerConfig.sectionGroupClass)
  6460. };
  6461. };
  6462. /**
  6463. * 从 sessionStorage 中获取保存下来的「当前状态」
  6464. *
  6465. * 如果解析失败,那么认为当前状态是 null
  6466. *
  6467. * @returns {State|null}
  6468. * @private
  6469. */
  6470. Router.prototype._getLastState = function() {
  6471. var currentState = sessionStorage.getItem(this.sessionNames.currentState);
  6472. try {
  6473. currentState = JSON.parse(currentState);
  6474. } catch(e) {
  6475. currentState = null;
  6476. }
  6477. return currentState;
  6478. };
  6479. /**
  6480. * 把一个状态设为当前状态,保存仅 sessionStorage 中
  6481. *
  6482. * @param {State} state
  6483. * @private
  6484. */
  6485. Router.prototype._saveAsCurrentState = function(state) {
  6486. sessionStorage.setItem(this.sessionNames.currentState, JSON.stringify(state));
  6487. };
  6488. /**
  6489. * 获取下一个 state 的 id
  6490. *
  6491. * 读取 sessionStorage 里的最后的状态的 id,然后 + 1;如果原没设置,那么返回 1
  6492. *
  6493. * @returns {number}
  6494. * @private
  6495. */
  6496. Router.prototype._getNextStateId = function() {
  6497. var maxStateId = sessionStorage.getItem(this.sessionNames.maxStateId);
  6498. return maxStateId ? parseInt(maxStateId, 10) + 1 : 1;
  6499. };
  6500. /**
  6501. * 把 sessionStorage 里的最后状态的 id 自加 1
  6502. *
  6503. * @private
  6504. */
  6505. Router.prototype._incMaxStateId = function() {
  6506. sessionStorage.setItem(this.sessionNames.maxStateId, this._getNextStateId());
  6507. };
  6508. /**
  6509. * 从一个文档切换为显示另一个文档
  6510. *
  6511. * @param $from 目前显示的文档
  6512. * @param $to 待切换显示的新文档
  6513. * @param $visibleSection 新文档中展示的 section 元素
  6514. * @param direction 新文档切入方向
  6515. * @private
  6516. */
  6517. Router.prototype._animateDocument = function($from, $to, $visibleSection, direction) {
  6518. var sectionId = $visibleSection.attr('id');
  6519. var $visibleSectionInFrom = $from.find('.' + routerConfig.curPageClass);
  6520. $visibleSectionInFrom.addClass(routerConfig.visiblePageClass).removeClass(routerConfig.curPageClass);
  6521. $visibleSection.trigger(EVENTS.pageAnimationStart, [sectionId, $visibleSection]);
  6522. this._animateElement($from, $to, direction);
  6523. $from.animationEnd(function() {
  6524. $visibleSectionInFrom.removeClass(routerConfig.visiblePageClass);
  6525. // 移除 document 前后,发送 beforePageRemove 和 pageRemoved 事件
  6526. $(window).trigger(EVENTS.beforePageRemove, [$from]);
  6527. $from.remove();
  6528. $(window).trigger(EVENTS.pageRemoved);
  6529. });
  6530. $to.animationEnd(function() {
  6531. $visibleSection.trigger(EVENTS.pageAnimationEnd, [sectionId, $visibleSection]);
  6532. // 外层(init.js)中会绑定 pageInitInternal 事件,然后对页面进行初始化
  6533. $visibleSection.trigger(EVENTS.pageInit, [sectionId, $visibleSection]);
  6534. });
  6535. };
  6536. /**
  6537. * 把当前文档的展示 section 从一个 section 切换到另一个 section
  6538. *
  6539. * @param $from
  6540. * @param $to
  6541. * @param direction
  6542. * @private
  6543. */
  6544. Router.prototype._animateSection = function($from, $to, direction) {
  6545. var toId = $to.attr('id');
  6546. $from.trigger(EVENTS.beforePageSwitch, [$from.attr('id'), $from]);
  6547. $from.removeClass(routerConfig.curPageClass);
  6548. $to.addClass(routerConfig.curPageClass);
  6549. $to.trigger(EVENTS.pageAnimationStart, [toId, $to]);
  6550. this._animateElement($from, $to, direction);
  6551. $to.animationEnd(function() {
  6552. $to.trigger(EVENTS.pageAnimationEnd, [toId, $to]);
  6553. // 外层(init.js)中会绑定 pageInitInternal 事件,然后对页面进行初始化
  6554. $to.trigger(EVENTS.pageInit, [toId, $to]);
  6555. });
  6556. };
  6557. /**
  6558. * 切换显示两个元素
  6559. *
  6560. * 切换是通过更新 class 来实现的,而具体的切换动画则是 class 关联的 css 来实现
  6561. *
  6562. * @param $from 当前显示的元素
  6563. * @param $to 待显示的元素
  6564. * @param direction 切换的方向
  6565. * @private
  6566. */
  6567. Router.prototype._animateElement = function($from, $to, direction) {
  6568. // todo: 可考虑如果入参不指定,那么尝试读取 $to 的属性,再没有再使用默认的
  6569. // 考虑读取点击的链接上指定的方向
  6570. if (typeof direction === 'undefined') {
  6571. direction = DIRECTION.rightToLeft;
  6572. }
  6573. var animPageClasses = [
  6574. 'page-from-center-to-left',
  6575. 'page-from-center-to-right',
  6576. 'page-from-right-to-center',
  6577. 'page-from-left-to-center'].join(' ');
  6578. var classForFrom, classForTo;
  6579. switch(direction) {
  6580. case DIRECTION.rightToLeft:
  6581. classForFrom = 'page-from-center-to-left';
  6582. classForTo = 'page-from-right-to-center';
  6583. break;
  6584. case DIRECTION.leftToRight:
  6585. classForFrom = 'page-from-center-to-right';
  6586. classForTo = 'page-from-left-to-center';
  6587. break;
  6588. default:
  6589. classForFrom = 'page-from-center-to-left';
  6590. classForTo = 'page-from-right-to-center';
  6591. break;
  6592. }
  6593. $from.removeClass(animPageClasses).addClass(classForFrom);
  6594. $to.removeClass(animPageClasses).addClass(classForTo);
  6595. $from.animationEnd(function() {
  6596. $from.removeClass(animPageClasses);
  6597. });
  6598. $to.animationEnd(function() {
  6599. $to.removeClass(animPageClasses);
  6600. });
  6601. };
  6602. /**
  6603. * 获取当前显示的第一个 section
  6604. *
  6605. * @returns {*}
  6606. * @private
  6607. */
  6608. Router.prototype._getCurrentSection = function() {
  6609. return this.$view.find('.' + routerConfig.curPageClass).eq(0);
  6610. };
  6611. /**
  6612. * popState 事件关联着的后退处理
  6613. *
  6614. * 判断两个 state 判断是否是属于同一个文档,然后做对应的 section 或文档切换;
  6615. * 同时在切换后把新 state 设为当前 state
  6616. *
  6617. * @param {State} state 新 state
  6618. * @param {State} fromState 旧 state
  6619. * @private
  6620. */
  6621. Router.prototype._back = function(state, fromState) {
  6622. if (this._isTheSameDocument(state.url.full, fromState.url.full)) {
  6623. var $newPage = $('#' + state.pageId);
  6624. if ($newPage.length) {
  6625. var $currentPage = this._getCurrentSection();
  6626. this._animateSection($currentPage, $newPage, DIRECTION.leftToRight);
  6627. this._saveAsCurrentState(state);
  6628. } else {
  6629. location.href = state.url.full;
  6630. }
  6631. } else {
  6632. this._saveDocumentIntoCache($(document), fromState.url.full);
  6633. this._switchToDocument(state.url.full, false, false, DIRECTION.leftToRight);
  6634. this._saveAsCurrentState(state);
  6635. }
  6636. };
  6637. /**
  6638. * popState 事件关联着的前进处理,类似于 _back,不同的是切换方向
  6639. *
  6640. * @param {State} state 新 state
  6641. * @param {State} fromState 旧 state
  6642. * @private
  6643. */
  6644. Router.prototype._forward = function(state, fromState) {
  6645. if (this._isTheSameDocument(state.url.full, fromState.url.full)) {
  6646. var $newPage = $('#' + state.pageId);
  6647. if ($newPage.length) {
  6648. var $currentPage = this._getCurrentSection();
  6649. this._animateSection($currentPage, $newPage, DIRECTION.rightToLeft);
  6650. this._saveAsCurrentState(state);
  6651. } else {
  6652. location.href = state.url.full;
  6653. }
  6654. } else {
  6655. this._saveDocumentIntoCache($(document), fromState.url.full);
  6656. this._switchToDocument(state.url.full, false, false, DIRECTION.rightToLeft);
  6657. this._saveAsCurrentState(state);
  6658. }
  6659. };
  6660. /**
  6661. * popState 事件处理
  6662. *
  6663. * 根据 pop 出来的 state 和当前 state 来判断是前进还是后退
  6664. *
  6665. * @param event
  6666. * @private
  6667. */
  6668. Router.prototype._onPopState = function(event) {
  6669. var state = event.state;
  6670. // if not a valid state, do nothing
  6671. if (!state || !state.pageId) {
  6672. return;
  6673. }
  6674. var lastState = this._getLastState();
  6675. if (!lastState) {
  6676. console.error && console.error('Missing last state when backward or forward');
  6677. return;
  6678. }
  6679. if (state.id === lastState.id) {
  6680. return;
  6681. }
  6682. if (state.id < lastState.id) {
  6683. this._back(state, lastState);
  6684. } else {
  6685. this._forward(state, lastState);
  6686. }
  6687. };
  6688. /**
  6689. * 页面进入到一个新状态
  6690. *
  6691. * 把新状态 push 进去,设置为当前的状态,然后把 maxState 的 id +1。
  6692. *
  6693. * @param {String} url 新状态的 url
  6694. * @param {String} sectionId 新状态中显示的 section 元素的 id
  6695. * @private
  6696. */
  6697. Router.prototype._pushNewState = function(url, sectionId) {
  6698. var state = {
  6699. id: this._getNextStateId(),
  6700. pageId: sectionId,
  6701. url: Util.toUrlObject(url)
  6702. };
  6703. theHistory.pushState(state, '', url);
  6704. this._saveAsCurrentState(state);
  6705. this._incMaxStateId();
  6706. };
  6707. /**
  6708. * 生成一个随机的 id
  6709. *
  6710. * @returns {string}
  6711. * @private
  6712. */
  6713. Router.prototype._generateRandomId = function() {
  6714. return "page-" + (+new Date());
  6715. };
  6716. Router.prototype.dispatch = function(event) {
  6717. var e = new CustomEvent(event, {
  6718. bubbles: true,
  6719. cancelable: true
  6720. });
  6721. //noinspection JSUnresolvedFunction
  6722. window.dispatchEvent(e);
  6723. };
  6724. /**
  6725. * 判断一个链接是否使用 router 来处理
  6726. *
  6727. * @param $link
  6728. * @returns {boolean}
  6729. */
  6730. function isInRouterBlackList($link) {
  6731. var classBlackList = [
  6732. 'external',
  6733. 'tab-link',
  6734. 'open-popup',
  6735. 'close-popup',
  6736. 'open-panel',
  6737. 'close-panel'
  6738. ];
  6739. for (var i = classBlackList.length -1 ; i >= 0; i--) {
  6740. if ($link.hasClass(classBlackList[i])) {
  6741. return true;
  6742. }
  6743. }
  6744. var linkEle = $link.get(0);
  6745. var linkHref = linkEle.getAttribute('href');
  6746. var protoWhiteList = [
  6747. 'http',
  6748. 'https'
  6749. ];
  6750. //如果非noscheme形式的链接,且协议不是http(s),那么路由不会处理这类链接
  6751. if (/^(\w+):/.test(linkHref) && protoWhiteList.indexOf(RegExp.$1) < 0) {
  6752. return true;
  6753. }
  6754. //noinspection RedundantIfStatementJS
  6755. if (linkEle.hasAttribute('external')) {
  6756. return true;
  6757. }
  6758. return false;
  6759. }
  6760. /**
  6761. * 自定义是否执行路由功能的过滤器
  6762. *
  6763. * 可以在外部定义 $.config.routerFilter 函数,实参是点击链接的 Zepto 对象。
  6764. *
  6765. * @param $link 当前点击的链接的 Zepto 对象
  6766. * @returns {boolean} 返回 true 表示执行路由功能,否则不做路由处理
  6767. */
  6768. function customClickFilter($link) {
  6769. var customRouterFilter = $.smConfig.routerFilter;
  6770. if ($.isFunction(customRouterFilter)) {
  6771. var filterResult = customRouterFilter($link);
  6772. if (typeof filterResult === 'boolean') {
  6773. return filterResult;
  6774. }
  6775. }
  6776. return true;
  6777. }
  6778. $(function() {
  6779. // 用户可选关闭router功能
  6780. if (!$.smConfig.router) {
  6781. return;
  6782. }
  6783. if (!Util.supportStorage()) {
  6784. return;
  6785. }
  6786. var $pages = $('.' + routerConfig.pageClass);
  6787. if (!$pages.length) {
  6788. var warnMsg = 'Disable router function because of no .page elements';
  6789. if (window.console && window.console.warn) {
  6790. console.warn(warnMsg);
  6791. }
  6792. return;
  6793. }
  6794. var router = $.router = new Router();
  6795. $(document).on('click', 'a', function(e) {
  6796. var $target = $(e.currentTarget);
  6797. var filterResult = customClickFilter($target);
  6798. if (!filterResult) {
  6799. return;
  6800. }
  6801. if (isInRouterBlackList($target)) {
  6802. return;
  6803. }
  6804. e.preventDefault();
  6805. if ($target.hasClass('back')) {
  6806. router.back();
  6807. } else {
  6808. var url = $target.attr('href');
  6809. if (!url || url === '#') {
  6810. return;
  6811. }
  6812. var ignoreCache = $target.attr('data-no-cache') === 'true';
  6813. router.load(url, ignoreCache);
  6814. }
  6815. });
  6816. });
  6817. }(Zepto);
  6818. /**
  6819. * @typedef {Object} State
  6820. * @property {Number} id
  6821. * @property {String} url
  6822. * @property {String} pageId
  6823. */
  6824. /**
  6825. * @typedef {Object} UrlObject 字符串 url 转为的对象
  6826. * @property {String} base url 的基本路径
  6827. * @property {String} full url 的完整绝对路径
  6828. * @property {String} origin 转换前的 url
  6829. * @property {String} fragment url 的 fragment
  6830. */
  6831. /**
  6832. * @typedef {Object} DocumentCache
  6833. * @property {*|HTMLElement} $doc 看做是 $(document)
  6834. * @property {*|HTMLElement} $content $doc 里的 routerConfig.innerViewClass 元素
  6835. */
  6836. /*======================================================
  6837. ************ Modals ************
  6838. ======================================================*/
  6839. /*jshint unused: false*/
  6840. +function ($) {
  6841. "use strict";
  6842. $.lastPosition =function(options) {
  6843. if ( !sessionStorage) {
  6844. return;
  6845. }
  6846. // 需要记忆模块的className
  6847. var needMemoryClass = options.needMemoryClass || [];
  6848. $(window).off('beforePageSwitch').on('beforePageSwitch', function(event,id,arg) {
  6849. updateMemory(id,arg);
  6850. });
  6851. $(window).off('pageAnimationStart').on('pageAnimationStart', function(event,id,arg) {
  6852. getMemory(id,arg);
  6853. });
  6854. //让后退页面回到之前的高度
  6855. function getMemory(id,arg){
  6856. needMemoryClass.forEach(function(item, index) {
  6857. if ($(item).length === 0) {
  6858. return;
  6859. }
  6860. var positionName = id ;
  6861. // 遍历对应节点设置存储的高度
  6862. var memoryHeight = sessionStorage.getItem(positionName);
  6863. arg.find(item).scrollTop(parseInt(memoryHeight));
  6864. });
  6865. }
  6866. //记住即将离开的页面的高度
  6867. function updateMemory(id,arg) {
  6868. var positionName = id ;
  6869. // 存储需要记忆模块的高度
  6870. needMemoryClass.forEach(function(item, index) {
  6871. if ($(item).length === 0) {
  6872. return;
  6873. }
  6874. sessionStorage.setItem(
  6875. positionName,
  6876. arg.find(item).scrollTop()
  6877. );
  6878. });
  6879. }
  6880. };
  6881. }(Zepto);
  6882. /*jshint unused: false*/
  6883. +function($) {
  6884. 'use strict';
  6885. var getPage = function() {
  6886. var $page = $(".page-current");
  6887. if (!$page[0]) $page = $(".page").addClass('page-current');
  6888. return $page;
  6889. };
  6890. //初始化页面中的JS组件
  6891. $.initPage = function(page) {
  6892. var $page = getPage();
  6893. if (!$page[0]) $page = $(document.body);
  6894. var $content = $page.hasClass('content') ?
  6895. $page :
  6896. $page.find('.content');
  6897. $content.scroller(); //注意滚动条一定要最先初始化
  6898. $.initPullToRefresh($content);
  6899. $.initInfiniteScroll($content);
  6900. $.initCalendar($content);
  6901. //extend
  6902. if ($.initSwiper) $.initSwiper($content);
  6903. };
  6904. if ($.smConfig.showPageLoadingIndicator) {
  6905. //这里的 以 push 开头的是私有事件,不要用
  6906. $(window).on('pageLoadStart', function() {
  6907. $.showIndicator();
  6908. });
  6909. $(window).on('pageAnimationStart', function() {
  6910. $.hideIndicator();
  6911. });
  6912. $(window).on('pageLoadCancel', function() {
  6913. $.hideIndicator();
  6914. });
  6915. $(window).on('pageLoadComplete', function() {
  6916. $.hideIndicator();
  6917. });
  6918. $(window).on('pageLoadError', function() {
  6919. $.hideIndicator();
  6920. $.toast('加载失败');
  6921. });
  6922. }
  6923. $(window).on('pageAnimationStart', function(event,id,page) {
  6924. // 在路由切换页面动画开始前,为了把位于 .page 之外的 popup 等隐藏,此处做些处理
  6925. $.closeModal();
  6926. $.closePanel();
  6927. // 如果 panel 的 effect 是 reveal 时,似乎是 page 的动画或别的样式原因导致了 transitionEnd 时间不会触发
  6928. // 这里暂且处理一下
  6929. $('body').removeClass('panel-closing');
  6930. $.allowPanelOpen = true;
  6931. });
  6932. $(window).on('pageInit', function() {
  6933. $.hideIndicator();
  6934. $.lastPosition({
  6935. needMemoryClass: [
  6936. '.content'
  6937. ]
  6938. });
  6939. });
  6940. // safari 在后退的时候会使用缓存技术,但实现上似乎存在些问题,
  6941. // 导致路由中绑定的点击事件不会正常如期的运行(log 和 debugger 都没法调试),
  6942. // 从而后续的跳转等完全乱了套。
  6943. // 所以,这里检测到是 safari 的 cache 的情况下,做一次 reload
  6944. // 测试路径(后缀 D 表示是 document,E 表示 external,不使用路由跳转):
  6945. // 1. aD -> bDE
  6946. // 2. back
  6947. // 3. aD -> bD
  6948. window.addEventListener('pageshow', function(event) {
  6949. if (event.persisted) {
  6950. location.reload();
  6951. }
  6952. });
  6953. $.init = function() {
  6954. var $page = getPage();
  6955. var id = $page[0].id;
  6956. $.initPage();
  6957. $page.trigger('pageInit', [id, $page]);
  6958. };
  6959. //DOM READY
  6960. $(function() {
  6961. //直接绑定
  6962. FastClick.attach(document.body);
  6963. if ($.smConfig.autoInit) {
  6964. $.init();
  6965. }
  6966. $(document).on('pageInitInternal', function(e, id, page) {
  6967. $.init();
  6968. });
  6969. });
  6970. }(Zepto);
  6971. /**
  6972. * ScrollFix v0.1
  6973. * http://www.joelambert.co.uk
  6974. *
  6975. * Copyright 2011, Joe Lambert.
  6976. * Free to use under the MIT license.
  6977. * http://www.opensource.org/licenses/mit-license.php
  6978. */
  6979. /* ===============================================================================
  6980. ************ ScrollFix ************
  6981. =============================================================================== */
  6982. + function($) {
  6983. "use strict";
  6984. //安卓微信中使用scrollfix会有问题,因此只在ios中使用,安卓机器按照原来的逻辑
  6985. if($.device.ios){
  6986. var ScrollFix = function(elem) {
  6987. // Variables to track inputs
  6988. var startY;
  6989. var startTopScroll;
  6990. elem = elem || document.querySelector(elem);
  6991. // If there is no element, then do nothing
  6992. if(!elem)
  6993. return;
  6994. // Handle the start of interactions
  6995. elem.addEventListener('touchstart', function(event){
  6996. startY = event.touches[0].pageY;
  6997. startTopScroll = elem.scrollTop;
  6998. if(startTopScroll <= 0)
  6999. elem.scrollTop = 1;
  7000. if(startTopScroll + elem.offsetHeight >= elem.scrollHeight)
  7001. elem.scrollTop = elem.scrollHeight - elem.offsetHeight - 1;
  7002. }, false);
  7003. };
  7004. var initScrollFix = function(){
  7005. var prefix = $('.page-current').length > 0 ? '.page-current ' : '';
  7006. var scrollable = $(prefix + ".content");
  7007. new ScrollFix(scrollable[0]);
  7008. };
  7009. $(document).on($.touchEvents.move, ".page-current .bar",function(){
  7010. event.preventDefault();
  7011. });
  7012. //监听ajax页面跳转
  7013. $(document).on("pageLoadComplete", function(){
  7014. initScrollFix();
  7015. });
  7016. //监听内联页面跳转
  7017. $(document).on("pageAnimationEnd", function(){
  7018. initScrollFix();
  7019. });
  7020. initScrollFix();
  7021. }
  7022. }(Zepto);