jquery.mobile-1.1.0.js 233 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366336733683369337033713372337333743375337633773378337933803381338233833384338533863387338833893390339133923393339433953396339733983399340034013402340334043405340634073408340934103411341234133414341534163417341834193420342134223423342434253426342734283429343034313432343334343435343634373438343934403441344234433444344534463447344834493450345134523453345434553456345734583459346034613462346334643465346634673468346934703471347234733474347534763477347834793480348134823483348434853486348734883489349034913492349334943495349634973498349935003501350235033504350535063507350835093510351135123513351435153516351735183519352035213522352335243525352635273528352935303531353235333534353535363537353835393540354135423543354435453546354735483549355035513552355335543555355635573558355935603561356235633564356535663567356835693570357135723573357435753576357735783579358035813582358335843585358635873588358935903591359235933594359535963597359835993600360136023603360436053606360736083609361036113612361336143615361636173618361936203621362236233624362536263627362836293630363136323633363436353636363736383639364036413642364336443645364636473648364936503651365236533654365536563657365836593660366136623663366436653666366736683669367036713672367336743675367636773678367936803681368236833684368536863687368836893690369136923693369436953696369736983699370037013702370337043705370637073708370937103711371237133714371537163717371837193720372137223723372437253726372737283729373037313732373337343735373637373738373937403741374237433744374537463747374837493750375137523753375437553756375737583759376037613762376337643765376637673768376937703771377237733774377537763777377837793780378137823783378437853786378737883789379037913792379337943795379637973798379938003801380238033804380538063807380838093810381138123813381438153816381738183819382038213822382338243825382638273828382938303831383238333834383538363837383838393840384138423843384438453846384738483849385038513852385338543855385638573858385938603861386238633864386538663867386838693870387138723873387438753876387738783879388038813882388338843885388638873888388938903891389238933894389538963897389838993900390139023903390439053906390739083909391039113912391339143915391639173918391939203921392239233924392539263927392839293930393139323933393439353936393739383939394039413942394339443945394639473948394939503951395239533954395539563957395839593960396139623963396439653966396739683969397039713972397339743975397639773978397939803981398239833984398539863987398839893990399139923993399439953996399739983999400040014002400340044005400640074008400940104011401240134014401540164017401840194020402140224023402440254026402740284029403040314032403340344035403640374038403940404041404240434044404540464047404840494050405140524053405440554056405740584059406040614062406340644065406640674068406940704071407240734074407540764077407840794080408140824083408440854086408740884089409040914092409340944095409640974098409941004101410241034104410541064107410841094110411141124113411441154116411741184119412041214122412341244125412641274128412941304131413241334134413541364137413841394140414141424143414441454146414741484149415041514152415341544155415641574158415941604161416241634164416541664167416841694170417141724173417441754176417741784179418041814182418341844185418641874188418941904191419241934194419541964197419841994200420142024203420442054206420742084209421042114212421342144215421642174218421942204221422242234224422542264227422842294230423142324233423442354236423742384239424042414242424342444245424642474248424942504251425242534254425542564257425842594260426142624263426442654266426742684269427042714272427342744275427642774278427942804281428242834284428542864287428842894290429142924293429442954296429742984299430043014302430343044305430643074308430943104311431243134314431543164317431843194320432143224323432443254326432743284329433043314332433343344335433643374338433943404341434243434344434543464347434843494350435143524353435443554356435743584359436043614362436343644365436643674368436943704371437243734374437543764377437843794380438143824383438443854386438743884389439043914392439343944395439643974398439944004401440244034404440544064407440844094410441144124413441444154416441744184419442044214422442344244425442644274428442944304431443244334434443544364437443844394440444144424443444444454446444744484449445044514452445344544455445644574458445944604461446244634464446544664467446844694470447144724473447444754476447744784479448044814482448344844485448644874488448944904491449244934494449544964497449844994500450145024503450445054506450745084509451045114512451345144515451645174518451945204521452245234524452545264527452845294530453145324533453445354536453745384539454045414542454345444545454645474548454945504551455245534554455545564557455845594560456145624563456445654566456745684569457045714572457345744575457645774578457945804581458245834584458545864587458845894590459145924593459445954596459745984599460046014602460346044605460646074608460946104611461246134614461546164617461846194620462146224623462446254626462746284629463046314632463346344635463646374638463946404641464246434644464546464647464846494650465146524653465446554656465746584659466046614662466346644665466646674668466946704671467246734674467546764677467846794680468146824683468446854686468746884689469046914692469346944695469646974698469947004701470247034704470547064707470847094710471147124713471447154716471747184719472047214722472347244725472647274728472947304731473247334734473547364737473847394740474147424743474447454746474747484749475047514752475347544755475647574758475947604761476247634764476547664767476847694770477147724773477447754776477747784779478047814782478347844785478647874788478947904791479247934794479547964797479847994800480148024803480448054806480748084809481048114812481348144815481648174818481948204821482248234824482548264827482848294830483148324833483448354836483748384839484048414842484348444845484648474848484948504851485248534854485548564857485848594860486148624863486448654866486748684869487048714872487348744875487648774878487948804881488248834884488548864887488848894890489148924893489448954896489748984899490049014902490349044905490649074908490949104911491249134914491549164917491849194920492149224923492449254926492749284929493049314932493349344935493649374938493949404941494249434944494549464947494849494950495149524953495449554956495749584959496049614962496349644965496649674968496949704971497249734974497549764977497849794980498149824983498449854986498749884989499049914992499349944995499649974998499950005001500250035004500550065007500850095010501150125013501450155016501750185019502050215022502350245025502650275028502950305031503250335034503550365037503850395040504150425043504450455046504750485049505050515052505350545055505650575058505950605061506250635064506550665067506850695070507150725073507450755076507750785079508050815082508350845085508650875088508950905091509250935094509550965097509850995100510151025103510451055106510751085109511051115112511351145115511651175118511951205121512251235124512551265127512851295130513151325133513451355136513751385139514051415142514351445145514651475148514951505151515251535154515551565157515851595160516151625163516451655166516751685169517051715172517351745175517651775178517951805181518251835184518551865187518851895190519151925193519451955196519751985199520052015202520352045205520652075208520952105211521252135214521552165217521852195220522152225223522452255226522752285229523052315232523352345235523652375238523952405241524252435244524552465247524852495250525152525253525452555256525752585259526052615262526352645265526652675268526952705271527252735274527552765277527852795280528152825283528452855286528752885289529052915292529352945295529652975298529953005301530253035304530553065307530853095310531153125313531453155316531753185319532053215322532353245325532653275328532953305331533253335334533553365337533853395340534153425343534453455346534753485349535053515352535353545355535653575358535953605361536253635364536553665367536853695370537153725373537453755376537753785379538053815382538353845385538653875388538953905391539253935394539553965397539853995400540154025403540454055406540754085409541054115412541354145415541654175418541954205421542254235424542554265427542854295430543154325433543454355436543754385439544054415442544354445445544654475448544954505451545254535454545554565457545854595460546154625463546454655466546754685469547054715472547354745475547654775478547954805481548254835484548554865487548854895490549154925493549454955496549754985499550055015502550355045505550655075508550955105511551255135514551555165517551855195520552155225523552455255526552755285529553055315532553355345535553655375538553955405541554255435544554555465547554855495550555155525553555455555556555755585559556055615562556355645565556655675568556955705571557255735574557555765577557855795580558155825583558455855586558755885589559055915592559355945595559655975598559956005601560256035604560556065607560856095610561156125613561456155616561756185619562056215622562356245625562656275628562956305631563256335634563556365637563856395640564156425643564456455646564756485649565056515652565356545655565656575658565956605661566256635664566556665667566856695670567156725673567456755676567756785679568056815682568356845685568656875688568956905691569256935694569556965697569856995700570157025703570457055706570757085709571057115712571357145715571657175718571957205721572257235724572557265727572857295730573157325733573457355736573757385739574057415742574357445745574657475748574957505751575257535754575557565757575857595760576157625763576457655766576757685769577057715772577357745775577657775778577957805781578257835784578557865787578857895790579157925793579457955796579757985799580058015802580358045805580658075808580958105811581258135814581558165817581858195820582158225823582458255826582758285829583058315832583358345835583658375838583958405841584258435844584558465847584858495850585158525853585458555856585758585859586058615862586358645865586658675868586958705871587258735874587558765877587858795880588158825883588458855886588758885889589058915892589358945895589658975898589959005901590259035904590559065907590859095910591159125913591459155916591759185919592059215922592359245925592659275928592959305931593259335934593559365937593859395940594159425943594459455946594759485949595059515952595359545955595659575958595959605961596259635964596559665967596859695970597159725973597459755976597759785979598059815982598359845985598659875988598959905991599259935994599559965997599859996000600160026003600460056006600760086009601060116012601360146015601660176018601960206021602260236024602560266027602860296030603160326033603460356036603760386039604060416042604360446045604660476048604960506051605260536054605560566057605860596060606160626063606460656066606760686069607060716072607360746075607660776078607960806081608260836084608560866087608860896090609160926093609460956096609760986099610061016102610361046105610661076108610961106111611261136114611561166117611861196120612161226123612461256126612761286129613061316132613361346135613661376138613961406141614261436144614561466147614861496150615161526153615461556156615761586159616061616162616361646165616661676168616961706171617261736174617561766177617861796180618161826183618461856186618761886189619061916192619361946195619661976198619962006201620262036204620562066207620862096210621162126213621462156216621762186219622062216222622362246225622662276228622962306231623262336234623562366237623862396240624162426243624462456246624762486249625062516252625362546255625662576258625962606261626262636264626562666267626862696270627162726273627462756276627762786279628062816282628362846285628662876288628962906291629262936294629562966297629862996300630163026303630463056306630763086309631063116312631363146315631663176318631963206321632263236324632563266327632863296330633163326333633463356336633763386339634063416342634363446345634663476348634963506351635263536354635563566357635863596360636163626363636463656366636763686369637063716372637363746375637663776378637963806381638263836384638563866387638863896390639163926393639463956396639763986399640064016402640364046405640664076408640964106411641264136414641564166417641864196420642164226423642464256426642764286429643064316432643364346435643664376438643964406441644264436444644564466447644864496450645164526453645464556456645764586459646064616462646364646465646664676468646964706471647264736474647564766477647864796480648164826483648464856486648764886489649064916492649364946495649664976498649965006501650265036504650565066507650865096510651165126513651465156516651765186519652065216522652365246525652665276528652965306531653265336534653565366537653865396540654165426543654465456546654765486549655065516552655365546555655665576558655965606561656265636564656565666567656865696570657165726573657465756576657765786579658065816582658365846585658665876588658965906591659265936594659565966597659865996600660166026603660466056606660766086609661066116612661366146615661666176618661966206621662266236624662566266627662866296630663166326633663466356636663766386639664066416642664366446645664666476648664966506651665266536654665566566657665866596660666166626663666466656666666766686669667066716672667366746675667666776678667966806681668266836684668566866687668866896690669166926693669466956696669766986699670067016702670367046705670667076708670967106711671267136714671567166717671867196720672167226723672467256726672767286729673067316732673367346735673667376738673967406741674267436744674567466747674867496750675167526753675467556756675767586759676067616762676367646765676667676768676967706771677267736774677567766777677867796780678167826783678467856786678767886789679067916792679367946795679667976798679968006801680268036804680568066807680868096810681168126813681468156816681768186819682068216822682368246825682668276828682968306831683268336834683568366837683868396840684168426843684468456846684768486849685068516852685368546855685668576858685968606861686268636864686568666867686868696870687168726873687468756876687768786879688068816882688368846885688668876888688968906891689268936894689568966897689868996900690169026903690469056906690769086909691069116912691369146915691669176918691969206921692269236924692569266927692869296930693169326933693469356936693769386939694069416942694369446945694669476948694969506951695269536954695569566957695869596960696169626963696469656966696769686969697069716972697369746975697669776978697969806981698269836984698569866987698869896990699169926993699469956996699769986999700070017002700370047005700670077008700970107011701270137014701570167017701870197020702170227023702470257026702770287029703070317032703370347035703670377038703970407041704270437044704570467047704870497050705170527053705470557056705770587059706070617062706370647065706670677068706970707071707270737074707570767077707870797080708170827083708470857086708770887089709070917092709370947095709670977098709971007101710271037104710571067107710871097110711171127113711471157116711771187119712071217122712371247125712671277128712971307131713271337134713571367137713871397140714171427143714471457146714771487149715071517152715371547155715671577158715971607161716271637164716571667167716871697170717171727173717471757176717771787179718071817182718371847185718671877188718971907191719271937194719571967197719871997200720172027203720472057206720772087209721072117212721372147215721672177218721972207221722272237224722572267227722872297230723172327233723472357236723772387239724072417242724372447245724672477248724972507251725272537254725572567257725872597260726172627263726472657266726772687269727072717272727372747275727672777278727972807281728272837284728572867287728872897290729172927293729472957296729772987299730073017302730373047305730673077308730973107311731273137314731573167317731873197320732173227323732473257326732773287329733073317332733373347335733673377338733973407341734273437344734573467347734873497350735173527353735473557356735773587359736073617362736373647365736673677368736973707371737273737374737573767377737873797380738173827383738473857386738773887389739073917392739373947395739673977398739974007401740274037404740574067407740874097410741174127413741474157416741774187419742074217422742374247425742674277428742974307431743274337434743574367437743874397440744174427443744474457446744774487449745074517452745374547455745674577458745974607461746274637464746574667467746874697470747174727473747474757476747774787479748074817482748374847485748674877488748974907491749274937494749574967497749874997500750175027503750475057506750775087509751075117512751375147515751675177518751975207521752275237524752575267527752875297530753175327533753475357536753775387539754075417542754375447545754675477548754975507551
  1. /*
  2. * jQuery Mobile Framework 1.1.0 db342b1f315c282692791aa870455901fdb46a55
  3. * http://jquerymobile.com
  4. *
  5. * Copyright 2011 (c) jQuery Project
  6. * Dual licensed under the MIT or GPL Version 2 licenses.
  7. * http://jquery.org/license
  8. *
  9. */
  10. (function ( root, doc, factory ) {
  11. if ( typeof define === "function" && define.amd ) {
  12. // AMD. Register as an anonymous module.
  13. define( [ "jquery" ], function ( $ ) {
  14. factory( $, root, doc );
  15. return $.mobile;
  16. });
  17. } else {
  18. // Browser globals
  19. factory( root.jQuery, root, doc );
  20. }
  21. }( this, document, function ( $, window, document, undefined ) {
  22. // This plugin is an experiment for abstracting away the touch and mouse
  23. // events so that developers don't have to worry about which method of input
  24. // the device their document is loaded on supports.
  25. //
  26. // The idea here is to allow the developer to register listeners for the
  27. // basic mouse events, such as mousedown, mousemove, mouseup, and click,
  28. // and the plugin will take care of registering the correct listeners
  29. // behind the scenes to invoke the listener at the fastest possible time
  30. // for that device, while still retaining the order of event firing in
  31. // the traditional mouse environment, should multiple handlers be registered
  32. // on the same element for different events.
  33. //
  34. // The current version exposes the following virtual events to jQuery bind methods:
  35. // "vmouseover vmousedown vmousemove vmouseup vclick vmouseout vmousecancel"
  36. (function( $, window, document, undefined ) {
  37. var dataPropertyName = "virtualMouseBindings",
  38. touchTargetPropertyName = "virtualTouchID",
  39. virtualEventNames = "vmouseover vmousedown vmousemove vmouseup vclick vmouseout vmousecancel".split( " " ),
  40. touchEventProps = "clientX clientY pageX pageY screenX screenY".split( " " ),
  41. mouseHookProps = $.event.mouseHooks ? $.event.mouseHooks.props : [],
  42. mouseEventProps = $.event.props.concat( mouseHookProps ),
  43. activeDocHandlers = {},
  44. resetTimerID = 0,
  45. startX = 0,
  46. startY = 0,
  47. didScroll = false,
  48. clickBlockList = [],
  49. blockMouseTriggers = false,
  50. blockTouchTriggers = false,
  51. eventCaptureSupported = "addEventListener" in document,
  52. $document = $( document ),
  53. nextTouchID = 1,
  54. lastTouchID = 0;
  55. $.vmouse = {
  56. moveDistanceThreshold: 10,
  57. clickDistanceThreshold: 10,
  58. resetTimerDuration: 1500
  59. };
  60. function getNativeEvent( event ) {
  61. while ( event && typeof event.originalEvent !== "undefined" ) {
  62. event = event.originalEvent;
  63. }
  64. return event;
  65. }
  66. function createVirtualEvent( event, eventType ) {
  67. var t = event.type,
  68. oe, props, ne, prop, ct, touch, i, j;
  69. event = $.Event(event);
  70. event.type = eventType;
  71. oe = event.originalEvent;
  72. props = $.event.props;
  73. // addresses separation of $.event.props in to $.event.mouseHook.props and Issue 3280
  74. // https://github.com/jquery/jquery-mobile/issues/3280
  75. if ( t.search( /^(mouse|click)/ ) > -1 ) {
  76. props = mouseEventProps;
  77. }
  78. // copy original event properties over to the new event
  79. // this would happen if we could call $.event.fix instead of $.Event
  80. // but we don't have a way to force an event to be fixed multiple times
  81. if ( oe ) {
  82. for ( i = props.length, prop; i; ) {
  83. prop = props[ --i ];
  84. event[ prop ] = oe[ prop ];
  85. }
  86. }
  87. // make sure that if the mouse and click virtual events are generated
  88. // without a .which one is defined
  89. if ( t.search(/mouse(down|up)|click/) > -1 && !event.which ){
  90. event.which = 1;
  91. }
  92. if ( t.search(/^touch/) !== -1 ) {
  93. ne = getNativeEvent( oe );
  94. t = ne.touches;
  95. ct = ne.changedTouches;
  96. touch = ( t && t.length ) ? t[0] : ( (ct && ct.length) ? ct[ 0 ] : undefined );
  97. if ( touch ) {
  98. for ( j = 0, len = touchEventProps.length; j < len; j++){
  99. prop = touchEventProps[ j ];
  100. event[ prop ] = touch[ prop ];
  101. }
  102. }
  103. }
  104. return event;
  105. }
  106. function getVirtualBindingFlags( element ) {
  107. var flags = {},
  108. b, k;
  109. while ( element ) {
  110. b = $.data( element, dataPropertyName );
  111. for ( k in b ) {
  112. if ( b[ k ] ) {
  113. flags[ k ] = flags.hasVirtualBinding = true;
  114. }
  115. }
  116. element = element.parentNode;
  117. }
  118. return flags;
  119. }
  120. function getClosestElementWithVirtualBinding( element, eventType ) {
  121. var b;
  122. while ( element ) {
  123. b = $.data( element, dataPropertyName );
  124. if ( b && ( !eventType || b[ eventType ] ) ) {
  125. return element;
  126. }
  127. element = element.parentNode;
  128. }
  129. return null;
  130. }
  131. function enableTouchBindings() {
  132. blockTouchTriggers = false;
  133. }
  134. function disableTouchBindings() {
  135. blockTouchTriggers = true;
  136. }
  137. function enableMouseBindings() {
  138. lastTouchID = 0;
  139. clickBlockList.length = 0;
  140. blockMouseTriggers = false;
  141. // When mouse bindings are enabled, our
  142. // touch bindings are disabled.
  143. disableTouchBindings();
  144. }
  145. function disableMouseBindings() {
  146. // When mouse bindings are disabled, our
  147. // touch bindings are enabled.
  148. enableTouchBindings();
  149. }
  150. function startResetTimer() {
  151. clearResetTimer();
  152. resetTimerID = setTimeout(function(){
  153. resetTimerID = 0;
  154. enableMouseBindings();
  155. }, $.vmouse.resetTimerDuration );
  156. }
  157. function clearResetTimer() {
  158. if ( resetTimerID ){
  159. clearTimeout( resetTimerID );
  160. resetTimerID = 0;
  161. }
  162. }
  163. function triggerVirtualEvent( eventType, event, flags ) {
  164. var ve;
  165. if ( ( flags && flags[ eventType ] ) ||
  166. ( !flags && getClosestElementWithVirtualBinding( event.target, eventType ) ) ) {
  167. ve = createVirtualEvent( event, eventType );
  168. $( event.target).trigger( ve );
  169. }
  170. return ve;
  171. }
  172. function mouseEventCallback( event ) {
  173. var touchID = $.data(event.target, touchTargetPropertyName);
  174. if ( !blockMouseTriggers && ( !lastTouchID || lastTouchID !== touchID ) ){
  175. var ve = triggerVirtualEvent( "v" + event.type, event );
  176. if ( ve ) {
  177. if ( ve.isDefaultPrevented() ) {
  178. event.preventDefault();
  179. }
  180. if ( ve.isPropagationStopped() ) {
  181. event.stopPropagation();
  182. }
  183. if ( ve.isImmediatePropagationStopped() ) {
  184. event.stopImmediatePropagation();
  185. }
  186. }
  187. }
  188. }
  189. function handleTouchStart( event ) {
  190. var touches = getNativeEvent( event ).touches,
  191. target, flags;
  192. if ( touches && touches.length === 1 ) {
  193. target = event.target;
  194. flags = getVirtualBindingFlags( target );
  195. if ( flags.hasVirtualBinding ) {
  196. lastTouchID = nextTouchID++;
  197. $.data( target, touchTargetPropertyName, lastTouchID );
  198. clearResetTimer();
  199. disableMouseBindings();
  200. didScroll = false;
  201. var t = getNativeEvent( event ).touches[ 0 ];
  202. startX = t.pageX;
  203. startY = t.pageY;
  204. triggerVirtualEvent( "vmouseover", event, flags );
  205. triggerVirtualEvent( "vmousedown", event, flags );
  206. }
  207. }
  208. }
  209. function handleScroll( event ) {
  210. if ( blockTouchTriggers ) {
  211. return;
  212. }
  213. if ( !didScroll ) {
  214. triggerVirtualEvent( "vmousecancel", event, getVirtualBindingFlags( event.target ) );
  215. }
  216. didScroll = true;
  217. startResetTimer();
  218. }
  219. function handleTouchMove( event ) {
  220. if ( blockTouchTriggers ) {
  221. return;
  222. }
  223. var t = getNativeEvent( event ).touches[ 0 ],
  224. didCancel = didScroll,
  225. moveThreshold = $.vmouse.moveDistanceThreshold;
  226. didScroll = didScroll ||
  227. ( Math.abs(t.pageX - startX) > moveThreshold ||
  228. Math.abs(t.pageY - startY) > moveThreshold ),
  229. flags = getVirtualBindingFlags( event.target );
  230. if ( didScroll && !didCancel ) {
  231. triggerVirtualEvent( "vmousecancel", event, flags );
  232. }
  233. triggerVirtualEvent( "vmousemove", event, flags );
  234. startResetTimer();
  235. }
  236. function handleTouchEnd( event ) {
  237. if ( blockTouchTriggers ) {
  238. return;
  239. }
  240. disableTouchBindings();
  241. var flags = getVirtualBindingFlags( event.target ),
  242. t;
  243. triggerVirtualEvent( "vmouseup", event, flags );
  244. if ( !didScroll ) {
  245. var ve = triggerVirtualEvent( "vclick", event, flags );
  246. if ( ve && ve.isDefaultPrevented() ) {
  247. // The target of the mouse events that follow the touchend
  248. // event don't necessarily match the target used during the
  249. // touch. This means we need to rely on coordinates for blocking
  250. // any click that is generated.
  251. t = getNativeEvent( event ).changedTouches[ 0 ];
  252. clickBlockList.push({
  253. touchID: lastTouchID,
  254. x: t.clientX,
  255. y: t.clientY
  256. });
  257. // Prevent any mouse events that follow from triggering
  258. // virtual event notifications.
  259. blockMouseTriggers = true;
  260. }
  261. }
  262. triggerVirtualEvent( "vmouseout", event, flags);
  263. didScroll = false;
  264. startResetTimer();
  265. }
  266. function hasVirtualBindings( ele ) {
  267. var bindings = $.data( ele, dataPropertyName ),
  268. k;
  269. if ( bindings ) {
  270. for ( k in bindings ) {
  271. if ( bindings[ k ] ) {
  272. return true;
  273. }
  274. }
  275. }
  276. return false;
  277. }
  278. function dummyMouseHandler(){}
  279. function getSpecialEventObject( eventType ) {
  280. var realType = eventType.substr( 1 );
  281. return {
  282. setup: function( data, namespace ) {
  283. // If this is the first virtual mouse binding for this element,
  284. // add a bindings object to its data.
  285. if ( !hasVirtualBindings( this ) ) {
  286. $.data( this, dataPropertyName, {});
  287. }
  288. // If setup is called, we know it is the first binding for this
  289. // eventType, so initialize the count for the eventType to zero.
  290. var bindings = $.data( this, dataPropertyName );
  291. bindings[ eventType ] = true;
  292. // If this is the first virtual mouse event for this type,
  293. // register a global handler on the document.
  294. activeDocHandlers[ eventType ] = ( activeDocHandlers[ eventType ] || 0 ) + 1;
  295. if ( activeDocHandlers[ eventType ] === 1 ) {
  296. $document.bind( realType, mouseEventCallback );
  297. }
  298. // Some browsers, like Opera Mini, won't dispatch mouse/click events
  299. // for elements unless they actually have handlers registered on them.
  300. // To get around this, we register dummy handlers on the elements.
  301. $( this ).bind( realType, dummyMouseHandler );
  302. // For now, if event capture is not supported, we rely on mouse handlers.
  303. if ( eventCaptureSupported ) {
  304. // If this is the first virtual mouse binding for the document,
  305. // register our touchstart handler on the document.
  306. activeDocHandlers[ "touchstart" ] = ( activeDocHandlers[ "touchstart" ] || 0) + 1;
  307. if (activeDocHandlers[ "touchstart" ] === 1) {
  308. $document.bind( "touchstart", handleTouchStart )
  309. .bind( "touchend", handleTouchEnd )
  310. // On touch platforms, touching the screen and then dragging your finger
  311. // causes the window content to scroll after some distance threshold is
  312. // exceeded. On these platforms, a scroll prevents a click event from being
  313. // dispatched, and on some platforms, even the touchend is suppressed. To
  314. // mimic the suppression of the click event, we need to watch for a scroll
  315. // event. Unfortunately, some platforms like iOS don't dispatch scroll
  316. // events until *AFTER* the user lifts their finger (touchend). This means
  317. // we need to watch both scroll and touchmove events to figure out whether
  318. // or not a scroll happenens before the touchend event is fired.
  319. .bind( "touchmove", handleTouchMove )
  320. .bind( "scroll", handleScroll );
  321. }
  322. }
  323. },
  324. teardown: function( data, namespace ) {
  325. // If this is the last virtual binding for this eventType,
  326. // remove its global handler from the document.
  327. --activeDocHandlers[ eventType ];
  328. if ( !activeDocHandlers[ eventType ] ) {
  329. $document.unbind( realType, mouseEventCallback );
  330. }
  331. if ( eventCaptureSupported ) {
  332. // If this is the last virtual mouse binding in existence,
  333. // remove our document touchstart listener.
  334. --activeDocHandlers[ "touchstart" ];
  335. if ( !activeDocHandlers[ "touchstart" ] ) {
  336. $document.unbind( "touchstart", handleTouchStart )
  337. .unbind( "touchmove", handleTouchMove )
  338. .unbind( "touchend", handleTouchEnd )
  339. .unbind( "scroll", handleScroll );
  340. }
  341. }
  342. var $this = $( this ),
  343. bindings = $.data( this, dataPropertyName );
  344. // teardown may be called when an element was
  345. // removed from the DOM. If this is the case,
  346. // jQuery core may have already stripped the element
  347. // of any data bindings so we need to check it before
  348. // using it.
  349. if ( bindings ) {
  350. bindings[ eventType ] = false;
  351. }
  352. // Unregister the dummy event handler.
  353. $this.unbind( realType, dummyMouseHandler );
  354. // If this is the last virtual mouse binding on the
  355. // element, remove the binding data from the element.
  356. if ( !hasVirtualBindings( this ) ) {
  357. $this.removeData( dataPropertyName );
  358. }
  359. }
  360. };
  361. }
  362. // Expose our custom events to the jQuery bind/unbind mechanism.
  363. for ( var i = 0; i < virtualEventNames.length; i++ ){
  364. $.event.special[ virtualEventNames[ i ] ] = getSpecialEventObject( virtualEventNames[ i ] );
  365. }
  366. // Add a capture click handler to block clicks.
  367. // Note that we require event capture support for this so if the device
  368. // doesn't support it, we punt for now and rely solely on mouse events.
  369. if ( eventCaptureSupported ) {
  370. document.addEventListener( "click", function( e ){
  371. var cnt = clickBlockList.length,
  372. target = e.target,
  373. x, y, ele, i, o, touchID;
  374. if ( cnt ) {
  375. x = e.clientX;
  376. y = e.clientY;
  377. threshold = $.vmouse.clickDistanceThreshold;
  378. // The idea here is to run through the clickBlockList to see if
  379. // the current click event is in the proximity of one of our
  380. // vclick events that had preventDefault() called on it. If we find
  381. // one, then we block the click.
  382. //
  383. // Why do we have to rely on proximity?
  384. //
  385. // Because the target of the touch event that triggered the vclick
  386. // can be different from the target of the click event synthesized
  387. // by the browser. The target of a mouse/click event that is syntehsized
  388. // from a touch event seems to be implementation specific. For example,
  389. // some browsers will fire mouse/click events for a link that is near
  390. // a touch event, even though the target of the touchstart/touchend event
  391. // says the user touched outside the link. Also, it seems that with most
  392. // browsers, the target of the mouse/click event is not calculated until the
  393. // time it is dispatched, so if you replace an element that you touched
  394. // with another element, the target of the mouse/click will be the new
  395. // element underneath that point.
  396. //
  397. // Aside from proximity, we also check to see if the target and any
  398. // of its ancestors were the ones that blocked a click. This is necessary
  399. // because of the strange mouse/click target calculation done in the
  400. // Android 2.1 browser, where if you click on an element, and there is a
  401. // mouse/click handler on one of its ancestors, the target will be the
  402. // innermost child of the touched element, even if that child is no where
  403. // near the point of touch.
  404. ele = target;
  405. while ( ele ) {
  406. for ( i = 0; i < cnt; i++ ) {
  407. o = clickBlockList[ i ];
  408. touchID = 0;
  409. if ( ( ele === target && Math.abs( o.x - x ) < threshold && Math.abs( o.y - y ) < threshold ) ||
  410. $.data( ele, touchTargetPropertyName ) === o.touchID ) {
  411. // XXX: We may want to consider removing matches from the block list
  412. // instead of waiting for the reset timer to fire.
  413. e.preventDefault();
  414. e.stopPropagation();
  415. return;
  416. }
  417. }
  418. ele = ele.parentNode;
  419. }
  420. }
  421. }, true);
  422. }
  423. })( jQuery, window, document );
  424. // Script: jQuery hashchange event
  425. //
  426. // *Version: 1.3, Last updated: 7/21/2010*
  427. //
  428. // Project Home - http://benalman.com/projects/jquery-hashchange-plugin/
  429. // GitHub - http://github.com/cowboy/jquery-hashchange/
  430. // Source - http://github.com/cowboy/jquery-hashchange/raw/master/jquery.ba-hashchange.js
  431. // (Minified) - http://github.com/cowboy/jquery-hashchange/raw/master/jquery.ba-hashchange.min.js (0.8kb gzipped)
  432. //
  433. // About: License
  434. //
  435. // Copyright (c) 2010 "Cowboy" Ben Alman,
  436. // Dual licensed under the MIT and GPL licenses.
  437. // http://benalman.com/about/license/
  438. //
  439. // About: Examples
  440. //
  441. // These working examples, complete with fully commented code, illustrate a few
  442. // ways in which this plugin can be used.
  443. //
  444. // hashchange event - http://benalman.com/code/projects/jquery-hashchange/examples/hashchange/
  445. // document.domain - http://benalman.com/code/projects/jquery-hashchange/examples/document_domain/
  446. //
  447. // About: Support and Testing
  448. //
  449. // Information about what version or versions of jQuery this plugin has been
  450. // tested with, what browsers it has been tested in, and where the unit tests
  451. // reside (so you can test it yourself).
  452. //
  453. // jQuery Versions - 1.2.6, 1.3.2, 1.4.1, 1.4.2
  454. // Browsers Tested - Internet Explorer 6-8, Firefox 2-4, Chrome 5-6, Safari 3.2-5,
  455. // Opera 9.6-10.60, iPhone 3.1, Android 1.6-2.2, BlackBerry 4.6-5.
  456. // Unit Tests - http://benalman.com/code/projects/jquery-hashchange/unit/
  457. //
  458. // About: Known issues
  459. //
  460. // While this jQuery hashchange event implementation is quite stable and
  461. // robust, there are a few unfortunate browser bugs surrounding expected
  462. // hashchange event-based behaviors, independent of any JavaScript
  463. // window.onhashchange abstraction. See the following examples for more
  464. // information:
  465. //
  466. // Chrome: Back Button - http://benalman.com/code/projects/jquery-hashchange/examples/bug-chrome-back-button/
  467. // Firefox: Remote XMLHttpRequest - http://benalman.com/code/projects/jquery-hashchange/examples/bug-firefox-remote-xhr/
  468. // WebKit: Back Button in an Iframe - http://benalman.com/code/projects/jquery-hashchange/examples/bug-webkit-hash-iframe/
  469. // Safari: Back Button from a different domain - http://benalman.com/code/projects/jquery-hashchange/examples/bug-safari-back-from-diff-domain/
  470. //
  471. // Also note that should a browser natively support the window.onhashchange
  472. // event, but not report that it does, the fallback polling loop will be used.
  473. //
  474. // About: Release History
  475. //
  476. // 1.3 - (7/21/2010) Reorganized IE6/7 Iframe code to make it more
  477. // "removable" for mobile-only development. Added IE6/7 document.title
  478. // support. Attempted to make Iframe as hidden as possible by using
  479. // techniques from http://www.paciellogroup.com/blog/?p=604. Added
  480. // support for the "shortcut" format $(window).hashchange( fn ) and
  481. // $(window).hashchange() like jQuery provides for built-in events.
  482. // Renamed jQuery.hashchangeDelay to <jQuery.fn.hashchange.delay> and
  483. // lowered its default value to 50. Added <jQuery.fn.hashchange.domain>
  484. // and <jQuery.fn.hashchange.src> properties plus document-domain.html
  485. // file to address access denied issues when setting document.domain in
  486. // IE6/7.
  487. // 1.2 - (2/11/2010) Fixed a bug where coming back to a page using this plugin
  488. // from a page on another domain would cause an error in Safari 4. Also,
  489. // IE6/7 Iframe is now inserted after the body (this actually works),
  490. // which prevents the page from scrolling when the event is first bound.
  491. // Event can also now be bound before DOM ready, but it won't be usable
  492. // before then in IE6/7.
  493. // 1.1 - (1/21/2010) Incorporated document.documentMode test to fix IE8 bug
  494. // where browser version is incorrectly reported as 8.0, despite
  495. // inclusion of the X-UA-Compatible IE=EmulateIE7 meta tag.
  496. // 1.0 - (1/9/2010) Initial Release. Broke out the jQuery BBQ event.special
  497. // window.onhashchange functionality into a separate plugin for users
  498. // who want just the basic event & back button support, without all the
  499. // extra awesomeness that BBQ provides. This plugin will be included as
  500. // part of jQuery BBQ, but also be available separately.
  501. (function($,window,undefined){
  502. // Reused string.
  503. var str_hashchange = 'hashchange',
  504. // Method / object references.
  505. doc = document,
  506. fake_onhashchange,
  507. special = $.event.special,
  508. // Does the browser support window.onhashchange? Note that IE8 running in
  509. // IE7 compatibility mode reports true for 'onhashchange' in window, even
  510. // though the event isn't supported, so also test document.documentMode.
  511. doc_mode = doc.documentMode,
  512. supports_onhashchange = 'on' + str_hashchange in window && ( doc_mode === undefined || doc_mode > 7 );
  513. // Get location.hash (or what you'd expect location.hash to be) sans any
  514. // leading #. Thanks for making this necessary, Firefox!
  515. function get_fragment( url ) {
  516. url = url || location.href;
  517. return '#' + url.replace( /^[^#]*#?(.*)$/, '$1' );
  518. };
  519. // Method: jQuery.fn.hashchange
  520. //
  521. // Bind a handler to the window.onhashchange event or trigger all bound
  522. // window.onhashchange event handlers. This behavior is consistent with
  523. // jQuery's built-in event handlers.
  524. //
  525. // Usage:
  526. //
  527. // > jQuery(window).hashchange( [ handler ] );
  528. //
  529. // Arguments:
  530. //
  531. // handler - (Function) Optional handler to be bound to the hashchange
  532. // event. This is a "shortcut" for the more verbose form:
  533. // jQuery(window).bind( 'hashchange', handler ). If handler is omitted,
  534. // all bound window.onhashchange event handlers will be triggered. This
  535. // is a shortcut for the more verbose
  536. // jQuery(window).trigger( 'hashchange' ). These forms are described in
  537. // the <hashchange event> section.
  538. //
  539. // Returns:
  540. //
  541. // (jQuery) The initial jQuery collection of elements.
  542. // Allow the "shortcut" format $(elem).hashchange( fn ) for binding and
  543. // $(elem).hashchange() for triggering, like jQuery does for built-in events.
  544. $.fn[ str_hashchange ] = function( fn ) {
  545. return fn ? this.bind( str_hashchange, fn ) : this.trigger( str_hashchange );
  546. };
  547. // Property: jQuery.fn.hashchange.delay
  548. //
  549. // The numeric interval (in milliseconds) at which the <hashchange event>
  550. // polling loop executes. Defaults to 50.
  551. // Property: jQuery.fn.hashchange.domain
  552. //
  553. // If you're setting document.domain in your JavaScript, and you want hash
  554. // history to work in IE6/7, not only must this property be set, but you must
  555. // also set document.domain BEFORE jQuery is loaded into the page. This
  556. // property is only applicable if you are supporting IE6/7 (or IE8 operating
  557. // in "IE7 compatibility" mode).
  558. //
  559. // In addition, the <jQuery.fn.hashchange.src> property must be set to the
  560. // path of the included "document-domain.html" file, which can be renamed or
  561. // modified if necessary (note that the document.domain specified must be the
  562. // same in both your main JavaScript as well as in this file).
  563. //
  564. // Usage:
  565. //
  566. // jQuery.fn.hashchange.domain = document.domain;
  567. // Property: jQuery.fn.hashchange.src
  568. //
  569. // If, for some reason, you need to specify an Iframe src file (for example,
  570. // when setting document.domain as in <jQuery.fn.hashchange.domain>), you can
  571. // do so using this property. Note that when using this property, history
  572. // won't be recorded in IE6/7 until the Iframe src file loads. This property
  573. // is only applicable if you are supporting IE6/7 (or IE8 operating in "IE7
  574. // compatibility" mode).
  575. //
  576. // Usage:
  577. //
  578. // jQuery.fn.hashchange.src = 'path/to/file.html';
  579. $.fn[ str_hashchange ].delay = 50;
  580. /*
  581. $.fn[ str_hashchange ].domain = null;
  582. $.fn[ str_hashchange ].src = null;
  583. */
  584. // Event: hashchange event
  585. //
  586. // Fired when location.hash changes. In browsers that support it, the native
  587. // HTML5 window.onhashchange event is used, otherwise a polling loop is
  588. // initialized, running every <jQuery.fn.hashchange.delay> milliseconds to
  589. // see if the hash has changed. In IE6/7 (and IE8 operating in "IE7
  590. // compatibility" mode), a hidden Iframe is created to allow the back button
  591. // and hash-based history to work.
  592. //
  593. // Usage as described in <jQuery.fn.hashchange>:
  594. //
  595. // > // Bind an event handler.
  596. // > jQuery(window).hashchange( function(e) {
  597. // > var hash = location.hash;
  598. // > ...
  599. // > });
  600. // >
  601. // > // Manually trigger the event handler.
  602. // > jQuery(window).hashchange();
  603. //
  604. // A more verbose usage that allows for event namespacing:
  605. //
  606. // > // Bind an event handler.
  607. // > jQuery(window).bind( 'hashchange', function(e) {
  608. // > var hash = location.hash;
  609. // > ...
  610. // > });
  611. // >
  612. // > // Manually trigger the event handler.
  613. // > jQuery(window).trigger( 'hashchange' );
  614. //
  615. // Additional Notes:
  616. //
  617. // * The polling loop and Iframe are not created until at least one handler
  618. // is actually bound to the 'hashchange' event.
  619. // * If you need the bound handler(s) to execute immediately, in cases where
  620. // a location.hash exists on page load, via bookmark or page refresh for
  621. // example, use jQuery(window).hashchange() or the more verbose
  622. // jQuery(window).trigger( 'hashchange' ).
  623. // * The event can be bound before DOM ready, but since it won't be usable
  624. // before then in IE6/7 (due to the necessary Iframe), recommended usage is
  625. // to bind it inside a DOM ready handler.
  626. // Override existing $.event.special.hashchange methods (allowing this plugin
  627. // to be defined after jQuery BBQ in BBQ's source code).
  628. special[ str_hashchange ] = $.extend( special[ str_hashchange ], {
  629. // Called only when the first 'hashchange' event is bound to window.
  630. setup: function() {
  631. // If window.onhashchange is supported natively, there's nothing to do..
  632. if ( supports_onhashchange ) { return false; }
  633. // Otherwise, we need to create our own. And we don't want to call this
  634. // until the user binds to the event, just in case they never do, since it
  635. // will create a polling loop and possibly even a hidden Iframe.
  636. $( fake_onhashchange.start );
  637. },
  638. // Called only when the last 'hashchange' event is unbound from window.
  639. teardown: function() {
  640. // If window.onhashchange is supported natively, there's nothing to do..
  641. if ( supports_onhashchange ) { return false; }
  642. // Otherwise, we need to stop ours (if possible).
  643. $( fake_onhashchange.stop );
  644. }
  645. });
  646. // fake_onhashchange does all the work of triggering the window.onhashchange
  647. // event for browsers that don't natively support it, including creating a
  648. // polling loop to watch for hash changes and in IE 6/7 creating a hidden
  649. // Iframe to enable back and forward.
  650. fake_onhashchange = (function(){
  651. var self = {},
  652. timeout_id,
  653. // Remember the initial hash so it doesn't get triggered immediately.
  654. last_hash = get_fragment(),
  655. fn_retval = function(val){ return val; },
  656. history_set = fn_retval,
  657. history_get = fn_retval;
  658. // Start the polling loop.
  659. self.start = function() {
  660. timeout_id || poll();
  661. };
  662. // Stop the polling loop.
  663. self.stop = function() {
  664. timeout_id && clearTimeout( timeout_id );
  665. timeout_id = undefined;
  666. };
  667. // This polling loop checks every $.fn.hashchange.delay milliseconds to see
  668. // if location.hash has changed, and triggers the 'hashchange' event on
  669. // window when necessary.
  670. function poll() {
  671. var hash = get_fragment(),
  672. history_hash = history_get( last_hash );
  673. if ( hash !== last_hash ) {
  674. history_set( last_hash = hash, history_hash );
  675. $(window).trigger( str_hashchange );
  676. } else if ( history_hash !== last_hash ) {
  677. location.href = location.href.replace( /#.*/, '' ) + history_hash;
  678. }
  679. timeout_id = setTimeout( poll, $.fn[ str_hashchange ].delay );
  680. };
  681. // vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
  682. // vvvvvvvvvvvvvvvvvvv REMOVE IF NOT SUPPORTING IE6/7/8 vvvvvvvvvvvvvvvvvvv
  683. // vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
  684. $.browser.msie && !supports_onhashchange && (function(){
  685. // Not only do IE6/7 need the "magical" Iframe treatment, but so does IE8
  686. // when running in "IE7 compatibility" mode.
  687. var iframe,
  688. iframe_src;
  689. // When the event is bound and polling starts in IE 6/7, create a hidden
  690. // Iframe for history handling.
  691. self.start = function(){
  692. if ( !iframe ) {
  693. iframe_src = $.fn[ str_hashchange ].src;
  694. iframe_src = iframe_src && iframe_src + get_fragment();
  695. // Create hidden Iframe. Attempt to make Iframe as hidden as possible
  696. // by using techniques from http://www.paciellogroup.com/blog/?p=604.
  697. iframe = $('<iframe tabindex="-1" title="empty"/>').hide()
  698. // When Iframe has completely loaded, initialize the history and
  699. // start polling.
  700. .one( 'load', function(){
  701. iframe_src || history_set( get_fragment() );
  702. poll();
  703. })
  704. // Load Iframe src if specified, otherwise nothing.
  705. .attr( 'src', iframe_src || 'javascript:0' )
  706. // Append Iframe after the end of the body to prevent unnecessary
  707. // initial page scrolling (yes, this works).
  708. .insertAfter( 'body' )[0].contentWindow;
  709. // Whenever `document.title` changes, update the Iframe's title to
  710. // prettify the back/next history menu entries. Since IE sometimes
  711. // errors with "Unspecified error" the very first time this is set
  712. // (yes, very useful) wrap this with a try/catch block.
  713. doc.onpropertychange = function(){
  714. try {
  715. if ( event.propertyName === 'title' ) {
  716. iframe.document.title = doc.title;
  717. }
  718. } catch(e) {}
  719. };
  720. }
  721. };
  722. // Override the "stop" method since an IE6/7 Iframe was created. Even
  723. // if there are no longer any bound event handlers, the polling loop
  724. // is still necessary for back/next to work at all!
  725. self.stop = fn_retval;
  726. // Get history by looking at the hidden Iframe's location.hash.
  727. history_get = function() {
  728. return get_fragment( iframe.location.href );
  729. };
  730. // Set a new history item by opening and then closing the Iframe
  731. // document, *then* setting its location.hash. If document.domain has
  732. // been set, update that as well.
  733. history_set = function( hash, history_hash ) {
  734. var iframe_doc = iframe.document,
  735. domain = $.fn[ str_hashchange ].domain;
  736. if ( hash !== history_hash ) {
  737. // Update Iframe with any initial `document.title` that might be set.
  738. iframe_doc.title = doc.title;
  739. // Opening the Iframe's document after it has been closed is what
  740. // actually adds a history entry.
  741. iframe_doc.open();
  742. // Set document.domain for the Iframe document as well, if necessary.
  743. domain && iframe_doc.write( '<script>document.domain="' + domain + '"</script>' );
  744. iframe_doc.close();
  745. // Update the Iframe's hash, for great justice.
  746. iframe.location.hash = hash;
  747. }
  748. };
  749. })();
  750. // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  751. // ^^^^^^^^^^^^^^^^^^^ REMOVE IF NOT SUPPORTING IE6/7/8 ^^^^^^^^^^^^^^^^^^^
  752. // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  753. return self;
  754. })();
  755. })(jQuery,this);
  756. /*!
  757. * jQuery UI Widget @VERSION
  758. *
  759. * Copyright 2010, AUTHORS.txt (http://jqueryui.com/about)
  760. * Dual licensed under the MIT or GPL Version 2 licenses.
  761. * http://jquery.org/license
  762. *
  763. * http://docs.jquery.com/UI/Widget
  764. */
  765. (function( $, undefined ) {
  766. // jQuery 1.4+
  767. if ( $.cleanData ) {
  768. var _cleanData = $.cleanData;
  769. $.cleanData = function( elems ) {
  770. for ( var i = 0, elem; (elem = elems[i]) != null; i++ ) {
  771. $( elem ).triggerHandler( "remove" );
  772. }
  773. _cleanData( elems );
  774. };
  775. } else {
  776. var _remove = $.fn.remove;
  777. $.fn.remove = function( selector, keepData ) {
  778. return this.each(function() {
  779. if ( !keepData ) {
  780. if ( !selector || $.filter( selector, [ this ] ).length ) {
  781. $( "*", this ).add( [ this ] ).each(function() {
  782. $( this ).triggerHandler( "remove" );
  783. });
  784. }
  785. }
  786. return _remove.call( $(this), selector, keepData );
  787. });
  788. };
  789. }
  790. $.widget = function( name, base, prototype ) {
  791. var namespace = name.split( "." )[ 0 ],
  792. fullName;
  793. name = name.split( "." )[ 1 ];
  794. fullName = namespace + "-" + name;
  795. if ( !prototype ) {
  796. prototype = base;
  797. base = $.Widget;
  798. }
  799. // create selector for plugin
  800. $.expr[ ":" ][ fullName ] = function( elem ) {
  801. return !!$.data( elem, name );
  802. };
  803. $[ namespace ] = $[ namespace ] || {};
  804. $[ namespace ][ name ] = function( options, element ) {
  805. // allow instantiation without initializing for simple inheritance
  806. if ( arguments.length ) {
  807. this._createWidget( options, element );
  808. }
  809. };
  810. var basePrototype = new base();
  811. // we need to make the options hash a property directly on the new instance
  812. // otherwise we'll modify the options hash on the prototype that we're
  813. // inheriting from
  814. // $.each( basePrototype, function( key, val ) {
  815. // if ( $.isPlainObject(val) ) {
  816. // basePrototype[ key ] = $.extend( {}, val );
  817. // }
  818. // });
  819. basePrototype.options = $.extend( true, {}, basePrototype.options );
  820. $[ namespace ][ name ].prototype = $.extend( true, basePrototype, {
  821. namespace: namespace,
  822. widgetName: name,
  823. widgetEventPrefix: $[ namespace ][ name ].prototype.widgetEventPrefix || name,
  824. widgetBaseClass: fullName
  825. }, prototype );
  826. $.widget.bridge( name, $[ namespace ][ name ] );
  827. };
  828. $.widget.bridge = function( name, object ) {
  829. $.fn[ name ] = function( options ) {
  830. var isMethodCall = typeof options === "string",
  831. args = Array.prototype.slice.call( arguments, 1 ),
  832. returnValue = this;
  833. // allow multiple hashes to be passed on init
  834. options = !isMethodCall && args.length ?
  835. $.extend.apply( null, [ true, options ].concat(args) ) :
  836. options;
  837. // prevent calls to internal methods
  838. if ( isMethodCall && options.charAt( 0 ) === "_" ) {
  839. return returnValue;
  840. }
  841. if ( isMethodCall ) {
  842. this.each(function() {
  843. var instance = $.data( this, name );
  844. if ( !instance ) {
  845. throw "cannot call methods on " + name + " prior to initialization; " +
  846. "attempted to call method '" + options + "'";
  847. }
  848. if ( !$.isFunction( instance[options] ) ) {
  849. throw "no such method '" + options + "' for " + name + " widget instance";
  850. }
  851. var methodValue = instance[ options ].apply( instance, args );
  852. if ( methodValue !== instance && methodValue !== undefined ) {
  853. returnValue = methodValue;
  854. return false;
  855. }
  856. });
  857. } else {
  858. this.each(function() {
  859. var instance = $.data( this, name );
  860. if ( instance ) {
  861. instance.option( options || {} )._init();
  862. } else {
  863. $.data( this, name, new object( options, this ) );
  864. }
  865. });
  866. }
  867. return returnValue;
  868. };
  869. };
  870. $.Widget = function( options, element ) {
  871. // allow instantiation without initializing for simple inheritance
  872. if ( arguments.length ) {
  873. this._createWidget( options, element );
  874. }
  875. };
  876. $.Widget.prototype = {
  877. widgetName: "widget",
  878. widgetEventPrefix: "",
  879. options: {
  880. disabled: false
  881. },
  882. _createWidget: function( options, element ) {
  883. // $.widget.bridge stores the plugin instance, but we do it anyway
  884. // so that it's stored even before the _create function runs
  885. $.data( element, this.widgetName, this );
  886. this.element = $( element );
  887. this.options = $.extend( true, {},
  888. this.options,
  889. this._getCreateOptions(),
  890. options );
  891. var self = this;
  892. this.element.bind( "remove." + this.widgetName, function() {
  893. self.destroy();
  894. });
  895. this._create();
  896. this._trigger( "create" );
  897. this._init();
  898. },
  899. _getCreateOptions: function() {
  900. var options = {};
  901. if ( $.metadata ) {
  902. options = $.metadata.get( element )[ this.widgetName ];
  903. }
  904. return options;
  905. },
  906. _create: function() {},
  907. _init: function() {},
  908. destroy: function() {
  909. this.element
  910. .unbind( "." + this.widgetName )
  911. .removeData( this.widgetName );
  912. this.widget()
  913. .unbind( "." + this.widgetName )
  914. .removeAttr( "aria-disabled" )
  915. .removeClass(
  916. this.widgetBaseClass + "-disabled " +
  917. "ui-state-disabled" );
  918. },
  919. widget: function() {
  920. return this.element;
  921. },
  922. option: function( key, value ) {
  923. var options = key;
  924. if ( arguments.length === 0 ) {
  925. // don't return a reference to the internal hash
  926. return $.extend( {}, this.options );
  927. }
  928. if (typeof key === "string" ) {
  929. if ( value === undefined ) {
  930. return this.options[ key ];
  931. }
  932. options = {};
  933. options[ key ] = value;
  934. }
  935. this._setOptions( options );
  936. return this;
  937. },
  938. _setOptions: function( options ) {
  939. var self = this;
  940. $.each( options, function( key, value ) {
  941. self._setOption( key, value );
  942. });
  943. return this;
  944. },
  945. _setOption: function( key, value ) {
  946. this.options[ key ] = value;
  947. if ( key === "disabled" ) {
  948. this.widget()
  949. [ value ? "addClass" : "removeClass"](
  950. this.widgetBaseClass + "-disabled" + " " +
  951. "ui-state-disabled" )
  952. .attr( "aria-disabled", value );
  953. }
  954. return this;
  955. },
  956. enable: function() {
  957. return this._setOption( "disabled", false );
  958. },
  959. disable: function() {
  960. return this._setOption( "disabled", true );
  961. },
  962. _trigger: function( type, event, data ) {
  963. var callback = this.options[ type ];
  964. event = $.Event( event );
  965. event.type = ( type === this.widgetEventPrefix ?
  966. type :
  967. this.widgetEventPrefix + type ).toLowerCase();
  968. data = data || {};
  969. // copy original event properties over to the new event
  970. // this would happen if we could call $.event.fix instead of $.Event
  971. // but we don't have a way to force an event to be fixed multiple times
  972. if ( event.originalEvent ) {
  973. for ( var i = $.event.props.length, prop; i; ) {
  974. prop = $.event.props[ --i ];
  975. event[ prop ] = event.originalEvent[ prop ];
  976. }
  977. }
  978. this.element.trigger( event, data );
  979. return !( $.isFunction(callback) &&
  980. callback.call( this.element[0], event, data ) === false ||
  981. event.isDefaultPrevented() );
  982. }
  983. };
  984. })( jQuery );
  985. (function( $, undefined ) {
  986. $.widget( "mobile.widget", {
  987. // decorate the parent _createWidget to trigger `widgetinit` for users
  988. // who wish to do post post `widgetcreate` alterations/additions
  989. //
  990. // TODO create a pull request for jquery ui to trigger this event
  991. // in the original _createWidget
  992. _createWidget: function() {
  993. $.Widget.prototype._createWidget.apply( this, arguments );
  994. this._trigger( 'init' );
  995. },
  996. _getCreateOptions: function() {
  997. var elem = this.element,
  998. options = {};
  999. $.each( this.options, function( option ) {
  1000. var value = elem.jqmData( option.replace( /[A-Z]/g, function( c ) {
  1001. return "-" + c.toLowerCase();
  1002. })
  1003. );
  1004. if ( value !== undefined ) {
  1005. options[ option ] = value;
  1006. }
  1007. });
  1008. return options;
  1009. },
  1010. enhanceWithin: function( target, useKeepNative ) {
  1011. this.enhance( $( this.options.initSelector, $( target )), useKeepNative );
  1012. },
  1013. enhance: function( targets, useKeepNative ) {
  1014. var page, keepNative, $widgetElements = $( targets ), self = this;
  1015. // if ignoreContentEnabled is set to true the framework should
  1016. // only enhance the selected elements when they do NOT have a
  1017. // parent with the data-namespace-ignore attribute
  1018. $widgetElements = $.mobile.enhanceable( $widgetElements );
  1019. if ( useKeepNative && $widgetElements.length ) {
  1020. // TODO remove dependency on the page widget for the keepNative.
  1021. // Currently the keepNative value is defined on the page prototype so
  1022. // the method is as well
  1023. page = $.mobile.closestPageData( $widgetElements );
  1024. keepNative = (page && page.keepNativeSelector()) || "";
  1025. $widgetElements = $widgetElements.not( keepNative );
  1026. }
  1027. $widgetElements[ this.widgetName ]();
  1028. },
  1029. raise: function( msg ) {
  1030. throw "Widget [" + this.widgetName + "]: " + msg;
  1031. }
  1032. });
  1033. })( jQuery );
  1034. (function( $, window, undefined ) {
  1035. var nsNormalizeDict = {};
  1036. // jQuery.mobile configurable options
  1037. $.mobile = $.extend( {}, {
  1038. // Version of the jQuery Mobile Framework
  1039. version: "1.1.0",
  1040. // Namespace used framework-wide for data-attrs. Default is no namespace
  1041. ns: "",
  1042. // Define the url parameter used for referencing widget-generated sub-pages.
  1043. // Translates to to example.html&ui-page=subpageIdentifier
  1044. // hash segment before &ui-page= is used to make Ajax request
  1045. subPageUrlKey: "ui-page",
  1046. // Class assigned to page currently in view, and during transitions
  1047. activePageClass: "ui-page-active",
  1048. // Class used for "active" button state, from CSS framework
  1049. activeBtnClass: "ui-btn-active",
  1050. // Class used for "focus" form element state, from CSS framework
  1051. focusClass: "ui-focus",
  1052. // Automatically handle clicks and form submissions through Ajax, when same-domain
  1053. ajaxEnabled: true,
  1054. // Automatically load and show pages based on location.hash
  1055. hashListeningEnabled: true,
  1056. // disable to prevent jquery from bothering with links
  1057. linkBindingEnabled: true,
  1058. // Set default page transition - 'none' for no transitions
  1059. defaultPageTransition: "fade",
  1060. // Set maximum window width for transitions to apply - 'false' for no limit
  1061. maxTransitionWidth: false,
  1062. // Minimum scroll distance that will be remembered when returning to a page
  1063. minScrollBack: 250,
  1064. // DEPRECATED: the following property is no longer in use, but defined until 2.0 to prevent conflicts
  1065. touchOverflowEnabled: false,
  1066. // Set default dialog transition - 'none' for no transitions
  1067. defaultDialogTransition: "pop",
  1068. // Show loading message during Ajax requests
  1069. // if false, message will not appear, but loading classes will still be toggled on html el
  1070. loadingMessage: "loading",
  1071. // Error response message - appears when an Ajax page request fails
  1072. pageLoadErrorMessage: "Error Loading Page",
  1073. // Should the text be visble in the loading message?
  1074. loadingMessageTextVisible: false,
  1075. // When the text is visible, what theme does the loading box use?
  1076. loadingMessageTheme: "a",
  1077. // For error messages, which theme does the box uses?
  1078. pageLoadErrorMessageTheme: "e",
  1079. //automatically initialize the DOM when it's ready
  1080. autoInitializePage: true,
  1081. pushStateEnabled: true,
  1082. // allows users to opt in to ignoring content by marking a parent element as
  1083. // data-ignored
  1084. ignoreContentEnabled: false,
  1085. // turn of binding to the native orientationchange due to android orientation behavior
  1086. orientationChangeEnabled: true,
  1087. buttonMarkup: {
  1088. hoverDelay: 200
  1089. },
  1090. // TODO might be useful upstream in jquery itself ?
  1091. keyCode: {
  1092. ALT: 18,
  1093. BACKSPACE: 8,
  1094. CAPS_LOCK: 20,
  1095. COMMA: 188,
  1096. COMMAND: 91,
  1097. COMMAND_LEFT: 91, // COMMAND
  1098. COMMAND_RIGHT: 93,
  1099. CONTROL: 17,
  1100. DELETE: 46,
  1101. DOWN: 40,
  1102. END: 35,
  1103. ENTER: 13,
  1104. ESCAPE: 27,
  1105. HOME: 36,
  1106. INSERT: 45,
  1107. LEFT: 37,
  1108. MENU: 93, // COMMAND_RIGHT
  1109. NUMPAD_ADD: 107,
  1110. NUMPAD_DECIMAL: 110,
  1111. NUMPAD_DIVIDE: 111,
  1112. NUMPAD_ENTER: 108,
  1113. NUMPAD_MULTIPLY: 106,
  1114. NUMPAD_SUBTRACT: 109,
  1115. PAGE_DOWN: 34,
  1116. PAGE_UP: 33,
  1117. PERIOD: 190,
  1118. RIGHT: 39,
  1119. SHIFT: 16,
  1120. SPACE: 32,
  1121. TAB: 9,
  1122. UP: 38,
  1123. WINDOWS: 91 // COMMAND
  1124. },
  1125. // Scroll page vertically: scroll to 0 to hide iOS address bar, or pass a Y value
  1126. silentScroll: function( ypos ) {
  1127. if ( $.type( ypos ) !== "number" ) {
  1128. ypos = $.mobile.defaultHomeScroll;
  1129. }
  1130. // prevent scrollstart and scrollstop events
  1131. $.event.special.scrollstart.enabled = false;
  1132. setTimeout(function() {
  1133. window.scrollTo( 0, ypos );
  1134. $( document ).trigger( "silentscroll", { x: 0, y: ypos });
  1135. }, 20 );
  1136. setTimeout(function() {
  1137. $.event.special.scrollstart.enabled = true;
  1138. }, 150 );
  1139. },
  1140. // Expose our cache for testing purposes.
  1141. nsNormalizeDict: nsNormalizeDict,
  1142. // Take a data attribute property, prepend the namespace
  1143. // and then camel case the attribute string. Add the result
  1144. // to our nsNormalizeDict so we don't have to do this again.
  1145. nsNormalize: function( prop ) {
  1146. if ( !prop ) {
  1147. return;
  1148. }
  1149. return nsNormalizeDict[ prop ] || ( nsNormalizeDict[ prop ] = $.camelCase( $.mobile.ns + prop ) );
  1150. },
  1151. getInheritedTheme: function( el, defaultTheme ) {
  1152. // Find the closest parent with a theme class on it. Note that
  1153. // we are not using $.fn.closest() on purpose here because this
  1154. // method gets called quite a bit and we need it to be as fast
  1155. // as possible.
  1156. var e = el[ 0 ],
  1157. ltr = "",
  1158. re = /ui-(bar|body|overlay)-([a-z])\b/,
  1159. c, m;
  1160. while ( e ) {
  1161. var c = e.className || "";
  1162. if ( ( m = re.exec( c ) ) && ( ltr = m[ 2 ] ) ) {
  1163. // We found a parent with a theme class
  1164. // on it so bail from this loop.
  1165. break;
  1166. }
  1167. e = e.parentNode;
  1168. }
  1169. // Return the theme letter we found, if none, return the
  1170. // specified default.
  1171. return ltr || defaultTheme || "a";
  1172. },
  1173. // TODO the following $ and $.fn extensions can/probably should be moved into jquery.mobile.core.helpers
  1174. //
  1175. // Find the closest javascript page element to gather settings data jsperf test
  1176. // http://jsperf.com/single-complex-selector-vs-many-complex-selectors/edit
  1177. // possibly naive, but it shows that the parsing overhead for *just* the page selector vs
  1178. // the page and dialog selector is negligable. This could probably be speed up by
  1179. // doing a similar parent node traversal to the one found in the inherited theme code above
  1180. closestPageData: function( $target ) {
  1181. return $target
  1182. .closest(':jqmData(role="page"), :jqmData(role="dialog")')
  1183. .data("page");
  1184. },
  1185. enhanceable: function( $set ) {
  1186. return this.haveParents( $set, "enhance" );
  1187. },
  1188. hijackable: function( $set ) {
  1189. return this.haveParents( $set, "ajax" );
  1190. },
  1191. haveParents: function( $set, attr ) {
  1192. if( !$.mobile.ignoreContentEnabled ){
  1193. return $set;
  1194. }
  1195. var count = $set.length,
  1196. $newSet = $(),
  1197. e, $element, excluded;
  1198. for ( var i = 0; i < count; i++ ) {
  1199. $element = $set.eq( i );
  1200. excluded = false;
  1201. e = $set[ i ];
  1202. while ( e ) {
  1203. var c = e.getAttribute ? e.getAttribute( "data-" + $.mobile.ns + attr ) : "";
  1204. if ( c === "false" ) {
  1205. excluded = true;
  1206. break;
  1207. }
  1208. e = e.parentNode;
  1209. }
  1210. if ( !excluded ) {
  1211. $newSet = $newSet.add( $element );
  1212. }
  1213. }
  1214. return $newSet;
  1215. }
  1216. }, $.mobile );
  1217. // Mobile version of data and removeData and hasData methods
  1218. // ensures all data is set and retrieved using jQuery Mobile's data namespace
  1219. $.fn.jqmData = function( prop, value ) {
  1220. var result;
  1221. if ( typeof prop != "undefined" ) {
  1222. if ( prop ) {
  1223. prop = $.mobile.nsNormalize( prop );
  1224. }
  1225. result = this.data.apply( this, arguments.length < 2 ? [ prop ] : [ prop, value ] );
  1226. }
  1227. return result;
  1228. };
  1229. $.jqmData = function( elem, prop, value ) {
  1230. var result;
  1231. if ( typeof prop != "undefined" ) {
  1232. result = $.data( elem, prop ? $.mobile.nsNormalize( prop ) : prop, value );
  1233. }
  1234. return result;
  1235. };
  1236. $.fn.jqmRemoveData = function( prop ) {
  1237. return this.removeData( $.mobile.nsNormalize( prop ) );
  1238. };
  1239. $.jqmRemoveData = function( elem, prop ) {
  1240. return $.removeData( elem, $.mobile.nsNormalize( prop ) );
  1241. };
  1242. $.fn.removeWithDependents = function() {
  1243. $.removeWithDependents( this );
  1244. };
  1245. $.removeWithDependents = function( elem ) {
  1246. var $elem = $( elem );
  1247. ( $elem.jqmData('dependents') || $() ).remove();
  1248. $elem.remove();
  1249. };
  1250. $.fn.addDependents = function( newDependents ) {
  1251. $.addDependents( $(this), newDependents );
  1252. };
  1253. $.addDependents = function( elem, newDependents ) {
  1254. var dependents = $(elem).jqmData( 'dependents' ) || $();
  1255. $(elem).jqmData( 'dependents', $.merge(dependents, newDependents) );
  1256. };
  1257. // note that this helper doesn't attempt to handle the callback
  1258. // or setting of an html elements text, its only purpose is
  1259. // to return the html encoded version of the text in all cases. (thus the name)
  1260. $.fn.getEncodedText = function() {
  1261. return $( "<div/>" ).text( $(this).text() ).html();
  1262. };
  1263. // fluent helper function for the mobile namespaced equivalent
  1264. $.fn.jqmEnhanceable = function() {
  1265. return $.mobile.enhanceable( this );
  1266. };
  1267. $.fn.jqmHijackable = function() {
  1268. return $.mobile.hijackable( this );
  1269. };
  1270. // Monkey-patching Sizzle to filter the :jqmData selector
  1271. var oldFind = $.find,
  1272. jqmDataRE = /:jqmData\(([^)]*)\)/g;
  1273. $.find = function( selector, context, ret, extra ) {
  1274. selector = selector.replace( jqmDataRE, "[data-" + ( $.mobile.ns || "" ) + "$1]" );
  1275. return oldFind.call( this, selector, context, ret, extra );
  1276. };
  1277. $.extend( $.find, oldFind );
  1278. $.find.matches = function( expr, set ) {
  1279. return $.find( expr, null, null, set );
  1280. };
  1281. $.find.matchesSelector = function( node, expr ) {
  1282. return $.find( expr, null, null, [ node ] ).length > 0;
  1283. };
  1284. })( jQuery, this );
  1285. (function( $, undefined ) {
  1286. var $window = $( window ),
  1287. $html = $( "html" );
  1288. /* $.mobile.media method: pass a CSS media type or query and get a bool return
  1289. note: this feature relies on actual media query support for media queries, though types will work most anywhere
  1290. examples:
  1291. $.mobile.media('screen') // tests for screen media type
  1292. $.mobile.media('screen and (min-width: 480px)') // tests for screen media type with window width > 480px
  1293. $.mobile.media('@media screen and (-webkit-min-device-pixel-ratio: 2)') // tests for webkit 2x pixel ratio (iPhone 4)
  1294. */
  1295. $.mobile.media = (function() {
  1296. // TODO: use window.matchMedia once at least one UA implements it
  1297. var cache = {},
  1298. testDiv = $( "<div id='jquery-mediatest'>" ),
  1299. fakeBody = $( "<body>" ).append( testDiv );
  1300. return function( query ) {
  1301. if ( !( query in cache ) ) {
  1302. var styleBlock = document.createElement( "style" ),
  1303. cssrule = "@media " + query + " { #jquery-mediatest { position:absolute; } }";
  1304. //must set type for IE!
  1305. styleBlock.type = "text/css";
  1306. if ( styleBlock.styleSheet ){
  1307. styleBlock.styleSheet.cssText = cssrule;
  1308. } else {
  1309. styleBlock.appendChild( document.createTextNode(cssrule) );
  1310. }
  1311. $html.prepend( fakeBody ).prepend( styleBlock );
  1312. cache[ query ] = testDiv.css( "position" ) === "absolute";
  1313. fakeBody.add( styleBlock ).remove();
  1314. }
  1315. return cache[ query ];
  1316. };
  1317. })();
  1318. })(jQuery);
  1319. (function( $, undefined ) {
  1320. var fakeBody = $( "<body>" ).prependTo( "html" ),
  1321. fbCSS = fakeBody[ 0 ].style,
  1322. vendors = [ "Webkit", "Moz", "O" ],
  1323. webos = "palmGetResource" in window, //only used to rule out scrollTop
  1324. operamini = window.operamini && ({}).toString.call( window.operamini ) === "[object OperaMini]",
  1325. bb = window.blackberry; //only used to rule out box shadow, as it's filled opaque on BB
  1326. // thx Modernizr
  1327. function propExists( prop ) {
  1328. var uc_prop = prop.charAt( 0 ).toUpperCase() + prop.substr( 1 ),
  1329. props = ( prop + " " + vendors.join( uc_prop + " " ) + uc_prop ).split( " " );
  1330. for ( var v in props ){
  1331. if ( fbCSS[ props[ v ] ] !== undefined ) {
  1332. return true;
  1333. }
  1334. }
  1335. }
  1336. function validStyle( prop, value, check_vend ) {
  1337. var div = document.createElement('div'),
  1338. uc = function( txt ) {
  1339. return txt.charAt( 0 ).toUpperCase() + txt.substr( 1 )
  1340. },
  1341. vend_pref = function( vend ) {
  1342. return "-" + vend.charAt( 0 ).toLowerCase() + vend.substr( 1 ) + "-";
  1343. },
  1344. check_style = function( vend ) {
  1345. var vend_prop = vend_pref( vend ) + prop + ": " + value + ";",
  1346. uc_vend = uc( vend ),
  1347. propStyle = uc_vend + uc( prop );
  1348. div.setAttribute( "style", vend_prop );
  1349. if( !!div.style[ propStyle ] ) {
  1350. ret = true;
  1351. }
  1352. },
  1353. check_vends = check_vend ? [ check_vend ] : vendors,
  1354. ret;
  1355. for( i = 0; i < check_vends.length; i++ ) {
  1356. check_style( check_vends[i] );
  1357. }
  1358. return !!ret;
  1359. }
  1360. // Thanks to Modernizr src for this test idea. `perspective` check is limited to Moz to prevent a false positive for 3D transforms on Android.
  1361. function transform3dTest() {
  1362. var prop = "transform-3d";
  1363. return validStyle( 'perspective', '10px', 'moz' ) || $.mobile.media( "(-" + vendors.join( "-" + prop + "),(-" ) + "-" + prop + "),(" + prop + ")" );
  1364. }
  1365. // Test for dynamic-updating base tag support ( allows us to avoid href,src attr rewriting )
  1366. function baseTagTest() {
  1367. var fauxBase = location.protocol + "//" + location.host + location.pathname + "ui-dir/",
  1368. base = $( "head base" ),
  1369. fauxEle = null,
  1370. href = "",
  1371. link, rebase;
  1372. if ( !base.length ) {
  1373. base = fauxEle = $( "<base>", { "href": fauxBase }).appendTo( "head" );
  1374. } else {
  1375. href = base.attr( "href" );
  1376. }
  1377. link = $( "<a href='testurl' />" ).prependTo( fakeBody );
  1378. rebase = link[ 0 ].href;
  1379. base[ 0 ].href = href || location.pathname;
  1380. if ( fauxEle ) {
  1381. fauxEle.remove();
  1382. }
  1383. return rebase.indexOf( fauxBase ) === 0;
  1384. }
  1385. // non-UA-based IE version check by James Padolsey, modified by jdalton - from http://gist.github.com/527683
  1386. // allows for inclusion of IE 6+, including Windows Mobile 7
  1387. $.extend( $.mobile, { browser: {} } );
  1388. $.mobile.browser.ie = (function() {
  1389. var v = 3,
  1390. div = document.createElement( "div" ),
  1391. a = div.all || [];
  1392. // added {} to silence closure compiler warnings. registering my dislike of all things
  1393. // overly clever here for future reference
  1394. while ( div.innerHTML = "<!--[if gt IE " + ( ++v ) + "]><br><![endif]-->", a[ 0 ] ){};
  1395. return v > 4 ? v : !v;
  1396. })();
  1397. $.extend( $.support, {
  1398. orientation: "orientation" in window && "onorientationchange" in window,
  1399. touch: "ontouchend" in document,
  1400. cssTransitions: "WebKitTransitionEvent" in window || validStyle( 'transition', 'height 100ms linear' ),
  1401. pushState: "pushState" in history && "replaceState" in history,
  1402. mediaquery: $.mobile.media( "only all" ),
  1403. cssPseudoElement: !!propExists( "content" ),
  1404. touchOverflow: !!propExists( "overflowScrolling" ),
  1405. cssTransform3d: transform3dTest(),
  1406. boxShadow: !!propExists( "boxShadow" ) && !bb,
  1407. scrollTop: ( "pageXOffset" in window || "scrollTop" in document.documentElement || "scrollTop" in fakeBody[ 0 ] ) && !webos && !operamini,
  1408. dynamicBaseTag: baseTagTest()
  1409. });
  1410. fakeBody.remove();
  1411. // $.mobile.ajaxBlacklist is used to override ajaxEnabled on platforms that have known conflicts with hash history updates (BB5, Symbian)
  1412. // or that generally work better browsing in regular http for full page refreshes (Opera Mini)
  1413. // Note: This detection below is used as a last resort.
  1414. // We recommend only using these detection methods when all other more reliable/forward-looking approaches are not possible
  1415. var nokiaLTE7_3 = (function(){
  1416. var ua = window.navigator.userAgent;
  1417. //The following is an attempt to match Nokia browsers that are running Symbian/s60, with webkit, version 7.3 or older
  1418. return ua.indexOf( "Nokia" ) > -1 &&
  1419. ( ua.indexOf( "Symbian/3" ) > -1 || ua.indexOf( "Series60/5" ) > -1 ) &&
  1420. ua.indexOf( "AppleWebKit" ) > -1 &&
  1421. ua.match( /(BrowserNG|NokiaBrowser)\/7\.[0-3]/ );
  1422. })();
  1423. // Support conditions that must be met in order to proceed
  1424. // default enhanced qualifications are media query support OR IE 7+
  1425. $.mobile.gradeA = function(){
  1426. return $.support.mediaquery || $.mobile.browser.ie && $.mobile.browser.ie >= 7;
  1427. };
  1428. $.mobile.ajaxBlacklist =
  1429. // BlackBerry browsers, pre-webkit
  1430. window.blackberry && !window.WebKitPoint ||
  1431. // Opera Mini
  1432. operamini ||
  1433. // Symbian webkits pre 7.3
  1434. nokiaLTE7_3;
  1435. // Lastly, this workaround is the only way we've found so far to get pre 7.3 Symbian webkit devices
  1436. // to render the stylesheets when they're referenced before this script, as we'd recommend doing.
  1437. // This simply reappends the CSS in place, which for some reason makes it apply
  1438. if ( nokiaLTE7_3 ) {
  1439. $(function() {
  1440. $( "head link[rel='stylesheet']" ).attr( "rel", "alternate stylesheet" ).attr( "rel", "stylesheet" );
  1441. });
  1442. }
  1443. // For ruling out shadows via css
  1444. if ( !$.support.boxShadow ) {
  1445. $( "html" ).addClass( "ui-mobile-nosupport-boxshadow" );
  1446. }
  1447. })( jQuery );
  1448. (function( $, window, undefined ) {
  1449. // add new event shortcuts
  1450. $.each( ( "touchstart touchmove touchend orientationchange throttledresize " +
  1451. "tap taphold swipe swipeleft swiperight scrollstart scrollstop" ).split( " " ), function( i, name ) {
  1452. $.fn[ name ] = function( fn ) {
  1453. return fn ? this.bind( name, fn ) : this.trigger( name );
  1454. };
  1455. $.attrFn[ name ] = true;
  1456. });
  1457. var supportTouch = $.support.touch,
  1458. scrollEvent = "touchmove scroll",
  1459. touchStartEvent = supportTouch ? "touchstart" : "mousedown",
  1460. touchStopEvent = supportTouch ? "touchend" : "mouseup",
  1461. touchMoveEvent = supportTouch ? "touchmove" : "mousemove";
  1462. function triggerCustomEvent( obj, eventType, event ) {
  1463. var originalType = event.type;
  1464. event.type = eventType;
  1465. $.event.handle.call( obj, event );
  1466. event.type = originalType;
  1467. }
  1468. // also handles scrollstop
  1469. $.event.special.scrollstart = {
  1470. enabled: true,
  1471. setup: function() {
  1472. var thisObject = this,
  1473. $this = $( thisObject ),
  1474. scrolling,
  1475. timer;
  1476. function trigger( event, state ) {
  1477. scrolling = state;
  1478. triggerCustomEvent( thisObject, scrolling ? "scrollstart" : "scrollstop", event );
  1479. }
  1480. // iPhone triggers scroll after a small delay; use touchmove instead
  1481. $this.bind( scrollEvent, function( event ) {
  1482. if ( !$.event.special.scrollstart.enabled ) {
  1483. return;
  1484. }
  1485. if ( !scrolling ) {
  1486. trigger( event, true );
  1487. }
  1488. clearTimeout( timer );
  1489. timer = setTimeout(function() {
  1490. trigger( event, false );
  1491. }, 50 );
  1492. });
  1493. }
  1494. };
  1495. // also handles taphold
  1496. $.event.special.tap = {
  1497. setup: function() {
  1498. var thisObject = this,
  1499. $this = $( thisObject );
  1500. $this.bind( "vmousedown", function( event ) {
  1501. if ( event.which && event.which !== 1 ) {
  1502. return false;
  1503. }
  1504. var origTarget = event.target,
  1505. origEvent = event.originalEvent,
  1506. timer;
  1507. function clearTapTimer() {
  1508. clearTimeout( timer );
  1509. }
  1510. function clearTapHandlers() {
  1511. clearTapTimer();
  1512. $this.unbind( "vclick", clickHandler )
  1513. .unbind( "vmouseup", clearTapTimer );
  1514. $( document ).unbind( "vmousecancel", clearTapHandlers );
  1515. }
  1516. function clickHandler(event) {
  1517. clearTapHandlers();
  1518. // ONLY trigger a 'tap' event if the start target is
  1519. // the same as the stop target.
  1520. if ( origTarget == event.target ) {
  1521. triggerCustomEvent( thisObject, "tap", event );
  1522. }
  1523. }
  1524. $this.bind( "vmouseup", clearTapTimer )
  1525. .bind( "vclick", clickHandler );
  1526. $( document ).bind( "vmousecancel", clearTapHandlers );
  1527. timer = setTimeout(function() {
  1528. triggerCustomEvent( thisObject, "taphold", $.Event( "taphold", { target: origTarget } ) );
  1529. }, 750 );
  1530. });
  1531. }
  1532. };
  1533. // also handles swipeleft, swiperight
  1534. $.event.special.swipe = {
  1535. scrollSupressionThreshold: 10, // More than this horizontal displacement, and we will suppress scrolling.
  1536. durationThreshold: 1000, // More time than this, and it isn't a swipe.
  1537. horizontalDistanceThreshold: 30, // Swipe horizontal displacement must be more than this.
  1538. verticalDistanceThreshold: 75, // Swipe vertical displacement must be less than this.
  1539. setup: function() {
  1540. var thisObject = this,
  1541. $this = $( thisObject );
  1542. $this.bind( touchStartEvent, function( event ) {
  1543. var data = event.originalEvent.touches ?
  1544. event.originalEvent.touches[ 0 ] : event,
  1545. start = {
  1546. time: ( new Date() ).getTime(),
  1547. coords: [ data.pageX, data.pageY ],
  1548. origin: $( event.target )
  1549. },
  1550. stop;
  1551. function moveHandler( event ) {
  1552. if ( !start ) {
  1553. return;
  1554. }
  1555. var data = event.originalEvent.touches ?
  1556. event.originalEvent.touches[ 0 ] : event;
  1557. stop = {
  1558. time: ( new Date() ).getTime(),
  1559. coords: [ data.pageX, data.pageY ]
  1560. };
  1561. // prevent scrolling
  1562. if ( Math.abs( start.coords[ 0 ] - stop.coords[ 0 ] ) > $.event.special.swipe.scrollSupressionThreshold ) {
  1563. event.preventDefault();
  1564. }
  1565. }
  1566. $this.bind( touchMoveEvent, moveHandler )
  1567. .one( touchStopEvent, function( event ) {
  1568. $this.unbind( touchMoveEvent, moveHandler );
  1569. if ( start && stop ) {
  1570. if ( stop.time - start.time < $.event.special.swipe.durationThreshold &&
  1571. Math.abs( start.coords[ 0 ] - stop.coords[ 0 ] ) > $.event.special.swipe.horizontalDistanceThreshold &&
  1572. Math.abs( start.coords[ 1 ] - stop.coords[ 1 ] ) < $.event.special.swipe.verticalDistanceThreshold ) {
  1573. start.origin.trigger( "swipe" )
  1574. .trigger( start.coords[0] > stop.coords[ 0 ] ? "swipeleft" : "swiperight" );
  1575. }
  1576. }
  1577. start = stop = undefined;
  1578. });
  1579. });
  1580. }
  1581. };
  1582. (function( $, window ) {
  1583. // "Cowboy" Ben Alman
  1584. var win = $( window ),
  1585. special_event,
  1586. get_orientation,
  1587. last_orientation,
  1588. initial_orientation_is_landscape,
  1589. initial_orientation_is_default,
  1590. portrait_map = { "0": true, "180": true };
  1591. // It seems that some device/browser vendors use window.orientation values 0 and 180 to
  1592. // denote the "default" orientation. For iOS devices, and most other smart-phones tested,
  1593. // the default orientation is always "portrait", but in some Android and RIM based tablets,
  1594. // the default orientation is "landscape". The following code attempts to use the window
  1595. // dimensions to figure out what the current orientation is, and then makes adjustments
  1596. // to the to the portrait_map if necessary, so that we can properly decode the
  1597. // window.orientation value whenever get_orientation() is called.
  1598. //
  1599. // Note that we used to use a media query to figure out what the orientation the browser
  1600. // thinks it is in:
  1601. //
  1602. // initial_orientation_is_landscape = $.mobile.media("all and (orientation: landscape)");
  1603. //
  1604. // but there was an iPhone/iPod Touch bug beginning with iOS 4.2, up through iOS 5.1,
  1605. // where the browser *ALWAYS* applied the landscape media query. This bug does not
  1606. // happen on iPad.
  1607. if ( $.support.orientation ) {
  1608. // Check the window width and height to figure out what the current orientation
  1609. // of the device is at this moment. Note that we've initialized the portrait map
  1610. // values to 0 and 180, *AND* we purposely check for landscape so that if we guess
  1611. // wrong, , we default to the assumption that portrait is the default orientation.
  1612. // We use a threshold check below because on some platforms like iOS, the iPhone
  1613. // form-factor can report a larger width than height if the user turns on the
  1614. // developer console. The actual threshold value is somewhat arbitrary, we just
  1615. // need to make sure it is large enough to exclude the developer console case.
  1616. var ww = window.innerWidth || $( window ).width(),
  1617. wh = window.innerHeight || $( window ).height(),
  1618. landscape_threshold = 50;
  1619. initial_orientation_is_landscape = ww > wh && ( ww - wh ) > landscape_threshold;
  1620. // Now check to see if the current window.orientation is 0 or 180.
  1621. initial_orientation_is_default = portrait_map[ window.orientation ];
  1622. // If the initial orientation is landscape, but window.orientation reports 0 or 180, *OR*
  1623. // if the initial orientation is portrait, but window.orientation reports 90 or -90, we
  1624. // need to flip our portrait_map values because landscape is the default orientation for
  1625. // this device/browser.
  1626. if ( ( initial_orientation_is_landscape && initial_orientation_is_default ) || ( !initial_orientation_is_landscape && !initial_orientation_is_default ) ) {
  1627. portrait_map = { "-90": true, "90": true };
  1628. }
  1629. }
  1630. $.event.special.orientationchange = special_event = {
  1631. setup: function() {
  1632. // If the event is supported natively, return false so that jQuery
  1633. // will bind to the event using DOM methods.
  1634. if ( $.support.orientation && $.mobile.orientationChangeEnabled ) {
  1635. return false;
  1636. }
  1637. // Get the current orientation to avoid initial double-triggering.
  1638. last_orientation = get_orientation();
  1639. // Because the orientationchange event doesn't exist, simulate the
  1640. // event by testing window dimensions on resize.
  1641. win.bind( "throttledresize", handler );
  1642. },
  1643. teardown: function(){
  1644. // If the event is not supported natively, return false so that
  1645. // jQuery will unbind the event using DOM methods.
  1646. if ( $.support.orientation && $.mobile.orientationChangeEnabled ) {
  1647. return false;
  1648. }
  1649. // Because the orientationchange event doesn't exist, unbind the
  1650. // resize event handler.
  1651. win.unbind( "throttledresize", handler );
  1652. },
  1653. add: function( handleObj ) {
  1654. // Save a reference to the bound event handler.
  1655. var old_handler = handleObj.handler;
  1656. handleObj.handler = function( event ) {
  1657. // Modify event object, adding the .orientation property.
  1658. event.orientation = get_orientation();
  1659. // Call the originally-bound event handler and return its result.
  1660. return old_handler.apply( this, arguments );
  1661. };
  1662. }
  1663. };
  1664. // If the event is not supported natively, this handler will be bound to
  1665. // the window resize event to simulate the orientationchange event.
  1666. function handler() {
  1667. // Get the current orientation.
  1668. var orientation = get_orientation();
  1669. if ( orientation !== last_orientation ) {
  1670. // The orientation has changed, so trigger the orientationchange event.
  1671. last_orientation = orientation;
  1672. win.trigger( "orientationchange" );
  1673. }
  1674. }
  1675. // Get the current page orientation. This method is exposed publicly, should it
  1676. // be needed, as jQuery.event.special.orientationchange.orientation()
  1677. $.event.special.orientationchange.orientation = get_orientation = function() {
  1678. var isPortrait = true, elem = document.documentElement;
  1679. // prefer window orientation to the calculation based on screensize as
  1680. // the actual screen resize takes place before or after the orientation change event
  1681. // has been fired depending on implementation (eg android 2.3 is before, iphone after).
  1682. // More testing is required to determine if a more reliable method of determining the new screensize
  1683. // is possible when orientationchange is fired. (eg, use media queries + element + opacity)
  1684. if ( $.support.orientation ) {
  1685. // if the window orientation registers as 0 or 180 degrees report
  1686. // portrait, otherwise landscape
  1687. isPortrait = portrait_map[ window.orientation ];
  1688. } else {
  1689. isPortrait = elem && elem.clientWidth / elem.clientHeight < 1.1;
  1690. }
  1691. return isPortrait ? "portrait" : "landscape";
  1692. };
  1693. })( jQuery, window );
  1694. // throttled resize event
  1695. (function() {
  1696. $.event.special.throttledresize = {
  1697. setup: function() {
  1698. $( this ).bind( "resize", handler );
  1699. },
  1700. teardown: function(){
  1701. $( this ).unbind( "resize", handler );
  1702. }
  1703. };
  1704. var throttle = 250,
  1705. handler = function() {
  1706. curr = ( new Date() ).getTime();
  1707. diff = curr - lastCall;
  1708. if ( diff >= throttle ) {
  1709. lastCall = curr;
  1710. $( this ).trigger( "throttledresize" );
  1711. } else {
  1712. if ( heldCall ) {
  1713. clearTimeout( heldCall );
  1714. }
  1715. // Promise a held call will still execute
  1716. heldCall = setTimeout( handler, throttle - diff );
  1717. }
  1718. },
  1719. lastCall = 0,
  1720. heldCall,
  1721. curr,
  1722. diff;
  1723. })();
  1724. $.each({
  1725. scrollstop: "scrollstart",
  1726. taphold: "tap",
  1727. swipeleft: "swipe",
  1728. swiperight: "swipe"
  1729. }, function( event, sourceEvent ) {
  1730. $.event.special[ event ] = {
  1731. setup: function() {
  1732. $( this ).bind( sourceEvent, $.noop );
  1733. }
  1734. };
  1735. });
  1736. })( jQuery, this );
  1737. (function( $, undefined ) {
  1738. $.widget( "mobile.page", $.mobile.widget, {
  1739. options: {
  1740. theme: "c",
  1741. domCache: false,
  1742. keepNativeDefault: ":jqmData(role='none'), :jqmData(role='nojs')"
  1743. },
  1744. _create: function() {
  1745. var self = this;
  1746. // if false is returned by the callbacks do not create the page
  1747. if( self._trigger( "beforecreate" ) === false ){
  1748. return false;
  1749. }
  1750. self.element
  1751. .attr( "tabindex", "0" )
  1752. .addClass( "ui-page ui-body-" + self.options.theme )
  1753. .bind( "pagebeforehide", function(){
  1754. self.removeContainerBackground();
  1755. } )
  1756. .bind( "pagebeforeshow", function(){
  1757. self.setContainerBackground();
  1758. } );
  1759. },
  1760. removeContainerBackground: function(){
  1761. $.mobile.pageContainer.removeClass( "ui-overlay-" + $.mobile.getInheritedTheme( this.element.parent() ) );
  1762. },
  1763. // set the page container background to the page theme
  1764. setContainerBackground: function( theme ){
  1765. if( this.options.theme ){
  1766. $.mobile.pageContainer.addClass( "ui-overlay-" + ( theme || this.options.theme ) );
  1767. }
  1768. },
  1769. keepNativeSelector: function() {
  1770. var options = this.options,
  1771. keepNativeDefined = options.keepNative && $.trim(options.keepNative);
  1772. if( keepNativeDefined && options.keepNative !== options.keepNativeDefault ){
  1773. return [options.keepNative, options.keepNativeDefault].join(", ");
  1774. }
  1775. return options.keepNativeDefault;
  1776. }
  1777. });
  1778. })( jQuery );
  1779. (function( $, window, undefined ) {
  1780. var createHandler = function( sequential ){
  1781. // Default to sequential
  1782. if( sequential === undefined ){
  1783. sequential = true;
  1784. }
  1785. return function( name, reverse, $to, $from ) {
  1786. var deferred = new $.Deferred(),
  1787. reverseClass = reverse ? " reverse" : "",
  1788. active = $.mobile.urlHistory.getActive(),
  1789. toScroll = active.lastScroll || $.mobile.defaultHomeScroll,
  1790. screenHeight = $.mobile.getScreenHeight(),
  1791. maxTransitionOverride = $.mobile.maxTransitionWidth !== false && $( window ).width() > $.mobile.maxTransitionWidth,
  1792. none = !$.support.cssTransitions || maxTransitionOverride || !name || name === "none",
  1793. toggleViewportClass = function(){
  1794. $.mobile.pageContainer.toggleClass( "ui-mobile-viewport-transitioning viewport-" + name );
  1795. },
  1796. scrollPage = function(){
  1797. // By using scrollTo instead of silentScroll, we can keep things better in order
  1798. // Just to be precautios, disable scrollstart listening like silentScroll would
  1799. $.event.special.scrollstart.enabled = false;
  1800. window.scrollTo( 0, toScroll );
  1801. // reenable scrollstart listening like silentScroll would
  1802. setTimeout(function() {
  1803. $.event.special.scrollstart.enabled = true;
  1804. }, 150 );
  1805. },
  1806. cleanFrom = function(){
  1807. $from
  1808. .removeClass( $.mobile.activePageClass + " out in reverse " + name )
  1809. .height( "" );
  1810. },
  1811. startOut = function(){
  1812. // if it's not sequential, call the doneOut transition to start the TO page animating in simultaneously
  1813. if( !sequential ){
  1814. doneOut();
  1815. }
  1816. else {
  1817. $from.animationComplete( doneOut );
  1818. }
  1819. // Set the from page's height and start it transitioning out
  1820. // Note: setting an explicit height helps eliminate tiling in the transitions
  1821. $from
  1822. .height( screenHeight + $(window ).scrollTop() )
  1823. .addClass( name + " out" + reverseClass );
  1824. },
  1825. doneOut = function() {
  1826. if ( $from && sequential ) {
  1827. cleanFrom();
  1828. }
  1829. startIn();
  1830. },
  1831. startIn = function(){
  1832. $to.addClass( $.mobile.activePageClass );
  1833. // Send focus to page as it is now display: block
  1834. $.mobile.focusPage( $to );
  1835. // Set to page height
  1836. $to.height( screenHeight + toScroll );
  1837. scrollPage();
  1838. if( !none ){
  1839. $to.animationComplete( doneIn );
  1840. }
  1841. $to.addClass( name + " in" + reverseClass );
  1842. if( none ){
  1843. doneIn();
  1844. }
  1845. },
  1846. doneIn = function() {
  1847. if ( !sequential ) {
  1848. if( $from ){
  1849. cleanFrom();
  1850. }
  1851. }
  1852. $to
  1853. .removeClass( "out in reverse " + name )
  1854. .height( "" );
  1855. toggleViewportClass();
  1856. // In some browsers (iOS5), 3D transitions block the ability to scroll to the desired location during transition
  1857. // This ensures we jump to that spot after the fact, if we aren't there already.
  1858. if( $( window ).scrollTop() !== toScroll ){
  1859. scrollPage();
  1860. }
  1861. deferred.resolve( name, reverse, $to, $from, true );
  1862. };
  1863. toggleViewportClass();
  1864. if ( $from && !none ) {
  1865. startOut();
  1866. }
  1867. else {
  1868. doneOut();
  1869. }
  1870. return deferred.promise();
  1871. };
  1872. }
  1873. // generate the handlers from the above
  1874. var sequentialHandler = createHandler(),
  1875. simultaneousHandler = createHandler( false );
  1876. // Make our transition handler the public default.
  1877. $.mobile.defaultTransitionHandler = sequentialHandler;
  1878. //transition handler dictionary for 3rd party transitions
  1879. $.mobile.transitionHandlers = {
  1880. "default": $.mobile.defaultTransitionHandler,
  1881. "sequential": sequentialHandler,
  1882. "simultaneous": simultaneousHandler
  1883. };
  1884. $.mobile.transitionFallbacks = {};
  1885. })( jQuery, this );
  1886. ( function( $, undefined ) {
  1887. //define vars for interal use
  1888. var $window = $( window ),
  1889. $html = $( 'html' ),
  1890. $head = $( 'head' ),
  1891. //url path helpers for use in relative url management
  1892. path = {
  1893. // This scary looking regular expression parses an absolute URL or its relative
  1894. // variants (protocol, site, document, query, and hash), into the various
  1895. // components (protocol, host, path, query, fragment, etc that make up the
  1896. // URL as well as some other commonly used sub-parts. When used with RegExp.exec()
  1897. // or String.match, it parses the URL into a results array that looks like this:
  1898. //
  1899. // [0]: http://jblas:password@mycompany.com:8080/mail/inbox?msg=1234&type=unread#msg-content
  1900. // [1]: http://jblas:password@mycompany.com:8080/mail/inbox?msg=1234&type=unread
  1901. // [2]: http://jblas:password@mycompany.com:8080/mail/inbox
  1902. // [3]: http://jblas:password@mycompany.com:8080
  1903. // [4]: http:
  1904. // [5]: //
  1905. // [6]: jblas:password@mycompany.com:8080
  1906. // [7]: jblas:password
  1907. // [8]: jblas
  1908. // [9]: password
  1909. // [10]: mycompany.com:8080
  1910. // [11]: mycompany.com
  1911. // [12]: 8080
  1912. // [13]: /mail/inbox
  1913. // [14]: /mail/
  1914. // [15]: inbox
  1915. // [16]: ?msg=1234&type=unread
  1916. // [17]: #msg-content
  1917. //
  1918. urlParseRE: /^(((([^:\/#\?]+:)?(?:(\/\/)((?:(([^:@\/#\?]+)(?:\:([^:@\/#\?]+))?)@)?(([^:\/#\?\]\[]+|\[[^\/\]@#?]+\])(?:\:([0-9]+))?))?)?)?((\/?(?:[^\/\?#]+\/+)*)([^\?#]*)))?(\?[^#]+)?)(#.*)?/,
  1919. //Parse a URL into a structure that allows easy access to
  1920. //all of the URL components by name.
  1921. parseUrl: function( url ) {
  1922. // If we're passed an object, we'll assume that it is
  1923. // a parsed url object and just return it back to the caller.
  1924. if ( $.type( url ) === "object" ) {
  1925. return url;
  1926. }
  1927. var matches = path.urlParseRE.exec( url || "" ) || [];
  1928. // Create an object that allows the caller to access the sub-matches
  1929. // by name. Note that IE returns an empty string instead of undefined,
  1930. // like all other browsers do, so we normalize everything so its consistent
  1931. // no matter what browser we're running on.
  1932. return {
  1933. href: matches[ 0 ] || "",
  1934. hrefNoHash: matches[ 1 ] || "",
  1935. hrefNoSearch: matches[ 2 ] || "",
  1936. domain: matches[ 3 ] || "",
  1937. protocol: matches[ 4 ] || "",
  1938. doubleSlash: matches[ 5 ] || "",
  1939. authority: matches[ 6 ] || "",
  1940. username: matches[ 8 ] || "",
  1941. password: matches[ 9 ] || "",
  1942. host: matches[ 10 ] || "",
  1943. hostname: matches[ 11 ] || "",
  1944. port: matches[ 12 ] || "",
  1945. pathname: matches[ 13 ] || "",
  1946. directory: matches[ 14 ] || "",
  1947. filename: matches[ 15 ] || "",
  1948. search: matches[ 16 ] || "",
  1949. hash: matches[ 17 ] || ""
  1950. };
  1951. },
  1952. //Turn relPath into an asbolute path. absPath is
  1953. //an optional absolute path which describes what
  1954. //relPath is relative to.
  1955. makePathAbsolute: function( relPath, absPath ) {
  1956. if ( relPath && relPath.charAt( 0 ) === "/" ) {
  1957. return relPath;
  1958. }
  1959. relPath = relPath || "";
  1960. absPath = absPath ? absPath.replace( /^\/|(\/[^\/]*|[^\/]+)$/g, "" ) : "";
  1961. var absStack = absPath ? absPath.split( "/" ) : [],
  1962. relStack = relPath.split( "/" );
  1963. for ( var i = 0; i < relStack.length; i++ ) {
  1964. var d = relStack[ i ];
  1965. switch ( d ) {
  1966. case ".":
  1967. break;
  1968. case "..":
  1969. if ( absStack.length ) {
  1970. absStack.pop();
  1971. }
  1972. break;
  1973. default:
  1974. absStack.push( d );
  1975. break;
  1976. }
  1977. }
  1978. return "/" + absStack.join( "/" );
  1979. },
  1980. //Returns true if both urls have the same domain.
  1981. isSameDomain: function( absUrl1, absUrl2 ) {
  1982. return path.parseUrl( absUrl1 ).domain === path.parseUrl( absUrl2 ).domain;
  1983. },
  1984. //Returns true for any relative variant.
  1985. isRelativeUrl: function( url ) {
  1986. // All relative Url variants have one thing in common, no protocol.
  1987. return path.parseUrl( url ).protocol === "";
  1988. },
  1989. //Returns true for an absolute url.
  1990. isAbsoluteUrl: function( url ) {
  1991. return path.parseUrl( url ).protocol !== "";
  1992. },
  1993. //Turn the specified realtive URL into an absolute one. This function
  1994. //can handle all relative variants (protocol, site, document, query, fragment).
  1995. makeUrlAbsolute: function( relUrl, absUrl ) {
  1996. if ( !path.isRelativeUrl( relUrl ) ) {
  1997. return relUrl;
  1998. }
  1999. var relObj = path.parseUrl( relUrl ),
  2000. absObj = path.parseUrl( absUrl ),
  2001. protocol = relObj.protocol || absObj.protocol,
  2002. doubleSlash = relObj.protocol ? relObj.doubleSlash : ( relObj.doubleSlash || absObj.doubleSlash ),
  2003. authority = relObj.authority || absObj.authority,
  2004. hasPath = relObj.pathname !== "",
  2005. pathname = path.makePathAbsolute( relObj.pathname || absObj.filename, absObj.pathname ),
  2006. search = relObj.search || ( !hasPath && absObj.search ) || "",
  2007. hash = relObj.hash;
  2008. return protocol + doubleSlash + authority + pathname + search + hash;
  2009. },
  2010. //Add search (aka query) params to the specified url.
  2011. addSearchParams: function( url, params ) {
  2012. var u = path.parseUrl( url ),
  2013. p = ( typeof params === "object" ) ? $.param( params ) : params,
  2014. s = u.search || "?";
  2015. return u.hrefNoSearch + s + ( s.charAt( s.length - 1 ) !== "?" ? "&" : "" ) + p + ( u.hash || "" );
  2016. },
  2017. convertUrlToDataUrl: function( absUrl ) {
  2018. var u = path.parseUrl( absUrl );
  2019. if ( path.isEmbeddedPage( u ) ) {
  2020. // For embedded pages, remove the dialog hash key as in getFilePath(),
  2021. // otherwise the Data Url won't match the id of the embedded Page.
  2022. return u.hash.split( dialogHashKey )[0].replace( /^#/, "" );
  2023. } else if ( path.isSameDomain( u, documentBase ) ) {
  2024. return u.hrefNoHash.replace( documentBase.domain, "" );
  2025. }
  2026. return absUrl;
  2027. },
  2028. //get path from current hash, or from a file path
  2029. get: function( newPath ) {
  2030. if( newPath === undefined ) {
  2031. newPath = location.hash;
  2032. }
  2033. return path.stripHash( newPath ).replace( /[^\/]*\.[^\/*]+$/, '' );
  2034. },
  2035. //return the substring of a filepath before the sub-page key, for making a server request
  2036. getFilePath: function( path ) {
  2037. var splitkey = '&' + $.mobile.subPageUrlKey;
  2038. return path && path.split( splitkey )[0].split( dialogHashKey )[0];
  2039. },
  2040. //set location hash to path
  2041. set: function( path ) {
  2042. location.hash = path;
  2043. },
  2044. //test if a given url (string) is a path
  2045. //NOTE might be exceptionally naive
  2046. isPath: function( url ) {
  2047. return ( /\// ).test( url );
  2048. },
  2049. //return a url path with the window's location protocol/hostname/pathname removed
  2050. clean: function( url ) {
  2051. return url.replace( documentBase.domain, "" );
  2052. },
  2053. //just return the url without an initial #
  2054. stripHash: function( url ) {
  2055. return url.replace( /^#/, "" );
  2056. },
  2057. //remove the preceding hash, any query params, and dialog notations
  2058. cleanHash: function( hash ) {
  2059. return path.stripHash( hash.replace( /\?.*$/, "" ).replace( dialogHashKey, "" ) );
  2060. },
  2061. //check whether a url is referencing the same domain, or an external domain or different protocol
  2062. //could be mailto, etc
  2063. isExternal: function( url ) {
  2064. var u = path.parseUrl( url );
  2065. return u.protocol && u.domain !== documentUrl.domain ? true : false;
  2066. },
  2067. hasProtocol: function( url ) {
  2068. return ( /^(:?\w+:)/ ).test( url );
  2069. },
  2070. //check if the specified url refers to the first page in the main application document.
  2071. isFirstPageUrl: function( url ) {
  2072. // We only deal with absolute paths.
  2073. var u = path.parseUrl( path.makeUrlAbsolute( url, documentBase ) ),
  2074. // Does the url have the same path as the document?
  2075. samePath = u.hrefNoHash === documentUrl.hrefNoHash || ( documentBaseDiffers && u.hrefNoHash === documentBase.hrefNoHash ),
  2076. // Get the first page element.
  2077. fp = $.mobile.firstPage,
  2078. // Get the id of the first page element if it has one.
  2079. fpId = fp && fp[0] ? fp[0].id : undefined;
  2080. // The url refers to the first page if the path matches the document and
  2081. // it either has no hash value, or the hash is exactly equal to the id of the
  2082. // first page element.
  2083. return samePath && ( !u.hash || u.hash === "#" || ( fpId && u.hash.replace( /^#/, "" ) === fpId ) );
  2084. },
  2085. isEmbeddedPage: function( url ) {
  2086. var u = path.parseUrl( url );
  2087. //if the path is absolute, then we need to compare the url against
  2088. //both the documentUrl and the documentBase. The main reason for this
  2089. //is that links embedded within external documents will refer to the
  2090. //application document, whereas links embedded within the application
  2091. //document will be resolved against the document base.
  2092. if ( u.protocol !== "" ) {
  2093. return ( u.hash && ( u.hrefNoHash === documentUrl.hrefNoHash || ( documentBaseDiffers && u.hrefNoHash === documentBase.hrefNoHash ) ) );
  2094. }
  2095. return (/^#/).test( u.href );
  2096. }
  2097. },
  2098. //will be defined when a link is clicked and given an active class
  2099. $activeClickedLink = null,
  2100. //urlHistory is purely here to make guesses at whether the back or forward button was clicked
  2101. //and provide an appropriate transition
  2102. urlHistory = {
  2103. // Array of pages that are visited during a single page load.
  2104. // Each has a url and optional transition, title, and pageUrl (which represents the file path, in cases where URL is obscured, such as dialogs)
  2105. stack: [],
  2106. //maintain an index number for the active page in the stack
  2107. activeIndex: 0,
  2108. //get active
  2109. getActive: function() {
  2110. return urlHistory.stack[ urlHistory.activeIndex ];
  2111. },
  2112. getPrev: function() {
  2113. return urlHistory.stack[ urlHistory.activeIndex - 1 ];
  2114. },
  2115. getNext: function() {
  2116. return urlHistory.stack[ urlHistory.activeIndex + 1 ];
  2117. },
  2118. // addNew is used whenever a new page is added
  2119. addNew: function( url, transition, title, pageUrl, role ) {
  2120. //if there's forward history, wipe it
  2121. if( urlHistory.getNext() ) {
  2122. urlHistory.clearForward();
  2123. }
  2124. urlHistory.stack.push( {url : url, transition: transition, title: title, pageUrl: pageUrl, role: role } );
  2125. urlHistory.activeIndex = urlHistory.stack.length - 1;
  2126. },
  2127. //wipe urls ahead of active index
  2128. clearForward: function() {
  2129. urlHistory.stack = urlHistory.stack.slice( 0, urlHistory.activeIndex + 1 );
  2130. },
  2131. directHashChange: function( opts ) {
  2132. var back , forward, newActiveIndex, prev = this.getActive();
  2133. // check if url isp in history and if it's ahead or behind current page
  2134. $.each( urlHistory.stack, function( i, historyEntry ) {
  2135. //if the url is in the stack, it's a forward or a back
  2136. if( opts.currentUrl === historyEntry.url ) {
  2137. //define back and forward by whether url is older or newer than current page
  2138. back = i < urlHistory.activeIndex;
  2139. forward = !back;
  2140. newActiveIndex = i;
  2141. }
  2142. });
  2143. // save new page index, null check to prevent falsey 0 result
  2144. this.activeIndex = newActiveIndex !== undefined ? newActiveIndex : this.activeIndex;
  2145. if( back ) {
  2146. ( opts.either || opts.isBack )( true );
  2147. } else if( forward ) {
  2148. ( opts.either || opts.isForward )( false );
  2149. }
  2150. },
  2151. //disable hashchange event listener internally to ignore one change
  2152. //toggled internally when location.hash is updated to match the url of a successful page load
  2153. ignoreNextHashChange: false
  2154. },
  2155. //define first selector to receive focus when a page is shown
  2156. focusable = "[tabindex],a,button:visible,select:visible,input",
  2157. //queue to hold simultanious page transitions
  2158. pageTransitionQueue = [],
  2159. //indicates whether or not page is in process of transitioning
  2160. isPageTransitioning = false,
  2161. //nonsense hash change key for dialogs, so they create a history entry
  2162. dialogHashKey = "&ui-state=dialog",
  2163. //existing base tag?
  2164. $base = $head.children( "base" ),
  2165. //tuck away the original document URL minus any fragment.
  2166. documentUrl = path.parseUrl( location.href ),
  2167. //if the document has an embedded base tag, documentBase is set to its
  2168. //initial value. If a base tag does not exist, then we default to the documentUrl.
  2169. documentBase = $base.length ? path.parseUrl( path.makeUrlAbsolute( $base.attr( "href" ), documentUrl.href ) ) : documentUrl,
  2170. //cache the comparison once.
  2171. documentBaseDiffers = ( documentUrl.hrefNoHash !== documentBase.hrefNoHash );
  2172. //base element management, defined depending on dynamic base tag support
  2173. var base = $.support.dynamicBaseTag ? {
  2174. //define base element, for use in routing asset urls that are referenced in Ajax-requested markup
  2175. element: ( $base.length ? $base : $( "<base>", { href: documentBase.hrefNoHash } ).prependTo( $head ) ),
  2176. //set the generated BASE element's href attribute to a new page's base path
  2177. set: function( href ) {
  2178. base.element.attr( "href", path.makeUrlAbsolute( href, documentBase ) );
  2179. },
  2180. //set the generated BASE element's href attribute to a new page's base path
  2181. reset: function() {
  2182. base.element.attr( "href", documentBase.hrefNoHash );
  2183. }
  2184. } : undefined;
  2185. /*
  2186. internal utility functions
  2187. --------------------------------------*/
  2188. //direct focus to the page title, or otherwise first focusable element
  2189. $.mobile.focusPage = function ( page ) {
  2190. var autofocus = page.find("[autofocus]"),
  2191. pageTitle = page.find( ".ui-title:eq(0)" );
  2192. if( autofocus.length ) {
  2193. autofocus.focus();
  2194. return;
  2195. }
  2196. if( pageTitle.length ) {
  2197. pageTitle.focus();
  2198. }
  2199. else{
  2200. page.focus();
  2201. }
  2202. }
  2203. //remove active classes after page transition or error
  2204. function removeActiveLinkClass( forceRemoval ) {
  2205. if( !!$activeClickedLink && ( !$activeClickedLink.closest( '.ui-page-active' ).length || forceRemoval ) ) {
  2206. $activeClickedLink.removeClass( $.mobile.activeBtnClass );
  2207. }
  2208. $activeClickedLink = null;
  2209. }
  2210. function releasePageTransitionLock() {
  2211. isPageTransitioning = false;
  2212. if( pageTransitionQueue.length > 0 ) {
  2213. $.mobile.changePage.apply( null, pageTransitionQueue.pop() );
  2214. }
  2215. }
  2216. // Save the last scroll distance per page, before it is hidden
  2217. var setLastScrollEnabled = true,
  2218. setLastScroll, delayedSetLastScroll;
  2219. setLastScroll = function() {
  2220. // this barrier prevents setting the scroll value based on the browser
  2221. // scrolling the window based on a hashchange
  2222. if( !setLastScrollEnabled ) {
  2223. return;
  2224. }
  2225. var active = $.mobile.urlHistory.getActive();
  2226. if( active ) {
  2227. var lastScroll = $window.scrollTop();
  2228. // Set active page's lastScroll prop.
  2229. // If the location we're scrolling to is less than minScrollBack, let it go.
  2230. active.lastScroll = lastScroll < $.mobile.minScrollBack ? $.mobile.defaultHomeScroll : lastScroll;
  2231. }
  2232. };
  2233. // bind to scrollstop to gather scroll position. The delay allows for the hashchange
  2234. // event to fire and disable scroll recording in the case where the browser scrolls
  2235. // to the hash targets location (sometimes the top of the page). once pagechange fires
  2236. // getLastScroll is again permitted to operate
  2237. delayedSetLastScroll = function() {
  2238. setTimeout( setLastScroll, 100 );
  2239. };
  2240. // disable an scroll setting when a hashchange has been fired, this only works
  2241. // because the recording of the scroll position is delayed for 100ms after
  2242. // the browser might have changed the position because of the hashchange
  2243. $window.bind( $.support.pushState ? "popstate" : "hashchange", function() {
  2244. setLastScrollEnabled = false;
  2245. });
  2246. // handle initial hashchange from chrome :(
  2247. $window.one( $.support.pushState ? "popstate" : "hashchange", function() {
  2248. setLastScrollEnabled = true;
  2249. });
  2250. // wait until the mobile page container has been determined to bind to pagechange
  2251. $window.one( "pagecontainercreate", function(){
  2252. // once the page has changed, re-enable the scroll recording
  2253. $.mobile.pageContainer.bind( "pagechange", function() {
  2254. setLastScrollEnabled = true;
  2255. // remove any binding that previously existed on the get scroll
  2256. // which may or may not be different than the scroll element determined for
  2257. // this page previously
  2258. $window.unbind( "scrollstop", delayedSetLastScroll );
  2259. // determine and bind to the current scoll element which may be the window
  2260. // or in the case of touch overflow the element with touch overflow
  2261. $window.bind( "scrollstop", delayedSetLastScroll );
  2262. });
  2263. });
  2264. // bind to scrollstop for the first page as "pagechange" won't be fired in that case
  2265. $window.bind( "scrollstop", delayedSetLastScroll );
  2266. //function for transitioning between two existing pages
  2267. function transitionPages( toPage, fromPage, transition, reverse ) {
  2268. if( fromPage ) {
  2269. //trigger before show/hide events
  2270. fromPage.data( "page" )._trigger( "beforehide", null, { nextPage: toPage } );
  2271. }
  2272. toPage.data( "page" )._trigger( "beforeshow", null, { prevPage: fromPage || $( "" ) } );
  2273. //clear page loader
  2274. $.mobile.hidePageLoadingMsg();
  2275. // If transition is defined, check if css 3D transforms are supported, and if not, if a fallback is specified
  2276. if( transition && !$.support.cssTransform3d && $.mobile.transitionFallbacks[ transition ] ){
  2277. transition = $.mobile.transitionFallbacks[ transition ];
  2278. }
  2279. //find the transition handler for the specified transition. If there
  2280. //isn't one in our transitionHandlers dictionary, use the default one.
  2281. //call the handler immediately to kick-off the transition.
  2282. var th = $.mobile.transitionHandlers[ transition || "default" ] || $.mobile.defaultTransitionHandler,
  2283. promise = th( transition, reverse, toPage, fromPage );
  2284. promise.done(function() {
  2285. //trigger show/hide events
  2286. if( fromPage ) {
  2287. fromPage.data( "page" )._trigger( "hide", null, { nextPage: toPage } );
  2288. }
  2289. //trigger pageshow, define prevPage as either fromPage or empty jQuery obj
  2290. toPage.data( "page" )._trigger( "show", null, { prevPage: fromPage || $( "" ) } );
  2291. });
  2292. return promise;
  2293. }
  2294. //simply set the active page's minimum height to screen height, depending on orientation
  2295. function getScreenHeight(){
  2296. // Native innerHeight returns more accurate value for this across platforms,
  2297. // jQuery version is here as a normalized fallback for platforms like Symbian
  2298. return window.innerHeight || $( window ).height();
  2299. }
  2300. $.mobile.getScreenHeight = getScreenHeight;
  2301. //simply set the active page's minimum height to screen height, depending on orientation
  2302. function resetActivePageHeight(){
  2303. var aPage = $( "." + $.mobile.activePageClass ),
  2304. aPagePadT = parseFloat( aPage.css( "padding-top" ) ),
  2305. aPagePadB = parseFloat( aPage.css( "padding-bottom" ) );
  2306. aPage.css( "min-height", getScreenHeight() - aPagePadT - aPagePadB );
  2307. }
  2308. //shared page enhancements
  2309. function enhancePage( $page, role ) {
  2310. // If a role was specified, make sure the data-role attribute
  2311. // on the page element is in sync.
  2312. if( role ) {
  2313. $page.attr( "data-" + $.mobile.ns + "role", role );
  2314. }
  2315. //run page plugin
  2316. $page.page();
  2317. }
  2318. /* exposed $.mobile methods */
  2319. //animation complete callback
  2320. $.fn.animationComplete = function( callback ) {
  2321. if( $.support.cssTransitions ) {
  2322. return $( this ).one( 'webkitAnimationEnd animationend', callback );
  2323. }
  2324. else{
  2325. // defer execution for consistency between webkit/non webkit
  2326. setTimeout( callback, 0 );
  2327. return $( this );
  2328. }
  2329. };
  2330. //expose path object on $.mobile
  2331. $.mobile.path = path;
  2332. //expose base object on $.mobile
  2333. $.mobile.base = base;
  2334. //history stack
  2335. $.mobile.urlHistory = urlHistory;
  2336. $.mobile.dialogHashKey = dialogHashKey;
  2337. //enable cross-domain page support
  2338. $.mobile.allowCrossDomainPages = false;
  2339. //return the original document url
  2340. $.mobile.getDocumentUrl = function(asParsedObject) {
  2341. return asParsedObject ? $.extend( {}, documentUrl ) : documentUrl.href;
  2342. };
  2343. //return the original document base url
  2344. $.mobile.getDocumentBase = function(asParsedObject) {
  2345. return asParsedObject ? $.extend( {}, documentBase ) : documentBase.href;
  2346. };
  2347. $.mobile._bindPageRemove = function() {
  2348. var page = $(this);
  2349. // when dom caching is not enabled or the page is embedded bind to remove the page on hide
  2350. if( !page.data("page").options.domCache
  2351. && page.is(":jqmData(external-page='true')") ) {
  2352. page.bind( 'pagehide.remove', function() {
  2353. var $this = $( this ),
  2354. prEvent = new $.Event( "pageremove" );
  2355. $this.trigger( prEvent );
  2356. if( !prEvent.isDefaultPrevented() ){
  2357. $this.removeWithDependents();
  2358. }
  2359. });
  2360. }
  2361. };
  2362. // Load a page into the DOM.
  2363. $.mobile.loadPage = function( url, options ) {
  2364. // This function uses deferred notifications to let callers
  2365. // know when the page is done loading, or if an error has occurred.
  2366. var deferred = $.Deferred(),
  2367. // The default loadPage options with overrides specified by
  2368. // the caller.
  2369. settings = $.extend( {}, $.mobile.loadPage.defaults, options ),
  2370. // The DOM element for the page after it has been loaded.
  2371. page = null,
  2372. // If the reloadPage option is true, and the page is already
  2373. // in the DOM, dupCachedPage will be set to the page element
  2374. // so that it can be removed after the new version of the
  2375. // page is loaded off the network.
  2376. dupCachedPage = null,
  2377. // determine the current base url
  2378. findBaseWithDefault = function(){
  2379. var closestBase = ( $.mobile.activePage && getClosestBaseUrl( $.mobile.activePage ) );
  2380. return closestBase || documentBase.hrefNoHash;
  2381. },
  2382. // The absolute version of the URL passed into the function. This
  2383. // version of the URL may contain dialog/subpage params in it.
  2384. absUrl = path.makeUrlAbsolute( url, findBaseWithDefault() );
  2385. // If the caller provided data, and we're using "get" request,
  2386. // append the data to the URL.
  2387. if ( settings.data && settings.type === "get" ) {
  2388. absUrl = path.addSearchParams( absUrl, settings.data );
  2389. settings.data = undefined;
  2390. }
  2391. // If the caller is using a "post" request, reloadPage must be true
  2392. if( settings.data && settings.type === "post" ){
  2393. settings.reloadPage = true;
  2394. }
  2395. // The absolute version of the URL minus any dialog/subpage params.
  2396. // In otherwords the real URL of the page to be loaded.
  2397. var fileUrl = path.getFilePath( absUrl ),
  2398. // The version of the Url actually stored in the data-url attribute of
  2399. // the page. For embedded pages, it is just the id of the page. For pages
  2400. // within the same domain as the document base, it is the site relative
  2401. // path. For cross-domain pages (Phone Gap only) the entire absolute Url
  2402. // used to load the page.
  2403. dataUrl = path.convertUrlToDataUrl( absUrl );
  2404. // Make sure we have a pageContainer to work with.
  2405. settings.pageContainer = settings.pageContainer || $.mobile.pageContainer;
  2406. // Check to see if the page already exists in the DOM.
  2407. page = settings.pageContainer.children( ":jqmData(url='" + dataUrl + "')" );
  2408. // If we failed to find the page, check to see if the url is a
  2409. // reference to an embedded page. If so, it may have been dynamically
  2410. // injected by a developer, in which case it would be lacking a data-url
  2411. // attribute and in need of enhancement.
  2412. if ( page.length === 0 && dataUrl && !path.isPath( dataUrl ) ) {
  2413. page = settings.pageContainer.children( "#" + dataUrl )
  2414. .attr( "data-" + $.mobile.ns + "url", dataUrl );
  2415. }
  2416. // If we failed to find a page in the DOM, check the URL to see if it
  2417. // refers to the first page in the application. If it isn't a reference
  2418. // to the first page and refers to non-existent embedded page, error out.
  2419. if ( page.length === 0 ) {
  2420. if ( $.mobile.firstPage && path.isFirstPageUrl( fileUrl ) ) {
  2421. // Check to make sure our cached-first-page is actually
  2422. // in the DOM. Some user deployed apps are pruning the first
  2423. // page from the DOM for various reasons, we check for this
  2424. // case here because we don't want a first-page with an id
  2425. // falling through to the non-existent embedded page error
  2426. // case. If the first-page is not in the DOM, then we let
  2427. // things fall through to the ajax loading code below so
  2428. // that it gets reloaded.
  2429. if ( $.mobile.firstPage.parent().length ) {
  2430. page = $( $.mobile.firstPage );
  2431. }
  2432. } else if ( path.isEmbeddedPage( fileUrl ) ) {
  2433. deferred.reject( absUrl, options );
  2434. return deferred.promise();
  2435. }
  2436. }
  2437. // Reset base to the default document base.
  2438. if ( base ) {
  2439. base.reset();
  2440. }
  2441. // If the page we are interested in is already in the DOM,
  2442. // and the caller did not indicate that we should force a
  2443. // reload of the file, we are done. Otherwise, track the
  2444. // existing page as a duplicated.
  2445. if ( page.length ) {
  2446. if ( !settings.reloadPage ) {
  2447. enhancePage( page, settings.role );
  2448. deferred.resolve( absUrl, options, page );
  2449. return deferred.promise();
  2450. }
  2451. dupCachedPage = page;
  2452. }
  2453. var mpc = settings.pageContainer,
  2454. pblEvent = new $.Event( "pagebeforeload" ),
  2455. triggerData = { url: url, absUrl: absUrl, dataUrl: dataUrl, deferred: deferred, options: settings };
  2456. // Let listeners know we're about to load a page.
  2457. mpc.trigger( pblEvent, triggerData );
  2458. // If the default behavior is prevented, stop here!
  2459. if( pblEvent.isDefaultPrevented() ){
  2460. return deferred.promise();
  2461. }
  2462. if ( settings.showLoadMsg ) {
  2463. // This configurable timeout allows cached pages a brief delay to load without showing a message
  2464. var loadMsgDelay = setTimeout(function(){
  2465. $.mobile.showPageLoadingMsg();
  2466. }, settings.loadMsgDelay ),
  2467. // Shared logic for clearing timeout and removing message.
  2468. hideMsg = function(){
  2469. // Stop message show timer
  2470. clearTimeout( loadMsgDelay );
  2471. // Hide loading message
  2472. $.mobile.hidePageLoadingMsg();
  2473. };
  2474. }
  2475. if ( !( $.mobile.allowCrossDomainPages || path.isSameDomain( documentUrl, absUrl ) ) ) {
  2476. deferred.reject( absUrl, options );
  2477. } else {
  2478. // Load the new page.
  2479. $.ajax({
  2480. url: fileUrl,
  2481. type: settings.type,
  2482. data: settings.data,
  2483. dataType: "html",
  2484. success: function( html, textStatus, xhr ) {
  2485. //pre-parse html to check for a data-url,
  2486. //use it as the new fileUrl, base path, etc
  2487. var all = $( "<div></div>" ),
  2488. //page title regexp
  2489. newPageTitle = html.match( /<title[^>]*>([^<]*)/ ) && RegExp.$1,
  2490. // TODO handle dialogs again
  2491. pageElemRegex = new RegExp( "(<[^>]+\\bdata-" + $.mobile.ns + "role=[\"']?page[\"']?[^>]*>)" ),
  2492. dataUrlRegex = new RegExp( "\\bdata-" + $.mobile.ns + "url=[\"']?([^\"'>]*)[\"']?" );
  2493. // data-url must be provided for the base tag so resource requests can be directed to the
  2494. // correct url. loading into a temprorary element makes these requests immediately
  2495. if( pageElemRegex.test( html )
  2496. && RegExp.$1
  2497. && dataUrlRegex.test( RegExp.$1 )
  2498. && RegExp.$1 ) {
  2499. url = fileUrl = path.getFilePath( RegExp.$1 );
  2500. }
  2501. if ( base ) {
  2502. base.set( fileUrl );
  2503. }
  2504. //workaround to allow scripts to execute when included in page divs
  2505. all.get( 0 ).innerHTML = html;
  2506. page = all.find( ":jqmData(role='page'), :jqmData(role='dialog')" ).first();
  2507. //if page elem couldn't be found, create one and insert the body element's contents
  2508. if( !page.length ){
  2509. page = $( "<div data-" + $.mobile.ns + "role='page'>" + html.split( /<\/?body[^>]*>/gmi )[1] + "</div>" );
  2510. }
  2511. if ( newPageTitle && !page.jqmData( "title" ) ) {
  2512. if ( ~newPageTitle.indexOf( "&" ) ) {
  2513. newPageTitle = $( "<div>" + newPageTitle + "</div>" ).text();
  2514. }
  2515. page.jqmData( "title", newPageTitle );
  2516. }
  2517. //rewrite src and href attrs to use a base url
  2518. if( !$.support.dynamicBaseTag ) {
  2519. var newPath = path.get( fileUrl );
  2520. page.find( "[src], link[href], a[rel='external'], :jqmData(ajax='false'), a[target]" ).each(function() {
  2521. var thisAttr = $( this ).is( '[href]' ) ? 'href' :
  2522. $(this).is('[src]') ? 'src' : 'action',
  2523. thisUrl = $( this ).attr( thisAttr );
  2524. // XXX_jblas: We need to fix this so that it removes the document
  2525. // base URL, and then prepends with the new page URL.
  2526. //if full path exists and is same, chop it - helps IE out
  2527. thisUrl = thisUrl.replace( location.protocol + '//' + location.host + location.pathname, '' );
  2528. if( !/^(\w+:|#|\/)/.test( thisUrl ) ) {
  2529. $( this ).attr( thisAttr, newPath + thisUrl );
  2530. }
  2531. });
  2532. }
  2533. //append to page and enhance
  2534. // TODO taging a page with external to make sure that embedded pages aren't removed
  2535. // by the various page handling code is bad. Having page handling code in many
  2536. // places is bad. Solutions post 1.0
  2537. page
  2538. .attr( "data-" + $.mobile.ns + "url", path.convertUrlToDataUrl( fileUrl ) )
  2539. .attr( "data-" + $.mobile.ns + "external-page", true )
  2540. .appendTo( settings.pageContainer );
  2541. // wait for page creation to leverage options defined on widget
  2542. page.one( 'pagecreate', $.mobile._bindPageRemove );
  2543. enhancePage( page, settings.role );
  2544. // Enhancing the page may result in new dialogs/sub pages being inserted
  2545. // into the DOM. If the original absUrl refers to a sub-page, that is the
  2546. // real page we are interested in.
  2547. if ( absUrl.indexOf( "&" + $.mobile.subPageUrlKey ) > -1 ) {
  2548. page = settings.pageContainer.children( ":jqmData(url='" + dataUrl + "')" );
  2549. }
  2550. //bind pageHide to removePage after it's hidden, if the page options specify to do so
  2551. // Remove loading message.
  2552. if ( settings.showLoadMsg ) {
  2553. hideMsg();
  2554. }
  2555. // Add the page reference and xhr to our triggerData.
  2556. triggerData.xhr = xhr;
  2557. triggerData.textStatus = textStatus;
  2558. triggerData.page = page;
  2559. // Let listeners know the page loaded successfully.
  2560. settings.pageContainer.trigger( "pageload", triggerData );
  2561. deferred.resolve( absUrl, options, page, dupCachedPage );
  2562. },
  2563. error: function( xhr, textStatus, errorThrown ) {
  2564. //set base back to current path
  2565. if( base ) {
  2566. base.set( path.get() );
  2567. }
  2568. // Add error info to our triggerData.
  2569. triggerData.xhr = xhr;
  2570. triggerData.textStatus = textStatus;
  2571. triggerData.errorThrown = errorThrown;
  2572. var plfEvent = new $.Event( "pageloadfailed" );
  2573. // Let listeners know the page load failed.
  2574. settings.pageContainer.trigger( plfEvent, triggerData );
  2575. // If the default behavior is prevented, stop here!
  2576. // Note that it is the responsibility of the listener/handler
  2577. // that called preventDefault(), to resolve/reject the
  2578. // deferred object within the triggerData.
  2579. if( plfEvent.isDefaultPrevented() ){
  2580. return;
  2581. }
  2582. // Remove loading message.
  2583. if ( settings.showLoadMsg ) {
  2584. // Remove loading message.
  2585. hideMsg();
  2586. // show error message
  2587. $.mobile.showPageLoadingMsg( $.mobile.pageLoadErrorMessageTheme, $.mobile.pageLoadErrorMessage, true );
  2588. // hide after delay
  2589. setTimeout( $.mobile.hidePageLoadingMsg, 1500 );
  2590. }
  2591. deferred.reject( absUrl, options );
  2592. }
  2593. });
  2594. }
  2595. return deferred.promise();
  2596. };
  2597. $.mobile.loadPage.defaults = {
  2598. type: "get",
  2599. data: undefined,
  2600. reloadPage: false,
  2601. role: undefined, // By default we rely on the role defined by the @data-role attribute.
  2602. showLoadMsg: false,
  2603. pageContainer: undefined,
  2604. loadMsgDelay: 50 // This delay allows loads that pull from browser cache to occur without showing the loading message.
  2605. };
  2606. // Show a specific page in the page container.
  2607. $.mobile.changePage = function( toPage, options ) {
  2608. // If we are in the midst of a transition, queue the current request.
  2609. // We'll call changePage() once we're done with the current transition to
  2610. // service the request.
  2611. if( isPageTransitioning ) {
  2612. pageTransitionQueue.unshift( arguments );
  2613. return;
  2614. }
  2615. var settings = $.extend( {}, $.mobile.changePage.defaults, options );
  2616. // Make sure we have a pageContainer to work with.
  2617. settings.pageContainer = settings.pageContainer || $.mobile.pageContainer;
  2618. // Make sure we have a fromPage.
  2619. settings.fromPage = settings.fromPage || $.mobile.activePage;
  2620. var mpc = settings.pageContainer,
  2621. pbcEvent = new $.Event( "pagebeforechange" ),
  2622. triggerData = { toPage: toPage, options: settings };
  2623. // Let listeners know we're about to change the current page.
  2624. mpc.trigger( pbcEvent, triggerData );
  2625. // If the default behavior is prevented, stop here!
  2626. if( pbcEvent.isDefaultPrevented() ){
  2627. return;
  2628. }
  2629. // We allow "pagebeforechange" observers to modify the toPage in the trigger
  2630. // data to allow for redirects. Make sure our toPage is updated.
  2631. toPage = triggerData.toPage;
  2632. // Set the isPageTransitioning flag to prevent any requests from
  2633. // entering this method while we are in the midst of loading a page
  2634. // or transitioning.
  2635. isPageTransitioning = true;
  2636. // If the caller passed us a url, call loadPage()
  2637. // to make sure it is loaded into the DOM. We'll listen
  2638. // to the promise object it returns so we know when
  2639. // it is done loading or if an error ocurred.
  2640. if ( typeof toPage == "string" ) {
  2641. $.mobile.loadPage( toPage, settings )
  2642. .done(function( url, options, newPage, dupCachedPage ) {
  2643. isPageTransitioning = false;
  2644. options.duplicateCachedPage = dupCachedPage;
  2645. $.mobile.changePage( newPage, options );
  2646. })
  2647. .fail(function( url, options ) {
  2648. isPageTransitioning = false;
  2649. //clear out the active button state
  2650. removeActiveLinkClass( true );
  2651. //release transition lock so navigation is free again
  2652. releasePageTransitionLock();
  2653. settings.pageContainer.trigger( "pagechangefailed", triggerData );
  2654. });
  2655. return;
  2656. }
  2657. // If we are going to the first-page of the application, we need to make
  2658. // sure settings.dataUrl is set to the application document url. This allows
  2659. // us to avoid generating a document url with an id hash in the case where the
  2660. // first-page of the document has an id attribute specified.
  2661. if ( toPage[ 0 ] === $.mobile.firstPage[ 0 ] && !settings.dataUrl ) {
  2662. settings.dataUrl = documentUrl.hrefNoHash;
  2663. }
  2664. // The caller passed us a real page DOM element. Update our
  2665. // internal state and then trigger a transition to the page.
  2666. var fromPage = settings.fromPage,
  2667. url = ( settings.dataUrl && path.convertUrlToDataUrl( settings.dataUrl ) ) || toPage.jqmData( "url" ),
  2668. // The pageUrl var is usually the same as url, except when url is obscured as a dialog url. pageUrl always contains the file path
  2669. pageUrl = url,
  2670. fileUrl = path.getFilePath( url ),
  2671. active = urlHistory.getActive(),
  2672. activeIsInitialPage = urlHistory.activeIndex === 0,
  2673. historyDir = 0,
  2674. pageTitle = document.title,
  2675. isDialog = settings.role === "dialog" || toPage.jqmData( "role" ) === "dialog";
  2676. // By default, we prevent changePage requests when the fromPage and toPage
  2677. // are the same element, but folks that generate content manually/dynamically
  2678. // and reuse pages want to be able to transition to the same page. To allow
  2679. // this, they will need to change the default value of allowSamePageTransition
  2680. // to true, *OR*, pass it in as an option when they manually call changePage().
  2681. // It should be noted that our default transition animations assume that the
  2682. // formPage and toPage are different elements, so they may behave unexpectedly.
  2683. // It is up to the developer that turns on the allowSamePageTransitiona option
  2684. // to either turn off transition animations, or make sure that an appropriate
  2685. // animation transition is used.
  2686. if( fromPage && fromPage[0] === toPage[0] && !settings.allowSamePageTransition ) {
  2687. isPageTransitioning = false;
  2688. mpc.trigger( "pagechange", triggerData );
  2689. return;
  2690. }
  2691. // We need to make sure the page we are given has already been enhanced.
  2692. enhancePage( toPage, settings.role );
  2693. // If the changePage request was sent from a hashChange event, check to see if the
  2694. // page is already within the urlHistory stack. If so, we'll assume the user hit
  2695. // the forward/back button and will try to match the transition accordingly.
  2696. if( settings.fromHashChange ) {
  2697. urlHistory.directHashChange({
  2698. currentUrl: url,
  2699. isBack: function() { historyDir = -1; },
  2700. isForward: function() { historyDir = 1; }
  2701. });
  2702. }
  2703. // Kill the keyboard.
  2704. // XXX_jblas: We need to stop crawling the entire document to kill focus. Instead,
  2705. // we should be tracking focus with a delegate() handler so we already have
  2706. // the element in hand at this point.
  2707. // Wrap this in a try/catch block since IE9 throw "Unspecified error" if document.activeElement
  2708. // is undefined when we are in an IFrame.
  2709. try {
  2710. if(document.activeElement && document.activeElement.nodeName.toLowerCase() != 'body') {
  2711. $(document.activeElement).blur();
  2712. } else {
  2713. $( "input:focus, textarea:focus, select:focus" ).blur();
  2714. }
  2715. } catch(e) {}
  2716. // If we're displaying the page as a dialog, we don't want the url
  2717. // for the dialog content to be used in the hash. Instead, we want
  2718. // to append the dialogHashKey to the url of the current page.
  2719. if ( isDialog && active ) {
  2720. // on the initial page load active.url is undefined and in that case should
  2721. // be an empty string. Moving the undefined -> empty string back into
  2722. // urlHistory.addNew seemed imprudent given undefined better represents
  2723. // the url state
  2724. url = ( active.url || "" ) + dialogHashKey;
  2725. }
  2726. // Set the location hash.
  2727. if( settings.changeHash !== false && url ) {
  2728. //disable hash listening temporarily
  2729. urlHistory.ignoreNextHashChange = true;
  2730. //update hash and history
  2731. path.set( url );
  2732. }
  2733. // if title element wasn't found, try the page div data attr too
  2734. // If this is a deep-link or a reload ( active === undefined ) then just use pageTitle
  2735. var newPageTitle = ( !active )? pageTitle : toPage.jqmData( "title" ) || toPage.children(":jqmData(role='header')").find(".ui-title" ).getEncodedText();
  2736. if( !!newPageTitle && pageTitle == document.title ) {
  2737. pageTitle = newPageTitle;
  2738. }
  2739. if ( !toPage.jqmData( "title" ) ) {
  2740. toPage.jqmData( "title", pageTitle );
  2741. }
  2742. // Make sure we have a transition defined.
  2743. settings.transition = settings.transition
  2744. || ( ( historyDir && !activeIsInitialPage ) ? active.transition : undefined )
  2745. || ( isDialog ? $.mobile.defaultDialogTransition : $.mobile.defaultPageTransition );
  2746. //add page to history stack if it's not back or forward
  2747. if( !historyDir ) {
  2748. urlHistory.addNew( url, settings.transition, pageTitle, pageUrl, settings.role );
  2749. }
  2750. //set page title
  2751. document.title = urlHistory.getActive().title;
  2752. //set "toPage" as activePage
  2753. $.mobile.activePage = toPage;
  2754. // If we're navigating back in the URL history, set reverse accordingly.
  2755. settings.reverse = settings.reverse || historyDir < 0;
  2756. transitionPages( toPage, fromPage, settings.transition, settings.reverse )
  2757. .done(function( name, reverse, $to, $from, alreadyFocused ) {
  2758. removeActiveLinkClass();
  2759. //if there's a duplicateCachedPage, remove it from the DOM now that it's hidden
  2760. if ( settings.duplicateCachedPage ) {
  2761. settings.duplicateCachedPage.remove();
  2762. }
  2763. // Send focus to the newly shown page. Moved from promise .done binding in transitionPages
  2764. // itself to avoid ie bug that reports offsetWidth as > 0 (core check for visibility)
  2765. // despite visibility: hidden addresses issue #2965
  2766. // https://github.com/jquery/jquery-mobile/issues/2965
  2767. if( !alreadyFocused ){
  2768. $.mobile.focusPage( toPage );
  2769. }
  2770. releasePageTransitionLock();
  2771. // Let listeners know we're all done changing the current page.
  2772. mpc.trigger( "pagechange", triggerData );
  2773. });
  2774. };
  2775. $.mobile.changePage.defaults = {
  2776. transition: undefined,
  2777. reverse: false,
  2778. changeHash: true,
  2779. fromHashChange: false,
  2780. role: undefined, // By default we rely on the role defined by the @data-role attribute.
  2781. duplicateCachedPage: undefined,
  2782. pageContainer: undefined,
  2783. showLoadMsg: true, //loading message shows by default when pages are being fetched during changePage
  2784. dataUrl: undefined,
  2785. fromPage: undefined,
  2786. allowSamePageTransition: false
  2787. };
  2788. /* Event Bindings - hashchange, submit, and click */
  2789. function findClosestLink( ele )
  2790. {
  2791. while ( ele ) {
  2792. // Look for the closest element with a nodeName of "a".
  2793. // Note that we are checking if we have a valid nodeName
  2794. // before attempting to access it. This is because the
  2795. // node we get called with could have originated from within
  2796. // an embedded SVG document where some symbol instance elements
  2797. // don't have nodeName defined on them, or strings are of type
  2798. // SVGAnimatedString.
  2799. if ( ( typeof ele.nodeName === "string" ) && ele.nodeName.toLowerCase() == "a" ) {
  2800. break;
  2801. }
  2802. ele = ele.parentNode;
  2803. }
  2804. return ele;
  2805. }
  2806. // The base URL for any given element depends on the page it resides in.
  2807. function getClosestBaseUrl( ele )
  2808. {
  2809. // Find the closest page and extract out its url.
  2810. var url = $( ele ).closest( ".ui-page" ).jqmData( "url" ),
  2811. base = documentBase.hrefNoHash;
  2812. if ( !url || !path.isPath( url ) ) {
  2813. url = base;
  2814. }
  2815. return path.makeUrlAbsolute( url, base);
  2816. }
  2817. //The following event bindings should be bound after mobileinit has been triggered
  2818. //the following function is called in the init file
  2819. $.mobile._registerInternalEvents = function(){
  2820. //bind to form submit events, handle with Ajax
  2821. $( document ).delegate( "form", "submit", function( event ) {
  2822. var $this = $( this );
  2823. if( !$.mobile.ajaxEnabled ||
  2824. // test that the form is, itself, ajax false
  2825. $this.is(":jqmData(ajax='false')") ||
  2826. // test that $.mobile.ignoreContentEnabled is set and
  2827. // the form or one of it's parents is ajax=false
  2828. !$this.jqmHijackable().length ) {
  2829. return;
  2830. }
  2831. var type = $this.attr( "method" ),
  2832. target = $this.attr( "target" ),
  2833. url = $this.attr( "action" );
  2834. // If no action is specified, browsers default to using the
  2835. // URL of the document containing the form. Since we dynamically
  2836. // pull in pages from external documents, the form should submit
  2837. // to the URL for the source document of the page containing
  2838. // the form.
  2839. if ( !url ) {
  2840. // Get the @data-url for the page containing the form.
  2841. url = getClosestBaseUrl( $this );
  2842. if ( url === documentBase.hrefNoHash ) {
  2843. // The url we got back matches the document base,
  2844. // which means the page must be an internal/embedded page,
  2845. // so default to using the actual document url as a browser
  2846. // would.
  2847. url = documentUrl.hrefNoSearch;
  2848. }
  2849. }
  2850. url = path.makeUrlAbsolute( url, getClosestBaseUrl($this) );
  2851. //external submits use regular HTTP
  2852. if( path.isExternal( url ) || target ) {
  2853. return;
  2854. }
  2855. $.mobile.changePage(
  2856. url,
  2857. {
  2858. type: type && type.length && type.toLowerCase() || "get",
  2859. data: $this.serialize(),
  2860. transition: $this.jqmData( "transition" ),
  2861. direction: $this.jqmData( "direction" ),
  2862. reloadPage: true
  2863. }
  2864. );
  2865. event.preventDefault();
  2866. });
  2867. //add active state on vclick
  2868. $( document ).bind( "vclick", function( event ) {
  2869. // if this isn't a left click we don't care. Its important to note
  2870. // that when the virtual event is generated it will create the which attr
  2871. if ( event.which > 1 || !$.mobile.linkBindingEnabled ) {
  2872. return;
  2873. }
  2874. var link = findClosestLink( event.target );
  2875. // split from the previous return logic to avoid find closest where possible
  2876. // TODO teach $.mobile.hijackable to operate on raw dom elements so the link wrapping
  2877. // can be avoided
  2878. if ( !$(link).jqmHijackable().length ) {
  2879. return;
  2880. }
  2881. if ( link ) {
  2882. if ( path.parseUrl( link.getAttribute( "href" ) || "#" ).hash !== "#" ) {
  2883. removeActiveLinkClass( true );
  2884. $activeClickedLink = $( link ).closest( ".ui-btn" ).not( ".ui-disabled" );
  2885. $activeClickedLink.addClass( $.mobile.activeBtnClass );
  2886. $( "." + $.mobile.activePageClass + " .ui-btn" ).not( link ).blur();
  2887. // By caching the href value to data and switching the href to a #, we can avoid address bar showing in iOS. The click handler resets the href during its initial steps if this data is present
  2888. $( link )
  2889. .jqmData( "href", $( link ).attr( "href" ) )
  2890. .attr( "href", "#" );
  2891. }
  2892. }
  2893. });
  2894. // click routing - direct to HTTP or Ajax, accordingly
  2895. $( document ).bind( "click", function( event ) {
  2896. if( !$.mobile.linkBindingEnabled ){
  2897. return;
  2898. }
  2899. var link = findClosestLink( event.target ), $link = $( link ), httpCleanup;
  2900. // If there is no link associated with the click or its not a left
  2901. // click we want to ignore the click
  2902. // TODO teach $.mobile.hijackable to operate on raw dom elements so the link wrapping
  2903. // can be avoided
  2904. if ( !link || event.which > 1 || !$link.jqmHijackable().length ) {
  2905. return;
  2906. }
  2907. //remove active link class if external (then it won't be there if you come back)
  2908. httpCleanup = function(){
  2909. window.setTimeout( function() { removeActiveLinkClass( true ); }, 200 );
  2910. };
  2911. // If there's data cached for the real href value, set the link's href back to it again. This pairs with an address bar workaround from the vclick handler
  2912. if( $link.jqmData( "href" ) ){
  2913. $link.attr( "href", $link.jqmData( "href" ) );
  2914. }
  2915. //if there's a data-rel=back attr, go back in history
  2916. if( $link.is( ":jqmData(rel='back')" ) ) {
  2917. window.history.back();
  2918. return false;
  2919. }
  2920. var baseUrl = getClosestBaseUrl( $link ),
  2921. //get href, if defined, otherwise default to empty hash
  2922. href = path.makeUrlAbsolute( $link.attr( "href" ) || "#", baseUrl );
  2923. //if ajax is disabled, exit early
  2924. if( !$.mobile.ajaxEnabled && !path.isEmbeddedPage( href ) ){
  2925. httpCleanup();
  2926. //use default click handling
  2927. return;
  2928. }
  2929. // XXX_jblas: Ideally links to application pages should be specified as
  2930. // an url to the application document with a hash that is either
  2931. // the site relative path or id to the page. But some of the
  2932. // internal code that dynamically generates sub-pages for nested
  2933. // lists and select dialogs, just write a hash in the link they
  2934. // create. This means the actual URL path is based on whatever
  2935. // the current value of the base tag is at the time this code
  2936. // is called. For now we are just assuming that any url with a
  2937. // hash in it is an application page reference.
  2938. if ( href.search( "#" ) != -1 ) {
  2939. href = href.replace( /[^#]*#/, "" );
  2940. if ( !href ) {
  2941. //link was an empty hash meant purely
  2942. //for interaction, so we ignore it.
  2943. event.preventDefault();
  2944. return;
  2945. } else if ( path.isPath( href ) ) {
  2946. //we have apath so make it the href we want to load.
  2947. href = path.makeUrlAbsolute( href, baseUrl );
  2948. } else {
  2949. //we have a simple id so use the documentUrl as its base.
  2950. href = path.makeUrlAbsolute( "#" + href, documentUrl.hrefNoHash );
  2951. }
  2952. }
  2953. // Should we handle this link, or let the browser deal with it?
  2954. var useDefaultUrlHandling = $link.is( "[rel='external']" ) || $link.is( ":jqmData(ajax='false')" ) || $link.is( "[target]" ),
  2955. // Some embedded browsers, like the web view in Phone Gap, allow cross-domain XHR
  2956. // requests if the document doing the request was loaded via the file:// protocol.
  2957. // This is usually to allow the application to "phone home" and fetch app specific
  2958. // data. We normally let the browser handle external/cross-domain urls, but if the
  2959. // allowCrossDomainPages option is true, we will allow cross-domain http/https
  2960. // requests to go through our page loading logic.
  2961. isCrossDomainPageLoad = ( $.mobile.allowCrossDomainPages && documentUrl.protocol === "file:" && href.search( /^https?:/ ) != -1 ),
  2962. //check for protocol or rel and its not an embedded page
  2963. //TODO overlap in logic from isExternal, rel=external check should be
  2964. // moved into more comprehensive isExternalLink
  2965. isExternal = useDefaultUrlHandling || ( path.isExternal( href ) && !isCrossDomainPageLoad );
  2966. if( isExternal ) {
  2967. httpCleanup();
  2968. //use default click handling
  2969. return;
  2970. }
  2971. //use ajax
  2972. var transition = $link.jqmData( "transition" ),
  2973. direction = $link.jqmData( "direction" ),
  2974. reverse = ( direction && direction === "reverse" ) ||
  2975. // deprecated - remove by 1.0
  2976. $link.jqmData( "back" ),
  2977. //this may need to be more specific as we use data-rel more
  2978. role = $link.attr( "data-" + $.mobile.ns + "rel" ) || undefined;
  2979. $.mobile.changePage( href, { transition: transition, reverse: reverse, role: role } );
  2980. event.preventDefault();
  2981. });
  2982. //prefetch pages when anchors with data-prefetch are encountered
  2983. $( document ).delegate( ".ui-page", "pageshow.prefetch", function() {
  2984. var urls = [];
  2985. $( this ).find( "a:jqmData(prefetch)" ).each(function(){
  2986. var $link = $(this),
  2987. url = $link.attr( "href" );
  2988. if ( url && $.inArray( url, urls ) === -1 ) {
  2989. urls.push( url );
  2990. $.mobile.loadPage( url, {role: $link.attr("data-" + $.mobile.ns + "rel")} );
  2991. }
  2992. });
  2993. });
  2994. $.mobile._handleHashChange = function( hash ) {
  2995. //find first page via hash
  2996. var to = path.stripHash( hash ),
  2997. //transition is false if it's the first page, undefined otherwise (and may be overridden by default)
  2998. transition = $.mobile.urlHistory.stack.length === 0 ? "none" : undefined,
  2999. // default options for the changPage calls made after examining the current state
  3000. // of the page and the hash
  3001. changePageOptions = {
  3002. transition: transition,
  3003. changeHash: false,
  3004. fromHashChange: true
  3005. };
  3006. //if listening is disabled (either globally or temporarily), or it's a dialog hash
  3007. if( !$.mobile.hashListeningEnabled || urlHistory.ignoreNextHashChange ) {
  3008. urlHistory.ignoreNextHashChange = false;
  3009. return;
  3010. }
  3011. // special case for dialogs
  3012. if( urlHistory.stack.length > 1 && to.indexOf( dialogHashKey ) > -1 ) {
  3013. // If current active page is not a dialog skip the dialog and continue
  3014. // in the same direction
  3015. if(!$.mobile.activePage.is( ".ui-dialog" )) {
  3016. //determine if we're heading forward or backward and continue accordingly past
  3017. //the current dialog
  3018. urlHistory.directHashChange({
  3019. currentUrl: to,
  3020. isBack: function() { window.history.back(); },
  3021. isForward: function() { window.history.forward(); }
  3022. });
  3023. // prevent changePage()
  3024. return;
  3025. } else {
  3026. // if the current active page is a dialog and we're navigating
  3027. // to a dialog use the dialog objected saved in the stack
  3028. urlHistory.directHashChange({
  3029. currentUrl: to,
  3030. // regardless of the direction of the history change
  3031. // do the following
  3032. either: function( isBack ) {
  3033. var active = $.mobile.urlHistory.getActive();
  3034. to = active.pageUrl;
  3035. // make sure to set the role, transition and reversal
  3036. // as most of this is lost by the domCache cleaning
  3037. $.extend( changePageOptions, {
  3038. role: active.role,
  3039. transition: active.transition,
  3040. reverse: isBack
  3041. });
  3042. }
  3043. });
  3044. }
  3045. }
  3046. //if to is defined, load it
  3047. if ( to ) {
  3048. // At this point, 'to' can be one of 3 things, a cached page element from
  3049. // a history stack entry, an id, or site-relative/absolute URL. If 'to' is
  3050. // an id, we need to resolve it against the documentBase, not the location.href,
  3051. // since the hashchange could've been the result of a forward/backward navigation
  3052. // that crosses from an external page/dialog to an internal page/dialog.
  3053. to = ( typeof to === "string" && !path.isPath( to ) ) ? ( path.makeUrlAbsolute( '#' + to, documentBase ) ) : to;
  3054. $.mobile.changePage( to, changePageOptions );
  3055. } else {
  3056. //there's no hash, go to the first page in the dom
  3057. $.mobile.changePage( $.mobile.firstPage, changePageOptions );
  3058. }
  3059. };
  3060. //hashchange event handler
  3061. $window.bind( "hashchange", function( e, triggered ) {
  3062. $.mobile._handleHashChange( location.hash );
  3063. });
  3064. //set page min-heights to be device specific
  3065. $( document ).bind( "pageshow", resetActivePageHeight );
  3066. $( window ).bind( "throttledresize", resetActivePageHeight );
  3067. };//_registerInternalEvents callback
  3068. })( jQuery );
  3069. ( function( $, window ) {
  3070. // For now, let's Monkeypatch this onto the end of $.mobile._registerInternalEvents
  3071. // Scope self to pushStateHandler so we can reference it sanely within the
  3072. // methods handed off as event handlers
  3073. var pushStateHandler = {},
  3074. self = pushStateHandler,
  3075. $win = $( window ),
  3076. url = $.mobile.path.parseUrl( location.href );
  3077. $.extend( pushStateHandler, {
  3078. // TODO move to a path helper, this is rather common functionality
  3079. initialFilePath: (function() {
  3080. return url.pathname + url.search;
  3081. })(),
  3082. initialHref: url.hrefNoHash,
  3083. state: function() {
  3084. return {
  3085. hash: location.hash || "#" + self.initialFilePath,
  3086. title: document.title,
  3087. // persist across refresh
  3088. initialHref: self.initialHref
  3089. };
  3090. },
  3091. resetUIKeys: function( url ) {
  3092. var dialog = $.mobile.dialogHashKey,
  3093. subkey = "&" + $.mobile.subPageUrlKey,
  3094. dialogIndex = url.indexOf( dialog );
  3095. if( dialogIndex > -1 ) {
  3096. url = url.slice( 0, dialogIndex ) + "#" + url.slice( dialogIndex );
  3097. } else if( url.indexOf( subkey ) > -1 ) {
  3098. url = url.split( subkey ).join( "#" + subkey );
  3099. }
  3100. return url;
  3101. },
  3102. hashValueAfterReset: function( url ) {
  3103. var resetUrl = self.resetUIKeys( url );
  3104. return $.mobile.path.parseUrl( resetUrl ).hash;
  3105. },
  3106. // TODO sort out a single barrier to hashchange functionality
  3107. nextHashChangePrevented: function( value ) {
  3108. $.mobile.urlHistory.ignoreNextHashChange = value;
  3109. self.onHashChangeDisabled = value;
  3110. },
  3111. // on hash change we want to clean up the url
  3112. // NOTE this takes place *after* the vanilla navigation hash change
  3113. // handling has taken place and set the state of the DOM
  3114. onHashChange: function( e ) {
  3115. // disable this hash change
  3116. if( self.onHashChangeDisabled ){
  3117. return;
  3118. }
  3119. var href, state,
  3120. hash = location.hash,
  3121. isPath = $.mobile.path.isPath( hash ),
  3122. resolutionUrl = isPath ? location.href : $.mobile.getDocumentUrl();
  3123. hash = isPath ? hash.replace( "#", "" ) : hash;
  3124. // propulate the hash when its not available
  3125. state = self.state();
  3126. // make the hash abolute with the current href
  3127. href = $.mobile.path.makeUrlAbsolute( hash, resolutionUrl );
  3128. if ( isPath ) {
  3129. href = self.resetUIKeys( href );
  3130. }
  3131. // replace the current url with the new href and store the state
  3132. // Note that in some cases we might be replacing an url with the
  3133. // same url. We do this anyways because we need to make sure that
  3134. // all of our history entries have a state object associated with
  3135. // them. This allows us to work around the case where window.history.back()
  3136. // is called to transition from an external page to an embedded page.
  3137. // In that particular case, a hashchange event is *NOT* generated by the browser.
  3138. // Ensuring each history entry has a state object means that onPopState()
  3139. // will always trigger our hashchange callback even when a hashchange event
  3140. // is not fired.
  3141. history.replaceState( state, document.title, href );
  3142. },
  3143. // on popstate (ie back or forward) we need to replace the hash that was there previously
  3144. // cleaned up by the additional hash handling
  3145. onPopState: function( e ) {
  3146. var poppedState = e.originalEvent.state,
  3147. timeout, fromHash, toHash, hashChanged;
  3148. // if there's no state its not a popstate we care about, eg chrome's initial popstate
  3149. if( poppedState ) {
  3150. // the active url in the history stack will still be from the previous state
  3151. // so we can use it to verify if a hashchange will be fired from the popstate
  3152. fromHash = self.hashValueAfterReset( $.mobile.urlHistory.getActive().url );
  3153. // the hash stored in the state popped off the stack will be our currenturl or
  3154. // the url to which we wish to navigate
  3155. toHash = self.hashValueAfterReset( poppedState.hash.replace("#", "") );
  3156. // if the hashes of the urls are different we must assume that the browser
  3157. // will fire a hashchange
  3158. hashChanged = fromHash !== toHash;
  3159. // unlock hash handling once the hashchange caused be the popstate has fired
  3160. if( hashChanged ) {
  3161. $win.one( "hashchange.pushstate", function() {
  3162. self.nextHashChangePrevented( false );
  3163. });
  3164. }
  3165. // enable hash handling for the the _handleHashChange call
  3166. self.nextHashChangePrevented( false );
  3167. // change the page based on the hash
  3168. $.mobile._handleHashChange( poppedState.hash );
  3169. // only prevent another hash change handling if a hash change will be fired
  3170. // by the browser
  3171. if( hashChanged ) {
  3172. // disable hash handling until one of the above timers fires
  3173. self.nextHashChangePrevented( true );
  3174. }
  3175. }
  3176. },
  3177. init: function() {
  3178. $win.bind( "hashchange", self.onHashChange );
  3179. // Handle popstate events the occur through history changes
  3180. $win.bind( "popstate", self.onPopState );
  3181. // if there's no hash, we need to replacestate for returning to home
  3182. if ( location.hash === "" ) {
  3183. history.replaceState( self.state(), document.title, location.href );
  3184. }
  3185. }
  3186. });
  3187. $( function() {
  3188. if( $.mobile.pushStateEnabled && $.support.pushState ){
  3189. pushStateHandler.init();
  3190. }
  3191. });
  3192. })( jQuery, this );
  3193. /*
  3194. * fallback transition for pop in non-3D supporting browsers (which tend to handle complex transitions poorly in general
  3195. */
  3196. (function( $, window, undefined ) {
  3197. $.mobile.transitionFallbacks.pop = "fade";
  3198. })( jQuery, this );
  3199. /*
  3200. * fallback transition for slide in non-3D supporting browsers (which tend to handle complex transitions poorly in general
  3201. */
  3202. (function( $, window, undefined ) {
  3203. // Use the simultaneous transition handler for slide transitions
  3204. $.mobile.transitionHandlers.slide = $.mobile.transitionHandlers.simultaneous;
  3205. // Set the slide transition's fallback to "fade"
  3206. $.mobile.transitionFallbacks.slide = "fade";
  3207. })( jQuery, this );
  3208. /*
  3209. * fallback transition for slidedown in non-3D supporting browsers (which tend to handle complex transitions poorly in general
  3210. */
  3211. (function( $, window, undefined ) {
  3212. $.mobile.transitionFallbacks.slidedown = "fade";
  3213. })( jQuery, this );
  3214. /*
  3215. * fallback transition for slideup in non-3D supporting browsers (which tend to handle complex transitions poorly in general
  3216. */
  3217. (function( $, window, undefined ) {
  3218. $.mobile.transitionFallbacks.slideup = "fade";
  3219. })( jQuery, this );
  3220. /*
  3221. * fallback transition for flip in non-3D supporting browsers (which tend to handle complex transitions poorly in general
  3222. */
  3223. (function( $, window, undefined ) {
  3224. $.mobile.transitionFallbacks.flip = "fade";
  3225. })( jQuery, this );
  3226. /*
  3227. * fallback transition for flow in non-3D supporting browsers (which tend to handle complex transitions poorly in general
  3228. */
  3229. (function( $, window, undefined ) {
  3230. $.mobile.transitionFallbacks.flow = "fade";
  3231. })( jQuery, this );
  3232. /*
  3233. * fallback transition for turn in non-3D supporting browsers (which tend to handle complex transitions poorly in general
  3234. */
  3235. (function( $, window, undefined ) {
  3236. $.mobile.transitionFallbacks.turn = "fade";
  3237. })( jQuery, this );
  3238. (function( $, undefined ) {
  3239. $.mobile.page.prototype.options.degradeInputs = {
  3240. color: false,
  3241. date: false,
  3242. datetime: false,
  3243. "datetime-local": false,
  3244. email: false,
  3245. month: false,
  3246. number: false,
  3247. range: "number",
  3248. search: "text",
  3249. tel: false,
  3250. time: false,
  3251. url: false,
  3252. week: false
  3253. };
  3254. //auto self-init widgets
  3255. $( document ).bind( "pagecreate create", function( e ){
  3256. var page = $.mobile.closestPageData($(e.target)), options;
  3257. if( !page ) {
  3258. return;
  3259. }
  3260. options = page.options;
  3261. // degrade inputs to avoid poorly implemented native functionality
  3262. $( e.target ).find( "input" ).not( page.keepNativeSelector() ).each(function() {
  3263. var $this = $( this ),
  3264. type = this.getAttribute( "type" ),
  3265. optType = options.degradeInputs[ type ] || "text";
  3266. if ( options.degradeInputs[ type ] ) {
  3267. var html = $( "<div>" ).html( $this.clone() ).html(),
  3268. // In IE browsers, the type sometimes doesn't exist in the cloned markup, so we replace the closing tag instead
  3269. hasType = html.indexOf( " type=" ) > -1,
  3270. findstr = hasType ? /\s+type=["']?\w+['"]?/ : /\/?>/,
  3271. repstr = " type=\"" + optType + "\" data-" + $.mobile.ns + "type=\"" + type + "\"" + ( hasType ? "" : ">" );
  3272. $this.replaceWith( html.replace( findstr, repstr ) );
  3273. }
  3274. });
  3275. });
  3276. })( jQuery );
  3277. (function( $, window, undefined ) {
  3278. $.widget( "mobile.dialog", $.mobile.widget, {
  3279. options: {
  3280. closeBtnText : "Close",
  3281. overlayTheme : "a",
  3282. initSelector : ":jqmData(role='dialog')"
  3283. },
  3284. _create: function() {
  3285. var self = this,
  3286. $el = this.element,
  3287. headerCloseButton = $( "<a href='#' data-" + $.mobile.ns + "icon='delete' data-" + $.mobile.ns + "iconpos='notext'>"+ this.options.closeBtnText + "</a>" ),
  3288. dialogWrap = $("<div/>", {
  3289. "role" : "dialog",
  3290. "class" : "ui-dialog-contain ui-corner-all ui-overlay-shadow"
  3291. });
  3292. $el.addClass( "ui-dialog ui-overlay-" + this.options.overlayTheme );
  3293. // Class the markup for dialog styling
  3294. // Set aria role
  3295. $el
  3296. .wrapInner( dialogWrap )
  3297. .children()
  3298. .find( ":jqmData(role='header')" )
  3299. .prepend( headerCloseButton )
  3300. .end()
  3301. .children( ':first-child')
  3302. .addClass( "ui-corner-top" )
  3303. .end()
  3304. .children( ":last-child" )
  3305. .addClass( "ui-corner-bottom" );
  3306. // this must be an anonymous function so that select menu dialogs can replace
  3307. // the close method. This is a change from previously just defining data-rel=back
  3308. // on the button and letting nav handle it
  3309. //
  3310. // Use click rather than vclick in order to prevent the possibility of unintentionally
  3311. // reopening the dialog if the dialog opening item was directly under the close button.
  3312. headerCloseButton.bind( "click", function() {
  3313. self.close();
  3314. });
  3315. /* bind events
  3316. - clicks and submits should use the closing transition that the dialog opened with
  3317. unless a data-transition is specified on the link/form
  3318. - if the click was on the close button, or the link has a data-rel="back" it'll go back in history naturally
  3319. */
  3320. $el.bind( "vclick submit", function( event ) {
  3321. var $target = $( event.target ).closest( event.type === "vclick" ? "a" : "form" ),
  3322. active;
  3323. if ( $target.length && !$target.jqmData( "transition" ) ) {
  3324. active = $.mobile.urlHistory.getActive() || {};
  3325. $target.attr( "data-" + $.mobile.ns + "transition", ( active.transition || $.mobile.defaultDialogTransition ) )
  3326. .attr( "data-" + $.mobile.ns + "direction", "reverse" );
  3327. }
  3328. })
  3329. .bind( "pagehide", function( e, ui ) {
  3330. $( this ).find( "." + $.mobile.activeBtnClass ).removeClass( $.mobile.activeBtnClass );
  3331. })
  3332. // Override the theme set by the page plugin on pageshow
  3333. .bind( "pagebeforeshow", function(){
  3334. if( self.options.overlayTheme ){
  3335. self.element
  3336. .page( "removeContainerBackground" )
  3337. .page( "setContainerBackground", self.options.overlayTheme );
  3338. }
  3339. });
  3340. },
  3341. // Close method goes back in history
  3342. close: function() {
  3343. window.history.back();
  3344. }
  3345. });
  3346. //auto self-init widgets
  3347. $( document ).delegate( $.mobile.dialog.prototype.options.initSelector, "pagecreate", function(){
  3348. $.mobile.dialog.prototype.enhance( this );
  3349. });
  3350. })( jQuery, this );
  3351. (function( $, undefined ) {
  3352. $.fn.fieldcontain = function( options ) {
  3353. return this.addClass( "ui-field-contain ui-body ui-br" );
  3354. };
  3355. //auto self-init widgets
  3356. $( document ).bind( "pagecreate create", function( e ){
  3357. $( ":jqmData(role='fieldcontain')", e.target ).jqmEnhanceable().fieldcontain();
  3358. });
  3359. })( jQuery );
  3360. (function( $, undefined ) {
  3361. $.fn.grid = function( options ) {
  3362. return this.each(function() {
  3363. var $this = $( this ),
  3364. o = $.extend({
  3365. grid: null
  3366. },options),
  3367. $kids = $this.children(),
  3368. gridCols = {solo:1, a:2, b:3, c:4, d:5},
  3369. grid = o.grid,
  3370. iterator;
  3371. if ( !grid ) {
  3372. if ( $kids.length <= 5 ) {
  3373. for ( var letter in gridCols ) {
  3374. if ( gridCols[ letter ] === $kids.length ) {
  3375. grid = letter;
  3376. }
  3377. }
  3378. } else {
  3379. grid = "a";
  3380. }
  3381. }
  3382. iterator = gridCols[grid];
  3383. $this.addClass( "ui-grid-" + grid );
  3384. $kids.filter( ":nth-child(" + iterator + "n+1)" ).addClass( "ui-block-a" );
  3385. if ( iterator > 1 ) {
  3386. $kids.filter( ":nth-child(" + iterator + "n+2)" ).addClass( "ui-block-b" );
  3387. }
  3388. if ( iterator > 2 ) {
  3389. $kids.filter( ":nth-child(3n+3)" ).addClass( "ui-block-c" );
  3390. }
  3391. if ( iterator > 3 ) {
  3392. $kids.filter( ":nth-child(4n+4)" ).addClass( "ui-block-d" );
  3393. }
  3394. if ( iterator > 4 ) {
  3395. $kids.filter( ":nth-child(5n+5)" ).addClass( "ui-block-e" );
  3396. }
  3397. });
  3398. };
  3399. })( jQuery );
  3400. (function( $, undefined ) {
  3401. $( document ).bind( "pagecreate create", function( e ){
  3402. $( ":jqmData(role='nojs')", e.target ).addClass( "ui-nojs" );
  3403. });
  3404. })( jQuery );
  3405. ( function( $, undefined ) {
  3406. $.fn.buttonMarkup = function( options ) {
  3407. var $workingSet = this;
  3408. // Enforce options to be of type string
  3409. options = ( options && ( $.type( options ) == "object" ) )? options : {};
  3410. for ( var i = 0; i < $workingSet.length; i++ ) {
  3411. var el = $workingSet.eq( i ),
  3412. e = el[ 0 ],
  3413. o = $.extend( {}, $.fn.buttonMarkup.defaults, {
  3414. icon: options.icon !== undefined ? options.icon : el.jqmData( "icon" ),
  3415. iconpos: options.iconpos !== undefined ? options.iconpos : el.jqmData( "iconpos" ),
  3416. theme: options.theme !== undefined ? options.theme : el.jqmData( "theme" ) || $.mobile.getInheritedTheme( el, "c" ),
  3417. inline: options.inline !== undefined ? options.inline : el.jqmData( "inline" ),
  3418. shadow: options.shadow !== undefined ? options.shadow : el.jqmData( "shadow" ),
  3419. corners: options.corners !== undefined ? options.corners : el.jqmData( "corners" ),
  3420. iconshadow: options.iconshadow !== undefined ? options.iconshadow : el.jqmData( "iconshadow" ),
  3421. mini: options.mini !== undefined ? options.mini : el.jqmData( "mini" )
  3422. }, options ),
  3423. // Classes Defined
  3424. innerClass = "ui-btn-inner",
  3425. textClass = "ui-btn-text",
  3426. buttonClass, iconClass,
  3427. // Button inner markup
  3428. buttonInner,
  3429. buttonText,
  3430. buttonIcon,
  3431. buttonElements;
  3432. $.each(o, function(key, value) {
  3433. e.setAttribute( "data-" + $.mobile.ns + key, value );
  3434. el.jqmData(key, value);
  3435. });
  3436. // Check if this element is already enhanced
  3437. buttonElements = $.data(((e.tagName === "INPUT" || e.tagName === "BUTTON") ? e.parentNode : e), "buttonElements");
  3438. if (buttonElements) {
  3439. e = buttonElements.outer;
  3440. el = $(e);
  3441. buttonInner = buttonElements.inner;
  3442. buttonText = buttonElements.text;
  3443. // We will recreate this icon below
  3444. $(buttonElements.icon).remove();
  3445. buttonElements.icon = null;
  3446. }
  3447. else {
  3448. buttonInner = document.createElement( o.wrapperEls );
  3449. buttonText = document.createElement( o.wrapperEls );
  3450. }
  3451. buttonIcon = o.icon ? document.createElement( "span" ) : null;
  3452. if ( attachEvents && !buttonElements) {
  3453. attachEvents();
  3454. }
  3455. // if not, try to find closest theme container
  3456. if ( !o.theme ) {
  3457. o.theme = $.mobile.getInheritedTheme( el, "c" );
  3458. }
  3459. buttonClass = "ui-btn ui-btn-up-" + o.theme;
  3460. buttonClass += o.inline ? " ui-btn-inline" : "";
  3461. buttonClass += o.shadow ? " ui-shadow" : "";
  3462. buttonClass += o.corners ? " ui-btn-corner-all" : "";
  3463. if ( o.mini !== undefined ) {
  3464. // Used to control styling in headers/footers, where buttons default to `mini` style.
  3465. buttonClass += o.mini ? " ui-mini" : " ui-fullsize";
  3466. }
  3467. if ( o.inline !== undefined ) {
  3468. // Used to control styling in headers/footers, where buttons default to `mini` style.
  3469. buttonClass += o.inline === false ? " ui-btn-block" : " ui-btn-inline";
  3470. }
  3471. if ( o.icon ) {
  3472. o.icon = "ui-icon-" + o.icon;
  3473. o.iconpos = o.iconpos || "left";
  3474. iconClass = "ui-icon " + o.icon;
  3475. if ( o.iconshadow ) {
  3476. iconClass += " ui-icon-shadow";
  3477. }
  3478. }
  3479. if ( o.iconpos ) {
  3480. buttonClass += " ui-btn-icon-" + o.iconpos;
  3481. if ( o.iconpos == "notext" && !el.attr( "title" ) ) {
  3482. el.attr( "title", el.getEncodedText() );
  3483. }
  3484. }
  3485. innerClass += o.corners ? " ui-btn-corner-all" : "";
  3486. if ( o.iconpos && o.iconpos === "notext" && !el.attr( "title" ) ) {
  3487. el.attr( "title", el.getEncodedText() );
  3488. }
  3489. if ( buttonElements ) {
  3490. el.removeClass( buttonElements.bcls || "" );
  3491. }
  3492. el.removeClass( "ui-link" ).addClass( buttonClass );
  3493. buttonInner.className = innerClass;
  3494. buttonText.className = textClass;
  3495. if ( !buttonElements ) {
  3496. buttonInner.appendChild( buttonText );
  3497. }
  3498. if ( buttonIcon ) {
  3499. buttonIcon.className = iconClass;
  3500. if ( !(buttonElements && buttonElements.icon) ) {
  3501. buttonIcon.appendChild( document.createTextNode("\u00a0") );
  3502. buttonInner.appendChild( buttonIcon );
  3503. }
  3504. }
  3505. while ( e.firstChild && !buttonElements) {
  3506. buttonText.appendChild( e.firstChild );
  3507. }
  3508. if ( !buttonElements ) {
  3509. e.appendChild( buttonInner );
  3510. }
  3511. // Assign a structure containing the elements of this button to the elements of this button. This
  3512. // will allow us to recognize this as an already-enhanced button in future calls to buttonMarkup().
  3513. buttonElements = {
  3514. bcls : buttonClass,
  3515. outer : e,
  3516. inner : buttonInner,
  3517. text : buttonText,
  3518. icon : buttonIcon
  3519. };
  3520. $.data(e, 'buttonElements', buttonElements);
  3521. $.data(buttonInner, 'buttonElements', buttonElements);
  3522. $.data(buttonText, 'buttonElements', buttonElements);
  3523. if (buttonIcon) {
  3524. $.data(buttonIcon, 'buttonElements', buttonElements);
  3525. }
  3526. }
  3527. return this;
  3528. };
  3529. $.fn.buttonMarkup.defaults = {
  3530. corners: true,
  3531. shadow: true,
  3532. iconshadow: true,
  3533. wrapperEls: "span"
  3534. };
  3535. function closestEnabledButton( element ) {
  3536. var cname;
  3537. while ( element ) {
  3538. // Note that we check for typeof className below because the element we
  3539. // handed could be in an SVG DOM where className on SVG elements is defined to
  3540. // be of a different type (SVGAnimatedString). We only operate on HTML DOM
  3541. // elements, so we look for plain "string".
  3542. cname = ( typeof element.className === 'string' ) && (element.className + ' ');
  3543. if ( cname && cname.indexOf("ui-btn ") > -1 && cname.indexOf("ui-disabled ") < 0 ) {
  3544. break;
  3545. }
  3546. element = element.parentNode;
  3547. }
  3548. return element;
  3549. }
  3550. var attachEvents = function() {
  3551. var hoverDelay = $.mobile.buttonMarkup.hoverDelay, hov, foc;
  3552. $( document ).bind( {
  3553. "vmousedown vmousecancel vmouseup vmouseover vmouseout focus blur scrollstart": function( event ) {
  3554. var theme,
  3555. $btn = $( closestEnabledButton( event.target ) ),
  3556. evt = event.type;
  3557. if ( $btn.length ) {
  3558. theme = $btn.attr( "data-" + $.mobile.ns + "theme" );
  3559. if ( evt === "vmousedown" ) {
  3560. if ( $.support.touch ) {
  3561. hov = setTimeout(function() {
  3562. $btn.removeClass( "ui-btn-up-" + theme ).addClass( "ui-btn-down-" + theme );
  3563. }, hoverDelay );
  3564. } else {
  3565. $btn.removeClass( "ui-btn-up-" + theme ).addClass( "ui-btn-down-" + theme );
  3566. }
  3567. } else if ( evt === "vmousecancel" || evt === "vmouseup" ) {
  3568. $btn.removeClass( "ui-btn-down-" + theme ).addClass( "ui-btn-up-" + theme );
  3569. } else if ( evt === "vmouseover" || evt === "focus" ) {
  3570. if ( $.support.touch ) {
  3571. foc = setTimeout(function() {
  3572. $btn.removeClass( "ui-btn-up-" + theme ).addClass( "ui-btn-hover-" + theme );
  3573. }, hoverDelay );
  3574. } else {
  3575. $btn.removeClass( "ui-btn-up-" + theme ).addClass( "ui-btn-hover-" + theme );
  3576. }
  3577. } else if ( evt === "vmouseout" || evt === "blur" || evt === "scrollstart" ) {
  3578. $btn.removeClass( "ui-btn-hover-" + theme + " ui-btn-down-" + theme ).addClass( "ui-btn-up-" + theme );
  3579. if ( hov ) {
  3580. clearTimeout( hov );
  3581. }
  3582. if ( foc ) {
  3583. clearTimeout( foc );
  3584. }
  3585. }
  3586. }
  3587. },
  3588. "focusin focus": function( event ){
  3589. $( closestEnabledButton( event.target ) ).addClass( $.mobile.focusClass );
  3590. },
  3591. "focusout blur": function( event ){
  3592. $( closestEnabledButton( event.target ) ).removeClass( $.mobile.focusClass );
  3593. }
  3594. });
  3595. attachEvents = null;
  3596. };
  3597. //links in bars, or those with data-role become buttons
  3598. //auto self-init widgets
  3599. $( document ).bind( "pagecreate create", function( e ){
  3600. $( ":jqmData(role='button'), .ui-bar > a, .ui-header > a, .ui-footer > a, .ui-bar > :jqmData(role='controlgroup') > a", e.target )
  3601. .not( ".ui-btn, :jqmData(role='none'), :jqmData(role='nojs')" )
  3602. .buttonMarkup();
  3603. });
  3604. })( jQuery );
  3605. (function( $, undefined ) {
  3606. $.mobile.page.prototype.options.backBtnText = "Back";
  3607. $.mobile.page.prototype.options.addBackBtn = false;
  3608. $.mobile.page.prototype.options.backBtnTheme = null;
  3609. $.mobile.page.prototype.options.headerTheme = "a";
  3610. $.mobile.page.prototype.options.footerTheme = "a";
  3611. $.mobile.page.prototype.options.contentTheme = null;
  3612. $( document ).delegate( ":jqmData(role='page'), :jqmData(role='dialog')", "pagecreate", function( e ) {
  3613. var $page = $( this ),
  3614. o = $page.data( "page" ).options,
  3615. pageRole = $page.jqmData( "role" ),
  3616. pageTheme = o.theme;
  3617. $( ":jqmData(role='header'), :jqmData(role='footer'), :jqmData(role='content')", this )
  3618. .jqmEnhanceable()
  3619. .each(function() {
  3620. var $this = $( this ),
  3621. role = $this.jqmData( "role" ),
  3622. theme = $this.jqmData( "theme" ),
  3623. contentTheme = theme || o.contentTheme || ( pageRole === "dialog" && pageTheme ),
  3624. $headeranchors,
  3625. leftbtn,
  3626. rightbtn,
  3627. backBtn;
  3628. $this.addClass( "ui-" + role );
  3629. //apply theming and markup modifications to page,header,content,footer
  3630. if ( role === "header" || role === "footer" ) {
  3631. var thisTheme = theme || ( role === "header" ? o.headerTheme : o.footerTheme ) || pageTheme;
  3632. $this
  3633. //add theme class
  3634. .addClass( "ui-bar-" + thisTheme )
  3635. // Add ARIA role
  3636. .attr( "role", role === "header" ? "banner" : "contentinfo" );
  3637. if( role === "header") {
  3638. // Right,left buttons
  3639. $headeranchors = $this.children( "a" );
  3640. leftbtn = $headeranchors.hasClass( "ui-btn-left" );
  3641. rightbtn = $headeranchors.hasClass( "ui-btn-right" );
  3642. leftbtn = leftbtn || $headeranchors.eq( 0 ).not( ".ui-btn-right" ).addClass( "ui-btn-left" ).length;
  3643. rightbtn = rightbtn || $headeranchors.eq( 1 ).addClass( "ui-btn-right" ).length;
  3644. }
  3645. // Auto-add back btn on pages beyond first view
  3646. if ( o.addBackBtn &&
  3647. role === "header" &&
  3648. $( ".ui-page" ).length > 1 &&
  3649. $page.jqmData( "url" ) !== $.mobile.path.stripHash( location.hash ) &&
  3650. !leftbtn ) {
  3651. backBtn = $( "<a href='#' class='ui-btn-left' data-"+ $.mobile.ns +"rel='back' data-"+ $.mobile.ns +"icon='arrow-l'>"+ o.backBtnText +"</a>" )
  3652. // If theme is provided, override default inheritance
  3653. .attr( "data-"+ $.mobile.ns +"theme", o.backBtnTheme || thisTheme )
  3654. .prependTo( $this );
  3655. }
  3656. // Page title
  3657. $this.children( "h1, h2, h3, h4, h5, h6" )
  3658. .addClass( "ui-title" )
  3659. // Regardless of h element number in src, it becomes h1 for the enhanced page
  3660. .attr({
  3661. "role": "heading",
  3662. "aria-level": "1"
  3663. });
  3664. } else if ( role === "content" ) {
  3665. if ( contentTheme ) {
  3666. $this.addClass( "ui-body-" + ( contentTheme ) );
  3667. }
  3668. // Add ARIA role
  3669. $this.attr( "role", "main" );
  3670. }
  3671. });
  3672. });
  3673. })( jQuery );
  3674. (function( $, undefined ) {
  3675. $.widget( "mobile.collapsible", $.mobile.widget, {
  3676. options: {
  3677. expandCueText: " click to expand contents",
  3678. collapseCueText: " click to collapse contents",
  3679. collapsed: true,
  3680. heading: "h1,h2,h3,h4,h5,h6,legend",
  3681. theme: null,
  3682. contentTheme: null,
  3683. iconTheme: "d",
  3684. mini: false,
  3685. initSelector: ":jqmData(role='collapsible')"
  3686. },
  3687. _create: function() {
  3688. var $el = this.element,
  3689. o = this.options,
  3690. collapsible = $el.addClass( "ui-collapsible" ),
  3691. collapsibleHeading = $el.children( o.heading ).first(),
  3692. collapsibleContent = collapsible.wrapInner( "<div class='ui-collapsible-content'></div>" ).find( ".ui-collapsible-content" ),
  3693. collapsibleSet = $el.closest( ":jqmData(role='collapsible-set')" ).addClass( "ui-collapsible-set" );
  3694. // Replace collapsibleHeading if it's a legend
  3695. if ( collapsibleHeading.is( "legend" ) ) {
  3696. collapsibleHeading = $( "<div role='heading'>"+ collapsibleHeading.html() +"</div>" ).insertBefore( collapsibleHeading );
  3697. collapsibleHeading.next().remove();
  3698. }
  3699. // If we are in a collapsible set
  3700. if ( collapsibleSet.length ) {
  3701. // Inherit the theme from collapsible-set
  3702. if ( !o.theme ) {
  3703. o.theme = collapsibleSet.jqmData("theme") || $.mobile.getInheritedTheme( collapsibleSet, "c" );
  3704. }
  3705. // Inherit the content-theme from collapsible-set
  3706. if ( !o.contentTheme ) {
  3707. o.contentTheme = collapsibleSet.jqmData( "content-theme" );
  3708. }
  3709. // Gets the preference icon position in the set
  3710. if ( !o.iconPos ) {
  3711. o.iconPos = collapsibleSet.jqmData( "iconpos" );
  3712. }
  3713. if( !o.mini ) {
  3714. o.mini = collapsibleSet.jqmData( "mini" );
  3715. }
  3716. }
  3717. collapsibleContent.addClass( ( o.contentTheme ) ? ( "ui-body-" + o.contentTheme ) : "");
  3718. collapsibleHeading
  3719. //drop heading in before content
  3720. .insertBefore( collapsibleContent )
  3721. //modify markup & attributes
  3722. .addClass( "ui-collapsible-heading" )
  3723. .append( "<span class='ui-collapsible-heading-status'></span>" )
  3724. .wrapInner( "<a href='#' class='ui-collapsible-heading-toggle'></a>" )
  3725. .find( "a" )
  3726. .first()
  3727. .buttonMarkup({
  3728. shadow: false,
  3729. corners: false,
  3730. iconpos: $el.jqmData( "iconpos" ) || o.iconPos || "left",
  3731. icon: "plus",
  3732. mini: o.mini,
  3733. theme: o.theme
  3734. })
  3735. .add( ".ui-btn-inner", $el )
  3736. .addClass( "ui-corner-top ui-corner-bottom" );
  3737. //events
  3738. collapsible
  3739. .bind( "expand collapse", function( event ) {
  3740. if ( !event.isDefaultPrevented() ) {
  3741. event.preventDefault();
  3742. var $this = $( this ),
  3743. isCollapse = ( event.type === "collapse" ),
  3744. contentTheme = o.contentTheme;
  3745. collapsibleHeading
  3746. .toggleClass( "ui-collapsible-heading-collapsed", isCollapse)
  3747. .find( ".ui-collapsible-heading-status" )
  3748. .text( isCollapse ? o.expandCueText : o.collapseCueText )
  3749. .end()
  3750. .find( ".ui-icon" )
  3751. .toggleClass( "ui-icon-minus", !isCollapse )
  3752. .toggleClass( "ui-icon-plus", isCollapse );
  3753. $this.toggleClass( "ui-collapsible-collapsed", isCollapse );
  3754. collapsibleContent.toggleClass( "ui-collapsible-content-collapsed", isCollapse ).attr( "aria-hidden", isCollapse );
  3755. if ( contentTheme && ( !collapsibleSet.length || collapsible.jqmData( "collapsible-last" ) ) ) {
  3756. collapsibleHeading
  3757. .find( "a" ).first().add( collapsibleHeading.find( ".ui-btn-inner" ) )
  3758. .toggleClass( "ui-corner-bottom", isCollapse );
  3759. collapsibleContent.toggleClass( "ui-corner-bottom", !isCollapse );
  3760. }
  3761. collapsibleContent.trigger( "updatelayout" );
  3762. }
  3763. })
  3764. .trigger( o.collapsed ? "collapse" : "expand" );
  3765. collapsibleHeading
  3766. .bind( "click", function( event ) {
  3767. var type = collapsibleHeading.is( ".ui-collapsible-heading-collapsed" ) ?
  3768. "expand" : "collapse";
  3769. collapsible.trigger( type );
  3770. event.preventDefault();
  3771. });
  3772. }
  3773. });
  3774. //auto self-init widgets
  3775. $( document ).bind( "pagecreate create", function( e ){
  3776. $.mobile.collapsible.prototype.enhanceWithin( e.target );
  3777. });
  3778. })( jQuery );
  3779. (function( $, undefined ) {
  3780. $.widget( "mobile.collapsibleset", $.mobile.widget, {
  3781. options: {
  3782. initSelector: ":jqmData(role='collapsible-set')"
  3783. },
  3784. _create: function() {
  3785. var $el = this.element.addClass( "ui-collapsible-set" ),
  3786. o = this.options;
  3787. // Inherit the theme from collapsible-set
  3788. if ( !o.theme ) {
  3789. o.theme = $.mobile.getInheritedTheme( $el, "c" );
  3790. }
  3791. // Inherit the content-theme from collapsible-set
  3792. if ( !o.contentTheme ) {
  3793. o.contentTheme = $el.jqmData( "content-theme" );
  3794. }
  3795. if ( !o.corners ) {
  3796. o.corners = $el.jqmData( "corners" ) === undefined ? true : false;
  3797. }
  3798. // Initialize the collapsible set if it's not already initialized
  3799. if ( !$el.jqmData( "collapsiblebound" ) ) {
  3800. $el
  3801. .jqmData( "collapsiblebound", true )
  3802. .bind( "expand collapse", function( event ) {
  3803. var isCollapse = ( event.type === "collapse" ),
  3804. collapsible = $( event.target ).closest( ".ui-collapsible" ),
  3805. widget = collapsible.data( "collapsible" ),
  3806. contentTheme = widget.options.contentTheme;
  3807. if ( contentTheme && collapsible.jqmData( "collapsible-last" ) ) {
  3808. collapsible.find( widget.options.heading ).first()
  3809. .find( "a" ).first()
  3810. .add( ".ui-btn-inner" )
  3811. .toggleClass( "ui-corner-bottom", isCollapse );
  3812. collapsible.find( ".ui-collapsible-content" ).toggleClass( "ui-corner-bottom", !isCollapse );
  3813. }
  3814. })
  3815. .bind( "expand", function( event ) {
  3816. $( event.target )
  3817. .closest( ".ui-collapsible" )
  3818. .siblings( ".ui-collapsible" )
  3819. .trigger( "collapse" );
  3820. });
  3821. }
  3822. },
  3823. _init: function() {
  3824. this.refresh();
  3825. },
  3826. refresh: function() {
  3827. var $el = this.element,
  3828. o = this.options,
  3829. collapsiblesInSet = $el.children( ":jqmData(role='collapsible')" );
  3830. $.mobile.collapsible.prototype.enhance( collapsiblesInSet.not( ".ui-collapsible" ) );
  3831. // clean up borders
  3832. collapsiblesInSet.each( function() {
  3833. $( this ).find( $.mobile.collapsible.prototype.options.heading )
  3834. .find( "a" ).first()
  3835. .add( ".ui-btn-inner" )
  3836. .removeClass( "ui-corner-top ui-corner-bottom" );
  3837. });
  3838. collapsiblesInSet.first()
  3839. .find( "a" )
  3840. .first()
  3841. .addClass( o.corners ? "ui-corner-top" : "" )
  3842. .find( ".ui-btn-inner" )
  3843. .addClass( "ui-corner-top" );
  3844. collapsiblesInSet.last()
  3845. .jqmData( "collapsible-last", true )
  3846. .find( "a" )
  3847. .first()
  3848. .addClass( o.corners ? "ui-corner-bottom" : "" )
  3849. .find( ".ui-btn-inner" )
  3850. .addClass( "ui-corner-bottom" );
  3851. }
  3852. });
  3853. //auto self-init widgets
  3854. $( document ).bind( "pagecreate create", function( e ){
  3855. $.mobile.collapsibleset.prototype.enhanceWithin( e.target );
  3856. });
  3857. })( jQuery );
  3858. (function( $, undefined ) {
  3859. $.widget( "mobile.navbar", $.mobile.widget, {
  3860. options: {
  3861. iconpos: "top",
  3862. grid: null,
  3863. initSelector: ":jqmData(role='navbar')"
  3864. },
  3865. _create: function(){
  3866. var $navbar = this.element,
  3867. $navbtns = $navbar.find( "a" ),
  3868. iconpos = $navbtns.filter( ":jqmData(icon)" ).length ?
  3869. this.options.iconpos : undefined;
  3870. $navbar.addClass( "ui-navbar" )
  3871. .attr( "role","navigation" )
  3872. .find( "ul" )
  3873. .jqmEnhanceable()
  3874. .grid({ grid: this.options.grid });
  3875. if ( !iconpos ) {
  3876. $navbar.addClass( "ui-navbar-noicons" );
  3877. }
  3878. $navbtns.buttonMarkup({
  3879. corners: false,
  3880. shadow: false,
  3881. inline: true,
  3882. iconpos: iconpos
  3883. });
  3884. $navbar.delegate( "a", "vclick", function( event ) {
  3885. if( !$(event.target).hasClass("ui-disabled") ) {
  3886. $navbtns.removeClass( $.mobile.activeBtnClass );
  3887. $( this ).addClass( $.mobile.activeBtnClass );
  3888. }
  3889. });
  3890. // Buttons in the navbar with ui-state-persist class should regain their active state before page show
  3891. $navbar.closest( ".ui-page" ).bind( "pagebeforeshow", function() {
  3892. $navbtns.filter( ".ui-state-persist" ).addClass( $.mobile.activeBtnClass );
  3893. });
  3894. }
  3895. });
  3896. //auto self-init widgets
  3897. $( document ).bind( "pagecreate create", function( e ){
  3898. $.mobile.navbar.prototype.enhanceWithin( e.target );
  3899. });
  3900. })( jQuery );
  3901. (function( $, undefined ) {
  3902. //Keeps track of the number of lists per page UID
  3903. //This allows support for multiple nested list in the same page
  3904. //https://github.com/jquery/jquery-mobile/issues/1617
  3905. var listCountPerPage = {};
  3906. $.widget( "mobile.listview", $.mobile.widget, {
  3907. options: {
  3908. theme: null,
  3909. countTheme: "c",
  3910. headerTheme: "b",
  3911. dividerTheme: "b",
  3912. splitIcon: "arrow-r",
  3913. splitTheme: "b",
  3914. mini: false,
  3915. inset: false,
  3916. initSelector: ":jqmData(role='listview')"
  3917. },
  3918. _create: function() {
  3919. var t = this,
  3920. listviewClasses = "";
  3921. listviewClasses += t.options.inset ? " ui-listview-inset ui-corner-all ui-shadow " : "";
  3922. listviewClasses += t.element.jqmData( "mini" ) || t.options.mini === true ? " ui-mini" : "";
  3923. // create listview markup
  3924. t.element.addClass(function( i, orig ) {
  3925. return orig + " ui-listview " + listviewClasses;
  3926. });
  3927. t.refresh( true );
  3928. },
  3929. _removeCorners: function( li, which ) {
  3930. var top = "ui-corner-top ui-corner-tr ui-corner-tl",
  3931. bot = "ui-corner-bottom ui-corner-br ui-corner-bl";
  3932. li = li.add( li.find( ".ui-btn-inner, .ui-li-link-alt, .ui-li-thumb" ) );
  3933. if ( which === "top" ) {
  3934. li.removeClass( top );
  3935. } else if ( which === "bottom" ) {
  3936. li.removeClass( bot );
  3937. } else {
  3938. li.removeClass( top + " " + bot );
  3939. }
  3940. },
  3941. _refreshCorners: function( create ) {
  3942. var $li,
  3943. $visibleli,
  3944. $topli,
  3945. $bottomli;
  3946. if ( this.options.inset ) {
  3947. $li = this.element.children( "li" );
  3948. // at create time the li are not visible yet so we need to rely on .ui-screen-hidden
  3949. $visibleli = create?$li.not( ".ui-screen-hidden" ):$li.filter( ":visible" );
  3950. this._removeCorners( $li );
  3951. // Select the first visible li element
  3952. $topli = $visibleli.first()
  3953. .addClass( "ui-corner-top" );
  3954. $topli.add( $topli.find( ".ui-btn-inner" )
  3955. .not( ".ui-li-link-alt span:first-child" ) )
  3956. .addClass( "ui-corner-top" )
  3957. .end()
  3958. .find( ".ui-li-link-alt, .ui-li-link-alt span:first-child" )
  3959. .addClass( "ui-corner-tr" )
  3960. .end()
  3961. .find( ".ui-li-thumb" )
  3962. .not(".ui-li-icon")
  3963. .addClass( "ui-corner-tl" );
  3964. // Select the last visible li element
  3965. $bottomli = $visibleli.last()
  3966. .addClass( "ui-corner-bottom" );
  3967. $bottomli.add( $bottomli.find( ".ui-btn-inner" ) )
  3968. .find( ".ui-li-link-alt" )
  3969. .addClass( "ui-corner-br" )
  3970. .end()
  3971. .find( ".ui-li-thumb" )
  3972. .not(".ui-li-icon")
  3973. .addClass( "ui-corner-bl" );
  3974. }
  3975. if ( !create ) {
  3976. this.element.trigger( "updatelayout" );
  3977. }
  3978. },
  3979. // This is a generic utility method for finding the first
  3980. // node with a given nodeName. It uses basic DOM traversal
  3981. // to be fast and is meant to be a substitute for simple
  3982. // $.fn.closest() and $.fn.children() calls on a single
  3983. // element. Note that callers must pass both the lowerCase
  3984. // and upperCase version of the nodeName they are looking for.
  3985. // The main reason for this is that this function will be
  3986. // called many times and we want to avoid having to lowercase
  3987. // the nodeName from the element every time to ensure we have
  3988. // a match. Note that this function lives here for now, but may
  3989. // be moved into $.mobile if other components need a similar method.
  3990. _findFirstElementByTagName: function( ele, nextProp, lcName, ucName )
  3991. {
  3992. var dict = {};
  3993. dict[ lcName ] = dict[ ucName ] = true;
  3994. while ( ele ) {
  3995. if ( dict[ ele.nodeName ] ) {
  3996. return ele;
  3997. }
  3998. ele = ele[ nextProp ];
  3999. }
  4000. return null;
  4001. },
  4002. _getChildrenByTagName: function( ele, lcName, ucName )
  4003. {
  4004. var results = [],
  4005. dict = {};
  4006. dict[ lcName ] = dict[ ucName ] = true;
  4007. ele = ele.firstChild;
  4008. while ( ele ) {
  4009. if ( dict[ ele.nodeName ] ) {
  4010. results.push( ele );
  4011. }
  4012. ele = ele.nextSibling;
  4013. }
  4014. return $( results );
  4015. },
  4016. _addThumbClasses: function( containers )
  4017. {
  4018. var i, img, len = containers.length;
  4019. for ( i = 0; i < len; i++ ) {
  4020. img = $( this._findFirstElementByTagName( containers[ i ].firstChild, "nextSibling", "img", "IMG" ) );
  4021. if ( img.length ) {
  4022. img.addClass( "ui-li-thumb" );
  4023. $( this._findFirstElementByTagName( img[ 0 ].parentNode, "parentNode", "li", "LI" ) ).addClass( img.is( ".ui-li-icon" ) ? "ui-li-has-icon" : "ui-li-has-thumb" );
  4024. }
  4025. }
  4026. },
  4027. refresh: function( create ) {
  4028. this.parentPage = this.element.closest( ".ui-page" );
  4029. this._createSubPages();
  4030. var o = this.options,
  4031. $list = this.element,
  4032. self = this,
  4033. dividertheme = $list.jqmData( "dividertheme" ) || o.dividerTheme,
  4034. listsplittheme = $list.jqmData( "splittheme" ),
  4035. listspliticon = $list.jqmData( "spliticon" ),
  4036. li = this._getChildrenByTagName( $list[ 0 ], "li", "LI" ),
  4037. counter = $.support.cssPseudoElement || !$.nodeName( $list[ 0 ], "ol" ) ? 0 : 1,
  4038. itemClassDict = {},
  4039. item, itemClass, itemTheme,
  4040. a, last, splittheme, countParent, icon, imgParents, img, linkIcon;
  4041. if ( counter ) {
  4042. $list.find( ".ui-li-dec" ).remove();
  4043. }
  4044. if ( !o.theme ) {
  4045. o.theme = $.mobile.getInheritedTheme( this.element, "c" );
  4046. }
  4047. for ( var pos = 0, numli = li.length; pos < numli; pos++ ) {
  4048. item = li.eq( pos );
  4049. itemClass = "ui-li";
  4050. // If we're creating the element, we update it regardless
  4051. if ( create || !item.hasClass( "ui-li" ) ) {
  4052. itemTheme = item.jqmData("theme") || o.theme;
  4053. a = this._getChildrenByTagName( item[ 0 ], "a", "A" );
  4054. if ( a.length ) {
  4055. icon = item.jqmData("icon");
  4056. item.buttonMarkup({
  4057. wrapperEls: "div",
  4058. shadow: false,
  4059. corners: false,
  4060. iconpos: "right",
  4061. icon: a.length > 1 || icon === false ? false : icon || "arrow-r",
  4062. theme: itemTheme
  4063. });
  4064. if ( ( icon != false ) && ( a.length == 1 ) ) {
  4065. item.addClass( "ui-li-has-arrow" );
  4066. }
  4067. a.first().removeClass( "ui-link" ).addClass( "ui-link-inherit" );
  4068. if ( a.length > 1 ) {
  4069. itemClass += " ui-li-has-alt";
  4070. last = a.last();
  4071. splittheme = listsplittheme || last.jqmData( "theme" ) || o.splitTheme;
  4072. linkIcon = last.jqmData("icon");
  4073. last.appendTo(item)
  4074. .attr( "title", last.getEncodedText() )
  4075. .addClass( "ui-li-link-alt" )
  4076. .empty()
  4077. .buttonMarkup({
  4078. shadow: false,
  4079. corners: false,
  4080. theme: itemTheme,
  4081. icon: false,
  4082. iconpos: false
  4083. })
  4084. .find( ".ui-btn-inner" )
  4085. .append(
  4086. $( document.createElement( "span" ) ).buttonMarkup({
  4087. shadow: true,
  4088. corners: true,
  4089. theme: splittheme,
  4090. iconpos: "notext",
  4091. // link icon overrides list item icon overrides ul element overrides options
  4092. icon: linkIcon || icon || listspliticon || o.splitIcon
  4093. })
  4094. );
  4095. }
  4096. } else if ( item.jqmData( "role" ) === "list-divider" ) {
  4097. itemClass += " ui-li-divider ui-bar-" + dividertheme;
  4098. item.attr( "role", "heading" );
  4099. //reset counter when a divider heading is encountered
  4100. if ( counter ) {
  4101. counter = 1;
  4102. }
  4103. } else {
  4104. itemClass += " ui-li-static ui-body-" + itemTheme;
  4105. }
  4106. }
  4107. if ( counter && itemClass.indexOf( "ui-li-divider" ) < 0 ) {
  4108. countParent = item.is( ".ui-li-static:first" ) ? item : item.find( ".ui-link-inherit" );
  4109. countParent.addClass( "ui-li-jsnumbering" )
  4110. .prepend( "<span class='ui-li-dec'>" + (counter++) + ". </span>" );
  4111. }
  4112. // Instead of setting item class directly on the list item and its
  4113. // btn-inner at this point in time, push the item into a dictionary
  4114. // that tells us what class to set on it so we can do this after this
  4115. // processing loop is finished.
  4116. if ( !itemClassDict[ itemClass ] ) {
  4117. itemClassDict[ itemClass ] = [];
  4118. }
  4119. itemClassDict[ itemClass ].push( item[ 0 ] );
  4120. }
  4121. // Set the appropriate listview item classes on each list item
  4122. // and their btn-inner elements. The main reason we didn't do this
  4123. // in the for-loop above is because we can eliminate per-item function overhead
  4124. // by calling addClass() and children() once or twice afterwards. This
  4125. // can give us a significant boost on platforms like WP7.5.
  4126. for ( itemClass in itemClassDict ) {
  4127. $( itemClassDict[ itemClass ] ).addClass( itemClass ).children( ".ui-btn-inner" ).addClass( itemClass );
  4128. }
  4129. $list.find( "h1, h2, h3, h4, h5, h6" ).addClass( "ui-li-heading" )
  4130. .end()
  4131. .find( "p, dl" ).addClass( "ui-li-desc" )
  4132. .end()
  4133. .find( ".ui-li-aside" ).each(function() {
  4134. var $this = $(this);
  4135. $this.prependTo( $this.parent() ); //shift aside to front for css float
  4136. })
  4137. .end()
  4138. .find( ".ui-li-count" ).each( function() {
  4139. $( this ).closest( "li" ).addClass( "ui-li-has-count" );
  4140. }).addClass( "ui-btn-up-" + ( $list.jqmData( "counttheme" ) || this.options.countTheme) + " ui-btn-corner-all" );
  4141. // The idea here is to look at the first image in the list item
  4142. // itself, and any .ui-link-inherit element it may contain, so we
  4143. // can place the appropriate classes on the image and list item.
  4144. // Note that we used to use something like:
  4145. //
  4146. // li.find(">img:eq(0), .ui-link-inherit>img:eq(0)").each( ... );
  4147. //
  4148. // But executing a find() like that on Windows Phone 7.5 took a
  4149. // really long time. Walking things manually with the code below
  4150. // allows the 400 listview item page to load in about 3 seconds as
  4151. // opposed to 30 seconds.
  4152. this._addThumbClasses( li );
  4153. this._addThumbClasses( $list.find( ".ui-link-inherit" ) );
  4154. this._refreshCorners( create );
  4155. },
  4156. //create a string for ID/subpage url creation
  4157. _idStringEscape: function( str ) {
  4158. return str.replace(/[^a-zA-Z0-9]/g, '-');
  4159. },
  4160. _createSubPages: function() {
  4161. var parentList = this.element,
  4162. parentPage = parentList.closest( ".ui-page" ),
  4163. parentUrl = parentPage.jqmData( "url" ),
  4164. parentId = parentUrl || parentPage[ 0 ][ $.expando ],
  4165. parentListId = parentList.attr( "id" ),
  4166. o = this.options,
  4167. dns = "data-" + $.mobile.ns,
  4168. self = this,
  4169. persistentFooterID = parentPage.find( ":jqmData(role='footer')" ).jqmData( "id" ),
  4170. hasSubPages;
  4171. if ( typeof listCountPerPage[ parentId ] === "undefined" ) {
  4172. listCountPerPage[ parentId ] = -1;
  4173. }
  4174. parentListId = parentListId || ++listCountPerPage[ parentId ];
  4175. $( parentList.find( "li>ul, li>ol" ).toArray().reverse() ).each(function( i ) {
  4176. var self = this,
  4177. list = $( this ),
  4178. listId = list.attr( "id" ) || parentListId + "-" + i,
  4179. parent = list.parent(),
  4180. nodeEls = $( list.prevAll().toArray().reverse() ),
  4181. nodeEls = nodeEls.length ? nodeEls : $( "<span>" + $.trim(parent.contents()[ 0 ].nodeValue) + "</span>" ),
  4182. title = nodeEls.first().getEncodedText(),//url limits to first 30 chars of text
  4183. id = ( parentUrl || "" ) + "&" + $.mobile.subPageUrlKey + "=" + listId,
  4184. theme = list.jqmData( "theme" ) || o.theme,
  4185. countTheme = list.jqmData( "counttheme" ) || parentList.jqmData( "counttheme" ) || o.countTheme,
  4186. newPage, anchor;
  4187. //define hasSubPages for use in later removal
  4188. hasSubPages = true;
  4189. newPage = list.detach()
  4190. .wrap( "<div " + dns + "role='page' " + dns + "url='" + id + "' " + dns + "theme='" + theme + "' " + dns + "count-theme='" + countTheme + "'><div " + dns + "role='content'></div></div>" )
  4191. .parent()
  4192. .before( "<div " + dns + "role='header' " + dns + "theme='" + o.headerTheme + "'><div class='ui-title'>" + title + "</div></div>" )
  4193. .after( persistentFooterID ? $( "<div " + dns + "role='footer' " + dns + "id='"+ persistentFooterID +"'>") : "" )
  4194. .parent()
  4195. .appendTo( $.mobile.pageContainer );
  4196. newPage.page();
  4197. anchor = parent.find('a:first');
  4198. if ( !anchor.length ) {
  4199. anchor = $( "<a/>" ).html( nodeEls || title ).prependTo( parent.empty() );
  4200. }
  4201. anchor.attr( "href", "#" + id );
  4202. }).listview();
  4203. // on pagehide, remove any nested pages along with the parent page, as long as they aren't active
  4204. // and aren't embedded
  4205. if( hasSubPages &&
  4206. parentPage.is( ":jqmData(external-page='true')" ) &&
  4207. parentPage.data("page").options.domCache === false ) {
  4208. var newRemove = function( e, ui ){
  4209. var nextPage = ui.nextPage, npURL;
  4210. if( ui.nextPage ){
  4211. npURL = nextPage.jqmData( "url" );
  4212. if( npURL.indexOf( parentUrl + "&" + $.mobile.subPageUrlKey ) !== 0 ){
  4213. self.childPages().remove();
  4214. parentPage.remove();
  4215. }
  4216. }
  4217. };
  4218. // unbind the original page remove and replace with our specialized version
  4219. parentPage
  4220. .unbind( "pagehide.remove" )
  4221. .bind( "pagehide.remove", newRemove);
  4222. }
  4223. },
  4224. // TODO sort out a better way to track sub pages of the listview this is brittle
  4225. childPages: function(){
  4226. var parentUrl = this.parentPage.jqmData( "url" );
  4227. return $( ":jqmData(url^='"+ parentUrl + "&" + $.mobile.subPageUrlKey +"')");
  4228. }
  4229. });
  4230. //auto self-init widgets
  4231. $( document ).bind( "pagecreate create", function( e ){
  4232. $.mobile.listview.prototype.enhanceWithin( e.target );
  4233. });
  4234. })( jQuery );
  4235. /*
  4236. * "checkboxradio" plugin
  4237. */
  4238. (function( $, undefined ) {
  4239. $.widget( "mobile.checkboxradio", $.mobile.widget, {
  4240. options: {
  4241. theme: null,
  4242. initSelector: "input[type='checkbox'],input[type='radio']"
  4243. },
  4244. _create: function() {
  4245. var self = this,
  4246. input = this.element,
  4247. inheritAttr = function( input, dataAttr ) {
  4248. return input.jqmData( dataAttr ) || input.closest( "form,fieldset" ).jqmData( dataAttr )
  4249. },
  4250. // NOTE: Windows Phone could not find the label through a selector
  4251. // filter works though.
  4252. parentLabel = $( input ).closest( "label" ),
  4253. label = parentLabel.length ? parentLabel : $( input ).closest( "form,fieldset,:jqmData(role='page'),:jqmData(role='dialog')" ).find( "label" ).filter( "[for='" + input[0].id + "']" ),
  4254. inputtype = input[0].type,
  4255. mini = inheritAttr( input, "mini" ),
  4256. checkedState = inputtype + "-on",
  4257. uncheckedState = inputtype + "-off",
  4258. icon = input.parents( ":jqmData(type='horizontal')" ).length ? undefined : uncheckedState,
  4259. iconpos = inheritAttr( input, "iconpos" ),
  4260. activeBtn = icon ? "" : " " + $.mobile.activeBtnClass,
  4261. checkedClass = "ui-" + checkedState + activeBtn,
  4262. uncheckedClass = "ui-" + uncheckedState,
  4263. checkedicon = "ui-icon-" + checkedState,
  4264. uncheckedicon = "ui-icon-" + uncheckedState;
  4265. if ( inputtype !== "checkbox" && inputtype !== "radio" ) {
  4266. return;
  4267. }
  4268. // Expose for other methods
  4269. $.extend( this, {
  4270. label: label,
  4271. inputtype: inputtype,
  4272. checkedClass: checkedClass,
  4273. uncheckedClass: uncheckedClass,
  4274. checkedicon: checkedicon,
  4275. uncheckedicon: uncheckedicon
  4276. });
  4277. // If there's no selected theme check the data attr
  4278. if( !this.options.theme ) {
  4279. this.options.theme = $.mobile.getInheritedTheme( this.element, "c" );
  4280. }
  4281. label.buttonMarkup({
  4282. theme: this.options.theme,
  4283. icon: icon,
  4284. shadow: false,
  4285. mini: mini,
  4286. iconpos: iconpos
  4287. });
  4288. // Wrap the input + label in a div
  4289. var wrapper = document.createElement('div');
  4290. wrapper.className = 'ui-' + inputtype;
  4291. input.add( label ).wrapAll( wrapper );
  4292. label.bind({
  4293. vmouseover: function( event ) {
  4294. if ( $( this ).parent().is( ".ui-disabled" ) ) {
  4295. event.stopPropagation();
  4296. }
  4297. },
  4298. vclick: function( event ) {
  4299. if ( input.is( ":disabled" ) ) {
  4300. event.preventDefault();
  4301. return;
  4302. }
  4303. self._cacheVals();
  4304. input.prop( "checked", inputtype === "radio" && true || !input.prop( "checked" ) );
  4305. // trigger click handler's bound directly to the input as a substitute for
  4306. // how label clicks behave normally in the browsers
  4307. // TODO: it would be nice to let the browser's handle the clicks and pass them
  4308. // through to the associate input. we can swallow that click at the parent
  4309. // wrapper element level
  4310. input.triggerHandler( 'click' );
  4311. // Input set for common radio buttons will contain all the radio
  4312. // buttons, but will not for checkboxes. clearing the checked status
  4313. // of other radios ensures the active button state is applied properly
  4314. self._getInputSet().not( input ).prop( "checked", false );
  4315. self._updateAll();
  4316. return false;
  4317. }
  4318. });
  4319. input
  4320. .bind({
  4321. vmousedown: function() {
  4322. self._cacheVals();
  4323. },
  4324. vclick: function() {
  4325. var $this = $(this);
  4326. // Adds checked attribute to checked input when keyboard is used
  4327. if ( $this.is( ":checked" ) ) {
  4328. $this.prop( "checked", true);
  4329. self._getInputSet().not($this).prop( "checked", false );
  4330. } else {
  4331. $this.prop( "checked", false );
  4332. }
  4333. self._updateAll();
  4334. },
  4335. focus: function() {
  4336. label.addClass( $.mobile.focusClass );
  4337. },
  4338. blur: function() {
  4339. label.removeClass( $.mobile.focusClass );
  4340. }
  4341. });
  4342. this.refresh();
  4343. },
  4344. _cacheVals: function() {
  4345. this._getInputSet().each(function() {
  4346. $(this).jqmData( "cacheVal", this.checked );
  4347. });
  4348. },
  4349. //returns either a set of radios with the same name attribute, or a single checkbox
  4350. _getInputSet: function(){
  4351. if(this.inputtype === "checkbox") {
  4352. return this.element;
  4353. }
  4354. return this.element.closest( "form,fieldset,:jqmData(role='page')" )
  4355. .find( "input[name='"+ this.element[0].name +"'][type='"+ this.inputtype +"']" );
  4356. },
  4357. _updateAll: function() {
  4358. var self = this;
  4359. this._getInputSet().each(function() {
  4360. var $this = $(this);
  4361. if ( this.checked || self.inputtype === "checkbox" ) {
  4362. $this.trigger( "change" );
  4363. }
  4364. })
  4365. .checkboxradio( "refresh" );
  4366. },
  4367. refresh: function() {
  4368. var input = this.element[0],
  4369. label = this.label,
  4370. icon = label.find( ".ui-icon" );
  4371. if ( input.checked ) {
  4372. label.addClass( this.checkedClass ).removeClass( this.uncheckedClass );
  4373. icon.addClass( this.checkedicon ).removeClass( this.uncheckedicon );
  4374. } else {
  4375. label.removeClass( this.checkedClass ).addClass( this.uncheckedClass );
  4376. icon.removeClass( this.checkedicon ).addClass( this.uncheckedicon );
  4377. }
  4378. if ( input.disabled ) {
  4379. this.disable();
  4380. } else {
  4381. this.enable();
  4382. }
  4383. },
  4384. disable: function() {
  4385. this.element.prop( "disabled", true ).parent().addClass( "ui-disabled" );
  4386. },
  4387. enable: function() {
  4388. this.element.prop( "disabled", false ).parent().removeClass( "ui-disabled" );
  4389. }
  4390. });
  4391. //auto self-init widgets
  4392. $( document ).bind( "pagecreate create", function( e ){
  4393. $.mobile.checkboxradio.prototype.enhanceWithin( e.target, true );
  4394. });
  4395. })( jQuery );
  4396. (function( $, undefined ) {
  4397. $.widget( "mobile.button", $.mobile.widget, {
  4398. options: {
  4399. theme: null,
  4400. icon: null,
  4401. iconpos: null,
  4402. inline: false,
  4403. corners: true,
  4404. shadow: true,
  4405. iconshadow: true,
  4406. initSelector: "button, [type='button'], [type='submit'], [type='reset'], [type='image']",
  4407. mini: false
  4408. },
  4409. _create: function() {
  4410. var $el = this.element,
  4411. $button,
  4412. o = this.options,
  4413. type,
  4414. name,
  4415. classes = "",
  4416. $buttonPlaceholder;
  4417. // if this is a link, check if it's been enhanced and, if not, use the right function
  4418. if( $el[ 0 ].tagName === "A" ) {
  4419. !$el.hasClass( "ui-btn" ) && $el.buttonMarkup();
  4420. return;
  4421. }
  4422. // get the inherited theme
  4423. // TODO centralize for all widgets
  4424. if ( !this.options.theme ) {
  4425. this.options.theme = $.mobile.getInheritedTheme( this.element, "c" );
  4426. }
  4427. // TODO: Post 1.1--once we have time to test thoroughly--any classes manually applied to the original element should be carried over to the enhanced element, with an `-enhanced` suffix. See https://github.com/jquery/jquery-mobile/issues/3577
  4428. /* if( $el[0].className.length ) {
  4429. classes = $el[0].className;
  4430. } */
  4431. if( !!~$el[0].className.indexOf( "ui-btn-left" ) ) {
  4432. classes = "ui-btn-left";
  4433. }
  4434. if( !!~$el[0].className.indexOf( "ui-btn-right" ) ) {
  4435. classes = "ui-btn-right";
  4436. }
  4437. // Add ARIA role
  4438. this.button = $( "<div></div>" )
  4439. .text( $el.text() || $el.val() )
  4440. .insertBefore( $el )
  4441. .buttonMarkup({
  4442. theme: o.theme,
  4443. icon: o.icon,
  4444. iconpos: o.iconpos,
  4445. inline: o.inline,
  4446. corners: o.corners,
  4447. shadow: o.shadow,
  4448. iconshadow: o.iconshadow,
  4449. mini: o.mini
  4450. })
  4451. .addClass( classes )
  4452. .append( $el.addClass( "ui-btn-hidden" ) );
  4453. $button = this.button;
  4454. type = $el.attr( "type" );
  4455. name = $el.attr( "name" );
  4456. // Add hidden input during submit if input type="submit" has a name.
  4457. if ( type !== "button" && type !== "reset" && name ) {
  4458. $el.bind( "vclick", function() {
  4459. // Add hidden input if it doesn’t already exist.
  4460. if( $buttonPlaceholder === undefined ) {
  4461. $buttonPlaceholder = $( "<input>", {
  4462. type: "hidden",
  4463. name: $el.attr( "name" ),
  4464. value: $el.attr( "value" )
  4465. }).insertBefore( $el );
  4466. // Bind to doc to remove after submit handling
  4467. $( document ).one("submit", function(){
  4468. $buttonPlaceholder.remove();
  4469. // reset the local var so that the hidden input
  4470. // will be re-added on subsequent clicks
  4471. $buttonPlaceholder = undefined;
  4472. });
  4473. }
  4474. });
  4475. }
  4476. $el.bind({
  4477. focus: function() {
  4478. $button.addClass( $.mobile.focusClass );
  4479. },
  4480. blur: function() {
  4481. $button.removeClass( $.mobile.focusClass );
  4482. }
  4483. });
  4484. this.refresh();
  4485. },
  4486. enable: function() {
  4487. this.element.attr( "disabled", false );
  4488. this.button.removeClass( "ui-disabled" ).attr( "aria-disabled", false );
  4489. return this._setOption( "disabled", false );
  4490. },
  4491. disable: function() {
  4492. this.element.attr( "disabled", true );
  4493. this.button.addClass( "ui-disabled" ).attr( "aria-disabled", true );
  4494. return this._setOption( "disabled", true );
  4495. },
  4496. refresh: function() {
  4497. var $el = this.element;
  4498. if ( $el.prop("disabled") ) {
  4499. this.disable();
  4500. } else {
  4501. this.enable();
  4502. }
  4503. // Grab the button's text element from its implementation-independent data item
  4504. $( this.button.data( 'buttonElements' ).text ).text( $el.text() || $el.val() );
  4505. }
  4506. });
  4507. //auto self-init widgets
  4508. $( document ).bind( "pagecreate create", function( e ){
  4509. $.mobile.button.prototype.enhanceWithin( e.target, true );
  4510. });
  4511. })( jQuery );
  4512. (function( $, undefined ) {
  4513. $.fn.controlgroup = function( options ) {
  4514. function flipClasses( els, flCorners ) {
  4515. els.removeClass( "ui-btn-corner-all ui-shadow" )
  4516. .eq( 0 ).addClass( flCorners[ 0 ] )
  4517. .end()
  4518. .last().addClass( flCorners[ 1 ] ).addClass( "ui-controlgroup-last" );
  4519. }
  4520. return this.each(function() {
  4521. var $el = $( this ),
  4522. o = $.extend({
  4523. direction: $el.jqmData( "type" ) || "vertical",
  4524. shadow: false,
  4525. excludeInvisible: true,
  4526. mini: $el.jqmData( "mini" )
  4527. }, options ),
  4528. groupheading = $el.children( "legend" ),
  4529. flCorners = o.direction == "horizontal" ? [ "ui-corner-left", "ui-corner-right" ] : [ "ui-corner-top", "ui-corner-bottom" ],
  4530. type = $el.find( "input" ).first().attr( "type" );
  4531. // Replace legend with more stylable replacement div
  4532. if ( groupheading.length ) {
  4533. $el.wrapInner( "<div class='ui-controlgroup-controls'></div>" );
  4534. $( "<div role='heading' class='ui-controlgroup-label'>" + groupheading.html() + "</div>" ).insertBefore( $el.children(0) );
  4535. groupheading.remove();
  4536. }
  4537. $el.addClass( "ui-corner-all ui-controlgroup ui-controlgroup-" + o.direction );
  4538. flipClasses( $el.find( ".ui-btn" + ( o.excludeInvisible ? ":visible" : "" ) ).not('.ui-slider-handle'), flCorners );
  4539. flipClasses( $el.find( ".ui-btn-inner" ), flCorners );
  4540. if ( o.shadow ) {
  4541. $el.addClass( "ui-shadow" );
  4542. }
  4543. if ( o.mini ) {
  4544. $el.addClass( "ui-mini" );
  4545. }
  4546. });
  4547. };
  4548. // The pagecreate handler for controlgroup is in jquery.mobile.init because of the soft-dependency on the wrapped widgets
  4549. })(jQuery);
  4550. (function( $, undefined ) {
  4551. $( document ).bind( "pagecreate create", function( e ){
  4552. //links within content areas, tests included with page
  4553. $( e.target )
  4554. .find( "a" )
  4555. .jqmEnhanceable()
  4556. .not( ".ui-btn, .ui-link-inherit, :jqmData(role='none'), :jqmData(role='nojs')" )
  4557. .addClass( "ui-link" );
  4558. });
  4559. })( jQuery );
  4560. ( function( $ ) {
  4561. var meta = $( "meta[name=viewport]" ),
  4562. initialContent = meta.attr( "content" ),
  4563. disabledZoom = initialContent + ",maximum-scale=1, user-scalable=no",
  4564. enabledZoom = initialContent + ",maximum-scale=10, user-scalable=yes",
  4565. disabledInitially = /(user-scalable[\s]*=[\s]*no)|(maximum-scale[\s]*=[\s]*1)[$,\s]/.test( initialContent );
  4566. $.mobile.zoom = $.extend( {}, {
  4567. enabled: !disabledInitially,
  4568. locked: false,
  4569. disable: function( lock ) {
  4570. if( !disabledInitially && !$.mobile.zoom.locked ){
  4571. meta.attr( "content", disabledZoom );
  4572. $.mobile.zoom.enabled = false;
  4573. $.mobile.zoom.locked = lock || false;
  4574. }
  4575. },
  4576. enable: function( unlock ) {
  4577. if( !disabledInitially && ( !$.mobile.zoom.locked || unlock === true ) ){
  4578. meta.attr( "content", enabledZoom );
  4579. $.mobile.zoom.enabled = true;
  4580. $.mobile.zoom.locked = false;
  4581. }
  4582. },
  4583. restore: function() {
  4584. if( !disabledInitially ){
  4585. meta.attr( "content", initialContent );
  4586. $.mobile.zoom.enabled = true;
  4587. }
  4588. }
  4589. });
  4590. }( jQuery ));
  4591. (function( $, undefined ) {
  4592. $.widget( "mobile.textinput", $.mobile.widget, {
  4593. options: {
  4594. theme: null,
  4595. // This option defaults to true on iOS devices.
  4596. preventFocusZoom: /iPhone|iPad|iPod/.test( navigator.platform ) && navigator.userAgent.indexOf( "AppleWebKit" ) > -1,
  4597. initSelector: "input[type='text'], input[type='search'], :jqmData(type='search'), input[type='number'], :jqmData(type='number'), input[type='password'], input[type='email'], input[type='url'], input[type='tel'], textarea, input[type='time'], input[type='date'], input[type='month'], input[type='week'], input[type='datetime'], input[type='datetime-local'], input[type='color'], input:not([type])",
  4598. clearSearchButtonText: "clear text"
  4599. },
  4600. _create: function() {
  4601. var input = this.element,
  4602. o = this.options,
  4603. theme = o.theme || $.mobile.getInheritedTheme( this.element, "c" ),
  4604. themeclass = " ui-body-" + theme,
  4605. mini = input.jqmData("mini") == true,
  4606. miniclass = mini ? " ui-mini" : "",
  4607. focusedEl, clearbtn;
  4608. $( "label[for='" + input.attr( "id" ) + "']" ).addClass( "ui-input-text" );
  4609. focusedEl = input.addClass("ui-input-text ui-body-"+ theme );
  4610. // XXX: Temporary workaround for issue 785 (Apple bug 8910589).
  4611. // Turn off autocorrect and autocomplete on non-iOS 5 devices
  4612. // since the popup they use can't be dismissed by the user. Note
  4613. // that we test for the presence of the feature by looking for
  4614. // the autocorrect property on the input element. We currently
  4615. // have no test for iOS 5 or newer so we're temporarily using
  4616. // the touchOverflow support flag for jQM 1.0. Yes, I feel dirty. - jblas
  4617. if ( typeof input[0].autocorrect !== "undefined" && !$.support.touchOverflow ) {
  4618. // Set the attribute instead of the property just in case there
  4619. // is code that attempts to make modifications via HTML.
  4620. input[0].setAttribute( "autocorrect", "off" );
  4621. input[0].setAttribute( "autocomplete", "off" );
  4622. }
  4623. //"search" input widget
  4624. if ( input.is( "[type='search'],:jqmData(type='search')" ) ) {
  4625. focusedEl = input.wrap( "<div class='ui-input-search ui-shadow-inset ui-btn-corner-all ui-btn-shadow ui-icon-searchfield" + themeclass + miniclass + "'></div>" ).parent();
  4626. clearbtn = $( "<a href='#' class='ui-input-clear' title='" + o.clearSearchButtonText + "'>" + o.clearSearchButtonText + "</a>" )
  4627. .bind('click', function( event ) {
  4628. input
  4629. .val( "" )
  4630. .focus()
  4631. .trigger( "change" );
  4632. clearbtn.addClass( "ui-input-clear-hidden" );
  4633. event.preventDefault();
  4634. })
  4635. .appendTo( focusedEl )
  4636. .buttonMarkup({
  4637. icon: "delete",
  4638. iconpos: "notext",
  4639. corners: true,
  4640. shadow: true,
  4641. mini: mini
  4642. });
  4643. function toggleClear() {
  4644. setTimeout(function() {
  4645. clearbtn.toggleClass( "ui-input-clear-hidden", !input.val() );
  4646. }, 0);
  4647. }
  4648. toggleClear();
  4649. input.bind('paste cut keyup focus change blur', toggleClear);
  4650. } else {
  4651. input.addClass( "ui-corner-all ui-shadow-inset" + themeclass + miniclass );
  4652. }
  4653. input.focus(function() {
  4654. focusedEl.addClass( $.mobile.focusClass );
  4655. })
  4656. .blur(function(){
  4657. focusedEl.removeClass( $.mobile.focusClass );
  4658. })
  4659. // In many situations, iOS will zoom into the select upon tap, this prevents that from happening
  4660. .bind( "focus", function() {
  4661. if( o.preventFocusZoom ){
  4662. $.mobile.zoom.disable( true );
  4663. }
  4664. })
  4665. .bind( "blur", function() {
  4666. if( o.preventFocusZoom ){
  4667. $.mobile.zoom.enable( true );
  4668. }
  4669. });
  4670. // Autogrow
  4671. if ( input.is( "textarea" ) ) {
  4672. var extraLineHeight = 15,
  4673. keyupTimeoutBuffer = 100,
  4674. keyup = function() {
  4675. var scrollHeight = input[ 0 ].scrollHeight,
  4676. clientHeight = input[ 0 ].clientHeight;
  4677. if ( clientHeight < scrollHeight ) {
  4678. input.height(scrollHeight + extraLineHeight);
  4679. }
  4680. },
  4681. keyupTimeout;
  4682. input.keyup(function() {
  4683. clearTimeout( keyupTimeout );
  4684. keyupTimeout = setTimeout( keyup, keyupTimeoutBuffer );
  4685. });
  4686. // binding to pagechange here ensures that for pages loaded via
  4687. // ajax the height is recalculated without user input
  4688. $( document ).one( "pagechange", keyup );
  4689. // Issue 509: the browser is not providing scrollHeight properly until the styles load
  4690. if ( $.trim( input.val() ) ) {
  4691. // bind to the window load to make sure the height is calculated based on BOTH
  4692. // the DOM and CSS
  4693. $( window ).load( keyup );
  4694. }
  4695. }
  4696. },
  4697. disable: function(){
  4698. ( this.element.attr( "disabled", true ).is( "[type='search'],:jqmData(type='search')" ) ?
  4699. this.element.parent() : this.element ).addClass( "ui-disabled" );
  4700. },
  4701. enable: function(){
  4702. ( this.element.attr( "disabled", false).is( "[type='search'],:jqmData(type='search')" ) ?
  4703. this.element.parent() : this.element ).removeClass( "ui-disabled" );
  4704. }
  4705. });
  4706. //auto self-init widgets
  4707. $( document ).bind( "pagecreate create", function( e ){
  4708. $.mobile.textinput.prototype.enhanceWithin( e.target, true );
  4709. });
  4710. })( jQuery );
  4711. (function( $, undefined ) {
  4712. $.mobile.listview.prototype.options.filter = false;
  4713. $.mobile.listview.prototype.options.filterPlaceholder = "Filter items...";
  4714. $.mobile.listview.prototype.options.filterTheme = "c";
  4715. $.mobile.listview.prototype.options.filterCallback = function( text, searchValue ){
  4716. return text.toLowerCase().indexOf( searchValue ) === -1;
  4717. };
  4718. $( document ).delegate( ":jqmData(role='listview')", "listviewcreate", function() {
  4719. var list = $( this ),
  4720. listview = list.data( "listview" );
  4721. if ( !listview.options.filter ) {
  4722. return;
  4723. }
  4724. var wrapper = $( "<form>", {
  4725. "class": "ui-listview-filter ui-bar-" + listview.options.filterTheme,
  4726. "role": "search"
  4727. }),
  4728. search = $( "<input>", {
  4729. placeholder: listview.options.filterPlaceholder
  4730. })
  4731. .attr( "data-" + $.mobile.ns + "type", "search" )
  4732. .jqmData( "lastval", "" )
  4733. .bind( "keyup change", function() {
  4734. var $this = $(this),
  4735. val = this.value.toLowerCase(),
  4736. listItems = null,
  4737. lastval = $this.jqmData( "lastval" ) + "",
  4738. childItems = false,
  4739. itemtext = "",
  4740. item;
  4741. // Change val as lastval for next execution
  4742. $this.jqmData( "lastval" , val );
  4743. if ( val.length < lastval.length || val.indexOf(lastval) !== 0 ) {
  4744. // Removed chars or pasted something totally different, check all items
  4745. listItems = list.children();
  4746. } else {
  4747. // Only chars added, not removed, only use visible subset
  4748. listItems = list.children( ":not(.ui-screen-hidden)" );
  4749. }
  4750. if ( val ) {
  4751. // This handles hiding regular rows without the text we search for
  4752. // and any list dividers without regular rows shown under it
  4753. for ( var i = listItems.length - 1; i >= 0; i-- ) {
  4754. item = $( listItems[ i ] );
  4755. itemtext = item.jqmData( "filtertext" ) || item.text();
  4756. if ( item.is( "li:jqmData(role=list-divider)" ) ) {
  4757. item.toggleClass( "ui-filter-hidequeue" , !childItems );
  4758. // New bucket!
  4759. childItems = false;
  4760. } else if ( listview.options.filterCallback( itemtext, val ) ) {
  4761. //mark to be hidden
  4762. item.toggleClass( "ui-filter-hidequeue" , true );
  4763. } else {
  4764. // There's a shown item in the bucket
  4765. childItems = true;
  4766. }
  4767. }
  4768. // Show items, not marked to be hidden
  4769. listItems
  4770. .filter( ":not(.ui-filter-hidequeue)" )
  4771. .toggleClass( "ui-screen-hidden", false );
  4772. // Hide items, marked to be hidden
  4773. listItems
  4774. .filter( ".ui-filter-hidequeue" )
  4775. .toggleClass( "ui-screen-hidden", true )
  4776. .toggleClass( "ui-filter-hidequeue", false );
  4777. } else {
  4778. //filtervalue is empty => show all
  4779. listItems.toggleClass( "ui-screen-hidden", false );
  4780. }
  4781. listview._refreshCorners();
  4782. })
  4783. .appendTo( wrapper )
  4784. .textinput();
  4785. if ( listview.options.inset ) {
  4786. wrapper.addClass( "ui-listview-filter-inset" );
  4787. }
  4788. wrapper.bind( "submit", function() {
  4789. return false;
  4790. })
  4791. .insertBefore( list );
  4792. });
  4793. })( jQuery );
  4794. ( function( $, undefined ) {
  4795. $.widget( "mobile.slider", $.mobile.widget, {
  4796. options: {
  4797. theme: null,
  4798. trackTheme: null,
  4799. disabled: false,
  4800. initSelector: "input[type='range'], :jqmData(type='range'), :jqmData(role='slider')",
  4801. mini: false
  4802. },
  4803. _create: function() {
  4804. // TODO: Each of these should have comments explain what they're for
  4805. var self = this,
  4806. control = this.element,
  4807. parentTheme = $.mobile.getInheritedTheme( control, "c" ),
  4808. theme = this.options.theme || parentTheme,
  4809. trackTheme = this.options.trackTheme || parentTheme,
  4810. cType = control[ 0 ].nodeName.toLowerCase(),
  4811. selectClass = ( cType == "select" ) ? "ui-slider-switch" : "",
  4812. controlID = control.attr( "id" ),
  4813. labelID = controlID + "-label",
  4814. label = $( "[for='"+ controlID +"']" ).attr( "id", labelID ),
  4815. val = function() {
  4816. return cType == "input" ? parseFloat( control.val() ) : control[0].selectedIndex;
  4817. },
  4818. min = cType == "input" ? parseFloat( control.attr( "min" ) ) : 0,
  4819. max = cType == "input" ? parseFloat( control.attr( "max" ) ) : control.find( "option" ).length-1,
  4820. step = window.parseFloat( control.attr( "step" ) || 1 ),
  4821. inlineClass = ( this.options.inline || control.jqmData("inline") == true ) ? " ui-slider-inline" : "",
  4822. miniClass = ( this.options.mini || control.jqmData("mini") ) ? " ui-slider-mini" : "",
  4823. domHandle = document.createElement('a'),
  4824. handle = $( domHandle ),
  4825. domSlider = document.createElement('div'),
  4826. slider = $( domSlider ),
  4827. valuebg = control.jqmData("highlight") && cType != "select" ? (function() {
  4828. var bg = document.createElement('div');
  4829. bg.className = 'ui-slider-bg ui-btn-active ui-btn-corner-all';
  4830. return $( bg ).prependTo( slider );
  4831. })() : false,
  4832. options;
  4833. domHandle.setAttribute( 'href', "#" );
  4834. domSlider.setAttribute('role','application');
  4835. domSlider.className = ['ui-slider ',selectClass," ui-btn-down-",trackTheme,' ui-btn-corner-all', inlineClass, miniClass].join("");
  4836. domHandle.className = 'ui-slider-handle';
  4837. domSlider.appendChild(domHandle);
  4838. handle.buttonMarkup({ corners: true, theme: theme, shadow: true })
  4839. .attr({
  4840. "role": "slider",
  4841. "aria-valuemin": min,
  4842. "aria-valuemax": max,
  4843. "aria-valuenow": val(),
  4844. "aria-valuetext": val(),
  4845. "title": val(),
  4846. "aria-labelledby": labelID
  4847. });
  4848. $.extend( this, {
  4849. slider: slider,
  4850. handle: handle,
  4851. valuebg: valuebg,
  4852. dragging: false,
  4853. beforeStart: null,
  4854. userModified: false,
  4855. mouseMoved: false
  4856. });
  4857. if ( cType == "select" ) {
  4858. var wrapper = document.createElement('div');
  4859. wrapper.className = 'ui-slider-inneroffset';
  4860. for(var j = 0,length = domSlider.childNodes.length;j < length;j++){
  4861. wrapper.appendChild(domSlider.childNodes[j]);
  4862. }
  4863. domSlider.appendChild(wrapper);
  4864. // slider.wrapInner( "<div class='ui-slider-inneroffset'></div>" );
  4865. // make the handle move with a smooth transition
  4866. handle.addClass( "ui-slider-handle-snapping" );
  4867. options = control.find( "option" );
  4868. for(var i = 0, optionsCount = options.length; i < optionsCount; i++){
  4869. var side = !i ? "b":"a",
  4870. sliderTheme = !i ? " ui-btn-down-" + trackTheme :( " " + $.mobile.activeBtnClass ),
  4871. sliderLabel = document.createElement('div'),
  4872. sliderImg = document.createElement('span');
  4873. sliderImg.className = ['ui-slider-label ui-slider-label-',side,sliderTheme," ui-btn-corner-all"].join("");
  4874. sliderImg.setAttribute('role','img');
  4875. sliderImg.appendChild(document.createTextNode(options[i].innerHTML));
  4876. $(sliderImg).prependTo( slider );
  4877. }
  4878. self._labels = $( ".ui-slider-label", slider );
  4879. }
  4880. label.addClass( "ui-slider" );
  4881. // monitor the input for updated values
  4882. control.addClass( cType === "input" ? "ui-slider-input" : "ui-slider-switch" )
  4883. .change( function() {
  4884. // if the user dragged the handle, the "change" event was triggered from inside refresh(); don't call refresh() again
  4885. if (!self.mouseMoved) {
  4886. self.refresh( val(), true );
  4887. }
  4888. })
  4889. .keyup( function() { // necessary?
  4890. self.refresh( val(), true, true );
  4891. })
  4892. .blur( function() {
  4893. self.refresh( val(), true );
  4894. });
  4895. // prevent screen drag when slider activated
  4896. $( document ).bind( "vmousemove", function( event ) {
  4897. if ( self.dragging ) {
  4898. // self.mouseMoved must be updated before refresh() because it will be used in the control "change" event
  4899. self.mouseMoved = true;
  4900. if ( cType === "select" ) {
  4901. // make the handle move in sync with the mouse
  4902. handle.removeClass( "ui-slider-handle-snapping" );
  4903. }
  4904. self.refresh( event );
  4905. // only after refresh() you can calculate self.userModified
  4906. self.userModified = self.beforeStart !== control[0].selectedIndex;
  4907. return false;
  4908. }
  4909. });
  4910. slider.bind( "vmousedown", function( event ) {
  4911. self.dragging = true;
  4912. self.userModified = false;
  4913. self.mouseMoved = false;
  4914. if ( cType === "select" ) {
  4915. self.beforeStart = control[0].selectedIndex;
  4916. }
  4917. self.refresh( event );
  4918. return false;
  4919. })
  4920. .bind( "vclick", false );
  4921. slider.add( document )
  4922. .bind( "vmouseup", function() {
  4923. if ( self.dragging ) {
  4924. self.dragging = false;
  4925. if ( cType === "select") {
  4926. // make the handle move with a smooth transition
  4927. handle.addClass( "ui-slider-handle-snapping" );
  4928. if ( self.mouseMoved ) {
  4929. // this is a drag, change the value only if user dragged enough
  4930. if ( self.userModified ) {
  4931. self.refresh( self.beforeStart == 0 ? 1 : 0 );
  4932. }
  4933. else {
  4934. self.refresh( self.beforeStart );
  4935. }
  4936. }
  4937. else {
  4938. // this is just a click, change the value
  4939. self.refresh( self.beforeStart == 0 ? 1 : 0 );
  4940. }
  4941. }
  4942. self.mouseMoved = false;
  4943. return false;
  4944. }
  4945. });
  4946. slider.insertAfter( control );
  4947. // Only add focus class to toggle switch, sliders get it automatically from ui-btn
  4948. if( cType == 'select' ) {
  4949. this.handle.bind({
  4950. focus: function() {
  4951. slider.addClass( $.mobile.focusClass );
  4952. },
  4953. blur: function() {
  4954. slider.removeClass( $.mobile.focusClass );
  4955. }
  4956. });
  4957. }
  4958. this.handle.bind({
  4959. // NOTE force focus on handle
  4960. vmousedown: function() {
  4961. $( this ).focus();
  4962. },
  4963. vclick: false,
  4964. keydown: function( event ) {
  4965. var index = val();
  4966. if ( self.options.disabled ) {
  4967. return;
  4968. }
  4969. // In all cases prevent the default and mark the handle as active
  4970. switch ( event.keyCode ) {
  4971. case $.mobile.keyCode.HOME:
  4972. case $.mobile.keyCode.END:
  4973. case $.mobile.keyCode.PAGE_UP:
  4974. case $.mobile.keyCode.PAGE_DOWN:
  4975. case $.mobile.keyCode.UP:
  4976. case $.mobile.keyCode.RIGHT:
  4977. case $.mobile.keyCode.DOWN:
  4978. case $.mobile.keyCode.LEFT:
  4979. event.preventDefault();
  4980. if ( !self._keySliding ) {
  4981. self._keySliding = true;
  4982. $( this ).addClass( "ui-state-active" );
  4983. }
  4984. break;
  4985. }
  4986. // move the slider according to the keypress
  4987. switch ( event.keyCode ) {
  4988. case $.mobile.keyCode.HOME:
  4989. self.refresh( min );
  4990. break;
  4991. case $.mobile.keyCode.END:
  4992. self.refresh( max );
  4993. break;
  4994. case $.mobile.keyCode.PAGE_UP:
  4995. case $.mobile.keyCode.UP:
  4996. case $.mobile.keyCode.RIGHT:
  4997. self.refresh( index + step );
  4998. break;
  4999. case $.mobile.keyCode.PAGE_DOWN:
  5000. case $.mobile.keyCode.DOWN:
  5001. case $.mobile.keyCode.LEFT:
  5002. self.refresh( index - step );
  5003. break;
  5004. }
  5005. }, // remove active mark
  5006. keyup: function( event ) {
  5007. if ( self._keySliding ) {
  5008. self._keySliding = false;
  5009. $( this ).removeClass( "ui-state-active" );
  5010. }
  5011. }
  5012. });
  5013. this.refresh(undefined, undefined, true);
  5014. },
  5015. refresh: function( val, isfromControl, preventInputUpdate ) {
  5016. if ( this.options.disabled || this.element.attr('disabled')) {
  5017. this.disable();
  5018. }
  5019. var control = this.element, percent,
  5020. cType = control[0].nodeName.toLowerCase(),
  5021. min = cType === "input" ? parseFloat( control.attr( "min" ) ) : 0,
  5022. max = cType === "input" ? parseFloat( control.attr( "max" ) ) : control.find( "option" ).length - 1,
  5023. step = (cType === "input" && parseFloat( control.attr( "step" ) ) > 0) ? parseFloat(control.attr("step")) : 1;
  5024. if ( typeof val === "object" ) {
  5025. var data = val,
  5026. // a slight tolerance helped get to the ends of the slider
  5027. tol = 8;
  5028. if ( !this.dragging ||
  5029. data.pageX < this.slider.offset().left - tol ||
  5030. data.pageX > this.slider.offset().left + this.slider.width() + tol ) {
  5031. return;
  5032. }
  5033. percent = Math.round( ( ( data.pageX - this.slider.offset().left ) / this.slider.width() ) * 100 );
  5034. } else {
  5035. if ( val == null ) {
  5036. val = cType === "input" ? parseFloat( control.val() || 0 ) : control[0].selectedIndex;
  5037. }
  5038. percent = ( parseFloat( val ) - min ) / ( max - min ) * 100;
  5039. }
  5040. if ( isNaN( percent ) ) {
  5041. return;
  5042. }
  5043. if ( percent < 0 ) {
  5044. percent = 0;
  5045. }
  5046. if ( percent > 100 ) {
  5047. percent = 100;
  5048. }
  5049. var newval = ( percent / 100 ) * ( max - min ) + min;
  5050. //from jQuery UI slider, the following source will round to the nearest step
  5051. var valModStep = ( newval - min ) % step;
  5052. var alignValue = newval - valModStep;
  5053. if ( Math.abs( valModStep ) * 2 >= step ) {
  5054. alignValue += ( valModStep > 0 ) ? step : ( -step );
  5055. }
  5056. // Since JavaScript has problems with large floats, round
  5057. // the final value to 5 digits after the decimal point (see jQueryUI: #4124)
  5058. newval = parseFloat( alignValue.toFixed(5) );
  5059. if ( newval < min ) {
  5060. newval = min;
  5061. }
  5062. if ( newval > max ) {
  5063. newval = max;
  5064. }
  5065. this.handle.css( "left", percent + "%" );
  5066. this.handle.attr( {
  5067. "aria-valuenow": cType === "input" ? newval : control.find( "option" ).eq( newval ).attr( "value" ),
  5068. "aria-valuetext": cType === "input" ? newval : control.find( "option" ).eq( newval ).getEncodedText(),
  5069. title: cType === "input" ? newval : control.find( "option" ).eq( newval ).getEncodedText()
  5070. });
  5071. this.valuebg && this.valuebg.css( "width", percent + "%" );
  5072. // drag the label widths
  5073. if ( this._labels ) {
  5074. var handlePercent = this.handle.width() / this.slider.width() * 100,
  5075. aPercent = percent && handlePercent + ( 100 - handlePercent ) * percent / 100,
  5076. bPercent = percent === 100 ? 0 : Math.min( handlePercent + 100 - aPercent, 100 );
  5077. this._labels.each(function(){
  5078. var ab = $(this).is( ".ui-slider-label-a" );
  5079. $( this ).width( ( ab ? aPercent : bPercent ) + "%" );
  5080. });
  5081. }
  5082. if ( !preventInputUpdate ) {
  5083. var valueChanged = false;
  5084. // update control"s value
  5085. if ( cType === "input" ) {
  5086. valueChanged = control.val() !== newval;
  5087. control.val( newval );
  5088. } else {
  5089. valueChanged = control[ 0 ].selectedIndex !== newval;
  5090. control[ 0 ].selectedIndex = newval;
  5091. }
  5092. if ( !isfromControl && valueChanged ) {
  5093. control.trigger( "change" );
  5094. }
  5095. }
  5096. },
  5097. enable: function() {
  5098. this.element.attr( "disabled", false );
  5099. this.slider.removeClass( "ui-disabled" ).attr( "aria-disabled", false );
  5100. return this._setOption( "disabled", false );
  5101. },
  5102. disable: function() {
  5103. this.element.attr( "disabled", true );
  5104. this.slider.addClass( "ui-disabled" ).attr( "aria-disabled", true );
  5105. return this._setOption( "disabled", true );
  5106. }
  5107. });
  5108. //auto self-init widgets
  5109. $( document ).bind( "pagecreate create", function( e ){
  5110. $.mobile.slider.prototype.enhanceWithin( e.target, true );
  5111. });
  5112. })( jQuery );
  5113. (function( $, undefined ) {
  5114. $.widget( "mobile.selectmenu", $.mobile.widget, {
  5115. options: {
  5116. theme: null,
  5117. disabled: false,
  5118. icon: "arrow-d",
  5119. iconpos: "right",
  5120. inline: false,
  5121. corners: true,
  5122. shadow: true,
  5123. iconshadow: true,
  5124. overlayTheme: "a",
  5125. hidePlaceholderMenuItems: true,
  5126. closeText: "Close",
  5127. nativeMenu: true,
  5128. // This option defaults to true on iOS devices.
  5129. preventFocusZoom: /iPhone|iPad|iPod/.test( navigator.platform ) && navigator.userAgent.indexOf( "AppleWebKit" ) > -1,
  5130. initSelector: "select:not(:jqmData(role='slider'))",
  5131. mini: false
  5132. },
  5133. _button: function(){
  5134. return $( "<div/>" );
  5135. },
  5136. _setDisabled: function( value ) {
  5137. this.element.attr( "disabled", value );
  5138. this.button.attr( "aria-disabled", value );
  5139. return this._setOption( "disabled", value );
  5140. },
  5141. _focusButton : function() {
  5142. var self = this;
  5143. setTimeout( function() {
  5144. self.button.focus();
  5145. }, 40);
  5146. },
  5147. _selectOptions: function() {
  5148. return this.select.find( "option" );
  5149. },
  5150. // setup items that are generally necessary for select menu extension
  5151. _preExtension: function(){
  5152. var classes = "";
  5153. // TODO: Post 1.1--once we have time to test thoroughly--any classes manually applied to the original element should be carried over to the enhanced element, with an `-enhanced` suffix. See https://github.com/jquery/jquery-mobile/issues/3577
  5154. /* if( $el[0].className.length ) {
  5155. classes = $el[0].className;
  5156. } */
  5157. if( !!~this.element[0].className.indexOf( "ui-btn-left" ) ) {
  5158. classes = " ui-btn-left";
  5159. }
  5160. if( !!~this.element[0].className.indexOf( "ui-btn-right" ) ) {
  5161. classes = " ui-btn-right";
  5162. }
  5163. this.select = this.element.wrap( "<div class='ui-select" + classes + "'>" );
  5164. this.selectID = this.select.attr( "id" );
  5165. this.label = $( "label[for='"+ this.selectID +"']" ).addClass( "ui-select" );
  5166. this.isMultiple = this.select[ 0 ].multiple;
  5167. if ( !this.options.theme ) {
  5168. this.options.theme = $.mobile.getInheritedTheme( this.select, "c" );
  5169. }
  5170. },
  5171. _create: function() {
  5172. this._preExtension();
  5173. // Allows for extension of the native select for custom selects and other plugins
  5174. // see select.custom for example extension
  5175. // TODO explore plugin registration
  5176. this._trigger( "beforeCreate" );
  5177. this.button = this._button();
  5178. var self = this,
  5179. options = this.options,
  5180. // IE throws an exception at options.item() function when
  5181. // there is no selected item
  5182. // select first in this case
  5183. selectedIndex = this.select[ 0 ].selectedIndex == -1 ? 0 : this.select[ 0 ].selectedIndex,
  5184. // TODO values buttonId and menuId are undefined here
  5185. button = this.button
  5186. .text( $( this.select[ 0 ].options.item( selectedIndex ) ).text() )
  5187. .insertBefore( this.select )
  5188. .buttonMarkup( {
  5189. theme: options.theme,
  5190. icon: options.icon,
  5191. iconpos: options.iconpos,
  5192. inline: options.inline,
  5193. corners: options.corners,
  5194. shadow: options.shadow,
  5195. iconshadow: options.iconshadow,
  5196. mini: options.mini
  5197. });
  5198. // Opera does not properly support opacity on select elements
  5199. // In Mini, it hides the element, but not its text
  5200. // On the desktop,it seems to do the opposite
  5201. // for these reasons, using the nativeMenu option results in a full native select in Opera
  5202. if ( options.nativeMenu && window.opera && window.opera.version ) {
  5203. this.select.addClass( "ui-select-nativeonly" );
  5204. }
  5205. // Add counter for multi selects
  5206. if ( this.isMultiple ) {
  5207. this.buttonCount = $( "<span>" )
  5208. .addClass( "ui-li-count ui-btn-up-c ui-btn-corner-all" )
  5209. .hide()
  5210. .appendTo( button.addClass('ui-li-has-count') );
  5211. }
  5212. // Disable if specified
  5213. if ( options.disabled || this.element.attr('disabled')) {
  5214. this.disable();
  5215. }
  5216. // Events on native select
  5217. this.select.change( function() {
  5218. self.refresh();
  5219. });
  5220. this.build();
  5221. },
  5222. build: function() {
  5223. var self = this;
  5224. this.select
  5225. .appendTo( self.button )
  5226. .bind( "vmousedown", function() {
  5227. // Add active class to button
  5228. self.button.addClass( $.mobile.activeBtnClass );
  5229. })
  5230. .bind( "focus", function() {
  5231. self.button.addClass( $.mobile.focusClass );
  5232. })
  5233. .bind( "blur", function() {
  5234. self.button.removeClass( $.mobile.focusClass );
  5235. })
  5236. .bind( "focus vmouseover", function() {
  5237. self.button.trigger( "vmouseover" );
  5238. })
  5239. .bind( "vmousemove", function() {
  5240. // Remove active class on scroll/touchmove
  5241. self.button.removeClass( $.mobile.activeBtnClass );
  5242. })
  5243. .bind( "change blur vmouseout", function() {
  5244. self.button.trigger( "vmouseout" )
  5245. .removeClass( $.mobile.activeBtnClass );
  5246. })
  5247. .bind( "change blur", function() {
  5248. self.button.removeClass( "ui-btn-down-" + self.options.theme );
  5249. });
  5250. // In many situations, iOS will zoom into the select upon tap, this prevents that from happening
  5251. self.button.bind( "vmousedown", function() {
  5252. if( self.options.preventFocusZoom ){
  5253. $.mobile.zoom.disable( true );
  5254. }
  5255. })
  5256. .bind( "mouseup", function() {
  5257. if( self.options.preventFocusZoom ){
  5258. $.mobile.zoom.enable( true );
  5259. }
  5260. });
  5261. },
  5262. selected: function() {
  5263. return this._selectOptions().filter( ":selected" );
  5264. },
  5265. selectedIndices: function() {
  5266. var self = this;
  5267. return this.selected().map( function() {
  5268. return self._selectOptions().index( this );
  5269. }).get();
  5270. },
  5271. setButtonText: function() {
  5272. var self = this, selected = this.selected();
  5273. this.button.find( ".ui-btn-text" ).text( function() {
  5274. if ( !self.isMultiple ) {
  5275. return selected.text();
  5276. }
  5277. return selected.length ? selected.map( function() {
  5278. return $( this ).text();
  5279. }).get().join( ", " ) : self.placeholder;
  5280. });
  5281. },
  5282. setButtonCount: function() {
  5283. var selected = this.selected();
  5284. // multiple count inside button
  5285. if ( this.isMultiple ) {
  5286. this.buttonCount[ selected.length > 1 ? "show" : "hide" ]().text( selected.length );
  5287. }
  5288. },
  5289. refresh: function() {
  5290. this.setButtonText();
  5291. this.setButtonCount();
  5292. },
  5293. // open and close preserved in native selects
  5294. // to simplify users code when looping over selects
  5295. open: $.noop,
  5296. close: $.noop,
  5297. disable: function() {
  5298. this._setDisabled( true );
  5299. this.button.addClass( "ui-disabled" );
  5300. },
  5301. enable: function() {
  5302. this._setDisabled( false );
  5303. this.button.removeClass( "ui-disabled" );
  5304. }
  5305. });
  5306. //auto self-init widgets
  5307. $( document ).bind( "pagecreate create", function( e ){
  5308. $.mobile.selectmenu.prototype.enhanceWithin( e.target, true );
  5309. });
  5310. })( jQuery );
  5311. /*
  5312. * custom "selectmenu" plugin
  5313. */
  5314. (function( $, undefined ) {
  5315. var extendSelect = function( widget ){
  5316. var select = widget.select,
  5317. selectID = widget.selectID,
  5318. label = widget.label,
  5319. thisPage = widget.select.closest( ".ui-page" ),
  5320. screen = $( "<div>", {"class": "ui-selectmenu-screen ui-screen-hidden"} ).appendTo( thisPage ),
  5321. selectOptions = widget._selectOptions(),
  5322. isMultiple = widget.isMultiple = widget.select[ 0 ].multiple,
  5323. buttonId = selectID + "-button",
  5324. menuId = selectID + "-menu",
  5325. menuPage = $( "<div data-" + $.mobile.ns + "role='dialog' data-" +$.mobile.ns + "theme='"+ widget.options.theme +"' data-" +$.mobile.ns + "overlay-theme='"+ widget.options.overlayTheme +"'>" +
  5326. "<div data-" + $.mobile.ns + "role='header'>" +
  5327. "<div class='ui-title'>" + label.getEncodedText() + "</div>"+
  5328. "</div>"+
  5329. "<div data-" + $.mobile.ns + "role='content'></div>"+
  5330. "</div>" ),
  5331. listbox = $("<div>", { "class": "ui-selectmenu ui-selectmenu-hidden ui-overlay-shadow ui-corner-all ui-body-" + widget.options.overlayTheme + " " + $.mobile.defaultDialogTransition } ).insertAfter(screen),
  5332. list = $( "<ul>", {
  5333. "class": "ui-selectmenu-list",
  5334. "id": menuId,
  5335. "role": "listbox",
  5336. "aria-labelledby": buttonId
  5337. }).attr( "data-" + $.mobile.ns + "theme", widget.options.theme ).appendTo( listbox ),
  5338. header = $( "<div>", {
  5339. "class": "ui-header ui-bar-" + widget.options.theme
  5340. }).prependTo( listbox ),
  5341. headerTitle = $( "<h1>", {
  5342. "class": "ui-title"
  5343. }).appendTo( header ),
  5344. menuPageContent,
  5345. menuPageClose,
  5346. headerClose;
  5347. if( widget.isMultiple ) {
  5348. headerClose = $( "<a>", {
  5349. "text": widget.options.closeText,
  5350. "href": "#",
  5351. "class": "ui-btn-left"
  5352. }).attr( "data-" + $.mobile.ns + "iconpos", "notext" ).attr( "data-" + $.mobile.ns + "icon", "delete" ).appendTo( header ).buttonMarkup();
  5353. }
  5354. $.extend( widget, {
  5355. select: widget.select,
  5356. selectID: selectID,
  5357. buttonId: buttonId,
  5358. menuId: menuId,
  5359. thisPage: thisPage,
  5360. menuPage: menuPage,
  5361. label: label,
  5362. screen: screen,
  5363. selectOptions: selectOptions,
  5364. isMultiple: isMultiple,
  5365. theme: widget.options.theme,
  5366. listbox: listbox,
  5367. list: list,
  5368. header: header,
  5369. headerTitle: headerTitle,
  5370. headerClose: headerClose,
  5371. menuPageContent: menuPageContent,
  5372. menuPageClose: menuPageClose,
  5373. placeholder: "",
  5374. build: function() {
  5375. var self = this;
  5376. // Create list from select, update state
  5377. self.refresh();
  5378. self.select.attr( "tabindex", "-1" ).focus(function() {
  5379. $( this ).blur();
  5380. self.button.focus();
  5381. });
  5382. // Button events
  5383. self.button.bind( "vclick keydown" , function( event ) {
  5384. if ( event.type == "vclick" ||
  5385. event.keyCode && ( event.keyCode === $.mobile.keyCode.ENTER ||
  5386. event.keyCode === $.mobile.keyCode.SPACE ) ) {
  5387. self.open();
  5388. event.preventDefault();
  5389. }
  5390. });
  5391. // Events for list items
  5392. self.list.attr( "role", "listbox" )
  5393. .bind( "focusin", function( e ){
  5394. $( e.target )
  5395. .attr( "tabindex", "0" )
  5396. .trigger( "vmouseover" );
  5397. })
  5398. .bind( "focusout", function( e ){
  5399. $( e.target )
  5400. .attr( "tabindex", "-1" )
  5401. .trigger( "vmouseout" );
  5402. })
  5403. .delegate( "li:not(.ui-disabled, .ui-li-divider)", "click", function( event ) {
  5404. // index of option tag to be selected
  5405. var oldIndex = self.select[ 0 ].selectedIndex,
  5406. newIndex = self.list.find( "li:not(.ui-li-divider)" ).index( this ),
  5407. option = self._selectOptions().eq( newIndex )[ 0 ];
  5408. // toggle selected status on the tag for multi selects
  5409. option.selected = self.isMultiple ? !option.selected : true;
  5410. // toggle checkbox class for multiple selects
  5411. if ( self.isMultiple ) {
  5412. $( this ).find( ".ui-icon" )
  5413. .toggleClass( "ui-icon-checkbox-on", option.selected )
  5414. .toggleClass( "ui-icon-checkbox-off", !option.selected );
  5415. }
  5416. // trigger change if value changed
  5417. if ( self.isMultiple || oldIndex !== newIndex ) {
  5418. self.select.trigger( "change" );
  5419. }
  5420. //hide custom select for single selects only
  5421. if ( !self.isMultiple ) {
  5422. self.close();
  5423. }
  5424. event.preventDefault();
  5425. })
  5426. .keydown(function( event ) { //keyboard events for menu items
  5427. var target = $( event.target ),
  5428. li = target.closest( "li" ),
  5429. prev, next;
  5430. // switch logic based on which key was pressed
  5431. switch ( event.keyCode ) {
  5432. // up or left arrow keys
  5433. case 38:
  5434. prev = li.prev().not( ".ui-selectmenu-placeholder" );
  5435. if( prev.is( ".ui-li-divider" ) ) {
  5436. prev = prev.prev();
  5437. }
  5438. // if there's a previous option, focus it
  5439. if ( prev.length ) {
  5440. target
  5441. .blur()
  5442. .attr( "tabindex", "-1" );
  5443. prev.addClass( "ui-btn-down-" + widget.options.theme ).find( "a" ).first().focus();
  5444. }
  5445. return false;
  5446. break;
  5447. // down or right arrow keys
  5448. case 40:
  5449. next = li.next();
  5450. if( next.is( ".ui-li-divider" ) ) {
  5451. next = next.next();
  5452. }
  5453. // if there's a next option, focus it
  5454. if ( next.length ) {
  5455. target
  5456. .blur()
  5457. .attr( "tabindex", "-1" );
  5458. next.addClass( "ui-btn-down-" + widget.options.theme ).find( "a" ).first().focus();
  5459. }
  5460. return false;
  5461. break;
  5462. // If enter or space is pressed, trigger click
  5463. case 13:
  5464. case 32:
  5465. target.trigger( "click" );
  5466. return false;
  5467. break;
  5468. }
  5469. });
  5470. // button refocus ensures proper height calculation
  5471. // by removing the inline style and ensuring page inclusion
  5472. self.menuPage.bind( "pagehide", function() {
  5473. self.list.appendTo( self.listbox );
  5474. self._focusButton();
  5475. // TODO centralize page removal binding / handling in the page plugin.
  5476. // Suggestion from @jblas to do refcounting
  5477. //
  5478. // TODO extremely confusing dependency on the open method where the pagehide.remove
  5479. // bindings are stripped to prevent the parent page from disappearing. The way
  5480. // we're keeping pages in the DOM right now sucks
  5481. //
  5482. // rebind the page remove that was unbound in the open function
  5483. // to allow for the parent page removal from actions other than the use
  5484. // of a dialog sized custom select
  5485. //
  5486. // doing this here provides for the back button on the custom select dialog
  5487. $.mobile._bindPageRemove.call( self.thisPage );
  5488. });
  5489. // Events on "screen" overlay
  5490. self.screen.bind( "vclick", function( event ) {
  5491. self.close();
  5492. });
  5493. // Close button on small overlays
  5494. if( self.isMultiple ){
  5495. self.headerClose.click( function() {
  5496. if ( self.menuType == "overlay" ) {
  5497. self.close();
  5498. return false;
  5499. }
  5500. });
  5501. }
  5502. // track this dependency so that when the parent page
  5503. // is removed on pagehide it will also remove the menupage
  5504. self.thisPage.addDependents( this.menuPage );
  5505. },
  5506. _isRebuildRequired: function() {
  5507. var list = this.list.find( "li" ),
  5508. options = this._selectOptions();
  5509. // TODO exceedingly naive method to determine difference
  5510. // ignores value changes etc in favor of a forcedRebuild
  5511. // from the user in the refresh method
  5512. return options.text() !== list.text();
  5513. },
  5514. refresh: function( forceRebuild , foo ){
  5515. var self = this,
  5516. select = this.element,
  5517. isMultiple = this.isMultiple,
  5518. options = this._selectOptions(),
  5519. selected = this.selected(),
  5520. // return an array of all selected index's
  5521. indicies = this.selectedIndices();
  5522. if ( forceRebuild || this._isRebuildRequired() ) {
  5523. self._buildList();
  5524. }
  5525. self.setButtonText();
  5526. self.setButtonCount();
  5527. self.list.find( "li:not(.ui-li-divider)" )
  5528. .removeClass( $.mobile.activeBtnClass )
  5529. .attr( "aria-selected", false )
  5530. .each(function( i ) {
  5531. if ( $.inArray( i, indicies ) > -1 ) {
  5532. var item = $( this );
  5533. // Aria selected attr
  5534. item.attr( "aria-selected", true );
  5535. // Multiple selects: add the "on" checkbox state to the icon
  5536. if ( self.isMultiple ) {
  5537. item.find( ".ui-icon" ).removeClass( "ui-icon-checkbox-off" ).addClass( "ui-icon-checkbox-on" );
  5538. } else {
  5539. if( item.is( ".ui-selectmenu-placeholder" ) ) {
  5540. item.next().addClass( $.mobile.activeBtnClass );
  5541. } else {
  5542. item.addClass( $.mobile.activeBtnClass );
  5543. }
  5544. }
  5545. }
  5546. });
  5547. },
  5548. close: function() {
  5549. if ( this.options.disabled || !this.isOpen ) {
  5550. return;
  5551. }
  5552. var self = this;
  5553. if ( self.menuType == "page" ) {
  5554. // doesn't solve the possible issue with calling change page
  5555. // where the objects don't define data urls which prevents dialog key
  5556. // stripping - changePage has incoming refactor
  5557. window.history.back();
  5558. } else {
  5559. self.screen.addClass( "ui-screen-hidden" );
  5560. self.listbox.addClass( "ui-selectmenu-hidden" ).removeAttr( "style" ).removeClass( "in" );
  5561. self.list.appendTo( self.listbox );
  5562. self._focusButton();
  5563. }
  5564. // allow the dialog to be closed again
  5565. self.isOpen = false;
  5566. },
  5567. open: function() {
  5568. if ( this.options.disabled ) {
  5569. return;
  5570. }
  5571. var self = this,
  5572. $window = $( window ),
  5573. selfListParent = self.list.parent(),
  5574. menuHeight = selfListParent.outerHeight(),
  5575. menuWidth = selfListParent.outerWidth(),
  5576. activePage = $( ".ui-page-active" ),
  5577. tScrollElem = activePage,
  5578. scrollTop = $window.scrollTop(),
  5579. btnOffset = self.button.offset().top,
  5580. screenHeight = $window.height(),
  5581. screenWidth = $window.width();
  5582. //add active class to button
  5583. self.button.addClass( $.mobile.activeBtnClass );
  5584. //remove after delay
  5585. setTimeout( function() {
  5586. self.button.removeClass( $.mobile.activeBtnClass );
  5587. }, 300);
  5588. function focusMenuItem() {
  5589. self.list.find( "." + $.mobile.activeBtnClass + " a" ).focus();
  5590. }
  5591. if ( menuHeight > screenHeight - 80 || !$.support.scrollTop ) {
  5592. self.menuPage.appendTo( $.mobile.pageContainer ).page();
  5593. self.menuPageContent = menuPage.find( ".ui-content" );
  5594. self.menuPageClose = menuPage.find( ".ui-header a" );
  5595. // prevent the parent page from being removed from the DOM,
  5596. // otherwise the results of selecting a list item in the dialog
  5597. // fall into a black hole
  5598. self.thisPage.unbind( "pagehide.remove" );
  5599. //for WebOS/Opera Mini (set lastscroll using button offset)
  5600. if ( scrollTop == 0 && btnOffset > screenHeight ) {
  5601. self.thisPage.one( "pagehide", function() {
  5602. $( this ).jqmData( "lastScroll", btnOffset );
  5603. });
  5604. }
  5605. self.menuPage.one( "pageshow", function() {
  5606. focusMenuItem();
  5607. self.isOpen = true;
  5608. });
  5609. self.menuType = "page";
  5610. self.menuPageContent.append( self.list );
  5611. self.menuPage.find("div .ui-title").text(self.label.text());
  5612. $.mobile.changePage( self.menuPage, {
  5613. transition: $.mobile.defaultDialogTransition
  5614. });
  5615. } else {
  5616. self.menuType = "overlay";
  5617. self.screen.height( $(document).height() )
  5618. .removeClass( "ui-screen-hidden" );
  5619. // Try and center the overlay over the button
  5620. var roomtop = btnOffset - scrollTop,
  5621. roombot = scrollTop + screenHeight - btnOffset,
  5622. halfheight = menuHeight / 2,
  5623. maxwidth = parseFloat( self.list.parent().css( "max-width" ) ),
  5624. newtop, newleft;
  5625. if ( roomtop > menuHeight / 2 && roombot > menuHeight / 2 ) {
  5626. newtop = btnOffset + ( self.button.outerHeight() / 2 ) - halfheight;
  5627. } else {
  5628. // 30px tolerance off the edges
  5629. newtop = roomtop > roombot ? scrollTop + screenHeight - menuHeight - 30 : scrollTop + 30;
  5630. }
  5631. // If the menuwidth is smaller than the screen center is
  5632. if ( menuWidth < maxwidth ) {
  5633. newleft = ( screenWidth - menuWidth ) / 2;
  5634. } else {
  5635. //otherwise insure a >= 30px offset from the left
  5636. newleft = self.button.offset().left + self.button.outerWidth() / 2 - menuWidth / 2;
  5637. // 30px tolerance off the edges
  5638. if ( newleft < 30 ) {
  5639. newleft = 30;
  5640. } else if ( (newleft + menuWidth) > screenWidth ) {
  5641. newleft = screenWidth - menuWidth - 30;
  5642. }
  5643. }
  5644. self.listbox.append( self.list )
  5645. .removeClass( "ui-selectmenu-hidden" )
  5646. .css({
  5647. top: newtop,
  5648. left: newleft
  5649. })
  5650. .addClass( "in" );
  5651. focusMenuItem();
  5652. // duplicate with value set in page show for dialog sized selects
  5653. self.isOpen = true;
  5654. }
  5655. },
  5656. _buildList: function() {
  5657. var self = this,
  5658. o = this.options,
  5659. placeholder = this.placeholder,
  5660. needPlaceholder = true,
  5661. optgroups = [],
  5662. lis = [],
  5663. dataIcon = self.isMultiple ? "checkbox-off" : "false";
  5664. self.list.empty().filter( ".ui-listview" ).listview( "destroy" );
  5665. var $options = self.select.find("option"),
  5666. numOptions = $options.length,
  5667. select = this.select[ 0 ],
  5668. dataPrefix = 'data-' + $.mobile.ns,
  5669. dataIndexAttr = dataPrefix + 'option-index',
  5670. dataIconAttr = dataPrefix + 'icon',
  5671. dataRoleAttr = dataPrefix + 'role',
  5672. fragment = document.createDocumentFragment(),
  5673. optGroup;
  5674. for (var i = 0; i < numOptions;i++){
  5675. var option = $options[i],
  5676. $option = $(option),
  5677. parent = option.parentNode,
  5678. text = $option.text(),
  5679. anchor = document.createElement('a'),
  5680. classes = [];
  5681. anchor.setAttribute('href','#');
  5682. anchor.appendChild(document.createTextNode(text));
  5683. // Are we inside an optgroup?
  5684. if (parent !== select && parent.nodeName.toLowerCase() === "optgroup"){
  5685. var optLabel = parent.getAttribute('label');
  5686. if ( optLabel != optGroup) {
  5687. var divider = document.createElement('li');
  5688. divider.setAttribute(dataRoleAttr,'list-divider');
  5689. divider.setAttribute('role','option');
  5690. divider.setAttribute('tabindex','-1');
  5691. divider.appendChild(document.createTextNode(optLabel));
  5692. fragment.appendChild(divider);
  5693. optGroup = optLabel;
  5694. }
  5695. }
  5696. if (needPlaceholder && (!option.getAttribute( "value" ) || text.length == 0 || $option.jqmData( "placeholder" ))) {
  5697. needPlaceholder = false;
  5698. if ( o.hidePlaceholderMenuItems ) {
  5699. classes.push( "ui-selectmenu-placeholder" );
  5700. }
  5701. if (!placeholder) {
  5702. placeholder = self.placeholder = text;
  5703. }
  5704. }
  5705. var item = document.createElement('li');
  5706. if ( option.disabled ) {
  5707. classes.push( "ui-disabled" );
  5708. item.setAttribute('aria-disabled',true);
  5709. }
  5710. item.setAttribute(dataIndexAttr,i);
  5711. item.setAttribute(dataIconAttr,dataIcon);
  5712. item.className = classes.join(" ");
  5713. item.setAttribute('role','option');
  5714. anchor.setAttribute('tabindex','-1');
  5715. item.appendChild(anchor);
  5716. fragment.appendChild(item);
  5717. }
  5718. self.list[0].appendChild(fragment);
  5719. // Hide header if it's not a multiselect and there's no placeholder
  5720. if ( !this.isMultiple && !placeholder.length ) {
  5721. this.header.hide();
  5722. } else {
  5723. this.headerTitle.text( this.placeholder );
  5724. }
  5725. // Now populated, create listview
  5726. self.list.listview();
  5727. },
  5728. _button: function(){
  5729. return $( "<a>", {
  5730. "href": "#",
  5731. "role": "button",
  5732. // TODO value is undefined at creation
  5733. "id": this.buttonId,
  5734. "aria-haspopup": "true",
  5735. // TODO value is undefined at creation
  5736. "aria-owns": this.menuId
  5737. });
  5738. }
  5739. });
  5740. };
  5741. // issue #3894 - core doesn't triggered events on disabled delegates
  5742. $( document ).bind( "selectmenubeforecreate", function( event ){
  5743. var selectmenuWidget = $( event.target ).data( "selectmenu" );
  5744. if( !selectmenuWidget.options.nativeMenu ){
  5745. extendSelect( selectmenuWidget );
  5746. }
  5747. });
  5748. })( jQuery );
  5749. (function( $, undefined ) {
  5750. $.widget( "mobile.fixedtoolbar", $.mobile.widget, {
  5751. options: {
  5752. visibleOnPageShow: true,
  5753. disablePageZoom: true,
  5754. transition: "slide", //can be none, fade, slide (slide maps to slideup or slidedown)
  5755. fullscreen: false,
  5756. tapToggle: true,
  5757. tapToggleBlacklist: "a, input, select, textarea, .ui-header-fixed, .ui-footer-fixed",
  5758. hideDuringFocus: "input, textarea, select",
  5759. updatePagePadding: true,
  5760. trackPersistentToolbars: true,
  5761. // Browser detection! Weeee, here we go...
  5762. // Unfortunately, position:fixed is costly, not to mention probably impossible, to feature-detect accurately.
  5763. // Some tests exist, but they currently return false results in critical devices and browsers, which could lead to a broken experience.
  5764. // Testing fixed positioning is also pretty obtrusive to page load, requiring injected elements and scrolling the window
  5765. // The following function serves to rule out some popular browsers with known fixed-positioning issues
  5766. // This is a plugin option like any other, so feel free to improve or overwrite it
  5767. supportBlacklist: function(){
  5768. var w = window,
  5769. ua = navigator.userAgent,
  5770. platform = navigator.platform,
  5771. // Rendering engine is Webkit, and capture major version
  5772. wkmatch = ua.match( /AppleWebKit\/([0-9]+)/ ),
  5773. wkversion = !!wkmatch && wkmatch[ 1 ],
  5774. ffmatch = ua.match( /Fennec\/([0-9]+)/ ),
  5775. ffversion = !!ffmatch && ffmatch[ 1 ],
  5776. operammobilematch = ua.match( /Opera Mobi\/([0-9]+)/ ),
  5777. omversion = !!operammobilematch && operammobilematch[ 1 ];
  5778. if(
  5779. // iOS 4.3 and older : Platform is iPhone/Pad/Touch and Webkit version is less than 534 (ios5)
  5780. ( ( platform.indexOf( "iPhone" ) > -1 || platform.indexOf( "iPad" ) > -1 || platform.indexOf( "iPod" ) > -1 ) && wkversion && wkversion < 534 )
  5781. ||
  5782. // Opera Mini
  5783. ( w.operamini && ({}).toString.call( w.operamini ) === "[object OperaMini]" )
  5784. ||
  5785. ( operammobilematch && omversion < 7458 )
  5786. ||
  5787. //Android lte 2.1: Platform is Android and Webkit version is less than 533 (Android 2.2)
  5788. ( ua.indexOf( "Android" ) > -1 && wkversion && wkversion < 533 )
  5789. ||
  5790. // Firefox Mobile before 6.0 -
  5791. ( ffversion && ffversion < 6 )
  5792. ||
  5793. // WebOS less than 3
  5794. ( "palmGetResource" in window && wkversion && wkversion < 534 )
  5795. ||
  5796. // MeeGo
  5797. ( ua.indexOf( "MeeGo" ) > -1 && ua.indexOf( "NokiaBrowser/8.5.0" ) > -1 )
  5798. ){
  5799. return true;
  5800. }
  5801. return false;
  5802. },
  5803. initSelector: ":jqmData(position='fixed')"
  5804. },
  5805. _create: function() {
  5806. var self = this,
  5807. o = self.options,
  5808. $el = self.element,
  5809. tbtype = $el.is( ":jqmData(role='header')" ) ? "header" : "footer",
  5810. $page = $el.closest(".ui-page");
  5811. // Feature detecting support for
  5812. if( o.supportBlacklist() ){
  5813. self.destroy();
  5814. return;
  5815. }
  5816. $el.addClass( "ui-"+ tbtype +"-fixed" );
  5817. // "fullscreen" overlay positioning
  5818. if( o.fullscreen ){
  5819. $el.addClass( "ui-"+ tbtype +"-fullscreen" );
  5820. $page.addClass( "ui-page-" + tbtype + "-fullscreen" );
  5821. }
  5822. // If not fullscreen, add class to page to set top or bottom padding
  5823. else{
  5824. $page.addClass( "ui-page-" + tbtype + "-fixed" );
  5825. }
  5826. self._addTransitionClass();
  5827. self._bindPageEvents();
  5828. self._bindToggleHandlers();
  5829. },
  5830. _addTransitionClass: function(){
  5831. var tclass = this.options.transition;
  5832. if( tclass && tclass !== "none" ){
  5833. // use appropriate slide for header or footer
  5834. if( tclass === "slide" ){
  5835. tclass = this.element.is( ".ui-header" ) ? "slidedown" : "slideup";
  5836. }
  5837. this.element.addClass( tclass );
  5838. }
  5839. },
  5840. _bindPageEvents: function(){
  5841. var self = this,
  5842. o = self.options,
  5843. $el = self.element;
  5844. //page event bindings
  5845. // Fixed toolbars require page zoom to be disabled, otherwise usability issues crop up
  5846. // This method is meant to disable zoom while a fixed-positioned toolbar page is visible
  5847. $el.closest( ".ui-page" )
  5848. .bind( "pagebeforeshow", function(){
  5849. if( o.disablePageZoom ){
  5850. $.mobile.zoom.disable( true );
  5851. }
  5852. if( !o.visibleOnPageShow ){
  5853. self.hide( true );
  5854. }
  5855. } )
  5856. .bind( "webkitAnimationStart animationstart updatelayout", function(){
  5857. if( o.updatePagePadding ){
  5858. self.updatePagePadding();
  5859. }
  5860. })
  5861. .bind( "pageshow", function(){
  5862. self.updatePagePadding();
  5863. if( o.updatePagePadding ){
  5864. $( window ).bind( "throttledresize." + self.widgetName, function(){
  5865. self.updatePagePadding();
  5866. });
  5867. }
  5868. })
  5869. .bind( "pagebeforehide", function( e, ui ){
  5870. if( o.disablePageZoom ){
  5871. $.mobile.zoom.enable( true );
  5872. }
  5873. if( o.updatePagePadding ){
  5874. $( window ).unbind( "throttledresize." + self.widgetName );
  5875. }
  5876. if( o.trackPersistentToolbars ){
  5877. var thisFooter = $( ".ui-footer-fixed:jqmData(id)", this ),
  5878. thisHeader = $( ".ui-header-fixed:jqmData(id)", this ),
  5879. nextFooter = thisFooter.length && ui.nextPage && $( ".ui-footer-fixed:jqmData(id='" + thisFooter.jqmData( "id" ) + "')", ui.nextPage ),
  5880. nextHeader = thisHeader.length && ui.nextPage && $( ".ui-header-fixed:jqmData(id='" + thisHeader.jqmData( "id" ) + "')", ui.nextPage );
  5881. nextFooter = nextFooter || $();
  5882. if( nextFooter.length || nextHeader.length ){
  5883. nextFooter.add( nextHeader ).appendTo( $.mobile.pageContainer );
  5884. ui.nextPage.one( "pageshow", function(){
  5885. nextFooter.add( nextHeader ).appendTo( this );
  5886. });
  5887. }
  5888. }
  5889. });
  5890. },
  5891. _visible: true,
  5892. // This will set the content element's top or bottom padding equal to the toolbar's height
  5893. updatePagePadding: function() {
  5894. var $el = this.element,
  5895. header = $el.is( ".ui-header" );
  5896. // This behavior only applies to "fixed", not "fullscreen"
  5897. if( this.options.fullscreen ){ return; }
  5898. $el.closest( ".ui-page" ).css( "padding-" + ( header ? "top" : "bottom" ), $el.outerHeight() );
  5899. },
  5900. _useTransition: function( notransition ){
  5901. var $win = $( window ),
  5902. $el = this.element,
  5903. scroll = $win.scrollTop(),
  5904. elHeight = $el.height(),
  5905. pHeight = $el.closest( ".ui-page" ).height(),
  5906. viewportHeight = $.mobile.getScreenHeight(),
  5907. tbtype = $el.is( ":jqmData(role='header')" ) ? "header" : "footer";
  5908. return !notransition &&
  5909. ( this.options.transition && this.options.transition !== "none" &&
  5910. (
  5911. ( tbtype === "header" && !this.options.fullscreen && scroll > elHeight ) ||
  5912. ( tbtype === "footer" && !this.options.fullscreen && scroll + viewportHeight < pHeight - elHeight )
  5913. ) || this.options.fullscreen
  5914. );
  5915. },
  5916. show: function( notransition ){
  5917. var hideClass = "ui-fixed-hidden",
  5918. $el = this.element;
  5919. if( this._useTransition( notransition ) ){
  5920. $el
  5921. .removeClass( "out " + hideClass )
  5922. .addClass( "in" );
  5923. }
  5924. else {
  5925. $el.removeClass( hideClass );
  5926. }
  5927. this._visible = true;
  5928. },
  5929. hide: function( notransition ){
  5930. var hideClass = "ui-fixed-hidden",
  5931. $el = this.element,
  5932. // if it's a slide transition, our new transitions need the reverse class as well to slide outward
  5933. outclass = "out" + ( this.options.transition === "slide" ? " reverse" : "" );
  5934. if( this._useTransition( notransition ) ){
  5935. $el
  5936. .addClass( outclass )
  5937. .removeClass( "in" )
  5938. .animationComplete( function(){
  5939. $el.addClass( hideClass ).removeClass( outclass );
  5940. });
  5941. }
  5942. else {
  5943. $el.addClass( hideClass ).removeClass( outclass );
  5944. }
  5945. this._visible = false;
  5946. },
  5947. toggle: function(){
  5948. this[ this._visible ? "hide" : "show" ]();
  5949. },
  5950. _bindToggleHandlers: function(){
  5951. var self = this,
  5952. o = self.options,
  5953. $el = self.element;
  5954. // tap toggle
  5955. $el.closest( ".ui-page" )
  5956. .bind( "vclick", function( e ){
  5957. if( o.tapToggle && !$( e.target ).closest( o.tapToggleBlacklist ).length ){
  5958. self.toggle();
  5959. }
  5960. })
  5961. .bind( "focusin focusout", function( e ){
  5962. if( screen.width < 500 && $( e.target ).is( o.hideDuringFocus ) && !$( e.target ).closest( ".ui-header-fixed, .ui-footer-fixed" ).length ){
  5963. self[ ( e.type === "focusin" && self._visible ) ? "hide" : "show" ]();
  5964. }
  5965. });
  5966. },
  5967. destroy: function(){
  5968. this.element.removeClass( "ui-header-fixed ui-footer-fixed ui-header-fullscreen ui-footer-fullscreen in out fade slidedown slideup ui-fixed-hidden" );
  5969. this.element.closest( ".ui-page" ).removeClass( "ui-page-header-fixed ui-page-footer-fixed ui-page-header-fullscreen ui-page-footer-fullscreen" );
  5970. }
  5971. });
  5972. //auto self-init widgets
  5973. $( document )
  5974. .bind( "pagecreate create", function( e ){
  5975. // DEPRECATED in 1.1: support for data-fullscreen=true|false on the page element.
  5976. // This line ensures it still works, but we recommend moving the attribute to the toolbars themselves.
  5977. if( $( e.target ).jqmData( "fullscreen" ) ){
  5978. $( $.mobile.fixedtoolbar.prototype.options.initSelector, e.target ).not( ":jqmData(fullscreen)" ).jqmData( "fullscreen", true );
  5979. }
  5980. $.mobile.fixedtoolbar.prototype.enhanceWithin( e.target );
  5981. });
  5982. })( jQuery );
  5983. ( function( $, window ) {
  5984. // This fix addresses an iOS bug, so return early if the UA claims it's something else.
  5985. if( !(/iPhone|iPad|iPod/.test( navigator.platform ) && navigator.userAgent.indexOf( "AppleWebKit" ) > -1 ) ){
  5986. return;
  5987. }
  5988. var zoom = $.mobile.zoom,
  5989. evt, x, y, z, aig;
  5990. function checkTilt( e ){
  5991. evt = e.originalEvent;
  5992. aig = evt.accelerationIncludingGravity;
  5993. x = Math.abs( aig.x );
  5994. y = Math.abs( aig.y );
  5995. z = Math.abs( aig.z );
  5996. // If portrait orientation and in one of the danger zones
  5997. if( !window.orientation && ( x > 7 || ( ( z > 6 && y < 8 || z < 8 && y > 6 ) && x > 5 ) ) ){
  5998. if( zoom.enabled ){
  5999. zoom.disable();
  6000. }
  6001. }
  6002. else if( !zoom.enabled ){
  6003. zoom.enable();
  6004. }
  6005. }
  6006. $( window )
  6007. .bind( "orientationchange.iosorientationfix", zoom.enable )
  6008. .bind( "devicemotion.iosorientationfix", checkTilt );
  6009. }( jQuery, this ));
  6010. ( function( $, window, undefined ) {
  6011. var $html = $( "html" ),
  6012. $head = $( "head" ),
  6013. $window = $( window );
  6014. // trigger mobileinit event - useful hook for configuring $.mobile settings before they're used
  6015. $( window.document ).trigger( "mobileinit" );
  6016. // support conditions
  6017. // if device support condition(s) aren't met, leave things as they are -> a basic, usable experience,
  6018. // otherwise, proceed with the enhancements
  6019. if ( !$.mobile.gradeA() ) {
  6020. return;
  6021. }
  6022. // override ajaxEnabled on platforms that have known conflicts with hash history updates
  6023. // or generally work better browsing in regular http for full page refreshes (BB5, Opera Mini)
  6024. if ( $.mobile.ajaxBlacklist ) {
  6025. $.mobile.ajaxEnabled = false;
  6026. }
  6027. // Add mobile, initial load "rendering" classes to docEl
  6028. $html.addClass( "ui-mobile ui-mobile-rendering" );
  6029. // This is a fallback. If anything goes wrong (JS errors, etc), or events don't fire,
  6030. // this ensures the rendering class is removed after 5 seconds, so content is visible and accessible
  6031. setTimeout( hideRenderingClass, 5000 );
  6032. // loading div which appears during Ajax requests
  6033. // will not appear if $.mobile.loadingMessage is false
  6034. var loaderClass = "ui-loader",
  6035. $loader = $( "<div class='" + loaderClass + "'><span class='ui-icon ui-icon-loading'></span><h1></h1></div>" );
  6036. // For non-fixed supportin browsers. Position at y center (if scrollTop supported), above the activeBtn (if defined), or just 100px from top
  6037. function fakeFixLoader(){
  6038. var activeBtn = $( "." + $.mobile.activeBtnClass ).first();
  6039. $loader
  6040. .css({
  6041. top: $.support.scrollTop && $window.scrollTop() + $window.height() / 2 ||
  6042. activeBtn.length && activeBtn.offset().top || 100
  6043. });
  6044. }
  6045. // check position of loader to see if it appears to be "fixed" to center
  6046. // if not, use abs positioning
  6047. function checkLoaderPosition(){
  6048. var offset = $loader.offset(),
  6049. scrollTop = $window.scrollTop(),
  6050. screenHeight = $.mobile.getScreenHeight();
  6051. if( offset.top < scrollTop || (offset.top - scrollTop) > screenHeight ) {
  6052. $loader.addClass( "ui-loader-fakefix" );
  6053. fakeFixLoader();
  6054. $window
  6055. .unbind( "scroll", checkLoaderPosition )
  6056. .bind( "scroll", fakeFixLoader );
  6057. }
  6058. }
  6059. //remove initial build class (only present on first pageshow)
  6060. function hideRenderingClass(){
  6061. $html.removeClass( "ui-mobile-rendering" );
  6062. }
  6063. $.extend($.mobile, {
  6064. // turn on/off page loading message.
  6065. showPageLoadingMsg: function( theme, msgText, textonly ) {
  6066. $html.addClass( "ui-loading" );
  6067. if ( $.mobile.loadingMessage ) {
  6068. // text visibility from argument takes priority
  6069. var textVisible = textonly || $.mobile.loadingMessageTextVisible;
  6070. theme = theme || $.mobile.loadingMessageTheme,
  6071. $loader
  6072. .attr( "class", loaderClass + " ui-corner-all ui-body-" + ( theme || "a" ) + " ui-loader-" + ( textVisible ? "verbose" : "default" ) + ( textonly ? " ui-loader-textonly" : "" ) )
  6073. .find( "h1" )
  6074. .text( msgText || $.mobile.loadingMessage )
  6075. .end()
  6076. .appendTo( $.mobile.pageContainer );
  6077. checkLoaderPosition();
  6078. $window.bind( "scroll", checkLoaderPosition );
  6079. }
  6080. },
  6081. hidePageLoadingMsg: function() {
  6082. $html.removeClass( "ui-loading" );
  6083. if( $.mobile.loadingMessage ){
  6084. $loader.removeClass( "ui-loader-fakefix" );
  6085. }
  6086. $( window ).unbind( "scroll", fakeFixLoader );
  6087. $( window ).unbind( "scroll", checkLoaderPosition );
  6088. },
  6089. // find and enhance the pages in the dom and transition to the first page.
  6090. initializePage: function() {
  6091. // find present pages
  6092. var $pages = $( ":jqmData(role='page'), :jqmData(role='dialog')" );
  6093. // if no pages are found, create one with body's inner html
  6094. if ( !$pages.length ) {
  6095. $pages = $( "body" ).wrapInner( "<div data-" + $.mobile.ns + "role='page'></div>" ).children( 0 );
  6096. }
  6097. // add dialogs, set data-url attrs
  6098. $pages.each(function() {
  6099. var $this = $(this);
  6100. // unless the data url is already set set it to the pathname
  6101. if ( !$this.jqmData("url") ) {
  6102. $this.attr( "data-" + $.mobile.ns + "url", $this.attr( "id" ) || location.pathname + location.search );
  6103. }
  6104. });
  6105. // define first page in dom case one backs out to the directory root (not always the first page visited, but defined as fallback)
  6106. $.mobile.firstPage = $pages.first();
  6107. // define page container
  6108. $.mobile.pageContainer = $pages.first().parent().addClass( "ui-mobile-viewport" );
  6109. // alert listeners that the pagecontainer has been determined for binding
  6110. // to events triggered on it
  6111. $window.trigger( "pagecontainercreate" );
  6112. // cue page loading message
  6113. $.mobile.showPageLoadingMsg();
  6114. //remove initial build class (only present on first pageshow)
  6115. hideRenderingClass();
  6116. // if hashchange listening is disabled or there's no hash deeplink, change to the first page in the DOM
  6117. if ( !$.mobile.hashListeningEnabled || !$.mobile.path.stripHash( location.hash ) ) {
  6118. $.mobile.changePage( $.mobile.firstPage, { transition: "none", reverse: true, changeHash: false, fromHashChange: true } );
  6119. }
  6120. // otherwise, trigger a hashchange to load a deeplink
  6121. else {
  6122. $window.trigger( "hashchange", [ true ] );
  6123. }
  6124. }
  6125. });
  6126. // initialize events now, after mobileinit has occurred
  6127. $.mobile._registerInternalEvents();
  6128. // check which scrollTop value should be used by scrolling to 1 immediately at domready
  6129. // then check what the scroll top is. Android will report 0... others 1
  6130. // note that this initial scroll won't hide the address bar. It's just for the check.
  6131. $(function() {
  6132. window.scrollTo( 0, 1 );
  6133. // if defaultHomeScroll hasn't been set yet, see if scrollTop is 1
  6134. // it should be 1 in most browsers, but android treats 1 as 0 (for hiding addr bar)
  6135. // so if it's 1, use 0 from now on
  6136. $.mobile.defaultHomeScroll = ( !$.support.scrollTop || $(window).scrollTop() === 1 ) ? 0 : 1;
  6137. // TODO: Implement a proper registration mechanism with dependency handling in order to not have exceptions like the one below
  6138. //auto self-init widgets for those widgets that have a soft dependency on others
  6139. if ( $.fn.controlgroup ) {
  6140. $( document ).bind( "pagecreate create", function( e ){
  6141. $( ":jqmData(role='controlgroup')", e.target )
  6142. .jqmEnhanceable()
  6143. .controlgroup({ excludeInvisible: false });
  6144. });
  6145. }
  6146. //dom-ready inits
  6147. if( $.mobile.autoInitializePage ){
  6148. $.mobile.initializePage();
  6149. }
  6150. // window load event
  6151. // hide iOS browser chrome on load
  6152. $window.load( $.mobile.silentScroll );
  6153. });
  6154. }( jQuery, this ));
  6155. }));