| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366336733683369337033713372337333743375337633773378337933803381338233833384338533863387338833893390339133923393339433953396339733983399340034013402340334043405340634073408340934103411341234133414341534163417341834193420342134223423342434253426342734283429343034313432343334343435343634373438343934403441344234433444344534463447344834493450345134523453345434553456345734583459346034613462346334643465346634673468346934703471347234733474347534763477347834793480348134823483348434853486348734883489349034913492349334943495349634973498349935003501350235033504350535063507350835093510351135123513351435153516351735183519352035213522352335243525352635273528352935303531353235333534353535363537353835393540354135423543354435453546354735483549355035513552355335543555355635573558355935603561356235633564356535663567356835693570357135723573357435753576357735783579358035813582358335843585358635873588358935903591359235933594359535963597359835993600360136023603360436053606360736083609361036113612361336143615361636173618361936203621362236233624362536263627362836293630363136323633363436353636363736383639364036413642364336443645364636473648364936503651365236533654365536563657365836593660366136623663366436653666366736683669367036713672367336743675367636773678367936803681368236833684368536863687368836893690369136923693369436953696369736983699370037013702370337043705370637073708370937103711371237133714371537163717371837193720372137223723372437253726372737283729373037313732373337343735373637373738373937403741374237433744374537463747374837493750375137523753375437553756375737583759376037613762376337643765376637673768376937703771377237733774377537763777377837793780378137823783378437853786378737883789379037913792379337943795379637973798379938003801380238033804380538063807380838093810381138123813381438153816381738183819382038213822382338243825382638273828382938303831383238333834383538363837383838393840384138423843384438453846384738483849385038513852385338543855385638573858385938603861386238633864386538663867386838693870387138723873387438753876387738783879388038813882388338843885388638873888388938903891389238933894389538963897389838993900390139023903390439053906390739083909391039113912391339143915391639173918391939203921392239233924392539263927392839293930393139323933393439353936393739383939394039413942394339443945394639473948394939503951395239533954395539563957395839593960396139623963396439653966396739683969397039713972397339743975397639773978397939803981398239833984398539863987398839893990399139923993399439953996399739983999400040014002400340044005400640074008400940104011401240134014401540164017401840194020402140224023402440254026402740284029403040314032403340344035403640374038403940404041404240434044404540464047404840494050405140524053405440554056405740584059406040614062406340644065406640674068406940704071407240734074407540764077407840794080408140824083408440854086408740884089409040914092409340944095409640974098409941004101410241034104410541064107410841094110411141124113411441154116411741184119412041214122412341244125412641274128412941304131413241334134413541364137413841394140414141424143414441454146414741484149415041514152415341544155415641574158415941604161416241634164416541664167416841694170417141724173417441754176417741784179418041814182418341844185418641874188418941904191419241934194419541964197419841994200420142024203420442054206420742084209421042114212421342144215421642174218421942204221422242234224422542264227422842294230423142324233423442354236423742384239424042414242424342444245424642474248424942504251425242534254425542564257425842594260426142624263426442654266426742684269427042714272427342744275427642774278427942804281428242834284428542864287428842894290429142924293429442954296429742984299430043014302430343044305430643074308430943104311431243134314431543164317431843194320432143224323432443254326432743284329433043314332433343344335433643374338433943404341434243434344434543464347434843494350435143524353435443554356435743584359436043614362436343644365436643674368436943704371437243734374437543764377437843794380438143824383438443854386438743884389439043914392439343944395439643974398439944004401440244034404440544064407440844094410441144124413441444154416441744184419442044214422442344244425442644274428442944304431443244334434443544364437443844394440444144424443444444454446444744484449445044514452445344544455445644574458445944604461446244634464446544664467446844694470447144724473447444754476447744784479448044814482448344844485448644874488448944904491449244934494449544964497449844994500450145024503450445054506450745084509451045114512451345144515451645174518451945204521452245234524452545264527452845294530453145324533453445354536453745384539454045414542454345444545454645474548454945504551455245534554455545564557455845594560456145624563456445654566456745684569457045714572457345744575457645774578457945804581458245834584458545864587458845894590459145924593459445954596459745984599460046014602460346044605460646074608460946104611461246134614461546164617461846194620462146224623462446254626462746284629463046314632463346344635463646374638463946404641464246434644464546464647464846494650465146524653465446554656465746584659466046614662466346644665466646674668466946704671467246734674467546764677467846794680468146824683468446854686468746884689469046914692469346944695469646974698469947004701470247034704470547064707470847094710471147124713471447154716471747184719472047214722472347244725472647274728472947304731473247334734473547364737473847394740474147424743474447454746474747484749475047514752475347544755475647574758475947604761476247634764476547664767476847694770477147724773477447754776477747784779478047814782478347844785478647874788478947904791479247934794479547964797479847994800480148024803480448054806480748084809481048114812481348144815481648174818481948204821482248234824482548264827482848294830483148324833483448354836483748384839484048414842484348444845484648474848484948504851485248534854485548564857485848594860486148624863486448654866486748684869487048714872487348744875487648774878487948804881488248834884488548864887488848894890489148924893489448954896489748984899490049014902490349044905490649074908490949104911491249134914491549164917491849194920492149224923492449254926492749284929493049314932493349344935493649374938493949404941494249434944494549464947494849494950495149524953495449554956495749584959496049614962496349644965496649674968496949704971497249734974497549764977497849794980498149824983498449854986498749884989499049914992499349944995499649974998499950005001500250035004500550065007500850095010501150125013501450155016501750185019502050215022502350245025502650275028502950305031503250335034503550365037503850395040504150425043504450455046504750485049505050515052505350545055505650575058505950605061506250635064506550665067506850695070507150725073507450755076507750785079508050815082508350845085508650875088508950905091509250935094509550965097509850995100510151025103510451055106510751085109511051115112511351145115511651175118511951205121512251235124512551265127512851295130513151325133513451355136513751385139514051415142514351445145514651475148514951505151515251535154515551565157515851595160516151625163516451655166516751685169517051715172517351745175517651775178517951805181518251835184518551865187518851895190519151925193519451955196519751985199520052015202520352045205520652075208520952105211521252135214521552165217521852195220522152225223522452255226522752285229523052315232523352345235523652375238523952405241524252435244524552465247524852495250525152525253525452555256525752585259526052615262526352645265526652675268526952705271527252735274527552765277527852795280528152825283528452855286528752885289529052915292529352945295529652975298529953005301530253035304530553065307530853095310531153125313531453155316531753185319532053215322532353245325532653275328532953305331533253335334533553365337533853395340534153425343534453455346534753485349535053515352535353545355535653575358535953605361536253635364536553665367536853695370537153725373537453755376537753785379538053815382538353845385538653875388538953905391539253935394539553965397539853995400540154025403540454055406540754085409541054115412541354145415541654175418541954205421542254235424542554265427542854295430543154325433543454355436543754385439544054415442544354445445544654475448544954505451545254535454545554565457545854595460546154625463546454655466546754685469547054715472547354745475547654775478547954805481548254835484548554865487548854895490549154925493549454955496549754985499550055015502550355045505550655075508550955105511551255135514551555165517551855195520552155225523552455255526552755285529553055315532553355345535553655375538553955405541554255435544554555465547554855495550555155525553555455555556555755585559556055615562556355645565556655675568556955705571557255735574557555765577557855795580558155825583558455855586558755885589559055915592559355945595559655975598559956005601560256035604560556065607560856095610561156125613561456155616561756185619562056215622562356245625562656275628562956305631563256335634563556365637563856395640564156425643564456455646564756485649565056515652565356545655565656575658565956605661566256635664566556665667566856695670567156725673567456755676567756785679568056815682568356845685568656875688568956905691569256935694569556965697569856995700570157025703570457055706570757085709571057115712571357145715571657175718571957205721572257235724572557265727572857295730573157325733573457355736573757385739574057415742574357445745574657475748574957505751575257535754575557565757575857595760576157625763576457655766576757685769577057715772577357745775577657775778577957805781578257835784578557865787578857895790579157925793579457955796579757985799580058015802580358045805580658075808580958105811581258135814581558165817581858195820582158225823582458255826582758285829583058315832583358345835583658375838583958405841584258435844584558465847584858495850585158525853585458555856585758585859586058615862586358645865586658675868586958705871587258735874587558765877587858795880588158825883588458855886588758885889589058915892589358945895589658975898589959005901590259035904590559065907590859095910591159125913591459155916591759185919592059215922592359245925592659275928592959305931593259335934593559365937593859395940594159425943594459455946594759485949595059515952595359545955595659575958595959605961596259635964596559665967596859695970597159725973597459755976597759785979598059815982598359845985598659875988598959905991599259935994599559965997599859996000600160026003600460056006600760086009601060116012601360146015601660176018601960206021602260236024602560266027602860296030603160326033603460356036603760386039604060416042604360446045604660476048604960506051605260536054605560566057605860596060606160626063606460656066606760686069607060716072607360746075607660776078607960806081608260836084608560866087608860896090609160926093609460956096609760986099610061016102610361046105610661076108610961106111611261136114611561166117611861196120612161226123612461256126612761286129613061316132613361346135613661376138613961406141614261436144614561466147614861496150615161526153615461556156615761586159616061616162616361646165616661676168616961706171617261736174617561766177617861796180618161826183618461856186618761886189619061916192619361946195619661976198619962006201620262036204620562066207620862096210621162126213621462156216621762186219622062216222622362246225622662276228622962306231623262336234623562366237623862396240624162426243624462456246624762486249625062516252625362546255625662576258625962606261626262636264626562666267626862696270627162726273627462756276627762786279628062816282628362846285628662876288628962906291629262936294629562966297629862996300630163026303630463056306630763086309631063116312631363146315631663176318631963206321632263236324632563266327632863296330633163326333633463356336633763386339634063416342634363446345634663476348634963506351635263536354635563566357635863596360636163626363636463656366636763686369637063716372637363746375637663776378637963806381638263836384638563866387638863896390639163926393639463956396639763986399640064016402640364046405640664076408640964106411641264136414641564166417641864196420642164226423642464256426642764286429643064316432643364346435643664376438643964406441644264436444644564466447644864496450645164526453645464556456645764586459646064616462646364646465646664676468646964706471647264736474647564766477647864796480648164826483648464856486648764886489649064916492649364946495649664976498649965006501650265036504650565066507650865096510651165126513651465156516651765186519652065216522652365246525652665276528652965306531653265336534653565366537653865396540654165426543654465456546654765486549655065516552655365546555655665576558655965606561656265636564656565666567656865696570657165726573657465756576657765786579658065816582658365846585658665876588658965906591659265936594659565966597659865996600660166026603660466056606660766086609661066116612661366146615661666176618661966206621662266236624662566266627662866296630663166326633663466356636663766386639664066416642664366446645664666476648664966506651665266536654665566566657665866596660666166626663666466656666666766686669667066716672667366746675667666776678667966806681668266836684668566866687668866896690669166926693669466956696669766986699670067016702670367046705670667076708670967106711671267136714671567166717671867196720672167226723672467256726672767286729673067316732673367346735673667376738673967406741674267436744674567466747674867496750675167526753675467556756675767586759676067616762676367646765676667676768676967706771677267736774677567766777677867796780678167826783678467856786678767886789679067916792679367946795679667976798679968006801680268036804680568066807680868096810681168126813681468156816681768186819682068216822682368246825682668276828682968306831683268336834683568366837683868396840684168426843684468456846684768486849685068516852685368546855685668576858685968606861686268636864686568666867686868696870687168726873687468756876687768786879688068816882688368846885688668876888688968906891689268936894689568966897689868996900690169026903690469056906690769086909691069116912691369146915691669176918691969206921692269236924692569266927692869296930693169326933693469356936693769386939694069416942694369446945694669476948694969506951695269536954695569566957695869596960696169626963696469656966696769686969697069716972697369746975697669776978697969806981698269836984698569866987698869896990699169926993699469956996699769986999700070017002700370047005700670077008700970107011701270137014701570167017701870197020702170227023702470257026702770287029703070317032703370347035703670377038703970407041704270437044704570467047704870497050705170527053705470557056705770587059706070617062706370647065706670677068706970707071707270737074707570767077707870797080708170827083708470857086708770887089709070917092709370947095709670977098709971007101710271037104710571067107710871097110711171127113711471157116711771187119712071217122712371247125712671277128712971307131713271337134713571367137713871397140714171427143714471457146714771487149715071517152715371547155715671577158715971607161716271637164716571667167716871697170717171727173717471757176717771787179718071817182718371847185718671877188718971907191719271937194719571967197719871997200720172027203720472057206720772087209721072117212721372147215721672177218721972207221722272237224722572267227722872297230723172327233723472357236723772387239724072417242724372447245724672477248724972507251725272537254725572567257725872597260726172627263726472657266726772687269727072717272727372747275727672777278727972807281728272837284728572867287728872897290729172927293729472957296729772987299730073017302730373047305730673077308730973107311731273137314731573167317731873197320732173227323732473257326732773287329733073317332733373347335733673377338733973407341734273437344734573467347734873497350735173527353735473557356735773587359736073617362736373647365736673677368736973707371737273737374737573767377737873797380738173827383738473857386738773887389739073917392739373947395739673977398739974007401740274037404740574067407740874097410741174127413741474157416741774187419742074217422742374247425742674277428742974307431743274337434743574367437743874397440744174427443744474457446744774487449745074517452745374547455745674577458745974607461746274637464746574667467746874697470747174727473747474757476747774787479748074817482748374847485748674877488748974907491749274937494749574967497749874997500750175027503750475057506750775087509751075117512751375147515751675177518751975207521752275237524752575267527752875297530753175327533753475357536753775387539754075417542754375447545754675477548754975507551755275537554755575567557755875597560756175627563756475657566756775687569757075717572757375747575757675777578757975807581758275837584758575867587758875897590759175927593759475957596759775987599760076017602760376047605760676077608760976107611761276137614761576167617761876197620762176227623762476257626762776287629763076317632763376347635763676377638763976407641764276437644764576467647764876497650765176527653765476557656765776587659766076617662766376647665766676677668766976707671767276737674767576767677767876797680768176827683768476857686768776887689769076917692769376947695769676977698769977007701770277037704770577067707770877097710771177127713771477157716771777187719772077217722772377247725772677277728772977307731773277337734773577367737773877397740774177427743774477457746774777487749775077517752775377547755775677577758775977607761776277637764776577667767776877697770777177727773777477757776777777787779778077817782778377847785778677877788778977907791779277937794779577967797779877997800780178027803780478057806780778087809781078117812781378147815781678177818781978207821782278237824782578267827782878297830783178327833783478357836783778387839784078417842784378447845784678477848784978507851785278537854785578567857785878597860786178627863786478657866786778687869787078717872787378747875787678777878787978807881788278837884788578867887788878897890789178927893789478957896789778987899790079017902790379047905790679077908790979107911791279137914791579167917791879197920792179227923792479257926792779287929793079317932793379347935793679377938793979407941794279437944794579467947794879497950795179527953795479557956795779587959796079617962796379647965796679677968796979707971797279737974797579767977797879797980798179827983798479857986798779887989799079917992799379947995799679977998799980008001800280038004800580068007800880098010801180128013801480158016801780188019802080218022802380248025802680278028802980308031803280338034803580368037803880398040804180428043804480458046804780488049805080518052805380548055805680578058805980608061806280638064806580668067806880698070807180728073807480758076807780788079808080818082808380848085808680878088808980908091809280938094809580968097809880998100810181028103810481058106810781088109811081118112811381148115811681178118811981208121812281238124812581268127812881298130813181328133813481358136813781388139814081418142814381448145814681478148814981508151815281538154815581568157815881598160816181628163816481658166816781688169817081718172817381748175817681778178817981808181818281838184818581868187818881898190819181928193819481958196819781988199820082018202820382048205820682078208820982108211821282138214821582168217821882198220822182228223822482258226822782288229823082318232823382348235823682378238823982408241824282438244824582468247824882498250825182528253825482558256825782588259826082618262826382648265826682678268826982708271827282738274827582768277827882798280828182828283828482858286828782888289829082918292829382948295829682978298829983008301830283038304830583068307830883098310831183128313831483158316831783188319832083218322832383248325832683278328832983308331833283338334833583368337833883398340834183428343834483458346834783488349835083518352835383548355835683578358835983608361836283638364836583668367836883698370837183728373837483758376837783788379838083818382838383848385838683878388838983908391839283938394839583968397839883998400840184028403840484058406840784088409841084118412841384148415841684178418841984208421842284238424842584268427842884298430843184328433843484358436843784388439844084418442844384448445844684478448844984508451845284538454845584568457845884598460846184628463846484658466846784688469847084718472847384748475847684778478847984808481848284838484848584868487848884898490849184928493849484958496849784988499850085018502850385048505850685078508850985108511851285138514851585168517851885198520852185228523852485258526852785288529853085318532853385348535853685378538853985408541854285438544854585468547854885498550855185528553855485558556855785588559856085618562856385648565856685678568856985708571857285738574857585768577857885798580858185828583858485858586858785888589859085918592859385948595859685978598859986008601860286038604860586068607860886098610861186128613861486158616861786188619862086218622862386248625862686278628862986308631863286338634863586368637863886398640864186428643864486458646864786488649865086518652865386548655865686578658865986608661866286638664866586668667866886698670867186728673867486758676867786788679868086818682868386848685868686878688868986908691869286938694869586968697869886998700870187028703870487058706870787088709871087118712871387148715871687178718871987208721872287238724872587268727872887298730873187328733873487358736873787388739874087418742874387448745874687478748874987508751875287538754875587568757875887598760876187628763876487658766876787688769877087718772877387748775877687778778877987808781878287838784878587868787878887898790879187928793879487958796879787988799880088018802880388048805880688078808880988108811881288138814881588168817881888198820882188228823882488258826882788288829883088318832883388348835883688378838883988408841884288438844884588468847884888498850885188528853885488558856885788588859886088618862886388648865886688678868886988708871887288738874887588768877887888798880888188828883888488858886888788888889889088918892889388948895889688978898889989008901890289038904890589068907890889098910891189128913891489158916891789188919892089218922892389248925892689278928892989308931893289338934893589368937893889398940894189428943894489458946894789488949895089518952895389548955895689578958895989608961896289638964896589668967896889698970897189728973897489758976897789788979898089818982898389848985898689878988898989908991899289938994899589968997899889999000900190029003900490059006900790089009901090119012901390149015901690179018901990209021902290239024902590269027902890299030903190329033903490359036903790389039904090419042904390449045904690479048904990509051905290539054905590569057905890599060906190629063906490659066906790689069907090719072907390749075907690779078907990809081908290839084908590869087908890899090909190929093909490959096909790989099910091019102910391049105910691079108910991109111911291139114911591169117911891199120912191229123912491259126912791289129913091319132913391349135913691379138913991409141914291439144914591469147914891499150915191529153915491559156915791589159916091619162916391649165916691679168916991709171917291739174917591769177917891799180918191829183918491859186918791889189919091919192919391949195919691979198919992009201920292039204920592069207920892099210921192129213921492159216921792189219922092219222922392249225922692279228922992309231923292339234923592369237923892399240924192429243924492459246924792489249925092519252925392549255925692579258925992609261926292639264926592669267926892699270927192729273927492759276927792789279928092819282928392849285928692879288928992909291929292939294929592969297929892999300930193029303930493059306930793089309931093119312931393149315931693179318931993209321932293239324932593269327932893299330933193329333933493359336933793389339934093419342934393449345934693479348934993509351935293539354935593569357935893599360936193629363936493659366936793689369937093719372937393749375937693779378937993809381938293839384938593869387938893899390939193929393939493959396939793989399940094019402940394049405940694079408940994109411941294139414941594169417941894199420942194229423942494259426942794289429943094319432943394349435943694379438943994409441944294439444944594469447944894499450945194529453945494559456945794589459946094619462946394649465946694679468946994709471947294739474947594769477947894799480948194829483948494859486948794889489949094919492949394949495949694979498949995009501950295039504950595069507950895099510951195129513951495159516951795189519952095219522952395249525952695279528952995309531953295339534953595369537953895399540954195429543954495459546954795489549955095519552955395549555955695579558955995609561956295639564956595669567956895699570957195729573957495759576957795789579958095819582958395849585958695879588958995909591959295939594959595969597959895999600960196029603960496059606960796089609961096119612961396149615961696179618961996209621962296239624962596269627962896299630963196329633963496359636963796389639964096419642964396449645964696479648964996509651965296539654965596569657965896599660966196629663966496659666966796689669967096719672967396749675967696779678967996809681968296839684968596869687968896899690969196929693969496959696969796989699970097019702970397049705970697079708970997109711971297139714971597169717971897199720972197229723972497259726972797289729973097319732973397349735973697379738973997409741974297439744974597469747974897499750975197529753975497559756975797589759976097619762976397649765976697679768976997709771977297739774977597769777977897799780978197829783978497859786978797889789979097919792979397949795979697979798979998009801980298039804980598069807980898099810981198129813981498159816981798189819982098219822982398249825982698279828982998309831983298339834983598369837983898399840984198429843984498459846984798489849985098519852985398549855985698579858985998609861986298639864986598669867986898699870987198729873987498759876987798789879988098819882988398849885988698879888988998909891989298939894989598969897989898999900990199029903990499059906990799089909991099119912991399149915991699179918991999209921992299239924992599269927992899299930993199329933993499359936993799389939994099419942994399449945994699479948994999509951995299539954995599569957995899599960996199629963996499659966996799689969997099719972997399749975997699779978997999809981998299839984998599869987998899899990999199929993999499959996999799989999100001000110002100031000410005100061000710008100091001010011100121001310014100151001610017100181001910020100211002210023100241002510026100271002810029100301003110032100331003410035100361003710038100391004010041100421004310044100451004610047100481004910050100511005210053100541005510056100571005810059100601006110062100631006410065100661006710068100691007010071100721007310074100751007610077100781007910080100811008210083100841008510086100871008810089100901009110092100931009410095100961009710098100991010010101101021010310104101051010610107101081010910110101111011210113101141011510116101171011810119101201012110122101231012410125101261012710128101291013010131101321013310134101351013610137101381013910140101411014210143101441014510146101471014810149101501015110152101531015410155101561015710158101591016010161101621016310164101651016610167101681016910170101711017210173101741017510176101771017810179101801018110182101831018410185101861018710188101891019010191101921019310194101951019610197101981019910200102011020210203102041020510206102071020810209102101021110212102131021410215102161021710218102191022010221102221022310224102251022610227102281022910230102311023210233102341023510236102371023810239102401024110242102431024410245102461024710248102491025010251102521025310254102551025610257102581025910260102611026210263102641026510266102671026810269102701027110272102731027410275102761027710278102791028010281102821028310284102851028610287102881028910290102911029210293102941029510296102971029810299103001030110302103031030410305103061030710308103091031010311103121031310314103151031610317103181031910320103211032210323103241032510326103271032810329103301033110332103331033410335103361033710338103391034010341103421034310344103451034610347103481034910350103511035210353103541035510356103571035810359103601036110362103631036410365103661036710368103691037010371103721037310374103751037610377103781037910380103811038210383103841038510386103871038810389103901039110392103931039410395103961039710398103991040010401104021040310404104051040610407104081040910410104111041210413104141041510416104171041810419104201042110422104231042410425104261042710428104291043010431104321043310434104351043610437104381043910440104411044210443104441044510446104471044810449104501045110452104531045410455104561045710458104591046010461104621046310464104651046610467104681046910470104711047210473104741047510476104771047810479104801048110482104831048410485104861048710488104891049010491104921049310494104951049610497104981049910500105011050210503105041050510506105071050810509105101051110512105131051410515105161051710518105191052010521105221052310524105251052610527105281052910530105311053210533105341053510536105371053810539105401054110542105431054410545105461054710548105491055010551105521055310554105551055610557105581055910560105611056210563105641056510566105671056810569105701057110572105731057410575105761057710578105791058010581105821058310584105851058610587105881058910590105911059210593105941059510596105971059810599106001060110602106031060410605106061060710608106091061010611106121061310614106151061610617106181061910620106211062210623106241062510626106271062810629106301063110632106331063410635106361063710638106391064010641106421064310644106451064610647106481064910650106511065210653106541065510656106571065810659106601066110662106631066410665106661066710668106691067010671106721067310674106751067610677106781067910680106811068210683106841068510686106871068810689106901069110692106931069410695106961069710698106991070010701107021070310704107051070610707107081070910710107111071210713107141071510716107171071810719107201072110722107231072410725107261072710728107291073010731107321073310734107351073610737107381073910740107411074210743107441074510746107471074810749107501075110752107531075410755107561075710758107591076010761107621076310764107651076610767107681076910770107711077210773107741077510776107771077810779107801078110782107831078410785107861078710788107891079010791107921079310794107951079610797107981079910800108011080210803108041080510806108071080810809108101081110812108131081410815108161081710818108191082010821108221082310824108251082610827108281082910830108311083210833108341083510836108371083810839108401084110842108431084410845108461084710848108491085010851108521085310854108551085610857108581085910860108611086210863108641086510866108671086810869108701087110872108731087410875108761087710878108791088010881108821088310884108851088610887108881088910890108911089210893108941089510896108971089810899109001090110902109031090410905109061090710908109091091010911109121091310914109151091610917109181091910920109211092210923109241092510926109271092810929109301093110932109331093410935109361093710938109391094010941109421094310944109451094610947109481094910950109511095210953109541095510956109571095810959109601096110962109631096410965109661096710968109691097010971109721097310974109751097610977109781097910980109811098210983109841098510986109871098810989109901099110992109931099410995109961099710998109991100011001110021100311004110051100611007110081100911010110111101211013110141101511016110171101811019110201102111022110231102411025110261102711028110291103011031110321103311034110351103611037110381103911040110411104211043110441104511046110471104811049110501105111052110531105411055110561105711058110591106011061110621106311064110651106611067110681106911070110711107211073110741107511076110771107811079110801108111082110831108411085110861108711088110891109011091110921109311094110951109611097110981109911100111011110211103111041110511106111071110811109111101111111112111131111411115111161111711118111191112011121111221112311124111251112611127111281112911130111311113211133111341113511136111371113811139111401114111142111431114411145111461114711148111491115011151111521115311154111551115611157111581115911160111611116211163111641116511166111671116811169111701117111172111731117411175111761117711178111791118011181111821118311184111851118611187111881118911190111911119211193111941119511196111971119811199112001120111202112031120411205112061120711208112091121011211112121121311214112151121611217112181121911220112211122211223112241122511226112271122811229112301123111232112331123411235112361123711238112391124011241112421124311244112451124611247112481124911250112511125211253112541125511256112571125811259112601126111262112631126411265112661126711268112691127011271112721127311274112751127611277112781127911280112811128211283112841128511286112871128811289112901129111292112931129411295112961129711298112991130011301113021130311304113051130611307113081130911310113111131211313113141131511316113171131811319113201132111322113231132411325113261132711328113291133011331113321133311334113351133611337113381133911340113411134211343113441134511346113471134811349113501135111352113531135411355113561135711358113591136011361113621136311364113651136611367113681136911370113711137211373113741137511376113771137811379113801138111382113831138411385113861138711388113891139011391113921139311394113951139611397113981139911400114011140211403114041140511406114071140811409114101141111412114131141411415114161141711418114191142011421114221142311424114251142611427114281142911430114311143211433114341143511436114371143811439114401144111442114431144411445114461144711448114491145011451114521145311454114551145611457114581145911460114611146211463114641146511466114671146811469114701147111472114731147411475114761147711478114791148011481114821148311484114851148611487114881148911490114911149211493114941149511496114971149811499115001150111502115031150411505115061150711508115091151011511115121151311514115151151611517115181151911520115211152211523115241152511526115271152811529115301153111532115331153411535115361153711538115391154011541115421154311544115451154611547115481154911550115511155211553115541155511556115571155811559115601156111562115631156411565115661156711568115691157011571115721157311574115751157611577115781157911580115811158211583115841158511586115871158811589115901159111592115931159411595115961159711598115991160011601116021160311604116051160611607116081160911610116111161211613116141161511616116171161811619116201162111622116231162411625116261162711628116291163011631116321163311634116351163611637116381163911640116411164211643116441164511646116471164811649116501165111652116531165411655116561165711658116591166011661116621166311664116651166611667116681166911670116711167211673116741167511676116771167811679116801168111682116831168411685116861168711688116891169011691116921169311694116951169611697116981169911700117011170211703117041170511706117071170811709117101171111712117131171411715117161171711718117191172011721117221172311724117251172611727117281172911730117311173211733117341173511736117371173811739117401174111742117431174411745117461174711748117491175011751117521175311754117551175611757117581175911760117611176211763117641176511766117671176811769117701177111772117731177411775117761177711778117791178011781117821178311784117851178611787117881178911790117911179211793117941179511796117971179811799118001180111802118031180411805118061180711808118091181011811118121181311814118151181611817118181181911820118211182211823118241182511826118271182811829118301183111832118331183411835118361183711838118391184011841118421184311844118451184611847118481184911850118511185211853118541185511856118571185811859118601186111862118631186411865118661186711868118691187011871118721187311874118751187611877118781187911880118811188211883118841188511886118871188811889118901189111892118931189411895118961189711898118991190011901119021190311904119051190611907119081190911910119111191211913119141191511916119171191811919119201192111922119231192411925119261192711928119291193011931119321193311934119351193611937119381193911940119411194211943119441194511946119471194811949119501195111952119531195411955119561195711958119591196011961119621196311964119651196611967119681196911970119711197211973119741197511976119771197811979119801198111982119831198411985119861198711988119891199011991119921199311994119951199611997119981199912000120011200212003120041200512006120071200812009120101201112012120131201412015120161201712018120191202012021120221202312024120251202612027120281202912030120311203212033120341203512036120371203812039120401204112042120431204412045120461204712048120491205012051120521205312054120551205612057120581205912060120611206212063120641206512066120671206812069120701207112072120731207412075120761207712078120791208012081120821208312084120851208612087120881208912090120911209212093120941209512096120971209812099121001210112102121031210412105121061210712108121091211012111121121211312114121151211612117121181211912120121211212212123121241212512126121271212812129121301213112132121331213412135121361213712138121391214012141121421214312144121451214612147121481214912150121511215212153121541215512156121571215812159121601216112162121631216412165121661216712168121691217012171121721217312174121751217612177121781217912180121811218212183121841218512186121871218812189121901219112192121931219412195121961219712198121991220012201122021220312204122051220612207122081220912210122111221212213122141221512216122171221812219122201222112222122231222412225122261222712228122291223012231122321223312234122351223612237122381223912240122411224212243122441224512246122471224812249122501225112252122531225412255122561225712258122591226012261122621226312264122651226612267122681226912270122711227212273122741227512276122771227812279122801228112282122831228412285122861228712288122891229012291122921229312294122951229612297122981229912300123011230212303123041230512306123071230812309123101231112312123131231412315123161231712318123191232012321123221232312324123251232612327123281232912330123311233212333123341233512336123371233812339123401234112342123431234412345123461234712348123491235012351123521235312354123551235612357123581235912360123611236212363123641236512366123671236812369123701237112372123731237412375123761237712378123791238012381123821238312384123851238612387123881238912390123911239212393123941239512396123971239812399124001240112402124031240412405124061240712408124091241012411124121241312414124151241612417124181241912420124211242212423124241242512426124271242812429124301243112432124331243412435124361243712438124391244012441124421244312444124451244612447124481244912450124511245212453124541245512456124571245812459124601246112462124631246412465124661246712468124691247012471124721247312474124751247612477124781247912480124811248212483124841248512486124871248812489124901249112492124931249412495124961249712498124991250012501125021250312504125051250612507125081250912510125111251212513125141251512516125171251812519125201252112522125231252412525125261252712528125291253012531125321253312534125351253612537125381253912540125411254212543125441254512546125471254812549125501255112552125531255412555125561255712558125591256012561125621256312564125651256612567125681256912570125711257212573125741257512576125771257812579125801258112582125831258412585125861258712588125891259012591125921259312594125951259612597125981259912600126011260212603126041260512606126071260812609126101261112612126131261412615126161261712618126191262012621126221262312624126251262612627126281262912630126311263212633126341263512636126371263812639126401264112642126431264412645126461264712648126491265012651126521265312654126551265612657126581265912660126611266212663126641266512666126671266812669126701267112672126731267412675126761267712678126791268012681126821268312684126851268612687126881268912690126911269212693126941269512696126971269812699127001270112702127031270412705127061270712708127091271012711127121271312714127151271612717127181271912720127211272212723127241272512726127271272812729127301273112732127331273412735127361273712738127391274012741127421274312744127451274612747127481274912750127511275212753127541275512756127571275812759127601276112762127631276412765127661276712768127691277012771127721277312774127751277612777127781277912780127811278212783127841278512786127871278812789127901279112792127931279412795127961279712798127991280012801128021280312804128051280612807128081280912810128111281212813128141281512816128171281812819128201282112822128231282412825128261282712828128291283012831128321283312834128351283612837128381283912840128411284212843128441284512846128471284812849128501285112852128531285412855128561285712858128591286012861128621286312864128651286612867128681286912870128711287212873128741287512876128771287812879128801288112882128831288412885128861288712888128891289012891128921289312894128951289612897128981289912900129011290212903129041290512906129071290812909129101291112912129131291412915129161291712918129191292012921129221292312924129251292612927129281292912930129311293212933129341293512936129371293812939129401294112942129431294412945129461294712948129491295012951129521295312954129551295612957129581295912960129611296212963129641296512966129671296812969129701297112972129731297412975129761297712978129791298012981129821298312984129851298612987129881298912990129911299212993129941299512996129971299812999130001300113002130031300413005130061300713008130091301013011130121301313014130151301613017130181301913020130211302213023130241302513026130271302813029130301303113032130331303413035130361303713038130391304013041130421304313044130451304613047130481304913050130511305213053130541305513056130571305813059130601306113062130631306413065130661306713068130691307013071130721307313074130751307613077130781307913080130811308213083130841308513086130871308813089130901309113092130931309413095130961309713098130991310013101131021310313104131051310613107131081310913110131111311213113131141311513116131171311813119131201312113122131231312413125131261312713128131291313013131131321313313134131351313613137131381313913140131411314213143131441314513146131471314813149131501315113152131531315413155131561315713158131591316013161131621316313164131651316613167131681316913170131711317213173131741317513176131771317813179131801318113182131831318413185131861318713188131891319013191131921319313194131951319613197131981319913200132011320213203132041320513206132071320813209132101321113212132131321413215132161321713218132191322013221132221322313224132251322613227132281322913230132311323213233132341323513236132371323813239132401324113242132431324413245132461324713248132491325013251132521325313254132551325613257132581325913260132611326213263132641326513266132671326813269132701327113272132731327413275132761327713278132791328013281132821328313284132851328613287132881328913290132911329213293132941329513296132971329813299133001330113302133031330413305133061330713308133091331013311133121331313314133151331613317133181331913320133211332213323133241332513326133271332813329133301333113332133331333413335133361333713338133391334013341133421334313344133451334613347133481334913350133511335213353133541335513356 | /*! * Copyright 2015 Drifty Co. * http://drifty.com/ * * Ionic, v1.3.1 * A powerful HTML5 mobile app framework. * http://ionicframework.com/ * * By @maxlynch, @benjsperry, @adamdbradley <3 * * Licensed under the MIT license. Please see LICENSE for more information. * */(function() {// Create global ionic obj and its namespaces// build processes may have already created an ionic objwindow.ionic = window.ionic || {};window.ionic.views = {};window.ionic.version = '1.3.1';(function (ionic) {  ionic.DelegateService = function(methodNames) {    if (methodNames.indexOf('$getByHandle') > -1) {      throw new Error("Method '$getByHandle' is implicitly added to each delegate service. Do not list it as a method.");    }    function trueFn() { return true; }    return ['$log', function($log) {      /*       * Creates a new object that will have all the methodNames given,       * and call them on the given the controller instance matching given       * handle.       * The reason we don't just let $getByHandle return the controller instance       * itself is that the controller instance might not exist yet.       *       * We want people to be able to do       * `var instance = $ionicScrollDelegate.$getByHandle('foo')` on controller       * instantiation, but on controller instantiation a child directive       * may not have been compiled yet!       *       * So this is our way of solving this problem: we create an object       * that will only try to fetch the controller with given handle       * once the methods are actually called.       */      function DelegateInstance(instances, handle) {        this._instances = instances;        this.handle = handle;      }      methodNames.forEach(function(methodName) {        DelegateInstance.prototype[methodName] = instanceMethodCaller(methodName);      });      /**       * The delegate service (eg $ionicNavBarDelegate) is just an instance       * with a non-defined handle, a couple extra methods for registering       * and narrowing down to a specific handle.       */      function DelegateService() {        this._instances = [];      }      DelegateService.prototype = DelegateInstance.prototype;      DelegateService.prototype._registerInstance = function(instance, handle, filterFn) {        var instances = this._instances;        instance.$$delegateHandle = handle;        instance.$$filterFn = filterFn || trueFn;        instances.push(instance);        return function deregister() {          var index = instances.indexOf(instance);          if (index !== -1) {            instances.splice(index, 1);          }        };      };      DelegateService.prototype.$getByHandle = function(handle) {        return new DelegateInstance(this._instances, handle);      };      return new DelegateService();      function instanceMethodCaller(methodName) {        return function caller() {          var handle = this.handle;          var args = arguments;          var foundInstancesCount = 0;          var returnValue;          this._instances.forEach(function(instance) {            if ((!handle || handle == instance.$$delegateHandle) && instance.$$filterFn(instance)) {              foundInstancesCount++;              var ret = instance[methodName].apply(instance, args);              //Only return the value from the first call              if (foundInstancesCount === 1) {                returnValue = ret;              }            }          });          if (!foundInstancesCount && handle) {            return $log.warn(              'Delegate for handle "' + handle + '" could not find a ' +              'corresponding element with delegate-handle="' + handle + '"! ' +              methodName + '() was not called!\n' +              'Possible cause: If you are calling ' + methodName + '() immediately, and ' +              'your element with delegate-handle="' + handle + '" is a child of your ' +              'controller, then your element may not be compiled yet. Put a $timeout ' +              'around your call to ' + methodName + '() and try again.'            );          }          return returnValue;        };      }    }];  };})(window.ionic);(function(window, document, ionic) {  var readyCallbacks = [];  var isDomReady = document.readyState === 'complete' || document.readyState === 'interactive';  function domReady() {    isDomReady = true;    for (var x = 0; x < readyCallbacks.length; x++) {      ionic.requestAnimationFrame(readyCallbacks[x]);    }    readyCallbacks = [];    document.removeEventListener('DOMContentLoaded', domReady);  }  if (!isDomReady) {    document.addEventListener('DOMContentLoaded', domReady);  }  // From the man himself, Mr. Paul Irish.  // The requestAnimationFrame polyfill  // Put it on window just to preserve its context  // without having to use .call  window._rAF = (function() {    return window.requestAnimationFrame ||           window.webkitRequestAnimationFrame ||           window.mozRequestAnimationFrame ||           function(callback) {             window.setTimeout(callback, 16);           };  })();  var cancelAnimationFrame = window.cancelAnimationFrame ||    window.webkitCancelAnimationFrame ||    window.mozCancelAnimationFrame ||    window.webkitCancelRequestAnimationFrame;  /**  * @ngdoc utility  * @name ionic.DomUtil  * @module ionic  */  ionic.DomUtil = {    //Call with proper context    /**     * @ngdoc method     * @name ionic.DomUtil#requestAnimationFrame     * @alias ionic.requestAnimationFrame     * @description Calls [requestAnimationFrame](https://developer.mozilla.org/en-US/docs/Web/API/window.requestAnimationFrame), or a polyfill if not available.     * @param {function} callback The function to call when the next frame     * happens.     */    requestAnimationFrame: function(cb) {      return window._rAF(cb);    },    cancelAnimationFrame: function(requestId) {      cancelAnimationFrame(requestId);    },    /**     * @ngdoc method     * @name ionic.DomUtil#animationFrameThrottle     * @alias ionic.animationFrameThrottle     * @description     * When given a callback, if that callback is called 100 times between     * animation frames, adding Throttle will make it only run the last of     * the 100 calls.     *     * @param {function} callback a function which will be throttled to     * requestAnimationFrame     * @returns {function} A function which will then call the passed in callback.     * The passed in callback will receive the context the returned function is     * called with.     */    animationFrameThrottle: function(cb) {      var args, isQueued, context;      return function() {        args = arguments;        context = this;        if (!isQueued) {          isQueued = true;          ionic.requestAnimationFrame(function() {            cb.apply(context, args);            isQueued = false;          });        }      };    },    contains: function(parentNode, otherNode) {      var current = otherNode;      while (current) {        if (current === parentNode) return true;        current = current.parentNode;      }    },    /**     * @ngdoc method     * @name ionic.DomUtil#getPositionInParent     * @description     * Find an element's scroll offset within its container.     * @param {DOMElement} element The element to find the offset of.     * @returns {object} A position object with the following properties:     *   - `{number}` `left` The left offset of the element.     *   - `{number}` `top` The top offset of the element.     */    getPositionInParent: function(el) {      return {        left: el.offsetLeft,        top: el.offsetTop      };    },    getOffsetTop: function(el) {      var curtop = 0;      if (el.offsetParent) {        do {          curtop += el.offsetTop;          el = el.offsetParent;        } while (el)        return curtop;      }    },    /**     * @ngdoc method     * @name ionic.DomUtil#ready     * @description     * Call a function when the DOM is ready, or if it is already ready     * call the function immediately.     * @param {function} callback The function to be called.     */    ready: function(cb) {      if (isDomReady) {        ionic.requestAnimationFrame(cb);      } else {        readyCallbacks.push(cb);      }    },    /**     * @ngdoc method     * @name ionic.DomUtil#getTextBounds     * @description     * Get a rect representing the bounds of the given textNode.     * @param {DOMElement} textNode The textNode to find the bounds of.     * @returns {object} An object representing the bounds of the node. Properties:     *   - `{number}` `left` The left position of the textNode.     *   - `{number}` `right` The right position of the textNode.     *   - `{number}` `top` The top position of the textNode.     *   - `{number}` `bottom` The bottom position of the textNode.     *   - `{number}` `width` The width of the textNode.     *   - `{number}` `height` The height of the textNode.     */    getTextBounds: function(textNode) {      if (document.createRange) {        var range = document.createRange();        range.selectNodeContents(textNode);        if (range.getBoundingClientRect) {          var rect = range.getBoundingClientRect();          if (rect) {            var sx = window.scrollX;            var sy = window.scrollY;            return {              top: rect.top + sy,              left: rect.left + sx,              right: rect.left + sx + rect.width,              bottom: rect.top + sy + rect.height,              width: rect.width,              height: rect.height            };          }        }      }      return null;    },    /**     * @ngdoc method     * @name ionic.DomUtil#getChildIndex     * @description     * Get the first index of a child node within the given element of the     * specified type.     * @param {DOMElement} element The element to find the index of.     * @param {string} type The nodeName to match children of element against.     * @returns {number} The index, or -1, of a child with nodeName matching type.     */    getChildIndex: function(element, type) {      if (type) {        var ch = element.parentNode.children;        var c;        for (var i = 0, k = 0, j = ch.length; i < j; i++) {          c = ch[i];          if (c.nodeName && c.nodeName.toLowerCase() == type) {            if (c == element) {              return k;            }            k++;          }        }      }      return Array.prototype.slice.call(element.parentNode.children).indexOf(element);    },    /**     * @private     */    swapNodes: function(src, dest) {      dest.parentNode.insertBefore(src, dest);    },    elementIsDescendant: function(el, parent, stopAt) {      var current = el;      do {        if (current === parent) return true;        current = current.parentNode;      } while (current && current !== stopAt);      return false;    },    /**     * @ngdoc method     * @name ionic.DomUtil#getParentWithClass     * @param {DOMElement} element     * @param {string} className     * @returns {DOMElement} The closest parent of element matching the     * className, or null.     */    getParentWithClass: function(e, className, depth) {      depth = depth || 10;      while (e.parentNode && depth--) {        if (e.parentNode.classList && e.parentNode.classList.contains(className)) {          return e.parentNode;        }        e = e.parentNode;      }      return null;    },    /**     * @ngdoc method     * @name ionic.DomUtil#getParentOrSelfWithClass     * @param {DOMElement} element     * @param {string} className     * @returns {DOMElement} The closest parent or self matching the     * className, or null.     */    getParentOrSelfWithClass: function(e, className, depth) {      depth = depth || 10;      while (e && depth--) {        if (e.classList && e.classList.contains(className)) {          return e;        }        e = e.parentNode;      }      return null;    },    /**     * @ngdoc method     * @name ionic.DomUtil#rectContains     * @param {number} x     * @param {number} y     * @param {number} x1     * @param {number} y1     * @param {number} x2     * @param {number} y2     * @returns {boolean} Whether {x,y} fits within the rectangle defined by     * {x1,y1,x2,y2}.     */    rectContains: function(x, y, x1, y1, x2, y2) {      if (x < x1 || x > x2) return false;      if (y < y1 || y > y2) return false;      return true;    },    /**     * @ngdoc method     * @name ionic.DomUtil#blurAll     * @description     * Blurs any currently focused input element     * @returns {DOMElement} The element blurred or null     */    blurAll: function() {      if (document.activeElement && document.activeElement != document.body) {        document.activeElement.blur();        return document.activeElement;      }      return null;    },    cachedAttr: function(ele, key, value) {      ele = ele && ele.length && ele[0] || ele;      if (ele && ele.setAttribute) {        var dataKey = '$attr-' + key;        if (arguments.length > 2) {          if (ele[dataKey] !== value) {            ele.setAttribute(key, value);            ele[dataKey] = value;          }        } else if (typeof ele[dataKey] == 'undefined') {          ele[dataKey] = ele.getAttribute(key);        }        return ele[dataKey];      }    },    cachedStyles: function(ele, styles) {      ele = ele && ele.length && ele[0] || ele;      if (ele && ele.style) {        for (var prop in styles) {          if (ele['$style-' + prop] !== styles[prop]) {            ele.style[prop] = ele['$style-' + prop] = styles[prop];          }        }      }    }  };  //Shortcuts  ionic.requestAnimationFrame = ionic.DomUtil.requestAnimationFrame;  ionic.cancelAnimationFrame = ionic.DomUtil.cancelAnimationFrame;  ionic.animationFrameThrottle = ionic.DomUtil.animationFrameThrottle;})(window, document, ionic);/** * ion-events.js * * Author: Max Lynch <max@drifty.com> * * Framework events handles various mobile browser events, and * detects special events like tap/swipe/etc. and emits them * as custom events that can be used in an app. * * Portions lovingly adapted from github.com/maker/ratchet and github.com/alexgibson/tap.js - thanks guys! */(function(ionic) {  // Custom event polyfill  ionic.CustomEvent = (function() {    if( typeof window.CustomEvent === 'function' ) return CustomEvent;    var customEvent = function(event, params) {      var evt;      params = params || {        bubbles: false,        cancelable: false,        detail: undefined      };      try {        evt = document.createEvent("CustomEvent");        evt.initCustomEvent(event, params.bubbles, params.cancelable, params.detail);      } catch (error) {        // fallback for browsers that don't support createEvent('CustomEvent')        evt = document.createEvent("Event");        for (var param in params) {          evt[param] = params[param];        }        evt.initEvent(event, params.bubbles, params.cancelable);      }      return evt;    };    customEvent.prototype = window.Event.prototype;    return customEvent;  })();  /**   * @ngdoc utility   * @name ionic.EventController   * @module ionic   */  ionic.EventController = {    VIRTUALIZED_EVENTS: ['tap', 'swipe', 'swiperight', 'swipeleft', 'drag', 'hold', 'release'],    /**     * @ngdoc method     * @name ionic.EventController#trigger     * @alias ionic.trigger     * @param {string} eventType The event to trigger.     * @param {object} data The data for the event. Hint: pass in     * `{target: targetElement}`     * @param {boolean=} bubbles Whether the event should bubble up the DOM.     * @param {boolean=} cancelable Whether the event should be cancelable.     */    // Trigger a new event    trigger: function(eventType, data, bubbles, cancelable) {      var event = new ionic.CustomEvent(eventType, {        detail: data,        bubbles: !!bubbles,        cancelable: !!cancelable      });      // Make sure to trigger the event on the given target, or dispatch it from      // the window if we don't have an event target      data && data.target && data.target.dispatchEvent && data.target.dispatchEvent(event) || window.dispatchEvent(event);    },    /**     * @ngdoc method     * @name ionic.EventController#on     * @alias ionic.on     * @description Listen to an event on an element.     * @param {string} type The event to listen for.     * @param {function} callback The listener to be called.     * @param {DOMElement} element The element to listen for the event on.     */    on: function(type, callback, element) {      var e = element || window;      // Bind a gesture if it's a virtual event      for(var i = 0, j = this.VIRTUALIZED_EVENTS.length; i < j; i++) {        if(type == this.VIRTUALIZED_EVENTS[i]) {          var gesture = new ionic.Gesture(element);          gesture.on(type, callback);          return gesture;        }      }      // Otherwise bind a normal event      e.addEventListener(type, callback);    },    /**     * @ngdoc method     * @name ionic.EventController#off     * @alias ionic.off     * @description Remove an event listener.     * @param {string} type     * @param {function} callback     * @param {DOMElement} element     */    off: function(type, callback, element) {      element.removeEventListener(type, callback);    },    /**     * @ngdoc method     * @name ionic.EventController#onGesture     * @alias ionic.onGesture     * @description Add an event listener for a gesture on an element.     *     * Available eventTypes (from [hammer.js](http://eightmedia.github.io/hammer.js/)):     *     * `hold`, `tap`, `doubletap`, `drag`, `dragstart`, `dragend`, `dragup`, `dragdown`, <br/>     * `dragleft`, `dragright`, `swipe`, `swipeup`, `swipedown`, `swipeleft`, `swiperight`, <br/>     * `transform`, `transformstart`, `transformend`, `rotate`, `pinch`, `pinchin`, `pinchout`, <br/>     * `touch`, `release`     *     * @param {string} eventType The gesture event to listen for.     * @param {function(e)} callback The function to call when the gesture     * happens.     * @param {DOMElement} element The angular element to listen for the event on.     * @param {object} options object.     * @returns {ionic.Gesture} The gesture object (use this to remove the gesture later on).     */    onGesture: function(type, callback, element, options) {      var gesture = new ionic.Gesture(element, options);      gesture.on(type, callback);      return gesture;    },    /**     * @ngdoc method     * @name ionic.EventController#offGesture     * @alias ionic.offGesture     * @description Remove an event listener for a gesture created on an element.     * @param {ionic.Gesture} gesture The gesture that should be removed.     * @param {string} eventType The gesture event to remove the listener for.     * @param {function(e)} callback The listener to remove.     */    offGesture: function(gesture, type, callback) {      gesture && gesture.off(type, callback);    },    handlePopState: function() {}  };  // Map some convenient top-level functions for event handling  ionic.on = function() { ionic.EventController.on.apply(ionic.EventController, arguments); };  ionic.off = function() { ionic.EventController.off.apply(ionic.EventController, arguments); };  ionic.trigger = ionic.EventController.trigger;//function() { ionic.EventController.trigger.apply(ionic.EventController.trigger, arguments); };  ionic.onGesture = function() { return ionic.EventController.onGesture.apply(ionic.EventController.onGesture, arguments); };  ionic.offGesture = function() { return ionic.EventController.offGesture.apply(ionic.EventController.offGesture, arguments); };})(window.ionic);/* eslint camelcase:0 *//**  * Simple gesture controllers with some common gestures that emit  * gesture events.  *  * Ported from github.com/EightMedia/hammer.js Gestures - thanks!  */(function(ionic) {  /**   * ionic.Gestures   * use this to create instances   * @param   {HTMLElement}   element   * @param   {Object}        options   * @returns {ionic.Gestures.Instance}   * @constructor   */  ionic.Gesture = function(element, options) {    return new ionic.Gestures.Instance(element, options || {});  };  ionic.Gestures = {};  // default settings  ionic.Gestures.defaults = {    // add css to the element to prevent the browser from doing    // its native behavior. this doesnt prevent the scrolling,    // but cancels the contextmenu, tap highlighting etc    // set to false to disable this    stop_browser_behavior: 'disable-user-behavior'  };  // detect touchevents  ionic.Gestures.HAS_POINTEREVENTS = window.navigator.pointerEnabled || window.navigator.msPointerEnabled;  ionic.Gestures.HAS_TOUCHEVENTS = ('ontouchstart' in window);  // dont use mouseevents on mobile devices  ionic.Gestures.MOBILE_REGEX = /mobile|tablet|ip(ad|hone|od)|android|silk/i;  ionic.Gestures.NO_MOUSEEVENTS = ionic.Gestures.HAS_TOUCHEVENTS && window.navigator.userAgent.match(ionic.Gestures.MOBILE_REGEX);  // eventtypes per touchevent (start, move, end)  // are filled by ionic.Gestures.event.determineEventTypes on setup  ionic.Gestures.EVENT_TYPES = {};  // direction defines  ionic.Gestures.DIRECTION_DOWN = 'down';  ionic.Gestures.DIRECTION_LEFT = 'left';  ionic.Gestures.DIRECTION_UP = 'up';  ionic.Gestures.DIRECTION_RIGHT = 'right';  // pointer type  ionic.Gestures.POINTER_MOUSE = 'mouse';  ionic.Gestures.POINTER_TOUCH = 'touch';  ionic.Gestures.POINTER_PEN = 'pen';  // touch event defines  ionic.Gestures.EVENT_START = 'start';  ionic.Gestures.EVENT_MOVE = 'move';  ionic.Gestures.EVENT_END = 'end';  // hammer document where the base events are added at  ionic.Gestures.DOCUMENT = window.document;  // plugins namespace  ionic.Gestures.plugins = {};  // if the window events are set...  ionic.Gestures.READY = false;  /**   * setup events to detect gestures on the document   */  function setup() {    if(ionic.Gestures.READY) {      return;    }    // find what eventtypes we add listeners to    ionic.Gestures.event.determineEventTypes();    // Register all gestures inside ionic.Gestures.gestures    for(var name in ionic.Gestures.gestures) {      if(ionic.Gestures.gestures.hasOwnProperty(name)) {        ionic.Gestures.detection.register(ionic.Gestures.gestures[name]);      }    }    // Add touch events on the document    ionic.Gestures.event.onTouch(ionic.Gestures.DOCUMENT, ionic.Gestures.EVENT_MOVE, ionic.Gestures.detection.detect);    ionic.Gestures.event.onTouch(ionic.Gestures.DOCUMENT, ionic.Gestures.EVENT_END, ionic.Gestures.detection.detect);    // ionic.Gestures is ready...!    ionic.Gestures.READY = true;  }  /**   * create new hammer instance   * all methods should return the instance itself, so it is chainable.   * @param   {HTMLElement}       element   * @param   {Object}            [options={}]   * @returns {ionic.Gestures.Instance}   * @name Gesture.Instance   * @constructor   */  ionic.Gestures.Instance = function(element, options) {    var self = this;    // A null element was passed into the instance, which means    // whatever lookup was done to find this element failed to find it    // so we can't listen for events on it.    if(element === null) {      void 0;      return this;    }    // setup ionic.GesturesJS window events and register all gestures    // this also sets up the default options    setup();    this.element = element;    // start/stop detection option    this.enabled = true;    // merge options    this.options = ionic.Gestures.utils.extend(        ionic.Gestures.utils.extend({}, ionic.Gestures.defaults),        options || {});    // add some css to the element to prevent the browser from doing its native behavoir    if(this.options.stop_browser_behavior) {      ionic.Gestures.utils.stopDefaultBrowserBehavior(this.element, this.options.stop_browser_behavior);    }    // start detection on touchstart    ionic.Gestures.event.onTouch(element, ionic.Gestures.EVENT_START, function(ev) {      if(self.enabled) {        ionic.Gestures.detection.startDetect(self, ev);      }    });    // return instance    return this;  };  ionic.Gestures.Instance.prototype = {    /**     * bind events to the instance     * @param   {String}      gesture     * @param   {Function}    handler     * @returns {ionic.Gestures.Instance}     */    on: function onEvent(gesture, handler){      var gestures = gesture.split(' ');      for(var t = 0; t < gestures.length; t++) {        this.element.addEventListener(gestures[t], handler, false);      }      return this;    },    /**     * unbind events to the instance     * @param   {String}      gesture     * @param   {Function}    handler     * @returns {ionic.Gestures.Instance}     */    off: function offEvent(gesture, handler){      var gestures = gesture.split(' ');      for(var t = 0; t < gestures.length; t++) {        this.element.removeEventListener(gestures[t], handler, false);      }      return this;    },    /**     * trigger gesture event     * @param   {String}      gesture     * @param   {Object}      eventData     * @returns {ionic.Gestures.Instance}     */    trigger: function triggerEvent(gesture, eventData){      // create DOM event      var event = ionic.Gestures.DOCUMENT.createEvent('Event');      event.initEvent(gesture, true, true);      event.gesture = eventData;      // trigger on the target if it is in the instance element,      // this is for event delegation tricks      var element = this.element;      if(ionic.Gestures.utils.hasParent(eventData.target, element)) {        element = eventData.target;      }      element.dispatchEvent(event);      return this;    },    /**     * enable of disable hammer.js detection     * @param   {Boolean}   state     * @returns {ionic.Gestures.Instance}     */    enable: function enable(state) {      this.enabled = state;      return this;    }  };  /**   * this holds the last move event,   * used to fix empty touchend issue   * see the onTouch event for an explanation   * type {Object}   */  var last_move_event = null;  /**   * when the mouse is hold down, this is true   * type {Boolean}   */  var enable_detect = false;  /**   * when touch events have been fired, this is true   * type {Boolean}   */  var touch_triggered = false;  ionic.Gestures.event = {    /**     * simple addEventListener     * @param   {HTMLElement}   element     * @param   {String}        type     * @param   {Function}      handler     */    bindDom: function(element, type, handler) {      var types = type.split(' ');      for(var t = 0; t < types.length; t++) {        element.addEventListener(types[t], handler, false);      }    },    /**     * touch events with mouse fallback     * @param   {HTMLElement}   element     * @param   {String}        eventType        like ionic.Gestures.EVENT_MOVE     * @param   {Function}      handler     */    onTouch: function onTouch(element, eventType, handler) {      var self = this;      this.bindDom(element, ionic.Gestures.EVENT_TYPES[eventType], function bindDomOnTouch(ev) {        var sourceEventType = ev.type.toLowerCase();        // onmouseup, but when touchend has been fired we do nothing.        // this is for touchdevices which also fire a mouseup on touchend        if(sourceEventType.match(/mouse/) && touch_triggered) {          return;        }        // mousebutton must be down or a touch event        else if( sourceEventType.match(/touch/) ||   // touch events are always on screen          sourceEventType.match(/pointerdown/) || // pointerevents touch          (sourceEventType.match(/mouse/) && ev.which === 1)   // mouse is pressed          ){            enable_detect = true;          }        // mouse isn't pressed        else if(sourceEventType.match(/mouse/) && ev.which !== 1) {          enable_detect = false;        }        // we are in a touch event, set the touch triggered bool to true,        // this for the conflicts that may occur on ios and android        if(sourceEventType.match(/touch|pointer/)) {          touch_triggered = true;        }        // count the total touches on the screen        var count_touches = 0;        // when touch has been triggered in this detection session        // and we are now handling a mouse event, we stop that to prevent conflicts        if(enable_detect) {          // update pointerevent          if(ionic.Gestures.HAS_POINTEREVENTS && eventType != ionic.Gestures.EVENT_END) {            count_touches = ionic.Gestures.PointerEvent.updatePointer(eventType, ev);          }          // touch          else if(sourceEventType.match(/touch/)) {            count_touches = ev.touches.length;          }          // mouse          else if(!touch_triggered) {            count_touches = sourceEventType.match(/up/) ? 0 : 1;          }          // if we are in a end event, but when we remove one touch and          // we still have enough, set eventType to move          if(count_touches > 0 && eventType == ionic.Gestures.EVENT_END) {            eventType = ionic.Gestures.EVENT_MOVE;          }          // no touches, force the end event          else if(!count_touches) {            eventType = ionic.Gestures.EVENT_END;          }          // store the last move event          if(count_touches || last_move_event === null) {            last_move_event = ev;          }          // trigger the handler          handler.call(ionic.Gestures.detection, self.collectEventData(element, eventType, self.getTouchList(last_move_event, eventType), ev));          // remove pointerevent from list          if(ionic.Gestures.HAS_POINTEREVENTS && eventType == ionic.Gestures.EVENT_END) {            count_touches = ionic.Gestures.PointerEvent.updatePointer(eventType, ev);          }        }        //debug(sourceEventType +" "+ eventType);        // on the end we reset everything        if(!count_touches) {          last_move_event = null;          enable_detect = false;          touch_triggered = false;          ionic.Gestures.PointerEvent.reset();        }      });    },    /**     * we have different events for each device/browser     * determine what we need and set them in the ionic.Gestures.EVENT_TYPES constant     */    determineEventTypes: function determineEventTypes() {      // determine the eventtype we want to set      var types;      // pointerEvents magic      if(ionic.Gestures.HAS_POINTEREVENTS) {        types = ionic.Gestures.PointerEvent.getEvents();      }      // on Android, iOS, blackberry, windows mobile we dont want any mouseevents      else if(ionic.Gestures.NO_MOUSEEVENTS) {        types = [          'touchstart',          'touchmove',          'touchend touchcancel'];      }      // for non pointer events browsers and mixed browsers,      // like chrome on windows8 touch laptop      else {        types = [          'touchstart mousedown',          'touchmove mousemove',          'touchend touchcancel mouseup'];      }      ionic.Gestures.EVENT_TYPES[ionic.Gestures.EVENT_START] = types[0];      ionic.Gestures.EVENT_TYPES[ionic.Gestures.EVENT_MOVE] = types[1];      ionic.Gestures.EVENT_TYPES[ionic.Gestures.EVENT_END] = types[2];    },    /**     * create touchlist depending on the event     * @param   {Object}    ev     * @param   {String}    eventType   used by the fakemultitouch plugin     */    getTouchList: function getTouchList(ev/*, eventType*/) {      // get the fake pointerEvent touchlist      if(ionic.Gestures.HAS_POINTEREVENTS) {        return ionic.Gestures.PointerEvent.getTouchList();      }      // get the touchlist      else if(ev.touches) {        return ev.touches;      }      // make fake touchlist from mouse position      else {        ev.identifier = 1;        return [ev];      }    },    /**     * collect event data for ionic.Gestures js     * @param   {HTMLElement}   element     * @param   {String}        eventType        like ionic.Gestures.EVENT_MOVE     * @param   {Object}        eventData     */    collectEventData: function collectEventData(element, eventType, touches, ev) {      // find out pointerType      var pointerType = ionic.Gestures.POINTER_TOUCH;      if(ev.type.match(/mouse/) || ionic.Gestures.PointerEvent.matchType(ionic.Gestures.POINTER_MOUSE, ev)) {        pointerType = ionic.Gestures.POINTER_MOUSE;      }      return {        center: ionic.Gestures.utils.getCenter(touches),        timeStamp: new Date().getTime(),        target: ev.target,        touches: touches,        eventType: eventType,        pointerType: pointerType,        srcEvent: ev,        /**         * prevent the browser default actions         * mostly used to disable scrolling of the browser         */        preventDefault: function() {          if(this.srcEvent.preventManipulation) {            this.srcEvent.preventManipulation();          }          if(this.srcEvent.preventDefault) {            // this.srcEvent.preventDefault();          }        },        /**         * stop bubbling the event up to its parents         */        stopPropagation: function() {          this.srcEvent.stopPropagation();        },        /**         * immediately stop gesture detection         * might be useful after a swipe was detected         * @return {*}         */        stopDetect: function() {          return ionic.Gestures.detection.stopDetect();        }      };    }  };  ionic.Gestures.PointerEvent = {    /**     * holds all pointers     * type {Object}     */    pointers: {},    /**     * get a list of pointers     * @returns {Array}     touchlist     */    getTouchList: function() {      var self = this;      var touchlist = [];      // we can use forEach since pointerEvents only is in IE10      Object.keys(self.pointers).sort().forEach(function(id) {        touchlist.push(self.pointers[id]);      });      return touchlist;    },    /**     * update the position of a pointer     * @param   {String}   type             ionic.Gestures.EVENT_END     * @param   {Object}   pointerEvent     */    updatePointer: function(type, pointerEvent) {      if(type == ionic.Gestures.EVENT_END) {        this.pointers = {};      }      else {        pointerEvent.identifier = pointerEvent.pointerId;        this.pointers[pointerEvent.pointerId] = pointerEvent;      }      return Object.keys(this.pointers).length;    },    /**     * check if ev matches pointertype     * @param   {String}        pointerType     ionic.Gestures.POINTER_MOUSE     * @param   {PointerEvent}  ev     */    matchType: function(pointerType, ev) {      if(!ev.pointerType) {        return false;      }      var types = {};      types[ionic.Gestures.POINTER_MOUSE] = (ev.pointerType == ev.MSPOINTER_TYPE_MOUSE || ev.pointerType == ionic.Gestures.POINTER_MOUSE);      types[ionic.Gestures.POINTER_TOUCH] = (ev.pointerType == ev.MSPOINTER_TYPE_TOUCH || ev.pointerType == ionic.Gestures.POINTER_TOUCH);      types[ionic.Gestures.POINTER_PEN] = (ev.pointerType == ev.MSPOINTER_TYPE_PEN || ev.pointerType == ionic.Gestures.POINTER_PEN);      return types[pointerType];    },    /**     * get events     */    getEvents: function() {      return [        'pointerdown MSPointerDown',      'pointermove MSPointerMove',      'pointerup pointercancel MSPointerUp MSPointerCancel'        ];    },    /**     * reset the list     */    reset: function() {      this.pointers = {};    }  };  ionic.Gestures.utils = {    /**     * extend method,     * also used for cloning when dest is an empty object     * @param   {Object}    dest     * @param   {Object}    src     * @param	{Boolean}	merge		do a merge     * @returns {Object}    dest     */    extend: function extend(dest, src, merge) {      for (var key in src) {        if(dest[key] !== undefined && merge) {          continue;        }        dest[key] = src[key];      }      return dest;    },    /**     * find if a node is in the given parent     * used for event delegation tricks     * @param   {HTMLElement}   node     * @param   {HTMLElement}   parent     * @returns {boolean}       has_parent     */    hasParent: function(node, parent) {      while(node){        if(node == parent) {          return true;        }        node = node.parentNode;      }      return false;    },    /**     * get the center of all the touches     * @param   {Array}     touches     * @returns {Object}    center     */    getCenter: function getCenter(touches) {      var valuesX = [], valuesY = [];      for(var t = 0, len = touches.length; t < len; t++) {        valuesX.push(touches[t].pageX);        valuesY.push(touches[t].pageY);      }      return {        pageX: ((Math.min.apply(Math, valuesX) + Math.max.apply(Math, valuesX)) / 2),          pageY: ((Math.min.apply(Math, valuesY) + Math.max.apply(Math, valuesY)) / 2)      };    },    /**     * calculate the velocity between two points     * @param   {Number}    delta_time     * @param   {Number}    delta_x     * @param   {Number}    delta_y     * @returns {Object}    velocity     */    getVelocity: function getVelocity(delta_time, delta_x, delta_y) {      return {        x: Math.abs(delta_x / delta_time) || 0,        y: Math.abs(delta_y / delta_time) || 0      };    },    /**     * calculate the angle between two coordinates     * @param   {Touch}     touch1     * @param   {Touch}     touch2     * @returns {Number}    angle     */    getAngle: function getAngle(touch1, touch2) {      var y = touch2.pageY - touch1.pageY,      x = touch2.pageX - touch1.pageX;      return Math.atan2(y, x) * 180 / Math.PI;    },    /**     * angle to direction define     * @param   {Touch}     touch1     * @param   {Touch}     touch2     * @returns {String}    direction constant, like ionic.Gestures.DIRECTION_LEFT     */    getDirection: function getDirection(touch1, touch2) {      var x = Math.abs(touch1.pageX - touch2.pageX),      y = Math.abs(touch1.pageY - touch2.pageY);      if(x >= y) {        return touch1.pageX - touch2.pageX > 0 ? ionic.Gestures.DIRECTION_LEFT : ionic.Gestures.DIRECTION_RIGHT;      }      else {        return touch1.pageY - touch2.pageY > 0 ? ionic.Gestures.DIRECTION_UP : ionic.Gestures.DIRECTION_DOWN;      }    },    /**     * calculate the distance between two touches     * @param   {Touch}     touch1     * @param   {Touch}     touch2     * @returns {Number}    distance     */    getDistance: function getDistance(touch1, touch2) {      var x = touch2.pageX - touch1.pageX,      y = touch2.pageY - touch1.pageY;      return Math.sqrt((x * x) + (y * y));    },    /**     * calculate the scale factor between two touchLists (fingers)     * no scale is 1, and goes down to 0 when pinched together, and bigger when pinched out     * @param   {Array}     start     * @param   {Array}     end     * @returns {Number}    scale     */    getScale: function getScale(start, end) {      // need two fingers...      if(start.length >= 2 && end.length >= 2) {        return this.getDistance(end[0], end[1]) /          this.getDistance(start[0], start[1]);      }      return 1;    },    /**     * calculate the rotation degrees between two touchLists (fingers)     * @param   {Array}     start     * @param   {Array}     end     * @returns {Number}    rotation     */    getRotation: function getRotation(start, end) {      // need two fingers      if(start.length >= 2 && end.length >= 2) {        return this.getAngle(end[1], end[0]) -          this.getAngle(start[1], start[0]);      }      return 0;    },    /**     * boolean if the direction is vertical     * @param    {String}    direction     * @returns  {Boolean}   is_vertical     */    isVertical: function isVertical(direction) {      return (direction == ionic.Gestures.DIRECTION_UP || direction == ionic.Gestures.DIRECTION_DOWN);    },    /**     * stop browser default behavior with css class     * @param   {HtmlElement}   element     * @param   {Object}        css_class     */    stopDefaultBrowserBehavior: function stopDefaultBrowserBehavior(element, css_class) {      // changed from making many style changes to just adding a preset classname      // less DOM manipulations, less code, and easier to control in the CSS side of things      // hammer.js doesn't come with CSS, but ionic does, which is why we prefer this method      if(element && element.classList) {        element.classList.add(css_class);        element.onselectstart = function() {          return false;        };      }    }  };  ionic.Gestures.detection = {    // contains all registred ionic.Gestures.gestures in the correct order    gestures: [],    // data of the current ionic.Gestures.gesture detection session    current: null,    // the previous ionic.Gestures.gesture session data    // is a full clone of the previous gesture.current object    previous: null,    // when this becomes true, no gestures are fired    stopped: false,    /**     * start ionic.Gestures.gesture detection     * @param   {ionic.Gestures.Instance}   inst     * @param   {Object}            eventData     */    startDetect: function startDetect(inst, eventData) {      // already busy with a ionic.Gestures.gesture detection on an element      if(this.current) {        return;      }      this.stopped = false;      this.current = {        inst: inst, // reference to ionic.GesturesInstance we're working for        startEvent: ionic.Gestures.utils.extend({}, eventData), // start eventData for distances, timing etc        lastEvent: false, // last eventData        name: '' // current gesture we're in/detected, can be 'tap', 'hold' etc      };      this.detect(eventData);    },    /**     * ionic.Gestures.gesture detection     * @param   {Object}    eventData     */    detect: function detect(eventData) {      if(!this.current || this.stopped) {        return null;      }      // extend event data with calculations about scale, distance etc      eventData = this.extendEventData(eventData);      // instance options      var inst_options = this.current.inst.options;      // call ionic.Gestures.gesture handlers      for(var g = 0, len = this.gestures.length; g < len; g++) {        var gesture = this.gestures[g];        // only when the instance options have enabled this gesture        if(!this.stopped && inst_options[gesture.name] !== false) {          // if a handler returns false, we stop with the detection          if(gesture.handler.call(gesture, eventData, this.current.inst) === false) {            this.stopDetect();            break;          }        }      }      // store as previous event event      if(this.current) {        this.current.lastEvent = eventData;      }      // endevent, but not the last touch, so dont stop      if(eventData.eventType == ionic.Gestures.EVENT_END && !eventData.touches.length - 1) {        this.stopDetect();      }      return eventData;    },    /**     * clear the ionic.Gestures.gesture vars     * this is called on endDetect, but can also be used when a final ionic.Gestures.gesture has been detected     * to stop other ionic.Gestures.gestures from being fired     */    stopDetect: function stopDetect() {      // clone current data to the store as the previous gesture      // used for the double tap gesture, since this is an other gesture detect session      this.previous = ionic.Gestures.utils.extend({}, this.current);      // reset the current      this.current = null;      // stopped!      this.stopped = true;    },    /**     * extend eventData for ionic.Gestures.gestures     * @param   {Object}   ev     * @returns {Object}   ev     */    extendEventData: function extendEventData(ev) {      var startEv = this.current.startEvent;      // if the touches change, set the new touches over the startEvent touches      // this because touchevents don't have all the touches on touchstart, or the      // user must place his fingers at the EXACT same time on the screen, which is not realistic      // but, sometimes it happens that both fingers are touching at the EXACT same time      if(startEv && (ev.touches.length != startEv.touches.length || ev.touches === startEv.touches)) {        // extend 1 level deep to get the touchlist with the touch objects        startEv.touches = [];        for(var i = 0, len = ev.touches.length; i < len; i++) {          startEv.touches.push(ionic.Gestures.utils.extend({}, ev.touches[i]));        }      }      var delta_time = ev.timeStamp - startEv.timeStamp,          delta_x = ev.center.pageX - startEv.center.pageX,          delta_y = ev.center.pageY - startEv.center.pageY,          velocity = ionic.Gestures.utils.getVelocity(delta_time, delta_x, delta_y);      ionic.Gestures.utils.extend(ev, {        deltaTime: delta_time,        deltaX: delta_x,        deltaY: delta_y,        velocityX: velocity.x,        velocityY: velocity.y,        distance: ionic.Gestures.utils.getDistance(startEv.center, ev.center),        angle: ionic.Gestures.utils.getAngle(startEv.center, ev.center),        direction: ionic.Gestures.utils.getDirection(startEv.center, ev.center),        scale: ionic.Gestures.utils.getScale(startEv.touches, ev.touches),        rotation: ionic.Gestures.utils.getRotation(startEv.touches, ev.touches),        startEvent: startEv      });      return ev;    },    /**     * register new gesture     * @param   {Object}    gesture object, see gestures.js for documentation     * @returns {Array}     gestures     */    register: function register(gesture) {      // add an enable gesture options if there is no given      var options = gesture.defaults || {};      if(options[gesture.name] === undefined) {        options[gesture.name] = true;      }      // extend ionic.Gestures default options with the ionic.Gestures.gesture options      ionic.Gestures.utils.extend(ionic.Gestures.defaults, options, true);      // set its index      gesture.index = gesture.index || 1000;      // add ionic.Gestures.gesture to the list      this.gestures.push(gesture);      // sort the list by index      this.gestures.sort(function(a, b) {        if (a.index < b.index) {          return -1;        }        if (a.index > b.index) {          return 1;        }        return 0;      });      return this.gestures;    }  };  ionic.Gestures.gestures = ionic.Gestures.gestures || {};  /**   * Custom gestures   * ==============================   *   * Gesture object   * --------------------   * The object structure of a gesture:   *   * { name: 'mygesture',   *   index: 1337,   *   defaults: {   *     mygesture_option: true   *   }   *   handler: function(type, ev, inst) {   *     // trigger gesture event   *     inst.trigger(this.name, ev);   *   }   * }   * @param   {String}    name   * this should be the name of the gesture, lowercase   * it is also being used to disable/enable the gesture per instance config.   *   * @param   {Number}    [index=1000]   * the index of the gesture, where it is going to be in the stack of gestures detection   * like when you build an gesture that depends on the drag gesture, it is a good   * idea to place it after the index of the drag gesture.   *   * @param   {Object}    [defaults={}]   * the default settings of the gesture. these are added to the instance settings,   * and can be overruled per instance. you can also add the name of the gesture,   * but this is also added by default (and set to true).   *   * @param   {Function}  handler   * this handles the gesture detection of your custom gesture and receives the   * following arguments:   *   *      @param  {Object}    eventData   *      event data containing the following properties:   *          timeStamp   {Number}        time the event occurred   *          target      {HTMLElement}   target element   *          touches     {Array}         touches (fingers, pointers, mouse) on the screen   *          pointerType {String}        kind of pointer that was used. matches ionic.Gestures.POINTER_MOUSE|TOUCH   *          center      {Object}        center position of the touches. contains pageX and pageY   *          deltaTime   {Number}        the total time of the touches in the screen   *          deltaX      {Number}        the delta on x axis we haved moved   *          deltaY      {Number}        the delta on y axis we haved moved   *          velocityX   {Number}        the velocity on the x   *          velocityY   {Number}        the velocity on y   *          angle       {Number}        the angle we are moving   *          direction   {String}        the direction we are moving. matches ionic.Gestures.DIRECTION_UP|DOWN|LEFT|RIGHT   *          distance    {Number}        the distance we haved moved   *          scale       {Number}        scaling of the touches, needs 2 touches   *          rotation    {Number}        rotation of the touches, needs 2 touches *   *          eventType   {String}        matches ionic.Gestures.EVENT_START|MOVE|END   *          srcEvent    {Object}        the source event, like TouchStart or MouseDown *   *          startEvent  {Object}        contains the same properties as above,   *                                      but from the first touch. this is used to calculate   *                                      distances, deltaTime, scaling etc   *   *      @param  {ionic.Gestures.Instance}    inst   *      the instance we are doing the detection for. you can get the options from   *      the inst.options object and trigger the gesture event by calling inst.trigger   *   *   * Handle gestures   * --------------------   * inside the handler you can get/set ionic.Gestures.detectionic.current. This is the current   * detection sessionic. It has the following properties   *      @param  {String}    name   *      contains the name of the gesture we have detected. it has not a real function,   *      only to check in other gestures if something is detected.   *      like in the drag gesture we set it to 'drag' and in the swipe gesture we can   *      check if the current gesture is 'drag' by accessing ionic.Gestures.detectionic.current.name   *   *      readonly   *      @param  {ionic.Gestures.Instance}    inst   *      the instance we do the detection for   *   *      readonly   *      @param  {Object}    startEvent   *      contains the properties of the first gesture detection in this sessionic.   *      Used for calculations about timing, distance, etc.   *   *      readonly   *      @param  {Object}    lastEvent   *      contains all the properties of the last gesture detect in this sessionic.   *   * after the gesture detection session has been completed (user has released the screen)   * the ionic.Gestures.detectionic.current object is copied into ionic.Gestures.detectionic.previous,   * this is usefull for gestures like doubletap, where you need to know if the   * previous gesture was a tap   *   * options that have been set by the instance can be received by calling inst.options   *   * You can trigger a gesture event by calling inst.trigger("mygesture", event).   * The first param is the name of your gesture, the second the event argument   *   *   * Register gestures   * --------------------   * When an gesture is added to the ionic.Gestures.gestures object, it is auto registered   * at the setup of the first ionic.Gestures instance. You can also call ionic.Gestures.detectionic.register   * manually and pass your gesture object as a param   *   */  /**   * Hold   * Touch stays at the same place for x time   * events  hold   */  ionic.Gestures.gestures.Hold = {    name: 'hold',    index: 10,    defaults: {      hold_timeout: 500,      hold_threshold: 9    },    timer: null,    handler: function holdGesture(ev, inst) {      switch(ev.eventType) {        case ionic.Gestures.EVENT_START:          // clear any running timers          clearTimeout(this.timer);          // set the gesture so we can check in the timeout if it still is          ionic.Gestures.detection.current.name = this.name;          // set timer and if after the timeout it still is hold,          // we trigger the hold event          this.timer = setTimeout(function() {            if(ionic.Gestures.detection.current.name == 'hold') {              ionic.tap.cancelClick();              inst.trigger('hold', ev);            }          }, inst.options.hold_timeout);          break;          // when you move or end we clear the timer        case ionic.Gestures.EVENT_MOVE:          if(ev.distance > inst.options.hold_threshold) {            clearTimeout(this.timer);          }          break;        case ionic.Gestures.EVENT_END:          clearTimeout(this.timer);          break;      }    }  };  /**   * Tap/DoubleTap   * Quick touch at a place or double at the same place   * events  tap, doubletap   */  ionic.Gestures.gestures.Tap = {    name: 'tap',    index: 100,    defaults: {      tap_max_touchtime: 250,      tap_max_distance: 10,      tap_always: true,      doubletap_distance: 20,      doubletap_interval: 300    },    handler: function tapGesture(ev, inst) {      if(ev.eventType == ionic.Gestures.EVENT_END && ev.srcEvent.type != 'touchcancel') {        // previous gesture, for the double tap since these are two different gesture detections        var prev = ionic.Gestures.detection.previous,        did_doubletap = false;        // when the touchtime is higher then the max touch time        // or when the moving distance is too much        if(ev.deltaTime > inst.options.tap_max_touchtime ||            ev.distance > inst.options.tap_max_distance) {              return;            }        // check if double tap        if(prev && prev.name == 'tap' &&            (ev.timeStamp - prev.lastEvent.timeStamp) < inst.options.doubletap_interval &&            ev.distance < inst.options.doubletap_distance) {              inst.trigger('doubletap', ev);              did_doubletap = true;            }        // do a single tap        if(!did_doubletap || inst.options.tap_always) {          ionic.Gestures.detection.current.name = 'tap';          inst.trigger('tap', ev);        }      }    }  };  /**   * Swipe   * triggers swipe events when the end velocity is above the threshold   * events  swipe, swipeleft, swiperight, swipeup, swipedown   */  ionic.Gestures.gestures.Swipe = {    name: 'swipe',    index: 40,    defaults: {      // set 0 for unlimited, but this can conflict with transform      swipe_max_touches: 1,      swipe_velocity: 0.4    },    handler: function swipeGesture(ev, inst) {      if(ev.eventType == ionic.Gestures.EVENT_END) {        // max touches        if(inst.options.swipe_max_touches > 0 &&            ev.touches.length > inst.options.swipe_max_touches) {              return;            }        // when the distance we moved is too small we skip this gesture        // or we can be already in dragging        if(ev.velocityX > inst.options.swipe_velocity ||            ev.velocityY > inst.options.swipe_velocity) {              // trigger swipe events              inst.trigger(this.name, ev);              inst.trigger(this.name + ev.direction, ev);            }      }    }  };  /**   * Drag   * Move with x fingers (default 1) around on the page. Blocking the scrolling when   * moving left and right is a good practice. When all the drag events are blocking   * you disable scrolling on that area.   * events  drag, drapleft, dragright, dragup, dragdown   */  ionic.Gestures.gestures.Drag = {    name: 'drag',    index: 50,    defaults: {      drag_min_distance: 10,      // Set correct_for_drag_min_distance to true to make the starting point of the drag      // be calculated from where the drag was triggered, not from where the touch started.      // Useful to avoid a jerk-starting drag, which can make fine-adjustments      // through dragging difficult, and be visually unappealing.      correct_for_drag_min_distance: true,      // set 0 for unlimited, but this can conflict with transform      drag_max_touches: 1,      // prevent default browser behavior when dragging occurs      // be careful with it, it makes the element a blocking element      // when you are using the drag gesture, it is a good practice to set this true      drag_block_horizontal: true,      drag_block_vertical: true,      // drag_lock_to_axis keeps the drag gesture on the axis that it started on,      // It disallows vertical directions if the initial direction was horizontal, and vice versa.      drag_lock_to_axis: false,      // drag lock only kicks in when distance > drag_lock_min_distance      // This way, locking occurs only when the distance has become large enough to reliably determine the direction      drag_lock_min_distance: 25,      // prevent default if the gesture is going the given direction      prevent_default_directions: []    },    triggered: false,    handler: function dragGesture(ev, inst) {      if (ev.srcEvent.type == 'touchstart' || ev.srcEvent.type == 'touchend') {        this.preventedFirstMove = false;      } else if (!this.preventedFirstMove && ev.srcEvent.type == 'touchmove') {        // Prevent gestures that are not intended for this event handler from firing subsequent times        if (inst.options.prevent_default_directions.length > 0            && inst.options.prevent_default_directions.indexOf(ev.direction) != -1) {          ev.srcEvent.preventDefault();        }        this.preventedFirstMove = true;      }      // current gesture isnt drag, but dragged is true      // this means an other gesture is busy. now call dragend      if(ionic.Gestures.detection.current.name != this.name && this.triggered) {        inst.trigger(this.name + 'end', ev);        this.triggered = false;        return;      }      // max touches      if(inst.options.drag_max_touches > 0 &&          ev.touches.length > inst.options.drag_max_touches) {            return;          }      switch(ev.eventType) {        case ionic.Gestures.EVENT_START:          this.triggered = false;          break;        case ionic.Gestures.EVENT_MOVE:          // when the distance we moved is too small we skip this gesture          // or we can be already in dragging          if(ev.distance < inst.options.drag_min_distance &&              ionic.Gestures.detection.current.name != this.name) {                return;              }          // we are dragging!          if(ionic.Gestures.detection.current.name != this.name) {            ionic.Gestures.detection.current.name = this.name;            if (inst.options.correct_for_drag_min_distance) {              // When a drag is triggered, set the event center to drag_min_distance pixels from the original event center.              // Without this correction, the dragged distance would jumpstart at drag_min_distance pixels instead of at 0.              // It might be useful to save the original start point somewhere              var factor = Math.abs(inst.options.drag_min_distance / ev.distance);              ionic.Gestures.detection.current.startEvent.center.pageX += ev.deltaX * factor;              ionic.Gestures.detection.current.startEvent.center.pageY += ev.deltaY * factor;              // recalculate event data using new start point              ev = ionic.Gestures.detection.extendEventData(ev);            }          }          // lock drag to axis?          if(ionic.Gestures.detection.current.lastEvent.drag_locked_to_axis || (inst.options.drag_lock_to_axis && inst.options.drag_lock_min_distance <= ev.distance)) {            ev.drag_locked_to_axis = true;          }          var last_direction = ionic.Gestures.detection.current.lastEvent.direction;          if(ev.drag_locked_to_axis && last_direction !== ev.direction) {            // keep direction on the axis that the drag gesture started on            if(ionic.Gestures.utils.isVertical(last_direction)) {              ev.direction = (ev.deltaY < 0) ? ionic.Gestures.DIRECTION_UP : ionic.Gestures.DIRECTION_DOWN;            }            else {              ev.direction = (ev.deltaX < 0) ? ionic.Gestures.DIRECTION_LEFT : ionic.Gestures.DIRECTION_RIGHT;            }          }          // first time, trigger dragstart event          if(!this.triggered) {            inst.trigger(this.name + 'start', ev);            this.triggered = true;          }          // trigger normal event          inst.trigger(this.name, ev);          // direction event, like dragdown          inst.trigger(this.name + ev.direction, ev);          // block the browser events          if( (inst.options.drag_block_vertical && ionic.Gestures.utils.isVertical(ev.direction)) ||              (inst.options.drag_block_horizontal && !ionic.Gestures.utils.isVertical(ev.direction))) {                ev.preventDefault();              }          break;        case ionic.Gestures.EVENT_END:          // trigger dragend          if(this.triggered) {            inst.trigger(this.name + 'end', ev);          }          this.triggered = false;          break;      }    }  };  /**   * Transform   * User want to scale or rotate with 2 fingers   * events  transform, pinch, pinchin, pinchout, rotate   */  ionic.Gestures.gestures.Transform = {    name: 'transform',    index: 45,    defaults: {      // factor, no scale is 1, zoomin is to 0 and zoomout until higher then 1      transform_min_scale: 0.01,      // rotation in degrees      transform_min_rotation: 1,      // prevent default browser behavior when two touches are on the screen      // but it makes the element a blocking element      // when you are using the transform gesture, it is a good practice to set this true      transform_always_block: false    },    triggered: false,    handler: function transformGesture(ev, inst) {      // current gesture isnt drag, but dragged is true      // this means an other gesture is busy. now call dragend      if(ionic.Gestures.detection.current.name != this.name && this.triggered) {        inst.trigger(this.name + 'end', ev);        this.triggered = false;        return;      }      // atleast multitouch      if(ev.touches.length < 2) {        return;      }      // prevent default when two fingers are on the screen      if(inst.options.transform_always_block) {        ev.preventDefault();      }      switch(ev.eventType) {        case ionic.Gestures.EVENT_START:          this.triggered = false;          break;        case ionic.Gestures.EVENT_MOVE:          var scale_threshold = Math.abs(1 - ev.scale);          var rotation_threshold = Math.abs(ev.rotation);          // when the distance we moved is too small we skip this gesture          // or we can be already in dragging          if(scale_threshold < inst.options.transform_min_scale &&              rotation_threshold < inst.options.transform_min_rotation) {                return;              }          // we are transforming!          ionic.Gestures.detection.current.name = this.name;          // first time, trigger dragstart event          if(!this.triggered) {            inst.trigger(this.name + 'start', ev);            this.triggered = true;          }          inst.trigger(this.name, ev); // basic transform event          // trigger rotate event          if(rotation_threshold > inst.options.transform_min_rotation) {            inst.trigger('rotate', ev);          }          // trigger pinch event          if(scale_threshold > inst.options.transform_min_scale) {            inst.trigger('pinch', ev);            inst.trigger('pinch' + ((ev.scale < 1) ? 'in' : 'out'), ev);          }          break;        case ionic.Gestures.EVENT_END:          // trigger dragend          if(this.triggered) {            inst.trigger(this.name + 'end', ev);          }          this.triggered = false;          break;      }    }  };  /**   * Touch   * Called as first, tells the user has touched the screen   * events  touch   */  ionic.Gestures.gestures.Touch = {    name: 'touch',    index: -Infinity,    defaults: {      // call preventDefault at touchstart, and makes the element blocking by      // disabling the scrolling of the page, but it improves gestures like      // transforming and dragging.      // be careful with using this, it can be very annoying for users to be stuck      // on the page      prevent_default: false,      // disable mouse events, so only touch (or pen!) input triggers events      prevent_mouseevents: false    },    handler: function touchGesture(ev, inst) {      if(inst.options.prevent_mouseevents && ev.pointerType == ionic.Gestures.POINTER_MOUSE) {        ev.stopDetect();        return;      }      if(inst.options.prevent_default) {        ev.preventDefault();      }      if(ev.eventType == ionic.Gestures.EVENT_START) {        inst.trigger(this.name, ev);      }    }  };  /**   * Release   * Called as last, tells the user has released the screen   * events  release   */  ionic.Gestures.gestures.Release = {    name: 'release',    index: Infinity,    handler: function releaseGesture(ev, inst) {      if(ev.eventType == ionic.Gestures.EVENT_END) {        inst.trigger(this.name, ev);      }    }  };})(window.ionic);(function(window, document, ionic) {  function getParameterByName(name) {    name = name.replace(/[\[]/, "\\[").replace(/[\]]/, "\\]");    var regex = new RegExp("[\\?&]" + name + "=([^&#]*)"),    results = regex.exec(location.search);    return results === null ? "" : decodeURIComponent(results[1].replace(/\+/g, " "));  }  var IOS = 'ios';  var ANDROID = 'android';  var WINDOWS_PHONE = 'windowsphone';  var EDGE = 'edge';  var CROSSWALK = 'crosswalk';  var requestAnimationFrame = ionic.requestAnimationFrame;  /**   * @ngdoc utility   * @name ionic.Platform   * @module ionic   * @description   * A set of utility methods that can be used to retrieve the device ready state and   * various other information such as what kind of platform the app is currently installed on.   *   * @usage   * ```js   * angular.module('PlatformApp', ['ionic'])   * .controller('PlatformCtrl', function($scope) {   *   *   ionic.Platform.ready(function(){   *     // will execute when device is ready, or immediately if the device is already ready.   *   });   *   *   var deviceInformation = ionic.Platform.device();   *   *   var isWebView = ionic.Platform.isWebView();   *   var isIPad = ionic.Platform.isIPad();   *   var isIOS = ionic.Platform.isIOS();   *   var isAndroid = ionic.Platform.isAndroid();   *   var isWindowsPhone = ionic.Platform.isWindowsPhone();   *   *   var currentPlatform = ionic.Platform.platform();   *   var currentPlatformVersion = ionic.Platform.version();   *   *   ionic.Platform.exitApp(); // stops the app   * });   * ```   */  var self = ionic.Platform = {    // Put navigator on platform so it can be mocked and set    // the browser does not allow window.navigator to be set    navigator: window.navigator,    /**     * @ngdoc property     * @name ionic.Platform#isReady     * @returns {boolean} Whether the device is ready.     */    isReady: false,    /**     * @ngdoc property     * @name ionic.Platform#isFullScreen     * @returns {boolean} Whether the device is fullscreen.     */    isFullScreen: false,    /**     * @ngdoc property     * @name ionic.Platform#platforms     * @returns {Array(string)} An array of all platforms found.     */    platforms: null,    /**     * @ngdoc property     * @name ionic.Platform#grade     * @returns {string} What grade the current platform is.     */    grade: null,    /**     * @ngdoc property     * @name ionic.Platform#ua     * @returns {string} What User Agent is.     */    ua: navigator.userAgent,    /**     * @ngdoc method     * @name ionic.Platform#ready     * @description     * Trigger a callback once the device is ready, or immediately     * if the device is already ready. This method can be run from     * anywhere and does not need to be wrapped by any additonal methods.     * When the app is within a WebView (Cordova), it'll fire     * the callback once the device is ready. If the app is within     * a web browser, it'll fire the callback after `window.load`.     * Please remember that Cordova features (Camera, FileSystem, etc) still     * will not work in a web browser.     * @param {function} callback The function to call.     */    ready: function(cb) {      // run through tasks to complete now that the device is ready      if (self.isReady) {        cb();      } else {        // the platform isn't ready yet, add it to this array        // which will be called once the platform is ready        readyCallbacks.push(cb);      }    },    /**     * @private     */    detect: function() {      self._checkPlatforms();      requestAnimationFrame(function() {        // only add to the body class if we got platform info        for (var i = 0; i < self.platforms.length; i++) {          document.body.classList.add('platform-' + self.platforms[i]);        }      });    },    /**     * @ngdoc method     * @name ionic.Platform#setGrade     * @description Set the grade of the device: 'a', 'b', or 'c'. 'a' is the best     * (most css features enabled), 'c' is the worst.  By default, sets the grade     * depending on the current device.     * @param {string} grade The new grade to set.     */    setGrade: function(grade) {      var oldGrade = self.grade;      self.grade = grade;      requestAnimationFrame(function() {        if (oldGrade) {          document.body.classList.remove('grade-' + oldGrade);        }        document.body.classList.add('grade-' + grade);      });    },    /**     * @ngdoc method     * @name ionic.Platform#device     * @description Return the current device (given by cordova).     * @returns {object} The device object.     */    device: function() {      return window.device || {};    },    _checkPlatforms: function() {      self.platforms = [];      var grade = 'a';      if (self.isWebView()) {        self.platforms.push('webview');        if (!(!window.cordova && !window.PhoneGap && !window.phonegap)) {          self.platforms.push('cordova');        } else if (typeof window.forge === 'object') {          self.platforms.push('trigger');        }      } else {        self.platforms.push('browser');      }      if (self.isIPad()) self.platforms.push('ipad');      var platform = self.platform();      if (platform) {        self.platforms.push(platform);        var version = self.version();        if (version) {          var v = version.toString();          if (v.indexOf('.') > 0) {            v = v.replace('.', '_');          } else {            v += '_0';          }          self.platforms.push(platform + v.split('_')[0]);          self.platforms.push(platform + v);          if (self.isAndroid() && version < 4.4) {            grade = (version < 4 ? 'c' : 'b');          } else if (self.isWindowsPhone()) {            grade = 'b';          }        }      }      self.setGrade(grade);    },    /**     * @ngdoc method     * @name ionic.Platform#isWebView     * @returns {boolean} Check if we are running within a WebView (such as Cordova).     */    isWebView: function() {      return !(!window.cordova && !window.PhoneGap && !window.phonegap && window.forge !== 'object');    },    /**     * @ngdoc method     * @name ionic.Platform#isIPad     * @returns {boolean} Whether we are running on iPad.     */    isIPad: function() {      if (/iPad/i.test(self.navigator.platform)) {        return true;      }      return /iPad/i.test(self.ua);    },    /**     * @ngdoc method     * @name ionic.Platform#isIOS     * @returns {boolean} Whether we are running on iOS.     */    isIOS: function() {      return self.is(IOS);    },    /**     * @ngdoc method     * @name ionic.Platform#isAndroid     * @returns {boolean} Whether we are running on Android.     */    isAndroid: function() {      return self.is(ANDROID);    },    /**     * @ngdoc method     * @name ionic.Platform#isWindowsPhone     * @returns {boolean} Whether we are running on Windows Phone.     */    isWindowsPhone: function() {      return self.is(WINDOWS_PHONE);    },    /**     * @ngdoc method     * @name ionic.Platform#isEdge     * @returns {boolean} Whether we are running on MS Edge/Windows 10 (inc. Phone)     */    isEdge: function() {      return self.is(EDGE);    },    isCrosswalk: function() {      return self.is(CROSSWALK);    },    /**     * @ngdoc method     * @name ionic.Platform#platform     * @returns {string} The name of the current platform.     */    platform: function() {      // singleton to get the platform name      if (platformName === null) self.setPlatform(self.device().platform);      return platformName;    },    /**     * @private     */    setPlatform: function(n) {      if (typeof n != 'undefined' && n !== null && n.length) {        platformName = n.toLowerCase();      } else if (getParameterByName('ionicplatform')) {        platformName = getParameterByName('ionicplatform');      } else if (self.ua.indexOf('Edge') > -1) {        platformName = EDGE;      } else if (self.ua.indexOf('Windows Phone') > -1) {        platformName = WINDOWS_PHONE;      } else if (self.ua.indexOf('Android') > 0) {        platformName = ANDROID;      } else if (/iPhone|iPad|iPod/.test(self.ua)) {        platformName = IOS;      } else {        platformName = self.navigator.platform && navigator.platform.toLowerCase().split(' ')[0] || '';      }    },    /**     * @ngdoc method     * @name ionic.Platform#version     * @returns {number} The version of the current device platform.     */    version: function() {      // singleton to get the platform version      if (platformVersion === null) self.setVersion(self.device().version);      return platformVersion;    },    /**     * @private     */    setVersion: function(v) {      if (typeof v != 'undefined' && v !== null) {        v = v.split('.');        v = parseFloat(v[0] + '.' + (v.length > 1 ? v[1] : 0));        if (!isNaN(v)) {          platformVersion = v;          return;        }      }      platformVersion = 0;      // fallback to user-agent checking      var pName = self.platform();      var versionMatch = {        'android': /Android (\d+).(\d+)?/,        'ios': /OS (\d+)_(\d+)?/,        'windowsphone': /Windows Phone (\d+).(\d+)?/      };      if (versionMatch[pName]) {        v = self.ua.match(versionMatch[pName]);        if (v && v.length > 2) {          platformVersion = parseFloat(v[1] + '.' + v[2]);        }      }    },    /**     * @ngdoc method     * @name ionic.Platform#is     * @param {string} Platform name.     * @returns {boolean} Whether the platform name provided is detected.     */    is: function(type) {      type = type.toLowerCase();      // check if it has an array of platforms      if (self.platforms) {        for (var x = 0; x < self.platforms.length; x++) {          if (self.platforms[x] === type) return true;        }      }      // exact match      var pName = self.platform();      if (pName) {        return pName === type.toLowerCase();      }      // A quick hack for to check userAgent      return self.ua.toLowerCase().indexOf(type) >= 0;    },    /**     * @ngdoc method     * @name ionic.Platform#exitApp     * @description Exit the app.     */    exitApp: function() {      self.ready(function() {        navigator.app && navigator.app.exitApp && navigator.app.exitApp();      });    },    /**     * @ngdoc method     * @name ionic.Platform#showStatusBar     * @description Shows or hides the device status bar (in Cordova). Requires `cordova plugin add org.apache.cordova.statusbar`     * @param {boolean} shouldShow Whether or not to show the status bar.     */    showStatusBar: function(val) {      // Only useful when run within cordova      self._showStatusBar = val;      self.ready(function() {        // run this only when or if the platform (cordova) is ready        requestAnimationFrame(function() {          if (self._showStatusBar) {            // they do not want it to be full screen            window.StatusBar && window.StatusBar.show();            document.body.classList.remove('status-bar-hide');          } else {            // it should be full screen            window.StatusBar && window.StatusBar.hide();            document.body.classList.add('status-bar-hide');          }        });      });    },    /**     * @ngdoc method     * @name ionic.Platform#fullScreen     * @description     * Sets whether the app is fullscreen or not (in Cordova).     * @param {boolean=} showFullScreen Whether or not to set the app to fullscreen. Defaults to true. Requires `cordova plugin add org.apache.cordova.statusbar`     * @param {boolean=} showStatusBar Whether or not to show the device's status bar. Defaults to false.     */    fullScreen: function(showFullScreen, showStatusBar) {      // showFullScreen: default is true if no param provided      self.isFullScreen = (showFullScreen !== false);      // add/remove the fullscreen classname to the body      ionic.DomUtil.ready(function() {        // run this only when or if the DOM is ready        requestAnimationFrame(function() {          if (self.isFullScreen) {            document.body.classList.add('fullscreen');          } else {            document.body.classList.remove('fullscreen');          }        });        // showStatusBar: default is false if no param provided        self.showStatusBar((showStatusBar === true));      });    }  };  var platformName = null, // just the name, like iOS or Android  platformVersion = null, // a float of the major and minor, like 7.1  readyCallbacks = [],  windowLoadListenderAttached,  platformReadyTimer = 2000; // How long to wait for platform ready before emitting a warning  verifyPlatformReady();  // Warn the user if deviceready did not fire in a reasonable amount of time, and how to fix it.  function verifyPlatformReady() {    setTimeout(function() {      if(!self.isReady && self.isWebView()) {        void 0;      }    }, platformReadyTimer);  }  // setup listeners to know when the device is ready to go  function onWindowLoad() {    if (self.isWebView()) {      // the window and scripts are fully loaded, and a cordova/phonegap      // object exists then let's listen for the deviceready      document.addEventListener("deviceready", onPlatformReady, false);    } else {      // the window and scripts are fully loaded, but the window object doesn't have the      // cordova/phonegap object, so its just a browser, not a webview wrapped w/ cordova      onPlatformReady();    }    if (windowLoadListenderAttached) {      window.removeEventListener("load", onWindowLoad, false);    }  }  if (document.readyState === 'complete') {    onWindowLoad();  } else {    windowLoadListenderAttached = true;    window.addEventListener("load", onWindowLoad, false);  }  function onPlatformReady() {    // the device is all set to go, init our own stuff then fire off our event    self.isReady = true;    self.detect();    for (var x = 0; x < readyCallbacks.length; x++) {      // fire off all the callbacks that were added before the platform was ready      readyCallbacks[x]();    }    readyCallbacks = [];    ionic.trigger('platformready', { target: document });    requestAnimationFrame(function() {      document.body.classList.add('platform-ready');    });  }})(window, document, ionic);(function(document, ionic) {  'use strict';  // Ionic CSS polyfills  ionic.CSS = {};  ionic.CSS.TRANSITION = [];  ionic.CSS.TRANSFORM = [];  ionic.EVENTS = {};  (function() {    // transform    var i, keys = ['webkitTransform', 'transform', '-webkit-transform', 'webkit-transform',                   '-moz-transform', 'moz-transform', 'MozTransform', 'mozTransform', 'msTransform'];    for (i = 0; i < keys.length; i++) {      if (document.documentElement.style[keys[i]] !== undefined) {        ionic.CSS.TRANSFORM = keys[i];        break;      }    }    // transition    keys = ['webkitTransition', 'mozTransition', 'msTransition', 'transition'];    for (i = 0; i < keys.length; i++) {      if (document.documentElement.style[keys[i]] !== undefined) {        ionic.CSS.TRANSITION = keys[i];        break;      }    }    // Fallback in case the keys don't exist at all    ionic.CSS.TRANSITION = ionic.CSS.TRANSITION || 'transition';    // The only prefix we care about is webkit for transitions.    var isWebkit = ionic.CSS.TRANSITION.indexOf('webkit') > -1;    // transition duration    ionic.CSS.TRANSITION_DURATION = (isWebkit ? '-webkit-' : '') + 'transition-duration';    // To be sure transitionend works everywhere, include *both* the webkit and non-webkit events    ionic.CSS.TRANSITIONEND = (isWebkit ? 'webkitTransitionEnd ' : '') + 'transitionend';  })();  (function() {      var touchStartEvent = 'touchstart';      var touchMoveEvent = 'touchmove';      var touchEndEvent = 'touchend';      var touchCancelEvent = 'touchcancel';      if (window.navigator.pointerEnabled) {        touchStartEvent = 'pointerdown';        touchMoveEvent = 'pointermove';        touchEndEvent = 'pointerup';        touchCancelEvent = 'pointercancel';      } else if (window.navigator.msPointerEnabled) {        touchStartEvent = 'MSPointerDown';        touchMoveEvent = 'MSPointerMove';        touchEndEvent = 'MSPointerUp';        touchCancelEvent = 'MSPointerCancel';      }      ionic.EVENTS.touchstart = touchStartEvent;      ionic.EVENTS.touchmove = touchMoveEvent;      ionic.EVENTS.touchend = touchEndEvent;      ionic.EVENTS.touchcancel = touchCancelEvent;  })();  // classList polyfill for them older Androids  // https://gist.github.com/devongovett/1381839  if (!("classList" in document.documentElement) && Object.defineProperty && typeof HTMLElement !== 'undefined') {    Object.defineProperty(HTMLElement.prototype, 'classList', {      get: function() {        var self = this;        function update(fn) {          return function() {            var x, classes = self.className.split(/\s+/);            for (x = 0; x < arguments.length; x++) {              fn(classes, classes.indexOf(arguments[x]), arguments[x]);            }            self.className = classes.join(" ");          };        }        return {          add: update(function(classes, index, value) {            ~index || classes.push(value);          }),          remove: update(function(classes, index) {            ~index && classes.splice(index, 1);          }),          toggle: update(function(classes, index, value) {            ~index ? classes.splice(index, 1) : classes.push(value);          }),          contains: function(value) {            return !!~self.className.split(/\s+/).indexOf(value);          },          item: function(i) {            return self.className.split(/\s+/)[i] || null;          }        };      }    });  }})(document, ionic);/** * @ngdoc page * @name tap * @module ionic * @description * On touch devices such as a phone or tablet, some browsers implement a 300ms delay between * the time the user stops touching the display and the moment the browser executes the * click. This delay was initially introduced so the browser can know whether the user wants to * double-tap to zoom in on the webpage.  Basically, the browser waits roughly 300ms to see if * the user is double-tapping, or just tapping on the display once. * * Out of the box, Ionic automatically removes the 300ms delay in order to make Ionic apps * feel more "native" like. Resultingly, other solutions such as * [fastclick](https://github.com/ftlabs/fastclick) and Angular's * [ngTouch](https://docs.angularjs.org/api/ngTouch) should not be included, to avoid conflicts. * * Some browsers already remove the delay with certain settings, such as the CSS property * `touch-events: none` or with specific meta tag viewport values. However, each of these * browsers still handle clicks differently, such as when to fire off or cancel the event * (like scrolling when the target is a button, or holding a button down). * For browsers that already remove the 300ms delay, consider Ionic's tap system as a way to * normalize how clicks are handled across the various devices so there's an expected response * no matter what the device, platform or version. Additionally, Ionic will prevent * ghostclicks which even browsers that remove the delay still experience. * * In some cases, third-party libraries may also be working with touch events which can interfere * with the tap system. For example, mapping libraries like Google or Leaflet Maps often implement * a touch detection system which conflicts with Ionic's tap system. * * ### Disabling the tap system * * To disable the tap for an element and all of its children elements, * add the attribute `data-tap-disabled="true"`. * * ```html * <div data-tap-disabled="true"> *     <div id="google-map"></div> * </div> * ``` * * ### Additional Notes: * * - Ionic tap  works with Ionic's JavaScript scrolling * - Elements can come and go from the DOM and Ionic tap doesn't keep adding and removing *   listeners * - No "tap delay" after the first "tap" (you can tap as fast as you want, they all click) * - Minimal events listeners, only being added to document * - Correct focus in/out on each input type (select, textearea, range) on each platform/device * - Shows and hides virtual keyboard correctly for each platform/device * - Works with labels surrounding inputs * - Does not fire off a click if the user moves the pointer too far * - Adds and removes an 'activated' css class * - Multiple [unit tests](https://github.com/driftyco/ionic/blob/master/test/unit/utils/tap.unit.js) for each scenario * *//* IONIC TAP --------------- - Both touch and mouse events are added to the document.body on DOM ready - If a touch event happens, it does not use mouse event listeners - On touchend, if the distance between start and end was small, trigger a click - In the triggered click event, add a 'isIonicTap' property - The triggered click receives the same x,y coordinates as as the end event - On document.body click listener (with useCapture=true), only allow clicks with 'isIonicTap' - Triggering clicks with mouse events work the same as touch, except with mousedown/mouseup - Tapping inputs is disabled during scrolling*/var tapDoc; // the element which the listeners are on (document.body)var tapActiveEle; // the element which is active (probably has focus)var tapEnabledTouchEvents;var tapMouseResetTimer;var tapPointerMoved;var tapPointerStart;var tapTouchFocusedInput;var tapLastTouchTarget;var tapTouchMoveListener = 'touchmove';// how much the coordinates can be off between start/end, but still a clickvar TAP_RELEASE_TOLERANCE = 12; // default tolerancevar TAP_RELEASE_BUTTON_TOLERANCE = 50; // button elements should have a larger tolerancevar tapEventListeners = {  'click': tapClickGateKeeper,  'mousedown': tapMouseDown,  'mouseup': tapMouseUp,  'mousemove': tapMouseMove,  'touchstart': tapTouchStart,  'touchend': tapTouchEnd,  'touchcancel': tapTouchCancel,  'touchmove': tapTouchMove,  'pointerdown': tapTouchStart,  'pointerup': tapTouchEnd,  'pointercancel': tapTouchCancel,  'pointermove': tapTouchMove,  'MSPointerDown': tapTouchStart,  'MSPointerUp': tapTouchEnd,  'MSPointerCancel': tapTouchCancel,  'MSPointerMove': tapTouchMove,  'focusin': tapFocusIn,  'focusout': tapFocusOut};ionic.tap = {  register: function(ele) {    tapDoc = ele;    tapEventListener('click', true, true);    tapEventListener('mouseup');    tapEventListener('mousedown');    if (window.navigator.pointerEnabled) {      tapEventListener('pointerdown');      tapEventListener('pointerup');      tapEventListener('pointercancel');      tapTouchMoveListener = 'pointermove';    } else if (window.navigator.msPointerEnabled) {      tapEventListener('MSPointerDown');      tapEventListener('MSPointerUp');      tapEventListener('MSPointerCancel');      tapTouchMoveListener = 'MSPointerMove';    } else {      tapEventListener('touchstart');      tapEventListener('touchend');      tapEventListener('touchcancel');    }    tapEventListener('focusin');    tapEventListener('focusout');    return function() {      for (var type in tapEventListeners) {        tapEventListener(type, false);      }      tapDoc = null;      tapActiveEle = null;      tapEnabledTouchEvents = false;      tapPointerMoved = false;      tapPointerStart = null;    };  },  ignoreScrollStart: function(e) {    return (e.defaultPrevented) ||  // defaultPrevented has been assigned by another component handling the event           (/^(file|range)$/i).test(e.target.type) ||           (e.target.dataset ? e.target.dataset.preventScroll : e.target.getAttribute('data-prevent-scroll')) == 'true' || // manually set within an elements attributes           (!!(/^(object|embed)$/i).test(e.target.tagName)) ||  // flash/movie/object touches should not try to scroll           ionic.tap.isElementTapDisabled(e.target); // check if this element, or an ancestor, has `data-tap-disabled` attribute  },  isTextInput: function(ele) {    return !!ele &&           (ele.tagName == 'TEXTAREA' ||            ele.contentEditable === 'true' ||            (ele.tagName == 'INPUT' && !(/^(radio|checkbox|range|file|submit|reset|color|image|button)$/i).test(ele.type)));  },  isDateInput: function(ele) {    return !!ele &&            (ele.tagName == 'INPUT' && (/^(date|time|datetime-local|month|week)$/i).test(ele.type));  },  isVideo: function(ele) {    return !!ele &&            (ele.tagName == 'VIDEO');  },  isKeyboardElement: function(ele) {    if ( !ionic.Platform.isIOS() || ionic.Platform.isIPad() ) {      return ionic.tap.isTextInput(ele) && !ionic.tap.isDateInput(ele);    } else {      return ionic.tap.isTextInput(ele) || ( !!ele && ele.tagName == "SELECT");    }  },  isLabelWithTextInput: function(ele) {    var container = tapContainingElement(ele, false);    return !!container &&           ionic.tap.isTextInput(tapTargetElement(container));  },  containsOrIsTextInput: function(ele) {    return ionic.tap.isTextInput(ele) || ionic.tap.isLabelWithTextInput(ele);  },  cloneFocusedInput: function(container) {    if (ionic.tap.hasCheckedClone) return;    ionic.tap.hasCheckedClone = true;    ionic.requestAnimationFrame(function() {      var focusInput = container.querySelector(':focus');      if (ionic.tap.isTextInput(focusInput) && !ionic.tap.isDateInput(focusInput)) {        var clonedInput = focusInput.cloneNode(true);        clonedInput.value = focusInput.value;        clonedInput.classList.add('cloned-text-input');        clonedInput.readOnly = true;        if (focusInput.isContentEditable) {          clonedInput.contentEditable = focusInput.contentEditable;          clonedInput.innerHTML = focusInput.innerHTML;        }        focusInput.parentElement.insertBefore(clonedInput, focusInput);        focusInput.classList.add('previous-input-focus');        clonedInput.scrollTop = focusInput.scrollTop;      }    });  },  hasCheckedClone: false,  removeClonedInputs: function(container) {    ionic.tap.hasCheckedClone = false;    ionic.requestAnimationFrame(function() {      var clonedInputs = container.querySelectorAll('.cloned-text-input');      var previousInputFocus = container.querySelectorAll('.previous-input-focus');      var x;      for (x = 0; x < clonedInputs.length; x++) {        clonedInputs[x].parentElement.removeChild(clonedInputs[x]);      }      for (x = 0; x < previousInputFocus.length; x++) {        previousInputFocus[x].classList.remove('previous-input-focus');        previousInputFocus[x].style.top = '';        if ( ionic.keyboard.isOpen && !ionic.keyboard.isClosing ) previousInputFocus[x].focus();      }    });  },  requiresNativeClick: function(ele) {    if (ionic.Platform.isWindowsPhone() && (ele.tagName == 'A' || ele.tagName == 'BUTTON' || ele.hasAttribute('ng-click') || (ele.tagName == 'INPUT' && (ele.type == 'button' || ele.type == 'submit')))) {      return true; //Windows Phone edge case, prevent ng-click (and similar) events from firing twice on this platform    }    if (!ele || ele.disabled || (/^(file|range)$/i).test(ele.type) || (/^(object|video)$/i).test(ele.tagName) || ionic.tap.isLabelContainingFileInput(ele)) {      return true;    }    return ionic.tap.isElementTapDisabled(ele);  },  isLabelContainingFileInput: function(ele) {    var lbl = tapContainingElement(ele);    if (lbl.tagName !== 'LABEL') return false;    var fileInput = lbl.querySelector('input[type=file]');    if (fileInput && fileInput.disabled === false) return true;    return false;  },  isElementTapDisabled: function(ele) {    if (ele && ele.nodeType === 1) {      var element = ele;      while (element) {        if (element.getAttribute && element.getAttribute('data-tap-disabled') == 'true') {          return true;        }        element = element.parentElement;      }    }    return false;  },  setTolerance: function(releaseTolerance, releaseButtonTolerance) {    TAP_RELEASE_TOLERANCE = releaseTolerance;    TAP_RELEASE_BUTTON_TOLERANCE = releaseButtonTolerance;  },  cancelClick: function() {    // used to cancel any simulated clicks which may happen on a touchend/mouseup    // gestures uses this method within its tap and hold events    tapPointerMoved = true;  },  pointerCoord: function(event) {    // This method can get coordinates for both a mouse click    // or a touch depending on the given event    var c = { x: 0, y: 0 };    if (event) {      var touches = event.touches && event.touches.length ? event.touches : [event];      var e = (event.changedTouches && event.changedTouches[0]) || touches[0];      if (e) {        c.x = e.clientX || e.pageX || 0;        c.y = e.clientY || e.pageY || 0;      }    }    return c;  }};function tapEventListener(type, enable, useCapture) {  if (enable !== false) {    tapDoc.addEventListener(type, tapEventListeners[type], useCapture);  } else {    tapDoc.removeEventListener(type, tapEventListeners[type]);  }}function tapClick(e) {  // simulate a normal click by running the element's click method then focus on it  var container = tapContainingElement(e.target);  var ele = tapTargetElement(container);  if (ionic.tap.requiresNativeClick(ele) || tapPointerMoved) return false;  var c = ionic.tap.pointerCoord(e);  //console.log('tapClick', e.type, ele.tagName, '('+c.x+','+c.y+')');  triggerMouseEvent('click', ele, c.x, c.y);  // if it's an input, focus in on the target, otherwise blur  tapHandleFocus(ele);}function triggerMouseEvent(type, ele, x, y) {  // using initMouseEvent instead of MouseEvent for our Android friends  var clickEvent = document.createEvent("MouseEvents");  clickEvent.initMouseEvent(type, true, true, window, 1, 0, 0, x, y, false, false, false, false, 0, null);  clickEvent.isIonicTap = true;  ele.dispatchEvent(clickEvent);}function tapClickGateKeeper(e) {  //console.log('click ' + Date.now() + ' isIonicTap: ' + (e.isIonicTap ? true : false));  if (e.target.type == 'submit' && e.detail === 0) {    // do not prevent click if it came from an "Enter" or "Go" keypress submit    return null;  }  // do not allow through any click events that were not created by ionic.tap  if ((ionic.scroll.isScrolling && ionic.tap.containsOrIsTextInput(e.target)) ||      (!e.isIonicTap && !ionic.tap.requiresNativeClick(e.target))) {    //console.log('clickPrevent', e.target.tagName);    e.stopPropagation();    if (!ionic.tap.isLabelWithTextInput(e.target)) {      // labels clicks from native should not preventDefault othersize keyboard will not show on input focus      e.preventDefault();    }    return false;  }}// MOUSEfunction tapMouseDown(e) {  //console.log('mousedown ' + Date.now());  if (e.isIonicTap || tapIgnoreEvent(e)) return null;  if (tapEnabledTouchEvents) {    //console.log('mousedown', 'stop event');    e.stopPropagation();    if (!ionic.Platform.isEdge() && (!ionic.tap.isTextInput(e.target) || tapLastTouchTarget !== e.target) &&      !isSelectOrOption(e.target.tagName) && !ionic.tap.isVideo(e.target)) {      // If you preventDefault on a text input then you cannot move its text caret/cursor.      // Allow through only the text input default. However, without preventDefault on an      // input the 300ms delay can change focus on inputs after the keyboard shows up.      // The focusin event handles the chance of focus changing after the keyboard shows.      // Windows Phone - if you preventDefault on a video element then you cannot operate      // its native controls.      e.preventDefault();    }    return false;  }  tapPointerMoved = false;  tapPointerStart = ionic.tap.pointerCoord(e);  tapEventListener('mousemove');  ionic.activator.start(e);}function tapMouseUp(e) {  //console.log("mouseup " + Date.now());  if (tapEnabledTouchEvents) {    e.stopPropagation();    e.preventDefault();    return false;  }  if (tapIgnoreEvent(e) || isSelectOrOption(e.target.tagName)) return false;  if (!tapHasPointerMoved(e)) {    tapClick(e);  }  tapEventListener('mousemove', false);  ionic.activator.end();  tapPointerMoved = false;}function tapMouseMove(e) {  if (tapHasPointerMoved(e)) {    tapEventListener('mousemove', false);    ionic.activator.end();    tapPointerMoved = true;    return false;  }}// TOUCHfunction tapTouchStart(e) {  //console.log("touchstart " + Date.now());  if (tapIgnoreEvent(e)) return;  tapPointerMoved = false;  tapEnableTouchEvents();  tapPointerStart = ionic.tap.pointerCoord(e);  tapEventListener(tapTouchMoveListener);  ionic.activator.start(e);  if (ionic.Platform.isIOS() && ionic.tap.isLabelWithTextInput(e.target)) {    // if the tapped element is a label, which has a child input    // then preventDefault so iOS doesn't ugly auto scroll to the input    // but do not prevent default on Android or else you cannot move the text caret    // and do not prevent default on Android or else no virtual keyboard shows up    var textInput = tapTargetElement(tapContainingElement(e.target));    if (textInput !== tapActiveEle) {      // don't preventDefault on an already focused input or else iOS's text caret isn't usable      //console.log('Would prevent default here');      e.preventDefault();    }  }}function tapTouchEnd(e) {  //console.log('touchend ' + Date.now());  if (tapIgnoreEvent(e)) return;  tapEnableTouchEvents();  if (!tapHasPointerMoved(e)) {    tapClick(e);    if (isSelectOrOption(e.target.tagName)) {      e.preventDefault();    }  }  tapLastTouchTarget = e.target;  tapTouchCancel();}function tapTouchMove(e) {  if (tapHasPointerMoved(e)) {    tapPointerMoved = true;    tapEventListener(tapTouchMoveListener, false);    ionic.activator.end();    return false;  }}function tapTouchCancel() {  tapEventListener(tapTouchMoveListener, false);  ionic.activator.end();  tapPointerMoved = false;}function tapEnableTouchEvents() {  tapEnabledTouchEvents = true;  clearTimeout(tapMouseResetTimer);  tapMouseResetTimer = setTimeout(function() {    tapEnabledTouchEvents = false;  }, 600);}function tapIgnoreEvent(e) {  if (e.isTapHandled) return true;  e.isTapHandled = true;  if(ionic.tap.isElementTapDisabled(e.target)) {    return true;  }  if (ionic.scroll.isScrolling && ionic.tap.containsOrIsTextInput(e.target)) {    e.preventDefault();    return true;  }}function tapHandleFocus(ele) {  tapTouchFocusedInput = null;  var triggerFocusIn = false;  if (ele.tagName == 'SELECT') {    // trick to force Android options to show up    triggerMouseEvent('mousedown', ele, 0, 0);    ele.focus && ele.focus();    triggerFocusIn = true;  } else if (tapActiveElement() === ele) {    // already is the active element and has focus    triggerFocusIn = true;  } else if ((/^(input|textarea|ion-label)$/i).test(ele.tagName) || ele.isContentEditable) {    triggerFocusIn = true;    ele.focus && ele.focus();    ele.value = ele.value;    if (tapEnabledTouchEvents) {      tapTouchFocusedInput = ele;    }  } else {    tapFocusOutActive();  }  if (triggerFocusIn) {    tapActiveElement(ele);    ionic.trigger('ionic.focusin', {      target: ele    }, true);  }}function tapFocusOutActive() {  var ele = tapActiveElement();  if (ele && ((/^(input|textarea|select)$/i).test(ele.tagName) || ele.isContentEditable)) {    //console.log('tapFocusOutActive', ele.tagName);    ele.blur();  }  tapActiveElement(null);}function tapFocusIn(e) {  //console.log('focusin ' + Date.now());  // Because a text input doesn't preventDefault (so the caret still works) there's a chance  // that its mousedown event 300ms later will change the focus to another element after  // the keyboard shows up.  if (tapEnabledTouchEvents &&      ionic.tap.isTextInput(tapActiveElement()) &&      ionic.tap.isTextInput(tapTouchFocusedInput) &&      tapTouchFocusedInput !== e.target) {    // 1) The pointer is from touch events    // 2) There is an active element which is a text input    // 3) A text input was just set to be focused on by a touch event    // 4) A new focus has been set, however the target isn't the one the touch event wanted    //console.log('focusin', 'tapTouchFocusedInput');    tapTouchFocusedInput.focus();    tapTouchFocusedInput = null;  }  ionic.scroll.isScrolling = false;}function tapFocusOut() {  //console.log("focusout");  tapActiveElement(null);}function tapActiveElement(ele) {  if (arguments.length) {    tapActiveEle = ele;  }  return tapActiveEle || document.activeElement;}function tapHasPointerMoved(endEvent) {  if (!endEvent || endEvent.target.nodeType !== 1 || !tapPointerStart || (tapPointerStart.x === 0 && tapPointerStart.y === 0)) {    return false;  }  var endCoordinates = ionic.tap.pointerCoord(endEvent);  var hasClassList = !!(endEvent.target.classList && endEvent.target.classList.contains &&    typeof endEvent.target.classList.contains === 'function');  var releaseTolerance = hasClassList && endEvent.target.classList.contains('button') ?    TAP_RELEASE_BUTTON_TOLERANCE :    TAP_RELEASE_TOLERANCE;  return Math.abs(tapPointerStart.x - endCoordinates.x) > releaseTolerance ||         Math.abs(tapPointerStart.y - endCoordinates.y) > releaseTolerance;}function tapContainingElement(ele, allowSelf) {  var climbEle = ele;  for (var x = 0; x < 6; x++) {    if (!climbEle) break;    if (climbEle.tagName === 'LABEL') return climbEle;    climbEle = climbEle.parentElement;  }  if (allowSelf !== false) return ele;}function tapTargetElement(ele) {  if (ele && ele.tagName === 'LABEL') {    if (ele.control) return ele.control;    // older devices do not support the "control" property    if (ele.querySelector) {      var control = ele.querySelector('input,textarea,select');      if (control) return control;    }  }  return ele;}function isSelectOrOption(tagName){  return (/^(select|option)$/i).test(tagName);}ionic.DomUtil.ready(function() {  var ng = typeof angular !== 'undefined' ? angular : null;  //do nothing for e2e tests  if (!ng || (ng && !ng.scenario)) {    ionic.tap.register(document);  }});(function(document, ionic) {  'use strict';  var queueElements = {};   // elements that should get an active state in XX milliseconds  var activeElements = {};  // elements that are currently active  var keyId = 0;            // a counter for unique keys for the above ojects  var ACTIVATED_CLASS = 'activated';  ionic.activator = {    start: function(e) {      var hitX = ionic.tap.pointerCoord(e).x;      if (hitX > 0 && hitX < 30) {        return;      }      // when an element is touched/clicked, it climbs up a few      // parents to see if it is an .item or .button element      ionic.requestAnimationFrame(function() {        if ((ionic.scroll && ionic.scroll.isScrolling) || ionic.tap.requiresNativeClick(e.target)) return;        var ele = e.target;        var eleToActivate;        for (var x = 0; x < 6; x++) {          if (!ele || ele.nodeType !== 1) break;          if (eleToActivate && ele.classList && ele.classList.contains('item')) {            eleToActivate = ele;            break;          }          if (ele.tagName == 'A' || ele.tagName == 'BUTTON' || ele.hasAttribute('ng-click')) {            eleToActivate = ele;            break;          }          if (ele.classList && ele.classList.contains('button')) {            eleToActivate = ele;            break;          }          // no sense climbing past these          if (ele.tagName == 'ION-CONTENT' || (ele.classList && ele.classList.contains('pane')) || ele.tagName == 'BODY') {            break;          }          ele = ele.parentElement;        }        if (eleToActivate) {          // queue that this element should be set to active          queueElements[keyId] = eleToActivate;          // on the next frame, set the queued elements to active          ionic.requestAnimationFrame(activateElements);          keyId = (keyId > 29 ? 0 : keyId + 1);        }      });    },    end: function() {      // clear out any active/queued elements after XX milliseconds      setTimeout(clear, 200);    }  };  function clear() {    // clear out any elements that are queued to be set to active    queueElements = {};    // in the next frame, remove the active class from all active elements    ionic.requestAnimationFrame(deactivateElements);  }  function activateElements() {    // activate all elements in the queue    for (var key in queueElements) {      if (queueElements[key]) {        queueElements[key].classList.add(ACTIVATED_CLASS);        activeElements[key] = queueElements[key];      }    }    queueElements = {};  }  function deactivateElements() {    if (ionic.transition && ionic.transition.isActive) {      setTimeout(deactivateElements, 400);      return;    }    for (var key in activeElements) {      if (activeElements[key]) {        activeElements[key].classList.remove(ACTIVATED_CLASS);        delete activeElements[key];      }    }  }})(document, ionic);(function(ionic) {  /* for nextUid function below */  var nextId = 0;  /**   * Various utilities used throughout Ionic   *   * Some of these are adopted from underscore.js and backbone.js, both also MIT licensed.   */  ionic.Utils = {    arrayMove: function(arr, oldIndex, newIndex) {      if (newIndex >= arr.length) {        var k = newIndex - arr.length;        while ((k--) + 1) {          arr.push(undefined);        }      }      arr.splice(newIndex, 0, arr.splice(oldIndex, 1)[0]);      return arr;    },    /**     * Return a function that will be called with the given context     */    proxy: function(func, context) {      var args = Array.prototype.slice.call(arguments, 2);      return function() {        return func.apply(context, args.concat(Array.prototype.slice.call(arguments)));      };    },    /**     * Only call a function once in the given interval.     *     * @param func {Function} the function to call     * @param wait {int} how long to wait before/after to allow function calls     * @param immediate {boolean} whether to call immediately or after the wait interval     */     debounce: function(func, wait, immediate) {      var timeout, args, context, timestamp, result;      return function() {        context = this;        args = arguments;        timestamp = new Date();        var later = function() {          var last = (new Date()) - timestamp;          if (last < wait) {            timeout = setTimeout(later, wait - last);          } else {            timeout = null;            if (!immediate) result = func.apply(context, args);          }        };        var callNow = immediate && !timeout;        if (!timeout) {          timeout = setTimeout(later, wait);        }        if (callNow) result = func.apply(context, args);        return result;      };    },    /**     * Throttle the given fun, only allowing it to be     * called at most every `wait` ms.     */    throttle: function(func, wait, options) {      var context, args, result;      var timeout = null;      var previous = 0;      options || (options = {});      var later = function() {        previous = options.leading === false ? 0 : Date.now();        timeout = null;        result = func.apply(context, args);      };      return function() {        var now = Date.now();        if (!previous && options.leading === false) previous = now;        var remaining = wait - (now - previous);        context = this;        args = arguments;        if (remaining <= 0) {          clearTimeout(timeout);          timeout = null;          previous = now;          result = func.apply(context, args);        } else if (!timeout && options.trailing !== false) {          timeout = setTimeout(later, remaining);        }        return result;      };    },     // Borrowed from Backbone.js's extend     // Helper function to correctly set up the prototype chain, for subclasses.     // Similar to `goog.inherits`, but uses a hash of prototype properties and     // class properties to be extended.    inherit: function(protoProps, staticProps) {      var parent = this;      var child;      // The constructor function for the new subclass is either defined by you      // (the "constructor" property in your `extend` definition), or defaulted      // by us to simply call the parent's constructor.      if (protoProps && protoProps.hasOwnProperty('constructor')) {        child = protoProps.constructor;      } else {        child = function() { return parent.apply(this, arguments); };      }      // Add static properties to the constructor function, if supplied.      ionic.extend(child, parent, staticProps);      // Set the prototype chain to inherit from `parent`, without calling      // `parent`'s constructor function.      var Surrogate = function() { this.constructor = child; };      Surrogate.prototype = parent.prototype;      child.prototype = new Surrogate();      // Add prototype properties (instance properties) to the subclass,      // if supplied.      if (protoProps) ionic.extend(child.prototype, protoProps);      // Set a convenience property in case the parent's prototype is needed      // later.      child.__super__ = parent.prototype;      return child;    },    // Extend adapted from Underscore.js    extend: function(obj) {       var args = Array.prototype.slice.call(arguments, 1);       for (var i = 0; i < args.length; i++) {         var source = args[i];         if (source) {           for (var prop in source) {             obj[prop] = source[prop];           }         }       }       return obj;    },    nextUid: function() {      return 'ion' + (nextId++);    },    disconnectScope: function disconnectScope(scope) {      if (!scope) return;      if (scope.$root === scope) {        return; // we can't disconnect the root node;      }      var parent = scope.$parent;      scope.$$disconnected = true;      scope.$broadcast('$ionic.disconnectScope', scope);      // See Scope.$destroy      if (parent.$$childHead === scope) {        parent.$$childHead = scope.$$nextSibling;      }      if (parent.$$childTail === scope) {        parent.$$childTail = scope.$$prevSibling;      }      if (scope.$$prevSibling) {        scope.$$prevSibling.$$nextSibling = scope.$$nextSibling;      }      if (scope.$$nextSibling) {        scope.$$nextSibling.$$prevSibling = scope.$$prevSibling;      }      scope.$$nextSibling = scope.$$prevSibling = null;    },    reconnectScope: function reconnectScope(scope) {      if (!scope) return;      if (scope.$root === scope) {        return; // we can't disconnect the root node;      }      if (!scope.$$disconnected) {        return;      }      var parent = scope.$parent;      scope.$$disconnected = false;      scope.$broadcast('$ionic.reconnectScope', scope);      // See Scope.$new for this logic...      scope.$$prevSibling = parent.$$childTail;      if (parent.$$childHead) {        parent.$$childTail.$$nextSibling = scope;        parent.$$childTail = scope;      } else {        parent.$$childHead = parent.$$childTail = scope;      }    },    isScopeDisconnected: function(scope) {      var climbScope = scope;      while (climbScope) {        if (climbScope.$$disconnected) return true;        climbScope = climbScope.$parent;      }      return false;    }  };  // Bind a few of the most useful functions to the ionic scope  ionic.inherit = ionic.Utils.inherit;  ionic.extend = ionic.Utils.extend;  ionic.throttle = ionic.Utils.throttle;  ionic.proxy = ionic.Utils.proxy;  ionic.debounce = ionic.Utils.debounce;})(window.ionic);/** * @ngdoc page * @name keyboard * @module ionic * @description * On both Android and iOS, Ionic will attempt to prevent the keyboard from * obscuring inputs and focusable elements when it appears by scrolling them * into view.  In order for this to work, any focusable elements must be within * a [Scroll View](http://ionicframework.com/docs/api/directive/ionScroll/) * or a directive such as [Content](http://ionicframework.com/docs/api/directive/ionContent/) * that has a Scroll View. * * It will also attempt to prevent the native overflow scrolling on focus, * which can cause layout issues such as pushing headers up and out of view. * * The keyboard fixes work best in conjunction with the * [Ionic Keyboard Plugin](https://github.com/driftyco/ionic-plugins-keyboard), * although it will perform reasonably well without.  However, if you are using * Cordova there is no reason not to use the plugin. * * ### Hide when keyboard shows * * To hide an element when the keyboard is open, add the class `hide-on-keyboard-open`. * * ```html * <div class="hide-on-keyboard-open"> *   <div id="google-map"></div> * </div> * ``` * * Note: For performance reasons, elements will not be hidden for 400ms after the start of the `native.keyboardshow` event * from the Ionic Keyboard plugin. If you would like them to disappear immediately, you could do something * like: * * ```js *   window.addEventListener('native.keyboardshow', function(){ *     document.body.classList.add('keyboard-open'); *   }); * ``` * This adds the same `keyboard-open` class that is normally added by Ionic 400ms after the keyboard * opens. However, bear in mind that adding this class to the body immediately may cause jank in any * animations on Android that occur when the keyboard opens (for example, scrolling any obscured inputs into view). * * ---------- * * ### Plugin Usage * Information on using the plugin can be found at * [https://github.com/driftyco/ionic-plugins-keyboard](https://github.com/driftyco/ionic-plugins-keyboard). * * ---------- * * ### Android Notes * - If your app is running in fullscreen, i.e. you have *   `<preference name="Fullscreen" value="true" />` in your `config.xml` file *   you will need to set `ionic.Platform.isFullScreen = true` manually. * * - You can configure the behavior of the web view when the keyboard shows by setting *   [android:windowSoftInputMode](http://developer.android.com/reference/android/R.attr.html#windowSoftInputMode) *   to either `adjustPan`, `adjustResize` or `adjustNothing` in your app's *   activity in `AndroidManifest.xml`. `adjustResize` is the recommended setting *   for Ionic, but if for some reason you do use `adjustPan` you will need to *   set `ionic.Platform.isFullScreen = true`. * *   ```xml *   <activity android:windowSoftInputMode="adjustResize"> * *   ``` * * ### iOS Notes * - If the content of your app (including the header) is being pushed up and *   out of view on input focus, try setting `cordova.plugins.Keyboard.disableScroll(true)`. *   This does **not** disable scrolling in the Ionic scroll view, rather it *   disables the native overflow scrolling that happens automatically as a *   result of focusing on inputs below the keyboard. * *//** * The current viewport height. */var keyboardCurrentViewportHeight = 0;/** * The viewport height when in portrait orientation. */var keyboardPortraitViewportHeight = 0;/** * The viewport height when in landscape orientation. */var keyboardLandscapeViewportHeight = 0;/** * The currently focused input. */var keyboardActiveElement;/** * The previously focused input used to reset keyboard after focusing on a * new non-keyboard element */var lastKeyboardActiveElement;/** * The scroll view containing the currently focused input. */var scrollView;/** * Timer for the setInterval that polls window.innerHeight to determine whether * the layout has updated for the keyboard showing/hiding. */var waitForResizeTimer;/** * Sometimes when switching inputs or orientations, focusout will fire before * focusin, so this timer is for the small setTimeout to determine if we should * really focusout/hide the keyboard. */var keyboardFocusOutTimer;/** * on Android, orientationchange will fire before the keyboard plugin notifies * the browser that the keyboard will show/is showing, so this flag indicates * to nativeShow that there was an orientationChange and we should update * the viewport height with an accurate keyboard height value */var wasOrientationChange = false;/** * CSS class added to the body indicating the keyboard is open. */var KEYBOARD_OPEN_CSS = 'keyboard-open';/** * CSS class that indicates a scroll container. */var SCROLL_CONTAINER_CSS = 'scroll-content';/** * Debounced keyboardFocusIn function */var debouncedKeyboardFocusIn = ionic.debounce(keyboardFocusIn, 200, true);/** * Debounced keyboardNativeShow function */var debouncedKeyboardNativeShow = ionic.debounce(keyboardNativeShow, 100, true);/** * Ionic keyboard namespace. * @namespace keyboard */ionic.keyboard = {  /**   * Whether the keyboard is open or not.   */  isOpen: false,  /**   * Whether the keyboard is closing or not.   */  isClosing: false,  /**   * Whether the keyboard is opening or not.   */  isOpening: false,  /**   * The height of the keyboard in pixels, as reported by the keyboard plugin.   * If the plugin is not available, calculated as the difference in   * window.innerHeight after the keyboard has shown.   */  height: 0,  /**   * Whether the device is in landscape orientation or not.   */  isLandscape: false,  /**   * Whether the keyboard event listeners have been added or not   */  isInitialized: false,  /**   * Hide the keyboard, if it is open.   */  hide: function() {    if (keyboardHasPlugin()) {      cordova.plugins.Keyboard.close();    }    keyboardActiveElement && keyboardActiveElement.blur();  },  /**   * An alias for cordova.plugins.Keyboard.show(). If the keyboard plugin   * is installed, show the keyboard.   */  show: function() {    if (keyboardHasPlugin()) {      cordova.plugins.Keyboard.show();    }  },  /**   * Remove all keyboard related event listeners, effectively disabling Ionic's   * keyboard adjustments.   */  disable: function() {    if (keyboardHasPlugin()) {      window.removeEventListener('native.keyboardshow', debouncedKeyboardNativeShow );      window.removeEventListener('native.keyboardhide', keyboardFocusOut);    } else {      document.body.removeEventListener('focusout', keyboardFocusOut);    }    document.body.removeEventListener('ionic.focusin', debouncedKeyboardFocusIn);    document.body.removeEventListener('focusin', debouncedKeyboardFocusIn);    window.removeEventListener('orientationchange', keyboardOrientationChange);    if ( window.navigator.msPointerEnabled ) {      document.removeEventListener("MSPointerDown", keyboardInit);    } else {      document.removeEventListener('touchstart', keyboardInit);    }    ionic.keyboard.isInitialized = false;  },  /**   * Alias for keyboardInit, initialize all keyboard related event listeners.   */  enable: function() {    keyboardInit();  }};// Initialize the viewport height (after ionic.keyboard.height has been// defined).keyboardCurrentViewportHeight = getViewportHeight();                             /* Event handlers *//* ------------------------------------------------------------------------- *//** * Event handler for first touch event, initializes all event listeners * for keyboard related events. Also aliased by ionic.keyboard.enable. */function keyboardInit() {  if (ionic.keyboard.isInitialized) return;  if (keyboardHasPlugin()) {    window.addEventListener('native.keyboardshow', debouncedKeyboardNativeShow);    window.addEventListener('native.keyboardhide', keyboardFocusOut);  } else {    document.body.addEventListener('focusout', keyboardFocusOut);  }  document.body.addEventListener('ionic.focusin', debouncedKeyboardFocusIn);  document.body.addEventListener('focusin', debouncedKeyboardFocusIn);  if (window.navigator.msPointerEnabled) {    document.removeEventListener("MSPointerDown", keyboardInit);  } else {    document.removeEventListener('touchstart', keyboardInit);  }  ionic.keyboard.isInitialized = true;}/** * Event handler for 'native.keyboardshow' event, sets keyboard.height to the * reported height and keyboard.isOpening to true. Then calls * keyboardWaitForResize with keyboardShow or keyboardUpdateViewportHeight as * the callback depending on whether the event was triggered by a focusin or * an orientationchange. */function keyboardNativeShow(e) {  clearTimeout(keyboardFocusOutTimer);  //console.log("keyboardNativeShow fired at: " + Date.now());  //console.log("keyboardNativeshow window.innerHeight: " + window.innerHeight);  if (!ionic.keyboard.isOpen || ionic.keyboard.isClosing) {    ionic.keyboard.isOpening = true;    ionic.keyboard.isClosing = false;  }  ionic.keyboard.height = e.keyboardHeight;  //console.log('nativeshow keyboard height:' + e.keyboardHeight);  if (wasOrientationChange) {    keyboardWaitForResize(keyboardUpdateViewportHeight, true);  } else {    keyboardWaitForResize(keyboardShow, true);  }}/** * Event handler for 'focusin' and 'ionic.focusin' events. Initializes * keyboard state (keyboardActiveElement and keyboard.isOpening) for the * appropriate adjustments once the window has resized.  If not using the * keyboard plugin, calls keyboardWaitForResize with keyboardShow as the * callback or keyboardShow right away if the keyboard is already open.  If * using the keyboard plugin does nothing and lets keyboardNativeShow handle * adjustments with a more accurate keyboard height. */function keyboardFocusIn(e) {  clearTimeout(keyboardFocusOutTimer);  //console.log("keyboardFocusIn from: " + e.type + " at: " + Date.now());  if (!e.target ||      e.target.readOnly ||      !ionic.tap.isKeyboardElement(e.target) ||      !(scrollView = ionic.DomUtil.getParentWithClass(e.target, SCROLL_CONTAINER_CSS))) {    if (keyboardActiveElement) {        lastKeyboardActiveElement = keyboardActiveElement;    }    keyboardActiveElement = null;    return;  }  keyboardActiveElement = e.target;  // if using JS scrolling, undo the effects of native overflow scroll so the  // scroll view is positioned correctly  if (!scrollView.classList.contains("overflow-scroll")) {    document.body.scrollTop = 0;    scrollView.scrollTop = 0;    ionic.requestAnimationFrame(function(){      document.body.scrollTop = 0;      scrollView.scrollTop = 0;    });    // any showing part of the document that isn't within the scroll the user    // could touchmove and cause some ugly changes to the app, so disable    // any touchmove events while the keyboard is open using e.preventDefault()    if (window.navigator.msPointerEnabled) {      document.addEventListener("MSPointerMove", keyboardPreventDefault, false);    } else {      document.addEventListener('touchmove', keyboardPreventDefault, false);    }  }  if (!ionic.keyboard.isOpen || ionic.keyboard.isClosing) {    ionic.keyboard.isOpening = true;    ionic.keyboard.isClosing = false;  }  // attempt to prevent browser from natively scrolling input into view while  // we are trying to do the same (while we are scrolling) if the user taps the  // keyboard  document.addEventListener('keydown', keyboardOnKeyDown, false);  // if we aren't using the plugin and the keyboard isn't open yet, wait for the  // window to resize so we can get an accurate estimate of the keyboard size,  // otherwise we do nothing and let nativeShow call keyboardShow once we have  // an exact keyboard height  // if the keyboard is already open, go ahead and scroll the input into view  // if necessary  if (!ionic.keyboard.isOpen && !keyboardHasPlugin()) {    keyboardWaitForResize(keyboardShow, true);  } else if (ionic.keyboard.isOpen) {    keyboardShow();  }}/** * Event handler for 'focusout' events. Sets keyboard.isClosing to true and * calls keyboardWaitForResize with keyboardHide as the callback after a small * timeout. */function keyboardFocusOut() {  clearTimeout(keyboardFocusOutTimer);  //console.log("keyboardFocusOut fired at: " + Date.now());  //console.log("keyboardFocusOut event type: " + e.type);  if (ionic.keyboard.isOpen || ionic.keyboard.isOpening) {    ionic.keyboard.isClosing = true;    ionic.keyboard.isOpening = false;  }  // Call keyboardHide with a slight delay because sometimes on focus or  // orientation change focusin is called immediately after, so we give it time  // to cancel keyboardHide  keyboardFocusOutTimer = setTimeout(function() {    ionic.requestAnimationFrame(function() {      // focusOut during or right after an orientationchange, so we didn't get      // a chance to update the viewport height yet, do it and keyboardHide      //console.log("focusOut, wasOrientationChange: " + wasOrientationChange);      if (wasOrientationChange) {        keyboardWaitForResize(function(){          keyboardUpdateViewportHeight();          keyboardHide();        }, false);      } else {        keyboardWaitForResize(keyboardHide, false);      }    });  }, 50);}/** * Event handler for 'orientationchange' events. If using the keyboard plugin * and the keyboard is open on Android, sets wasOrientationChange to true so * nativeShow can update the viewport height with an accurate keyboard height. * If the keyboard isn't open or keyboard plugin isn't being used, * waits for the window to resize before updating the viewport height. * * On iOS, where orientationchange fires after the keyboard has already shown, * updates the viewport immediately, regardless of if the keyboard is already * open. */function keyboardOrientationChange() {  //console.log("orientationchange fired at: " + Date.now());  //console.log("orientation was: " + (ionic.keyboard.isLandscape ? "landscape" : "portrait"));  // toggle orientation  ionic.keyboard.isLandscape = !ionic.keyboard.isLandscape;  // //console.log("now orientation is: " + (ionic.keyboard.isLandscape ? "landscape" : "portrait"));  // no need to wait for resizing on iOS, and orientationchange always fires  // after the keyboard has opened, so it doesn't matter if it's open or not  if (ionic.Platform.isIOS()) {    keyboardUpdateViewportHeight();  }  // On Android, if the keyboard isn't open or we aren't using the keyboard  // plugin, update the viewport height once everything has resized. If the  // keyboard is open and we are using the keyboard plugin do nothing and let  // nativeShow handle it using an accurate keyboard height.  if ( ionic.Platform.isAndroid()) {    if (!ionic.keyboard.isOpen || !keyboardHasPlugin()) {      keyboardWaitForResize(keyboardUpdateViewportHeight, false);    } else {      wasOrientationChange = true;    }  }}/** * Event handler for 'keydown' event. Tries to prevent browser from natively * scrolling an input into view when a user taps the keyboard while we are * scrolling the input into view ourselves with JS. */function keyboardOnKeyDown(e) {  if (ionic.scroll.isScrolling) {    keyboardPreventDefault(e);  }}/** * Event for 'touchmove' or 'MSPointerMove'. Prevents native scrolling on * elements outside the scroll view while the keyboard is open. */function keyboardPreventDefault(e) {  if (e.target.tagName !== 'TEXTAREA') {    e.preventDefault();  }}                              /* Private API *//* -------------------------------------------------------------------------- *//** * Polls window.innerHeight until it has updated to an expected value (or * sufficient time has passed) before calling the specified callback function. * Only necessary for non-fullscreen Android which sometimes reports multiple * window.innerHeight values during interim layouts while it is resizing. * * On iOS, the window.innerHeight will already be updated, but we use the 50ms * delay as essentially a timeout so that scroll view adjustments happen after * the keyboard has shown so there isn't a white flash from us resizing too * quickly. * * @param {Function} callback the function to call once the window has resized * @param {boolean} isOpening whether the resize is from the keyboard opening * or not */function keyboardWaitForResize(callback, isOpening) {  clearInterval(waitForResizeTimer);  var count = 0;  var maxCount;  var initialHeight = getViewportHeight();  var viewportHeight = initialHeight;  //console.log("waitForResize initial viewport height: " + viewportHeight);  //var start = Date.now();  //console.log("start: " + start);  // want to fail relatively quickly on modern android devices, since it's much  // more likely we just have a bad keyboard height  if (ionic.Platform.isAndroid() && ionic.Platform.version() < 4.4) {    maxCount = 30;  } else if (ionic.Platform.isAndroid()) {    maxCount = 10;  } else {    maxCount = 1;  }  // poll timer  waitForResizeTimer = setInterval(function(){    viewportHeight = getViewportHeight();    // height hasn't updated yet, try again in 50ms    // if not using plugin, wait for maxCount to ensure we have waited long enough    // to get an accurate keyboard height    if (++count < maxCount &&        ((!isPortraitViewportHeight(viewportHeight) &&         !isLandscapeViewportHeight(viewportHeight)) ||         !ionic.keyboard.height)) {      return;    }    // infer the keyboard height from the resize if not using the keyboard plugin    if (!keyboardHasPlugin()) {      ionic.keyboard.height = Math.abs(initialHeight - window.innerHeight);    }    // set to true if we were waiting for the keyboard to open    ionic.keyboard.isOpen = isOpening;    clearInterval(waitForResizeTimer);    //var end = Date.now();    //console.log("waitForResize count: " + count);    //console.log("end: " + end);    //console.log("difference: " + ( end - start ) + "ms");    //console.log("callback: " + callback.name);    callback();  }, 50);  return maxCount; //for tests}/** * On keyboard close sets keyboard state to closed, resets the scroll view, * removes CSS from body indicating keyboard was open, removes any event * listeners for when the keyboard is open and on Android blurs the active * element (which in some cases will still have focus even if the keyboard * is closed and can cause it to reappear on subsequent taps). */function keyboardHide() {  clearTimeout(keyboardFocusOutTimer);  //console.log("keyboardHide");  ionic.keyboard.isOpen = false;  ionic.keyboard.isClosing = false;  if (keyboardActiveElement || lastKeyboardActiveElement) {    ionic.trigger('resetScrollView', {      target: keyboardActiveElement || lastKeyboardActiveElement    }, true);  }  ionic.requestAnimationFrame(function(){    document.body.classList.remove(KEYBOARD_OPEN_CSS);  });  // the keyboard is gone now, remove the touchmove that disables native scroll  if (window.navigator.msPointerEnabled) {    document.removeEventListener("MSPointerMove", keyboardPreventDefault);  } else {    document.removeEventListener('touchmove', keyboardPreventDefault);  }  document.removeEventListener('keydown', keyboardOnKeyDown);  if (ionic.Platform.isAndroid()) {    // on android closing the keyboard with the back/dismiss button won't remove    // focus and keyboard can re-appear on subsequent taps (like scrolling)    if (keyboardHasPlugin()) cordova.plugins.Keyboard.close();    keyboardActiveElement && keyboardActiveElement.blur();  }  keyboardActiveElement = null;  lastKeyboardActiveElement = null;}/** * On keyboard open sets keyboard state to open, adds CSS to the body * indicating the keyboard is open and tells the scroll view to resize and * the currently focused input into view if necessary. */function keyboardShow() {  ionic.keyboard.isOpen = true;  ionic.keyboard.isOpening = false;  var details = {    keyboardHeight: keyboardGetHeight(),    viewportHeight: keyboardCurrentViewportHeight  };  if (keyboardActiveElement) {    details.target = keyboardActiveElement;    var elementBounds = keyboardActiveElement.getBoundingClientRect();    details.elementTop = Math.round(elementBounds.top);    details.elementBottom = Math.round(elementBounds.bottom);    details.windowHeight = details.viewportHeight - details.keyboardHeight;    //console.log("keyboardShow viewportHeight: " + details.viewportHeight +    //", windowHeight: " + details.windowHeight +    //", keyboardHeight: " + details.keyboardHeight);    // figure out if the element is under the keyboard    details.isElementUnderKeyboard = (details.elementBottom > details.windowHeight);    //console.log("isUnderKeyboard: " + details.isElementUnderKeyboard);    //console.log("elementBottom: " + details.elementBottom);    // send event so the scroll view adjusts    ionic.trigger('scrollChildIntoView', details, true);  }  setTimeout(function(){    document.body.classList.add(KEYBOARD_OPEN_CSS);  }, 400);  return details; //for testing}/* eslint no-unused-vars:0 */function keyboardGetHeight() {  // check if we already have a keyboard height from the plugin or resize calculations  if (ionic.keyboard.height) {    return ionic.keyboard.height;  }  if (ionic.Platform.isAndroid()) {    // should be using the plugin, no way to know how big the keyboard is, so guess    if ( ionic.Platform.isFullScreen ) {      return 275;    }    // otherwise just calculate it    var contentHeight = window.innerHeight;    if (contentHeight < keyboardCurrentViewportHeight) {      return keyboardCurrentViewportHeight - contentHeight;    } else {      return 0;    }  }  // fallback for when it's the webview without the plugin  // or for just the standard web browser  // TODO: have these be based on device  if (ionic.Platform.isIOS()) {    if (ionic.keyboard.isLandscape) {      return 206;    }    if (!ionic.Platform.isWebView()) {      return 216;    }    return 260;  }  // safe guess  return 275;}function isPortraitViewportHeight(viewportHeight) {  return !!(!ionic.keyboard.isLandscape &&         keyboardPortraitViewportHeight &&         (Math.abs(keyboardPortraitViewportHeight - viewportHeight) < 2));}function isLandscapeViewportHeight(viewportHeight) {  return !!(ionic.keyboard.isLandscape &&         keyboardLandscapeViewportHeight &&         (Math.abs(keyboardLandscapeViewportHeight - viewportHeight) < 2));}function keyboardUpdateViewportHeight() {  wasOrientationChange = false;  keyboardCurrentViewportHeight = getViewportHeight();  if (ionic.keyboard.isLandscape && !keyboardLandscapeViewportHeight) {    //console.log("saved landscape: " + keyboardCurrentViewportHeight);    keyboardLandscapeViewportHeight = keyboardCurrentViewportHeight;  } else if (!ionic.keyboard.isLandscape && !keyboardPortraitViewportHeight) {    //console.log("saved portrait: " + keyboardCurrentViewportHeight);    keyboardPortraitViewportHeight = keyboardCurrentViewportHeight;  }  if (keyboardActiveElement) {    ionic.trigger('resetScrollView', {      target: keyboardActiveElement    }, true);  }  if (ionic.keyboard.isOpen && ionic.tap.isTextInput(keyboardActiveElement)) {    keyboardShow();  }}function keyboardInitViewportHeight() {  var viewportHeight = getViewportHeight();  //console.log("Keyboard init VP: " + viewportHeight + " " + window.innerWidth);  // can't just use window.innerHeight in case the keyboard is opened immediately  if ((viewportHeight / window.innerWidth) < 1) {    ionic.keyboard.isLandscape = true;  }  //console.log("ionic.keyboard.isLandscape is: " + ionic.keyboard.isLandscape);  // initialize or update the current viewport height values  keyboardCurrentViewportHeight = viewportHeight;  if (ionic.keyboard.isLandscape && !keyboardLandscapeViewportHeight) {    keyboardLandscapeViewportHeight = keyboardCurrentViewportHeight;  } else if (!ionic.keyboard.isLandscape && !keyboardPortraitViewportHeight) {    keyboardPortraitViewportHeight = keyboardCurrentViewportHeight;  }}function getViewportHeight() {  var windowHeight = window.innerHeight;  //console.log('window.innerHeight is: ' + windowHeight);  //console.log('kb height is: ' + ionic.keyboard.height);  //console.log('kb isOpen: ' + ionic.keyboard.isOpen);  //TODO: add iPad undocked/split kb once kb plugin supports it  // the keyboard overlays the window on Android fullscreen  if (!(ionic.Platform.isAndroid() && ionic.Platform.isFullScreen) &&      (ionic.keyboard.isOpen || ionic.keyboard.isOpening) &&      !ionic.keyboard.isClosing) {     return windowHeight + keyboardGetHeight();  }  return windowHeight;}function keyboardHasPlugin() {  return !!(window.cordova && cordova.plugins && cordova.plugins.Keyboard);}ionic.Platform.ready(function() {  keyboardInitViewportHeight();  window.addEventListener('orientationchange', keyboardOrientationChange);  // if orientation changes while app is in background, update on resuming  /*  if ( ionic.Platform.isWebView() ) {    document.addEventListener('resume', keyboardInitViewportHeight);    if (ionic.Platform.isAndroid()) {      //TODO: onbackpressed to detect keyboard close without focusout or plugin    }  }  */  // if orientation changes while app is in background, update on resuming/*  if ( ionic.Platform.isWebView() ) {    document.addEventListener('pause', function() {      window.removeEventListener('orientationchange', keyboardOrientationChange);    })    document.addEventListener('resume', function() {      keyboardInitViewportHeight();      window.addEventListener('orientationchange', keyboardOrientationChange)    });  }*/  // Android sometimes reports bad innerHeight on window.load  // try it again in a lil bit to play it safe  setTimeout(keyboardInitViewportHeight, 999);  // only initialize the adjustments for the virtual keyboard  // if a touchstart event happens  if (window.navigator.msPointerEnabled) {    document.addEventListener("MSPointerDown", keyboardInit, false);  } else {    document.addEventListener('touchstart', keyboardInit, false);  }});var viewportTag;var viewportProperties = {};ionic.viewport = {  orientation: function() {    // 0 = Portrait    // 90 = Landscape    // not using window.orientation because each device has a different implementation    return (window.innerWidth > window.innerHeight ? 90 : 0);  }};function viewportLoadTag() {  var x;  for (x = 0; x < document.head.children.length; x++) {    if (document.head.children[x].name == 'viewport') {      viewportTag = document.head.children[x];      break;    }  }  if (viewportTag) {    var props = viewportTag.content.toLowerCase().replace(/\s+/g, '').split(',');    var keyValue;    for (x = 0; x < props.length; x++) {      if (props[x]) {        keyValue = props[x].split('=');        viewportProperties[ keyValue[0] ] = (keyValue.length > 1 ? keyValue[1] : '_');      }    }    viewportUpdate();  }}function viewportUpdate() {  // unit tests in viewport.unit.js  var initWidth = viewportProperties.width;  var initHeight = viewportProperties.height;  var p = ionic.Platform;  var version = p.version();  var DEVICE_WIDTH = 'device-width';  var DEVICE_HEIGHT = 'device-height';  var orientation = ionic.viewport.orientation();  // Most times we're removing the height and adding the width  // So this is the default to start with, then modify per platform/version/oreintation  delete viewportProperties.height;  viewportProperties.width = DEVICE_WIDTH;  if (p.isIPad()) {    // iPad    if (version > 7) {      // iPad >= 7.1      // https://issues.apache.org/jira/browse/CB-4323      delete viewportProperties.width;    } else {      // iPad <= 7.0      if (p.isWebView()) {        // iPad <= 7.0 WebView        if (orientation == 90) {          // iPad <= 7.0 WebView Landscape          viewportProperties.height = '0';        } else if (version == 7) {          // iPad <= 7.0 WebView Portait          viewportProperties.height = DEVICE_HEIGHT;        }      } else {        // iPad <= 6.1 Browser        if (version < 7) {          viewportProperties.height = '0';        }      }    }  } else if (p.isIOS()) {    // iPhone    if (p.isWebView()) {      // iPhone WebView      if (version > 7) {        // iPhone >= 7.1 WebView        delete viewportProperties.width;      } else if (version < 7) {        // iPhone <= 6.1 WebView        // if height was set it needs to get removed with this hack for <= 6.1        if (initHeight) viewportProperties.height = '0';      } else if (version == 7) {        //iPhone == 7.0 WebView        viewportProperties.height = DEVICE_HEIGHT;      }    } else {      // iPhone Browser      if (version < 7) {        // iPhone <= 6.1 Browser        // if height was set it needs to get removed with this hack for <= 6.1        if (initHeight) viewportProperties.height = '0';      }    }  }  // only update the viewport tag if there was a change  if (initWidth !== viewportProperties.width || initHeight !== viewportProperties.height) {    viewportTagUpdate();  }}function viewportTagUpdate() {  var key, props = [];  for (key in viewportProperties) {    if (viewportProperties[key]) {      props.push(key + (viewportProperties[key] == '_' ? '' : '=' + viewportProperties[key]));    }  }  viewportTag.content = props.join(', ');}ionic.Platform.ready(function() {  viewportLoadTag();  window.addEventListener("orientationchange", function() {    setTimeout(viewportUpdate, 1000);  }, false);});(function(ionic) {'use strict';  ionic.views.View = function() {    this.initialize.apply(this, arguments);  };  ionic.views.View.inherit = ionic.inherit;  ionic.extend(ionic.views.View.prototype, {    initialize: function() {}  });})(window.ionic);/* * Scroller * http://github.com/zynga/scroller * * Copyright 2011, Zynga Inc. * Licensed under the MIT License. * https://raw.github.com/zynga/scroller/master/MIT-LICENSE.txt * * Based on the work of: Unify Project (unify-project.org) * http://unify-project.org * Copyright 2011, Deutsche Telekom AG * License: MIT + Apache (V2) *//* jshint eqnull: true *//** * Generic animation class with support for dropped frames both optional easing and duration. * * Optional duration is useful when the lifetime is defined by another condition than time * e.g. speed of an animating object, etc. * * Dropped frame logic allows to keep using the same updater logic independent from the actual * rendering. This eases a lot of cases where it might be pretty complex to break down a state * based on the pure time difference. */var zyngaCore = { effect: {} };(function(global) {  var time = Date.now || function() {    return +new Date();  };  var desiredFrames = 60;  var millisecondsPerSecond = 1000;  var running = {};  var counter = 1;  zyngaCore.effect.Animate = {    /**     * A requestAnimationFrame wrapper / polyfill.     *     * @param callback {Function} The callback to be invoked before the next repaint.     * @param root {HTMLElement} The root element for the repaint     */    requestAnimationFrame: (function() {      // Check for request animation Frame support      var requestFrame = global.requestAnimationFrame || global.webkitRequestAnimationFrame || global.mozRequestAnimationFrame || global.oRequestAnimationFrame;      var isNative = !!requestFrame;      if (requestFrame && !/requestAnimationFrame\(\)\s*\{\s*\[native code\]\s*\}/i.test(requestFrame.toString())) {        isNative = false;      }      if (isNative) {        return function(callback, root) {          requestFrame(callback, root);        };      }      var TARGET_FPS = 60;      var requests = {};      var requestCount = 0;      var rafHandle = 1;      var intervalHandle = null;      var lastActive = +new Date();      return function(callback) {        var callbackHandle = rafHandle++;        // Store callback        requests[callbackHandle] = callback;        requestCount++;        // Create timeout at first request        if (intervalHandle === null) {          intervalHandle = setInterval(function() {            var time = +new Date();            var currentRequests = requests;            // Reset data structure before executing callbacks            requests = {};            requestCount = 0;            for(var key in currentRequests) {              if (currentRequests.hasOwnProperty(key)) {                currentRequests[key](time);                lastActive = time;              }            }            // Disable the timeout when nothing happens for a certain            // period of time            if (time - lastActive > 2500) {              clearInterval(intervalHandle);              intervalHandle = null;            }          }, 1000 / TARGET_FPS);        }        return callbackHandle;      };    })(),    /**     * Stops the given animation.     *     * @param id {Integer} Unique animation ID     * @return {Boolean} Whether the animation was stopped (aka, was running before)     */    stop: function(id) {      var cleared = running[id] != null;      if (cleared) {        running[id] = null;      }      return cleared;    },    /**     * Whether the given animation is still running.     *     * @param id {Integer} Unique animation ID     * @return {Boolean} Whether the animation is still running     */    isRunning: function(id) {      return running[id] != null;    },    /**     * Start the animation.     *     * @param stepCallback {Function} Pointer to function which is executed on every step.     *   Signature of the method should be `function(percent, now, virtual) { return continueWithAnimation; }`     * @param verifyCallback {Function} Executed before every animation step.     *   Signature of the method should be `function() { return continueWithAnimation; }`     * @param completedCallback {Function}     *   Signature of the method should be `function(droppedFrames, finishedAnimation) {}`     * @param duration {Integer} Milliseconds to run the animation     * @param easingMethod {Function} Pointer to easing function     *   Signature of the method should be `function(percent) { return modifiedValue; }`     * @param root {Element} Render root, when available. Used for internal     *   usage of requestAnimationFrame.     * @return {Integer} Identifier of animation. Can be used to stop it any time.     */    start: function(stepCallback, verifyCallback, completedCallback, duration, easingMethod, root) {      var start = time();      var lastFrame = start;      var percent = 0;      var dropCounter = 0;      var id = counter++;      if (!root) {        root = document.body;      }      // Compacting running db automatically every few new animations      if (id % 20 === 0) {        var newRunning = {};        for (var usedId in running) {          newRunning[usedId] = true;        }        running = newRunning;      }      // This is the internal step method which is called every few milliseconds      var step = function(virtual) {        // Normalize virtual value        var render = virtual !== true;        // Get current time        var now = time();        // Verification is executed before next animation step        if (!running[id] || (verifyCallback && !verifyCallback(id))) {          running[id] = null;          completedCallback && completedCallback(desiredFrames - (dropCounter / ((now - start) / millisecondsPerSecond)), id, false);          return;        }        // For the current rendering to apply let's update omitted steps in memory.        // This is important to bring internal state variables up-to-date with progress in time.        if (render) {          var droppedFrames = Math.round((now - lastFrame) / (millisecondsPerSecond / desiredFrames)) - 1;          for (var j = 0; j < Math.min(droppedFrames, 4); j++) {            step(true);            dropCounter++;          }        }        // Compute percent value        if (duration) {          percent = (now - start) / duration;          if (percent > 1) {            percent = 1;          }        }        // Execute step callback, then...        var value = easingMethod ? easingMethod(percent) : percent;        if ((stepCallback(value, now, render) === false || percent === 1) && render) {          running[id] = null;          completedCallback && completedCallback(desiredFrames - (dropCounter / ((now - start) / millisecondsPerSecond)), id, percent === 1 || duration == null);        } else if (render) {          lastFrame = now;          zyngaCore.effect.Animate.requestAnimationFrame(step, root);        }      };      // Mark as running      running[id] = true;      // Init first step      zyngaCore.effect.Animate.requestAnimationFrame(step, root);      // Return unique animation ID      return id;    }  };})(window);/* * Scroller * http://github.com/zynga/scroller * * Copyright 2011, Zynga Inc. * Licensed under the MIT License. * https://raw.github.com/zynga/scroller/master/MIT-LICENSE.txt * * Based on the work of: Unify Project (unify-project.org) * http://unify-project.org * Copyright 2011, Deutsche Telekom AG * License: MIT + Apache (V2) */(function(ionic) {  var NOOP = function(){};  // Easing Equations (c) 2003 Robert Penner, all rights reserved.  // Open source under the BSD License.  /**   * @param pos {Number} position between 0 (start of effect) and 1 (end of effect)  **/  var easeOutCubic = function(pos) {    return (Math.pow((pos - 1), 3) + 1);  };  /**   * @param pos {Number} position between 0 (start of effect) and 1 (end of effect)  **/  var easeInOutCubic = function(pos) {    if ((pos /= 0.5) < 1) {      return 0.5 * Math.pow(pos, 3);    }    return 0.5 * (Math.pow((pos - 2), 3) + 2);  };/** * ionic.views.Scroll * A powerful scroll view with support for bouncing, pull to refresh, and paging. * @param   {Object}        options options for the scroll view * @class A scroll view system * @memberof ionic.views */ionic.views.Scroll = ionic.views.View.inherit({  initialize: function(options) {    var self = this;    self.__container = options.el;    self.__content = options.el.firstElementChild;    //Remove any scrollTop attached to these elements; they are virtual scroll now    //This also stops on-load-scroll-to-window.location.hash that the browser does    setTimeout(function() {      if (self.__container && self.__content) {        self.__container.scrollTop = 0;        self.__content.scrollTop = 0;      }    });    self.options = {      /** Disable scrolling on x-axis by default */      scrollingX: false,      scrollbarX: true,      /** Enable scrolling on y-axis */      scrollingY: true,      scrollbarY: true,      startX: 0,      startY: 0,      /** The amount to dampen mousewheel events */      wheelDampen: 6,      /** The minimum size the scrollbars scale to while scrolling */      minScrollbarSizeX: 5,      minScrollbarSizeY: 5,      /** Scrollbar fading after scrolling */      scrollbarsFade: true,      scrollbarFadeDelay: 300,      /** The initial fade delay when the pane is resized or initialized */      scrollbarResizeFadeDelay: 1000,      /** Enable animations for deceleration, snap back, zooming and scrolling */      animating: true,      /** duration for animations triggered by scrollTo/zoomTo */      animationDuration: 250,      /** The velocity required to make the scroll view "slide" after touchend */      decelVelocityThreshold: 4,      /** The velocity required to make the scroll view "slide" after touchend when using paging */      decelVelocityThresholdPaging: 4,      /** Enable bouncing (content can be slowly moved outside and jumps back after releasing) */      bouncing: true,      /** Enable locking to the main axis if user moves only slightly on one of them at start */      locking: true,      /** Enable pagination mode (switching between full page content panes) */      paging: false,      /** Enable snapping of content to a configured pixel grid */      snapping: false,      /** Enable zooming of content via API, fingers and mouse wheel */      zooming: false,      /** Minimum zoom level */      minZoom: 0.5,      /** Maximum zoom level */      maxZoom: 3,      /** Multiply or decrease scrolling speed **/      speedMultiplier: 1,      deceleration: 0.97,      /** Whether to prevent default on a scroll operation to capture drag events **/      preventDefault: false,      /** Callback that is fired on the later of touch end or deceleration end,        provided that another scrolling action has not begun. Used to know        when to fade out a scrollbar. */      scrollingComplete: NOOP,      /** This configures the amount of change applied to deceleration when reaching boundaries  **/      penetrationDeceleration: 0.03,      /** This configures the amount of change applied to acceleration when reaching boundaries  **/      penetrationAcceleration: 0.08,      // The ms interval for triggering scroll events      scrollEventInterval: 10,      freeze: false,      getContentWidth: function() {        return Math.max(self.__content.scrollWidth, self.__content.offsetWidth);      },      getContentHeight: function() {        return Math.max(self.__content.scrollHeight, self.__content.offsetHeight + (self.__content.offsetTop * 2));      }    };    for (var key in options) {      self.options[key] = options[key];    }    self.hintResize = ionic.debounce(function() {      self.resize();    }, 1000, true);    self.onScroll = function() {      if (!ionic.scroll.isScrolling) {        setTimeout(self.setScrollStart, 50);      } else {        clearTimeout(self.scrollTimer);        self.scrollTimer = setTimeout(self.setScrollStop, 80);      }    };    self.freeze = function(shouldFreeze) {      if (arguments.length) {        self.options.freeze = shouldFreeze;      }      return self.options.freeze;    };    // We can just use the standard freeze pop in our mouth    self.freezeShut = self.freeze;    self.setScrollStart = function() {      ionic.scroll.isScrolling = Math.abs(ionic.scroll.lastTop - self.__scrollTop) > 1;      clearTimeout(self.scrollTimer);      self.scrollTimer = setTimeout(self.setScrollStop, 80);    };    self.setScrollStop = function() {      ionic.scroll.isScrolling = false;      ionic.scroll.lastTop = self.__scrollTop;    };    self.triggerScrollEvent = ionic.throttle(function() {      self.onScroll();      ionic.trigger('scroll', {        scrollTop: self.__scrollTop,        scrollLeft: self.__scrollLeft,        target: self.__container      });    }, self.options.scrollEventInterval);    self.triggerScrollEndEvent = function() {      ionic.trigger('scrollend', {        scrollTop: self.__scrollTop,        scrollLeft: self.__scrollLeft,        target: self.__container      });    };    self.__scrollLeft = self.options.startX;    self.__scrollTop = self.options.startY;    // Get the render update function, initialize event handlers,    // and calculate the size of the scroll container    self.__callback = self.getRenderFn();    self.__initEventHandlers();    self.__createScrollbars();  },  run: function() {    this.resize();    // Fade them out    this.__fadeScrollbars('out', this.options.scrollbarResizeFadeDelay);  },  /*  ---------------------------------------------------------------------------    INTERNAL FIELDS :: STATUS  ---------------------------------------------------------------------------  */  /** Whether only a single finger is used in touch handling */  __isSingleTouch: false,  /** Whether a touch event sequence is in progress */  __isTracking: false,  /** Whether a deceleration animation went to completion. */  __didDecelerationComplete: false,  /**   * Whether a gesture zoom/rotate event is in progress. Activates when   * a gesturestart event happens. This has higher priority than dragging.   */  __isGesturing: false,  /**   * Whether the user has moved by such a distance that we have enabled   * dragging mode. Hint: It's only enabled after some pixels of movement to   * not interrupt with clicks etc.   */  __isDragging: false,  /**   * Not touching and dragging anymore, and smoothly animating the   * touch sequence using deceleration.   */  __isDecelerating: false,  /**   * Smoothly animating the currently configured change   */  __isAnimating: false,  /*  ---------------------------------------------------------------------------    INTERNAL FIELDS :: DIMENSIONS  ---------------------------------------------------------------------------  */  /** Available outer left position (from document perspective) */  __clientLeft: 0,  /** Available outer top position (from document perspective) */  __clientTop: 0,  /** Available outer width */  __clientWidth: 0,  /** Available outer height */  __clientHeight: 0,  /** Outer width of content */  __contentWidth: 0,  /** Outer height of content */  __contentHeight: 0,  /** Snapping width for content */  __snapWidth: 100,  /** Snapping height for content */  __snapHeight: 100,  /** Height to assign to refresh area */  __refreshHeight: null,  /** Whether the refresh process is enabled when the event is released now */  __refreshActive: false,  /** Callback to execute on activation. This is for signalling the user about a refresh is about to happen when he release */  __refreshActivate: null,  /** Callback to execute on deactivation. This is for signalling the user about the refresh being cancelled */  __refreshDeactivate: null,  /** Callback to execute to start the actual refresh. Call {@link #refreshFinish} when done */  __refreshStart: null,  /** Zoom level */  __zoomLevel: 1,  /** Scroll position on x-axis */  __scrollLeft: 0,  /** Scroll position on y-axis */  __scrollTop: 0,  /** Maximum allowed scroll position on x-axis */  __maxScrollLeft: 0,  /** Maximum allowed scroll position on y-axis */  __maxScrollTop: 0,  /* Scheduled left position (final position when animating) */  __scheduledLeft: 0,  /* Scheduled top position (final position when animating) */  __scheduledTop: 0,  /* Scheduled zoom level (final scale when animating) */  __scheduledZoom: 0,  /*  ---------------------------------------------------------------------------    INTERNAL FIELDS :: LAST POSITIONS  ---------------------------------------------------------------------------  */  /** Left position of finger at start */  __lastTouchLeft: null,  /** Top position of finger at start */  __lastTouchTop: null,  /** Timestamp of last move of finger. Used to limit tracking range for deceleration speed. */  __lastTouchMove: null,  /** List of positions, uses three indexes for each state: left, top, timestamp */  __positions: null,  /*  ---------------------------------------------------------------------------    INTERNAL FIELDS :: DECELERATION SUPPORT  ---------------------------------------------------------------------------  */  /** Minimum left scroll position during deceleration */  __minDecelerationScrollLeft: null,  /** Minimum top scroll position during deceleration */  __minDecelerationScrollTop: null,  /** Maximum left scroll position during deceleration */  __maxDecelerationScrollLeft: null,  /** Maximum top scroll position during deceleration */  __maxDecelerationScrollTop: null,  /** Current factor to modify horizontal scroll position with on every step */  __decelerationVelocityX: null,  /** Current factor to modify vertical scroll position with on every step */  __decelerationVelocityY: null,  /** the browser-specific property to use for transforms */  __transformProperty: null,  __perspectiveProperty: null,  /** scrollbar indicators */  __indicatorX: null,  __indicatorY: null,  /** Timeout for scrollbar fading */  __scrollbarFadeTimeout: null,  /** whether we've tried to wait for size already */  __didWaitForSize: null,  __sizerTimeout: null,  __initEventHandlers: function() {    var self = this;    // Event Handler    var container = self.__container;    // save height when scroll view is shrunk so we don't need to reflow    var scrollViewOffsetHeight;    /**     * Shrink the scroll view when the keyboard is up if necessary and if the     * focused input is below the bottom of the shrunk scroll view, scroll it     * into view.     */    self.scrollChildIntoView = function(e) {      //console.log("scrollChildIntoView at: " + Date.now());      // D      var scrollBottomOffsetToTop = container.getBoundingClientRect().bottom;      // D - A      scrollViewOffsetHeight = container.offsetHeight;      var alreadyShrunk = self.isShrunkForKeyboard;      var isModal = container.parentNode.classList.contains('modal');      // 680px is when the media query for 60% modal width kicks in      var isInsetModal = isModal && window.innerWidth >= 680;     /*      *  _______      * |---A---| <- top of scroll view      * |       |      * |---B---| <- keyboard      * |   C   | <- input      * |---D---| <- initial bottom of scroll view      * |___E___| <- bottom of viewport      *      *  All commented calculations relative to the top of the viewport (ie E      *  is the viewport height, not 0)      */      if (!alreadyShrunk) {        // shrink scrollview so we can actually scroll if the input is hidden        // if it isn't shrink so we can scroll to inputs under the keyboard        // inset modals won't shrink on Android on their own when the keyboard appears        if ( ionic.Platform.isIOS() || ionic.Platform.isFullScreen || isInsetModal ) {          // if there are things below the scroll view account for them and          // subtract them from the keyboard height when resizing          // E - D                         E                         D          var scrollBottomOffsetToBottom = e.detail.viewportHeight - scrollBottomOffsetToTop;          // 0 or D - B if D > B           E - B                     E - D          var keyboardOffset = Math.max(0, e.detail.keyboardHeight - scrollBottomOffsetToBottom);          ionic.requestAnimationFrame(function(){            // D - A or B - A if D > B       D - A             max(0, D - B)            scrollViewOffsetHeight = scrollViewOffsetHeight - keyboardOffset;            container.style.height = scrollViewOffsetHeight + "px";            container.style.overflow = "visible";            //update scroll view            self.resize();          });        }        self.isShrunkForKeyboard = true;      }      /*       *  _______       * |---A---| <- top of scroll view       * |   *   | <- where we want to scroll to       * |--B-D--| <- keyboard, bottom of scroll view       * |   C   | <- input       * |       |       * |___E___| <- bottom of viewport       *       *  All commented calculations relative to the top of the viewport (ie E       *  is the viewport height, not 0)       */      // if the element is positioned under the keyboard scroll it into view      if (e.detail.isElementUnderKeyboard) {        ionic.requestAnimationFrame(function(){          container.scrollTop = 0;          // update D if we shrunk          if (self.isShrunkForKeyboard && !alreadyShrunk) {            scrollBottomOffsetToTop = container.getBoundingClientRect().bottom;          }          // middle of the scrollview, this is where we want to scroll to          // (D - A) / 2          var scrollMidpointOffset = scrollViewOffsetHeight * 0.5;          //console.log("container.offsetHeight: " + scrollViewOffsetHeight);          // middle of the input we want to scroll into view          // C          var inputMidpoint = ((e.detail.elementBottom + e.detail.elementTop) / 2);          // distance from middle of input to the bottom of the scroll view          // C - D                                C               D          var inputMidpointOffsetToScrollBottom = inputMidpoint - scrollBottomOffsetToTop;          //C - D + (D - A)/2          C - D                     (D - A)/ 2          var scrollTop = inputMidpointOffsetToScrollBottom + scrollMidpointOffset;          if ( scrollTop > 0) {            if (ionic.Platform.isIOS()) ionic.tap.cloneFocusedInput(container, self);            self.scrollBy(0, scrollTop, true);            self.onScroll();          }        });      }      // Only the first scrollView parent of the element that broadcasted this event      // (the active element that needs to be shown) should receive this event      e.stopPropagation();    };    self.resetScrollView = function() {      //return scrollview to original height once keyboard has hidden      if ( self.isShrunkForKeyboard ) {        self.isShrunkForKeyboard = false;        container.style.height = "";        container.style.overflow = "";      }      self.resize();    };    //Broadcasted when keyboard is shown on some platforms.    //See js/utils/keyboard.js    container.addEventListener('scrollChildIntoView', self.scrollChildIntoView);    // Listen on document because container may not have had the last    // keyboardActiveElement, for example after closing a modal with a focused    // input and returning to a previously resized scroll view in an ion-content.    // Since we can only resize scroll views that are currently visible, just resize    // the current scroll view when the keyboard is closed.    document.addEventListener('resetScrollView', self.resetScrollView);    function getEventTouches(e) {      return e.touches && e.touches.length ? e.touches : [{        pageX: e.pageX,        pageY: e.pageY      }];    }    self.touchStart = function(e) {      self.startCoordinates = ionic.tap.pointerCoord(e);      if ( ionic.tap.ignoreScrollStart(e) ) {        return;      }      self.__isDown = true;      if ( ionic.tap.containsOrIsTextInput(e.target) || e.target.tagName === 'SELECT' ) {        // do not start if the target is a text input        // if there is a touchmove on this input, then we can start the scroll        self.__hasStarted = false;        return;      }      self.__isSelectable = true;      self.__enableScrollY = true;      self.__hasStarted = true;      self.doTouchStart(getEventTouches(e), e.timeStamp);      e.preventDefault();    };    self.touchMove = function(e) {      if (self.options.freeze || !self.__isDown ||        (!self.__isDown && e.defaultPrevented) ||        (e.target.tagName === 'TEXTAREA' && e.target.parentElement.querySelector(':focus')) ) {        return;      }      if ( !self.__hasStarted && ( ionic.tap.containsOrIsTextInput(e.target) || e.target.tagName === 'SELECT' ) ) {        // the target is a text input and scroll has started        // since the text input doesn't start on touchStart, do it here        self.__hasStarted = true;        self.doTouchStart(getEventTouches(e), e.timeStamp);        e.preventDefault();        return;      }      if (self.startCoordinates) {        // we have start coordinates, so get this touch move's current coordinates        var currentCoordinates = ionic.tap.pointerCoord(e);        if ( self.__isSelectable &&            ionic.tap.isTextInput(e.target) &&            Math.abs(self.startCoordinates.x - currentCoordinates.x) > 20 ) {          // user slid the text input's caret on its x axis, disable any future y scrolling          self.__enableScrollY = false;          self.__isSelectable = true;        }        if ( self.__enableScrollY && Math.abs(self.startCoordinates.y - currentCoordinates.y) > 10 ) {          // user scrolled the entire view on the y axis          // disabled being able to select text on an input          // hide the input which has focus, and show a cloned one that doesn't have focus          self.__isSelectable = false;          ionic.tap.cloneFocusedInput(container, self);        }      }      self.doTouchMove(getEventTouches(e), e.timeStamp, e.scale);      self.__isDown = true;    };    self.touchMoveBubble = function(e) {      if(self.__isDown && self.options.preventDefault) {        e.preventDefault();      }    };    self.touchEnd = function(e) {      if (!self.__isDown) return;      self.doTouchEnd(e, e.timeStamp);      self.__isDown = false;      self.__hasStarted = false;      self.__isSelectable = true;      self.__enableScrollY = true;      if ( !self.__isDragging && !self.__isDecelerating && !self.__isAnimating ) {        ionic.tap.removeClonedInputs(container, self);      }    };    self.mouseWheel = ionic.animationFrameThrottle(function(e) {      var scrollParent = ionic.DomUtil.getParentOrSelfWithClass(e.target, 'ionic-scroll');      if (!self.options.freeze && scrollParent === self.__container) {        self.hintResize();        self.scrollBy(          (e.wheelDeltaX || e.deltaX || 0) / self.options.wheelDampen,          (-e.wheelDeltaY || e.deltaY || 0) / self.options.wheelDampen        );        self.__fadeScrollbars('in');        clearTimeout(self.__wheelHideBarTimeout);        self.__wheelHideBarTimeout = setTimeout(function() {          self.__fadeScrollbars('out');        }, 100);      }    });    if ('ontouchstart' in window) {      // Touch Events      container.addEventListener("touchstart", self.touchStart, false);      if(self.options.preventDefault) container.addEventListener("touchmove", self.touchMoveBubble, false);      document.addEventListener("touchmove", self.touchMove, false);      document.addEventListener("touchend", self.touchEnd, false);      document.addEventListener("touchcancel", self.touchEnd, false);      document.addEventListener("wheel", self.mouseWheel, false);    } else if (window.navigator.pointerEnabled) {      // Pointer Events      container.addEventListener("pointerdown", self.touchStart, false);      if(self.options.preventDefault) container.addEventListener("pointermove", self.touchMoveBubble, false);      document.addEventListener("pointermove", self.touchMove, false);      document.addEventListener("pointerup", self.touchEnd, false);      document.addEventListener("pointercancel", self.touchEnd, false);      document.addEventListener("wheel", self.mouseWheel, false);    } else if (window.navigator.msPointerEnabled) {      // IE10, WP8 (Pointer Events)      container.addEventListener("MSPointerDown", self.touchStart, false);      if(self.options.preventDefault) container.addEventListener("MSPointerMove", self.touchMoveBubble, false);      document.addEventListener("MSPointerMove", self.touchMove, false);      document.addEventListener("MSPointerUp", self.touchEnd, false);      document.addEventListener("MSPointerCancel", self.touchEnd, false);      document.addEventListener("wheel", self.mouseWheel, false);    } else {      // Mouse Events      var mousedown = false;      self.mouseDown = function(e) {        if ( ionic.tap.ignoreScrollStart(e) || e.target.tagName === 'SELECT' ) {          return;        }        self.doTouchStart(getEventTouches(e), e.timeStamp);        if ( !ionic.tap.isTextInput(e.target) ) {          e.preventDefault();        }        mousedown = true;      };      self.mouseMove = function(e) {        if (self.options.freeze || !mousedown || (!mousedown && e.defaultPrevented)) {          return;        }        self.doTouchMove(getEventTouches(e), e.timeStamp);        mousedown = true;      };      self.mouseMoveBubble = function(e) {        if (mousedown && self.options.preventDefault) {          e.preventDefault();        }      };      self.mouseUp = function(e) {        if (!mousedown) {          return;        }        self.doTouchEnd(e, e.timeStamp);        mousedown = false;      };      container.addEventListener("mousedown", self.mouseDown, false);      if(self.options.preventDefault) container.addEventListener("mousemove", self.mouseMoveBubble, false);      document.addEventListener("mousemove", self.mouseMove, false);      document.addEventListener("mouseup", self.mouseUp, false);      document.addEventListener('mousewheel', self.mouseWheel, false);      document.addEventListener('wheel', self.mouseWheel, false);    }  },  __cleanup: function() {    var self = this;    var container = self.__container;    container.removeEventListener('touchstart', self.touchStart);    container.removeEventListener('touchmove', self.touchMoveBubble);    document.removeEventListener('touchmove', self.touchMove);    document.removeEventListener('touchend', self.touchEnd);    document.removeEventListener('touchcancel', self.touchEnd);    container.removeEventListener("pointerdown", self.touchStart);    container.removeEventListener("pointermove", self.touchMoveBubble);    document.removeEventListener("pointermove", self.touchMove);    document.removeEventListener("pointerup", self.touchEnd);    document.removeEventListener("pointercancel", self.touchEnd);    container.removeEventListener("MSPointerDown", self.touchStart);    container.removeEventListener("MSPointerMove", self.touchMoveBubble);    document.removeEventListener("MSPointerMove", self.touchMove);    document.removeEventListener("MSPointerUp", self.touchEnd);    document.removeEventListener("MSPointerCancel", self.touchEnd);    container.removeEventListener("mousedown", self.mouseDown);    container.removeEventListener("mousemove", self.mouseMoveBubble);    document.removeEventListener("mousemove", self.mouseMove);    document.removeEventListener("mouseup", self.mouseUp);    document.removeEventListener('mousewheel', self.mouseWheel);    document.removeEventListener('wheel', self.mouseWheel);    container.removeEventListener('scrollChildIntoView', self.scrollChildIntoView);    document.removeEventListener('resetScrollView', self.resetScrollView);    ionic.tap.removeClonedInputs(container, self);    delete self.__container;    delete self.__content;    delete self.__indicatorX;    delete self.__indicatorY;    delete self.options.el;    self.__callback = self.scrollChildIntoView = self.resetScrollView = NOOP;    self.mouseMove = self.mouseDown = self.mouseUp = self.mouseWheel =      self.touchStart = self.touchMove = self.touchEnd = self.touchCancel = NOOP;    self.resize = self.scrollTo = self.zoomTo =      self.__scrollingComplete = NOOP;    container = null;  },  /** Create a scroll bar div with the given direction **/  __createScrollbar: function(direction) {    var bar = document.createElement('div'),      indicator = document.createElement('div');    indicator.className = 'scroll-bar-indicator scroll-bar-fade-out';    if (direction == 'h') {      bar.className = 'scroll-bar scroll-bar-h';    } else {      bar.className = 'scroll-bar scroll-bar-v';    }    bar.appendChild(indicator);    return bar;  },  __createScrollbars: function() {    var self = this;    var indicatorX, indicatorY;    if (self.options.scrollingX) {      indicatorX = {        el: self.__createScrollbar('h'),        sizeRatio: 1      };      indicatorX.indicator = indicatorX.el.children[0];      if (self.options.scrollbarX) {        self.__container.appendChild(indicatorX.el);      }      self.__indicatorX = indicatorX;    }    if (self.options.scrollingY) {      indicatorY = {        el: self.__createScrollbar('v'),        sizeRatio: 1      };      indicatorY.indicator = indicatorY.el.children[0];      if (self.options.scrollbarY) {        self.__container.appendChild(indicatorY.el);      }      self.__indicatorY = indicatorY;    }  },  __resizeScrollbars: function() {    var self = this;    // Update horiz bar    if (self.__indicatorX) {      var width = Math.max(Math.round(self.__clientWidth * self.__clientWidth / (self.__contentWidth)), 20);      if (width > self.__contentWidth) {        width = 0;      }      if (width !== self.__indicatorX.size) {        ionic.requestAnimationFrame(function(){          self.__indicatorX.indicator.style.width = width + 'px';        });      }      self.__indicatorX.size = width;      self.__indicatorX.minScale = self.options.minScrollbarSizeX / width;      self.__indicatorX.maxPos = self.__clientWidth - width;      self.__indicatorX.sizeRatio = self.__maxScrollLeft ? self.__indicatorX.maxPos / self.__maxScrollLeft : 1;    }    // Update vert bar    if (self.__indicatorY) {      var height = Math.max(Math.round(self.__clientHeight * self.__clientHeight / (self.__contentHeight)), 20);      if (height > self.__contentHeight) {        height = 0;      }      if (height !== self.__indicatorY.size) {        ionic.requestAnimationFrame(function(){          self.__indicatorY && (self.__indicatorY.indicator.style.height = height + 'px');        });      }      self.__indicatorY.size = height;      self.__indicatorY.minScale = self.options.minScrollbarSizeY / height;      self.__indicatorY.maxPos = self.__clientHeight - height;      self.__indicatorY.sizeRatio = self.__maxScrollTop ? self.__indicatorY.maxPos / self.__maxScrollTop : 1;    }  },  /**   * Move and scale the scrollbars as the page scrolls.   */  __repositionScrollbars: function() {    var self = this,        heightScale, widthScale,        widthDiff, heightDiff,        x, y,        xstop = 0, ystop = 0;    if (self.__indicatorX) {      // Handle the X scrollbar      // Don't go all the way to the right if we have a vertical scrollbar as well      if (self.__indicatorY) xstop = 10;      x = Math.round(self.__indicatorX.sizeRatio * self.__scrollLeft) || 0;      // The the difference between the last content X position, and our overscrolled one      widthDiff = self.__scrollLeft - (self.__maxScrollLeft - xstop);      if (self.__scrollLeft < 0) {        widthScale = Math.max(self.__indicatorX.minScale,            (self.__indicatorX.size - Math.abs(self.__scrollLeft)) / self.__indicatorX.size);        // Stay at left        x = 0;        // Make sure scale is transformed from the left/center origin point        self.__indicatorX.indicator.style[self.__transformOriginProperty] = 'left center';      } else if (widthDiff > 0) {        widthScale = Math.max(self.__indicatorX.minScale,            (self.__indicatorX.size - widthDiff) / self.__indicatorX.size);        // Stay at the furthest x for the scrollable viewport        x = self.__indicatorX.maxPos - xstop;        // Make sure scale is transformed from the right/center origin point        self.__indicatorX.indicator.style[self.__transformOriginProperty] = 'right center';      } else {        // Normal motion        x = Math.min(self.__maxScrollLeft, Math.max(0, x));        widthScale = 1;      }      var translate3dX = 'translate3d(' + x + 'px, 0, 0) scaleX(' + widthScale + ')';      if (self.__indicatorX.transformProp !== translate3dX) {        self.__indicatorX.indicator.style[self.__transformProperty] = translate3dX;        self.__indicatorX.transformProp = translate3dX;      }    }    if (self.__indicatorY) {      y = Math.round(self.__indicatorY.sizeRatio * self.__scrollTop) || 0;      // Don't go all the way to the right if we have a vertical scrollbar as well      if (self.__indicatorX) ystop = 10;      heightDiff = self.__scrollTop - (self.__maxScrollTop - ystop);      if (self.__scrollTop < 0) {        heightScale = Math.max(self.__indicatorY.minScale, (self.__indicatorY.size - Math.abs(self.__scrollTop)) / self.__indicatorY.size);        // Stay at top        y = 0;        // Make sure scale is transformed from the center/top origin point        if (self.__indicatorY.originProp !== 'center top') {          self.__indicatorY.indicator.style[self.__transformOriginProperty] = 'center top';          self.__indicatorY.originProp = 'center top';        }      } else if (heightDiff > 0) {        heightScale = Math.max(self.__indicatorY.minScale, (self.__indicatorY.size - heightDiff) / self.__indicatorY.size);        // Stay at bottom of scrollable viewport        y = self.__indicatorY.maxPos - ystop;        // Make sure scale is transformed from the center/bottom origin point        if (self.__indicatorY.originProp !== 'center bottom') {          self.__indicatorY.indicator.style[self.__transformOriginProperty] = 'center bottom';          self.__indicatorY.originProp = 'center bottom';        }      } else {        // Normal motion        y = Math.min(self.__maxScrollTop, Math.max(0, y));        heightScale = 1;      }      var translate3dY = 'translate3d(0,' + y + 'px, 0) scaleY(' + heightScale + ')';      if (self.__indicatorY.transformProp !== translate3dY) {        self.__indicatorY.indicator.style[self.__transformProperty] = translate3dY;        self.__indicatorY.transformProp = translate3dY;      }    }  },  __fadeScrollbars: function(direction, delay) {    var self = this;    if (!self.options.scrollbarsFade) {      return;    }    var className = 'scroll-bar-fade-out';    if (self.options.scrollbarsFade === true) {      clearTimeout(self.__scrollbarFadeTimeout);      if (direction == 'in') {        if (self.__indicatorX) { self.__indicatorX.indicator.classList.remove(className); }        if (self.__indicatorY) { self.__indicatorY.indicator.classList.remove(className); }      } else {        self.__scrollbarFadeTimeout = setTimeout(function() {          if (self.__indicatorX) { self.__indicatorX.indicator.classList.add(className); }          if (self.__indicatorY) { self.__indicatorY.indicator.classList.add(className); }        }, delay || self.options.scrollbarFadeDelay);      }    }  },  __scrollingComplete: function() {    this.options.scrollingComplete();    ionic.tap.removeClonedInputs(this.__container, this);    this.__fadeScrollbars('out');  },  resize: function(continueScrolling) {    var self = this;    if (!self.__container || !self.options) return;    // Update Scroller dimensions for changed content    // Add padding to bottom of content    self.setDimensions(      self.__container.clientWidth,      self.__container.clientHeight,      self.options.getContentWidth(),      self.options.getContentHeight(),      continueScrolling    );  },  /*  ---------------------------------------------------------------------------    PUBLIC API  ---------------------------------------------------------------------------  */  getRenderFn: function() {    var self = this;    var content = self.__content;    var docStyle = document.documentElement.style;    var engine;    if ('MozAppearance' in docStyle) {      engine = 'gecko';    } else if ('WebkitAppearance' in docStyle) {      engine = 'webkit';    } else if (typeof navigator.cpuClass === 'string') {      engine = 'trident';    }    var vendorPrefix = {      trident: 'ms',      gecko: 'Moz',      webkit: 'Webkit',      presto: 'O'    }[engine];    var helperElem = document.createElement("div");    var undef;    var perspectiveProperty = vendorPrefix + "Perspective";    var transformProperty = vendorPrefix + "Transform";    var transformOriginProperty = vendorPrefix + 'TransformOrigin';    self.__perspectiveProperty = transformProperty;    self.__transformProperty = transformProperty;    self.__transformOriginProperty = transformOriginProperty;    if (helperElem.style[perspectiveProperty] !== undef) {      return function(left, top, zoom, wasResize) {        var translate3d = 'translate3d(' + (-left) + 'px,' + (-top) + 'px,0) scale(' + zoom + ')';        if (translate3d !== self.contentTransform) {          content.style[transformProperty] = translate3d;          self.contentTransform = translate3d;        }        self.__repositionScrollbars();        if (!wasResize) {          self.triggerScrollEvent();        }      };    } else if (helperElem.style[transformProperty] !== undef) {      return function(left, top, zoom, wasResize) {        content.style[transformProperty] = 'translate(' + (-left) + 'px,' + (-top) + 'px) scale(' + zoom + ')';        self.__repositionScrollbars();        if (!wasResize) {          self.triggerScrollEvent();        }      };    } else {      return function(left, top, zoom, wasResize) {        content.style.marginLeft = left ? (-left / zoom) + 'px' : '';        content.style.marginTop = top ? (-top / zoom) + 'px' : '';        content.style.zoom = zoom || '';        self.__repositionScrollbars();        if (!wasResize) {          self.triggerScrollEvent();        }      };    }  },  /**   * Configures the dimensions of the client (outer) and content (inner) elements.   * Requires the available space for the outer element and the outer size of the inner element.   * All values which are falsy (null or zero etc.) are ignored and the old value is kept.   *   * @param clientWidth {Integer} Inner width of outer element   * @param clientHeight {Integer} Inner height of outer element   * @param contentWidth {Integer} Outer width of inner element   * @param contentHeight {Integer} Outer height of inner element   */  setDimensions: function(clientWidth, clientHeight, contentWidth, contentHeight, continueScrolling) {    var self = this;    if (!clientWidth && !clientHeight && !contentWidth && !contentHeight) {      // this scrollview isn't rendered, don't bother      return;    }    // Only update values which are defined    if (clientWidth === +clientWidth) {      self.__clientWidth = clientWidth;    }    if (clientHeight === +clientHeight) {      self.__clientHeight = clientHeight;    }    if (contentWidth === +contentWidth) {      self.__contentWidth = contentWidth;    }    if (contentHeight === +contentHeight) {      self.__contentHeight = contentHeight;    }    // Refresh maximums    self.__computeScrollMax();    self.__resizeScrollbars();    // Refresh scroll position    if (!continueScrolling) {      self.scrollTo(self.__scrollLeft, self.__scrollTop, true, null, true);    }  },  /**   * Sets the client coordinates in relation to the document.   *   * @param left {Integer} Left position of outer element   * @param top {Integer} Top position of outer element   */  setPosition: function(left, top) {    this.__clientLeft = left || 0;    this.__clientTop = top || 0;  },  /**   * Configures the snapping (when snapping is active)   *   * @param width {Integer} Snapping width   * @param height {Integer} Snapping height   */  setSnapSize: function(width, height) {    this.__snapWidth = width;    this.__snapHeight = height;  },  /**   * Activates pull-to-refresh. A special zone on the top of the list to start a list refresh whenever   * the user event is released during visibility of this zone. This was introduced by some apps on iOS like   * the official Twitter client.   *   * @param height {Integer} Height of pull-to-refresh zone on top of rendered list   * @param activateCallback {Function} Callback to execute on activation. This is for signalling the user about a refresh is about to happen when he release.   * @param deactivateCallback {Function} Callback to execute on deactivation. This is for signalling the user about the refresh being cancelled.   * @param startCallback {Function} Callback to execute to start the real async refresh action. Call {@link #finishPullToRefresh} after finish of refresh.   * @param showCallback {Function} Callback to execute when the refresher should be shown. This is for showing the refresher during a negative scrollTop.   * @param hideCallback {Function} Callback to execute when the refresher should be hidden. This is for hiding the refresher when it's behind the nav bar.   * @param tailCallback {Function} Callback to execute just before the refresher returns to it's original state. This is for zooming out the refresher.   * @param pullProgressCallback Callback to state the progress while pulling to refresh   */  activatePullToRefresh: function(height, refresherMethods) {    var self = this;    self.__refreshHeight = height;    self.__refreshActivate = function() { ionic.requestAnimationFrame(refresherMethods.activate); };    self.__refreshDeactivate = function() { ionic.requestAnimationFrame(refresherMethods.deactivate); };    self.__refreshStart = function() { ionic.requestAnimationFrame(refresherMethods.start); };    self.__refreshShow = function() { ionic.requestAnimationFrame(refresherMethods.show); };    self.__refreshHide = function() { ionic.requestAnimationFrame(refresherMethods.hide); };    self.__refreshTail = function() { ionic.requestAnimationFrame(refresherMethods.tail); };    self.__refreshTailTime = 100;    self.__minSpinTime = 600;  },  /**   * Starts pull-to-refresh manually.   */  triggerPullToRefresh: function() {    // Use publish instead of scrollTo to allow scrolling to out of boundary position    // We don't need to normalize scrollLeft, zoomLevel, etc. here because we only y-scrolling when pull-to-refresh is enabled    this.__publish(this.__scrollLeft, -this.__refreshHeight, this.__zoomLevel, true);    var d = new Date();    this.refreshStartTime = d.getTime();    if (this.__refreshStart) {      this.__refreshStart();    }  },  /**   * Signalizes that pull-to-refresh is finished.   */  finishPullToRefresh: function() {    var self = this;    // delay to make sure the spinner has a chance to spin for a split second before it's dismissed    var d = new Date();    var delay = 0;    if (self.refreshStartTime + self.__minSpinTime > d.getTime()) {      delay = self.refreshStartTime + self.__minSpinTime - d.getTime();    }    setTimeout(function() {      if (self.__refreshTail) {        self.__refreshTail();      }      setTimeout(function() {        self.__refreshActive = false;        if (self.__refreshDeactivate) {          self.__refreshDeactivate();        }        if (self.__refreshHide) {          self.__refreshHide();        }        self.scrollTo(self.__scrollLeft, self.__scrollTop, true);      }, self.__refreshTailTime);    }, delay);  },  /**   * Returns the scroll position and zooming values   *   * @return {Map} `left` and `top` scroll position and `zoom` level   */  getValues: function() {    return {      left: this.__scrollLeft,      top: this.__scrollTop,      zoom: this.__zoomLevel    };  },  /**   * Returns the maximum scroll values   *   * @return {Map} `left` and `top` maximum scroll values   */  getScrollMax: function() {    return {      left: this.__maxScrollLeft,      top: this.__maxScrollTop    };  },  /**   * Zooms to the given level. Supports optional animation. Zooms   * the center when no coordinates are given.   *   * @param level {Number} Level to zoom to   * @param animate {Boolean} Whether to use animation   * @param originLeft {Number} Zoom in at given left coordinate   * @param originTop {Number} Zoom in at given top coordinate   */  zoomTo: function(level, animate, originLeft, originTop) {    var self = this;    if (!self.options.zooming) {      throw new Error("Zooming is not enabled!");    }    // Stop deceleration    if (self.__isDecelerating) {      zyngaCore.effect.Animate.stop(self.__isDecelerating);      self.__isDecelerating = false;    }    var oldLevel = self.__zoomLevel;    // Normalize input origin to center of viewport if not defined    if (originLeft == null) {      originLeft = self.__clientWidth / 2;    }    if (originTop == null) {      originTop = self.__clientHeight / 2;    }    // Limit level according to configuration    level = Math.max(Math.min(level, self.options.maxZoom), self.options.minZoom);    // Recompute maximum values while temporary tweaking maximum scroll ranges    self.__computeScrollMax(level);    // Recompute left and top coordinates based on new zoom level    var left = ((originLeft + self.__scrollLeft) * level / oldLevel) - originLeft;    var top = ((originTop + self.__scrollTop) * level / oldLevel) - originTop;    // Limit x-axis    if (left > self.__maxScrollLeft) {      left = self.__maxScrollLeft;    } else if (left < 0) {      left = 0;    }    // Limit y-axis    if (top > self.__maxScrollTop) {      top = self.__maxScrollTop;    } else if (top < 0) {      top = 0;    }    // Push values out    self.__publish(left, top, level, animate);  },  /**   * Zooms the content by the given factor.   *   * @param factor {Number} Zoom by given factor   * @param animate {Boolean} Whether to use animation   * @param originLeft {Number} Zoom in at given left coordinate   * @param originTop {Number} Zoom in at given top coordinate   */  zoomBy: function(factor, animate, originLeft, originTop) {    this.zoomTo(this.__zoomLevel * factor, animate, originLeft, originTop);  },  /**   * Scrolls to the given position. Respect limitations and snapping automatically.   *   * @param left {Number} Horizontal scroll position, keeps current if value is <code>null</code>   * @param top {Number} Vertical scroll position, keeps current if value is <code>null</code>   * @param animate {Boolean} Whether the scrolling should happen using an animation   * @param zoom {Number} Zoom level to go to   */  scrollTo: function(left, top, animate, zoom, wasResize) {    var self = this;    // Stop deceleration    if (self.__isDecelerating) {      zyngaCore.effect.Animate.stop(self.__isDecelerating);      self.__isDecelerating = false;    }    // Correct coordinates based on new zoom level    if (zoom != null && zoom !== self.__zoomLevel) {      if (!self.options.zooming) {        throw new Error("Zooming is not enabled!");      }      left *= zoom;      top *= zoom;      // Recompute maximum values while temporary tweaking maximum scroll ranges      self.__computeScrollMax(zoom);    } else {      // Keep zoom when not defined      zoom = self.__zoomLevel;    }    if (!self.options.scrollingX) {      left = self.__scrollLeft;    } else {      if (self.options.paging) {        left = Math.round(left / self.__clientWidth) * self.__clientWidth;      } else if (self.options.snapping) {        left = Math.round(left / self.__snapWidth) * self.__snapWidth;      }    }    if (!self.options.scrollingY) {      top = self.__scrollTop;    } else {      if (self.options.paging) {        top = Math.round(top / self.__clientHeight) * self.__clientHeight;      } else if (self.options.snapping) {        top = Math.round(top / self.__snapHeight) * self.__snapHeight;      }    }    // Limit for allowed ranges    left = Math.max(Math.min(self.__maxScrollLeft, left), 0);    top = Math.max(Math.min(self.__maxScrollTop, top), 0);    // Don't animate when no change detected, still call publish to make sure    // that rendered position is really in-sync with internal data    if (left === self.__scrollLeft && top === self.__scrollTop) {      animate = false;    }    // Publish new values    self.__publish(left, top, zoom, animate, wasResize);  },  /**   * Scroll by the given offset   *   * @param left {Number} Scroll x-axis by given offset   * @param top {Number} Scroll y-axis by given offset   * @param animate {Boolean} Whether to animate the given change   */  scrollBy: function(left, top, animate) {    var self = this;    var startLeft = self.__isAnimating ? self.__scheduledLeft : self.__scrollLeft;    var startTop = self.__isAnimating ? self.__scheduledTop : self.__scrollTop;    self.scrollTo(startLeft + (left || 0), startTop + (top || 0), animate);  },  /*  ---------------------------------------------------------------------------    EVENT CALLBACKS  ---------------------------------------------------------------------------  */  /**   * Mouse wheel handler for zooming support   */  doMouseZoom: function(wheelDelta, timeStamp, pageX, pageY) {    var change = wheelDelta > 0 ? 0.97 : 1.03;    return this.zoomTo(this.__zoomLevel * change, false, pageX - this.__clientLeft, pageY - this.__clientTop);  },  /**   * Touch start handler for scrolling support   */  doTouchStart: function(touches, timeStamp) {    var self = this;    // remember if the deceleration was just stopped    self.__decStopped = !!(self.__isDecelerating || self.__isAnimating);    self.hintResize();    if (timeStamp instanceof Date) {      timeStamp = timeStamp.valueOf();    }    if (typeof timeStamp !== "number") {      timeStamp = Date.now();    }    // Reset interruptedAnimation flag    self.__interruptedAnimation = true;    // Stop deceleration    if (self.__isDecelerating) {      zyngaCore.effect.Animate.stop(self.__isDecelerating);      self.__isDecelerating = false;      self.__interruptedAnimation = true;    }    // Stop animation    if (self.__isAnimating) {      zyngaCore.effect.Animate.stop(self.__isAnimating);      self.__isAnimating = false;      self.__interruptedAnimation = true;    }    // Use center point when dealing with two fingers    var currentTouchLeft, currentTouchTop;    var isSingleTouch = touches.length === 1;    if (isSingleTouch) {      currentTouchLeft = touches[0].pageX;      currentTouchTop = touches[0].pageY;    } else {      currentTouchLeft = Math.abs(touches[0].pageX + touches[1].pageX) / 2;      currentTouchTop = Math.abs(touches[0].pageY + touches[1].pageY) / 2;    }    // Store initial positions    self.__initialTouchLeft = currentTouchLeft;    self.__initialTouchTop = currentTouchTop;    // Store initial touchList for scale calculation    self.__initialTouches = touches;    // Store current zoom level    self.__zoomLevelStart = self.__zoomLevel;    // Store initial touch positions    self.__lastTouchLeft = currentTouchLeft;    self.__lastTouchTop = currentTouchTop;    // Store initial move time stamp    self.__lastTouchMove = timeStamp;    // Reset initial scale    self.__lastScale = 1;    // Reset locking flags    self.__enableScrollX = !isSingleTouch && self.options.scrollingX;    self.__enableScrollY = !isSingleTouch && self.options.scrollingY;    // Reset tracking flag    self.__isTracking = true;    // Reset deceleration complete flag    self.__didDecelerationComplete = false;    // Dragging starts directly with two fingers, otherwise lazy with an offset    self.__isDragging = !isSingleTouch;    // Some features are disabled in multi touch scenarios    self.__isSingleTouch = isSingleTouch;    // Clearing data structure    self.__positions = [];  },  /**   * Touch move handler for scrolling support   */  doTouchMove: function(touches, timeStamp, scale) {    if (timeStamp instanceof Date) {      timeStamp = timeStamp.valueOf();    }    if (typeof timeStamp !== "number") {      timeStamp = Date.now();    }    var self = this;    // Ignore event when tracking is not enabled (event might be outside of element)    if (!self.__isTracking) {      return;    }    var currentTouchLeft, currentTouchTop;    // Compute move based around of center of fingers    if (touches.length === 2) {      currentTouchLeft = Math.abs(touches[0].pageX + touches[1].pageX) / 2;      currentTouchTop = Math.abs(touches[0].pageY + touches[1].pageY) / 2;      // Calculate scale when not present and only when touches are used      if (!scale && self.options.zooming) {        scale = self.__getScale(self.__initialTouches, touches);      }    } else {      currentTouchLeft = touches[0].pageX;      currentTouchTop = touches[0].pageY;    }    var positions = self.__positions;    // Are we already is dragging mode?    if (self.__isDragging) {        self.__decStopped = false;      // Compute move distance      var moveX = currentTouchLeft - self.__lastTouchLeft;      var moveY = currentTouchTop - self.__lastTouchTop;      // Read previous scroll position and zooming      var scrollLeft = self.__scrollLeft;      var scrollTop = self.__scrollTop;      var level = self.__zoomLevel;      // Work with scaling      if (scale != null && self.options.zooming) {        var oldLevel = level;        // Recompute level based on previous scale and new scale        level = level / self.__lastScale * scale;        // Limit level according to configuration        level = Math.max(Math.min(level, self.options.maxZoom), self.options.minZoom);        // Only do further compution when change happened        if (oldLevel !== level) {          // Compute relative event position to container          var currentTouchLeftRel = currentTouchLeft - self.__clientLeft;          var currentTouchTopRel = currentTouchTop - self.__clientTop;          // Recompute left and top coordinates based on new zoom level          scrollLeft = ((currentTouchLeftRel + scrollLeft) * level / oldLevel) - currentTouchLeftRel;          scrollTop = ((currentTouchTopRel + scrollTop) * level / oldLevel) - currentTouchTopRel;          // Recompute max scroll values          self.__computeScrollMax(level);        }      }      if (self.__enableScrollX) {        scrollLeft -= moveX * self.options.speedMultiplier;        var maxScrollLeft = self.__maxScrollLeft;        if (scrollLeft > maxScrollLeft || scrollLeft < 0) {          // Slow down on the edges          if (self.options.bouncing) {            scrollLeft += (moveX / 2 * self.options.speedMultiplier);          } else if (scrollLeft > maxScrollLeft) {            scrollLeft = maxScrollLeft;          } else {            scrollLeft = 0;          }        }      }      // Compute new vertical scroll position      if (self.__enableScrollY) {        scrollTop -= moveY * self.options.speedMultiplier;        var maxScrollTop = self.__maxScrollTop;        if (scrollTop > maxScrollTop || scrollTop < 0) {          // Slow down on the edges          if (self.options.bouncing || (self.__refreshHeight && scrollTop < 0)) {            scrollTop += (moveY / 2 * self.options.speedMultiplier);            // Support pull-to-refresh (only when only y is scrollable)            if (!self.__enableScrollX && self.__refreshHeight != null) {              // hide the refresher when it's behind the header bar in case of header transparency              if (scrollTop < 0) {                self.__refreshHidden = false;                self.__refreshShow();              } else {                self.__refreshHide();                self.__refreshHidden = true;              }              if (!self.__refreshActive && scrollTop <= -self.__refreshHeight) {                self.__refreshActive = true;                if (self.__refreshActivate) {                  self.__refreshActivate();                }              } else if (self.__refreshActive && scrollTop > -self.__refreshHeight) {                self.__refreshActive = false;                if (self.__refreshDeactivate) {                  self.__refreshDeactivate();                }              }            }          } else if (scrollTop > maxScrollTop) {            scrollTop = maxScrollTop;          } else {            scrollTop = 0;          }        } else if (self.__refreshHeight && !self.__refreshHidden) {          // if a positive scroll value and the refresher is still not hidden, hide it          self.__refreshHide();          self.__refreshHidden = true;        }      }      // Keep list from growing infinitely (holding min 10, max 20 measure points)      if (positions.length > 60) {        positions.splice(0, 30);      }      // Track scroll movement for decleration      positions.push(scrollLeft, scrollTop, timeStamp);      // Sync scroll position      self.__publish(scrollLeft, scrollTop, level);    // Otherwise figure out whether we are switching into dragging mode now.    } else {      var minimumTrackingForScroll = self.options.locking ? 3 : 0;      var minimumTrackingForDrag = 5;      var distanceX = Math.abs(currentTouchLeft - self.__initialTouchLeft);      var distanceY = Math.abs(currentTouchTop - self.__initialTouchTop);      self.__enableScrollX = self.options.scrollingX && distanceX >= minimumTrackingForScroll;      self.__enableScrollY = self.options.scrollingY && distanceY >= minimumTrackingForScroll;      positions.push(self.__scrollLeft, self.__scrollTop, timeStamp);      self.__isDragging = (self.__enableScrollX || self.__enableScrollY) && (distanceX >= minimumTrackingForDrag || distanceY >= minimumTrackingForDrag);      if (self.__isDragging) {        self.__interruptedAnimation = false;        self.__fadeScrollbars('in');      }    }    // Update last touch positions and time stamp for next event    self.__lastTouchLeft = currentTouchLeft;    self.__lastTouchTop = currentTouchTop;    self.__lastTouchMove = timeStamp;    self.__lastScale = scale;  },  /**   * Touch end handler for scrolling support   */  doTouchEnd: function(e, timeStamp) {    if (timeStamp instanceof Date) {      timeStamp = timeStamp.valueOf();    }    if (typeof timeStamp !== "number") {      timeStamp = Date.now();    }    var self = this;    // Ignore event when tracking is not enabled (no touchstart event on element)    // This is required as this listener ('touchmove') sits on the document and not on the element itself.    if (!self.__isTracking) {      return;    }    // Not touching anymore (when two finger hit the screen there are two touch end events)    self.__isTracking = false;    // Be sure to reset the dragging flag now. Here we also detect whether    // the finger has moved fast enough to switch into a deceleration animation.    if (self.__isDragging) {      // Reset dragging flag      self.__isDragging = false;      // Start deceleration      // Verify that the last move detected was in some relevant time frame      if (self.__isSingleTouch && self.options.animating && (timeStamp - self.__lastTouchMove) <= 100) {        // Then figure out what the scroll position was about 100ms ago        var positions = self.__positions;        var endPos = positions.length - 1;        var startPos = endPos;        // Move pointer to position measured 100ms ago        for (var i = endPos; i > 0 && positions[i] > (self.__lastTouchMove - 100); i -= 3) {          startPos = i;        }        // If start and stop position is identical in a 100ms timeframe,        // we cannot compute any useful deceleration.        if (startPos !== endPos) {          // Compute relative movement between these two points          var timeOffset = positions[endPos] - positions[startPos];          var movedLeft = self.__scrollLeft - positions[startPos - 2];          var movedTop = self.__scrollTop - positions[startPos - 1];          // Based on 50ms compute the movement to apply for each render step          self.__decelerationVelocityX = movedLeft / timeOffset * (1000 / 60);          self.__decelerationVelocityY = movedTop / timeOffset * (1000 / 60);          // How much velocity is required to start the deceleration          var minVelocityToStartDeceleration = self.options.paging || self.options.snapping ? self.options.decelVelocityThresholdPaging : self.options.decelVelocityThreshold;          // Verify that we have enough velocity to start deceleration          if (Math.abs(self.__decelerationVelocityX) > minVelocityToStartDeceleration || Math.abs(self.__decelerationVelocityY) > minVelocityToStartDeceleration) {            // Deactivate pull-to-refresh when decelerating            if (!self.__refreshActive) {              self.__startDeceleration(timeStamp);            }          }        } else {          self.__scrollingComplete();        }      } else if ((timeStamp - self.__lastTouchMove) > 100) {        self.__scrollingComplete();      }    } else if (self.__decStopped) {      // the deceleration was stopped      // user flicked the scroll fast, and stop dragging, then did a touchstart to stop the srolling      // tell the touchend event code to do nothing, we don't want to actually send a click      e.isTapHandled = true;      self.__decStopped = false;    }    // If this was a slower move it is per default non decelerated, but this    // still means that we want snap back to the bounds which is done here.    // This is placed outside the condition above to improve edge case stability    // e.g. touchend fired without enabled dragging. This should normally do not    // have modified the scroll positions or even showed the scrollbars though.    if (!self.__isDecelerating) {      if (self.__refreshActive && self.__refreshStart) {        // Use publish instead of scrollTo to allow scrolling to out of boundary position        // We don't need to normalize scrollLeft, zoomLevel, etc. here because we only y-scrolling when pull-to-refresh is enabled        self.__publish(self.__scrollLeft, -self.__refreshHeight, self.__zoomLevel, true);        var d = new Date();        self.refreshStartTime = d.getTime();        if (self.__refreshStart) {          self.__refreshStart();        }        // for iOS-ey style scrolling        if (!ionic.Platform.isAndroid())self.__startDeceleration();      } else {        if (self.__interruptedAnimation || self.__isDragging) {          self.__scrollingComplete();        }        self.scrollTo(self.__scrollLeft, self.__scrollTop, true, self.__zoomLevel);        // Directly signalize deactivation (nothing todo on refresh?)        if (self.__refreshActive) {          self.__refreshActive = false;          if (self.__refreshDeactivate) {            self.__refreshDeactivate();          }        }      }    }    // Fully cleanup list    self.__positions.length = 0;  },  /*  ---------------------------------------------------------------------------    PRIVATE API  ---------------------------------------------------------------------------  */  /**   * Applies the scroll position to the content element   *   * @param left {Number} Left scroll position   * @param top {Number} Top scroll position   * @param animate {Boolean} Whether animation should be used to move to the new coordinates   */  __publish: function(left, top, zoom, animate, wasResize) {    var self = this;    // Remember whether we had an animation, then we try to continue based on the current "drive" of the animation    var wasAnimating = self.__isAnimating;    if (wasAnimating) {      zyngaCore.effect.Animate.stop(wasAnimating);      self.__isAnimating = false;    }    if (animate && self.options.animating) {      // Keep scheduled positions for scrollBy/zoomBy functionality      self.__scheduledLeft = left;      self.__scheduledTop = top;      self.__scheduledZoom = zoom;      var oldLeft = self.__scrollLeft;      var oldTop = self.__scrollTop;      var oldZoom = self.__zoomLevel;      var diffLeft = left - oldLeft;      var diffTop = top - oldTop;      var diffZoom = zoom - oldZoom;      var step = function(percent, now, render) {        if (render) {          self.__scrollLeft = oldLeft + (diffLeft * percent);          self.__scrollTop = oldTop + (diffTop * percent);          self.__zoomLevel = oldZoom + (diffZoom * percent);          // Push values out          if (self.__callback) {            self.__callback(self.__scrollLeft, self.__scrollTop, self.__zoomLevel, wasResize);          }        }      };      var verify = function(id) {        return self.__isAnimating === id;      };      var completed = function(renderedFramesPerSecond, animationId, wasFinished) {        if (animationId === self.__isAnimating) {          self.__isAnimating = false;        }        if (self.__didDecelerationComplete || wasFinished) {          self.__scrollingComplete();        }        if (self.options.zooming) {          self.__computeScrollMax();        }      };      // When continuing based on previous animation we choose an ease-out animation instead of ease-in-out      self.__isAnimating = zyngaCore.effect.Animate.start(step, verify, completed, self.options.animationDuration, wasAnimating ? easeOutCubic : easeInOutCubic);    } else {      self.__scheduledLeft = self.__scrollLeft = left;      self.__scheduledTop = self.__scrollTop = top;      self.__scheduledZoom = self.__zoomLevel = zoom;      // Push values out      if (self.__callback) {        self.__callback(left, top, zoom, wasResize);      }      // Fix max scroll ranges      if (self.options.zooming) {        self.__computeScrollMax();      }    }  },  /**   * Recomputes scroll minimum values based on client dimensions and content dimensions.   */  __computeScrollMax: function(zoomLevel) {    var self = this;    if (zoomLevel == null) {      zoomLevel = self.__zoomLevel;    }    self.__maxScrollLeft = Math.max((self.__contentWidth * zoomLevel) - self.__clientWidth, 0);    self.__maxScrollTop = Math.max((self.__contentHeight * zoomLevel) - self.__clientHeight, 0);    if (!self.__didWaitForSize && !self.__maxScrollLeft && !self.__maxScrollTop) {      self.__didWaitForSize = true;      self.__waitForSize();    }  },  /**   * If the scroll view isn't sized correctly on start, wait until we have at least some size   */  __waitForSize: function() {    var self = this;    clearTimeout(self.__sizerTimeout);    var sizer = function() {      self.resize(true);    };    sizer();    self.__sizerTimeout = setTimeout(sizer, 500);  },  /*  ---------------------------------------------------------------------------    ANIMATION (DECELERATION) SUPPORT  ---------------------------------------------------------------------------  */  /**   * Called when a touch sequence end and the speed of the finger was high enough   * to switch into deceleration mode.   */  __startDeceleration: function() {    var self = this;    if (self.options.paging) {      var scrollLeft = Math.max(Math.min(self.__scrollLeft, self.__maxScrollLeft), 0);      var scrollTop = Math.max(Math.min(self.__scrollTop, self.__maxScrollTop), 0);      var clientWidth = self.__clientWidth;      var clientHeight = self.__clientHeight;      // We limit deceleration not to the min/max values of the allowed range, but to the size of the visible client area.      // Each page should have exactly the size of the client area.      self.__minDecelerationScrollLeft = Math.floor(scrollLeft / clientWidth) * clientWidth;      self.__minDecelerationScrollTop = Math.floor(scrollTop / clientHeight) * clientHeight;      self.__maxDecelerationScrollLeft = Math.ceil(scrollLeft / clientWidth) * clientWidth;      self.__maxDecelerationScrollTop = Math.ceil(scrollTop / clientHeight) * clientHeight;    } else {      self.__minDecelerationScrollLeft = 0;      self.__minDecelerationScrollTop = 0;      self.__maxDecelerationScrollLeft = self.__maxScrollLeft;      self.__maxDecelerationScrollTop = self.__maxScrollTop;      if (self.__refreshActive) self.__minDecelerationScrollTop = self.__refreshHeight * -1;    }    // Wrap class method    var step = function(percent, now, render) {      self.__stepThroughDeceleration(render);    };    // How much velocity is required to keep the deceleration running    self.__minVelocityToKeepDecelerating = self.options.snapping ? 4 : 0.1;    // Detect whether it's still worth to continue animating steps    // If we are already slow enough to not being user perceivable anymore, we stop the whole process here.    var verify = function() {      var shouldContinue = Math.abs(self.__decelerationVelocityX) >= self.__minVelocityToKeepDecelerating ||        Math.abs(self.__decelerationVelocityY) >= self.__minVelocityToKeepDecelerating;      if (!shouldContinue) {        self.__didDecelerationComplete = true;        //Make sure the scroll values are within the boundaries after a bounce,        //not below 0 or above maximum        if (self.options.bouncing && !self.__refreshActive) {          self.scrollTo(            Math.min( Math.max(self.__scrollLeft, 0), self.__maxScrollLeft ),            Math.min( Math.max(self.__scrollTop, 0), self.__maxScrollTop ),            self.__refreshActive          );        }      }      return shouldContinue;    };    var completed = function() {      self.__isDecelerating = false;      if (self.__didDecelerationComplete) {        self.__scrollingComplete();      }      // Animate to grid when snapping is active, otherwise just fix out-of-boundary positions      if (self.options.paging) {        self.scrollTo(self.__scrollLeft, self.__scrollTop, self.options.snapping);      }    };    // Start animation and switch on flag    self.__isDecelerating = zyngaCore.effect.Animate.start(step, verify, completed);  },  /**   * Called on every step of the animation   *   * @param inMemory {Boolean} Whether to not render the current step, but keep it in memory only. Used internally only!   */  __stepThroughDeceleration: function(render) {    var self = this;    //    // COMPUTE NEXT SCROLL POSITION    //    // Add deceleration to scroll position    var scrollLeft = self.__scrollLeft + self.__decelerationVelocityX;// * self.options.deceleration);    var scrollTop = self.__scrollTop + self.__decelerationVelocityY;// * self.options.deceleration);    //    // HARD LIMIT SCROLL POSITION FOR NON BOUNCING MODE    //    if (!self.options.bouncing) {      var scrollLeftFixed = Math.max(Math.min(self.__maxDecelerationScrollLeft, scrollLeft), self.__minDecelerationScrollLeft);      if (scrollLeftFixed !== scrollLeft) {        scrollLeft = scrollLeftFixed;        self.__decelerationVelocityX = 0;      }      var scrollTopFixed = Math.max(Math.min(self.__maxDecelerationScrollTop, scrollTop), self.__minDecelerationScrollTop);      if (scrollTopFixed !== scrollTop) {        scrollTop = scrollTopFixed;        self.__decelerationVelocityY = 0;      }    }    //    // UPDATE SCROLL POSITION    //    if (render) {      self.__publish(scrollLeft, scrollTop, self.__zoomLevel);    } else {      self.__scrollLeft = scrollLeft;      self.__scrollTop = scrollTop;    }    //    // SLOW DOWN    //    // Slow down velocity on every iteration    if (!self.options.paging) {      // This is the factor applied to every iteration of the animation      // to slow down the process. This should emulate natural behavior where      // objects slow down when the initiator of the movement is removed      var frictionFactor = self.options.deceleration;      self.__decelerationVelocityX *= frictionFactor;      self.__decelerationVelocityY *= frictionFactor;    }    //    // BOUNCING SUPPORT    //    if (self.options.bouncing) {      var scrollOutsideX = 0;      var scrollOutsideY = 0;      // This configures the amount of change applied to deceleration/acceleration when reaching boundaries      var penetrationDeceleration = self.options.penetrationDeceleration;      var penetrationAcceleration = self.options.penetrationAcceleration;      // Check limits      if (scrollLeft < self.__minDecelerationScrollLeft) {        scrollOutsideX = self.__minDecelerationScrollLeft - scrollLeft;      } else if (scrollLeft > self.__maxDecelerationScrollLeft) {        scrollOutsideX = self.__maxDecelerationScrollLeft - scrollLeft;      }      if (scrollTop < self.__minDecelerationScrollTop) {        scrollOutsideY = self.__minDecelerationScrollTop - scrollTop;      } else if (scrollTop > self.__maxDecelerationScrollTop) {        scrollOutsideY = self.__maxDecelerationScrollTop - scrollTop;      }      // Slow down until slow enough, then flip back to snap position      if (scrollOutsideX !== 0) {        var isHeadingOutwardsX = scrollOutsideX * self.__decelerationVelocityX <= self.__minDecelerationScrollLeft;        if (isHeadingOutwardsX) {          self.__decelerationVelocityX += scrollOutsideX * penetrationDeceleration;        }        var isStoppedX = Math.abs(self.__decelerationVelocityX) <= self.__minVelocityToKeepDecelerating;        //If we're not heading outwards, or if the above statement got us below minDeceleration, go back towards bounds        if (!isHeadingOutwardsX || isStoppedX) {          self.__decelerationVelocityX = scrollOutsideX * penetrationAcceleration;        }      }      if (scrollOutsideY !== 0) {        var isHeadingOutwardsY = scrollOutsideY * self.__decelerationVelocityY <= self.__minDecelerationScrollTop;        if (isHeadingOutwardsY) {          self.__decelerationVelocityY += scrollOutsideY * penetrationDeceleration;        }        var isStoppedY = Math.abs(self.__decelerationVelocityY) <= self.__minVelocityToKeepDecelerating;        //If we're not heading outwards, or if the above statement got us below minDeceleration, go back towards bounds        if (!isHeadingOutwardsY || isStoppedY) {          self.__decelerationVelocityY = scrollOutsideY * penetrationAcceleration;        }      }    }  },  /**   * calculate the distance between two touches   * @param   {Touch}     touch1   * @param   {Touch}     touch2   * @returns {Number}    distance   */  __getDistance: function getDistance(touch1, touch2) {    var x = touch2.pageX - touch1.pageX,    y = touch2.pageY - touch1.pageY;    return Math.sqrt((x * x) + (y * y));  },  /**   * calculate the scale factor between two touchLists (fingers)   * no scale is 1, and goes down to 0 when pinched together, and bigger when pinched out   * @param   {Array}     start   * @param   {Array}     end   * @returns {Number}    scale   */  __getScale: function getScale(start, end) {    // need two fingers...    if (start.length >= 2 && end.length >= 2) {      return this.__getDistance(end[0], end[1]) /        this.__getDistance(start[0], start[1]);    }    return 1;  }});ionic.scroll = {  isScrolling: false,  lastTop: 0};})(ionic);(function(ionic) {  var NOOP = function() {};  var deprecated = function(name) {    void 0;  };  ionic.views.ScrollNative = ionic.views.View.inherit({    initialize: function(options) {      var self = this;      self.__container = self.el = options.el;      self.__content = options.el.firstElementChild;      // Whether scrolling is frozen or not      self.__frozen = false;      self.isNative = true;      self.__scrollTop = self.el.scrollTop;      self.__scrollLeft = self.el.scrollLeft;      self.__clientHeight = self.__content.clientHeight;      self.__clientWidth = self.__content.clientWidth;      self.__maxScrollTop = Math.max((self.__contentHeight) - self.__clientHeight, 0);      self.__maxScrollLeft = Math.max((self.__contentWidth) - self.__clientWidth, 0);      if(options.startY >= 0 || options.startX >= 0) {        ionic.requestAnimationFrame(function() {          self.el.scrollTop = options.startY || 0;          self.el.scrollLeft = options.startX || 0;          self.__scrollTop = self.el.scrollTop;          self.__scrollLeft = self.el.scrollLeft;        });      }      self.options = {        freeze: false,        getContentWidth: function() {          return Math.max(self.__content.scrollWidth, self.__content.offsetWidth);        },        getContentHeight: function() {          return Math.max(self.__content.scrollHeight, self.__content.offsetHeight + (self.__content.offsetTop * 2));        }      };      for (var key in options) {        self.options[key] = options[key];      }      /**       * Sets isScrolling to true, and automatically deactivates if not called again in 80ms.       */      self.onScroll = function() {        if (!ionic.scroll.isScrolling) {          ionic.scroll.isScrolling = true;        }        clearTimeout(self.scrollTimer);        self.scrollTimer = setTimeout(function() {          ionic.scroll.isScrolling = false;        }, 80);      };      self.freeze = function(shouldFreeze) {        self.__frozen = shouldFreeze;      };      // A more powerful freeze pop that dominates all other freeze pops      self.freezeShut = function(shouldFreezeShut) {        self.__frozenShut = shouldFreezeShut;      };      self.__initEventHandlers();    },    /**  Methods not used in native scrolling */    __callback: function() { deprecated('__callback'); },    zoomTo: function() { deprecated('zoomTo'); },    zoomBy: function() { deprecated('zoomBy'); },    activatePullToRefresh: function() { deprecated('activatePullToRefresh'); },    /**     * Returns the scroll position and zooming values     *     * @return {Map} `left` and `top` scroll position and `zoom` level     */    resize: function(continueScrolling) {      var self = this;      if (!self.__container || !self.options) return;      // Update Scroller dimensions for changed content      // Add padding to bottom of content      self.setDimensions(        self.__container.clientWidth,        self.__container.clientHeight,        self.options.getContentWidth(),        self.options.getContentHeight(),        continueScrolling      );    },    /**     * Initialize the scrollview     * In native scrolling, this only means we need to gather size information     */    run: function() {      this.resize();    },    /**     * Returns the scroll position and zooming values     *     * @return {Map} `left` and `top` scroll position and `zoom` level     */    getValues: function() {      var self = this;      self.update();      return {        left: self.__scrollLeft,        top: self.__scrollTop,        zoom: 1      };    },    /**     * Updates the __scrollLeft and __scrollTop values to el's current value     */    update: function() {      var self = this;      self.__scrollLeft = self.el.scrollLeft;      self.__scrollTop = self.el.scrollTop;    },    /**     * Configures the dimensions of the client (outer) and content (inner) elements.     * Requires the available space for the outer element and the outer size of the inner element.     * All values which are falsy (null or zero etc.) are ignored and the old value is kept.     *     * @param clientWidth {Integer} Inner width of outer element     * @param clientHeight {Integer} Inner height of outer element     * @param contentWidth {Integer} Outer width of inner element     * @param contentHeight {Integer} Outer height of inner element     */    setDimensions: function(clientWidth, clientHeight, contentWidth, contentHeight) {      var self = this;      if (!clientWidth && !clientHeight && !contentWidth && !contentHeight) {        // this scrollview isn't rendered, don't bother        return;      }      // Only update values which are defined      if (clientWidth === +clientWidth) {        self.__clientWidth = clientWidth;      }      if (clientHeight === +clientHeight) {        self.__clientHeight = clientHeight;      }      if (contentWidth === +contentWidth) {        self.__contentWidth = contentWidth;      }      if (contentHeight === +contentHeight) {        self.__contentHeight = contentHeight;      }      // Refresh maximums      self.__computeScrollMax();    },    /**     * Returns the maximum scroll values     *     * @return {Map} `left` and `top` maximum scroll values     */    getScrollMax: function() {      return {        left: this.__maxScrollLeft,        top: this.__maxScrollTop      };    },    /**     * Scrolls by the given amount in px.     *     * @param left {Number} Horizontal scroll position, keeps current if value is <code>null</code>     * @param top {Number} Vertical scroll position, keeps current if value is <code>null</code>     * @param animate {Boolean} Whether the scrolling should happen using an animation     */    scrollBy: function(left, top, animate) {      var self = this;      // update scroll vars before refferencing them      self.update();      var startLeft = self.__isAnimating ? self.__scheduledLeft : self.__scrollLeft;      var startTop = self.__isAnimating ? self.__scheduledTop : self.__scrollTop;      self.scrollTo(startLeft + (left || 0), startTop + (top || 0), animate);    },    /**     * Scrolls to the given position in px.     *     * @param left {Number} Horizontal scroll position, keeps current if value is <code>null</code>     * @param top {Number} Vertical scroll position, keeps current if value is <code>null</code>     * @param animate {Boolean} Whether the scrolling should happen using an animation     */    scrollTo: function(left, top, animate) {      var self = this;      if (!animate) {        self.el.scrollTop = top;        self.el.scrollLeft = left;        self.resize();        return;      }      var oldOverflowX = self.el.style.overflowX;      var oldOverflowY = self.el.style.overflowY;      clearTimeout(self.__scrollToCleanupTimeout);      self.__scrollToCleanupTimeout = setTimeout(function() {        self.el.style.overflowX = oldOverflowX;        self.el.style.overflowY = oldOverflowY;      }, 500);      self.el.style.overflowY = 'hidden';      self.el.style.overflowX = 'hidden';      animateScroll(top, left);      function animateScroll(Y, X) {        // scroll animation loop w/ easing        // credit https://gist.github.com/dezinezync/5487119        var start = Date.now(),          duration = 250, //milliseconds          fromY = self.el.scrollTop,          fromX = self.el.scrollLeft;        if (fromY === Y && fromX === X) {          self.el.style.overflowX = oldOverflowX;          self.el.style.overflowY = oldOverflowY;          self.resize();          return; /* Prevent scrolling to the Y point if already there */        }        // decelerating to zero velocity        function easeOutCubic(t) {          return (--t) * t * t + 1;        }        // scroll loop        function animateScrollStep() {          var currentTime = Date.now(),            time = Math.min(1, ((currentTime - start) / duration)),          // where .5 would be 50% of time on a linear scale easedT gives a          // fraction based on the easing method            easedT = easeOutCubic(time);          if (fromY != Y) {            self.el.scrollTop = parseInt((easedT * (Y - fromY)) + fromY, 10);          }          if (fromX != X) {            self.el.scrollLeft = parseInt((easedT * (X - fromX)) + fromX, 10);          }          if (time < 1) {            ionic.requestAnimationFrame(animateScrollStep);          } else {            // done            ionic.tap.removeClonedInputs(self.__container, self);            self.el.style.overflowX = oldOverflowX;            self.el.style.overflowY = oldOverflowY;            self.resize();          }        }        // start scroll loop        ionic.requestAnimationFrame(animateScrollStep);      }    },    /*     ---------------------------------------------------------------------------     PRIVATE API     ---------------------------------------------------------------------------     */    /**     * If the scroll view isn't sized correctly on start, wait until we have at least some size     */    __waitForSize: function() {      var self = this;      clearTimeout(self.__sizerTimeout);      var sizer = function() {        self.resize(true);      };      sizer();      self.__sizerTimeout = setTimeout(sizer, 500);    },    /**     * Recomputes scroll minimum values based on client dimensions and content dimensions.     */    __computeScrollMax: function() {      var self = this;      self.__maxScrollLeft = Math.max((self.__contentWidth) - self.__clientWidth, 0);      self.__maxScrollTop = Math.max((self.__contentHeight) - self.__clientHeight, 0);      if (!self.__didWaitForSize && !self.__maxScrollLeft && !self.__maxScrollTop) {        self.__didWaitForSize = true;        self.__waitForSize();      }    },    __initEventHandlers: function() {      var self = this;      // Event Handler      var container = self.__container;      // save height when scroll view is shrunk so we don't need to reflow      var scrollViewOffsetHeight;      var lastKeyboardHeight;      /**       * Shrink the scroll view when the keyboard is up if necessary and if the       * focused input is below the bottom of the shrunk scroll view, scroll it       * into view.       */      self.scrollChildIntoView = function(e) {        var rect = container.getBoundingClientRect();        if(!self.__originalContainerHeight) {          self.__originalContainerHeight = rect.height;        }        // D        //var scrollBottomOffsetToTop = rect.bottom;        // D - A        scrollViewOffsetHeight = self.__originalContainerHeight;        //console.log('Scroll view offset height', scrollViewOffsetHeight);        //console.dir(container);        var alreadyShrunk = self.isShrunkForKeyboard;        var isModal = container.parentNode.classList.contains('modal');        var isPopover = container.parentNode.classList.contains('popover');        // 680px is when the media query for 60% modal width kicks in        var isInsetModal = isModal && window.innerWidth >= 680;       /*        *  _______        * |---A---| <- top of scroll view        * |       |        * |---B---| <- keyboard        * |   C   | <- input        * |---D---| <- initial bottom of scroll view        * |___E___| <- bottom of viewport        *        *  All commented calculations relative to the top of the viewport (ie E        *  is the viewport height, not 0)        */        var changedKeyboardHeight = lastKeyboardHeight && (lastKeyboardHeight !== e.detail.keyboardHeight);        if (!alreadyShrunk || changedKeyboardHeight) {          // shrink scrollview so we can actually scroll if the input is hidden          // if it isn't shrink so we can scroll to inputs under the keyboard          // inset modals won't shrink on Android on their own when the keyboard appears          if ( !isPopover && (ionic.Platform.isIOS() || ionic.Platform.isFullScreen || isInsetModal) ) {            // if there are things below the scroll view account for them and            // subtract them from the keyboard height when resizing            // E - D                         E                         D            //var scrollBottomOffsetToBottom = e.detail.viewportHeight - scrollBottomOffsetToTop;            // 0 or D - B if D > B           E - B                     E - D            //var keyboardOffset = e.detail.keyboardHeight - scrollBottomOffsetToBottom;            ionic.requestAnimationFrame(function(){              // D - A or B - A if D > B       D - A             max(0, D - B)              scrollViewOffsetHeight = Math.max(0, Math.min(self.__originalContainerHeight, self.__originalContainerHeight - (e.detail.keyboardHeight - 43)));//keyboardOffset >= 0 ? scrollViewOffsetHeight - keyboardOffset : scrollViewOffsetHeight + keyboardOffset;              //console.log('Old container height', self.__originalContainerHeight, 'New container height', scrollViewOffsetHeight, 'Keyboard height', e.detail.keyboardHeight);              container.style.height = scrollViewOffsetHeight + "px";              /*              if (ionic.Platform.isIOS()) {                // Force redraw to avoid disappearing content                var disp = container.style.display;                container.style.display = 'none';                var trick = container.offsetHeight;                container.style.display = disp;              }              */              container.classList.add('keyboard-up');              //update scroll view              self.resize();            });          }          self.isShrunkForKeyboard = true;        }        lastKeyboardHeight = e.detail.keyboardHeight;        /*         *  _______         * |---A---| <- top of scroll view         * |   *   | <- where we want to scroll to         * |--B-D--| <- keyboard, bottom of scroll view         * |   C   | <- input         * |       |         * |___E___| <- bottom of viewport         *         *  All commented calculations relative to the top of the viewport (ie E         *  is the viewport height, not 0)         */        // if the element is positioned under the keyboard scroll it into view        if (e.detail.isElementUnderKeyboard) {          ionic.requestAnimationFrame(function(){            var pos = ionic.DomUtil.getOffsetTop(e.detail.target);            setTimeout(function() {              if (ionic.Platform.isIOS()) {                ionic.tap.cloneFocusedInput(container, self);              }              // Scroll the input into view, with a 100px buffer              self.scrollTo(0, pos - (rect.top + 100), true);              self.onScroll();            }, 32);            /*            // update D if we shrunk            if (self.isShrunkForKeyboard && !alreadyShrunk) {              scrollBottomOffsetToTop = container.getBoundingClientRect().bottom;              console.log('Scroll bottom', scrollBottomOffsetToTop);            }            // middle of the scrollview, this is where we want to scroll to            // (D - A) / 2            var scrollMidpointOffset = scrollViewOffsetHeight * 0.5;            console.log('Midpoint', scrollMidpointOffset);            //console.log("container.offsetHeight: " + scrollViewOffsetHeight);            // middle of the input we want to scroll into view            // C            var inputMidpoint = ((e.detail.elementBottom + e.detail.elementTop) / 2);            console.log('Input midpoint');            // distance from middle of input to the bottom of the scroll view            // C - D                                C               D            var inputMidpointOffsetToScrollBottom = inputMidpoint - scrollBottomOffsetToTop;            console.log('Input midpoint offset', inputMidpointOffsetToScrollBottom);            //C - D + (D - A)/2          C - D                     (D - A)/ 2            var scrollTop = inputMidpointOffsetToScrollBottom + scrollMidpointOffset;            console.log('Scroll top', scrollTop);            if ( scrollTop > 0) {              if (ionic.Platform.isIOS()) {                //just shrank scroll view, give it some breathing room before scrolling                setTimeout(function(){                  ionic.tap.cloneFocusedInput(container, self);                  self.scrollBy(0, scrollTop, true);                  self.onScroll();                }, 32);              } else {                self.scrollBy(0, scrollTop, true);                self.onScroll();              }            }            */          });        }        // Only the first scrollView parent of the element that broadcasted this event        // (the active element that needs to be shown) should receive this event        e.stopPropagation();      };      self.resetScrollView = function() {        //return scrollview to original height once keyboard has hidden        if (self.isShrunkForKeyboard) {          self.isShrunkForKeyboard = false;          container.style.height = "";          /*          if (ionic.Platform.isIOS()) {            // Force redraw to avoid disappearing content            var disp = container.style.display;            container.style.display = 'none';            var trick = container.offsetHeight;            container.style.display = disp;          }          */          self.__originalContainerHeight = container.getBoundingClientRect().height;          if (ionic.Platform.isIOS()) {            ionic.requestAnimationFrame(function() {              container.classList.remove('keyboard-up');            });          }        }        self.resize();      };      self.handleTouchMove = function(e) {        if (self.__frozenShut) {          e.preventDefault();          e.stopPropagation();          return false;        } else if ( self.__frozen ){          e.preventDefault();          // let it propagate so other events such as drag events can happen,          // but don't let it actually scroll          return false;        }        return true;      };      container.addEventListener('scroll', self.onScroll);      //Broadcasted when keyboard is shown on some platforms.      //See js/utils/keyboard.js      container.addEventListener('scrollChildIntoView', self.scrollChildIntoView);      container.addEventListener(ionic.EVENTS.touchstart, self.handleTouchMove);      container.addEventListener(ionic.EVENTS.touchmove, self.handleTouchMove);      // Listen on document because container may not have had the last      // keyboardActiveElement, for example after closing a modal with a focused      // input and returning to a previously resized scroll view in an ion-content.      // Since we can only resize scroll views that are currently visible, just resize      // the current scroll view when the keyboard is closed.      document.addEventListener('resetScrollView', self.resetScrollView);    },    __cleanup: function() {      var self = this;      var container = self.__container;      container.removeEventListener('resetScrollView', self.resetScrollView);      container.removeEventListener('scroll', self.onScroll);      container.removeEventListener('scrollChildIntoView', self.scrollChildIntoView);      container.removeEventListener('resetScrollView', self.resetScrollView);      container.removeEventListener(ionic.EVENTS.touchstart, self.handleTouchMove);      container.removeEventListener(ionic.EVENTS.touchmove, self.handleTouchMove);      ionic.tap.removeClonedInputs(container, self);      delete self.__container;      delete self.__content;      delete self.__indicatorX;      delete self.__indicatorY;      delete self.options.el;      self.resize = self.scrollTo = self.onScroll = self.resetScrollView = NOOP;      self.scrollChildIntoView = NOOP;      container = null;    }  });})(ionic);(function(ionic) {'use strict';  var ITEM_CLASS = 'item';  var ITEM_CONTENT_CLASS = 'item-content';  var ITEM_SLIDING_CLASS = 'item-sliding';  var ITEM_OPTIONS_CLASS = 'item-options';  var ITEM_PLACEHOLDER_CLASS = 'item-placeholder';  var ITEM_REORDERING_CLASS = 'item-reordering';  var ITEM_REORDER_BTN_CLASS = 'item-reorder';  var DragOp = function() {};  DragOp.prototype = {    start: function(){},    drag: function(){},    end: function(){},    isSameItem: function() {      return false;    }  };  var SlideDrag = function(opts) {    this.dragThresholdX = opts.dragThresholdX || 10;    this.el = opts.el;    this.item = opts.item;    this.canSwipe = opts.canSwipe;  };  SlideDrag.prototype = new DragOp();  SlideDrag.prototype.start = function(e) {    var content, buttons, offsetX, buttonsWidth;    if (!this.canSwipe()) {      return;    }    if (e.target.classList.contains(ITEM_CONTENT_CLASS)) {      content = e.target;    } else if (e.target.classList.contains(ITEM_CLASS)) {      content = e.target.querySelector('.' + ITEM_CONTENT_CLASS);    } else {      content = ionic.DomUtil.getParentWithClass(e.target, ITEM_CONTENT_CLASS);    }    // If we don't have a content area as one of our children (or ourselves), skip    if (!content) {      return;    }    // Make sure we aren't animating as we slide    content.classList.remove(ITEM_SLIDING_CLASS);    // Grab the starting X point for the item (for example, so we can tell whether it is open or closed to start)    offsetX = parseFloat(content.style[ionic.CSS.TRANSFORM].replace('translate3d(', '').split(',')[0]) || 0;    // Grab the buttons    buttons = content.parentNode.querySelector('.' + ITEM_OPTIONS_CLASS);    if (!buttons) {      return;    }    buttons.classList.remove('invisible');    buttonsWidth = buttons.offsetWidth;    this._currentDrag = {      buttons: buttons,      buttonsWidth: buttonsWidth,      content: content,      startOffsetX: offsetX    };  };  /**   * Check if this is the same item that was previously dragged.   */  SlideDrag.prototype.isSameItem = function(op) {    if (op._lastDrag && this._currentDrag) {      return this._currentDrag.content == op._lastDrag.content;    }    return false;  };  SlideDrag.prototype.clean = function(isInstant) {    var lastDrag = this._lastDrag;    if (!lastDrag || !lastDrag.content) return;    lastDrag.content.style[ionic.CSS.TRANSITION] = '';    lastDrag.content.style[ionic.CSS.TRANSFORM] = '';    if (isInstant) {      lastDrag.content.style[ionic.CSS.TRANSITION] = 'none';      makeInvisible();      ionic.requestAnimationFrame(function() {        lastDrag.content.style[ionic.CSS.TRANSITION] = '';      });    } else {      ionic.requestAnimationFrame(function() {        setTimeout(makeInvisible, 250);      });    }    function makeInvisible() {      lastDrag.buttons && lastDrag.buttons.classList.add('invisible');    }  };  SlideDrag.prototype.drag = ionic.animationFrameThrottle(function(e) {    var buttonsWidth;    // We really aren't dragging    if (!this._currentDrag) {      return;    }    // Check if we should start dragging. Check if we've dragged past the threshold,    // or we are starting from the open state.    if (!this._isDragging &&        ((Math.abs(e.gesture.deltaX) > this.dragThresholdX) ||        (Math.abs(this._currentDrag.startOffsetX) > 0))) {      this._isDragging = true;    }    if (this._isDragging) {      buttonsWidth = this._currentDrag.buttonsWidth;      // Grab the new X point, capping it at zero      var newX = Math.min(0, this._currentDrag.startOffsetX + e.gesture.deltaX);      // If the new X position is past the buttons, we need to slow down the drag (rubber band style)      if (newX < -buttonsWidth) {        // Calculate the new X position, capped at the top of the buttons        newX = Math.min(-buttonsWidth, -buttonsWidth + (((e.gesture.deltaX + buttonsWidth) * 0.4)));      }      this._currentDrag.content.$$ionicOptionsOpen = newX !== 0;      this._currentDrag.content.style[ionic.CSS.TRANSFORM] = 'translate3d(' + newX + 'px, 0, 0)';      this._currentDrag.content.style[ionic.CSS.TRANSITION] = 'none';    }  });  SlideDrag.prototype.end = function(e, doneCallback) {    var self = this;    // There is no drag, just end immediately    if (!self._currentDrag) {      doneCallback && doneCallback();      return;    }    // If we are currently dragging, we want to snap back into place    // The final resting point X will be the width of the exposed buttons    var restingPoint = -self._currentDrag.buttonsWidth;    // Check if the drag didn't clear the buttons mid-point    // and we aren't moving fast enough to swipe open    if (e.gesture.deltaX > -(self._currentDrag.buttonsWidth / 2)) {      // If we are going left but too slow, or going right, go back to resting      if (e.gesture.direction == "left" && Math.abs(e.gesture.velocityX) < 0.3) {        restingPoint = 0;      } else if (e.gesture.direction == "right") {        restingPoint = 0;      }    }    ionic.requestAnimationFrame(function() {      if (restingPoint === 0) {        self._currentDrag.content.style[ionic.CSS.TRANSFORM] = '';        var buttons = self._currentDrag.buttons;        setTimeout(function() {          buttons && buttons.classList.add('invisible');        }, 250);      } else {        self._currentDrag.content.style[ionic.CSS.TRANSFORM] = 'translate3d(' + restingPoint + 'px,0,0)';      }      self._currentDrag.content.style[ionic.CSS.TRANSITION] = '';      // Kill the current drag      if (!self._lastDrag) {        self._lastDrag = {};      }      ionic.extend(self._lastDrag, self._currentDrag);      if (self._currentDrag) {        self._currentDrag.buttons = null;        self._currentDrag.content = null;      }      self._currentDrag = null;      // We are done, notify caller      doneCallback && doneCallback();    });  };  var ReorderDrag = function(opts) {    var self = this;    self.dragThresholdY = opts.dragThresholdY || 0;    self.onReorder = opts.onReorder;    self.listEl = opts.listEl;    self.el = self.item = opts.el;    self.scrollEl = opts.scrollEl;    self.scrollView = opts.scrollView;    // Get the True Top of the list el http://www.quirksmode.org/js/findpos.html    self.listElTrueTop = 0;    if (self.listEl.offsetParent) {      var obj = self.listEl;      do {        self.listElTrueTop += obj.offsetTop;        obj = obj.offsetParent;      } while (obj);    }  };  ReorderDrag.prototype = new DragOp();  ReorderDrag.prototype._moveElement = function(e) {    var y = e.gesture.center.pageY +      this.scrollView.getValues().top -      (this._currentDrag.elementHeight / 2) -      this.listElTrueTop;    this.el.style[ionic.CSS.TRANSFORM] = 'translate3d(0, ' + y + 'px, 0)';  };  ReorderDrag.prototype.deregister = function() {    this.listEl = this.el = this.scrollEl = this.scrollView = null;  };  ReorderDrag.prototype.start = function(e) {    var startIndex = ionic.DomUtil.getChildIndex(this.el, this.el.nodeName.toLowerCase());    var elementHeight = this.el.scrollHeight;    var placeholder = this.el.cloneNode(true);    placeholder.classList.add(ITEM_PLACEHOLDER_CLASS);    this.el.parentNode.insertBefore(placeholder, this.el);    this.el.classList.add(ITEM_REORDERING_CLASS);    this._currentDrag = {      elementHeight: elementHeight,      startIndex: startIndex,      placeholder: placeholder,      scrollHeight: scroll,      list: placeholder.parentNode    };    this._moveElement(e);  };  ReorderDrag.prototype.drag = ionic.animationFrameThrottle(function(e) {    // We really aren't dragging    var self = this;    if (!this._currentDrag) {      return;    }    var scrollY = 0;    var pageY = e.gesture.center.pageY;    var offset = this.listElTrueTop;    //If we have a scrollView, check scroll boundaries for dragged element and scroll if necessary    if (this.scrollView) {      var container = this.scrollView.__container;      scrollY = this.scrollView.getValues().top;      var containerTop = container.offsetTop;      var pixelsPastTop = containerTop - pageY + this._currentDrag.elementHeight / 2;      var pixelsPastBottom = pageY + this._currentDrag.elementHeight / 2 - containerTop - container.offsetHeight;      if (e.gesture.deltaY < 0 && pixelsPastTop > 0 && scrollY > 0) {        this.scrollView.scrollBy(null, -pixelsPastTop);        //Trigger another drag so the scrolling keeps going        ionic.requestAnimationFrame(function() {          self.drag(e);        });      }      if (e.gesture.deltaY > 0 && pixelsPastBottom > 0) {        if (scrollY < this.scrollView.getScrollMax().top) {          this.scrollView.scrollBy(null, pixelsPastBottom);          //Trigger another drag so the scrolling keeps going          ionic.requestAnimationFrame(function() {            self.drag(e);          });        }      }    }    // Check if we should start dragging. Check if we've dragged past the threshold,    // or we are starting from the open state.    if (!this._isDragging && Math.abs(e.gesture.deltaY) > this.dragThresholdY) {      this._isDragging = true;    }    if (this._isDragging) {      this._moveElement(e);      this._currentDrag.currentY = scrollY + pageY - offset;      // this._reorderItems();    }  });  // When an item is dragged, we need to reorder any items for sorting purposes  ReorderDrag.prototype._getReorderIndex = function() {    var self = this;    var siblings = Array.prototype.slice.call(self._currentDrag.placeholder.parentNode.children)      .filter(function(el) {        return el.nodeName === self.el.nodeName && el !== self.el;      });    var dragOffsetTop = self._currentDrag.currentY;    var el;    for (var i = 0, len = siblings.length; i < len; i++) {      el = siblings[i];      if (i === len - 1) {        if (dragOffsetTop > el.offsetTop) {          return i;        }      } else if (i === 0) {        if (dragOffsetTop < el.offsetTop + el.offsetHeight) {          return i;        }      } else if (dragOffsetTop > el.offsetTop - el.offsetHeight / 2 &&                 dragOffsetTop < el.offsetTop + el.offsetHeight) {        return i;      }    }    return self._currentDrag.startIndex;  };  ReorderDrag.prototype.end = function(e, doneCallback) {    if (!this._currentDrag) {      doneCallback && doneCallback();      return;    }    var placeholder = this._currentDrag.placeholder;    var finalIndex = this._getReorderIndex();    // Reposition the element    this.el.classList.remove(ITEM_REORDERING_CLASS);    this.el.style[ionic.CSS.TRANSFORM] = '';    placeholder.parentNode.insertBefore(this.el, placeholder);    placeholder.parentNode.removeChild(placeholder);    this.onReorder && this.onReorder(this.el, this._currentDrag.startIndex, finalIndex);    this._currentDrag = {      placeholder: null,      content: null    };    this._currentDrag = null;    doneCallback && doneCallback();  };  /**   * The ListView handles a list of items. It will process drag animations, edit mode,   * and other operations that are common on mobile lists or table views.   */  ionic.views.ListView = ionic.views.View.inherit({    initialize: function(opts) {      var self = this;      opts = ionic.extend({        onReorder: function() {},        virtualRemoveThreshold: -200,        virtualAddThreshold: 200,        canSwipe: function() {          return true;        }      }, opts);      ionic.extend(self, opts);      if (!self.itemHeight && self.listEl) {        self.itemHeight = self.listEl.children[0] && parseInt(self.listEl.children[0].style.height, 10);      }      self.onRefresh = opts.onRefresh || function() {};      self.onRefreshOpening = opts.onRefreshOpening || function() {};      self.onRefreshHolding = opts.onRefreshHolding || function() {};      var gestureOpts = {};      // don't prevent native scrolling      if (ionic.DomUtil.getParentOrSelfWithClass(self.el, 'overflow-scroll')) {        gestureOpts.prevent_default_directions = ['left', 'right'];      }      window.ionic.onGesture('release', function(e) {        self._handleEndDrag(e);      }, self.el, gestureOpts);      window.ionic.onGesture('drag', function(e) {        self._handleDrag(e);      }, self.el, gestureOpts);      // Start the drag states      self._initDrag();    },    /**     * Be sure to cleanup references.     */    deregister: function() {      this.el = this.listEl = this.scrollEl = this.scrollView = null;      // ensure no scrolls have been left frozen      if (this.isScrollFreeze) {        self.scrollView.freeze(false);      }    },    /**     * Called to tell the list to stop refreshing. This is useful     * if you are refreshing the list and are done with refreshing.     */    stopRefreshing: function() {      var refresher = this.el.querySelector('.list-refresher');      refresher.style.height = '0';    },    /**     * If we scrolled and have virtual mode enabled, compute the window     * of active elements in order to figure out the viewport to render.     */    didScroll: function(e) {      var self = this;      if (self.isVirtual) {        var itemHeight = self.itemHeight;        // Grab the total height of the list        var scrollHeight = e.target.scrollHeight;        // Get the viewport height        var viewportHeight = self.el.parentNode.offsetHeight;        // High water is the pixel position of the first element to include (everything before        // that will be removed)        var highWater = Math.max(0, e.scrollTop + self.virtualRemoveThreshold);        // Low water is the pixel position of the last element to include (everything after        // that will be removed)        var lowWater = Math.min(scrollHeight, Math.abs(e.scrollTop) + viewportHeight + self.virtualAddThreshold);        // Get the first and last elements in the list based on how many can fit        // between the pixel range of lowWater and highWater        var first = parseInt(Math.abs(highWater / itemHeight), 10);        var last = parseInt(Math.abs(lowWater / itemHeight), 10);        // Get the items we need to remove        self._virtualItemsToRemove = Array.prototype.slice.call(self.listEl.children, 0, first);        self.renderViewport && self.renderViewport(highWater, lowWater, first, last);      }    },    didStopScrolling: function() {      if (this.isVirtual) {        for (var i = 0; i < this._virtualItemsToRemove.length; i++) {          //el.parentNode.removeChild(el);          this.didHideItem && this.didHideItem(i);        }        // Once scrolling stops, check if we need to remove old items      }    },    /**     * Clear any active drag effects on the list.     */    clearDragEffects: function(isInstant) {      if (this._lastDragOp) {        this._lastDragOp.clean && this._lastDragOp.clean(isInstant);        this._lastDragOp.deregister && this._lastDragOp.deregister();        this._lastDragOp = null;      }    },    _initDrag: function() {      // Store the last one      if (this._lastDragOp) {        this._lastDragOp.deregister && this._lastDragOp.deregister();      }      this._lastDragOp = this._dragOp;      this._dragOp = null;    },    // Return the list item from the given target    _getItem: function(target) {      while (target) {        if (target.classList && target.classList.contains(ITEM_CLASS)) {          return target;        }        target = target.parentNode;      }      return null;    },    _startDrag: function(e) {      var self = this;      self._isDragging = false;      var lastDragOp = self._lastDragOp;      var item;      // If we have an open SlideDrag and we're scrolling the list. Clear it.      if (self._didDragUpOrDown && lastDragOp instanceof SlideDrag) {          lastDragOp.clean && lastDragOp.clean();      }      // Check if this is a reorder drag      if (ionic.DomUtil.getParentOrSelfWithClass(e.target, ITEM_REORDER_BTN_CLASS) && (e.gesture.direction == 'up' || e.gesture.direction == 'down')) {        item = self._getItem(e.target);        if (item) {          self._dragOp = new ReorderDrag({            listEl: self.el,            el: item,            scrollEl: self.scrollEl,            scrollView: self.scrollView,            onReorder: function(el, start, end) {              self.onReorder && self.onReorder(el, start, end);            }          });          self._dragOp.start(e);          e.preventDefault();        }      }      // Or check if this is a swipe to the side drag      else if (!self._didDragUpOrDown && (e.gesture.direction == 'left' || e.gesture.direction == 'right') && Math.abs(e.gesture.deltaX) > 5) {        // Make sure this is an item with buttons        item = self._getItem(e.target);        if (item && item.querySelector('.item-options')) {          self._dragOp = new SlideDrag({            el: self.el,            item: item,            canSwipe: self.canSwipe          });          self._dragOp.start(e);          e.preventDefault();          self.isScrollFreeze = self.scrollView.freeze(true);        }      }      // If we had a last drag operation and this is a new one on a different item, clean that last one      if (lastDragOp && self._dragOp && !self._dragOp.isSameItem(lastDragOp) && e.defaultPrevented) {        lastDragOp.clean && lastDragOp.clean();      }    },    _handleEndDrag: function(e) {      var self = this;      if (self.scrollView) {        self.isScrollFreeze = self.scrollView.freeze(false);      }      self._didDragUpOrDown = false;      if (!self._dragOp) {        return;      }      self._dragOp.end(e, function() {        self._initDrag();      });    },    /**     * Process the drag event to move the item to the left or right.     */    _handleDrag: function(e) {      var self = this;      if (Math.abs(e.gesture.deltaY) > 5) {        self._didDragUpOrDown = true;      }      // If we get a drag event, make sure we aren't in another drag, then check if we should      // start one      if (!self.isDragging && !self._dragOp) {        self._startDrag(e);      }      // No drag still, pass it up      if (!self._dragOp) {        return;      }      e.gesture.srcEvent.preventDefault();      self._dragOp.drag(e);    }  });})(ionic);(function(ionic) {'use strict';  ionic.views.Modal = ionic.views.View.inherit({    initialize: function(opts) {      opts = ionic.extend({        focusFirstInput: false,        unfocusOnHide: true,        focusFirstDelay: 600,        backdropClickToClose: true,        hardwareBackButtonClose: true,      }, opts);      ionic.extend(this, opts);      this.el = opts.el;    },    show: function() {      var self = this;      if(self.focusFirstInput) {        // Let any animations run first        window.setTimeout(function() {          var input = self.el.querySelector('input, textarea');          input && input.focus && input.focus();        }, self.focusFirstDelay);      }    },    hide: function() {      // Unfocus all elements      if(this.unfocusOnHide) {        var inputs = this.el.querySelectorAll('input, textarea');        // Let any animations run first        window.setTimeout(function() {          for(var i = 0; i < inputs.length; i++) {            inputs[i].blur && inputs[i].blur();          }        });      }    }  });})(ionic);(function(ionic) {'use strict';  /**   * The side menu view handles one of the side menu's in a Side Menu Controller   * configuration.   * It takes a DOM reference to that side menu element.   */  ionic.views.SideMenu = ionic.views.View.inherit({    initialize: function(opts) {      this.el = opts.el;      this.isEnabled = (typeof opts.isEnabled === 'undefined') ? true : opts.isEnabled;      this.setWidth(opts.width);    },    getFullWidth: function() {      return this.width;    },    setWidth: function(width) {      this.width = width;      this.el.style.width = width + 'px';    },    setIsEnabled: function(isEnabled) {      this.isEnabled = isEnabled;    },    bringUp: function() {      if(this.el.style.zIndex !== '0') {        this.el.style.zIndex = '0';      }    },    pushDown: function() {      if(this.el.style.zIndex !== '-1') {        this.el.style.zIndex = '-1';      }    }  });  ionic.views.SideMenuContent = ionic.views.View.inherit({    initialize: function(opts) {      ionic.extend(this, {        animationClass: 'menu-animated',        onDrag: function() {},        onEndDrag: function() {}      }, opts);      ionic.onGesture('drag', ionic.proxy(this._onDrag, this), this.el);      ionic.onGesture('release', ionic.proxy(this._onEndDrag, this), this.el);    },    _onDrag: function(e) {      this.onDrag && this.onDrag(e);    },    _onEndDrag: function(e) {      this.onEndDrag && this.onEndDrag(e);    },    disableAnimation: function() {      this.el.classList.remove(this.animationClass);    },    enableAnimation: function() {      this.el.classList.add(this.animationClass);    },    getTranslateX: function() {      return parseFloat(this.el.style[ionic.CSS.TRANSFORM].replace('translate3d(', '').split(',')[0]);    },    setTranslateX: ionic.animationFrameThrottle(function(x) {      this.el.style[ionic.CSS.TRANSFORM] = 'translate3d(' + x + 'px, 0, 0)';    })  });})(ionic);/* * Adapted from Swipe.js 2.0 * * Brad Birdsall * Copyright 2013, MIT License **/(function(ionic) {'use strict';ionic.views.Slider = ionic.views.View.inherit({  initialize: function (options) {    var slider = this;    var touchStartEvent, touchMoveEvent, touchEndEvent;    if (window.navigator.pointerEnabled) {      touchStartEvent = 'pointerdown';      touchMoveEvent = 'pointermove';      touchEndEvent = 'pointerup';    } else if (window.navigator.msPointerEnabled) {      touchStartEvent = 'MSPointerDown';      touchMoveEvent = 'MSPointerMove';      touchEndEvent = 'MSPointerUp';    } else {      touchStartEvent = 'touchstart';      touchMoveEvent = 'touchmove';      touchEndEvent = 'touchend';    }    var mouseStartEvent = 'mousedown';    var mouseMoveEvent = 'mousemove';    var mouseEndEvent = 'mouseup';    // utilities    var noop = function() {}; // simple no operation function    var offloadFn = function(fn) { setTimeout(fn || noop, 0); }; // offload a functions execution    // check browser capabilities    var browser = {      addEventListener: !!window.addEventListener,      transitions: (function(temp) {        var props = ['transitionProperty', 'WebkitTransition', 'MozTransition', 'OTransition', 'msTransition'];        for ( var i in props ) if (temp.style[ props[i] ] !== undefined) return true;        return false;      })(document.createElement('swipe'))    };    var container = options.el;    // quit if no root element    if (!container) return;    var element = container.children[0];    var slides, slidePos, width, length;    options = options || {};    var index = parseInt(options.startSlide, 10) || 0;    var speed = options.speed || 300;    options.continuous = options.continuous !== undefined ? options.continuous : true;    function setup() {      // do not setup if the container has no width      if (!container.offsetWidth) {        return;      }      // cache slides      slides = element.children;      length = slides.length;      // set continuous to false if only one slide      if (slides.length < 2) options.continuous = false;      //special case if two slides      if (browser.transitions && options.continuous && slides.length < 3) {        element.appendChild(slides[0].cloneNode(true));        element.appendChild(element.children[1].cloneNode(true));        slides = element.children;      }      // create an array to store current positions of each slide      slidePos = new Array(slides.length);      // determine width of each slide      width = container.offsetWidth || container.getBoundingClientRect().width;      element.style.width = (slides.length * width) + 'px';      // stack elements      var pos = slides.length;      while(pos--) {        var slide = slides[pos];        slide.style.width = width + 'px';        slide.setAttribute('data-index', pos);        if (browser.transitions) {          slide.style.left = (pos * -width) + 'px';          move(pos, index > pos ? -width : (index < pos ? width : 0), 0);        }      }      // reposition elements before and after index      if (options.continuous && browser.transitions) {        move(circle(index - 1), -width, 0);        move(circle(index + 1), width, 0);      }      if (!browser.transitions) element.style.left = (index * -width) + 'px';      container.style.visibility = 'visible';      options.slidesChanged && options.slidesChanged();    }    function prev(slideSpeed) {      if (options.continuous) slide(index - 1, slideSpeed);      else if (index) slide(index - 1, slideSpeed);    }    function next(slideSpeed) {      if (options.continuous) slide(index + 1, slideSpeed);      else if (index < slides.length - 1) slide(index + 1, slideSpeed);    }    function circle(index) {      // a simple positive modulo using slides.length      return (slides.length + (index % slides.length)) % slides.length;    }    function slide(to, slideSpeed) {      // do nothing if already on requested slide      if (index == to) return;      if (!slides) {        index = to;        return;      }      if (browser.transitions) {        var direction = Math.abs(index - to) / (index - to); // 1: backward, -1: forward        // get the actual position of the slide        if (options.continuous) {          var naturalDirection = direction;          direction = -slidePos[circle(to)] / width;          // if going forward but to < index, use to = slides.length + to          // if going backward but to > index, use to = -slides.length + to          if (direction !== naturalDirection) to = -direction * slides.length + to;        }        var diff = Math.abs(index - to) - 1;        // move all the slides between index and to in the right direction        while (diff--) move( circle((to > index ? to : index) - diff - 1), width * direction, 0);        to = circle(to);        move(index, width * direction, slideSpeed || speed);        move(to, 0, slideSpeed || speed);        if (options.continuous) move(circle(to - direction), -(width * direction), 0); // we need to get the next in place      } else {        to = circle(to);        animate(index * -width, to * -width, slideSpeed || speed);        //no fallback for a circular continuous if the browser does not accept transitions      }      index = to;      offloadFn(options.callback && options.callback(index, slides[index]));    }    function move(index, dist, speed) {      translate(index, dist, speed);      slidePos[index] = dist;    }    function translate(index, dist, speed) {      var slide = slides[index];      var style = slide && slide.style;      if (!style) return;      style.webkitTransitionDuration =      style.MozTransitionDuration =      style.msTransitionDuration =      style.OTransitionDuration =      style.transitionDuration = speed + 'ms';      style.webkitTransform = 'translate(' + dist + 'px,0)' + 'translateZ(0)';      style.msTransform =      style.MozTransform =      style.OTransform = 'translateX(' + dist + 'px)';    }    function animate(from, to, speed) {      // if not an animation, just reposition      if (!speed) {        element.style.left = to + 'px';        return;      }      var start = +new Date();      var timer = setInterval(function() {        var timeElap = +new Date() - start;        if (timeElap > speed) {          element.style.left = to + 'px';          if (delay) begin();          options.transitionEnd && options.transitionEnd.call(event, index, slides[index]);          clearInterval(timer);          return;        }        element.style.left = (( (to - from) * (Math.floor((timeElap / speed) * 100) / 100) ) + from) + 'px';      }, 4);    }    // setup auto slideshow    var delay = options.auto || 0;    var interval;    function begin() {      interval = setTimeout(next, delay);    }    function stop() {      delay = options.auto || 0;      clearTimeout(interval);    }    // setup initial vars    var start = {};    var delta = {};    var isScrolling;    // setup event capturing    var events = {      handleEvent: function(event) {        if(!event.touches && event.pageX && event.pageY) {          event.touches = [{            pageX: event.pageX,            pageY: event.pageY          }];        }        switch (event.type) {          case touchStartEvent: this.start(event); break;          case mouseStartEvent: this.start(event); break;          case touchMoveEvent: this.touchmove(event); break;          case mouseMoveEvent: this.touchmove(event); break;          case touchEndEvent: offloadFn(this.end(event)); break;          case mouseEndEvent: offloadFn(this.end(event)); break;          case 'webkitTransitionEnd':          case 'msTransitionEnd':          case 'oTransitionEnd':          case 'otransitionend':          case 'transitionend': offloadFn(this.transitionEnd(event)); break;          case 'resize': offloadFn(setup); break;        }        if (options.stopPropagation) event.stopPropagation();      },      start: function(event) {        // prevent to start if there is no valid event        if (!event.touches) {          return;        }        var touches = event.touches[0];        // measure start values        start = {          // get initial touch coords          x: touches.pageX,          y: touches.pageY,          // store time to determine touch duration          time: +new Date()        };        // used for testing first move event        isScrolling = undefined;        // reset delta and end measurements        delta = {};        // attach touchmove and touchend listeners        element.addEventListener(touchMoveEvent, this, false);        element.addEventListener(mouseMoveEvent, this, false);        element.addEventListener(touchEndEvent, this, false);        element.addEventListener(mouseEndEvent, this, false);        document.addEventListener(touchEndEvent, this, false);        document.addEventListener(mouseEndEvent, this, false);      },      touchmove: function(event) {        // ensure there is a valid event        // ensure swiping with one touch and not pinching        // ensure sliding is enabled        if (!event.touches ||            event.touches.length > 1 ||            event.scale && event.scale !== 1 ||            slider.slideIsDisabled) {          return;        }        if (options.disableScroll) event.preventDefault();        var touches = event.touches[0];        // measure change in x and y        delta = {          x: touches.pageX - start.x,          y: touches.pageY - start.y        };        // determine if scrolling test has run - one time test        if ( typeof isScrolling == 'undefined') {          isScrolling = !!( isScrolling || Math.abs(delta.x) < Math.abs(delta.y) );        }        // if user is not trying to scroll vertically        if (!isScrolling) {          // prevent native scrolling          event.preventDefault();          // stop slideshow          stop();          // increase resistance if first or last slide          if (options.continuous) { // we don't add resistance at the end            translate(circle(index - 1), delta.x + slidePos[circle(index - 1)], 0);            translate(index, delta.x + slidePos[index], 0);            translate(circle(index + 1), delta.x + slidePos[circle(index + 1)], 0);          } else {            // If the slider bounces, do the bounce!            if(options.bouncing) {              delta.x =               delta.x /                 ( (!index && delta.x > 0 ||         // if first slide and sliding left                   index == slides.length - 1 &&     // or if last slide and sliding right                   delta.x < 0                       // and if sliding at all                 ) ?                 ( Math.abs(delta.x) / width + 1 )      // determine resistance level                 : 1 );                                 // no resistance if false             } else {               if(width * index - delta.x < 0) {               //We are trying scroll past left boundary                 delta.x = Math.min(delta.x, width * index);  //Set delta.x so we don't go past left screen               }               if(Math.abs(delta.x) > width * (slides.length - index - 1)){         //We are trying to scroll past right bondary                 delta.x = Math.max( -width * (slides.length - index - 1), delta.x);  //Set delta.x so we don't go past right screen               }             }            // translate 1:1            translate(index - 1, delta.x + slidePos[index - 1], 0);            translate(index, delta.x + slidePos[index], 0);            translate(index + 1, delta.x + slidePos[index + 1], 0);          }          options.onDrag && options.onDrag();        }      },      end: function() {        // measure duration        var duration = +new Date() - start.time;        // determine if slide attempt triggers next/prev slide        var isValidSlide =              Number(duration) < 250 &&         // if slide duration is less than 250ms              Math.abs(delta.x) > 20 ||         // and if slide amt is greater than 20px              Math.abs(delta.x) > width / 2;      // or if slide amt is greater than half the width        // determine if slide attempt is past start and end        var isPastBounds = (!index && delta.x > 0) ||      // if first slide and slide amt is greater than 0              (index == slides.length - 1 && delta.x < 0); // or if last slide and slide amt is less than 0        if (options.continuous) isPastBounds = false;        // determine direction of swipe (true:right, false:left)        var direction = delta.x < 0;        // if not scrolling vertically        if (!isScrolling) {          if (isValidSlide && !isPastBounds) {            if (direction) {              if (options.continuous) { // we need to get the next in this direction in place                move(circle(index - 1), -width, 0);                move(circle(index + 2), width, 0);              } else {                move(index - 1, -width, 0);              }              move(index, slidePos[index] - width, speed);              move(circle(index + 1), slidePos[circle(index + 1)] - width, speed);              index = circle(index + 1);            } else {              if (options.continuous) { // we need to get the next in this direction in place                move(circle(index + 1), width, 0);                move(circle(index - 2), -width, 0);              } else {                move(index + 1, width, 0);              }              move(index, slidePos[index] + width, speed);              move(circle(index - 1), slidePos[circle(index - 1)] + width, speed);              index = circle(index - 1);            }            options.callback && options.callback(index, slides[index]);          } else {            if (options.continuous) {              move(circle(index - 1), -width, speed);              move(index, 0, speed);              move(circle(index + 1), width, speed);            } else {              move(index - 1, -width, speed);              move(index, 0, speed);              move(index + 1, width, speed);            }          }        }        // kill touchmove and touchend event listeners until touchstart called again        element.removeEventListener(touchMoveEvent, events, false);        element.removeEventListener(mouseMoveEvent, events, false);        element.removeEventListener(touchEndEvent, events, false);        element.removeEventListener(mouseEndEvent, events, false);        document.removeEventListener(touchEndEvent, events, false);        document.removeEventListener(mouseEndEvent, events, false);        options.onDragEnd && options.onDragEnd();      },      transitionEnd: function(event) {        if (parseInt(event.target.getAttribute('data-index'), 10) == index) {          if (delay) begin();          options.transitionEnd && options.transitionEnd.call(event, index, slides[index]);        }      }    };    // Public API    this.update = function() {      setTimeout(setup);    };    this.setup = function() {      setup();    };    this.loop = function(value) {      if (arguments.length) options.continuous = !!value;      return options.continuous;    };    this.enableSlide = function(shouldEnable) {      if (arguments.length) {        this.slideIsDisabled = !shouldEnable;      }      return !this.slideIsDisabled;    };    this.slide = this.select = function(to, speed) {      // cancel slideshow      stop();      slide(to, speed);    };    this.prev = this.previous = function() {      // cancel slideshow      stop();      prev();    };    this.next = function() {      // cancel slideshow      stop();      next();    };    this.stop = function() {      // cancel slideshow      stop();    };    this.start = function() {      begin();    };    this.autoPlay = function(newDelay) {      if (!delay || delay < 0) {        stop();      } else {        delay = newDelay;        begin();      }    };    this.currentIndex = this.selected = function() {      // return current index position      return index;    };    this.slidesCount = this.count = function() {      // return total number of slides      return length;    };    this.kill = function() {      // cancel slideshow      stop();      // reset element      element.style.width = '';      element.style.left = '';      // reset slides so no refs are held on to      slides && (slides = []);      // removed event listeners      if (browser.addEventListener) {        // remove current event listeners        element.removeEventListener(touchStartEvent, events, false);        element.removeEventListener(mouseStartEvent, events, false);        element.removeEventListener('webkitTransitionEnd', events, false);        element.removeEventListener('msTransitionEnd', events, false);        element.removeEventListener('oTransitionEnd', events, false);        element.removeEventListener('otransitionend', events, false);        element.removeEventListener('transitionend', events, false);        window.removeEventListener('resize', events, false);      }      else {        window.onresize = null;      }    };    this.load = function() {      // trigger setup      setup();      // start auto slideshow if applicable      if (delay) begin();      // add event listeners      if (browser.addEventListener) {        // set touchstart event on element        element.addEventListener(touchStartEvent, events, false);        element.addEventListener(mouseStartEvent, events, false);        if (browser.transitions) {          element.addEventListener('webkitTransitionEnd', events, false);          element.addEventListener('msTransitionEnd', events, false);          element.addEventListener('oTransitionEnd', events, false);          element.addEventListener('otransitionend', events, false);          element.addEventListener('transitionend', events, false);        }        // set resize event on window        window.addEventListener('resize', events, false);      } else {        window.onresize = function () { setup(); }; // to play nice with old IE      }    };  }});})(ionic);/*eslint space-after-keywords: 0*//** * Swiper 3.2.7 * Most modern mobile touch slider and framework with hardware accelerated transitions * * http://www.idangero.us/swiper/ * * Copyright 2015, Vladimir Kharlampidi * The iDangero.us * http://www.idangero.us/ * * Licensed under MIT * * Released on: December 7, 2015 */(function () {    'use strict';    var $;    /*===========================    Swiper    ===========================*/    var Swiper = function (container, params, _scope, $compile) {        if (!(this instanceof Swiper)) return new Swiper(container, params);        var defaults = {            direction: 'horizontal',            touchEventsTarget: 'container',            initialSlide: 0,            speed: 300,            // autoplay            autoplay: false,            autoplayDisableOnInteraction: true,            // To support iOS's swipe-to-go-back gesture (when being used in-app, with UIWebView).            iOSEdgeSwipeDetection: false,            iOSEdgeSwipeThreshold: 20,            // Free mode            freeMode: false,            freeModeMomentum: true,            freeModeMomentumRatio: 1,            freeModeMomentumBounce: true,            freeModeMomentumBounceRatio: 1,            freeModeSticky: false,            freeModeMinimumVelocity: 0.02,            // Autoheight            autoHeight: false,            // Set wrapper width            setWrapperSize: false,            // Virtual Translate            virtualTranslate: false,            // Effects            effect: 'slide', // 'slide' or 'fade' or 'cube' or 'coverflow'            coverflow: {                rotate: 50,                stretch: 0,                depth: 100,                modifier: 1,                slideShadows : true            },            cube: {                slideShadows: true,                shadow: true,                shadowOffset: 20,                shadowScale: 0.94            },            fade: {                crossFade: false            },            // Parallax            parallax: false,            // Scrollbar            scrollbar: null,            scrollbarHide: true,            scrollbarDraggable: false,            scrollbarSnapOnRelease: false,            // Keyboard Mousewheel            keyboardControl: false,            mousewheelControl: false,            mousewheelReleaseOnEdges: false,            mousewheelInvert: false,            mousewheelForceToAxis: false,            mousewheelSensitivity: 1,            // Hash Navigation            hashnav: false,            // Breakpoints            breakpoints: undefined,            // Slides grid            spaceBetween: 0,            slidesPerView: 1,            slidesPerColumn: 1,            slidesPerColumnFill: 'column',            slidesPerGroup: 1,            centeredSlides: false,            slidesOffsetBefore: 0, // in px            slidesOffsetAfter: 0, // in px            // Round length            roundLengths: false,            // Touches            touchRatio: 1,            touchAngle: 45,            simulateTouch: true,            shortSwipes: true,            longSwipes: true,            longSwipesRatio: 0.5,            longSwipesMs: 300,            followFinger: true,            onlyExternal: false,            threshold: 0,            touchMoveStopPropagation: true,            // Pagination            pagination: null,            paginationElement: 'span',            paginationClickable: false,            paginationHide: false,            paginationBulletRender: null,            // Resistance            resistance: true,            resistanceRatio: 0.85,            // Next/prev buttons            nextButton: null,            prevButton: null,            // Progress            watchSlidesProgress: false,            watchSlidesVisibility: false,            // Cursor            grabCursor: false,            // Clicks            preventClicks: true,            preventClicksPropagation: true,            slideToClickedSlide: false,            // Lazy Loading            lazyLoading: false,            lazyLoadingInPrevNext: false,            lazyLoadingOnTransitionStart: false,            // Images            preloadImages: true,            updateOnImagesReady: true,            // loop            loop: false,            loopAdditionalSlides: 0,            loopedSlides: null,            // Control            control: undefined,            controlInverse: false,            controlBy: 'slide', //or 'container'            // Swiping/no swiping            allowSwipeToPrev: true,            allowSwipeToNext: true,            swipeHandler: null, //'.swipe-handler',            noSwiping: true,            noSwipingClass: 'swiper-no-swiping',            // NS            slideClass: 'swiper-slide',            slideActiveClass: 'swiper-slide-active',            slideVisibleClass: 'swiper-slide-visible',            slideDuplicateClass: 'swiper-slide-duplicate',            slideNextClass: 'swiper-slide-next',            slidePrevClass: 'swiper-slide-prev',            wrapperClass: 'swiper-wrapper',            bulletClass: 'swiper-pagination-bullet',            bulletActiveClass: 'swiper-pagination-bullet-active',            buttonDisabledClass: 'swiper-button-disabled',            paginationHiddenClass: 'swiper-pagination-hidden',            // Observer            observer: false,            observeParents: false,            // Accessibility            a11y: false,            prevSlideMessage: 'Previous slide',            nextSlideMessage: 'Next slide',            firstSlideMessage: 'This is the first slide',            lastSlideMessage: 'This is the last slide',            paginationBulletMessage: 'Go to slide {{index}}',            // Callbacks            runCallbacksOnInit: true            /*            Callbacks:            onInit: function (swiper)            onDestroy: function (swiper)            onClick: function (swiper, e)            onTap: function (swiper, e)            onDoubleTap: function (swiper, e)            onSliderMove: function (swiper, e)            onSlideChangeStart: function (swiper)            onSlideChangeEnd: function (swiper)            onTransitionStart: function (swiper)            onTransitionEnd: function (swiper)            onImagesReady: function (swiper)            onProgress: function (swiper, progress)            onTouchStart: function (swiper, e)            onTouchMove: function (swiper, e)            onTouchMoveOpposite: function (swiper, e)            onTouchEnd: function (swiper, e)            onReachBeginning: function (swiper)            onReachEnd: function (swiper)            onSetTransition: function (swiper, duration)            onSetTranslate: function (swiper, translate)            onAutoplayStart: function (swiper)            onAutoplayStop: function (swiper),            onLazyImageLoad: function (swiper, slide, image)            onLazyImageReady: function (swiper, slide, image)            */        };        var initialVirtualTranslate = params && params.virtualTranslate;        params = params || {};        var originalParams = {};        for (var param in params) {            if (typeof params[param] === 'object' && !(params[param].nodeType || params[param] === window || params[param] === document || (typeof Dom7 !== 'undefined' && params[param] instanceof Dom7) || (typeof jQuery !== 'undefined' && params[param] instanceof jQuery))) {                originalParams[param] = {};                for (var deepParam in params[param]) {                    originalParams[param][deepParam] = params[param][deepParam];                }            }            else {                originalParams[param] = params[param];            }        }        for (var def in defaults) {            if (typeof params[def] === 'undefined') {                params[def] = defaults[def];            }            else if (typeof params[def] === 'object') {                for (var deepDef in defaults[def]) {                    if (typeof params[def][deepDef] === 'undefined') {                        params[def][deepDef] = defaults[def][deepDef];                    }                }            }        }        // Swiper        var s = this;        // Params        s.params = params;        s.originalParams = originalParams;        // Classname        s.classNames = [];        /*=========================          Dom Library and plugins          ===========================*/        if (typeof $ !== 'undefined' && typeof Dom7 !== 'undefined'){            $ = Dom7;        }        if (typeof $ === 'undefined') {            if (typeof Dom7 === 'undefined') {                $ = window.Dom7 || window.Zepto || window.jQuery;            }            else {                $ = Dom7;            }            if (!$) return;        }        // Export it to Swiper instance        s.$ = $;        /*=========================          Breakpoints          ===========================*/        s.currentBreakpoint = undefined;        s.getActiveBreakpoint = function () {            //Get breakpoint for window width            if (!s.params.breakpoints) return false;            var breakpoint = false;            var points = [], point;            for ( point in s.params.breakpoints ) {                if (s.params.breakpoints.hasOwnProperty(point)) {                    points.push(point);                }            }            points.sort(function (a, b) {                return parseInt(a, 10) > parseInt(b, 10);            });            for (var i = 0; i < points.length; i++) {                point = points[i];                if (point >= window.innerWidth && !breakpoint) {                    breakpoint = point;                }            }            return breakpoint || 'max';        };        s.setBreakpoint = function () {            //Set breakpoint for window width and update parameters            var breakpoint = s.getActiveBreakpoint();            if (breakpoint && s.currentBreakpoint !== breakpoint) {                var breakPointsParams = breakpoint in s.params.breakpoints ? s.params.breakpoints[breakpoint] : s.originalParams;                for ( var param in breakPointsParams ) {                    s.params[param] = breakPointsParams[param];                }                s.currentBreakpoint = breakpoint;            }        };        // Set breakpoint on load        if (s.params.breakpoints) {            s.setBreakpoint();        }        /*=========================          Preparation - Define Container, Wrapper and Pagination          ===========================*/        s.container = $(container);        if (s.container.length === 0) return;        if (s.container.length > 1) {            s.container.each(function () {                new Swiper(this, params);            });            return;        }        // Save instance in container HTML Element and in data        s.container[0].swiper = s;        s.container.data('swiper', s);        s.classNames.push('swiper-container-' + s.params.direction);        if (s.params.freeMode) {            s.classNames.push('swiper-container-free-mode');        }        if (!s.support.flexbox) {            s.classNames.push('swiper-container-no-flexbox');            s.params.slidesPerColumn = 1;        }        if (s.params.autoHeight) {            s.classNames.push('swiper-container-autoheight');        }        // Enable slides progress when required        if (s.params.parallax || s.params.watchSlidesVisibility) {            s.params.watchSlidesProgress = true;        }        // Coverflow / 3D        if (['cube', 'coverflow'].indexOf(s.params.effect) >= 0) {            if (s.support.transforms3d) {                s.params.watchSlidesProgress = true;                s.classNames.push('swiper-container-3d');            }            else {                s.params.effect = 'slide';            }        }        if (s.params.effect !== 'slide') {            s.classNames.push('swiper-container-' + s.params.effect);        }        if (s.params.effect === 'cube') {            s.params.resistanceRatio = 0;            s.params.slidesPerView = 1;            s.params.slidesPerColumn = 1;            s.params.slidesPerGroup = 1;            s.params.centeredSlides = false;            s.params.spaceBetween = 0;            s.params.virtualTranslate = true;            s.params.setWrapperSize = false;        }        if (s.params.effect === 'fade') {            s.params.slidesPerView = 1;            s.params.slidesPerColumn = 1;            s.params.slidesPerGroup = 1;            s.params.watchSlidesProgress = true;            s.params.spaceBetween = 0;            if (typeof initialVirtualTranslate === 'undefined') {                s.params.virtualTranslate = true;            }        }        // Grab Cursor        if (s.params.grabCursor && s.support.touch) {            s.params.grabCursor = false;        }        // Wrapper        s.wrapper = s.container.children('.' + s.params.wrapperClass);        // Pagination        if (s.params.pagination) {            s.paginationContainer = $(s.params.pagination);            if (s.params.paginationClickable) {                s.paginationContainer.addClass('swiper-pagination-clickable');            }        }        // Is Horizontal        function isH() {            return s.params.direction === 'horizontal';        }        // RTL        s.rtl = isH() && (s.container[0].dir.toLowerCase() === 'rtl' || s.container.css('direction') === 'rtl');        if (s.rtl) {            s.classNames.push('swiper-container-rtl');        }        // Wrong RTL support        if (s.rtl) {            s.wrongRTL = s.wrapper.css('display') === '-webkit-box';        }        // Columns        if (s.params.slidesPerColumn > 1) {            s.classNames.push('swiper-container-multirow');        }        // Check for Android        if (s.device.android) {            s.classNames.push('swiper-container-android');        }        // Add classes        s.container.addClass(s.classNames.join(' '));        // Translate        s.translate = 0;        // Progress        s.progress = 0;        // Velocity        s.velocity = 0;        /*=========================          Locks, unlocks          ===========================*/        s.lockSwipeToNext = function () {            s.params.allowSwipeToNext = false;        };        s.lockSwipeToPrev = function () {            s.params.allowSwipeToPrev = false;        };        s.lockSwipes = function () {            s.params.allowSwipeToNext = s.params.allowSwipeToPrev = false;        };        s.unlockSwipeToNext = function () {            s.params.allowSwipeToNext = true;        };        s.unlockSwipeToPrev = function () {            s.params.allowSwipeToPrev = true;        };        s.unlockSwipes = function () {            s.params.allowSwipeToNext = s.params.allowSwipeToPrev = true;        };        /*=========================          Round helper          ===========================*/        function round(a) {            return Math.floor(a);        }        /*=========================          Set grab cursor          ===========================*/        if (s.params.grabCursor) {            s.container[0].style.cursor = 'move';            s.container[0].style.cursor = '-webkit-grab';            s.container[0].style.cursor = '-moz-grab';            s.container[0].style.cursor = 'grab';        }        /*=========================          Update on Images Ready          ===========================*/        s.imagesToLoad = [];        s.imagesLoaded = 0;        s.loadImage = function (imgElement, src, srcset, checkForComplete, callback) {            var image;            function onReady () {                if (callback) callback();            }            if (!imgElement.complete || !checkForComplete) {                if (src) {                    image = new window.Image();                    image.onload = onReady;                    image.onerror = onReady;                    if (srcset) {                        image.srcset = srcset;                    }                    if (src) {                        image.src = src;                    }                } else {                    onReady();                }            } else {//image already loaded...                onReady();            }        };        s.preloadImages = function () {            s.imagesToLoad = s.container.find('img');            function _onReady() {                if (typeof s === 'undefined' || s === null) return;                if (s.imagesLoaded !== undefined) s.imagesLoaded++;                if (s.imagesLoaded === s.imagesToLoad.length) {                    if (s.params.updateOnImagesReady) s.update();                    s.emit('onImagesReady', s);                }            }            for (var i = 0; i < s.imagesToLoad.length; i++) {                s.loadImage(s.imagesToLoad[i], (s.imagesToLoad[i].currentSrc || s.imagesToLoad[i].getAttribute('src')), (s.imagesToLoad[i].srcset || s.imagesToLoad[i].getAttribute('srcset')), true, _onReady);            }        };        /*=========================          Autoplay          ===========================*/        s.autoplayTimeoutId = undefined;        s.autoplaying = false;        s.autoplayPaused = false;        function autoplay() {            s.autoplayTimeoutId = setTimeout(function () {                if (s.params.loop) {                    s.fixLoop();                    s._slideNext();                }                else {                    if (!s.isEnd) {                        s._slideNext();                    }                    else {                        if (!params.autoplayStopOnLast) {                            s._slideTo(0);                        }                        else {                            s.stopAutoplay();                        }                    }                }            }, s.params.autoplay);        }        s.startAutoplay = function () {            if (typeof s.autoplayTimeoutId !== 'undefined') return false;            if (!s.params.autoplay) return false;            if (s.autoplaying) return false;            s.autoplaying = true;            s.emit('onAutoplayStart', s);            autoplay();        };        s.stopAutoplay = function (internal) {            if (!s.autoplayTimeoutId) return;            if (s.autoplayTimeoutId) clearTimeout(s.autoplayTimeoutId);            s.autoplaying = false;            s.autoplayTimeoutId = undefined;            s.emit('onAutoplayStop', s);        };        s.pauseAutoplay = function (speed) {            if (s.autoplayPaused) return;            if (s.autoplayTimeoutId) clearTimeout(s.autoplayTimeoutId);            s.autoplayPaused = true;            if (speed === 0) {                s.autoplayPaused = false;                autoplay();            }            else {                s.wrapper.transitionEnd(function () {                    if (!s) return;                    s.autoplayPaused = false;                    if (!s.autoplaying) {                        s.stopAutoplay();                    }                    else {                        autoplay();                    }                });            }        };        /*=========================          Min/Max Translate          ===========================*/        s.minTranslate = function () {            return (-s.snapGrid[0]);        };        s.maxTranslate = function () {            return (-s.snapGrid[s.snapGrid.length - 1]);        };        /*=========================          Slider/slides sizes          ===========================*/        s.updateAutoHeight = function () {            // Update Height            var newHeight = s.slides.eq(s.activeIndex)[0].offsetHeight;            if (newHeight) s.wrapper.css('height', s.slides.eq(s.activeIndex)[0].offsetHeight + 'px');        };        s.updateContainerSize = function () {            var width, height;            if (typeof s.params.width !== 'undefined') {                width = s.params.width;            }            else {                width = s.container[0].clientWidth;            }            if (typeof s.params.height !== 'undefined') {                height = s.params.height;            }            else {                height = s.container[0].clientHeight;            }            if (width === 0 && isH() || height === 0 && !isH()) {                return;            }            //Subtract paddings            width = width - parseInt(s.container.css('padding-left'), 10) - parseInt(s.container.css('padding-right'), 10);            height = height - parseInt(s.container.css('padding-top'), 10) - parseInt(s.container.css('padding-bottom'), 10);            // Store values            s.width = width;            s.height = height;            s.size = isH() ? s.width : s.height;        };        s.updateSlidesSize = function () {            s.slides = s.wrapper.children('.' + s.params.slideClass);            s.snapGrid = [];            s.slidesGrid = [];            s.slidesSizesGrid = [];            var spaceBetween = s.params.spaceBetween,                slidePosition = -s.params.slidesOffsetBefore,                i,                prevSlideSize = 0,                index = 0;            if (typeof spaceBetween === 'string' && spaceBetween.indexOf('%') >= 0) {                spaceBetween = parseFloat(spaceBetween.replace('%', '')) / 100 * s.size;            }            s.virtualSize = -spaceBetween;            // reset margins            if (s.rtl) s.slides.css({marginLeft: '', marginTop: ''});            else s.slides.css({marginRight: '', marginBottom: ''});            var slidesNumberEvenToRows;            if (s.params.slidesPerColumn > 1) {                if (Math.floor(s.slides.length / s.params.slidesPerColumn) === s.slides.length / s.params.slidesPerColumn) {                    slidesNumberEvenToRows = s.slides.length;                }                else {                    slidesNumberEvenToRows = Math.ceil(s.slides.length / s.params.slidesPerColumn) * s.params.slidesPerColumn;                }                if (s.params.slidesPerView !== 'auto' && s.params.slidesPerColumnFill === 'row') {                    slidesNumberEvenToRows = Math.max(slidesNumberEvenToRows, s.params.slidesPerView * s.params.slidesPerColumn);                }            }            // Calc slides            var slideSize;            var slidesPerColumn = s.params.slidesPerColumn;            var slidesPerRow = slidesNumberEvenToRows / slidesPerColumn;            var numFullColumns = slidesPerRow - (s.params.slidesPerColumn * slidesPerRow - s.slides.length);            for (i = 0; i < s.slides.length; i++) {                slideSize = 0;                var slide = s.slides.eq(i);                if (s.params.slidesPerColumn > 1) {                    // Set slides order                    var newSlideOrderIndex;                    var column, row;                    if (s.params.slidesPerColumnFill === 'column') {                        column = Math.floor(i / slidesPerColumn);                        row = i - column * slidesPerColumn;                        if (column > numFullColumns || (column === numFullColumns && row === slidesPerColumn-1)) {                            if (++row >= slidesPerColumn) {                                row = 0;                                column++;                            }                        }                        newSlideOrderIndex = column + row * slidesNumberEvenToRows / slidesPerColumn;                        slide                            .css({                                '-webkit-box-ordinal-group': newSlideOrderIndex,                                '-moz-box-ordinal-group': newSlideOrderIndex,                                '-ms-flex-order': newSlideOrderIndex,                                '-webkit-order': newSlideOrderIndex,                                'order': newSlideOrderIndex                            });                    }                    else {                        row = Math.floor(i / slidesPerRow);                        column = i - row * slidesPerRow;                    }                    slide                        .css({                            'margin-top': (row !== 0 && s.params.spaceBetween) && (s.params.spaceBetween + 'px')                        })                        .attr('data-swiper-column', column)                        .attr('data-swiper-row', row);                }                if (slide.css('display') === 'none') continue;                if (s.params.slidesPerView === 'auto') {                    slideSize = isH() ? slide.outerWidth(true) : slide.outerHeight(true);                    if (s.params.roundLengths) slideSize = round(slideSize);                }                else {                    slideSize = (s.size - (s.params.slidesPerView - 1) * spaceBetween) / s.params.slidesPerView;                    if (s.params.roundLengths) slideSize = round(slideSize);                    if (isH()) {                        s.slides[i].style.width = slideSize + 'px';                    }                    else {                        s.slides[i].style.height = slideSize + 'px';                    }                }                s.slides[i].swiperSlideSize = slideSize;                s.slidesSizesGrid.push(slideSize);                if (s.params.centeredSlides) {                    slidePosition = slidePosition + slideSize / 2 + prevSlideSize / 2 + spaceBetween;                    if (i === 0) slidePosition = slidePosition - s.size / 2 - spaceBetween;                    if (Math.abs(slidePosition) < 1 / 1000) slidePosition = 0;                    if ((index) % s.params.slidesPerGroup === 0) s.snapGrid.push(slidePosition);                    s.slidesGrid.push(slidePosition);                }                else {                    if ((index) % s.params.slidesPerGroup === 0) s.snapGrid.push(slidePosition);                    s.slidesGrid.push(slidePosition);                    slidePosition = slidePosition + slideSize + spaceBetween;                }                s.virtualSize += slideSize + spaceBetween;                prevSlideSize = slideSize;                index ++;            }            s.virtualSize = Math.max(s.virtualSize, s.size) + s.params.slidesOffsetAfter;            var newSlidesGrid;            if (                s.rtl && s.wrongRTL && (s.params.effect === 'slide' || s.params.effect === 'coverflow')) {                s.wrapper.css({width: s.virtualSize + s.params.spaceBetween + 'px'});            }            if (!s.support.flexbox || s.params.setWrapperSize) {                if (isH()) s.wrapper.css({width: s.virtualSize + s.params.spaceBetween + 'px'});                else s.wrapper.css({height: s.virtualSize + s.params.spaceBetween + 'px'});            }            if (s.params.slidesPerColumn > 1) {                s.virtualSize = (slideSize + s.params.spaceBetween) * slidesNumberEvenToRows;                s.virtualSize = Math.ceil(s.virtualSize / s.params.slidesPerColumn) - s.params.spaceBetween;                s.wrapper.css({width: s.virtualSize + s.params.spaceBetween + 'px'});                if (s.params.centeredSlides) {                    newSlidesGrid = [];                    for (i = 0; i < s.snapGrid.length; i++) {                        if (s.snapGrid[i] < s.virtualSize + s.snapGrid[0]) newSlidesGrid.push(s.snapGrid[i]);                    }                    s.snapGrid = newSlidesGrid;                }            }            // Remove last grid elements depending on width            if (!s.params.centeredSlides) {                newSlidesGrid = [];                for (i = 0; i < s.snapGrid.length; i++) {                    if (s.snapGrid[i] <= s.virtualSize - s.size) {                        newSlidesGrid.push(s.snapGrid[i]);                    }                }                s.snapGrid = newSlidesGrid;                if (Math.floor(s.virtualSize - s.size) > Math.floor(s.snapGrid[s.snapGrid.length - 1])) {                    s.snapGrid.push(s.virtualSize - s.size);                }            }            if (s.snapGrid.length === 0) s.snapGrid = [0];            if (s.params.spaceBetween !== 0) {                if (isH()) {                    if (s.rtl) s.slides.css({marginLeft: spaceBetween + 'px'});                    else s.slides.css({marginRight: spaceBetween + 'px'});                }                else s.slides.css({marginBottom: spaceBetween + 'px'});            }            if (s.params.watchSlidesProgress) {                s.updateSlidesOffset();            }        };        s.updateSlidesOffset = function () {            for (var i = 0; i < s.slides.length; i++) {                s.slides[i].swiperSlideOffset = isH() ? s.slides[i].offsetLeft : s.slides[i].offsetTop;            }        };        /*=========================          Slider/slides progress          ===========================*/        s.updateSlidesProgress = function (translate) {            if (typeof translate === 'undefined') {                translate = s.translate || 0;            }            if (s.slides.length === 0) return;            if (typeof s.slides[0].swiperSlideOffset === 'undefined') s.updateSlidesOffset();            var offsetCenter = -translate;            if (s.rtl) offsetCenter = translate;            // Visible Slides            s.slides.removeClass(s.params.slideVisibleClass);            for (var i = 0; i < s.slides.length; i++) {                var slide = s.slides[i];                var slideProgress = (offsetCenter - slide.swiperSlideOffset) / (slide.swiperSlideSize + s.params.spaceBetween);                if (s.params.watchSlidesVisibility) {                    var slideBefore = -(offsetCenter - slide.swiperSlideOffset);                    var slideAfter = slideBefore + s.slidesSizesGrid[i];                    var isVisible =                        (slideBefore >= 0 && slideBefore < s.size) ||                        (slideAfter > 0 && slideAfter <= s.size) ||                        (slideBefore <= 0 && slideAfter >= s.size);                    if (isVisible) {                        s.slides.eq(i).addClass(s.params.slideVisibleClass);                    }                }                slide.progress = s.rtl ? -slideProgress : slideProgress;            }        };        s.updateProgress = function (translate) {            if (typeof translate === 'undefined') {                translate = s.translate || 0;            }            var translatesDiff = s.maxTranslate() - s.minTranslate();            var wasBeginning = s.isBeginning;            var wasEnd = s.isEnd;            if (translatesDiff === 0) {                s.progress = 0;                s.isBeginning = s.isEnd = true;            }            else {                s.progress = (translate - s.minTranslate()) / (translatesDiff);                s.isBeginning = s.progress <= 0;                s.isEnd = s.progress >= 1;            }            if (s.isBeginning && !wasBeginning) s.emit('onReachBeginning', s);            if (s.isEnd && !wasEnd) s.emit('onReachEnd', s);            if (s.params.watchSlidesProgress) s.updateSlidesProgress(translate);            s.emit('onProgress', s, s.progress);        };        s.updateActiveIndex = function () {            var translate = s.rtl ? s.translate : -s.translate;            var newActiveIndex, i, snapIndex;            for (i = 0; i < s.slidesGrid.length; i ++) {                if (typeof s.slidesGrid[i + 1] !== 'undefined') {                    if (translate >= s.slidesGrid[i] && translate < s.slidesGrid[i + 1] - (s.slidesGrid[i + 1] - s.slidesGrid[i]) / 2) {                        newActiveIndex = i;                    }                    else if (translate >= s.slidesGrid[i] && translate < s.slidesGrid[i + 1]) {                        newActiveIndex = i + 1;                    }                }                else {                    if (translate >= s.slidesGrid[i]) {                        newActiveIndex = i;                    }                }            }            // Normalize slideIndex            if (newActiveIndex < 0 || typeof newActiveIndex === 'undefined') newActiveIndex = 0;            // for (i = 0; i < s.slidesGrid.length; i++) {                // if (- translate >= s.slidesGrid[i]) {                    // newActiveIndex = i;                // }            // }            snapIndex = Math.floor(newActiveIndex / s.params.slidesPerGroup);            if (snapIndex >= s.snapGrid.length) snapIndex = s.snapGrid.length - 1;            if (newActiveIndex === s.activeIndex) {                return;            }            s.snapIndex = snapIndex;            s.previousIndex = s.activeIndex;            s.activeIndex = newActiveIndex;            s.updateClasses();        };        /*=========================          Classes          ===========================*/        s.updateClasses = function () {            s.slides.removeClass(s.params.slideActiveClass + ' ' + s.params.slideNextClass + ' ' + s.params.slidePrevClass);            var activeSlide = s.slides.eq(s.activeIndex);            // Active classes            activeSlide.addClass(s.params.slideActiveClass);            activeSlide.next('.' + s.params.slideClass).addClass(s.params.slideNextClass);            activeSlide.prev('.' + s.params.slideClass).addClass(s.params.slidePrevClass);            // Pagination            if (s.bullets && s.bullets.length > 0) {                s.bullets.removeClass(s.params.bulletActiveClass);                var bulletIndex;                if (s.params.loop) {                    bulletIndex = Math.ceil(s.activeIndex - s.loopedSlides)/s.params.slidesPerGroup;                    if (bulletIndex > s.slides.length - 1 - s.loopedSlides * 2) {                        bulletIndex = bulletIndex - (s.slides.length - s.loopedSlides * 2);                    }                    if (bulletIndex > s.bullets.length - 1) bulletIndex = bulletIndex - s.bullets.length;                }                else {                    if (typeof s.snapIndex !== 'undefined') {                        bulletIndex = s.snapIndex;                    }                    else {                        bulletIndex = s.activeIndex || 0;                    }                }                if (s.paginationContainer.length > 1) {                    s.bullets.each(function () {                        if ($(this).index() === bulletIndex) $(this).addClass(s.params.bulletActiveClass);                    });                }                else {                    s.bullets.eq(bulletIndex).addClass(s.params.bulletActiveClass);                }            }            // Next/active buttons            if (!s.params.loop) {                if (s.params.prevButton) {                    if (s.isBeginning) {                        $(s.params.prevButton).addClass(s.params.buttonDisabledClass);                        if (s.params.a11y && s.a11y) s.a11y.disable($(s.params.prevButton));                    }                    else {                        $(s.params.prevButton).removeClass(s.params.buttonDisabledClass);                        if (s.params.a11y && s.a11y) s.a11y.enable($(s.params.prevButton));                    }                }                if (s.params.nextButton) {                    if (s.isEnd) {                        $(s.params.nextButton).addClass(s.params.buttonDisabledClass);                        if (s.params.a11y && s.a11y) s.a11y.disable($(s.params.nextButton));                    }                    else {                        $(s.params.nextButton).removeClass(s.params.buttonDisabledClass);                        if (s.params.a11y && s.a11y) s.a11y.enable($(s.params.nextButton));                    }                }            }        };        /*=========================          Pagination          ===========================*/        s.updatePagination = function () {            if (!s.params.pagination) return;            if (s.paginationContainer && s.paginationContainer.length > 0) {                var bulletsHTML = '';                var numberOfBullets = s.params.loop ? Math.ceil((s.slides.length - s.loopedSlides * 2) / s.params.slidesPerGroup) : s.snapGrid.length;                for (var i = 0; i < numberOfBullets; i++) {                    if (s.params.paginationBulletRender) {                        bulletsHTML += s.params.paginationBulletRender(i, s.params.bulletClass);                    }                    else {                        bulletsHTML += '<' + s.params.paginationElement+' class="' + s.params.bulletClass + '"></' + s.params.paginationElement + '>';                    }                }                s.paginationContainer.html(bulletsHTML);                s.bullets = s.paginationContainer.find('.' + s.params.bulletClass);                if (s.params.paginationClickable && s.params.a11y && s.a11y) {                    s.a11y.initPagination();                }            }        };        /*=========================          Common update method          ===========================*/        s.update = function (updateTranslate) {            s.updateContainerSize();            s.updateSlidesSize();            s.updateProgress();            s.updatePagination();            s.updateClasses();            if (s.params.scrollbar && s.scrollbar) {                s.scrollbar.set();            }            function forceSetTranslate() {                newTranslate = Math.min(Math.max(s.translate, s.maxTranslate()), s.minTranslate());                s.setWrapperTranslate(newTranslate);                s.updateActiveIndex();                s.updateClasses();            }            if (updateTranslate) {                var translated, newTranslate;                if (s.controller && s.controller.spline) {                    s.controller.spline = undefined;                }                if (s.params.freeMode) {                    forceSetTranslate();                    if (s.params.autoHeight) {                        s.updateAutoHeight();                    }                }                else {                    if ((s.params.slidesPerView === 'auto' || s.params.slidesPerView > 1) && s.isEnd && !s.params.centeredSlides) {                        translated = s.slideTo(s.slides.length - 1, 0, false, true);                    }                    else {                        translated = s.slideTo(s.activeIndex, 0, false, true);                    }                    if (!translated) {                        forceSetTranslate();                    }                }            }            else if (s.params.autoHeight) {                s.updateAutoHeight();            }        };        /*=========================          Resize Handler          ===========================*/        s.onResize = function (forceUpdatePagination) {            //Breakpoints            if (s.params.breakpoints) {                s.setBreakpoint();            }            // Disable locks on resize            var allowSwipeToPrev = s.params.allowSwipeToPrev;            var allowSwipeToNext = s.params.allowSwipeToNext;            s.params.allowSwipeToPrev = s.params.allowSwipeToNext = true;            s.updateContainerSize();            s.updateSlidesSize();            if (s.params.slidesPerView === 'auto' || s.params.freeMode || forceUpdatePagination) s.updatePagination();            if (s.params.scrollbar && s.scrollbar) {                s.scrollbar.set();            }            if (s.controller && s.controller.spline) {                s.controller.spline = undefined;            }            if (s.params.freeMode) {                var newTranslate = Math.min(Math.max(s.translate, s.maxTranslate()), s.minTranslate());                s.setWrapperTranslate(newTranslate);                s.updateActiveIndex();                s.updateClasses();                if (s.params.autoHeight) {                    s.updateAutoHeight();                }            }            else {                s.updateClasses();                if ((s.params.slidesPerView === 'auto' || s.params.slidesPerView > 1) && s.isEnd && !s.params.centeredSlides) {                    s.slideTo(s.slides.length - 1, 0, false, true);                }                else {                    s.slideTo(s.activeIndex, 0, false, true);                }            }            // Return locks after resize            s.params.allowSwipeToPrev = allowSwipeToPrev;            s.params.allowSwipeToNext = allowSwipeToNext;        };        /*=========================          Events          ===========================*/        //Define Touch Events        var desktopEvents = ['mousedown', 'mousemove', 'mouseup'];        if (window.navigator.pointerEnabled) desktopEvents = ['pointerdown', 'pointermove', 'pointerup'];        else if (window.navigator.msPointerEnabled) desktopEvents = ['MSPointerDown', 'MSPointerMove', 'MSPointerUp'];        s.touchEvents = {            start : s.support.touch || !s.params.simulateTouch  ? 'touchstart' : desktopEvents[0],            move : s.support.touch || !s.params.simulateTouch ? 'touchmove' : desktopEvents[1],            end : s.support.touch || !s.params.simulateTouch ? 'touchend' : desktopEvents[2]        };        // WP8 Touch Events Fix        if (window.navigator.pointerEnabled || window.navigator.msPointerEnabled) {            (s.params.touchEventsTarget === 'container' ? s.container : s.wrapper).addClass('swiper-wp8-' + s.params.direction);        }        // Attach/detach events        s.initEvents = function (detach) {            var actionDom = detach ? 'off' : 'on';            var action = detach ? 'removeEventListener' : 'addEventListener';            var touchEventsTarget = s.params.touchEventsTarget === 'container' ? s.container[0] : s.wrapper[0];            var target = s.support.touch ? touchEventsTarget : document;            var moveCapture = s.params.nested ? true : false;            //Touch Events            if (s.browser.ie) {                touchEventsTarget[action](s.touchEvents.start, s.onTouchStart, false);                target[action](s.touchEvents.move, s.onTouchMove, moveCapture);                target[action](s.touchEvents.end, s.onTouchEnd, false);            }            else {                if (s.support.touch) {                    touchEventsTarget[action](s.touchEvents.start, s.onTouchStart, false);                    touchEventsTarget[action](s.touchEvents.move, s.onTouchMove, moveCapture);                    touchEventsTarget[action](s.touchEvents.end, s.onTouchEnd, false);                }                if (params.simulateTouch && !s.device.ios && !s.device.android) {                    touchEventsTarget[action]('mousedown', s.onTouchStart, false);                    document[action]('mousemove', s.onTouchMove, moveCapture);                    document[action]('mouseup', s.onTouchEnd, false);                }            }            window[action]('resize', s.onResize);            // Next, Prev, Index            if (s.params.nextButton) {                $(s.params.nextButton)[actionDom]('click', s.onClickNext);                if (s.params.a11y && s.a11y) $(s.params.nextButton)[actionDom]('keydown', s.a11y.onEnterKey);            }            if (s.params.prevButton) {                $(s.params.prevButton)[actionDom]('click', s.onClickPrev);                if (s.params.a11y && s.a11y) $(s.params.prevButton)[actionDom]('keydown', s.a11y.onEnterKey);            }            if (s.params.pagination && s.params.paginationClickable) {                $(s.paginationContainer)[actionDom]('click', '.' + s.params.bulletClass, s.onClickIndex);                if (s.params.a11y && s.a11y) $(s.paginationContainer)[actionDom]('keydown', '.' + s.params.bulletClass, s.a11y.onEnterKey);            }            // Prevent Links Clicks            if (s.params.preventClicks || s.params.preventClicksPropagation) touchEventsTarget[action]('click', s.preventClicks, true);        };        s.attachEvents = function (detach) {            s.initEvents();        };        s.detachEvents = function () {            s.initEvents(true);        };        /*=========================          Handle Clicks          ===========================*/        // Prevent Clicks        s.allowClick = true;        s.preventClicks = function (e) {            if (!s.allowClick) {                if (s.params.preventClicks) e.preventDefault();                if (s.params.preventClicksPropagation && s.animating) {                    e.stopPropagation();                    e.stopImmediatePropagation();                }            }        };        // Clicks        s.onClickNext = function (e) {            e.preventDefault();            if (s.isEnd && !s.params.loop) return;            s.slideNext();        };        s.onClickPrev = function (e) {            e.preventDefault();            if (s.isBeginning && !s.params.loop) return;            s.slidePrev();        };        s.onClickIndex = function (e) {            e.preventDefault();            var index = $(this).index() * s.params.slidesPerGroup;            if (s.params.loop) index = index + s.loopedSlides;            s.slideTo(index);        };        /*=========================          Handle Touches          ===========================*/        function findElementInEvent(e, selector) {            var el = $(e.target);            if (!el.is(selector)) {                if (typeof selector === 'string') {                    el = el.parents(selector);                }                else if (selector.nodeType) {                    var found;                    el.parents().each(function (index, _el) {                        if (_el === selector) found = selector;                    });                    if (!found) return undefined;                    else return selector;                }            }            if (el.length === 0) {                return undefined;            }            return el[0];        }        s.updateClickedSlide = function (e) {            var slide = findElementInEvent(e, '.' + s.params.slideClass);            var slideFound = false;            if (slide) {                for (var i = 0; i < s.slides.length; i++) {                    if (s.slides[i] === slide) slideFound = true;                }            }            if (slide && slideFound) {                s.clickedSlide = slide;                s.clickedIndex = $(slide).index();            }            else {                s.clickedSlide = undefined;                s.clickedIndex = undefined;                return;            }            if (s.params.slideToClickedSlide && s.clickedIndex !== undefined && s.clickedIndex !== s.activeIndex) {                var slideToIndex = s.clickedIndex,                    realIndex,                    duplicatedSlides;                if (s.params.loop) {                    if (s.animating) return;                    realIndex = $(s.clickedSlide).attr('data-swiper-slide-index');                    if (s.params.centeredSlides) {                        if ((slideToIndex < s.loopedSlides - s.params.slidesPerView/2) || (slideToIndex > s.slides.length - s.loopedSlides + s.params.slidesPerView/2)) {                            s.fixLoop();                            slideToIndex = s.wrapper.children('.' + s.params.slideClass + '[data-swiper-slide-index="' + realIndex + '"]:not(.swiper-slide-duplicate)').eq(0).index();                            setTimeout(function () {                                s.slideTo(slideToIndex);                            }, 0);                        }                        else {                            s.slideTo(slideToIndex);                        }                    }                    else {                        if (slideToIndex > s.slides.length - s.params.slidesPerView) {                            s.fixLoop();                            slideToIndex = s.wrapper.children('.' + s.params.slideClass + '[data-swiper-slide-index="' + realIndex + '"]:not(.swiper-slide-duplicate)').eq(0).index();                            setTimeout(function () {                                s.slideTo(slideToIndex);                            }, 0);                        }                        else {                            s.slideTo(slideToIndex);                        }                    }                }                else {                    s.slideTo(slideToIndex);                }            }        };        var isTouched,            isMoved,            allowTouchCallbacks,            touchStartTime,            isScrolling,            currentTranslate,            startTranslate,            allowThresholdMove,            // Form elements to match            formElements = 'input, select, textarea, button',            // Last click time            lastClickTime = Date.now(), clickTimeout,            //Velocities            velocities = [],            allowMomentumBounce;        // Animating Flag        s.animating = false;        // Touches information        s.touches = {            startX: 0,            startY: 0,            currentX: 0,            currentY: 0,            diff: 0        };        // Touch handlers        var isTouchEvent, startMoving;        s.onTouchStart = function (e) {            if (e.originalEvent) e = e.originalEvent;            isTouchEvent = e.type === 'touchstart';            if (!isTouchEvent && 'which' in e && e.which === 3) return;            if (s.params.noSwiping && findElementInEvent(e, '.' + s.params.noSwipingClass)) {                s.allowClick = true;                return;            }            if (s.params.swipeHandler) {                if (!findElementInEvent(e, s.params.swipeHandler)) return;            }            var startX = s.touches.currentX = e.type === 'touchstart' ? e.targetTouches[0].pageX : e.pageX;            var startY = s.touches.currentY = e.type === 'touchstart' ? e.targetTouches[0].pageY : e.pageY;            // Do NOT start if iOS edge swipe is detected. Otherwise iOS app (UIWebView) cannot swipe-to-go-back anymore            if(s.device.ios && s.params.iOSEdgeSwipeDetection && startX <= s.params.iOSEdgeSwipeThreshold) {                return;            }            isTouched = true;            isMoved = false;            allowTouchCallbacks = true;            isScrolling = undefined;            startMoving = undefined;            s.touches.startX = startX;            s.touches.startY = startY;            touchStartTime = Date.now();            s.allowClick = true;            s.updateContainerSize();            s.swipeDirection = undefined;            if (s.params.threshold > 0) allowThresholdMove = false;            if (e.type !== 'touchstart') {                var preventDefault = true;                if ($(e.target).is(formElements)) preventDefault = false;                if (document.activeElement && $(document.activeElement).is(formElements)) {                    document.activeElement.blur();                }                if (preventDefault) {                    e.preventDefault();                }            }            s.emit('onTouchStart', s, e);        };        s.onTouchMove = function (e) {            if (e.originalEvent) e = e.originalEvent;            if (isTouchEvent && e.type === 'mousemove') return;            if (e.preventedByNestedSwiper) return;            if (s.params.onlyExternal) {                // isMoved = true;                s.allowClick = false;                if (isTouched) {                    s.touches.startX = s.touches.currentX = e.type === 'touchmove' ? e.targetTouches[0].pageX : e.pageX;                    s.touches.startY = s.touches.currentY = e.type === 'touchmove' ? e.targetTouches[0].pageY : e.pageY;                    touchStartTime = Date.now();                }                return;            }            if (isTouchEvent && document.activeElement) {                if (e.target === document.activeElement && $(e.target).is(formElements)) {                    isMoved = true;                    s.allowClick = false;                    return;                }            }            if (allowTouchCallbacks) {                s.emit('onTouchMove', s, e);            }            if (e.targetTouches && e.targetTouches.length > 1) return;            s.touches.currentX = e.type === 'touchmove' ? e.targetTouches[0].pageX : e.pageX;            s.touches.currentY = e.type === 'touchmove' ? e.targetTouches[0].pageY : e.pageY;            if (typeof isScrolling === 'undefined') {                var touchAngle = Math.atan2(Math.abs(s.touches.currentY - s.touches.startY), Math.abs(s.touches.currentX - s.touches.startX)) * 180 / Math.PI;                isScrolling = isH() ? touchAngle > s.params.touchAngle : (90 - touchAngle > s.params.touchAngle);            }            if (isScrolling) {                s.emit('onTouchMoveOpposite', s, e);            }            if (typeof startMoving === 'undefined' && s.browser.ieTouch) {                if (s.touches.currentX !== s.touches.startX || s.touches.currentY !== s.touches.startY) {                    startMoving = true;                }            }            if (!isTouched) return;            if (isScrolling)  {                isTouched = false;                return;            }            if (!startMoving && s.browser.ieTouch) {                return;            }            s.allowClick = false;            s.emit('onSliderMove', s, e);            e.preventDefault();            if (s.params.touchMoveStopPropagation && !s.params.nested) {                e.stopPropagation();            }            if (!isMoved) {                if (params.loop) {                    s.fixLoop();                }                startTranslate = s.getWrapperTranslate();                s.setWrapperTransition(0);                if (s.animating) {                    s.wrapper.trigger('webkitTransitionEnd transitionend oTransitionEnd MSTransitionEnd msTransitionEnd');                }                if (s.params.autoplay && s.autoplaying) {                    if (s.params.autoplayDisableOnInteraction) {                        s.stopAutoplay();                    }                    else {                        s.pauseAutoplay();                    }                }                allowMomentumBounce = false;                //Grab Cursor                if (s.params.grabCursor) {                    s.container[0].style.cursor = 'move';                    s.container[0].style.cursor = '-webkit-grabbing';                    s.container[0].style.cursor = '-moz-grabbin';                    s.container[0].style.cursor = 'grabbing';                }            }            isMoved = true;            var diff = s.touches.diff = isH() ? s.touches.currentX - s.touches.startX : s.touches.currentY - s.touches.startY;            diff = diff * s.params.touchRatio;            if (s.rtl) diff = -diff;            s.swipeDirection = diff > 0 ? 'prev' : 'next';            currentTranslate = diff + startTranslate;            var disableParentSwiper = true;            if ((diff > 0 && currentTranslate > s.minTranslate())) {                disableParentSwiper = false;                if (s.params.resistance) currentTranslate = s.minTranslate() - 1 + Math.pow(-s.minTranslate() + startTranslate + diff, s.params.resistanceRatio);            }            else if (diff < 0 && currentTranslate < s.maxTranslate()) {                disableParentSwiper = false;                if (s.params.resistance) currentTranslate = s.maxTranslate() + 1 - Math.pow(s.maxTranslate() - startTranslate - diff, s.params.resistanceRatio);            }            if (disableParentSwiper) {                e.preventedByNestedSwiper = true;            }            // Directions locks            if (!s.params.allowSwipeToNext && s.swipeDirection === 'next' && currentTranslate < startTranslate) {                currentTranslate = startTranslate;            }            if (!s.params.allowSwipeToPrev && s.swipeDirection === 'prev' && currentTranslate > startTranslate) {                currentTranslate = startTranslate;            }            if (!s.params.followFinger) return;            // Threshold            if (s.params.threshold > 0) {                if (Math.abs(diff) > s.params.threshold || allowThresholdMove) {                    if (!allowThresholdMove) {                        allowThresholdMove = true;                        s.touches.startX = s.touches.currentX;                        s.touches.startY = s.touches.currentY;                        currentTranslate = startTranslate;                        s.touches.diff = isH() ? s.touches.currentX - s.touches.startX : s.touches.currentY - s.touches.startY;                        return;                    }                }                else {                    currentTranslate = startTranslate;                    return;                }            }            // Update active index in free mode            if (s.params.freeMode || s.params.watchSlidesProgress) {                s.updateActiveIndex();            }            if (s.params.freeMode) {                //Velocity                if (velocities.length === 0) {                    velocities.push({                        position: s.touches[isH() ? 'startX' : 'startY'],                        time: touchStartTime                    });                }                velocities.push({                    position: s.touches[isH() ? 'currentX' : 'currentY'],                    time: (new window.Date()).getTime()                });            }            // Update progress            s.updateProgress(currentTranslate);            // Update translate            s.setWrapperTranslate(currentTranslate);        };        s.onTouchEnd = function (e) {            if (e.originalEvent) e = e.originalEvent;            if (allowTouchCallbacks) {                s.emit('onTouchEnd', s, e);            }            allowTouchCallbacks = false;            if (!isTouched) return;            //Return Grab Cursor            if (s.params.grabCursor && isMoved && isTouched) {                s.container[0].style.cursor = 'move';                s.container[0].style.cursor = '-webkit-grab';                s.container[0].style.cursor = '-moz-grab';                s.container[0].style.cursor = 'grab';            }            // Time diff            var touchEndTime = Date.now();            var timeDiff = touchEndTime - touchStartTime;            // Tap, doubleTap, Click            if (s.allowClick) {                s.updateClickedSlide(e);                s.emit('onTap', s, e);                if (timeDiff < 300 && (touchEndTime - lastClickTime) > 300) {                    if (clickTimeout) clearTimeout(clickTimeout);                    clickTimeout = setTimeout(function () {                        if (!s) return;                        if (s.params.paginationHide && s.paginationContainer.length > 0 && !$(e.target).hasClass(s.params.bulletClass)) {                            s.paginationContainer.toggleClass(s.params.paginationHiddenClass);                        }                        s.emit('onClick', s, e);                    }, 300);                }                if (timeDiff < 300 && (touchEndTime - lastClickTime) < 300) {                    if (clickTimeout) clearTimeout(clickTimeout);                    s.emit('onDoubleTap', s, e);                }            }            lastClickTime = Date.now();            setTimeout(function () {                if (s) s.allowClick = true;            }, 0);            if (!isTouched || !isMoved || !s.swipeDirection || s.touches.diff === 0 || currentTranslate === startTranslate) {                isTouched = isMoved = false;                return;            }            isTouched = isMoved = false;            var currentPos;            if (s.params.followFinger) {                currentPos = s.rtl ? s.translate : -s.translate;            }            else {                currentPos = -currentTranslate;            }            if (s.params.freeMode) {                if (currentPos < -s.minTranslate()) {                    s.slideTo(s.activeIndex);                    return;                }                else if (currentPos > -s.maxTranslate()) {                    if (s.slides.length < s.snapGrid.length) {                        s.slideTo(s.snapGrid.length - 1);                    }                    else {                        s.slideTo(s.slides.length - 1);                    }                    return;                }                if (s.params.freeModeMomentum) {                    if (velocities.length > 1) {                        var lastMoveEvent = velocities.pop(), velocityEvent = velocities.pop();                        var distance = lastMoveEvent.position - velocityEvent.position;                        var time = lastMoveEvent.time - velocityEvent.time;                        s.velocity = distance / time;                        s.velocity = s.velocity / 2;                        if (Math.abs(s.velocity) < s.params.freeModeMinimumVelocity) {                            s.velocity = 0;                        }                        // this implies that the user stopped moving a finger then released.                        // There would be no events with distance zero, so the last event is stale.                        if (time > 150 || (new window.Date().getTime() - lastMoveEvent.time) > 300) {                            s.velocity = 0;                        }                    } else {                        s.velocity = 0;                    }                    velocities.length = 0;                    var momentumDuration = 1000 * s.params.freeModeMomentumRatio;                    var momentumDistance = s.velocity * momentumDuration;                    var newPosition = s.translate + momentumDistance;                    if (s.rtl) newPosition = - newPosition;                    var doBounce = false;                    var afterBouncePosition;                    var bounceAmount = Math.abs(s.velocity) * 20 * s.params.freeModeMomentumBounceRatio;                    if (newPosition < s.maxTranslate()) {                        if (s.params.freeModeMomentumBounce) {                            if (newPosition + s.maxTranslate() < -bounceAmount) {                                newPosition = s.maxTranslate() - bounceAmount;                            }                            afterBouncePosition = s.maxTranslate();                            doBounce = true;                            allowMomentumBounce = true;                        }                        else {                            newPosition = s.maxTranslate();                        }                    }                    else if (newPosition > s.minTranslate()) {                        if (s.params.freeModeMomentumBounce) {                            if (newPosition - s.minTranslate() > bounceAmount) {                                newPosition = s.minTranslate() + bounceAmount;                            }                            afterBouncePosition = s.minTranslate();                            doBounce = true;                            allowMomentumBounce = true;                        }                        else {                            newPosition = s.minTranslate();                        }                    }                    else if (s.params.freeModeSticky) {                        var j = 0,                            nextSlide;                        for (j = 0; j < s.snapGrid.length; j += 1) {                            if (s.snapGrid[j] > -newPosition) {                                nextSlide = j;                                break;                            }                        }                        if (Math.abs(s.snapGrid[nextSlide] - newPosition) < Math.abs(s.snapGrid[nextSlide - 1] - newPosition) || s.swipeDirection === 'next') {                            newPosition = s.snapGrid[nextSlide];                        } else {                            newPosition = s.snapGrid[nextSlide - 1];                        }                        if (!s.rtl) newPosition = - newPosition;                    }                    //Fix duration                    if (s.velocity !== 0) {                        if (s.rtl) {                            momentumDuration = Math.abs((-newPosition - s.translate) / s.velocity);                        }                        else {                            momentumDuration = Math.abs((newPosition - s.translate) / s.velocity);                        }                    }                    else if (s.params.freeModeSticky) {                        s.slideReset();                        return;                    }                    if (s.params.freeModeMomentumBounce && doBounce) {                        s.updateProgress(afterBouncePosition);                        s.setWrapperTransition(momentumDuration);                        s.setWrapperTranslate(newPosition);                        s.onTransitionStart();                        s.animating = true;                        s.wrapper.transitionEnd(function () {                            if (!s || !allowMomentumBounce) return;                            s.emit('onMomentumBounce', s);                            s.setWrapperTransition(s.params.speed);                            s.setWrapperTranslate(afterBouncePosition);                            s.wrapper.transitionEnd(function () {                                if (!s) return;                                s.onTransitionEnd();                            });                        });                    } else if (s.velocity) {                        s.updateProgress(newPosition);                        s.setWrapperTransition(momentumDuration);                        s.setWrapperTranslate(newPosition);                        s.onTransitionStart();                        if (!s.animating) {                            s.animating = true;                            s.wrapper.transitionEnd(function () {                                if (!s) return;                                s.onTransitionEnd();                            });                        }                    } else {                        s.updateProgress(newPosition);                    }                    s.updateActiveIndex();                }                if (!s.params.freeModeMomentum || timeDiff >= s.params.longSwipesMs) {                    s.updateProgress();                    s.updateActiveIndex();                }                return;            }            // Find current slide            var i, stopIndex = 0, groupSize = s.slidesSizesGrid[0];            for (i = 0; i < s.slidesGrid.length; i += s.params.slidesPerGroup) {                if (typeof s.slidesGrid[i + s.params.slidesPerGroup] !== 'undefined') {                    if (currentPos >= s.slidesGrid[i] && currentPos < s.slidesGrid[i + s.params.slidesPerGroup]) {                        stopIndex = i;                        groupSize = s.slidesGrid[i + s.params.slidesPerGroup] - s.slidesGrid[i];                    }                }                else {                    if (currentPos >= s.slidesGrid[i]) {                        stopIndex = i;                        groupSize = s.slidesGrid[s.slidesGrid.length - 1] - s.slidesGrid[s.slidesGrid.length - 2];                    }                }            }            // Find current slide size            var ratio = (currentPos - s.slidesGrid[stopIndex]) / groupSize;            if (timeDiff > s.params.longSwipesMs) {                // Long touches                if (!s.params.longSwipes) {                    s.slideTo(s.activeIndex);                    return;                }                if (s.swipeDirection === 'next') {                    if (ratio >= s.params.longSwipesRatio) s.slideTo(stopIndex + s.params.slidesPerGroup);                    else s.slideTo(stopIndex);                }                if (s.swipeDirection === 'prev') {                    if (ratio > (1 - s.params.longSwipesRatio)) s.slideTo(stopIndex + s.params.slidesPerGroup);                    else s.slideTo(stopIndex);                }            }            else {                // Short swipes                if (!s.params.shortSwipes) {                    s.slideTo(s.activeIndex);                    return;                }                if (s.swipeDirection === 'next') {                    s.slideTo(stopIndex + s.params.slidesPerGroup);                }                if (s.swipeDirection === 'prev') {                    s.slideTo(stopIndex);                }            }        };        /*=========================          Transitions          ===========================*/        s._slideTo = function (slideIndex, speed) {            return s.slideTo(slideIndex, speed, true, true);        };        s.slideTo = function (slideIndex, speed, runCallbacks, internal) {            if (typeof runCallbacks === 'undefined') runCallbacks = true;            if (typeof slideIndex === 'undefined') slideIndex = 0;            if (slideIndex < 0) slideIndex = 0;            s.snapIndex = Math.floor(slideIndex / s.params.slidesPerGroup);            if (s.snapIndex >= s.snapGrid.length) s.snapIndex = s.snapGrid.length - 1;            var translate = - s.snapGrid[s.snapIndex];            // Stop autoplay            if (s.params.autoplay && s.autoplaying) {                if (internal || !s.params.autoplayDisableOnInteraction) {                    s.pauseAutoplay(speed);                }                else {                    s.stopAutoplay();                }            }            // Update progress            s.updateProgress(translate);            // Normalize slideIndex            for (var i = 0; i < s.slidesGrid.length; i++) {                if (- Math.floor(translate * 100) >= Math.floor(s.slidesGrid[i] * 100)) {                    slideIndex = i;                }            }            // Directions locks            if (!s.params.allowSwipeToNext && translate < s.translate && translate < s.minTranslate()) {                return false;            }            if (!s.params.allowSwipeToPrev && translate > s.translate && translate > s.maxTranslate()) {                if ((s.activeIndex || 0) !== slideIndex ) return false;            }            // Update Index            if (typeof speed === 'undefined') speed = s.params.speed;            s.previousIndex = s.activeIndex || 0;            s.activeIndex = slideIndex;            if ((s.rtl && -translate === s.translate) || (!s.rtl && translate === s.translate)) {                // Update Height                if (s.params.autoHeight) {                    s.updateAutoHeight();                }                s.updateClasses();                if (s.params.effect !== 'slide') {                    s.setWrapperTranslate(translate);                }                return false;            }            s.updateClasses();            s.onTransitionStart(runCallbacks);            if (speed === 0) {                s.setWrapperTranslate(translate);                s.setWrapperTransition(0);                s.onTransitionEnd(runCallbacks);            }            else {                s.setWrapperTranslate(translate);                s.setWrapperTransition(speed);                if (!s.animating) {                    s.animating = true;                    s.wrapper.transitionEnd(function () {                        if (!s) return;                        s.onTransitionEnd(runCallbacks);                    });                }            }            return true;        };        s.onTransitionStart = function (runCallbacks) {            if (typeof runCallbacks === 'undefined') runCallbacks = true;            if (s.params.autoHeight) {                s.updateAutoHeight();            }            if (s.lazy) s.lazy.onTransitionStart();            if (runCallbacks) {                s.emit('onTransitionStart', s);                if (s.activeIndex !== s.previousIndex) {                    s.emit('onSlideChangeStart', s);                    _scope.$emit("$ionicSlides.slideChangeStart", {                      slider: s,                      activeIndex: s.getSlideDataIndex(s.activeIndex),                      previousIndex: s.getSlideDataIndex(s.previousIndex)                    });                    if (s.activeIndex > s.previousIndex) {                        s.emit('onSlideNextStart', s);                    }                    else {                        s.emit('onSlidePrevStart', s);                    }                }            }        };        s.onTransitionEnd = function (runCallbacks) {            s.animating = false;            s.setWrapperTransition(0);            if (typeof runCallbacks === 'undefined') runCallbacks = true;            if (s.lazy) s.lazy.onTransitionEnd();            if (runCallbacks) {                s.emit('onTransitionEnd', s);                if (s.activeIndex !== s.previousIndex) {                    s.emit('onSlideChangeEnd', s);                    _scope.$emit("$ionicSlides.slideChangeEnd", {                      slider: s,                      activeIndex: s.getSlideDataIndex(s.activeIndex),                      previousIndex: s.getSlideDataIndex(s.previousIndex)                    });                    if (s.activeIndex > s.previousIndex) {                        s.emit('onSlideNextEnd', s);                    }                    else {                        s.emit('onSlidePrevEnd', s);                    }                }            }            if (s.params.hashnav && s.hashnav) {                s.hashnav.setHash();            }        };        s.slideNext = function (runCallbacks, speed, internal) {            if (s.params.loop) {                if (s.animating) return false;                s.fixLoop();                var clientLeft = s.container[0].clientLeft;                return s.slideTo(s.activeIndex + s.params.slidesPerGroup, speed, runCallbacks, internal);            }            else return s.slideTo(s.activeIndex + s.params.slidesPerGroup, speed, runCallbacks, internal);        };        s._slideNext = function (speed) {            return s.slideNext(true, speed, true);        };        s.slidePrev = function (runCallbacks, speed, internal) {            if (s.params.loop) {                if (s.animating) return false;                s.fixLoop();                var clientLeft = s.container[0].clientLeft;                return s.slideTo(s.activeIndex - 1, speed, runCallbacks, internal);            }            else return s.slideTo(s.activeIndex - 1, speed, runCallbacks, internal);        };        s._slidePrev = function (speed) {            return s.slidePrev(true, speed, true);        };        s.slideReset = function (runCallbacks, speed, internal) {            return s.slideTo(s.activeIndex, speed, runCallbacks);        };        /*=========================          Translate/transition helpers          ===========================*/        s.setWrapperTransition = function (duration, byController) {            s.wrapper.transition(duration);            if (s.params.effect !== 'slide' && s.effects[s.params.effect]) {                s.effects[s.params.effect].setTransition(duration);            }            if (s.params.parallax && s.parallax) {                s.parallax.setTransition(duration);            }            if (s.params.scrollbar && s.scrollbar) {                s.scrollbar.setTransition(duration);            }            if (s.params.control && s.controller) {                s.controller.setTransition(duration, byController);            }            s.emit('onSetTransition', s, duration);        };        s.setWrapperTranslate = function (translate, updateActiveIndex, byController) {            var x = 0, y = 0, z = 0;            if (isH()) {                x = s.rtl ? -translate : translate;            }            else {                y = translate;            }            if (s.params.roundLengths) {                x = round(x);                y = round(y);            }            if (!s.params.virtualTranslate) {                if (s.support.transforms3d) s.wrapper.transform('translate3d(' + x + 'px, ' + y + 'px, ' + z + 'px)');                else s.wrapper.transform('translate(' + x + 'px, ' + y + 'px)');            }            s.translate = isH() ? x : y;            // Check if we need to update progress            var progress;            var translatesDiff = s.maxTranslate() - s.minTranslate();            if (translatesDiff === 0) {                progress = 0;            }            else {                progress = (translate - s.minTranslate()) / (translatesDiff);            }            if (progress !== s.progress) {                s.updateProgress(translate);            }            if (updateActiveIndex) s.updateActiveIndex();            if (s.params.effect !== 'slide' && s.effects[s.params.effect]) {                s.effects[s.params.effect].setTranslate(s.translate);            }            if (s.params.parallax && s.parallax) {                s.parallax.setTranslate(s.translate);            }            if (s.params.scrollbar && s.scrollbar) {                s.scrollbar.setTranslate(s.translate);            }            if (s.params.control && s.controller) {                s.controller.setTranslate(s.translate, byController);            }            s.emit('onSetTranslate', s, s.translate);        };        s.getTranslate = function (el, axis) {            var matrix, curTransform, curStyle, transformMatrix;            // automatic axis detection            if (typeof axis === 'undefined') {                axis = 'x';            }            if (s.params.virtualTranslate) {                return s.rtl ? -s.translate : s.translate;            }            curStyle = window.getComputedStyle(el, null);            if (window.WebKitCSSMatrix) {                curTransform = curStyle.transform || curStyle.webkitTransform;                if (curTransform.split(',').length > 6) {                    curTransform = curTransform.split(', ').map(function(a){                        return a.replace(',','.');                    }).join(', ');                }                // Some old versions of Webkit choke when 'none' is passed; pass                // empty string instead in this case                transformMatrix = new window.WebKitCSSMatrix(curTransform === 'none' ? '' : curTransform);            }            else {                transformMatrix = curStyle.MozTransform || curStyle.OTransform || curStyle.MsTransform || curStyle.msTransform  || curStyle.transform || curStyle.getPropertyValue('transform').replace('translate(', 'matrix(1, 0, 0, 1,');                matrix = transformMatrix.toString().split(',');            }            if (axis === 'x') {                //Latest Chrome and webkits Fix                if (window.WebKitCSSMatrix)                    curTransform = transformMatrix.m41;                //Crazy IE10 Matrix                else if (matrix.length === 16)                    curTransform = parseFloat(matrix[12]);                //Normal Browsers                else                    curTransform = parseFloat(matrix[4]);            }            if (axis === 'y') {                //Latest Chrome and webkits Fix                if (window.WebKitCSSMatrix)                    curTransform = transformMatrix.m42;                //Crazy IE10 Matrix                else if (matrix.length === 16)                    curTransform = parseFloat(matrix[13]);                //Normal Browsers                else                    curTransform = parseFloat(matrix[5]);            }            if (s.rtl && curTransform) curTransform = -curTransform;            return curTransform || 0;        };        s.getWrapperTranslate = function (axis) {            if (typeof axis === 'undefined') {                axis = isH() ? 'x' : 'y';            }            return s.getTranslate(s.wrapper[0], axis);        };        /*=========================          Observer          ===========================*/        s.observers = [];        function initObserver(target, options) {            options = options || {};            // create an observer instance            var ObserverFunc = window.MutationObserver || window.WebkitMutationObserver;            var observer = new ObserverFunc(function (mutations) {                mutations.forEach(function (mutation) {                    s.onResize(true);                    s.emit('onObserverUpdate', s, mutation);                });            });            observer.observe(target, {                attributes: typeof options.attributes === 'undefined' ? true : options.attributes,                childList: typeof options.childList === 'undefined' ? true : options.childList,                characterData: typeof options.characterData === 'undefined' ? true : options.characterData            });            s.observers.push(observer);        }        s.initObservers = function () {            if (s.params.observeParents) {                var containerParents = s.container.parents();                for (var i = 0; i < containerParents.length; i++) {                    initObserver(containerParents[i]);                }            }            // Observe container            initObserver(s.container[0], {childList: false});            // Observe wrapper            initObserver(s.wrapper[0], {attributes: false});        };        s.disconnectObservers = function () {            for (var i = 0; i < s.observers.length; i++) {                s.observers[i].disconnect();            }            s.observers = [];        };        s.updateLoop = function(){          var currentSlide = s.slides.eq(s.activeIndex);          if ( angular.element(currentSlide).hasClass(s.params.slideDuplicateClass) ){            // we're on a duplicate, so slide to the non-duplicate            var swiperSlideIndex = angular.element(currentSlide).attr("data-swiper-slide-index");            var slides = s.wrapper.children('.' + s.params.slideClass);            for ( var i = 0; i < slides.length; i++ ){              if ( !angular.element(slides[i]).hasClass(s.params.slideDuplicateClass) && angular.element(slides[i]).attr("data-swiper-slide-index") === swiperSlideIndex ){                s.slideTo(i, 0, false, true);                break;              }            }            // if we needed to switch slides, we did that.  So, now call the createLoop function internally            setTimeout(function(){              s.createLoop();            }, 50);          }        }        s.getSlideDataIndex = function(slideIndex){          // this is an Ionic custom function          // Swiper loops utilize duplicate DOM elements for slides when in a loop          // which means that we cannot rely on the actual slide index for our events          // because index 0 does not necessarily point to index 0          // and index n+1 does not necessarily point to the expected piece of data          // therefore, rather than using the actual slide index we should          // use the data index that swiper includes as an attribute on the dom elements          // because this is what will be meaningful to the consumer of our events          var slide = s.slides.eq(slideIndex);          var attributeIndex = angular.element(slide).attr("data-swiper-slide-index");          return parseInt(attributeIndex);        }        /*=========================          Loop          ===========================*/        // Create looped slides        s.createLoop = function () {          //console.log("Slider create loop method");            //var toRemove = s.wrapper.children('.' + s.params.slideClass + '.' + s.params.slideDuplicateClass);            //angular.element(toRemove).remove();            s.wrapper.children('.' + s.params.slideClass + '.' + s.params.slideDuplicateClass).remove();            var slides = s.wrapper.children('.' + s.params.slideClass);            if(s.params.slidesPerView === 'auto' && !s.params.loopedSlides) s.params.loopedSlides = slides.length;            s.loopedSlides = parseInt(s.params.loopedSlides || s.params.slidesPerView, 10);            s.loopedSlides = s.loopedSlides + s.params.loopAdditionalSlides;            if (s.loopedSlides > slides.length) {                s.loopedSlides = slides.length;            }            var prependSlides = [], appendSlides = [], i, scope, newNode;            slides.each(function (index, el) {                var slide = $(this);                if (index < s.loopedSlides) appendSlides.push(el);                if (index < slides.length && index >= slides.length - s.loopedSlides) prependSlides.push(el);                slide.attr('data-swiper-slide-index', index);            });            for (i = 0; i < appendSlides.length; i++) {              newNode = angular.element(appendSlides[i]).clone().addClass(s.params.slideDuplicateClass);              newNode.removeAttr('ng-transclude');              newNode.removeAttr('ng-repeat');              scope = angular.element(appendSlides[i]).scope();              newNode = $compile(newNode)(scope);              angular.element(s.wrapper).append(newNode);              //s.wrapper.append($(appendSlides[i].cloneNode(true)).addClass(s.params.slideDuplicateClass));            }            for (i = prependSlides.length - 1; i >= 0; i--) {              //s.wrapper.prepend($(prependSlides[i].cloneNode(true)).addClass(s.params.slideDuplicateClass));              newNode = angular.element(prependSlides[i]).clone().addClass(s.params.slideDuplicateClass);              newNode.removeAttr('ng-transclude');              newNode.removeAttr('ng-repeat');              scope = angular.element(prependSlides[i]).scope();              newNode = $compile(newNode)(scope);              angular.element(s.wrapper).prepend(newNode);            }        };        s.destroyLoop = function () {            s.wrapper.children('.' + s.params.slideClass + '.' + s.params.slideDuplicateClass).remove();            s.slides.removeAttr('data-swiper-slide-index');        };        s.fixLoop = function () {            var newIndex;            //Fix For Negative Oversliding            if (s.activeIndex < s.loopedSlides) {                newIndex = s.slides.length - s.loopedSlides * 3 + s.activeIndex;                newIndex = newIndex + s.loopedSlides;                s.slideTo(newIndex, 0, false, true);            }            //Fix For Positive Oversliding            else if ((s.params.slidesPerView === 'auto' && s.activeIndex >= s.loopedSlides * 2) || (s.activeIndex > s.slides.length - s.params.slidesPerView * 2)) {                newIndex = -s.slides.length + s.activeIndex + s.loopedSlides;                newIndex = newIndex + s.loopedSlides;                s.slideTo(newIndex, 0, false, true);            }        };        /*=========================          Append/Prepend/Remove Slides          ===========================*/        s.appendSlide = function (slides) {            if (s.params.loop) {                s.destroyLoop();            }            if (typeof slides === 'object' && slides.length) {                for (var i = 0; i < slides.length; i++) {                    if (slides[i]) s.wrapper.append(slides[i]);                }            }            else {                s.wrapper.append(slides);            }            if (s.params.loop) {                s.createLoop();            }            if (!(s.params.observer && s.support.observer)) {                s.update(true);            }        };        s.prependSlide = function (slides) {            if (s.params.loop) {                s.destroyLoop();            }            var newActiveIndex = s.activeIndex + 1;            if (typeof slides === 'object' && slides.length) {                for (var i = 0; i < slides.length; i++) {                    if (slides[i]) s.wrapper.prepend(slides[i]);                }                newActiveIndex = s.activeIndex + slides.length;            }            else {                s.wrapper.prepend(slides);            }            if (s.params.loop) {                s.createLoop();            }            if (!(s.params.observer && s.support.observer)) {                s.update(true);            }            s.slideTo(newActiveIndex, 0, false);        };        s.removeSlide = function (slidesIndexes) {            if (s.params.loop) {                s.destroyLoop();                s.slides = s.wrapper.children('.' + s.params.slideClass);            }            var newActiveIndex = s.activeIndex,                indexToRemove;            if (typeof slidesIndexes === 'object' && slidesIndexes.length) {                for (var i = 0; i < slidesIndexes.length; i++) {                    indexToRemove = slidesIndexes[i];                    if (s.slides[indexToRemove]) s.slides.eq(indexToRemove).remove();                    if (indexToRemove < newActiveIndex) newActiveIndex--;                }                newActiveIndex = Math.max(newActiveIndex, 0);            }            else {                indexToRemove = slidesIndexes;                if (s.slides[indexToRemove]) s.slides.eq(indexToRemove).remove();                if (indexToRemove < newActiveIndex) newActiveIndex--;                newActiveIndex = Math.max(newActiveIndex, 0);            }            if (s.params.loop) {                s.createLoop();            }            if (!(s.params.observer && s.support.observer)) {                s.update(true);            }            if (s.params.loop) {                s.slideTo(newActiveIndex + s.loopedSlides, 0, false);            }            else {                s.slideTo(newActiveIndex, 0, false);            }        };        s.removeAllSlides = function () {            var slidesIndexes = [];            for (var i = 0; i < s.slides.length; i++) {                slidesIndexes.push(i);            }            s.removeSlide(slidesIndexes);        };        /*=========================          Effects          ===========================*/        s.effects = {            fade: {                setTranslate: function () {                    for (var i = 0; i < s.slides.length; i++) {                        var slide = s.slides.eq(i);                        var offset = slide[0].swiperSlideOffset;                        var tx = -offset;                        if (!s.params.virtualTranslate) tx = tx - s.translate;                        var ty = 0;                        if (!isH()) {                            ty = tx;                            tx = 0;                        }                        var slideOpacity = s.params.fade.crossFade ?                                Math.max(1 - Math.abs(slide[0].progress), 0) :                                1 + Math.min(Math.max(slide[0].progress, -1), 0);                        slide                            .css({                                opacity: slideOpacity                            })                            .transform('translate3d(' + tx + 'px, ' + ty + 'px, 0px)');                    }                },                setTransition: function (duration) {                    s.slides.transition(duration);                    if (s.params.virtualTranslate && duration !== 0) {                        var eventTriggered = false;                        s.slides.transitionEnd(function () {                            if (eventTriggered) return;                            if (!s) return;                            eventTriggered = true;                            s.animating = false;                            var triggerEvents = ['webkitTransitionEnd', 'transitionend', 'oTransitionEnd', 'MSTransitionEnd', 'msTransitionEnd'];                            for (var i = 0; i < triggerEvents.length; i++) {                                s.wrapper.trigger(triggerEvents[i]);                            }                        });                    }                }            },            cube: {                setTranslate: function () {                    var wrapperRotate = 0, cubeShadow;                    if (s.params.cube.shadow) {                        if (isH()) {                            cubeShadow = s.wrapper.find('.swiper-cube-shadow');                            if (cubeShadow.length === 0) {                                cubeShadow = $('<div class="swiper-cube-shadow"></div>');                                s.wrapper.append(cubeShadow);                            }                            cubeShadow.css({height: s.width + 'px'});                        }                        else {                            cubeShadow = s.container.find('.swiper-cube-shadow');                            if (cubeShadow.length === 0) {                                cubeShadow = $('<div class="swiper-cube-shadow"></div>');                                s.container.append(cubeShadow);                            }                        }                    }                    for (var i = 0; i < s.slides.length; i++) {                        var slide = s.slides.eq(i);                        var slideAngle = i * 90;                        var round = Math.floor(slideAngle / 360);                        if (s.rtl) {                            slideAngle = -slideAngle;                            round = Math.floor(-slideAngle / 360);                        }                        var progress = Math.max(Math.min(slide[0].progress, 1), -1);                        var tx = 0, ty = 0, tz = 0;                        if (i % 4 === 0) {                            tx = - round * 4 * s.size;                            tz = 0;                        }                        else if ((i - 1) % 4 === 0) {                            tx = 0;                            tz = - round * 4 * s.size;                        }                        else if ((i - 2) % 4 === 0) {                            tx = s.size + round * 4 * s.size;                            tz = s.size;                        }                        else if ((i - 3) % 4 === 0) {                            tx = - s.size;                            tz = 3 * s.size + s.size * 4 * round;                        }                        if (s.rtl) {                            tx = -tx;                        }                        if (!isH()) {                            ty = tx;                            tx = 0;                        }                        var transform = 'rotateX(' + (isH() ? 0 : -slideAngle) + 'deg) rotateY(' + (isH() ? slideAngle : 0) + 'deg) translate3d(' + tx + 'px, ' + ty + 'px, ' + tz + 'px)';                        if (progress <= 1 && progress > -1) {                            wrapperRotate = i * 90 + progress * 90;                            if (s.rtl) wrapperRotate = -i * 90 - progress * 90;                        }                        slide.transform(transform);                        if (s.params.cube.slideShadows) {                            //Set shadows                            var shadowBefore = isH() ? slide.find('.swiper-slide-shadow-left') : slide.find('.swiper-slide-shadow-top');                            var shadowAfter = isH() ? slide.find('.swiper-slide-shadow-right') : slide.find('.swiper-slide-shadow-bottom');                            if (shadowBefore.length === 0) {                                shadowBefore = $('<div class="swiper-slide-shadow-' + (isH() ? 'left' : 'top') + '"></div>');                                slide.append(shadowBefore);                            }                            if (shadowAfter.length === 0) {                                shadowAfter = $('<div class="swiper-slide-shadow-' + (isH() ? 'right' : 'bottom') + '"></div>');                                slide.append(shadowAfter);                            }                            var shadowOpacity = slide[0].progress;                            if (shadowBefore.length) shadowBefore[0].style.opacity = -slide[0].progress;                            if (shadowAfter.length) shadowAfter[0].style.opacity = slide[0].progress;                        }                    }                    s.wrapper.css({                        '-webkit-transform-origin': '50% 50% -' + (s.size / 2) + 'px',                        '-moz-transform-origin': '50% 50% -' + (s.size / 2) + 'px',                        '-ms-transform-origin': '50% 50% -' + (s.size / 2) + 'px',                        'transform-origin': '50% 50% -' + (s.size / 2) + 'px'                    });                    if (s.params.cube.shadow) {                        if (isH()) {                            cubeShadow.transform('translate3d(0px, ' + (s.width / 2 + s.params.cube.shadowOffset) + 'px, ' + (-s.width / 2) + 'px) rotateX(90deg) rotateZ(0deg) scale(' + (s.params.cube.shadowScale) + ')');                        }                        else {                            var shadowAngle = Math.abs(wrapperRotate) - Math.floor(Math.abs(wrapperRotate) / 90) * 90;                            var multiplier = 1.5 - (Math.sin(shadowAngle * 2 * Math.PI / 360) / 2 + Math.cos(shadowAngle * 2 * Math.PI / 360) / 2);                            var scale1 = s.params.cube.shadowScale,                                scale2 = s.params.cube.shadowScale / multiplier,                                offset = s.params.cube.shadowOffset;                            cubeShadow.transform('scale3d(' + scale1 + ', 1, ' + scale2 + ') translate3d(0px, ' + (s.height / 2 + offset) + 'px, ' + (-s.height / 2 / scale2) + 'px) rotateX(-90deg)');                        }                    }                    var zFactor = (s.isSafari || s.isUiWebView) ? (-s.size / 2) : 0;                    s.wrapper.transform('translate3d(0px,0,' + zFactor + 'px) rotateX(' + (isH() ? 0 : wrapperRotate) + 'deg) rotateY(' + (isH() ? -wrapperRotate : 0) + 'deg)');                },                setTransition: function (duration) {                    s.slides.transition(duration).find('.swiper-slide-shadow-top, .swiper-slide-shadow-right, .swiper-slide-shadow-bottom, .swiper-slide-shadow-left').transition(duration);                    if (s.params.cube.shadow && !isH()) {                        s.container.find('.swiper-cube-shadow').transition(duration);                    }                }            },            coverflow: {                setTranslate: function () {                    var transform = s.translate;                    var center = isH() ? -transform + s.width / 2 : -transform + s.height / 2;                    var rotate = isH() ? s.params.coverflow.rotate: -s.params.coverflow.rotate;                    var translate = s.params.coverflow.depth;                    //Each slide offset from center                    for (var i = 0, length = s.slides.length; i < length; i++) {                        var slide = s.slides.eq(i);                        var slideSize = s.slidesSizesGrid[i];                        var slideOffset = slide[0].swiperSlideOffset;                        var offsetMultiplier = (center - slideOffset - slideSize / 2) / slideSize * s.params.coverflow.modifier;                        var rotateY = isH() ? rotate * offsetMultiplier : 0;                        var rotateX = isH() ? 0 : rotate * offsetMultiplier;                        // var rotateZ = 0                        var translateZ = -translate * Math.abs(offsetMultiplier);                        var translateY = isH() ? 0 : s.params.coverflow.stretch * (offsetMultiplier);                        var translateX = isH() ? s.params.coverflow.stretch * (offsetMultiplier) : 0;                        //Fix for ultra small values                        if (Math.abs(translateX) < 0.001) translateX = 0;                        if (Math.abs(translateY) < 0.001) translateY = 0;                        if (Math.abs(translateZ) < 0.001) translateZ = 0;                        if (Math.abs(rotateY) < 0.001) rotateY = 0;                        if (Math.abs(rotateX) < 0.001) rotateX = 0;                        var slideTransform = 'translate3d(' + translateX + 'px,' + translateY + 'px,' + translateZ + 'px)  rotateX(' + rotateX + 'deg) rotateY(' + rotateY + 'deg)';                        slide.transform(slideTransform);                        slide[0].style.zIndex = -Math.abs(Math.round(offsetMultiplier)) + 1;                        if (s.params.coverflow.slideShadows) {                            //Set shadows                            var shadowBefore = isH() ? slide.find('.swiper-slide-shadow-left') : slide.find('.swiper-slide-shadow-top');                            var shadowAfter = isH() ? slide.find('.swiper-slide-shadow-right') : slide.find('.swiper-slide-shadow-bottom');                            if (shadowBefore.length === 0) {                                shadowBefore = $('<div class="swiper-slide-shadow-' + (isH() ? 'left' : 'top') + '"></div>');                                slide.append(shadowBefore);                            }                            if (shadowAfter.length === 0) {                                shadowAfter = $('<div class="swiper-slide-shadow-' + (isH() ? 'right' : 'bottom') + '"></div>');                                slide.append(shadowAfter);                            }                            if (shadowBefore.length) shadowBefore[0].style.opacity = offsetMultiplier > 0 ? offsetMultiplier : 0;                            if (shadowAfter.length) shadowAfter[0].style.opacity = (-offsetMultiplier) > 0 ? -offsetMultiplier : 0;                        }                    }                    //Set correct perspective for IE10                    if (s.browser.ie) {                        var ws = s.wrapper[0].style;                        ws.perspectiveOrigin = center + 'px 50%';                    }                },                setTransition: function (duration) {                    s.slides.transition(duration).find('.swiper-slide-shadow-top, .swiper-slide-shadow-right, .swiper-slide-shadow-bottom, .swiper-slide-shadow-left').transition(duration);                }            }        };        /*=========================          Images Lazy Loading          ===========================*/        s.lazy = {            initialImageLoaded: false,            loadImageInSlide: function (index, loadInDuplicate) {                if (typeof index === 'undefined') return;                if (typeof loadInDuplicate === 'undefined') loadInDuplicate = true;                if (s.slides.length === 0) return;                var slide = s.slides.eq(index);                var img = slide.find('.swiper-lazy:not(.swiper-lazy-loaded):not(.swiper-lazy-loading)');                if (slide.hasClass('swiper-lazy') && !slide.hasClass('swiper-lazy-loaded') && !slide.hasClass('swiper-lazy-loading')) {                    img = img.add(slide[0]);                }                if (img.length === 0) return;                img.each(function () {                    var _img = $(this);                    _img.addClass('swiper-lazy-loading');                    var background = _img.attr('data-background');                    var src = _img.attr('data-src'),                        srcset = _img.attr('data-srcset');                    s.loadImage(_img[0], (src || background), srcset, false, function () {                        if (background) {                            _img.css('background-image', 'url(' + background + ')');                            _img.removeAttr('data-background');                        }                        else {                            if (srcset) {                                _img.attr('srcset', srcset);                                _img.removeAttr('data-srcset');                            }                            if (src) {                                _img.attr('src', src);                                _img.removeAttr('data-src');                            }                        }                        _img.addClass('swiper-lazy-loaded').removeClass('swiper-lazy-loading');                        slide.find('.swiper-lazy-preloader, .preloader').remove();                        if (s.params.loop && loadInDuplicate) {                            var slideOriginalIndex = slide.attr('data-swiper-slide-index');                            if (slide.hasClass(s.params.slideDuplicateClass)) {                                var originalSlide = s.wrapper.children('[data-swiper-slide-index="' + slideOriginalIndex + '"]:not(.' + s.params.slideDuplicateClass + ')');                                s.lazy.loadImageInSlide(originalSlide.index(), false);                            }                            else {                                var duplicatedSlide = s.wrapper.children('.' + s.params.slideDuplicateClass + '[data-swiper-slide-index="' + slideOriginalIndex + '"]');                                s.lazy.loadImageInSlide(duplicatedSlide.index(), false);                            }                        }                        s.emit('onLazyImageReady', s, slide[0], _img[0]);                    });                    s.emit('onLazyImageLoad', s, slide[0], _img[0]);                });            },            load: function () {                var i;                if (s.params.watchSlidesVisibility) {                    s.wrapper.children('.' + s.params.slideVisibleClass).each(function () {                        s.lazy.loadImageInSlide($(this).index());                    });                }                else {                    if (s.params.slidesPerView > 1) {                        for (i = s.activeIndex; i < s.activeIndex + s.params.slidesPerView ; i++) {                            if (s.slides[i]) s.lazy.loadImageInSlide(i);                        }                    }                    else {                        s.lazy.loadImageInSlide(s.activeIndex);                    }                }                if (s.params.lazyLoadingInPrevNext) {                    if (s.params.slidesPerView > 1) {                        // Next Slides                        for (i = s.activeIndex + s.params.slidesPerView; i < s.activeIndex + s.params.slidesPerView + s.params.slidesPerView; i++) {                            if (s.slides[i]) s.lazy.loadImageInSlide(i);                        }                        // Prev Slides                        for (i = s.activeIndex - s.params.slidesPerView; i < s.activeIndex ; i++) {                            if (s.slides[i]) s.lazy.loadImageInSlide(i);                        }                    }                    else {                        var nextSlide = s.wrapper.children('.' + s.params.slideNextClass);                        if (nextSlide.length > 0) s.lazy.loadImageInSlide(nextSlide.index());                        var prevSlide = s.wrapper.children('.' + s.params.slidePrevClass);                        if (prevSlide.length > 0) s.lazy.loadImageInSlide(prevSlide.index());                    }                }            },            onTransitionStart: function () {                if (s.params.lazyLoading) {                    if (s.params.lazyLoadingOnTransitionStart || (!s.params.lazyLoadingOnTransitionStart && !s.lazy.initialImageLoaded)) {                        s.lazy.load();                    }                }            },            onTransitionEnd: function () {                if (s.params.lazyLoading && !s.params.lazyLoadingOnTransitionStart) {                    s.lazy.load();                }            }        };        /*=========================          Scrollbar          ===========================*/        s.scrollbar = {            isTouched: false,            setDragPosition: function (e) {                var sb = s.scrollbar;                var x = 0, y = 0;                var translate;                var pointerPosition = isH() ?                    ((e.type === 'touchstart' || e.type === 'touchmove') ? e.targetTouches[0].pageX : e.pageX || e.clientX) :                    ((e.type === 'touchstart' || e.type === 'touchmove') ? e.targetTouches[0].pageY : e.pageY || e.clientY) ;                var position = (pointerPosition) - sb.track.offset()[isH() ? 'left' : 'top'] - sb.dragSize / 2;                var positionMin = -s.minTranslate() * sb.moveDivider;                var positionMax = -s.maxTranslate() * sb.moveDivider;                if (position < positionMin) {                    position = positionMin;                }                else if (position > positionMax) {                    position = positionMax;                }                position = -position / sb.moveDivider;                s.updateProgress(position);                s.setWrapperTranslate(position, true);            },            dragStart: function (e) {                var sb = s.scrollbar;                sb.isTouched = true;                e.preventDefault();                e.stopPropagation();                sb.setDragPosition(e);                clearTimeout(sb.dragTimeout);                sb.track.transition(0);                if (s.params.scrollbarHide) {                    sb.track.css('opacity', 1);                }                s.wrapper.transition(100);                sb.drag.transition(100);                s.emit('onScrollbarDragStart', s);            },            dragMove: function (e) {                var sb = s.scrollbar;                if (!sb.isTouched) return;                if (e.preventDefault) e.preventDefault();                else e.returnValue = false;                sb.setDragPosition(e);                s.wrapper.transition(0);                sb.track.transition(0);                sb.drag.transition(0);                s.emit('onScrollbarDragMove', s);            },            dragEnd: function (e) {                var sb = s.scrollbar;                if (!sb.isTouched) return;                sb.isTouched = false;                if (s.params.scrollbarHide) {                    clearTimeout(sb.dragTimeout);                    sb.dragTimeout = setTimeout(function () {                        sb.track.css('opacity', 0);                        sb.track.transition(400);                    }, 1000);                }                s.emit('onScrollbarDragEnd', s);                if (s.params.scrollbarSnapOnRelease) {                    s.slideReset();                }            },            enableDraggable: function () {                var sb = s.scrollbar;                var target = s.support.touch ? sb.track : document;                $(sb.track).on(s.touchEvents.start, sb.dragStart);                $(target).on(s.touchEvents.move, sb.dragMove);                $(target).on(s.touchEvents.end, sb.dragEnd);            },            disableDraggable: function () {                var sb = s.scrollbar;                var target = s.support.touch ? sb.track : document;                $(sb.track).off(s.touchEvents.start, sb.dragStart);                $(target).off(s.touchEvents.move, sb.dragMove);                $(target).off(s.touchEvents.end, sb.dragEnd);            },            set: function () {                if (!s.params.scrollbar) return;                var sb = s.scrollbar;                sb.track = $(s.params.scrollbar);                sb.drag = sb.track.find('.swiper-scrollbar-drag');                if (sb.drag.length === 0) {                    sb.drag = $('<div class="swiper-scrollbar-drag"></div>');                    sb.track.append(sb.drag);                }                sb.drag[0].style.width = '';                sb.drag[0].style.height = '';                sb.trackSize = isH() ? sb.track[0].offsetWidth : sb.track[0].offsetHeight;                sb.divider = s.size / s.virtualSize;                sb.moveDivider = sb.divider * (sb.trackSize / s.size);                sb.dragSize = sb.trackSize * sb.divider;                if (isH()) {                    sb.drag[0].style.width = sb.dragSize + 'px';                }                else {                    sb.drag[0].style.height = sb.dragSize + 'px';                }                if (sb.divider >= 1) {                    sb.track[0].style.display = 'none';                }                else {                    sb.track[0].style.display = '';                }                if (s.params.scrollbarHide) {                    sb.track[0].style.opacity = 0;                }            },            setTranslate: function () {                if (!s.params.scrollbar) return;                var diff;                var sb = s.scrollbar;                var translate = s.translate || 0;                var newPos;                var newSize = sb.dragSize;                newPos = (sb.trackSize - sb.dragSize) * s.progress;                if (s.rtl && isH()) {                    newPos = -newPos;                    if (newPos > 0) {                        newSize = sb.dragSize - newPos;                        newPos = 0;                    }                    else if (-newPos + sb.dragSize > sb.trackSize) {                        newSize = sb.trackSize + newPos;                    }                }                else {                    if (newPos < 0) {                        newSize = sb.dragSize + newPos;                        newPos = 0;                    }                    else if (newPos + sb.dragSize > sb.trackSize) {                        newSize = sb.trackSize - newPos;                    }                }                if (isH()) {                    if (s.support.transforms3d) {                        sb.drag.transform('translate3d(' + (newPos) + 'px, 0, 0)');                    }                    else {                        sb.drag.transform('translateX(' + (newPos) + 'px)');                    }                    sb.drag[0].style.width = newSize + 'px';                }                else {                    if (s.support.transforms3d) {                        sb.drag.transform('translate3d(0px, ' + (newPos) + 'px, 0)');                    }                    else {                        sb.drag.transform('translateY(' + (newPos) + 'px)');                    }                    sb.drag[0].style.height = newSize + 'px';                }                if (s.params.scrollbarHide) {                    clearTimeout(sb.timeout);                    sb.track[0].style.opacity = 1;                    sb.timeout = setTimeout(function () {                        sb.track[0].style.opacity = 0;                        sb.track.transition(400);                    }, 1000);                }            },            setTransition: function (duration) {                if (!s.params.scrollbar) return;                s.scrollbar.drag.transition(duration);            }        };        /*=========================          Controller          ===========================*/        s.controller = {            LinearSpline: function (x, y) {                this.x = x;                this.y = y;                this.lastIndex = x.length - 1;                // Given an x value (x2), return the expected y2 value:                // (x1,y1) is the known point before given value,                // (x3,y3) is the known point after given value.                var i1, i3;                var l = this.x.length;                this.interpolate = function (x2) {                    if (!x2) return 0;                    // Get the indexes of x1 and x3 (the array indexes before and after given x2):                    i3 = binarySearch(this.x, x2);                    i1 = i3 - 1;                    // We have our indexes i1 & i3, so we can calculate already:                    // y2 := ((x2−x1) × (y3−y1)) ÷ (x3−x1) + y1                    return ((x2 - this.x[i1]) * (this.y[i3] - this.y[i1])) / (this.x[i3] - this.x[i1]) + this.y[i1];                };                var binarySearch = (function() {                    var maxIndex, minIndex, guess;                    return function(array, val) {                        minIndex = -1;                        maxIndex = array.length;                        while (maxIndex - minIndex > 1)                            if (array[guess = maxIndex + minIndex >> 1] <= val) {                                minIndex = guess;                            } else {                                maxIndex = guess;                            }                        return maxIndex;                    };                })();            },            //xxx: for now i will just save one spline function to to            getInterpolateFunction: function(c){                if(!s.controller.spline) s.controller.spline = s.params.loop ?                    new s.controller.LinearSpline(s.slidesGrid, c.slidesGrid) :                    new s.controller.LinearSpline(s.snapGrid, c.snapGrid);            },            setTranslate: function (translate, byController) {               var controlled = s.params.control;               var multiplier, controlledTranslate;               function setControlledTranslate(c) {                    // this will create an Interpolate function based on the snapGrids                    // x is the Grid of the scrolled scroller and y will be the controlled scroller                    // it makes sense to create this only once and recall it for the interpolation                    // the function does a lot of value caching for performance                    translate = c.rtl && c.params.direction === 'horizontal' ? -s.translate : s.translate;                    if (s.params.controlBy === 'slide') {                        s.controller.getInterpolateFunction(c);                        // i am not sure why the values have to be multiplicated this way, tried to invert the snapGrid                        // but it did not work out                        controlledTranslate = -s.controller.spline.interpolate(-translate);                    }                    if(!controlledTranslate || s.params.controlBy === 'container'){                        multiplier = (c.maxTranslate() - c.minTranslate()) / (s.maxTranslate() - s.minTranslate());                        controlledTranslate = (translate - s.minTranslate()) * multiplier + c.minTranslate();                    }                    if (s.params.controlInverse) {                        controlledTranslate = c.maxTranslate() - controlledTranslate;                    }                    c.updateProgress(controlledTranslate);                    c.setWrapperTranslate(controlledTranslate, false, s);                    c.updateActiveIndex();               }               if (s.isArray(controlled)) {                   for (var i = 0; i < controlled.length; i++) {                       if (controlled[i] !== byController && controlled[i] instanceof Swiper) {                           setControlledTranslate(controlled[i]);                       }                   }               }               else if (controlled instanceof Swiper && byController !== controlled) {                   setControlledTranslate(controlled);               }            },            setTransition: function (duration, byController) {                var controlled = s.params.control;                var i;                function setControlledTransition(c) {                    c.setWrapperTransition(duration, s);                    if (duration !== 0) {                        c.onTransitionStart();                        c.wrapper.transitionEnd(function(){                            if (!controlled) return;                            if (c.params.loop && s.params.controlBy === 'slide') {                                c.fixLoop();                            }                            c.onTransitionEnd();                        });                    }                }                if (s.isArray(controlled)) {                    for (i = 0; i < controlled.length; i++) {                        if (controlled[i] !== byController && controlled[i] instanceof Swiper) {                            setControlledTransition(controlled[i]);                        }                    }                }                else if (controlled instanceof Swiper && byController !== controlled) {                    setControlledTransition(controlled);                }            }        };        /*=========================          Hash Navigation          ===========================*/        s.hashnav = {            init: function () {                if (!s.params.hashnav) return;                s.hashnav.initialized = true;                var hash = document.location.hash.replace('#', '');                if (!hash) return;                var speed = 0;                for (var i = 0, length = s.slides.length; i < length; i++) {                    var slide = s.slides.eq(i);                    var slideHash = slide.attr('data-hash');                    if (slideHash === hash && !slide.hasClass(s.params.slideDuplicateClass)) {                        var index = slide.index();                        s.slideTo(index, speed, s.params.runCallbacksOnInit, true);                    }                }            },            setHash: function () {                if (!s.hashnav.initialized || !s.params.hashnav) return;                document.location.hash = s.slides.eq(s.activeIndex).attr('data-hash') || '';            }        };        /*=========================          Keyboard Control          ===========================*/        function handleKeyboard(e) {            if (e.originalEvent) e = e.originalEvent; //jquery fix            var kc = e.keyCode || e.charCode;            // Directions locks            if (!s.params.allowSwipeToNext && (isH() && kc === 39 || !isH() && kc === 40)) {                return false;            }            if (!s.params.allowSwipeToPrev && (isH() && kc === 37 || !isH() && kc === 38)) {                return false;            }            if (e.shiftKey || e.altKey || e.ctrlKey || e.metaKey) {                return;            }            if (document.activeElement && document.activeElement.nodeName && (document.activeElement.nodeName.toLowerCase() === 'input' || document.activeElement.nodeName.toLowerCase() === 'textarea')) {                return;            }            if (kc === 37 || kc === 39 || kc === 38 || kc === 40) {                var inView = false;                //Check that swiper should be inside of visible area of window                if (s.container.parents('.swiper-slide').length > 0 && s.container.parents('.swiper-slide-active').length === 0) {                    return;                }                var windowScroll = {                    left: window.pageXOffset,                    top: window.pageYOffset                };                var windowWidth = window.innerWidth;                var windowHeight = window.innerHeight;                var swiperOffset = s.container.offset();                if (s.rtl) swiperOffset.left = swiperOffset.left - s.container[0].scrollLeft;                var swiperCoord = [                    [swiperOffset.left, swiperOffset.top],                    [swiperOffset.left + s.width, swiperOffset.top],                    [swiperOffset.left, swiperOffset.top + s.height],                    [swiperOffset.left + s.width, swiperOffset.top + s.height]                ];                for (var i = 0; i < swiperCoord.length; i++) {                    var point = swiperCoord[i];                    if (                        point[0] >= windowScroll.left && point[0] <= windowScroll.left + windowWidth &&                        point[1] >= windowScroll.top && point[1] <= windowScroll.top + windowHeight                    ) {                        inView = true;                    }                }                if (!inView) return;            }            if (isH()) {                if (kc === 37 || kc === 39) {                    if (e.preventDefault) e.preventDefault();                    else e.returnValue = false;                }                if ((kc === 39 && !s.rtl) || (kc === 37 && s.rtl)) s.slideNext();                if ((kc === 37 && !s.rtl) || (kc === 39 && s.rtl)) s.slidePrev();            }            else {                if (kc === 38 || kc === 40) {                    if (e.preventDefault) e.preventDefault();                    else e.returnValue = false;                }                if (kc === 40) s.slideNext();                if (kc === 38) s.slidePrev();            }        }        s.disableKeyboardControl = function () {            s.params.keyboardControl = false;            $(document).off('keydown', handleKeyboard);        };        s.enableKeyboardControl = function () {            s.params.keyboardControl = true;            $(document).on('keydown', handleKeyboard);        };        /*=========================          Mousewheel Control          ===========================*/        s.mousewheel = {            event: false,            lastScrollTime: (new window.Date()).getTime()        };        if (s.params.mousewheelControl) {            try {                new window.WheelEvent('wheel');                s.mousewheel.event = 'wheel';            } catch (e) {}            if (!s.mousewheel.event && document.onmousewheel !== undefined) {                s.mousewheel.event = 'mousewheel';            }            if (!s.mousewheel.event) {                s.mousewheel.event = 'DOMMouseScroll';            }        }        function handleMousewheel(e) {            if (e.originalEvent) e = e.originalEvent; //jquery fix            var we = s.mousewheel.event;            var delta = 0;            var rtlFactor = s.rtl ? -1 : 1;            //Opera & IE            if (e.detail) delta = -e.detail;            //WebKits            else if (we === 'mousewheel') {                if (s.params.mousewheelForceToAxis) {                    if (isH()) {                        if (Math.abs(e.wheelDeltaX) > Math.abs(e.wheelDeltaY)) delta = e.wheelDeltaX * rtlFactor;                        else return;                    }                    else {                        if (Math.abs(e.wheelDeltaY) > Math.abs(e.wheelDeltaX)) delta = e.wheelDeltaY;                        else return;                    }                }                else {                    delta = Math.abs(e.wheelDeltaX) > Math.abs(e.wheelDeltaY) ? - e.wheelDeltaX * rtlFactor : - e.wheelDeltaY;                }            }            //Old FireFox            else if (we === 'DOMMouseScroll') delta = -e.detail;            //New FireFox            else if (we === 'wheel') {                if (s.params.mousewheelForceToAxis) {                    if (isH()) {                        if (Math.abs(e.deltaX) > Math.abs(e.deltaY)) delta = -e.deltaX * rtlFactor;                        else return;                    }                    else {                        if (Math.abs(e.deltaY) > Math.abs(e.deltaX)) delta = -e.deltaY;                        else return;                    }                }                else {                    delta = Math.abs(e.deltaX) > Math.abs(e.deltaY) ? - e.deltaX * rtlFactor : - e.deltaY;                }            }            if (delta === 0) return;            if (s.params.mousewheelInvert) delta = -delta;            if (!s.params.freeMode) {                if ((new window.Date()).getTime() - s.mousewheel.lastScrollTime > 60) {                    if (delta < 0) {                        if ((!s.isEnd || s.params.loop) && !s.animating) s.slideNext();                        else if (s.params.mousewheelReleaseOnEdges) return true;                    }                    else {                        if ((!s.isBeginning || s.params.loop) && !s.animating) s.slidePrev();                        else if (s.params.mousewheelReleaseOnEdges) return true;                    }                }                s.mousewheel.lastScrollTime = (new window.Date()).getTime();            }            else {                //Freemode or scrollContainer:                var position = s.getWrapperTranslate() + delta * s.params.mousewheelSensitivity;                var wasBeginning = s.isBeginning,                    wasEnd = s.isEnd;                if (position >= s.minTranslate()) position = s.minTranslate();                if (position <= s.maxTranslate()) position = s.maxTranslate();                s.setWrapperTransition(0);                s.setWrapperTranslate(position);                s.updateProgress();                s.updateActiveIndex();                if (!wasBeginning && s.isBeginning || !wasEnd && s.isEnd) {                    s.updateClasses();                }                if (s.params.freeModeSticky) {                    clearTimeout(s.mousewheel.timeout);                    s.mousewheel.timeout = setTimeout(function () {                        s.slideReset();                    }, 300);                }                // Return page scroll on edge positions                if (position === 0 || position === s.maxTranslate()) return;            }            if (s.params.autoplay) s.stopAutoplay();            if (e.preventDefault) e.preventDefault();            else e.returnValue = false;            return false;        }        s.disableMousewheelControl = function () {            if (!s.mousewheel.event) return false;            s.container.off(s.mousewheel.event, handleMousewheel);            return true;        };        s.enableMousewheelControl = function () {            if (!s.mousewheel.event) return false;            s.container.on(s.mousewheel.event, handleMousewheel);            return true;        };        /*=========================          Parallax          ===========================*/        function setParallaxTransform(el, progress) {            el = $(el);            var p, pX, pY;            var rtlFactor = s.rtl ? -1 : 1;            p = el.attr('data-swiper-parallax') || '0';            pX = el.attr('data-swiper-parallax-x');            pY = el.attr('data-swiper-parallax-y');            if (pX || pY) {                pX = pX || '0';                pY = pY || '0';            }            else {                if (isH()) {                    pX = p;                    pY = '0';                }                else {                    pY = p;                    pX = '0';                }            }            if ((pX).indexOf('%') >= 0) {                pX = parseInt(pX, 10) * progress * rtlFactor + '%';            }            else {                pX = pX * progress * rtlFactor + 'px' ;            }            if ((pY).indexOf('%') >= 0) {                pY = parseInt(pY, 10) * progress + '%';            }            else {                pY = pY * progress + 'px' ;            }            el.transform('translate3d(' + pX + ', ' + pY + ',0px)');        }        s.parallax = {            setTranslate: function () {                s.container.children('[data-swiper-parallax], [data-swiper-parallax-x], [data-swiper-parallax-y]').each(function(){                    setParallaxTransform(this, s.progress);                });                s.slides.each(function () {                    var slide = $(this);                    slide.find('[data-swiper-parallax], [data-swiper-parallax-x], [data-swiper-parallax-y]').each(function () {                        var progress = Math.min(Math.max(slide[0].progress, -1), 1);                        setParallaxTransform(this, progress);                    });                });            },            setTransition: function (duration) {                if (typeof duration === 'undefined') duration = s.params.speed;                s.container.find('[data-swiper-parallax], [data-swiper-parallax-x], [data-swiper-parallax-y]').each(function(){                    var el = $(this);                    var parallaxDuration = parseInt(el.attr('data-swiper-parallax-duration'), 10) || duration;                    if (duration === 0) parallaxDuration = 0;                    el.transition(parallaxDuration);                });            }        };        /*=========================          Plugins API. Collect all and init all plugins          ===========================*/        s._plugins = [];        for (var plugin in s.plugins) {            var p = s.plugins[plugin](s, s.params[plugin]);            if (p) s._plugins.push(p);        }        // Method to call all plugins event/method        s.callPlugins = function (eventName) {            for (var i = 0; i < s._plugins.length; i++) {                if (eventName in s._plugins[i]) {                    s._plugins[i][eventName](arguments[1], arguments[2], arguments[3], arguments[4], arguments[5]);                }            }        };        /*=========================          Events/Callbacks/Plugins Emitter          ===========================*/        function normalizeEventName (eventName) {            if (eventName.indexOf('on') !== 0) {                if (eventName[0] !== eventName[0].toUpperCase()) {                    eventName = 'on' + eventName[0].toUpperCase() + eventName.substring(1);                }                else {                    eventName = 'on' + eventName;                }            }            return eventName;        }        s.emitterEventListeners = {        };        s.emit = function (eventName) {            // Trigger callbacks            if (s.params[eventName]) {                s.params[eventName](arguments[1], arguments[2], arguments[3], arguments[4], arguments[5]);            }            var i;            // Trigger events            if (s.emitterEventListeners[eventName]) {                for (i = 0; i < s.emitterEventListeners[eventName].length; i++) {                    s.emitterEventListeners[eventName][i](arguments[1], arguments[2], arguments[3], arguments[4], arguments[5]);                }            }            // Trigger plugins            if (s.callPlugins) s.callPlugins(eventName, arguments[1], arguments[2], arguments[3], arguments[4], arguments[5]);        };        s.on = function (eventName, handler) {            eventName = normalizeEventName(eventName);            if (!s.emitterEventListeners[eventName]) s.emitterEventListeners[eventName] = [];            s.emitterEventListeners[eventName].push(handler);            return s;        };        s.off = function (eventName, handler) {            var i;            eventName = normalizeEventName(eventName);            if (typeof handler === 'undefined') {                // Remove all handlers for such event                s.emitterEventListeners[eventName] = [];                return s;            }            if (!s.emitterEventListeners[eventName] || s.emitterEventListeners[eventName].length === 0) return;            for (i = 0; i < s.emitterEventListeners[eventName].length; i++) {                if(s.emitterEventListeners[eventName][i] === handler) s.emitterEventListeners[eventName].splice(i, 1);            }            return s;        };        s.once = function (eventName, handler) {            eventName = normalizeEventName(eventName);            var _handler = function () {                handler(arguments[0], arguments[1], arguments[2], arguments[3], arguments[4]);                s.off(eventName, _handler);            };            s.on(eventName, _handler);            return s;        };        // Accessibility tools        s.a11y = {            makeFocusable: function ($el) {                $el.attr('tabIndex', '0');                return $el;            },            addRole: function ($el, role) {                $el.attr('role', role);                return $el;            },            addLabel: function ($el, label) {                $el.attr('aria-label', label);                return $el;            },            disable: function ($el) {                $el.attr('aria-disabled', true);                return $el;            },            enable: function ($el) {                $el.attr('aria-disabled', false);                return $el;            },            onEnterKey: function (event) {                if (event.keyCode !== 13) return;                if ($(event.target).is(s.params.nextButton)) {                    s.onClickNext(event);                    if (s.isEnd) {                        s.a11y.notify(s.params.lastSlideMessage);                    }                    else {                        s.a11y.notify(s.params.nextSlideMessage);                    }                }                else if ($(event.target).is(s.params.prevButton)) {                    s.onClickPrev(event);                    if (s.isBeginning) {                        s.a11y.notify(s.params.firstSlideMessage);                    }                    else {                        s.a11y.notify(s.params.prevSlideMessage);                    }                }                if ($(event.target).is('.' + s.params.bulletClass)) {                    $(event.target)[0].click();                }            },            liveRegion: $('<span class="swiper-notification" aria-live="assertive" aria-atomic="true"></span>'),            notify: function (message) {                var notification = s.a11y.liveRegion;                if (notification.length === 0) return;                notification.html('');                notification.html(message);            },            init: function () {                // Setup accessibility                if (s.params.nextButton) {                    var nextButton = $(s.params.nextButton);                    s.a11y.makeFocusable(nextButton);                    s.a11y.addRole(nextButton, 'button');                    s.a11y.addLabel(nextButton, s.params.nextSlideMessage);                }                if (s.params.prevButton) {                    var prevButton = $(s.params.prevButton);                    s.a11y.makeFocusable(prevButton);                    s.a11y.addRole(prevButton, 'button');                    s.a11y.addLabel(prevButton, s.params.prevSlideMessage);                }                $(s.container).append(s.a11y.liveRegion);            },            initPagination: function () {                if (s.params.pagination && s.params.paginationClickable && s.bullets && s.bullets.length) {                    s.bullets.each(function () {                        var bullet = $(this);                        s.a11y.makeFocusable(bullet);                        s.a11y.addRole(bullet, 'button');                        s.a11y.addLabel(bullet, s.params.paginationBulletMessage.replace(/{{index}}/, bullet.index() + 1));                    });                }            },            destroy: function () {                if (s.a11y.liveRegion && s.a11y.liveRegion.length > 0) s.a11y.liveRegion.remove();            }        };        /*=========================          Init/Destroy          ===========================*/        s.init = function () {            if (s.params.loop) s.createLoop();            s.updateContainerSize();            s.updateSlidesSize();            s.updatePagination();            if (s.params.scrollbar && s.scrollbar) {                s.scrollbar.set();                if (s.params.scrollbarDraggable) {                    s.scrollbar.enableDraggable();                }            }            if (s.params.effect !== 'slide' && s.effects[s.params.effect]) {                if (!s.params.loop) s.updateProgress();                s.effects[s.params.effect].setTranslate();            }            if (s.params.loop) {                s.slideTo(s.params.initialSlide + s.loopedSlides, 0, s.params.runCallbacksOnInit);            }            else {                s.slideTo(s.params.initialSlide, 0, s.params.runCallbacksOnInit);                if (s.params.initialSlide === 0) {                    if (s.parallax && s.params.parallax) s.parallax.setTranslate();                    if (s.lazy && s.params.lazyLoading) {                        s.lazy.load();                        s.lazy.initialImageLoaded = true;                    }                }            }            s.attachEvents();            if (s.params.observer && s.support.observer) {                s.initObservers();            }            if (s.params.preloadImages && !s.params.lazyLoading) {                s.preloadImages();            }            if (s.params.autoplay) {                s.startAutoplay();            }            if (s.params.keyboardControl) {                if (s.enableKeyboardControl) s.enableKeyboardControl();            }            if (s.params.mousewheelControl) {                if (s.enableMousewheelControl) s.enableMousewheelControl();            }            if (s.params.hashnav) {                if (s.hashnav) s.hashnav.init();            }            if (s.params.a11y && s.a11y) s.a11y.init();            s.emit('onInit', s);        };        // Cleanup dynamic styles        s.cleanupStyles = function () {            // Container            s.container.removeClass(s.classNames.join(' ')).removeAttr('style');            // Wrapper            s.wrapper.removeAttr('style');            // Slides            if (s.slides && s.slides.length) {                s.slides                    .removeClass([                      s.params.slideVisibleClass,                      s.params.slideActiveClass,                      s.params.slideNextClass,                      s.params.slidePrevClass                    ].join(' '))                    .removeAttr('style')                    .removeAttr('data-swiper-column')                    .removeAttr('data-swiper-row');            }            // Pagination/Bullets            if (s.paginationContainer && s.paginationContainer.length) {                s.paginationContainer.removeClass(s.params.paginationHiddenClass);            }            if (s.bullets && s.bullets.length) {                s.bullets.removeClass(s.params.bulletActiveClass);            }            // Buttons            if (s.params.prevButton) $(s.params.prevButton).removeClass(s.params.buttonDisabledClass);            if (s.params.nextButton) $(s.params.nextButton).removeClass(s.params.buttonDisabledClass);            // Scrollbar            if (s.params.scrollbar && s.scrollbar) {                if (s.scrollbar.track && s.scrollbar.track.length) s.scrollbar.track.removeAttr('style');                if (s.scrollbar.drag && s.scrollbar.drag.length) s.scrollbar.drag.removeAttr('style');            }        };        // Destroy        s.destroy = function (deleteInstance, cleanupStyles) {            // Detach evebts            s.detachEvents();            // Stop autoplay            s.stopAutoplay();            // Disable draggable            if (s.params.scrollbar && s.scrollbar) {                if (s.params.scrollbarDraggable) {                    s.scrollbar.disableDraggable();                }            }            // Destroy loop            if (s.params.loop) {                s.destroyLoop();            }            // Cleanup styles            if (cleanupStyles) {                s.cleanupStyles();            }            // Disconnect observer            s.disconnectObservers();            // Disable keyboard/mousewheel            if (s.params.keyboardControl) {                if (s.disableKeyboardControl) s.disableKeyboardControl();            }            if (s.params.mousewheelControl) {                if (s.disableMousewheelControl) s.disableMousewheelControl();            }            // Disable a11y            if (s.params.a11y && s.a11y) s.a11y.destroy();            // Destroy callback            s.emit('onDestroy');            // Delete instance            if (deleteInstance !== false) s = null;        };        s.init();        // Return swiper instance        return s;    };    /*==================================================        Prototype    ====================================================*/    Swiper.prototype = {        isSafari: (function () {            var ua = navigator.userAgent.toLowerCase();            return (ua.indexOf('safari') >= 0 && ua.indexOf('chrome') < 0 && ua.indexOf('android') < 0);        })(),        isUiWebView: /(iPhone|iPod|iPad).*AppleWebKit(?!.*Safari)/i.test(navigator.userAgent),        isArray: function (arr) {            return Object.prototype.toString.apply(arr) === '[object Array]';        },        /*==================================================        Browser        ====================================================*/        browser: {            ie: window.navigator.pointerEnabled || window.navigator.msPointerEnabled,            ieTouch: (window.navigator.msPointerEnabled && window.navigator.msMaxTouchPoints > 1) || (window.navigator.pointerEnabled && window.navigator.maxTouchPoints > 1)        },        /*==================================================        Devices        ====================================================*/        device: (function () {            var ua = navigator.userAgent;            var android = ua.match(/(Android);?[\s\/]+([\d.]+)?/);            var ipad = ua.match(/(iPad).*OS\s([\d_]+)/);            var ipod = ua.match(/(iPod)(.*OS\s([\d_]+))?/);            var iphone = !ipad && ua.match(/(iPhone\sOS)\s([\d_]+)/);            return {                ios: ipad || iphone || ipod,                android: android            };        })(),        /*==================================================        Feature Detection        ====================================================*/        support: {            touch : (window.Modernizr && Modernizr.touch === true) || (function () {                return !!(('ontouchstart' in window) || window.DocumentTouch && document instanceof DocumentTouch);            })(),            transforms3d : (window.Modernizr && Modernizr.csstransforms3d === true) || (function () {                var div = document.createElement('div').style;                return ('webkitPerspective' in div || 'MozPerspective' in div || 'OPerspective' in div || 'MsPerspective' in div || 'perspective' in div);            })(),            flexbox: (function () {                var div = document.createElement('div').style;                var styles = ('alignItems webkitAlignItems webkitBoxAlign msFlexAlign mozBoxAlign webkitFlexDirection msFlexDirection mozBoxDirection mozBoxOrient webkitBoxDirection webkitBoxOrient').split(' ');                for (var i = 0; i < styles.length; i++) {                    if (styles[i] in div) return true;                }            })(),            observer: (function () {                return ('MutationObserver' in window || 'WebkitMutationObserver' in window);            })()        },        /*==================================================        Plugins        ====================================================*/        plugins: {}    };    /*===========================    Dom7 Library    ===========================*/    var Dom7 = (function () {        var Dom7 = function (arr) {            var _this = this, i = 0;            // Create array-like object            for (i = 0; i < arr.length; i++) {                _this[i] = arr[i];            }            _this.length = arr.length;            // Return collection with methods            return this;        };        var $ = function (selector, context) {            var arr = [], i = 0;            if (selector && !context) {                if (selector instanceof Dom7) {                    return selector;                }            }            if (selector) {                // String                if (typeof selector === 'string') {                    var els, tempParent, html = selector.trim();                    if (html.indexOf('<') >= 0 && html.indexOf('>') >= 0) {                        var toCreate = 'div';                        if (html.indexOf('<li') === 0) toCreate = 'ul';                        if (html.indexOf('<tr') === 0) toCreate = 'tbody';                        if (html.indexOf('<td') === 0 || html.indexOf('<th') === 0) toCreate = 'tr';                        if (html.indexOf('<tbody') === 0) toCreate = 'table';                        if (html.indexOf('<option') === 0) toCreate = 'select';                        tempParent = document.createElement(toCreate);                        tempParent.innerHTML = selector;                        for (i = 0; i < tempParent.childNodes.length; i++) {                            arr.push(tempParent.childNodes[i]);                        }                    }                    else {                        if (!context && selector[0] === '#' && !selector.match(/[ .<>:~]/)) {                            // Pure ID selector                            els = [document.getElementById(selector.split('#')[1])];                        }                        else {                            // Other selectors                            els = (context || document).querySelectorAll(selector);                        }                        for (i = 0; i < els.length; i++) {                            if (els[i]) arr.push(els[i]);                        }                    }                }                // Node/element                else if (selector.nodeType || selector === window || selector === document) {                    arr.push(selector);                }                //Array of elements or instance of Dom                else if (selector.length > 0 && selector[0].nodeType) {                    for (i = 0; i < selector.length; i++) {                        arr.push(selector[i]);                    }                }            }            return new Dom7(arr);        };        Dom7.prototype = {            // Classes and attriutes            addClass: function (className) {                if (typeof className === 'undefined') {                    return this;                }                var classes = className.split(' ');                for (var i = 0; i < classes.length; i++) {                    for (var j = 0; j < this.length; j++) {                        this[j].classList.add(classes[i]);                    }                }                return this;            },            removeClass: function (className) {                var classes = className.split(' ');                for (var i = 0; i < classes.length; i++) {                    for (var j = 0; j < this.length; j++) {                        this[j].classList.remove(classes[i]);                    }                }                return this;            },            hasClass: function (className) {                if (!this[0]) return false;                else return this[0].classList.contains(className);            },            toggleClass: function (className) {                var classes = className.split(' ');                for (var i = 0; i < classes.length; i++) {                    for (var j = 0; j < this.length; j++) {                        this[j].classList.toggle(classes[i]);                    }                }                return this;            },            attr: function (attrs, value) {                if (arguments.length === 1 && typeof attrs === 'string') {                    // Get attr                    if (this[0]) return this[0].getAttribute(attrs);                    else return undefined;                }                else {                    // Set attrs                    for (var i = 0; i < this.length; i++) {                        if (arguments.length === 2) {                            // String                            this[i].setAttribute(attrs, value);                        }                        else {                            // Object                            for (var attrName in attrs) {                                this[i][attrName] = attrs[attrName];                                this[i].setAttribute(attrName, attrs[attrName]);                            }                        }                    }                    return this;                }            },            removeAttr: function (attr) {                for (var i = 0; i < this.length; i++) {                    this[i].removeAttribute(attr);                }                return this;            },            data: function (key, value) {                if (typeof value === 'undefined') {                    // Get value                    if (this[0]) {                        var dataKey = this[0].getAttribute('data-' + key);                        if (dataKey) return dataKey;                        else if (this[0].dom7ElementDataStorage && (key in this[0].dom7ElementDataStorage)) return this[0].dom7ElementDataStorage[key];                        else return undefined;                    }                    else return undefined;                }                else {                    // Set value                    for (var i = 0; i < this.length; i++) {                        var el = this[i];                        if (!el.dom7ElementDataStorage) el.dom7ElementDataStorage = {};                        el.dom7ElementDataStorage[key] = value;                    }                    return this;                }            },            // Transforms            transform : function (transform) {                for (var i = 0; i < this.length; i++) {                    var elStyle = this[i].style;                    elStyle.webkitTransform = elStyle.MsTransform = elStyle.msTransform = elStyle.MozTransform = elStyle.OTransform = elStyle.transform = transform;                }                return this;            },            transition: function (duration) {                if (typeof duration !== 'string') {                    duration = duration + 'ms';                }                for (var i = 0; i < this.length; i++) {                    var elStyle = this[i].style;                    elStyle.webkitTransitionDuration = elStyle.MsTransitionDuration = elStyle.msTransitionDuration = elStyle.MozTransitionDuration = elStyle.OTransitionDuration = elStyle.transitionDuration = duration;                }                return this;            },            //Events            on: function (eventName, targetSelector, listener, capture) {                function handleLiveEvent(e) {                    var target = e.target;                    if ($(target).is(targetSelector)) listener.call(target, e);                    else {                        var parents = $(target).parents();                        for (var k = 0; k < parents.length; k++) {                            if ($(parents[k]).is(targetSelector)) listener.call(parents[k], e);                        }                    }                }                var events = eventName.split(' ');                var i, j;                for (i = 0; i < this.length; i++) {                    if (typeof targetSelector === 'function' || targetSelector === false) {                        // Usual events                        if (typeof targetSelector === 'function') {                            listener = arguments[1];                            capture = arguments[2] || false;                        }                        for (j = 0; j < events.length; j++) {                            this[i].addEventListener(events[j], listener, capture);                        }                    }                    else {                        //Live events                        for (j = 0; j < events.length; j++) {                            if (!this[i].dom7LiveListeners) this[i].dom7LiveListeners = [];                            this[i].dom7LiveListeners.push({listener: listener, liveListener: handleLiveEvent});                            this[i].addEventListener(events[j], handleLiveEvent, capture);                        }                    }                }                return this;            },            off: function (eventName, targetSelector, listener, capture) {                var events = eventName.split(' ');                for (var i = 0; i < events.length; i++) {                    for (var j = 0; j < this.length; j++) {                        if (typeof targetSelector === 'function' || targetSelector === false) {                            // Usual events                            if (typeof targetSelector === 'function') {                                listener = arguments[1];                                capture = arguments[2] || false;                            }                            this[j].removeEventListener(events[i], listener, capture);                        }                        else {                            // Live event                            if (this[j].dom7LiveListeners) {                                for (var k = 0; k < this[j].dom7LiveListeners.length; k++) {                                    if (this[j].dom7LiveListeners[k].listener === listener) {                                        this[j].removeEventListener(events[i], this[j].dom7LiveListeners[k].liveListener, capture);                                    }                                }                            }                        }                    }                }                return this;            },            once: function (eventName, targetSelector, listener, capture) {                var dom = this;                if (typeof targetSelector === 'function') {                    targetSelector = false;                    listener = arguments[1];                    capture = arguments[2];                }                function proxy(e) {                    listener(e);                    dom.off(eventName, targetSelector, proxy, capture);                }                dom.on(eventName, targetSelector, proxy, capture);            },            trigger: function (eventName, eventData) {                for (var i = 0; i < this.length; i++) {                    var evt;                    try {                        evt = new window.CustomEvent(eventName, {detail: eventData, bubbles: true, cancelable: true});                    }                    catch (e) {                        evt = document.createEvent('Event');                        evt.initEvent(eventName, true, true);                        evt.detail = eventData;                    }                    this[i].dispatchEvent(evt);                }                return this;            },            transitionEnd: function (callback) {                var events = ['webkitTransitionEnd', 'transitionend', 'oTransitionEnd', 'MSTransitionEnd', 'msTransitionEnd'],                    i, j, dom = this;                function fireCallBack(e) {                    /*jshint validthis:true */                    if (e.target !== this) return;                    callback.call(this, e);                    for (i = 0; i < events.length; i++) {                        dom.off(events[i], fireCallBack);                    }                }                if (callback) {                    for (i = 0; i < events.length; i++) {                        dom.on(events[i], fireCallBack);                    }                }                return this;            },            // Sizing/Styles            width: function () {                if (this[0] === window) {                    return window.innerWidth;                }                else {                    if (this.length > 0) {                        return parseFloat(this.css('width'));                    }                    else {                        return null;                    }                }            },            outerWidth: function (includeMargins) {                if (this.length > 0) {                    if (includeMargins)                        return this[0].offsetWidth + parseFloat(this.css('margin-right')) + parseFloat(this.css('margin-left'));                    else                        return this[0].offsetWidth;                }                else return null;            },            height: function () {                if (this[0] === window) {                    return window.innerHeight;                }                else {                    if (this.length > 0) {                        return parseFloat(this.css('height'));                    }                    else {                        return null;                    }                }            },            outerHeight: function (includeMargins) {                if (this.length > 0) {                    if (includeMargins)                        return this[0].offsetHeight + parseFloat(this.css('margin-top')) + parseFloat(this.css('margin-bottom'));                    else                        return this[0].offsetHeight;                }                else return null;            },            offset: function () {                if (this.length > 0) {                    var el = this[0];                    var box = el.getBoundingClientRect();                    var body = document.body;                    var clientTop  = el.clientTop  || body.clientTop  || 0;                    var clientLeft = el.clientLeft || body.clientLeft || 0;                    var scrollTop  = window.pageYOffset || el.scrollTop;                    var scrollLeft = window.pageXOffset || el.scrollLeft;                    return {                        top: box.top  + scrollTop  - clientTop,                        left: box.left + scrollLeft - clientLeft                    };                }                else {                    return null;                }            },            css: function (props, value) {                var i;                if (arguments.length === 1) {                    if (typeof props === 'string') {                        if (this[0]) return window.getComputedStyle(this[0], null).getPropertyValue(props);                    }                    else {                        for (i = 0; i < this.length; i++) {                            for (var prop in props) {                                this[i].style[prop] = props[prop];                            }                        }                        return this;                    }                }                if (arguments.length === 2 && typeof props === 'string') {                    for (i = 0; i < this.length; i++) {                        this[i].style[props] = value;                    }                    return this;                }                return this;            },            //Dom manipulation            each: function (callback) {                for (var i = 0; i < this.length; i++) {                    callback.call(this[i], i, this[i]);                }                return this;            },            html: function (html) {                if (typeof html === 'undefined') {                    return this[0] ? this[0].innerHTML : undefined;                }                else {                    for (var i = 0; i < this.length; i++) {                        this[i].innerHTML = html;                    }                    return this;                }            },            is: function (selector) {                if (!this[0]) return false;                var compareWith, i;                if (typeof selector === 'string') {                    var el = this[0];                    if (el === document) return selector === document;                    if (el === window) return selector === window;                    if (el.matches) return el.matches(selector);                    else if (el.webkitMatchesSelector) return el.webkitMatchesSelector(selector);                    else if (el.mozMatchesSelector) return el.mozMatchesSelector(selector);                    else if (el.msMatchesSelector) return el.msMatchesSelector(selector);                    else {                        compareWith = $(selector);                        for (i = 0; i < compareWith.length; i++) {                            if (compareWith[i] === this[0]) return true;                        }                        return false;                    }                }                else if (selector === document) return this[0] === document;                else if (selector === window) return this[0] === window;                else {                    if (selector.nodeType || selector instanceof Dom7) {                        compareWith = selector.nodeType ? [selector] : selector;                        for (i = 0; i < compareWith.length; i++) {                            if (compareWith[i] === this[0]) return true;                        }                        return false;                    }                    return false;                }            },            index: function () {                if (this[0]) {                    var child = this[0];                    var i = 0;                    while ((child = child.previousSibling) !== null) {                        if (child.nodeType === 1) i++;                    }                    return i;                }                else return undefined;            },            eq: function (index) {                if (typeof index === 'undefined') return this;                var length = this.length;                var returnIndex;                if (index > length - 1) {                    return new Dom7([]);                }                if (index < 0) {                    returnIndex = length + index;                    if (returnIndex < 0) return new Dom7([]);                    else return new Dom7([this[returnIndex]]);                }                return new Dom7([this[index]]);            },            append: function (newChild) {                var i, j;                for (i = 0; i < this.length; i++) {                    if (typeof newChild === 'string') {                        var tempDiv = document.createElement('div');                        tempDiv.innerHTML = newChild;                        while (tempDiv.firstChild) {                            this[i].appendChild(tempDiv.firstChild);                        }                    }                    else if (newChild instanceof Dom7) {                        for (j = 0; j < newChild.length; j++) {                            this[i].appendChild(newChild[j]);                        }                    }                    else {                        this[i].appendChild(newChild);                    }                }                return this;            },            prepend: function (newChild) {                var i, j;                for (i = 0; i < this.length; i++) {                    if (typeof newChild === 'string') {                        var tempDiv = document.createElement('div');                        tempDiv.innerHTML = newChild;                        for (j = tempDiv.childNodes.length - 1; j >= 0; j--) {                            this[i].insertBefore(tempDiv.childNodes[j], this[i].childNodes[0]);                        }                        // this[i].insertAdjacentHTML('afterbegin', newChild);                    }                    else if (newChild instanceof Dom7) {                        for (j = 0; j < newChild.length; j++) {                            this[i].insertBefore(newChild[j], this[i].childNodes[0]);                        }                    }                    else {                        this[i].insertBefore(newChild, this[i].childNodes[0]);                    }                }                return this;            },            insertBefore: function (selector) {                var before = $(selector);                for (var i = 0; i < this.length; i++) {                    if (before.length === 1) {                        before[0].parentNode.insertBefore(this[i], before[0]);                    }                    else if (before.length > 1) {                        for (var j = 0; j < before.length; j++) {                            before[j].parentNode.insertBefore(this[i].cloneNode(true), before[j]);                        }                    }                }            },            insertAfter: function (selector) {                var after = $(selector);                for (var i = 0; i < this.length; i++) {                    if (after.length === 1) {                        after[0].parentNode.insertBefore(this[i], after[0].nextSibling);                    }                    else if (after.length > 1) {                        for (var j = 0; j < after.length; j++) {                            after[j].parentNode.insertBefore(this[i].cloneNode(true), after[j].nextSibling);                        }                    }                }            },            next: function (selector) {                if (this.length > 0) {                    if (selector) {                        if (this[0].nextElementSibling && $(this[0].nextElementSibling).is(selector)) return new Dom7([this[0].nextElementSibling]);                        else return new Dom7([]);                    }                    else {                        if (this[0].nextElementSibling) return new Dom7([this[0].nextElementSibling]);                        else return new Dom7([]);                    }                }                else return new Dom7([]);            },            nextAll: function (selector) {                var nextEls = [];                var el = this[0];                if (!el) return new Dom7([]);                while (el.nextElementSibling) {                    var next = el.nextElementSibling;                    if (selector) {                        if($(next).is(selector)) nextEls.push(next);                    }                    else nextEls.push(next);                    el = next;                }                return new Dom7(nextEls);            },            prev: function (selector) {                if (this.length > 0) {                    if (selector) {                        if (this[0].previousElementSibling && $(this[0].previousElementSibling).is(selector)) return new Dom7([this[0].previousElementSibling]);                        else return new Dom7([]);                    }                    else {                        if (this[0].previousElementSibling) return new Dom7([this[0].previousElementSibling]);                        else return new Dom7([]);                    }                }                else return new Dom7([]);            },            prevAll: function (selector) {                var prevEls = [];                var el = this[0];                if (!el) return new Dom7([]);                while (el.previousElementSibling) {                    var prev = el.previousElementSibling;                    if (selector) {                        if($(prev).is(selector)) prevEls.push(prev);                    }                    else prevEls.push(prev);                    el = prev;                }                return new Dom7(prevEls);            },            parent: function (selector) {                var parents = [];                for (var i = 0; i < this.length; i++) {                    if (selector) {                        if ($(this[i].parentNode).is(selector)) parents.push(this[i].parentNode);                    }                    else {                        parents.push(this[i].parentNode);                    }                }                return $($.unique(parents));            },            parents: function (selector) {                var parents = [];                for (var i = 0; i < this.length; i++) {                    var parent = this[i].parentNode;                    while (parent) {                        if (selector) {                            if ($(parent).is(selector)) parents.push(parent);                        }                        else {                            parents.push(parent);                        }                        parent = parent.parentNode;                    }                }                return $($.unique(parents));            },            find : function (selector) {                var foundElements = [];                for (var i = 0; i < this.length; i++) {                    var found = this[i].querySelectorAll(selector);                    for (var j = 0; j < found.length; j++) {                        foundElements.push(found[j]);                    }                }                return new Dom7(foundElements);            },            children: function (selector) {                var children = [];                for (var i = 0; i < this.length; i++) {                    var childNodes = this[i].childNodes;                    for (var j = 0; j < childNodes.length; j++) {                        if (!selector) {                            if (childNodes[j].nodeType === 1) children.push(childNodes[j]);                        }                        else {                            if (childNodes[j].nodeType === 1 && $(childNodes[j]).is(selector)) children.push(childNodes[j]);                        }                    }                }                return new Dom7($.unique(children));            },            remove: function () {                for (var i = 0; i < this.length; i++) {                    if (this[i].parentNode) this[i].parentNode.removeChild(this[i]);                }                return this;            },            add: function () {                var dom = this;                var i, j;                for (i = 0; i < arguments.length; i++) {                    var toAdd = $(arguments[i]);                    for (j = 0; j < toAdd.length; j++) {                        dom[dom.length] = toAdd[j];                        dom.length++;                    }                }                return dom;            }        };        $.fn = Dom7.prototype;        $.unique = function (arr) {            var unique = [];            for (var i = 0; i < arr.length; i++) {                if (unique.indexOf(arr[i]) === -1) unique.push(arr[i]);            }            return unique;        };        return $;    })();    /*===========================     Get Dom libraries     ===========================*/    var swiperDomPlugins = ['jQuery', 'Zepto', 'Dom7'];    for (var i = 0; i < swiperDomPlugins.length; i++) {    	if (window[swiperDomPlugins[i]]) {    		addLibraryPlugin(window[swiperDomPlugins[i]]);    	}    }    // Required DOM Plugins    var domLib;    if (typeof Dom7 === 'undefined') {    	domLib = window.Dom7 || window.Zepto || window.jQuery;    }    else {    	domLib = Dom7;    }    /*===========================    Add .swiper plugin from Dom libraries    ===========================*/    function addLibraryPlugin(lib) {        lib.fn.swiper = function (params) {            var firstInstance;            lib(this).each(function () {                var s = new Swiper(this, params);                if (!firstInstance) firstInstance = s;            });            return firstInstance;        };    }    if (domLib) {        if (!('transitionEnd' in domLib.fn)) {            domLib.fn.transitionEnd = function (callback) {                var events = ['webkitTransitionEnd', 'transitionend', 'oTransitionEnd', 'MSTransitionEnd', 'msTransitionEnd'],                    i, j, dom = this;                function fireCallBack(e) {                    /*jshint validthis:true */                    if (e.target !== this) return;                    callback.call(this, e);                    for (i = 0; i < events.length; i++) {                        dom.off(events[i], fireCallBack);                    }                }                if (callback) {                    for (i = 0; i < events.length; i++) {                        dom.on(events[i], fireCallBack);                    }                }                return this;            };        }        if (!('transform' in domLib.fn)) {            domLib.fn.transform = function (transform) {                for (var i = 0; i < this.length; i++) {                    var elStyle = this[i].style;                    elStyle.webkitTransform = elStyle.MsTransform = elStyle.msTransform = elStyle.MozTransform = elStyle.OTransform = elStyle.transform = transform;                }                return this;            };        }        if (!('transition' in domLib.fn)) {            domLib.fn.transition = function (duration) {                if (typeof duration !== 'string') {                    duration = duration + 'ms';                }                for (var i = 0; i < this.length; i++) {                    var elStyle = this[i].style;                    elStyle.webkitTransitionDuration = elStyle.MsTransitionDuration = elStyle.msTransitionDuration = elStyle.MozTransitionDuration = elStyle.OTransitionDuration = elStyle.transitionDuration = duration;                }                return this;            };        }    }    ionic.views.Swiper = Swiper;})();(function(ionic) {'use strict';  ionic.views.Toggle = ionic.views.View.inherit({    initialize: function(opts) {      var self = this;      this.el = opts.el;      this.checkbox = opts.checkbox;      this.track = opts.track;      this.handle = opts.handle;      this.openPercent = -1;      this.onChange = opts.onChange || function() {};      this.triggerThreshold = opts.triggerThreshold || 20;      this.dragStartHandler = function(e) {        self.dragStart(e);      };      this.dragHandler = function(e) {        self.drag(e);      };      this.holdHandler = function(e) {        self.hold(e);      };      this.releaseHandler = function(e) {        self.release(e);      };      this.dragStartGesture = ionic.onGesture('dragstart', this.dragStartHandler, this.el);      this.dragGesture = ionic.onGesture('drag', this.dragHandler, this.el);      this.dragHoldGesture = ionic.onGesture('hold', this.holdHandler, this.el);      this.dragReleaseGesture = ionic.onGesture('release', this.releaseHandler, this.el);    },    destroy: function() {      ionic.offGesture(this.dragStartGesture, 'dragstart', this.dragStartGesture);      ionic.offGesture(this.dragGesture, 'drag', this.dragGesture);      ionic.offGesture(this.dragHoldGesture, 'hold', this.holdHandler);      ionic.offGesture(this.dragReleaseGesture, 'release', this.releaseHandler);    },    tap: function() {      if(this.el.getAttribute('disabled') !== 'disabled') {        this.val( !this.checkbox.checked );      }    },    dragStart: function(e) {      if(this.checkbox.disabled) return;      this._dragInfo = {        width: this.el.offsetWidth,        left: this.el.offsetLeft,        right: this.el.offsetLeft + this.el.offsetWidth,        triggerX: this.el.offsetWidth / 2,        initialState: this.checkbox.checked      };      // Stop any parent dragging      e.gesture.srcEvent.preventDefault();      // Trigger hold styles      this.hold(e);    },    drag: function(e) {      var self = this;      if(!this._dragInfo) { return; }      // Stop any parent dragging      e.gesture.srcEvent.preventDefault();      ionic.requestAnimationFrame(function () {        if (!self._dragInfo) { return; }        var px = e.gesture.touches[0].pageX - self._dragInfo.left;        var mx = self._dragInfo.width - self.triggerThreshold;        // The initial state was on, so "tend towards" on        if(self._dragInfo.initialState) {          if(px < self.triggerThreshold) {            self.setOpenPercent(0);          } else if(px > self._dragInfo.triggerX) {            self.setOpenPercent(100);          }        } else {          // The initial state was off, so "tend towards" off          if(px < self._dragInfo.triggerX) {            self.setOpenPercent(0);          } else if(px > mx) {            self.setOpenPercent(100);          }        }      });    },    endDrag: function() {      this._dragInfo = null;    },    hold: function() {      this.el.classList.add('dragging');    },    release: function(e) {      this.el.classList.remove('dragging');      this.endDrag(e);    },    setOpenPercent: function(openPercent) {      // only make a change if the new open percent has changed      if(this.openPercent < 0 || (openPercent < (this.openPercent - 3) || openPercent > (this.openPercent + 3) ) ) {        this.openPercent = openPercent;        if(openPercent === 0) {          this.val(false);        } else if(openPercent === 100) {          this.val(true);        } else {          var openPixel = Math.round( (openPercent / 100) * this.track.offsetWidth - (this.handle.offsetWidth) );          openPixel = (openPixel < 1 ? 0 : openPixel);          this.handle.style[ionic.CSS.TRANSFORM] = 'translate3d(' + openPixel + 'px,0,0)';        }      }    },    val: function(value) {      if(value === true || value === false) {        if(this.handle.style[ionic.CSS.TRANSFORM] !== "") {          this.handle.style[ionic.CSS.TRANSFORM] = "";        }        this.checkbox.checked = value;        this.openPercent = (value ? 100 : 0);        this.onChange && this.onChange();      }      return this.checkbox.checked;    }  });})(ionic);})();
 |