jquery.mobile-1.1.1.js 239 KB

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