1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301330233033304330533063307330833093310331133123313331433153316331733183319332033213322332333243325332633273328332933303331333233333334333533363337333833393340334133423343334433453346334733483349335033513352335333543355335633573358335933603361336233633364336533663367336833693370337133723373337433753376337733783379338033813382338333843385338633873388338933903391339233933394339533963397339833993400340134023403340434053406340734083409341034113412341334143415341634173418341934203421342234233424342534263427342834293430343134323433343434353436343734383439344034413442344334443445344634473448344934503451345234533454345534563457345834593460346134623463346434653466346734683469347034713472347334743475347634773478347934803481348234833484348534863487348834893490349134923493349434953496349734983499350035013502350335043505350635073508350935103511351235133514351535163517351835193520352135223523352435253526352735283529353035313532353335343535353635373538353935403541354235433544354535463547354835493550355135523553355435553556355735583559356035613562356335643565356635673568356935703571357235733574357535763577357835793580358135823583358435853586358735883589359035913592359335943595359635973598359936003601360236033604360536063607360836093610361136123613361436153616361736183619362036213622362336243625362636273628362936303631363236333634363536363637363836393640364136423643364436453646364736483649365036513652365336543655365636573658365936603661366236633664366536663667366836693670367136723673367436753676367736783679368036813682368336843685368636873688368936903691369236933694369536963697369836993700370137023703370437053706370737083709371037113712371337143715371637173718371937203721372237233724372537263727372837293730373137323733373437353736373737383739374037413742374337443745374637473748374937503751375237533754375537563757375837593760376137623763376437653766376737683769377037713772377337743775377637773778377937803781378237833784378537863787378837893790379137923793379437953796379737983799380038013802380338043805380638073808380938103811381238133814381538163817381838193820382138223823382438253826382738283829383038313832383338343835383638373838383938403841384238433844384538463847384838493850385138523853385438553856385738583859386038613862386338643865386638673868386938703871387238733874387538763877387838793880388138823883388438853886388738883889389038913892389338943895389638973898389939003901390239033904390539063907390839093910391139123913391439153916391739183919392039213922392339243925392639273928392939303931393239333934393539363937393839393940394139423943394439453946394739483949395039513952395339543955395639573958395939603961396239633964396539663967396839693970397139723973397439753976397739783979398039813982398339843985398639873988398939903991399239933994399539963997399839994000400140024003400440054006400740084009401040114012401340144015401640174018401940204021402240234024402540264027402840294030403140324033403440354036403740384039404040414042404340444045404640474048404940504051405240534054405540564057405840594060406140624063406440654066406740684069407040714072407340744075407640774078407940804081408240834084408540864087408840894090409140924093409440954096409740984099410041014102410341044105410641074108410941104111411241134114411541164117411841194120412141224123412441254126412741284129413041314132413341344135413641374138413941404141414241434144414541464147414841494150415141524153415441554156415741584159416041614162416341644165416641674168416941704171417241734174417541764177417841794180418141824183418441854186418741884189419041914192419341944195419641974198419942004201420242034204420542064207420842094210421142124213421442154216421742184219422042214222422342244225422642274228422942304231423242334234423542364237423842394240424142424243424442454246424742484249425042514252425342544255425642574258425942604261426242634264426542664267426842694270427142724273427442754276427742784279428042814282428342844285428642874288428942904291429242934294429542964297429842994300430143024303430443054306430743084309431043114312431343144315431643174318431943204321432243234324432543264327432843294330433143324333433443354336433743384339434043414342434343444345434643474348434943504351435243534354435543564357435843594360436143624363436443654366436743684369437043714372437343744375437643774378437943804381438243834384438543864387438843894390439143924393439443954396439743984399440044014402440344044405440644074408440944104411441244134414441544164417441844194420442144224423442444254426442744284429443044314432443344344435443644374438443944404441444244434444444544464447444844494450445144524453445444554456445744584459446044614462446344644465446644674468446944704471447244734474447544764477447844794480448144824483448444854486448744884489449044914492449344944495449644974498449945004501450245034504450545064507450845094510451145124513451445154516451745184519452045214522452345244525452645274528452945304531453245334534453545364537453845394540454145424543454445454546454745484549455045514552455345544555455645574558455945604561456245634564456545664567456845694570457145724573457445754576457745784579458045814582458345844585458645874588458945904591459245934594459545964597459845994600460146024603460446054606460746084609461046114612461346144615461646174618461946204621462246234624462546264627462846294630463146324633463446354636463746384639464046414642464346444645464646474648464946504651465246534654465546564657465846594660466146624663466446654666466746684669467046714672467346744675467646774678467946804681468246834684468546864687468846894690469146924693469446954696469746984699470047014702470347044705470647074708470947104711471247134714471547164717471847194720472147224723472447254726472747284729473047314732473347344735473647374738473947404741474247434744474547464747474847494750475147524753475447554756475747584759476047614762476347644765476647674768476947704771477247734774477547764777477847794780478147824783478447854786478747884789479047914792479347944795479647974798479948004801480248034804480548064807480848094810481148124813481448154816481748184819482048214822482348244825482648274828482948304831483248334834483548364837483848394840484148424843484448454846484748484849485048514852485348544855485648574858485948604861486248634864486548664867486848694870487148724873487448754876487748784879488048814882488348844885488648874888488948904891489248934894489548964897489848994900490149024903490449054906490749084909491049114912491349144915491649174918491949204921492249234924492549264927492849294930493149324933493449354936493749384939494049414942494349444945494649474948494949504951495249534954495549564957495849594960496149624963496449654966496749684969497049714972497349744975497649774978497949804981498249834984498549864987498849894990499149924993499449954996499749984999500050015002500350045005500650075008500950105011501250135014501550165017501850195020502150225023502450255026502750285029503050315032503350345035503650375038503950405041504250435044504550465047504850495050505150525053505450555056505750585059506050615062506350645065506650675068506950705071507250735074507550765077507850795080508150825083508450855086508750885089509050915092509350945095509650975098509951005101510251035104510551065107510851095110511151125113511451155116511751185119512051215122512351245125512651275128512951305131513251335134513551365137513851395140514151425143514451455146514751485149515051515152515351545155515651575158515951605161516251635164516551665167516851695170517151725173517451755176517751785179518051815182518351845185518651875188518951905191519251935194519551965197519851995200520152025203520452055206520752085209521052115212521352145215521652175218521952205221522252235224522552265227522852295230523152325233523452355236523752385239524052415242524352445245524652475248524952505251525252535254525552565257525852595260526152625263526452655266526752685269527052715272527352745275527652775278527952805281528252835284528552865287528852895290529152925293529452955296529752985299530053015302530353045305530653075308530953105311531253135314531553165317531853195320532153225323532453255326532753285329533053315332533353345335533653375338533953405341534253435344534553465347534853495350535153525353535453555356535753585359536053615362536353645365536653675368536953705371537253735374537553765377537853795380538153825383538453855386538753885389539053915392539353945395539653975398539954005401540254035404540554065407540854095410541154125413541454155416541754185419542054215422542354245425542654275428542954305431543254335434543554365437543854395440544154425443544454455446544754485449545054515452545354545455545654575458545954605461546254635464546554665467546854695470547154725473547454755476547754785479548054815482548354845485548654875488548954905491549254935494549554965497549854995500550155025503550455055506550755085509551055115512551355145515551655175518551955205521552255235524552555265527552855295530553155325533553455355536553755385539554055415542554355445545554655475548554955505551555255535554555555565557555855595560556155625563556455655566556755685569557055715572557355745575557655775578557955805581558255835584558555865587558855895590559155925593559455955596559755985599560056015602560356045605560656075608560956105611561256135614561556165617561856195620562156225623562456255626562756285629563056315632563356345635563656375638563956405641564256435644564556465647564856495650565156525653565456555656565756585659566056615662566356645665566656675668566956705671567256735674567556765677567856795680568156825683568456855686568756885689569056915692569356945695569656975698569957005701570257035704570557065707570857095710571157125713571457155716571757185719572057215722572357245725572657275728572957305731573257335734573557365737573857395740574157425743574457455746574757485749575057515752575357545755575657575758575957605761576257635764576557665767576857695770577157725773577457755776577757785779578057815782578357845785578657875788578957905791579257935794579557965797579857995800580158025803580458055806580758085809581058115812581358145815581658175818581958205821582258235824582558265827582858295830583158325833583458355836583758385839584058415842584358445845584658475848584958505851585258535854585558565857585858595860586158625863586458655866586758685869587058715872587358745875587658775878587958805881588258835884588558865887588858895890589158925893589458955896589758985899590059015902590359045905590659075908590959105911591259135914591559165917591859195920592159225923592459255926592759285929593059315932593359345935593659375938593959405941594259435944594559465947594859495950595159525953595459555956595759585959596059615962596359645965596659675968596959705971597259735974597559765977597859795980598159825983598459855986598759885989599059915992599359945995599659975998599960006001600260036004600560066007600860096010601160126013601460156016601760186019602060216022602360246025602660276028602960306031603260336034603560366037603860396040604160426043604460456046604760486049605060516052605360546055605660576058605960606061606260636064606560666067606860696070607160726073607460756076607760786079608060816082608360846085608660876088608960906091609260936094609560966097609860996100610161026103610461056106610761086109611061116112611361146115611661176118611961206121612261236124612561266127612861296130613161326133613461356136613761386139614061416142614361446145614661476148614961506151615261536154615561566157615861596160616161626163616461656166616761686169617061716172617361746175617661776178617961806181618261836184618561866187618861896190619161926193619461956196619761986199620062016202620362046205620662076208620962106211621262136214621562166217621862196220622162226223622462256226622762286229623062316232623362346235623662376238623962406241624262436244624562466247624862496250625162526253625462556256625762586259626062616262626362646265626662676268626962706271627262736274627562766277627862796280628162826283628462856286628762886289629062916292629362946295629662976298629963006301630263036304630563066307630863096310631163126313631463156316631763186319632063216322632363246325632663276328632963306331633263336334633563366337633863396340634163426343634463456346634763486349635063516352635363546355635663576358635963606361636263636364636563666367636863696370637163726373637463756376637763786379638063816382638363846385638663876388638963906391639263936394639563966397639863996400640164026403640464056406640764086409641064116412641364146415641664176418641964206421642264236424642564266427642864296430643164326433643464356436643764386439644064416442644364446445644664476448644964506451645264536454645564566457645864596460646164626463646464656466646764686469647064716472647364746475647664776478647964806481648264836484648564866487648864896490649164926493649464956496649764986499650065016502650365046505650665076508650965106511651265136514651565166517651865196520652165226523652465256526652765286529653065316532653365346535653665376538653965406541654265436544654565466547654865496550655165526553655465556556655765586559656065616562656365646565656665676568656965706571657265736574657565766577657865796580658165826583658465856586658765886589659065916592659365946595659665976598659966006601660266036604660566066607660866096610661166126613661466156616661766186619662066216622662366246625662666276628662966306631663266336634663566366637663866396640664166426643664466456646664766486649665066516652665366546655665666576658665966606661666266636664666566666667666866696670667166726673667466756676667766786679668066816682668366846685668666876688668966906691669266936694669566966697669866996700670167026703670467056706670767086709671067116712671367146715671667176718671967206721672267236724672567266727672867296730673167326733673467356736673767386739674067416742674367446745674667476748674967506751675267536754675567566757675867596760676167626763676467656766676767686769677067716772677367746775677667776778677967806781678267836784678567866787678867896790679167926793679467956796679767986799680068016802680368046805680668076808680968106811681268136814681568166817681868196820682168226823682468256826682768286829683068316832683368346835683668376838683968406841684268436844684568466847684868496850685168526853685468556856685768586859686068616862686368646865686668676868686968706871687268736874687568766877687868796880688168826883688468856886688768886889689068916892689368946895689668976898689969006901690269036904690569066907690869096910691169126913691469156916691769186919692069216922692369246925692669276928692969306931693269336934693569366937693869396940694169426943694469456946694769486949695069516952695369546955695669576958695969606961696269636964696569666967696869696970697169726973697469756976697769786979698069816982698369846985698669876988698969906991699269936994699569966997699869997000700170027003700470057006700770087009701070117012701370147015701670177018701970207021702270237024702570267027702870297030703170327033703470357036703770387039704070417042704370447045704670477048704970507051705270537054705570567057705870597060706170627063706470657066706770687069707070717072707370747075707670777078707970807081708270837084708570867087708870897090709170927093709470957096709770987099710071017102710371047105710671077108710971107111711271137114711571167117711871197120712171227123712471257126712771287129713071317132713371347135713671377138713971407141714271437144714571467147714871497150715171527153715471557156715771587159716071617162716371647165716671677168716971707171717271737174717571767177717871797180718171827183718471857186718771887189719071917192719371947195719671977198719972007201720272037204720572067207720872097210721172127213721472157216721772187219722072217222722372247225722672277228722972307231723272337234723572367237723872397240724172427243724472457246724772487249725072517252725372547255725672577258725972607261726272637264726572667267726872697270727172727273727472757276727772787279728072817282728372847285728672877288728972907291729272937294729572967297729872997300730173027303730473057306730773087309731073117312731373147315731673177318731973207321732273237324732573267327732873297330733173327333733473357336733773387339734073417342734373447345734673477348734973507351735273537354735573567357735873597360736173627363736473657366736773687369737073717372737373747375737673777378737973807381738273837384738573867387738873897390739173927393739473957396739773987399740074017402740374047405740674077408740974107411741274137414741574167417741874197420742174227423742474257426742774287429743074317432743374347435743674377438743974407441744274437444744574467447744874497450745174527453745474557456745774587459746074617462746374647465746674677468746974707471747274737474747574767477747874797480748174827483748474857486748774887489749074917492749374947495749674977498749975007501750275037504750575067507750875097510751175127513751475157516751775187519752075217522752375247525752675277528752975307531753275337534753575367537753875397540754175427543754475457546754775487549755075517552755375547555755675577558755975607561756275637564756575667567756875697570757175727573757475757576757775787579758075817582758375847585758675877588758975907591759275937594759575967597759875997600760176027603760476057606760776087609761076117612761376147615761676177618761976207621762276237624762576267627762876297630763176327633763476357636763776387639764076417642764376447645764676477648764976507651765276537654765576567657765876597660766176627663766476657666766776687669767076717672767376747675767676777678767976807681768276837684768576867687768876897690769176927693769476957696769776987699770077017702770377047705770677077708770977107711771277137714771577167717771877197720772177227723772477257726772777287729773077317732773377347735773677377738773977407741774277437744774577467747774877497750775177527753775477557756775777587759776077617762776377647765776677677768776977707771777277737774777577767777777877797780778177827783778477857786778777887789779077917792779377947795779677977798779978007801780278037804780578067807780878097810781178127813781478157816781778187819782078217822782378247825782678277828782978307831783278337834783578367837783878397840784178427843784478457846784778487849785078517852785378547855785678577858785978607861786278637864786578667867786878697870787178727873787478757876787778787879788078817882788378847885788678877888788978907891789278937894789578967897789878997900790179027903790479057906790779087909791079117912791379147915791679177918791979207921792279237924792579267927792879297930793179327933793479357936793779387939794079417942794379447945794679477948794979507951795279537954795579567957795879597960796179627963796479657966796779687969797079717972797379747975797679777978797979807981798279837984798579867987798879897990799179927993799479957996799779987999800080018002800380048005800680078008800980108011801280138014801580168017801880198020802180228023802480258026802780288029803080318032803380348035803680378038803980408041804280438044804580468047804880498050805180528053805480558056805780588059806080618062806380648065806680678068806980708071807280738074807580768077807880798080808180828083808480858086808780888089809080918092809380948095809680978098809981008101810281038104810581068107810881098110811181128113811481158116811781188119812081218122812381248125812681278128812981308131813281338134813581368137813881398140814181428143814481458146814781488149815081518152815381548155815681578158815981608161816281638164816581668167816881698170817181728173817481758176817781788179818081818182818381848185818681878188818981908191819281938194819581968197819881998200820182028203820482058206820782088209821082118212821382148215821682178218821982208221822282238224822582268227822882298230823182328233823482358236823782388239824082418242824382448245824682478248824982508251825282538254825582568257825882598260826182628263826482658266826782688269827082718272827382748275827682778278827982808281828282838284828582868287828882898290829182928293829482958296829782988299830083018302830383048305830683078308830983108311831283138314831583168317831883198320832183228323832483258326832783288329833083318332833383348335833683378338833983408341834283438344834583468347834883498350835183528353835483558356835783588359836083618362836383648365836683678368836983708371837283738374837583768377837883798380838183828383838483858386838783888389839083918392839383948395839683978398839984008401840284038404840584068407840884098410841184128413841484158416841784188419842084218422842384248425842684278428842984308431843284338434843584368437843884398440844184428443844484458446844784488449845084518452845384548455845684578458845984608461846284638464846584668467846884698470847184728473847484758476847784788479848084818482848384848485848684878488848984908491849284938494849584968497849884998500850185028503850485058506850785088509851085118512851385148515851685178518851985208521852285238524852585268527852885298530853185328533853485358536853785388539854085418542854385448545854685478548854985508551855285538554855585568557855885598560856185628563856485658566856785688569857085718572857385748575857685778578857985808581858285838584858585868587858885898590859185928593859485958596859785988599860086018602860386048605860686078608860986108611861286138614861586168617861886198620862186228623862486258626862786288629863086318632863386348635863686378638863986408641864286438644864586468647864886498650865186528653865486558656865786588659866086618662866386648665866686678668866986708671867286738674867586768677867886798680868186828683868486858686868786888689869086918692869386948695869686978698869987008701870287038704870587068707870887098710871187128713871487158716871787188719872087218722872387248725872687278728872987308731873287338734873587368737873887398740874187428743874487458746874787488749875087518752875387548755875687578758875987608761876287638764876587668767876887698770877187728773877487758776877787788779878087818782878387848785878687878788878987908791879287938794879587968797879887998800880188028803880488058806880788088809881088118812881388148815881688178818881988208821882288238824882588268827882888298830883188328833883488358836883788388839884088418842884388448845884688478848884988508851885288538854885588568857885888598860886188628863886488658866886788688869887088718872887388748875887688778878887988808881888288838884888588868887888888898890889188928893889488958896889788988899890089018902890389048905890689078908890989108911891289138914891589168917891889198920892189228923892489258926892789288929893089318932893389348935893689378938893989408941894289438944894589468947894889498950895189528953895489558956895789588959896089618962896389648965896689678968896989708971897289738974897589768977897889798980898189828983898489858986898789888989899089918992899389948995899689978998899990009001900290039004900590069007900890099010901190129013901490159016901790189019902090219022902390249025902690279028902990309031903290339034903590369037903890399040904190429043904490459046904790489049905090519052905390549055905690579058905990609061906290639064906590669067906890699070907190729073907490759076907790789079908090819082908390849085908690879088908990909091909290939094909590969097909890999100910191029103910491059106910791089109911091119112911391149115911691179118911991209121912291239124912591269127912891299130913191329133913491359136913791389139914091419142914391449145914691479148914991509151915291539154915591569157915891599160916191629163916491659166916791689169917091719172917391749175917691779178917991809181918291839184918591869187918891899190919191929193919491959196919791989199920092019202920392049205920692079208920992109211921292139214921592169217921892199220922192229223922492259226922792289229923092319232923392349235923692379238923992409241924292439244924592469247924892499250925192529253925492559256925792589259926092619262926392649265926692679268926992709271927292739274927592769277927892799280928192829283928492859286928792889289929092919292929392949295929692979298929993009301930293039304930593069307930893099310931193129313931493159316931793189319932093219322932393249325932693279328932993309331933293339334933593369337933893399340934193429343934493459346934793489349935093519352935393549355935693579358935993609361936293639364936593669367936893699370937193729373937493759376937793789379938093819382938393849385938693879388938993909391939293939394939593969397939893999400940194029403940494059406940794089409941094119412941394149415941694179418941994209421942294239424942594269427942894299430943194329433943494359436943794389439944094419442944394449445944694479448944994509451945294539454945594569457945894599460946194629463946494659466946794689469947094719472947394749475947694779478947994809481948294839484948594869487948894899490949194929493949494959496949794989499950095019502950395049505950695079508950995109511951295139514951595169517951895199520952195229523952495259526952795289529953095319532953395349535953695379538953995409541954295439544954595469547954895499550955195529553955495559556955795589559956095619562956395649565956695679568956995709571957295739574957595769577957895799580958195829583958495859586958795889589959095919592959395949595959695979598959996009601960296039604960596069607960896099610961196129613961496159616961796189619962096219622962396249625962696279628962996309631963296339634963596369637963896399640964196429643964496459646964796489649965096519652965396549655965696579658965996609661966296639664966596669667966896699670967196729673967496759676967796789679968096819682968396849685968696879688968996909691969296939694969596969697969896999700970197029703970497059706970797089709971097119712971397149715971697179718971997209721972297239724972597269727972897299730973197329733973497359736973797389739974097419742974397449745974697479748974997509751975297539754975597569757975897599760976197629763976497659766976797689769977097719772977397749775977697779778977997809781978297839784978597869787978897899790979197929793979497959796979797989799980098019802980398049805980698079808980998109811981298139814981598169817981898199820982198229823982498259826982798289829983098319832983398349835983698379838983998409841984298439844984598469847984898499850985198529853985498559856985798589859986098619862986398649865986698679868986998709871987298739874987598769877987898799880988198829883988498859886988798889889989098919892989398949895989698979898989999009901990299039904990599069907990899099910991199129913991499159916991799189919992099219922992399249925992699279928992999309931993299339934993599369937993899399940994199429943994499459946994799489949995099519952995399549955995699579958995999609961996299639964996599669967996899699970997199729973997499759976997799789979998099819982998399849985998699879988998999909991999299939994999599969997999899991000010001100021000310004100051000610007100081000910010100111001210013100141001510016100171001810019100201002110022100231002410025100261002710028100291003010031100321003310034100351003610037100381003910040100411004210043100441004510046100471004810049100501005110052100531005410055100561005710058100591006010061100621006310064100651006610067100681006910070100711007210073100741007510076100771007810079100801008110082100831008410085100861008710088100891009010091100921009310094100951009610097100981009910100101011010210103101041010510106101071010810109101101011110112101131011410115101161011710118101191012010121101221012310124101251012610127101281012910130101311013210133101341013510136101371013810139101401014110142101431014410145101461014710148101491015010151101521015310154101551015610157101581015910160101611016210163101641016510166101671016810169101701017110172101731017410175101761017710178101791018010181101821018310184101851018610187101881018910190101911019210193101941019510196101971019810199102001020110202102031020410205102061020710208102091021010211102121021310214102151021610217102181021910220102211022210223102241022510226102271022810229102301023110232102331023410235102361023710238102391024010241102421024310244102451024610247102481024910250102511025210253102541025510256102571025810259102601026110262102631026410265102661026710268102691027010271102721027310274102751027610277102781027910280102811028210283102841028510286102871028810289102901029110292102931029410295102961029710298102991030010301103021030310304103051030610307103081030910310103111031210313103141031510316103171031810319103201032110322103231032410325103261032710328103291033010331103321033310334103351033610337103381033910340103411034210343103441034510346103471034810349103501035110352103531035410355103561035710358103591036010361103621036310364103651036610367103681036910370103711037210373103741037510376103771037810379103801038110382103831038410385103861038710388103891039010391103921039310394103951039610397103981039910400104011040210403104041040510406104071040810409104101041110412104131041410415104161041710418104191042010421104221042310424104251042610427104281042910430104311043210433104341043510436104371043810439104401044110442104431044410445104461044710448104491045010451104521045310454104551045610457104581045910460104611046210463104641046510466104671046810469104701047110472104731047410475104761047710478104791048010481104821048310484104851048610487104881048910490104911049210493104941049510496104971049810499105001050110502105031050410505105061050710508105091051010511105121051310514105151051610517105181051910520105211052210523105241052510526105271052810529105301053110532105331053410535105361053710538105391054010541105421054310544105451054610547105481054910550105511055210553105541055510556105571055810559105601056110562105631056410565105661056710568105691057010571105721057310574105751057610577105781057910580105811058210583105841058510586105871058810589105901059110592105931059410595105961059710598105991060010601106021060310604106051060610607106081060910610106111061210613106141061510616106171061810619106201062110622106231062410625106261062710628106291063010631106321063310634106351063610637106381063910640106411064210643106441064510646106471064810649106501065110652106531065410655106561065710658106591066010661106621066310664106651066610667106681066910670106711067210673106741067510676106771067810679106801068110682106831068410685106861068710688106891069010691106921069310694106951069610697106981069910700107011070210703107041070510706107071070810709107101071110712107131071410715107161071710718107191072010721107221072310724107251072610727107281072910730107311073210733107341073510736107371073810739107401074110742107431074410745107461074710748107491075010751107521075310754107551075610757107581075910760107611076210763107641076510766107671076810769107701077110772107731077410775107761077710778107791078010781107821078310784107851078610787107881078910790107911079210793107941079510796107971079810799108001080110802108031080410805108061080710808108091081010811108121081310814108151081610817108181081910820108211082210823108241082510826108271082810829108301083110832108331083410835108361083710838108391084010841108421084310844108451084610847108481084910850108511085210853108541085510856108571085810859108601086110862108631086410865108661086710868108691087010871108721087310874108751087610877108781087910880108811088210883108841088510886108871088810889108901089110892108931089410895108961089710898108991090010901109021090310904109051090610907109081090910910109111091210913109141091510916109171091810919109201092110922109231092410925109261092710928109291093010931109321093310934109351093610937109381093910940109411094210943109441094510946109471094810949109501095110952109531095410955109561095710958109591096010961109621096310964109651096610967109681096910970109711097210973109741097510976109771097810979109801098110982109831098410985109861098710988109891099010991109921099310994109951099610997109981099911000110011100211003110041100511006110071100811009110101101111012110131101411015110161101711018110191102011021110221102311024110251102611027110281102911030110311103211033110341103511036110371103811039110401104111042110431104411045110461104711048110491105011051110521105311054110551105611057110581105911060110611106211063110641106511066110671106811069110701107111072110731107411075110761107711078110791108011081110821108311084110851108611087110881108911090110911109211093110941109511096110971109811099111001110111102111031110411105111061110711108111091111011111111121111311114111151111611117111181111911120111211112211123111241112511126111271112811129111301113111132111331113411135111361113711138111391114011141111421114311144111451114611147111481114911150111511115211153111541115511156111571115811159111601116111162111631116411165111661116711168111691117011171111721117311174111751117611177111781117911180111811118211183111841118511186111871118811189111901119111192111931119411195111961119711198111991120011201112021120311204112051120611207112081120911210112111121211213112141121511216112171121811219112201122111222112231122411225112261122711228112291123011231112321123311234112351123611237112381123911240112411124211243112441124511246112471124811249112501125111252112531125411255112561125711258112591126011261112621126311264112651126611267112681126911270112711127211273112741127511276112771127811279112801128111282112831128411285112861128711288112891129011291112921129311294112951129611297112981129911300113011130211303113041130511306113071130811309113101131111312113131131411315113161131711318113191132011321113221132311324113251132611327113281132911330113311133211333113341133511336113371133811339113401134111342113431134411345113461134711348113491135011351113521135311354113551135611357113581135911360113611136211363113641136511366113671136811369113701137111372113731137411375113761137711378113791138011381113821138311384113851138611387113881138911390113911139211393113941139511396113971139811399114001140111402114031140411405114061140711408114091141011411114121141311414114151141611417114181141911420114211142211423114241142511426114271142811429114301143111432114331143411435114361143711438114391144011441114421144311444114451144611447114481144911450114511145211453114541145511456114571145811459114601146111462114631146411465114661146711468114691147011471114721147311474114751147611477114781147911480114811148211483114841148511486114871148811489114901149111492114931149411495114961149711498114991150011501115021150311504115051150611507115081150911510115111151211513115141151511516115171151811519115201152111522115231152411525115261152711528115291153011531115321153311534115351153611537115381153911540115411154211543115441154511546115471154811549115501155111552115531155411555115561155711558115591156011561115621156311564115651156611567115681156911570115711157211573115741157511576115771157811579115801158111582115831158411585115861158711588115891159011591115921159311594115951159611597115981159911600116011160211603116041160511606116071160811609116101161111612116131161411615116161161711618116191162011621116221162311624116251162611627116281162911630116311163211633116341163511636116371163811639116401164111642116431164411645116461164711648116491165011651116521165311654116551165611657116581165911660116611166211663116641166511666116671166811669116701167111672116731167411675116761167711678116791168011681116821168311684116851168611687116881168911690116911169211693116941169511696116971169811699117001170111702117031170411705117061170711708117091171011711117121171311714117151171611717117181171911720117211172211723117241172511726117271172811729117301173111732117331173411735117361173711738117391174011741117421174311744117451174611747117481174911750117511175211753117541175511756117571175811759117601176111762117631176411765117661176711768117691177011771117721177311774117751177611777117781177911780117811178211783117841178511786117871178811789117901179111792117931179411795117961179711798117991180011801118021180311804118051180611807118081180911810118111181211813118141181511816118171181811819118201182111822118231182411825118261182711828118291183011831118321183311834118351183611837118381183911840118411184211843118441184511846118471184811849118501185111852118531185411855118561185711858118591186011861118621186311864118651186611867118681186911870118711187211873118741187511876118771187811879118801188111882118831188411885118861188711888118891189011891118921189311894118951189611897118981189911900119011190211903119041190511906119071190811909119101191111912119131191411915119161191711918119191192011921119221192311924119251192611927119281192911930119311193211933119341193511936119371193811939119401194111942119431194411945119461194711948119491195011951119521195311954119551195611957119581195911960119611196211963119641196511966119671196811969119701197111972119731197411975119761197711978119791198011981119821198311984119851198611987119881198911990119911199211993119941199511996119971199811999120001200112002120031200412005120061200712008120091201012011120121201312014120151201612017120181201912020120211202212023120241202512026120271202812029120301203112032120331203412035120361203712038120391204012041120421204312044120451204612047120481204912050120511205212053120541205512056120571205812059120601206112062120631206412065120661206712068120691207012071120721207312074120751207612077120781207912080120811208212083120841208512086120871208812089120901209112092120931209412095120961209712098120991210012101121021210312104121051210612107121081210912110121111211212113121141211512116121171211812119121201212112122121231212412125121261212712128121291213012131121321213312134121351213612137121381213912140121411214212143121441214512146121471214812149121501215112152121531215412155121561215712158121591216012161121621216312164121651216612167121681216912170121711217212173121741217512176121771217812179121801218112182121831218412185121861218712188121891219012191121921219312194121951219612197121981219912200122011220212203122041220512206122071220812209122101221112212122131221412215122161221712218122191222012221122221222312224122251222612227122281222912230122311223212233122341223512236122371223812239122401224112242122431224412245122461224712248122491225012251122521225312254122551225612257122581225912260122611226212263122641226512266122671226812269122701227112272122731227412275122761227712278122791228012281122821228312284122851228612287122881228912290122911229212293122941229512296122971229812299123001230112302123031230412305123061230712308123091231012311123121231312314123151231612317123181231912320123211232212323123241232512326123271232812329123301233112332123331233412335123361233712338123391234012341123421234312344123451234612347123481234912350123511235212353123541235512356123571235812359123601236112362123631236412365123661236712368123691237012371123721237312374123751237612377123781237912380123811238212383123841238512386123871238812389123901239112392123931239412395123961239712398123991240012401124021240312404124051240612407124081240912410124111241212413124141241512416124171241812419124201242112422124231242412425124261242712428124291243012431124321243312434124351243612437124381243912440124411244212443124441244512446124471244812449124501245112452124531245412455124561245712458124591246012461124621246312464124651246612467124681246912470124711247212473124741247512476124771247812479124801248112482124831248412485124861248712488124891249012491124921249312494124951249612497124981249912500125011250212503125041250512506125071250812509125101251112512125131251412515125161251712518125191252012521125221252312524125251252612527125281252912530125311253212533125341253512536125371253812539125401254112542125431254412545125461254712548125491255012551125521255312554125551255612557125581255912560125611256212563125641256512566125671256812569125701257112572125731257412575125761257712578125791258012581125821258312584125851258612587125881258912590125911259212593125941259512596125971259812599126001260112602126031260412605126061260712608126091261012611126121261312614126151261612617126181261912620126211262212623126241262512626126271262812629126301263112632126331263412635126361263712638126391264012641126421264312644126451264612647126481264912650126511265212653126541265512656126571265812659126601266112662126631266412665126661266712668126691267012671126721267312674126751267612677126781267912680126811268212683126841268512686126871268812689126901269112692126931269412695126961269712698126991270012701127021270312704127051270612707127081270912710127111271212713127141271512716127171271812719127201272112722127231272412725127261272712728127291273012731127321273312734127351273612737127381273912740127411274212743127441274512746127471274812749127501275112752127531275412755127561275712758127591276012761127621276312764127651276612767127681276912770127711277212773127741277512776127771277812779127801278112782127831278412785127861278712788127891279012791127921279312794127951279612797127981279912800128011280212803128041280512806128071280812809128101281112812128131281412815128161281712818128191282012821128221282312824128251282612827128281282912830128311283212833128341283512836128371283812839128401284112842128431284412845128461284712848128491285012851128521285312854128551285612857128581285912860128611286212863128641286512866128671286812869128701287112872128731287412875128761287712878128791288012881128821288312884128851288612887128881288912890128911289212893128941289512896128971289812899129001290112902129031290412905129061290712908129091291012911129121291312914129151291612917129181291912920129211292212923129241292512926129271292812929129301293112932129331293412935129361293712938129391294012941129421294312944129451294612947129481294912950129511295212953129541295512956129571295812959129601296112962129631296412965129661296712968129691297012971129721297312974129751297612977129781297912980129811298212983129841298512986129871298812989129901299112992129931299412995129961299712998129991300013001130021300313004130051300613007130081300913010130111301213013130141301513016130171301813019130201302113022130231302413025130261302713028130291303013031130321303313034130351303613037130381303913040130411304213043130441304513046130471304813049130501305113052130531305413055130561305713058130591306013061130621306313064130651306613067130681306913070130711307213073130741307513076130771307813079130801308113082130831308413085130861308713088130891309013091130921309313094130951309613097130981309913100131011310213103131041310513106131071310813109131101311113112131131311413115131161311713118131191312013121131221312313124131251312613127131281312913130131311313213133131341313513136131371313813139131401314113142131431314413145131461314713148131491315013151131521315313154131551315613157131581315913160131611316213163131641316513166131671316813169131701317113172131731317413175131761317713178131791318013181131821318313184131851318613187131881318913190131911319213193131941319513196131971319813199132001320113202132031320413205132061320713208132091321013211132121321313214132151321613217132181321913220132211322213223132241322513226132271322813229132301323113232132331323413235132361323713238132391324013241132421324313244132451324613247132481324913250132511325213253132541325513256132571325813259132601326113262132631326413265132661326713268132691327013271132721327313274132751327613277132781327913280132811328213283132841328513286132871328813289132901329113292132931329413295132961329713298132991330013301133021330313304133051330613307133081330913310133111331213313133141331513316133171331813319133201332113322133231332413325133261332713328133291333013331133321333313334133351333613337133381333913340133411334213343133441334513346133471334813349133501335113352133531335413355133561335713358133591336013361133621336313364133651336613367133681336913370133711337213373133741337513376133771337813379133801338113382133831338413385133861338713388133891339013391133921339313394133951339613397133981339913400134011340213403134041340513406134071340813409134101341113412134131341413415134161341713418134191342013421134221342313424134251342613427134281342913430134311343213433134341343513436134371343813439134401344113442134431344413445134461344713448134491345013451134521345313454134551345613457134581345913460134611346213463134641346513466134671346813469134701347113472134731347413475134761347713478134791348013481134821348313484134851348613487134881348913490134911349213493134941349513496134971349813499135001350113502135031350413505135061350713508135091351013511135121351313514135151351613517135181351913520135211352213523135241352513526135271352813529135301353113532135331353413535135361353713538135391354013541135421354313544135451354613547135481354913550135511355213553135541355513556135571355813559135601356113562135631356413565135661356713568135691357013571135721357313574135751357613577135781357913580135811358213583135841358513586135871358813589135901359113592135931359413595135961359713598135991360013601136021360313604136051360613607136081360913610136111361213613136141361513616136171361813619136201362113622136231362413625136261362713628136291363013631136321363313634136351363613637136381363913640136411364213643136441364513646136471364813649136501365113652136531365413655136561365713658136591366013661136621366313664136651366613667136681366913670136711367213673136741367513676136771367813679136801368113682136831368413685136861368713688136891369013691136921369313694136951369613697136981369913700137011370213703137041370513706137071370813709137101371113712137131371413715137161371713718137191372013721137221372313724137251372613727137281372913730137311373213733137341373513736137371373813739137401374113742137431374413745137461374713748137491375013751137521375313754137551375613757137581375913760137611376213763137641376513766137671376813769137701377113772137731377413775137761377713778137791378013781137821378313784137851378613787137881378913790137911379213793137941379513796137971379813799138001380113802138031380413805138061380713808138091381013811138121381313814138151381613817138181381913820138211382213823138241382513826138271382813829138301383113832138331383413835138361383713838138391384013841138421384313844138451384613847138481384913850138511385213853138541385513856138571385813859138601386113862138631386413865138661386713868138691387013871138721387313874138751387613877138781387913880138811388213883138841388513886138871388813889138901389113892138931389413895138961389713898138991390013901139021390313904139051390613907139081390913910139111391213913139141391513916139171391813919139201392113922139231392413925139261392713928139291393013931139321393313934139351393613937139381393913940139411394213943139441394513946139471394813949139501395113952139531395413955139561395713958139591396013961139621396313964139651396613967139681396913970139711397213973139741397513976139771397813979139801398113982139831398413985139861398713988139891399013991139921399313994139951399613997139981399914000140011400214003140041400514006140071400814009140101401114012140131401414015140161401714018140191402014021140221402314024140251402614027140281402914030140311403214033140341403514036140371403814039140401404114042140431404414045140461404714048140491405014051140521405314054140551405614057140581405914060140611406214063140641406514066140671406814069140701407114072140731407414075140761407714078140791408014081140821408314084140851408614087140881408914090140911409214093140941409514096140971409814099141001410114102141031410414105141061410714108141091411014111141121411314114141151411614117141181411914120141211412214123141241412514126141271412814129141301413114132141331413414135141361413714138141391414014141141421414314144141451414614147141481414914150141511415214153141541415514156141571415814159141601416114162141631416414165141661416714168141691417014171141721417314174141751417614177141781417914180141811418214183141841418514186141871418814189141901419114192141931419414195141961419714198141991420014201142021420314204142051420614207142081420914210142111421214213142141421514216142171421814219142201422114222142231422414225142261422714228142291423014231142321423314234142351423614237142381423914240142411424214243142441424514246142471424814249142501425114252142531425414255142561425714258142591426014261142621426314264142651426614267142681426914270142711427214273142741427514276142771427814279142801428114282142831428414285142861428714288142891429014291142921429314294142951429614297142981429914300143011430214303143041430514306143071430814309143101431114312143131431414315143161431714318143191432014321143221432314324143251432614327143281432914330143311433214333143341433514336143371433814339143401434114342143431434414345143461434714348143491435014351143521435314354143551435614357143581435914360143611436214363143641436514366143671436814369143701437114372143731437414375143761437714378143791438014381143821438314384143851438614387143881438914390143911439214393143941439514396143971439814399144001440114402144031440414405144061440714408144091441014411144121441314414144151441614417144181441914420144211442214423144241442514426144271442814429144301443114432144331443414435144361443714438144391444014441144421444314444144451444614447144481444914450144511445214453144541445514456144571445814459144601446114462144631446414465144661446714468144691447014471144721447314474144751447614477144781447914480144811448214483144841448514486144871448814489144901449114492144931449414495144961449714498144991450014501145021450314504145051450614507145081450914510145111451214513145141451514516145171451814519145201452114522145231452414525145261452714528145291453014531145321453314534145351453614537145381453914540145411454214543145441454514546145471454814549145501455114552145531455414555145561455714558145591456014561145621456314564145651456614567145681456914570145711457214573145741457514576145771457814579145801458114582145831458414585145861458714588145891459014591145921459314594145951459614597145981459914600146011460214603146041460514606146071460814609146101461114612146131461414615146161461714618146191462014621146221462314624146251462614627146281462914630146311463214633146341463514636146371463814639146401464114642146431464414645146461464714648146491465014651146521465314654146551465614657146581465914660146611466214663146641466514666146671466814669146701467114672146731467414675146761467714678146791468014681146821468314684146851468614687146881468914690146911469214693146941469514696146971469814699147001470114702147031470414705147061470714708147091471014711147121471314714147151471614717147181471914720147211472214723147241472514726147271472814729147301473114732147331473414735147361473714738147391474014741147421474314744147451474614747147481474914750147511475214753147541475514756147571475814759147601476114762147631476414765147661476714768147691477014771147721477314774147751477614777147781477914780147811478214783147841478514786147871478814789147901479114792147931479414795147961479714798147991480014801148021480314804148051480614807148081480914810148111481214813148141481514816148171481814819148201482114822148231482414825148261482714828148291483014831148321483314834148351483614837148381483914840148411484214843148441484514846148471484814849148501485114852148531485414855148561485714858148591486014861148621486314864148651486614867148681486914870148711487214873148741487514876148771487814879148801488114882148831488414885148861488714888148891489014891148921489314894148951489614897148981489914900149011490214903149041490514906149071490814909149101491114912149131491414915149161491714918149191492014921149221492314924149251492614927149281492914930149311493214933149341493514936149371493814939149401494114942149431494414945149461494714948149491495014951149521495314954149551495614957149581495914960149611496214963149641496514966149671496814969149701497114972149731497414975149761497714978149791498014981149821498314984149851498614987149881498914990149911499214993149941499514996149971499814999150001500115002150031500415005150061500715008150091501015011150121501315014150151501615017150181501915020150211502215023150241502515026150271502815029150301503115032150331503415035150361503715038150391504015041150421504315044150451504615047150481504915050150511505215053150541505515056150571505815059150601506115062150631506415065150661506715068150691507015071150721507315074150751507615077150781507915080150811508215083150841508515086150871508815089150901509115092150931509415095150961509715098150991510015101151021510315104151051510615107151081510915110151111511215113151141511515116151171511815119151201512115122151231512415125151261512715128151291513015131151321513315134151351513615137151381513915140151411514215143151441514515146151471514815149151501515115152151531515415155151561515715158151591516015161151621516315164151651516615167151681516915170151711517215173151741517515176151771517815179151801518115182151831518415185151861518715188151891519015191151921519315194151951519615197151981519915200152011520215203152041520515206152071520815209152101521115212152131521415215152161521715218152191522015221152221522315224152251522615227152281522915230152311523215233152341523515236152371523815239152401524115242152431524415245152461524715248152491525015251152521525315254152551525615257152581525915260152611526215263152641526515266152671526815269152701527115272152731527415275152761527715278152791528015281152821528315284152851528615287152881528915290152911529215293152941529515296152971529815299153001530115302153031530415305153061530715308153091531015311153121531315314153151531615317153181531915320153211532215323153241532515326153271532815329153301533115332153331533415335153361533715338153391534015341153421534315344153451534615347153481534915350153511535215353153541535515356153571535815359153601536115362153631536415365153661536715368153691537015371153721537315374153751537615377153781537915380153811538215383153841538515386153871538815389153901539115392153931539415395153961539715398153991540015401154021540315404154051540615407154081540915410154111541215413154141541515416154171541815419154201542115422154231542415425154261542715428154291543015431154321543315434154351543615437154381543915440154411544215443154441544515446154471544815449154501545115452154531545415455154561545715458154591546015461154621546315464154651546615467154681546915470154711547215473154741547515476154771547815479154801548115482154831548415485154861548715488154891549015491154921549315494154951549615497154981549915500155011550215503155041550515506155071550815509155101551115512155131551415515155161551715518155191552015521155221552315524155251552615527155281552915530155311553215533155341553515536155371553815539155401554115542155431554415545155461554715548155491555015551155521555315554155551555615557155581555915560155611556215563155641556515566155671556815569155701557115572155731557415575155761557715578155791558015581155821558315584155851558615587155881558915590155911559215593155941559515596155971559815599156001560115602156031560415605156061560715608156091561015611156121561315614156151561615617156181561915620156211562215623156241562515626156271562815629156301563115632156331563415635156361563715638156391564015641156421564315644156451564615647156481564915650156511565215653156541565515656156571565815659156601566115662156631566415665156661566715668156691567015671156721567315674156751567615677156781567915680156811568215683156841568515686156871568815689156901569115692156931569415695156961569715698156991570015701157021570315704157051570615707157081570915710157111571215713157141571515716157171571815719157201572115722157231572415725157261572715728157291573015731157321573315734157351573615737157381573915740157411574215743157441574515746157471574815749157501575115752157531575415755157561575715758157591576015761157621576315764157651576615767157681576915770157711577215773157741577515776157771577815779157801578115782157831578415785157861578715788157891579015791157921579315794157951579615797157981579915800158011580215803158041580515806158071580815809158101581115812158131581415815158161581715818158191582015821158221582315824158251582615827158281582915830158311583215833158341583515836158371583815839158401584115842158431584415845158461584715848158491585015851158521585315854158551585615857158581585915860158611586215863158641586515866158671586815869158701587115872158731587415875158761587715878158791588015881158821588315884158851588615887158881588915890158911589215893158941589515896158971589815899159001590115902159031590415905159061590715908159091591015911159121591315914159151591615917159181591915920159211592215923159241592515926159271592815929159301593115932159331593415935159361593715938159391594015941159421594315944159451594615947159481594915950159511595215953159541595515956159571595815959159601596115962159631596415965159661596715968159691597015971159721597315974159751597615977159781597915980159811598215983159841598515986159871598815989159901599115992159931599415995159961599715998159991600016001160021600316004160051600616007160081600916010160111601216013160141601516016160171601816019160201602116022160231602416025160261602716028160291603016031160321603316034160351603616037160381603916040160411604216043160441604516046160471604816049160501605116052160531605416055160561605716058160591606016061160621606316064160651606616067160681606916070160711607216073160741607516076160771607816079160801608116082160831608416085160861608716088160891609016091160921609316094160951609616097160981609916100161011610216103161041610516106161071610816109161101611116112161131611416115161161611716118161191612016121161221612316124161251612616127161281612916130161311613216133161341613516136161371613816139161401614116142161431614416145161461614716148161491615016151161521615316154161551615616157161581615916160161611616216163161641616516166161671616816169161701617116172161731617416175161761617716178161791618016181161821618316184161851618616187161881618916190161911619216193161941619516196161971619816199162001620116202162031620416205162061620716208162091621016211162121621316214162151621616217162181621916220162211622216223162241622516226162271622816229162301623116232162331623416235162361623716238162391624016241162421624316244162451624616247162481624916250162511625216253162541625516256162571625816259162601626116262162631626416265162661626716268162691627016271162721627316274162751627616277162781627916280162811628216283162841628516286162871628816289162901629116292162931629416295162961629716298162991630016301163021630316304163051630616307163081630916310163111631216313163141631516316163171631816319163201632116322163231632416325163261632716328163291633016331163321633316334163351633616337163381633916340163411634216343163441634516346163471634816349163501635116352163531635416355163561635716358163591636016361163621636316364163651636616367163681636916370163711637216373163741637516376163771637816379163801638116382163831638416385163861638716388163891639016391163921639316394163951639616397163981639916400164011640216403164041640516406164071640816409164101641116412164131641416415164161641716418164191642016421164221642316424164251642616427164281642916430164311643216433164341643516436164371643816439164401644116442164431644416445164461644716448164491645016451164521645316454164551645616457164581645916460164611646216463164641646516466164671646816469164701647116472164731647416475164761647716478164791648016481164821648316484164851648616487164881648916490164911649216493164941649516496164971649816499165001650116502165031650416505165061650716508165091651016511165121651316514165151651616517165181651916520165211652216523165241652516526165271652816529165301653116532165331653416535165361653716538165391654016541165421654316544165451654616547165481654916550165511655216553165541655516556165571655816559165601656116562165631656416565165661656716568165691657016571165721657316574165751657616577165781657916580165811658216583165841658516586165871658816589165901659116592165931659416595165961659716598165991660016601166021660316604166051660616607166081660916610166111661216613166141661516616166171661816619166201662116622166231662416625166261662716628166291663016631166321663316634166351663616637166381663916640166411664216643166441664516646166471664816649166501665116652166531665416655166561665716658166591666016661166621666316664166651666616667166681666916670166711667216673166741667516676166771667816679166801668116682166831668416685166861668716688166891669016691166921669316694166951669616697166981669916700167011670216703167041670516706167071670816709167101671116712167131671416715167161671716718167191672016721167221672316724167251672616727167281672916730167311673216733167341673516736167371673816739167401674116742167431674416745167461674716748167491675016751167521675316754167551675616757167581675916760167611676216763167641676516766167671676816769167701677116772167731677416775167761677716778167791678016781167821678316784167851678616787167881678916790167911679216793167941679516796167971679816799168001680116802168031680416805168061680716808168091681016811168121681316814168151681616817168181681916820168211682216823168241682516826168271682816829168301683116832168331683416835168361683716838168391684016841168421684316844168451684616847168481684916850168511685216853168541685516856168571685816859168601686116862168631686416865168661686716868168691687016871168721687316874168751687616877168781687916880168811688216883168841688516886168871688816889168901689116892168931689416895168961689716898168991690016901169021690316904169051690616907169081690916910169111691216913169141691516916169171691816919169201692116922169231692416925169261692716928169291693016931169321693316934169351693616937169381693916940169411694216943169441694516946169471694816949169501695116952169531695416955169561695716958169591696016961169621696316964169651696616967169681696916970169711697216973169741697516976169771697816979169801698116982169831698416985169861698716988169891699016991169921699316994169951699616997169981699917000170011700217003170041700517006170071700817009170101701117012170131701417015170161701717018170191702017021170221702317024170251702617027170281702917030170311703217033170341703517036170371703817039170401704117042170431704417045170461704717048170491705017051170521705317054170551705617057170581705917060170611706217063170641706517066170671706817069170701707117072170731707417075170761707717078170791708017081170821708317084170851708617087170881708917090170911709217093170941709517096170971709817099171001710117102171031710417105171061710717108171091711017111171121711317114171151711617117171181711917120171211712217123171241712517126171271712817129171301713117132171331713417135171361713717138171391714017141171421714317144171451714617147171481714917150171511715217153171541715517156171571715817159171601716117162171631716417165171661716717168171691717017171171721717317174171751717617177171781717917180171811718217183171841718517186171871718817189171901719117192171931719417195171961719717198171991720017201172021720317204172051720617207172081720917210172111721217213172141721517216172171721817219172201722117222172231722417225172261722717228172291723017231172321723317234172351723617237172381723917240172411724217243172441724517246172471724817249172501725117252172531725417255172561725717258172591726017261172621726317264172651726617267172681726917270172711727217273172741727517276172771727817279172801728117282172831728417285172861728717288172891729017291172921729317294172951729617297172981729917300173011730217303173041730517306173071730817309173101731117312173131731417315173161731717318173191732017321173221732317324173251732617327173281732917330173311733217333173341733517336173371733817339173401734117342173431734417345173461734717348173491735017351173521735317354173551735617357173581735917360173611736217363173641736517366173671736817369173701737117372173731737417375173761737717378173791738017381173821738317384173851738617387173881738917390173911739217393173941739517396173971739817399174001740117402174031740417405174061740717408174091741017411174121741317414174151741617417174181741917420174211742217423174241742517426174271742817429174301743117432174331743417435174361743717438174391744017441174421744317444174451744617447174481744917450174511745217453174541745517456174571745817459174601746117462174631746417465174661746717468174691747017471174721747317474174751747617477174781747917480174811748217483174841748517486174871748817489174901749117492174931749417495174961749717498174991750017501175021750317504175051750617507175081750917510175111751217513175141751517516175171751817519175201752117522175231752417525175261752717528175291753017531175321753317534175351753617537175381753917540175411754217543175441754517546175471754817549175501755117552175531755417555175561755717558175591756017561175621756317564175651756617567175681756917570175711757217573175741757517576175771757817579175801758117582175831758417585175861758717588175891759017591175921759317594175951759617597175981759917600176011760217603176041760517606176071760817609176101761117612176131761417615176161761717618176191762017621176221762317624176251762617627176281762917630176311763217633176341763517636176371763817639176401764117642176431764417645176461764717648176491765017651176521765317654176551765617657176581765917660176611766217663176641766517666176671766817669176701767117672176731767417675176761767717678176791768017681176821768317684176851768617687176881768917690176911769217693176941769517696176971769817699177001770117702177031770417705177061770717708177091771017711177121771317714177151771617717177181771917720177211772217723177241772517726177271772817729177301773117732177331773417735177361773717738177391774017741177421774317744177451774617747177481774917750177511775217753177541775517756177571775817759177601776117762177631776417765177661776717768177691777017771177721777317774177751777617777177781777917780177811778217783177841778517786177871778817789177901779117792177931779417795177961779717798177991780017801178021780317804178051780617807178081780917810178111781217813178141781517816178171781817819178201782117822178231782417825178261782717828178291783017831178321783317834178351783617837178381783917840178411784217843178441784517846178471784817849178501785117852178531785417855178561785717858178591786017861178621786317864178651786617867178681786917870178711787217873178741787517876178771787817879178801788117882178831788417885178861788717888178891789017891178921789317894178951789617897178981789917900179011790217903179041790517906179071790817909179101791117912179131791417915179161791717918179191792017921179221792317924179251792617927179281792917930179311793217933179341793517936179371793817939179401794117942179431794417945179461794717948179491795017951179521795317954179551795617957179581795917960179611796217963179641796517966179671796817969179701797117972179731797417975179761797717978179791798017981179821798317984179851798617987179881798917990179911799217993179941799517996179971799817999180001800118002180031800418005180061800718008180091801018011180121801318014180151801618017180181801918020180211802218023180241802518026180271802818029180301803118032180331803418035180361803718038180391804018041180421804318044180451804618047180481804918050180511805218053180541805518056180571805818059180601806118062180631806418065180661806718068180691807018071180721807318074180751807618077180781807918080180811808218083180841808518086180871808818089180901809118092180931809418095180961809718098180991810018101181021810318104181051810618107181081810918110181111811218113181141811518116181171811818119181201812118122181231812418125181261812718128181291813018131181321813318134181351813618137181381813918140181411814218143181441814518146181471814818149181501815118152181531815418155181561815718158181591816018161181621816318164181651816618167181681816918170181711817218173181741817518176181771817818179181801818118182181831818418185181861818718188181891819018191181921819318194181951819618197181981819918200182011820218203182041820518206182071820818209182101821118212182131821418215182161821718218182191822018221182221822318224182251822618227182281822918230182311823218233182341823518236182371823818239182401824118242182431824418245182461824718248182491825018251182521825318254182551825618257182581825918260182611826218263182641826518266182671826818269182701827118272182731827418275182761827718278182791828018281182821828318284182851828618287182881828918290182911829218293182941829518296182971829818299183001830118302183031830418305183061830718308183091831018311183121831318314183151831618317183181831918320183211832218323183241832518326183271832818329183301833118332183331833418335183361833718338183391834018341183421834318344183451834618347183481834918350183511835218353183541835518356183571835818359183601836118362183631836418365183661836718368183691837018371183721837318374183751837618377183781837918380183811838218383183841838518386183871838818389183901839118392183931839418395183961839718398183991840018401184021840318404184051840618407184081840918410184111841218413184141841518416184171841818419184201842118422184231842418425184261842718428184291843018431184321843318434184351843618437184381843918440184411844218443184441844518446184471844818449184501845118452184531845418455184561845718458184591846018461184621846318464184651846618467184681846918470184711847218473184741847518476184771847818479184801848118482184831848418485184861848718488184891849018491184921849318494184951849618497184981849918500185011850218503185041850518506185071850818509185101851118512185131851418515185161851718518185191852018521185221852318524185251852618527185281852918530185311853218533185341853518536185371853818539185401854118542185431854418545185461854718548185491855018551185521855318554185551855618557185581855918560185611856218563185641856518566185671856818569185701857118572185731857418575185761857718578185791858018581185821858318584185851858618587185881858918590185911859218593185941859518596185971859818599186001860118602186031860418605186061860718608186091861018611186121861318614186151861618617186181861918620186211862218623186241862518626186271862818629186301863118632186331863418635186361863718638186391864018641186421864318644186451864618647186481864918650186511865218653186541865518656186571865818659186601866118662186631866418665186661866718668186691867018671186721867318674186751867618677186781867918680186811868218683186841868518686186871868818689186901869118692186931869418695186961869718698186991870018701187021870318704187051870618707187081870918710187111871218713187141871518716187171871818719187201872118722187231872418725187261872718728187291873018731187321873318734187351873618737187381873918740187411874218743187441874518746187471874818749187501875118752187531875418755187561875718758187591876018761187621876318764187651876618767187681876918770187711877218773187741877518776187771877818779187801878118782187831878418785187861878718788187891879018791187921879318794187951879618797187981879918800188011880218803188041880518806188071880818809188101881118812188131881418815188161881718818188191882018821188221882318824188251882618827188281882918830188311883218833188341883518836188371883818839188401884118842188431884418845188461884718848188491885018851188521885318854188551885618857188581885918860188611886218863188641886518866188671886818869188701887118872188731887418875188761887718878188791888018881188821888318884188851888618887188881888918890188911889218893188941889518896188971889818899189001890118902189031890418905189061890718908189091891018911189121891318914189151891618917189181891918920189211892218923189241892518926189271892818929189301893118932189331893418935189361893718938189391894018941189421894318944189451894618947189481894918950189511895218953189541895518956189571895818959189601896118962189631896418965189661896718968189691897018971189721897318974189751897618977189781897918980189811898218983189841898518986189871898818989189901899118992189931899418995189961899718998189991900019001190021900319004190051900619007190081900919010190111901219013190141901519016190171901819019190201902119022190231902419025190261902719028190291903019031190321903319034190351903619037190381903919040190411904219043190441904519046190471904819049190501905119052190531905419055190561905719058190591906019061190621906319064190651906619067190681906919070190711907219073190741907519076190771907819079190801908119082190831908419085190861908719088190891909019091190921909319094190951909619097190981909919100191011910219103191041910519106191071910819109191101911119112191131911419115191161911719118191191912019121191221912319124191251912619127191281912919130191311913219133191341913519136191371913819139191401914119142191431914419145191461914719148191491915019151191521915319154191551915619157191581915919160191611916219163191641916519166191671916819169191701917119172191731917419175191761917719178191791918019181191821918319184191851918619187191881918919190191911919219193191941919519196191971919819199192001920119202192031920419205192061920719208192091921019211192121921319214192151921619217192181921919220192211922219223192241922519226192271922819229192301923119232192331923419235192361923719238192391924019241192421924319244192451924619247192481924919250192511925219253192541925519256192571925819259192601926119262192631926419265192661926719268192691927019271192721927319274192751927619277192781927919280192811928219283192841928519286192871928819289192901929119292192931929419295192961929719298192991930019301193021930319304193051930619307193081930919310193111931219313193141931519316193171931819319193201932119322193231932419325193261932719328193291933019331193321933319334193351933619337193381933919340193411934219343193441934519346193471934819349193501935119352193531935419355193561935719358193591936019361193621936319364193651936619367193681936919370193711937219373193741937519376193771937819379193801938119382193831938419385193861938719388193891939019391193921939319394193951939619397193981939919400194011940219403194041940519406194071940819409194101941119412194131941419415194161941719418194191942019421194221942319424194251942619427194281942919430194311943219433194341943519436194371943819439194401944119442194431944419445194461944719448194491945019451194521945319454194551945619457194581945919460194611946219463194641946519466194671946819469194701947119472194731947419475194761947719478194791948019481194821948319484194851948619487194881948919490194911949219493194941949519496194971949819499195001950119502195031950419505195061950719508195091951019511195121951319514195151951619517195181951919520195211952219523195241952519526195271952819529195301953119532195331953419535195361953719538195391954019541195421954319544195451954619547195481954919550195511955219553195541955519556195571955819559195601956119562195631956419565195661956719568195691957019571195721957319574195751957619577195781957919580195811958219583195841958519586195871958819589195901959119592195931959419595195961959719598195991960019601196021960319604196051960619607196081960919610196111961219613196141961519616196171961819619196201962119622196231962419625196261962719628196291963019631196321963319634196351963619637196381963919640196411964219643196441964519646196471964819649196501965119652196531965419655196561965719658196591966019661196621966319664196651966619667196681966919670196711967219673196741967519676196771967819679196801968119682196831968419685196861968719688196891969019691196921969319694196951969619697196981969919700197011970219703197041970519706197071970819709197101971119712197131971419715197161971719718197191972019721197221972319724197251972619727197281972919730197311973219733197341973519736197371973819739197401974119742197431974419745197461974719748197491975019751197521975319754197551975619757197581975919760197611976219763197641976519766197671976819769197701977119772197731977419775197761977719778197791978019781197821978319784197851978619787197881978919790197911979219793197941979519796197971979819799198001980119802198031980419805198061980719808198091981019811198121981319814198151981619817198181981919820198211982219823198241982519826198271982819829198301983119832198331983419835198361983719838198391984019841198421984319844198451984619847198481984919850198511985219853198541985519856198571985819859198601986119862198631986419865198661986719868198691987019871198721987319874198751987619877198781987919880198811988219883198841988519886198871988819889198901989119892198931989419895198961989719898198991990019901199021990319904199051990619907199081990919910199111991219913199141991519916199171991819919199201992119922199231992419925199261992719928199291993019931199321993319934199351993619937199381993919940199411994219943199441994519946199471994819949199501995119952199531995419955199561995719958199591996019961199621996319964199651996619967199681996919970199711997219973199741997519976199771997819979199801998119982199831998419985199861998719988199891999019991199921999319994199951999619997199981999920000200012000220003200042000520006200072000820009200102001120012200132001420015200162001720018200192002020021200222002320024200252002620027200282002920030200312003220033200342003520036200372003820039200402004120042200432004420045200462004720048200492005020051200522005320054200552005620057200582005920060200612006220063200642006520066200672006820069200702007120072200732007420075200762007720078200792008020081200822008320084200852008620087200882008920090200912009220093200942009520096200972009820099201002010120102201032010420105201062010720108201092011020111201122011320114201152011620117201182011920120201212012220123201242012520126201272012820129201302013120132201332013420135201362013720138201392014020141201422014320144201452014620147201482014920150201512015220153201542015520156201572015820159201602016120162201632016420165201662016720168201692017020171201722017320174201752017620177201782017920180201812018220183201842018520186201872018820189201902019120192201932019420195201962019720198201992020020201202022020320204202052020620207202082020920210202112021220213202142021520216202172021820219202202022120222202232022420225202262022720228202292023020231202322023320234202352023620237202382023920240202412024220243202442024520246202472024820249202502025120252202532025420255202562025720258202592026020261202622026320264202652026620267202682026920270202712027220273202742027520276202772027820279202802028120282202832028420285202862028720288202892029020291202922029320294202952029620297202982029920300203012030220303203042030520306203072030820309203102031120312203132031420315203162031720318203192032020321203222032320324203252032620327203282032920330203312033220333203342033520336203372033820339203402034120342203432034420345203462034720348203492035020351203522035320354203552035620357203582035920360203612036220363203642036520366203672036820369203702037120372203732037420375203762037720378203792038020381203822038320384203852038620387203882038920390203912039220393203942039520396203972039820399204002040120402204032040420405204062040720408204092041020411204122041320414204152041620417204182041920420204212042220423204242042520426204272042820429204302043120432204332043420435204362043720438204392044020441204422044320444204452044620447204482044920450204512045220453204542045520456204572045820459204602046120462204632046420465204662046720468204692047020471204722047320474204752047620477204782047920480204812048220483204842048520486204872048820489204902049120492204932049420495204962049720498204992050020501205022050320504205052050620507205082050920510205112051220513205142051520516205172051820519205202052120522205232052420525205262052720528205292053020531205322053320534205352053620537205382053920540205412054220543205442054520546205472054820549205502055120552205532055420555205562055720558205592056020561205622056320564205652056620567205682056920570205712057220573205742057520576205772057820579205802058120582205832058420585205862058720588205892059020591205922059320594205952059620597205982059920600206012060220603206042060520606206072060820609206102061120612206132061420615206162061720618206192062020621206222062320624206252062620627206282062920630206312063220633206342063520636206372063820639206402064120642206432064420645206462064720648206492065020651206522065320654206552065620657206582065920660206612066220663206642066520666206672066820669206702067120672206732067420675206762067720678206792068020681206822068320684206852068620687206882068920690206912069220693206942069520696206972069820699207002070120702207032070420705207062070720708207092071020711207122071320714207152071620717207182071920720207212072220723207242072520726207272072820729207302073120732207332073420735207362073720738207392074020741207422074320744207452074620747207482074920750207512075220753207542075520756207572075820759207602076120762207632076420765207662076720768207692077020771207722077320774207752077620777207782077920780207812078220783207842078520786207872078820789207902079120792207932079420795207962079720798207992080020801208022080320804208052080620807208082080920810208112081220813208142081520816208172081820819208202082120822208232082420825208262082720828208292083020831208322083320834208352083620837208382083920840208412084220843208442084520846208472084820849208502085120852208532085420855208562085720858208592086020861208622086320864208652086620867208682086920870208712087220873208742087520876208772087820879208802088120882208832088420885208862088720888208892089020891208922089320894208952089620897208982089920900209012090220903209042090520906209072090820909209102091120912209132091420915209162091720918209192092020921209222092320924209252092620927209282092920930209312093220933209342093520936209372093820939209402094120942209432094420945209462094720948209492095020951209522095320954209552095620957209582095920960209612096220963209642096520966209672096820969209702097120972209732097420975209762097720978209792098020981209822098320984209852098620987209882098920990209912099220993209942099520996209972099820999210002100121002210032100421005210062100721008210092101021011210122101321014210152101621017210182101921020210212102221023210242102521026210272102821029210302103121032210332103421035210362103721038210392104021041210422104321044210452104621047210482104921050210512105221053210542105521056210572105821059210602106121062210632106421065210662106721068210692107021071210722107321074210752107621077210782107921080210812108221083210842108521086210872108821089210902109121092210932109421095210962109721098210992110021101211022110321104211052110621107211082110921110211112111221113211142111521116211172111821119211202112121122211232112421125211262112721128211292113021131211322113321134211352113621137211382113921140211412114221143211442114521146211472114821149211502115121152211532115421155211562115721158211592116021161211622116321164211652116621167211682116921170211712117221173211742117521176211772117821179211802118121182211832118421185211862118721188211892119021191211922119321194211952119621197211982119921200212012120221203212042120521206212072120821209212102121121212212132121421215212162121721218212192122021221212222122321224212252122621227212282122921230212312123221233212342123521236212372123821239212402124121242212432124421245212462124721248212492125021251212522125321254212552125621257212582125921260212612126221263212642126521266212672126821269212702127121272212732127421275212762127721278212792128021281212822128321284212852128621287212882128921290212912129221293212942129521296212972129821299213002130121302213032130421305213062130721308213092131021311213122131321314213152131621317213182131921320213212132221323213242132521326213272132821329213302133121332213332133421335213362133721338213392134021341213422134321344213452134621347213482134921350213512135221353213542135521356213572135821359213602136121362213632136421365213662136721368213692137021371213722137321374213752137621377213782137921380213812138221383213842138521386213872138821389213902139121392213932139421395213962139721398213992140021401214022140321404214052140621407214082140921410214112141221413214142141521416214172141821419214202142121422214232142421425214262142721428214292143021431214322143321434214352143621437214382143921440214412144221443214442144521446214472144821449214502145121452214532145421455214562145721458214592146021461214622146321464214652146621467214682146921470214712147221473214742147521476214772147821479214802148121482214832148421485214862148721488214892149021491214922149321494214952149621497214982149921500215012150221503215042150521506215072150821509215102151121512215132151421515215162151721518215192152021521215222152321524215252152621527215282152921530215312153221533215342153521536215372153821539215402154121542215432154421545215462154721548215492155021551215522155321554215552155621557215582155921560215612156221563215642156521566215672156821569215702157121572215732157421575215762157721578215792158021581215822158321584215852158621587215882158921590215912159221593215942159521596215972159821599216002160121602216032160421605216062160721608216092161021611216122161321614216152161621617216182161921620216212162221623216242162521626216272162821629216302163121632216332163421635216362163721638216392164021641216422164321644216452164621647216482164921650216512165221653216542165521656216572165821659216602166121662216632166421665216662166721668216692167021671216722167321674216752167621677216782167921680216812168221683216842168521686216872168821689216902169121692216932169421695216962169721698216992170021701217022170321704217052170621707217082170921710217112171221713217142171521716217172171821719217202172121722217232172421725217262172721728217292173021731217322173321734217352173621737217382173921740217412174221743217442174521746217472174821749217502175121752217532175421755217562175721758217592176021761217622176321764217652176621767217682176921770217712177221773217742177521776217772177821779217802178121782217832178421785217862178721788217892179021791217922179321794217952179621797217982179921800218012180221803218042180521806218072180821809218102181121812218132181421815218162181721818218192182021821218222182321824218252182621827218282182921830218312183221833218342183521836218372183821839218402184121842218432184421845218462184721848218492185021851218522185321854218552185621857218582185921860218612186221863218642186521866218672186821869218702187121872218732187421875218762187721878218792188021881218822188321884218852188621887218882188921890218912189221893218942189521896218972189821899219002190121902219032190421905219062190721908219092191021911219122191321914219152191621917219182191921920219212192221923219242192521926219272192821929219302193121932219332193421935219362193721938219392194021941219422194321944219452194621947219482194921950219512195221953219542195521956219572195821959219602196121962219632196421965219662196721968219692197021971219722197321974219752197621977219782197921980219812198221983219842198521986219872198821989219902199121992219932199421995219962199721998219992200022001220022200322004220052200622007220082200922010220112201222013220142201522016220172201822019220202202122022220232202422025220262202722028220292203022031220322203322034220352203622037220382203922040220412204222043220442204522046220472204822049220502205122052220532205422055220562205722058220592206022061220622206322064220652206622067220682206922070220712207222073220742207522076220772207822079220802208122082220832208422085220862208722088220892209022091220922209322094220952209622097220982209922100221012210222103221042210522106221072210822109221102211122112221132211422115221162211722118221192212022121221222212322124221252212622127221282212922130221312213222133221342213522136221372213822139221402214122142221432214422145221462214722148221492215022151221522215322154221552215622157221582215922160221612216222163221642216522166221672216822169221702217122172221732217422175221762217722178221792218022181221822218322184221852218622187221882218922190221912219222193221942219522196221972219822199222002220122202222032220422205222062220722208222092221022211222122221322214222152221622217222182221922220222212222222223222242222522226222272222822229222302223122232222332223422235222362223722238222392224022241222422224322244222452224622247222482224922250222512225222253222542225522256222572225822259222602226122262222632226422265222662226722268222692227022271222722227322274222752227622277222782227922280222812228222283222842228522286222872228822289222902229122292222932229422295222962229722298222992230022301223022230322304223052230622307223082230922310223112231222313223142231522316223172231822319223202232122322223232232422325223262232722328223292233022331223322233322334223352233622337223382233922340223412234222343223442234522346223472234822349223502235122352223532235422355223562235722358223592236022361223622236322364223652236622367223682236922370223712237222373223742237522376223772237822379223802238122382223832238422385223862238722388223892239022391223922239322394223952239622397223982239922400224012240222403224042240522406224072240822409224102241122412224132241422415224162241722418224192242022421224222242322424224252242622427224282242922430224312243222433224342243522436224372243822439224402244122442224432244422445224462244722448224492245022451224522245322454224552245622457224582245922460224612246222463224642246522466224672246822469224702247122472224732247422475224762247722478224792248022481224822248322484224852248622487224882248922490224912249222493224942249522496224972249822499225002250122502225032250422505225062250722508225092251022511225122251322514225152251622517225182251922520225212252222523225242252522526225272252822529225302253122532225332253422535225362253722538225392254022541225422254322544225452254622547225482254922550225512255222553225542255522556225572255822559225602256122562225632256422565225662256722568225692257022571225722257322574225752257622577225782257922580225812258222583225842258522586225872258822589225902259122592225932259422595225962259722598225992260022601226022260322604226052260622607226082260922610226112261222613226142261522616226172261822619226202262122622226232262422625226262262722628226292263022631226322263322634226352263622637226382263922640226412264222643226442264522646226472264822649226502265122652226532265422655226562265722658226592266022661226622266322664226652266622667226682266922670226712267222673226742267522676226772267822679226802268122682226832268422685226862268722688226892269022691226922269322694226952269622697226982269922700227012270222703227042270522706227072270822709227102271122712227132271422715227162271722718227192272022721227222272322724227252272622727227282272922730227312273222733227342273522736227372273822739227402274122742227432274422745227462274722748227492275022751227522275322754227552275622757227582275922760227612276222763227642276522766227672276822769227702277122772227732277422775227762277722778227792278022781227822278322784227852278622787227882278922790227912279222793227942279522796227972279822799228002280122802228032280422805228062280722808228092281022811228122281322814228152281622817228182281922820228212282222823228242282522826228272282822829228302283122832228332283422835228362283722838228392284022841228422284322844228452284622847228482284922850228512285222853228542285522856228572285822859228602286122862228632286422865228662286722868228692287022871228722287322874228752287622877228782287922880228812288222883228842288522886228872288822889228902289122892228932289422895228962289722898228992290022901229022290322904229052290622907229082290922910229112291222913229142291522916229172291822919229202292122922229232292422925229262292722928229292293022931229322293322934229352293622937229382293922940229412294222943229442294522946229472294822949229502295122952229532295422955229562295722958229592296022961229622296322964229652296622967229682296922970229712297222973229742297522976229772297822979229802298122982229832298422985229862298722988229892299022991229922299322994229952299622997229982299923000230012300223003230042300523006230072300823009230102301123012230132301423015230162301723018230192302023021230222302323024230252302623027230282302923030230312303223033230342303523036230372303823039230402304123042230432304423045230462304723048230492305023051230522305323054230552305623057230582305923060230612306223063230642306523066230672306823069230702307123072230732307423075230762307723078230792308023081230822308323084230852308623087230882308923090230912309223093230942309523096230972309823099231002310123102231032310423105231062310723108231092311023111231122311323114231152311623117231182311923120231212312223123231242312523126231272312823129231302313123132231332313423135231362313723138231392314023141231422314323144231452314623147231482314923150231512315223153231542315523156231572315823159231602316123162231632316423165231662316723168231692317023171231722317323174231752317623177231782317923180231812318223183231842318523186231872318823189231902319123192231932319423195231962319723198231992320023201232022320323204232052320623207232082320923210232112321223213232142321523216232172321823219232202322123222232232322423225232262322723228232292323023231232322323323234232352323623237232382323923240232412324223243232442324523246232472324823249232502325123252232532325423255232562325723258232592326023261232622326323264232652326623267232682326923270232712327223273232742327523276232772327823279232802328123282232832328423285232862328723288232892329023291232922329323294232952329623297232982329923300233012330223303233042330523306233072330823309233102331123312233132331423315233162331723318233192332023321233222332323324233252332623327233282332923330233312333223333233342333523336233372333823339233402334123342233432334423345233462334723348233492335023351233522335323354233552335623357233582335923360233612336223363233642336523366233672336823369233702337123372233732337423375233762337723378233792338023381233822338323384233852338623387233882338923390233912339223393233942339523396233972339823399234002340123402234032340423405234062340723408234092341023411234122341323414234152341623417234182341923420234212342223423234242342523426234272342823429234302343123432234332343423435234362343723438234392344023441234422344323444234452344623447234482344923450234512345223453234542345523456234572345823459234602346123462234632346423465234662346723468234692347023471234722347323474234752347623477234782347923480234812348223483234842348523486234872348823489234902349123492234932349423495234962349723498234992350023501235022350323504235052350623507235082350923510235112351223513235142351523516235172351823519235202352123522235232352423525235262352723528235292353023531235322353323534235352353623537235382353923540235412354223543235442354523546235472354823549235502355123552235532355423555235562355723558235592356023561235622356323564235652356623567235682356923570235712357223573235742357523576235772357823579235802358123582235832358423585235862358723588235892359023591235922359323594235952359623597235982359923600236012360223603236042360523606236072360823609236102361123612236132361423615236162361723618236192362023621236222362323624236252362623627236282362923630236312363223633236342363523636236372363823639236402364123642236432364423645236462364723648236492365023651236522365323654236552365623657236582365923660236612366223663236642366523666236672366823669236702367123672236732367423675236762367723678236792368023681236822368323684236852368623687236882368923690236912369223693236942369523696236972369823699237002370123702237032370423705237062370723708237092371023711237122371323714237152371623717237182371923720237212372223723237242372523726237272372823729237302373123732237332373423735237362373723738237392374023741237422374323744237452374623747237482374923750237512375223753237542375523756237572375823759237602376123762237632376423765237662376723768237692377023771237722377323774237752377623777237782377923780237812378223783237842378523786237872378823789237902379123792237932379423795237962379723798237992380023801238022380323804238052380623807238082380923810238112381223813238142381523816238172381823819238202382123822238232382423825238262382723828238292383023831238322383323834238352383623837238382383923840238412384223843238442384523846238472384823849238502385123852238532385423855238562385723858238592386023861238622386323864238652386623867238682386923870238712387223873238742387523876238772387823879238802388123882238832388423885238862388723888238892389023891238922389323894238952389623897238982389923900239012390223903239042390523906239072390823909239102391123912239132391423915239162391723918239192392023921239222392323924239252392623927239282392923930239312393223933239342393523936239372393823939239402394123942239432394423945239462394723948239492395023951239522395323954239552395623957239582395923960239612396223963239642396523966239672396823969239702397123972239732397423975239762397723978239792398023981239822398323984239852398623987239882398923990239912399223993239942399523996239972399823999240002400124002240032400424005240062400724008240092401024011240122401324014240152401624017240182401924020240212402224023240242402524026240272402824029240302403124032240332403424035240362403724038240392404024041240422404324044240452404624047240482404924050240512405224053240542405524056240572405824059240602406124062240632406424065240662406724068240692407024071240722407324074240752407624077240782407924080240812408224083240842408524086240872408824089240902409124092240932409424095240962409724098240992410024101241022410324104241052410624107241082410924110241112411224113241142411524116241172411824119241202412124122241232412424125241262412724128241292413024131241322413324134241352413624137241382413924140241412414224143241442414524146241472414824149241502415124152241532415424155241562415724158241592416024161241622416324164241652416624167241682416924170241712417224173241742417524176241772417824179241802418124182241832418424185241862418724188241892419024191241922419324194241952419624197241982419924200242012420224203242042420524206242072420824209242102421124212242132421424215242162421724218242192422024221242222422324224242252422624227242282422924230242312423224233242342423524236242372423824239242402424124242242432424424245242462424724248242492425024251242522425324254242552425624257242582425924260242612426224263242642426524266242672426824269242702427124272242732427424275242762427724278242792428024281242822428324284242852428624287242882428924290242912429224293242942429524296242972429824299243002430124302243032430424305243062430724308243092431024311243122431324314243152431624317243182431924320243212432224323243242432524326243272432824329243302433124332243332433424335243362433724338243392434024341243422434324344243452434624347243482434924350243512435224353243542435524356243572435824359243602436124362243632436424365243662436724368243692437024371243722437324374243752437624377243782437924380243812438224383243842438524386243872438824389243902439124392243932439424395243962439724398243992440024401244022440324404244052440624407244082440924410244112441224413244142441524416244172441824419244202442124422244232442424425244262442724428244292443024431244322443324434244352443624437244382443924440244412444224443244442444524446244472444824449244502445124452244532445424455244562445724458244592446024461244622446324464244652446624467244682446924470244712447224473244742447524476244772447824479244802448124482244832448424485244862448724488244892449024491244922449324494244952449624497244982449924500245012450224503245042450524506245072450824509245102451124512245132451424515245162451724518245192452024521245222452324524245252452624527245282452924530245312453224533245342453524536245372453824539245402454124542245432454424545245462454724548245492455024551245522455324554245552455624557245582455924560245612456224563245642456524566245672456824569245702457124572245732457424575245762457724578245792458024581245822458324584245852458624587245882458924590245912459224593245942459524596245972459824599246002460124602246032460424605246062460724608246092461024611246122461324614246152461624617246182461924620246212462224623246242462524626246272462824629246302463124632246332463424635246362463724638246392464024641246422464324644246452464624647246482464924650246512465224653246542465524656246572465824659246602466124662246632466424665246662466724668246692467024671246722467324674246752467624677246782467924680246812468224683246842468524686246872468824689246902469124692246932469424695246962469724698246992470024701247022470324704247052470624707247082470924710247112471224713247142471524716247172471824719247202472124722247232472424725247262472724728247292473024731247322473324734247352473624737247382473924740247412474224743247442474524746247472474824749247502475124752247532475424755247562475724758247592476024761247622476324764247652476624767247682476924770247712477224773247742477524776247772477824779247802478124782247832478424785247862478724788247892479024791247922479324794247952479624797247982479924800248012480224803248042480524806248072480824809248102481124812248132481424815248162481724818248192482024821248222482324824248252482624827248282482924830248312483224833248342483524836248372483824839248402484124842248432484424845248462484724848248492485024851248522485324854248552485624857248582485924860248612486224863248642486524866248672486824869248702487124872248732487424875248762487724878248792488024881248822488324884248852488624887248882488924890248912489224893248942489524896248972489824899249002490124902249032490424905249062490724908249092491024911249122491324914249152491624917249182491924920249212492224923249242492524926249272492824929249302493124932249332493424935249362493724938249392494024941249422494324944249452494624947249482494924950249512495224953249542495524956249572495824959249602496124962249632496424965249662496724968249692497024971249722497324974249752497624977249782497924980249812498224983249842498524986249872498824989249902499124992249932499424995249962499724998249992500025001250022500325004250052500625007250082500925010250112501225013250142501525016250172501825019250202502125022250232502425025250262502725028250292503025031250322503325034250352503625037250382503925040250412504225043250442504525046250472504825049250502505125052250532505425055250562505725058250592506025061250622506325064250652506625067250682506925070250712507225073250742507525076250772507825079250802508125082250832508425085250862508725088250892509025091250922509325094250952509625097250982509925100251012510225103251042510525106251072510825109251102511125112251132511425115251162511725118251192512025121251222512325124251252512625127251282512925130251312513225133251342513525136251372513825139251402514125142251432514425145251462514725148251492515025151251522515325154251552515625157251582515925160251612516225163251642516525166251672516825169251702517125172251732517425175251762517725178251792518025181251822518325184251852518625187251882518925190251912519225193251942519525196251972519825199252002520125202252032520425205252062520725208252092521025211252122521325214252152521625217252182521925220252212522225223252242522525226252272522825229252302523125232252332523425235252362523725238252392524025241252422524325244252452524625247252482524925250252512525225253252542525525256252572525825259252602526125262252632526425265252662526725268252692527025271252722527325274252752527625277252782527925280252812528225283252842528525286252872528825289252902529125292252932529425295252962529725298252992530025301253022530325304253052530625307253082530925310253112531225313253142531525316253172531825319253202532125322253232532425325253262532725328253292533025331253322533325334253352533625337253382533925340253412534225343253442534525346253472534825349253502535125352253532535425355253562535725358253592536025361253622536325364253652536625367253682536925370253712537225373253742537525376253772537825379253802538125382253832538425385253862538725388253892539025391253922539325394253952539625397253982539925400254012540225403254042540525406254072540825409254102541125412254132541425415254162541725418254192542025421254222542325424254252542625427254282542925430254312543225433254342543525436254372543825439254402544125442254432544425445254462544725448254492545025451254522545325454254552545625457254582545925460254612546225463254642546525466254672546825469254702547125472254732547425475254762547725478254792548025481254822548325484254852548625487254882548925490254912549225493254942549525496254972549825499255002550125502255032550425505255062550725508255092551025511255122551325514255152551625517255182551925520255212552225523255242552525526255272552825529255302553125532255332553425535255362553725538255392554025541255422554325544255452554625547255482554925550255512555225553255542555525556255572555825559255602556125562255632556425565255662556725568255692557025571255722557325574255752557625577255782557925580255812558225583255842558525586255872558825589255902559125592255932559425595255962559725598255992560025601256022560325604256052560625607256082560925610256112561225613256142561525616256172561825619256202562125622256232562425625256262562725628256292563025631256322563325634256352563625637256382563925640256412564225643256442564525646256472564825649256502565125652256532565425655256562565725658256592566025661256622566325664256652566625667256682566925670256712567225673256742567525676256772567825679256802568125682256832568425685256862568725688256892569025691256922569325694256952569625697256982569925700257012570225703257042570525706257072570825709257102571125712257132571425715257162571725718257192572025721257222572325724257252572625727257282572925730257312573225733257342573525736257372573825739257402574125742257432574425745257462574725748257492575025751257522575325754257552575625757257582575925760257612576225763257642576525766257672576825769257702577125772257732577425775257762577725778257792578025781257822578325784257852578625787257882578925790257912579225793257942579525796257972579825799258002580125802258032580425805258062580725808258092581025811258122581325814258152581625817258182581925820258212582225823258242582525826258272582825829258302583125832258332583425835258362583725838258392584025841258422584325844258452584625847258482584925850258512585225853258542585525856258572585825859258602586125862258632586425865258662586725868258692587025871258722587325874258752587625877258782587925880258812588225883258842588525886258872588825889258902589125892258932589425895258962589725898258992590025901259022590325904259052590625907259082590925910259112591225913259142591525916259172591825919259202592125922259232592425925259262592725928259292593025931259322593325934259352593625937259382593925940259412594225943259442594525946259472594825949259502595125952259532595425955259562595725958259592596025961259622596325964259652596625967259682596925970259712597225973259742597525976259772597825979259802598125982259832598425985259862598725988259892599025991259922599325994259952599625997259982599926000260012600226003260042600526006260072600826009260102601126012260132601426015260162601726018260192602026021260222602326024260252602626027260282602926030260312603226033260342603526036260372603826039260402604126042260432604426045260462604726048260492605026051260522605326054260552605626057260582605926060260612606226063260642606526066260672606826069260702607126072260732607426075260762607726078260792608026081260822608326084260852608626087260882608926090260912609226093260942609526096260972609826099261002610126102261032610426105261062610726108261092611026111261122611326114261152611626117261182611926120261212612226123261242612526126261272612826129261302613126132261332613426135261362613726138261392614026141261422614326144261452614626147261482614926150261512615226153261542615526156261572615826159261602616126162261632616426165261662616726168261692617026171261722617326174261752617626177261782617926180261812618226183261842618526186261872618826189261902619126192261932619426195261962619726198261992620026201262022620326204262052620626207262082620926210262112621226213262142621526216262172621826219262202622126222262232622426225262262622726228262292623026231262322623326234262352623626237262382623926240262412624226243262442624526246262472624826249262502625126252262532625426255262562625726258262592626026261262622626326264262652626626267262682626926270262712627226273262742627526276262772627826279262802628126282262832628426285262862628726288262892629026291262922629326294262952629626297262982629926300263012630226303263042630526306263072630826309263102631126312263132631426315263162631726318263192632026321263222632326324263252632626327263282632926330263312633226333263342633526336263372633826339263402634126342263432634426345263462634726348263492635026351263522635326354263552635626357263582635926360263612636226363263642636526366263672636826369263702637126372263732637426375263762637726378263792638026381263822638326384263852638626387263882638926390263912639226393263942639526396263972639826399264002640126402264032640426405264062640726408264092641026411264122641326414264152641626417264182641926420264212642226423264242642526426264272642826429264302643126432264332643426435264362643726438264392644026441264422644326444264452644626447264482644926450264512645226453264542645526456264572645826459264602646126462264632646426465264662646726468264692647026471264722647326474264752647626477264782647926480264812648226483264842648526486264872648826489264902649126492264932649426495264962649726498264992650026501265022650326504265052650626507265082650926510265112651226513265142651526516265172651826519265202652126522265232652426525265262652726528265292653026531265322653326534265352653626537265382653926540265412654226543265442654526546265472654826549265502655126552265532655426555265562655726558265592656026561265622656326564265652656626567265682656926570265712657226573265742657526576265772657826579265802658126582265832658426585265862658726588265892659026591265922659326594265952659626597265982659926600266012660226603266042660526606266072660826609266102661126612266132661426615266162661726618266192662026621266222662326624266252662626627266282662926630266312663226633266342663526636266372663826639266402664126642266432664426645266462664726648266492665026651266522665326654266552665626657266582665926660266612666226663266642666526666266672666826669266702667126672266732667426675266762667726678266792668026681266822668326684266852668626687266882668926690266912669226693266942669526696266972669826699267002670126702267032670426705267062670726708267092671026711267122671326714267152671626717267182671926720267212672226723267242672526726267272672826729267302673126732267332673426735267362673726738267392674026741267422674326744267452674626747267482674926750267512675226753267542675526756267572675826759267602676126762267632676426765267662676726768267692677026771267722677326774267752677626777267782677926780267812678226783267842678526786267872678826789267902679126792267932679426795267962679726798267992680026801268022680326804268052680626807268082680926810268112681226813268142681526816268172681826819268202682126822268232682426825268262682726828268292683026831268322683326834268352683626837268382683926840268412684226843268442684526846268472684826849268502685126852268532685426855268562685726858268592686026861268622686326864268652686626867268682686926870268712687226873268742687526876268772687826879268802688126882268832688426885268862688726888268892689026891268922689326894268952689626897268982689926900269012690226903269042690526906269072690826909269102691126912269132691426915269162691726918269192692026921269222692326924269252692626927269282692926930269312693226933269342693526936269372693826939269402694126942269432694426945269462694726948269492695026951269522695326954269552695626957269582695926960269612696226963269642696526966269672696826969269702697126972269732697426975269762697726978269792698026981269822698326984269852698626987269882698926990269912699226993269942699526996269972699826999270002700127002270032700427005270062700727008270092701027011270122701327014270152701627017270182701927020270212702227023270242702527026270272702827029270302703127032270332703427035270362703727038270392704027041270422704327044270452704627047270482704927050270512705227053270542705527056270572705827059270602706127062270632706427065270662706727068270692707027071270722707327074270752707627077270782707927080270812708227083270842708527086270872708827089270902709127092270932709427095270962709727098270992710027101271022710327104271052710627107271082710927110271112711227113271142711527116271172711827119271202712127122271232712427125271262712727128271292713027131271322713327134271352713627137271382713927140271412714227143271442714527146271472714827149271502715127152271532715427155271562715727158271592716027161271622716327164271652716627167271682716927170271712717227173271742717527176271772717827179271802718127182271832718427185271862718727188271892719027191271922719327194271952719627197271982719927200272012720227203272042720527206272072720827209272102721127212272132721427215272162721727218272192722027221272222722327224272252722627227272282722927230272312723227233272342723527236272372723827239272402724127242272432724427245272462724727248272492725027251272522725327254272552725627257272582725927260272612726227263272642726527266272672726827269272702727127272272732727427275272762727727278272792728027281272822728327284272852728627287272882728927290272912729227293272942729527296272972729827299273002730127302273032730427305273062730727308273092731027311273122731327314273152731627317273182731927320273212732227323273242732527326273272732827329273302733127332273332733427335273362733727338273392734027341273422734327344273452734627347273482734927350273512735227353273542735527356273572735827359273602736127362273632736427365273662736727368273692737027371273722737327374273752737627377273782737927380273812738227383273842738527386273872738827389273902739127392273932739427395273962739727398273992740027401274022740327404274052740627407274082740927410274112741227413274142741527416274172741827419274202742127422274232742427425274262742727428274292743027431274322743327434274352743627437274382743927440274412744227443274442744527446274472744827449274502745127452274532745427455274562745727458274592746027461274622746327464274652746627467274682746927470274712747227473274742747527476274772747827479274802748127482274832748427485274862748727488274892749027491274922749327494274952749627497274982749927500275012750227503275042750527506275072750827509275102751127512275132751427515275162751727518275192752027521275222752327524275252752627527275282752927530275312753227533275342753527536275372753827539275402754127542275432754427545275462754727548275492755027551275522755327554275552755627557275582755927560275612756227563275642756527566275672756827569275702757127572275732757427575275762757727578275792758027581275822758327584275852758627587275882758927590275912759227593275942759527596275972759827599276002760127602276032760427605276062760727608276092761027611276122761327614276152761627617276182761927620276212762227623276242762527626276272762827629276302763127632276332763427635276362763727638276392764027641276422764327644276452764627647276482764927650276512765227653276542765527656276572765827659276602766127662276632766427665276662766727668276692767027671276722767327674276752767627677276782767927680276812768227683276842768527686276872768827689276902769127692276932769427695276962769727698276992770027701277022770327704277052770627707277082770927710277112771227713277142771527716277172771827719277202772127722277232772427725277262772727728277292773027731277322773327734277352773627737277382773927740277412774227743277442774527746277472774827749277502775127752277532775427755277562775727758277592776027761277622776327764277652776627767277682776927770277712777227773277742777527776277772777827779277802778127782277832778427785277862778727788277892779027791277922779327794277952779627797277982779927800278012780227803278042780527806278072780827809278102781127812278132781427815278162781727818278192782027821278222782327824278252782627827278282782927830278312783227833278342783527836278372783827839278402784127842278432784427845278462784727848278492785027851278522785327854278552785627857278582785927860278612786227863278642786527866278672786827869278702787127872278732787427875278762787727878278792788027881278822788327884278852788627887278882788927890278912789227893278942789527896278972789827899279002790127902279032790427905279062790727908279092791027911279122791327914279152791627917279182791927920279212792227923279242792527926279272792827929279302793127932279332793427935279362793727938279392794027941279422794327944279452794627947279482794927950279512795227953279542795527956279572795827959279602796127962279632796427965279662796727968279692797027971279722797327974279752797627977279782797927980279812798227983279842798527986279872798827989279902799127992279932799427995279962799727998279992800028001280022800328004280052800628007280082800928010280112801228013280142801528016280172801828019280202802128022280232802428025280262802728028280292803028031280322803328034280352803628037280382803928040280412804228043280442804528046280472804828049280502805128052280532805428055280562805728058280592806028061280622806328064280652806628067280682806928070280712807228073280742807528076280772807828079280802808128082280832808428085280862808728088280892809028091280922809328094280952809628097280982809928100281012810228103281042810528106281072810828109281102811128112281132811428115281162811728118281192812028121281222812328124281252812628127281282812928130281312813228133281342813528136281372813828139281402814128142281432814428145281462814728148 |
- /*! jQuery v3.5.1 | (c) JS Foundation and other contributors | jquery.org/license */
- !function(e,t){"use strict";"object"==typeof module&&"object"==typeof module.exports?module.exports=e.document?t(e,!0):function(e){if(!e.document)throw new Error("jQuery requires a window with a document");return t(e)}:t(e)}("undefined"!=typeof window?window:this,function(C,e){"use strict";var t=[],r=Object.getPrototypeOf,s=t.slice,g=t.flat?function(e){return t.flat.call(e)}:function(e){return t.concat.apply([],e)},u=t.push,i=t.indexOf,n={},o=n.toString,v=n.hasOwnProperty,a=v.toString,l=a.call(Object),y={},m=function(e){return"function"==typeof e&&"number"!=typeof e.nodeType},x=function(e){return null!=e&&e===e.window},E=C.document,c={type:!0,src:!0,nonce:!0,noModule:!0};function b(e,t,n){var r,i,o=(n=n||E).createElement("script");if(o.text=e,t)for(r in c)(i=t[r]||t.getAttribute&&t.getAttribute(r))&&o.setAttribute(r,i);n.head.appendChild(o).parentNode.removeChild(o)}function w(e){return null==e?e+"":"object"==typeof e||"function"==typeof e?n[o.call(e)]||"object":typeof e}var f="3.5.1",S=function(e,t){return new S.fn.init(e,t)};function p(e){var t=!!e&&"length"in e&&e.length,n=w(e);return!m(e)&&!x(e)&&("array"===n||0===t||"number"==typeof t&&0<t&&t-1 in e)}S.fn=S.prototype={jquery:f,constructor:S,length:0,toArray:function(){return s.call(this)},get:function(e){return null==e?s.call(this):e<0?this[e+this.length]:this[e]},pushStack:function(e){var t=S.merge(this.constructor(),e);return t.prevObject=this,t},each:function(e){return S.each(this,e)},map:function(n){return this.pushStack(S.map(this,function(e,t){return n.call(e,t,e)}))},slice:function(){return this.pushStack(s.apply(this,arguments))},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},even:function(){return this.pushStack(S.grep(this,function(e,t){return(t+1)%2}))},odd:function(){return this.pushStack(S.grep(this,function(e,t){return t%2}))},eq:function(e){var t=this.length,n=+e+(e<0?t:0);return this.pushStack(0<=n&&n<t?[this[n]]:[])},end:function(){return this.prevObject||this.constructor()},push:u,sort:t.sort,splice:t.splice},S.extend=S.fn.extend=function(){var e,t,n,r,i,o,a=arguments[0]||{},s=1,u=arguments.length,l=!1;for("boolean"==typeof a&&(l=a,a=arguments[s]||{},s++),"object"==typeof a||m(a)||(a={}),s===u&&(a=this,s--);s<u;s++)if(null!=(e=arguments[s]))for(t in e)r=e[t],"__proto__"!==t&&a!==r&&(l&&r&&(S.isPlainObject(r)||(i=Array.isArray(r)))?(n=a[t],o=i&&!Array.isArray(n)?[]:i||S.isPlainObject(n)?n:{},i=!1,a[t]=S.extend(l,o,r)):void 0!==r&&(a[t]=r));return a},S.extend({expando:"jQuery"+(f+Math.random()).replace(/\D/g,""),isReady:!0,error:function(e){throw new Error(e)},noop:function(){},isPlainObject:function(e){var t,n;return!(!e||"[object Object]"!==o.call(e))&&(!(t=r(e))||"function"==typeof(n=v.call(t,"constructor")&&t.constructor)&&a.call(n)===l)},isEmptyObject:function(e){var t;for(t in e)return!1;return!0},globalEval:function(e,t,n){b(e,{nonce:t&&t.nonce},n)},each:function(e,t){var n,r=0;if(p(e)){for(n=e.length;r<n;r++)if(!1===t.call(e[r],r,e[r]))break}else for(r in e)if(!1===t.call(e[r],r,e[r]))break;return e},makeArray:function(e,t){var n=t||[];return null!=e&&(p(Object(e))?S.merge(n,"string"==typeof e?[e]:e):u.call(n,e)),n},inArray:function(e,t,n){return null==t?-1:i.call(t,e,n)},merge:function(e,t){for(var n=+t.length,r=0,i=e.length;r<n;r++)e[i++]=t[r];return e.length=i,e},grep:function(e,t,n){for(var r=[],i=0,o=e.length,a=!n;i<o;i++)!t(e[i],i)!==a&&r.push(e[i]);return r},map:function(e,t,n){var r,i,o=0,a=[];if(p(e))for(r=e.length;o<r;o++)null!=(i=t(e[o],o,n))&&a.push(i);else for(o in e)null!=(i=t(e[o],o,n))&&a.push(i);return g(a)},guid:1,support:y}),"function"==typeof Symbol&&(S.fn[Symbol.iterator]=t[Symbol.iterator]),S.each("Boolean Number String Function Array Date RegExp Object Error Symbol".split(" "),function(e,t){n["[object "+t+"]"]=t.toLowerCase()});var d=function(n){var e,d,b,o,i,h,f,g,w,u,l,T,C,a,E,v,s,c,y,S="sizzle"+1*new Date,p=n.document,k=0,r=0,m=ue(),x=ue(),A=ue(),N=ue(),D=function(e,t){return e===t&&(l=!0),0},j={}.hasOwnProperty,t=[],q=t.pop,L=t.push,H=t.push,O=t.slice,P=function(e,t){for(var n=0,r=e.length;n<r;n++)if(e[n]===t)return n;return-1},R="checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped",M="[\\x20\\t\\r\\n\\f]",I="(?:\\\\[\\da-fA-F]{1,6}"+M+"?|\\\\[^\\r\\n\\f]|[\\w-]|[^\0-\\x7f])+",W="\\["+M+"*("+I+")(?:"+M+"*([*^$|!~]?=)"+M+"*(?:'((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\"|("+I+"))|)"+M+"*\\]",F=":("+I+")(?:\\((('((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\")|((?:\\\\.|[^\\\\()[\\]]|"+W+")*)|.*)\\)|)",B=new RegExp(M+"+","g"),$=new RegExp("^"+M+"+|((?:^|[^\\\\])(?:\\\\.)*)"+M+"+$","g"),_=new RegExp("^"+M+"*,"+M+"*"),z=new RegExp("^"+M+"*([>+~]|"+M+")"+M+"*"),U=new RegExp(M+"|>"),X=new RegExp(F),V=new RegExp("^"+I+"$"),G={ID:new RegExp("^#("+I+")"),CLASS:new RegExp("^\\.("+I+")"),TAG:new RegExp("^("+I+"|[*])"),ATTR:new RegExp("^"+W),PSEUDO:new RegExp("^"+F),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+M+"*(even|odd|(([+-]|)(\\d*)n|)"+M+"*(?:([+-]|)"+M+"*(\\d+)|))"+M+"*\\)|)","i"),bool:new RegExp("^(?:"+R+")$","i"),needsContext:new RegExp("^"+M+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+M+"*((?:-\\d)?\\d*)"+M+"*\\)|)(?=[^-]|$)","i")},Y=/HTML$/i,Q=/^(?:input|select|textarea|button)$/i,J=/^h\d$/i,K=/^[^{]+\{\s*\[native \w/,Z=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,ee=/[+~]/,te=new RegExp("\\\\[\\da-fA-F]{1,6}"+M+"?|\\\\([^\\r\\n\\f])","g"),ne=function(e,t){var n="0x"+e.slice(1)-65536;return t||(n<0?String.fromCharCode(n+65536):String.fromCharCode(n>>10|55296,1023&n|56320))},re=/([\0-\x1f\x7f]|^-?\d)|^-$|[^\0-\x1f\x7f-\uFFFF\w-]/g,ie=function(e,t){return t?"\0"===e?"\ufffd":e.slice(0,-1)+"\\"+e.charCodeAt(e.length-1).toString(16)+" ":"\\"+e},oe=function(){T()},ae=be(function(e){return!0===e.disabled&&"fieldset"===e.nodeName.toLowerCase()},{dir:"parentNode",next:"legend"});try{H.apply(t=O.call(p.childNodes),p.childNodes),t[p.childNodes.length].nodeType}catch(e){H={apply:t.length?function(e,t){L.apply(e,O.call(t))}:function(e,t){var n=e.length,r=0;while(e[n++]=t[r++]);e.length=n-1}}}function se(t,e,n,r){var i,o,a,s,u,l,c,f=e&&e.ownerDocument,p=e?e.nodeType:9;if(n=n||[],"string"!=typeof t||!t||1!==p&&9!==p&&11!==p)return n;if(!r&&(T(e),e=e||C,E)){if(11!==p&&(u=Z.exec(t)))if(i=u[1]){if(9===p){if(!(a=e.getElementById(i)))return n;if(a.id===i)return n.push(a),n}else if(f&&(a=f.getElementById(i))&&y(e,a)&&a.id===i)return n.push(a),n}else{if(u[2])return H.apply(n,e.getElementsByTagName(t)),n;if((i=u[3])&&d.getElementsByClassName&&e.getElementsByClassName)return H.apply(n,e.getElementsByClassName(i)),n}if(d.qsa&&!N[t+" "]&&(!v||!v.test(t))&&(1!==p||"object"!==e.nodeName.toLowerCase())){if(c=t,f=e,1===p&&(U.test(t)||z.test(t))){(f=ee.test(t)&&ye(e.parentNode)||e)===e&&d.scope||((s=e.getAttribute("id"))?s=s.replace(re,ie):e.setAttribute("id",s=S)),o=(l=h(t)).length;while(o--)l[o]=(s?"#"+s:":scope")+" "+xe(l[o]);c=l.join(",")}try{return H.apply(n,f.querySelectorAll(c)),n}catch(e){N(t,!0)}finally{s===S&&e.removeAttribute("id")}}}return g(t.replace($,"$1"),e,n,r)}function ue(){var r=[];return function e(t,n){return r.push(t+" ")>b.cacheLength&&delete e[r.shift()],e[t+" "]=n}}function le(e){return e[S]=!0,e}function ce(e){var t=C.createElement("fieldset");try{return!!e(t)}catch(e){return!1}finally{t.parentNode&&t.parentNode.removeChild(t),t=null}}function fe(e,t){var n=e.split("|"),r=n.length;while(r--)b.attrHandle[n[r]]=t}function pe(e,t){var n=t&&e,r=n&&1===e.nodeType&&1===t.nodeType&&e.sourceIndex-t.sourceIndex;if(r)return r;if(n)while(n=n.nextSibling)if(n===t)return-1;return e?1:-1}function de(t){return function(e){return"input"===e.nodeName.toLowerCase()&&e.type===t}}function he(n){return function(e){var t=e.nodeName.toLowerCase();return("input"===t||"button"===t)&&e.type===n}}function ge(t){return function(e){return"form"in e?e.parentNode&&!1===e.disabled?"label"in e?"label"in e.parentNode?e.parentNode.disabled===t:e.disabled===t:e.isDisabled===t||e.isDisabled!==!t&&ae(e)===t:e.disabled===t:"label"in e&&e.disabled===t}}function ve(a){return le(function(o){return o=+o,le(function(e,t){var n,r=a([],e.length,o),i=r.length;while(i--)e[n=r[i]]&&(e[n]=!(t[n]=e[n]))})})}function ye(e){return e&&"undefined"!=typeof e.getElementsByTagName&&e}for(e in d=se.support={},i=se.isXML=function(e){var t=e.namespaceURI,n=(e.ownerDocument||e).documentElement;return!Y.test(t||n&&n.nodeName||"HTML")},T=se.setDocument=function(e){var t,n,r=e?e.ownerDocument||e:p;return r!=C&&9===r.nodeType&&r.documentElement&&(a=(C=r).documentElement,E=!i(C),p!=C&&(n=C.defaultView)&&n.top!==n&&(n.addEventListener?n.addEventListener("unload",oe,!1):n.attachEvent&&n.attachEvent("onunload",oe)),d.scope=ce(function(e){return a.appendChild(e).appendChild(C.createElement("div")),"undefined"!=typeof e.querySelectorAll&&!e.querySelectorAll(":scope fieldset div").length}),d.attributes=ce(function(e){return e.className="i",!e.getAttribute("className")}),d.getElementsByTagName=ce(function(e){return e.appendChild(C.createComment("")),!e.getElementsByTagName("*").length}),d.getElementsByClassName=K.test(C.getElementsByClassName),d.getById=ce(function(e){return a.appendChild(e).id=S,!C.getElementsByName||!C.getElementsByName(S).length}),d.getById?(b.filter.ID=function(e){var t=e.replace(te,ne);return function(e){return e.getAttribute("id")===t}},b.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&E){var n=t.getElementById(e);return n?[n]:[]}}):(b.filter.ID=function(e){var n=e.replace(te,ne);return function(e){var t="undefined"!=typeof e.getAttributeNode&&e.getAttributeNode("id");return t&&t.value===n}},b.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&E){var n,r,i,o=t.getElementById(e);if(o){if((n=o.getAttributeNode("id"))&&n.value===e)return[o];i=t.getElementsByName(e),r=0;while(o=i[r++])if((n=o.getAttributeNode("id"))&&n.value===e)return[o]}return[]}}),b.find.TAG=d.getElementsByTagName?function(e,t){return"undefined"!=typeof t.getElementsByTagName?t.getElementsByTagName(e):d.qsa?t.querySelectorAll(e):void 0}:function(e,t){var n,r=[],i=0,o=t.getElementsByTagName(e);if("*"===e){while(n=o[i++])1===n.nodeType&&r.push(n);return r}return o},b.find.CLASS=d.getElementsByClassName&&function(e,t){if("undefined"!=typeof t.getElementsByClassName&&E)return t.getElementsByClassName(e)},s=[],v=[],(d.qsa=K.test(C.querySelectorAll))&&(ce(function(e){var t;a.appendChild(e).innerHTML="<a id='"+S+"'></a><select id='"+S+"-\r\\' msallowcapture=''><option selected=''></option></select>",e.querySelectorAll("[msallowcapture^='']").length&&v.push("[*^$]="+M+"*(?:''|\"\")"),e.querySelectorAll("[selected]").length||v.push("\\["+M+"*(?:value|"+R+")"),e.querySelectorAll("[id~="+S+"-]").length||v.push("~="),(t=C.createElement("input")).setAttribute("name",""),e.appendChild(t),e.querySelectorAll("[name='']").length||v.push("\\["+M+"*name"+M+"*="+M+"*(?:''|\"\")"),e.querySelectorAll(":checked").length||v.push(":checked"),e.querySelectorAll("a#"+S+"+*").length||v.push(".#.+[+~]"),e.querySelectorAll("\\\f"),v.push("[\\r\\n\\f]")}),ce(function(e){e.innerHTML="<a href='' disabled='disabled'></a><select disabled='disabled'><option/></select>";var t=C.createElement("input");t.setAttribute("type","hidden"),e.appendChild(t).setAttribute("name","D"),e.querySelectorAll("[name=d]").length&&v.push("name"+M+"*[*^$|!~]?="),2!==e.querySelectorAll(":enabled").length&&v.push(":enabled",":disabled"),a.appendChild(e).disabled=!0,2!==e.querySelectorAll(":disabled").length&&v.push(":enabled",":disabled"),e.querySelectorAll("*,:x"),v.push(",.*:")})),(d.matchesSelector=K.test(c=a.matches||a.webkitMatchesSelector||a.mozMatchesSelector||a.oMatchesSelector||a.msMatchesSelector))&&ce(function(e){d.disconnectedMatch=c.call(e,"*"),c.call(e,"[s!='']:x"),s.push("!=",F)}),v=v.length&&new RegExp(v.join("|")),s=s.length&&new RegExp(s.join("|")),t=K.test(a.compareDocumentPosition),y=t||K.test(a.contains)?function(e,t){var n=9===e.nodeType?e.documentElement:e,r=t&&t.parentNode;return e===r||!(!r||1!==r.nodeType||!(n.contains?n.contains(r):e.compareDocumentPosition&&16&e.compareDocumentPosition(r)))}:function(e,t){if(t)while(t=t.parentNode)if(t===e)return!0;return!1},D=t?function(e,t){if(e===t)return l=!0,0;var n=!e.compareDocumentPosition-!t.compareDocumentPosition;return n||(1&(n=(e.ownerDocument||e)==(t.ownerDocument||t)?e.compareDocumentPosition(t):1)||!d.sortDetached&&t.compareDocumentPosition(e)===n?e==C||e.ownerDocument==p&&y(p,e)?-1:t==C||t.ownerDocument==p&&y(p,t)?1:u?P(u,e)-P(u,t):0:4&n?-1:1)}:function(e,t){if(e===t)return l=!0,0;var n,r=0,i=e.parentNode,o=t.parentNode,a=[e],s=[t];if(!i||!o)return e==C?-1:t==C?1:i?-1:o?1:u?P(u,e)-P(u,t):0;if(i===o)return pe(e,t);n=e;while(n=n.parentNode)a.unshift(n);n=t;while(n=n.parentNode)s.unshift(n);while(a[r]===s[r])r++;return r?pe(a[r],s[r]):a[r]==p?-1:s[r]==p?1:0}),C},se.matches=function(e,t){return se(e,null,null,t)},se.matchesSelector=function(e,t){if(T(e),d.matchesSelector&&E&&!N[t+" "]&&(!s||!s.test(t))&&(!v||!v.test(t)))try{var n=c.call(e,t);if(n||d.disconnectedMatch||e.document&&11!==e.document.nodeType)return n}catch(e){N(t,!0)}return 0<se(t,C,null,[e]).length},se.contains=function(e,t){return(e.ownerDocument||e)!=C&&T(e),y(e,t)},se.attr=function(e,t){(e.ownerDocument||e)!=C&&T(e);var n=b.attrHandle[t.toLowerCase()],r=n&&j.call(b.attrHandle,t.toLowerCase())?n(e,t,!E):void 0;return void 0!==r?r:d.attributes||!E?e.getAttribute(t):(r=e.getAttributeNode(t))&&r.specified?r.value:null},se.escape=function(e){return(e+"").replace(re,ie)},se.error=function(e){throw new Error("Syntax error, unrecognized expression: "+e)},se.uniqueSort=function(e){var t,n=[],r=0,i=0;if(l=!d.detectDuplicates,u=!d.sortStable&&e.slice(0),e.sort(D),l){while(t=e[i++])t===e[i]&&(r=n.push(i));while(r--)e.splice(n[r],1)}return u=null,e},o=se.getText=function(e){var t,n="",r=0,i=e.nodeType;if(i){if(1===i||9===i||11===i){if("string"==typeof e.textContent)return e.textContent;for(e=e.firstChild;e;e=e.nextSibling)n+=o(e)}else if(3===i||4===i)return e.nodeValue}else while(t=e[r++])n+=o(t);return n},(b=se.selectors={cacheLength:50,createPseudo:le,match:G,attrHandle:{},find:{},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(e){return e[1]=e[1].replace(te,ne),e[3]=(e[3]||e[4]||e[5]||"").replace(te,ne),"~="===e[2]&&(e[3]=" "+e[3]+" "),e.slice(0,4)},CHILD:function(e){return e[1]=e[1].toLowerCase(),"nth"===e[1].slice(0,3)?(e[3]||se.error(e[0]),e[4]=+(e[4]?e[5]+(e[6]||1):2*("even"===e[3]||"odd"===e[3])),e[5]=+(e[7]+e[8]||"odd"===e[3])):e[3]&&se.error(e[0]),e},PSEUDO:function(e){var t,n=!e[6]&&e[2];return G.CHILD.test(e[0])?null:(e[3]?e[2]=e[4]||e[5]||"":n&&X.test(n)&&(t=h(n,!0))&&(t=n.indexOf(")",n.length-t)-n.length)&&(e[0]=e[0].slice(0,t),e[2]=n.slice(0,t)),e.slice(0,3))}},filter:{TAG:function(e){var t=e.replace(te,ne).toLowerCase();return"*"===e?function(){return!0}:function(e){return e.nodeName&&e.nodeName.toLowerCase()===t}},CLASS:function(e){var t=m[e+" "];return t||(t=new RegExp("(^|"+M+")"+e+"("+M+"|$)"))&&m(e,function(e){return t.test("string"==typeof e.className&&e.className||"undefined"!=typeof e.getAttribute&&e.getAttribute("class")||"")})},ATTR:function(n,r,i){return function(e){var t=se.attr(e,n);return null==t?"!="===r:!r||(t+="","="===r?t===i:"!="===r?t!==i:"^="===r?i&&0===t.indexOf(i):"*="===r?i&&-1<t.indexOf(i):"$="===r?i&&t.slice(-i.length)===i:"~="===r?-1<(" "+t.replace(B," ")+" ").indexOf(i):"|="===r&&(t===i||t.slice(0,i.length+1)===i+"-"))}},CHILD:function(h,e,t,g,v){var y="nth"!==h.slice(0,3),m="last"!==h.slice(-4),x="of-type"===e;return 1===g&&0===v?function(e){return!!e.parentNode}:function(e,t,n){var r,i,o,a,s,u,l=y!==m?"nextSibling":"previousSibling",c=e.parentNode,f=x&&e.nodeName.toLowerCase(),p=!n&&!x,d=!1;if(c){if(y){while(l){a=e;while(a=a[l])if(x?a.nodeName.toLowerCase()===f:1===a.nodeType)return!1;u=l="only"===h&&!u&&"nextSibling"}return!0}if(u=[m?c.firstChild:c.lastChild],m&&p){d=(s=(r=(i=(o=(a=c)[S]||(a[S]={}))[a.uniqueID]||(o[a.uniqueID]={}))[h]||[])[0]===k&&r[1])&&r[2],a=s&&c.childNodes[s];while(a=++s&&a&&a[l]||(d=s=0)||u.pop())if(1===a.nodeType&&++d&&a===e){i[h]=[k,s,d];break}}else if(p&&(d=s=(r=(i=(o=(a=e)[S]||(a[S]={}))[a.uniqueID]||(o[a.uniqueID]={}))[h]||[])[0]===k&&r[1]),!1===d)while(a=++s&&a&&a[l]||(d=s=0)||u.pop())if((x?a.nodeName.toLowerCase()===f:1===a.nodeType)&&++d&&(p&&((i=(o=a[S]||(a[S]={}))[a.uniqueID]||(o[a.uniqueID]={}))[h]=[k,d]),a===e))break;return(d-=v)===g||d%g==0&&0<=d/g}}},PSEUDO:function(e,o){var t,a=b.pseudos[e]||b.setFilters[e.toLowerCase()]||se.error("unsupported pseudo: "+e);return a[S]?a(o):1<a.length?(t=[e,e,"",o],b.setFilters.hasOwnProperty(e.toLowerCase())?le(function(e,t){var n,r=a(e,o),i=r.length;while(i--)e[n=P(e,r[i])]=!(t[n]=r[i])}):function(e){return a(e,0,t)}):a}},pseudos:{not:le(function(e){var r=[],i=[],s=f(e.replace($,"$1"));return s[S]?le(function(e,t,n,r){var i,o=s(e,null,r,[]),a=e.length;while(a--)(i=o[a])&&(e[a]=!(t[a]=i))}):function(e,t,n){return r[0]=e,s(r,null,n,i),r[0]=null,!i.pop()}}),has:le(function(t){return function(e){return 0<se(t,e).length}}),contains:le(function(t){return t=t.replace(te,ne),function(e){return-1<(e.textContent||o(e)).indexOf(t)}}),lang:le(function(n){return V.test(n||"")||se.error("unsupported lang: "+n),n=n.replace(te,ne).toLowerCase(),function(e){var t;do{if(t=E?e.lang:e.getAttribute("xml:lang")||e.getAttribute("lang"))return(t=t.toLowerCase())===n||0===t.indexOf(n+"-")}while((e=e.parentNode)&&1===e.nodeType);return!1}}),target:function(e){var t=n.location&&n.location.hash;return t&&t.slice(1)===e.id},root:function(e){return e===a},focus:function(e){return e===C.activeElement&&(!C.hasFocus||C.hasFocus())&&!!(e.type||e.href||~e.tabIndex)},enabled:ge(!1),disabled:ge(!0),checked:function(e){var t=e.nodeName.toLowerCase();return"input"===t&&!!e.checked||"option"===t&&!!e.selected},selected:function(e){return e.parentNode&&e.parentNode.selectedIndex,!0===e.selected},empty:function(e){for(e=e.firstChild;e;e=e.nextSibling)if(e.nodeType<6)return!1;return!0},parent:function(e){return!b.pseudos.empty(e)},header:function(e){return J.test(e.nodeName)},input:function(e){return Q.test(e.nodeName)},button:function(e){var t=e.nodeName.toLowerCase();return"input"===t&&"button"===e.type||"button"===t},text:function(e){var t;return"input"===e.nodeName.toLowerCase()&&"text"===e.type&&(null==(t=e.getAttribute("type"))||"text"===t.toLowerCase())},first:ve(function(){return[0]}),last:ve(function(e,t){return[t-1]}),eq:ve(function(e,t,n){return[n<0?n+t:n]}),even:ve(function(e,t){for(var n=0;n<t;n+=2)e.push(n);return e}),odd:ve(function(e,t){for(var n=1;n<t;n+=2)e.push(n);return e}),lt:ve(function(e,t,n){for(var r=n<0?n+t:t<n?t:n;0<=--r;)e.push(r);return e}),gt:ve(function(e,t,n){for(var r=n<0?n+t:n;++r<t;)e.push(r);return e})}}).pseudos.nth=b.pseudos.eq,{radio:!0,checkbox:!0,file:!0,password:!0,image:!0})b.pseudos[e]=de(e);for(e in{submit:!0,reset:!0})b.pseudos[e]=he(e);function me(){}function xe(e){for(var t=0,n=e.length,r="";t<n;t++)r+=e[t].value;return r}function be(s,e,t){var u=e.dir,l=e.next,c=l||u,f=t&&"parentNode"===c,p=r++;return e.first?function(e,t,n){while(e=e[u])if(1===e.nodeType||f)return s(e,t,n);return!1}:function(e,t,n){var r,i,o,a=[k,p];if(n){while(e=e[u])if((1===e.nodeType||f)&&s(e,t,n))return!0}else while(e=e[u])if(1===e.nodeType||f)if(i=(o=e[S]||(e[S]={}))[e.uniqueID]||(o[e.uniqueID]={}),l&&l===e.nodeName.toLowerCase())e=e[u]||e;else{if((r=i[c])&&r[0]===k&&r[1]===p)return a[2]=r[2];if((i[c]=a)[2]=s(e,t,n))return!0}return!1}}function we(i){return 1<i.length?function(e,t,n){var r=i.length;while(r--)if(!i[r](e,t,n))return!1;return!0}:i[0]}function Te(e,t,n,r,i){for(var o,a=[],s=0,u=e.length,l=null!=t;s<u;s++)(o=e[s])&&(n&&!n(o,r,i)||(a.push(o),l&&t.push(s)));return a}function Ce(d,h,g,v,y,e){return v&&!v[S]&&(v=Ce(v)),y&&!y[S]&&(y=Ce(y,e)),le(function(e,t,n,r){var i,o,a,s=[],u=[],l=t.length,c=e||function(e,t,n){for(var r=0,i=t.length;r<i;r++)se(e,t[r],n);return n}(h||"*",n.nodeType?[n]:n,[]),f=!d||!e&&h?c:Te(c,s,d,n,r),p=g?y||(e?d:l||v)?[]:t:f;if(g&&g(f,p,n,r),v){i=Te(p,u),v(i,[],n,r),o=i.length;while(o--)(a=i[o])&&(p[u[o]]=!(f[u[o]]=a))}if(e){if(y||d){if(y){i=[],o=p.length;while(o--)(a=p[o])&&i.push(f[o]=a);y(null,p=[],i,r)}o=p.length;while(o--)(a=p[o])&&-1<(i=y?P(e,a):s[o])&&(e[i]=!(t[i]=a))}}else p=Te(p===t?p.splice(l,p.length):p),y?y(null,t,p,r):H.apply(t,p)})}function Ee(e){for(var i,t,n,r=e.length,o=b.relative[e[0].type],a=o||b.relative[" "],s=o?1:0,u=be(function(e){return e===i},a,!0),l=be(function(e){return-1<P(i,e)},a,!0),c=[function(e,t,n){var r=!o&&(n||t!==w)||((i=t).nodeType?u(e,t,n):l(e,t,n));return i=null,r}];s<r;s++)if(t=b.relative[e[s].type])c=[be(we(c),t)];else{if((t=b.filter[e[s].type].apply(null,e[s].matches))[S]){for(n=++s;n<r;n++)if(b.relative[e[n].type])break;return Ce(1<s&&we(c),1<s&&xe(e.slice(0,s-1).concat({value:" "===e[s-2].type?"*":""})).replace($,"$1"),t,s<n&&Ee(e.slice(s,n)),n<r&&Ee(e=e.slice(n)),n<r&&xe(e))}c.push(t)}return we(c)}return me.prototype=b.filters=b.pseudos,b.setFilters=new me,h=se.tokenize=function(e,t){var n,r,i,o,a,s,u,l=x[e+" "];if(l)return t?0:l.slice(0);a=e,s=[],u=b.preFilter;while(a){for(o in n&&!(r=_.exec(a))||(r&&(a=a.slice(r[0].length)||a),s.push(i=[])),n=!1,(r=z.exec(a))&&(n=r.shift(),i.push({value:n,type:r[0].replace($," ")}),a=a.slice(n.length)),b.filter)!(r=G[o].exec(a))||u[o]&&!(r=u[o](r))||(n=r.shift(),i.push({value:n,type:o,matches:r}),a=a.slice(n.length));if(!n)break}return t?a.length:a?se.error(e):x(e,s).slice(0)},f=se.compile=function(e,t){var n,v,y,m,x,r,i=[],o=[],a=A[e+" "];if(!a){t||(t=h(e)),n=t.length;while(n--)(a=Ee(t[n]))[S]?i.push(a):o.push(a);(a=A(e,(v=o,m=0<(y=i).length,x=0<v.length,r=function(e,t,n,r,i){var o,a,s,u=0,l="0",c=e&&[],f=[],p=w,d=e||x&&b.find.TAG("*",i),h=k+=null==p?1:Math.random()||.1,g=d.length;for(i&&(w=t==C||t||i);l!==g&&null!=(o=d[l]);l++){if(x&&o){a=0,t||o.ownerDocument==C||(T(o),n=!E);while(s=v[a++])if(s(o,t||C,n)){r.push(o);break}i&&(k=h)}m&&((o=!s&&o)&&u--,e&&c.push(o))}if(u+=l,m&&l!==u){a=0;while(s=y[a++])s(c,f,t,n);if(e){if(0<u)while(l--)c[l]||f[l]||(f[l]=q.call(r));f=Te(f)}H.apply(r,f),i&&!e&&0<f.length&&1<u+y.length&&se.uniqueSort(r)}return i&&(k=h,w=p),c},m?le(r):r))).selector=e}return a},g=se.select=function(e,t,n,r){var i,o,a,s,u,l="function"==typeof e&&e,c=!r&&h(e=l.selector||e);if(n=n||[],1===c.length){if(2<(o=c[0]=c[0].slice(0)).length&&"ID"===(a=o[0]).type&&9===t.nodeType&&E&&b.relative[o[1].type]){if(!(t=(b.find.ID(a.matches[0].replace(te,ne),t)||[])[0]))return n;l&&(t=t.parentNode),e=e.slice(o.shift().value.length)}i=G.needsContext.test(e)?0:o.length;while(i--){if(a=o[i],b.relative[s=a.type])break;if((u=b.find[s])&&(r=u(a.matches[0].replace(te,ne),ee.test(o[0].type)&&ye(t.parentNode)||t))){if(o.splice(i,1),!(e=r.length&&xe(o)))return H.apply(n,r),n;break}}}return(l||f(e,c))(r,t,!E,n,!t||ee.test(e)&&ye(t.parentNode)||t),n},d.sortStable=S.split("").sort(D).join("")===S,d.detectDuplicates=!!l,T(),d.sortDetached=ce(function(e){return 1&e.compareDocumentPosition(C.createElement("fieldset"))}),ce(function(e){return e.innerHTML="<a href='#'></a>","#"===e.firstChild.getAttribute("href")})||fe("type|href|height|width",function(e,t,n){if(!n)return e.getAttribute(t,"type"===t.toLowerCase()?1:2)}),d.attributes&&ce(function(e){return e.innerHTML="<input/>",e.firstChild.setAttribute("value",""),""===e.firstChild.getAttribute("value")})||fe("value",function(e,t,n){if(!n&&"input"===e.nodeName.toLowerCase())return e.defaultValue}),ce(function(e){return null==e.getAttribute("disabled")})||fe(R,function(e,t,n){var r;if(!n)return!0===e[t]?t.toLowerCase():(r=e.getAttributeNode(t))&&r.specified?r.value:null}),se}(C);S.find=d,S.expr=d.selectors,S.expr[":"]=S.expr.pseudos,S.uniqueSort=S.unique=d.uniqueSort,S.text=d.getText,S.isXMLDoc=d.isXML,S.contains=d.contains,S.escapeSelector=d.escape;var h=function(e,t,n){var r=[],i=void 0!==n;while((e=e[t])&&9!==e.nodeType)if(1===e.nodeType){if(i&&S(e).is(n))break;r.push(e)}return r},T=function(e,t){for(var n=[];e;e=e.nextSibling)1===e.nodeType&&e!==t&&n.push(e);return n},k=S.expr.match.needsContext;function A(e,t){return e.nodeName&&e.nodeName.toLowerCase()===t.toLowerCase()}var N=/^<([a-z][^\/\0>:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i;function D(e,n,r){return m(n)?S.grep(e,function(e,t){return!!n.call(e,t,e)!==r}):n.nodeType?S.grep(e,function(e){return e===n!==r}):"string"!=typeof n?S.grep(e,function(e){return-1<i.call(n,e)!==r}):S.filter(n,e,r)}S.filter=function(e,t,n){var r=t[0];return n&&(e=":not("+e+")"),1===t.length&&1===r.nodeType?S.find.matchesSelector(r,e)?[r]:[]:S.find.matches(e,S.grep(t,function(e){return 1===e.nodeType}))},S.fn.extend({find:function(e){var t,n,r=this.length,i=this;if("string"!=typeof e)return this.pushStack(S(e).filter(function(){for(t=0;t<r;t++)if(S.contains(i[t],this))return!0}));for(n=this.pushStack([]),t=0;t<r;t++)S.find(e,i[t],n);return 1<r?S.uniqueSort(n):n},filter:function(e){return this.pushStack(D(this,e||[],!1))},not:function(e){return this.pushStack(D(this,e||[],!0))},is:function(e){return!!D(this,"string"==typeof e&&k.test(e)?S(e):e||[],!1).length}});var j,q=/^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]+))$/;(S.fn.init=function(e,t,n){var r,i;if(!e)return this;if(n=n||j,"string"==typeof e){if(!(r="<"===e[0]&&">"===e[e.length-1]&&3<=e.length?[null,e,null]:q.exec(e))||!r[1]&&t)return!t||t.jquery?(t||n).find(e):this.constructor(t).find(e);if(r[1]){if(t=t instanceof S?t[0]:t,S.merge(this,S.parseHTML(r[1],t&&t.nodeType?t.ownerDocument||t:E,!0)),N.test(r[1])&&S.isPlainObject(t))for(r in t)m(this[r])?this[r](t[r]):this.attr(r,t[r]);return this}return(i=E.getElementById(r[2]))&&(this[0]=i,this.length=1),this}return e.nodeType?(this[0]=e,this.length=1,this):m(e)?void 0!==n.ready?n.ready(e):e(S):S.makeArray(e,this)}).prototype=S.fn,j=S(E);var L=/^(?:parents|prev(?:Until|All))/,H={children:!0,contents:!0,next:!0,prev:!0};function O(e,t){while((e=e[t])&&1!==e.nodeType);return e}S.fn.extend({has:function(e){var t=S(e,this),n=t.length;return this.filter(function(){for(var e=0;e<n;e++)if(S.contains(this,t[e]))return!0})},closest:function(e,t){var n,r=0,i=this.length,o=[],a="string"!=typeof e&&S(e);if(!k.test(e))for(;r<i;r++)for(n=this[r];n&&n!==t;n=n.parentNode)if(n.nodeType<11&&(a?-1<a.index(n):1===n.nodeType&&S.find.matchesSelector(n,e))){o.push(n);break}return this.pushStack(1<o.length?S.uniqueSort(o):o)},index:function(e){return e?"string"==typeof e?i.call(S(e),this[0]):i.call(this,e.jquery?e[0]:e):this[0]&&this[0].parentNode?this.first().prevAll().length:-1},add:function(e,t){return this.pushStack(S.uniqueSort(S.merge(this.get(),S(e,t))))},addBack:function(e){return this.add(null==e?this.prevObject:this.prevObject.filter(e))}}),S.each({parent:function(e){var t=e.parentNode;return t&&11!==t.nodeType?t:null},parents:function(e){return h(e,"parentNode")},parentsUntil:function(e,t,n){return h(e,"parentNode",n)},next:function(e){return O(e,"nextSibling")},prev:function(e){return O(e,"previousSibling")},nextAll:function(e){return h(e,"nextSibling")},prevAll:function(e){return h(e,"previousSibling")},nextUntil:function(e,t,n){return h(e,"nextSibling",n)},prevUntil:function(e,t,n){return h(e,"previousSibling",n)},siblings:function(e){return T((e.parentNode||{}).firstChild,e)},children:function(e){return T(e.firstChild)},contents:function(e){return null!=e.contentDocument&&r(e.contentDocument)?e.contentDocument:(A(e,"template")&&(e=e.content||e),S.merge([],e.childNodes))}},function(r,i){S.fn[r]=function(e,t){var n=S.map(this,i,e);return"Until"!==r.slice(-5)&&(t=e),t&&"string"==typeof t&&(n=S.filter(t,n)),1<this.length&&(H[r]||S.uniqueSort(n),L.test(r)&&n.reverse()),this.pushStack(n)}});var P=/[^\x20\t\r\n\f]+/g;function R(e){return e}function M(e){throw e}function I(e,t,n,r){var i;try{e&&m(i=e.promise)?i.call(e).done(t).fail(n):e&&m(i=e.then)?i.call(e,t,n):t.apply(void 0,[e].slice(r))}catch(e){n.apply(void 0,[e])}}S.Callbacks=function(r){var e,n;r="string"==typeof r?(e=r,n={},S.each(e.match(P)||[],function(e,t){n[t]=!0}),n):S.extend({},r);var i,t,o,a,s=[],u=[],l=-1,c=function(){for(a=a||r.once,o=i=!0;u.length;l=-1){t=u.shift();while(++l<s.length)!1===s[l].apply(t[0],t[1])&&r.stopOnFalse&&(l=s.length,t=!1)}r.memory||(t=!1),i=!1,a&&(s=t?[]:"")},f={add:function(){return s&&(t&&!i&&(l=s.length-1,u.push(t)),function n(e){S.each(e,function(e,t){m(t)?r.unique&&f.has(t)||s.push(t):t&&t.length&&"string"!==w(t)&&n(t)})}(arguments),t&&!i&&c()),this},remove:function(){return S.each(arguments,function(e,t){var n;while(-1<(n=S.inArray(t,s,n)))s.splice(n,1),n<=l&&l--}),this},has:function(e){return e?-1<S.inArray(e,s):0<s.length},empty:function(){return s&&(s=[]),this},disable:function(){return a=u=[],s=t="",this},disabled:function(){return!s},lock:function(){return a=u=[],t||i||(s=t=""),this},locked:function(){return!!a},fireWith:function(e,t){return a||(t=[e,(t=t||[]).slice?t.slice():t],u.push(t),i||c()),this},fire:function(){return f.fireWith(this,arguments),this},fired:function(){return!!o}};return f},S.extend({Deferred:function(e){var o=[["notify","progress",S.Callbacks("memory"),S.Callbacks("memory"),2],["resolve","done",S.Callbacks("once memory"),S.Callbacks("once memory"),0,"resolved"],["reject","fail",S.Callbacks("once memory"),S.Callbacks("once memory"),1,"rejected"]],i="pending",a={state:function(){return i},always:function(){return s.done(arguments).fail(arguments),this},"catch":function(e){return a.then(null,e)},pipe:function(){var i=arguments;return S.Deferred(function(r){S.each(o,function(e,t){var n=m(i[t[4]])&&i[t[4]];s[t[1]](function(){var e=n&&n.apply(this,arguments);e&&m(e.promise)?e.promise().progress(r.notify).done(r.resolve).fail(r.reject):r[t[0]+"With"](this,n?[e]:arguments)})}),i=null}).promise()},then:function(t,n,r){var u=0;function l(i,o,a,s){return function(){var n=this,r=arguments,e=function(){var e,t;if(!(i<u)){if((e=a.apply(n,r))===o.promise())throw new TypeError("Thenable self-resolution");t=e&&("object"==typeof e||"function"==typeof e)&&e.then,m(t)?s?t.call(e,l(u,o,R,s),l(u,o,M,s)):(u++,t.call(e,l(u,o,R,s),l(u,o,M,s),l(u,o,R,o.notifyWith))):(a!==R&&(n=void 0,r=[e]),(s||o.resolveWith)(n,r))}},t=s?e:function(){try{e()}catch(e){S.Deferred.exceptionHook&&S.Deferred.exceptionHook(e,t.stackTrace),u<=i+1&&(a!==M&&(n=void 0,r=[e]),o.rejectWith(n,r))}};i?t():(S.Deferred.getStackHook&&(t.stackTrace=S.Deferred.getStackHook()),C.setTimeout(t))}}return S.Deferred(function(e){o[0][3].add(l(0,e,m(r)?r:R,e.notifyWith)),o[1][3].add(l(0,e,m(t)?t:R)),o[2][3].add(l(0,e,m(n)?n:M))}).promise()},promise:function(e){return null!=e?S.extend(e,a):a}},s={};return S.each(o,function(e,t){var n=t[2],r=t[5];a[t[1]]=n.add,r&&n.add(function(){i=r},o[3-e][2].disable,o[3-e][3].disable,o[0][2].lock,o[0][3].lock),n.add(t[3].fire),s[t[0]]=function(){return s[t[0]+"With"](this===s?void 0:this,arguments),this},s[t[0]+"With"]=n.fireWith}),a.promise(s),e&&e.call(s,s),s},when:function(e){var n=arguments.length,t=n,r=Array(t),i=s.call(arguments),o=S.Deferred(),a=function(t){return function(e){r[t]=this,i[t]=1<arguments.length?s.call(arguments):e,--n||o.resolveWith(r,i)}};if(n<=1&&(I(e,o.done(a(t)).resolve,o.reject,!n),"pending"===o.state()||m(i[t]&&i[t].then)))return o.then();while(t--)I(i[t],a(t),o.reject);return o.promise()}});var W=/^(Eval|Internal|Range|Reference|Syntax|Type|URI)Error$/;S.Deferred.exceptionHook=function(e,t){C.console&&C.console.warn&&e&&W.test(e.name)&&C.console.warn("jQuery.Deferred exception: "+e.message,e.stack,t)},S.readyException=function(e){C.setTimeout(function(){throw e})};var F=S.Deferred();function B(){E.removeEventListener("DOMContentLoaded",B),C.removeEventListener("load",B),S.ready()}S.fn.ready=function(e){return F.then(e)["catch"](function(e){S.readyException(e)}),this},S.extend({isReady:!1,readyWait:1,ready:function(e){(!0===e?--S.readyWait:S.isReady)||(S.isReady=!0)!==e&&0<--S.readyWait||F.resolveWith(E,[S])}}),S.ready.then=F.then,"complete"===E.readyState||"loading"!==E.readyState&&!E.documentElement.doScroll?C.setTimeout(S.ready):(E.addEventListener("DOMContentLoaded",B),C.addEventListener("load",B));var $=function(e,t,n,r,i,o,a){var s=0,u=e.length,l=null==n;if("object"===w(n))for(s in i=!0,n)$(e,t,s,n[s],!0,o,a);else if(void 0!==r&&(i=!0,m(r)||(a=!0),l&&(a?(t.call(e,r),t=null):(l=t,t=function(e,t,n){return l.call(S(e),n)})),t))for(;s<u;s++)t(e[s],n,a?r:r.call(e[s],s,t(e[s],n)));return i?e:l?t.call(e):u?t(e[0],n):o},_=/^-ms-/,z=/-([a-z])/g;function U(e,t){return t.toUpperCase()}function X(e){return e.replace(_,"ms-").replace(z,U)}var V=function(e){return 1===e.nodeType||9===e.nodeType||!+e.nodeType};function G(){this.expando=S.expando+G.uid++}G.uid=1,G.prototype={cache:function(e){var t=e[this.expando];return t||(t={},V(e)&&(e.nodeType?e[this.expando]=t:Object.defineProperty(e,this.expando,{value:t,configurable:!0}))),t},set:function(e,t,n){var r,i=this.cache(e);if("string"==typeof t)i[X(t)]=n;else for(r in t)i[X(r)]=t[r];return i},get:function(e,t){return void 0===t?this.cache(e):e[this.expando]&&e[this.expando][X(t)]},access:function(e,t,n){return void 0===t||t&&"string"==typeof t&&void 0===n?this.get(e,t):(this.set(e,t,n),void 0!==n?n:t)},remove:function(e,t){var n,r=e[this.expando];if(void 0!==r){if(void 0!==t){n=(t=Array.isArray(t)?t.map(X):(t=X(t))in r?[t]:t.match(P)||[]).length;while(n--)delete r[t[n]]}(void 0===t||S.isEmptyObject(r))&&(e.nodeType?e[this.expando]=void 0:delete e[this.expando])}},hasData:function(e){var t=e[this.expando];return void 0!==t&&!S.isEmptyObject(t)}};var Y=new G,Q=new G,J=/^(?:\{[\w\W]*\}|\[[\w\W]*\])$/,K=/[A-Z]/g;function Z(e,t,n){var r,i;if(void 0===n&&1===e.nodeType)if(r="data-"+t.replace(K,"-$&").toLowerCase(),"string"==typeof(n=e.getAttribute(r))){try{n="true"===(i=n)||"false"!==i&&("null"===i?null:i===+i+""?+i:J.test(i)?JSON.parse(i):i)}catch(e){}Q.set(e,t,n)}else n=void 0;return n}S.extend({hasData:function(e){return Q.hasData(e)||Y.hasData(e)},data:function(e,t,n){return Q.access(e,t,n)},removeData:function(e,t){Q.remove(e,t)},_data:function(e,t,n){return Y.access(e,t,n)},_removeData:function(e,t){Y.remove(e,t)}}),S.fn.extend({data:function(n,e){var t,r,i,o=this[0],a=o&&o.attributes;if(void 0===n){if(this.length&&(i=Q.get(o),1===o.nodeType&&!Y.get(o,"hasDataAttrs"))){t=a.length;while(t--)a[t]&&0===(r=a[t].name).indexOf("data-")&&(r=X(r.slice(5)),Z(o,r,i[r]));Y.set(o,"hasDataAttrs",!0)}return i}return"object"==typeof n?this.each(function(){Q.set(this,n)}):$(this,function(e){var t;if(o&&void 0===e)return void 0!==(t=Q.get(o,n))?t:void 0!==(t=Z(o,n))?t:void 0;this.each(function(){Q.set(this,n,e)})},null,e,1<arguments.length,null,!0)},removeData:function(e){return this.each(function(){Q.remove(this,e)})}}),S.extend({queue:function(e,t,n){var r;if(e)return t=(t||"fx")+"queue",r=Y.get(e,t),n&&(!r||Array.isArray(n)?r=Y.access(e,t,S.makeArray(n)):r.push(n)),r||[]},dequeue:function(e,t){t=t||"fx";var n=S.queue(e,t),r=n.length,i=n.shift(),o=S._queueHooks(e,t);"inprogress"===i&&(i=n.shift(),r--),i&&("fx"===t&&n.unshift("inprogress"),delete o.stop,i.call(e,function(){S.dequeue(e,t)},o)),!r&&o&&o.empty.fire()},_queueHooks:function(e,t){var n=t+"queueHooks";return Y.get(e,n)||Y.access(e,n,{empty:S.Callbacks("once memory").add(function(){Y.remove(e,[t+"queue",n])})})}}),S.fn.extend({queue:function(t,n){var e=2;return"string"!=typeof t&&(n=t,t="fx",e--),arguments.length<e?S.queue(this[0],t):void 0===n?this:this.each(function(){var e=S.queue(this,t,n);S._queueHooks(this,t),"fx"===t&&"inprogress"!==e[0]&&S.dequeue(this,t)})},dequeue:function(e){return this.each(function(){S.dequeue(this,e)})},clearQueue:function(e){return this.queue(e||"fx",[])},promise:function(e,t){var n,r=1,i=S.Deferred(),o=this,a=this.length,s=function(){--r||i.resolveWith(o,[o])};"string"!=typeof e&&(t=e,e=void 0),e=e||"fx";while(a--)(n=Y.get(o[a],e+"queueHooks"))&&n.empty&&(r++,n.empty.add(s));return s(),i.promise(t)}});var ee=/[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/.source,te=new RegExp("^(?:([+-])=|)("+ee+")([a-z%]*)$","i"),ne=["Top","Right","Bottom","Left"],re=E.documentElement,ie=function(e){return S.contains(e.ownerDocument,e)},oe={composed:!0};re.getRootNode&&(ie=function(e){return S.contains(e.ownerDocument,e)||e.getRootNode(oe)===e.ownerDocument});var ae=function(e,t){return"none"===(e=t||e).style.display||""===e.style.display&&ie(e)&&"none"===S.css(e,"display")};function se(e,t,n,r){var i,o,a=20,s=r?function(){return r.cur()}:function(){return S.css(e,t,"")},u=s(),l=n&&n[3]||(S.cssNumber[t]?"":"px"),c=e.nodeType&&(S.cssNumber[t]||"px"!==l&&+u)&&te.exec(S.css(e,t));if(c&&c[3]!==l){u/=2,l=l||c[3],c=+u||1;while(a--)S.style(e,t,c+l),(1-o)*(1-(o=s()/u||.5))<=0&&(a=0),c/=o;c*=2,S.style(e,t,c+l),n=n||[]}return n&&(c=+c||+u||0,i=n[1]?c+(n[1]+1)*n[2]:+n[2],r&&(r.unit=l,r.start=c,r.end=i)),i}var ue={};function le(e,t){for(var n,r,i,o,a,s,u,l=[],c=0,f=e.length;c<f;c++)(r=e[c]).style&&(n=r.style.display,t?("none"===n&&(l[c]=Y.get(r,"display")||null,l[c]||(r.style.display="")),""===r.style.display&&ae(r)&&(l[c]=(u=a=o=void 0,a=(i=r).ownerDocument,s=i.nodeName,(u=ue[s])||(o=a.body.appendChild(a.createElement(s)),u=S.css(o,"display"),o.parentNode.removeChild(o),"none"===u&&(u="block"),ue[s]=u)))):"none"!==n&&(l[c]="none",Y.set(r,"display",n)));for(c=0;c<f;c++)null!=l[c]&&(e[c].style.display=l[c]);return e}S.fn.extend({show:function(){return le(this,!0)},hide:function(){return le(this)},toggle:function(e){return"boolean"==typeof e?e?this.show():this.hide():this.each(function(){ae(this)?S(this).show():S(this).hide()})}});var ce,fe,pe=/^(?:checkbox|radio)$/i,de=/<([a-z][^\/\0>\x20\t\r\n\f]*)/i,he=/^$|^module$|\/(?:java|ecma)script/i;ce=E.createDocumentFragment().appendChild(E.createElement("div")),(fe=E.createElement("input")).setAttribute("type","radio"),fe.setAttribute("checked","checked"),fe.setAttribute("name","t"),ce.appendChild(fe),y.checkClone=ce.cloneNode(!0).cloneNode(!0).lastChild.checked,ce.innerHTML="<textarea>x</textarea>",y.noCloneChecked=!!ce.cloneNode(!0).lastChild.defaultValue,ce.innerHTML="<option></option>",y.option=!!ce.lastChild;var ge={thead:[1,"<table>","</table>"],col:[2,"<table><colgroup>","</colgroup></table>"],tr:[2,"<table><tbody>","</tbody></table>"],td:[3,"<table><tbody><tr>","</tr></tbody></table>"],_default:[0,"",""]};function ve(e,t){var n;return n="undefined"!=typeof e.getElementsByTagName?e.getElementsByTagName(t||"*"):"undefined"!=typeof e.querySelectorAll?e.querySelectorAll(t||"*"):[],void 0===t||t&&A(e,t)?S.merge([e],n):n}function ye(e,t){for(var n=0,r=e.length;n<r;n++)Y.set(e[n],"globalEval",!t||Y.get(t[n],"globalEval"))}ge.tbody=ge.tfoot=ge.colgroup=ge.caption=ge.thead,ge.th=ge.td,y.option||(ge.optgroup=ge.option=[1,"<select multiple='multiple'>","</select>"]);var me=/<|&#?\w+;/;function xe(e,t,n,r,i){for(var o,a,s,u,l,c,f=t.createDocumentFragment(),p=[],d=0,h=e.length;d<h;d++)if((o=e[d])||0===o)if("object"===w(o))S.merge(p,o.nodeType?[o]:o);else if(me.test(o)){a=a||f.appendChild(t.createElement("div")),s=(de.exec(o)||["",""])[1].toLowerCase(),u=ge[s]||ge._default,a.innerHTML=u[1]+S.htmlPrefilter(o)+u[2],c=u[0];while(c--)a=a.lastChild;S.merge(p,a.childNodes),(a=f.firstChild).textContent=""}else p.push(t.createTextNode(o));f.textContent="",d=0;while(o=p[d++])if(r&&-1<S.inArray(o,r))i&&i.push(o);else if(l=ie(o),a=ve(f.appendChild(o),"script"),l&&ye(a),n){c=0;while(o=a[c++])he.test(o.type||"")&&n.push(o)}return f}var be=/^key/,we=/^(?:mouse|pointer|contextmenu|drag|drop)|click/,Te=/^([^.]*)(?:\.(.+)|)/;function Ce(){return!0}function Ee(){return!1}function Se(e,t){return e===function(){try{return E.activeElement}catch(e){}}()==("focus"===t)}function ke(e,t,n,r,i,o){var a,s;if("object"==typeof t){for(s in"string"!=typeof n&&(r=r||n,n=void 0),t)ke(e,s,n,r,t[s],o);return e}if(null==r&&null==i?(i=n,r=n=void 0):null==i&&("string"==typeof n?(i=r,r=void 0):(i=r,r=n,n=void 0)),!1===i)i=Ee;else if(!i)return e;return 1===o&&(a=i,(i=function(e){return S().off(e),a.apply(this,arguments)}).guid=a.guid||(a.guid=S.guid++)),e.each(function(){S.event.add(this,t,i,r,n)})}function Ae(e,i,o){o?(Y.set(e,i,!1),S.event.add(e,i,{namespace:!1,handler:function(e){var t,n,r=Y.get(this,i);if(1&e.isTrigger&&this[i]){if(r.length)(S.event.special[i]||{}).delegateType&&e.stopPropagation();else if(r=s.call(arguments),Y.set(this,i,r),t=o(this,i),this[i](),r!==(n=Y.get(this,i))||t?Y.set(this,i,!1):n={},r!==n)return e.stopImmediatePropagation(),e.preventDefault(),n.value}else r.length&&(Y.set(this,i,{value:S.event.trigger(S.extend(r[0],S.Event.prototype),r.slice(1),this)}),e.stopImmediatePropagation())}})):void 0===Y.get(e,i)&&S.event.add(e,i,Ce)}S.event={global:{},add:function(t,e,n,r,i){var o,a,s,u,l,c,f,p,d,h,g,v=Y.get(t);if(V(t)){n.handler&&(n=(o=n).handler,i=o.selector),i&&S.find.matchesSelector(re,i),n.guid||(n.guid=S.guid++),(u=v.events)||(u=v.events=Object.create(null)),(a=v.handle)||(a=v.handle=function(e){return"undefined"!=typeof S&&S.event.triggered!==e.type?S.event.dispatch.apply(t,arguments):void 0}),l=(e=(e||"").match(P)||[""]).length;while(l--)d=g=(s=Te.exec(e[l])||[])[1],h=(s[2]||"").split(".").sort(),d&&(f=S.event.special[d]||{},d=(i?f.delegateType:f.bindType)||d,f=S.event.special[d]||{},c=S.extend({type:d,origType:g,data:r,handler:n,guid:n.guid,selector:i,needsContext:i&&S.expr.match.needsContext.test(i),namespace:h.join(".")},o),(p=u[d])||((p=u[d]=[]).delegateCount=0,f.setup&&!1!==f.setup.call(t,r,h,a)||t.addEventListener&&t.addEventListener(d,a)),f.add&&(f.add.call(t,c),c.handler.guid||(c.handler.guid=n.guid)),i?p.splice(p.delegateCount++,0,c):p.push(c),S.event.global[d]=!0)}},remove:function(e,t,n,r,i){var o,a,s,u,l,c,f,p,d,h,g,v=Y.hasData(e)&&Y.get(e);if(v&&(u=v.events)){l=(t=(t||"").match(P)||[""]).length;while(l--)if(d=g=(s=Te.exec(t[l])||[])[1],h=(s[2]||"").split(".").sort(),d){f=S.event.special[d]||{},p=u[d=(r?f.delegateType:f.bindType)||d]||[],s=s[2]&&new RegExp("(^|\\.)"+h.join("\\.(?:.*\\.|)")+"(\\.|$)"),a=o=p.length;while(o--)c=p[o],!i&&g!==c.origType||n&&n.guid!==c.guid||s&&!s.test(c.namespace)||r&&r!==c.selector&&("**"!==r||!c.selector)||(p.splice(o,1),c.selector&&p.delegateCount--,f.remove&&f.remove.call(e,c));a&&!p.length&&(f.teardown&&!1!==f.teardown.call(e,h,v.handle)||S.removeEvent(e,d,v.handle),delete u[d])}else for(d in u)S.event.remove(e,d+t[l],n,r,!0);S.isEmptyObject(u)&&Y.remove(e,"handle events")}},dispatch:function(e){var t,n,r,i,o,a,s=new Array(arguments.length),u=S.event.fix(e),l=(Y.get(this,"events")||Object.create(null))[u.type]||[],c=S.event.special[u.type]||{};for(s[0]=u,t=1;t<arguments.length;t++)s[t]=arguments[t];if(u.delegateTarget=this,!c.preDispatch||!1!==c.preDispatch.call(this,u)){a=S.event.handlers.call(this,u,l),t=0;while((i=a[t++])&&!u.isPropagationStopped()){u.currentTarget=i.elem,n=0;while((o=i.handlers[n++])&&!u.isImmediatePropagationStopped())u.rnamespace&&!1!==o.namespace&&!u.rnamespace.test(o.namespace)||(u.handleObj=o,u.data=o.data,void 0!==(r=((S.event.special[o.origType]||{}).handle||o.handler).apply(i.elem,s))&&!1===(u.result=r)&&(u.preventDefault(),u.stopPropagation()))}return c.postDispatch&&c.postDispatch.call(this,u),u.result}},handlers:function(e,t){var n,r,i,o,a,s=[],u=t.delegateCount,l=e.target;if(u&&l.nodeType&&!("click"===e.type&&1<=e.button))for(;l!==this;l=l.parentNode||this)if(1===l.nodeType&&("click"!==e.type||!0!==l.disabled)){for(o=[],a={},n=0;n<u;n++)void 0===a[i=(r=t[n]).selector+" "]&&(a[i]=r.needsContext?-1<S(i,this).index(l):S.find(i,this,null,[l]).length),a[i]&&o.push(r);o.length&&s.push({elem:l,handlers:o})}return l=this,u<t.length&&s.push({elem:l,handlers:t.slice(u)}),s},addProp:function(t,e){Object.defineProperty(S.Event.prototype,t,{enumerable:!0,configurable:!0,get:m(e)?function(){if(this.originalEvent)return e(this.originalEvent)}:function(){if(this.originalEvent)return this.originalEvent[t]},set:function(e){Object.defineProperty(this,t,{enumerable:!0,configurable:!0,writable:!0,value:e})}})},fix:function(e){return e[S.expando]?e:new S.Event(e)},special:{load:{noBubble:!0},click:{setup:function(e){var t=this||e;return pe.test(t.type)&&t.click&&A(t,"input")&&Ae(t,"click",Ce),!1},trigger:function(e){var t=this||e;return pe.test(t.type)&&t.click&&A(t,"input")&&Ae(t,"click"),!0},_default:function(e){var t=e.target;return pe.test(t.type)&&t.click&&A(t,"input")&&Y.get(t,"click")||A(t,"a")}},beforeunload:{postDispatch:function(e){void 0!==e.result&&e.originalEvent&&(e.originalEvent.returnValue=e.result)}}}},S.removeEvent=function(e,t,n){e.removeEventListener&&e.removeEventListener(t,n)},S.Event=function(e,t){if(!(this instanceof S.Event))return new S.Event(e,t);e&&e.type?(this.originalEvent=e,this.type=e.type,this.isDefaultPrevented=e.defaultPrevented||void 0===e.defaultPrevented&&!1===e.returnValue?Ce:Ee,this.target=e.target&&3===e.target.nodeType?e.target.parentNode:e.target,this.currentTarget=e.currentTarget,this.relatedTarget=e.relatedTarget):this.type=e,t&&S.extend(this,t),this.timeStamp=e&&e.timeStamp||Date.now(),this[S.expando]=!0},S.Event.prototype={constructor:S.Event,isDefaultPrevented:Ee,isPropagationStopped:Ee,isImmediatePropagationStopped:Ee,isSimulated:!1,preventDefault:function(){var e=this.originalEvent;this.isDefaultPrevented=Ce,e&&!this.isSimulated&&e.preventDefault()},stopPropagation:function(){var e=this.originalEvent;this.isPropagationStopped=Ce,e&&!this.isSimulated&&e.stopPropagation()},stopImmediatePropagation:function(){var e=this.originalEvent;this.isImmediatePropagationStopped=Ce,e&&!this.isSimulated&&e.stopImmediatePropagation(),this.stopPropagation()}},S.each({altKey:!0,bubbles:!0,cancelable:!0,changedTouches:!0,ctrlKey:!0,detail:!0,eventPhase:!0,metaKey:!0,pageX:!0,pageY:!0,shiftKey:!0,view:!0,"char":!0,code:!0,charCode:!0,key:!0,keyCode:!0,button:!0,buttons:!0,clientX:!0,clientY:!0,offsetX:!0,offsetY:!0,pointerId:!0,pointerType:!0,screenX:!0,screenY:!0,targetTouches:!0,toElement:!0,touches:!0,which:function(e){var t=e.button;return null==e.which&&be.test(e.type)?null!=e.charCode?e.charCode:e.keyCode:!e.which&&void 0!==t&&we.test(e.type)?1&t?1:2&t?3:4&t?2:0:e.which}},S.event.addProp),S.each({focus:"focusin",blur:"focusout"},function(e,t){S.event.special[e]={setup:function(){return Ae(this,e,Se),!1},trigger:function(){return Ae(this,e),!0},delegateType:t}}),S.each({mouseenter:"mouseover",mouseleave:"mouseout",pointerenter:"pointerover",pointerleave:"pointerout"},function(e,i){S.event.special[e]={delegateType:i,bindType:i,handle:function(e){var t,n=e.relatedTarget,r=e.handleObj;return n&&(n===this||S.contains(this,n))||(e.type=r.origType,t=r.handler.apply(this,arguments),e.type=i),t}}}),S.fn.extend({on:function(e,t,n,r){return ke(this,e,t,n,r)},one:function(e,t,n,r){return ke(this,e,t,n,r,1)},off:function(e,t,n){var r,i;if(e&&e.preventDefault&&e.handleObj)return r=e.handleObj,S(e.delegateTarget).off(r.namespace?r.origType+"."+r.namespace:r.origType,r.selector,r.handler),this;if("object"==typeof e){for(i in e)this.off(i,t,e[i]);return this}return!1!==t&&"function"!=typeof t||(n=t,t=void 0),!1===n&&(n=Ee),this.each(function(){S.event.remove(this,e,n,t)})}});var Ne=/<script|<style|<link/i,De=/checked\s*(?:[^=]|=\s*.checked.)/i,je=/^\s*<!(?:\[CDATA\[|--)|(?:\]\]|--)>\s*$/g;function qe(e,t){return A(e,"table")&&A(11!==t.nodeType?t:t.firstChild,"tr")&&S(e).children("tbody")[0]||e}function Le(e){return e.type=(null!==e.getAttribute("type"))+"/"+e.type,e}function He(e){return"true/"===(e.type||"").slice(0,5)?e.type=e.type.slice(5):e.removeAttribute("type"),e}function Oe(e,t){var n,r,i,o,a,s;if(1===t.nodeType){if(Y.hasData(e)&&(s=Y.get(e).events))for(i in Y.remove(t,"handle events"),s)for(n=0,r=s[i].length;n<r;n++)S.event.add(t,i,s[i][n]);Q.hasData(e)&&(o=Q.access(e),a=S.extend({},o),Q.set(t,a))}}function Pe(n,r,i,o){r=g(r);var e,t,a,s,u,l,c=0,f=n.length,p=f-1,d=r[0],h=m(d);if(h||1<f&&"string"==typeof d&&!y.checkClone&&De.test(d))return n.each(function(e){var t=n.eq(e);h&&(r[0]=d.call(this,e,t.html())),Pe(t,r,i,o)});if(f&&(t=(e=xe(r,n[0].ownerDocument,!1,n,o)).firstChild,1===e.childNodes.length&&(e=t),t||o)){for(s=(a=S.map(ve(e,"script"),Le)).length;c<f;c++)u=e,c!==p&&(u=S.clone(u,!0,!0),s&&S.merge(a,ve(u,"script"))),i.call(n[c],u,c);if(s)for(l=a[a.length-1].ownerDocument,S.map(a,He),c=0;c<s;c++)u=a[c],he.test(u.type||"")&&!Y.access(u,"globalEval")&&S.contains(l,u)&&(u.src&&"module"!==(u.type||"").toLowerCase()?S._evalUrl&&!u.noModule&&S._evalUrl(u.src,{nonce:u.nonce||u.getAttribute("nonce")},l):b(u.textContent.replace(je,""),u,l))}return n}function Re(e,t,n){for(var r,i=t?S.filter(t,e):e,o=0;null!=(r=i[o]);o++)n||1!==r.nodeType||S.cleanData(ve(r)),r.parentNode&&(n&&ie(r)&&ye(ve(r,"script")),r.parentNode.removeChild(r));return e}S.extend({htmlPrefilter:function(e){return e},clone:function(e,t,n){var r,i,o,a,s,u,l,c=e.cloneNode(!0),f=ie(e);if(!(y.noCloneChecked||1!==e.nodeType&&11!==e.nodeType||S.isXMLDoc(e)))for(a=ve(c),r=0,i=(o=ve(e)).length;r<i;r++)s=o[r],u=a[r],void 0,"input"===(l=u.nodeName.toLowerCase())&&pe.test(s.type)?u.checked=s.checked:"input"!==l&&"textarea"!==l||(u.defaultValue=s.defaultValue);if(t)if(n)for(o=o||ve(e),a=a||ve(c),r=0,i=o.length;r<i;r++)Oe(o[r],a[r]);else Oe(e,c);return 0<(a=ve(c,"script")).length&&ye(a,!f&&ve(e,"script")),c},cleanData:function(e){for(var t,n,r,i=S.event.special,o=0;void 0!==(n=e[o]);o++)if(V(n)){if(t=n[Y.expando]){if(t.events)for(r in t.events)i[r]?S.event.remove(n,r):S.removeEvent(n,r,t.handle);n[Y.expando]=void 0}n[Q.expando]&&(n[Q.expando]=void 0)}}}),S.fn.extend({detach:function(e){return Re(this,e,!0)},remove:function(e){return Re(this,e)},text:function(e){return $(this,function(e){return void 0===e?S.text(this):this.empty().each(function(){1!==this.nodeType&&11!==this.nodeType&&9!==this.nodeType||(this.textContent=e)})},null,e,arguments.length)},append:function(){return Pe(this,arguments,function(e){1!==this.nodeType&&11!==this.nodeType&&9!==this.nodeType||qe(this,e).appendChild(e)})},prepend:function(){return Pe(this,arguments,function(e){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var t=qe(this,e);t.insertBefore(e,t.firstChild)}})},before:function(){return Pe(this,arguments,function(e){this.parentNode&&this.parentNode.insertBefore(e,this)})},after:function(){return Pe(this,arguments,function(e){this.parentNode&&this.parentNode.insertBefore(e,this.nextSibling)})},empty:function(){for(var e,t=0;null!=(e=this[t]);t++)1===e.nodeType&&(S.cleanData(ve(e,!1)),e.textContent="");return this},clone:function(e,t){return e=null!=e&&e,t=null==t?e:t,this.map(function(){return S.clone(this,e,t)})},html:function(e){return $(this,function(e){var t=this[0]||{},n=0,r=this.length;if(void 0===e&&1===t.nodeType)return t.innerHTML;if("string"==typeof e&&!Ne.test(e)&&!ge[(de.exec(e)||["",""])[1].toLowerCase()]){e=S.htmlPrefilter(e);try{for(;n<r;n++)1===(t=this[n]||{}).nodeType&&(S.cleanData(ve(t,!1)),t.innerHTML=e);t=0}catch(e){}}t&&this.empty().append(e)},null,e,arguments.length)},replaceWith:function(){var n=[];return Pe(this,arguments,function(e){var t=this.parentNode;S.inArray(this,n)<0&&(S.cleanData(ve(this)),t&&t.replaceChild(e,this))},n)}}),S.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(e,a){S.fn[e]=function(e){for(var t,n=[],r=S(e),i=r.length-1,o=0;o<=i;o++)t=o===i?this:this.clone(!0),S(r[o])[a](t),u.apply(n,t.get());return this.pushStack(n)}});var Me=new RegExp("^("+ee+")(?!px)[a-z%]+$","i"),Ie=function(e){var t=e.ownerDocument.defaultView;return t&&t.opener||(t=C),t.getComputedStyle(e)},We=function(e,t,n){var r,i,o={};for(i in t)o[i]=e.style[i],e.style[i]=t[i];for(i in r=n.call(e),t)e.style[i]=o[i];return r},Fe=new RegExp(ne.join("|"),"i");function Be(e,t,n){var r,i,o,a,s=e.style;return(n=n||Ie(e))&&(""!==(a=n.getPropertyValue(t)||n[t])||ie(e)||(a=S.style(e,t)),!y.pixelBoxStyles()&&Me.test(a)&&Fe.test(t)&&(r=s.width,i=s.minWidth,o=s.maxWidth,s.minWidth=s.maxWidth=s.width=a,a=n.width,s.width=r,s.minWidth=i,s.maxWidth=o)),void 0!==a?a+"":a}function $e(e,t){return{get:function(){if(!e())return(this.get=t).apply(this,arguments);delete this.get}}}!function(){function e(){if(l){u.style.cssText="position:absolute;left:-11111px;width:60px;margin-top:1px;padding:0;border:0",l.style.cssText="position:relative;display:block;box-sizing:border-box;overflow:scroll;margin:auto;border:1px;padding:1px;width:60%;top:1%",re.appendChild(u).appendChild(l);var e=C.getComputedStyle(l);n="1%"!==e.top,s=12===t(e.marginLeft),l.style.right="60%",o=36===t(e.right),r=36===t(e.width),l.style.position="absolute",i=12===t(l.offsetWidth/3),re.removeChild(u),l=null}}function t(e){return Math.round(parseFloat(e))}var n,r,i,o,a,s,u=E.createElement("div"),l=E.createElement("div");l.style&&(l.style.backgroundClip="content-box",l.cloneNode(!0).style.backgroundClip="",y.clearCloneStyle="content-box"===l.style.backgroundClip,S.extend(y,{boxSizingReliable:function(){return e(),r},pixelBoxStyles:function(){return e(),o},pixelPosition:function(){return e(),n},reliableMarginLeft:function(){return e(),s},scrollboxSize:function(){return e(),i},reliableTrDimensions:function(){var e,t,n,r;return null==a&&(e=E.createElement("table"),t=E.createElement("tr"),n=E.createElement("div"),e.style.cssText="position:absolute;left:-11111px",t.style.height="1px",n.style.height="9px",re.appendChild(e).appendChild(t).appendChild(n),r=C.getComputedStyle(t),a=3<parseInt(r.height),re.removeChild(e)),a}}))}();var _e=["Webkit","Moz","ms"],ze=E.createElement("div").style,Ue={};function Xe(e){var t=S.cssProps[e]||Ue[e];return t||(e in ze?e:Ue[e]=function(e){var t=e[0].toUpperCase()+e.slice(1),n=_e.length;while(n--)if((e=_e[n]+t)in ze)return e}(e)||e)}var Ve=/^(none|table(?!-c[ea]).+)/,Ge=/^--/,Ye={position:"absolute",visibility:"hidden",display:"block"},Qe={letterSpacing:"0",fontWeight:"400"};function Je(e,t,n){var r=te.exec(t);return r?Math.max(0,r[2]-(n||0))+(r[3]||"px"):t}function Ke(e,t,n,r,i,o){var a="width"===t?1:0,s=0,u=0;if(n===(r?"border":"content"))return 0;for(;a<4;a+=2)"margin"===n&&(u+=S.css(e,n+ne[a],!0,i)),r?("content"===n&&(u-=S.css(e,"padding"+ne[a],!0,i)),"margin"!==n&&(u-=S.css(e,"border"+ne[a]+"Width",!0,i))):(u+=S.css(e,"padding"+ne[a],!0,i),"padding"!==n?u+=S.css(e,"border"+ne[a]+"Width",!0,i):s+=S.css(e,"border"+ne[a]+"Width",!0,i));return!r&&0<=o&&(u+=Math.max(0,Math.ceil(e["offset"+t[0].toUpperCase()+t.slice(1)]-o-u-s-.5))||0),u}function Ze(e,t,n){var r=Ie(e),i=(!y.boxSizingReliable()||n)&&"border-box"===S.css(e,"boxSizing",!1,r),o=i,a=Be(e,t,r),s="offset"+t[0].toUpperCase()+t.slice(1);if(Me.test(a)){if(!n)return a;a="auto"}return(!y.boxSizingReliable()&&i||!y.reliableTrDimensions()&&A(e,"tr")||"auto"===a||!parseFloat(a)&&"inline"===S.css(e,"display",!1,r))&&e.getClientRects().length&&(i="border-box"===S.css(e,"boxSizing",!1,r),(o=s in e)&&(a=e[s])),(a=parseFloat(a)||0)+Ke(e,t,n||(i?"border":"content"),o,r,a)+"px"}function et(e,t,n,r,i){return new et.prototype.init(e,t,n,r,i)}S.extend({cssHooks:{opacity:{get:function(e,t){if(t){var n=Be(e,"opacity");return""===n?"1":n}}}},cssNumber:{animationIterationCount:!0,columnCount:!0,fillOpacity:!0,flexGrow:!0,flexShrink:!0,fontWeight:!0,gridArea:!0,gridColumn:!0,gridColumnEnd:!0,gridColumnStart:!0,gridRow:!0,gridRowEnd:!0,gridRowStart:!0,lineHeight:!0,opacity:!0,order:!0,orphans:!0,widows:!0,zIndex:!0,zoom:!0},cssProps:{},style:function(e,t,n,r){if(e&&3!==e.nodeType&&8!==e.nodeType&&e.style){var i,o,a,s=X(t),u=Ge.test(t),l=e.style;if(u||(t=Xe(s)),a=S.cssHooks[t]||S.cssHooks[s],void 0===n)return a&&"get"in a&&void 0!==(i=a.get(e,!1,r))?i:l[t];"string"===(o=typeof n)&&(i=te.exec(n))&&i[1]&&(n=se(e,t,i),o="number"),null!=n&&n==n&&("number"!==o||u||(n+=i&&i[3]||(S.cssNumber[s]?"":"px")),y.clearCloneStyle||""!==n||0!==t.indexOf("background")||(l[t]="inherit"),a&&"set"in a&&void 0===(n=a.set(e,n,r))||(u?l.setProperty(t,n):l[t]=n))}},css:function(e,t,n,r){var i,o,a,s=X(t);return Ge.test(t)||(t=Xe(s)),(a=S.cssHooks[t]||S.cssHooks[s])&&"get"in a&&(i=a.get(e,!0,n)),void 0===i&&(i=Be(e,t,r)),"normal"===i&&t in Qe&&(i=Qe[t]),""===n||n?(o=parseFloat(i),!0===n||isFinite(o)?o||0:i):i}}),S.each(["height","width"],function(e,u){S.cssHooks[u]={get:function(e,t,n){if(t)return!Ve.test(S.css(e,"display"))||e.getClientRects().length&&e.getBoundingClientRect().width?Ze(e,u,n):We(e,Ye,function(){return Ze(e,u,n)})},set:function(e,t,n){var r,i=Ie(e),o=!y.scrollboxSize()&&"absolute"===i.position,a=(o||n)&&"border-box"===S.css(e,"boxSizing",!1,i),s=n?Ke(e,u,n,a,i):0;return a&&o&&(s-=Math.ceil(e["offset"+u[0].toUpperCase()+u.slice(1)]-parseFloat(i[u])-Ke(e,u,"border",!1,i)-.5)),s&&(r=te.exec(t))&&"px"!==(r[3]||"px")&&(e.style[u]=t,t=S.css(e,u)),Je(0,t,s)}}}),S.cssHooks.marginLeft=$e(y.reliableMarginLeft,function(e,t){if(t)return(parseFloat(Be(e,"marginLeft"))||e.getBoundingClientRect().left-We(e,{marginLeft:0},function(){return e.getBoundingClientRect().left}))+"px"}),S.each({margin:"",padding:"",border:"Width"},function(i,o){S.cssHooks[i+o]={expand:function(e){for(var t=0,n={},r="string"==typeof e?e.split(" "):[e];t<4;t++)n[i+ne[t]+o]=r[t]||r[t-2]||r[0];return n}},"margin"!==i&&(S.cssHooks[i+o].set=Je)}),S.fn.extend({css:function(e,t){return $(this,function(e,t,n){var r,i,o={},a=0;if(Array.isArray(t)){for(r=Ie(e),i=t.length;a<i;a++)o[t[a]]=S.css(e,t[a],!1,r);return o}return void 0!==n?S.style(e,t,n):S.css(e,t)},e,t,1<arguments.length)}}),((S.Tween=et).prototype={constructor:et,init:function(e,t,n,r,i,o){this.elem=e,this.prop=n,this.easing=i||S.easing._default,this.options=t,this.start=this.now=this.cur(),this.end=r,this.unit=o||(S.cssNumber[n]?"":"px")},cur:function(){var e=et.propHooks[this.prop];return e&&e.get?e.get(this):et.propHooks._default.get(this)},run:function(e){var t,n=et.propHooks[this.prop];return this.options.duration?this.pos=t=S.easing[this.easing](e,this.options.duration*e,0,1,this.options.duration):this.pos=t=e,this.now=(this.end-this.start)*t+this.start,this.options.step&&this.options.step.call(this.elem,this.now,this),n&&n.set?n.set(this):et.propHooks._default.set(this),this}}).init.prototype=et.prototype,(et.propHooks={_default:{get:function(e){var t;return 1!==e.elem.nodeType||null!=e.elem[e.prop]&&null==e.elem.style[e.prop]?e.elem[e.prop]:(t=S.css(e.elem,e.prop,""))&&"auto"!==t?t:0},set:function(e){S.fx.step[e.prop]?S.fx.step[e.prop](e):1!==e.elem.nodeType||!S.cssHooks[e.prop]&&null==e.elem.style[Xe(e.prop)]?e.elem[e.prop]=e.now:S.style(e.elem,e.prop,e.now+e.unit)}}}).scrollTop=et.propHooks.scrollLeft={set:function(e){e.elem.nodeType&&e.elem.parentNode&&(e.elem[e.prop]=e.now)}},S.easing={linear:function(e){return e},swing:function(e){return.5-Math.cos(e*Math.PI)/2},_default:"swing"},S.fx=et.prototype.init,S.fx.step={};var tt,nt,rt,it,ot=/^(?:toggle|show|hide)$/,at=/queueHooks$/;function st(){nt&&(!1===E.hidden&&C.requestAnimationFrame?C.requestAnimationFrame(st):C.setTimeout(st,S.fx.interval),S.fx.tick())}function ut(){return C.setTimeout(function(){tt=void 0}),tt=Date.now()}function lt(e,t){var n,r=0,i={height:e};for(t=t?1:0;r<4;r+=2-t)i["margin"+(n=ne[r])]=i["padding"+n]=e;return t&&(i.opacity=i.width=e),i}function ct(e,t,n){for(var r,i=(ft.tweeners[t]||[]).concat(ft.tweeners["*"]),o=0,a=i.length;o<a;o++)if(r=i[o].call(n,t,e))return r}function ft(o,e,t){var n,a,r=0,i=ft.prefilters.length,s=S.Deferred().always(function(){delete u.elem}),u=function(){if(a)return!1;for(var e=tt||ut(),t=Math.max(0,l.startTime+l.duration-e),n=1-(t/l.duration||0),r=0,i=l.tweens.length;r<i;r++)l.tweens[r].run(n);return s.notifyWith(o,[l,n,t]),n<1&&i?t:(i||s.notifyWith(o,[l,1,0]),s.resolveWith(o,[l]),!1)},l=s.promise({elem:o,props:S.extend({},e),opts:S.extend(!0,{specialEasing:{},easing:S.easing._default},t),originalProperties:e,originalOptions:t,startTime:tt||ut(),duration:t.duration,tweens:[],createTween:function(e,t){var n=S.Tween(o,l.opts,e,t,l.opts.specialEasing[e]||l.opts.easing);return l.tweens.push(n),n},stop:function(e){var t=0,n=e?l.tweens.length:0;if(a)return this;for(a=!0;t<n;t++)l.tweens[t].run(1);return e?(s.notifyWith(o,[l,1,0]),s.resolveWith(o,[l,e])):s.rejectWith(o,[l,e]),this}}),c=l.props;for(!function(e,t){var n,r,i,o,a;for(n in e)if(i=t[r=X(n)],o=e[n],Array.isArray(o)&&(i=o[1],o=e[n]=o[0]),n!==r&&(e[r]=o,delete e[n]),(a=S.cssHooks[r])&&"expand"in a)for(n in o=a.expand(o),delete e[r],o)n in e||(e[n]=o[n],t[n]=i);else t[r]=i}(c,l.opts.specialEasing);r<i;r++)if(n=ft.prefilters[r].call(l,o,c,l.opts))return m(n.stop)&&(S._queueHooks(l.elem,l.opts.queue).stop=n.stop.bind(n)),n;return S.map(c,ct,l),m(l.opts.start)&&l.opts.start.call(o,l),l.progress(l.opts.progress).done(l.opts.done,l.opts.complete).fail(l.opts.fail).always(l.opts.always),S.fx.timer(S.extend(u,{elem:o,anim:l,queue:l.opts.queue})),l}S.Animation=S.extend(ft,{tweeners:{"*":[function(e,t){var n=this.createTween(e,t);return se(n.elem,e,te.exec(t),n),n}]},tweener:function(e,t){m(e)?(t=e,e=["*"]):e=e.match(P);for(var n,r=0,i=e.length;r<i;r++)n=e[r],ft.tweeners[n]=ft.tweeners[n]||[],ft.tweeners[n].unshift(t)},prefilters:[function(e,t,n){var r,i,o,a,s,u,l,c,f="width"in t||"height"in t,p=this,d={},h=e.style,g=e.nodeType&&ae(e),v=Y.get(e,"fxshow");for(r in n.queue||(null==(a=S._queueHooks(e,"fx")).unqueued&&(a.unqueued=0,s=a.empty.fire,a.empty.fire=function(){a.unqueued||s()}),a.unqueued++,p.always(function(){p.always(function(){a.unqueued--,S.queue(e,"fx").length||a.empty.fire()})})),t)if(i=t[r],ot.test(i)){if(delete t[r],o=o||"toggle"===i,i===(g?"hide":"show")){if("show"!==i||!v||void 0===v[r])continue;g=!0}d[r]=v&&v[r]||S.style(e,r)}if((u=!S.isEmptyObject(t))||!S.isEmptyObject(d))for(r in f&&1===e.nodeType&&(n.overflow=[h.overflow,h.overflowX,h.overflowY],null==(l=v&&v.display)&&(l=Y.get(e,"display")),"none"===(c=S.css(e,"display"))&&(l?c=l:(le([e],!0),l=e.style.display||l,c=S.css(e,"display"),le([e]))),("inline"===c||"inline-block"===c&&null!=l)&&"none"===S.css(e,"float")&&(u||(p.done(function(){h.display=l}),null==l&&(c=h.display,l="none"===c?"":c)),h.display="inline-block")),n.overflow&&(h.overflow="hidden",p.always(function(){h.overflow=n.overflow[0],h.overflowX=n.overflow[1],h.overflowY=n.overflow[2]})),u=!1,d)u||(v?"hidden"in v&&(g=v.hidden):v=Y.access(e,"fxshow",{display:l}),o&&(v.hidden=!g),g&&le([e],!0),p.done(function(){for(r in g||le([e]),Y.remove(e,"fxshow"),d)S.style(e,r,d[r])})),u=ct(g?v[r]:0,r,p),r in v||(v[r]=u.start,g&&(u.end=u.start,u.start=0))}],prefilter:function(e,t){t?ft.prefilters.unshift(e):ft.prefilters.push(e)}}),S.speed=function(e,t,n){var r=e&&"object"==typeof e?S.extend({},e):{complete:n||!n&&t||m(e)&&e,duration:e,easing:n&&t||t&&!m(t)&&t};return S.fx.off?r.duration=0:"number"!=typeof r.duration&&(r.duration in S.fx.speeds?r.duration=S.fx.speeds[r.duration]:r.duration=S.fx.speeds._default),null!=r.queue&&!0!==r.queue||(r.queue="fx"),r.old=r.complete,r.complete=function(){m(r.old)&&r.old.call(this),r.queue&&S.dequeue(this,r.queue)},r},S.fn.extend({fadeTo:function(e,t,n,r){return this.filter(ae).css("opacity",0).show().end().animate({opacity:t},e,n,r)},animate:function(t,e,n,r){var i=S.isEmptyObject(t),o=S.speed(e,n,r),a=function(){var e=ft(this,S.extend({},t),o);(i||Y.get(this,"finish"))&&e.stop(!0)};return a.finish=a,i||!1===o.queue?this.each(a):this.queue(o.queue,a)},stop:function(i,e,o){var a=function(e){var t=e.stop;delete e.stop,t(o)};return"string"!=typeof i&&(o=e,e=i,i=void 0),e&&this.queue(i||"fx",[]),this.each(function(){var e=!0,t=null!=i&&i+"queueHooks",n=S.timers,r=Y.get(this);if(t)r[t]&&r[t].stop&&a(r[t]);else for(t in r)r[t]&&r[t].stop&&at.test(t)&&a(r[t]);for(t=n.length;t--;)n[t].elem!==this||null!=i&&n[t].queue!==i||(n[t].anim.stop(o),e=!1,n.splice(t,1));!e&&o||S.dequeue(this,i)})},finish:function(a){return!1!==a&&(a=a||"fx"),this.each(function(){var e,t=Y.get(this),n=t[a+"queue"],r=t[a+"queueHooks"],i=S.timers,o=n?n.length:0;for(t.finish=!0,S.queue(this,a,[]),r&&r.stop&&r.stop.call(this,!0),e=i.length;e--;)i[e].elem===this&&i[e].queue===a&&(i[e].anim.stop(!0),i.splice(e,1));for(e=0;e<o;e++)n[e]&&n[e].finish&&n[e].finish.call(this);delete t.finish})}}),S.each(["toggle","show","hide"],function(e,r){var i=S.fn[r];S.fn[r]=function(e,t,n){return null==e||"boolean"==typeof e?i.apply(this,arguments):this.animate(lt(r,!0),e,t,n)}}),S.each({slideDown:lt("show"),slideUp:lt("hide"),slideToggle:lt("toggle"),fadeIn:{opacity:"show"},fadeOut:{opacity:"hide"},fadeToggle:{opacity:"toggle"}},function(e,r){S.fn[e]=function(e,t,n){return this.animate(r,e,t,n)}}),S.timers=[],S.fx.tick=function(){var e,t=0,n=S.timers;for(tt=Date.now();t<n.length;t++)(e=n[t])()||n[t]!==e||n.splice(t--,1);n.length||S.fx.stop(),tt=void 0},S.fx.timer=function(e){S.timers.push(e),S.fx.start()},S.fx.interval=13,S.fx.start=function(){nt||(nt=!0,st())},S.fx.stop=function(){nt=null},S.fx.speeds={slow:600,fast:200,_default:400},S.fn.delay=function(r,e){return r=S.fx&&S.fx.speeds[r]||r,e=e||"fx",this.queue(e,function(e,t){var n=C.setTimeout(e,r);t.stop=function(){C.clearTimeout(n)}})},rt=E.createElement("input"),it=E.createElement("select").appendChild(E.createElement("option")),rt.type="checkbox",y.checkOn=""!==rt.value,y.optSelected=it.selected,(rt=E.createElement("input")).value="t",rt.type="radio",y.radioValue="t"===rt.value;var pt,dt=S.expr.attrHandle;S.fn.extend({attr:function(e,t){return $(this,S.attr,e,t,1<arguments.length)},removeAttr:function(e){return this.each(function(){S.removeAttr(this,e)})}}),S.extend({attr:function(e,t,n){var r,i,o=e.nodeType;if(3!==o&&8!==o&&2!==o)return"undefined"==typeof e.getAttribute?S.prop(e,t,n):(1===o&&S.isXMLDoc(e)||(i=S.attrHooks[t.toLowerCase()]||(S.expr.match.bool.test(t)?pt:void 0)),void 0!==n?null===n?void S.removeAttr(e,t):i&&"set"in i&&void 0!==(r=i.set(e,n,t))?r:(e.setAttribute(t,n+""),n):i&&"get"in i&&null!==(r=i.get(e,t))?r:null==(r=S.find.attr(e,t))?void 0:r)},attrHooks:{type:{set:function(e,t){if(!y.radioValue&&"radio"===t&&A(e,"input")){var n=e.value;return e.setAttribute("type",t),n&&(e.value=n),t}}}},removeAttr:function(e,t){var n,r=0,i=t&&t.match(P);if(i&&1===e.nodeType)while(n=i[r++])e.removeAttribute(n)}}),pt={set:function(e,t,n){return!1===t?S.removeAttr(e,n):e.setAttribute(n,n),n}},S.each(S.expr.match.bool.source.match(/\w+/g),function(e,t){var a=dt[t]||S.find.attr;dt[t]=function(e,t,n){var r,i,o=t.toLowerCase();return n||(i=dt[o],dt[o]=r,r=null!=a(e,t,n)?o:null,dt[o]=i),r}});var ht=/^(?:input|select|textarea|button)$/i,gt=/^(?:a|area)$/i;function vt(e){return(e.match(P)||[]).join(" ")}function yt(e){return e.getAttribute&&e.getAttribute("class")||""}function mt(e){return Array.isArray(e)?e:"string"==typeof e&&e.match(P)||[]}S.fn.extend({prop:function(e,t){return $(this,S.prop,e,t,1<arguments.length)},removeProp:function(e){return this.each(function(){delete this[S.propFix[e]||e]})}}),S.extend({prop:function(e,t,n){var r,i,o=e.nodeType;if(3!==o&&8!==o&&2!==o)return 1===o&&S.isXMLDoc(e)||(t=S.propFix[t]||t,i=S.propHooks[t]),void 0!==n?i&&"set"in i&&void 0!==(r=i.set(e,n,t))?r:e[t]=n:i&&"get"in i&&null!==(r=i.get(e,t))?r:e[t]},propHooks:{tabIndex:{get:function(e){var t=S.find.attr(e,"tabindex");return t?parseInt(t,10):ht.test(e.nodeName)||gt.test(e.nodeName)&&e.href?0:-1}}},propFix:{"for":"htmlFor","class":"className"}}),y.optSelected||(S.propHooks.selected={get:function(e){var t=e.parentNode;return t&&t.parentNode&&t.parentNode.selectedIndex,null},set:function(e){var t=e.parentNode;t&&(t.selectedIndex,t.parentNode&&t.parentNode.selectedIndex)}}),S.each(["tabIndex","readOnly","maxLength","cellSpacing","cellPadding","rowSpan","colSpan","useMap","frameBorder","contentEditable"],function(){S.propFix[this.toLowerCase()]=this}),S.fn.extend({addClass:function(t){var e,n,r,i,o,a,s,u=0;if(m(t))return this.each(function(e){S(this).addClass(t.call(this,e,yt(this)))});if((e=mt(t)).length)while(n=this[u++])if(i=yt(n),r=1===n.nodeType&&" "+vt(i)+" "){a=0;while(o=e[a++])r.indexOf(" "+o+" ")<0&&(r+=o+" ");i!==(s=vt(r))&&n.setAttribute("class",s)}return this},removeClass:function(t){var e,n,r,i,o,a,s,u=0;if(m(t))return this.each(function(e){S(this).removeClass(t.call(this,e,yt(this)))});if(!arguments.length)return this.attr("class","");if((e=mt(t)).length)while(n=this[u++])if(i=yt(n),r=1===n.nodeType&&" "+vt(i)+" "){a=0;while(o=e[a++])while(-1<r.indexOf(" "+o+" "))r=r.replace(" "+o+" "," ");i!==(s=vt(r))&&n.setAttribute("class",s)}return this},toggleClass:function(i,t){var o=typeof i,a="string"===o||Array.isArray(i);return"boolean"==typeof t&&a?t?this.addClass(i):this.removeClass(i):m(i)?this.each(function(e){S(this).toggleClass(i.call(this,e,yt(this),t),t)}):this.each(function(){var e,t,n,r;if(a){t=0,n=S(this),r=mt(i);while(e=r[t++])n.hasClass(e)?n.removeClass(e):n.addClass(e)}else void 0!==i&&"boolean"!==o||((e=yt(this))&&Y.set(this,"__className__",e),this.setAttribute&&this.setAttribute("class",e||!1===i?"":Y.get(this,"__className__")||""))})},hasClass:function(e){var t,n,r=0;t=" "+e+" ";while(n=this[r++])if(1===n.nodeType&&-1<(" "+vt(yt(n))+" ").indexOf(t))return!0;return!1}});var xt=/\r/g;S.fn.extend({val:function(n){var r,e,i,t=this[0];return arguments.length?(i=m(n),this.each(function(e){var t;1===this.nodeType&&(null==(t=i?n.call(this,e,S(this).val()):n)?t="":"number"==typeof t?t+="":Array.isArray(t)&&(t=S.map(t,function(e){return null==e?"":e+""})),(r=S.valHooks[this.type]||S.valHooks[this.nodeName.toLowerCase()])&&"set"in r&&void 0!==r.set(this,t,"value")||(this.value=t))})):t?(r=S.valHooks[t.type]||S.valHooks[t.nodeName.toLowerCase()])&&"get"in r&&void 0!==(e=r.get(t,"value"))?e:"string"==typeof(e=t.value)?e.replace(xt,""):null==e?"":e:void 0}}),S.extend({valHooks:{option:{get:function(e){var t=S.find.attr(e,"value");return null!=t?t:vt(S.text(e))}},select:{get:function(e){var t,n,r,i=e.options,o=e.selectedIndex,a="select-one"===e.type,s=a?null:[],u=a?o+1:i.length;for(r=o<0?u:a?o:0;r<u;r++)if(((n=i[r]).selected||r===o)&&!n.disabled&&(!n.parentNode.disabled||!A(n.parentNode,"optgroup"))){if(t=S(n).val(),a)return t;s.push(t)}return s},set:function(e,t){var n,r,i=e.options,o=S.makeArray(t),a=i.length;while(a--)((r=i[a]).selected=-1<S.inArray(S.valHooks.option.get(r),o))&&(n=!0);return n||(e.selectedIndex=-1),o}}}}),S.each(["radio","checkbox"],function(){S.valHooks[this]={set:function(e,t){if(Array.isArray(t))return e.checked=-1<S.inArray(S(e).val(),t)}},y.checkOn||(S.valHooks[this].get=function(e){return null===e.getAttribute("value")?"on":e.value})}),y.focusin="onfocusin"in C;var bt=/^(?:focusinfocus|focusoutblur)$/,wt=function(e){e.stopPropagation()};S.extend(S.event,{trigger:function(e,t,n,r){var i,o,a,s,u,l,c,f,p=[n||E],d=v.call(e,"type")?e.type:e,h=v.call(e,"namespace")?e.namespace.split("."):[];if(o=f=a=n=n||E,3!==n.nodeType&&8!==n.nodeType&&!bt.test(d+S.event.triggered)&&(-1<d.indexOf(".")&&(d=(h=d.split(".")).shift(),h.sort()),u=d.indexOf(":")<0&&"on"+d,(e=e[S.expando]?e:new S.Event(d,"object"==typeof e&&e)).isTrigger=r?2:3,e.namespace=h.join("."),e.rnamespace=e.namespace?new RegExp("(^|\\.)"+h.join("\\.(?:.*\\.|)")+"(\\.|$)"):null,e.result=void 0,e.target||(e.target=n),t=null==t?[e]:S.makeArray(t,[e]),c=S.event.special[d]||{},r||!c.trigger||!1!==c.trigger.apply(n,t))){if(!r&&!c.noBubble&&!x(n)){for(s=c.delegateType||d,bt.test(s+d)||(o=o.parentNode);o;o=o.parentNode)p.push(o),a=o;a===(n.ownerDocument||E)&&p.push(a.defaultView||a.parentWindow||C)}i=0;while((o=p[i++])&&!e.isPropagationStopped())f=o,e.type=1<i?s:c.bindType||d,(l=(Y.get(o,"events")||Object.create(null))[e.type]&&Y.get(o,"handle"))&&l.apply(o,t),(l=u&&o[u])&&l.apply&&V(o)&&(e.result=l.apply(o,t),!1===e.result&&e.preventDefault());return e.type=d,r||e.isDefaultPrevented()||c._default&&!1!==c._default.apply(p.pop(),t)||!V(n)||u&&m(n[d])&&!x(n)&&((a=n[u])&&(n[u]=null),S.event.triggered=d,e.isPropagationStopped()&&f.addEventListener(d,wt),n[d](),e.isPropagationStopped()&&f.removeEventListener(d,wt),S.event.triggered=void 0,a&&(n[u]=a)),e.result}},simulate:function(e,t,n){var r=S.extend(new S.Event,n,{type:e,isSimulated:!0});S.event.trigger(r,null,t)}}),S.fn.extend({trigger:function(e,t){return this.each(function(){S.event.trigger(e,t,this)})},triggerHandler:function(e,t){var n=this[0];if(n)return S.event.trigger(e,t,n,!0)}}),y.focusin||S.each({focus:"focusin",blur:"focusout"},function(n,r){var i=function(e){S.event.simulate(r,e.target,S.event.fix(e))};S.event.special[r]={setup:function(){var e=this.ownerDocument||this.document||this,t=Y.access(e,r);t||e.addEventListener(n,i,!0),Y.access(e,r,(t||0)+1)},teardown:function(){var e=this.ownerDocument||this.document||this,t=Y.access(e,r)-1;t?Y.access(e,r,t):(e.removeEventListener(n,i,!0),Y.remove(e,r))}}});var Tt=C.location,Ct={guid:Date.now()},Et=/\?/;S.parseXML=function(e){var t;if(!e||"string"!=typeof e)return null;try{t=(new C.DOMParser).parseFromString(e,"text/xml")}catch(e){t=void 0}return t&&!t.getElementsByTagName("parsererror").length||S.error("Invalid XML: "+e),t};var St=/\[\]$/,kt=/\r?\n/g,At=/^(?:submit|button|image|reset|file)$/i,Nt=/^(?:input|select|textarea|keygen)/i;function Dt(n,e,r,i){var t;if(Array.isArray(e))S.each(e,function(e,t){r||St.test(n)?i(n,t):Dt(n+"["+("object"==typeof t&&null!=t?e:"")+"]",t,r,i)});else if(r||"object"!==w(e))i(n,e);else for(t in e)Dt(n+"["+t+"]",e[t],r,i)}S.param=function(e,t){var n,r=[],i=function(e,t){var n=m(t)?t():t;r[r.length]=encodeURIComponent(e)+"="+encodeURIComponent(null==n?"":n)};if(null==e)return"";if(Array.isArray(e)||e.jquery&&!S.isPlainObject(e))S.each(e,function(){i(this.name,this.value)});else for(n in e)Dt(n,e[n],t,i);return r.join("&")},S.fn.extend({serialize:function(){return S.param(this.serializeArray())},serializeArray:function(){return this.map(function(){var e=S.prop(this,"elements");return e?S.makeArray(e):this}).filter(function(){var e=this.type;return this.name&&!S(this).is(":disabled")&&Nt.test(this.nodeName)&&!At.test(e)&&(this.checked||!pe.test(e))}).map(function(e,t){var n=S(this).val();return null==n?null:Array.isArray(n)?S.map(n,function(e){return{name:t.name,value:e.replace(kt,"\r\n")}}):{name:t.name,value:n.replace(kt,"\r\n")}}).get()}});var jt=/%20/g,qt=/#.*$/,Lt=/([?&])_=[^&]*/,Ht=/^(.*?):[ \t]*([^\r\n]*)$/gm,Ot=/^(?:GET|HEAD)$/,Pt=/^\/\//,Rt={},Mt={},It="*/".concat("*"),Wt=E.createElement("a");function Ft(o){return function(e,t){"string"!=typeof e&&(t=e,e="*");var n,r=0,i=e.toLowerCase().match(P)||[];if(m(t))while(n=i[r++])"+"===n[0]?(n=n.slice(1)||"*",(o[n]=o[n]||[]).unshift(t)):(o[n]=o[n]||[]).push(t)}}function Bt(t,i,o,a){var s={},u=t===Mt;function l(e){var r;return s[e]=!0,S.each(t[e]||[],function(e,t){var n=t(i,o,a);return"string"!=typeof n||u||s[n]?u?!(r=n):void 0:(i.dataTypes.unshift(n),l(n),!1)}),r}return l(i.dataTypes[0])||!s["*"]&&l("*")}function $t(e,t){var n,r,i=S.ajaxSettings.flatOptions||{};for(n in t)void 0!==t[n]&&((i[n]?e:r||(r={}))[n]=t[n]);return r&&S.extend(!0,e,r),e}Wt.href=Tt.href,S.extend({active:0,lastModified:{},etag:{},ajaxSettings:{url:Tt.href,type:"GET",isLocal:/^(?:about|app|app-storage|.+-extension|file|res|widget):$/.test(Tt.protocol),global:!0,processData:!0,async:!0,contentType:"application/x-www-form-urlencoded; charset=UTF-8",accepts:{"*":It,text:"text/plain",html:"text/html",xml:"application/xml, text/xml",json:"application/json, text/javascript"},contents:{xml:/\bxml\b/,html:/\bhtml/,json:/\bjson\b/},responseFields:{xml:"responseXML",text:"responseText",json:"responseJSON"},converters:{"* text":String,"text html":!0,"text json":JSON.parse,"text xml":S.parseXML},flatOptions:{url:!0,context:!0}},ajaxSetup:function(e,t){return t?$t($t(e,S.ajaxSettings),t):$t(S.ajaxSettings,e)},ajaxPrefilter:Ft(Rt),ajaxTransport:Ft(Mt),ajax:function(e,t){"object"==typeof e&&(t=e,e=void 0),t=t||{};var c,f,p,n,d,r,h,g,i,o,v=S.ajaxSetup({},t),y=v.context||v,m=v.context&&(y.nodeType||y.jquery)?S(y):S.event,x=S.Deferred(),b=S.Callbacks("once memory"),w=v.statusCode||{},a={},s={},u="canceled",T={readyState:0,getResponseHeader:function(e){var t;if(h){if(!n){n={};while(t=Ht.exec(p))n[t[1].toLowerCase()+" "]=(n[t[1].toLowerCase()+" "]||[]).concat(t[2])}t=n[e.toLowerCase()+" "]}return null==t?null:t.join(", ")},getAllResponseHeaders:function(){return h?p:null},setRequestHeader:function(e,t){return null==h&&(e=s[e.toLowerCase()]=s[e.toLowerCase()]||e,a[e]=t),this},overrideMimeType:function(e){return null==h&&(v.mimeType=e),this},statusCode:function(e){var t;if(e)if(h)T.always(e[T.status]);else for(t in e)w[t]=[w[t],e[t]];return this},abort:function(e){var t=e||u;return c&&c.abort(t),l(0,t),this}};if(x.promise(T),v.url=((e||v.url||Tt.href)+"").replace(Pt,Tt.protocol+"//"),v.type=t.method||t.type||v.method||v.type,v.dataTypes=(v.dataType||"*").toLowerCase().match(P)||[""],null==v.crossDomain){r=E.createElement("a");try{r.href=v.url,r.href=r.href,v.crossDomain=Wt.protocol+"//"+Wt.host!=r.protocol+"//"+r.host}catch(e){v.crossDomain=!0}}if(v.data&&v.processData&&"string"!=typeof v.data&&(v.data=S.param(v.data,v.traditional)),Bt(Rt,v,t,T),h)return T;for(i in(g=S.event&&v.global)&&0==S.active++&&S.event.trigger("ajaxStart"),v.type=v.type.toUpperCase(),v.hasContent=!Ot.test(v.type),f=v.url.replace(qt,""),v.hasContent?v.data&&v.processData&&0===(v.contentType||"").indexOf("application/x-www-form-urlencoded")&&(v.data=v.data.replace(jt,"+")):(o=v.url.slice(f.length),v.data&&(v.processData||"string"==typeof v.data)&&(f+=(Et.test(f)?"&":"?")+v.data,delete v.data),!1===v.cache&&(f=f.replace(Lt,"$1"),o=(Et.test(f)?"&":"?")+"_="+Ct.guid+++o),v.url=f+o),v.ifModified&&(S.lastModified[f]&&T.setRequestHeader("If-Modified-Since",S.lastModified[f]),S.etag[f]&&T.setRequestHeader("If-None-Match",S.etag[f])),(v.data&&v.hasContent&&!1!==v.contentType||t.contentType)&&T.setRequestHeader("Content-Type",v.contentType),T.setRequestHeader("Accept",v.dataTypes[0]&&v.accepts[v.dataTypes[0]]?v.accepts[v.dataTypes[0]]+("*"!==v.dataTypes[0]?", "+It+"; q=0.01":""):v.accepts["*"]),v.headers)T.setRequestHeader(i,v.headers[i]);if(v.beforeSend&&(!1===v.beforeSend.call(y,T,v)||h))return T.abort();if(u="abort",b.add(v.complete),T.done(v.success),T.fail(v.error),c=Bt(Mt,v,t,T)){if(T.readyState=1,g&&m.trigger("ajaxSend",[T,v]),h)return T;v.async&&0<v.timeout&&(d=C.setTimeout(function(){T.abort("timeout")},v.timeout));try{h=!1,c.send(a,l)}catch(e){if(h)throw e;l(-1,e)}}else l(-1,"No Transport");function l(e,t,n,r){var i,o,a,s,u,l=t;h||(h=!0,d&&C.clearTimeout(d),c=void 0,p=r||"",T.readyState=0<e?4:0,i=200<=e&&e<300||304===e,n&&(s=function(e,t,n){var r,i,o,a,s=e.contents,u=e.dataTypes;while("*"===u[0])u.shift(),void 0===r&&(r=e.mimeType||t.getResponseHeader("Content-Type"));if(r)for(i in s)if(s[i]&&s[i].test(r)){u.unshift(i);break}if(u[0]in n)o=u[0];else{for(i in n){if(!u[0]||e.converters[i+" "+u[0]]){o=i;break}a||(a=i)}o=o||a}if(o)return o!==u[0]&&u.unshift(o),n[o]}(v,T,n)),!i&&-1<S.inArray("script",v.dataTypes)&&(v.converters["text script"]=function(){}),s=function(e,t,n,r){var i,o,a,s,u,l={},c=e.dataTypes.slice();if(c[1])for(a in e.converters)l[a.toLowerCase()]=e.converters[a];o=c.shift();while(o)if(e.responseFields[o]&&(n[e.responseFields[o]]=t),!u&&r&&e.dataFilter&&(t=e.dataFilter(t,e.dataType)),u=o,o=c.shift())if("*"===o)o=u;else if("*"!==u&&u!==o){if(!(a=l[u+" "+o]||l["* "+o]))for(i in l)if((s=i.split(" "))[1]===o&&(a=l[u+" "+s[0]]||l["* "+s[0]])){!0===a?a=l[i]:!0!==l[i]&&(o=s[0],c.unshift(s[1]));break}if(!0!==a)if(a&&e["throws"])t=a(t);else try{t=a(t)}catch(e){return{state:"parsererror",error:a?e:"No conversion from "+u+" to "+o}}}return{state:"success",data:t}}(v,s,T,i),i?(v.ifModified&&((u=T.getResponseHeader("Last-Modified"))&&(S.lastModified[f]=u),(u=T.getResponseHeader("etag"))&&(S.etag[f]=u)),204===e||"HEAD"===v.type?l="nocontent":304===e?l="notmodified":(l=s.state,o=s.data,i=!(a=s.error))):(a=l,!e&&l||(l="error",e<0&&(e=0))),T.status=e,T.statusText=(t||l)+"",i?x.resolveWith(y,[o,l,T]):x.rejectWith(y,[T,l,a]),T.statusCode(w),w=void 0,g&&m.trigger(i?"ajaxSuccess":"ajaxError",[T,v,i?o:a]),b.fireWith(y,[T,l]),g&&(m.trigger("ajaxComplete",[T,v]),--S.active||S.event.trigger("ajaxStop")))}return T},getJSON:function(e,t,n){return S.get(e,t,n,"json")},getScript:function(e,t){return S.get(e,void 0,t,"script")}}),S.each(["get","post"],function(e,i){S[i]=function(e,t,n,r){return m(t)&&(r=r||n,n=t,t=void 0),S.ajax(S.extend({url:e,type:i,dataType:r,data:t,success:n},S.isPlainObject(e)&&e))}}),S.ajaxPrefilter(function(e){var t;for(t in e.headers)"content-type"===t.toLowerCase()&&(e.contentType=e.headers[t]||"")}),S._evalUrl=function(e,t,n){return S.ajax({url:e,type:"GET",dataType:"script",cache:!0,async:!1,global:!1,converters:{"text script":function(){}},dataFilter:function(e){S.globalEval(e,t,n)}})},S.fn.extend({wrapAll:function(e){var t;return this[0]&&(m(e)&&(e=e.call(this[0])),t=S(e,this[0].ownerDocument).eq(0).clone(!0),this[0].parentNode&&t.insertBefore(this[0]),t.map(function(){var e=this;while(e.firstElementChild)e=e.firstElementChild;return e}).append(this)),this},wrapInner:function(n){return m(n)?this.each(function(e){S(this).wrapInner(n.call(this,e))}):this.each(function(){var e=S(this),t=e.contents();t.length?t.wrapAll(n):e.append(n)})},wrap:function(t){var n=m(t);return this.each(function(e){S(this).wrapAll(n?t.call(this,e):t)})},unwrap:function(e){return this.parent(e).not("body").each(function(){S(this).replaceWith(this.childNodes)}),this}}),S.expr.pseudos.hidden=function(e){return!S.expr.pseudos.visible(e)},S.expr.pseudos.visible=function(e){return!!(e.offsetWidth||e.offsetHeight||e.getClientRects().length)},S.ajaxSettings.xhr=function(){try{return new C.XMLHttpRequest}catch(e){}};var _t={0:200,1223:204},zt=S.ajaxSettings.xhr();y.cors=!!zt&&"withCredentials"in zt,y.ajax=zt=!!zt,S.ajaxTransport(function(i){var o,a;if(y.cors||zt&&!i.crossDomain)return{send:function(e,t){var n,r=i.xhr();if(r.open(i.type,i.url,i.async,i.username,i.password),i.xhrFields)for(n in i.xhrFields)r[n]=i.xhrFields[n];for(n in i.mimeType&&r.overrideMimeType&&r.overrideMimeType(i.mimeType),i.crossDomain||e["X-Requested-With"]||(e["X-Requested-With"]="XMLHttpRequest"),e)r.setRequestHeader(n,e[n]);o=function(e){return function(){o&&(o=a=r.onload=r.onerror=r.onabort=r.ontimeout=r.onreadystatechange=null,"abort"===e?r.abort():"error"===e?"number"!=typeof r.status?t(0,"error"):t(r.status,r.statusText):t(_t[r.status]||r.status,r.statusText,"text"!==(r.responseType||"text")||"string"!=typeof r.responseText?{binary:r.response}:{text:r.responseText},r.getAllResponseHeaders()))}},r.onload=o(),a=r.onerror=r.ontimeout=o("error"),void 0!==r.onabort?r.onabort=a:r.onreadystatechange=function(){4===r.readyState&&C.setTimeout(function(){o&&a()})},o=o("abort");try{r.send(i.hasContent&&i.data||null)}catch(e){if(o)throw e}},abort:function(){o&&o()}}}),S.ajaxPrefilter(function(e){e.crossDomain&&(e.contents.script=!1)}),S.ajaxSetup({accepts:{script:"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"},contents:{script:/\b(?:java|ecma)script\b/},converters:{"text script":function(e){return S.globalEval(e),e}}}),S.ajaxPrefilter("script",function(e){void 0===e.cache&&(e.cache=!1),e.crossDomain&&(e.type="GET")}),S.ajaxTransport("script",function(n){var r,i;if(n.crossDomain||n.scriptAttrs)return{send:function(e,t){r=S("<script>").attr(n.scriptAttrs||{}).prop({charset:n.scriptCharset,src:n.url}).on("load error",i=function(e){r.remove(),i=null,e&&t("error"===e.type?404:200,e.type)}),E.head.appendChild(r[0])},abort:function(){i&&i()}}});var Ut,Xt=[],Vt=/(=)\?(?=&|$)|\?\?/;S.ajaxSetup({jsonp:"callback",jsonpCallback:function(){var e=Xt.pop()||S.expando+"_"+Ct.guid++;return this[e]=!0,e}}),S.ajaxPrefilter("json jsonp",function(e,t,n){var r,i,o,a=!1!==e.jsonp&&(Vt.test(e.url)?"url":"string"==typeof e.data&&0===(e.contentType||"").indexOf("application/x-www-form-urlencoded")&&Vt.test(e.data)&&"data");if(a||"jsonp"===e.dataTypes[0])return r=e.jsonpCallback=m(e.jsonpCallback)?e.jsonpCallback():e.jsonpCallback,a?e[a]=e[a].replace(Vt,"$1"+r):!1!==e.jsonp&&(e.url+=(Et.test(e.url)?"&":"?")+e.jsonp+"="+r),e.converters["script json"]=function(){return o||S.error(r+" was not called"),o[0]},e.dataTypes[0]="json",i=C[r],C[r]=function(){o=arguments},n.always(function(){void 0===i?S(C).removeProp(r):C[r]=i,e[r]&&(e.jsonpCallback=t.jsonpCallback,Xt.push(r)),o&&m(i)&&i(o[0]),o=i=void 0}),"script"}),y.createHTMLDocument=((Ut=E.implementation.createHTMLDocument("").body).innerHTML="<form></form><form></form>",2===Ut.childNodes.length),S.parseHTML=function(e,t,n){return"string"!=typeof e?[]:("boolean"==typeof t&&(n=t,t=!1),t||(y.createHTMLDocument?((r=(t=E.implementation.createHTMLDocument("")).createElement("base")).href=E.location.href,t.head.appendChild(r)):t=E),o=!n&&[],(i=N.exec(e))?[t.createElement(i[1])]:(i=xe([e],t,o),o&&o.length&&S(o).remove(),S.merge([],i.childNodes)));var r,i,o},S.fn.load=function(e,t,n){var r,i,o,a=this,s=e.indexOf(" ");return-1<s&&(r=vt(e.slice(s)),e=e.slice(0,s)),m(t)?(n=t,t=void 0):t&&"object"==typeof t&&(i="POST"),0<a.length&&S.ajax({url:e,type:i||"GET",dataType:"html",data:t}).done(function(e){o=arguments,a.html(r?S("<div>").append(S.parseHTML(e)).find(r):e)}).always(n&&function(e,t){a.each(function(){n.apply(this,o||[e.responseText,t,e])})}),this},S.expr.pseudos.animated=function(t){return S.grep(S.timers,function(e){return t===e.elem}).length},S.offset={setOffset:function(e,t,n){var r,i,o,a,s,u,l=S.css(e,"position"),c=S(e),f={};"static"===l&&(e.style.position="relative"),s=c.offset(),o=S.css(e,"top"),u=S.css(e,"left"),("absolute"===l||"fixed"===l)&&-1<(o+u).indexOf("auto")?(a=(r=c.position()).top,i=r.left):(a=parseFloat(o)||0,i=parseFloat(u)||0),m(t)&&(t=t.call(e,n,S.extend({},s))),null!=t.top&&(f.top=t.top-s.top+a),null!=t.left&&(f.left=t.left-s.left+i),"using"in t?t.using.call(e,f):("number"==typeof f.top&&(f.top+="px"),"number"==typeof f.left&&(f.left+="px"),c.css(f))}},S.fn.extend({offset:function(t){if(arguments.length)return void 0===t?this:this.each(function(e){S.offset.setOffset(this,t,e)});var e,n,r=this[0];return r?r.getClientRects().length?(e=r.getBoundingClientRect(),n=r.ownerDocument.defaultView,{top:e.top+n.pageYOffset,left:e.left+n.pageXOffset}):{top:0,left:0}:void 0},position:function(){if(this[0]){var e,t,n,r=this[0],i={top:0,left:0};if("fixed"===S.css(r,"position"))t=r.getBoundingClientRect();else{t=this.offset(),n=r.ownerDocument,e=r.offsetParent||n.documentElement;while(e&&(e===n.body||e===n.documentElement)&&"static"===S.css(e,"position"))e=e.parentNode;e&&e!==r&&1===e.nodeType&&((i=S(e).offset()).top+=S.css(e,"borderTopWidth",!0),i.left+=S.css(e,"borderLeftWidth",!0))}return{top:t.top-i.top-S.css(r,"marginTop",!0),left:t.left-i.left-S.css(r,"marginLeft",!0)}}},offsetParent:function(){return this.map(function(){var e=this.offsetParent;while(e&&"static"===S.css(e,"position"))e=e.offsetParent;return e||re})}}),S.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(t,i){var o="pageYOffset"===i;S.fn[t]=function(e){return $(this,function(e,t,n){var r;if(x(e)?r=e:9===e.nodeType&&(r=e.defaultView),void 0===n)return r?r[i]:e[t];r?r.scrollTo(o?r.pageXOffset:n,o?n:r.pageYOffset):e[t]=n},t,e,arguments.length)}}),S.each(["top","left"],function(e,n){S.cssHooks[n]=$e(y.pixelPosition,function(e,t){if(t)return t=Be(e,n),Me.test(t)?S(e).position()[n]+"px":t})}),S.each({Height:"height",Width:"width"},function(a,s){S.each({padding:"inner"+a,content:s,"":"outer"+a},function(r,o){S.fn[o]=function(e,t){var n=arguments.length&&(r||"boolean"!=typeof e),i=r||(!0===e||!0===t?"margin":"border");return $(this,function(e,t,n){var r;return x(e)?0===o.indexOf("outer")?e["inner"+a]:e.document.documentElement["client"+a]:9===e.nodeType?(r=e.documentElement,Math.max(e.body["scroll"+a],r["scroll"+a],e.body["offset"+a],r["offset"+a],r["client"+a])):void 0===n?S.css(e,t,i):S.style(e,t,n,i)},s,n?e:void 0,n)}})}),S.each(["ajaxStart","ajaxStop","ajaxComplete","ajaxError","ajaxSuccess","ajaxSend"],function(e,t){S.fn[t]=function(e){return this.on(t,e)}}),S.fn.extend({bind:function(e,t,n){return this.on(e,null,t,n)},unbind:function(e,t){return this.off(e,null,t)},delegate:function(e,t,n,r){return this.on(t,e,n,r)},undelegate:function(e,t,n){return 1===arguments.length?this.off(e,"**"):this.off(t,e||"**",n)},hover:function(e,t){return this.mouseenter(e).mouseleave(t||e)}}),S.each("blur focus focusin focusout resize scroll click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup contextmenu".split(" "),function(e,n){S.fn[n]=function(e,t){return 0<arguments.length?this.on(n,null,e,t):this.trigger(n)}});var Gt=/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g;S.proxy=function(e,t){var n,r,i;if("string"==typeof t&&(n=e[t],t=e,e=n),m(e))return r=s.call(arguments,2),(i=function(){return e.apply(t||this,r.concat(s.call(arguments)))}).guid=e.guid=e.guid||S.guid++,i},S.holdReady=function(e){e?S.readyWait++:S.ready(!0)},S.isArray=Array.isArray,S.parseJSON=JSON.parse,S.nodeName=A,S.isFunction=m,S.isWindow=x,S.camelCase=X,S.type=w,S.now=Date.now,S.isNumeric=function(e){var t=S.type(e);return("number"===t||"string"===t)&&!isNaN(e-parseFloat(e))},S.trim=function(e){return null==e?"":(e+"").replace(Gt,"")},"function"==typeof define&&define.amd&&define("jquery",[],function(){return S});var Yt=C.jQuery,Qt=C.$;return S.noConflict=function(e){return C.$===S&&(C.$=Qt),e&&C.jQuery===S&&(C.jQuery=Yt),S},"undefined"==typeof e&&(C.jQuery=C.$=S),S});
- ;/*
- Copyright (C) Federico Zivolo 2020
- Distributed under the MIT License (license terms are at http://opensource.org/licenses/MIT).
- */(function(e,t){'object'==typeof exports&&'undefined'!=typeof module?module.exports=t():'function'==typeof define&&define.amd?define(t):e.Popper=t()})(this,function(){'use strict';function e(e){return e&&'[object Function]'==={}.toString.call(e)}function t(e,t){if(1!==e.nodeType)return[];var o=e.ownerDocument.defaultView,n=o.getComputedStyle(e,null);return t?n[t]:n}function o(e){return'HTML'===e.nodeName?e:e.parentNode||e.host}function n(e){if(!e)return document.body;switch(e.nodeName){case'HTML':case'BODY':return e.ownerDocument.body;case'#document':return e.body;}var i=t(e),r=i.overflow,p=i.overflowX,s=i.overflowY;return /(auto|scroll|overlay)/.test(r+s+p)?e:n(o(e))}function i(e){return e&&e.referenceNode?e.referenceNode:e}function r(e){return 11===e?re:10===e?pe:re||pe}function p(e){if(!e)return document.documentElement;for(var o=r(10)?document.body:null,n=e.offsetParent||null;n===o&&e.nextElementSibling;)n=(e=e.nextElementSibling).offsetParent;var i=n&&n.nodeName;return i&&'BODY'!==i&&'HTML'!==i?-1!==['TH','TD','TABLE'].indexOf(n.nodeName)&&'static'===t(n,'position')?p(n):n:e?e.ownerDocument.documentElement:document.documentElement}function s(e){var t=e.nodeName;return'BODY'!==t&&('HTML'===t||p(e.firstElementChild)===e)}function d(e){return null===e.parentNode?e:d(e.parentNode)}function a(e,t){if(!e||!e.nodeType||!t||!t.nodeType)return document.documentElement;var o=e.compareDocumentPosition(t)&Node.DOCUMENT_POSITION_FOLLOWING,n=o?e:t,i=o?t:e,r=document.createRange();r.setStart(n,0),r.setEnd(i,0);var l=r.commonAncestorContainer;if(e!==l&&t!==l||n.contains(i))return s(l)?l:p(l);var f=d(e);return f.host?a(f.host,t):a(e,d(t).host)}function l(e){var t=1<arguments.length&&void 0!==arguments[1]?arguments[1]:'top',o='top'===t?'scrollTop':'scrollLeft',n=e.nodeName;if('BODY'===n||'HTML'===n){var i=e.ownerDocument.documentElement,r=e.ownerDocument.scrollingElement||i;return r[o]}return e[o]}function f(e,t){var o=2<arguments.length&&void 0!==arguments[2]&&arguments[2],n=l(t,'top'),i=l(t,'left'),r=o?-1:1;return e.top+=n*r,e.bottom+=n*r,e.left+=i*r,e.right+=i*r,e}function m(e,t){var o='x'===t?'Left':'Top',n='Left'==o?'Right':'Bottom';return parseFloat(e['border'+o+'Width'])+parseFloat(e['border'+n+'Width'])}function h(e,t,o,n){return ee(t['offset'+e],t['scroll'+e],o['client'+e],o['offset'+e],o['scroll'+e],r(10)?parseInt(o['offset'+e])+parseInt(n['margin'+('Height'===e?'Top':'Left')])+parseInt(n['margin'+('Height'===e?'Bottom':'Right')]):0)}function c(e){var t=e.body,o=e.documentElement,n=r(10)&&getComputedStyle(o);return{height:h('Height',t,o,n),width:h('Width',t,o,n)}}function g(e){return le({},e,{right:e.left+e.width,bottom:e.top+e.height})}function u(e){var o={};try{if(r(10)){o=e.getBoundingClientRect();var n=l(e,'top'),i=l(e,'left');o.top+=n,o.left+=i,o.bottom+=n,o.right+=i}else o=e.getBoundingClientRect()}catch(t){}var p={left:o.left,top:o.top,width:o.right-o.left,height:o.bottom-o.top},s='HTML'===e.nodeName?c(e.ownerDocument):{},d=s.width||e.clientWidth||p.width,a=s.height||e.clientHeight||p.height,f=e.offsetWidth-d,h=e.offsetHeight-a;if(f||h){var u=t(e);f-=m(u,'x'),h-=m(u,'y'),p.width-=f,p.height-=h}return g(p)}function b(e,o){var i=2<arguments.length&&void 0!==arguments[2]&&arguments[2],p=r(10),s='HTML'===o.nodeName,d=u(e),a=u(o),l=n(e),m=t(o),h=parseFloat(m.borderTopWidth),c=parseFloat(m.borderLeftWidth);i&&s&&(a.top=ee(a.top,0),a.left=ee(a.left,0));var b=g({top:d.top-a.top-h,left:d.left-a.left-c,width:d.width,height:d.height});if(b.marginTop=0,b.marginLeft=0,!p&&s){var w=parseFloat(m.marginTop),y=parseFloat(m.marginLeft);b.top-=h-w,b.bottom-=h-w,b.left-=c-y,b.right-=c-y,b.marginTop=w,b.marginLeft=y}return(p&&!i?o.contains(l):o===l&&'BODY'!==l.nodeName)&&(b=f(b,o)),b}function w(e){var t=1<arguments.length&&void 0!==arguments[1]&&arguments[1],o=e.ownerDocument.documentElement,n=b(e,o),i=ee(o.clientWidth,window.innerWidth||0),r=ee(o.clientHeight,window.innerHeight||0),p=t?0:l(o),s=t?0:l(o,'left'),d={top:p-n.top+n.marginTop,left:s-n.left+n.marginLeft,width:i,height:r};return g(d)}function y(e){var n=e.nodeName;if('BODY'===n||'HTML'===n)return!1;if('fixed'===t(e,'position'))return!0;var i=o(e);return!!i&&y(i)}function E(e){if(!e||!e.parentElement||r())return document.documentElement;for(var o=e.parentElement;o&&'none'===t(o,'transform');)o=o.parentElement;return o||document.documentElement}function v(e,t,r,p){var s=4<arguments.length&&void 0!==arguments[4]&&arguments[4],d={top:0,left:0},l=s?E(e):a(e,i(t));if('viewport'===p)d=w(l,s);else{var f;'scrollParent'===p?(f=n(o(t)),'BODY'===f.nodeName&&(f=e.ownerDocument.documentElement)):'window'===p?f=e.ownerDocument.documentElement:f=p;var m=b(f,l,s);if('HTML'===f.nodeName&&!y(l)){var h=c(e.ownerDocument),g=h.height,u=h.width;d.top+=m.top-m.marginTop,d.bottom=g+m.top,d.left+=m.left-m.marginLeft,d.right=u+m.left}else d=m}r=r||0;var v='number'==typeof r;return d.left+=v?r:r.left||0,d.top+=v?r:r.top||0,d.right-=v?r:r.right||0,d.bottom-=v?r:r.bottom||0,d}function x(e){var t=e.width,o=e.height;return t*o}function O(e,t,o,n,i){var r=5<arguments.length&&void 0!==arguments[5]?arguments[5]:0;if(-1===e.indexOf('auto'))return e;var p=v(o,n,r,i),s={top:{width:p.width,height:t.top-p.top},right:{width:p.right-t.right,height:p.height},bottom:{width:p.width,height:p.bottom-t.bottom},left:{width:t.left-p.left,height:p.height}},d=Object.keys(s).map(function(e){return le({key:e},s[e],{area:x(s[e])})}).sort(function(e,t){return t.area-e.area}),a=d.filter(function(e){var t=e.width,n=e.height;return t>=o.clientWidth&&n>=o.clientHeight}),l=0<a.length?a[0].key:d[0].key,f=e.split('-')[1];return l+(f?'-'+f:'')}function L(e,t,o){var n=3<arguments.length&&void 0!==arguments[3]?arguments[3]:null,r=n?E(t):a(t,i(o));return b(o,r,n)}function S(e){var t=e.ownerDocument.defaultView,o=t.getComputedStyle(e),n=parseFloat(o.marginTop||0)+parseFloat(o.marginBottom||0),i=parseFloat(o.marginLeft||0)+parseFloat(o.marginRight||0),r={width:e.offsetWidth+i,height:e.offsetHeight+n};return r}function T(e){var t={left:'right',right:'left',bottom:'top',top:'bottom'};return e.replace(/left|right|bottom|top/g,function(e){return t[e]})}function C(e,t,o){o=o.split('-')[0];var n=S(e),i={width:n.width,height:n.height},r=-1!==['right','left'].indexOf(o),p=r?'top':'left',s=r?'left':'top',d=r?'height':'width',a=r?'width':'height';return i[p]=t[p]+t[d]/2-n[d]/2,i[s]=o===s?t[s]-n[a]:t[T(s)],i}function D(e,t){return Array.prototype.find?e.find(t):e.filter(t)[0]}function N(e,t,o){if(Array.prototype.findIndex)return e.findIndex(function(e){return e[t]===o});var n=D(e,function(e){return e[t]===o});return e.indexOf(n)}function P(t,o,n){var i=void 0===n?t:t.slice(0,N(t,'name',n));return i.forEach(function(t){t['function']&&console.warn('`modifier.function` is deprecated, use `modifier.fn`!');var n=t['function']||t.fn;t.enabled&&e(n)&&(o.offsets.popper=g(o.offsets.popper),o.offsets.reference=g(o.offsets.reference),o=n(o,t))}),o}function k(){if(!this.state.isDestroyed){var e={instance:this,styles:{},arrowStyles:{},attributes:{},flipped:!1,offsets:{}};e.offsets.reference=L(this.state,this.popper,this.reference,this.options.positionFixed),e.placement=O(this.options.placement,e.offsets.reference,this.popper,this.reference,this.options.modifiers.flip.boundariesElement,this.options.modifiers.flip.padding),e.originalPlacement=e.placement,e.positionFixed=this.options.positionFixed,e.offsets.popper=C(this.popper,e.offsets.reference,e.placement),e.offsets.popper.position=this.options.positionFixed?'fixed':'absolute',e=P(this.modifiers,e),this.state.isCreated?this.options.onUpdate(e):(this.state.isCreated=!0,this.options.onCreate(e))}}function W(e,t){return e.some(function(e){var o=e.name,n=e.enabled;return n&&o===t})}function B(e){for(var t=[!1,'ms','Webkit','Moz','O'],o=e.charAt(0).toUpperCase()+e.slice(1),n=0;n<t.length;n++){var i=t[n],r=i?''+i+o:e;if('undefined'!=typeof document.body.style[r])return r}return null}function H(){return this.state.isDestroyed=!0,W(this.modifiers,'applyStyle')&&(this.popper.removeAttribute('x-placement'),this.popper.style.position='',this.popper.style.top='',this.popper.style.left='',this.popper.style.right='',this.popper.style.bottom='',this.popper.style.willChange='',this.popper.style[B('transform')]=''),this.disableEventListeners(),this.options.removeOnDestroy&&this.popper.parentNode.removeChild(this.popper),this}function A(e){var t=e.ownerDocument;return t?t.defaultView:window}function M(e,t,o,i){var r='BODY'===e.nodeName,p=r?e.ownerDocument.defaultView:e;p.addEventListener(t,o,{passive:!0}),r||M(n(p.parentNode),t,o,i),i.push(p)}function F(e,t,o,i){o.updateBound=i,A(e).addEventListener('resize',o.updateBound,{passive:!0});var r=n(e);return M(r,'scroll',o.updateBound,o.scrollParents),o.scrollElement=r,o.eventsEnabled=!0,o}function I(){this.state.eventsEnabled||(this.state=F(this.reference,this.options,this.state,this.scheduleUpdate))}function R(e,t){return A(e).removeEventListener('resize',t.updateBound),t.scrollParents.forEach(function(e){e.removeEventListener('scroll',t.updateBound)}),t.updateBound=null,t.scrollParents=[],t.scrollElement=null,t.eventsEnabled=!1,t}function U(){this.state.eventsEnabled&&(cancelAnimationFrame(this.scheduleUpdate),this.state=R(this.reference,this.state))}function Y(e){return''!==e&&!isNaN(parseFloat(e))&&isFinite(e)}function V(e,t){Object.keys(t).forEach(function(o){var n='';-1!==['width','height','top','right','bottom','left'].indexOf(o)&&Y(t[o])&&(n='px'),e.style[o]=t[o]+n})}function j(e,t){Object.keys(t).forEach(function(o){var n=t[o];!1===n?e.removeAttribute(o):e.setAttribute(o,t[o])})}function q(e,t){var o=e.offsets,n=o.popper,i=o.reference,r=$,p=function(e){return e},s=r(i.width),d=r(n.width),a=-1!==['left','right'].indexOf(e.placement),l=-1!==e.placement.indexOf('-'),f=t?a||l||s%2==d%2?r:Z:p,m=t?r:p;return{left:f(1==s%2&&1==d%2&&!l&&t?n.left-1:n.left),top:m(n.top),bottom:m(n.bottom),right:f(n.right)}}function K(e,t,o){var n=D(e,function(e){var o=e.name;return o===t}),i=!!n&&e.some(function(e){return e.name===o&&e.enabled&&e.order<n.order});if(!i){var r='`'+t+'`';console.warn('`'+o+'`'+' modifier is required by '+r+' modifier in order to work, be sure to include it before '+r+'!')}return i}function z(e){return'end'===e?'start':'start'===e?'end':e}function G(e){var t=1<arguments.length&&void 0!==arguments[1]&&arguments[1],o=he.indexOf(e),n=he.slice(o+1).concat(he.slice(0,o));return t?n.reverse():n}function _(e,t,o,n){var i=e.match(/((?:\-|\+)?\d*\.?\d*)(.*)/),r=+i[1],p=i[2];if(!r)return e;if(0===p.indexOf('%')){var s;switch(p){case'%p':s=o;break;case'%':case'%r':default:s=n;}var d=g(s);return d[t]/100*r}if('vh'===p||'vw'===p){var a;return a='vh'===p?ee(document.documentElement.clientHeight,window.innerHeight||0):ee(document.documentElement.clientWidth,window.innerWidth||0),a/100*r}return r}function X(e,t,o,n){var i=[0,0],r=-1!==['right','left'].indexOf(n),p=e.split(/(\+|\-)/).map(function(e){return e.trim()}),s=p.indexOf(D(p,function(e){return-1!==e.search(/,|\s/)}));p[s]&&-1===p[s].indexOf(',')&&console.warn('Offsets separated by white space(s) are deprecated, use a comma (,) instead.');var d=/\s*,\s*|\s+/,a=-1===s?[p]:[p.slice(0,s).concat([p[s].split(d)[0]]),[p[s].split(d)[1]].concat(p.slice(s+1))];return a=a.map(function(e,n){var i=(1===n?!r:r)?'height':'width',p=!1;return e.reduce(function(e,t){return''===e[e.length-1]&&-1!==['+','-'].indexOf(t)?(e[e.length-1]=t,p=!0,e):p?(e[e.length-1]+=t,p=!1,e):e.concat(t)},[]).map(function(e){return _(e,i,t,o)})}),a.forEach(function(e,t){e.forEach(function(o,n){Y(o)&&(i[t]+=o*('-'===e[n-1]?-1:1))})}),i}function J(e,t){var o,n=t.offset,i=e.placement,r=e.offsets,p=r.popper,s=r.reference,d=i.split('-')[0];return o=Y(+n)?[+n,0]:X(n,p,s,d),'left'===d?(p.top+=o[0],p.left-=o[1]):'right'===d?(p.top+=o[0],p.left+=o[1]):'top'===d?(p.left+=o[0],p.top-=o[1]):'bottom'===d&&(p.left+=o[0],p.top+=o[1]),e.popper=p,e}var Q=Math.min,Z=Math.floor,$=Math.round,ee=Math.max,te='undefined'!=typeof window&&'undefined'!=typeof document&&'undefined'!=typeof navigator,oe=function(){for(var e=['Edge','Trident','Firefox'],t=0;t<e.length;t+=1)if(te&&0<=navigator.userAgent.indexOf(e[t]))return 1;return 0}(),ne=te&&window.Promise,ie=ne?function(e){var t=!1;return function(){t||(t=!0,window.Promise.resolve().then(function(){t=!1,e()}))}}:function(e){var t=!1;return function(){t||(t=!0,setTimeout(function(){t=!1,e()},oe))}},re=te&&!!(window.MSInputMethodContext&&document.documentMode),pe=te&&/MSIE 10/.test(navigator.userAgent),se=function(e,t){if(!(e instanceof t))throw new TypeError('Cannot call a class as a function')},de=function(){function e(e,t){for(var o,n=0;n<t.length;n++)o=t[n],o.enumerable=o.enumerable||!1,o.configurable=!0,'value'in o&&(o.writable=!0),Object.defineProperty(e,o.key,o)}return function(t,o,n){return o&&e(t.prototype,o),n&&e(t,n),t}}(),ae=function(e,t,o){return t in e?Object.defineProperty(e,t,{value:o,enumerable:!0,configurable:!0,writable:!0}):e[t]=o,e},le=Object.assign||function(e){for(var t,o=1;o<arguments.length;o++)for(var n in t=arguments[o],t)Object.prototype.hasOwnProperty.call(t,n)&&(e[n]=t[n]);return e},fe=te&&/Firefox/i.test(navigator.userAgent),me=['auto-start','auto','auto-end','top-start','top','top-end','right-start','right','right-end','bottom-end','bottom','bottom-start','left-end','left','left-start'],he=me.slice(3),ce={FLIP:'flip',CLOCKWISE:'clockwise',COUNTERCLOCKWISE:'counterclockwise'},ge=function(){function t(o,n){var i=this,r=2<arguments.length&&void 0!==arguments[2]?arguments[2]:{};se(this,t),this.scheduleUpdate=function(){return requestAnimationFrame(i.update)},this.update=ie(this.update.bind(this)),this.options=le({},t.Defaults,r),this.state={isDestroyed:!1,isCreated:!1,scrollParents:[]},this.reference=o&&o.jquery?o[0]:o,this.popper=n&&n.jquery?n[0]:n,this.options.modifiers={},Object.keys(le({},t.Defaults.modifiers,r.modifiers)).forEach(function(e){i.options.modifiers[e]=le({},t.Defaults.modifiers[e]||{},r.modifiers?r.modifiers[e]:{})}),this.modifiers=Object.keys(this.options.modifiers).map(function(e){return le({name:e},i.options.modifiers[e])}).sort(function(e,t){return e.order-t.order}),this.modifiers.forEach(function(t){t.enabled&&e(t.onLoad)&&t.onLoad(i.reference,i.popper,i.options,t,i.state)}),this.update();var p=this.options.eventsEnabled;p&&this.enableEventListeners(),this.state.eventsEnabled=p}return de(t,[{key:'update',value:function(){return k.call(this)}},{key:'destroy',value:function(){return H.call(this)}},{key:'enableEventListeners',value:function(){return I.call(this)}},{key:'disableEventListeners',value:function(){return U.call(this)}}]),t}();return ge.Utils=('undefined'==typeof window?global:window).PopperUtils,ge.placements=me,ge.Defaults={placement:'bottom',positionFixed:!1,eventsEnabled:!0,removeOnDestroy:!1,onCreate:function(){},onUpdate:function(){},modifiers:{shift:{order:100,enabled:!0,fn:function(e){var t=e.placement,o=t.split('-')[0],n=t.split('-')[1];if(n){var i=e.offsets,r=i.reference,p=i.popper,s=-1!==['bottom','top'].indexOf(o),d=s?'left':'top',a=s?'width':'height',l={start:ae({},d,r[d]),end:ae({},d,r[d]+r[a]-p[a])};e.offsets.popper=le({},p,l[n])}return e}},offset:{order:200,enabled:!0,fn:J,offset:0},preventOverflow:{order:300,enabled:!0,fn:function(e,t){var o=t.boundariesElement||p(e.instance.popper);e.instance.reference===o&&(o=p(o));var n=B('transform'),i=e.instance.popper.style,r=i.top,s=i.left,d=i[n];i.top='',i.left='',i[n]='';var a=v(e.instance.popper,e.instance.reference,t.padding,o,e.positionFixed);i.top=r,i.left=s,i[n]=d,t.boundaries=a;var l=t.priority,f=e.offsets.popper,m={primary:function(e){var o=f[e];return f[e]<a[e]&&!t.escapeWithReference&&(o=ee(f[e],a[e])),ae({},e,o)},secondary:function(e){var o='right'===e?'left':'top',n=f[o];return f[e]>a[e]&&!t.escapeWithReference&&(n=Q(f[o],a[e]-('right'===e?f.width:f.height))),ae({},o,n)}};return l.forEach(function(e){var t=-1===['left','top'].indexOf(e)?'secondary':'primary';f=le({},f,m[t](e))}),e.offsets.popper=f,e},priority:['left','right','top','bottom'],padding:5,boundariesElement:'scrollParent'},keepTogether:{order:400,enabled:!0,fn:function(e){var t=e.offsets,o=t.popper,n=t.reference,i=e.placement.split('-')[0],r=Z,p=-1!==['top','bottom'].indexOf(i),s=p?'right':'bottom',d=p?'left':'top',a=p?'width':'height';return o[s]<r(n[d])&&(e.offsets.popper[d]=r(n[d])-o[a]),o[d]>r(n[s])&&(e.offsets.popper[d]=r(n[s])),e}},arrow:{order:500,enabled:!0,fn:function(e,o){var n;if(!K(e.instance.modifiers,'arrow','keepTogether'))return e;var i=o.element;if('string'==typeof i){if(i=e.instance.popper.querySelector(i),!i)return e;}else if(!e.instance.popper.contains(i))return console.warn('WARNING: `arrow.element` must be child of its popper element!'),e;var r=e.placement.split('-')[0],p=e.offsets,s=p.popper,d=p.reference,a=-1!==['left','right'].indexOf(r),l=a?'height':'width',f=a?'Top':'Left',m=f.toLowerCase(),h=a?'left':'top',c=a?'bottom':'right',u=S(i)[l];d[c]-u<s[m]&&(e.offsets.popper[m]-=s[m]-(d[c]-u)),d[m]+u>s[c]&&(e.offsets.popper[m]+=d[m]+u-s[c]),e.offsets.popper=g(e.offsets.popper);var b=d[m]+d[l]/2-u/2,w=t(e.instance.popper),y=parseFloat(w['margin'+f]),E=parseFloat(w['border'+f+'Width']),v=b-e.offsets.popper[m]-y-E;return v=ee(Q(s[l]-u,v),0),e.arrowElement=i,e.offsets.arrow=(n={},ae(n,m,$(v)),ae(n,h,''),n),e},element:'[x-arrow]'},flip:{order:600,enabled:!0,fn:function(e,t){if(W(e.instance.modifiers,'inner'))return e;if(e.flipped&&e.placement===e.originalPlacement)return e;var o=v(e.instance.popper,e.instance.reference,t.padding,t.boundariesElement,e.positionFixed),n=e.placement.split('-')[0],i=T(n),r=e.placement.split('-')[1]||'',p=[];switch(t.behavior){case ce.FLIP:p=[n,i];break;case ce.CLOCKWISE:p=G(n);break;case ce.COUNTERCLOCKWISE:p=G(n,!0);break;default:p=t.behavior;}return p.forEach(function(s,d){if(n!==s||p.length===d+1)return e;n=e.placement.split('-')[0],i=T(n);var a=e.offsets.popper,l=e.offsets.reference,f=Z,m='left'===n&&f(a.right)>f(l.left)||'right'===n&&f(a.left)<f(l.right)||'top'===n&&f(a.bottom)>f(l.top)||'bottom'===n&&f(a.top)<f(l.bottom),h=f(a.left)<f(o.left),c=f(a.right)>f(o.right),g=f(a.top)<f(o.top),u=f(a.bottom)>f(o.bottom),b='left'===n&&h||'right'===n&&c||'top'===n&&g||'bottom'===n&&u,w=-1!==['top','bottom'].indexOf(n),y=!!t.flipVariations&&(w&&'start'===r&&h||w&&'end'===r&&c||!w&&'start'===r&&g||!w&&'end'===r&&u),E=!!t.flipVariationsByContent&&(w&&'start'===r&&c||w&&'end'===r&&h||!w&&'start'===r&&u||!w&&'end'===r&&g),v=y||E;(m||b||v)&&(e.flipped=!0,(m||b)&&(n=p[d+1]),v&&(r=z(r)),e.placement=n+(r?'-'+r:''),e.offsets.popper=le({},e.offsets.popper,C(e.instance.popper,e.offsets.reference,e.placement)),e=P(e.instance.modifiers,e,'flip'))}),e},behavior:'flip',padding:5,boundariesElement:'viewport',flipVariations:!1,flipVariationsByContent:!1},inner:{order:700,enabled:!1,fn:function(e){var t=e.placement,o=t.split('-')[0],n=e.offsets,i=n.popper,r=n.reference,p=-1!==['left','right'].indexOf(o),s=-1===['top','left'].indexOf(o);return i[p?'left':'top']=r[o]-(s?i[p?'width':'height']:0),e.placement=T(t),e.offsets.popper=g(i),e}},hide:{order:800,enabled:!0,fn:function(e){if(!K(e.instance.modifiers,'hide','preventOverflow'))return e;var t=e.offsets.reference,o=D(e.instance.modifiers,function(e){return'preventOverflow'===e.name}).boundaries;if(t.bottom<o.top||t.left>o.right||t.top>o.bottom||t.right<o.left){if(!0===e.hide)return e;e.hide=!0,e.attributes['x-out-of-boundaries']=''}else{if(!1===e.hide)return e;e.hide=!1,e.attributes['x-out-of-boundaries']=!1}return e}},computeStyle:{order:850,enabled:!0,fn:function(e,t){var o=t.x,n=t.y,i=e.offsets.popper,r=D(e.instance.modifiers,function(e){return'applyStyle'===e.name}).gpuAcceleration;void 0!==r&&console.warn('WARNING: `gpuAcceleration` option moved to `computeStyle` modifier and will not be supported in future versions of Popper.js!');var s,d,a=void 0===r?t.gpuAcceleration:r,l=p(e.instance.popper),f=u(l),m={position:i.position},h=q(e,2>window.devicePixelRatio||!fe),c='bottom'===o?'top':'bottom',g='right'===n?'left':'right',b=B('transform');if(d='bottom'==c?'HTML'===l.nodeName?-l.clientHeight+h.bottom:-f.height+h.bottom:h.top,s='right'==g?'HTML'===l.nodeName?-l.clientWidth+h.right:-f.width+h.right:h.left,a&&b)m[b]='translate3d('+s+'px, '+d+'px, 0)',m[c]=0,m[g]=0,m.willChange='transform';else{var w='bottom'==c?-1:1,y='right'==g?-1:1;m[c]=d*w,m[g]=s*y,m.willChange=c+', '+g}var E={"x-placement":e.placement};return e.attributes=le({},E,e.attributes),e.styles=le({},m,e.styles),e.arrowStyles=le({},e.offsets.arrow,e.arrowStyles),e},gpuAcceleration:!0,x:'bottom',y:'right'},applyStyle:{order:900,enabled:!0,fn:function(e){return V(e.instance.popper,e.styles),j(e.instance.popper,e.attributes),e.arrowElement&&Object.keys(e.arrowStyles).length&&V(e.arrowElement,e.arrowStyles),e},onLoad:function(e,t,o,n,i){var r=L(i,t,e,o.positionFixed),p=O(o.placement,r,t,e,o.modifiers.flip.boundariesElement,o.modifiers.flip.padding);return t.setAttribute('x-placement',p),V(t,{position:o.positionFixed?'fixed':'absolute'}),o},gpuAcceleration:void 0}}},ge});
- //# sourceMappingURL=popper.min.js.map
- ;//! openseadragon 2.4.1
- //! Built on 2019-07-03
- //! Git commit: v2.4.1-0-244790e
- //! http://openseadragon.github.io
- //! License: http://openseadragon.github.io/license/
- /*
- * OpenSeadragon
- *
- * Copyright (C) 2009 CodePlex Foundation
- * Copyright (C) 2010-2013 OpenSeadragon contributors
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are
- * met:
- *
- * - Redistributions of source code must retain the above copyright notice,
- * this list of conditions and the following disclaimer.
- *
- * - Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * - Neither the name of CodePlex Foundation nor the names of its
- * contributors may be used to endorse or promote products derived from
- * this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
- * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
- * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
- * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
- /*
- * Portions of this source file taken from jQuery:
- *
- * Copyright 2011 John Resig
- *
- * Permission is hereby granted, free of charge, to any person obtaining
- * a copy of this software and associated documentation files (the
- * "Software"), to deal in the Software without restriction, including
- * without limitation the rights to use, copy, modify, merge, publish,
- * distribute, sublicense, and/or sell copies of the Software, and to
- * permit persons to whom the Software is furnished to do so, subject to
- * the following conditions:
- *
- * The above copyright notice and this permission notice shall be
- * included in all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
- * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
- * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
- * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
- */
- /*
- * Portions of this source file taken from mattsnider.com:
- *
- * Copyright (c) 2006-2013 Matt Snider
- *
- * Permission is hereby granted, free of charge, to any person obtaining a
- * copy of this software and associated documentation files (the "Software"),
- * to deal in the Software without restriction, including without limitation
- * the rights to use, copy, modify, merge, publish, distribute, sublicense,
- * and/or sell copies of the Software, and to permit persons to whom the
- * Software is furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included
- * in all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
- * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
- * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
- * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT
- * OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR
- * THE USE OR OTHER DEALINGS IN THE SOFTWARE.
- */
- /**
- * @namespace OpenSeadragon
- * @version openseadragon 2.4.1
- * @classdesc The root namespace for OpenSeadragon. All utility methods
- * and classes are defined on or below this namespace.
- *
- */
- // Typedefs
- /**
- * All required and optional settings for instantiating a new instance of an OpenSeadragon image viewer.
- *
- * @typedef {Object} Options
- * @memberof OpenSeadragon
- *
- * @property {String} id
- * Id of the element to append the viewer's container element to. If not provided, the 'element' property must be provided.
- * If both the element and id properties are specified, the viewer is appended to the element provided in the element property.
- *
- * @property {Element} element
- * The element to append the viewer's container element to. If not provided, the 'id' property must be provided.
- * If both the element and id properties are specified, the viewer is appended to the element provided in the element property.
- *
- * @property {Array|String|Function|Object} [tileSources=null]
- * Tile source(s) to open initially. This is a complex parameter; see
- * {@link OpenSeadragon.Viewer#open} for details.
- *
- * @property {Number} [tabIndex=0]
- * Tabbing order index to assign to the viewer element. Positive values are selected in increasing order. When tabIndex is 0
- * source order is used. A negative value omits the viewer from the tabbing order.
- *
- * @property {Array} overlays Array of objects defining permanent overlays of
- * the viewer. The overlays added via this option and later removed with
- * {@link OpenSeadragon.Viewer#removeOverlay} will be added back when a new
- * image is opened.
- * To add overlays which can be definitively removed, one must use
- * {@link OpenSeadragon.Viewer#addOverlay}
- * If displaying a sequence of images, the overlays can be associated
- * with a specific page by passing the overlays array to the page's
- * tile source configuration.
- * Expected properties:
- * * x, y, (or px, py for pixel coordinates) to define the location.
- * * width, height in point if using x,y or in pixels if using px,py. If width
- * and height are specified, the overlay size is adjusted when zooming,
- * otherwise the size stays the size of the content (or the size defined by CSS).
- * * className to associate a class to the overlay
- * * id to set the overlay element. If an element with this id already exists,
- * it is reused, otherwise it is created. If not specified, a new element is
- * created.
- * * placement a string to define the relative position to the viewport.
- * Only used if no width and height are specified. Default: 'TOP_LEFT'.
- * See {@link OpenSeadragon.Placement} for possible values.
- *
- * @property {String} [xmlPath=null]
- * <strong>DEPRECATED</strong>. A relative path to load a DZI file from the server.
- * Prefer the newer Options.tileSources.
- *
- * @property {String} [prefixUrl='/images/']
- * Prepends the prefixUrl to navImages paths, which is very useful
- * since the default paths are rarely useful for production
- * environments.
- *
- * @property {OpenSeadragon.NavImages} [navImages]
- * An object with a property for each button or other built-in navigation
- * control, eg the current 'zoomIn', 'zoomOut', 'home', and 'fullpage'.
- * Each of those in turn provides an image path for each state of the button
- * or navigation control, eg 'REST', 'GROUP', 'HOVER', 'PRESS'. Finally the
- * image paths, by default assume there is a folder on the servers root path
- * called '/images', eg '/images/zoomin_rest.png'. If you need to adjust
- * these paths, prefer setting the option.prefixUrl rather than overriding
- * every image path directly through this setting.
- *
- * @property {Boolean} [debugMode=false]
- * TODO: provide an in-screen panel providing event detail feedback.
- *
- * @property {String} [debugGridColor=['#437AB2', '#1B9E77', '#D95F02', '#7570B3', '#E7298A', '#66A61E', '#E6AB02', '#A6761D', '#666666']]
- * The colors of grids in debug mode. Each tiled image's grid uses a consecutive color.
- * If there are more tiled images than provided colors, the color vector is recycled.
- *
- * @property {Number} [blendTime=0]
- * Specifies the duration of animation as higher or lower level tiles are
- * replacing the existing tile.
- *
- * @property {Boolean} [alwaysBlend=false]
- * Forces the tile to always blend. By default the tiles skip blending
- * when the blendTime is surpassed and the current animation frame would
- * not complete the blend.
- *
- * @property {Boolean} [autoHideControls=true]
- * If the user stops interacting with the viewport, fade the navigation
- * controls. Useful for presentation since the controls are by default
- * floated on top of the image the user is viewing.
- *
- * @property {Boolean} [immediateRender=false]
- * Render the best closest level first, ignoring the lowering levels which
- * provide the effect of very blurry to sharp. It is recommended to change
- * setting to true for mobile devices.
- *
- * @property {Number} [defaultZoomLevel=0]
- * Zoom level to use when image is first opened or the home button is clicked.
- * If 0, adjusts to fit viewer.
- *
- * @property {Number} [opacity=1]
- * Default proportional opacity of the tiled images (1=opaque, 0=hidden)
- * Hidden images do not draw and only load when preloading is allowed.
- *
- * @property {Boolean} [preload=false]
- * Default switch for loading hidden images (true loads, false blocks)
- *
- * @property {String} [compositeOperation=null]
- * Valid values are 'source-over', 'source-atop', 'source-in', 'source-out',
- * 'destination-over', 'destination-atop', 'destination-in',
- * 'destination-out', 'lighter', 'copy' or 'xor'
- *
- * @property {Boolean} [imageSmoothingEnabled=true]
- * Image smoothing for canvas rendering (only if canvas is used). Note: Ignored
- * by some (especially older) browsers which do not support this canvas property.
- * This property can be changed in {@link Viewer.Drawer.setImageSmoothingEnabled}.
- *
- * @property {String|CanvasGradient|CanvasPattern|Function} [placeholderFillStyle=null]
- * Draws a colored rectangle behind the tile if it is not loaded yet.
- * You can pass a CSS color value like "#FF8800".
- * When passing a function the tiledImage and canvas context are available as argument which is useful when you draw a gradient or pattern.
- *
- * @property {Number} [degrees=0]
- * Initial rotation.
- *
- * @property {Boolean} [flipped=false]
- * Initial flip state.
- *
- * @property {Number} [minZoomLevel=null]
- *
- * @property {Number} [maxZoomLevel=null]
- *
- * @property {Boolean} [homeFillsViewer=false]
- * Make the 'home' button fill the viewer and clip the image, instead
- * of fitting the image to the viewer and letterboxing.
- *
- * @property {Boolean} [panHorizontal=true]
- * Allow horizontal pan.
- *
- * @property {Boolean} [panVertical=true]
- * Allow vertical pan.
- *
- * @property {Boolean} [constrainDuringPan=false]
- *
- * @property {Boolean} [wrapHorizontal=false]
- * Set to true to force the image to wrap horizontally within the viewport.
- * Useful for maps or images representing the surface of a sphere or cylinder.
- *
- * @property {Boolean} [wrapVertical=false]
- * Set to true to force the image to wrap vertically within the viewport.
- * Useful for maps or images representing the surface of a sphere or cylinder.
- *
- * @property {Number} [minZoomImageRatio=0.9]
- * The minimum percentage ( expressed as a number between 0 and 1 ) of
- * the viewport height or width at which the zoom out will be constrained.
- * Setting it to 0, for example will allow you to zoom out infinity.
- *
- * @property {Number} [maxZoomPixelRatio=1.1]
- * The maximum ratio to allow a zoom-in to affect the highest level pixel
- * ratio. This can be set to Infinity to allow 'infinite' zooming into the
- * image though it is less effective visually if the HTML5 Canvas is not
- * available on the viewing device.
- *
- * @property {Number} [smoothTileEdgesMinZoom=1.1]
- * A zoom percentage ( where 1 is 100% ) of the highest resolution level.
- * When zoomed in beyond this value alternative compositing will be used to
- * smooth out the edges between tiles. This will have a performance impact.
- * Can be set to Infinity to turn it off.
- * Note: This setting is ignored on iOS devices due to a known bug (See {@link https://github.com/openseadragon/openseadragon/issues/952})
- *
- * @property {Boolean} [iOSDevice=?]
- * True if running on an iOS device, false otherwise.
- * Used to disable certain features that behave differently on iOS devices.
- *
- * @property {Boolean} [autoResize=true]
- * Set to false to prevent polling for viewer size changes. Useful for providing custom resize behavior.
- *
- * @property {Boolean} [preserveImageSizeOnResize=false]
- * Set to true to have the image size preserved when the viewer is resized. This requires autoResize=true (default).
- *
- * @property {Number} [minScrollDeltaTime=50]
- * Number of milliseconds between canvas-scroll events. This value helps normalize the rate of canvas-scroll
- * events between different devices, causing the faster devices to slow down enough to make the zoom control
- * more manageable.
- *
- * @property {Number} [rotationIncrement=90]
- * The number of degrees to rotate right or left when the rotate buttons or keyboard shortcuts are activated.
- *
- * @property {Number} [pixelsPerWheelLine=40]
- * For pixel-resolution scrolling devices, the number of pixels equal to one scroll line.
- *
- * @property {Number} [pixelsPerArrowPress=40]
- * The number of pixels viewport moves when an arrow key is pressed.
- *
- * @property {Number} [visibilityRatio=0.5]
- * The percentage ( as a number from 0 to 1 ) of the source image which
- * must be kept within the viewport. If the image is dragged beyond that
- * limit, it will 'bounce' back until the minimum visibility ratio is
- * achieved. Setting this to 0 and wrapHorizontal ( or wrapVertical ) to
- * true will provide the effect of an infinitely scrolling viewport.
- *
- * @property {Object} [viewportMargins={}]
- * Pushes the "home" region in from the sides by the specified amounts.
- * Possible subproperties (Numbers, in screen coordinates): left, top, right, bottom.
- *
- * @property {Number} [imageLoaderLimit=0]
- * The maximum number of image requests to make concurrently. By default
- * it is set to 0 allowing the browser to make the maximum number of
- * image requests in parallel as allowed by the browsers policy.
- *
- * @property {Number} [clickTimeThreshold=300]
- * The number of milliseconds within which a pointer down-up event combination
- * will be treated as a click gesture.
- *
- * @property {Number} [clickDistThreshold=5]
- * The maximum distance allowed between a pointer down event and a pointer up event
- * to be treated as a click gesture.
- *
- * @property {Number} [dblClickTimeThreshold=300]
- * The number of milliseconds within which two pointer down-up event combinations
- * will be treated as a double-click gesture.
- *
- * @property {Number} [dblClickDistThreshold=20]
- * The maximum distance allowed between two pointer click events
- * to be treated as a double-click gesture.
- *
- * @property {Number} [springStiffness=6.5]
- *
- * @property {Number} [animationTime=1.2]
- * Specifies the animation duration per each {@link OpenSeadragon.Spring}
- * which occur when the image is dragged or zoomed.
- *
- * @property {OpenSeadragon.GestureSettings} [gestureSettingsMouse]
- * Settings for gestures generated by a mouse pointer device. (See {@link OpenSeadragon.GestureSettings})
- * @property {Boolean} [gestureSettingsMouse.scrollToZoom=true] - Zoom on scroll gesture
- * @property {Boolean} [gestureSettingsMouse.clickToZoom=true] - Zoom on click gesture
- * @property {Boolean} [gestureSettingsMouse.dblClickToZoom=false] - Zoom on double-click gesture. Note: If set to true
- * then clickToZoom should be set to false to prevent multiple zooms.
- * @property {Boolean} [gestureSettingsMouse.pinchToZoom=false] - Zoom on pinch gesture
- * @property {Boolean} [gestureSettingsMouse.zoomToRefPoint=true] - If zoomToRefPoint is true, the zoom is centered at the pointer position. Otherwise,
- * the zoom is centered at the canvas center.
- * @property {Boolean} [gestureSettingsMouse.flickEnabled=false] - Enable flick gesture
- * @property {Number} [gestureSettingsMouse.flickMinSpeed=120] - If flickEnabled is true, the minimum speed to initiate a flick gesture (pixels-per-second)
- * @property {Number} [gestureSettingsMouse.flickMomentum=0.25] - If flickEnabled is true, the momentum factor for the flick gesture
- * @property {Boolean} [gestureSettingsMouse.pinchRotate=false] - If pinchRotate is true, the user will have the ability to rotate the image using their fingers.
- *
- * @property {OpenSeadragon.GestureSettings} [gestureSettingsTouch]
- * Settings for gestures generated by a touch pointer device. (See {@link OpenSeadragon.GestureSettings})
- * @property {Boolean} [gestureSettingsTouch.scrollToZoom=false] - Zoom on scroll gesture
- * @property {Boolean} [gestureSettingsTouch.clickToZoom=false] - Zoom on click gesture
- * @property {Boolean} [gestureSettingsTouch.dblClickToZoom=true] - Zoom on double-click gesture. Note: If set to true
- * then clickToZoom should be set to false to prevent multiple zooms.
- * @property {Boolean} [gestureSettingsTouch.pinchToZoom=true] - Zoom on pinch gesture
- * @property {Boolean} [gestureSettingsTouch.zoomToRefPoint=true] - If zoomToRefPoint is true, the zoom is centered at the pointer position. Otherwise,
- * the zoom is centered at the canvas center.
- * @property {Boolean} [gestureSettingsTouch.flickEnabled=true] - Enable flick gesture
- * @property {Number} [gestureSettingsTouch.flickMinSpeed=120] - If flickEnabled is true, the minimum speed to initiate a flick gesture (pixels-per-second)
- * @property {Number} [gestureSettingsTouch.flickMomentum=0.25] - If flickEnabled is true, the momentum factor for the flick gesture
- * @property {Boolean} [gestureSettingsTouch.pinchRotate=false] - If pinchRotate is true, the user will have the ability to rotate the image using their fingers.
- *
- * @property {OpenSeadragon.GestureSettings} [gestureSettingsPen]
- * Settings for gestures generated by a pen pointer device. (See {@link OpenSeadragon.GestureSettings})
- * @property {Boolean} [gestureSettingsPen.scrollToZoom=false] - Zoom on scroll gesture
- * @property {Boolean} [gestureSettingsPen.clickToZoom=true] - Zoom on click gesture
- * @property {Boolean} [gestureSettingsPen.dblClickToZoom=false] - Zoom on double-click gesture. Note: If set to true
- * then clickToZoom should be set to false to prevent multiple zooms.
- * @property {Boolean} [gestureSettingsPen.pinchToZoom=false] - Zoom on pinch gesture
- * @property {Boolean} [gestureSettingsPan.zoomToRefPoint=true] - If zoomToRefPoint is true, the zoom is centered at the pointer position. Otherwise,
- * the zoom is centered at the canvas center.
- * @property {Boolean} [gestureSettingsPen.flickEnabled=false] - Enable flick gesture
- * @property {Number} [gestureSettingsPen.flickMinSpeed=120] - If flickEnabled is true, the minimum speed to initiate a flick gesture (pixels-per-second)
- * @property {Number} [gestureSettingsPen.flickMomentum=0.25] - If flickEnabled is true, the momentum factor for the flick gesture
- * @property {Boolean} [gestureSettingsPen.pinchRotate=false] - If pinchRotate is true, the user will have the ability to rotate the image using their fingers.
- *
- * @property {OpenSeadragon.GestureSettings} [gestureSettingsUnknown]
- * Settings for gestures generated by unknown pointer devices. (See {@link OpenSeadragon.GestureSettings})
- * @property {Boolean} [gestureSettingsUnknown.scrollToZoom=true] - Zoom on scroll gesture
- * @property {Boolean} [gestureSettingsUnknown.clickToZoom=false] - Zoom on click gesture
- * @property {Boolean} [gestureSettingsUnknown.dblClickToZoom=true] - Zoom on double-click gesture. Note: If set to true
- * then clickToZoom should be set to false to prevent multiple zooms.
- * @property {Boolean} [gestureSettingsUnknown.pinchToZoom=true] - Zoom on pinch gesture
- * @property {Boolean} [gestureSettingsUnknown.zoomToRefPoint=true] - If zoomToRefPoint is true, the zoom is centered at the pointer position. Otherwise,
- * the zoom is centered at the canvas center.
- * @property {Boolean} [gestureSettingsUnknown.flickEnabled=true] - Enable flick gesture
- * @property {Number} [gestureSettingsUnknown.flickMinSpeed=120] - If flickEnabled is true, the minimum speed to initiate a flick gesture (pixels-per-second)
- * @property {Number} [gestureSettingsUnknown.flickMomentum=0.25] - If flickEnabled is true, the momentum factor for the flick gesture
- * @property {Boolean} [gestureSettingsUnknown.pinchRotate=false] - If pinchRotate is true, the user will have the ability to rotate the image using their fingers.
- *
- * @property {Number} [zoomPerClick=2.0]
- * The "zoom distance" per mouse click or touch tap. <em><strong>Note:</strong> Setting this to 1.0 effectively disables the click-to-zoom feature (also see gestureSettings[Mouse|Touch|Pen].clickToZoom/dblClickToZoom).</em>
- *
- * @property {Number} [zoomPerScroll=1.2]
- * The "zoom distance" per mouse scroll or touch pinch. <em><strong>Note:</strong> Setting this to 1.0 effectively disables the mouse-wheel zoom feature (also see gestureSettings[Mouse|Touch|Pen].scrollToZoom}).</em>
- *
- * @property {Number} [zoomPerSecond=1.0]
- * The number of seconds to animate a single zoom event over.
- *
- * @property {Boolean} [showNavigator=false]
- * Set to true to make the navigator minimap appear.
- *
- * @property {String} [navigatorId=navigator-GENERATED DATE]
- * The ID of a div to hold the navigator minimap.
- * If an ID is specified, the navigatorPosition, navigatorSizeRatio, navigatorMaintainSizeRatio, navigator[Top|Left|Height|Width] and navigatorAutoFade options will be ignored.
- * If an ID is not specified, a div element will be generated and placed on top of the main image.
- *
- * @property {String} [navigatorPosition='TOP_RIGHT']
- * Valid values are 'TOP_LEFT', 'TOP_RIGHT', 'BOTTOM_LEFT', 'BOTTOM_RIGHT', or 'ABSOLUTE'.<br>
- * If 'ABSOLUTE' is specified, then navigator[Top|Left|Height|Width] determines the size and position of the navigator minimap in the viewer, and navigatorSizeRatio and navigatorMaintainSizeRatio are ignored.<br>
- * For 'TOP_LEFT', 'TOP_RIGHT', 'BOTTOM_LEFT', and 'BOTTOM_RIGHT', the navigatorSizeRatio or navigator[Height|Width] values determine the size of the navigator minimap.
- *
- * @property {Number} [navigatorSizeRatio=0.2]
- * Ratio of navigator size to viewer size. Ignored if navigator[Height|Width] are specified.
- *
- * @property {Boolean} [navigatorMaintainSizeRatio=false]
- * If true, the navigator minimap is resized (using navigatorSizeRatio) when the viewer size changes.
- *
- * @property {Number|String} [navigatorTop=null]
- * Specifies the location of the navigator minimap (see navigatorPosition).
- *
- * @property {Number|String} [navigatorLeft=null]
- * Specifies the location of the navigator minimap (see navigatorPosition).
- *
- * @property {Number|String} [navigatorHeight=null]
- * Specifies the size of the navigator minimap (see navigatorPosition).
- * If specified, navigatorSizeRatio and navigatorMaintainSizeRatio are ignored.
- *
- * @property {Number|String} [navigatorWidth=null]
- * Specifies the size of the navigator minimap (see navigatorPosition).
- * If specified, navigatorSizeRatio and navigatorMaintainSizeRatio are ignored.
- *
- * @property {Boolean} [navigatorAutoResize=true]
- * Set to false to prevent polling for navigator size changes. Useful for providing custom resize behavior.
- * Setting to false can also improve performance when the navigator is configured to a fixed size.
- *
- * @property {Boolean} [navigatorAutoFade=true]
- * If the user stops interacting with the viewport, fade the navigator minimap.
- * Setting to false will make the navigator minimap always visible.
- *
- * @property {Boolean} [navigatorRotate=true]
- * If true, the navigator will be rotated together with the viewer.
- *
- * @property {String} [navigatorBackground='#000']
- * Specifies the background color of the navigator minimap
- *
- * @property {Number} [navigatorOpacity=0.8]
- * Specifies the opacity of the navigator minimap.
- *
- * @property {String} [navigatorBorderColor='#555']
- * Specifies the border color of the navigator minimap
- *
- * @property {String} [navigatorDisplayRegionColor='#900']
- * Specifies the border color of the display region rectangle of the navigator minimap
- *
- * @property {Number} [controlsFadeDelay=2000]
- * The number of milliseconds to wait once the user has stopped interacting
- * with the interface before beginning to fade the controls. Assumes
- * showNavigationControl and autoHideControls are both true.
- *
- * @property {Number} [controlsFadeLength=1500]
- * The number of milliseconds to animate the controls fading out.
- *
- * @property {Number} [maxImageCacheCount=200]
- * The max number of images we should keep in memory (per drawer).
- *
- * @property {Number} [timeout=30000]
- * The max number of milliseconds that an image job may take to complete.
- *
- * @property {Boolean} [useCanvas=true]
- * Set to false to not use an HTML canvas element for image rendering even if canvas is supported.
- *
- * @property {Number} [minPixelRatio=0.5]
- * The higher the minPixelRatio, the lower the quality of the image that
- * is considered sufficient to stop rendering a given zoom level. For
- * example, if you are targeting mobile devices with less bandwidth you may
- * try setting this to 1.5 or higher.
- *
- * @property {Boolean} [mouseNavEnabled=true]
- * Is the user able to interact with the image via mouse or touch. Default
- * interactions include draging the image in a plane, and zooming in toward
- * and away from the image.
- *
- * @property {Boolean} [showNavigationControl=true]
- * Set to false to prevent the appearance of the default navigation controls.<br>
- * Note that if set to false, the customs buttons set by the options
- * zoomInButton, zoomOutButton etc, are rendered inactive.
- *
- * @property {OpenSeadragon.ControlAnchor} [navigationControlAnchor=TOP_LEFT]
- * Placement of the default navigation controls.
- * To set the placement of the sequence controls, see the
- * sequenceControlAnchor option.
- *
- * @property {Boolean} [showZoomControl=true]
- * If true then + and - buttons to zoom in and out are displayed.<br>
- * Note: {@link OpenSeadragon.Options.showNavigationControl} is overriding
- * this setting when set to false.
- *
- * @property {Boolean} [showHomeControl=true]
- * If true then the 'Go home' button is displayed to go back to the original
- * zoom and pan.<br>
- * Note: {@link OpenSeadragon.Options.showNavigationControl} is overriding
- * this setting when set to false.
- *
- * @property {Boolean} [showFullPageControl=true]
- * If true then the 'Toggle full page' button is displayed to switch
- * between full page and normal mode.<br>
- * Note: {@link OpenSeadragon.Options.showNavigationControl} is overriding
- * this setting when set to false.
- *
- * @property {Boolean} [showRotationControl=false]
- * If true then the rotate left/right controls will be displayed as part of the
- * standard controls. This is also subject to the browser support for rotate
- * (e.g. viewer.drawer.canRotate()).<br>
- * Note: {@link OpenSeadragon.Options.showNavigationControl} is overriding
- * this setting when set to false.
- *
- * @property {Boolean} [showFlipControl=false]
- * If true then the flip controls will be displayed as part of the
- * standard controls.
- *
- * @property {Boolean} [showSequenceControl=true]
- * If sequenceMode is true, then provide buttons for navigating forward and
- * backward through the images.
- *
- * @property {OpenSeadragon.ControlAnchor} [sequenceControlAnchor=TOP_LEFT]
- * Placement of the default sequence controls.
- *
- * @property {Boolean} [navPrevNextWrap=false]
- * If true then the 'previous' button will wrap to the last image when
- * viewing the first image and the 'next' button will wrap to the first
- * image when viewing the last image.
- *
- * @property {String} zoomInButton
- * Set the id of the custom 'Zoom in' button to use.
- * This is useful to have a custom button anywhere in the web page.<br>
- * To only change the button images, consider using
- * {@link OpenSeadragon.Options.navImages}
- *
- * @property {String} zoomOutButton
- * Set the id of the custom 'Zoom out' button to use.
- * This is useful to have a custom button anywhere in the web page.<br>
- * To only change the button images, consider using
- * {@link OpenSeadragon.Options.navImages}
- *
- * @property {String} homeButton
- * Set the id of the custom 'Go home' button to use.
- * This is useful to have a custom button anywhere in the web page.<br>
- * To only change the button images, consider using
- * {@link OpenSeadragon.Options.navImages}
- *
- * @property {String} fullPageButton
- * Set the id of the custom 'Toggle full page' button to use.
- * This is useful to have a custom button anywhere in the web page.<br>
- * To only change the button images, consider using
- * {@link OpenSeadragon.Options.navImages}
- *
- * @property {String} rotateLeftButton
- * Set the id of the custom 'Rotate left' button to use.
- * This is useful to have a custom button anywhere in the web page.<br>
- * To only change the button images, consider using
- * {@link OpenSeadragon.Options.navImages}
- *
- * @property {String} rotateRightButton
- * Set the id of the custom 'Rotate right' button to use.
- * This is useful to have a custom button anywhere in the web page.<br>
- * To only change the button images, consider using
- * {@link OpenSeadragon.Options.navImages}
- *
- * @property {String} previousButton
- * Set the id of the custom 'Previous page' button to use.
- * This is useful to have a custom button anywhere in the web page.<br>
- * To only change the button images, consider using
- * {@link OpenSeadragon.Options.navImages}
- *
- * @property {String} nextButton
- * Set the id of the custom 'Next page' button to use.
- * This is useful to have a custom button anywhere in the web page.<br>
- * To only change the button images, consider using
- * {@link OpenSeadragon.Options.navImages}
- *
- * @property {Boolean} [sequenceMode=false]
- * Set to true to have the viewer treat your tilesources as a sequence of images to
- * be opened one at a time rather than all at once.
- *
- * @property {Number} [initialPage=0]
- * If sequenceMode is true, display this page initially.
- *
- * @property {Boolean} [preserveViewport=false]
- * If sequenceMode is true, then normally navigating through each image resets the
- * viewport to 'home' position. If preserveViewport is set to true, then the viewport
- * position is preserved when navigating between images in the sequence.
- *
- * @property {Boolean} [preserveOverlays=false]
- * If sequenceMode is true, then normally navigating through each image
- * resets the overlays.
- * If preserveOverlays is set to true, then the overlays added with {@link OpenSeadragon.Viewer#addOverlay}
- * are preserved when navigating between images in the sequence.
- * Note: setting preserveOverlays overrides any overlays specified in the global
- * "overlays" option for the Viewer. It's also not compatible with specifying
- * per-tileSource overlays via the options, as those overlays will persist
- * even after the tileSource is closed.
- *
- * @property {Boolean} [showReferenceStrip=false]
- * If sequenceMode is true, then display a scrolling strip of image thumbnails for
- * navigating through the images.
- *
- * @property {String} [referenceStripScroll='horizontal']
- *
- * @property {Element} [referenceStripElement=null]
- *
- * @property {Number} [referenceStripHeight=null]
- *
- * @property {Number} [referenceStripWidth=null]
- *
- * @property {String} [referenceStripPosition='BOTTOM_LEFT']
- *
- * @property {Number} [referenceStripSizeRatio=0.2]
- *
- * @property {Boolean} [collectionMode=false]
- * Set to true to have the viewer arrange your TiledImages in a grid or line.
- *
- * @property {Number} [collectionRows=3]
- * If collectionMode is true, specifies how many rows the grid should have. Use 1 to make a line.
- * If collectionLayout is 'vertical', specifies how many columns instead.
- *
- * @property {Number} [collectionColumns=0]
- * If collectionMode is true, specifies how many columns the grid should have. Use 1 to make a line.
- * If collectionLayout is 'vertical', specifies how many rows instead. Ignored if collectionRows is not set to a falsy value.
- *
- * @property {String} [collectionLayout='horizontal']
- * If collectionMode is true, specifies whether to arrange vertically or horizontally.
- *
- * @property {Number} [collectionTileSize=800]
- * If collectionMode is true, specifies the size, in viewport coordinates, for each TiledImage to fit into.
- * The TiledImage will be centered within a square of the specified size.
- *
- * @property {Number} [collectionTileMargin=80]
- * If collectionMode is true, specifies the margin, in viewport coordinates, between each TiledImage.
- *
- * @property {String|Boolean} [crossOriginPolicy=false]
- * Valid values are 'Anonymous', 'use-credentials', and false. If false, canvas requests will
- * not use CORS, and the canvas will be tainted.
- *
- * @property {Boolean} [ajaxWithCredentials=false]
- * Whether to set the withCredentials XHR flag for AJAX requests.
- * Note that this can be overridden at the {@link OpenSeadragon.TileSource} level.
- *
- * @property {Boolean} [loadTilesWithAjax=false]
- * Whether to load tile data using AJAX requests.
- * Note that this can be overridden at the {@link OpenSeadragon.TileSource} level.
- *
- * @property {Object} [ajaxHeaders={}]
- * A set of headers to include when making AJAX requests for tile sources or tiles.
- *
- */
- /**
- * Settings for gestures generated by a pointer device.
- *
- * @typedef {Object} GestureSettings
- * @memberof OpenSeadragon
- *
- * @property {Boolean} scrollToZoom
- * Set to false to disable zooming on scroll gestures.
- *
- * @property {Boolean} clickToZoom
- * Set to false to disable zooming on click gestures.
- *
- * @property {Boolean} dblClickToZoom
- * Set to false to disable zooming on double-click gestures. Note: If set to true
- * then clickToZoom should be set to false to prevent multiple zooms.
- *
- * @property {Boolean} pinchToZoom
- * Set to false to disable zooming on pinch gestures.
- *
- * @property {Boolean} flickEnabled
- * Set to false to disable the kinetic panning effect (flick) at the end of a drag gesture.
- *
- * @property {Number} flickMinSpeed
- * If flickEnabled is true, the minimum speed (in pixels-per-second) required to cause the kinetic panning effect (flick) at the end of a drag gesture.
- *
- * @property {Number} flickMomentum
- * If flickEnabled is true, a constant multiplied by the velocity to determine the distance of the kinetic panning effect (flick) at the end of a drag gesture.
- * A larger value will make the flick feel "lighter", while a smaller value will make the flick feel "heavier".
- * Note: springStiffness and animationTime also affect the "spring" used to stop the flick animation.
- *
- */
- /**
- * The names for the image resources used for the image navigation buttons.
- *
- * @typedef {Object} NavImages
- * @memberof OpenSeadragon
- *
- * @property {Object} zoomIn - Images for the zoom-in button.
- * @property {String} zoomIn.REST
- * @property {String} zoomIn.GROUP
- * @property {String} zoomIn.HOVER
- * @property {String} zoomIn.DOWN
- *
- * @property {Object} zoomOut - Images for the zoom-out button.
- * @property {String} zoomOut.REST
- * @property {String} zoomOut.GROUP
- * @property {String} zoomOut.HOVER
- * @property {String} zoomOut.DOWN
- *
- * @property {Object} home - Images for the home button.
- * @property {String} home.REST
- * @property {String} home.GROUP
- * @property {String} home.HOVER
- * @property {String} home.DOWN
- *
- * @property {Object} fullpage - Images for the full-page button.
- * @property {String} fullpage.REST
- * @property {String} fullpage.GROUP
- * @property {String} fullpage.HOVER
- * @property {String} fullpage.DOWN
- *
- * @property {Object} rotateleft - Images for the rotate left button.
- * @property {String} rotateleft.REST
- * @property {String} rotateleft.GROUP
- * @property {String} rotateleft.HOVER
- * @property {String} rotateleft.DOWN
- *
- * @property {Object} rotateright - Images for the rotate right button.
- * @property {String} rotateright.REST
- * @property {String} rotateright.GROUP
- * @property {String} rotateright.HOVER
- * @property {String} rotateright.DOWN
- *
- * @property {Object} flip - Images for the flip button.
- * @property {String} flip.REST
- * @property {String} flip.GROUP
- * @property {String} flip.HOVER
- * @property {String} flip.DOWN
- *
- * @property {Object} previous - Images for the previous button.
- * @property {String} previous.REST
- * @property {String} previous.GROUP
- * @property {String} previous.HOVER
- * @property {String} previous.DOWN
- *
- * @property {Object} next - Images for the next button.
- * @property {String} next.REST
- * @property {String} next.GROUP
- * @property {String} next.HOVER
- * @property {String} next.DOWN
- *
- */
- function OpenSeadragon( options ){
- return new OpenSeadragon.Viewer( options );
- }
- (function( $ ){
- /**
- * The OpenSeadragon version.
- *
- * @member {Object} OpenSeadragon.version
- * @property {String} versionStr - The version number as a string ('major.minor.revision').
- * @property {Number} major - The major version number.
- * @property {Number} minor - The minor version number.
- * @property {Number} revision - The revision number.
- * @since 1.0.0
- */
- $.version = {
- versionStr: '2.4.1',
- major: parseInt('2', 10),
- minor: parseInt('4', 10),
- revision: parseInt('1', 10)
- };
- /**
- * Taken from jquery 1.6.1
- * [[Class]] -> type pairs
- * @private
- */
- var class2type = {
- '[object Boolean]': 'boolean',
- '[object Number]': 'number',
- '[object String]': 'string',
- '[object Function]': 'function',
- '[object Array]': 'array',
- '[object Date]': 'date',
- '[object RegExp]': 'regexp',
- '[object Object]': 'object'
- },
- // Save a reference to some core methods
- toString = Object.prototype.toString,
- hasOwn = Object.prototype.hasOwnProperty;
- /**
- * Taken from jQuery 1.6.1
- * @function isFunction
- * @memberof OpenSeadragon
- * @see {@link http://www.jquery.com/ jQuery}
- */
- $.isFunction = function( obj ) {
- return $.type(obj) === "function";
- };
- /**
- * Taken from jQuery 1.6.1
- * @function isArray
- * @memberof OpenSeadragon
- * @see {@link http://www.jquery.com/ jQuery}
- */
- $.isArray = Array.isArray || function( obj ) {
- return $.type(obj) === "array";
- };
- /**
- * A crude way of determining if an object is a window.
- * Taken from jQuery 1.6.1
- * @function isWindow
- * @memberof OpenSeadragon
- * @see {@link http://www.jquery.com/ jQuery}
- */
- $.isWindow = function( obj ) {
- return obj && typeof obj === "object" && "setInterval" in obj;
- };
- /**
- * Taken from jQuery 1.6.1
- * @function type
- * @memberof OpenSeadragon
- * @see {@link http://www.jquery.com/ jQuery}
- */
- $.type = function( obj ) {
- return ( obj === null ) || ( obj === undefined ) ?
- String( obj ) :
- class2type[ toString.call(obj) ] || "object";
- };
- /**
- * Taken from jQuery 1.6.1
- * @function isPlainObject
- * @memberof OpenSeadragon
- * @see {@link http://www.jquery.com/ jQuery}
- */
- $.isPlainObject = function( obj ) {
- // Must be an Object.
- // Because of IE, we also have to check the presence of the constructor property.
- // Make sure that DOM nodes and window objects don't pass through, as well
- if ( !obj || OpenSeadragon.type(obj) !== "object" || obj.nodeType || $.isWindow( obj ) ) {
- return false;
- }
- // Not own constructor property must be Object
- if ( obj.constructor &&
- !hasOwn.call(obj, "constructor") &&
- !hasOwn.call(obj.constructor.prototype, "isPrototypeOf") ) {
- return false;
- }
- // Own properties are enumerated firstly, so to speed up,
- // if last one is own, then all properties are own.
- var lastKey;
- for (var key in obj ) {
- lastKey = key;
- }
- return lastKey === undefined || hasOwn.call( obj, lastKey );
- };
- /**
- * Taken from jQuery 1.6.1
- * @function isEmptyObject
- * @memberof OpenSeadragon
- * @see {@link http://www.jquery.com/ jQuery}
- */
- $.isEmptyObject = function( obj ) {
- for ( var name in obj ) {
- return false;
- }
- return true;
- };
- /**
- * Shim around Object.freeze. Does nothing if Object.freeze is not supported.
- * @param {Object} obj The object to freeze.
- * @return {Object} obj The frozen object.
- */
- $.freezeObject = function(obj) {
- if (Object.freeze) {
- $.freezeObject = Object.freeze;
- } else {
- $.freezeObject = function(obj) {
- return obj;
- };
- }
- return $.freezeObject(obj);
- };
- /**
- * True if the browser supports the HTML5 canvas element
- * @member {Boolean} supportsCanvas
- * @memberof OpenSeadragon
- */
- $.supportsCanvas = (function () {
- var canvasElement = document.createElement( 'canvas' );
- return !!( $.isFunction( canvasElement.getContext ) &&
- canvasElement.getContext( '2d' ) );
- }());
- /**
- * Test whether the submitted canvas is tainted or not.
- * @argument {Canvas} canvas The canvas to test.
- * @returns {Boolean} True if the canvas is tainted.
- */
- $.isCanvasTainted = function(canvas) {
- var isTainted = false;
- try {
- // We test if the canvas is tainted by retrieving data from it.
- // An exception will be raised if the canvas is tainted.
- canvas.getContext('2d').getImageData(0, 0, 1, 1);
- } catch (e) {
- isTainted = true;
- }
- return isTainted;
- };
- /**
- * A ratio comparing the device screen's pixel density to the canvas's backing store pixel density,
- * clamped to a minimum of 1. Defaults to 1 if canvas isn't supported by the browser.
- * @member {Number} pixelDensityRatio
- * @memberof OpenSeadragon
- */
- $.pixelDensityRatio = (function () {
- if ( $.supportsCanvas ) {
- var context = document.createElement('canvas').getContext('2d');
- var devicePixelRatio = window.devicePixelRatio || 1;
- var backingStoreRatio = context.webkitBackingStorePixelRatio ||
- context.mozBackingStorePixelRatio ||
- context.msBackingStorePixelRatio ||
- context.oBackingStorePixelRatio ||
- context.backingStorePixelRatio || 1;
- return Math.max(devicePixelRatio, 1) / backingStoreRatio;
- } else {
- return 1;
- }
- }());
- }( OpenSeadragon ));
- /**
- * This closure defines all static methods available to the OpenSeadragon
- * namespace. Many, if not most, are taked directly from jQuery for use
- * to simplify and reduce common programming patterns. More static methods
- * from jQuery may eventually make their way into this though we are
- * attempting to avoid an explicit dependency on jQuery only because
- * OpenSeadragon is a broadly useful code base and would be made less broad
- * by requiring jQuery fully.
- *
- * Some static methods have also been refactored from the original OpenSeadragon
- * project.
- */
- (function( $ ){
- /**
- * Taken from jQuery 1.6.1
- * @function extend
- * @memberof OpenSeadragon
- * @see {@link http://www.jquery.com/ jQuery}
- */
- $.extend = function() {
- var options,
- name,
- src,
- copy,
- copyIsArray,
- clone,
- target = arguments[ 0 ] || {},
- length = arguments.length,
- deep = false,
- i = 1;
- // Handle a deep copy situation
- if ( typeof target === "boolean" ) {
- deep = target;
- target = arguments[ 1 ] || {};
- // skip the boolean and the target
- i = 2;
- }
- // Handle case when target is a string or something (possible in deep copy)
- if ( typeof target !== "object" && !OpenSeadragon.isFunction( target ) ) {
- target = {};
- }
- // extend jQuery itself if only one argument is passed
- if ( length === i ) {
- target = this;
- --i;
- }
- for ( ; i < length; i++ ) {
- // Only deal with non-null/undefined values
- options = arguments[ i ];
- if ( options !== null || options !== undefined ) {
- // Extend the base object
- for ( name in options ) {
- src = target[ name ];
- copy = options[ name ];
- // Prevent never-ending loop
- if ( target === copy ) {
- continue;
- }
- // Recurse if we're merging plain objects or arrays
- if ( deep && copy && ( OpenSeadragon.isPlainObject( copy ) || ( copyIsArray = OpenSeadragon.isArray( copy ) ) ) ) {
- if ( copyIsArray ) {
- copyIsArray = false;
- clone = src && OpenSeadragon.isArray( src ) ? src : [];
- } else {
- clone = src && OpenSeadragon.isPlainObject( src ) ? src : {};
- }
- // Never move original objects, clone them
- target[ name ] = OpenSeadragon.extend( deep, clone, copy );
- // Don't bring in undefined values
- } else if ( copy !== undefined ) {
- target[ name ] = copy;
- }
- }
- }
- }
- // Return the modified object
- return target;
- };
- var isIOSDevice = function () {
- if (typeof navigator !== 'object') {
- return false;
- }
- var userAgent = navigator.userAgent;
- if (typeof userAgent !== 'string') {
- return false;
- }
- return userAgent.indexOf('iPhone') !== -1 ||
- userAgent.indexOf('iPad') !== -1 ||
- userAgent.indexOf('iPod') !== -1;
- };
- $.extend( $, /** @lends OpenSeadragon */{
- /**
- * The default values for the optional settings documented at {@link OpenSeadragon.Options}.
- * @static
- * @type {Object}
- */
- DEFAULT_SETTINGS: {
- //DATA SOURCE DETAILS
- xmlPath: null,
- tileSources: null,
- tileHost: null,
- initialPage: 0,
- crossOriginPolicy: false,
- ajaxWithCredentials: false,
- loadTilesWithAjax: false,
- ajaxHeaders: {},
- //PAN AND ZOOM SETTINGS AND CONSTRAINTS
- panHorizontal: true,
- panVertical: true,
- constrainDuringPan: false,
- wrapHorizontal: false,
- wrapVertical: false,
- visibilityRatio: 0.5, //-> how much of the viewer can be negative space
- minPixelRatio: 0.5, //->closer to 0 draws tiles meant for a higher zoom at this zoom
- defaultZoomLevel: 0,
- minZoomLevel: null,
- maxZoomLevel: null,
- homeFillsViewer: false,
- //UI RESPONSIVENESS AND FEEL
- clickTimeThreshold: 300,
- clickDistThreshold: 5,
- dblClickTimeThreshold: 300,
- dblClickDistThreshold: 20,
- springStiffness: 6.5,
- animationTime: 1.2,
- gestureSettingsMouse: {
- scrollToZoom: true,
- clickToZoom: true,
- dblClickToZoom: false,
- pinchToZoom: false,
- zoomToRefPoint: true,
- flickEnabled: false,
- flickMinSpeed: 120,
- flickMomentum: 0.25,
- pinchRotate: false
- },
- gestureSettingsTouch: {
- scrollToZoom: false,
- clickToZoom: false,
- dblClickToZoom: true,
- pinchToZoom: true,
- zoomToRefPoint: true,
- flickEnabled: true,
- flickMinSpeed: 120,
- flickMomentum: 0.25,
- pinchRotate: false
- },
- gestureSettingsPen: {
- scrollToZoom: false,
- clickToZoom: true,
- dblClickToZoom: false,
- pinchToZoom: false,
- zoomToRefPoint: true,
- flickEnabled: false,
- flickMinSpeed: 120,
- flickMomentum: 0.25,
- pinchRotate: false
- },
- gestureSettingsUnknown: {
- scrollToZoom: false,
- clickToZoom: false,
- dblClickToZoom: true,
- pinchToZoom: true,
- zoomToRefPoint: true,
- flickEnabled: true,
- flickMinSpeed: 120,
- flickMomentum: 0.25,
- pinchRotate: false
- },
- zoomPerClick: 2,
- zoomPerScroll: 1.2,
- zoomPerSecond: 1.0,
- blendTime: 0,
- alwaysBlend: false,
- autoHideControls: true,
- immediateRender: false,
- minZoomImageRatio: 0.9, //-> closer to 0 allows zoom out to infinity
- maxZoomPixelRatio: 1.1, //-> higher allows 'over zoom' into pixels
- smoothTileEdgesMinZoom: 1.1, //-> higher than maxZoomPixelRatio disables it
- iOSDevice: isIOSDevice(),
- pixelsPerWheelLine: 40,
- pixelsPerArrowPress: 40,
- autoResize: true,
- preserveImageSizeOnResize: false, // requires autoResize=true
- minScrollDeltaTime: 50,
- rotationIncrement: 90,
- //DEFAULT CONTROL SETTINGS
- showSequenceControl: true, //SEQUENCE
- sequenceControlAnchor: null, //SEQUENCE
- preserveViewport: false, //SEQUENCE
- preserveOverlays: false, //SEQUENCE
- navPrevNextWrap: false, //SEQUENCE
- showNavigationControl: true, //ZOOM/HOME/FULL/ROTATION
- navigationControlAnchor: null, //ZOOM/HOME/FULL/ROTATION
- showZoomControl: true, //ZOOM
- showHomeControl: true, //HOME
- showFullPageControl: true, //FULL
- showRotationControl: false, //ROTATION
- showFlipControl: false, //FLIP
- controlsFadeDelay: 2000, //ZOOM/HOME/FULL/SEQUENCE
- controlsFadeLength: 1500, //ZOOM/HOME/FULL/SEQUENCE
- mouseNavEnabled: true, //GENERAL MOUSE INTERACTIVITY
- //VIEWPORT NAVIGATOR SETTINGS
- showNavigator: false,
- navigatorId: null,
- navigatorPosition: null,
- navigatorSizeRatio: 0.2,
- navigatorMaintainSizeRatio: false,
- navigatorTop: null,
- navigatorLeft: null,
- navigatorHeight: null,
- navigatorWidth: null,
- navigatorAutoResize: true,
- navigatorAutoFade: true,
- navigatorRotate: true,
- navigatorBackground: '#000',
- navigatorOpacity: 0.8,
- navigatorBorderColor: '#555',
- navigatorDisplayRegionColor: '#900',
- // INITIAL ROTATION
- degrees: 0,
- // INITIAL FLIP STATE
- flipped: false,
- // APPEARANCE
- opacity: 1,
- preload: false,
- compositeOperation: null,
- imageSmoothingEnabled: true,
- placeholderFillStyle: null,
- //REFERENCE STRIP SETTINGS
- showReferenceStrip: false,
- referenceStripScroll: 'horizontal',
- referenceStripElement: null,
- referenceStripHeight: null,
- referenceStripWidth: null,
- referenceStripPosition: 'BOTTOM_LEFT',
- referenceStripSizeRatio: 0.2,
- //COLLECTION VISUALIZATION SETTINGS
- collectionRows: 3, //or columns depending on layout
- collectionColumns: 0, //columns in horizontal layout, rows in vertical layout
- collectionLayout: 'horizontal', //vertical
- collectionMode: false,
- collectionTileSize: 800,
- collectionTileMargin: 80,
- //PERFORMANCE SETTINGS
- imageLoaderLimit: 0,
- maxImageCacheCount: 200,
- timeout: 30000,
- useCanvas: true, // Use canvas element for drawing if available
- //INTERFACE RESOURCE SETTINGS
- prefixUrl: "/images/",
- navImages: {
- zoomIn: {
- REST: 'zoomin_rest.png',
- GROUP: 'zoomin_grouphover.png',
- HOVER: 'zoomin_hover.png',
- DOWN: 'zoomin_pressed.png'
- },
- zoomOut: {
- REST: 'zoomout_rest.png',
- GROUP: 'zoomout_grouphover.png',
- HOVER: 'zoomout_hover.png',
- DOWN: 'zoomout_pressed.png'
- },
- home: {
- REST: 'home_rest.png',
- GROUP: 'home_grouphover.png',
- HOVER: 'home_hover.png',
- DOWN: 'home_pressed.png'
- },
- fullpage: {
- REST: 'fullpage_rest.png',
- GROUP: 'fullpage_grouphover.png',
- HOVER: 'fullpage_hover.png',
- DOWN: 'fullpage_pressed.png'
- },
- rotateleft: {
- REST: 'rotateleft_rest.png',
- GROUP: 'rotateleft_grouphover.png',
- HOVER: 'rotateleft_hover.png',
- DOWN: 'rotateleft_pressed.png'
- },
- rotateright: {
- REST: 'rotateright_rest.png',
- GROUP: 'rotateright_grouphover.png',
- HOVER: 'rotateright_hover.png',
- DOWN: 'rotateright_pressed.png'
- },
- flip: { // Flip icon designed by Yaroslav Samoylov from the Noun Project and modified by Nelson Campos ncampos@criteriamarathon.com, https://thenounproject.com/term/flip/136289/
- REST: 'flip_rest.png',
- GROUP: 'flip_grouphover.png',
- HOVER: 'flip_hover.png',
- DOWN: 'flip_pressed.png'
- },
- previous: {
- REST: 'previous_rest.png',
- GROUP: 'previous_grouphover.png',
- HOVER: 'previous_hover.png',
- DOWN: 'previous_pressed.png'
- },
- next: {
- REST: 'next_rest.png',
- GROUP: 'next_grouphover.png',
- HOVER: 'next_hover.png',
- DOWN: 'next_pressed.png'
- }
- },
- //DEVELOPER SETTINGS
- debugMode: false,
- debugGridColor: ['#437AB2', '#1B9E77', '#D95F02', '#7570B3', '#E7298A', '#66A61E', '#E6AB02', '#A6761D', '#666666']
- },
- /**
- * TODO: get rid of this. I can't see how it's required at all. Looks
- * like an early legacy code artifact.
- * @static
- * @ignore
- */
- SIGNAL: "----seadragon----",
- /**
- * Returns a function which invokes the method as if it were a method belonging to the object.
- * @function
- * @param {Object} object
- * @param {Function} method
- * @returns {Function}
- */
- delegate: function( object, method ) {
- return function(){
- var args = arguments;
- if ( args === undefined ){
- args = [];
- }
- return method.apply( object, args );
- };
- },
- /**
- * An enumeration of Browser vendors.
- * @static
- * @type {Object}
- * @property {Number} UNKNOWN
- * @property {Number} IE
- * @property {Number} FIREFOX
- * @property {Number} SAFARI
- * @property {Number} CHROME
- * @property {Number} OPERA
- */
- BROWSERS: {
- UNKNOWN: 0,
- IE: 1,
- FIREFOX: 2,
- SAFARI: 3,
- CHROME: 4,
- OPERA: 5
- },
- /**
- * Returns a DOM Element for the given id or element.
- * @function
- * @param {String|Element} element Accepts an id or element.
- * @returns {Element} The element with the given id, null, or the element itself.
- */
- getElement: function( element ) {
- if ( typeof ( element ) == "string" ) {
- element = document.getElementById( element );
- }
- return element;
- },
- /**
- * Determines the position of the upper-left corner of the element.
- * @function
- * @param {Element|String} element - the element we want the position for.
- * @returns {OpenSeadragon.Point} - the position of the upper left corner of the element.
- */
- getElementPosition: function( element ) {
- var result = new $.Point(),
- isFixed,
- offsetParent;
- element = $.getElement( element );
- isFixed = $.getElementStyle( element ).position == "fixed";
- offsetParent = getOffsetParent( element, isFixed );
- while ( offsetParent ) {
- result.x += element.offsetLeft;
- result.y += element.offsetTop;
- if ( isFixed ) {
- result = result.plus( $.getPageScroll() );
- }
- element = offsetParent;
- isFixed = $.getElementStyle( element ).position == "fixed";
- offsetParent = getOffsetParent( element, isFixed );
- }
- return result;
- },
- /**
- * Determines the position of the upper-left corner of the element adjusted for current page and/or element scroll.
- * @function
- * @param {Element|String} element - the element we want the position for.
- * @returns {OpenSeadragon.Point} - the position of the upper left corner of the element adjusted for current page and/or element scroll.
- */
- getElementOffset: function( element ) {
- element = $.getElement( element );
- var doc = element && element.ownerDocument,
- docElement,
- win,
- boundingRect = { top: 0, left: 0 };
- if ( !doc ) {
- return new $.Point();
- }
- docElement = doc.documentElement;
- if ( typeof element.getBoundingClientRect !== typeof undefined ) {
- boundingRect = element.getBoundingClientRect();
- }
- win = ( doc == doc.window ) ?
- doc :
- ( doc.nodeType === 9 ) ?
- doc.defaultView || doc.parentWindow :
- false;
- return new $.Point(
- boundingRect.left + ( win.pageXOffset || docElement.scrollLeft ) - ( docElement.clientLeft || 0 ),
- boundingRect.top + ( win.pageYOffset || docElement.scrollTop ) - ( docElement.clientTop || 0 )
- );
- },
- /**
- * Determines the height and width of the given element.
- * @function
- * @param {Element|String} element
- * @returns {OpenSeadragon.Point}
- */
- getElementSize: function( element ) {
- element = $.getElement( element );
- return new $.Point(
- element.clientWidth,
- element.clientHeight
- );
- },
- /**
- * Returns the CSSStyle object for the given element.
- * @function
- * @param {Element|String} element
- * @returns {CSSStyle}
- */
- getElementStyle:
- document.documentElement.currentStyle ?
- function( element ) {
- element = $.getElement( element );
- return element.currentStyle;
- } :
- function( element ) {
- element = $.getElement( element );
- return window.getComputedStyle( element, "" );
- },
- /**
- * Returns the property with the correct vendor prefix appended.
- * @param {String} property the property name
- * @returns {String} the property with the correct prefix or null if not
- * supported.
- */
- getCssPropertyWithVendorPrefix: function(property) {
- var memo = {};
- $.getCssPropertyWithVendorPrefix = function(property) {
- if (memo[property] !== undefined) {
- return memo[property];
- }
- var style = document.createElement('div').style;
- var result = null;
- if (style[property] !== undefined) {
- result = property;
- } else {
- var prefixes = ['Webkit', 'Moz', 'MS', 'O',
- 'webkit', 'moz', 'ms', 'o'];
- var suffix = $.capitalizeFirstLetter(property);
- for (var i = 0; i < prefixes.length; i++) {
- var prop = prefixes[i] + suffix;
- if (style[prop] !== undefined) {
- result = prop;
- break;
- }
- }
- }
- memo[property] = result;
- return result;
- };
- return $.getCssPropertyWithVendorPrefix(property);
- },
- /**
- * Capitalizes the first letter of a string
- * @param {String} string
- * @returns {String} The string with the first letter capitalized
- */
- capitalizeFirstLetter: function(string) {
- return string.charAt(0).toUpperCase() + string.slice(1);
- },
- /**
- * Compute the modulo of a number but makes sure to always return
- * a positive value.
- * @param {Number} number the number to computes the modulo of
- * @param {Number} modulo the modulo
- * @returns {Number} the result of the modulo of number
- */
- positiveModulo: function(number, modulo) {
- var result = number % modulo;
- if (result < 0) {
- result += modulo;
- }
- return result;
- },
- /**
- * Determines if a point is within the bounding rectangle of the given element (hit-test).
- * @function
- * @param {Element|String} element
- * @param {OpenSeadragon.Point} point
- * @returns {Boolean}
- */
- pointInElement: function( element, point ) {
- element = $.getElement( element );
- var offset = $.getElementOffset( element ),
- size = $.getElementSize( element );
- return point.x >= offset.x && point.x < offset.x + size.x && point.y < offset.y + size.y && point.y >= offset.y;
- },
- /**
- * Gets the latest event, really only useful internally since its
- * specific to IE behavior.
- * @function
- * @param {Event} [event]
- * @returns {Event}
- * @deprecated For internal use only
- * @private
- */
- getEvent: function( event ) {
- if( event ){
- $.getEvent = function( event ) {
- return event;
- };
- } else {
- $.getEvent = function() {
- return window.event;
- };
- }
- return $.getEvent( event );
- },
- /**
- * Gets the position of the mouse on the screen for a given event.
- * @function
- * @param {Event} [event]
- * @returns {OpenSeadragon.Point}
- */
- getMousePosition: function( event ) {
- if ( typeof ( event.pageX ) == "number" ) {
- $.getMousePosition = function( event ){
- var result = new $.Point();
- event = $.getEvent( event );
- result.x = event.pageX;
- result.y = event.pageY;
- return result;
- };
- } else if ( typeof ( event.clientX ) == "number" ) {
- $.getMousePosition = function( event ){
- var result = new $.Point();
- event = $.getEvent( event );
- result.x =
- event.clientX +
- document.body.scrollLeft +
- document.documentElement.scrollLeft;
- result.y =
- event.clientY +
- document.body.scrollTop +
- document.documentElement.scrollTop;
- return result;
- };
- } else {
- throw new Error(
- "Unknown event mouse position, no known technique."
- );
- }
- return $.getMousePosition( event );
- },
- /**
- * Determines the page's current scroll position.
- * @function
- * @returns {OpenSeadragon.Point}
- */
- getPageScroll: function() {
- var docElement = document.documentElement || {},
- body = document.body || {};
- if ( typeof ( window.pageXOffset ) == "number" ) {
- $.getPageScroll = function(){
- return new $.Point(
- window.pageXOffset,
- window.pageYOffset
- );
- };
- } else if ( body.scrollLeft || body.scrollTop ) {
- $.getPageScroll = function(){
- return new $.Point(
- document.body.scrollLeft,
- document.body.scrollTop
- );
- };
- } else if ( docElement.scrollLeft || docElement.scrollTop ) {
- $.getPageScroll = function(){
- return new $.Point(
- document.documentElement.scrollLeft,
- document.documentElement.scrollTop
- );
- };
- } else {
- // We can't reassign the function yet, as there was no scroll.
- return new $.Point(0, 0);
- }
- return $.getPageScroll();
- },
- /**
- * Set the page scroll position.
- * @function
- * @returns {OpenSeadragon.Point}
- */
- setPageScroll: function( scroll ) {
- if ( typeof ( window.scrollTo ) !== "undefined" ) {
- $.setPageScroll = function( scroll ) {
- window.scrollTo( scroll.x, scroll.y );
- };
- } else {
- var originalScroll = $.getPageScroll();
- if ( originalScroll.x === scroll.x &&
- originalScroll.y === scroll.y ) {
- // We are already correctly positioned and there
- // is no way to detect the correct method.
- return;
- }
- document.body.scrollLeft = scroll.x;
- document.body.scrollTop = scroll.y;
- var currentScroll = $.getPageScroll();
- if ( currentScroll.x !== originalScroll.x &&
- currentScroll.y !== originalScroll.y ) {
- $.setPageScroll = function( scroll ) {
- document.body.scrollLeft = scroll.x;
- document.body.scrollTop = scroll.y;
- };
- return;
- }
- document.documentElement.scrollLeft = scroll.x;
- document.documentElement.scrollTop = scroll.y;
- currentScroll = $.getPageScroll();
- if ( currentScroll.x !== originalScroll.x &&
- currentScroll.y !== originalScroll.y ) {
- $.setPageScroll = function( scroll ) {
- document.documentElement.scrollLeft = scroll.x;
- document.documentElement.scrollTop = scroll.y;
- };
- return;
- }
- // We can't find anything working, so we do nothing.
- $.setPageScroll = function( scroll ) {
- };
- }
- return $.setPageScroll( scroll );
- },
- /**
- * Determines the size of the browsers window.
- * @function
- * @returns {OpenSeadragon.Point}
- */
- getWindowSize: function() {
- var docElement = document.documentElement || {},
- body = document.body || {};
- if ( typeof ( window.innerWidth ) == 'number' ) {
- $.getWindowSize = function(){
- return new $.Point(
- window.innerWidth,
- window.innerHeight
- );
- };
- } else if ( docElement.clientWidth || docElement.clientHeight ) {
- $.getWindowSize = function(){
- return new $.Point(
- document.documentElement.clientWidth,
- document.documentElement.clientHeight
- );
- };
- } else if ( body.clientWidth || body.clientHeight ) {
- $.getWindowSize = function(){
- return new $.Point(
- document.body.clientWidth,
- document.body.clientHeight
- );
- };
- } else {
- throw new Error("Unknown window size, no known technique.");
- }
- return $.getWindowSize();
- },
- /**
- * Wraps the given element in a nest of divs so that the element can
- * be easily centered using CSS tables
- * @function
- * @param {Element|String} element
- * @returns {Element} outermost wrapper element
- */
- makeCenteredNode: function( element ) {
- // Convert a possible ID to an actual HTMLElement
- element = $.getElement( element );
- /*
- CSS tables require you to have a display:table/row/cell hierarchy so we need to create
- three nested wrapper divs:
- */
- var wrappers = [
- $.makeNeutralElement( 'div' ),
- $.makeNeutralElement( 'div' ),
- $.makeNeutralElement( 'div' )
- ];
- // It feels like we should be able to pass style dicts to makeNeutralElement:
- $.extend(wrappers[0].style, {
- display: "table",
- height: "100%",
- width: "100%"
- });
- $.extend(wrappers[1].style, {
- display: "table-row"
- });
- $.extend(wrappers[2].style, {
- display: "table-cell",
- verticalAlign: "middle",
- textAlign: "center"
- });
- wrappers[0].appendChild(wrappers[1]);
- wrappers[1].appendChild(wrappers[2]);
- wrappers[2].appendChild(element);
- return wrappers[0];
- },
- /**
- * Creates an easily positionable element of the given type that therefor
- * serves as an excellent container element.
- * @function
- * @param {String} tagName
- * @returns {Element}
- */
- makeNeutralElement: function( tagName ) {
- var element = document.createElement( tagName ),
- style = element.style;
- style.background = "transparent none";
- style.border = "none";
- style.margin = "0px";
- style.padding = "0px";
- style.position = "static";
- return element;
- },
- /**
- * Returns the current milliseconds, using Date.now() if available
- * @function
- */
- now: function( ) {
- if (Date.now) {
- $.now = Date.now;
- } else {
- $.now = function() {
- return new Date().getTime();
- };
- }
- return $.now();
- },
- /**
- * Ensures an image is loaded correctly to support alpha transparency.
- * Generally only IE has issues doing this correctly for formats like
- * png.
- * @function
- * @param {String} src
- * @returns {Element}
- */
- makeTransparentImage: function( src ) {
- $.makeTransparentImage = function( src ){
- var img = $.makeNeutralElement( "img" );
- img.src = src;
- return img;
- };
- if ( $.Browser.vendor == $.BROWSERS.IE && $.Browser.version < 7 ) {
- $.makeTransparentImage = function( src ){
- var img = $.makeNeutralElement( "img" ),
- element = null;
- element = $.makeNeutralElement("span");
- element.style.display = "inline-block";
- img.onload = function() {
- element.style.width = element.style.width || img.width + "px";
- element.style.height = element.style.height || img.height + "px";
- img.onload = null;
- img = null; // to prevent memory leaks in IE
- };
- img.src = src;
- element.style.filter =
- "progid:DXImageTransform.Microsoft.AlphaImageLoader(src='" +
- src +
- "', sizingMethod='scale')";
- return element;
- };
- }
- return $.makeTransparentImage( src );
- },
- /**
- * Sets the opacity of the specified element.
- * @function
- * @param {Element|String} element
- * @param {Number} opacity
- * @param {Boolean} [usesAlpha]
- */
- setElementOpacity: function( element, opacity, usesAlpha ) {
- var ieOpacity,
- ieFilter;
- element = $.getElement( element );
- if ( usesAlpha && !$.Browser.alpha ) {
- opacity = Math.round( opacity );
- }
- if ( $.Browser.opacity ) {
- element.style.opacity = opacity < 1 ? opacity : "";
- } else {
- if ( opacity < 1 ) {
- ieOpacity = Math.round( 100 * opacity );
- ieFilter = "alpha(opacity=" + ieOpacity + ")";
- element.style.filter = ieFilter;
- } else {
- element.style.filter = "";
- }
- }
- },
- /**
- * Sets the specified element's touch-action style attribute to 'none'.
- * @function
- * @param {Element|String} element
- */
- setElementTouchActionNone: function( element ) {
- element = $.getElement( element );
- if ( typeof element.style.touchAction !== 'undefined' ) {
- element.style.touchAction = 'none';
- } else if ( typeof element.style.msTouchAction !== 'undefined' ) {
- element.style.msTouchAction = 'none';
- }
- },
- /**
- * Add the specified CSS class to the element if not present.
- * @function
- * @param {Element|String} element
- * @param {String} className
- */
- addClass: function( element, className ) {
- element = $.getElement( element );
- if (!element.className) {
- element.className = className;
- } else if ( ( ' ' + element.className + ' ' ).
- indexOf( ' ' + className + ' ' ) === -1 ) {
- element.className += ' ' + className;
- }
- },
- /**
- * Find the first index at which an element is found in an array or -1
- * if not present.
- *
- * Code taken and adapted from
- * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/indexOf#Compatibility
- *
- * @function
- * @param {Array} array The array from which to find the element
- * @param {Object} searchElement The element to find
- * @param {Number} [fromIndex=0] Index to start research.
- * @returns {Number} The index of the element in the array.
- */
- indexOf: function( array, searchElement, fromIndex ) {
- if ( Array.prototype.indexOf ) {
- this.indexOf = function( array, searchElement, fromIndex ) {
- return array.indexOf( searchElement, fromIndex );
- };
- } else {
- this.indexOf = function( array, searchElement, fromIndex ) {
- var i,
- pivot = ( fromIndex ) ? fromIndex : 0,
- length;
- if ( !array ) {
- throw new TypeError( );
- }
- length = array.length;
- if ( length === 0 || pivot >= length ) {
- return -1;
- }
- if ( pivot < 0 ) {
- pivot = length - Math.abs( pivot );
- }
- for ( i = pivot; i < length; i++ ) {
- if ( array[i] === searchElement ) {
- return i;
- }
- }
- return -1;
- };
- }
- return this.indexOf( array, searchElement, fromIndex );
- },
- /**
- * Remove the specified CSS class from the element.
- * @function
- * @param {Element|String} element
- * @param {String} className
- */
- removeClass: function( element, className ) {
- var oldClasses,
- newClasses = [],
- i;
- element = $.getElement( element );
- oldClasses = element.className.split( /\s+/ );
- for ( i = 0; i < oldClasses.length; i++ ) {
- if ( oldClasses[ i ] && oldClasses[ i ] !== className ) {
- newClasses.push( oldClasses[ i ] );
- }
- }
- element.className = newClasses.join(' ');
- },
- /**
- * Adds an event listener for the given element, eventName and handler.
- * @function
- * @param {Element|String} element
- * @param {String} eventName
- * @param {Function} handler
- * @param {Boolean} [useCapture]
- */
- addEvent: (function () {
- if ( window.addEventListener ) {
- return function ( element, eventName, handler, useCapture ) {
- element = $.getElement( element );
- element.addEventListener( eventName, handler, useCapture );
- };
- } else if ( window.attachEvent ) {
- return function ( element, eventName, handler, useCapture ) {
- element = $.getElement( element );
- element.attachEvent( 'on' + eventName, handler );
- };
- } else {
- throw new Error( "No known event model." );
- }
- }()),
- /**
- * Remove a given event listener for the given element, event type and
- * handler.
- * @function
- * @param {Element|String} element
- * @param {String} eventName
- * @param {Function} handler
- * @param {Boolean} [useCapture]
- */
- removeEvent: (function () {
- if ( window.removeEventListener ) {
- return function ( element, eventName, handler, useCapture ) {
- element = $.getElement( element );
- element.removeEventListener( eventName, handler, useCapture );
- };
- } else if ( window.detachEvent ) {
- return function( element, eventName, handler, useCapture ) {
- element = $.getElement( element );
- element.detachEvent( 'on' + eventName, handler );
- };
- } else {
- throw new Error( "No known event model." );
- }
- }()),
- /**
- * Cancels the default browser behavior had the event propagated all
- * the way up the DOM to the window object.
- * @function
- * @param {Event} [event]
- */
- cancelEvent: function( event ) {
- event = $.getEvent( event );
- if ( event.preventDefault ) {
- $.cancelEvent = function( event ){
- // W3C for preventing default
- event.preventDefault();
- };
- } else {
- $.cancelEvent = function( event ){
- event = $.getEvent( event );
- // legacy for preventing default
- event.cancel = true;
- // IE for preventing default
- event.returnValue = false;
- };
- }
- $.cancelEvent( event );
- },
- /**
- * Stops the propagation of the event up the DOM.
- * @function
- * @param {Event} [event]
- */
- stopEvent: function( event ) {
- event = $.getEvent( event );
- if ( event.stopPropagation ) {
- // W3C for stopping propagation
- $.stopEvent = function( event ){
- event.stopPropagation();
- };
- } else {
- // IE for stopping propagation
- $.stopEvent = function( event ){
- event = $.getEvent( event );
- event.cancelBubble = true;
- };
- }
- $.stopEvent( event );
- },
- /**
- * Similar to OpenSeadragon.delegate, but it does not immediately call
- * the method on the object, returning a function which can be called
- * repeatedly to delegate the method. It also allows additional arguments
- * to be passed during construction which will be added during each
- * invocation, and each invocation can add additional arguments as well.
- *
- * @function
- * @param {Object} object
- * @param {Function} method
- * @param [args] any additional arguments are passed as arguments to the
- * created callback
- * @returns {Function}
- */
- createCallback: function( object, method ) {
- //TODO: This pattern is painful to use and debug. It's much cleaner
- // to use pinning plus anonymous functions. Get rid of this
- // pattern!
- var initialArgs = [],
- i;
- for ( i = 2; i < arguments.length; i++ ) {
- initialArgs.push( arguments[ i ] );
- }
- return function() {
- var args = initialArgs.concat( [] ),
- i;
- for ( i = 0; i < arguments.length; i++ ) {
- args.push( arguments[ i ] );
- }
- return method.apply( object, args );
- };
- },
- /**
- * Retrieves the value of a url parameter from the window.location string.
- * @function
- * @param {String} key
- * @returns {String} The value of the url parameter or null if no param matches.
- */
- getUrlParameter: function( key ) {
- // eslint-disable-next-line no-use-before-define
- var value = URLPARAMS[ key ];
- return value ? value : null;
- },
- /**
- * Retrieves the protocol used by the url. The url can either be absolute
- * or relative.
- * @function
- * @private
- * @param {String} url The url to retrieve the protocol from.
- * @return {String} The protocol (http:, https:, file:, ftp: ...)
- */
- getUrlProtocol: function( url ) {
- var match = url.match(/^([a-z]+:)\/\//i);
- if ( match === null ) {
- // Relative URL, retrive the protocol from window.location
- return window.location.protocol;
- }
- return match[1].toLowerCase();
- },
- /**
- * Create an XHR object
- * @private
- * @param {type} [local] If set to true, the XHR will be file: protocol
- * compatible if possible (but may raise a warning in the browser).
- * @returns {XMLHttpRequest}
- */
- createAjaxRequest: function( local ) {
- // IE11 does not support window.ActiveXObject so we just try to
- // create one to see if it is supported.
- // See: http://msdn.microsoft.com/en-us/library/ie/dn423948%28v=vs.85%29.aspx
- var supportActiveX;
- try {
- /* global ActiveXObject:true */
- supportActiveX = !!new ActiveXObject( "Microsoft.XMLHTTP" );
- } catch( e ) {
- supportActiveX = false;
- }
- if ( supportActiveX ) {
- if ( window.XMLHttpRequest ) {
- $.createAjaxRequest = function( local ) {
- if ( local ) {
- return new ActiveXObject( "Microsoft.XMLHTTP" );
- }
- return new XMLHttpRequest();
- };
- } else {
- $.createAjaxRequest = function() {
- return new ActiveXObject( "Microsoft.XMLHTTP" );
- };
- }
- } else if ( window.XMLHttpRequest ) {
- $.createAjaxRequest = function() {
- return new XMLHttpRequest();
- };
- } else {
- throw new Error( "Browser doesn't support XMLHttpRequest." );
- }
- return $.createAjaxRequest( local );
- },
- /**
- * Makes an AJAX request.
- * @param {Object} options
- * @param {String} options.url - the url to request
- * @param {Function} options.success - a function to call on a successful response
- * @param {Function} options.error - a function to call on when an error occurs
- * @param {Object} options.headers - headers to add to the AJAX request
- * @param {String} options.responseType - the response type of the the AJAX request
- * @param {Boolean} [options.withCredentials=false] - whether to set the XHR's withCredentials
- * @throws {Error}
- * @returns {XMLHttpRequest}
- */
- makeAjaxRequest: function( url, onSuccess, onError ) {
- var withCredentials;
- var headers;
- var responseType;
- // Note that our preferred API is that you pass in a single object; the named
- // arguments are for legacy support.
- if( $.isPlainObject( url ) ){
- onSuccess = url.success;
- onError = url.error;
- withCredentials = url.withCredentials;
- headers = url.headers;
- responseType = url.responseType || null;
- url = url.url;
- }
- var protocol = $.getUrlProtocol( url );
- var request = $.createAjaxRequest( protocol === "file:" );
- if ( !$.isFunction( onSuccess ) ) {
- throw new Error( "makeAjaxRequest requires a success callback" );
- }
- request.onreadystatechange = function() {
- // 4 = DONE (https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest#Properties)
- if ( request.readyState == 4 ) {
- request.onreadystatechange = function(){};
- // With protocols other than http/https, a successful request status is in
- // the 200's on Firefox and 0 on other browsers
- if ( (request.status >= 200 && request.status < 300) ||
- ( request.status === 0 &&
- protocol !== "http:" &&
- protocol !== "https:" )) {
- onSuccess( request );
- } else {
- $.console.log( "AJAX request returned %d: %s", request.status, url );
- if ( $.isFunction( onError ) ) {
- onError( request );
- }
- }
- }
- };
- try {
- request.open( "GET", url, true );
- if (responseType) {
- request.responseType = responseType;
- }
- if (headers) {
- for (var headerName in headers) {
- if (headers.hasOwnProperty(headerName) && headers[headerName]) {
- request.setRequestHeader(headerName, headers[headerName]);
- }
- }
- }
- if (withCredentials) {
- request.withCredentials = true;
- }
- request.send(null);
- } catch (e) {
- var msg = e.message;
- /*
- IE < 10 does not support CORS and an XHR request to a different origin will fail as soon
- as send() is called. This is particularly easy to miss during development and appear in
- production if you use a CDN or domain sharding and the security policy is likely to break
- exception handlers since any attempt to access a property of the request object will
- raise an access denied TypeError inside the catch block.
- To be friendlier, we'll check for this specific error and add a documentation pointer
- to point developers in the right direction. We test the exception number because IE's
- error messages are localized.
- */
- var oldIE = $.Browser.vendor == $.BROWSERS.IE && $.Browser.version < 10;
- if ( oldIE && typeof ( e.number ) != "undefined" && e.number == -2147024891 ) {
- msg += "\nSee http://msdn.microsoft.com/en-us/library/ms537505(v=vs.85).aspx#xdomain";
- }
- $.console.log( "%s while making AJAX request: %s", e.name, msg );
- request.onreadystatechange = function(){};
- if (window.XDomainRequest) { // IE9 or IE8 might as well try to use XDomainRequest
- var xdr = new XDomainRequest();
- if (xdr) {
- xdr.onload = function (e) {
- if ( $.isFunction( onSuccess ) ) {
- onSuccess({ // Faking an xhr object
- responseText: xdr.responseText,
- status: 200, // XDomainRequest doesn't support status codes, so we just fake one! :/
- statusText: 'OK'
- });
- }
- };
- xdr.onerror = function (e) {
- if ($.isFunction(onError)) {
- onError({ // Faking an xhr object
- responseText: xdr.responseText,
- status: 444, // 444 No Response
- statusText: 'An error happened. Due to an XDomainRequest deficiency we can not extract any information about this error. Upgrade your browser.'
- });
- }
- };
- try {
- xdr.open('GET', url);
- xdr.send();
- } catch (e2) {
- if ( $.isFunction( onError ) ) {
- onError( request, e );
- }
- }
- }
- } else {
- if ( $.isFunction( onError ) ) {
- onError( request, e );
- }
- }
- }
- return request;
- },
- /**
- * Taken from jQuery 1.6.1
- * @function
- * @param {Object} options
- * @param {String} options.url
- * @param {Function} options.callback
- * @param {String} [options.param='callback'] The name of the url parameter
- * to request the jsonp provider with.
- * @param {String} [options.callbackName=] The name of the callback to
- * request the jsonp provider with.
- */
- jsonp: function( options ){
- var script,
- url = options.url,
- head = document.head ||
- document.getElementsByTagName( "head" )[ 0 ] ||
- document.documentElement,
- jsonpCallback = options.callbackName || 'openseadragon' + $.now(),
- previous = window[ jsonpCallback ],
- replace = "$1" + jsonpCallback + "$2",
- callbackParam = options.param || 'callback',
- callback = options.callback;
- url = url.replace( /(\=)\?(&|$)|\?\?/i, replace );
- // Add callback manually
- url += (/\?/.test( url ) ? "&" : "?") + callbackParam + "=" + jsonpCallback;
- // Install callback
- window[ jsonpCallback ] = function( response ) {
- if ( !previous ){
- try{
- delete window[ jsonpCallback ];
- }catch(e){
- //swallow
- }
- } else {
- window[ jsonpCallback ] = previous;
- }
- if( callback && $.isFunction( callback ) ){
- callback( response );
- }
- };
- script = document.createElement( "script" );
- //TODO: having an issue with async info requests
- if( undefined !== options.async || false !== options.async ){
- script.async = "async";
- }
- if ( options.scriptCharset ) {
- script.charset = options.scriptCharset;
- }
- script.src = url;
- // Attach handlers for all browsers
- script.onload = script.onreadystatechange = function( _, isAbort ) {
- if ( isAbort || !script.readyState || /loaded|complete/.test( script.readyState ) ) {
- // Handle memory leak in IE
- script.onload = script.onreadystatechange = null;
- // Remove the script
- if ( head && script.parentNode ) {
- head.removeChild( script );
- }
- // Dereference the script
- script = undefined;
- }
- };
- // Use insertBefore instead of appendChild to circumvent an IE6 bug.
- // This arises when a base node is used (#2709 and #4378).
- head.insertBefore( script, head.firstChild );
- },
- /**
- * Fully deprecated. Will throw an error.
- * @function
- * @deprecated use {@link OpenSeadragon.Viewer#open}
- */
- createFromDZI: function() {
- throw "OpenSeadragon.createFromDZI is deprecated, use Viewer.open.";
- },
- /**
- * Parses an XML string into a DOM Document.
- * @function
- * @param {String} string
- * @returns {Document}
- */
- parseXml: function( string ) {
- if ( window.DOMParser ) {
- $.parseXml = function( string ) {
- var xmlDoc = null,
- parser;
- parser = new DOMParser();
- xmlDoc = parser.parseFromString( string, "text/xml" );
- return xmlDoc;
- };
- } else if ( window.ActiveXObject ) {
- $.parseXml = function( string ) {
- var xmlDoc = null;
- xmlDoc = new ActiveXObject( "Microsoft.XMLDOM" );
- xmlDoc.async = false;
- xmlDoc.loadXML( string );
- return xmlDoc;
- };
- } else {
- throw new Error( "Browser doesn't support XML DOM." );
- }
- return $.parseXml( string );
- },
- /**
- * Parses a JSON string into a Javascript object.
- * @function
- * @param {String} string
- * @returns {Object}
- */
- parseJSON: function(string) {
- if (window.JSON && window.JSON.parse) {
- $.parseJSON = window.JSON.parse;
- } else {
- // Should only be used by IE8 in non standards mode
- $.parseJSON = function(string) {
- /*jshint evil:true*/
- //eslint-disable-next-line no-eval
- return eval('(' + string + ')');
- };
- }
- return $.parseJSON(string);
- },
- /**
- * Reports whether the image format is supported for tiling in this
- * version.
- * @function
- * @param {String} [extension]
- * @returns {Boolean}
- */
- imageFormatSupported: function( extension ) {
- extension = extension ? extension : "";
- // eslint-disable-next-line no-use-before-define
- return !!FILEFORMATS[ extension.toLowerCase() ];
- }
- });
- /**
- * The current browser vendor, version, and related information regarding detected features.
- * @member {Object} Browser
- * @memberof OpenSeadragon
- * @static
- * @type {Object}
- * @property {OpenSeadragon.BROWSERS} vendor - One of the {@link OpenSeadragon.BROWSERS} enumeration values.
- * @property {Number} version
- * @property {Boolean} alpha - Does the browser support image alpha transparency.
- */
- $.Browser = {
- vendor: $.BROWSERS.UNKNOWN,
- version: 0,
- alpha: true
- };
- var FILEFORMATS = {
- "bmp": false,
- "jpeg": true,
- "jpg": true,
- "png": true,
- "tif": false,
- "wdp": false
- },
- URLPARAMS = {};
- (function() {
- //A small auto-executing routine to determine the browser vendor,
- //version and supporting feature sets.
- var ver = navigator.appVersion,
- ua = navigator.userAgent,
- regex;
- //console.error( 'appName: ' + navigator.appName );
- //console.error( 'appVersion: ' + navigator.appVersion );
- //console.error( 'userAgent: ' + navigator.userAgent );
- switch( navigator.appName ){
- case "Microsoft Internet Explorer":
- if( !!window.attachEvent &&
- !!window.ActiveXObject ) {
- $.Browser.vendor = $.BROWSERS.IE;
- $.Browser.version = parseFloat(
- ua.substring(
- ua.indexOf( "MSIE" ) + 5,
- ua.indexOf( ";", ua.indexOf( "MSIE" ) ) )
- );
- }
- break;
- case "Netscape":
- if (window.addEventListener) {
- if ( ua.indexOf( "Firefox" ) >= 0 ) {
- $.Browser.vendor = $.BROWSERS.FIREFOX;
- $.Browser.version = parseFloat(
- ua.substring( ua.indexOf( "Firefox" ) + 8 )
- );
- } else if ( ua.indexOf( "Safari" ) >= 0 ) {
- $.Browser.vendor = ua.indexOf( "Chrome" ) >= 0 ?
- $.BROWSERS.CHROME :
- $.BROWSERS.SAFARI;
- $.Browser.version = parseFloat(
- ua.substring(
- ua.substring( 0, ua.indexOf( "Safari" ) ).lastIndexOf( "/" ) + 1,
- ua.indexOf( "Safari" )
- )
- );
- } else {
- regex = new RegExp( "Trident/.*rv:([0-9]{1,}[.0-9]{0,})");
- if ( regex.exec( ua ) !== null ) {
- $.Browser.vendor = $.BROWSERS.IE;
- $.Browser.version = parseFloat( RegExp.$1 );
- }
- }
- }
- break;
- case "Opera":
- $.Browser.vendor = $.BROWSERS.OPERA;
- $.Browser.version = parseFloat( ver );
- break;
- }
- // ignore '?' portion of query string
- var query = window.location.search.substring( 1 ),
- parts = query.split('&'),
- part,
- sep,
- i;
- for ( i = 0; i < parts.length; i++ ) {
- part = parts[ i ];
- sep = part.indexOf( '=' );
- if ( sep > 0 ) {
- URLPARAMS[ part.substring( 0, sep ) ] =
- decodeURIComponent( part.substring( sep + 1 ) );
- }
- }
- //determine if this browser supports image alpha transparency
- $.Browser.alpha = !(
- (
- $.Browser.vendor == $.BROWSERS.IE &&
- $.Browser.version < 9
- ) || (
- $.Browser.vendor == $.BROWSERS.CHROME &&
- $.Browser.version < 2
- )
- );
- //determine if this browser supports element.style.opacity
- $.Browser.opacity = !(
- $.Browser.vendor == $.BROWSERS.IE &&
- $.Browser.version < 9
- );
- })();
- //TODO: $.console is often used inside a try/catch block which generally
- // prevents allowings errors to occur with detection until a debugger
- // is attached. Although I've been guilty of the same anti-pattern
- // I eventually was convinced that errors should naturally propagate in
- // all but the most special cases.
- /**
- * A convenient alias for console when available, and a simple null
- * function when console is unavailable.
- * @static
- * @private
- */
- var nullfunction = function( msg ){
- //document.location.hash = msg;
- };
- $.console = window.console || {
- log: nullfunction,
- debug: nullfunction,
- info: nullfunction,
- warn: nullfunction,
- error: nullfunction,
- assert: nullfunction
- };
- // Adding support for HTML5's requestAnimationFrame as suggested by acdha.
- // Implementation taken from matt synder's post here:
- // http://mattsnider.com/cross-browser-and-legacy-supported-requestframeanimation/
- (function( w ) {
- // most browsers have an implementation
- var requestAnimationFrame = w.requestAnimationFrame ||
- w.mozRequestAnimationFrame ||
- w.webkitRequestAnimationFrame ||
- w.msRequestAnimationFrame;
- var cancelAnimationFrame = w.cancelAnimationFrame ||
- w.mozCancelAnimationFrame ||
- w.webkitCancelAnimationFrame ||
- w.msCancelAnimationFrame;
- // polyfill, when necessary
- if ( requestAnimationFrame && cancelAnimationFrame ) {
- // We can't assign these window methods directly to $ because they
- // expect their "this" to be "window", so we call them in wrappers.
- $.requestAnimationFrame = function(){
- return requestAnimationFrame.apply( w, arguments );
- };
- $.cancelAnimationFrame = function(){
- return cancelAnimationFrame.apply( w, arguments );
- };
- } else {
- var aAnimQueue = [],
- processing = [],
- iRequestId = 0,
- iIntervalId;
- // create a mock requestAnimationFrame function
- $.requestAnimationFrame = function( callback ) {
- aAnimQueue.push( [ ++iRequestId, callback ] );
- if ( !iIntervalId ) {
- iIntervalId = setInterval( function() {
- if ( aAnimQueue.length ) {
- var time = $.now();
- // Process all of the currently outstanding frame
- // requests, but none that get added during the
- // processing.
- // Swap the arrays so we don't have to create a new
- // array every frame.
- var temp = processing;
- processing = aAnimQueue;
- aAnimQueue = temp;
- while ( processing.length ) {
- processing.shift()[ 1 ]( time );
- }
- } else {
- // don't continue the interval, if unnecessary
- clearInterval( iIntervalId );
- iIntervalId = undefined;
- }
- }, 1000 / 50); // estimating support for 50 frames per second
- }
- return iRequestId;
- };
- // create a mock cancelAnimationFrame function
- $.cancelAnimationFrame = function( requestId ) {
- // find the request ID and remove it
- var i, j;
- for ( i = 0, j = aAnimQueue.length; i < j; i += 1 ) {
- if ( aAnimQueue[ i ][ 0 ] === requestId ) {
- aAnimQueue.splice( i, 1 );
- return;
- }
- }
- // If it's not in the queue, it may be in the set we're currently
- // processing (if cancelAnimationFrame is called from within a
- // requestAnimationFrame callback).
- for ( i = 0, j = processing.length; i < j; i += 1 ) {
- if ( processing[ i ][ 0 ] === requestId ) {
- processing.splice( i, 1 );
- return;
- }
- }
- };
- }
- })( window );
- /**
- * @private
- * @inner
- * @function
- * @param {Element} element
- * @param {Boolean} [isFixed]
- * @returns {Element}
- */
- function getOffsetParent( element, isFixed ) {
- if ( isFixed && element != document.body ) {
- return document.body;
- } else {
- return element.offsetParent;
- }
- }
- }(OpenSeadragon));
- // Universal Module Definition, supports CommonJS, AMD and simple script tag
- (function (root, factory) {
- if (typeof define === 'function' && define.amd) {
- // expose as amd module
- define([], factory);
- } else if (typeof module === 'object' && module.exports) {
- // expose as commonjs module
- module.exports = factory();
- } else {
- // expose as window.OpenSeadragon
- root.OpenSeadragon = factory();
- }
- }(this, function () {
- return OpenSeadragon;
- }));
- /*
- * OpenSeadragon - full-screen support functions
- *
- * Copyright (C) 2009 CodePlex Foundation
- * Copyright (C) 2010-2013 OpenSeadragon contributors
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are
- * met:
- *
- * - Redistributions of source code must retain the above copyright notice,
- * this list of conditions and the following disclaimer.
- *
- * - Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * - Neither the name of CodePlex Foundation nor the names of its
- * contributors may be used to endorse or promote products derived from
- * this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
- * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
- * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
- * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
- (function( $ ) {
- /**
- * Determine native full screen support we can get from the browser.
- * @member fullScreenApi
- * @memberof OpenSeadragon
- * @type {object}
- * @property {Boolean} supportsFullScreen Return true if full screen API is supported.
- * @property {Function} isFullScreen Return true if currently in full screen mode.
- * @property {Function} getFullScreenElement Return the element currently in full screen mode.
- * @property {Function} requestFullScreen Make a request to go in full screen mode.
- * @property {Function} exitFullScreen Make a request to exit full screen mode.
- * @property {Function} cancelFullScreen Deprecated, use exitFullScreen instead.
- * @property {String} fullScreenEventName Event fired when the full screen mode change.
- * @property {String} fullScreenErrorEventName Event fired when a request to go
- * in full screen mode failed.
- */
- var fullScreenApi = {
- supportsFullScreen: false,
- isFullScreen: function() { return false; },
- getFullScreenElement: function() { return null; },
- requestFullScreen: function() {},
- exitFullScreen: function() {},
- cancelFullScreen: function() {},
- fullScreenEventName: '',
- fullScreenErrorEventName: ''
- };
- // check for native support
- if ( document.exitFullscreen ) {
- // W3C standard
- fullScreenApi.supportsFullScreen = true;
- fullScreenApi.getFullScreenElement = function() {
- return document.fullscreenElement;
- };
- fullScreenApi.requestFullScreen = function( element ) {
- return element.requestFullscreen();
- };
- fullScreenApi.exitFullScreen = function() {
- document.exitFullscreen();
- };
- fullScreenApi.fullScreenEventName = "fullscreenchange";
- fullScreenApi.fullScreenErrorEventName = "fullscreenerror";
- } else if ( document.msExitFullscreen ) {
- // IE 11
- fullScreenApi.supportsFullScreen = true;
- fullScreenApi.getFullScreenElement = function() {
- return document.msFullscreenElement;
- };
- fullScreenApi.requestFullScreen = function( element ) {
- return element.msRequestFullscreen();
- };
- fullScreenApi.exitFullScreen = function() {
- document.msExitFullscreen();
- };
- fullScreenApi.fullScreenEventName = "MSFullscreenChange";
- fullScreenApi.fullScreenErrorEventName = "MSFullscreenError";
- } else if ( document.webkitExitFullscreen ) {
- // Recent webkit
- fullScreenApi.supportsFullScreen = true;
- fullScreenApi.getFullScreenElement = function() {
- return document.webkitFullscreenElement;
- };
- fullScreenApi.requestFullScreen = function( element ) {
- return element.webkitRequestFullscreen();
- };
- fullScreenApi.exitFullScreen = function() {
- document.webkitExitFullscreen();
- };
- fullScreenApi.fullScreenEventName = "webkitfullscreenchange";
- fullScreenApi.fullScreenErrorEventName = "webkitfullscreenerror";
- } else if ( document.webkitCancelFullScreen ) {
- // Old webkit
- fullScreenApi.supportsFullScreen = true;
- fullScreenApi.getFullScreenElement = function() {
- return document.webkitCurrentFullScreenElement;
- };
- fullScreenApi.requestFullScreen = function( element ) {
- return element.webkitRequestFullScreen();
- };
- fullScreenApi.exitFullScreen = function() {
- document.webkitCancelFullScreen();
- };
- fullScreenApi.fullScreenEventName = "webkitfullscreenchange";
- fullScreenApi.fullScreenErrorEventName = "webkitfullscreenerror";
- } else if ( document.mozCancelFullScreen ) {
- // Firefox
- fullScreenApi.supportsFullScreen = true;
- fullScreenApi.getFullScreenElement = function() {
- return document.mozFullScreenElement;
- };
- fullScreenApi.requestFullScreen = function( element ) {
- return element.mozRequestFullScreen();
- };
- fullScreenApi.exitFullScreen = function() {
- document.mozCancelFullScreen();
- };
- fullScreenApi.fullScreenEventName = "mozfullscreenchange";
- fullScreenApi.fullScreenErrorEventName = "mozfullscreenerror";
- }
- fullScreenApi.isFullScreen = function() {
- return fullScreenApi.getFullScreenElement() !== null;
- };
- fullScreenApi.cancelFullScreen = function() {
- $.console.error("cancelFullScreen is deprecated. Use exitFullScreen instead.");
- fullScreenApi.exitFullScreen();
- };
- // export api
- $.extend( $, fullScreenApi );
- })( OpenSeadragon );
- /*
- * OpenSeadragon - EventSource
- *
- * Copyright (C) 2009 CodePlex Foundation
- * Copyright (C) 2010-2013 OpenSeadragon contributors
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are
- * met:
- *
- * - Redistributions of source code must retain the above copyright notice,
- * this list of conditions and the following disclaimer.
- *
- * - Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * - Neither the name of CodePlex Foundation nor the names of its
- * contributors may be used to endorse or promote products derived from
- * this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
- * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
- * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
- * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
- (function($){
- /**
- * Event handler method signature used by all OpenSeadragon events.
- *
- * @callback EventHandler
- * @memberof OpenSeadragon
- * @param {Object} event - See individual events for event-specific properties.
- */
- /**
- * @class EventSource
- * @classdesc For use by classes which want to support custom, non-browser events.
- *
- * @memberof OpenSeadragon
- */
- $.EventSource = function() {
- this.events = {};
- };
- /** @lends OpenSeadragon.EventSource.prototype */
- $.EventSource.prototype = {
- /**
- * Add an event handler to be triggered only once (or a given number of times)
- * for a given event.
- * @function
- * @param {String} eventName - Name of event to register.
- * @param {OpenSeadragon.EventHandler} handler - Function to call when event
- * is triggered.
- * @param {Object} [userData=null] - Arbitrary object to be passed unchanged
- * to the handler.
- * @param {Number} [times=1] - The number of times to handle the event
- * before removing it.
- */
- addOnceHandler: function(eventName, handler, userData, times) {
- var self = this;
- times = times || 1;
- var count = 0;
- var onceHandler = function(event) {
- count++;
- if (count === times) {
- self.removeHandler(eventName, onceHandler);
- }
- handler(event);
- };
- this.addHandler(eventName, onceHandler, userData);
- },
- /**
- * Add an event handler for a given event.
- * @function
- * @param {String} eventName - Name of event to register.
- * @param {OpenSeadragon.EventHandler} handler - Function to call when event is triggered.
- * @param {Object} [userData=null] - Arbitrary object to be passed unchanged to the handler.
- */
- addHandler: function ( eventName, handler, userData ) {
- var events = this.events[ eventName ];
- if ( !events ) {
- this.events[ eventName ] = events = [];
- }
- if ( handler && $.isFunction( handler ) ) {
- events[ events.length ] = { handler: handler, userData: userData || null };
- }
- },
- /**
- * Remove a specific event handler for a given event.
- * @function
- * @param {String} eventName - Name of event for which the handler is to be removed.
- * @param {OpenSeadragon.EventHandler} handler - Function to be removed.
- */
- removeHandler: function ( eventName, handler ) {
- var events = this.events[ eventName ],
- handlers = [],
- i;
- if ( !events ) {
- return;
- }
- if ( $.isArray( events ) ) {
- for ( i = 0; i < events.length; i++ ) {
- if ( events[i].handler !== handler ) {
- handlers.push( events[ i ] );
- }
- }
- this.events[ eventName ] = handlers;
- }
- },
- /**
- * Remove all event handlers for a given event type. If no type is given all
- * event handlers for every event type are removed.
- * @function
- * @param {String} eventName - Name of event for which all handlers are to be removed.
- */
- removeAllHandlers: function( eventName ) {
- if ( eventName ){
- this.events[ eventName ] = [];
- } else{
- for ( var eventType in this.events ) {
- this.events[ eventType ] = [];
- }
- }
- },
- /**
- * Get a function which iterates the list of all handlers registered for a given event, calling the handler for each.
- * @function
- * @param {String} eventName - Name of event to get handlers for.
- */
- getHandler: function ( eventName ) {
- var events = this.events[ eventName ];
- if ( !events || !events.length ) {
- return null;
- }
- events = events.length === 1 ?
- [ events[ 0 ] ] :
- Array.apply( null, events );
- return function ( source, args ) {
- var i,
- length = events.length;
- for ( i = 0; i < length; i++ ) {
- if ( events[ i ] ) {
- args.eventSource = source;
- args.userData = events[ i ].userData;
- events[ i ].handler( args );
- }
- }
- };
- },
- /**
- * Trigger an event, optionally passing additional information.
- * @function
- * @param {String} eventName - Name of event to register.
- * @param {Object} eventArgs - Event-specific data.
- */
- raiseEvent: function( eventName, eventArgs ) {
- //uncomment if you want to get a log of all events
- //$.console.log( eventName );
- var handler = this.getHandler( eventName );
- if ( handler ) {
- if ( !eventArgs ) {
- eventArgs = {};
- }
- handler( this, eventArgs );
- }
- }
- };
- }( OpenSeadragon ));
- /*
- * OpenSeadragon - MouseTracker
- *
- * Copyright (C) 2009 CodePlex Foundation
- * Copyright (C) 2010-2013 OpenSeadragon contributors
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are
- * met:
- *
- * - Redistributions of source code must retain the above copyright notice,
- * this list of conditions and the following disclaimer.
- *
- * - Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * - Neither the name of CodePlex Foundation nor the names of its
- * contributors may be used to endorse or promote products derived from
- * this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
- * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
- * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
- * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
- (function ( $ ) {
- // All MouseTracker instances
- var MOUSETRACKERS = [];
- // dictionary from hash to private properties
- var THIS = {};
- /**
- * @class MouseTracker
- * @classdesc Provides simplified handling of common pointer device (mouse, touch, pen, etc.) gestures
- * and keyboard events on a specified element.
- * @memberof OpenSeadragon
- * @param {Object} options
- * Allows configurable properties to be entirely specified by passing
- * an options object to the constructor. The constructor also supports
- * the original positional arguments 'element', 'clickTimeThreshold',
- * and 'clickDistThreshold' in that order.
- * @param {Element|String} options.element
- * A reference to an element or an element id for which the pointer/key
- * events will be monitored.
- * @param {Boolean} [options.startDisabled=false]
- * If true, event tracking on the element will not start until
- * {@link OpenSeadragon.MouseTracker.setTracking|setTracking} is called.
- * @param {Number} options.clickTimeThreshold
- * The number of milliseconds within which a pointer down-up event combination
- * will be treated as a click gesture.
- * @param {Number} options.clickDistThreshold
- * The maximum distance allowed between a pointer down event and a pointer up event
- * to be treated as a click gesture.
- * @param {Number} options.dblClickTimeThreshold
- * The number of milliseconds within which two pointer down-up event combinations
- * will be treated as a double-click gesture.
- * @param {Number} options.dblClickDistThreshold
- * The maximum distance allowed between two pointer click events
- * to be treated as a click gesture.
- * @param {Number} [options.stopDelay=50]
- * The number of milliseconds without pointer move before the stop
- * event is fired.
- * @param {OpenSeadragon.EventHandler} [options.enterHandler=null]
- * An optional handler for pointer enter.
- * @param {OpenSeadragon.EventHandler} [options.exitHandler=null]
- * An optional handler for pointer exit.
- * @param {OpenSeadragon.EventHandler} [options.pressHandler=null]
- * An optional handler for pointer press.
- * @param {OpenSeadragon.EventHandler} [options.nonPrimaryPressHandler=null]
- * An optional handler for pointer non-primary button press.
- * @param {OpenSeadragon.EventHandler} [options.releaseHandler=null]
- * An optional handler for pointer release.
- * @param {OpenSeadragon.EventHandler} [options.nonPrimaryReleaseHandler=null]
- * An optional handler for pointer non-primary button release.
- * @param {OpenSeadragon.EventHandler} [options.moveHandler=null]
- * An optional handler for pointer move.
- * @param {OpenSeadragon.EventHandler} [options.scrollHandler=null]
- * An optional handler for mouse wheel scroll.
- * @param {OpenSeadragon.EventHandler} [options.clickHandler=null]
- * An optional handler for pointer click.
- * @param {OpenSeadragon.EventHandler} [options.dblClickHandler=null]
- * An optional handler for pointer double-click.
- * @param {OpenSeadragon.EventHandler} [options.dragHandler=null]
- * An optional handler for the drag gesture.
- * @param {OpenSeadragon.EventHandler} [options.dragEndHandler=null]
- * An optional handler for after a drag gesture.
- * @param {OpenSeadragon.EventHandler} [options.pinchHandler=null]
- * An optional handler for the pinch gesture.
- * @param {OpenSeadragon.EventHandler} [options.keyDownHandler=null]
- * An optional handler for keydown.
- * @param {OpenSeadragon.EventHandler} [options.keyUpHandler=null]
- * An optional handler for keyup.
- * @param {OpenSeadragon.EventHandler} [options.keyHandler=null]
- * An optional handler for keypress.
- * @param {OpenSeadragon.EventHandler} [options.focusHandler=null]
- * An optional handler for focus.
- * @param {OpenSeadragon.EventHandler} [options.blurHandler=null]
- * An optional handler for blur.
- * @param {Object} [options.userData=null]
- * Arbitrary object to be passed unchanged to any attached handler methods.
- */
- $.MouseTracker = function ( options ) {
- MOUSETRACKERS.push( this );
- var args = arguments;
- if ( !$.isPlainObject( options ) ) {
- options = {
- element: args[ 0 ],
- clickTimeThreshold: args[ 1 ],
- clickDistThreshold: args[ 2 ]
- };
- }
- this.hash = Math.random(); // An unique hash for this tracker.
- /**
- * The element for which pointer events are being monitored.
- * @member {Element} element
- * @memberof OpenSeadragon.MouseTracker#
- */
- this.element = $.getElement( options.element );
- /**
- * The number of milliseconds within which a pointer down-up event combination
- * will be treated as a click gesture.
- * @member {Number} clickTimeThreshold
- * @memberof OpenSeadragon.MouseTracker#
- */
- this.clickTimeThreshold = options.clickTimeThreshold || $.DEFAULT_SETTINGS.clickTimeThreshold;
- /**
- * The maximum distance allowed between a pointer down event and a pointer up event
- * to be treated as a click gesture.
- * @member {Number} clickDistThreshold
- * @memberof OpenSeadragon.MouseTracker#
- */
- this.clickDistThreshold = options.clickDistThreshold || $.DEFAULT_SETTINGS.clickDistThreshold;
- /**
- * The number of milliseconds within which two pointer down-up event combinations
- * will be treated as a double-click gesture.
- * @member {Number} dblClickTimeThreshold
- * @memberof OpenSeadragon.MouseTracker#
- */
- this.dblClickTimeThreshold = options.dblClickTimeThreshold || $.DEFAULT_SETTINGS.dblClickTimeThreshold;
- /**
- * The maximum distance allowed between two pointer click events
- * to be treated as a click gesture.
- * @member {Number} clickDistThreshold
- * @memberof OpenSeadragon.MouseTracker#
- */
- this.dblClickDistThreshold = options.dblClickDistThreshold || $.DEFAULT_SETTINGS.dblClickDistThreshold;
- /*eslint-disable no-multi-spaces*/
- this.userData = options.userData || null;
- this.stopDelay = options.stopDelay || 50;
- this.enterHandler = options.enterHandler || null;
- this.exitHandler = options.exitHandler || null;
- this.pressHandler = options.pressHandler || null;
- this.nonPrimaryPressHandler = options.nonPrimaryPressHandler || null;
- this.releaseHandler = options.releaseHandler || null;
- this.nonPrimaryReleaseHandler = options.nonPrimaryReleaseHandler || null;
- this.moveHandler = options.moveHandler || null;
- this.scrollHandler = options.scrollHandler || null;
- this.clickHandler = options.clickHandler || null;
- this.dblClickHandler = options.dblClickHandler || null;
- this.dragHandler = options.dragHandler || null;
- this.dragEndHandler = options.dragEndHandler || null;
- this.pinchHandler = options.pinchHandler || null;
- this.stopHandler = options.stopHandler || null;
- this.keyDownHandler = options.keyDownHandler || null;
- this.keyUpHandler = options.keyUpHandler || null;
- this.keyHandler = options.keyHandler || null;
- this.focusHandler = options.focusHandler || null;
- this.blurHandler = options.blurHandler || null;
- /*eslint-enable no-multi-spaces*/
- //Store private properties in a scope sealed hash map
- var _this = this;
- /**
- * @private
- * @property {Boolean} tracking
- * Are we currently tracking pointer events for this element.
- */
- THIS[ this.hash ] = {
- click: function ( event ) { onClick( _this, event ); },
- dblclick: function ( event ) { onDblClick( _this, event ); },
- keydown: function ( event ) { onKeyDown( _this, event ); },
- keyup: function ( event ) { onKeyUp( _this, event ); },
- keypress: function ( event ) { onKeyPress( _this, event ); },
- focus: function ( event ) { onFocus( _this, event ); },
- blur: function ( event ) { onBlur( _this, event ); },
- wheel: function ( event ) { onWheel( _this, event ); },
- mousewheel: function ( event ) { onMouseWheel( _this, event ); },
- DOMMouseScroll: function ( event ) { onMouseWheel( _this, event ); },
- MozMousePixelScroll: function ( event ) { onMouseWheel( _this, event ); },
- mouseenter: function ( event ) { onMouseEnter( _this, event ); }, // Used on IE8 only
- mouseleave: function ( event ) { onMouseLeave( _this, event ); }, // Used on IE8 only
- mouseover: function ( event ) { onMouseOver( _this, event ); },
- mouseout: function ( event ) { onMouseOut( _this, event ); },
- mousedown: function ( event ) { onMouseDown( _this, event ); },
- mouseup: function ( event ) { onMouseUp( _this, event ); },
- mouseupcaptured: function ( event ) { onMouseUpCaptured( _this, event ); },
- mousemove: function ( event ) { onMouseMove( _this, event ); },
- mousemovecaptured: function ( event ) { onMouseMoveCaptured( _this, event ); },
- touchstart: function ( event ) { onTouchStart( _this, event ); },
- touchend: function ( event ) { onTouchEnd( _this, event ); },
- touchendcaptured: function ( event ) { onTouchEndCaptured( _this, event ); },
- touchmove: function ( event ) { onTouchMove( _this, event ); },
- touchmovecaptured: function ( event ) { onTouchMoveCaptured( _this, event ); },
- touchcancel: function ( event ) { onTouchCancel( _this, event ); },
- gesturestart: function ( event ) { onGestureStart( _this, event ); },
- gesturechange: function ( event ) { onGestureChange( _this, event ); },
- pointerover: function ( event ) { onPointerOver( _this, event ); },
- MSPointerOver: function ( event ) { onPointerOver( _this, event ); },
- pointerout: function ( event ) { onPointerOut( _this, event ); },
- MSPointerOut: function ( event ) { onPointerOut( _this, event ); },
- pointerdown: function ( event ) { onPointerDown( _this, event ); },
- MSPointerDown: function ( event ) { onPointerDown( _this, event ); },
- pointerup: function ( event ) { onPointerUp( _this, event ); },
- MSPointerUp: function ( event ) { onPointerUp( _this, event ); },
- pointermove: function ( event ) { onPointerMove( _this, event ); },
- MSPointerMove: function ( event ) { onPointerMove( _this, event ); },
- pointercancel: function ( event ) { onPointerCancel( _this, event ); },
- MSPointerCancel: function ( event ) { onPointerCancel( _this, event ); },
- pointerupcaptured: function ( event ) { onPointerUpCaptured( _this, event ); },
- pointermovecaptured: function ( event ) { onPointerMoveCaptured( _this, event ); },
- tracking: false,
- // Active pointers lists. Array of GesturePointList objects, one for each pointer device type.
- // GesturePointList objects are added each time a pointer is tracked by a new pointer device type (see getActivePointersListByType()).
- // Active pointers are any pointer being tracked for this element which are in the hit-test area
- // of the element (for hover-capable devices) and/or have contact or a button press initiated in the element.
- activePointersLists: [],
- // Tracking for double-click gesture
- lastClickPos: null,
- dblClickTimeOut: null,
- // Tracking for pinch gesture
- pinchGPoints: [],
- lastPinchDist: 0,
- currentPinchDist: 0,
- lastPinchCenter: null,
- currentPinchCenter: null
- };
- if ( !options.startDisabled ) {
- this.setTracking( true );
- }
- };
- /** @lends OpenSeadragon.MouseTracker.prototype */
- $.MouseTracker.prototype = {
- /**
- * Clean up any events or objects created by the tracker.
- * @function
- */
- destroy: function () {
- var i;
- stopTracking( this );
- this.element = null;
- for ( i = 0; i < MOUSETRACKERS.length; i++ ) {
- if ( MOUSETRACKERS[ i ] === this ) {
- MOUSETRACKERS.splice( i, 1 );
- break;
- }
- }
- THIS[ this.hash ] = null;
- delete THIS[ this.hash ];
- },
- /**
- * Are we currently tracking events on this element.
- * @deprecated Just use this.tracking
- * @function
- * @returns {Boolean} Are we currently tracking events on this element.
- */
- isTracking: function () {
- return THIS[ this.hash ].tracking;
- },
- /**
- * Enable or disable whether or not we are tracking events on this element.
- * @function
- * @param {Boolean} track True to start tracking, false to stop tracking.
- * @returns {OpenSeadragon.MouseTracker} Chainable.
- */
- setTracking: function ( track ) {
- if ( track ) {
- startTracking( this );
- } else {
- stopTracking( this );
- }
- //chain
- return this;
- },
- /**
- * Returns the {@link OpenSeadragon.MouseTracker.GesturePointList|GesturePointList} for all but the given pointer device type.
- * @function
- * @param {String} type - The pointer device type: "mouse", "touch", "pen", etc.
- * @returns {Array.<OpenSeadragon.MouseTracker.GesturePointList>}
- */
- getActivePointersListsExceptType: function ( type ) {
- var delegate = THIS[ this.hash ];
- var listArray = [];
- for (var i = 0; i < delegate.activePointersLists.length; ++i) {
- if (delegate.activePointersLists[i].type !== type) {
- listArray.push(delegate.activePointersLists[i]);
- }
- }
- return listArray;
- },
- /**
- * Returns the {@link OpenSeadragon.MouseTracker.GesturePointList|GesturePointList} for the given pointer device type,
- * creating and caching a new {@link OpenSeadragon.MouseTracker.GesturePointList|GesturePointList} if one doesn't already exist for the type.
- * @function
- * @param {String} type - The pointer device type: "mouse", "touch", "pen", etc.
- * @returns {OpenSeadragon.MouseTracker.GesturePointList}
- */
- getActivePointersListByType: function ( type ) {
- var delegate = THIS[ this.hash ],
- i,
- len = delegate.activePointersLists.length,
- list;
- for ( i = 0; i < len; i++ ) {
- if ( delegate.activePointersLists[ i ].type === type ) {
- return delegate.activePointersLists[ i ];
- }
- }
- list = new $.MouseTracker.GesturePointList( type );
- delegate.activePointersLists.push( list );
- return list;
- },
- /**
- * Returns the total number of pointers currently active on the tracked element.
- * @function
- * @returns {Number}
- */
- getActivePointerCount: function () {
- var delegate = THIS[ this.hash ],
- i,
- len = delegate.activePointersLists.length,
- count = 0;
- for ( i = 0; i < len; i++ ) {
- count += delegate.activePointersLists[ i ].getLength();
- }
- return count;
- },
- /**
- * Implement or assign implementation to these handlers during or after
- * calling the constructor.
- * @function
- * @param {Object} event
- * @param {OpenSeadragon.MouseTracker} event.eventSource
- * A reference to the tracker instance.
- * @param {String} event.pointerType
- * "mouse", "touch", "pen", etc.
- * @param {OpenSeadragon.Point} event.position
- * The position of the event relative to the tracked element.
- * @param {Number} event.buttons
- * Current buttons pressed.
- * Combination of bit flags 0: none, 1: primary (or touch contact), 2: secondary, 4: aux (often middle), 8: X1 (often back), 16: X2 (often forward), 32: pen eraser.
- * @param {Number} event.pointers
- * Number of pointers (all types) active in the tracked element.
- * @param {Boolean} event.insideElementPressed
- * True if the left mouse button is currently being pressed and was
- * initiated inside the tracked element, otherwise false.
- * @param {Boolean} event.buttonDownAny
- * Was the button down anywhere in the screen during the event. <span style="color:red;">Deprecated. Use buttons instead.</span>
- * @param {Boolean} event.isTouchEvent
- * True if the original event is a touch event, otherwise false. <span style="color:red;">Deprecated. Use pointerType and/or originalEvent instead.</span>
- * @param {Object} event.originalEvent
- * The original event object.
- * @param {Boolean} event.preventDefaultAction
- * Set to true to prevent the tracker subscriber from performing its default action (subscriber implementation dependent). Default: false.
- * @param {Object} event.userData
- * Arbitrary user-defined object.
- */
- enterHandler: function () { },
- /**
- * Implement or assign implementation to these handlers during or after
- * calling the constructor.
- * @function
- * @param {Object} event
- * @param {OpenSeadragon.MouseTracker} event.eventSource
- * A reference to the tracker instance.
- * @param {String} event.pointerType
- * "mouse", "touch", "pen", etc.
- * @param {OpenSeadragon.Point} event.position
- * The position of the event relative to the tracked element.
- * @param {Number} event.buttons
- * Current buttons pressed.
- * Combination of bit flags 0: none, 1: primary (or touch contact), 2: secondary, 4: aux (often middle), 8: X1 (often back), 16: X2 (often forward), 32: pen eraser.
- * @param {Number} event.pointers
- * Number of pointers (all types) active in the tracked element.
- * @param {Boolean} event.insideElementPressed
- * True if the left mouse button is currently being pressed and was
- * initiated inside the tracked element, otherwise false.
- * @param {Boolean} event.buttonDownAny
- * Was the button down anywhere in the screen during the event. <span style="color:red;">Deprecated. Use buttons instead.</span>
- * @param {Boolean} event.isTouchEvent
- * True if the original event is a touch event, otherwise false. <span style="color:red;">Deprecated. Use pointerType and/or originalEvent instead.</span>
- * @param {Object} event.originalEvent
- * The original event object.
- * @param {Boolean} event.preventDefaultAction
- * Set to true to prevent the tracker subscriber from performing its default action (subscriber implementation dependent). Default: false.
- * @param {Object} event.userData
- * Arbitrary user-defined object.
- */
- exitHandler: function () { },
- /**
- * Implement or assign implementation to these handlers during or after
- * calling the constructor.
- * @function
- * @param {Object} event
- * @param {OpenSeadragon.MouseTracker} event.eventSource
- * A reference to the tracker instance.
- * @param {String} event.pointerType
- * "mouse", "touch", "pen", etc.
- * @param {OpenSeadragon.Point} event.position
- * The position of the event relative to the tracked element.
- * @param {Number} event.buttons
- * Current buttons pressed.
- * Combination of bit flags 0: none, 1: primary (or touch contact), 2: secondary, 4: aux (often middle), 8: X1 (often back), 16: X2 (often forward), 32: pen eraser.
- * @param {Boolean} event.isTouchEvent
- * True if the original event is a touch event, otherwise false. <span style="color:red;">Deprecated. Use pointerType and/or originalEvent instead.</span>
- * @param {Object} event.originalEvent
- * The original event object.
- * @param {Boolean} event.preventDefaultAction
- * Set to true to prevent the tracker subscriber from performing its default action (subscriber implementation dependent). Default: false.
- * @param {Object} event.userData
- * Arbitrary user-defined object.
- */
- pressHandler: function () { },
- /**
- * Implement or assign implementation to these handlers during or after
- * calling the constructor.
- * @function
- * @param {Object} event
- * @param {OpenSeadragon.MouseTracker} event.eventSource
- * A reference to the tracker instance.
- * @param {String} event.pointerType
- * "mouse", "touch", "pen", etc.
- * @param {OpenSeadragon.Point} event.position
- * The position of the event relative to the tracked element.
- * @param {Number} event.button
- * Button which caused the event.
- * -1: none, 0: primary/left, 1: aux/middle, 2: secondary/right, 3: X1/back, 4: X2/forward, 5: pen eraser.
- * @param {Number} event.buttons
- * Current buttons pressed.
- * Combination of bit flags 0: none, 1: primary (or touch contact), 2: secondary, 4: aux (often middle), 8: X1 (often back), 16: X2 (often forward), 32: pen eraser.
- * @param {Boolean} event.isTouchEvent
- * True if the original event is a touch event, otherwise false. <span style="color:red;">Deprecated. Use pointerType and/or originalEvent instead.</span>
- * @param {Object} event.originalEvent
- * The original event object.
- * @param {Boolean} event.preventDefaultAction
- * Set to true to prevent the tracker subscriber from performing its default action (subscriber implementation dependent). Default: false.
- * @param {Object} event.userData
- * Arbitrary user-defined object.
- */
- nonPrimaryPressHandler: function () { },
- /**
- * Implement or assign implementation to these handlers during or after
- * calling the constructor.
- * @function
- * @param {Object} event
- * @param {OpenSeadragon.MouseTracker} event.eventSource
- * A reference to the tracker instance.
- * @param {String} event.pointerType
- * "mouse", "touch", "pen", etc.
- * @param {OpenSeadragon.Point} event.position
- * The position of the event relative to the tracked element.
- * @param {Number} event.buttons
- * Current buttons pressed.
- * Combination of bit flags 0: none, 1: primary (or touch contact), 2: secondary, 4: aux (often middle), 8: X1 (often back), 16: X2 (often forward), 32: pen eraser.
- * @param {Boolean} event.insideElementPressed
- * True if the left mouse button is currently being pressed and was
- * initiated inside the tracked element, otherwise false.
- * @param {Boolean} event.insideElementReleased
- * True if the cursor inside the tracked element when the button was released.
- * @param {Boolean} event.isTouchEvent
- * True if the original event is a touch event, otherwise false. <span style="color:red;">Deprecated. Use pointerType and/or originalEvent instead.</span>
- * @param {Object} event.originalEvent
- * The original event object.
- * @param {Boolean} event.preventDefaultAction
- * Set to true to prevent the tracker subscriber from performing its default action (subscriber implementation dependent). Default: false.
- * @param {Object} event.userData
- * Arbitrary user-defined object.
- */
- releaseHandler: function () { },
- /**
- * Implement or assign implementation to these handlers during or after
- * calling the constructor.
- * @function
- * @param {Object} event
- * @param {OpenSeadragon.MouseTracker} event.eventSource
- * A reference to the tracker instance.
- * @param {String} event.pointerType
- * "mouse", "touch", "pen", etc.
- * @param {OpenSeadragon.Point} event.position
- * The position of the event relative to the tracked element.
- * @param {Number} event.button
- * Button which caused the event.
- * -1: none, 0: primary/left, 1: aux/middle, 2: secondary/right, 3: X1/back, 4: X2/forward, 5: pen eraser.
- * @param {Number} event.buttons
- * Current buttons pressed.
- * Combination of bit flags 0: none, 1: primary (or touch contact), 2: secondary, 4: aux (often middle), 8: X1 (often back), 16: X2 (often forward), 32: pen eraser.
- * @param {Boolean} event.isTouchEvent
- * True if the original event is a touch event, otherwise false. <span style="color:red;">Deprecated. Use pointerType and/or originalEvent instead.</span>
- * @param {Object} event.originalEvent
- * The original event object.
- * @param {Boolean} event.preventDefaultAction
- * Set to true to prevent the tracker subscriber from performing its default action (subscriber implementation dependent). Default: false.
- * @param {Object} event.userData
- * Arbitrary user-defined object.
- */
- nonPrimaryReleaseHandler: function () { },
- /**
- * Implement or assign implementation to these handlers during or after
- * calling the constructor.
- * @function
- * @param {Object} event
- * @param {OpenSeadragon.MouseTracker} event.eventSource
- * A reference to the tracker instance.
- * @param {String} event.pointerType
- * "mouse", "touch", "pen", etc.
- * @param {OpenSeadragon.Point} event.position
- * The position of the event relative to the tracked element.
- * @param {Number} event.buttons
- * Current buttons pressed.
- * Combination of bit flags 0: none, 1: primary (or touch contact), 2: secondary, 4: aux (often middle), 8: X1 (often back), 16: X2 (often forward), 32: pen eraser.
- * @param {Boolean} event.isTouchEvent
- * True if the original event is a touch event, otherwise false. <span style="color:red;">Deprecated. Use pointerType and/or originalEvent instead.</span>
- * @param {Object} event.originalEvent
- * The original event object.
- * @param {Boolean} event.preventDefaultAction
- * Set to true to prevent the tracker subscriber from performing its default action (subscriber implementation dependent). Default: false.
- * @param {Object} event.userData
- * Arbitrary user-defined object.
- */
- moveHandler: function () { },
- /**
- * Implement or assign implementation to these handlers during or after
- * calling the constructor.
- * @function
- * @param {Object} event
- * @param {OpenSeadragon.MouseTracker} event.eventSource
- * A reference to the tracker instance.
- * @param {String} event.pointerType
- * "mouse", "touch", "pen", etc.
- * @param {OpenSeadragon.Point} event.position
- * The position of the event relative to the tracked element.
- * @param {Number} event.scroll
- * The scroll delta for the event.
- * @param {Boolean} event.shift
- * True if the shift key was pressed during this event.
- * @param {Boolean} event.isTouchEvent
- * True if the original event is a touch event, otherwise false. <span style="color:red;">Deprecated. Use pointerType and/or originalEvent instead. Touch devices no longer generate scroll event.</span>
- * @param {Object} event.originalEvent
- * The original event object.
- * @param {Boolean} event.preventDefaultAction
- * Set to true to prevent the tracker subscriber from performing its default action (subscriber implementation dependent). Default: false.
- * @param {Object} event.userData
- * Arbitrary user-defined object.
- */
- scrollHandler: function () { },
- /**
- * Implement or assign implementation to these handlers during or after
- * calling the constructor.
- * @function
- * @param {Object} event
- * @param {OpenSeadragon.MouseTracker} event.eventSource
- * A reference to the tracker instance.
- * @param {String} event.pointerType
- * "mouse", "touch", "pen", etc.
- * @param {OpenSeadragon.Point} event.position
- * The position of the event relative to the tracked element.
- * @param {Boolean} event.quick
- * True only if the clickDistThreshold and clickTimeThreshold are both passed. Useful for ignoring drag events.
- * @param {Boolean} event.shift
- * True if the shift key was pressed during this event.
- * @param {Boolean} event.isTouchEvent
- * True if the original event is a touch event, otherwise false. <span style="color:red;">Deprecated. Use pointerType and/or originalEvent instead.</span>
- * @param {Object} event.originalEvent
- * The original event object.
- * @param {Boolean} event.preventDefaultAction
- * Set to true to prevent the tracker subscriber from performing its default action (subscriber implementation dependent). Default: false.
- * @param {Object} event.userData
- * Arbitrary user-defined object.
- */
- clickHandler: function () { },
- /**
- * Implement or assign implementation to these handlers during or after
- * calling the constructor.
- * @function
- * @param {Object} event
- * @param {OpenSeadragon.MouseTracker} event.eventSource
- * A reference to the tracker instance.
- * @param {String} event.pointerType
- * "mouse", "touch", "pen", etc.
- * @param {OpenSeadragon.Point} event.position
- * The position of the event relative to the tracked element.
- * @param {Boolean} event.shift
- * True if the shift key was pressed during this event.
- * @param {Boolean} event.isTouchEvent
- * True if the original event is a touch event, otherwise false. <span style="color:red;">Deprecated. Use pointerType and/or originalEvent instead.</span>
- * @param {Object} event.originalEvent
- * The original event object.
- * @param {Boolean} event.preventDefaultAction
- * Set to true to prevent the tracker subscriber from performing its default action (subscriber implementation dependent). Default: false.
- * @param {Object} event.userData
- * Arbitrary user-defined object.
- */
- dblClickHandler: function () { },
- /**
- * Implement or assign implementation to these handlers during or after
- * calling the constructor.
- * @function
- * @param {Object} event
- * @param {OpenSeadragon.MouseTracker} event.eventSource
- * A reference to the tracker instance.
- * @param {String} event.pointerType
- * "mouse", "touch", "pen", etc.
- * @param {OpenSeadragon.Point} event.position
- * The position of the event relative to the tracked element.
- * @param {Number} event.buttons
- * Current buttons pressed.
- * Combination of bit flags 0: none, 1: primary (or touch contact), 2: secondary, 4: aux (often middle), 8: X1 (often back), 16: X2 (often forward), 32: pen eraser.
- * @param {OpenSeadragon.Point} event.delta
- * The x,y components of the difference between the current position and the last drag event position. Useful for ignoring or weighting the events.
- * @param {Number} event.speed
- * Current computed speed, in pixels per second.
- * @param {Number} event.direction
- * Current computed direction, expressed as an angle counterclockwise relative to the positive X axis (-pi to pi, in radians). Only valid if speed > 0.
- * @param {Boolean} event.shift
- * True if the shift key was pressed during this event.
- * @param {Boolean} event.isTouchEvent
- * True if the original event is a touch event, otherwise false. <span style="color:red;">Deprecated. Use pointerType and/or originalEvent instead.</span>
- * @param {Object} event.originalEvent
- * The original event object.
- * @param {Boolean} event.preventDefaultAction
- * Set to true to prevent the tracker subscriber from performing its default action (subscriber implementation dependent). Default: false.
- * @param {Object} event.userData
- * Arbitrary user-defined object.
- */
- dragHandler: function () { },
- /**
- * Implement or assign implementation to these handlers during or after
- * calling the constructor.
- * @function
- * @param {Object} event
- * @param {OpenSeadragon.MouseTracker} event.eventSource
- * A reference to the tracker instance.
- * @param {String} event.pointerType
- * "mouse", "touch", "pen", etc.
- * @param {OpenSeadragon.Point} event.position
- * The position of the event relative to the tracked element.
- * @param {Number} event.speed
- * Speed at the end of a drag gesture, in pixels per second.
- * @param {Number} event.direction
- * Direction at the end of a drag gesture, expressed as an angle counterclockwise relative to the positive X axis (-pi to pi, in radians). Only valid if speed > 0.
- * @param {Boolean} event.shift
- * True if the shift key was pressed during this event.
- * @param {Boolean} event.isTouchEvent
- * True if the original event is a touch event, otherwise false. <span style="color:red;">Deprecated. Use pointerType and/or originalEvent instead.</span>
- * @param {Object} event.originalEvent
- * The original event object.
- * @param {Boolean} event.preventDefaultAction
- * Set to true to prevent the tracker subscriber from performing its default action (subscriber implementation dependent). Default: false.
- * @param {Object} event.userData
- * Arbitrary user-defined object.
- */
- dragEndHandler: function () { },
- /**
- * Implement or assign implementation to these handlers during or after
- * calling the constructor.
- * @function
- * @param {Object} event
- * @param {OpenSeadragon.MouseTracker} event.eventSource
- * A reference to the tracker instance.
- * @param {String} event.pointerType
- * "mouse", "touch", "pen", etc.
- * @param {Array.<OpenSeadragon.MouseTracker.GesturePoint>} event.gesturePoints
- * Gesture points associated with the gesture. Velocity data can be found here.
- * @param {OpenSeadragon.Point} event.lastCenter
- * The previous center point of the two pinch contact points relative to the tracked element.
- * @param {OpenSeadragon.Point} event.center
- * The center point of the two pinch contact points relative to the tracked element.
- * @param {Number} event.lastDistance
- * The previous distance between the two pinch contact points in CSS pixels.
- * @param {Number} event.distance
- * The distance between the two pinch contact points in CSS pixels.
- * @param {Boolean} event.shift
- * True if the shift key was pressed during this event.
- * @param {Object} event.originalEvent
- * The original event object.
- * @param {Boolean} event.preventDefaultAction
- * Set to true to prevent the tracker subscriber from performing its default action (subscriber implementation dependent). Default: false.
- * @param {Object} event.userData
- * Arbitrary user-defined object.
- */
- pinchHandler: function () { },
- /**
- * Implement or assign implementation to these handlers during or after
- * calling the constructor.
- * @function
- * @param {Object} event
- * @param {OpenSeadragon.MouseTracker} event.eventSource
- * A reference to the tracker instance.
- * @param {String} event.pointerType
- * "mouse", "touch", "pen", etc.
- * @param {OpenSeadragon.Point} event.position
- * The position of the event relative to the tracked element.
- * @param {Number} event.buttons
- * Current buttons pressed.
- * Combination of bit flags 0: none, 1: primary (or touch contact), 2: secondary, 4: aux (often middle), 8: X1 (often back), 16: X2 (often forward), 32: pen eraser.
- * @param {Boolean} event.isTouchEvent
- * True if the original event is a touch event, otherwise false. <span style="color:red;">Deprecated. Use pointerType and/or originalEvent instead.</span>
- * @param {Object} event.originalEvent
- * The original event object.
- * @param {Boolean} event.preventDefaultAction
- * Set to true to prevent the tracker subscriber from performing its default action (subscriber implementation dependent). Default: false.
- * @param {Object} event.userData
- * Arbitrary user-defined object.
- */
- stopHandler: function () { },
- /**
- * Implement or assign implementation to these handlers during or after
- * calling the constructor.
- * @function
- * @param {Object} event
- * @param {OpenSeadragon.MouseTracker} event.eventSource
- * A reference to the tracker instance.
- * @param {Number} event.keyCode
- * The key code that was pressed.
- * @param {Boolean} event.ctrl
- * True if the ctrl key was pressed during this event.
- * @param {Boolean} event.shift
- * True if the shift key was pressed during this event.
- * @param {Boolean} event.alt
- * True if the alt key was pressed during this event.
- * @param {Boolean} event.meta
- * True if the meta key was pressed during this event.
- * @param {Object} event.originalEvent
- * The original event object.
- * @param {Boolean} event.preventDefaultAction
- * Set to true to prevent the tracker subscriber from performing its default action (subscriber implementation dependent). Default: false.
- * @param {Object} event.userData
- * Arbitrary user-defined object.
- */
- keyDownHandler: function () { },
- /**
- * Implement or assign implementation to these handlers during or after
- * calling the constructor.
- * @function
- * @param {Object} event
- * @param {OpenSeadragon.MouseTracker} event.eventSource
- * A reference to the tracker instance.
- * @param {Number} event.keyCode
- * The key code that was pressed.
- * @param {Boolean} event.ctrl
- * True if the ctrl key was pressed during this event.
- * @param {Boolean} event.shift
- * True if the shift key was pressed during this event.
- * @param {Boolean} event.alt
- * True if the alt key was pressed during this event.
- * @param {Boolean} event.meta
- * True if the meta key was pressed during this event.
- * @param {Object} event.originalEvent
- * The original event object.
- * @param {Boolean} event.preventDefaultAction
- * Set to true to prevent the tracker subscriber from performing its default action (subscriber implementation dependent). Default: false.
- * @param {Object} event.userData
- * Arbitrary user-defined object.
- */
- keyUpHandler: function () { },
- /**
- * Implement or assign implementation to these handlers during or after
- * calling the constructor.
- * @function
- * @param {Object} event
- * @param {OpenSeadragon.MouseTracker} event.eventSource
- * A reference to the tracker instance.
- * @param {Number} event.keyCode
- * The key code that was pressed.
- * @param {Boolean} event.ctrl
- * True if the ctrl key was pressed during this event.
- * @param {Boolean} event.shift
- * True if the shift key was pressed during this event.
- * @param {Boolean} event.alt
- * True if the alt key was pressed during this event.
- * @param {Boolean} event.meta
- * True if the meta key was pressed during this event.
- * @param {Object} event.originalEvent
- * The original event object.
- * @param {Boolean} event.preventDefaultAction
- * Set to true to prevent the tracker subscriber from performing its default action (subscriber implementation dependent). Default: false.
- * @param {Object} event.userData
- * Arbitrary user-defined object.
- */
- keyHandler: function () { },
- /**
- * Implement or assign implementation to these handlers during or after
- * calling the constructor.
- * @function
- * @param {Object} event
- * @param {OpenSeadragon.MouseTracker} event.eventSource
- * A reference to the tracker instance.
- * @param {Object} event.originalEvent
- * The original event object.
- * @param {Boolean} event.preventDefaultAction
- * Set to true to prevent the tracker subscriber from performing its default action (subscriber implementation dependent). Default: false.
- * @param {Object} event.userData
- * Arbitrary user-defined object.
- */
- focusHandler: function () { },
- /**
- * Implement or assign implementation to these handlers during or after
- * calling the constructor.
- * @function
- * @param {Object} event
- * @param {OpenSeadragon.MouseTracker} event.eventSource
- * A reference to the tracker instance.
- * @param {Object} event.originalEvent
- * The original event object.
- * @param {Boolean} event.preventDefaultAction
- * Set to true to prevent the tracker subscriber from performing its default action (subscriber implementation dependent). Default: false.
- * @param {Object} event.userData
- * Arbitrary user-defined object.
- */
- blurHandler: function () { }
- };
- /**
- * Resets all active mousetrakers. (Added to patch issue #697 "Mouse up outside map will cause "canvas-drag" event to stick")
- *
- * @private
- * @member resetAllMouseTrackers
- * @memberof OpenSeadragon.MouseTracker
- */
- $.MouseTracker.resetAllMouseTrackers = function(){
- for(var i = 0; i < MOUSETRACKERS.length; i++){
- if (MOUSETRACKERS[i].isTracking()){
- MOUSETRACKERS[i].setTracking(false);
- MOUSETRACKERS[i].setTracking(true);
- }
- }
- };
- /**
- * Provides continuous computation of velocity (speed and direction) of active pointers.
- * This is a singleton, used by all MouseTracker instances, as it is unlikely there will ever be more than
- * two active gesture pointers at a time.
- *
- * @private
- * @member gesturePointVelocityTracker
- * @memberof OpenSeadragon.MouseTracker
- */
- $.MouseTracker.gesturePointVelocityTracker = (function () {
- var trackerPoints = [],
- intervalId = 0,
- lastTime = 0;
- // Generates a unique identifier for a tracked gesture point
- var _generateGuid = function ( tracker, gPoint ) {
- return tracker.hash.toString() + gPoint.type + gPoint.id.toString();
- };
- // Interval timer callback. Computes velocity for all tracked gesture points.
- var _doTracking = function () {
- var i,
- len = trackerPoints.length,
- trackPoint,
- gPoint,
- now = $.now(),
- elapsedTime,
- distance,
- speed;
- elapsedTime = now - lastTime;
- lastTime = now;
- for ( i = 0; i < len; i++ ) {
- trackPoint = trackerPoints[ i ];
- gPoint = trackPoint.gPoint;
- // Math.atan2 gives us just what we need for a velocity vector, as we can simply
- // use cos()/sin() to extract the x/y velocity components.
- gPoint.direction = Math.atan2( gPoint.currentPos.y - trackPoint.lastPos.y, gPoint.currentPos.x - trackPoint.lastPos.x );
- // speed = distance / elapsed time
- distance = trackPoint.lastPos.distanceTo( gPoint.currentPos );
- trackPoint.lastPos = gPoint.currentPos;
- speed = 1000 * distance / ( elapsedTime + 1 );
- // Simple biased average, favors the most recent speed computation. Smooths out erratic gestures a bit.
- gPoint.speed = 0.75 * speed + 0.25 * gPoint.speed;
- }
- };
- // Public. Add a gesture point to be tracked
- var addPoint = function ( tracker, gPoint ) {
- var guid = _generateGuid( tracker, gPoint );
- trackerPoints.push(
- {
- guid: guid,
- gPoint: gPoint,
- lastPos: gPoint.currentPos
- } );
- // Only fire up the interval timer when there's gesture pointers to track
- if ( trackerPoints.length === 1 ) {
- lastTime = $.now();
- intervalId = window.setInterval( _doTracking, 50 );
- }
- };
- // Public. Stop tracking a gesture point
- var removePoint = function ( tracker, gPoint ) {
- var guid = _generateGuid( tracker, gPoint ),
- i,
- len = trackerPoints.length;
- for ( i = 0; i < len; i++ ) {
- if ( trackerPoints[ i ].guid === guid ) {
- trackerPoints.splice( i, 1 );
- // Only run the interval timer if theres gesture pointers to track
- len--;
- if ( len === 0 ) {
- window.clearInterval( intervalId );
- }
- break;
- }
- }
- };
- return {
- addPoint: addPoint,
- removePoint: removePoint
- };
- } )();
- ///////////////////////////////////////////////////////////////////////////////
- // Pointer event model and feature detection
- ///////////////////////////////////////////////////////////////////////////////
- $.MouseTracker.captureElement = document;
- /**
- * Detect available mouse wheel event name.
- */
- $.MouseTracker.wheelEventName = ( $.Browser.vendor == $.BROWSERS.IE && $.Browser.version > 8 ) ||
- ( 'onwheel' in document.createElement( 'div' ) ) ? 'wheel' : // Modern browsers support 'wheel'
- document.onmousewheel !== undefined ? 'mousewheel' : // Webkit and IE support at least 'mousewheel'
- 'DOMMouseScroll'; // Assume old Firefox
- /**
- * Detect legacy mouse capture support.
- */
- $.MouseTracker.supportsMouseCapture = (function () {
- var divElement = document.createElement( 'div' );
- return $.isFunction( divElement.setCapture ) && $.isFunction( divElement.releaseCapture );
- }());
- /**
- * Detect browser pointer device event model(s) and build appropriate list of events to subscribe to.
- */
- $.MouseTracker.subscribeEvents = [ "click", "dblclick", "keydown", "keyup", "keypress", "focus", "blur", $.MouseTracker.wheelEventName ];
- if( $.MouseTracker.wheelEventName == "DOMMouseScroll" ) {
- // Older Firefox
- $.MouseTracker.subscribeEvents.push( "MozMousePixelScroll" );
- }
- // Note: window.navigator.pointerEnable is deprecated on IE 11 and not part of W3C spec.
- if ( window.PointerEvent && ( window.navigator.pointerEnabled || $.Browser.vendor !== $.BROWSERS.IE ) ) {
- // IE11 and other W3C Pointer Event implementations (see http://www.w3.org/TR/pointerevents)
- $.MouseTracker.havePointerEvents = true;
- $.MouseTracker.subscribeEvents.push( "pointerover", "pointerout", "pointerdown", "pointerup", "pointermove", "pointercancel" );
- $.MouseTracker.unprefixedPointerEvents = true;
- if( navigator.maxTouchPoints ) {
- $.MouseTracker.maxTouchPoints = navigator.maxTouchPoints;
- } else {
- $.MouseTracker.maxTouchPoints = 0;
- }
- $.MouseTracker.haveMouseEnter = false;
- } else if ( window.MSPointerEvent && window.navigator.msPointerEnabled ) {
- // IE10
- $.MouseTracker.havePointerEvents = true;
- $.MouseTracker.subscribeEvents.push( "MSPointerOver", "MSPointerOut", "MSPointerDown", "MSPointerUp", "MSPointerMove", "MSPointerCancel" );
- $.MouseTracker.unprefixedPointerEvents = false;
- if( navigator.msMaxTouchPoints ) {
- $.MouseTracker.maxTouchPoints = navigator.msMaxTouchPoints;
- } else {
- $.MouseTracker.maxTouchPoints = 0;
- }
- $.MouseTracker.haveMouseEnter = false;
- } else {
- // Legacy W3C mouse events
- $.MouseTracker.havePointerEvents = false;
- if ( $.Browser.vendor === $.BROWSERS.IE && $.Browser.version < 9 ) {
- $.MouseTracker.subscribeEvents.push( "mouseenter", "mouseleave" );
- $.MouseTracker.haveMouseEnter = true;
- } else {
- $.MouseTracker.subscribeEvents.push( "mouseover", "mouseout" );
- $.MouseTracker.haveMouseEnter = false;
- }
- $.MouseTracker.subscribeEvents.push( "mousedown", "mouseup", "mousemove" );
- if ( 'ontouchstart' in window ) {
- // iOS, Android, and other W3c Touch Event implementations
- // (see http://www.w3.org/TR/touch-events/)
- // (see https://developer.apple.com/library/ios/documentation/AppleApplications/Reference/SafariWebContent/HandlingEvents/HandlingEvents.html)
- // (see https://developer.apple.com/library/safari/documentation/AppleApplications/Reference/SafariWebContent/HandlingEvents/HandlingEvents.html)
- $.MouseTracker.subscribeEvents.push( "touchstart", "touchend", "touchmove", "touchcancel" );
- }
- if ( 'ongesturestart' in window ) {
- // iOS (see https://developer.apple.com/library/ios/documentation/AppleApplications/Reference/SafariWebContent/HandlingEvents/HandlingEvents.html)
- // Subscribe to these to prevent default gesture handling
- $.MouseTracker.subscribeEvents.push( "gesturestart", "gesturechange" );
- }
- $.MouseTracker.mousePointerId = "legacy-mouse";
- $.MouseTracker.maxTouchPoints = 10;
- }
- ///////////////////////////////////////////////////////////////////////////////
- // Classes and typedefs
- ///////////////////////////////////////////////////////////////////////////////
- /**
- * Represents a point of contact on the screen made by a mouse cursor, pen, touch, or other pointer device.
- *
- * @typedef {Object} GesturePoint
- * @memberof OpenSeadragon.MouseTracker
- *
- * @property {Number} id
- * Identifier unique from all other active GesturePoints for a given pointer device.
- * @property {String} type
- * The pointer device type: "mouse", "touch", "pen", etc.
- * @property {Boolean} captured
- * True if events for the gesture point are captured to the tracked element.
- * @property {Boolean} isPrimary
- * True if the gesture point is a master pointer amongst the set of active pointers for each pointer type. True for mouse and primary (first) touch/pen pointers.
- * @property {Boolean} insideElementPressed
- * True if button pressed or contact point initiated inside the screen area of the tracked element.
- * @property {Boolean} insideElement
- * True if pointer or contact point is currently inside the bounds of the tracked element.
- * @property {Number} speed
- * Current computed speed, in pixels per second.
- * @property {Number} direction
- * Current computed direction, expressed as an angle counterclockwise relative to the positive X axis (-pi to pi, in radians). Only valid if speed > 0.
- * @property {OpenSeadragon.Point} contactPos
- * The initial pointer contact position, relative to the page including any scrolling. Only valid if the pointer has contact (pressed, touch contact, pen contact).
- * @property {Number} contactTime
- * The initial pointer contact time, in milliseconds. Only valid if the pointer has contact (pressed, touch contact, pen contact).
- * @property {OpenSeadragon.Point} lastPos
- * The last pointer position, relative to the page including any scrolling.
- * @property {Number} lastTime
- * The last pointer contact time, in milliseconds.
- * @property {OpenSeadragon.Point} currentPos
- * The current pointer position, relative to the page including any scrolling.
- * @property {Number} currentTime
- * The current pointer contact time, in milliseconds.
- */
- /**
- * @class GesturePointList
- * @classdesc Provides an abstraction for a set of active {@link OpenSeadragon.MouseTracker.GesturePoint|GesturePoint} objects for a given pointer device type.
- * Active pointers are any pointer being tracked for this element which are in the hit-test area
- * of the element (for hover-capable devices) and/or have contact or a button press initiated in the element.
- * @memberof OpenSeadragon.MouseTracker
- * @param {String} type - The pointer device type: "mouse", "touch", "pen", etc.
- */
- $.MouseTracker.GesturePointList = function ( type ) {
- this._gPoints = [];
- /**
- * The pointer device type: "mouse", "touch", "pen", etc.
- * @member {String} type
- * @memberof OpenSeadragon.MouseTracker.GesturePointList#
- */
- this.type = type;
- /**
- * Current buttons pressed for the device.
- * Combination of bit flags 0: none, 1: primary (or touch contact), 2: secondary, 4: aux (often middle), 8: X1 (often back), 16: X2 (often forward), 32: pen eraser.
- * @member {Number} buttons
- * @memberof OpenSeadragon.MouseTracker.GesturePointList#
- */
- this.buttons = 0;
- /**
- * Current number of contact points (touch points, mouse down, etc.) for the device.
- * @member {Number} contacts
- * @memberof OpenSeadragon.MouseTracker.GesturePointList#
- */
- this.contacts = 0;
- /**
- * Current number of clicks for the device. Used for multiple click gesture tracking.
- * @member {Number} clicks
- * @memberof OpenSeadragon.MouseTracker.GesturePointList#
- */
- this.clicks = 0;
- /**
- * Current number of captured pointers for the device.
- * @member {Number} captureCount
- * @memberof OpenSeadragon.MouseTracker.GesturePointList#
- */
- this.captureCount = 0;
- };
- /** @lends OpenSeadragon.MouseTracker.GesturePointList.prototype */
- $.MouseTracker.GesturePointList.prototype = {
- /**
- * @function
- * @returns {Number} Number of gesture points in the list.
- */
- getLength: function () {
- return this._gPoints.length;
- },
- /**
- * @function
- * @returns {Array.<OpenSeadragon.MouseTracker.GesturePoint>} The list of gesture points in the list as an array (read-only).
- */
- asArray: function () {
- return this._gPoints;
- },
- /**
- * @function
- * @param {OpenSeadragon.MouseTracker.GesturePoint} gesturePoint - A gesture point to add to the list.
- * @returns {Number} Number of gesture points in the list.
- */
- add: function ( gp ) {
- return this._gPoints.push( gp );
- },
- /**
- * @function
- * @param {Number} id - The id of the gesture point to remove from the list.
- * @returns {Number} Number of gesture points in the list.
- */
- removeById: function ( id ) {
- var i,
- len = this._gPoints.length;
- for ( i = 0; i < len; i++ ) {
- if ( this._gPoints[ i ].id === id ) {
- this._gPoints.splice( i, 1 );
- break;
- }
- }
- return this._gPoints.length;
- },
- /**
- * @function
- * @param {Number} index - The index of the gesture point to retrieve from the list.
- * @returns {OpenSeadragon.MouseTracker.GesturePoint|null} The gesture point at the given index, or null if not found.
- */
- getByIndex: function ( index ) {
- if ( index < this._gPoints.length) {
- return this._gPoints[ index ];
- }
- return null;
- },
- /**
- * @function
- * @param {Number} id - The id of the gesture point to retrieve from the list.
- * @returns {OpenSeadragon.MouseTracker.GesturePoint|null} The gesture point with the given id, or null if not found.
- */
- getById: function ( id ) {
- var i,
- len = this._gPoints.length;
- for ( i = 0; i < len; i++ ) {
- if ( this._gPoints[ i ].id === id ) {
- return this._gPoints[ i ];
- }
- }
- return null;
- },
- /**
- * @function
- * @returns {OpenSeadragon.MouseTracker.GesturePoint|null} The primary gesture point in the list, or null if not found.
- */
- getPrimary: function ( id ) {
- var i,
- len = this._gPoints.length;
- for ( i = 0; i < len; i++ ) {
- if ( this._gPoints[ i ].isPrimary ) {
- return this._gPoints[ i ];
- }
- }
- return null;
- },
- /**
- * Increment this pointer's contact count.
- * It will evaluate whether this pointer type is allowed to have multiple contacts.
- * @function
- */
- addContact: function() {
- ++this.contacts;
- if (this.contacts > 1 && (this.type === "mouse" || this.type === "pen")) {
- this.contacts = 1;
- }
- },
- /**
- * Decrement this pointer's contact count.
- * It will make sure the count does not go below 0.
- * @function
- */
- removeContact: function() {
- --this.contacts;
- if (this.contacts < 0) {
- this.contacts = 0;
- }
- }
- };
- ///////////////////////////////////////////////////////////////////////////////
- // Utility functions
- ///////////////////////////////////////////////////////////////////////////////
- /**
- * Removes all tracked pointers.
- * @private
- * @inner
- */
- function clearTrackedPointers( tracker ) {
- var delegate = THIS[ tracker.hash ],
- i,
- pointerListCount = delegate.activePointersLists.length;
- for ( i = 0; i < pointerListCount; i++ ) {
- if ( delegate.activePointersLists[ i ].captureCount > 0 ) {
- $.removeEvent(
- $.MouseTracker.captureElement,
- 'mousemove',
- delegate.mousemovecaptured,
- true
- );
- $.removeEvent(
- $.MouseTracker.captureElement,
- 'mouseup',
- delegate.mouseupcaptured,
- true
- );
- $.removeEvent(
- $.MouseTracker.captureElement,
- $.MouseTracker.unprefixedPointerEvents ? 'pointermove' : 'MSPointerMove',
- delegate.pointermovecaptured,
- true
- );
- $.removeEvent(
- $.MouseTracker.captureElement,
- $.MouseTracker.unprefixedPointerEvents ? 'pointerup' : 'MSPointerUp',
- delegate.pointerupcaptured,
- true
- );
- $.removeEvent(
- $.MouseTracker.captureElement,
- 'touchmove',
- delegate.touchmovecaptured,
- true
- );
- $.removeEvent(
- $.MouseTracker.captureElement,
- 'touchend',
- delegate.touchendcaptured,
- true
- );
- delegate.activePointersLists[ i ].captureCount = 0;
- }
- }
- for ( i = 0; i < pointerListCount; i++ ) {
- delegate.activePointersLists.pop();
- }
- }
- /**
- * Starts tracking pointer events on the tracked element.
- * @private
- * @inner
- */
- function startTracking( tracker ) {
- var delegate = THIS[ tracker.hash ],
- event,
- i;
- if ( !delegate.tracking ) {
- for ( i = 0; i < $.MouseTracker.subscribeEvents.length; i++ ) {
- event = $.MouseTracker.subscribeEvents[ i ];
- $.addEvent(
- tracker.element,
- event,
- delegate[ event ],
- false
- );
- }
- clearTrackedPointers( tracker );
- delegate.tracking = true;
- }
- }
- /**
- * Stops tracking pointer events on the tracked element.
- * @private
- * @inner
- */
- function stopTracking( tracker ) {
- var delegate = THIS[ tracker.hash ],
- event,
- i;
- if ( delegate.tracking ) {
- for ( i = 0; i < $.MouseTracker.subscribeEvents.length; i++ ) {
- event = $.MouseTracker.subscribeEvents[ i ];
- $.removeEvent(
- tracker.element,
- event,
- delegate[ event ],
- false
- );
- }
- clearTrackedPointers( tracker );
- delegate.tracking = false;
- }
- }
- /**
- * @private
- * @inner
- */
- function getCaptureEventParams( tracker, pointerType ) {
- var delegate = THIS[ tracker.hash ];
- if ( pointerType === 'pointerevent' ) {
- return {
- upName: $.MouseTracker.unprefixedPointerEvents ? 'pointerup' : 'MSPointerUp',
- upHandler: delegate.pointerupcaptured,
- moveName: $.MouseTracker.unprefixedPointerEvents ? 'pointermove' : 'MSPointerMove',
- moveHandler: delegate.pointermovecaptured
- };
- } else if ( pointerType === 'mouse' ) {
- return {
- upName: 'mouseup',
- upHandler: delegate.mouseupcaptured,
- moveName: 'mousemove',
- moveHandler: delegate.mousemovecaptured
- };
- } else if ( pointerType === 'touch' ) {
- return {
- upName: 'touchend',
- upHandler: delegate.touchendcaptured,
- moveName: 'touchmove',
- moveHandler: delegate.touchmovecaptured
- };
- } else {
- throw new Error( "MouseTracker.getCaptureEventParams: Unknown pointer type." );
- }
- }
- /**
- * Begin capturing pointer events to the tracked element.
- * @private
- * @inner
- */
- function capturePointer( tracker, pointerType, pointerCount ) {
- var pointsList = tracker.getActivePointersListByType( pointerType ),
- eventParams;
- pointsList.captureCount += (pointerCount || 1);
- if ( pointsList.captureCount === 1 ) {
- if ( $.Browser.vendor === $.BROWSERS.IE && $.Browser.version < 9 ) {
- tracker.element.setCapture( true );
- } else {
- eventParams = getCaptureEventParams( tracker, $.MouseTracker.havePointerEvents ? 'pointerevent' : pointerType );
- // We emulate mouse capture by hanging listeners on the document object.
- // (Note we listen on the capture phase so the captured handlers will get called first)
- // eslint-disable-next-line no-use-before-define
- if (isInIframe && canAccessEvents(window.top)) {
- $.addEvent(
- window.top,
- eventParams.upName,
- eventParams.upHandler,
- true
- );
- }
- $.addEvent(
- $.MouseTracker.captureElement,
- eventParams.upName,
- eventParams.upHandler,
- true
- );
- $.addEvent(
- $.MouseTracker.captureElement,
- eventParams.moveName,
- eventParams.moveHandler,
- true
- );
- }
- }
- }
- /**
- * Stop capturing pointer events to the tracked element.
- * @private
- * @inner
- */
- function releasePointer( tracker, pointerType, pointerCount ) {
- var pointsList = tracker.getActivePointersListByType( pointerType ),
- eventParams;
- pointsList.captureCount -= (pointerCount || 1);
- if ( pointsList.captureCount === 0 ) {
- if ( $.Browser.vendor === $.BROWSERS.IE && $.Browser.version < 9 ) {
- tracker.element.releaseCapture();
- } else {
- eventParams = getCaptureEventParams( tracker, $.MouseTracker.havePointerEvents ? 'pointerevent' : pointerType );
- // We emulate mouse capture by hanging listeners on the document object.
- // (Note we listen on the capture phase so the captured handlers will get called first)
- // eslint-disable-next-line no-use-before-define
- if (isInIframe && canAccessEvents(window.top)) {
- $.removeEvent(
- window.top,
- eventParams.upName,
- eventParams.upHandler,
- true
- );
- }
- $.removeEvent(
- $.MouseTracker.captureElement,
- eventParams.moveName,
- eventParams.moveHandler,
- true
- );
- $.removeEvent(
- $.MouseTracker.captureElement,
- eventParams.upName,
- eventParams.upHandler,
- true
- );
- }
- }
- }
- /**
- * Gets a W3C Pointer Events model compatible pointer type string from a DOM pointer event.
- * IE10 used a long integer value, but the W3C specification (and IE11+) use a string "mouse", "touch", "pen", etc.
- * @private
- * @inner
- */
- function getPointerType( event ) {
- var pointerTypeStr;
- if ( $.MouseTracker.unprefixedPointerEvents ) {
- pointerTypeStr = event.pointerType;
- } else {
- // IE10
- // MSPOINTER_TYPE_TOUCH: 0x00000002
- // MSPOINTER_TYPE_PEN: 0x00000003
- // MSPOINTER_TYPE_MOUSE: 0x00000004
- switch( event.pointerType )
- {
- case 0x00000002:
- pointerTypeStr = 'touch';
- break;
- case 0x00000003:
- pointerTypeStr = 'pen';
- break;
- case 0x00000004:
- pointerTypeStr = 'mouse';
- break;
- default:
- pointerTypeStr = '';
- }
- }
- return pointerTypeStr;
- }
- /**
- * @private
- * @inner
- */
- function getMouseAbsolute( event ) {
- return $.getMousePosition( event );
- }
- /**
- * @private
- * @inner
- */
- function getMouseRelative( event, element ) {
- return getPointRelativeToAbsolute( getMouseAbsolute( event ), element );
- }
- /**
- * @private
- * @inner
- */
- function getPointRelativeToAbsolute( point, element ) {
- var offset = $.getElementOffset( element );
- return point.minus( offset );
- }
- /**
- * @private
- * @inner
- */
- function getCenterPoint( point1, point2 ) {
- return new $.Point( ( point1.x + point2.x ) / 2, ( point1.y + point2.y ) / 2 );
- }
- ///////////////////////////////////////////////////////////////////////////////
- // Device-specific DOM event handlers
- ///////////////////////////////////////////////////////////////////////////////
- /**
- * @private
- * @inner
- */
- function onClick( tracker, event ) {
- if ( tracker.clickHandler ) {
- $.cancelEvent( event );
- }
- }
- /**
- * @private
- * @inner
- */
- function onDblClick( tracker, event ) {
- if ( tracker.dblClickHandler ) {
- $.cancelEvent( event );
- }
- }
- /**
- * @private
- * @inner
- */
- function onKeyDown( tracker, event ) {
- //$.console.log( "keydown %s %s %s %s %s", event.keyCode, event.charCode, event.ctrlKey, event.shiftKey, event.altKey );
- var propagate;
- if ( tracker.keyDownHandler ) {
- event = $.getEvent( event );
- propagate = tracker.keyDownHandler(
- {
- eventSource: tracker,
- keyCode: event.keyCode ? event.keyCode : event.charCode,
- ctrl: event.ctrlKey,
- shift: event.shiftKey,
- alt: event.altKey,
- meta: event.metaKey,
- originalEvent: event,
- preventDefaultAction: false,
- userData: tracker.userData
- }
- );
- if ( !propagate ) {
- $.cancelEvent( event );
- }
- }
- }
- /**
- * @private
- * @inner
- */
- function onKeyUp( tracker, event ) {
- //$.console.log( "keyup %s %s %s %s %s", event.keyCode, event.charCode, event.ctrlKey, event.shiftKey, event.altKey );
- var propagate;
- if ( tracker.keyUpHandler ) {
- event = $.getEvent( event );
- propagate = tracker.keyUpHandler(
- {
- eventSource: tracker,
- keyCode: event.keyCode ? event.keyCode : event.charCode,
- ctrl: event.ctrlKey,
- shift: event.shiftKey,
- alt: event.altKey,
- meta: event.metaKey,
- originalEvent: event,
- preventDefaultAction: false,
- userData: tracker.userData
- }
- );
- if ( !propagate ) {
- $.cancelEvent( event );
- }
- }
- }
- /**
- * @private
- * @inner
- */
- function onKeyPress( tracker, event ) {
- //$.console.log( "keypress %s %s %s %s %s", event.keyCode, event.charCode, event.ctrlKey, event.shiftKey, event.altKey );
- var propagate;
- if ( tracker.keyHandler ) {
- event = $.getEvent( event );
- propagate = tracker.keyHandler(
- {
- eventSource: tracker,
- keyCode: event.keyCode ? event.keyCode : event.charCode,
- ctrl: event.ctrlKey,
- shift: event.shiftKey,
- alt: event.altKey,
- meta: event.metaKey,
- originalEvent: event,
- preventDefaultAction: false,
- userData: tracker.userData
- }
- );
- if ( !propagate ) {
- $.cancelEvent( event );
- }
- }
- }
- /**
- * @private
- * @inner
- */
- function onFocus( tracker, event ) {
- //console.log( "focus %s", event );
- var propagate;
- if ( tracker.focusHandler ) {
- event = $.getEvent( event );
- propagate = tracker.focusHandler(
- {
- eventSource: tracker,
- originalEvent: event,
- preventDefaultAction: false,
- userData: tracker.userData
- }
- );
- if ( propagate === false ) {
- $.cancelEvent( event );
- }
- }
- }
- /**
- * @private
- * @inner
- */
- function onBlur( tracker, event ) {
- //console.log( "blur %s", event );
- var propagate;
- if ( tracker.blurHandler ) {
- event = $.getEvent( event );
- propagate = tracker.blurHandler(
- {
- eventSource: tracker,
- originalEvent: event,
- preventDefaultAction: false,
- userData: tracker.userData
- }
- );
- if ( propagate === false ) {
- $.cancelEvent( event );
- }
- }
- }
- /**
- * Handler for 'wheel' events
- *
- * @private
- * @inner
- */
- function onWheel( tracker, event ) {
- handleWheelEvent( tracker, event, event );
- }
- /**
- * Handler for 'mousewheel', 'DOMMouseScroll', and 'MozMousePixelScroll' events
- *
- * @private
- * @inner
- */
- function onMouseWheel( tracker, event ) {
- event = $.getEvent( event );
- // Simulate a 'wheel' event
- var simulatedEvent = {
- target: event.target || event.srcElement,
- type: "wheel",
- shiftKey: event.shiftKey || false,
- clientX: event.clientX,
- clientY: event.clientY,
- pageX: event.pageX ? event.pageX : event.clientX,
- pageY: event.pageY ? event.pageY : event.clientY,
- deltaMode: event.type == "MozMousePixelScroll" ? 0 : 1, // 0=pixel, 1=line, 2=page
- deltaX: 0,
- deltaZ: 0
- };
- // Calculate deltaY
- if ( $.MouseTracker.wheelEventName == "mousewheel" ) {
- simulatedEvent.deltaY = -event.wheelDelta / $.DEFAULT_SETTINGS.pixelsPerWheelLine;
- } else {
- simulatedEvent.deltaY = event.detail;
- }
- handleWheelEvent( tracker, simulatedEvent, event );
- }
- /**
- * Handles 'wheel' events.
- * The event may be simulated by the legacy mouse wheel event handler (onMouseWheel()).
- *
- * @private
- * @inner
- */
- function handleWheelEvent( tracker, event, originalEvent ) {
- var nDelta = 0,
- propagate;
- // The nDelta variable is gated to provide smooth z-index scrolling
- // since the mouse wheel allows for substantial deltas meant for rapid
- // y-index scrolling.
- // event.deltaMode: 0=pixel, 1=line, 2=page
- // TODO: Deltas in pixel mode should be accumulated then a scroll value computed after $.DEFAULT_SETTINGS.pixelsPerWheelLine threshold reached
- nDelta = event.deltaY < 0 ? 1 : -1;
- if ( tracker.scrollHandler ) {
- propagate = tracker.scrollHandler(
- {
- eventSource: tracker,
- pointerType: 'mouse',
- position: getMouseRelative( event, tracker.element ),
- scroll: nDelta,
- shift: event.shiftKey,
- isTouchEvent: false,
- originalEvent: originalEvent,
- preventDefaultAction: false,
- userData: tracker.userData
- }
- );
- if ( propagate === false ) {
- $.cancelEvent( originalEvent );
- }
- }
- }
- /**
- * @private
- * @inner
- */
- function isParentChild( parent, child )
- {
- if ( parent === child ) {
- return false;
- }
- while ( child && child !== parent ) {
- child = child.parentNode;
- }
- return child === parent;
- }
- /**
- * Only used on IE 8
- *
- * @private
- * @inner
- */
- function onMouseEnter( tracker, event ) {
- event = $.getEvent( event );
- handleMouseEnter( tracker, event );
- }
- /**
- * @private
- * @inner
- */
- function onMouseOver( tracker, event ) {
- event = $.getEvent( event );
- if ( event.currentTarget === event.relatedTarget || isParentChild( event.currentTarget, event.relatedTarget ) ) {
- return;
- }
- handleMouseEnter( tracker, event );
- }
- /**
- * @private
- * @inner
- */
- function handleMouseEnter( tracker, event ) {
- var gPoint = {
- id: $.MouseTracker.mousePointerId,
- type: 'mouse',
- isPrimary: true,
- currentPos: getMouseAbsolute( event ),
- currentTime: $.now()
- };
- updatePointersEnter( tracker, event, [ gPoint ] );
- }
- /**
- * Only used on IE 8
- *
- * @private
- * @inner
- */
- function onMouseLeave( tracker, event ) {
- event = $.getEvent( event );
- handleMouseExit( tracker, event );
- }
- /**
- * @private
- * @inner
- */
- function onMouseOut( tracker, event ) {
- event = $.getEvent( event );
- if ( event.currentTarget === event.relatedTarget || isParentChild( event.currentTarget, event.relatedTarget ) ) {
- return;
- }
- handleMouseExit( tracker, event );
- }
- /**
- * @private
- * @inner
- */
- function handleMouseExit( tracker, event ) {
- var gPoint = {
- id: $.MouseTracker.mousePointerId,
- type: 'mouse',
- isPrimary: true,
- currentPos: getMouseAbsolute( event ),
- currentTime: $.now()
- };
- updatePointersExit( tracker, event, [ gPoint ] );
- }
- /**
- * Returns a W3C DOM level 3 standard button value given an event.button property:
- * -1 == none, 0 == primary/left, 1 == middle, 2 == secondary/right, 3 == X1/back, 4 == X2/forward, 5 == eraser (pen)
- * @private
- * @inner
- */
- function getStandardizedButton( button ) {
- if ( $.Browser.vendor === $.BROWSERS.IE && $.Browser.version < 9 ) {
- // On IE 8, 0 == none, 1 == left, 2 == right, 3 == left and right, 4 == middle, 5 == left and middle, 6 == right and middle, 7 == all three
- // TODO: Support chorded (multiple) button presses on IE 8?
- if ( button === 1 ) {
- return 0;
- } else if ( button === 2 ) {
- return 2;
- } else if ( button === 4 ) {
- return 1;
- } else {
- return -1;
- }
- } else {
- return button;
- }
- }
- /**
- * @private
- * @inner
- */
- function onMouseDown( tracker, event ) {
- var gPoint;
- event = $.getEvent( event );
- gPoint = {
- id: $.MouseTracker.mousePointerId,
- type: 'mouse',
- isPrimary: true,
- currentPos: getMouseAbsolute( event ),
- currentTime: $.now()
- };
- if ( updatePointersDown( tracker, event, [ gPoint ], getStandardizedButton( event.button ) ) ) {
- $.stopEvent( event );
- capturePointer( tracker, 'mouse' );
- }
- if ( tracker.clickHandler || tracker.dblClickHandler || tracker.pressHandler || tracker.dragHandler || tracker.dragEndHandler ) {
- $.cancelEvent( event );
- }
- }
- /**
- * @private
- * @inner
- */
- function onMouseUp( tracker, event ) {
- handleMouseUp( tracker, event );
- }
- /**
- * This handler is attached to the window object (on the capture phase) to emulate mouse capture.
- * onMouseUp is still attached to the tracked element, so stop propagation to avoid processing twice.
- *
- * @private
- * @inner
- */
- function onMouseUpCaptured( tracker, event ) {
- handleMouseUp( tracker, event );
- $.stopEvent( event );
- }
- /**
- * @private
- * @inner
- */
- function handleMouseUp( tracker, event ) {
- var gPoint;
- event = $.getEvent( event );
- gPoint = {
- id: $.MouseTracker.mousePointerId,
- type: 'mouse',
- isPrimary: true,
- currentPos: getMouseAbsolute( event ),
- currentTime: $.now()
- };
- if ( updatePointersUp( tracker, event, [ gPoint ], getStandardizedButton( event.button ) ) ) {
- releasePointer( tracker, 'mouse' );
- }
- }
- /**
- * @private
- * @inner
- */
- function onMouseMove( tracker, event ) {
- handleMouseMove( tracker, event );
- }
- /**
- * This handler is attached to the window object (on the capture phase) to emulate mouse capture.
- * onMouseMove is still attached to the tracked element, so stop propagation to avoid processing twice.
- *
- * @private
- * @inner
- */
- function onMouseMoveCaptured( tracker, event ) {
- handleMouseMove( tracker, event );
- $.stopEvent( event );
- }
- /**
- * @private
- * @inner
- */
- function handleMouseMove( tracker, event ) {
- var gPoint;
- event = $.getEvent( event );
- gPoint = {
- id: $.MouseTracker.mousePointerId,
- type: 'mouse',
- isPrimary: true,
- currentPos: getMouseAbsolute( event ),
- currentTime: $.now()
- };
- updatePointersMove( tracker, event, [ gPoint ] );
- }
- /**
- * @private
- * @inner
- */
- function abortContacts( tracker, event, pointsList ) {
- var i,
- gPointCount = pointsList.getLength(),
- abortGPoints = [];
- // Check contact count for hoverable pointer types before aborting
- if (pointsList.type === 'touch' || pointsList.contacts > 0) {
- for ( i = 0; i < gPointCount; i++ ) {
- abortGPoints.push( pointsList.getByIndex( i ) );
- }
- if ( abortGPoints.length > 0 ) {
- // simulate touchend/mouseup
- updatePointersUp( tracker, event, abortGPoints, 0 ); // 0 means primary button press/release or touch contact
- // release pointer capture
- pointsList.captureCount = 1;
- releasePointer( tracker, pointsList.type );
- // simulate touchleave/mouseout
- updatePointersExit( tracker, event, abortGPoints );
- }
- }
- }
- /**
- * @private
- * @inner
- */
- function onTouchStart( tracker, event ) {
- var time,
- i,
- j,
- touchCount = event.changedTouches.length,
- gPoints = [],
- parentGPoints,
- pointsList = tracker.getActivePointersListByType( 'touch' );
- time = $.now();
- if ( pointsList.getLength() > event.touches.length - touchCount ) {
- $.console.warn('Tracked touch contact count doesn\'t match event.touches.length. Removing all tracked touch pointers.');
- abortContacts( tracker, event, pointsList );
- }
- for ( i = 0; i < touchCount; i++ ) {
- gPoints.push( {
- id: event.changedTouches[ i ].identifier,
- type: 'touch',
- // isPrimary not set - let the updatePointers functions determine it
- currentPos: getMouseAbsolute( event.changedTouches[ i ] ),
- currentTime: time
- } );
- }
- // simulate touchenter on our tracked element
- updatePointersEnter( tracker, event, gPoints );
- // simulate touchenter on our tracked element's tracked ancestor elements
- for ( i = 0; i < MOUSETRACKERS.length; i++ ) {
- if ( MOUSETRACKERS[ i ] !== tracker && MOUSETRACKERS[ i ].isTracking() && isParentChild( MOUSETRACKERS[ i ].element, tracker.element ) ) {
- parentGPoints = [];
- for ( j = 0; j < touchCount; j++ ) {
- parentGPoints.push( {
- id: event.changedTouches[ j ].identifier,
- type: 'touch',
- // isPrimary not set - let the updatePointers functions determine it
- currentPos: getMouseAbsolute( event.changedTouches[ j ] ),
- currentTime: time
- } );
- }
- updatePointersEnter( MOUSETRACKERS[ i ], event, parentGPoints );
- }
- }
- if ( updatePointersDown( tracker, event, gPoints, 0 ) ) { // 0 means primary button press/release or touch contact
- $.stopEvent( event );
- capturePointer( tracker, 'touch', touchCount );
- }
- $.cancelEvent( event );
- }
- /**
- * @private
- * @inner
- */
- function onTouchEnd( tracker, event ) {
- handleTouchEnd( tracker, event );
- }
- /**
- * This handler is attached to the window object (on the capture phase) to emulate pointer capture.
- * onTouchEnd is still attached to the tracked element, so stop propagation to avoid processing twice.
- *
- * @private
- * @inner
- */
- function onTouchEndCaptured( tracker, event ) {
- handleTouchEnd( tracker, event );
- $.stopEvent( event );
- }
- /**
- * @private
- * @inner
- */
- function handleTouchEnd( tracker, event ) {
- var time,
- i,
- j,
- touchCount = event.changedTouches.length,
- gPoints = [],
- parentGPoints;
- time = $.now();
- for ( i = 0; i < touchCount; i++ ) {
- gPoints.push( {
- id: event.changedTouches[ i ].identifier,
- type: 'touch',
- // isPrimary not set - let the updatePointers functions determine it
- currentPos: getMouseAbsolute( event.changedTouches[ i ] ),
- currentTime: time
- } );
- }
- if ( updatePointersUp( tracker, event, gPoints, 0 ) ) {
- releasePointer( tracker, 'touch', touchCount );
- }
- // simulate touchleave on our tracked element
- updatePointersExit( tracker, event, gPoints );
- // simulate touchleave on our tracked element's tracked ancestor elements
- for ( i = 0; i < MOUSETRACKERS.length; i++ ) {
- if ( MOUSETRACKERS[ i ] !== tracker && MOUSETRACKERS[ i ].isTracking() && isParentChild( MOUSETRACKERS[ i ].element, tracker.element ) ) {
- parentGPoints = [];
- for ( j = 0; j < touchCount; j++ ) {
- parentGPoints.push( {
- id: event.changedTouches[ j ].identifier,
- type: 'touch',
- // isPrimary not set - let the updatePointers functions determine it
- currentPos: getMouseAbsolute( event.changedTouches[ j ] ),
- currentTime: time
- } );
- }
- updatePointersExit( MOUSETRACKERS[ i ], event, parentGPoints );
- }
- }
- $.cancelEvent( event );
- }
- /**
- * @private
- * @inner
- */
- function onTouchMove( tracker, event ) {
- handleTouchMove( tracker, event );
- }
- /**
- * This handler is attached to the window object (on the capture phase) to emulate pointer capture.
- * onTouchMove is still attached to the tracked element, so stop propagation to avoid processing twice.
- *
- * @private
- * @inner
- */
- function onTouchMoveCaptured( tracker, event ) {
- handleTouchMove( tracker, event );
- $.stopEvent( event );
- }
- /**
- * @private
- * @inner
- */
- function handleTouchMove( tracker, event ) {
- var i,
- touchCount = event.changedTouches.length,
- gPoints = [];
- for ( i = 0; i < touchCount; i++ ) {
- gPoints.push( {
- id: event.changedTouches[ i ].identifier,
- type: 'touch',
- // isPrimary not set - let the updatePointers functions determine it
- currentPos: getMouseAbsolute( event.changedTouches[ i ] ),
- currentTime: $.now()
- } );
- }
- updatePointersMove( tracker, event, gPoints );
- $.cancelEvent( event );
- }
- /**
- * @private
- * @inner
- */
- function onTouchCancel( tracker, event ) {
- var pointsList = tracker.getActivePointersListByType('touch');
- abortContacts( tracker, event, pointsList );
- }
- /**
- * @private
- * @inner
- */
- function onGestureStart( tracker, event ) {
- event.stopPropagation();
- event.preventDefault();
- return false;
- }
- /**
- * @private
- * @inner
- */
- function onGestureChange( tracker, event ) {
- event.stopPropagation();
- event.preventDefault();
- return false;
- }
- /**
- * @private
- * @inner
- */
- function onPointerOver( tracker, event ) {
- var gPoint;
- if ( event.currentTarget === event.relatedTarget || isParentChild( event.currentTarget, event.relatedTarget ) ) {
- return;
- }
- gPoint = {
- id: event.pointerId,
- type: getPointerType( event ),
- isPrimary: event.isPrimary,
- currentPos: getMouseAbsolute( event ),
- currentTime: $.now()
- };
- updatePointersEnter( tracker, event, [ gPoint ] );
- }
- /**
- * @private
- * @inner
- */
- function onPointerOut( tracker, event ) {
- var gPoint;
- if ( event.currentTarget === event.relatedTarget || isParentChild( event.currentTarget, event.relatedTarget ) ) {
- return;
- }
- gPoint = {
- id: event.pointerId,
- type: getPointerType( event ),
- isPrimary: event.isPrimary,
- currentPos: getMouseAbsolute( event ),
- currentTime: $.now()
- };
- updatePointersExit( tracker, event, [ gPoint ] );
- }
- /**
- * @private
- * @inner
- */
- function onPointerDown( tracker, event ) {
- var gPoint;
- gPoint = {
- id: event.pointerId,
- type: getPointerType( event ),
- isPrimary: event.isPrimary,
- currentPos: getMouseAbsolute( event ),
- currentTime: $.now()
- };
- if ( updatePointersDown( tracker, event, [ gPoint ], event.button ) ) {
- $.stopEvent( event );
- capturePointer( tracker, gPoint.type );
- }
- if ( tracker.clickHandler || tracker.dblClickHandler || tracker.pressHandler || tracker.dragHandler || tracker.dragEndHandler || tracker.pinchHandler ) {
- $.cancelEvent( event );
- }
- }
- /**
- * @private
- * @inner
- */
- function onPointerUp( tracker, event ) {
- handlePointerUp( tracker, event );
- }
- /**
- * This handler is attached to the window object (on the capture phase) to emulate mouse capture.
- * onPointerUp is still attached to the tracked element, so stop propagation to avoid processing twice.
- *
- * @private
- * @inner
- */
- function onPointerUpCaptured( tracker, event ) {
- var pointsList = tracker.getActivePointersListByType( getPointerType( event ) );
- if ( pointsList.getById( event.pointerId ) ) {
- handlePointerUp( tracker, event );
- }
- $.stopEvent( event );
- }
- /**
- * @private
- * @inner
- */
- function handlePointerUp( tracker, event ) {
- var gPoint;
- gPoint = {
- id: event.pointerId,
- type: getPointerType( event ),
- isPrimary: event.isPrimary,
- currentPos: getMouseAbsolute( event ),
- currentTime: $.now()
- };
- if ( updatePointersUp( tracker, event, [ gPoint ], event.button ) ) {
- releasePointer( tracker, gPoint.type );
- }
- }
- /**
- * @private
- * @inner
- */
- function onPointerMove( tracker, event ) {
- handlePointerMove( tracker, event );
- }
- /**
- * This handler is attached to the window object (on the capture phase) to emulate mouse capture.
- * onPointerMove is still attached to the tracked element, so stop propagation to avoid processing twice.
- *
- * @private
- * @inner
- */
- function onPointerMoveCaptured( tracker, event ) {
- var pointsList = tracker.getActivePointersListByType( getPointerType( event ) );
- if ( pointsList.getById( event.pointerId ) ) {
- handlePointerMove( tracker, event );
- }
- $.stopEvent( event );
- }
- /**
- * @private
- * @inner
- */
- function handlePointerMove( tracker, event ) {
- // Pointer changed coordinates, button state, pressure, tilt, or contact geometry (e.g. width and height)
- var gPoint;
- gPoint = {
- id: event.pointerId,
- type: getPointerType( event ),
- isPrimary: event.isPrimary,
- currentPos: getMouseAbsolute( event ),
- currentTime: $.now()
- };
- updatePointersMove( tracker, event, [ gPoint ] );
- }
- /**
- * @private
- * @inner
- */
- function onPointerCancel( tracker, event ) {
- var gPoint;
- gPoint = {
- id: event.pointerId,
- type: getPointerType( event )
- };
- updatePointersCancel( tracker, event, [ gPoint ] );
- }
- ///////////////////////////////////////////////////////////////////////////////
- // Device-agnostic DOM event handlers
- ///////////////////////////////////////////////////////////////////////////////
- /**
- * @function
- * @private
- * @inner
- * @param {OpenSeadragon.MouseTracker.GesturePointList} pointsList
- * The GesturePointList to track the pointer in.
- * @param {OpenSeadragon.MouseTracker.GesturePoint} gPoint
- * Gesture point to track.
- * @returns {Number} Number of gesture points in pointsList.
- */
- function startTrackingPointer( pointsList, gPoint ) {
- // If isPrimary is not known for the pointer then set it according to our rules:
- // true if the first pointer in the gesture, otherwise false
- if ( !gPoint.hasOwnProperty( 'isPrimary' ) ) {
- if ( pointsList.getLength() === 0 ) {
- gPoint.isPrimary = true;
- } else {
- gPoint.isPrimary = false;
- }
- }
- gPoint.speed = 0;
- gPoint.direction = 0;
- gPoint.contactPos = gPoint.currentPos;
- gPoint.contactTime = gPoint.currentTime;
- gPoint.lastPos = gPoint.currentPos;
- gPoint.lastTime = gPoint.currentTime;
- return pointsList.add( gPoint );
- }
- /**
- * @function
- * @private
- * @inner
- * @param {OpenSeadragon.MouseTracker.GesturePointList} pointsList
- * The GesturePointList to stop tracking the pointer on.
- * @param {OpenSeadragon.MouseTracker.GesturePoint} gPoint
- * Gesture point to stop tracking.
- * @returns {Number} Number of gesture points in pointsList.
- */
- function stopTrackingPointer( pointsList, gPoint ) {
- var listLength,
- primaryPoint;
- if ( pointsList.getById( gPoint.id ) ) {
- listLength = pointsList.removeById( gPoint.id );
- // If isPrimary is not known for the pointer and we just removed the primary pointer from the list then we need to set another pointer as primary
- if ( !gPoint.hasOwnProperty( 'isPrimary' ) ) {
- primaryPoint = pointsList.getPrimary();
- if ( !primaryPoint ) {
- primaryPoint = pointsList.getByIndex( 0 );
- if ( primaryPoint ) {
- primaryPoint.isPrimary = true;
- }
- }
- }
- } else {
- listLength = pointsList.getLength();
- }
- return listLength;
- }
- /**
- * @function
- * @private
- * @inner
- * @param {OpenSeadragon.MouseTracker} tracker
- * A reference to the MouseTracker instance.
- * @param {Object} event
- * A reference to the originating DOM event.
- * @param {Array.<OpenSeadragon.MouseTracker.GesturePoint>} gPoints
- * Gesture points associated with the event.
- */
- function updatePointersEnter( tracker, event, gPoints ) {
- var pointsList = tracker.getActivePointersListByType( gPoints[ 0 ].type ),
- i,
- gPointCount = gPoints.length,
- curGPoint,
- updateGPoint,
- propagate;
- for ( i = 0; i < gPointCount; i++ ) {
- curGPoint = gPoints[ i ];
- updateGPoint = pointsList.getById( curGPoint.id );
- if ( updateGPoint ) {
- // Already tracking the pointer...update it
- updateGPoint.insideElement = true;
- updateGPoint.lastPos = updateGPoint.currentPos;
- updateGPoint.lastTime = updateGPoint.currentTime;
- updateGPoint.currentPos = curGPoint.currentPos;
- updateGPoint.currentTime = curGPoint.currentTime;
- curGPoint = updateGPoint;
- } else {
- // Initialize for tracking and add to the tracking list
- curGPoint.captured = false;
- curGPoint.insideElementPressed = false;
- curGPoint.insideElement = true;
- startTrackingPointer( pointsList, curGPoint );
- }
- // Enter
- if ( tracker.enterHandler ) {
- propagate = tracker.enterHandler(
- {
- eventSource: tracker,
- pointerType: curGPoint.type,
- position: getPointRelativeToAbsolute( curGPoint.currentPos, tracker.element ),
- buttons: pointsList.buttons,
- pointers: tracker.getActivePointerCount(),
- insideElementPressed: curGPoint.insideElementPressed,
- buttonDownAny: pointsList.buttons !== 0,
- isTouchEvent: curGPoint.type === 'touch',
- originalEvent: event,
- preventDefaultAction: false,
- userData: tracker.userData
- }
- );
- if ( propagate === false ) {
- $.cancelEvent( event );
- }
- }
- }
- }
- /**
- * @function
- * @private
- * @inner
- * @param {OpenSeadragon.MouseTracker} tracker
- * A reference to the MouseTracker instance.
- * @param {Object} event
- * A reference to the originating DOM event.
- * @param {Array.<OpenSeadragon.MouseTracker.GesturePoint>} gPoints
- * Gesture points associated with the event.
- */
- function updatePointersExit( tracker, event, gPoints ) {
- var pointsList = tracker.getActivePointersListByType(gPoints[0].type),
- i,
- gPointCount = gPoints.length,
- curGPoint,
- updateGPoint,
- propagate;
- for ( i = 0; i < gPointCount; i++ ) {
- curGPoint = gPoints[ i ];
- updateGPoint = pointsList.getById( curGPoint.id );
- if ( updateGPoint ) {
- // Already tracking the pointer. If captured then update it, else stop tracking it
- if ( updateGPoint.captured ) {
- updateGPoint.insideElement = false;
- updateGPoint.lastPos = updateGPoint.currentPos;
- updateGPoint.lastTime = updateGPoint.currentTime;
- updateGPoint.currentPos = curGPoint.currentPos;
- updateGPoint.currentTime = curGPoint.currentTime;
- } else {
- stopTrackingPointer( pointsList, updateGPoint );
- }
- curGPoint = updateGPoint;
- }
- // Exit
- if ( tracker.exitHandler ) {
- propagate = tracker.exitHandler(
- {
- eventSource: tracker,
- pointerType: curGPoint.type,
- position: getPointRelativeToAbsolute( curGPoint.currentPos, tracker.element ),
- buttons: pointsList.buttons,
- pointers: tracker.getActivePointerCount(),
- insideElementPressed: updateGPoint ? updateGPoint.insideElementPressed : false,
- buttonDownAny: pointsList.buttons !== 0,
- isTouchEvent: curGPoint.type === 'touch',
- originalEvent: event,
- preventDefaultAction: false,
- userData: tracker.userData
- }
- );
- if ( propagate === false ) {
- $.cancelEvent( event );
- }
- }
- }
- }
- /**
- * @function
- * @private
- * @inner
- * @param {OpenSeadragon.MouseTracker} tracker
- * A reference to the MouseTracker instance.
- * @param {Object} event
- * A reference to the originating DOM event.
- * @param {Array.<OpenSeadragon.MouseTracker.GesturePoint>} gPoints
- * Gesture points associated with the event.
- * @param {Number} buttonChanged
- * The button involved in the event: -1: none, 0: primary/left, 1: aux/middle, 2: secondary/right, 3: X1/back, 4: X2/forward, 5: pen eraser.
- * Note on chorded button presses (a button pressed when another button is already pressed): In the W3C Pointer Events model,
- * only one pointerdown/pointerup event combo is fired. Chorded button state changes instead fire pointermove events.
- *
- * @returns {Boolean} True if pointers should be captured to the tracked element, otherwise false.
- */
- function updatePointersDown( tracker, event, gPoints, buttonChanged ) {
- var delegate = THIS[ tracker.hash ],
- propagate,
- pointsList = tracker.getActivePointersListByType( gPoints[ 0 ].type ),
- i,
- gPointCount = gPoints.length,
- curGPoint,
- updateGPoint;
- if ( typeof event.buttons !== 'undefined' ) {
- pointsList.buttons = event.buttons;
- } else {
- if ( $.Browser.vendor === $.BROWSERS.IE && $.Browser.version < 9 ) {
- if ( buttonChanged === 0 ) {
- // Primary
- pointsList.buttons += 1;
- } else if ( buttonChanged === 1 ) {
- // Aux
- pointsList.buttons += 4;
- } else if ( buttonChanged === 2 ) {
- // Secondary
- pointsList.buttons += 2;
- } else if ( buttonChanged === 3 ) {
- // X1 (Back)
- pointsList.buttons += 8;
- } else if ( buttonChanged === 4 ) {
- // X2 (Forward)
- pointsList.buttons += 16;
- } else if ( buttonChanged === 5 ) {
- // Pen Eraser
- pointsList.buttons += 32;
- }
- } else {
- if ( buttonChanged === 0 ) {
- // Primary
- pointsList.buttons |= 1;
- } else if ( buttonChanged === 1 ) {
- // Aux
- pointsList.buttons |= 4;
- } else if ( buttonChanged === 2 ) {
- // Secondary
- pointsList.buttons |= 2;
- } else if ( buttonChanged === 3 ) {
- // X1 (Back)
- pointsList.buttons |= 8;
- } else if ( buttonChanged === 4 ) {
- // X2 (Forward)
- pointsList.buttons |= 16;
- } else if ( buttonChanged === 5 ) {
- // Pen Eraser
- pointsList.buttons |= 32;
- }
- }
- }
- // Some pointers may steal control from another pointer without firing the appropriate release events
- // e.g. Touching a screen while click-dragging with certain mice.
- var otherPointsLists = tracker.getActivePointersListsExceptType(gPoints[ 0 ].type);
- for (i = 0; i < otherPointsLists.length; i++) {
- //If another pointer has contact, simulate the release
- abortContacts(tracker, event, otherPointsLists[i]); // No-op if no active pointer
- }
- // Only capture and track primary button, pen, and touch contacts
- if ( buttonChanged !== 0 ) {
- // Aux Press
- if ( tracker.nonPrimaryPressHandler ) {
- propagate = tracker.nonPrimaryPressHandler(
- {
- eventSource: tracker,
- pointerType: gPoints[ 0 ].type,
- position: getPointRelativeToAbsolute( gPoints[ 0 ].currentPos, tracker.element ),
- button: buttonChanged,
- buttons: pointsList.buttons,
- isTouchEvent: gPoints[ 0 ].type === 'touch',
- originalEvent: event,
- preventDefaultAction: false,
- userData: tracker.userData
- }
- );
- if ( propagate === false ) {
- $.cancelEvent( event );
- }
- }
- return false;
- }
- for ( i = 0; i < gPointCount; i++ ) {
- curGPoint = gPoints[ i ];
- updateGPoint = pointsList.getById( curGPoint.id );
- if ( updateGPoint ) {
- // Already tracking the pointer...update it
- updateGPoint.captured = true;
- updateGPoint.insideElementPressed = true;
- updateGPoint.insideElement = true;
- updateGPoint.contactPos = curGPoint.currentPos;
- updateGPoint.contactTime = curGPoint.currentTime;
- updateGPoint.lastPos = updateGPoint.currentPos;
- updateGPoint.lastTime = updateGPoint.currentTime;
- updateGPoint.currentPos = curGPoint.currentPos;
- updateGPoint.currentTime = curGPoint.currentTime;
- curGPoint = updateGPoint;
- } else {
- // Initialize for tracking and add to the tracking list (no pointerover or pointermove event occurred before this)
- curGPoint.captured = true;
- curGPoint.insideElementPressed = true;
- curGPoint.insideElement = true;
- startTrackingPointer( pointsList, curGPoint );
- }
- pointsList.addContact();
- //$.console.log('contacts++ ', pointsList.contacts);
- if ( tracker.dragHandler || tracker.dragEndHandler || tracker.pinchHandler ) {
- $.MouseTracker.gesturePointVelocityTracker.addPoint( tracker, curGPoint );
- }
- if ( pointsList.contacts === 1 ) {
- // Press
- if ( tracker.pressHandler ) {
- propagate = tracker.pressHandler(
- {
- eventSource: tracker,
- pointerType: curGPoint.type,
- position: getPointRelativeToAbsolute( curGPoint.contactPos, tracker.element ),
- buttons: pointsList.buttons,
- isTouchEvent: curGPoint.type === 'touch',
- originalEvent: event,
- preventDefaultAction: false,
- userData: tracker.userData
- }
- );
- if ( propagate === false ) {
- $.cancelEvent( event );
- }
- }
- } else if ( pointsList.contacts === 2 ) {
- if ( tracker.pinchHandler && curGPoint.type === 'touch' ) {
- // Initialize for pinch
- delegate.pinchGPoints = pointsList.asArray();
- delegate.lastPinchDist = delegate.currentPinchDist = delegate.pinchGPoints[ 0 ].currentPos.distanceTo( delegate.pinchGPoints[ 1 ].currentPos );
- delegate.lastPinchCenter = delegate.currentPinchCenter = getCenterPoint( delegate.pinchGPoints[ 0 ].currentPos, delegate.pinchGPoints[ 1 ].currentPos );
- }
- }
- }
- return true;
- }
- /**
- * @function
- * @private
- * @inner
- * @param {OpenSeadragon.MouseTracker} tracker
- * A reference to the MouseTracker instance.
- * @param {Object} event
- * A reference to the originating DOM event.
- * @param {Array.<OpenSeadragon.MouseTracker.GesturePoint>} gPoints
- * Gesture points associated with the event.
- * @param {Number} buttonChanged
- * The button involved in the event: -1: none, 0: primary/left, 1: aux/middle, 2: secondary/right, 3: X1/back, 4: X2/forward, 5: pen eraser.
- * Note on chorded button presses (a button pressed when another button is already pressed): In the W3C Pointer Events model,
- * only one pointerdown/pointerup event combo is fired. Chorded button state changes instead fire pointermove events.
- *
- * @returns {Boolean} True if pointer capture should be released from the tracked element, otherwise false.
- */
- function updatePointersUp( tracker, event, gPoints, buttonChanged ) {
- var delegate = THIS[ tracker.hash ],
- pointsList = tracker.getActivePointersListByType( gPoints[ 0 ].type ),
- propagate,
- releasePoint,
- releaseTime,
- i,
- gPointCount = gPoints.length,
- curGPoint,
- updateGPoint,
- releaseCapture = false,
- wasCaptured = false,
- quick;
- if ( typeof event.buttons !== 'undefined' ) {
- pointsList.buttons = event.buttons;
- } else {
- if ( $.Browser.vendor === $.BROWSERS.IE && $.Browser.version < 9 ) {
- if ( buttonChanged === 0 ) {
- // Primary
- pointsList.buttons -= 1;
- } else if ( buttonChanged === 1 ) {
- // Aux
- pointsList.buttons -= 4;
- } else if ( buttonChanged === 2 ) {
- // Secondary
- pointsList.buttons -= 2;
- } else if ( buttonChanged === 3 ) {
- // X1 (Back)
- pointsList.buttons -= 8;
- } else if ( buttonChanged === 4 ) {
- // X2 (Forward)
- pointsList.buttons -= 16;
- } else if ( buttonChanged === 5 ) {
- // Pen Eraser
- pointsList.buttons -= 32;
- }
- } else {
- if ( buttonChanged === 0 ) {
- // Primary
- pointsList.buttons ^= ~1;
- } else if ( buttonChanged === 1 ) {
- // Aux
- pointsList.buttons ^= ~4;
- } else if ( buttonChanged === 2 ) {
- // Secondary
- pointsList.buttons ^= ~2;
- } else if ( buttonChanged === 3 ) {
- // X1 (Back)
- pointsList.buttons ^= ~8;
- } else if ( buttonChanged === 4 ) {
- // X2 (Forward)
- pointsList.buttons ^= ~16;
- } else if ( buttonChanged === 5 ) {
- // Pen Eraser
- pointsList.buttons ^= ~32;
- }
- }
- }
- // Only capture and track primary button, pen, and touch contacts
- if ( buttonChanged !== 0 ) {
- // Aux Release
- if ( tracker.nonPrimaryReleaseHandler ) {
- propagate = tracker.nonPrimaryReleaseHandler(
- {
- eventSource: tracker,
- pointerType: gPoints[ 0 ].type,
- position: getPointRelativeToAbsolute(gPoints[0].currentPos, tracker.element),
- button: buttonChanged,
- buttons: pointsList.buttons,
- isTouchEvent: gPoints[ 0 ].type === 'touch',
- originalEvent: event,
- preventDefaultAction: false,
- userData: tracker.userData
- }
- );
- if ( propagate === false ) {
- $.cancelEvent( event );
- }
- }
- // A primary mouse button may have been released while the non-primary button was down
- var otherPointsList = tracker.getActivePointersListByType("mouse");
- // Stop tracking the mouse; see https://github.com/openseadragon/openseadragon/pull/1223
- abortContacts(tracker, event, otherPointsList); // No-op if no active pointer
- return false;
- }
- for ( i = 0; i < gPointCount; i++ ) {
- curGPoint = gPoints[ i ];
- updateGPoint = pointsList.getById( curGPoint.id );
- if ( updateGPoint ) {
- // Update the pointer, stop tracking it if not still in this element
- if ( updateGPoint.captured ) {
- updateGPoint.captured = false;
- releaseCapture = true;
- wasCaptured = true;
- }
- updateGPoint.lastPos = updateGPoint.currentPos;
- updateGPoint.lastTime = updateGPoint.currentTime;
- updateGPoint.currentPos = curGPoint.currentPos;
- updateGPoint.currentTime = curGPoint.currentTime;
- if ( !updateGPoint.insideElement ) {
- stopTrackingPointer( pointsList, updateGPoint );
- }
- releasePoint = updateGPoint.currentPos;
- releaseTime = updateGPoint.currentTime;
- if ( wasCaptured ) {
- // Pointer was activated in our element but could have been removed in any element since events are captured to our element
- pointsList.removeContact();
- //$.console.log('contacts-- ', pointsList.contacts);
- if ( tracker.dragHandler || tracker.dragEndHandler || tracker.pinchHandler ) {
- $.MouseTracker.gesturePointVelocityTracker.removePoint( tracker, updateGPoint );
- }
- if ( pointsList.contacts === 0 ) {
- // Release (pressed in our element)
- if ( tracker.releaseHandler ) {
- propagate = tracker.releaseHandler(
- {
- eventSource: tracker,
- pointerType: updateGPoint.type,
- position: getPointRelativeToAbsolute( releasePoint, tracker.element ),
- buttons: pointsList.buttons,
- insideElementPressed: updateGPoint.insideElementPressed,
- insideElementReleased: updateGPoint.insideElement,
- isTouchEvent: updateGPoint.type === 'touch',
- originalEvent: event,
- preventDefaultAction: false,
- userData: tracker.userData
- }
- );
- if ( propagate === false ) {
- $.cancelEvent( event );
- }
- }
- // Drag End
- if ( tracker.dragEndHandler && !updateGPoint.currentPos.equals( updateGPoint.contactPos ) ) {
- propagate = tracker.dragEndHandler(
- {
- eventSource: tracker,
- pointerType: updateGPoint.type,
- position: getPointRelativeToAbsolute( updateGPoint.currentPos, tracker.element ),
- speed: updateGPoint.speed,
- direction: updateGPoint.direction,
- shift: event.shiftKey,
- isTouchEvent: updateGPoint.type === 'touch',
- originalEvent: event,
- preventDefaultAction: false,
- userData: tracker.userData
- }
- );
- if ( propagate === false ) {
- $.cancelEvent( event );
- }
- }
- // Click / Double-Click
- if ( ( tracker.clickHandler || tracker.dblClickHandler ) && updateGPoint.insideElement ) {
- quick = releaseTime - updateGPoint.contactTime <= tracker.clickTimeThreshold &&
- updateGPoint.contactPos.distanceTo( releasePoint ) <= tracker.clickDistThreshold;
- // Click
- if ( tracker.clickHandler ) {
- propagate = tracker.clickHandler(
- {
- eventSource: tracker,
- pointerType: updateGPoint.type,
- position: getPointRelativeToAbsolute( updateGPoint.currentPos, tracker.element ),
- quick: quick,
- shift: event.shiftKey,
- isTouchEvent: updateGPoint.type === 'touch',
- originalEvent: event,
- preventDefaultAction: false,
- userData: tracker.userData
- }
- );
- if ( propagate === false ) {
- $.cancelEvent( event );
- }
- }
- // Double-Click
- if ( tracker.dblClickHandler && quick ) {
- pointsList.clicks++;
- if ( pointsList.clicks === 1 ) {
- delegate.lastClickPos = releasePoint;
- /*jshint loopfunc:true*/
- delegate.dblClickTimeOut = setTimeout( function() {
- pointsList.clicks = 0;
- }, tracker.dblClickTimeThreshold );
- /*jshint loopfunc:false*/
- } else if ( pointsList.clicks === 2 ) {
- clearTimeout( delegate.dblClickTimeOut );
- pointsList.clicks = 0;
- if ( delegate.lastClickPos.distanceTo( releasePoint ) <= tracker.dblClickDistThreshold ) {
- propagate = tracker.dblClickHandler(
- {
- eventSource: tracker,
- pointerType: updateGPoint.type,
- position: getPointRelativeToAbsolute( updateGPoint.currentPos, tracker.element ),
- shift: event.shiftKey,
- isTouchEvent: updateGPoint.type === 'touch',
- originalEvent: event,
- preventDefaultAction: false,
- userData: tracker.userData
- }
- );
- if ( propagate === false ) {
- $.cancelEvent( event );
- }
- }
- delegate.lastClickPos = null;
- }
- }
- }
- } else if ( pointsList.contacts === 2 ) {
- if ( tracker.pinchHandler && updateGPoint.type === 'touch' ) {
- // Reset for pinch
- delegate.pinchGPoints = pointsList.asArray();
- delegate.lastPinchDist = delegate.currentPinchDist = delegate.pinchGPoints[ 0 ].currentPos.distanceTo( delegate.pinchGPoints[ 1 ].currentPos );
- delegate.lastPinchCenter = delegate.currentPinchCenter = getCenterPoint( delegate.pinchGPoints[ 0 ].currentPos, delegate.pinchGPoints[ 1 ].currentPos );
- }
- }
- } else {
- // Pointer was activated in another element but removed in our element
- // Release (pressed in another element)
- if ( tracker.releaseHandler ) {
- propagate = tracker.releaseHandler(
- {
- eventSource: tracker,
- pointerType: updateGPoint.type,
- position: getPointRelativeToAbsolute( releasePoint, tracker.element ),
- buttons: pointsList.buttons,
- insideElementPressed: updateGPoint.insideElementPressed,
- insideElementReleased: updateGPoint.insideElement,
- isTouchEvent: updateGPoint.type === 'touch',
- originalEvent: event,
- preventDefaultAction: false,
- userData: tracker.userData
- }
- );
- if ( propagate === false ) {
- $.cancelEvent( event );
- }
- }
- }
- }
- }
- return releaseCapture;
- }
- /**
- * Call when pointer(s) change coordinates, button state, pressure, tilt, or contact geometry (e.g. width and height)
- *
- * @function
- * @private
- * @inner
- * @param {OpenSeadragon.MouseTracker} tracker
- * A reference to the MouseTracker instance.
- * @param {Object} event
- * A reference to the originating DOM event.
- * @param {Array.<OpenSeadragon.MouseTracker.GesturePoint>} gPoints
- * Gesture points associated with the event.
- */
- function updatePointersMove( tracker, event, gPoints ) {
- var delegate = THIS[ tracker.hash ],
- pointsList = tracker.getActivePointersListByType( gPoints[ 0 ].type ),
- i,
- gPointCount = gPoints.length,
- curGPoint,
- updateGPoint,
- gPointArray,
- delta,
- propagate;
- if ( typeof event.buttons !== 'undefined' ) {
- pointsList.buttons = event.buttons;
- }
- for ( i = 0; i < gPointCount; i++ ) {
- curGPoint = gPoints[ i ];
- updateGPoint = pointsList.getById( curGPoint.id );
- if ( updateGPoint ) {
- // Already tracking the pointer...update it
- if ( curGPoint.hasOwnProperty( 'isPrimary' ) ) {
- updateGPoint.isPrimary = curGPoint.isPrimary;
- }
- updateGPoint.lastPos = updateGPoint.currentPos;
- updateGPoint.lastTime = updateGPoint.currentTime;
- updateGPoint.currentPos = curGPoint.currentPos;
- updateGPoint.currentTime = curGPoint.currentTime;
- } else {
- // Initialize for tracking and add to the tracking list (no pointerover or pointerdown event occurred before this)
- curGPoint.captured = false;
- curGPoint.insideElementPressed = false;
- curGPoint.insideElement = true;
- startTrackingPointer( pointsList, curGPoint );
- }
- }
- // Stop (mouse only)
- if ( tracker.stopHandler && gPoints[ 0 ].type === 'mouse' ) {
- clearTimeout( tracker.stopTimeOut );
- tracker.stopTimeOut = setTimeout( function() {
- handlePointerStop( tracker, event, gPoints[ 0 ].type );
- }, tracker.stopDelay );
- }
- if ( pointsList.contacts === 0 ) {
- // Move (no contacts: hovering mouse or other hover-capable device)
- if ( tracker.moveHandler ) {
- propagate = tracker.moveHandler(
- {
- eventSource: tracker,
- pointerType: gPoints[ 0 ].type,
- position: getPointRelativeToAbsolute( gPoints[ 0 ].currentPos, tracker.element ),
- buttons: pointsList.buttons,
- isTouchEvent: gPoints[ 0 ].type === 'touch',
- originalEvent: event,
- preventDefaultAction: false,
- userData: tracker.userData
- }
- );
- if ( propagate === false ) {
- $.cancelEvent( event );
- }
- }
- } else if ( pointsList.contacts === 1 ) {
- // Move (1 contact)
- if ( tracker.moveHandler ) {
- updateGPoint = pointsList.asArray()[ 0 ];
- propagate = tracker.moveHandler(
- {
- eventSource: tracker,
- pointerType: updateGPoint.type,
- position: getPointRelativeToAbsolute( updateGPoint.currentPos, tracker.element ),
- buttons: pointsList.buttons,
- isTouchEvent: updateGPoint.type === 'touch',
- originalEvent: event,
- preventDefaultAction: false,
- userData: tracker.userData
- }
- );
- if ( propagate === false ) {
- $.cancelEvent( event );
- }
- }
- // Drag
- if ( tracker.dragHandler ) {
- updateGPoint = pointsList.asArray()[ 0 ];
- delta = updateGPoint.currentPos.minus( updateGPoint.lastPos );
- propagate = tracker.dragHandler(
- {
- eventSource: tracker,
- pointerType: updateGPoint.type,
- position: getPointRelativeToAbsolute( updateGPoint.currentPos, tracker.element ),
- buttons: pointsList.buttons,
- delta: delta,
- speed: updateGPoint.speed,
- direction: updateGPoint.direction,
- shift: event.shiftKey,
- isTouchEvent: updateGPoint.type === 'touch',
- originalEvent: event,
- preventDefaultAction: false,
- userData: tracker.userData
- }
- );
- if ( propagate === false ) {
- $.cancelEvent( event );
- }
- }
- } else if ( pointsList.contacts === 2 ) {
- // Move (2 contacts, use center)
- if ( tracker.moveHandler ) {
- gPointArray = pointsList.asArray();
- propagate = tracker.moveHandler(
- {
- eventSource: tracker,
- pointerType: gPointArray[ 0 ].type,
- position: getPointRelativeToAbsolute( getCenterPoint( gPointArray[ 0 ].currentPos, gPointArray[ 1 ].currentPos ), tracker.element ),
- buttons: pointsList.buttons,
- isTouchEvent: gPointArray[ 0 ].type === 'touch',
- originalEvent: event,
- preventDefaultAction: false,
- userData: tracker.userData
- }
- );
- if ( propagate === false ) {
- $.cancelEvent( event );
- }
- }
- // Pinch
- if ( tracker.pinchHandler && gPoints[ 0 ].type === 'touch' ) {
- delta = delegate.pinchGPoints[ 0 ].currentPos.distanceTo( delegate.pinchGPoints[ 1 ].currentPos );
- if ( delta != delegate.currentPinchDist ) {
- delegate.lastPinchDist = delegate.currentPinchDist;
- delegate.currentPinchDist = delta;
- delegate.lastPinchCenter = delegate.currentPinchCenter;
- delegate.currentPinchCenter = getCenterPoint( delegate.pinchGPoints[ 0 ].currentPos, delegate.pinchGPoints[ 1 ].currentPos );
- propagate = tracker.pinchHandler(
- {
- eventSource: tracker,
- pointerType: 'touch',
- gesturePoints: delegate.pinchGPoints,
- lastCenter: getPointRelativeToAbsolute( delegate.lastPinchCenter, tracker.element ),
- center: getPointRelativeToAbsolute( delegate.currentPinchCenter, tracker.element ),
- lastDistance: delegate.lastPinchDist,
- distance: delegate.currentPinchDist,
- shift: event.shiftKey,
- originalEvent: event,
- preventDefaultAction: false,
- userData: tracker.userData
- }
- );
- if ( propagate === false ) {
- $.cancelEvent( event );
- }
- }
- }
- }
- }
- /**
- * @function
- * @private
- * @inner
- * @param {OpenSeadragon.MouseTracker} tracker
- * A reference to the MouseTracker instance.
- * @param {Object} event
- * A reference to the originating DOM event.
- * @param {Array.<OpenSeadragon.MouseTracker.GesturePoint>} gPoints
- * Gesture points associated with the event.
- */
- function updatePointersCancel( tracker, event, gPoints ) {
- updatePointersUp( tracker, event, gPoints, 0 );
- updatePointersExit( tracker, event, gPoints );
- }
- /**
- * @private
- * @inner
- */
- function handlePointerStop( tracker, originalMoveEvent, pointerType ) {
- if ( tracker.stopHandler ) {
- tracker.stopHandler( {
- eventSource: tracker,
- pointerType: pointerType,
- position: getMouseRelative( originalMoveEvent, tracker.element ),
- buttons: tracker.getActivePointersListByType( pointerType ).buttons,
- isTouchEvent: pointerType === 'touch',
- originalEvent: originalMoveEvent,
- preventDefaultAction: false,
- userData: tracker.userData
- } );
- }
- }
- /**
- * True if inside an iframe, otherwise false.
- * @member {Boolean} isInIframe
- * @private
- * @inner
- */
- var isInIframe = (function() {
- try {
- return window.self !== window.top;
- } catch (e) {
- return true;
- }
- })();
- /**
- * @function
- * @private
- * @inner
- * @returns {Boolean} True if the target has access rights to events, otherwise false.
- */
- function canAccessEvents (target) {
- try {
- return target.addEventListener && target.removeEventListener;
- } catch (e) {
- return false;
- }
- }
- }(OpenSeadragon));
- /*
- * OpenSeadragon - Control
- *
- * Copyright (C) 2009 CodePlex Foundation
- * Copyright (C) 2010-2013 OpenSeadragon contributors
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are
- * met:
- *
- * - Redistributions of source code must retain the above copyright notice,
- * this list of conditions and the following disclaimer.
- *
- * - Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * - Neither the name of CodePlex Foundation nor the names of its
- * contributors may be used to endorse or promote products derived from
- * this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
- * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
- * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
- * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
- (function( $ ){
- /**
- * An enumeration of supported locations where controls can be anchored.
- * The anchoring is always relative to the container.
- * @member ControlAnchor
- * @memberof OpenSeadragon
- * @static
- * @type {Object}
- * @property {Number} NONE
- * @property {Number} TOP_LEFT
- * @property {Number} TOP_RIGHT
- * @property {Number} BOTTOM_LEFT
- * @property {Number} BOTTOM_RIGHT
- * @property {Number} ABSOLUTE
- */
- $.ControlAnchor = {
- NONE: 0,
- TOP_LEFT: 1,
- TOP_RIGHT: 2,
- BOTTOM_RIGHT: 3,
- BOTTOM_LEFT: 4,
- ABSOLUTE: 5
- };
- /**
- * @class Control
- * @classdesc A Control represents any interface element which is meant to allow the user
- * to interact with the zoomable interface. Any control can be anchored to any
- * element.
- *
- * @memberof OpenSeadragon
- * @param {Element} element - the control element to be anchored in the container.
- * @param {Object } options - All required and optional settings for configuring a control element.
- * @param {OpenSeadragon.ControlAnchor} [options.anchor=OpenSeadragon.ControlAnchor.NONE] - the position of the control
- * relative to the container.
- * @param {Boolean} [options.attachToViewer=true] - Whether the control should be added directly to the viewer, or
- * directly to the container
- * @param {Boolean} [options.autoFade=true] - Whether the control should have the autofade behavior
- * @param {Element} container - the element to control will be anchored too.
- */
- $.Control = function ( element, options, container ) {
- var parent = element.parentNode;
- if (typeof options === 'number')
- {
- $.console.error("Passing an anchor directly into the OpenSeadragon.Control constructor is deprecated; " +
- "please use an options object instead. " +
- "Support for this deprecated variant is scheduled for removal in December 2013");
- options = {anchor: options};
- }
- options.attachToViewer = (typeof options.attachToViewer === 'undefined') ? true : options.attachToViewer;
- /**
- * True if the control should have autofade behavior.
- * @member {Boolean} autoFade
- * @memberof OpenSeadragon.Control#
- */
- this.autoFade = (typeof options.autoFade === 'undefined') ? true : options.autoFade;
- /**
- * The element providing the user interface with some type of control (e.g. a zoom-in button).
- * @member {Element} element
- * @memberof OpenSeadragon.Control#
- */
- this.element = element;
- /**
- * The position of the Control relative to its container.
- * @member {OpenSeadragon.ControlAnchor} anchor
- * @memberof OpenSeadragon.Control#
- */
- this.anchor = options.anchor;
- /**
- * The Control's containing element.
- * @member {Element} container
- * @memberof OpenSeadragon.Control#
- */
- this.container = container;
- /**
- * A neutral element surrounding the control element.
- * @member {Element} wrapper
- * @memberof OpenSeadragon.Control#
- */
- if ( this.anchor == $.ControlAnchor.ABSOLUTE ) {
- this.wrapper = $.makeNeutralElement( "div" );
- this.wrapper.style.position = "absolute";
- this.wrapper.style.top = typeof (options.top) == "number" ? (options.top + 'px') : options.top;
- this.wrapper.style.left = typeof (options.left) == "number" ? (options.left + 'px') : options.left;
- this.wrapper.style.height = typeof (options.height) == "number" ? (options.height + 'px') : options.height;
- this.wrapper.style.width = typeof (options.width) == "number" ? (options.width + 'px') : options.width;
- this.wrapper.style.margin = "0px";
- this.wrapper.style.padding = "0px";
- this.element.style.position = "relative";
- this.element.style.top = "0px";
- this.element.style.left = "0px";
- this.element.style.height = "100%";
- this.element.style.width = "100%";
- } else {
- this.wrapper = $.makeNeutralElement( "div" );
- this.wrapper.style.display = "inline-block";
- if ( this.anchor == $.ControlAnchor.NONE ) {
- // IE6 fix
- this.wrapper.style.width = this.wrapper.style.height = "100%";
- }
- }
- this.wrapper.appendChild( this.element );
- if (options.attachToViewer ) {
- if ( this.anchor == $.ControlAnchor.TOP_RIGHT ||
- this.anchor == $.ControlAnchor.BOTTOM_RIGHT ) {
- this.container.insertBefore(
- this.wrapper,
- this.container.firstChild
- );
- } else {
- this.container.appendChild( this.wrapper );
- }
- } else {
- parent.appendChild( this.wrapper );
- }
- };
- /** @lends OpenSeadragon.Control.prototype */
- $.Control.prototype = {
- /**
- * Removes the control from the container.
- * @function
- */
- destroy: function() {
- this.wrapper.removeChild( this.element );
- this.container.removeChild( this.wrapper );
- },
- /**
- * Determines if the control is currently visible.
- * @function
- * @return {Boolean} true if currently visible, false otherwise.
- */
- isVisible: function() {
- return this.wrapper.style.display != "none";
- },
- /**
- * Toggles the visibility of the control.
- * @function
- * @param {Boolean} visible - true to make visible, false to hide.
- */
- setVisible: function( visible ) {
- this.wrapper.style.display = visible ?
- ( this.anchor == $.ControlAnchor.ABSOLUTE ? 'block' : 'inline-block' ) :
- "none";
- },
- /**
- * Sets the opacity level for the control.
- * @function
- * @param {Number} opactiy - a value between 1 and 0 inclusively.
- */
- setOpacity: function( opacity ) {
- if ( this.element[ $.SIGNAL ] && $.Browser.vendor == $.BROWSERS.IE ) {
- $.setElementOpacity( this.element, opacity, true );
- } else {
- $.setElementOpacity( this.wrapper, opacity, true );
- }
- }
- };
- }( OpenSeadragon ));
- /*
- * OpenSeadragon - ControlDock
- *
- * Copyright (C) 2009 CodePlex Foundation
- * Copyright (C) 2010-2013 OpenSeadragon contributors
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are
- * met:
- *
- * - Redistributions of source code must retain the above copyright notice,
- * this list of conditions and the following disclaimer.
- *
- * - Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * - Neither the name of CodePlex Foundation nor the names of its
- * contributors may be used to endorse or promote products derived from
- * this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
- * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
- * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
- * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
- (function( $ ){
- /**
- * @class ControlDock
- * @classdesc Provides a container element (a <form> element) with support for the layout of control elements.
- *
- * @memberof OpenSeadragon
- */
- $.ControlDock = function( options ){
- var layouts = [ 'topleft', 'topright', 'bottomright', 'bottomleft'],
- layout,
- i;
- $.extend( true, this, {
- id: 'controldock-' + $.now() + '-' + Math.floor(Math.random() * 1000000),
- container: $.makeNeutralElement( 'div' ),
- controls: []
- }, options );
- // Disable the form's submit; otherwise button clicks and return keys
- // can trigger it.
- this.container.onsubmit = function() {
- return false;
- };
- if( this.element ){
- this.element = $.getElement( this.element );
- this.element.appendChild( this.container );
- this.element.style.position = 'relative';
- this.container.style.width = '100%';
- this.container.style.height = '100%';
- }
- for( i = 0; i < layouts.length; i++ ){
- layout = layouts[ i ];
- this.controls[ layout ] = $.makeNeutralElement( "div" );
- this.controls[ layout ].style.position = 'absolute';
- if ( layout.match( 'left' ) ){
- this.controls[ layout ].style.left = '0px';
- }
- if ( layout.match( 'right' ) ){
- this.controls[ layout ].style.right = '0px';
- }
- if ( layout.match( 'top' ) ){
- this.controls[ layout ].style.top = '0px';
- }
- if ( layout.match( 'bottom' ) ){
- this.controls[ layout ].style.bottom = '0px';
- }
- }
- this.container.appendChild( this.controls.topleft );
- this.container.appendChild( this.controls.topright );
- this.container.appendChild( this.controls.bottomright );
- this.container.appendChild( this.controls.bottomleft );
- };
- /** @lends OpenSeadragon.ControlDock.prototype */
- $.ControlDock.prototype = {
- /**
- * @function
- */
- addControl: function ( element, controlOptions ) {
- element = $.getElement( element );
- var div = null;
- if ( getControlIndex( this, element ) >= 0 ) {
- return; // they're trying to add a duplicate control
- }
- switch ( controlOptions.anchor ) {
- case $.ControlAnchor.TOP_RIGHT:
- div = this.controls.topright;
- element.style.position = "relative";
- element.style.paddingRight = "0px";
- element.style.paddingTop = "0px";
- break;
- case $.ControlAnchor.BOTTOM_RIGHT:
- div = this.controls.bottomright;
- element.style.position = "relative";
- element.style.paddingRight = "0px";
- element.style.paddingBottom = "0px";
- break;
- case $.ControlAnchor.BOTTOM_LEFT:
- div = this.controls.bottomleft;
- element.style.position = "relative";
- element.style.paddingLeft = "0px";
- element.style.paddingBottom = "0px";
- break;
- case $.ControlAnchor.TOP_LEFT:
- div = this.controls.topleft;
- element.style.position = "relative";
- element.style.paddingLeft = "0px";
- element.style.paddingTop = "0px";
- break;
- case $.ControlAnchor.ABSOLUTE:
- div = this.container;
- element.style.margin = "0px";
- element.style.padding = "0px";
- break;
- default:
- case $.ControlAnchor.NONE:
- div = this.container;
- element.style.margin = "0px";
- element.style.padding = "0px";
- break;
- }
- this.controls.push(
- new $.Control( element, controlOptions, div )
- );
- element.style.display = "inline-block";
- },
- /**
- * @function
- * @return {OpenSeadragon.ControlDock} Chainable.
- */
- removeControl: function ( element ) {
- element = $.getElement( element );
- var i = getControlIndex( this, element );
- if ( i >= 0 ) {
- this.controls[ i ].destroy();
- this.controls.splice( i, 1 );
- }
- return this;
- },
- /**
- * @function
- * @return {OpenSeadragon.ControlDock} Chainable.
- */
- clearControls: function () {
- while ( this.controls.length > 0 ) {
- this.controls.pop().destroy();
- }
- return this;
- },
- /**
- * @function
- * @return {Boolean}
- */
- areControlsEnabled: function () {
- var i;
- for ( i = this.controls.length - 1; i >= 0; i-- ) {
- if ( this.controls[ i ].isVisible() ) {
- return true;
- }
- }
- return false;
- },
- /**
- * @function
- * @return {OpenSeadragon.ControlDock} Chainable.
- */
- setControlsEnabled: function( enabled ) {
- var i;
- for ( i = this.controls.length - 1; i >= 0; i-- ) {
- this.controls[ i ].setVisible( enabled );
- }
- return this;
- }
- };
- ///////////////////////////////////////////////////////////////////////////////
- // Utility methods
- ///////////////////////////////////////////////////////////////////////////////
- function getControlIndex( dock, element ) {
- var controls = dock.controls,
- i;
- for ( i = controls.length - 1; i >= 0; i-- ) {
- if ( controls[ i ].element == element ) {
- return i;
- }
- }
- return -1;
- }
- }( OpenSeadragon ));
- /*
- * OpenSeadragon - Placement
- *
- * Copyright (C) 2010-2016 OpenSeadragon contributors
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are
- * met:
- *
- * - Redistributions of source code must retain the above copyright notice,
- * this list of conditions and the following disclaimer.
- *
- * - Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * - Neither the name of CodePlex Foundation nor the names of its
- * contributors may be used to endorse or promote products derived from
- * this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
- * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
- * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
- * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
- (function($) {
- /**
- * An enumeration of positions to anchor an element.
- * @member Placement
- * @memberOf OpenSeadragon
- * @static
- * @readonly
- * @property {OpenSeadragon.Placement} CENTER
- * @property {OpenSeadragon.Placement} TOP_LEFT
- * @property {OpenSeadragon.Placement} TOP
- * @property {OpenSeadragon.Placement} TOP_RIGHT
- * @property {OpenSeadragon.Placement} RIGHT
- * @property {OpenSeadragon.Placement} BOTTOM_RIGHT
- * @property {OpenSeadragon.Placement} BOTTOM
- * @property {OpenSeadragon.Placement} BOTTOM_LEFT
- * @property {OpenSeadragon.Placement} LEFT
- */
- $.Placement = $.freezeObject({
- CENTER: 0,
- TOP_LEFT: 1,
- TOP: 2,
- TOP_RIGHT: 3,
- RIGHT: 4,
- BOTTOM_RIGHT: 5,
- BOTTOM: 6,
- BOTTOM_LEFT: 7,
- LEFT: 8,
- properties: {
- 0: {
- isLeft: false,
- isHorizontallyCentered: true,
- isRight: false,
- isTop: false,
- isVerticallyCentered: true,
- isBottom: false
- },
- 1: {
- isLeft: true,
- isHorizontallyCentered: false,
- isRight: false,
- isTop: true,
- isVerticallyCentered: false,
- isBottom: false
- },
- 2: {
- isLeft: false,
- isHorizontallyCentered: true,
- isRight: false,
- isTop: true,
- isVerticallyCentered: false,
- isBottom: false
- },
- 3: {
- isLeft: false,
- isHorizontallyCentered: false,
- isRight: true,
- isTop: true,
- isVerticallyCentered: false,
- isBottom: false
- },
- 4: {
- isLeft: false,
- isHorizontallyCentered: false,
- isRight: true,
- isTop: false,
- isVerticallyCentered: true,
- isBottom: false
- },
- 5: {
- isLeft: false,
- isHorizontallyCentered: false,
- isRight: true,
- isTop: false,
- isVerticallyCentered: false,
- isBottom: true
- },
- 6: {
- isLeft: false,
- isHorizontallyCentered: true,
- isRight: false,
- isTop: false,
- isVerticallyCentered: false,
- isBottom: true
- },
- 7: {
- isLeft: true,
- isHorizontallyCentered: false,
- isRight: false,
- isTop: false,
- isVerticallyCentered: false,
- isBottom: true
- },
- 8: {
- isLeft: true,
- isHorizontallyCentered: false,
- isRight: false,
- isTop: false,
- isVerticallyCentered: true,
- isBottom: false
- }
- }
- });
- }(OpenSeadragon));
- /*
- * OpenSeadragon - Viewer
- *
- * Copyright (C) 2009 CodePlex Foundation
- * Copyright (C) 2010-2013 OpenSeadragon contributors
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are
- * met:
- *
- * - Redistributions of source code must retain the above copyright notice,
- * this list of conditions and the following disclaimer.
- *
- * - Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * - Neither the name of CodePlex Foundation nor the names of its
- * contributors may be used to endorse or promote products derived from
- * this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
- * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
- * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
- * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
- (function( $ ){
- // dictionary from hash to private properties
- var THIS = {};
- var nextHash = 1;
- /**
- *
- * The main point of entry into creating a zoomable image on the page.<br>
- * <br>
- * We have provided an idiomatic javascript constructor which takes
- * a single object, but still support the legacy positional arguments.<br>
- * <br>
- * The options below are given in order that they appeared in the constructor
- * as arguments and we translate a positional call into an idiomatic call.<br>
- * <br>
- * To create a viewer, you can use either of this methods:<br>
- * <ul>
- * <li><code>var viewer = new OpenSeadragon.Viewer(options);</code></li>
- * <li><code>var viewer = OpenSeadragon(options);</code></li>
- * </ul>
- * @class Viewer
- * @classdesc The main OpenSeadragon viewer class.
- *
- * @memberof OpenSeadragon
- * @extends OpenSeadragon.EventSource
- * @extends OpenSeadragon.ControlDock
- * @param {OpenSeadragon.Options} options - Viewer options.
- *
- **/
- $.Viewer = function( options ) {
- var args = arguments,
- _this = this,
- i;
- //backward compatibility for positional args while preferring more
- //idiomatic javascript options object as the only argument
- if( !$.isPlainObject( options ) ){
- options = {
- id: args[ 0 ],
- xmlPath: args.length > 1 ? args[ 1 ] : undefined,
- prefixUrl: args.length > 2 ? args[ 2 ] : undefined,
- controls: args.length > 3 ? args[ 3 ] : undefined,
- overlays: args.length > 4 ? args[ 4 ] : undefined
- };
- }
- //options.config and the general config argument are deprecated
- //in favor of the more direct specification of optional settings
- //being pass directly on the options object
- if ( options.config ){
- $.extend( true, options, options.config );
- delete options.config;
- }
- //Public properties
- //Allow the options object to override global defaults
- $.extend( true, this, {
- //internal state and dom identifiers
- id: options.id,
- hash: options.hash || nextHash++,
- /**
- * Index for page to be shown first next time open() is called (only used in sequenceMode).
- * @member {Number} initialPage
- * @memberof OpenSeadragon.Viewer#
- */
- initialPage: 0,
- //dom nodes
- /**
- * The parent element of this Viewer instance, passed in when the Viewer was created.
- * @member {Element} element
- * @memberof OpenSeadragon.Viewer#
- */
- element: null,
- /**
- * A <div> element (provided by {@link OpenSeadragon.ControlDock}), the base element of this Viewer instance.<br><br>
- * Child element of {@link OpenSeadragon.Viewer#element}.
- * @member {Element} container
- * @memberof OpenSeadragon.Viewer#
- */
- container: null,
- /**
- * A <div> element, the element where user-input events are handled for panning and zooming.<br><br>
- * Child element of {@link OpenSeadragon.Viewer#container},
- * positioned on top of {@link OpenSeadragon.Viewer#keyboardCommandArea}.<br><br>
- * The parent of {@link OpenSeadragon.Drawer#canvas} instances.
- * @member {Element} canvas
- * @memberof OpenSeadragon.Viewer#
- */
- canvas: null,
- // Overlays list. An overlay allows to add html on top of the viewer.
- overlays: [],
- // Container inside the canvas where overlays are drawn.
- overlaysContainer: null,
- //private state properties
- previousBody: [],
- //This was originally initialized in the constructor and so could never
- //have anything in it. now it can because we allow it to be specified
- //in the options and is only empty by default if not specified. Also
- //this array was returned from get_controls which I find confusing
- //since this object has a controls property which is treated in other
- //functions like clearControls. I'm removing the accessors.
- customControls: [],
- //These are originally not part options but declared as members
- //in initialize. It's still considered idiomatic to put them here
- //source is here for backwards compatibility. It is not an official
- //part of the API and should not be relied upon.
- source: null,
- /**
- * Handles rendering of tiles in the viewer. Created for each TileSource opened.
- * @member {OpenSeadragon.Drawer} drawer
- * @memberof OpenSeadragon.Viewer#
- */
- drawer: null,
- /**
- * Keeps track of all of the tiled images in the scene.
- * @member {OpenSeadragon.Drawer} world
- * @memberof OpenSeadragon.Viewer#
- */
- world: null,
- /**
- * Handles coordinate-related functionality - zoom, pan, rotation, etc. Created for each TileSource opened.
- * @member {OpenSeadragon.Viewport} viewport
- * @memberof OpenSeadragon.Viewer#
- */
- viewport: null,
- /**
- * @member {OpenSeadragon.Navigator} navigator
- * @memberof OpenSeadragon.Viewer#
- */
- navigator: null,
- //A collection viewport is a separate viewport used to provide
- //simultaneous rendering of sets of tiles
- collectionViewport: null,
- collectionDrawer: null,
- //UI image resources
- //TODO: rename navImages to uiImages
- navImages: null,
- //interface button controls
- buttons: null,
- //TODO: this is defunct so safely remove it
- profiler: null
- }, $.DEFAULT_SETTINGS, options );
- if ( typeof ( this.hash) === "undefined" ) {
- throw new Error("A hash must be defined, either by specifying options.id or options.hash.");
- }
- if ( typeof ( THIS[ this.hash ] ) !== "undefined" ) {
- // We don't want to throw an error here, as the user might have discarded
- // the previous viewer with the same hash and now want to recreate it.
- $.console.warn("Hash " + this.hash + " has already been used.");
- }
- //Private state properties
- THIS[ this.hash ] = {
- "fsBoundsDelta": new $.Point( 1, 1 ),
- "prevContainerSize": null,
- "animating": false,
- "forceRedraw": false,
- "mouseInside": false,
- "group": null,
- // whether we should be continuously zooming
- "zooming": false,
- // how much we should be continuously zooming by
- "zoomFactor": null,
- "lastZoomTime": null,
- "fullPage": false,
- "onfullscreenchange": null
- };
- this._sequenceIndex = 0;
- this._firstOpen = true;
- this._updateRequestId = null;
- this._loadQueue = [];
- this.currentOverlays = [];
- this._lastScrollTime = $.now(); // variable used to help normalize the scroll event speed of different devices
- //Inherit some behaviors and properties
- $.EventSource.call( this );
- this.addHandler( 'open-failed', function ( event ) {
- var msg = $.getString( "Errors.OpenFailed", event.eventSource, event.message);
- _this._showMessage( msg );
- });
- $.ControlDock.call( this, options );
- //Deal with tile sources
- if (this.xmlPath) {
- //Deprecated option. Now it is preferred to use the tileSources option
- this.tileSources = [ this.xmlPath ];
- }
- this.element = this.element || document.getElementById( this.id );
- this.canvas = $.makeNeutralElement( "div" );
- this.canvas.className = "openseadragon-canvas";
- (function( style ){
- style.width = "100%";
- style.height = "100%";
- style.overflow = "hidden";
- style.position = "absolute";
- style.top = "0px";
- style.left = "0px";
- }(this.canvas.style));
- $.setElementTouchActionNone( this.canvas );
- if (options.tabIndex !== "") {
- this.canvas.tabIndex = (options.tabIndex === undefined ? 0 : options.tabIndex);
- }
- //the container is created through applying the ControlDock constructor above
- this.container.className = "openseadragon-container";
- (function( style ){
- style.width = "100%";
- style.height = "100%";
- style.position = "relative";
- style.overflow = "hidden";
- style.left = "0px";
- style.top = "0px";
- style.textAlign = "left"; // needed to protect against
- }( this.container.style ));
- this.container.insertBefore( this.canvas, this.container.firstChild );
- this.element.appendChild( this.container );
- //Used for toggling between fullscreen and default container size
- //TODO: these can be closure private and shared across Viewer
- // instances.
- this.bodyWidth = document.body.style.width;
- this.bodyHeight = document.body.style.height;
- this.bodyOverflow = document.body.style.overflow;
- this.docOverflow = document.documentElement.style.overflow;
- this.innerTracker = new $.MouseTracker({
- element: this.canvas,
- startDisabled: !this.mouseNavEnabled,
- clickTimeThreshold: this.clickTimeThreshold,
- clickDistThreshold: this.clickDistThreshold,
- dblClickTimeThreshold: this.dblClickTimeThreshold,
- dblClickDistThreshold: this.dblClickDistThreshold,
- keyDownHandler: $.delegate( this, onCanvasKeyDown ),
- keyHandler: $.delegate( this, onCanvasKeyPress ),
- clickHandler: $.delegate( this, onCanvasClick ),
- dblClickHandler: $.delegate( this, onCanvasDblClick ),
- dragHandler: $.delegate( this, onCanvasDrag ),
- dragEndHandler: $.delegate( this, onCanvasDragEnd ),
- enterHandler: $.delegate( this, onCanvasEnter ),
- exitHandler: $.delegate( this, onCanvasExit ),
- pressHandler: $.delegate( this, onCanvasPress ),
- releaseHandler: $.delegate( this, onCanvasRelease ),
- nonPrimaryPressHandler: $.delegate( this, onCanvasNonPrimaryPress ),
- nonPrimaryReleaseHandler: $.delegate( this, onCanvasNonPrimaryRelease ),
- scrollHandler: $.delegate( this, onCanvasScroll ),
- pinchHandler: $.delegate( this, onCanvasPinch )
- });
- this.outerTracker = new $.MouseTracker({
- element: this.container,
- startDisabled: !this.mouseNavEnabled,
- clickTimeThreshold: this.clickTimeThreshold,
- clickDistThreshold: this.clickDistThreshold,
- dblClickTimeThreshold: this.dblClickTimeThreshold,
- dblClickDistThreshold: this.dblClickDistThreshold,
- enterHandler: $.delegate( this, onContainerEnter ),
- exitHandler: $.delegate( this, onContainerExit )
- });
- if( this.toolbar ){
- this.toolbar = new $.ControlDock({ element: this.toolbar });
- }
- this.bindStandardControls();
- THIS[ this.hash ].prevContainerSize = _getSafeElemSize( this.container );
- // Create the world
- this.world = new $.World({
- viewer: this
- });
- this.world.addHandler('add-item', function(event) {
- // For backwards compatibility, we maintain the source property
- _this.source = _this.world.getItemAt(0).source;
- THIS[ _this.hash ].forceRedraw = true;
- if (!_this._updateRequestId) {
- _this._updateRequestId = scheduleUpdate( _this, updateMulti );
- }
- });
- this.world.addHandler('remove-item', function(event) {
- // For backwards compatibility, we maintain the source property
- if (_this.world.getItemCount()) {
- _this.source = _this.world.getItemAt(0).source;
- } else {
- _this.source = null;
- }
- THIS[ _this.hash ].forceRedraw = true;
- });
- this.world.addHandler('metrics-change', function(event) {
- if (_this.viewport) {
- _this.viewport._setContentBounds(_this.world.getHomeBounds(), _this.world.getContentFactor());
- }
- });
- this.world.addHandler('item-index-change', function(event) {
- // For backwards compatibility, we maintain the source property
- _this.source = _this.world.getItemAt(0).source;
- });
- // Create the viewport
- this.viewport = new $.Viewport({
- containerSize: THIS[ this.hash ].prevContainerSize,
- springStiffness: this.springStiffness,
- animationTime: this.animationTime,
- minZoomImageRatio: this.minZoomImageRatio,
- maxZoomPixelRatio: this.maxZoomPixelRatio,
- visibilityRatio: this.visibilityRatio,
- wrapHorizontal: this.wrapHorizontal,
- wrapVertical: this.wrapVertical,
- defaultZoomLevel: this.defaultZoomLevel,
- minZoomLevel: this.minZoomLevel,
- maxZoomLevel: this.maxZoomLevel,
- viewer: this,
- degrees: this.degrees,
- flipped: this.flipped,
- navigatorRotate: this.navigatorRotate,
- homeFillsViewer: this.homeFillsViewer,
- margins: this.viewportMargins
- });
- this.viewport._setContentBounds(this.world.getHomeBounds(), this.world.getContentFactor());
- // Create the image loader
- this.imageLoader = new $.ImageLoader({
- jobLimit: this.imageLoaderLimit,
- timeout: options.timeout
- });
- // Create the tile cache
- this.tileCache = new $.TileCache({
- maxImageCacheCount: this.maxImageCacheCount
- });
- // Create the drawer
- this.drawer = new $.Drawer({
- viewer: this,
- viewport: this.viewport,
- element: this.canvas,
- debugGridColor: this.debugGridColor
- });
- // Overlay container
- this.overlaysContainer = $.makeNeutralElement( "div" );
- this.canvas.appendChild( this.overlaysContainer );
- // Now that we have a drawer, see if it supports rotate. If not we need to remove the rotate buttons
- if (!this.drawer.canRotate()) {
- // Disable/remove the rotate left/right buttons since they aren't supported
- if (this.rotateLeft) {
- i = this.buttons.buttons.indexOf(this.rotateLeft);
- this.buttons.buttons.splice(i, 1);
- this.buttons.element.removeChild(this.rotateLeft.element);
- }
- if (this.rotateRight) {
- i = this.buttons.buttons.indexOf(this.rotateRight);
- this.buttons.buttons.splice(i, 1);
- this.buttons.element.removeChild(this.rotateRight.element);
- }
- }
- //Instantiate a navigator if configured
- if ( this.showNavigator){
- this.navigator = new $.Navigator({
- id: this.navigatorId,
- position: this.navigatorPosition,
- sizeRatio: this.navigatorSizeRatio,
- maintainSizeRatio: this.navigatorMaintainSizeRatio,
- top: this.navigatorTop,
- left: this.navigatorLeft,
- width: this.navigatorWidth,
- height: this.navigatorHeight,
- autoResize: this.navigatorAutoResize,
- autoFade: this.navigatorAutoFade,
- prefixUrl: this.prefixUrl,
- viewer: this,
- navigatorRotate: this.navigatorRotate,
- background: this.navigatorBackground,
- opacity: this.navigatorOpacity,
- borderColor: this.navigatorBorderColor,
- displayRegionColor: this.navigatorDisplayRegionColor,
- crossOriginPolicy: this.crossOriginPolicy
- });
- }
- // Sequence mode
- if (this.sequenceMode) {
- this.bindSequenceControls();
- }
- // Open initial tilesources
- if (this.tileSources) {
- this.open( this.tileSources );
- }
- // Add custom controls
- for ( i = 0; i < this.customControls.length; i++ ) {
- this.addControl(
- this.customControls[ i ].id,
- {anchor: this.customControls[ i ].anchor}
- );
- }
- // Initial fade out
- $.requestAnimationFrame( function(){
- beginControlsAutoHide( _this );
- } );
- // Initial canvas options
- if ( this.imageSmoothingEnabled !== undefined && !this.imageSmoothingEnabled){
- this.drawer.setImageSmoothingEnabled(this.imageSmoothingEnabled);
- }
- };
- $.extend( $.Viewer.prototype, $.EventSource.prototype, $.ControlDock.prototype, /** @lends OpenSeadragon.Viewer.prototype */{
- /**
- * @function
- * @return {Boolean}
- */
- isOpen: function () {
- return !!this.world.getItemCount();
- },
- // deprecated
- openDzi: function ( dzi ) {
- $.console.error( "[Viewer.openDzi] this function is deprecated; use Viewer.open() instead." );
- return this.open( dzi );
- },
- // deprecated
- openTileSource: function ( tileSource ) {
- $.console.error( "[Viewer.openTileSource] this function is deprecated; use Viewer.open() instead." );
- return this.open( tileSource );
- },
- /**
- * Open tiled images into the viewer, closing any others.
- * To get the TiledImage instance created by open, add an event listener for
- * {@link OpenSeadragon.Viewer.html#.event:open}, which when fired can be used to get access
- * to the instance, i.e., viewer.world.getItemAt(0).
- * @function
- * @param {Array|String|Object|Function} tileSources - This can be a TiledImage
- * specifier, a TileSource specifier, or an array of either. A TiledImage specifier
- * is the same as the options parameter for {@link OpenSeadragon.Viewer#addTiledImage},
- * except for the index property; images are added in sequence.
- * A TileSource specifier is anything you could pass as the tileSource property
- * of the options parameter for {@link OpenSeadragon.Viewer#addTiledImage}.
- * @param {Number} initialPage - If sequenceMode is true, display this page initially
- * for the given tileSources. If specified, will overwrite the Viewer's existing initialPage property.
- * @return {OpenSeadragon.Viewer} Chainable.
- * @fires OpenSeadragon.Viewer.event:open
- * @fires OpenSeadragon.Viewer.event:open-failed
- */
- open: function (tileSources, initialPage) {
- var _this = this;
- this.close();
- if (!tileSources) {
- return;
- }
- if (this.sequenceMode && $.isArray(tileSources)) {
- if (this.referenceStrip) {
- this.referenceStrip.destroy();
- this.referenceStrip = null;
- }
- if (typeof initialPage != 'undefined' && !isNaN(initialPage)) {
- this.initialPage = initialPage;
- }
- this.tileSources = tileSources;
- this._sequenceIndex = Math.max(0, Math.min(this.tileSources.length - 1, this.initialPage));
- if (this.tileSources.length) {
- this.open(this.tileSources[this._sequenceIndex]);
- if ( this.showReferenceStrip ){
- this.addReferenceStrip();
- }
- }
- this._updateSequenceButtons( this._sequenceIndex );
- return;
- }
- if (!$.isArray(tileSources)) {
- tileSources = [tileSources];
- }
- if (!tileSources.length) {
- return;
- }
- this._opening = true;
- var expected = tileSources.length;
- var successes = 0;
- var failures = 0;
- var failEvent;
- var checkCompletion = function() {
- if (successes + failures === expected) {
- if (successes) {
- if (_this._firstOpen || !_this.preserveViewport) {
- _this.viewport.goHome( true );
- _this.viewport.update();
- }
- _this._firstOpen = false;
- var source = tileSources[0];
- if (source.tileSource) {
- source = source.tileSource;
- }
- // Global overlays
- if( _this.overlays && !_this.preserveOverlays ){
- for ( var i = 0; i < _this.overlays.length; i++ ) {
- _this.currentOverlays[ i ] = getOverlayObject( _this, _this.overlays[ i ] );
- }
- }
- _this._drawOverlays();
- _this._opening = false;
- /**
- * Raised when the viewer has opened and loaded one or more TileSources.
- *
- * @event open
- * @memberof OpenSeadragon.Viewer
- * @type {object}
- * @property {OpenSeadragon.Viewer} eventSource - A reference to the Viewer which raised the event.
- * @property {OpenSeadragon.TileSource} source - The tile source that was opened.
- * @property {?Object} userData - Arbitrary subscriber-defined object.
- */
- // TODO: what if there are multiple sources?
- _this.raiseEvent( 'open', { source: source } );
- } else {
- _this._opening = false;
- /**
- * Raised when an error occurs loading a TileSource.
- *
- * @event open-failed
- * @memberof OpenSeadragon.Viewer
- * @type {object}
- * @property {OpenSeadragon.Viewer} eventSource - A reference to the Viewer which raised the event.
- * @property {String} message - Information about what failed.
- * @property {String} source - The tile source that failed.
- * @property {?Object} userData - Arbitrary subscriber-defined object.
- */
- _this.raiseEvent( 'open-failed', failEvent );
- }
- }
- };
- var doOne = function(options) {
- if (!$.isPlainObject(options) || !options.tileSource) {
- options = {
- tileSource: options
- };
- }
- if (options.index !== undefined) {
- $.console.error('[Viewer.open] setting indexes here is not supported; use addTiledImage instead');
- delete options.index;
- }
- if (options.collectionImmediately === undefined) {
- options.collectionImmediately = true;
- }
- var originalSuccess = options.success;
- options.success = function(event) {
- successes++;
- // TODO: now that options has other things besides tileSource, the overlays
- // should probably be at the options level, not the tileSource level.
- if (options.tileSource.overlays) {
- for (var i = 0; i < options.tileSource.overlays.length; i++) {
- _this.addOverlay(options.tileSource.overlays[i]);
- }
- }
- if (originalSuccess) {
- originalSuccess(event);
- }
- checkCompletion();
- };
- var originalError = options.error;
- options.error = function(event) {
- failures++;
- if (!failEvent) {
- failEvent = event;
- }
- if (originalError) {
- originalError(event);
- }
- checkCompletion();
- };
- _this.addTiledImage(options);
- };
- // TileSources
- for (var i = 0; i < tileSources.length; i++) {
- doOne(tileSources[i]);
- }
- return this;
- },
- /**
- * @function
- * @return {OpenSeadragon.Viewer} Chainable.
- * @fires OpenSeadragon.Viewer.event:close
- */
- close: function ( ) {
- if ( !THIS[ this.hash ] ) {
- //this viewer has already been destroyed: returning immediately
- return this;
- }
- this._opening = false;
- if ( this.navigator ) {
- this.navigator.close();
- }
- if (!this.preserveOverlays) {
- this.clearOverlays();
- this.overlaysContainer.innerHTML = "";
- }
- THIS[ this.hash ].animating = false;
- this.world.removeAll();
- this.imageLoader.clear();
- /**
- * Raised when the viewer is closed (see {@link OpenSeadragon.Viewer#close}).
- *
- * @event close
- * @memberof OpenSeadragon.Viewer
- * @type {object}
- * @property {OpenSeadragon.Viewer} eventSource - A reference to the Viewer which raised the event.
- * @property {?Object} userData - Arbitrary subscriber-defined object.
- */
- this.raiseEvent( 'close' );
- return this;
- },
- /**
- * Function to destroy the viewer and clean up everything created by OpenSeadragon.
- *
- * Example:
- * var viewer = OpenSeadragon({
- * [...]
- * });
- *
- * //when you are done with the viewer:
- * viewer.destroy();
- * viewer = null; //important
- *
- * @function
- */
- destroy: function( ) {
- if ( !THIS[ this.hash ] ) {
- //this viewer has already been destroyed: returning immediately
- return;
- }
- this.close();
- this.clearOverlays();
- this.overlaysContainer.innerHTML = "";
- //TODO: implement this...
- //this.unbindSequenceControls()
- //this.unbindStandardControls()
- if (this.referenceStrip) {
- this.referenceStrip.destroy();
- this.referenceStrip = null;
- }
- if ( this._updateRequestId !== null ) {
- $.cancelAnimationFrame( this._updateRequestId );
- this._updateRequestId = null;
- }
- if ( this.drawer ) {
- this.drawer.destroy();
- }
- this.removeAllHandlers();
- // Go through top element (passed to us) and remove all children
- // Use removeChild to make sure it handles SVG or any non-html
- // also it performs better - http://jsperf.com/innerhtml-vs-removechild/15
- if (this.element){
- while (this.element.firstChild) {
- this.element.removeChild(this.element.firstChild);
- }
- }
- // destroy the mouse trackers
- if (this.innerTracker){
- this.innerTracker.destroy();
- }
- if (this.outerTracker){
- this.outerTracker.destroy();
- }
- THIS[ this.hash ] = null;
- delete THIS[ this.hash ];
- // clear all our references to dom objects
- this.canvas = null;
- this.container = null;
- // clear our reference to the main element - they will need to pass it in again, creating a new viewer
- this.element = null;
- },
- /**
- * @function
- * @return {Boolean}
- */
- isMouseNavEnabled: function () {
- return this.innerTracker.isTracking();
- },
- /**
- * @function
- * @param {Boolean} enabled - true to enable, false to disable
- * @return {OpenSeadragon.Viewer} Chainable.
- * @fires OpenSeadragon.Viewer.event:mouse-enabled
- */
- setMouseNavEnabled: function( enabled ){
- this.innerTracker.setTracking( enabled );
- this.outerTracker.setTracking( enabled );
- /**
- * Raised when mouse/touch navigation is enabled or disabled (see {@link OpenSeadragon.Viewer#setMouseNavEnabled}).
- *
- * @event mouse-enabled
- * @memberof OpenSeadragon.Viewer
- * @type {object}
- * @property {OpenSeadragon.Viewer} eventSource - A reference to the Viewer which raised the event.
- * @property {Boolean} enabled
- * @property {?Object} userData - Arbitrary subscriber-defined object.
- */
- this.raiseEvent( 'mouse-enabled', { enabled: enabled } );
- return this;
- },
- /**
- * @function
- * @return {Boolean}
- */
- areControlsEnabled: function () {
- var enabled = this.controls.length,
- i;
- for( i = 0; i < this.controls.length; i++ ){
- enabled = enabled && this.controls[ i ].isVisible();
- }
- return enabled;
- },
- /**
- * Shows or hides the controls (e.g. the default navigation buttons).
- *
- * @function
- * @param {Boolean} true to show, false to hide.
- * @return {OpenSeadragon.Viewer} Chainable.
- * @fires OpenSeadragon.Viewer.event:controls-enabled
- */
- setControlsEnabled: function( enabled ) {
- if( enabled ){
- abortControlsAutoHide( this );
- } else {
- beginControlsAutoHide( this );
- }
- /**
- * Raised when the navigation controls are shown or hidden (see {@link OpenSeadragon.Viewer#setControlsEnabled}).
- *
- * @event controls-enabled
- * @memberof OpenSeadragon.Viewer
- * @type {object}
- * @property {OpenSeadragon.Viewer} eventSource - A reference to the Viewer which raised the event.
- * @property {Boolean} enabled
- * @property {?Object} userData - Arbitrary subscriber-defined object.
- */
- this.raiseEvent( 'controls-enabled', { enabled: enabled } );
- return this;
- },
- /**
- * Turns debugging mode on or off for this viewer.
- *
- * @function
- * @param {Boolean} true to turn debug on, false to turn debug off.
- */
- setDebugMode: function(debugMode){
- for (var i = 0; i < this.world.getItemCount(); i++) {
- this.world.getItemAt(i).debugMode = debugMode;
- }
- this.debugMode = debugMode;
- this.forceRedraw();
- },
- /**
- * @function
- * @return {Boolean}
- */
- isFullPage: function () {
- return THIS[ this.hash ].fullPage;
- },
- /**
- * Toggle full page mode.
- * @function
- * @param {Boolean} fullPage
- * If true, enter full page mode. If false, exit full page mode.
- * @return {OpenSeadragon.Viewer} Chainable.
- * @fires OpenSeadragon.Viewer.event:pre-full-page
- * @fires OpenSeadragon.Viewer.event:full-page
- */
- setFullPage: function( fullPage ) {
- var body = document.body,
- bodyStyle = body.style,
- docStyle = document.documentElement.style,
- _this = this,
- nodes,
- i;
- //don't bother modifying the DOM if we are already in full page mode.
- if ( fullPage == this.isFullPage() ) {
- return this;
- }
- var fullPageEventArgs = {
- fullPage: fullPage,
- preventDefaultAction: false
- };
- /**
- * Raised when the viewer is about to change to/from full-page mode (see {@link OpenSeadragon.Viewer#setFullPage}).
- *
- * @event pre-full-page
- * @memberof OpenSeadragon.Viewer
- * @type {object}
- * @property {OpenSeadragon.Viewer} eventSource - A reference to the Viewer which raised the event.
- * @property {Boolean} fullPage - True if entering full-page mode, false if exiting full-page mode.
- * @property {Boolean} preventDefaultAction - Set to true to prevent full-page mode change. Default: false.
- * @property {?Object} userData - Arbitrary subscriber-defined object.
- */
- this.raiseEvent( 'pre-full-page', fullPageEventArgs );
- if ( fullPageEventArgs.preventDefaultAction ) {
- return this;
- }
- if ( fullPage ) {
- this.elementSize = $.getElementSize( this.element );
- this.pageScroll = $.getPageScroll();
- this.elementMargin = this.element.style.margin;
- this.element.style.margin = "0";
- this.elementPadding = this.element.style.padding;
- this.element.style.padding = "0";
- this.bodyMargin = bodyStyle.margin;
- this.docMargin = docStyle.margin;
- bodyStyle.margin = "0";
- docStyle.margin = "0";
- this.bodyPadding = bodyStyle.padding;
- this.docPadding = docStyle.padding;
- bodyStyle.padding = "0";
- docStyle.padding = "0";
- this.bodyWidth = bodyStyle.width;
- this.docWidth = docStyle.width;
- bodyStyle.width = "100%";
- docStyle.width = "100%";
- this.bodyHeight = bodyStyle.height;
- this.docHeight = docStyle.height;
- bodyStyle.height = "100%";
- docStyle.height = "100%";
- //when entering full screen on the ipad it wasn't sufficient to leave
- //the body intact as only only the top half of the screen would
- //respond to touch events on the canvas, while the bottom half treated
- //them as touch events on the document body. Thus we remove and store
- //the bodies elements and replace them when we leave full screen.
- this.previousBody = [];
- THIS[ this.hash ].prevElementParent = this.element.parentNode;
- THIS[ this.hash ].prevNextSibling = this.element.nextSibling;
- THIS[ this.hash ].prevElementWidth = this.element.style.width;
- THIS[ this.hash ].prevElementHeight = this.element.style.height;
- nodes = body.childNodes.length;
- for ( i = 0; i < nodes; i++ ) {
- this.previousBody.push( body.childNodes[ 0 ] );
- body.removeChild( body.childNodes[ 0 ] );
- }
- //If we've got a toolbar, we need to enable the user to use css to
- //preserve it in fullpage mode
- if ( this.toolbar && this.toolbar.element ) {
- //save a reference to the parent so we can put it back
- //in the long run we need a better strategy
- this.toolbar.parentNode = this.toolbar.element.parentNode;
- this.toolbar.nextSibling = this.toolbar.element.nextSibling;
- body.appendChild( this.toolbar.element );
- //Make sure the user has some ability to style the toolbar based
- //on the mode
- $.addClass( this.toolbar.element, 'fullpage' );
- }
- $.addClass( this.element, 'fullpage' );
- body.appendChild( this.element );
- this.element.style.height = $.getWindowSize().y + 'px';
- this.element.style.width = $.getWindowSize().x + 'px';
- if ( this.toolbar && this.toolbar.element ) {
- this.element.style.height = (
- $.getElementSize( this.element ).y - $.getElementSize( this.toolbar.element ).y
- ) + 'px';
- }
- THIS[ this.hash ].fullPage = true;
- // mouse will be inside container now
- $.delegate( this, onContainerEnter )( {} );
- } else {
- this.element.style.margin = this.elementMargin;
- this.element.style.padding = this.elementPadding;
- bodyStyle.margin = this.bodyMargin;
- docStyle.margin = this.docMargin;
- bodyStyle.padding = this.bodyPadding;
- docStyle.padding = this.docPadding;
- bodyStyle.width = this.bodyWidth;
- docStyle.width = this.docWidth;
- bodyStyle.height = this.bodyHeight;
- docStyle.height = this.docHeight;
- body.removeChild( this.element );
- nodes = this.previousBody.length;
- for ( i = 0; i < nodes; i++ ) {
- body.appendChild( this.previousBody.shift() );
- }
- $.removeClass( this.element, 'fullpage' );
- THIS[ this.hash ].prevElementParent.insertBefore(
- this.element,
- THIS[ this.hash ].prevNextSibling
- );
- //If we've got a toolbar, we need to enable the user to use css to
- //reset it to its original state
- if ( this.toolbar && this.toolbar.element ) {
- body.removeChild( this.toolbar.element );
- //Make sure the user has some ability to style the toolbar based
- //on the mode
- $.removeClass( this.toolbar.element, 'fullpage' );
- this.toolbar.parentNode.insertBefore(
- this.toolbar.element,
- this.toolbar.nextSibling
- );
- delete this.toolbar.parentNode;
- delete this.toolbar.nextSibling;
- }
- this.element.style.width = THIS[ this.hash ].prevElementWidth;
- this.element.style.height = THIS[ this.hash ].prevElementHeight;
- // After exiting fullPage or fullScreen, it can take some time
- // before the browser can actually set the scroll.
- var restoreScrollCounter = 0;
- var restoreScroll = function() {
- $.setPageScroll( _this.pageScroll );
- var pageScroll = $.getPageScroll();
- restoreScrollCounter++;
- if (restoreScrollCounter < 10 &&
- (pageScroll.x !== _this.pageScroll.x ||
- pageScroll.y !== _this.pageScroll.y)) {
- $.requestAnimationFrame( restoreScroll );
- }
- };
- $.requestAnimationFrame( restoreScroll );
- THIS[ this.hash ].fullPage = false;
- // mouse will likely be outside now
- $.delegate( this, onContainerExit )( { } );
- }
- if ( this.navigator && this.viewport ) {
- this.navigator.update( this.viewport );
- }
- /**
- * Raised when the viewer has changed to/from full-page mode (see {@link OpenSeadragon.Viewer#setFullPage}).
- *
- * @event full-page
- * @memberof OpenSeadragon.Viewer
- * @type {object}
- * @property {OpenSeadragon.Viewer} eventSource - A reference to the Viewer which raised the event.
- * @property {Boolean} fullPage - True if changed to full-page mode, false if exited full-page mode.
- * @property {?Object} userData - Arbitrary subscriber-defined object.
- */
- this.raiseEvent( 'full-page', { fullPage: fullPage } );
- return this;
- },
- /**
- * Toggle full screen mode if supported. Toggle full page mode otherwise.
- * @function
- * @param {Boolean} fullScreen
- * If true, enter full screen mode. If false, exit full screen mode.
- * @return {OpenSeadragon.Viewer} Chainable.
- * @fires OpenSeadragon.Viewer.event:pre-full-screen
- * @fires OpenSeadragon.Viewer.event:full-screen
- */
- setFullScreen: function( fullScreen ) {
- var _this = this;
- if ( !$.supportsFullScreen ) {
- return this.setFullPage( fullScreen );
- }
- if ( $.isFullScreen() === fullScreen ) {
- return this;
- }
- var fullScreeEventArgs = {
- fullScreen: fullScreen,
- preventDefaultAction: false
- };
- /**
- * Raised when the viewer is about to change to/from full-screen mode (see {@link OpenSeadragon.Viewer#setFullScreen}).
- * Note: the pre-full-screen event is not raised when the user is exiting
- * full-screen mode by pressing the Esc key. In that case, consider using
- * the full-screen, pre-full-page or full-page events.
- *
- * @event pre-full-screen
- * @memberof OpenSeadragon.Viewer
- * @type {object}
- * @property {OpenSeadragon.Viewer} eventSource - A reference to the Viewer which raised the event.
- * @property {Boolean} fullScreen - True if entering full-screen mode, false if exiting full-screen mode.
- * @property {Boolean} preventDefaultAction - Set to true to prevent full-screen mode change. Default: false.
- * @property {?Object} userData - Arbitrary subscriber-defined object.
- */
- this.raiseEvent( 'pre-full-screen', fullScreeEventArgs );
- if ( fullScreeEventArgs.preventDefaultAction ) {
- return this;
- }
- if ( fullScreen ) {
- this.setFullPage( true );
- // If the full page mode is not actually entered, we need to prevent
- // the full screen mode.
- if ( !this.isFullPage() ) {
- return this;
- }
- this.fullPageStyleWidth = this.element.style.width;
- this.fullPageStyleHeight = this.element.style.height;
- this.element.style.width = '100%';
- this.element.style.height = '100%';
- var onFullScreenChange = function() {
- var isFullScreen = $.isFullScreen();
- if ( !isFullScreen ) {
- $.removeEvent( document, $.fullScreenEventName, onFullScreenChange );
- $.removeEvent( document, $.fullScreenErrorEventName, onFullScreenChange );
- _this.setFullPage( false );
- if ( _this.isFullPage() ) {
- _this.element.style.width = _this.fullPageStyleWidth;
- _this.element.style.height = _this.fullPageStyleHeight;
- }
- }
- if ( _this.navigator && _this.viewport ) {
- //09/08/2018 - Fabroh : Fix issue #1504 : Ensure to get the navigator updated on fullscreen out with custom location with a timeout
- setTimeout(function(){
- _this.navigator.update( _this.viewport );
- });
- }
- /**
- * Raised when the viewer has changed to/from full-screen mode (see {@link OpenSeadragon.Viewer#setFullScreen}).
- *
- * @event full-screen
- * @memberof OpenSeadragon.Viewer
- * @type {object}
- * @property {OpenSeadragon.Viewer} eventSource - A reference to the Viewer which raised the event.
- * @property {Boolean} fullScreen - True if changed to full-screen mode, false if exited full-screen mode.
- * @property {?Object} userData - Arbitrary subscriber-defined object.
- */
- _this.raiseEvent( 'full-screen', { fullScreen: isFullScreen } );
- };
- $.addEvent( document, $.fullScreenEventName, onFullScreenChange );
- $.addEvent( document, $.fullScreenErrorEventName, onFullScreenChange );
- $.requestFullScreen( document.body );
- } else {
- $.exitFullScreen();
- }
- return this;
- },
- /**
- * @function
- * @return {Boolean}
- */
- isVisible: function () {
- return this.container.style.visibility != "hidden";
- },
- /**
- * @function
- * @param {Boolean} visible
- * @return {OpenSeadragon.Viewer} Chainable.
- * @fires OpenSeadragon.Viewer.event:visible
- */
- setVisible: function( visible ){
- this.container.style.visibility = visible ? "" : "hidden";
- /**
- * Raised when the viewer is shown or hidden (see {@link OpenSeadragon.Viewer#setVisible}).
- *
- * @event visible
- * @memberof OpenSeadragon.Viewer
- * @type {object}
- * @property {OpenSeadragon.Viewer} eventSource - A reference to the Viewer which raised the event.
- * @property {Boolean} visible
- * @property {?Object} userData - Arbitrary subscriber-defined object.
- */
- this.raiseEvent( 'visible', { visible: visible } );
- return this;
- },
- /**
- * Add a tiled image to the viewer.
- * options.tileSource can be anything that {@link OpenSeadragon.Viewer#open}
- * supports except arrays of images.
- * Note that you can specify options.width or options.height, but not both.
- * The other dimension will be calculated according to the item's aspect ratio.
- * If collectionMode is on (see {@link OpenSeadragon.Options}), the new image is
- * automatically arranged with the others.
- * @function
- * @param {Object} options
- * @param {String|Object|Function} options.tileSource - The TileSource specifier.
- * A String implies a url used to determine the tileSource implementation
- * based on the file extension of url. JSONP is implied by *.js,
- * otherwise the url is retrieved as text and the resulting text is
- * introspected to determine if its json, xml, or text and parsed.
- * An Object implies an inline configuration which has a single
- * property sufficient for being able to determine tileSource
- * implementation. If the object has a property which is a function
- * named 'getTileUrl', it is treated as a custom TileSource.
- * @param {Number} [options.index] The index of the item. Added on top of
- * all other items if not specified.
- * @param {Boolean} [options.replace=false] If true, the item at options.index will be
- * removed and the new item is added in its place. options.tileSource will be
- * interpreted and fetched if necessary before the old item is removed to avoid leaving
- * a gap in the world.
- * @param {Number} [options.x=0] The X position for the image in viewport coordinates.
- * @param {Number} [options.y=0] The Y position for the image in viewport coordinates.
- * @param {Number} [options.width=1] The width for the image in viewport coordinates.
- * @param {Number} [options.height] The height for the image in viewport coordinates.
- * @param {OpenSeadragon.Rect} [options.fitBounds] The bounds in viewport coordinates
- * to fit the image into. If specified, x, y, width and height get ignored.
- * @param {OpenSeadragon.Placement} [options.fitBoundsPlacement=OpenSeadragon.Placement.CENTER]
- * How to anchor the image in the bounds if options.fitBounds is set.
- * @param {OpenSeadragon.Rect} [options.clip] - An area, in image pixels, to clip to
- * (portions of the image outside of this area will not be visible). Only works on
- * browsers that support the HTML5 canvas.
- * @param {Number} [options.opacity=1] Proportional opacity of the tiled images (1=opaque, 0=hidden)
- * @param {Boolean} [options.preload=false] Default switch for loading hidden images (true loads, false blocks)
- * @param {Number} [options.degrees=0] Initial rotation of the tiled image around
- * its top left corner in degrees.
- * @param {String} [options.compositeOperation] How the image is composited onto other images.
- * @param {String} [options.crossOriginPolicy] The crossOriginPolicy for this specific image,
- * overriding viewer.crossOriginPolicy.
- * @param {Boolean} [options.ajaxWithCredentials] Whether to set withCredentials on tile AJAX
- * @param {Boolean} [options.loadTilesWithAjax]
- * Whether to load tile data using AJAX requests.
- * Defaults to the setting in {@link OpenSeadragon.Options}.
- * @param {Object} [options.ajaxHeaders]
- * A set of headers to include when making tile AJAX requests.
- * Note that these headers will be merged over any headers specified in {@link OpenSeadragon.Options}.
- * Specifying a falsy value for a header will clear its existing value set at the Viewer level (if any).
- * requests.
- * @param {Function} [options.success] A function that gets called when the image is
- * successfully added. It's passed the event object which contains a single property:
- * "item", which is the resulting instance of TiledImage.
- * @param {Function} [options.error] A function that gets called if the image is
- * unable to be added. It's passed the error event object, which contains "message"
- * and "source" properties.
- * @param {Boolean} [options.collectionImmediately=false] If collectionMode is on,
- * specifies whether to snap to the new arrangement immediately or to animate to it.
- * @param {String|CanvasGradient|CanvasPattern|Function} [options.placeholderFillStyle] - See {@link OpenSeadragon.Options}.
- * @fires OpenSeadragon.World.event:add-item
- * @fires OpenSeadragon.Viewer.event:add-item-failed
- */
- addTiledImage: function( options ) {
- $.console.assert(options, "[Viewer.addTiledImage] options is required");
- $.console.assert(options.tileSource, "[Viewer.addTiledImage] options.tileSource is required");
- $.console.assert(!options.replace || (options.index > -1 && options.index < this.world.getItemCount()),
- "[Viewer.addTiledImage] if options.replace is used, options.index must be a valid index in Viewer.world");
- var _this = this;
- if (options.replace) {
- options.replaceItem = _this.world.getItemAt(options.index);
- }
- this._hideMessage();
- if (options.placeholderFillStyle === undefined) {
- options.placeholderFillStyle = this.placeholderFillStyle;
- }
- if (options.opacity === undefined) {
- options.opacity = this.opacity;
- }
- if (options.preload === undefined) {
- options.preload = this.preload;
- }
- if (options.compositeOperation === undefined) {
- options.compositeOperation = this.compositeOperation;
- }
- if (options.crossOriginPolicy === undefined) {
- options.crossOriginPolicy = options.tileSource.crossOriginPolicy !== undefined ? options.tileSource.crossOriginPolicy : this.crossOriginPolicy;
- }
- if (options.ajaxWithCredentials === undefined) {
- options.ajaxWithCredentials = this.ajaxWithCredentials;
- }
- if (options.loadTilesWithAjax === undefined) {
- options.loadTilesWithAjax = this.loadTilesWithAjax;
- }
- if (options.ajaxHeaders === undefined || options.ajaxHeaders === null) {
- options.ajaxHeaders = this.ajaxHeaders;
- } else if ($.isPlainObject(options.ajaxHeaders) && $.isPlainObject(this.ajaxHeaders)) {
- options.ajaxHeaders = $.extend({}, this.ajaxHeaders, options.ajaxHeaders);
- }
- var myQueueItem = {
- options: options
- };
- function raiseAddItemFailed( event ) {
- for (var i = 0; i < _this._loadQueue.length; i++) {
- if (_this._loadQueue[i] === myQueueItem) {
- _this._loadQueue.splice(i, 1);
- break;
- }
- }
- if (_this._loadQueue.length === 0) {
- refreshWorld(myQueueItem);
- }
- /**
- * Raised when an error occurs while adding a item.
- * @event add-item-failed
- * @memberOf OpenSeadragon.Viewer
- * @type {object}
- * @property {OpenSeadragon.Viewer} eventSource - A reference to the Viewer which raised the event.
- * @property {String} message
- * @property {String} source
- * @property {Object} options The options passed to the addTiledImage method.
- * @property {?Object} userData - Arbitrary subscriber-defined object.
- */
- _this.raiseEvent( 'add-item-failed', event );
- if (options.error) {
- options.error(event);
- }
- }
- function refreshWorld(theItem) {
- if (_this.collectionMode) {
- _this.world.arrange({
- immediately: theItem.options.collectionImmediately,
- rows: _this.collectionRows,
- columns: _this.collectionColumns,
- layout: _this.collectionLayout,
- tileSize: _this.collectionTileSize,
- tileMargin: _this.collectionTileMargin
- });
- _this.world.setAutoRefigureSizes(true);
- }
- }
- if ($.isArray(options.tileSource)) {
- setTimeout(function() {
- raiseAddItemFailed({
- message: "[Viewer.addTiledImage] Sequences can not be added; add them one at a time instead.",
- source: options.tileSource,
- options: options
- });
- });
- return;
- }
- this._loadQueue.push(myQueueItem);
- function processReadyItems() {
- var queueItem, tiledImage, optionsClone;
- while (_this._loadQueue.length) {
- queueItem = _this._loadQueue[0];
- if (!queueItem.tileSource) {
- break;
- }
- _this._loadQueue.splice(0, 1);
- if (queueItem.options.replace) {
- var newIndex = _this.world.getIndexOfItem(queueItem.options.replaceItem);
- if (newIndex != -1) {
- queueItem.options.index = newIndex;
- }
- _this.world.removeItem(queueItem.options.replaceItem);
- }
- tiledImage = new $.TiledImage({
- viewer: _this,
- source: queueItem.tileSource,
- viewport: _this.viewport,
- drawer: _this.drawer,
- tileCache: _this.tileCache,
- imageLoader: _this.imageLoader,
- x: queueItem.options.x,
- y: queueItem.options.y,
- width: queueItem.options.width,
- height: queueItem.options.height,
- fitBounds: queueItem.options.fitBounds,
- fitBoundsPlacement: queueItem.options.fitBoundsPlacement,
- clip: queueItem.options.clip,
- placeholderFillStyle: queueItem.options.placeholderFillStyle,
- opacity: queueItem.options.opacity,
- preload: queueItem.options.preload,
- degrees: queueItem.options.degrees,
- compositeOperation: queueItem.options.compositeOperation,
- springStiffness: _this.springStiffness,
- animationTime: _this.animationTime,
- minZoomImageRatio: _this.minZoomImageRatio,
- wrapHorizontal: _this.wrapHorizontal,
- wrapVertical: _this.wrapVertical,
- immediateRender: _this.immediateRender,
- blendTime: _this.blendTime,
- alwaysBlend: _this.alwaysBlend,
- minPixelRatio: _this.minPixelRatio,
- smoothTileEdgesMinZoom: _this.smoothTileEdgesMinZoom,
- iOSDevice: _this.iOSDevice,
- crossOriginPolicy: queueItem.options.crossOriginPolicy,
- ajaxWithCredentials: queueItem.options.ajaxWithCredentials,
- loadTilesWithAjax: queueItem.options.loadTilesWithAjax,
- ajaxHeaders: queueItem.options.ajaxHeaders,
- debugMode: _this.debugMode
- });
- if (_this.collectionMode) {
- _this.world.setAutoRefigureSizes(false);
- }
- _this.world.addItem( tiledImage, {
- index: queueItem.options.index
- });
- if (_this._loadQueue.length === 0) {
- //this restores the autoRefigureSizes flag to true.
- refreshWorld(queueItem);
- }
- if (_this.world.getItemCount() === 1 && !_this.preserveViewport) {
- _this.viewport.goHome(true);
- }
- if (_this.navigator) {
- optionsClone = $.extend({}, queueItem.options, {
- replace: false, // navigator already removed the layer, nothing to replace
- originalTiledImage: tiledImage,
- tileSource: queueItem.tileSource
- });
- _this.navigator.addTiledImage(optionsClone);
- }
- if (queueItem.options.success) {
- queueItem.options.success({
- item: tiledImage
- });
- }
- }
- }
- getTileSourceImplementation( this, options.tileSource, options, function( tileSource ) {
- myQueueItem.tileSource = tileSource;
- // add everybody at the front of the queue that's ready to go
- processReadyItems();
- }, function( event ) {
- event.options = options;
- raiseAddItemFailed(event);
- // add everybody at the front of the queue that's ready to go
- processReadyItems();
- } );
- },
- /**
- * Add a simple image to the viewer.
- * The options are the same as the ones in {@link OpenSeadragon.Viewer#addTiledImage}
- * except for options.tileSource which is replaced by options.url.
- * @function
- * @param {Object} options - See {@link OpenSeadragon.Viewer#addTiledImage}
- * for all the options
- * @param {String} options.url - The URL of the image to add.
- * @fires OpenSeadragon.World.event:add-item
- * @fires OpenSeadragon.Viewer.event:add-item-failed
- */
- addSimpleImage: function(options) {
- $.console.assert(options, "[Viewer.addSimpleImage] options is required");
- $.console.assert(options.url, "[Viewer.addSimpleImage] options.url is required");
- var opts = $.extend({}, options, {
- tileSource: {
- type: 'image',
- url: options.url
- }
- });
- delete opts.url;
- this.addTiledImage(opts);
- },
- // deprecated
- addLayer: function( options ) {
- var _this = this;
- $.console.error( "[Viewer.addLayer] this function is deprecated; use Viewer.addTiledImage() instead." );
- var optionsClone = $.extend({}, options, {
- success: function(event) {
- _this.raiseEvent("add-layer", {
- options: options,
- drawer: event.item
- });
- },
- error: function(event) {
- _this.raiseEvent("add-layer-failed", event);
- }
- });
- this.addTiledImage(optionsClone);
- return this;
- },
- // deprecated
- getLayerAtLevel: function( level ) {
- $.console.error( "[Viewer.getLayerAtLevel] this function is deprecated; use World.getItemAt() instead." );
- return this.world.getItemAt(level);
- },
- // deprecated
- getLevelOfLayer: function( drawer ) {
- $.console.error( "[Viewer.getLevelOfLayer] this function is deprecated; use World.getIndexOfItem() instead." );
- return this.world.getIndexOfItem(drawer);
- },
- // deprecated
- getLayersCount: function() {
- $.console.error( "[Viewer.getLayersCount] this function is deprecated; use World.getItemCount() instead." );
- return this.world.getItemCount();
- },
- // deprecated
- setLayerLevel: function( drawer, level ) {
- $.console.error( "[Viewer.setLayerLevel] this function is deprecated; use World.setItemIndex() instead." );
- return this.world.setItemIndex(drawer, level);
- },
- // deprecated
- removeLayer: function( drawer ) {
- $.console.error( "[Viewer.removeLayer] this function is deprecated; use World.removeItem() instead." );
- return this.world.removeItem(drawer);
- },
- /**
- * Force the viewer to redraw its contents.
- * @returns {OpenSeadragon.Viewer} Chainable.
- */
- forceRedraw: function() {
- THIS[ this.hash ].forceRedraw = true;
- return this;
- },
- /**
- * @function
- * @return {OpenSeadragon.Viewer} Chainable.
- */
- bindSequenceControls: function(){
- //////////////////////////////////////////////////////////////////////////
- // Image Sequence Controls
- //////////////////////////////////////////////////////////////////////////
- var onFocusHandler = $.delegate( this, onFocus ),
- onBlurHandler = $.delegate( this, onBlur ),
- onNextHandler = $.delegate( this, onNext ),
- onPreviousHandler = $.delegate( this, onPrevious ),
- navImages = this.navImages,
- useGroup = true;
- if( this.showSequenceControl ){
- if( this.previousButton || this.nextButton ){
- //if we are binding to custom buttons then layout and
- //grouping is the responsibility of the page author
- useGroup = false;
- }
- this.previousButton = new $.Button({
- element: this.previousButton ? $.getElement( this.previousButton ) : null,
- clickTimeThreshold: this.clickTimeThreshold,
- clickDistThreshold: this.clickDistThreshold,
- tooltip: $.getString( "Tooltips.PreviousPage" ),
- srcRest: resolveUrl( this.prefixUrl, navImages.previous.REST ),
- srcGroup: resolveUrl( this.prefixUrl, navImages.previous.GROUP ),
- srcHover: resolveUrl( this.prefixUrl, navImages.previous.HOVER ),
- srcDown: resolveUrl( this.prefixUrl, navImages.previous.DOWN ),
- onRelease: onPreviousHandler,
- onFocus: onFocusHandler,
- onBlur: onBlurHandler
- });
- this.nextButton = new $.Button({
- element: this.nextButton ? $.getElement( this.nextButton ) : null,
- clickTimeThreshold: this.clickTimeThreshold,
- clickDistThreshold: this.clickDistThreshold,
- tooltip: $.getString( "Tooltips.NextPage" ),
- srcRest: resolveUrl( this.prefixUrl, navImages.next.REST ),
- srcGroup: resolveUrl( this.prefixUrl, navImages.next.GROUP ),
- srcHover: resolveUrl( this.prefixUrl, navImages.next.HOVER ),
- srcDown: resolveUrl( this.prefixUrl, navImages.next.DOWN ),
- onRelease: onNextHandler,
- onFocus: onFocusHandler,
- onBlur: onBlurHandler
- });
- if( !this.navPrevNextWrap ){
- this.previousButton.disable();
- }
- if (!this.tileSources || !this.tileSources.length) {
- this.nextButton.disable();
- }
- if( useGroup ){
- this.paging = new $.ButtonGroup({
- buttons: [
- this.previousButton,
- this.nextButton
- ],
- clickTimeThreshold: this.clickTimeThreshold,
- clickDistThreshold: this.clickDistThreshold
- });
- this.pagingControl = this.paging.element;
- if( this.toolbar ){
- this.toolbar.addControl(
- this.pagingControl,
- {anchor: $.ControlAnchor.BOTTOM_RIGHT}
- );
- }else{
- this.addControl(
- this.pagingControl,
- {anchor: this.sequenceControlAnchor || $.ControlAnchor.TOP_LEFT}
- );
- }
- }
- }
- return this;
- },
- /**
- * @function
- * @return {OpenSeadragon.Viewer} Chainable.
- */
- bindStandardControls: function(){
- //////////////////////////////////////////////////////////////////////////
- // Navigation Controls
- //////////////////////////////////////////////////////////////////////////
- var beginZoomingInHandler = $.delegate( this, beginZoomingIn ),
- endZoomingHandler = $.delegate( this, endZooming ),
- doSingleZoomInHandler = $.delegate( this, doSingleZoomIn ),
- beginZoomingOutHandler = $.delegate( this, beginZoomingOut ),
- doSingleZoomOutHandler = $.delegate( this, doSingleZoomOut ),
- onHomeHandler = $.delegate( this, onHome ),
- onFullScreenHandler = $.delegate( this, onFullScreen ),
- onRotateLeftHandler = $.delegate( this, onRotateLeft ),
- onRotateRightHandler = $.delegate( this, onRotateRight ),
- onFlipHandler = $.delegate( this, onFlip),
- onFocusHandler = $.delegate( this, onFocus ),
- onBlurHandler = $.delegate( this, onBlur ),
- navImages = this.navImages,
- buttons = [],
- useGroup = true;
- if ( this.showNavigationControl ) {
- if( this.zoomInButton || this.zoomOutButton ||
- this.homeButton || this.fullPageButton ||
- this.rotateLeftButton || this.rotateRightButton ||
- this.flipButton ) {
- //if we are binding to custom buttons then layout and
- //grouping is the responsibility of the page author
- useGroup = false;
- }
- if ( this.showZoomControl ) {
- buttons.push( this.zoomInButton = new $.Button({
- element: this.zoomInButton ? $.getElement( this.zoomInButton ) : null,
- clickTimeThreshold: this.clickTimeThreshold,
- clickDistThreshold: this.clickDistThreshold,
- tooltip: $.getString( "Tooltips.ZoomIn" ),
- srcRest: resolveUrl( this.prefixUrl, navImages.zoomIn.REST ),
- srcGroup: resolveUrl( this.prefixUrl, navImages.zoomIn.GROUP ),
- srcHover: resolveUrl( this.prefixUrl, navImages.zoomIn.HOVER ),
- srcDown: resolveUrl( this.prefixUrl, navImages.zoomIn.DOWN ),
- onPress: beginZoomingInHandler,
- onRelease: endZoomingHandler,
- onClick: doSingleZoomInHandler,
- onEnter: beginZoomingInHandler,
- onExit: endZoomingHandler,
- onFocus: onFocusHandler,
- onBlur: onBlurHandler
- }));
- buttons.push( this.zoomOutButton = new $.Button({
- element: this.zoomOutButton ? $.getElement( this.zoomOutButton ) : null,
- clickTimeThreshold: this.clickTimeThreshold,
- clickDistThreshold: this.clickDistThreshold,
- tooltip: $.getString( "Tooltips.ZoomOut" ),
- srcRest: resolveUrl( this.prefixUrl, navImages.zoomOut.REST ),
- srcGroup: resolveUrl( this.prefixUrl, navImages.zoomOut.GROUP ),
- srcHover: resolveUrl( this.prefixUrl, navImages.zoomOut.HOVER ),
- srcDown: resolveUrl( this.prefixUrl, navImages.zoomOut.DOWN ),
- onPress: beginZoomingOutHandler,
- onRelease: endZoomingHandler,
- onClick: doSingleZoomOutHandler,
- onEnter: beginZoomingOutHandler,
- onExit: endZoomingHandler,
- onFocus: onFocusHandler,
- onBlur: onBlurHandler
- }));
- }
- if ( this.showHomeControl ) {
- buttons.push( this.homeButton = new $.Button({
- element: this.homeButton ? $.getElement( this.homeButton ) : null,
- clickTimeThreshold: this.clickTimeThreshold,
- clickDistThreshold: this.clickDistThreshold,
- tooltip: $.getString( "Tooltips.Home" ),
- srcRest: resolveUrl( this.prefixUrl, navImages.home.REST ),
- srcGroup: resolveUrl( this.prefixUrl, navImages.home.GROUP ),
- srcHover: resolveUrl( this.prefixUrl, navImages.home.HOVER ),
- srcDown: resolveUrl( this.prefixUrl, navImages.home.DOWN ),
- onRelease: onHomeHandler,
- onFocus: onFocusHandler,
- onBlur: onBlurHandler
- }));
- }
- if ( this.showFullPageControl ) {
- buttons.push( this.fullPageButton = new $.Button({
- element: this.fullPageButton ? $.getElement( this.fullPageButton ) : null,
- clickTimeThreshold: this.clickTimeThreshold,
- clickDistThreshold: this.clickDistThreshold,
- tooltip: $.getString( "Tooltips.FullPage" ),
- srcRest: resolveUrl( this.prefixUrl, navImages.fullpage.REST ),
- srcGroup: resolveUrl( this.prefixUrl, navImages.fullpage.GROUP ),
- srcHover: resolveUrl( this.prefixUrl, navImages.fullpage.HOVER ),
- srcDown: resolveUrl( this.prefixUrl, navImages.fullpage.DOWN ),
- onRelease: onFullScreenHandler,
- onFocus: onFocusHandler,
- onBlur: onBlurHandler
- }));
- }
- if ( this.showRotationControl ) {
- buttons.push( this.rotateLeftButton = new $.Button({
- element: this.rotateLeftButton ? $.getElement( this.rotateLeftButton ) : null,
- clickTimeThreshold: this.clickTimeThreshold,
- clickDistThreshold: this.clickDistThreshold,
- tooltip: $.getString( "Tooltips.RotateLeft" ),
- srcRest: resolveUrl( this.prefixUrl, navImages.rotateleft.REST ),
- srcGroup: resolveUrl( this.prefixUrl, navImages.rotateleft.GROUP ),
- srcHover: resolveUrl( this.prefixUrl, navImages.rotateleft.HOVER ),
- srcDown: resolveUrl( this.prefixUrl, navImages.rotateleft.DOWN ),
- onRelease: onRotateLeftHandler,
- onFocus: onFocusHandler,
- onBlur: onBlurHandler
- }));
- buttons.push( this.rotateRightButton = new $.Button({
- element: this.rotateRightButton ? $.getElement( this.rotateRightButton ) : null,
- clickTimeThreshold: this.clickTimeThreshold,
- clickDistThreshold: this.clickDistThreshold,
- tooltip: $.getString( "Tooltips.RotateRight" ),
- srcRest: resolveUrl( this.prefixUrl, navImages.rotateright.REST ),
- srcGroup: resolveUrl( this.prefixUrl, navImages.rotateright.GROUP ),
- srcHover: resolveUrl( this.prefixUrl, navImages.rotateright.HOVER ),
- srcDown: resolveUrl( this.prefixUrl, navImages.rotateright.DOWN ),
- onRelease: onRotateRightHandler,
- onFocus: onFocusHandler,
- onBlur: onBlurHandler
- }));
- }
- if ( this.showFlipControl ) {
- buttons.push( this.flipButton = new $.Button({
- element: this.flipButton ? $.getElement( this.flipButton ) : null,
- clickTimeThreshold: this.clickTimeThreshold,
- clickDistThreshold: this.clickDistThreshold,
- tooltip: $.getString( "Tooltips.Flip" ),
- srcRest: resolveUrl( this.prefixUrl, navImages.flip.REST ),
- srcGroup: resolveUrl( this.prefixUrl, navImages.flip.GROUP ),
- srcHover: resolveUrl( this.prefixUrl, navImages.flip.HOVER ),
- srcDown: resolveUrl( this.prefixUrl, navImages.flip.DOWN ),
- onRelease: onFlipHandler,
- onFocus: onFocusHandler,
- onBlur: onBlurHandler
- }));
- }
- if ( useGroup ) {
- this.buttons = new $.ButtonGroup({
- buttons: buttons,
- clickTimeThreshold: this.clickTimeThreshold,
- clickDistThreshold: this.clickDistThreshold
- });
- this.navControl = this.buttons.element;
- this.addHandler( 'open', $.delegate( this, lightUp ) );
- if( this.toolbar ){
- this.toolbar.addControl(
- this.navControl,
- {anchor: this.navigationControlAnchor || $.ControlAnchor.TOP_LEFT}
- );
- } else {
- this.addControl(
- this.navControl,
- {anchor: this.navigationControlAnchor || $.ControlAnchor.TOP_LEFT}
- );
- }
- }
- }
- return this;
- },
- /**
- * Gets the active page of a sequence
- * @function
- * @return {Number}
- */
- currentPage: function() {
- return this._sequenceIndex;
- },
- /**
- * @function
- * @return {OpenSeadragon.Viewer} Chainable.
- * @fires OpenSeadragon.Viewer.event:page
- */
- goToPage: function( page ){
- if( this.tileSources && page >= 0 && page < this.tileSources.length ){
- this._sequenceIndex = page;
- this._updateSequenceButtons( page );
- this.open( this.tileSources[ page ] );
- if( this.referenceStrip ){
- this.referenceStrip.setFocus( page );
- }
- /**
- * Raised when the page is changed on a viewer configured with multiple image sources (see {@link OpenSeadragon.Viewer#goToPage}).
- *
- * @event page
- * @memberof OpenSeadragon.Viewer
- * @type {Object}
- * @property {OpenSeadragon.Viewer} eventSource - A reference to the Viewer which raised the event.
- * @property {Number} page - The page index.
- * @property {?Object} userData - Arbitrary subscriber-defined object.
- */
- this.raiseEvent( 'page', { page: page } );
- }
- return this;
- },
- /**
- * Adds an html element as an overlay to the current viewport. Useful for
- * highlighting words or areas of interest on an image or other zoomable
- * interface. The overlays added via this method are removed when the viewport
- * is closed which include when changing page.
- * @method
- * @param {Element|String|Object} element - A reference to an element or an id for
- * the element which will be overlaid. Or an Object specifying the configuration for the overlay.
- * If using an object, see {@link OpenSeadragon.Overlay} for a list of
- * all available options.
- * @param {OpenSeadragon.Point|OpenSeadragon.Rect} location - The point or
- * rectangle which will be overlaid. This is a viewport relative location.
- * @param {OpenSeadragon.Placement} placement - The position of the
- * viewport which the location coordinates will be treated as relative
- * to.
- * @param {function} onDraw - If supplied the callback is called when the overlay
- * needs to be drawn. It it the responsibility of the callback to do any drawing/positioning.
- * It is passed position, size and element.
- * @return {OpenSeadragon.Viewer} Chainable.
- * @fires OpenSeadragon.Viewer.event:add-overlay
- */
- addOverlay: function( element, location, placement, onDraw ) {
- var options;
- if( $.isPlainObject( element ) ){
- options = element;
- } else {
- options = {
- element: element,
- location: location,
- placement: placement,
- onDraw: onDraw
- };
- }
- element = $.getElement( options.element );
- if ( getOverlayIndex( this.currentOverlays, element ) >= 0 ) {
- // they're trying to add a duplicate overlay
- return this;
- }
- var overlay = getOverlayObject( this, options);
- this.currentOverlays.push(overlay);
- overlay.drawHTML( this.overlaysContainer, this.viewport );
- /**
- * Raised when an overlay is added to the viewer (see {@link OpenSeadragon.Viewer#addOverlay}).
- *
- * @event add-overlay
- * @memberof OpenSeadragon.Viewer
- * @type {object}
- * @property {OpenSeadragon.Viewer} eventSource - A reference to the Viewer which raised the event.
- * @property {Element} element - The overlay element.
- * @property {OpenSeadragon.Point|OpenSeadragon.Rect} location
- * @property {OpenSeadragon.Placement} placement
- * @property {?Object} userData - Arbitrary subscriber-defined object.
- */
- this.raiseEvent( 'add-overlay', {
- element: element,
- location: options.location,
- placement: options.placement
- });
- return this;
- },
- /**
- * Updates the overlay represented by the reference to the element or
- * element id moving it to the new location, relative to the new placement.
- * @method
- * @param {Element|String} element - A reference to an element or an id for
- * the element which is overlaid.
- * @param {OpenSeadragon.Point|OpenSeadragon.Rect} location - The point or
- * rectangle which will be overlaid. This is a viewport relative location.
- * @param {OpenSeadragon.Placement} placement - The position of the
- * viewport which the location coordinates will be treated as relative
- * to.
- * @return {OpenSeadragon.Viewer} Chainable.
- * @fires OpenSeadragon.Viewer.event:update-overlay
- */
- updateOverlay: function( element, location, placement ) {
- var i;
- element = $.getElement( element );
- i = getOverlayIndex( this.currentOverlays, element );
- if ( i >= 0 ) {
- this.currentOverlays[ i ].update( location, placement );
- THIS[ this.hash ].forceRedraw = true;
- /**
- * Raised when an overlay's location or placement changes
- * (see {@link OpenSeadragon.Viewer#updateOverlay}).
- *
- * @event update-overlay
- * @memberof OpenSeadragon.Viewer
- * @type {object}
- * @property {OpenSeadragon.Viewer} eventSource - A reference to the
- * Viewer which raised the event.
- * @property {Element} element
- * @property {OpenSeadragon.Point|OpenSeadragon.Rect} location
- * @property {OpenSeadragon.Placement} placement
- * @property {?Object} userData - Arbitrary subscriber-defined object.
- */
- this.raiseEvent( 'update-overlay', {
- element: element,
- location: location,
- placement: placement
- });
- }
- return this;
- },
- /**
- * Removes an overlay identified by the reference element or element id
- * and schedules an update.
- * @method
- * @param {Element|String} element - A reference to the element or an
- * element id which represent the ovelay content to be removed.
- * @return {OpenSeadragon.Viewer} Chainable.
- * @fires OpenSeadragon.Viewer.event:remove-overlay
- */
- removeOverlay: function( element ) {
- var i;
- element = $.getElement( element );
- i = getOverlayIndex( this.currentOverlays, element );
- if ( i >= 0 ) {
- this.currentOverlays[ i ].destroy();
- this.currentOverlays.splice( i, 1 );
- THIS[ this.hash ].forceRedraw = true;
- /**
- * Raised when an overlay is removed from the viewer
- * (see {@link OpenSeadragon.Viewer#removeOverlay}).
- *
- * @event remove-overlay
- * @memberof OpenSeadragon.Viewer
- * @type {object}
- * @property {OpenSeadragon.Viewer} eventSource - A reference to the
- * Viewer which raised the event.
- * @property {Element} element - The overlay element.
- * @property {?Object} userData - Arbitrary subscriber-defined object.
- */
- this.raiseEvent( 'remove-overlay', {
- element: element
- });
- }
- return this;
- },
- /**
- * Removes all currently configured Overlays from this Viewer and schedules
- * an update.
- * @method
- * @return {OpenSeadragon.Viewer} Chainable.
- * @fires OpenSeadragon.Viewer.event:clear-overlay
- */
- clearOverlays: function() {
- while ( this.currentOverlays.length > 0 ) {
- this.currentOverlays.pop().destroy();
- }
- THIS[ this.hash ].forceRedraw = true;
- /**
- * Raised when all overlays are removed from the viewer (see {@link OpenSeadragon.Drawer#clearOverlays}).
- *
- * @event clear-overlay
- * @memberof OpenSeadragon.Viewer
- * @type {object}
- * @property {OpenSeadragon.Viewer} eventSource - A reference to the Viewer which raised the event.
- * @property {?Object} userData - Arbitrary subscriber-defined object.
- */
- this.raiseEvent( 'clear-overlay', {} );
- return this;
- },
- /**
- * Finds an overlay identified by the reference element or element id
- * and returns it as an object, return null if not found.
- * @method
- * @param {Element|String} element - A reference to the element or an
- * element id which represents the overlay content.
- * @return {OpenSeadragon.Overlay} the matching overlay or null if none found.
- */
- getOverlayById: function( element ) {
- var i;
- element = $.getElement( element );
- i = getOverlayIndex( this.currentOverlays, element );
- if (i >= 0) {
- return this.currentOverlays[i];
- } else {
- return null;
- }
- },
- /**
- * Updates the sequence buttons.
- * @function OpenSeadragon.Viewer.prototype._updateSequenceButtons
- * @private
- * @param {Number} Sequence Value
- */
- _updateSequenceButtons: function( page ) {
- if ( this.nextButton ) {
- if(!this.tileSources || this.tileSources.length - 1 === page) {
- //Disable next button
- if ( !this.navPrevNextWrap ) {
- this.nextButton.disable();
- }
- } else {
- this.nextButton.enable();
- }
- }
- if ( this.previousButton ) {
- if ( page > 0 ) {
- //Enable previous button
- this.previousButton.enable();
- } else {
- if ( !this.navPrevNextWrap ) {
- this.previousButton.disable();
- }
- }
- }
- },
- /**
- * Display a message in the viewport
- * @function OpenSeadragon.Viewer.prototype._showMessage
- * @private
- * @param {String} text message
- */
- _showMessage: function ( message ) {
- this._hideMessage();
- var div = $.makeNeutralElement( "div" );
- div.appendChild( document.createTextNode( message ) );
- this.messageDiv = $.makeCenteredNode( div );
- $.addClass(this.messageDiv, "openseadragon-message");
- this.container.appendChild( this.messageDiv );
- },
- /**
- * Hide any currently displayed viewport message
- * @function OpenSeadragon.Viewer.prototype._hideMessage
- * @private
- */
- _hideMessage: function () {
- var div = this.messageDiv;
- if (div) {
- div.parentNode.removeChild(div);
- delete this.messageDiv;
- }
- },
- /**
- * Gets this viewer's gesture settings for the given pointer device type.
- * @method
- * @param {String} type - The pointer device type to get the gesture settings for ("mouse", "touch", "pen", etc.).
- * @return {OpenSeadragon.GestureSettings}
- */
- gestureSettingsByDeviceType: function ( type ) {
- switch ( type ) {
- case 'mouse':
- return this.gestureSettingsMouse;
- case 'touch':
- return this.gestureSettingsTouch;
- case 'pen':
- return this.gestureSettingsPen;
- default:
- return this.gestureSettingsUnknown;
- }
- },
- // private
- _drawOverlays: function() {
- var i,
- length = this.currentOverlays.length;
- for ( i = 0; i < length; i++ ) {
- this.currentOverlays[ i ].drawHTML( this.overlaysContainer, this.viewport );
- }
- },
- /**
- * Cancel the "in flight" images.
- */
- _cancelPendingImages: function() {
- this._loadQueue = [];
- },
- /**
- * Removes the reference strip and disables displaying it.
- * @function
- */
- removeReferenceStrip: function() {
- this.showReferenceStrip = false;
- if (this.referenceStrip) {
- this.referenceStrip.destroy();
- this.referenceStrip = null;
- }
- },
- /**
- * Enables and displays the reference strip based on the currently set tileSources.
- * Works only when the Viewer has sequenceMode set to true.
- * @function
- */
- addReferenceStrip: function() {
- this.showReferenceStrip = true;
- if (this.sequenceMode) {
- if (this.referenceStrip) {
- return;
- }
- if (this.tileSources.length && this.tileSources.length > 1) {
- this.referenceStrip = new $.ReferenceStrip({
- id: this.referenceStripElement,
- position: this.referenceStripPosition,
- sizeRatio: this.referenceStripSizeRatio,
- scroll: this.referenceStripScroll,
- height: this.referenceStripHeight,
- width: this.referenceStripWidth,
- tileSources: this.tileSources,
- prefixUrl: this.prefixUrl,
- viewer: this
- });
- this.referenceStrip.setFocus( this._sequenceIndex );
- }
- } else {
- $.console.warn('Attempting to display a reference strip while "sequenceMode" is off.');
- }
- }
- });
- /**
- * _getSafeElemSize is like getElementSize(), but refuses to return 0 for x or y,
- * which was causing some calling operations to return NaN.
- * @returns {Point}
- * @private
- */
- function _getSafeElemSize (oElement) {
- oElement = $.getElement( oElement );
- return new $.Point(
- (oElement.clientWidth === 0 ? 1 : oElement.clientWidth),
- (oElement.clientHeight === 0 ? 1 : oElement.clientHeight)
- );
- }
- /**
- * @function
- * @private
- */
- function getTileSourceImplementation( viewer, tileSource, imgOptions, successCallback,
- failCallback ) {
- var _this = viewer;
- //allow plain xml strings or json strings to be parsed here
- if ( $.type( tileSource ) == 'string' ) {
- //xml should start with "<" and end with ">"
- if ( tileSource.match( /^\s*<.*>\s*$/ ) ) {
- tileSource = $.parseXml( tileSource );
- //json should start with "{" or "[" and end with "}" or "]"
- } else if ( tileSource.match(/^\s*[\{\[].*[\}\]]\s*$/ ) ) {
- try {
- var tileSourceJ = $.parseJSON(tileSource);
- tileSource = tileSourceJ;
- } catch (e) {
- //tileSource = tileSource;
- }
- }
- }
- function waitUntilReady(tileSource, originalTileSource) {
- if (tileSource.ready) {
- successCallback(tileSource);
- } else {
- tileSource.addHandler('ready', function () {
- successCallback(tileSource);
- });
- tileSource.addHandler('open-failed', function (event) {
- failCallback({
- message: event.message,
- source: originalTileSource
- });
- });
- }
- }
- setTimeout( function() {
- if ( $.type( tileSource ) == 'string' ) {
- //If its still a string it means it must be a url at this point
- tileSource = new $.TileSource({
- url: tileSource,
- crossOriginPolicy: imgOptions.crossOriginPolicy !== undefined ?
- imgOptions.crossOriginPolicy : viewer.crossOriginPolicy,
- ajaxWithCredentials: viewer.ajaxWithCredentials,
- ajaxHeaders: viewer.ajaxHeaders,
- useCanvas: viewer.useCanvas,
- success: function( event ) {
- successCallback( event.tileSource );
- }
- });
- tileSource.addHandler( 'open-failed', function( event ) {
- failCallback( event );
- } );
- } else if ($.isPlainObject(tileSource) || tileSource.nodeType) {
- if (tileSource.crossOriginPolicy === undefined &&
- (imgOptions.crossOriginPolicy !== undefined || viewer.crossOriginPolicy !== undefined)) {
- tileSource.crossOriginPolicy = imgOptions.crossOriginPolicy !== undefined ?
- imgOptions.crossOriginPolicy : viewer.crossOriginPolicy;
- }
- if (tileSource.ajaxWithCredentials === undefined) {
- tileSource.ajaxWithCredentials = viewer.ajaxWithCredentials;
- }
- if (tileSource.useCanvas === undefined) {
- tileSource.useCanvas = viewer.useCanvas;
- }
- if ( $.isFunction( tileSource.getTileUrl ) ) {
- //Custom tile source
- var customTileSource = new $.TileSource( tileSource );
- customTileSource.getTileUrl = tileSource.getTileUrl;
- successCallback( customTileSource );
- } else {
- //inline configuration
- var $TileSource = $.TileSource.determineType( _this, tileSource );
- if ( !$TileSource ) {
- failCallback( {
- message: "Unable to load TileSource",
- source: tileSource
- });
- return;
- }
- var options = $TileSource.prototype.configure.apply( _this, [ tileSource ] );
- waitUntilReady(new $TileSource(options), tileSource);
- }
- } else {
- //can assume it's already a tile source implementation
- waitUntilReady(tileSource, tileSource);
- }
- });
- }
- function getOverlayObject( viewer, overlay ) {
- if ( overlay instanceof $.Overlay ) {
- return overlay;
- }
- var element = null;
- if ( overlay.element ) {
- element = $.getElement( overlay.element );
- } else {
- var id = overlay.id ?
- overlay.id :
- "openseadragon-overlay-" + Math.floor( Math.random() * 10000000 );
- element = $.getElement( overlay.id );
- if ( !element ) {
- element = document.createElement( "a" );
- element.href = "#/overlay/" + id;
- }
- element.id = id;
- $.addClass( element, overlay.className ?
- overlay.className :
- "openseadragon-overlay"
- );
- }
- var location = overlay.location;
- var width = overlay.width;
- var height = overlay.height;
- if (!location) {
- var x = overlay.x;
- var y = overlay.y;
- if (overlay.px !== undefined) {
- var rect = viewer.viewport.imageToViewportRectangle(new $.Rect(
- overlay.px,
- overlay.py,
- width || 0,
- height || 0));
- x = rect.x;
- y = rect.y;
- width = width !== undefined ? rect.width : undefined;
- height = height !== undefined ? rect.height : undefined;
- }
- location = new $.Point(x, y);
- }
- var placement = overlay.placement;
- if (placement && $.type(placement) === "string") {
- placement = $.Placement[overlay.placement.toUpperCase()];
- }
- return new $.Overlay({
- element: element,
- location: location,
- placement: placement,
- onDraw: overlay.onDraw,
- checkResize: overlay.checkResize,
- width: width,
- height: height,
- rotationMode: overlay.rotationMode
- });
- }
- /**
- * @private
- * @inner
- * Determines the index of the given overlay in the given overlays array.
- */
- function getOverlayIndex( overlays, element ) {
- var i;
- for ( i = overlays.length - 1; i >= 0; i-- ) {
- if ( overlays[ i ].element === element ) {
- return i;
- }
- }
- return -1;
- }
- ///////////////////////////////////////////////////////////////////////////////
- // Schedulers provide the general engine for animation
- ///////////////////////////////////////////////////////////////////////////////
- function scheduleUpdate( viewer, updateFunc ){
- return $.requestAnimationFrame( function(){
- updateFunc( viewer );
- } );
- }
- //provides a sequence in the fade animation
- function scheduleControlsFade( viewer ) {
- $.requestAnimationFrame( function(){
- updateControlsFade( viewer );
- });
- }
- //initiates an animation to hide the controls
- function beginControlsAutoHide( viewer ) {
- if ( !viewer.autoHideControls ) {
- return;
- }
- viewer.controlsShouldFade = true;
- viewer.controlsFadeBeginTime =
- $.now() +
- viewer.controlsFadeDelay;
- window.setTimeout( function(){
- scheduleControlsFade( viewer );
- }, viewer.controlsFadeDelay );
- }
- //determines if fade animation is done or continues the animation
- function updateControlsFade( viewer ) {
- var currentTime,
- deltaTime,
- opacity,
- i;
- if ( viewer.controlsShouldFade ) {
- currentTime = $.now();
- deltaTime = currentTime - viewer.controlsFadeBeginTime;
- opacity = 1.0 - deltaTime / viewer.controlsFadeLength;
- opacity = Math.min( 1.0, opacity );
- opacity = Math.max( 0.0, opacity );
- for ( i = viewer.controls.length - 1; i >= 0; i--) {
- if (viewer.controls[ i ].autoFade) {
- viewer.controls[ i ].setOpacity( opacity );
- }
- }
- if ( opacity > 0 ) {
- // fade again
- scheduleControlsFade( viewer );
- }
- }
- }
- //stop the fade animation on the controls and show them
- function abortControlsAutoHide( viewer ) {
- var i;
- viewer.controlsShouldFade = false;
- for ( i = viewer.controls.length - 1; i >= 0; i-- ) {
- viewer.controls[ i ].setOpacity( 1.0 );
- }
- }
- ///////////////////////////////////////////////////////////////////////////////
- // Default view event handlers.
- ///////////////////////////////////////////////////////////////////////////////
- function onFocus(){
- abortControlsAutoHide( this );
- }
- function onBlur(){
- beginControlsAutoHide( this );
- }
- function onCanvasKeyDown( event ) {
- var canvasKeyDownEventArgs = {
- originalEvent: event.originalEvent,
- preventDefaultAction: event.preventDefaultAction,
- preventVerticalPan: event.preventVerticalPan,
- preventHorizontalPan: event.preventHorizontalPan
- };
- /**
- * Raised when a keyboard key is pressed and the focus is on the {@link OpenSeadragon.Viewer#canvas} element.
- *
- * @event canvas-key
- * @memberof OpenSeadragon.Viewer
- * @type {object}
- * @property {OpenSeadragon.Viewer} eventSource - A reference to the Viewer which raised this event.
- * @property {Object} originalEvent - The original DOM event.
- * @property {Boolean} preventDefaultAction - Set to true to prevent default keyboard behaviour. Default: false.
- * @property {Boolean} preventVerticalPan - Set to true to prevent keyboard vertical panning. Default: false.
- * @property {Boolean} preventHorizontalPan - Set to true to prevent keyboard horizontal panning. Default: false.
- * @property {?Object} userData - Arbitrary subscriber-defined object.
- */
- this.raiseEvent('canvas-key', canvasKeyDownEventArgs);
- if ( !canvasKeyDownEventArgs.preventDefaultAction && !event.ctrl && !event.alt && !event.meta ) {
- switch( event.keyCode ){
- case 38://up arrow
- if (!canvasKeyDownEventArgs.preventVerticalPan) {
- if ( event.shift ) {
- this.viewport.zoomBy(1.1);
- } else {
- this.viewport.panBy(this.viewport.deltaPointsFromPixels(new $.Point(0, -this.pixelsPerArrowPress)));
- }
- this.viewport.applyConstraints();
- }
- return false;
- case 40://down arrow
- if (!canvasKeyDownEventArgs.preventVerticalPan) {
- if ( event.shift ) {
- this.viewport.zoomBy(0.9);
- } else {
- this.viewport.panBy(this.viewport.deltaPointsFromPixels(new $.Point(0, this.pixelsPerArrowPress)));
- }
- this.viewport.applyConstraints();
- }
- return false;
- case 37://left arrow
- if (!canvasKeyDownEventArgs.preventHorizontalPan) {
- this.viewport.panBy(this.viewport.deltaPointsFromPixels(new $.Point(-this.pixelsPerArrowPress, 0)));
- this.viewport.applyConstraints();
- }
- return false;
- case 39://right arrow
- if (!canvasKeyDownEventArgs.preventHorizontalPan) {
- this.viewport.panBy(this.viewport.deltaPointsFromPixels(new $.Point(this.pixelsPerArrowPress, 0)));
- this.viewport.applyConstraints();
- }
- return false;
- default:
- //console.log( 'navigator keycode %s', event.keyCode );
- return true;
- }
- } else {
- return true;
- }
- }
- function onCanvasKeyPress( event ) {
- var canvasKeyPressEventArgs = {
- originalEvent: event.originalEvent,
- preventDefaultAction: event.preventDefaultAction,
- preventVerticalPan: event.preventVerticalPan,
- preventHorizontalPan: event.preventHorizontalPan
- };
- // This event is documented in onCanvasKeyDown
- this.raiseEvent('canvas-key', canvasKeyPressEventArgs);
- if ( !canvasKeyPressEventArgs.preventDefaultAction && !event.ctrl && !event.alt && !event.meta ) {
- switch( event.keyCode ){
- case 43://=|+
- case 61://=|+
- this.viewport.zoomBy(1.1);
- this.viewport.applyConstraints();
- return false;
- case 45://-|_
- this.viewport.zoomBy(0.9);
- this.viewport.applyConstraints();
- return false;
- case 48://0|)
- this.viewport.goHome();
- this.viewport.applyConstraints();
- return false;
- case 119://w
- case 87://W
- if (!canvasKeyPressEventArgs.preventVerticalPan) {
- if ( event.shift ) {
- this.viewport.zoomBy(1.1);
- } else {
- this.viewport.panBy(this.viewport.deltaPointsFromPixels(new $.Point(0, -40)));
- }
- this.viewport.applyConstraints();
- }
- return false;
- case 115://s
- case 83://S
- if (!canvasKeyPressEventArgs.preventVerticalPan) {
- if ( event.shift ) {
- this.viewport.zoomBy(0.9);
- } else {
- this.viewport.panBy(this.viewport.deltaPointsFromPixels(new $.Point(0, 40)));
- }
- this.viewport.applyConstraints();
- }
- return false;
- case 97://a
- if (!canvasKeyPressEventArgs.preventHorizontalPan) {
- this.viewport.panBy(this.viewport.deltaPointsFromPixels(new $.Point(-40, 0)));
- this.viewport.applyConstraints();
- }
- return false;
- case 100://d
- if (!canvasKeyPressEventArgs.preventHorizontalPan) {
- this.viewport.panBy(this.viewport.deltaPointsFromPixels(new $.Point(40, 0)));
- this.viewport.applyConstraints();
- }
- return false;
- case 114: //r - clockwise rotation
- if(this.viewport.flipped){
- this.viewport.setRotation($.positiveModulo(this.viewport.degrees - this.rotationIncrement, 360));
- } else{
- this.viewport.setRotation($.positiveModulo(this.viewport.degrees + this.rotationIncrement, 360));
- }
- this.viewport.applyConstraints();
- return false;
- case 82: //R - counterclockwise rotation
- if(this.viewport.flipped){
- this.viewport.setRotation($.positiveModulo(this.viewport.degrees + this.rotationIncrement, 360));
- } else{
- this.viewport.setRotation($.positiveModulo(this.viewport.degrees - this.rotationIncrement, 360));
- }
- this.viewport.applyConstraints();
- return false;
- case 102: //f
- this.viewport.toggleFlip();
- return false;
- default:
- // console.log( 'navigator keycode %s', event.keyCode );
- return true;
- }
- } else {
- return true;
- }
- }
- function onCanvasClick( event ) {
- var gestureSettings;
- var haveKeyboardFocus = document.activeElement == this.canvas;
- // If we don't have keyboard focus, request it.
- if ( !haveKeyboardFocus ) {
- this.canvas.focus();
- }
- if(this.viewport.flipped){
- event.position.x = this.viewport.getContainerSize().x - event.position.x;
- }
- var canvasClickEventArgs = {
- tracker: event.eventSource,
- position: event.position,
- quick: event.quick,
- shift: event.shift,
- originalEvent: event.originalEvent,
- preventDefaultAction: event.preventDefaultAction
- };
- /**
- * Raised when a mouse press/release or touch/remove occurs on the {@link OpenSeadragon.Viewer#canvas} element.
- *
- * @event canvas-click
- * @memberof OpenSeadragon.Viewer
- * @type {object}
- * @property {OpenSeadragon.Viewer} eventSource - A reference to the Viewer which raised this event.
- * @property {OpenSeadragon.MouseTracker} tracker - A reference to the MouseTracker which originated this event.
- * @property {OpenSeadragon.Point} position - The position of the event relative to the tracked element.
- * @property {Boolean} quick - True only if the clickDistThreshold and clickTimeThreshold are both passed. Useful for differentiating between clicks and drags.
- * @property {Boolean} shift - True if the shift key was pressed during this event.
- * @property {Object} originalEvent - The original DOM event.
- * @property {Boolean} preventDefaultAction - Set to true to prevent default click to zoom behaviour. Default: false.
- * @property {?Object} userData - Arbitrary subscriber-defined object.
- */
- this.raiseEvent( 'canvas-click', canvasClickEventArgs);
- if ( !canvasClickEventArgs.preventDefaultAction && this.viewport && event.quick ) {
- gestureSettings = this.gestureSettingsByDeviceType( event.pointerType );
- if ( gestureSettings.clickToZoom ) {
- this.viewport.zoomBy(
- event.shift ? 1.0 / this.zoomPerClick : this.zoomPerClick,
- gestureSettings.zoomToRefPoint ? this.viewport.pointFromPixel( event.position, true ) : null
- );
- this.viewport.applyConstraints();
- }
- }
- }
- function onCanvasDblClick( event ) {
- var gestureSettings;
- var canvasDblClickEventArgs = {
- tracker: event.eventSource,
- position: event.position,
- shift: event.shift,
- originalEvent: event.originalEvent,
- preventDefaultAction: event.preventDefaultAction
- };
- /**
- * Raised when a double mouse press/release or touch/remove occurs on the {@link OpenSeadragon.Viewer#canvas} element.
- *
- * @event canvas-double-click
- * @memberof OpenSeadragon.Viewer
- * @type {object}
- * @property {OpenSeadragon.Viewer} eventSource - A reference to the Viewer which raised this event.
- * @property {OpenSeadragon.MouseTracker} tracker - A reference to the MouseTracker which originated this event.
- * @property {OpenSeadragon.Point} position - The position of the event relative to the tracked element.
- * @property {Boolean} shift - True if the shift key was pressed during this event.
- * @property {Object} originalEvent - The original DOM event.
- * @property {Boolean} preventDefaultAction - Set to true to prevent default double tap to zoom behaviour. Default: false.
- * @property {?Object} userData - Arbitrary subscriber-defined object.
- */
- this.raiseEvent( 'canvas-double-click', canvasDblClickEventArgs);
- if ( !canvasDblClickEventArgs.preventDefaultAction && this.viewport ) {
- gestureSettings = this.gestureSettingsByDeviceType( event.pointerType );
- if ( gestureSettings.dblClickToZoom ) {
- this.viewport.zoomBy(
- event.shift ? 1.0 / this.zoomPerClick : this.zoomPerClick,
- gestureSettings.zoomToRefPoint ? this.viewport.pointFromPixel( event.position, true ) : null
- );
- this.viewport.applyConstraints();
- }
- }
- }
- function onCanvasDrag( event ) {
- var gestureSettings;
- var canvasDragEventArgs = {
- tracker: event.eventSource,
- position: event.position,
- delta: event.delta,
- speed: event.speed,
- direction: event.direction,
- shift: event.shift,
- originalEvent: event.originalEvent,
- preventDefaultAction: event.preventDefaultAction
- };
- /**
- * Raised when a mouse or touch drag operation occurs on the {@link OpenSeadragon.Viewer#canvas} element.
- *
- * @event canvas-drag
- * @memberof OpenSeadragon.Viewer
- * @type {object}
- * @property {OpenSeadragon.Viewer} eventSource - A reference to the Viewer which raised this event.
- * @property {OpenSeadragon.MouseTracker} tracker - A reference to the MouseTracker which originated this event.
- * @property {OpenSeadragon.Point} position - The position of the event relative to the tracked element.
- * @property {OpenSeadragon.Point} delta - The x,y components of the difference between start drag and end drag.
- * @property {Number} speed - Current computed speed, in pixels per second.
- * @property {Number} direction - Current computed direction, expressed as an angle counterclockwise relative to the positive X axis (-pi to pi, in radians). Only valid if speed > 0.
- * @property {Boolean} shift - True if the shift key was pressed during this event.
- * @property {Object} originalEvent - The original DOM event.
- * @property {Boolean} preventDefaultAction - Set to true to prevent default drag behaviour. Default: false.
- * @property {?Object} userData - Arbitrary subscriber-defined object.
- */
- this.raiseEvent( 'canvas-drag', canvasDragEventArgs);
- if ( !canvasDragEventArgs.preventDefaultAction && this.viewport ) {
- gestureSettings = this.gestureSettingsByDeviceType( event.pointerType );
- if( !this.panHorizontal ){
- event.delta.x = 0;
- }
- if( !this.panVertical ){
- event.delta.y = 0;
- }
- if(this.viewport.flipped){
- event.delta.x = -event.delta.x;
- }
- if( this.constrainDuringPan ){
- var delta = this.viewport.deltaPointsFromPixels( event.delta.negate() );
- this.viewport.centerSpringX.target.value += delta.x;
- this.viewport.centerSpringY.target.value += delta.y;
- var bounds = this.viewport.getBounds();
- var constrainedBounds = this.viewport.getConstrainedBounds();
- this.viewport.centerSpringX.target.value -= delta.x;
- this.viewport.centerSpringY.target.value -= delta.y;
- if (bounds.x != constrainedBounds.x) {
- event.delta.x = 0;
- }
- if (bounds.y != constrainedBounds.y) {
- event.delta.y = 0;
- }
- }
- this.viewport.panBy( this.viewport.deltaPointsFromPixels( event.delta.negate() ), gestureSettings.flickEnabled && !this.constrainDuringPan);
- }
- }
- function onCanvasDragEnd( event ) {
- if (!event.preventDefaultAction && this.viewport) {
- var gestureSettings = this.gestureSettingsByDeviceType(event.pointerType);
- if (gestureSettings.flickEnabled &&
- event.speed >= gestureSettings.flickMinSpeed) {
- var amplitudeX = 0;
- if (this.panHorizontal) {
- amplitudeX = gestureSettings.flickMomentum * event.speed *
- Math.cos(event.direction);
- }
- var amplitudeY = 0;
- if (this.panVertical) {
- amplitudeY = gestureSettings.flickMomentum * event.speed *
- Math.sin(event.direction);
- }
- var center = this.viewport.pixelFromPoint(
- this.viewport.getCenter(true));
- var target = this.viewport.pointFromPixel(
- new $.Point(center.x - amplitudeX, center.y - amplitudeY));
- this.viewport.panTo(target, false);
- }
- this.viewport.applyConstraints();
- }
- /**
- * Raised when a mouse or touch drag operation ends on the {@link OpenSeadragon.Viewer#canvas} element.
- *
- * @event canvas-drag-end
- * @memberof OpenSeadragon.Viewer
- * @type {object}
- * @property {OpenSeadragon.Viewer} eventSource - A reference to the Viewer which raised this event.
- * @property {OpenSeadragon.MouseTracker} tracker - A reference to the MouseTracker which originated this event.
- * @property {OpenSeadragon.Point} position - The position of the event relative to the tracked element.
- * @property {Number} speed - Speed at the end of a drag gesture, in pixels per second.
- * @property {Number} direction - Direction at the end of a drag gesture, expressed as an angle counterclockwise relative to the positive X axis (-pi to pi, in radians). Only valid if speed > 0.
- * @property {Boolean} shift - True if the shift key was pressed during this event.
- * @property {Object} originalEvent - The original DOM event.
- * @property {?Object} userData - Arbitrary subscriber-defined object.
- */
- this.raiseEvent('canvas-drag-end', {
- tracker: event.eventSource,
- position: event.position,
- speed: event.speed,
- direction: event.direction,
- shift: event.shift,
- originalEvent: event.originalEvent
- });
- }
- function onCanvasEnter( event ) {
- /**
- * Raised when a pointer enters the {@link OpenSeadragon.Viewer#canvas} element.
- *
- * @event canvas-enter
- * @memberof OpenSeadragon.Viewer
- * @type {object}
- * @property {OpenSeadragon.Viewer} eventSource - A reference to the Viewer which raised this event.
- * @property {OpenSeadragon.MouseTracker} tracker - A reference to the MouseTracker which originated this event.
- * @property {String} pointerType - "mouse", "touch", "pen", etc.
- * @property {OpenSeadragon.Point} position - The position of the event relative to the tracked element.
- * @property {Number} buttons - Current buttons pressed. A combination of bit flags 0: none, 1: primary (or touch contact), 2: secondary, 4: aux (often middle), 8: X1 (often back), 16: X2 (often forward), 32: pen eraser.
- * @property {Number} pointers - Number of pointers (all types) active in the tracked element.
- * @property {Boolean} insideElementPressed - True if the left mouse button is currently being pressed and was initiated inside the tracked element, otherwise false.
- * @property {Boolean} buttonDownAny - Was the button down anywhere in the screen during the event. <span style="color:red;">Deprecated. Use buttons instead.</span>
- * @property {Object} originalEvent - The original DOM event.
- * @property {?Object} userData - Arbitrary subscriber-defined object.
- */
- this.raiseEvent( 'canvas-enter', {
- tracker: event.eventSource,
- pointerType: event.pointerType,
- position: event.position,
- buttons: event.buttons,
- pointers: event.pointers,
- insideElementPressed: event.insideElementPressed,
- buttonDownAny: event.buttonDownAny,
- originalEvent: event.originalEvent
- });
- }
- function onCanvasExit( event ) {
- if (window.location != window.parent.location){
- $.MouseTracker.resetAllMouseTrackers();
- }
- /**
- * Raised when a pointer leaves the {@link OpenSeadragon.Viewer#canvas} element.
- *
- * @event canvas-exit
- * @memberof OpenSeadragon.Viewer
- * @type {object}
- * @property {OpenSeadragon.Viewer} eventSource - A reference to the Viewer which raised this event.
- * @property {OpenSeadragon.MouseTracker} tracker - A reference to the MouseTracker which originated this event.
- * @property {String} pointerType - "mouse", "touch", "pen", etc.
- * @property {OpenSeadragon.Point} position - The position of the event relative to the tracked element.
- * @property {Number} buttons - Current buttons pressed. A combination of bit flags 0: none, 1: primary (or touch contact), 2: secondary, 4: aux (often middle), 8: X1 (often back), 16: X2 (often forward), 32: pen eraser.
- * @property {Number} pointers - Number of pointers (all types) active in the tracked element.
- * @property {Boolean} insideElementPressed - True if the left mouse button is currently being pressed and was initiated inside the tracked element, otherwise false.
- * @property {Boolean} buttonDownAny - Was the button down anywhere in the screen during the event. <span style="color:red;">Deprecated. Use buttons instead.</span>
- * @property {Object} originalEvent - The original DOM event.
- * @property {?Object} userData - Arbitrary subscriber-defined object.
- */
- this.raiseEvent( 'canvas-exit', {
- tracker: event.eventSource,
- pointerType: event.pointerType,
- position: event.position,
- buttons: event.buttons,
- pointers: event.pointers,
- insideElementPressed: event.insideElementPressed,
- buttonDownAny: event.buttonDownAny,
- originalEvent: event.originalEvent
- });
- }
- function onCanvasPress( event ) {
- /**
- * Raised when the primary mouse button is pressed or touch starts on the {@link OpenSeadragon.Viewer#canvas} element.
- *
- * @event canvas-press
- * @memberof OpenSeadragon.Viewer
- * @type {object}
- * @property {OpenSeadragon.Viewer} eventSource - A reference to the Viewer which raised this event.
- * @property {OpenSeadragon.MouseTracker} tracker - A reference to the MouseTracker which originated this event.
- * @property {String} pointerType - "mouse", "touch", "pen", etc.
- * @property {OpenSeadragon.Point} position - The position of the event relative to the tracked element.
- * @property {Boolean} insideElementPressed - True if the left mouse button is currently being pressed and was initiated inside the tracked element, otherwise false.
- * @property {Boolean} insideElementReleased - True if the cursor still inside the tracked element when the button was released.
- * @property {Object} originalEvent - The original DOM event.
- * @property {?Object} userData - Arbitrary subscriber-defined object.
- */
- this.raiseEvent( 'canvas-press', {
- tracker: event.eventSource,
- pointerType: event.pointerType,
- position: event.position,
- insideElementPressed: event.insideElementPressed,
- insideElementReleased: event.insideElementReleased,
- originalEvent: event.originalEvent
- });
- }
- function onCanvasRelease( event ) {
- /**
- * Raised when the primary mouse button is released or touch ends on the {@link OpenSeadragon.Viewer#canvas} element.
- *
- * @event canvas-release
- * @memberof OpenSeadragon.Viewer
- * @type {object}
- * @property {OpenSeadragon.Viewer} eventSource - A reference to the Viewer which raised this event.
- * @property {OpenSeadragon.MouseTracker} tracker - A reference to the MouseTracker which originated this event.
- * @property {String} pointerType - "mouse", "touch", "pen", etc.
- * @property {OpenSeadragon.Point} position - The position of the event relative to the tracked element.
- * @property {Boolean} insideElementPressed - True if the left mouse button is currently being pressed and was initiated inside the tracked element, otherwise false.
- * @property {Boolean} insideElementReleased - True if the cursor still inside the tracked element when the button was released.
- * @property {Object} originalEvent - The original DOM event.
- * @property {?Object} userData - Arbitrary subscriber-defined object.
- */
- this.raiseEvent( 'canvas-release', {
- tracker: event.eventSource,
- pointerType: event.pointerType,
- position: event.position,
- insideElementPressed: event.insideElementPressed,
- insideElementReleased: event.insideElementReleased,
- originalEvent: event.originalEvent
- });
- }
- function onCanvasNonPrimaryPress( event ) {
- /**
- * Raised when any non-primary pointer button is pressed on the {@link OpenSeadragon.Viewer#canvas} element.
- *
- * @event canvas-nonprimary-press
- * @memberof OpenSeadragon.Viewer
- * @type {object}
- * @property {OpenSeadragon.Viewer} eventSource - A reference to the Viewer which raised this event.
- * @property {OpenSeadragon.MouseTracker} tracker - A reference to the MouseTracker which originated this event.
- * @property {OpenSeadragon.Point} position - The position of the event relative to the tracked element.
- * @property {String} pointerType - "mouse", "touch", "pen", etc.
- * @property {Number} button - Button which caused the event.
- * -1: none, 0: primary/left, 1: aux/middle, 2: secondary/right, 3: X1/back, 4: X2/forward, 5: pen eraser.
- * @property {Number} buttons - Current buttons pressed.
- * Combination of bit flags 0: none, 1: primary (or touch contact), 2: secondary, 4: aux (often middle), 8: X1 (often back), 16: X2 (often forward), 32: pen eraser.
- * @property {Object} originalEvent - The original DOM event.
- * @property {?Object} userData - Arbitrary subscriber-defined object.
- */
- this.raiseEvent( 'canvas-nonprimary-press', {
- tracker: event.eventSource,
- position: event.position,
- pointerType: event.pointerType,
- button: event.button,
- buttons: event.buttons,
- originalEvent: event.originalEvent
- });
- }
- function onCanvasNonPrimaryRelease( event ) {
- /**
- * Raised when any non-primary pointer button is released on the {@link OpenSeadragon.Viewer#canvas} element.
- *
- * @event canvas-nonprimary-release
- * @memberof OpenSeadragon.Viewer
- * @type {object}
- * @property {OpenSeadragon.Viewer} eventSource - A reference to the Viewer which raised this event.
- * @property {OpenSeadragon.MouseTracker} tracker - A reference to the MouseTracker which originated this event.
- * @property {OpenSeadragon.Point} position - The position of the event relative to the tracked element.
- * @property {String} pointerType - "mouse", "touch", "pen", etc.
- * @property {Number} button - Button which caused the event.
- * -1: none, 0: primary/left, 1: aux/middle, 2: secondary/right, 3: X1/back, 4: X2/forward, 5: pen eraser.
- * @property {Number} buttons - Current buttons pressed.
- * Combination of bit flags 0: none, 1: primary (or touch contact), 2: secondary, 4: aux (often middle), 8: X1 (often back), 16: X2 (often forward), 32: pen eraser.
- * @property {Object} originalEvent - The original DOM event.
- * @property {?Object} userData - Arbitrary subscriber-defined object.
- */
- this.raiseEvent( 'canvas-nonprimary-release', {
- tracker: event.eventSource,
- position: event.position,
- pointerType: event.pointerType,
- button: event.button,
- buttons: event.buttons,
- originalEvent: event.originalEvent
- });
- }
- function onCanvasPinch( event ) {
- var gestureSettings,
- centerPt,
- lastCenterPt,
- panByPt;
- if ( !event.preventDefaultAction && this.viewport ) {
- gestureSettings = this.gestureSettingsByDeviceType( event.pointerType );
- if ( gestureSettings.pinchToZoom ) {
- centerPt = this.viewport.pointFromPixel( event.center, true );
- lastCenterPt = this.viewport.pointFromPixel( event.lastCenter, true );
- panByPt = lastCenterPt.minus( centerPt );
- if( !this.panHorizontal ) {
- panByPt.x = 0;
- }
- if( !this.panVertical ) {
- panByPt.y = 0;
- }
- this.viewport.zoomBy( event.distance / event.lastDistance, centerPt, true );
- if ( gestureSettings.zoomToRefPoint ) {
- this.viewport.panBy(panByPt, true);
- }
- this.viewport.applyConstraints();
- }
- if ( gestureSettings.pinchRotate ) {
- // Pinch rotate
- var angle1 = Math.atan2(event.gesturePoints[0].currentPos.y - event.gesturePoints[1].currentPos.y,
- event.gesturePoints[0].currentPos.x - event.gesturePoints[1].currentPos.x);
- var angle2 = Math.atan2(event.gesturePoints[0].lastPos.y - event.gesturePoints[1].lastPos.y,
- event.gesturePoints[0].lastPos.x - event.gesturePoints[1].lastPos.x);
- this.viewport.setRotation(this.viewport.getRotation() + ((angle1 - angle2) * (180 / Math.PI)));
- }
- }
- /**
- * Raised when a pinch event occurs on the {@link OpenSeadragon.Viewer#canvas} element.
- *
- * @event canvas-pinch
- * @memberof OpenSeadragon.Viewer
- * @type {object}
- * @property {OpenSeadragon.Viewer} eventSource - A reference to the Viewer which raised this event.
- * @property {OpenSeadragon.MouseTracker} tracker - A reference to the MouseTracker which originated this event.
- * @property {Array.<OpenSeadragon.MouseTracker.GesturePoint>} gesturePoints - Gesture points associated with the gesture. Velocity data can be found here.
- * @property {OpenSeadragon.Point} lastCenter - The previous center point of the two pinch contact points relative to the tracked element.
- * @property {OpenSeadragon.Point} center - The center point of the two pinch contact points relative to the tracked element.
- * @property {Number} lastDistance - The previous distance between the two pinch contact points in CSS pixels.
- * @property {Number} distance - The distance between the two pinch contact points in CSS pixels.
- * @property {Boolean} shift - True if the shift key was pressed during this event.
- * @property {Object} originalEvent - The original DOM event.
- * @property {?Object} userData - Arbitrary subscriber-defined object.
- */
- this.raiseEvent('canvas-pinch', {
- tracker: event.eventSource,
- gesturePoints: event.gesturePoints,
- lastCenter: event.lastCenter,
- center: event.center,
- lastDistance: event.lastDistance,
- distance: event.distance,
- shift: event.shift,
- originalEvent: event.originalEvent
- });
- //cancels event
- return false;
- }
- function onCanvasScroll( event ) {
- var gestureSettings,
- factor,
- thisScrollTime,
- deltaScrollTime;
- /* Certain scroll devices fire the scroll event way too fast so we are injecting a simple adjustment to keep things
- * partially normalized. If we have already fired an event within the last 'minScrollDelta' milliseconds we skip
- * this one and wait for the next event. */
- thisScrollTime = $.now();
- deltaScrollTime = thisScrollTime - this._lastScrollTime;
- if (deltaScrollTime > this.minScrollDeltaTime) {
- this._lastScrollTime = thisScrollTime;
- if(this.viewport.flipped){
- event.position.x = this.viewport.getContainerSize().x - event.position.x;
- }
- if ( !event.preventDefaultAction && this.viewport ) {
- gestureSettings = this.gestureSettingsByDeviceType( event.pointerType );
- if ( gestureSettings.scrollToZoom ) {
- factor = Math.pow( this.zoomPerScroll, event.scroll );
- this.viewport.zoomBy(
- factor,
- gestureSettings.zoomToRefPoint ? this.viewport.pointFromPixel( event.position, true ) : null
- );
- this.viewport.applyConstraints();
- }
- }
- /**
- * Raised when a scroll event occurs on the {@link OpenSeadragon.Viewer#canvas} element (mouse wheel).
- *
- * @event canvas-scroll
- * @memberof OpenSeadragon.Viewer
- * @type {object}
- * @property {OpenSeadragon.Viewer} eventSource - A reference to the Viewer which raised this event.
- * @property {OpenSeadragon.MouseTracker} tracker - A reference to the MouseTracker which originated this event.
- * @property {OpenSeadragon.Point} position - The position of the event relative to the tracked element.
- * @property {Number} scroll - The scroll delta for the event.
- * @property {Boolean} shift - True if the shift key was pressed during this event.
- * @property {Object} originalEvent - The original DOM event.
- * @property {?Object} userData - Arbitrary subscriber-defined object.
- */
- this.raiseEvent( 'canvas-scroll', {
- tracker: event.eventSource,
- position: event.position,
- scroll: event.scroll,
- shift: event.shift,
- originalEvent: event.originalEvent
- });
- if (gestureSettings && gestureSettings.scrollToZoom) {
- //cancels event
- return false;
- }
- }
- else {
- gestureSettings = this.gestureSettingsByDeviceType( event.pointerType );
- if (gestureSettings && gestureSettings.scrollToZoom) {
- return false; // We are swallowing this event
- }
- }
- }
- function onContainerEnter( event ) {
- THIS[ this.hash ].mouseInside = true;
- abortControlsAutoHide( this );
- /**
- * Raised when the cursor enters the {@link OpenSeadragon.Viewer#container} element.
- *
- * @event container-enter
- * @memberof OpenSeadragon.Viewer
- * @type {object}
- * @property {OpenSeadragon.Viewer} eventSource - A reference to the Viewer which raised this event.
- * @property {OpenSeadragon.MouseTracker} tracker - A reference to the MouseTracker which originated this event.
- * @property {OpenSeadragon.Point} position - The position of the event relative to the tracked element.
- * @property {Number} buttons - Current buttons pressed. A combination of bit flags 0: none, 1: primary (or touch contact), 2: secondary, 4: aux (often middle), 8: X1 (often back), 16: X2 (often forward), 32: pen eraser.
- * @property {Number} pointers - Number of pointers (all types) active in the tracked element.
- * @property {Boolean} insideElementPressed - True if the left mouse button is currently being pressed and was initiated inside the tracked element, otherwise false.
- * @property {Boolean} buttonDownAny - Was the button down anywhere in the screen during the event. <span style="color:red;">Deprecated. Use buttons instead.</span>
- * @property {Object} originalEvent - The original DOM event.
- * @property {?Object} userData - Arbitrary subscriber-defined object.
- */
- this.raiseEvent( 'container-enter', {
- tracker: event.eventSource,
- position: event.position,
- buttons: event.buttons,
- pointers: event.pointers,
- insideElementPressed: event.insideElementPressed,
- buttonDownAny: event.buttonDownAny,
- originalEvent: event.originalEvent
- });
- }
- function onContainerExit( event ) {
- if ( event.pointers < 1 ) {
- THIS[ this.hash ].mouseInside = false;
- if ( !THIS[ this.hash ].animating ) {
- beginControlsAutoHide( this );
- }
- }
- /**
- * Raised when the cursor leaves the {@link OpenSeadragon.Viewer#container} element.
- *
- * @event container-exit
- * @memberof OpenSeadragon.Viewer
- * @type {object}
- * @property {OpenSeadragon.Viewer} eventSource - A reference to the Viewer which raised this event.
- * @property {OpenSeadragon.MouseTracker} tracker - A reference to the MouseTracker which originated this event.
- * @property {OpenSeadragon.Point} position - The position of the event relative to the tracked element.
- * @property {Number} buttons - Current buttons pressed. A combination of bit flags 0: none, 1: primary (or touch contact), 2: secondary, 4: aux (often middle), 8: X1 (often back), 16: X2 (often forward), 32: pen eraser.
- * @property {Number} pointers - Number of pointers (all types) active in the tracked element.
- * @property {Boolean} insideElementPressed - True if the left mouse button is currently being pressed and was initiated inside the tracked element, otherwise false.
- * @property {Boolean} buttonDownAny - Was the button down anywhere in the screen during the event. <span style="color:red;">Deprecated. Use buttons instead.</span>
- * @property {Object} originalEvent - The original DOM event.
- * @property {?Object} userData - Arbitrary subscriber-defined object.
- */
- this.raiseEvent( 'container-exit', {
- tracker: event.eventSource,
- position: event.position,
- buttons: event.buttons,
- pointers: event.pointers,
- insideElementPressed: event.insideElementPressed,
- buttonDownAny: event.buttonDownAny,
- originalEvent: event.originalEvent
- });
- }
- ///////////////////////////////////////////////////////////////////////////////
- // Page update routines ( aka Views - for future reference )
- ///////////////////////////////////////////////////////////////////////////////
- function updateMulti( viewer ) {
- updateOnce( viewer );
- // Request the next frame, unless we've been closed
- if ( viewer.isOpen() ) {
- viewer._updateRequestId = scheduleUpdate( viewer, updateMulti );
- } else {
- viewer._updateRequestId = false;
- }
- }
- function updateOnce( viewer ) {
- //viewer.profiler.beginUpdate();
- if (viewer._opening) {
- return;
- }
- if (viewer.autoResize) {
- var containerSize = _getSafeElemSize(viewer.container);
- var prevContainerSize = THIS[viewer.hash].prevContainerSize;
- if (!containerSize.equals(prevContainerSize)) {
- var viewport = viewer.viewport;
- if (viewer.preserveImageSizeOnResize) {
- var resizeRatio = prevContainerSize.x / containerSize.x;
- var zoom = viewport.getZoom() * resizeRatio;
- var center = viewport.getCenter();
- viewport.resize(containerSize, false);
- viewport.zoomTo(zoom, null, true);
- viewport.panTo(center, true);
- } else {
- // maintain image position
- var oldBounds = viewport.getBounds();
- viewport.resize(containerSize, true);
- viewport.fitBoundsWithConstraints(oldBounds, true);
- }
- THIS[viewer.hash].prevContainerSize = containerSize;
- THIS[viewer.hash].forceRedraw = true;
- }
- }
- var viewportChange = viewer.viewport.update();
- var animated = viewer.world.update() || viewportChange;
- if (viewportChange) {
- /**
- * Raised when any spring animation update occurs (zoom, pan, etc.),
- * before the viewer has drawn the new location.
- *
- * @event viewport-change
- * @memberof OpenSeadragon.Viewer
- * @type {object}
- * @property {OpenSeadragon.Viewer} eventSource - A reference to the Viewer which raised this event.
- * @property {?Object} userData - Arbitrary subscriber-defined object.
- */
- viewer.raiseEvent('viewport-change');
- }
- if( viewer.referenceStrip ){
- animated = viewer.referenceStrip.update( viewer.viewport ) || animated;
- }
- if ( !THIS[ viewer.hash ].animating && animated ) {
- /**
- * Raised when any spring animation starts (zoom, pan, etc.).
- *
- * @event animation-start
- * @memberof OpenSeadragon.Viewer
- * @type {object}
- * @property {OpenSeadragon.Viewer} eventSource - A reference to the Viewer which raised this event.
- * @property {?Object} userData - Arbitrary subscriber-defined object.
- */
- viewer.raiseEvent( "animation-start" );
- abortControlsAutoHide( viewer );
- }
- if ( animated || THIS[ viewer.hash ].forceRedraw || viewer.world.needsDraw() ) {
- drawWorld( viewer );
- viewer._drawOverlays();
- if( viewer.navigator ){
- viewer.navigator.update( viewer.viewport );
- }
- THIS[ viewer.hash ].forceRedraw = false;
- if (animated) {
- /**
- * Raised when any spring animation update occurs (zoom, pan, etc.),
- * after the viewer has drawn the new location.
- *
- * @event animation
- * @memberof OpenSeadragon.Viewer
- * @type {object}
- * @property {OpenSeadragon.Viewer} eventSource - A reference to the Viewer which raised this event.
- * @property {?Object} userData - Arbitrary subscriber-defined object.
- */
- viewer.raiseEvent( "animation" );
- }
- }
- if ( THIS[ viewer.hash ].animating && !animated ) {
- /**
- * Raised when any spring animation ends (zoom, pan, etc.).
- *
- * @event animation-finish
- * @memberof OpenSeadragon.Viewer
- * @type {object}
- * @property {OpenSeadragon.Viewer} eventSource - A reference to the Viewer which raised this event.
- * @property {?Object} userData - Arbitrary subscriber-defined object.
- */
- viewer.raiseEvent( "animation-finish" );
- if ( !THIS[ viewer.hash ].mouseInside ) {
- beginControlsAutoHide( viewer );
- }
- }
- THIS[ viewer.hash ].animating = animated;
- //viewer.profiler.endUpdate();
- }
- function drawWorld( viewer ) {
- viewer.imageLoader.clear();
- viewer.drawer.clear();
- viewer.world.draw();
- /**
- * <em>- Needs documentation -</em>
- *
- * @event update-viewport
- * @memberof OpenSeadragon.Viewer
- * @type {object}
- * @property {OpenSeadragon.Viewer} eventSource - A reference to the Viewer which raised the event.
- * @property {?Object} userData - Arbitrary subscriber-defined object.
- */
- viewer.raiseEvent( 'update-viewport', {} );
- }
- ///////////////////////////////////////////////////////////////////////////////
- // Navigation Controls
- ///////////////////////////////////////////////////////////////////////////////
- function resolveUrl( prefix, url ) {
- return prefix ? prefix + url : url;
- }
- function beginZoomingIn() {
- THIS[ this.hash ].lastZoomTime = $.now();
- THIS[ this.hash ].zoomFactor = this.zoomPerSecond;
- THIS[ this.hash ].zooming = true;
- scheduleZoom( this );
- }
- function beginZoomingOut() {
- THIS[ this.hash ].lastZoomTime = $.now();
- THIS[ this.hash ].zoomFactor = 1.0 / this.zoomPerSecond;
- THIS[ this.hash ].zooming = true;
- scheduleZoom( this );
- }
- function endZooming() {
- THIS[ this.hash ].zooming = false;
- }
- function scheduleZoom( viewer ) {
- $.requestAnimationFrame( $.delegate( viewer, doZoom ) );
- }
- function doZoom() {
- var currentTime,
- deltaTime,
- adjustedFactor;
- if ( THIS[ this.hash ].zooming && this.viewport) {
- currentTime = $.now();
- deltaTime = currentTime - THIS[ this.hash ].lastZoomTime;
- adjustedFactor = Math.pow( THIS[ this.hash ].zoomFactor, deltaTime / 1000 );
- this.viewport.zoomBy( adjustedFactor );
- this.viewport.applyConstraints();
- THIS[ this.hash ].lastZoomTime = currentTime;
- scheduleZoom( this );
- }
- }
- function doSingleZoomIn() {
- if ( this.viewport ) {
- THIS[ this.hash ].zooming = false;
- this.viewport.zoomBy(
- this.zoomPerClick / 1.0
- );
- this.viewport.applyConstraints();
- }
- }
- function doSingleZoomOut() {
- if ( this.viewport ) {
- THIS[ this.hash ].zooming = false;
- this.viewport.zoomBy(
- 1.0 / this.zoomPerClick
- );
- this.viewport.applyConstraints();
- }
- }
- function lightUp() {
- this.buttons.emulateEnter();
- this.buttons.emulateExit();
- }
- function onHome() {
- if ( this.viewport ) {
- this.viewport.goHome();
- }
- }
- function onFullScreen() {
- if ( this.isFullPage() && !$.isFullScreen() ) {
- // Is fullPage but not fullScreen
- this.setFullPage( false );
- } else {
- this.setFullScreen( !this.isFullPage() );
- }
- // correct for no mouseout event on change
- if ( this.buttons ) {
- this.buttons.emulateExit();
- }
- this.fullPageButton.element.focus();
- if ( this.viewport ) {
- this.viewport.applyConstraints();
- }
- }
- function onRotateLeft() {
- if ( this.viewport ) {
- var currRotation = this.viewport.getRotation();
- if ( this.viewport.flipped ){
- currRotation = $.positiveModulo(currRotation + this.rotationIncrement, 360);
- } else {
- currRotation = $.positiveModulo(currRotation - this.rotationIncrement, 360);
- }
- this.viewport.setRotation(currRotation);
- }
- }
- function onRotateRight() {
- if ( this.viewport ) {
- var currRotation = this.viewport.getRotation();
- if ( this.viewport.flipped ){
- currRotation = $.positiveModulo(currRotation - this.rotationIncrement, 360);
- } else {
- currRotation = $.positiveModulo(currRotation + this.rotationIncrement, 360);
- }
- this.viewport.setRotation(currRotation);
- }
- }
- /**
- * Note: When pressed flip control button
- */
- function onFlip() {
- this.viewport.toggleFlip();
- }
- function onPrevious(){
- var previous = this._sequenceIndex - 1;
- if(this.navPrevNextWrap && previous < 0){
- previous += this.tileSources.length;
- }
- this.goToPage( previous );
- }
- function onNext(){
- var next = this._sequenceIndex + 1;
- if(this.navPrevNextWrap && next >= this.tileSources.length){
- next = 0;
- }
- this.goToPage( next );
- }
- }( OpenSeadragon ));
- /*
- * OpenSeadragon - Navigator
- *
- * Copyright (C) 2009 CodePlex Foundation
- * Copyright (C) 2010-2013 OpenSeadragon contributors
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are
- * met:
- *
- * - Redistributions of source code must retain the above copyright notice,
- * this list of conditions and the following disclaimer.
- *
- * - Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * - Neither the name of CodePlex Foundation nor the names of its
- * contributors may be used to endorse or promote products derived from
- * this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
- * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
- * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
- * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
- (function( $ ){
- /**
- * @class Navigator
- * @classdesc The Navigator provides a small view of the current image as fixed
- * while representing the viewport as a moving box serving as a frame
- * of reference in the larger viewport as to which portion of the image
- * is currently being examined. The navigator's viewport can be interacted
- * with using the keyboard or the mouse.
- *
- * @memberof OpenSeadragon
- * @extends OpenSeadragon.Viewer
- * @extends OpenSeadragon.EventSource
- * @param {Object} options
- */
- $.Navigator = function( options ){
- var viewer = options.viewer,
- _this = this,
- viewerSize,
- navigatorSize;
- //We may need to create a new element and id if they did not
- //provide the id for the existing element
- if( !options.id ){
- options.id = 'navigator-' + $.now();
- this.element = $.makeNeutralElement( "div" );
- options.controlOptions = {
- anchor: $.ControlAnchor.TOP_RIGHT,
- attachToViewer: true,
- autoFade: options.autoFade
- };
- if( options.position ){
- if( 'BOTTOM_RIGHT' == options.position ){
- options.controlOptions.anchor = $.ControlAnchor.BOTTOM_RIGHT;
- } else if( 'BOTTOM_LEFT' == options.position ){
- options.controlOptions.anchor = $.ControlAnchor.BOTTOM_LEFT;
- } else if( 'TOP_RIGHT' == options.position ){
- options.controlOptions.anchor = $.ControlAnchor.TOP_RIGHT;
- } else if( 'TOP_LEFT' == options.position ){
- options.controlOptions.anchor = $.ControlAnchor.TOP_LEFT;
- } else if( 'ABSOLUTE' == options.position ){
- options.controlOptions.anchor = $.ControlAnchor.ABSOLUTE;
- options.controlOptions.top = options.top;
- options.controlOptions.left = options.left;
- options.controlOptions.height = options.height;
- options.controlOptions.width = options.width;
- }
- }
- } else {
- this.element = document.getElementById( options.id );
- options.controlOptions = {
- anchor: $.ControlAnchor.NONE,
- attachToViewer: false,
- autoFade: false
- };
- }
- this.element.id = options.id;
- this.element.className += ' navigator';
- options = $.extend( true, {
- sizeRatio: $.DEFAULT_SETTINGS.navigatorSizeRatio
- }, options, {
- element: this.element,
- tabIndex: -1, // No keyboard navigation, omit from tab order
- //These need to be overridden to prevent recursion since
- //the navigator is a viewer and a viewer has a navigator
- showNavigator: false,
- mouseNavEnabled: false,
- showNavigationControl: false,
- showSequenceControl: false,
- immediateRender: true,
- blendTime: 0,
- animationTime: 0,
- autoResize: options.autoResize,
- // prevent resizing the navigator from adding unwanted space around the image
- minZoomImageRatio: 1.0,
- background: options.background,
- opacity: options.opacity,
- borderColor: options.borderColor,
- displayRegionColor: options.displayRegionColor
- });
- options.minPixelRatio = this.minPixelRatio = viewer.minPixelRatio;
- $.setElementTouchActionNone( this.element );
- this.borderWidth = 2;
- //At some browser magnification levels the display regions lines up correctly, but at some there appears to
- //be a one pixel gap.
- this.fudge = new $.Point(1, 1);
- this.totalBorderWidths = new $.Point(this.borderWidth * 2, this.borderWidth * 2).minus(this.fudge);
- if ( options.controlOptions.anchor != $.ControlAnchor.NONE ) {
- (function( style, borderWidth ){
- style.margin = '0px';
- style.border = borderWidth + 'px solid ' + options.borderColor;
- style.padding = '0px';
- style.background = options.background;
- style.opacity = options.opacity;
- style.overflow = 'hidden';
- }( this.element.style, this.borderWidth));
- }
- this.displayRegion = $.makeNeutralElement( "div" );
- this.displayRegion.id = this.element.id + '-displayregion';
- this.displayRegion.className = 'displayregion';
- (function( style, borderWidth ){
- style.position = 'relative';
- style.top = '0px';
- style.left = '0px';
- style.fontSize = '0px';
- style.overflow = 'hidden';
- style.border = borderWidth + 'px solid ' + options.displayRegionColor;
- style.margin = '0px';
- style.padding = '0px';
- //TODO: IE doesn't like this property being set
- //try{ style.outline = '2px auto #909'; }catch(e){/*ignore*/}
- style.background = 'transparent';
- // We use square bracket notation on the statement below, because float is a keyword.
- // This is important for the Google Closure compiler, if nothing else.
- /*jshint sub:true */
- style['float'] = 'left'; //Webkit
- style.cssFloat = 'left'; //Firefox
- style.styleFloat = 'left'; //IE
- style.zIndex = 999999999;
- style.cursor = 'default';
- }( this.displayRegion.style, this.borderWidth ));
- this.displayRegionContainer = $.makeNeutralElement("div");
- this.displayRegionContainer.id = this.element.id + '-displayregioncontainer';
- this.displayRegionContainer.className = "displayregioncontainer";
- this.displayRegionContainer.style.width = "100%";
- this.displayRegionContainer.style.height = "100%";
- viewer.addControl(
- this.element,
- options.controlOptions
- );
- this._resizeWithViewer = options.controlOptions.anchor != $.ControlAnchor.ABSOLUTE &&
- options.controlOptions.anchor != $.ControlAnchor.NONE;
- if ( this._resizeWithViewer ) {
- if ( options.width && options.height ) {
- this.element.style.height = typeof (options.height) == "number" ? (options.height + 'px') : options.height;
- this.element.style.width = typeof (options.width) == "number" ? (options.width + 'px') : options.width;
- } else {
- viewerSize = $.getElementSize( viewer.element );
- this.element.style.height = Math.round( viewerSize.y * options.sizeRatio ) + 'px';
- this.element.style.width = Math.round( viewerSize.x * options.sizeRatio ) + 'px';
- this.oldViewerSize = viewerSize;
- }
- navigatorSize = $.getElementSize( this.element );
- this.elementArea = navigatorSize.x * navigatorSize.y;
- }
- this.oldContainerSize = new $.Point( 0, 0 );
- $.Viewer.apply( this, [ options ] );
- this.displayRegionContainer.appendChild(this.displayRegion);
- this.element.getElementsByTagName('div')[0].appendChild(this.displayRegionContainer);
- function rotate(degrees) {
- _setTransformRotate(_this.displayRegionContainer, degrees);
- _setTransformRotate(_this.displayRegion, -degrees);
- _this.viewport.setRotation(degrees);
- }
- if (options.navigatorRotate) {
- var degrees = options.viewer.viewport ?
- options.viewer.viewport.getRotation() :
- options.viewer.degrees || 0;
- rotate(degrees);
- options.viewer.addHandler("rotate", function (args) {
- rotate(args.degrees);
- });
- }
- // Remove the base class' (Viewer's) innerTracker and replace it with our own
- this.innerTracker.destroy();
- this.innerTracker = new $.MouseTracker({
- element: this.element,
- dragHandler: $.delegate( this, onCanvasDrag ),
- clickHandler: $.delegate( this, onCanvasClick ),
- releaseHandler: $.delegate( this, onCanvasRelease ),
- scrollHandler: $.delegate( this, onCanvasScroll )
- });
- this.addHandler("reset-size", function() {
- if (_this.viewport) {
- _this.viewport.goHome(true);
- }
- });
- viewer.world.addHandler("item-index-change", function(event) {
- window.setTimeout(function(){
- var item = _this.world.getItemAt(event.previousIndex);
- _this.world.setItemIndex(item, event.newIndex);
- }, 1);
- });
- viewer.world.addHandler("remove-item", function(event) {
- var theirItem = event.item;
- var myItem = _this._getMatchingItem(theirItem);
- if (myItem) {
- _this.world.removeItem(myItem);
- }
- });
- this.update(viewer.viewport);
- };
- $.extend( $.Navigator.prototype, $.EventSource.prototype, $.Viewer.prototype, /** @lends OpenSeadragon.Navigator.prototype */{
- /**
- * Used to notify the navigator when its size has changed.
- * Especially useful when {@link OpenSeadragon.Options}.navigatorAutoResize is set to false and the navigator is resizable.
- * @function
- */
- updateSize: function () {
- if ( this.viewport ) {
- var containerSize = new $.Point(
- (this.container.clientWidth === 0 ? 1 : this.container.clientWidth),
- (this.container.clientHeight === 0 ? 1 : this.container.clientHeight)
- );
- if ( !containerSize.equals( this.oldContainerSize ) ) {
- this.viewport.resize( containerSize, true );
- this.viewport.goHome(true);
- this.oldContainerSize = containerSize;
- this.drawer.clear();
- this.world.draw();
- }
- }
- },
- /**
- /* Flip navigator element
- * @param {Boolean} state - Flip state to set.
- */
- setFlip: function(state) {
- this.viewport.setFlip(state);
- this.setDisplayTransform(this.viewer.viewport.getFlip() ? "scale(-1,1)" : "scale(1,1)");
- return this;
- },
- setDisplayTransform: function(rule) {
- setElementTransform(this.displayRegion, rule);
- setElementTransform(this.canvas, rule);
- setElementTransform(this.element, rule);
- },
- /**
- * Used to update the navigator minimap's viewport rectangle when a change in the viewer's viewport occurs.
- * @function
- * @param {OpenSeadragon.Viewport} The viewport this navigator is tracking.
- */
- update: function( viewport ) {
- var viewerSize,
- newWidth,
- newHeight,
- bounds,
- topleft,
- bottomright;
- viewerSize = $.getElementSize( this.viewer.element );
- if ( this._resizeWithViewer && viewerSize.x && viewerSize.y && !viewerSize.equals( this.oldViewerSize ) ) {
- this.oldViewerSize = viewerSize;
- if ( this.maintainSizeRatio || !this.elementArea) {
- newWidth = viewerSize.x * this.sizeRatio;
- newHeight = viewerSize.y * this.sizeRatio;
- } else {
- newWidth = Math.sqrt(this.elementArea * (viewerSize.x / viewerSize.y));
- newHeight = this.elementArea / newWidth;
- }
- this.element.style.width = Math.round( newWidth ) + 'px';
- this.element.style.height = Math.round( newHeight ) + 'px';
- if (!this.elementArea) {
- this.elementArea = newWidth * newHeight;
- }
- this.updateSize();
- }
- if (viewport && this.viewport) {
- bounds = viewport.getBoundsNoRotate(true);
- topleft = this.viewport.pixelFromPointNoRotate(bounds.getTopLeft(), false);
- bottomright = this.viewport.pixelFromPointNoRotate(bounds.getBottomRight(), false)
- .minus( this.totalBorderWidths );
- //update style for navigator-box
- var style = this.displayRegion.style;
- style.display = this.world.getItemCount() ? 'block' : 'none';
- style.top = Math.round( topleft.y ) + 'px';
- style.left = Math.round( topleft.x ) + 'px';
- var width = Math.abs( topleft.x - bottomright.x );
- var height = Math.abs( topleft.y - bottomright.y );
- // make sure width and height are non-negative so IE doesn't throw
- style.width = Math.round( Math.max( width, 0 ) ) + 'px';
- style.height = Math.round( Math.max( height, 0 ) ) + 'px';
- }
- },
- // overrides Viewer.addTiledImage
- addTiledImage: function(options) {
- var _this = this;
- var original = options.originalTiledImage;
- delete options.original;
- var optionsClone = $.extend({}, options, {
- success: function(event) {
- var myItem = event.item;
- myItem._originalForNavigator = original;
- _this._matchBounds(myItem, original, true);
- function matchBounds() {
- _this._matchBounds(myItem, original);
- }
- function matchOpacity() {
- _this._matchOpacity(myItem, original);
- }
- function matchCompositeOperation() {
- _this._matchCompositeOperation(myItem, original);
- }
- original.addHandler('bounds-change', matchBounds);
- original.addHandler('clip-change', matchBounds);
- original.addHandler('opacity-change', matchOpacity);
- original.addHandler('composite-operation-change', matchCompositeOperation);
- }
- });
- return $.Viewer.prototype.addTiledImage.apply(this, [optionsClone]);
- },
- // private
- _getMatchingItem: function(theirItem) {
- var count = this.world.getItemCount();
- var item;
- for (var i = 0; i < count; i++) {
- item = this.world.getItemAt(i);
- if (item._originalForNavigator === theirItem) {
- return item;
- }
- }
- return null;
- },
- // private
- _matchBounds: function(myItem, theirItem, immediately) {
- var bounds = theirItem.getBoundsNoRotate();
- myItem.setPosition(bounds.getTopLeft(), immediately);
- myItem.setWidth(bounds.width, immediately);
- myItem.setRotation(theirItem.getRotation(), immediately);
- myItem.setClip(theirItem.getClip());
- },
- // private
- _matchOpacity: function(myItem, theirItem) {
- myItem.setOpacity(theirItem.opacity);
- },
- // private
- _matchCompositeOperation: function(myItem, theirItem) {
- myItem.setCompositeOperation(theirItem.compositeOperation);
- }
- });
- /**
- * @private
- * @inner
- * @function
- */
- function onCanvasClick( event ) {
- var canvasClickEventArgs = {
- tracker: event.eventSource,
- position: event.position,
- quick: event.quick,
- shift: event.shift,
- originalEvent: event.originalEvent,
- preventDefaultAction: event.preventDefaultAction
- };
- /**
- * Raised when a click event occurs on the {@link OpenSeadragon.Viewer#navigator} element.
- *
- * @event navigator-click
- * @memberof OpenSeadragon.Viewer
- * @type {object}
- * @property {OpenSeadragon.Viewer} eventSource - A reference to the Viewer which raised this event.
- * @property {OpenSeadragon.MouseTracker} tracker - A reference to the MouseTracker which originated this event.
- * @property {OpenSeadragon.Point} position - The position of the event relative to the tracked element.
- * @property {Boolean} quick - True only if the clickDistThreshold and clickTimeThreshold are both passed. Useful for differentiating between clicks and drags.
- * @property {Boolean} shift - True if the shift key was pressed during this event.
- * @property {Object} originalEvent - The original DOM event.
- * @property {?Object} userData - Arbitrary subscriber-defined object.
- * @property {Boolean} preventDefaultAction - Set to true to prevent default click to zoom behaviour. Default: false.
- */
- this.viewer.raiseEvent('navigator-click', canvasClickEventArgs);
- if ( !canvasClickEventArgs.preventDefaultAction && event.quick && this.viewer.viewport && (this.panVertical || this.panHorizontal)) {
- if(this.viewer.viewport.flipped) {
- event.position.x = this.viewport.getContainerSize().x - event.position.x;
- }
- var target = this.viewport.pointFromPixel(event.position);
- if (!this.panVertical) {
- // perform only horizonal pan
- target.y = this.viewer.viewport.getCenter(true).y;
- } else if (!this.panHorizontal) {
- // perform only vertical pan
- target.x = this.viewer.viewport.getCenter(true).x;
- }
- this.viewer.viewport.panTo(target);
- this.viewer.viewport.applyConstraints();
- }
- }
- /**
- * @private
- * @inner
- * @function
- */
- function onCanvasDrag( event ) {
- var canvasDragEventArgs = {
- tracker: event.eventSource,
- position: event.position,
- delta: event.delta,
- speed: event.speed,
- direction: event.direction,
- shift: event.shift,
- originalEvent: event.originalEvent,
- preventDefaultAction: event.preventDefaultAction
- };
- /**
- * Raised when a drag event occurs on the {@link OpenSeadragon.Viewer#navigator} element.
- *
- * @event navigator-drag
- * @memberof OpenSeadragon.Viewer
- * @type {object}
- * @property {OpenSeadragon.Viewer} eventSource - A reference to the Viewer which raised this event.
- * @property {OpenSeadragon.MouseTracker} tracker - A reference to the MouseTracker which originated this event.
- * @property {OpenSeadragon.Point} position - The position of the event relative to the tracked element.
- * @property {OpenSeadragon.Point} delta - The x,y components of the difference between start drag and end drag.
- * @property {Number} speed - Current computed speed, in pixels per second.
- * @property {Number} direction - Current computed direction, expressed as an angle counterclockwise relative to the positive X axis (-pi to pi, in radians). Only valid if speed > 0.
- * @property {Boolean} shift - True if the shift key was pressed during this event.
- * @property {Object} originalEvent - The original DOM event.
- * @property {?Object} userData - Arbitrary subscriber-defined object.
- * @property {Boolean} preventDefaultAction - Set to true to prevent default click to zoom behaviour. Default: false.
- */
- this.viewer.raiseEvent('navigator-drag', canvasDragEventArgs);
- if ( !canvasDragEventArgs.preventDefaultAction && this.viewer.viewport ) {
- if( !this.panHorizontal ){
- event.delta.x = 0;
- }
- if( !this.panVertical ){
- event.delta.y = 0;
- }
- if(this.viewer.viewport.flipped){
- event.delta.x = -event.delta.x;
- }
- this.viewer.viewport.panBy(
- this.viewport.deltaPointsFromPixels(
- event.delta
- )
- );
- if( this.viewer.constrainDuringPan ){
- this.viewer.viewport.applyConstraints();
- }
- }
- }
- /**
- * @private
- * @inner
- * @function
- */
- function onCanvasRelease( event ) {
- if ( event.insideElementPressed && this.viewer.viewport ) {
- this.viewer.viewport.applyConstraints();
- }
- }
- /**
- * @private
- * @inner
- * @function
- */
- function onCanvasScroll( event ) {
- /**
- * Raised when a scroll event occurs on the {@link OpenSeadragon.Viewer#navigator} element (mouse wheel, touch pinch, etc.).
- *
- * @event navigator-scroll
- * @memberof OpenSeadragon.Viewer
- * @type {object}
- * @property {OpenSeadragon.Viewer} eventSource - A reference to the Viewer which raised this event.
- * @property {OpenSeadragon.MouseTracker} tracker - A reference to the MouseTracker which originated this event.
- * @property {OpenSeadragon.Point} position - The position of the event relative to the tracked element.
- * @property {Number} scroll - The scroll delta for the event.
- * @property {Boolean} shift - True if the shift key was pressed during this event.
- * @property {Object} originalEvent - The original DOM event.
- * @property {?Object} userData - Arbitrary subscriber-defined object.
- */
- this.viewer.raiseEvent( 'navigator-scroll', {
- tracker: event.eventSource,
- position: event.position,
- scroll: event.scroll,
- shift: event.shift,
- originalEvent: event.originalEvent
- });
- //don't scroll the page up and down if the user is scrolling
- //in the navigator
- return false;
- }
- /**
- * @function
- * @private
- * @param {Object} element
- * @param {Number} degrees
- */
- function _setTransformRotate( element, degrees ) {
- setElementTransform(element, "rotate(" + degrees + "deg)");
- }
- function setElementTransform( element, rule ) {
- element.style.webkitTransform = rule;
- element.style.mozTransform = rule;
- element.style.msTransform = rule;
- element.style.oTransform = rule;
- element.style.transform = rule;
- }
- }( OpenSeadragon ));
- /*
- * OpenSeadragon - getString/setString
- *
- * Copyright (C) 2009 CodePlex Foundation
- * Copyright (C) 2010-2013 OpenSeadragon contributors
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are
- * met:
- *
- * - Redistributions of source code must retain the above copyright notice,
- * this list of conditions and the following disclaimer.
- *
- * - Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * - Neither the name of CodePlex Foundation nor the names of its
- * contributors may be used to endorse or promote products derived from
- * this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
- * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
- * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
- * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
- (function( $ ){
- //TODO: I guess this is where the i18n needs to be reimplemented. I'll look
- // into existing patterns for i18n in javascript but i think that mimicking
- // pythons gettext might be a reasonable approach.
- var I18N = {
- Errors: {
- Dzc: "Sorry, we don't support Deep Zoom Collections!",
- Dzi: "Hmm, this doesn't appear to be a valid Deep Zoom Image.",
- Xml: "Hmm, this doesn't appear to be a valid Deep Zoom Image.",
- ImageFormat: "Sorry, we don't support {0}-based Deep Zoom Images.",
- Security: "It looks like a security restriction stopped us from " +
- "loading this Deep Zoom Image.",
- Status: "This space unintentionally left blank ({0} {1}).",
- OpenFailed: "Unable to open {0}: {1}"
- },
- Tooltips: {
- FullPage: "Toggle full page",
- Home: "Go home",
- ZoomIn: "Zoom in",
- ZoomOut: "Zoom out",
- NextPage: "Next page",
- PreviousPage: "Previous page",
- RotateLeft: "Rotate left",
- RotateRight: "Rotate right",
- Flip: "Flip Horizontally"
- }
- };
- $.extend( $, /** @lends OpenSeadragon */{
- /**
- * @function
- * @param {String} property
- */
- getString: function( prop ) {
- var props = prop.split('.'),
- string = null,
- args = arguments,
- container = I18N,
- i;
- for (i = 0; i < props.length - 1; i++) {
- // in case not a subproperty
- container = container[ props[ i ] ] || {};
- }
- string = container[ props[ i ] ];
- if ( typeof ( string ) != "string" ) {
- $.console.log( "Untranslated source string:", prop );
- string = ""; // FIXME: this breaks gettext()-style convention, which would return source
- }
- return string.replace(/\{\d+\}/g, function(capture) {
- var i = parseInt( capture.match( /\d+/ ), 10 ) + 1;
- return i < args.length ?
- args[ i ] :
- "";
- });
- },
- /**
- * @function
- * @param {String} property
- * @param {*} value
- */
- setString: function( prop, value ) {
- var props = prop.split('.'),
- container = I18N,
- i;
- for ( i = 0; i < props.length - 1; i++ ) {
- if ( !container[ props[ i ] ] ) {
- container[ props[ i ] ] = {};
- }
- container = container[ props[ i ] ];
- }
- container[ props[ i ] ] = value;
- }
- });
- }( OpenSeadragon ));
- /*
- * OpenSeadragon - Point
- *
- * Copyright (C) 2009 CodePlex Foundation
- * Copyright (C) 2010-2013 OpenSeadragon contributors
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are
- * met:
- *
- * - Redistributions of source code must retain the above copyright notice,
- * this list of conditions and the following disclaimer.
- *
- * - Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * - Neither the name of CodePlex Foundation nor the names of its
- * contributors may be used to endorse or promote products derived from
- * this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
- * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
- * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
- * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
- (function( $ ){
- /**
- * @class Point
- * @classdesc A Point is really used as a 2-dimensional vector, equally useful for
- * representing a point on a plane, or the height and width of a plane
- * not requiring any other frame of reference.
- *
- * @memberof OpenSeadragon
- * @param {Number} [x] The vector component 'x'. Defaults to the origin at 0.
- * @param {Number} [y] The vector component 'y'. Defaults to the origin at 0.
- */
- $.Point = function( x, y ) {
- /**
- * The vector component 'x'.
- * @member {Number} x
- * @memberof OpenSeadragon.Point#
- */
- this.x = typeof ( x ) == "number" ? x : 0;
- /**
- * The vector component 'y'.
- * @member {Number} y
- * @memberof OpenSeadragon.Point#
- */
- this.y = typeof ( y ) == "number" ? y : 0;
- };
- /** @lends OpenSeadragon.Point.prototype */
- $.Point.prototype = {
- /**
- * @function
- * @returns {OpenSeadragon.Point} a duplicate of this Point
- */
- clone: function() {
- return new $.Point(this.x, this.y);
- },
- /**
- * Add another Point to this point and return a new Point.
- * @function
- * @param {OpenSeadragon.Point} point The point to add vector components.
- * @returns {OpenSeadragon.Point} A new point representing the sum of the
- * vector components
- */
- plus: function( point ) {
- return new $.Point(
- this.x + point.x,
- this.y + point.y
- );
- },
- /**
- * Subtract another Point to this point and return a new Point.
- * @function
- * @param {OpenSeadragon.Point} point The point to subtract vector components.
- * @returns {OpenSeadragon.Point} A new point representing the subtraction of the
- * vector components
- */
- minus: function( point ) {
- return new $.Point(
- this.x - point.x,
- this.y - point.y
- );
- },
- /**
- * Multiply this point by a factor and return a new Point.
- * @function
- * @param {Number} factor The factor to multiply vector components.
- * @returns {OpenSeadragon.Point} A new point representing the multiplication
- * of the vector components by the factor
- */
- times: function( factor ) {
- return new $.Point(
- this.x * factor,
- this.y * factor
- );
- },
- /**
- * Divide this point by a factor and return a new Point.
- * @function
- * @param {Number} factor The factor to divide vector components.
- * @returns {OpenSeadragon.Point} A new point representing the division of the
- * vector components by the factor
- */
- divide: function( factor ) {
- return new $.Point(
- this.x / factor,
- this.y / factor
- );
- },
- /**
- * Compute the opposite of this point and return a new Point.
- * @function
- * @returns {OpenSeadragon.Point} A new point representing the opposite of the
- * vector components
- */
- negate: function() {
- return new $.Point( -this.x, -this.y );
- },
- /**
- * Compute the distance between this point and another point.
- * @function
- * @param {OpenSeadragon.Point} point The point to compute the distance with.
- * @returns {Number} The distance between the 2 points
- */
- distanceTo: function( point ) {
- return Math.sqrt(
- Math.pow( this.x - point.x, 2 ) +
- Math.pow( this.y - point.y, 2 )
- );
- },
- /**
- * Compute the squared distance between this point and another point.
- * Useful for optimizing things like comparing distances.
- * @function
- * @param {OpenSeadragon.Point} point The point to compute the squared distance with.
- * @returns {Number} The squared distance between the 2 points
- */
- squaredDistanceTo: function( point ) {
- return Math.pow( this.x - point.x, 2 ) +
- Math.pow( this.y - point.y, 2 );
- },
- /**
- * Apply a function to each coordinate of this point and return a new point.
- * @function
- * @param {function} func The function to apply to each coordinate.
- * @returns {OpenSeadragon.Point} A new point with the coordinates computed
- * by the specified function
- */
- apply: function( func ) {
- return new $.Point( func( this.x ), func( this.y ) );
- },
- /**
- * Check if this point is equal to another one.
- * @function
- * @param {OpenSeadragon.Point} point The point to compare this point with.
- * @returns {Boolean} true if they are equal, false otherwise.
- */
- equals: function( point ) {
- return (
- point instanceof $.Point
- ) && (
- this.x === point.x
- ) && (
- this.y === point.y
- );
- },
- /**
- * Rotates the point around the specified pivot
- * From http://stackoverflow.com/questions/4465931/rotate-rectangle-around-a-point
- * @function
- * @param {Number} degress to rotate around the pivot.
- * @param {OpenSeadragon.Point} [pivot=(0,0)] Point around which to rotate.
- * Defaults to the origin.
- * @returns {OpenSeadragon.Point}. A new point representing the point rotated around the specified pivot
- */
- rotate: function (degrees, pivot) {
- pivot = pivot || new $.Point(0, 0);
- var cos;
- var sin;
- // Avoid float computations when possible
- if (degrees % 90 === 0) {
- var d = $.positiveModulo(degrees, 360);
- switch (d) {
- case 0:
- cos = 1;
- sin = 0;
- break;
- case 90:
- cos = 0;
- sin = 1;
- break;
- case 180:
- cos = -1;
- sin = 0;
- break;
- case 270:
- cos = 0;
- sin = -1;
- break;
- }
- } else {
- var angle = degrees * Math.PI / 180.0;
- cos = Math.cos(angle);
- sin = Math.sin(angle);
- }
- var x = cos * (this.x - pivot.x) - sin * (this.y - pivot.y) + pivot.x;
- var y = sin * (this.x - pivot.x) + cos * (this.y - pivot.y) + pivot.y;
- return new $.Point(x, y);
- },
- /**
- * Convert this point to a string in the format (x,y) where x and y are
- * rounded to the nearest integer.
- * @function
- * @returns {String} A string representation of this point.
- */
- toString: function() {
- return "(" + (Math.round(this.x * 100) / 100) + "," + (Math.round(this.y * 100) / 100) + ")";
- }
- };
- }( OpenSeadragon ));
- /*
- * OpenSeadragon - TileSource
- *
- * Copyright (C) 2009 CodePlex Foundation
- * Copyright (C) 2010-2013 OpenSeadragon contributors
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are
- * met:
- *
- * - Redistributions of source code must retain the above copyright notice,
- * this list of conditions and the following disclaimer.
- *
- * - Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * - Neither the name of CodePlex Foundation nor the names of its
- * contributors may be used to endorse or promote products derived from
- * this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
- * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
- * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
- * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
- (function( $ ){
- /**
- * @class TileSource
- * @classdesc The TileSource contains the most basic implementation required to create a
- * smooth transition between layers in an image pyramid. It has only a single key
- * interface that must be implemented to complete its key functionality:
- * 'getTileUrl'. It also has several optional interfaces that can be
- * implemented if a new TileSource wishes to support configuration via a simple
- * object or array ('configure') and if the tile source supports or requires
- * configuration via retrieval of a document on the network ala AJAX or JSONP,
- * ('getImageInfo').
- * <br/>
- * By default the image pyramid is split into N layers where the image's longest
- * side in M (in pixels), where N is the smallest integer which satisfies
- * <strong>2^(N+1) >= M</strong>.
- *
- * @memberof OpenSeadragon
- * @extends OpenSeadragon.EventSource
- * @param {Object} options
- * You can either specify a URL, or literally define the TileSource (by specifying
- * width, height, tileSize, tileOverlap, minLevel, and maxLevel). For the former,
- * the extending class is expected to implement 'getImageInfo' and 'configure'.
- * For the latter, the construction is assumed to occur through
- * the extending classes implementation of 'configure'.
- * @param {String} [options.url]
- * The URL for the data necessary for this TileSource.
- * @param {String} [options.referenceStripThumbnailUrl]
- * The URL for a thumbnail image to be used by the reference strip
- * @param {Function} [options.success]
- * A function to be called upon successful creation.
- * @param {Boolean} [options.ajaxWithCredentials]
- * If this TileSource needs to make an AJAX call, this specifies whether to set
- * the XHR's withCredentials (for accessing secure data).
- * @param {Object} [options.ajaxHeaders]
- * A set of headers to include in AJAX requests.
- * @param {Number} [options.width]
- * Width of the source image at max resolution in pixels.
- * @param {Number} [options.height]
- * Height of the source image at max resolution in pixels.
- * @param {Number} [options.tileSize]
- * The size of the tiles to assumed to make up each pyramid layer in pixels.
- * Tile size determines the point at which the image pyramid must be
- * divided into a matrix of smaller images.
- * Use options.tileWidth and options.tileHeight to support non-square tiles.
- * @param {Number} [options.tileWidth]
- * The width of the tiles to assumed to make up each pyramid layer in pixels.
- * @param {Number} [options.tileHeight]
- * The height of the tiles to assumed to make up each pyramid layer in pixels.
- * @param {Number} [options.tileOverlap]
- * The number of pixels each tile is expected to overlap touching tiles.
- * @param {Number} [options.minLevel]
- * The minimum level to attempt to load.
- * @param {Number} [options.maxLevel]
- * The maximum level to attempt to load.
- */
- $.TileSource = function( width, height, tileSize, tileOverlap, minLevel, maxLevel ) {
- var _this = this;
- var args = arguments,
- options,
- i;
- if( $.isPlainObject( width ) ){
- options = width;
- }else{
- options = {
- width: args[0],
- height: args[1],
- tileSize: args[2],
- tileOverlap: args[3],
- minLevel: args[4],
- maxLevel: args[5]
- };
- }
- //Tile sources supply some events, namely 'ready' when they must be configured
- //by asynchronously fetching their configuration data.
- $.EventSource.call( this );
- //we allow options to override anything we don't treat as
- //required via idiomatic options or which is functionally
- //set depending on the state of the readiness of this tile
- //source
- $.extend( true, this, options );
- if (!this.success) {
- //Any functions that are passed as arguments are bound to the ready callback
- for ( i = 0; i < arguments.length; i++ ) {
- if ( $.isFunction( arguments[ i ] ) ) {
- this.success = arguments[ i ];
- //only one callback per constructor
- break;
- }
- }
- }
- if (this.success) {
- this.addHandler( 'ready', function ( event ) {
- _this.success( event );
- } );
- }
- /**
- * Ratio of width to height
- * @member {Number} aspectRatio
- * @memberof OpenSeadragon.TileSource#
- */
- /**
- * Vector storing x and y dimensions ( width and height respectively ).
- * @member {OpenSeadragon.Point} dimensions
- * @memberof OpenSeadragon.TileSource#
- */
- /**
- * The overlap in pixels each tile shares with its adjacent neighbors.
- * @member {Number} tileOverlap
- * @memberof OpenSeadragon.TileSource#
- */
- /**
- * The minimum pyramid level this tile source supports or should attempt to load.
- * @member {Number} minLevel
- * @memberof OpenSeadragon.TileSource#
- */
- /**
- * The maximum pyramid level this tile source supports or should attempt to load.
- * @member {Number} maxLevel
- * @memberof OpenSeadragon.TileSource#
- */
- /**
- *
- * @member {Boolean} ready
- * @memberof OpenSeadragon.TileSource#
- */
- if( 'string' == $.type( arguments[ 0 ] ) ){
- this.url = arguments[0];
- }
- if (this.url) {
- //in case the getImageInfo method is overridden and/or implies an
- //async mechanism set some safe defaults first
- this.aspectRatio = 1;
- this.dimensions = new $.Point( 10, 10 );
- this._tileWidth = 0;
- this._tileHeight = 0;
- this.tileOverlap = 0;
- this.minLevel = 0;
- this.maxLevel = 0;
- this.ready = false;
- //configuration via url implies the extending class
- //implements and 'configure'
- this.getImageInfo( this.url );
- } else {
- //explicit configuration via positional args in constructor
- //or the more idiomatic 'options' object
- this.ready = true;
- this.aspectRatio = (options.width && options.height) ?
- (options.width / options.height) : 1;
- this.dimensions = new $.Point( options.width, options.height );
- if ( this.tileSize ){
- this._tileWidth = this._tileHeight = this.tileSize;
- delete this.tileSize;
- } else {
- if( this.tileWidth ){
- // We were passed tileWidth in options, but we want to rename it
- // with a leading underscore to make clear that it is not safe to directly modify it
- this._tileWidth = this.tileWidth;
- delete this.tileWidth;
- } else {
- this._tileWidth = 0;
- }
- if( this.tileHeight ){
- // See note above about renaming this.tileWidth
- this._tileHeight = this.tileHeight;
- delete this.tileHeight;
- } else {
- this._tileHeight = 0;
- }
- }
- this.tileOverlap = options.tileOverlap ? options.tileOverlap : 0;
- this.minLevel = options.minLevel ? options.minLevel : 0;
- this.maxLevel = ( undefined !== options.maxLevel && null !== options.maxLevel ) ?
- options.maxLevel : (
- ( options.width && options.height ) ? Math.ceil(
- Math.log( Math.max( options.width, options.height ) ) /
- Math.log( 2 )
- ) : 0
- );
- if( this.success && $.isFunction( this.success ) ){
- this.success( this );
- }
- }
- };
- /** @lends OpenSeadragon.TileSource.prototype */
- $.TileSource.prototype = {
- getTileSize: function( level ) {
- $.console.error(
- "[TileSource.getTileSize] is deprecated. " +
- "Use TileSource.getTileWidth() and TileSource.getTileHeight() instead"
- );
- return this._tileWidth;
- },
- /**
- * Return the tileWidth for a given level.
- * Subclasses should override this if tileWidth can be different at different levels
- * such as in IIIFTileSource. Code should use this function rather than reading
- * from ._tileWidth directly.
- * @function
- * @param {Number} level
- */
- getTileWidth: function( level ) {
- if (!this._tileWidth) {
- return this.getTileSize(level);
- }
- return this._tileWidth;
- },
- /**
- * Return the tileHeight for a given level.
- * Subclasses should override this if tileHeight can be different at different levels
- * such as in IIIFTileSource. Code should use this function rather than reading
- * from ._tileHeight directly.
- * @function
- * @param {Number} level
- */
- getTileHeight: function( level ) {
- if (!this._tileHeight) {
- return this.getTileSize(level);
- }
- return this._tileHeight;
- },
- /**
- * @function
- * @param {Number} level
- */
- getLevelScale: function( level ) {
- // see https://github.com/openseadragon/openseadragon/issues/22
- // we use the tilesources implementation of getLevelScale to generate
- // a memoized re-implementation
- var levelScaleCache = {},
- i;
- for( i = 0; i <= this.maxLevel; i++ ){
- levelScaleCache[ i ] = 1 / Math.pow(2, this.maxLevel - i);
- }
- this.getLevelScale = function( _level ){
- return levelScaleCache[ _level ];
- };
- return this.getLevelScale( level );
- },
- /**
- * @function
- * @param {Number} level
- */
- getNumTiles: function( level ) {
- var scale = this.getLevelScale( level ),
- x = Math.ceil( scale * this.dimensions.x / this.getTileWidth(level) ),
- y = Math.ceil( scale * this.dimensions.y / this.getTileHeight(level) );
- return new $.Point( x, y );
- },
- /**
- * @function
- * @param {Number} level
- */
- getPixelRatio: function( level ) {
- var imageSizeScaled = this.dimensions.times( this.getLevelScale( level ) ),
- rx = 1.0 / imageSizeScaled.x,
- ry = 1.0 / imageSizeScaled.y;
- return new $.Point(rx, ry);
- },
- /**
- * @function
- * @returns {Number} The highest level in this tile source that can be contained in a single tile.
- */
- getClosestLevel: function() {
- var i,
- tiles;
- for (i = this.minLevel + 1; i <= this.maxLevel; i++){
- tiles = this.getNumTiles(i);
- if (tiles.x > 1 || tiles.y > 1) {
- break;
- }
- }
- return i - 1;
- },
- /**
- * @function
- * @param {Number} level
- * @param {OpenSeadragon.Point} point
- */
- getTileAtPoint: function(level, point) {
- var validPoint = point.x >= 0 && point.x <= 1 &&
- point.y >= 0 && point.y <= 1 / this.aspectRatio;
- $.console.assert(validPoint, "[TileSource.getTileAtPoint] must be called with a valid point.");
- var widthScaled = this.dimensions.x * this.getLevelScale(level);
- var pixelX = point.x * widthScaled;
- var pixelY = point.y * widthScaled;
- var x = Math.floor(pixelX / this.getTileWidth(level));
- var y = Math.floor(pixelY / this.getTileHeight(level));
- // When point.x == 1 or point.y == 1 / this.aspectRatio we want to
- // return the last tile of the row/column
- if (point.x >= 1) {
- x = this.getNumTiles(level).x - 1;
- }
- var EPSILON = 1e-15;
- if (point.y >= 1 / this.aspectRatio - EPSILON) {
- y = this.getNumTiles(level).y - 1;
- }
- return new $.Point(x, y);
- },
- /**
- * @function
- * @param {Number} level
- * @param {Number} x
- * @param {Number} y
- * @param {Boolean} [isSource=false] Whether to return the source bounds of the tile.
- * @returns {OpenSeadragon.Rect} Either where this tile fits (in normalized coordinates) or the
- * portion of the tile to use as the source of the drawing operation (in pixels), depending on
- * the isSource parameter.
- */
- getTileBounds: function( level, x, y, isSource ) {
- var dimensionsScaled = this.dimensions.times( this.getLevelScale( level ) ),
- tileWidth = this.getTileWidth(level),
- tileHeight = this.getTileHeight(level),
- px = ( x === 0 ) ? 0 : tileWidth * x - this.tileOverlap,
- py = ( y === 0 ) ? 0 : tileHeight * y - this.tileOverlap,
- sx = tileWidth + ( x === 0 ? 1 : 2 ) * this.tileOverlap,
- sy = tileHeight + ( y === 0 ? 1 : 2 ) * this.tileOverlap,
- scale = 1.0 / dimensionsScaled.x;
- sx = Math.min( sx, dimensionsScaled.x - px );
- sy = Math.min( sy, dimensionsScaled.y - py );
- if (isSource) {
- return new $.Rect(0, 0, sx, sy);
- }
- return new $.Rect( px * scale, py * scale, sx * scale, sy * scale );
- },
- /**
- * Responsible for retrieving, and caching the
- * image metadata pertinent to this TileSources implementation.
- * @function
- * @param {String} url
- * @throws {Error}
- */
- getImageInfo: function( url ) {
- var _this = this,
- callbackName,
- callback,
- readySource,
- options,
- urlParts,
- filename,
- lastDot;
- if( url ) {
- urlParts = url.split( '/' );
- filename = urlParts[ urlParts.length - 1 ];
- lastDot = filename.lastIndexOf( '.' );
- if ( lastDot > -1 ) {
- urlParts[ urlParts.length - 1 ] = filename.slice( 0, lastDot );
- }
- }
- callback = function( data ){
- if( typeof (data) === "string" ) {
- data = $.parseXml( data );
- }
- var $TileSource = $.TileSource.determineType( _this, data, url );
- if ( !$TileSource ) {
- /**
- * Raised when an error occurs loading a TileSource.
- *
- * @event open-failed
- * @memberof OpenSeadragon.TileSource
- * @type {object}
- * @property {OpenSeadragon.TileSource} eventSource - A reference to the TileSource which raised the event.
- * @property {String} message
- * @property {String} source
- * @property {?Object} userData - Arbitrary subscriber-defined object.
- */
- _this.raiseEvent( 'open-failed', { message: "Unable to load TileSource", source: url } );
- return;
- }
- options = $TileSource.prototype.configure.apply( _this, [ data, url ]);
- if (options.ajaxWithCredentials === undefined) {
- options.ajaxWithCredentials = _this.ajaxWithCredentials;
- }
- readySource = new $TileSource( options );
- _this.ready = true;
- /**
- * Raised when a TileSource is opened and initialized.
- *
- * @event ready
- * @memberof OpenSeadragon.TileSource
- * @type {object}
- * @property {OpenSeadragon.TileSource} eventSource - A reference to the TileSource which raised the event.
- * @property {Object} tileSource
- * @property {?Object} userData - Arbitrary subscriber-defined object.
- */
- _this.raiseEvent( 'ready', { tileSource: readySource } );
- };
- if( url.match(/\.js$/) ){
- //TODO: Its not very flexible to require tile sources to end jsonp
- // request for info with a url that ends with '.js' but for
- // now it's the only way I see to distinguish uniformly.
- callbackName = url.split('/').pop().replace('.js', '');
- $.jsonp({
- url: url,
- async: false,
- callbackName: callbackName,
- callback: callback
- });
- } else {
- // request info via xhr asynchronously.
- $.makeAjaxRequest( {
- url: url,
- withCredentials: this.ajaxWithCredentials,
- headers: this.ajaxHeaders,
- success: function( xhr ) {
- var data = processResponse( xhr );
- callback( data );
- },
- error: function ( xhr, exc ) {
- var msg;
- /*
- IE < 10 will block XHR requests to different origins. Any property access on the request
- object will raise an exception which we'll attempt to handle by formatting the original
- exception rather than the second one raised when we try to access xhr.status
- */
- try {
- msg = "HTTP " + xhr.status + " attempting to load TileSource";
- } catch ( e ) {
- var formattedExc;
- if ( typeof ( exc ) == "undefined" || !exc.toString ) {
- formattedExc = "Unknown error";
- } else {
- formattedExc = exc.toString();
- }
- msg = formattedExc + " attempting to load TileSource";
- }
- /***
- * Raised when an error occurs loading a TileSource.
- *
- * @event open-failed
- * @memberof OpenSeadragon.TileSource
- * @type {object}
- * @property {OpenSeadragon.TileSource} eventSource - A reference to the TileSource which raised the event.
- * @property {String} message
- * @property {String} source
- * @property {?Object} userData - Arbitrary subscriber-defined object.
- */
- _this.raiseEvent( 'open-failed', {
- message: msg,
- source: url
- });
- }
- });
- }
- },
- /**
- * Responsible determining if a the particular TileSource supports the
- * data format ( and allowed to apply logic against the url the data was
- * loaded from, if any ). Overriding implementations are expected to do
- * something smart with data and / or url to determine support. Also
- * understand that iteration order of TileSources is not guarunteed so
- * please make sure your data or url is expressive enough to ensure a simple
- * and sufficient mechanisim for clear determination.
- * @function
- * @param {String|Object|Array|Document} data
- * @param {String} url - the url the data was loaded
- * from if any.
- * @return {Boolean}
- */
- supports: function( data, url ) {
- return false;
- },
- /**
- * Responsible for parsing and configuring the
- * image metadata pertinent to this TileSources implementation.
- * This method is not implemented by this class other than to throw an Error
- * announcing you have to implement it. Because of the variety of tile
- * server technologies, and various specifications for building image
- * pyramids, this method is here to allow easy integration.
- * @function
- * @param {String|Object|Array|Document} data
- * @param {String} url - the url the data was loaded
- * from if any.
- * @return {Object} options - A dictionary of keyword arguments sufficient
- * to configure this tile sources constructor.
- * @throws {Error}
- */
- configure: function( data, url ) {
- throw new Error( "Method not implemented." );
- },
- /**
- * Responsible for retrieving the url which will return an image for the
- * region specified by the given x, y, and level components.
- * This method is not implemented by this class other than to throw an Error
- * announcing you have to implement it. Because of the variety of tile
- * server technologies, and various specifications for building image
- * pyramids, this method is here to allow easy integration.
- * @function
- * @param {Number} level
- * @param {Number} x
- * @param {Number} y
- * @throws {Error}
- */
- getTileUrl: function( level, x, y ) {
- throw new Error( "Method not implemented." );
- },
- /**
- * Responsible for retrieving the headers which will be attached to the image request for the
- * region specified by the given x, y, and level components.
- * This option is only relevant if {@link OpenSeadragon.Options}.loadTilesWithAjax is set to true.
- * The headers returned here will override headers specified at the Viewer or TiledImage level.
- * Specifying a falsy value for a header will clear its existing value set at the Viewer or
- * TiledImage level (if any).
- * @function
- * @param {Number} level
- * @param {Number} x
- * @param {Number} y
- * @returns {Object}
- */
- getTileAjaxHeaders: function( level, x, y ) {
- return {};
- },
- /**
- * @function
- * @param {Number} level
- * @param {Number} x
- * @param {Number} y
- */
- tileExists: function( level, x, y ) {
- var numTiles = this.getNumTiles( level );
- return level >= this.minLevel &&
- level <= this.maxLevel &&
- x >= 0 &&
- y >= 0 &&
- x < numTiles.x &&
- y < numTiles.y;
- }
- };
- $.extend( true, $.TileSource.prototype, $.EventSource.prototype );
- /**
- * Decides whether to try to process the response as xml, json, or hand back
- * the text
- * @private
- * @inner
- * @function
- * @param {XMLHttpRequest} xhr - the completed network request
- */
- function processResponse( xhr ){
- var responseText = xhr.responseText,
- status = xhr.status,
- statusText,
- data;
- if ( !xhr ) {
- throw new Error( $.getString( "Errors.Security" ) );
- } else if ( xhr.status !== 200 && xhr.status !== 0 ) {
- status = xhr.status;
- statusText = ( status == 404 ) ?
- "Not Found" :
- xhr.statusText;
- throw new Error( $.getString( "Errors.Status", status, statusText ) );
- }
- if( responseText.match(/\s*<.*/) ){
- try{
- data = ( xhr.responseXML && xhr.responseXML.documentElement ) ?
- xhr.responseXML :
- $.parseXml( responseText );
- } catch (e){
- data = xhr.responseText;
- }
- }else if( responseText.match(/\s*[\{\[].*/) ){
- try{
- data = $.parseJSON(responseText);
- } catch(e){
- data = responseText;
- }
- }else{
- data = responseText;
- }
- return data;
- }
- /**
- * Determines the TileSource Implementation by introspection of OpenSeadragon
- * namespace, calling each TileSource implementation of 'isType'
- * @private
- * @inner
- * @function
- * @param {Object|Array|Document} data - the tile source configuration object
- * @param {String} url - the url where the tile source configuration object was
- * loaded from, if any.
- */
- $.TileSource.determineType = function( tileSource, data, url ){
- var property;
- for( property in OpenSeadragon ){
- if( property.match(/.+TileSource$/) &&
- $.isFunction( OpenSeadragon[ property ] ) &&
- $.isFunction( OpenSeadragon[ property ].prototype.supports ) &&
- OpenSeadragon[ property ].prototype.supports.call( tileSource, data, url )
- ){
- return OpenSeadragon[ property ];
- }
- }
- $.console.error( "No TileSource was able to open %s %s", url, data );
- };
- }( OpenSeadragon ));
- /*
- * OpenSeadragon - DziTileSource
- *
- * Copyright (C) 2009 CodePlex Foundation
- * Copyright (C) 2010-2013 OpenSeadragon contributors
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are
- * met:
- *
- * - Redistributions of source code must retain the above copyright notice,
- * this list of conditions and the following disclaimer.
- *
- * - Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * - Neither the name of CodePlex Foundation nor the names of its
- * contributors may be used to endorse or promote products derived from
- * this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
- * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
- * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
- * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
- (function( $ ){
- /**
- * @class DziTileSource
- * @memberof OpenSeadragon
- * @extends OpenSeadragon.TileSource
- * @param {Number|Object} width - the pixel width of the image or the idiomatic
- * options object which is used instead of positional arguments.
- * @param {Number} height
- * @param {Number} tileSize
- * @param {Number} tileOverlap
- * @param {String} tilesUrl
- * @param {String} fileFormat
- * @param {OpenSeadragon.DisplayRect[]} displayRects
- * @property {String} tilesUrl
- * @property {String} fileFormat
- * @property {OpenSeadragon.DisplayRect[]} displayRects
- */
- $.DziTileSource = function( width, height, tileSize, tileOverlap, tilesUrl, fileFormat, displayRects, minLevel, maxLevel ) {
- var i,
- rect,
- level,
- options;
- if( $.isPlainObject( width ) ){
- options = width;
- }else{
- options = {
- width: arguments[ 0 ],
- height: arguments[ 1 ],
- tileSize: arguments[ 2 ],
- tileOverlap: arguments[ 3 ],
- tilesUrl: arguments[ 4 ],
- fileFormat: arguments[ 5 ],
- displayRects: arguments[ 6 ],
- minLevel: arguments[ 7 ],
- maxLevel: arguments[ 8 ]
- };
- }
- this._levelRects = {};
- this.tilesUrl = options.tilesUrl;
- this.fileFormat = options.fileFormat;
- this.displayRects = options.displayRects;
- if ( this.displayRects ) {
- for ( i = this.displayRects.length - 1; i >= 0; i-- ) {
- rect = this.displayRects[ i ];
- for ( level = rect.minLevel; level <= rect.maxLevel; level++ ) {
- if ( !this._levelRects[ level ] ) {
- this._levelRects[ level ] = [];
- }
- this._levelRects[ level ].push( rect );
- }
- }
- }
- $.TileSource.apply( this, [ options ] );
- };
- $.extend( $.DziTileSource.prototype, $.TileSource.prototype, /** @lends OpenSeadragon.DziTileSource.prototype */{
- /**
- * Determine if the data and/or url imply the image service is supported by
- * this tile source.
- * @function
- * @param {Object|Array} data
- * @param {String} optional - url
- */
- supports: function( data, url ){
- var ns;
- if ( data.Image ) {
- ns = data.Image.xmlns;
- } else if ( data.documentElement) {
- if ("Image" == data.documentElement.localName || "Image" == data.documentElement.tagName) {
- ns = data.documentElement.namespaceURI;
- }
- }
- ns = (ns || '').toLowerCase();
- return (ns.indexOf('schemas.microsoft.com/deepzoom/2008') !== -1 ||
- ns.indexOf('schemas.microsoft.com/deepzoom/2009') !== -1);
- },
- /**
- *
- * @function
- * @param {Object|XMLDocument} data - the raw configuration
- * @param {String} url - the url the data was retrieved from if any.
- * @return {Object} options - A dictionary of keyword arguments sufficient
- * to configure this tile sources constructor.
- */
- configure: function( data, url ){
- var options;
- if( !$.isPlainObject(data) ){
- options = configureFromXML( this, data );
- }else{
- options = configureFromObject( this, data );
- }
- if (url && !options.tilesUrl) {
- options.tilesUrl = url.replace(
- /([^\/]+?)(\.(dzi|xml|js)?(\?[^\/]*)?)?\/?$/, '$1_files/');
- if (url.search(/\.(dzi|xml|js)\?/) != -1) {
- options.queryParams = url.match(/\?.*/);
- }else{
- options.queryParams = '';
- }
- }
- return options;
- },
- /**
- * @function
- * @param {Number} level
- * @param {Number} x
- * @param {Number} y
- */
- getTileUrl: function( level, x, y ) {
- return [ this.tilesUrl, level, '/', x, '_', y, '.', this.fileFormat, this.queryParams ].join( '' );
- },
- /**
- * @function
- * @param {Number} level
- * @param {Number} x
- * @param {Number} y
- */
- tileExists: function( level, x, y ) {
- var rects = this._levelRects[ level ],
- rect,
- scale,
- xMin,
- yMin,
- xMax,
- yMax,
- i;
- if ((this.minLevel && level < this.minLevel) || (this.maxLevel && level > this.maxLevel)) {
- return false;
- }
- if ( !rects || !rects.length ) {
- return true;
- }
- for ( i = rects.length - 1; i >= 0; i-- ) {
- rect = rects[ i ];
- if ( level < rect.minLevel || level > rect.maxLevel ) {
- continue;
- }
- scale = this.getLevelScale( level );
- xMin = rect.x * scale;
- yMin = rect.y * scale;
- xMax = xMin + rect.width * scale;
- yMax = yMin + rect.height * scale;
- xMin = Math.floor( xMin / this._tileWidth );
- yMin = Math.floor( yMin / this._tileWidth ); // DZI tiles are square, so we just use _tileWidth
- xMax = Math.ceil( xMax / this._tileWidth );
- yMax = Math.ceil( yMax / this._tileWidth );
- if ( xMin <= x && x < xMax && yMin <= y && y < yMax ) {
- return true;
- }
- }
- return false;
- }
- });
- /**
- * @private
- * @inner
- * @function
- */
- function configureFromXML( tileSource, xmlDoc ){
- if ( !xmlDoc || !xmlDoc.documentElement ) {
- throw new Error( $.getString( "Errors.Xml" ) );
- }
- var root = xmlDoc.documentElement,
- rootName = root.localName || root.tagName,
- ns = xmlDoc.documentElement.namespaceURI,
- configuration = null,
- displayRects = [],
- dispRectNodes,
- dispRectNode,
- rectNode,
- sizeNode,
- i;
- if ( rootName == "Image" ) {
- try {
- sizeNode = root.getElementsByTagName("Size" )[ 0 ];
- if (sizeNode === undefined) {
- sizeNode = root.getElementsByTagNameNS(ns, "Size" )[ 0 ];
- }
- configuration = {
- Image: {
- xmlns: "http://schemas.microsoft.com/deepzoom/2008",
- Url: root.getAttribute( "Url" ),
- Format: root.getAttribute( "Format" ),
- DisplayRect: null,
- Overlap: parseInt( root.getAttribute( "Overlap" ), 10 ),
- TileSize: parseInt( root.getAttribute( "TileSize" ), 10 ),
- Size: {
- Height: parseInt( sizeNode.getAttribute( "Height" ), 10 ),
- Width: parseInt( sizeNode.getAttribute( "Width" ), 10 )
- }
- }
- };
- if ( !$.imageFormatSupported( configuration.Image.Format ) ) {
- throw new Error(
- $.getString( "Errors.ImageFormat", configuration.Image.Format.toUpperCase() )
- );
- }
- dispRectNodes = root.getElementsByTagName("DisplayRect" );
- if (dispRectNodes === undefined) {
- dispRectNodes = root.getElementsByTagNameNS(ns, "DisplayRect" )[ 0 ];
- }
- for ( i = 0; i < dispRectNodes.length; i++ ) {
- dispRectNode = dispRectNodes[ i ];
- rectNode = dispRectNode.getElementsByTagName("Rect" )[ 0 ];
- if (rectNode === undefined) {
- rectNode = dispRectNode.getElementsByTagNameNS(ns, "Rect" )[ 0 ];
- }
- displayRects.push({
- Rect: {
- X: parseInt( rectNode.getAttribute( "X" ), 10 ),
- Y: parseInt( rectNode.getAttribute( "Y" ), 10 ),
- Width: parseInt( rectNode.getAttribute( "Width" ), 10 ),
- Height: parseInt( rectNode.getAttribute( "Height" ), 10 ),
- MinLevel: parseInt( dispRectNode.getAttribute( "MinLevel" ), 10 ),
- MaxLevel: parseInt( dispRectNode.getAttribute( "MaxLevel" ), 10 )
- }
- });
- }
- if( displayRects.length ){
- configuration.Image.DisplayRect = displayRects;
- }
- return configureFromObject( tileSource, configuration );
- } catch ( e ) {
- throw (e instanceof Error) ?
- e :
- new Error( $.getString("Errors.Dzi") );
- }
- } else if ( rootName == "Collection" ) {
- throw new Error( $.getString( "Errors.Dzc" ) );
- } else if ( rootName == "Error" ) {
- var messageNode = root.getElementsByTagName("Message")[0];
- var message = messageNode.firstChild.nodeValue;
- throw new Error(message);
- }
- throw new Error( $.getString( "Errors.Dzi" ) );
- }
- /**
- * @private
- * @inner
- * @function
- */
- function configureFromObject( tileSource, configuration ){
- var imageData = configuration.Image,
- tilesUrl = imageData.Url,
- fileFormat = imageData.Format,
- sizeData = imageData.Size,
- dispRectData = imageData.DisplayRect || [],
- width = parseInt( sizeData.Width, 10 ),
- height = parseInt( sizeData.Height, 10 ),
- tileSize = parseInt( imageData.TileSize, 10 ),
- tileOverlap = parseInt( imageData.Overlap, 10 ),
- displayRects = [],
- rectData,
- i;
- //TODO: need to figure out out to better handle image format compatibility
- // which actually includes additional file formats like xml and pdf
- // and plain text for various tilesource implementations to avoid low
- // level errors.
- //
- // For now, just don't perform the check.
- //
- /*if ( !imageFormatSupported( fileFormat ) ) {
- throw new Error(
- $.getString( "Errors.ImageFormat", fileFormat.toUpperCase() )
- );
- }*/
- for ( i = 0; i < dispRectData.length; i++ ) {
- rectData = dispRectData[ i ].Rect;
- displayRects.push( new $.DisplayRect(
- parseInt( rectData.X, 10 ),
- parseInt( rectData.Y, 10 ),
- parseInt( rectData.Width, 10 ),
- parseInt( rectData.Height, 10 ),
- parseInt( rectData.MinLevel, 10 ),
- parseInt( rectData.MaxLevel, 10 )
- ));
- }
- return $.extend(true, {
- width: width, /* width *required */
- height: height, /* height *required */
- tileSize: tileSize, /* tileSize *required */
- tileOverlap: tileOverlap, /* tileOverlap *required */
- minLevel: null, /* minLevel */
- maxLevel: null, /* maxLevel */
- tilesUrl: tilesUrl, /* tilesUrl */
- fileFormat: fileFormat, /* fileFormat */
- displayRects: displayRects /* displayRects */
- }, configuration );
- }
- }( OpenSeadragon ));
- /*
- * OpenSeadragon - IIIFTileSource
- *
- * Copyright (C) 2009 CodePlex Foundation
- * Copyright (C) 2010-2013 OpenSeadragon contributors
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are
- * met:
- *
- * - Redistributions of source code must retain the above copyright notice,
- * this list of conditions and the following disclaimer.
- *
- * - Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * - Neither the name of CodePlex Foundation nor the names of its
- * contributors may be used to endorse or promote products derived from
- * this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
- * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
- * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
- * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
- (function( $ ){
- /**
- * @class IIIFTileSource
- * @classdesc A client implementation of the International Image Interoperability Framework
- * Format: Image API 1.0 - 2.1
- *
- * @memberof OpenSeadragon
- * @extends OpenSeadragon.TileSource
- * @see http://iiif.io/api/image/
- * @param {String} [options.tileFormat='jpg']
- * The extension that will be used when requiring tiles.
- */
- $.IIIFTileSource = function( options ){
- /* eslint-disable camelcase */
- $.extend( true, this, options );
- if ( !( this.height && this.width && this['@id'] ) ) {
- throw new Error( 'IIIF required parameters not provided.' );
- }
- options.tileSizePerScaleFactor = {};
- this.tileFormat = this.tileFormat || 'jpg';
- // N.B. 2.0 renamed scale_factors to scaleFactors
- if ( this.tile_width && this.tile_height ) {
- options.tileWidth = this.tile_width;
- options.tileHeight = this.tile_height;
- } else if ( this.tile_width ) {
- options.tileSize = this.tile_width;
- } else if ( this.tile_height ) {
- options.tileSize = this.tile_height;
- } else if ( this.tiles ) {
- // Version 2.0 forwards
- if ( this.tiles.length == 1 ) {
- options.tileWidth = this.tiles[0].width;
- // Use height if provided, otherwise assume square tiles and use width.
- options.tileHeight = this.tiles[0].height || this.tiles[0].width;
- this.scale_factors = this.tiles[0].scaleFactors;
- } else {
- // Multiple tile sizes at different levels
- this.scale_factors = [];
- for (var t = 0; t < this.tiles.length; t++ ) {
- for (var sf = 0; sf < this.tiles[t].scaleFactors.length; sf++) {
- var scaleFactor = this.tiles[t].scaleFactors[sf];
- this.scale_factors.push(scaleFactor);
- options.tileSizePerScaleFactor[scaleFactor] = {
- width: this.tiles[t].width,
- height: this.tiles[t].height || this.tiles[t].width
- };
- }
- }
- }
- } else if ( canBeTiled(options.profile) ) {
- // use the largest of tileOptions that is smaller than the short dimension
- var shortDim = Math.min( this.height, this.width ),
- tileOptions = [256, 512, 1024],
- smallerTiles = [];
- for ( var c = 0; c < tileOptions.length; c++ ) {
- if ( tileOptions[c] <= shortDim ) {
- smallerTiles.push( tileOptions[c] );
- }
- }
- if ( smallerTiles.length > 0 ) {
- options.tileSize = Math.max.apply( null, smallerTiles );
- } else {
- // If we're smaller than 256, just use the short side.
- options.tileSize = shortDim;
- }
- } else if (this.sizes && this.sizes.length > 0) {
- // This info.json can't be tiled, but we can still construct a legacy pyramid from the sizes array.
- // In this mode, IIIFTileSource will call functions from the abstract baseTileSource or the
- // LegacyTileSource instead of performing IIIF tiling.
- this.emulateLegacyImagePyramid = true;
- options.levels = constructLevels( this );
- // use the largest available size to define tiles
- $.extend( true, options, {
- width: options.levels[ options.levels.length - 1 ].width,
- height: options.levels[ options.levels.length - 1 ].height,
- tileSize: Math.max( options.height, options.width ),
- tileOverlap: 0,
- minLevel: 0,
- maxLevel: options.levels.length - 1
- });
- this.levels = options.levels;
- } else {
- $.console.error("Nothing in the info.json to construct image pyramids from");
- }
- if (!options.maxLevel && !this.emulateLegacyImagePyramid) {
- if (!this.scale_factors) {
- options.maxLevel = Number(Math.ceil(Math.log(Math.max(this.width, this.height), 2)));
- } else {
- var maxScaleFactor = Math.max.apply(null, this.scale_factors);
- options.maxLevel = Math.round(Math.log(maxScaleFactor) * Math.LOG2E);
- }
- }
- $.TileSource.apply( this, [ options ] );
- };
- $.extend( $.IIIFTileSource.prototype, $.TileSource.prototype, /** @lends OpenSeadragon.IIIFTileSource.prototype */{
- /**
- * Determine if the data and/or url imply the image service is supported by
- * this tile source.
- * @function
- * @param {Object|Array} data
- * @param {String} optional - url
- */
- supports: function( data, url ) {
- // Version 2.0 and forwards
- if (data.protocol && data.protocol == 'http://iiif.io/api/image') {
- return true;
- // Version 1.1
- } else if ( data['@context'] && (
- data['@context'] == "http://library.stanford.edu/iiif/image-api/1.1/context.json" ||
- data['@context'] == "http://iiif.io/api/image/1/context.json") ) {
- // N.B. the iiif.io context is wrong, but where the representation lives so likely to be used
- return true;
- // Version 1.0
- } else if ( data.profile &&
- data.profile.indexOf("http://library.stanford.edu/iiif/image-api/compliance.html") === 0) {
- return true;
- } else if ( data.identifier && data.width && data.height ) {
- return true;
- } else if ( data.documentElement &&
- "info" == data.documentElement.tagName &&
- "http://library.stanford.edu/iiif/image-api/ns/" ==
- data.documentElement.namespaceURI) {
- return true;
- // Not IIIF
- } else {
- return false;
- }
- },
- /**
- *
- * @function
- * @param {Object} data - the raw configuration
- * @example <caption>IIIF 1.1 Info Looks like this</caption>
- * {
- * "@context" : "http://library.stanford.edu/iiif/image-api/1.1/context.json",
- * "@id" : "http://iiif.example.com/prefix/1E34750D-38DB-4825-A38A-B60A345E591C",
- * "width" : 6000,
- * "height" : 4000,
- * "scale_factors" : [ 1, 2, 4 ],
- * "tile_width" : 1024,
- * "tile_height" : 1024,
- * "formats" : [ "jpg", "png" ],
- * "qualities" : [ "native", "grey" ],
- * "profile" : "http://library.stanford.edu/iiif/image-api/1.1/compliance.html#level0"
- * }
- */
- configure: function( data, url ){
- // Try to deduce our version and fake it upwards if needed
- if ( !$.isPlainObject(data) ) {
- var options = configureFromXml10( data );
- options['@context'] = "http://iiif.io/api/image/1.0/context.json";
- options['@id'] = url.replace('/info.xml', '');
- return options;
- } else {
- if ( !data['@context'] ) {
- data['@context'] = 'http://iiif.io/api/image/1.0/context.json';
- data['@id'] = url.replace('/info.json', '');
- }
- if(data.preferredFormats) {
- for (var f = 0; f < data.preferredFormats.length; f++ ) {
- if ( OpenSeadragon.imageFormatSupported(data.preferredFormats[f]) ) {
- data.tileFormat = data.preferredFormats[f];
- break;
- }
- }
- }
- return data;
- }
- },
- /**
- * Return the tileWidth for the given level.
- * @function
- * @param {Number} level
- */
- getTileWidth: function( level ) {
- if(this.emulateLegacyImagePyramid) {
- return $.TileSource.prototype.getTileWidth.call(this, level);
- }
- var scaleFactor = Math.pow(2, this.maxLevel - level);
- if (this.tileSizePerScaleFactor && this.tileSizePerScaleFactor[scaleFactor]) {
- return this.tileSizePerScaleFactor[scaleFactor].width;
- }
- return this._tileWidth;
- },
- /**
- * Return the tileHeight for the given level.
- * @function
- * @param {Number} level
- */
- getTileHeight: function( level ) {
- if(this.emulateLegacyImagePyramid) {
- return $.TileSource.prototype.getTileHeight.call(this, level);
- }
- var scaleFactor = Math.pow(2, this.maxLevel - level);
- if (this.tileSizePerScaleFactor && this.tileSizePerScaleFactor[scaleFactor]) {
- return this.tileSizePerScaleFactor[scaleFactor].height;
- }
- return this._tileHeight;
- },
- /**
- * @function
- * @param {Number} level
- */
- getLevelScale: function ( level ) {
- if(this.emulateLegacyImagePyramid) {
- var levelScale = NaN;
- if (this.levels.length > 0 && level >= this.minLevel && level <= this.maxLevel) {
- levelScale =
- this.levels[level].width /
- this.levels[this.maxLevel].width;
- }
- return levelScale;
- }
- return $.TileSource.prototype.getLevelScale.call(this, level);
- },
- /**
- * @function
- * @param {Number} level
- */
- getNumTiles: function( level ) {
- if(this.emulateLegacyImagePyramid) {
- var scale = this.getLevelScale(level);
- if (scale) {
- return new $.Point(1, 1);
- } else {
- return new $.Point(0, 0);
- }
- }
- return $.TileSource.prototype.getNumTiles.call(this, level);
- },
- /**
- * @function
- * @param {Number} level
- * @param {OpenSeadragon.Point} point
- */
- getTileAtPoint: function( level, point ) {
- if(this.emulateLegacyImagePyramid) {
- return new $.Point(0, 0);
- }
- return $.TileSource.prototype.getTileAtPoint.call(this, level, point);
- },
- /**
- * Responsible for retrieving the url which will return an image for the
- * region specified by the given x, y, and level components.
- * @function
- * @param {Number} level - z index
- * @param {Number} x
- * @param {Number} y
- * @throws {Error}
- */
- getTileUrl: function( level, x, y ){
- if(this.emulateLegacyImagePyramid) {
- var url = null;
- if ( this.levels.length > 0 && level >= this.minLevel && level <= this.maxLevel ) {
- url = this.levels[ level ].url;
- }
- return url;
- }
- //# constants
- var IIIF_ROTATION = '0',
- //## get the scale (level as a decimal)
- scale = Math.pow( 0.5, this.maxLevel - level ),
- //# image dimensions at this level
- levelWidth = Math.ceil( this.width * scale ),
- levelHeight = Math.ceil( this.height * scale ),
- //## iiif region
- tileWidth,
- tileHeight,
- iiifTileSizeWidth,
- iiifTileSizeHeight,
- iiifRegion,
- iiifTileX,
- iiifTileY,
- iiifTileW,
- iiifTileH,
- iiifSize,
- iiifSizeW,
- iiifQuality,
- uri,
- isv1;
- tileWidth = this.getTileWidth(level);
- tileHeight = this.getTileHeight(level);
- iiifTileSizeWidth = Math.ceil( tileWidth / scale );
- iiifTileSizeHeight = Math.ceil( tileHeight / scale );
- isv1 = ( this['@context'].indexOf('/1.0/context.json') > -1 ||
- this['@context'].indexOf('/1.1/context.json') > -1 ||
- this['@context'].indexOf('/1/context.json') > -1 );
- if (isv1) {
- iiifQuality = "native." + this.tileFormat;
- } else {
- iiifQuality = "default." + this.tileFormat;
- }
- if ( levelWidth < tileWidth && levelHeight < tileHeight ){
- if ( isv1 || levelWidth !== this.width ) {
- iiifSize = levelWidth + ",";
- } else {
- iiifSize = "max";
- }
- iiifRegion = 'full';
- } else {
- iiifTileX = x * iiifTileSizeWidth;
- iiifTileY = y * iiifTileSizeHeight;
- iiifTileW = Math.min( iiifTileSizeWidth, this.width - iiifTileX );
- iiifTileH = Math.min( iiifTileSizeHeight, this.height - iiifTileY );
- if ( x === 0 && y === 0 && iiifTileW === this.width && iiifTileH === this.height ) {
- iiifRegion = "full";
- } else {
- iiifRegion = [ iiifTileX, iiifTileY, iiifTileW, iiifTileH ].join( ',' );
- }
- iiifSizeW = Math.ceil( iiifTileW * scale );
- if ( (!isv1) && iiifSizeW === this.width ) {
- iiifSize = "max";
- } else {
- iiifSize = iiifSizeW + ",";
- }
- }
- uri = [ this['@id'], iiifRegion, iiifSize, IIIF_ROTATION, iiifQuality ].join( '/' );
- return uri;
- }
- });
- /**
- * Determine whether arbitrary tile requests can be made against a service with the given profile
- * @function
- * @param {array} profile - IIIF profile array
- * @throws {Error}
- */
- function canBeTiled ( profile ) {
- var level0Profiles = [
- "http://library.stanford.edu/iiif/image-api/compliance.html#level0",
- "http://library.stanford.edu/iiif/image-api/1.1/compliance.html#level0",
- "http://iiif.io/api/image/2/level0.json"
- ];
- var isLevel0 = (level0Profiles.indexOf(profile[0]) !== -1);
- var hasSizeByW = false;
- if ( profile.length > 1 && profile[1].supports ) {
- hasSizeByW = profile[1].supports.indexOf( "sizeByW" ) !== -1;
- }
- return !isLevel0 || hasSizeByW;
- }
- /**
- * Build the legacy pyramid URLs (one tile per level)
- * @function
- * @param {object} options - infoJson
- * @throws {Error}
- */
- function constructLevels(options) {
- var levels = [];
- for(var i = 0; i < options.sizes.length; i++) {
- levels.push({
- url: options['@id'] + '/full/' + options.sizes[i].width + ',/0/default.' + options.tileFormat,
- width: options.sizes[i].width,
- height: options.sizes[i].height
- });
- }
- return levels.sort(function(a, b) {
- return a.width - b.width;
- });
- }
- function configureFromXml10(xmlDoc) {
- //parse the xml
- if ( !xmlDoc || !xmlDoc.documentElement ) {
- throw new Error( $.getString( "Errors.Xml" ) );
- }
- var root = xmlDoc.documentElement,
- rootName = root.tagName,
- configuration = null;
- if ( rootName == "info" ) {
- try {
- configuration = {};
- parseXML10( root, configuration );
- return configuration;
- } catch ( e ) {
- throw (e instanceof Error) ?
- e :
- new Error( $.getString("Errors.IIIF") );
- }
- }
- throw new Error( $.getString( "Errors.IIIF" ) );
- }
- function parseXML10( node, configuration, property ) {
- var i,
- value;
- if ( node.nodeType == 3 && property ) {//text node
- value = node.nodeValue.trim();
- if( value.match(/^\d*$/)){
- value = Number( value );
- }
- if( !configuration[ property ] ){
- configuration[ property ] = value;
- }else{
- if( !$.isArray( configuration[ property ] ) ){
- configuration[ property ] = [ configuration[ property ] ];
- }
- configuration[ property ].push( value );
- }
- } else if( node.nodeType == 1 ){
- for( i = 0; i < node.childNodes.length; i++ ){
- parseXML10( node.childNodes[ i ], configuration, node.nodeName );
- }
- }
- }
- }( OpenSeadragon ));
- /*
- * OpenSeadragon - OsmTileSource
- *
- * Copyright (C) 2009 CodePlex Foundation
- * Copyright (C) 2010-2013 OpenSeadragon contributors
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are
- * met:
- *
- * - Redistributions of source code must retain the above copyright notice,
- * this list of conditions and the following disclaimer.
- *
- * - Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * - Neither the name of CodePlex Foundation nor the names of its
- * contributors may be used to endorse or promote products derived from
- * this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
- * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
- * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
- * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
- /*
- * Derived from the OSM tile source in Rainer Simon's seajax-utils project
- * <http://github.com/rsimon/seajax-utils>. Rainer Simon has contributed
- * the included code to the OpenSeadragon project under the New BSD license;
- * see <https://github.com/openseadragon/openseadragon/issues/58>.
- */
- (function( $ ){
- /**
- * @class OsmTileSource
- * @classdesc A tilesource implementation for OpenStreetMap.<br><br>
- *
- * Note 1. Zoomlevels. Deep Zoom and OSM define zoom levels differently. In Deep
- * Zoom, level 0 equals an image of 1x1 pixels. In OSM, level 0 equals an image of
- * 256x256 levels (see http://gasi.ch/blog/inside-deep-zoom-2). I.e. there is a
- * difference of log2(256)=8 levels.<br><br>
- *
- * Note 2. Image dimension. According to the OSM Wiki
- * (http://wiki.openstreetmap.org/wiki/Slippy_map_tilenames#Zoom_levels)
- * the highest Mapnik zoom level has 256.144x256.144 tiles, with a 256x256
- * pixel size. I.e. the Deep Zoom image dimension is 65.572.864x65.572.864
- * pixels.
- *
- * @memberof OpenSeadragon
- * @extends OpenSeadragon.TileSource
- * @param {Number|Object} width - the pixel width of the image or the idiomatic
- * options object which is used instead of positional arguments.
- * @param {Number} height
- * @param {Number} tileSize
- * @param {Number} tileOverlap
- * @param {String} tilesUrl
- */
- $.OsmTileSource = function( width, height, tileSize, tileOverlap, tilesUrl ) {
- var options;
- if( $.isPlainObject( width ) ){
- options = width;
- }else{
- options = {
- width: arguments[0],
- height: arguments[1],
- tileSize: arguments[2],
- tileOverlap: arguments[3],
- tilesUrl: arguments[4]
- };
- }
- //apply default setting for standard public OpenStreatMaps service
- //but allow them to be specified so fliks can host there own instance
- //or apply against other services supportting the same standard
- if( !options.width || !options.height ){
- options.width = 65572864;
- options.height = 65572864;
- }
- if( !options.tileSize ){
- options.tileSize = 256;
- options.tileOverlap = 0;
- }
- if( !options.tilesUrl ){
- options.tilesUrl = "http://tile.openstreetmap.org/";
- }
- options.minLevel = 8;
- $.TileSource.apply( this, [ options ] );
- };
- $.extend( $.OsmTileSource.prototype, $.TileSource.prototype, /** @lends OpenSeadragon.OsmTileSource.prototype */{
- /**
- * Determine if the data and/or url imply the image service is supported by
- * this tile source.
- * @function
- * @param {Object|Array} data
- * @param {String} optional - url
- */
- supports: function( data, url ){
- return (
- data.type &&
- "openstreetmaps" == data.type
- );
- },
- /**
- *
- * @function
- * @param {Object} data - the raw configuration
- * @param {String} url - the url the data was retrieved from if any.
- * @return {Object} options - A dictionary of keyword arguments sufficient
- * to configure this tile sources constructor.
- */
- configure: function( data, url ){
- return data;
- },
- /**
- * @function
- * @param {Number} level
- * @param {Number} x
- * @param {Number} y
- */
- getTileUrl: function( level, x, y ) {
- return this.tilesUrl + (level - 8) + "/" + x + "/" + y + ".png";
- }
- });
- }( OpenSeadragon ));
- /*
- * OpenSeadragon - TmsTileSource
- *
- * Copyright (C) 2009 CodePlex Foundation
- * Copyright (C) 2010-2013 OpenSeadragon contributors
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are
- * met:
- *
- * - Redistributions of source code must retain the above copyright notice,
- * this list of conditions and the following disclaimer.
- *
- * - Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * - Neither the name of CodePlex Foundation nor the names of its
- * contributors may be used to endorse or promote products derived from
- * this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
- * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
- * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
- * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
- /*
- * Derived from the TMS tile source in Rainer Simon's seajax-utils project
- * <http://github.com/rsimon/seajax-utils>. Rainer Simon has contributed
- * the included code to the OpenSeadragon project under the New BSD license;
- * see <https://github.com/openseadragon/openseadragon/issues/58>.
- */
- (function( $ ){
- /**
- * @class TmsTileSource
- * @classdesc A tilesource implementation for Tiled Map Services (TMS).
- * TMS tile scheme ( [ as supported by OpenLayers ] is described here
- * ( http://openlayers.org/dev/examples/tms.html ).
- *
- * @memberof OpenSeadragon
- * @extends OpenSeadragon.TileSource
- * @param {Number|Object} width - the pixel width of the image or the idiomatic
- * options object which is used instead of positional arguments.
- * @param {Number} height
- * @param {Number} tileSize
- * @param {Number} tileOverlap
- * @param {String} tilesUrl
- */
- $.TmsTileSource = function( width, height, tileSize, tileOverlap, tilesUrl ) {
- var options;
- if( $.isPlainObject( width ) ){
- options = width;
- }else{
- options = {
- width: arguments[0],
- height: arguments[1],
- tileSize: arguments[2],
- tileOverlap: arguments[3],
- tilesUrl: arguments[4]
- };
- }
- // TMS has integer multiples of 256 for width/height and adds buffer
- // if necessary -> account for this!
- var bufferedWidth = Math.ceil(options.width / 256) * 256,
- bufferedHeight = Math.ceil(options.height / 256) * 256,
- max;
- // Compute number of zoomlevels in this tileset
- if (bufferedWidth > bufferedHeight) {
- max = bufferedWidth / 256;
- } else {
- max = bufferedHeight / 256;
- }
- options.maxLevel = Math.ceil(Math.log(max) / Math.log(2)) - 1;
- options.tileSize = 256;
- options.width = bufferedWidth;
- options.height = bufferedHeight;
- $.TileSource.apply( this, [ options ] );
- };
- $.extend( $.TmsTileSource.prototype, $.TileSource.prototype, /** @lends OpenSeadragon.TmsTileSource.prototype */{
- /**
- * Determine if the data and/or url imply the image service is supported by
- * this tile source.
- * @function
- * @param {Object|Array} data
- * @param {String} optional - url
- */
- supports: function( data, url ){
- return ( data.type && "tiledmapservice" == data.type );
- },
- /**
- *
- * @function
- * @param {Object} data - the raw configuration
- * @param {String} url - the url the data was retrieved from if any.
- * @return {Object} options - A dictionary of keyword arguments sufficient
- * to configure this tile sources constructor.
- */
- configure: function( data, url ){
- return data;
- },
- /**
- * @function
- * @param {Number} level
- * @param {Number} x
- * @param {Number} y
- */
- getTileUrl: function( level, x, y ) {
- // Convert from Deep Zoom definition to TMS zoom definition
- var yTiles = this.getNumTiles( level ).y - 1;
- return this.tilesUrl + level + "/" + x + "/" + (yTiles - y) + ".png";
- }
- });
- }( OpenSeadragon ));
- (function($) {
- /**
- * @class ZoomifyTileSource
- * @classdesc A tilesource implementation for the zoomify format.
- *
- * A description of the format can be found here:
- * https://ecommons.cornell.edu/bitstream/handle/1813/5410/Introducing_Zoomify_Image.pdf
- *
- * There are two ways of creating a zoomify tilesource for openseadragon
- *
- * 1) Supplying all necessary information in the tilesource object. A minimal example object for this method looks like this:
- *
- * {
- * type: "zoomifytileservice",
- * width: 1000,
- * height: 1000,
- * tilesUrl: "/test/data/zoomify/"
- * }
- *
- * The tileSize is currently hardcoded to 256 (the usual Zoomify default). The tileUrl must the path to the image _directory_.
- *
- * 2) Loading image metadata from xml file: (CURRENTLY NOT SUPPORTED)
- *
- * When creating zoomify formatted images one "xml" like file with name ImageProperties.xml
- * will be created as well. Here is an example of such a file:
- *
- * <IMAGE_PROPERTIES WIDTH="1000" HEIGHT="1000" NUMTILES="21" NUMIMAGES="1" VERSION="1.8" TILESIZE="256" />
- *
- * To use this xml file as metadata source you must supply the path to the ImageProperties.xml file and leave out all other parameters:
- * As stated above, this method of loading a zoomify tilesource is currently not supported
- *
- * {
- * type: "zoomifytileservice",
- * tilesUrl: "/test/data/zoomify/ImageProperties.xml"
- * }
- *
- * @memberof OpenSeadragon
- * @extends OpenSeadragon.TileSource
- * @param {Number} width - the pixel width of the image.
- * @param {Number} height
- * @param {Number} tileSize
- * @param {String} tilesUrl
- */
- $.ZoomifyTileSource = function(options) {
- options.tileSize = 256;
- var currentImageSize = {
- x: options.width,
- y: options.height
- };
- options.imageSizes = [{
- x: options.width,
- y: options.height
- }];
- options.gridSize = [this._getGridSize(options.width, options.height, options.tileSize)];
- while (parseInt(currentImageSize.x, 10) > options.tileSize || parseInt(currentImageSize.y, 10) > options.tileSize) {
- currentImageSize.x = Math.floor(currentImageSize.x / 2);
- currentImageSize.y = Math.floor(currentImageSize.y / 2);
- options.imageSizes.push({
- x: currentImageSize.x,
- y: currentImageSize.y
- });
- options.gridSize.push(this._getGridSize(currentImageSize.x, currentImageSize.y, options.tileSize));
- }
- options.imageSizes.reverse();
- options.gridSize.reverse();
- options.minLevel = 0;
- options.maxLevel = options.gridSize.length - 1;
- OpenSeadragon.TileSource.apply(this, [options]);
- };
- $.extend($.ZoomifyTileSource.prototype, $.TileSource.prototype, /** @lends OpenSeadragon.ZoomifyTileSource.prototype */ {
- //private
- _getGridSize: function(width, height, tileSize) {
- return {
- x: Math.ceil(width / tileSize),
- y: Math.ceil(height / tileSize)
- };
- },
- //private
- _calculateAbsoluteTileNumber: function(level, x, y) {
- var num = 0;
- var size = {};
- //Sum up all tiles below the level we want the number of tiles
- for (var z = 0; z < level; z++) {
- size = this.gridSize[z];
- num += size.x * size.y;
- }
- //Add the tiles of the level
- size = this.gridSize[level];
- num += size.x * y + x;
- return num;
- },
- /**
- * Determine if the data and/or url imply the image service is supported by
- * this tile source.
- * @function
- * @param {Object|Array} data
- * @param {String} optional - url
- */
- supports: function(data, url) {
- return (data.type && "zoomifytileservice" == data.type);
- },
- /**
- *
- * @function
- * @param {Object} data - the raw configuration
- * @param {String} url - the url the data was retrieved from if any.
- * @return {Object} options - A dictionary of keyword arguments sufficient
- * to configure this tile sources constructor.
- */
- configure: function(data, url) {
- return data;
- },
- /**
- * @function
- * @param {Number} level
- * @param {Number} x
- * @param {Number} y
- */
- getTileUrl: function(level, x, y) {
- //console.log(level);
- var result = 0;
- var num = this._calculateAbsoluteTileNumber(level, x, y);
- result = Math.floor(num / 256);
- return this.tilesUrl + 'TileGroup' + result + '/' + level + '-' + x + '-' + y + '.jpg';
- }
- });
- }(OpenSeadragon));
- /*
- * OpenSeadragon - LegacyTileSource
- *
- * Copyright (C) 2009 CodePlex Foundation
- * Copyright (C) 2010-2013 OpenSeadragon contributors
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are
- * met:
- *
- * - Redistributions of source code must retain the above copyright notice,
- * this list of conditions and the following disclaimer.
- *
- * - Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * - Neither the name of CodePlex Foundation nor the names of its
- * contributors may be used to endorse or promote products derived from
- * this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
- * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
- * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
- * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
- (function( $ ){
- /**
- * @class LegacyTileSource
- * @classdesc The LegacyTileSource allows simple, traditional image pyramids to be loaded
- * into an OpenSeadragon Viewer. Basically, this translates to the historically
- * common practice of starting with a 'master' image, maybe a tiff for example,
- * and generating a set of 'service' images like one or more thumbnails, a medium
- * resolution image and a high resolution image in standard web formats like
- * png or jpg.
- *
- * @memberof OpenSeadragon
- * @extends OpenSeadragon.TileSource
- * @param {Array} levels An array of file descriptions, each is an object with
- * a 'url', a 'width', and a 'height'. Overriding classes can expect more
- * properties but these properties are sufficient for this implementation.
- * Additionally, the levels are required to be listed in order from
- * smallest to largest.
- * @property {Number} aspectRatio
- * @property {Number} dimensions
- * @property {Number} tileSize
- * @property {Number} tileOverlap
- * @property {Number} minLevel
- * @property {Number} maxLevel
- * @property {Array} levels
- */
- $.LegacyTileSource = function( levels ) {
- var options,
- width,
- height;
- if( $.isArray( levels ) ){
- options = {
- type: 'legacy-image-pyramid',
- levels: levels
- };
- }
- //clean up the levels to make sure we support all formats
- options.levels = filterFiles( options.levels );
- if ( options.levels.length > 0 ) {
- width = options.levels[ options.levels.length - 1 ].width;
- height = options.levels[ options.levels.length - 1 ].height;
- }
- else {
- width = 0;
- height = 0;
- $.console.error( "No supported image formats found" );
- }
- $.extend( true, options, {
- width: width,
- height: height,
- tileSize: Math.max( height, width ),
- tileOverlap: 0,
- minLevel: 0,
- maxLevel: options.levels.length > 0 ? options.levels.length - 1 : 0
- } );
- $.TileSource.apply( this, [ options ] );
- this.levels = options.levels;
- };
- $.extend( $.LegacyTileSource.prototype, $.TileSource.prototype, /** @lends OpenSeadragon.LegacyTileSource.prototype */{
- /**
- * Determine if the data and/or url imply the image service is supported by
- * this tile source.
- * @function
- * @param {Object|Array} data
- * @param {String} optional - url
- */
- supports: function( data, url ){
- return (
- data.type &&
- "legacy-image-pyramid" == data.type
- ) || (
- data.documentElement &&
- "legacy-image-pyramid" == data.documentElement.getAttribute('type')
- );
- },
- /**
- *
- * @function
- * @param {Object|XMLDocument} configuration - the raw configuration
- * @param {String} dataUrl - the url the data was retrieved from if any.
- * @return {Object} options - A dictionary of keyword arguments sufficient
- * to configure this tile sources constructor.
- */
- configure: function( configuration, dataUrl ){
- var options;
- if( !$.isPlainObject(configuration) ){
- options = configureFromXML( this, configuration );
- }else{
- options = configureFromObject( this, configuration );
- }
- return options;
- },
- /**
- * @function
- * @param {Number} level
- */
- getLevelScale: function ( level ) {
- var levelScale = NaN;
- if ( this.levels.length > 0 && level >= this.minLevel && level <= this.maxLevel ) {
- levelScale =
- this.levels[ level ].width /
- this.levels[ this.maxLevel ].width;
- }
- return levelScale;
- },
- /**
- * @function
- * @param {Number} level
- */
- getNumTiles: function( level ) {
- var scale = this.getLevelScale( level );
- if ( scale ){
- return new $.Point( 1, 1 );
- } else {
- return new $.Point( 0, 0 );
- }
- },
- /**
- * This method is not implemented by this class other than to throw an Error
- * announcing you have to implement it. Because of the variety of tile
- * server technologies, and various specifications for building image
- * pyramids, this method is here to allow easy integration.
- * @function
- * @param {Number} level
- * @param {Number} x
- * @param {Number} y
- * @throws {Error}
- */
- getTileUrl: function ( level, x, y ) {
- var url = null;
- if ( this.levels.length > 0 && level >= this.minLevel && level <= this.maxLevel ) {
- url = this.levels[ level ].url;
- }
- return url;
- }
- } );
- /**
- * This method removes any files from the Array which don't conform to our
- * basic requirements for a 'level' in the LegacyTileSource.
- * @private
- * @inner
- * @function
- */
- function filterFiles( files ){
- var filtered = [],
- file,
- i;
- for( i = 0; i < files.length; i++ ){
- file = files[ i ];
- if( file.height &&
- file.width &&
- file.url ){
- //This is sufficient to serve as a level
- filtered.push({
- url: file.url,
- width: Number( file.width ),
- height: Number( file.height )
- });
- }
- else {
- $.console.error( 'Unsupported image format: %s', file.url ? file.url : '<no URL>' );
- }
- }
- return filtered.sort(function(a, b) {
- return a.height - b.height;
- });
- }
- /**
- * @private
- * @inner
- * @function
- */
- function configureFromXML( tileSource, xmlDoc ){
- if ( !xmlDoc || !xmlDoc.documentElement ) {
- throw new Error( $.getString( "Errors.Xml" ) );
- }
- var root = xmlDoc.documentElement,
- rootName = root.tagName,
- conf = null,
- levels = [],
- level,
- i;
- if ( rootName == "image" ) {
- try {
- conf = {
- type: root.getAttribute( "type" ),
- levels: []
- };
- levels = root.getElementsByTagName( "level" );
- for ( i = 0; i < levels.length; i++ ) {
- level = levels[ i ];
- conf.levels.push({
- url: level.getAttribute( "url" ),
- width: parseInt( level.getAttribute( "width" ), 10 ),
- height: parseInt( level.getAttribute( "height" ), 10 )
- });
- }
- return configureFromObject( tileSource, conf );
- } catch ( e ) {
- throw (e instanceof Error) ?
- e :
- new Error( 'Unknown error parsing Legacy Image Pyramid XML.' );
- }
- } else if ( rootName == "collection" ) {
- throw new Error( 'Legacy Image Pyramid Collections not yet supported.' );
- } else if ( rootName == "error" ) {
- throw new Error( 'Error: ' + xmlDoc );
- }
- throw new Error( 'Unknown element ' + rootName );
- }
- /**
- * @private
- * @inner
- * @function
- */
- function configureFromObject( tileSource, configuration ){
- return configuration.levels;
- }
- }( OpenSeadragon ));
- /*
- * OpenSeadragon - ImageTileSource
- *
- * Copyright (C) 2009 CodePlex Foundation
- * Copyright (C) 2010-2013 OpenSeadragon contributors
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are
- * met:
- *
- * - Redistributions of source code must retain the above copyright notice,
- * this list of conditions and the following disclaimer.
- *
- * - Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * - Neither the name of CodePlex Foundation nor the names of its
- * contributors may be used to endorse or promote products derived from
- * this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
- * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
- * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
- * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
- (function ($) {
- /**
- * @class ImageTileSource
- * @classdesc The ImageTileSource allows a simple image to be loaded
- * into an OpenSeadragon Viewer.
- * There are 2 ways to open an ImageTileSource:
- * 1. viewer.open({type: 'image', url: fooUrl});
- * 2. viewer.open(new OpenSeadragon.ImageTileSource({url: fooUrl}));
- *
- * With the first syntax, the crossOriginPolicy, ajaxWithCredentials and
- * useCanvas options are inherited from the viewer if they are not
- * specified directly in the options object.
- *
- * @memberof OpenSeadragon
- * @extends OpenSeadragon.TileSource
- * @param {Object} options Options object.
- * @param {String} options.url URL of the image
- * @param {Boolean} [options.buildPyramid=true] If set to true (default), a
- * pyramid will be built internally to provide a better downsampling.
- * @param {String|Boolean} [options.crossOriginPolicy=false] Valid values are
- * 'Anonymous', 'use-credentials', and false. If false, image requests will
- * not use CORS preventing internal pyramid building for images from other
- * domains.
- * @param {String|Boolean} [options.ajaxWithCredentials=false] Whether to set
- * the withCredentials XHR flag for AJAX requests (when loading tile sources).
- * @param {Boolean} [options.useCanvas=true] Set to false to prevent any use
- * of the canvas API.
- */
- $.ImageTileSource = function (options) {
- options = $.extend({
- buildPyramid: true,
- crossOriginPolicy: false,
- ajaxWithCredentials: false,
- useCanvas: true
- }, options);
- $.TileSource.apply(this, [options]);
- };
- $.extend($.ImageTileSource.prototype, $.TileSource.prototype, /** @lends OpenSeadragon.ImageTileSource.prototype */{
- /**
- * Determine if the data and/or url imply the image service is supported by
- * this tile source.
- * @function
- * @param {Object|Array} data
- * @param {String} optional - url
- */
- supports: function (data, url) {
- return data.type && data.type === "image";
- },
- /**
- *
- * @function
- * @param {Object} options - the options
- * @param {String} dataUrl - the url the image was retrieved from, if any.
- * @return {Object} options - A dictionary of keyword arguments sufficient
- * to configure this tile sources constructor.
- */
- configure: function (options, dataUrl) {
- return options;
- },
- /**
- * Responsible for retrieving, and caching the
- * image metadata pertinent to this TileSources implementation.
- * @function
- * @param {String} url
- * @throws {Error}
- */
- getImageInfo: function (url) {
- var image = this._image = new Image();
- var _this = this;
- if (this.crossOriginPolicy) {
- image.crossOrigin = this.crossOriginPolicy;
- }
- if (this.ajaxWithCredentials) {
- image.useCredentials = this.ajaxWithCredentials;
- }
- $.addEvent(image, 'load', function () {
- /* IE8 fix since it has no naturalWidth and naturalHeight */
- _this.width = Object.prototype.hasOwnProperty.call(image, 'naturalWidth') ? image.naturalWidth : image.width;
- _this.height = Object.prototype.hasOwnProperty.call(image, 'naturalHeight') ? image.naturalHeight : image.height;
- _this.aspectRatio = _this.width / _this.height;
- _this.dimensions = new $.Point(_this.width, _this.height);
- _this._tileWidth = _this.width;
- _this._tileHeight = _this.height;
- _this.tileOverlap = 0;
- _this.minLevel = 0;
- _this.levels = _this._buildLevels();
- _this.maxLevel = _this.levels.length - 1;
- _this.ready = true;
- // Note: this event is documented elsewhere, in TileSource
- _this.raiseEvent('ready', {tileSource: _this});
- });
- $.addEvent(image, 'error', function () {
- // Note: this event is documented elsewhere, in TileSource
- _this.raiseEvent('open-failed', {
- message: "Error loading image at " + url,
- source: url
- });
- });
- image.src = url;
- },
- /**
- * @function
- * @param {Number} level
- */
- getLevelScale: function (level) {
- var levelScale = NaN;
- if (level >= this.minLevel && level <= this.maxLevel) {
- levelScale =
- this.levels[level].width /
- this.levels[this.maxLevel].width;
- }
- return levelScale;
- },
- /**
- * @function
- * @param {Number} level
- */
- getNumTiles: function (level) {
- var scale = this.getLevelScale(level);
- if (scale) {
- return new $.Point(1, 1);
- } else {
- return new $.Point(0, 0);
- }
- },
- /**
- * Retrieves a tile url
- * @function
- * @param {Number} level Level of the tile
- * @param {Number} x x coordinate of the tile
- * @param {Number} y y coordinate of the tile
- */
- getTileUrl: function (level, x, y) {
- var url = null;
- if (level >= this.minLevel && level <= this.maxLevel) {
- url = this.levels[level].url;
- }
- return url;
- },
- /**
- * Retrieves a tile context 2D
- * @function
- * @param {Number} level Level of the tile
- * @param {Number} x x coordinate of the tile
- * @param {Number} y y coordinate of the tile
- */
- getContext2D: function (level, x, y) {
- var context = null;
- if (level >= this.minLevel && level <= this.maxLevel) {
- context = this.levels[level].context2D;
- }
- return context;
- },
- // private
- //
- // Builds the different levels of the pyramid if possible
- // (i.e. if canvas API enabled and no canvas tainting issue).
- _buildLevels: function () {
- var levels = [{
- url: this._image.src,
- /* IE8 fix since it has no naturalWidth and naturalHeight */
- width: Object.prototype.hasOwnProperty.call(this._image, 'naturalWidth') ? this._image.naturalWidth : this._image.width,
- height: Object.prototype.hasOwnProperty.call(this._image, 'naturalHeight') ? this._image.naturalHeight : this._image.height
- }];
- if (!this.buildPyramid || !$.supportsCanvas || !this.useCanvas) {
- // We don't need the image anymore. Allows it to be GC.
- delete this._image;
- return levels;
- }
- /* IE8 fix since it has no naturalWidth and naturalHeight */
- var currentWidth = Object.prototype.hasOwnProperty.call(this._image, 'naturalWidth') ? this._image.naturalWidth : this._image.width;
- var currentHeight = Object.prototype.hasOwnProperty.call(this._image, 'naturalHeight') ? this._image.naturalHeight : this._image.height;
- var bigCanvas = document.createElement("canvas");
- var bigContext = bigCanvas.getContext("2d");
- bigCanvas.width = currentWidth;
- bigCanvas.height = currentHeight;
- bigContext.drawImage(this._image, 0, 0, currentWidth, currentHeight);
- // We cache the context of the highest level because the browser
- // is a lot faster at downsampling something it already has
- // downsampled before.
- levels[0].context2D = bigContext;
- // We don't need the image anymore. Allows it to be GC.
- delete this._image;
- if ($.isCanvasTainted(bigCanvas)) {
- // If the canvas is tainted, we can't compute the pyramid.
- return levels;
- }
- // We build smaller levels until either width or height becomes
- // 1 pixel wide.
- while (currentWidth >= 2 && currentHeight >= 2) {
- currentWidth = Math.floor(currentWidth / 2);
- currentHeight = Math.floor(currentHeight / 2);
- var smallCanvas = document.createElement("canvas");
- var smallContext = smallCanvas.getContext("2d");
- smallCanvas.width = currentWidth;
- smallCanvas.height = currentHeight;
- smallContext.drawImage(bigCanvas, 0, 0, currentWidth, currentHeight);
- levels.splice(0, 0, {
- context2D: smallContext,
- width: currentWidth,
- height: currentHeight
- });
- bigCanvas = smallCanvas;
- bigContext = smallContext;
- }
- return levels;
- }
- });
- }(OpenSeadragon));
- /*
- * OpenSeadragon - TileSourceCollection
- *
- * Copyright (C) 2009 CodePlex Foundation
- * Copyright (C) 2010-2013 OpenSeadragon contributors
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are
- * met:
- *
- * - Redistributions of source code must retain the above copyright notice,
- * this list of conditions and the following disclaimer.
- *
- * - Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * - Neither the name of CodePlex Foundation nor the names of its
- * contributors may be used to endorse or promote products derived from
- * this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
- * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
- * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
- * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
- (function($) {
- // deprecated
- $.TileSourceCollection = function(tileSize, tileSources, rows, layout) {
- $.console.error('TileSourceCollection is deprecated; use World instead');
- };
- }(OpenSeadragon));
- /*
- * OpenSeadragon - Button
- *
- * Copyright (C) 2009 CodePlex Foundation
- * Copyright (C) 2010-2013 OpenSeadragon contributors
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are
- * met:
- *
- * - Redistributions of source code must retain the above copyright notice,
- * this list of conditions and the following disclaimer.
- *
- * - Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * - Neither the name of CodePlex Foundation nor the names of its
- * contributors may be used to endorse or promote products derived from
- * this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
- * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
- * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
- * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
- (function( $ ){
- /**
- * An enumeration of button states
- * @member ButtonState
- * @memberof OpenSeadragon
- * @static
- * @type {Object}
- * @property {Number} REST
- * @property {Number} GROUP
- * @property {Number} HOVER
- * @property {Number} DOWN
- */
- $.ButtonState = {
- REST: 0,
- GROUP: 1,
- HOVER: 2,
- DOWN: 3
- };
- /**
- * @class Button
- * @classdesc Manages events, hover states for individual buttons, tool-tips, as well
- * as fading the buttons out when the user has not interacted with them
- * for a specified period.
- *
- * @memberof OpenSeadragon
- * @extends OpenSeadragon.EventSource
- * @param {Object} options
- * @param {Element} [options.element=null] Element to use as the button. If not specified, an HTML <div> element is created.
- * @param {String} [options.tooltip=null] Provides context help for the button when the
- * user hovers over it.
- * @param {String} [options.srcRest=null] URL of image to use in 'rest' state.
- * @param {String} [options.srcGroup=null] URL of image to use in 'up' state.
- * @param {String} [options.srcHover=null] URL of image to use in 'hover' state.
- * @param {String} [options.srcDown=null] URL of image to use in 'down' state.
- * @param {Number} [options.fadeDelay=0] How long to wait before fading.
- * @param {Number} [options.fadeLength=2000] How long should it take to fade the button.
- * @param {OpenSeadragon.EventHandler} [options.onPress=null] Event handler callback for {@link OpenSeadragon.Button.event:press}.
- * @param {OpenSeadragon.EventHandler} [options.onRelease=null] Event handler callback for {@link OpenSeadragon.Button.event:release}.
- * @param {OpenSeadragon.EventHandler} [options.onClick=null] Event handler callback for {@link OpenSeadragon.Button.event:click}.
- * @param {OpenSeadragon.EventHandler} [options.onEnter=null] Event handler callback for {@link OpenSeadragon.Button.event:enter}.
- * @param {OpenSeadragon.EventHandler} [options.onExit=null] Event handler callback for {@link OpenSeadragon.Button.event:exit}.
- * @param {OpenSeadragon.EventHandler} [options.onFocus=null] Event handler callback for {@link OpenSeadragon.Button.event:focus}.
- * @param {OpenSeadragon.EventHandler} [options.onBlur=null] Event handler callback for {@link OpenSeadragon.Button.event:blur}.
- */
- $.Button = function( options ) {
- var _this = this;
- $.EventSource.call( this );
- $.extend( true, this, {
- tooltip: null,
- srcRest: null,
- srcGroup: null,
- srcHover: null,
- srcDown: null,
- clickTimeThreshold: $.DEFAULT_SETTINGS.clickTimeThreshold,
- clickDistThreshold: $.DEFAULT_SETTINGS.clickDistThreshold,
- /**
- * How long to wait before fading.
- * @member {Number} fadeDelay
- * @memberof OpenSeadragon.Button#
- */
- fadeDelay: 0,
- /**
- * How long should it take to fade the button.
- * @member {Number} fadeLength
- * @memberof OpenSeadragon.Button#
- */
- fadeLength: 2000,
- onPress: null,
- onRelease: null,
- onClick: null,
- onEnter: null,
- onExit: null,
- onFocus: null,
- onBlur: null
- }, options );
- /**
- * The button element.
- * @member {Element} element
- * @memberof OpenSeadragon.Button#
- */
- this.element = options.element || $.makeNeutralElement("div");
- //if the user has specified the element to bind the control to explicitly
- //then do not add the default control images
- if ( !options.element ) {
- this.imgRest = $.makeTransparentImage( this.srcRest );
- this.imgGroup = $.makeTransparentImage( this.srcGroup );
- this.imgHover = $.makeTransparentImage( this.srcHover );
- this.imgDown = $.makeTransparentImage( this.srcDown );
- this.imgRest.alt =
- this.imgGroup.alt =
- this.imgHover.alt =
- this.imgDown.alt =
- this.tooltip;
- this.element.style.position = "relative";
- $.setElementTouchActionNone( this.element );
- this.imgGroup.style.position =
- this.imgHover.style.position =
- this.imgDown.style.position =
- "absolute";
- this.imgGroup.style.top =
- this.imgHover.style.top =
- this.imgDown.style.top =
- "0px";
- this.imgGroup.style.left =
- this.imgHover.style.left =
- this.imgDown.style.left =
- "0px";
- this.imgHover.style.visibility =
- this.imgDown.style.visibility =
- "hidden";
- if ($.Browser.vendor == $.BROWSERS.FIREFOX && $.Browser.version < 3) {
- this.imgGroup.style.top =
- this.imgHover.style.top =
- this.imgDown.style.top =
- "";
- }
- this.element.appendChild( this.imgRest );
- this.element.appendChild( this.imgGroup );
- this.element.appendChild( this.imgHover );
- this.element.appendChild( this.imgDown );
- }
- this.addHandler("press", this.onPress);
- this.addHandler("release", this.onRelease);
- this.addHandler("click", this.onClick);
- this.addHandler("enter", this.onEnter);
- this.addHandler("exit", this.onExit);
- this.addHandler("focus", this.onFocus);
- this.addHandler("blur", this.onBlur);
- /**
- * The button's current state.
- * @member {OpenSeadragon.ButtonState} currentState
- * @memberof OpenSeadragon.Button#
- */
- this.currentState = $.ButtonState.GROUP;
- // When the button last began to fade.
- this.fadeBeginTime = null;
- // Whether this button should fade after user stops interacting with the viewport.
- this.shouldFade = false;
- this.element.style.display = "inline-block";
- this.element.style.position = "relative";
- this.element.title = this.tooltip;
- /**
- * Tracks mouse/touch/key events on the button.
- * @member {OpenSeadragon.MouseTracker} tracker
- * @memberof OpenSeadragon.Button#
- */
- this.tracker = new $.MouseTracker({
- element: this.element,
- clickTimeThreshold: this.clickTimeThreshold,
- clickDistThreshold: this.clickDistThreshold,
- enterHandler: function( event ) {
- if ( event.insideElementPressed ) {
- inTo( _this, $.ButtonState.DOWN );
- /**
- * Raised when the cursor enters the Button element.
- *
- * @event enter
- * @memberof OpenSeadragon.Button
- * @type {object}
- * @property {OpenSeadragon.Button} eventSource - A reference to the Button which raised the event.
- * @property {Object} originalEvent - The original DOM event.
- * @property {?Object} userData - Arbitrary subscriber-defined object.
- */
- _this.raiseEvent( "enter", { originalEvent: event.originalEvent } );
- } else if ( !event.buttonDownAny ) {
- inTo( _this, $.ButtonState.HOVER );
- }
- },
- focusHandler: function ( event ) {
- this.enterHandler( event );
- /**
- * Raised when the Button element receives focus.
- *
- * @event focus
- * @memberof OpenSeadragon.Button
- * @type {object}
- * @property {OpenSeadragon.Button} eventSource - A reference to the Button which raised the event.
- * @property {Object} originalEvent - The original DOM event.
- * @property {?Object} userData - Arbitrary subscriber-defined object.
- */
- _this.raiseEvent( "focus", { originalEvent: event.originalEvent } );
- },
- exitHandler: function( event ) {
- outTo( _this, $.ButtonState.GROUP );
- if ( event.insideElementPressed ) {
- /**
- * Raised when the cursor leaves the Button element.
- *
- * @event exit
- * @memberof OpenSeadragon.Button
- * @type {object}
- * @property {OpenSeadragon.Button} eventSource - A reference to the Button which raised the event.
- * @property {Object} originalEvent - The original DOM event.
- * @property {?Object} userData - Arbitrary subscriber-defined object.
- */
- _this.raiseEvent( "exit", { originalEvent: event.originalEvent } );
- }
- },
- blurHandler: function ( event ) {
- this.exitHandler( event );
- /**
- * Raised when the Button element loses focus.
- *
- * @event blur
- * @memberof OpenSeadragon.Button
- * @type {object}
- * @property {OpenSeadragon.Button} eventSource - A reference to the Button which raised the event.
- * @property {Object} originalEvent - The original DOM event.
- * @property {?Object} userData - Arbitrary subscriber-defined object.
- */
- _this.raiseEvent( "blur", { originalEvent: event.originalEvent } );
- },
- pressHandler: function ( event ) {
- inTo( _this, $.ButtonState.DOWN );
- /**
- * Raised when a mouse button is pressed or touch occurs in the Button element.
- *
- * @event press
- * @memberof OpenSeadragon.Button
- * @type {object}
- * @property {OpenSeadragon.Button} eventSource - A reference to the Button which raised the event.
- * @property {Object} originalEvent - The original DOM event.
- * @property {?Object} userData - Arbitrary subscriber-defined object.
- */
- _this.raiseEvent( "press", { originalEvent: event.originalEvent } );
- },
- releaseHandler: function( event ) {
- if ( event.insideElementPressed && event.insideElementReleased ) {
- outTo( _this, $.ButtonState.HOVER );
- /**
- * Raised when the mouse button is released or touch ends in the Button element.
- *
- * @event release
- * @memberof OpenSeadragon.Button
- * @type {object}
- * @property {OpenSeadragon.Button} eventSource - A reference to the Button which raised the event.
- * @property {Object} originalEvent - The original DOM event.
- * @property {?Object} userData - Arbitrary subscriber-defined object.
- */
- _this.raiseEvent( "release", { originalEvent: event.originalEvent } );
- } else if ( event.insideElementPressed ) {
- outTo( _this, $.ButtonState.GROUP );
- } else {
- inTo( _this, $.ButtonState.HOVER );
- }
- },
- clickHandler: function( event ) {
- if ( event.quick ) {
- /**
- * Raised when a mouse button is pressed and released or touch is initiated and ended in the Button element within the time and distance threshold.
- *
- * @event click
- * @memberof OpenSeadragon.Button
- * @type {object}
- * @property {OpenSeadragon.Button} eventSource - A reference to the Button which raised the event.
- * @property {Object} originalEvent - The original DOM event.
- * @property {?Object} userData - Arbitrary subscriber-defined object.
- */
- _this.raiseEvent("click", { originalEvent: event.originalEvent });
- }
- },
- keyHandler: function( event ){
- //console.log( "%s : handling key %s!", _this.tooltip, event.keyCode);
- if( 13 === event.keyCode ){
- /***
- * Raised when a mouse button is pressed and released or touch is initiated and ended in the Button element within the time and distance threshold.
- *
- * @event click
- * @memberof OpenSeadragon.Button
- * @type {object}
- * @property {OpenSeadragon.Button} eventSource - A reference to the Button which raised the event.
- * @property {Object} originalEvent - The original DOM event.
- * @property {?Object} userData - Arbitrary subscriber-defined object.
- */
- _this.raiseEvent( "click", { originalEvent: event.originalEvent } );
- /***
- * Raised when the mouse button is released or touch ends in the Button element.
- *
- * @event release
- * @memberof OpenSeadragon.Button
- * @type {object}
- * @property {OpenSeadragon.Button} eventSource - A reference to the Button which raised the event.
- * @property {Object} originalEvent - The original DOM event.
- * @property {?Object} userData - Arbitrary subscriber-defined object.
- */
- _this.raiseEvent( "release", { originalEvent: event.originalEvent } );
- return false;
- }
- return true;
- }
- });
- outTo( this, $.ButtonState.REST );
- };
- $.extend( $.Button.prototype, $.EventSource.prototype, /** @lends OpenSeadragon.Button.prototype */{
- /**
- * TODO: Determine what this function is intended to do and if it's actually
- * useful as an API point.
- * @function
- */
- notifyGroupEnter: function() {
- inTo( this, $.ButtonState.GROUP );
- },
- /**
- * TODO: Determine what this function is intended to do and if it's actually
- * useful as an API point.
- * @function
- */
- notifyGroupExit: function() {
- outTo( this, $.ButtonState.REST );
- },
- /**
- * @function
- */
- disable: function(){
- this.notifyGroupExit();
- this.element.disabled = true;
- $.setElementOpacity( this.element, 0.2, true );
- },
- /**
- * @function
- */
- enable: function(){
- this.element.disabled = false;
- $.setElementOpacity( this.element, 1.0, true );
- this.notifyGroupEnter();
- }
- });
- function scheduleFade( button ) {
- $.requestAnimationFrame(function(){
- updateFade( button );
- });
- }
- function updateFade( button ) {
- var currentTime,
- deltaTime,
- opacity;
- if ( button.shouldFade ) {
- currentTime = $.now();
- deltaTime = currentTime - button.fadeBeginTime;
- opacity = 1.0 - deltaTime / button.fadeLength;
- opacity = Math.min( 1.0, opacity );
- opacity = Math.max( 0.0, opacity );
- if( button.imgGroup ){
- $.setElementOpacity( button.imgGroup, opacity, true );
- }
- if ( opacity > 0 ) {
- // fade again
- scheduleFade( button );
- }
- }
- }
- function beginFading( button ) {
- button.shouldFade = true;
- button.fadeBeginTime = $.now() + button.fadeDelay;
- window.setTimeout( function(){
- scheduleFade( button );
- }, button.fadeDelay );
- }
- function stopFading( button ) {
- button.shouldFade = false;
- if( button.imgGroup ){
- $.setElementOpacity( button.imgGroup, 1.0, true );
- }
- }
- function inTo( button, newState ) {
- if( button.element.disabled ){
- return;
- }
- if ( newState >= $.ButtonState.GROUP &&
- button.currentState == $.ButtonState.REST ) {
- stopFading( button );
- button.currentState = $.ButtonState.GROUP;
- }
- if ( newState >= $.ButtonState.HOVER &&
- button.currentState == $.ButtonState.GROUP ) {
- if( button.imgHover ){
- button.imgHover.style.visibility = "";
- }
- button.currentState = $.ButtonState.HOVER;
- }
- if ( newState >= $.ButtonState.DOWN &&
- button.currentState == $.ButtonState.HOVER ) {
- if( button.imgDown ){
- button.imgDown.style.visibility = "";
- }
- button.currentState = $.ButtonState.DOWN;
- }
- }
- function outTo( button, newState ) {
- if( button.element.disabled ){
- return;
- }
- if ( newState <= $.ButtonState.HOVER &&
- button.currentState == $.ButtonState.DOWN ) {
- if( button.imgDown ){
- button.imgDown.style.visibility = "hidden";
- }
- button.currentState = $.ButtonState.HOVER;
- }
- if ( newState <= $.ButtonState.GROUP &&
- button.currentState == $.ButtonState.HOVER ) {
- if( button.imgHover ){
- button.imgHover.style.visibility = "hidden";
- }
- button.currentState = $.ButtonState.GROUP;
- }
- if ( newState <= $.ButtonState.REST &&
- button.currentState == $.ButtonState.GROUP ) {
- beginFading( button );
- button.currentState = $.ButtonState.REST;
- }
- }
- }( OpenSeadragon ));
- /*
- * OpenSeadragon - ButtonGroup
- *
- * Copyright (C) 2009 CodePlex Foundation
- * Copyright (C) 2010-2013 OpenSeadragon contributors
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are
- * met:
- *
- * - Redistributions of source code must retain the above copyright notice,
- * this list of conditions and the following disclaimer.
- *
- * - Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * - Neither the name of CodePlex Foundation nor the names of its
- * contributors may be used to endorse or promote products derived from
- * this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
- * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
- * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
- * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
- (function( $ ){
- /**
- * @class ButtonGroup
- * @classdesc Manages events on groups of buttons.
- *
- * @memberof OpenSeadragon
- * @param {Object} options - A dictionary of settings applied against the entire group of buttons.
- * @param {Array} options.buttons Array of buttons
- * @param {Element} [options.element] Element to use as the container
- **/
- $.ButtonGroup = function( options ) {
- $.extend( true, this, {
- /**
- * An array containing the buttons themselves.
- * @member {Array} buttons
- * @memberof OpenSeadragon.ButtonGroup#
- */
- buttons: [],
- clickTimeThreshold: $.DEFAULT_SETTINGS.clickTimeThreshold,
- clickDistThreshold: $.DEFAULT_SETTINGS.clickDistThreshold,
- labelText: ""
- }, options );
- // copy the button elements TODO: Why?
- var buttons = this.buttons.concat([]),
- _this = this,
- i;
- /**
- * The shared container for the buttons.
- * @member {Element} element
- * @memberof OpenSeadragon.ButtonGroup#
- */
- this.element = options.element || $.makeNeutralElement( "div" );
- // TODO What if there IS an options.group specified?
- if( !options.group ){
- this.element.style.display = "inline-block";
- //this.label = $.makeNeutralElement( "label" );
- //TODO: support labels for ButtonGroups
- //this.label.innerHTML = this.labelText;
- //this.element.appendChild( this.label );
- for ( i = 0; i < buttons.length; i++ ) {
- this.element.appendChild( buttons[ i ].element );
- }
- }
- $.setElementTouchActionNone( this.element );
- /**
- * Tracks mouse/touch/key events across the group of buttons.
- * @member {OpenSeadragon.MouseTracker} tracker
- * @memberof OpenSeadragon.ButtonGroup#
- */
- this.tracker = new $.MouseTracker({
- element: this.element,
- clickTimeThreshold: this.clickTimeThreshold,
- clickDistThreshold: this.clickDistThreshold,
- enterHandler: function ( event ) {
- var i;
- for ( i = 0; i < _this.buttons.length; i++ ) {
- _this.buttons[ i ].notifyGroupEnter();
- }
- },
- exitHandler: function ( event ) {
- var i;
- if ( !event.insideElementPressed ) {
- for ( i = 0; i < _this.buttons.length; i++ ) {
- _this.buttons[ i ].notifyGroupExit();
- }
- }
- },
- });
- };
- /** @lends OpenSeadragon.ButtonGroup.prototype */
- $.ButtonGroup.prototype = {
- /**
- * TODO: Figure out why this is used on the public API and if a more useful
- * api can be created.
- * @function
- * @private
- */
- emulateEnter: function() {
- this.tracker.enterHandler( { eventSource: this.tracker } );
- },
- /**
- * TODO: Figure out why this is used on the public API and if a more useful
- * api can be created.
- * @function
- * @private
- */
- emulateExit: function() {
- this.tracker.exitHandler( { eventSource: this.tracker } );
- }
- };
- }( OpenSeadragon ));
- /*
- * OpenSeadragon - Rect
- *
- * Copyright (C) 2009 CodePlex Foundation
- * Copyright (C) 2010-2013 OpenSeadragon contributors
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are
- * met:
- *
- * - Redistributions of source code must retain the above copyright notice,
- * this list of conditions and the following disclaimer.
- *
- * - Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * - Neither the name of CodePlex Foundation nor the names of its
- * contributors may be used to endorse or promote products derived from
- * this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
- * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
- * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
- * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
- (function($) {
- /**
- * @class Rect
- * @classdesc A Rectangle is described by it top left coordinates (x, y), width,
- * height and degrees of rotation around (x, y).
- * Note that the coordinate system used is the one commonly used with images:
- * x increases when going to the right
- * y increases when going to the bottom
- * degrees increases clockwise with 0 being the horizontal
- *
- * The constructor normalizes the rectangle to always have 0 <= degrees < 90
- *
- * @memberof OpenSeadragon
- * @param {Number} [x=0] The vector component 'x'.
- * @param {Number} [y=0] The vector component 'y'.
- * @param {Number} [width=0] The vector component 'width'.
- * @param {Number} [height=0] The vector component 'height'.
- * @param {Number} [degrees=0] Rotation of the rectangle around (x,y) in degrees.
- */
- $.Rect = function(x, y, width, height, degrees) {
- /**
- * The vector component 'x'.
- * @member {Number} x
- * @memberof OpenSeadragon.Rect#
- */
- this.x = typeof (x) === "number" ? x : 0;
- /**
- * The vector component 'y'.
- * @member {Number} y
- * @memberof OpenSeadragon.Rect#
- */
- this.y = typeof (y) === "number" ? y : 0;
- /**
- * The vector component 'width'.
- * @member {Number} width
- * @memberof OpenSeadragon.Rect#
- */
- this.width = typeof (width) === "number" ? width : 0;
- /**
- * The vector component 'height'.
- * @member {Number} height
- * @memberof OpenSeadragon.Rect#
- */
- this.height = typeof (height) === "number" ? height : 0;
- this.degrees = typeof (degrees) === "number" ? degrees : 0;
- // Normalizes the rectangle.
- this.degrees = $.positiveModulo(this.degrees, 360);
- var newTopLeft, newWidth;
- if (this.degrees >= 270) {
- newTopLeft = this.getTopRight();
- this.x = newTopLeft.x;
- this.y = newTopLeft.y;
- newWidth = this.height;
- this.height = this.width;
- this.width = newWidth;
- this.degrees -= 270;
- } else if (this.degrees >= 180) {
- newTopLeft = this.getBottomRight();
- this.x = newTopLeft.x;
- this.y = newTopLeft.y;
- this.degrees -= 180;
- } else if (this.degrees >= 90) {
- newTopLeft = this.getBottomLeft();
- this.x = newTopLeft.x;
- this.y = newTopLeft.y;
- newWidth = this.height;
- this.height = this.width;
- this.width = newWidth;
- this.degrees -= 90;
- }
- };
- /**
- * Builds a rectangle having the 3 specified points as summits.
- * @static
- * @memberof OpenSeadragon.Rect
- * @param {OpenSeadragon.Point} topLeft
- * @param {OpenSeadragon.Point} topRight
- * @param {OpenSeadragon.Point} bottomLeft
- * @returns {OpenSeadragon.Rect}
- */
- $.Rect.fromSummits = function(topLeft, topRight, bottomLeft) {
- var width = topLeft.distanceTo(topRight);
- var height = topLeft.distanceTo(bottomLeft);
- var diff = topRight.minus(topLeft);
- var radians = Math.atan(diff.y / diff.x);
- if (diff.x < 0) {
- radians += Math.PI;
- } else if (diff.y < 0) {
- radians += 2 * Math.PI;
- }
- return new $.Rect(
- topLeft.x,
- topLeft.y,
- width,
- height,
- radians / Math.PI * 180);
- };
- /** @lends OpenSeadragon.Rect.prototype */
- $.Rect.prototype = {
- /**
- * @function
- * @returns {OpenSeadragon.Rect} a duplicate of this Rect
- */
- clone: function() {
- return new $.Rect(
- this.x,
- this.y,
- this.width,
- this.height,
- this.degrees);
- },
- /**
- * The aspect ratio is simply the ratio of width to height.
- * @function
- * @returns {Number} The ratio of width to height.
- */
- getAspectRatio: function() {
- return this.width / this.height;
- },
- /**
- * Provides the coordinates of the upper-left corner of the rectangle as a
- * point.
- * @function
- * @returns {OpenSeadragon.Point} The coordinate of the upper-left corner of
- * the rectangle.
- */
- getTopLeft: function() {
- return new $.Point(
- this.x,
- this.y
- );
- },
- /**
- * Provides the coordinates of the bottom-right corner of the rectangle as a
- * point.
- * @function
- * @returns {OpenSeadragon.Point} The coordinate of the bottom-right corner of
- * the rectangle.
- */
- getBottomRight: function() {
- return new $.Point(this.x + this.width, this.y + this.height)
- .rotate(this.degrees, this.getTopLeft());
- },
- /**
- * Provides the coordinates of the top-right corner of the rectangle as a
- * point.
- * @function
- * @returns {OpenSeadragon.Point} The coordinate of the top-right corner of
- * the rectangle.
- */
- getTopRight: function() {
- return new $.Point(this.x + this.width, this.y)
- .rotate(this.degrees, this.getTopLeft());
- },
- /**
- * Provides the coordinates of the bottom-left corner of the rectangle as a
- * point.
- * @function
- * @returns {OpenSeadragon.Point} The coordinate of the bottom-left corner of
- * the rectangle.
- */
- getBottomLeft: function() {
- return new $.Point(this.x, this.y + this.height)
- .rotate(this.degrees, this.getTopLeft());
- },
- /**
- * Computes the center of the rectangle.
- * @function
- * @returns {OpenSeadragon.Point} The center of the rectangle as represented
- * as represented by a 2-dimensional vector (x,y)
- */
- getCenter: function() {
- return new $.Point(
- this.x + this.width / 2.0,
- this.y + this.height / 2.0
- ).rotate(this.degrees, this.getTopLeft());
- },
- /**
- * Returns the width and height component as a vector OpenSeadragon.Point
- * @function
- * @returns {OpenSeadragon.Point} The 2 dimensional vector representing the
- * the width and height of the rectangle.
- */
- getSize: function() {
- return new $.Point(this.width, this.height);
- },
- /**
- * Determines if two Rectangles have equivalent components.
- * @function
- * @param {OpenSeadragon.Rect} rectangle The Rectangle to compare to.
- * @return {Boolean} 'true' if all components are equal, otherwise 'false'.
- */
- equals: function(other) {
- return (other instanceof $.Rect) &&
- this.x === other.x &&
- this.y === other.y &&
- this.width === other.width &&
- this.height === other.height &&
- this.degrees === other.degrees;
- },
- /**
- * Multiply all dimensions (except degrees) in this Rect by a factor and
- * return a new Rect.
- * @function
- * @param {Number} factor The factor to multiply vector components.
- * @returns {OpenSeadragon.Rect} A new rect representing the multiplication
- * of the vector components by the factor
- */
- times: function(factor) {
- return new $.Rect(
- this.x * factor,
- this.y * factor,
- this.width * factor,
- this.height * factor,
- this.degrees);
- },
- /**
- * Translate/move this Rect by a vector and return new Rect.
- * @function
- * @param {OpenSeadragon.Point} delta The translation vector.
- * @returns {OpenSeadragon.Rect} A new rect with altered position
- */
- translate: function(delta) {
- return new $.Rect(
- this.x + delta.x,
- this.y + delta.y,
- this.width,
- this.height,
- this.degrees);
- },
- /**
- * Returns the smallest rectangle that will contain this and the given
- * rectangle bounding boxes.
- * @param {OpenSeadragon.Rect} rect
- * @return {OpenSeadragon.Rect} The new rectangle.
- */
- union: function(rect) {
- var thisBoundingBox = this.getBoundingBox();
- var otherBoundingBox = rect.getBoundingBox();
- var left = Math.min(thisBoundingBox.x, otherBoundingBox.x);
- var top = Math.min(thisBoundingBox.y, otherBoundingBox.y);
- var right = Math.max(
- thisBoundingBox.x + thisBoundingBox.width,
- otherBoundingBox.x + otherBoundingBox.width);
- var bottom = Math.max(
- thisBoundingBox.y + thisBoundingBox.height,
- otherBoundingBox.y + otherBoundingBox.height);
- return new $.Rect(
- left,
- top,
- right - left,
- bottom - top);
- },
- /**
- * Returns the bounding box of the intersection of this rectangle with the
- * given rectangle.
- * @param {OpenSeadragon.Rect} rect
- * @return {OpenSeadragon.Rect} the bounding box of the intersection
- * or null if the rectangles don't intersect.
- */
- intersection: function(rect) {
- // Simplified version of Weiler Atherton clipping algorithm
- // https://en.wikipedia.org/wiki/Weiler%E2%80%93Atherton_clipping_algorithm
- // Because we just want the bounding box of the intersection,
- // we can just compute the bounding box of:
- // 1. all the summits of this which are inside rect
- // 2. all the summits of rect which are inside this
- // 3. all the intersections of rect and this
- var EPSILON = 0.0000000001;
- var intersectionPoints = [];
- var thisTopLeft = this.getTopLeft();
- if (rect.containsPoint(thisTopLeft, EPSILON)) {
- intersectionPoints.push(thisTopLeft);
- }
- var thisTopRight = this.getTopRight();
- if (rect.containsPoint(thisTopRight, EPSILON)) {
- intersectionPoints.push(thisTopRight);
- }
- var thisBottomLeft = this.getBottomLeft();
- if (rect.containsPoint(thisBottomLeft, EPSILON)) {
- intersectionPoints.push(thisBottomLeft);
- }
- var thisBottomRight = this.getBottomRight();
- if (rect.containsPoint(thisBottomRight, EPSILON)) {
- intersectionPoints.push(thisBottomRight);
- }
- var rectTopLeft = rect.getTopLeft();
- if (this.containsPoint(rectTopLeft, EPSILON)) {
- intersectionPoints.push(rectTopLeft);
- }
- var rectTopRight = rect.getTopRight();
- if (this.containsPoint(rectTopRight, EPSILON)) {
- intersectionPoints.push(rectTopRight);
- }
- var rectBottomLeft = rect.getBottomLeft();
- if (this.containsPoint(rectBottomLeft, EPSILON)) {
- intersectionPoints.push(rectBottomLeft);
- }
- var rectBottomRight = rect.getBottomRight();
- if (this.containsPoint(rectBottomRight, EPSILON)) {
- intersectionPoints.push(rectBottomRight);
- }
- var thisSegments = this._getSegments();
- var rectSegments = rect._getSegments();
- for (var i = 0; i < thisSegments.length; i++) {
- var thisSegment = thisSegments[i];
- for (var j = 0; j < rectSegments.length; j++) {
- var rectSegment = rectSegments[j];
- var intersect = getIntersection(thisSegment[0], thisSegment[1],
- rectSegment[0], rectSegment[1]);
- if (intersect) {
- intersectionPoints.push(intersect);
- }
- }
- }
- // Get intersection point of segments [a,b] and [c,d]
- function getIntersection(a, b, c, d) {
- // http://stackoverflow.com/a/1968345/1440403
- var abVector = b.minus(a);
- var cdVector = d.minus(c);
- var denom = -cdVector.x * abVector.y + abVector.x * cdVector.y;
- if (denom === 0) {
- return null;
- }
- var s = (abVector.x * (a.y - c.y) - abVector.y * (a.x - c.x)) / denom;
- var t = (cdVector.x * (a.y - c.y) - cdVector.y * (a.x - c.x)) / denom;
- if (-EPSILON <= s && s <= 1 - EPSILON &&
- -EPSILON <= t && t <= 1 - EPSILON) {
- return new $.Point(a.x + t * abVector.x, a.y + t * abVector.y);
- }
- return null;
- }
- if (intersectionPoints.length === 0) {
- return null;
- }
- var minX = intersectionPoints[0].x;
- var maxX = intersectionPoints[0].x;
- var minY = intersectionPoints[0].y;
- var maxY = intersectionPoints[0].y;
- for (var k = 1; k < intersectionPoints.length; k++) {
- var point = intersectionPoints[k];
- if (point.x < minX) {
- minX = point.x;
- }
- if (point.x > maxX) {
- maxX = point.x;
- }
- if (point.y < minY) {
- minY = point.y;
- }
- if (point.y > maxY) {
- maxY = point.y;
- }
- }
- return new $.Rect(minX, minY, maxX - minX, maxY - minY);
- },
- // private
- _getSegments: function() {
- var topLeft = this.getTopLeft();
- var topRight = this.getTopRight();
- var bottomLeft = this.getBottomLeft();
- var bottomRight = this.getBottomRight();
- return [[topLeft, topRight],
- [topRight, bottomRight],
- [bottomRight, bottomLeft],
- [bottomLeft, topLeft]];
- },
- /**
- * Rotates a rectangle around a point.
- * @function
- * @param {Number} degrees The angle in degrees to rotate.
- * @param {OpenSeadragon.Point} [pivot] The point about which to rotate.
- * Defaults to the center of the rectangle.
- * @return {OpenSeadragon.Rect}
- */
- rotate: function(degrees, pivot) {
- degrees = $.positiveModulo(degrees, 360);
- if (degrees === 0) {
- return this.clone();
- }
- pivot = pivot || this.getCenter();
- var newTopLeft = this.getTopLeft().rotate(degrees, pivot);
- var newTopRight = this.getTopRight().rotate(degrees, pivot);
- var diff = newTopRight.minus(newTopLeft);
- // Handle floating point error
- diff = diff.apply(function(x) {
- var EPSILON = 1e-15;
- return Math.abs(x) < EPSILON ? 0 : x;
- });
- var radians = Math.atan(diff.y / diff.x);
- if (diff.x < 0) {
- radians += Math.PI;
- } else if (diff.y < 0) {
- radians += 2 * Math.PI;
- }
- return new $.Rect(
- newTopLeft.x,
- newTopLeft.y,
- this.width,
- this.height,
- radians / Math.PI * 180);
- },
- /**
- * Retrieves the smallest horizontal (degrees=0) rectangle which contains
- * this rectangle.
- * @returns {OpenSeadragon.Rect}
- */
- getBoundingBox: function() {
- if (this.degrees === 0) {
- return this.clone();
- }
- var topLeft = this.getTopLeft();
- var topRight = this.getTopRight();
- var bottomLeft = this.getBottomLeft();
- var bottomRight = this.getBottomRight();
- var minX = Math.min(topLeft.x, topRight.x, bottomLeft.x, bottomRight.x);
- var maxX = Math.max(topLeft.x, topRight.x, bottomLeft.x, bottomRight.x);
- var minY = Math.min(topLeft.y, topRight.y, bottomLeft.y, bottomRight.y);
- var maxY = Math.max(topLeft.y, topRight.y, bottomLeft.y, bottomRight.y);
- return new $.Rect(
- minX,
- minY,
- maxX - minX,
- maxY - minY);
- },
- /**
- * Retrieves the smallest horizontal (degrees=0) rectangle which contains
- * this rectangle and has integers x, y, width and height
- * @returns {OpenSeadragon.Rect}
- */
- getIntegerBoundingBox: function() {
- var boundingBox = this.getBoundingBox();
- var x = Math.floor(boundingBox.x);
- var y = Math.floor(boundingBox.y);
- var width = Math.ceil(boundingBox.width + boundingBox.x - x);
- var height = Math.ceil(boundingBox.height + boundingBox.y - y);
- return new $.Rect(x, y, width, height);
- },
- /**
- * Determines whether a point is inside this rectangle (edge included).
- * @function
- * @param {OpenSeadragon.Point} point
- * @param {Number} [epsilon=0] the margin of error allowed
- * @returns {Boolean} true if the point is inside this rectangle, false
- * otherwise.
- */
- containsPoint: function(point, epsilon) {
- epsilon = epsilon || 0;
- // See http://stackoverflow.com/a/2752754/1440403 for explanation
- var topLeft = this.getTopLeft();
- var topRight = this.getTopRight();
- var bottomLeft = this.getBottomLeft();
- var topDiff = topRight.minus(topLeft);
- var leftDiff = bottomLeft.minus(topLeft);
- return ((point.x - topLeft.x) * topDiff.x +
- (point.y - topLeft.y) * topDiff.y >= -epsilon) &&
- ((point.x - topRight.x) * topDiff.x +
- (point.y - topRight.y) * topDiff.y <= epsilon) &&
- ((point.x - topLeft.x) * leftDiff.x +
- (point.y - topLeft.y) * leftDiff.y >= -epsilon) &&
- ((point.x - bottomLeft.x) * leftDiff.x +
- (point.y - bottomLeft.y) * leftDiff.y <= epsilon);
- },
- /**
- * Provides a string representation of the rectangle which is useful for
- * debugging.
- * @function
- * @returns {String} A string representation of the rectangle.
- */
- toString: function() {
- return "[" +
- (Math.round(this.x * 100) / 100) + ", " +
- (Math.round(this.y * 100) / 100) + ", " +
- (Math.round(this.width * 100) / 100) + "x" +
- (Math.round(this.height * 100) / 100) + ", " +
- (Math.round(this.degrees * 100) / 100) + "deg" +
- "]";
- }
- };
- }(OpenSeadragon));
- /*
- * OpenSeadragon - ReferenceStrip
- *
- * Copyright (C) 2009 CodePlex Foundation
- * Copyright (C) 2010-2013 OpenSeadragon contributors
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are
- * met:
- *
- * - Redistributions of source code must retain the above copyright notice,
- * this list of conditions and the following disclaimer.
- *
- * - Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * - Neither the name of CodePlex Foundation nor the names of its
- * contributors may be used to endorse or promote products derived from
- * this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
- * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
- * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
- * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
- (function ( $ ) {
- // dictionary from id to private properties
- var THIS = {};
- /**
- * The CollectionDrawer is a reimplementation if the Drawer API that
- * focuses on allowing a viewport to be redefined as a collection
- * of smaller viewports, defined by a clear number of rows and / or
- * columns of which each item in the matrix of viewports has its own
- * source.
- *
- * This idea is a reexpression of the idea of dzi collections
- * which allows a clearer algorithm to reuse the tile sources already
- * supported by OpenSeadragon, in heterogenious or homogenious
- * sequences just like mixed groups already supported by the viewer
- * for the purpose of image sequnces.
- *
- * TODO: The difficult part of this feature is figuring out how to express
- * this functionality as a combination of the functionality already
- * provided by Drawer, Viewport, TileSource, and Navigator. It may
- * require better abstraction at those points in order to efficiently
- * reuse those paradigms.
- */
- /**
- * @class ReferenceStrip
- * @memberof OpenSeadragon
- * @param {Object} options
- */
- $.ReferenceStrip = function ( options ) {
- var _this = this,
- viewer = options.viewer,
- viewerSize = $.getElementSize( viewer.element ),
- element,
- style,
- i;
- //We may need to create a new element and id if they did not
- //provide the id for the existing element
- if ( !options.id ) {
- options.id = 'referencestrip-' + $.now();
- this.element = $.makeNeutralElement( "div" );
- this.element.id = options.id;
- this.element.className = 'referencestrip';
- }
- options = $.extend( true, {
- sizeRatio: $.DEFAULT_SETTINGS.referenceStripSizeRatio,
- position: $.DEFAULT_SETTINGS.referenceStripPosition,
- scroll: $.DEFAULT_SETTINGS.referenceStripScroll,
- clickTimeThreshold: $.DEFAULT_SETTINGS.clickTimeThreshold
- }, options, {
- //required overrides
- element: this.element,
- //These need to be overridden to prevent recursion since
- //the navigator is a viewer and a viewer has a navigator
- showNavigator: false,
- mouseNavEnabled: false,
- showNavigationControl: false,
- showSequenceControl: false
- } );
- $.extend( this, options );
- //Private state properties
- THIS[this.id] = {
- "animating": false
- };
- this.minPixelRatio = this.viewer.minPixelRatio;
- style = this.element.style;
- style.marginTop = '0px';
- style.marginRight = '0px';
- style.marginBottom = '0px';
- style.marginLeft = '0px';
- style.left = '0px';
- style.bottom = '0px';
- style.border = '0px';
- style.background = '#000';
- style.position = 'relative';
- $.setElementTouchActionNone( this.element );
- $.setElementOpacity( this.element, 0.8 );
- this.viewer = viewer;
- this.innerTracker = new $.MouseTracker( {
- element: this.element,
- dragHandler: $.delegate( this, onStripDrag ),
- scrollHandler: $.delegate( this, onStripScroll ),
- enterHandler: $.delegate( this, onStripEnter ),
- exitHandler: $.delegate( this, onStripExit ),
- keyDownHandler: $.delegate( this, onKeyDown ),
- keyHandler: $.delegate( this, onKeyPress )
- } );
- //Controls the position and orientation of the reference strip and sets the
- //appropriate width and height
- if ( options.width && options.height ) {
- this.element.style.width = options.width + 'px';
- this.element.style.height = options.height + 'px';
- viewer.addControl(
- this.element,
- { anchor: $.ControlAnchor.BOTTOM_LEFT }
- );
- } else {
- if ( "horizontal" == options.scroll ) {
- this.element.style.width = (
- viewerSize.x *
- options.sizeRatio *
- viewer.tileSources.length
- ) + ( 12 * viewer.tileSources.length ) + 'px';
- this.element.style.height = (
- viewerSize.y *
- options.sizeRatio
- ) + 'px';
- viewer.addControl(
- this.element,
- { anchor: $.ControlAnchor.BOTTOM_LEFT }
- );
- } else {
- this.element.style.height = (
- viewerSize.y *
- options.sizeRatio *
- viewer.tileSources.length
- ) + ( 12 * viewer.tileSources.length ) + 'px';
- this.element.style.width = (
- viewerSize.x *
- options.sizeRatio
- ) + 'px';
- viewer.addControl(
- this.element,
- { anchor: $.ControlAnchor.TOP_LEFT }
- );
- }
- }
- this.panelWidth = ( viewerSize.x * this.sizeRatio ) + 8;
- this.panelHeight = ( viewerSize.y * this.sizeRatio ) + 8;
- this.panels = [];
- this.miniViewers = {};
- /*jshint loopfunc:true*/
- for ( i = 0; i < viewer.tileSources.length; i++ ) {
- element = $.makeNeutralElement( 'div' );
- element.id = this.element.id + "-" + i;
- element.style.width = _this.panelWidth + 'px';
- element.style.height = _this.panelHeight + 'px';
- element.style.display = 'inline';
- element.style.float = 'left'; //Webkit
- element.style.cssFloat = 'left'; //Firefox
- element.style.styleFloat = 'left'; //IE
- element.style.padding = '2px';
- $.setElementTouchActionNone( element );
- element.innerTracker = new $.MouseTracker( {
- element: element,
- clickTimeThreshold: this.clickTimeThreshold,
- clickDistThreshold: this.clickDistThreshold,
- pressHandler: function ( event ) {
- event.eventSource.dragging = $.now();
- },
- releaseHandler: function ( event ) {
- var tracker = event.eventSource,
- id = tracker.element.id,
- page = Number( id.split( '-' )[2] ),
- now = $.now();
- if ( event.insideElementPressed &&
- event.insideElementReleased &&
- tracker.dragging &&
- ( now - tracker.dragging ) < tracker.clickTimeThreshold ) {
- tracker.dragging = null;
- viewer.goToPage( page );
- }
- }
- } );
- this.element.appendChild( element );
- element.activePanel = false;
- this.panels.push( element );
- }
- loadPanels( this, this.scroll == 'vertical' ? viewerSize.y : viewerSize.x, 0 );
- this.setFocus( 0 );
- };
- $.extend( $.ReferenceStrip.prototype, $.EventSource.prototype, $.Viewer.prototype, /** @lends OpenSeadragon.ReferenceStrip.prototype */{
- /**
- * @function
- */
- setFocus: function ( page ) {
- var element = $.getElement( this.element.id + '-' + page ),
- viewerSize = $.getElementSize( this.viewer.canvas ),
- scrollWidth = Number( this.element.style.width.replace( 'px', '' ) ),
- scrollHeight = Number( this.element.style.height.replace( 'px', '' ) ),
- offsetLeft = -Number( this.element.style.marginLeft.replace( 'px', '' ) ),
- offsetTop = -Number( this.element.style.marginTop.replace( 'px', '' ) ),
- offset;
- if ( this.currentSelected !== element ) {
- if ( this.currentSelected ) {
- this.currentSelected.style.background = '#000';
- }
- this.currentSelected = element;
- this.currentSelected.style.background = '#999';
- if ( 'horizontal' == this.scroll ) {
- //right left
- offset = ( Number( page ) ) * ( this.panelWidth + 3 );
- if ( offset > offsetLeft + viewerSize.x - this.panelWidth ) {
- offset = Math.min( offset, ( scrollWidth - viewerSize.x ) );
- this.element.style.marginLeft = -offset + 'px';
- loadPanels( this, viewerSize.x, -offset );
- } else if ( offset < offsetLeft ) {
- offset = Math.max( 0, offset - viewerSize.x / 2 );
- this.element.style.marginLeft = -offset + 'px';
- loadPanels( this, viewerSize.x, -offset );
- }
- } else {
- offset = ( Number( page ) ) * ( this.panelHeight + 3 );
- if ( offset > offsetTop + viewerSize.y - this.panelHeight ) {
- offset = Math.min( offset, ( scrollHeight - viewerSize.y ) );
- this.element.style.marginTop = -offset + 'px';
- loadPanels( this, viewerSize.y, -offset );
- } else if ( offset < offsetTop ) {
- offset = Math.max( 0, offset - viewerSize.y / 2 );
- this.element.style.marginTop = -offset + 'px';
- loadPanels( this, viewerSize.y, -offset );
- }
- }
- this.currentPage = page;
- onStripEnter.call( this, { eventSource: this.innerTracker } );
- }
- },
- /**
- * @function
- */
- update: function () {
- if ( THIS[this.id].animating ) {
- $.console.log( 'image reference strip update' );
- return true;
- }
- return false;
- },
- // Overrides Viewer.destroy
- destroy: function() {
- if (this.miniViewers) {
- for (var key in this.miniViewers) {
- this.miniViewers[key].destroy();
- }
- }
- if (this.element) {
- this.element.parentNode.removeChild(this.element);
- }
- }
- } );
- /**
- * @private
- * @inner
- * @function
- */
- function onStripDrag( event ) {
- var offsetLeft = Number( this.element.style.marginLeft.replace( 'px', '' ) ),
- offsetTop = Number( this.element.style.marginTop.replace( 'px', '' ) ),
- scrollWidth = Number( this.element.style.width.replace( 'px', '' ) ),
- scrollHeight = Number( this.element.style.height.replace( 'px', '' ) ),
- viewerSize = $.getElementSize( this.viewer.canvas );
- this.dragging = true;
- if ( this.element ) {
- if ( 'horizontal' == this.scroll ) {
- if ( -event.delta.x > 0 ) {
- //forward
- if ( offsetLeft > -( scrollWidth - viewerSize.x ) ) {
- this.element.style.marginLeft = ( offsetLeft + ( event.delta.x * 2 ) ) + 'px';
- loadPanels( this, viewerSize.x, offsetLeft + ( event.delta.x * 2 ) );
- }
- } else if ( -event.delta.x < 0 ) {
- //reverse
- if ( offsetLeft < 0 ) {
- this.element.style.marginLeft = ( offsetLeft + ( event.delta.x * 2 ) ) + 'px';
- loadPanels( this, viewerSize.x, offsetLeft + ( event.delta.x * 2 ) );
- }
- }
- } else {
- if ( -event.delta.y > 0 ) {
- //forward
- if ( offsetTop > -( scrollHeight - viewerSize.y ) ) {
- this.element.style.marginTop = ( offsetTop + ( event.delta.y * 2 ) ) + 'px';
- loadPanels( this, viewerSize.y, offsetTop + ( event.delta.y * 2 ) );
- }
- } else if ( -event.delta.y < 0 ) {
- //reverse
- if ( offsetTop < 0 ) {
- this.element.style.marginTop = ( offsetTop + ( event.delta.y * 2 ) ) + 'px';
- loadPanels( this, viewerSize.y, offsetTop + ( event.delta.y * 2 ) );
- }
- }
- }
- }
- return false;
- }
- /**
- * @private
- * @inner
- * @function
- */
- function onStripScroll( event ) {
- var offsetLeft = Number( this.element.style.marginLeft.replace( 'px', '' ) ),
- offsetTop = Number( this.element.style.marginTop.replace( 'px', '' ) ),
- scrollWidth = Number( this.element.style.width.replace( 'px', '' ) ),
- scrollHeight = Number( this.element.style.height.replace( 'px', '' ) ),
- viewerSize = $.getElementSize( this.viewer.canvas );
- if ( this.element ) {
- if ( 'horizontal' == this.scroll ) {
- if ( event.scroll > 0 ) {
- //forward
- if ( offsetLeft > -( scrollWidth - viewerSize.x ) ) {
- this.element.style.marginLeft = ( offsetLeft - ( event.scroll * 60 ) ) + 'px';
- loadPanels( this, viewerSize.x, offsetLeft - ( event.scroll * 60 ) );
- }
- } else if ( event.scroll < 0 ) {
- //reverse
- if ( offsetLeft < 0 ) {
- this.element.style.marginLeft = ( offsetLeft - ( event.scroll * 60 ) ) + 'px';
- loadPanels( this, viewerSize.x, offsetLeft - ( event.scroll * 60 ) );
- }
- }
- } else {
- if ( event.scroll < 0 ) {
- //scroll up
- if ( offsetTop > viewerSize.y - scrollHeight ) {
- this.element.style.marginTop = ( offsetTop + ( event.scroll * 60 ) ) + 'px';
- loadPanels( this, viewerSize.y, offsetTop + ( event.scroll * 60 ) );
- }
- } else if ( event.scroll > 0 ) {
- //scroll dowm
- if ( offsetTop < 0 ) {
- this.element.style.marginTop = ( offsetTop + ( event.scroll * 60 ) ) + 'px';
- loadPanels( this, viewerSize.y, offsetTop + ( event.scroll * 60 ) );
- }
- }
- }
- }
- //cancels event
- return false;
- }
- function loadPanels( strip, viewerSize, scroll ) {
- var panelSize,
- activePanelsStart,
- activePanelsEnd,
- miniViewer,
- style,
- i,
- element;
- if ( 'horizontal' == strip.scroll ) {
- panelSize = strip.panelWidth;
- } else {
- panelSize = strip.panelHeight;
- }
- activePanelsStart = Math.ceil( viewerSize / panelSize ) + 5;
- activePanelsEnd = Math.ceil( ( Math.abs( scroll ) + viewerSize ) / panelSize ) + 1;
- activePanelsStart = activePanelsEnd - activePanelsStart;
- activePanelsStart = activePanelsStart < 0 ? 0 : activePanelsStart;
- for ( i = activePanelsStart; i < activePanelsEnd && i < strip.panels.length; i++ ) {
- element = strip.panels[i];
- if ( !element.activePanel ) {
- var miniTileSource;
- var originalTileSource = strip.viewer.tileSources[i];
- if (originalTileSource.referenceStripThumbnailUrl) {
- miniTileSource = {
- type: 'image',
- url: originalTileSource.referenceStripThumbnailUrl
- };
- } else {
- miniTileSource = originalTileSource;
- }
- miniViewer = new $.Viewer( {
- id: element.id,
- tileSources: [miniTileSource],
- element: element,
- navigatorSizeRatio: strip.sizeRatio,
- showNavigator: false,
- mouseNavEnabled: false,
- showNavigationControl: false,
- showSequenceControl: false,
- immediateRender: true,
- blendTime: 0,
- animationTime: 0
- } );
- miniViewer.displayRegion = $.makeNeutralElement( "div" );
- miniViewer.displayRegion.id = element.id + '-displayregion';
- miniViewer.displayRegion.className = 'displayregion';
- style = miniViewer.displayRegion.style;
- style.position = 'relative';
- style.top = '0px';
- style.left = '0px';
- style.fontSize = '0px';
- style.overflow = 'hidden';
- style.float = 'left'; //Webkit
- style.cssFloat = 'left'; //Firefox
- style.styleFloat = 'left'; //IE
- style.zIndex = 999999999;
- style.cursor = 'default';
- style.width = ( strip.panelWidth - 4 ) + 'px';
- style.height = ( strip.panelHeight - 4 ) + 'px';
- // TODO: What is this for? Future keyboard navigation support?
- miniViewer.displayRegion.innerTracker = new $.MouseTracker( {
- element: miniViewer.displayRegion,
- startDisabled: true
- } );
- element.getElementsByTagName( 'div' )[0].appendChild(
- miniViewer.displayRegion
- );
- strip.miniViewers[element.id] = miniViewer;
- element.activePanel = true;
- }
- }
- }
- /**
- * @private
- * @inner
- * @function
- */
- function onStripEnter( event ) {
- var element = event.eventSource.element;
- //$.setElementOpacity(element, 0.8);
- //element.style.border = '1px solid #555';
- //element.style.background = '#000';
- if ( 'horizontal' == this.scroll ) {
- //element.style.paddingTop = "0px";
- element.style.marginBottom = "0px";
- } else {
- //element.style.paddingRight = "0px";
- element.style.marginLeft = "0px";
- }
- return false;
- }
- /**
- * @private
- * @inner
- * @function
- */
- function onStripExit( event ) {
- var element = event.eventSource.element;
- if ( 'horizontal' == this.scroll ) {
- //element.style.paddingTop = "10px";
- element.style.marginBottom = "-" + ( $.getElementSize( element ).y / 2 ) + "px";
- } else {
- //element.style.paddingRight = "10px";
- element.style.marginLeft = "-" + ( $.getElementSize( element ).x / 2 ) + "px";
- }
- return false;
- }
- /**
- * @private
- * @inner
- * @function
- */
- function onKeyDown( event ) {
- //console.log( event.keyCode );
- if ( !event.preventDefaultAction && !event.ctrl && !event.alt && !event.meta ) {
- switch ( event.keyCode ) {
- case 38: //up arrow
- onStripScroll.call( this, { eventSource: this.tracker, position: null, scroll: 1, shift: null } );
- return false;
- case 40: //down arrow
- onStripScroll.call( this, { eventSource: this.tracker, position: null, scroll: -1, shift: null } );
- return false;
- case 37: //left arrow
- onStripScroll.call( this, { eventSource: this.tracker, position: null, scroll: -1, shift: null } );
- return false;
- case 39: //right arrow
- onStripScroll.call( this, { eventSource: this.tracker, position: null, scroll: 1, shift: null } );
- return false;
- default:
- //console.log( 'navigator keycode %s', event.keyCode );
- return true;
- }
- } else {
- return true;
- }
- }
- /**
- * @private
- * @inner
- * @function
- */
- function onKeyPress( event ) {
- //console.log( event.keyCode );
- if ( !event.preventDefaultAction && !event.ctrl && !event.alt && !event.meta ) {
- switch ( event.keyCode ) {
- case 61: //=|+
- onStripScroll.call( this, { eventSource: this.tracker, position: null, scroll: 1, shift: null } );
- return false;
- case 45: //-|_
- onStripScroll.call( this, { eventSource: this.tracker, position: null, scroll: -1, shift: null } );
- return false;
- case 48: //0|)
- case 119: //w
- case 87: //W
- onStripScroll.call( this, { eventSource: this.tracker, position: null, scroll: 1, shift: null } );
- return false;
- case 115: //s
- case 83: //S
- onStripScroll.call( this, { eventSource: this.tracker, position: null, scroll: -1, shift: null } );
- return false;
- case 97: //a
- onStripScroll.call( this, { eventSource: this.tracker, position: null, scroll: -1, shift: null } );
- return false;
- case 100: //d
- onStripScroll.call( this, { eventSource: this.tracker, position: null, scroll: 1, shift: null } );
- return false;
- default:
- //console.log( 'navigator keycode %s', event.keyCode );
- return true;
- }
- } else {
- return true;
- }
- }
- }(OpenSeadragon));
- /*
- * OpenSeadragon - DisplayRect
- *
- * Copyright (C) 2009 CodePlex Foundation
- * Copyright (C) 2010-2013 OpenSeadragon contributors
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are
- * met:
- *
- * - Redistributions of source code must retain the above copyright notice,
- * this list of conditions and the following disclaimer.
- *
- * - Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * - Neither the name of CodePlex Foundation nor the names of its
- * contributors may be used to endorse or promote products derived from
- * this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
- * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
- * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
- * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
- (function( $ ){
- /**
- * @class DisplayRect
- * @classdesc A display rectangle is very similar to {@link OpenSeadragon.Rect} but adds two
- * fields, 'minLevel' and 'maxLevel' which denote the supported zoom levels
- * for this rectangle.
- *
- * @memberof OpenSeadragon
- * @extends OpenSeadragon.Rect
- * @param {Number} x The vector component 'x'.
- * @param {Number} y The vector component 'y'.
- * @param {Number} width The vector component 'height'.
- * @param {Number} height The vector component 'width'.
- * @param {Number} minLevel The lowest zoom level supported.
- * @param {Number} maxLevel The highest zoom level supported.
- */
- $.DisplayRect = function( x, y, width, height, minLevel, maxLevel ) {
- $.Rect.apply( this, [ x, y, width, height ] );
- /**
- * The lowest zoom level supported.
- * @member {Number} minLevel
- * @memberof OpenSeadragon.DisplayRect#
- */
- this.minLevel = minLevel;
- /**
- * The highest zoom level supported.
- * @member {Number} maxLevel
- * @memberof OpenSeadragon.DisplayRect#
- */
- this.maxLevel = maxLevel;
- };
- $.extend( $.DisplayRect.prototype, $.Rect.prototype );
- }( OpenSeadragon ));
- /*
- * OpenSeadragon - Spring
- *
- * Copyright (C) 2009 CodePlex Foundation
- * Copyright (C) 2010-2013 OpenSeadragon contributors
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are
- * met:
- *
- * - Redistributions of source code must retain the above copyright notice,
- * this list of conditions and the following disclaimer.
- *
- * - Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * - Neither the name of CodePlex Foundation nor the names of its
- * contributors may be used to endorse or promote products derived from
- * this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
- * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
- * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
- * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
- (function( $ ){
- /**
- * @class Spring
- * @memberof OpenSeadragon
- * @param {Object} options - Spring configuration settings.
- * @param {Number} options.springStiffness - Spring stiffness. Must be greater than zero.
- * The closer to zero, the closer to linear animation.
- * @param {Number} options.animationTime - Animation duration per spring, in seconds.
- * Must be zero or greater.
- * @param {Number} [options.initial=0] - Initial value of spring.
- * @param {Boolean} [options.exponential=false] - Whether this spring represents
- * an exponential scale (such as zoom) and should be animated accordingly. Note that
- * exponential springs must have non-zero values.
- */
- $.Spring = function( options ) {
- var args = arguments;
- if( typeof ( options ) != 'object' ){
- //allows backward compatible use of ( initialValue, config ) as
- //constructor parameters
- options = {
- initial: args.length && typeof ( args[ 0 ] ) == "number" ?
- args[ 0 ] :
- undefined,
- /**
- * Spring stiffness.
- * @member {Number} springStiffness
- * @memberof OpenSeadragon.Spring#
- */
- springStiffness: args.length > 1 ?
- args[ 1 ].springStiffness :
- 5.0,
- /**
- * Animation duration per spring.
- * @member {Number} animationTime
- * @memberof OpenSeadragon.Spring#
- */
- animationTime: args.length > 1 ?
- args[ 1 ].animationTime :
- 1.5
- };
- }
- $.console.assert(typeof options.springStiffness === "number" && options.springStiffness !== 0,
- "[OpenSeadragon.Spring] options.springStiffness must be a non-zero number");
- $.console.assert(typeof options.animationTime === "number" && options.animationTime >= 0,
- "[OpenSeadragon.Spring] options.animationTime must be a number greater than or equal to 0");
- if (options.exponential) {
- this._exponential = true;
- delete options.exponential;
- }
- $.extend( true, this, options);
- /**
- * @member {Object} current
- * @memberof OpenSeadragon.Spring#
- * @property {Number} value
- * @property {Number} time
- */
- this.current = {
- value: typeof ( this.initial ) == "number" ?
- this.initial :
- (this._exponential ? 0 : 1),
- time: $.now() // always work in milliseconds
- };
- $.console.assert(!this._exponential || this.current.value !== 0,
- "[OpenSeadragon.Spring] value must be non-zero for exponential springs");
- /**
- * @member {Object} start
- * @memberof OpenSeadragon.Spring#
- * @property {Number} value
- * @property {Number} time
- */
- this.start = {
- value: this.current.value,
- time: this.current.time
- };
- /**
- * @member {Object} target
- * @memberof OpenSeadragon.Spring#
- * @property {Number} value
- * @property {Number} time
- */
- this.target = {
- value: this.current.value,
- time: this.current.time
- };
- if (this._exponential) {
- this.start._logValue = Math.log(this.start.value);
- this.target._logValue = Math.log(this.target.value);
- this.current._logValue = Math.log(this.current.value);
- }
- };
- /** @lends OpenSeadragon.Spring.prototype */
- $.Spring.prototype = {
- /**
- * @function
- * @param {Number} target
- */
- resetTo: function( target ) {
- $.console.assert(!this._exponential || target !== 0,
- "[OpenSeadragon.Spring.resetTo] target must be non-zero for exponential springs");
- this.start.value = this.target.value = this.current.value = target;
- this.start.time = this.target.time = this.current.time = $.now();
- if (this._exponential) {
- this.start._logValue = Math.log(this.start.value);
- this.target._logValue = Math.log(this.target.value);
- this.current._logValue = Math.log(this.current.value);
- }
- },
- /**
- * @function
- * @param {Number} target
- */
- springTo: function( target ) {
- $.console.assert(!this._exponential || target !== 0,
- "[OpenSeadragon.Spring.springTo] target must be non-zero for exponential springs");
- this.start.value = this.current.value;
- this.start.time = this.current.time;
- this.target.value = target;
- this.target.time = this.start.time + 1000 * this.animationTime;
- if (this._exponential) {
- this.start._logValue = Math.log(this.start.value);
- this.target._logValue = Math.log(this.target.value);
- }
- },
- /**
- * @function
- * @param {Number} delta
- */
- shiftBy: function( delta ) {
- this.start.value += delta;
- this.target.value += delta;
- if (this._exponential) {
- $.console.assert(this.target.value !== 0 && this.start.value !== 0,
- "[OpenSeadragon.Spring.shiftBy] spring value must be non-zero for exponential springs");
- this.start._logValue = Math.log(this.start.value);
- this.target._logValue = Math.log(this.target.value);
- }
- },
- setExponential: function(value) {
- this._exponential = value;
- if (this._exponential) {
- $.console.assert(this.current.value !== 0 && this.target.value !== 0 && this.start.value !== 0,
- "[OpenSeadragon.Spring.setExponential] spring value must be non-zero for exponential springs");
- this.start._logValue = Math.log(this.start.value);
- this.target._logValue = Math.log(this.target.value);
- this.current._logValue = Math.log(this.current.value);
- }
- },
- /**
- * @function
- * @returns true if the value got updated, false otherwise
- */
- update: function() {
- this.current.time = $.now();
- var startValue, targetValue;
- if (this._exponential) {
- startValue = this.start._logValue;
- targetValue = this.target._logValue;
- } else {
- startValue = this.start.value;
- targetValue = this.target.value;
- }
- var currentValue = (this.current.time >= this.target.time) ?
- targetValue :
- startValue +
- ( targetValue - startValue ) *
- transform(
- this.springStiffness,
- ( this.current.time - this.start.time ) /
- ( this.target.time - this.start.time )
- );
- var oldValue = this.current.value;
- if (this._exponential) {
- this.current.value = Math.exp(currentValue);
- } else {
- this.current.value = currentValue;
- }
- return oldValue != this.current.value;
- },
- /**
- * Returns whether the spring is at the target value
- * @function
- * @returns {Boolean} True if at target value, false otherwise
- */
- isAtTargetValue: function() {
- return this.current.value === this.target.value;
- }
- };
- /**
- * @private
- */
- function transform( stiffness, x ) {
- return ( 1.0 - Math.exp( stiffness * -x ) ) /
- ( 1.0 - Math.exp( -stiffness ) );
- }
- }( OpenSeadragon ));
- /*
- * OpenSeadragon - ImageLoader
- *
- * Copyright (C) 2009 CodePlex Foundation
- * Copyright (C) 2010-2013 OpenSeadragon contributors
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are
- * met:
- *
- * - Redistributions of source code must retain the above copyright notice,
- * this list of conditions and the following disclaimer.
- *
- * - Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * - Neither the name of CodePlex Foundation nor the names of its
- * contributors may be used to endorse or promote products derived from
- * this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
- * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
- * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
- * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
- (function($){
- /**
- * @private
- * @class ImageJob
- * @classdesc Handles downloading of a single image.
- * @param {Object} options - Options for this ImageJob.
- * @param {String} [options.src] - URL of image to download.
- * @param {String} [options.loadWithAjax] - Whether to load this image with AJAX.
- * @param {String} [options.ajaxHeaders] - Headers to add to the image request if using AJAX.
- * @param {String} [options.crossOriginPolicy] - CORS policy to use for downloads
- * @param {Function} [options.callback] - Called once image has been downloaded.
- * @param {Function} [options.abort] - Called when this image job is aborted.
- * @param {Number} [options.timeout] - The max number of milliseconds that this image job may take to complete.
- */
- function ImageJob (options) {
- $.extend(true, this, {
- timeout: $.DEFAULT_SETTINGS.timeout,
- jobId: null
- }, options);
- /**
- * Image object which will contain downloaded image.
- * @member {Image} image
- * @memberof OpenSeadragon.ImageJob#
- */
- this.image = null;
- }
- ImageJob.prototype = {
- errorMsg: null,
- /**
- * Starts the image job.
- * @method
- */
- start: function(){
- var self = this;
- var selfAbort = this.abort;
- this.image = new Image();
- this.image.onload = function(){
- self.finish(true);
- };
- this.image.onabort = this.image.onerror = function() {
- self.errorMsg = "Image load aborted";
- self.finish(false);
- };
- this.jobId = window.setTimeout(function(){
- self.errorMsg = "Image load exceeded timeout (" + self.timeout + " ms)";
- self.finish(false);
- }, this.timeout);
- // Load the tile with an AJAX request if the loadWithAjax option is
- // set. Otherwise load the image by setting the source proprety of the image object.
- if (this.loadWithAjax) {
- this.request = $.makeAjaxRequest({
- url: this.src,
- withCredentials: this.ajaxWithCredentials,
- headers: this.ajaxHeaders,
- responseType: "arraybuffer",
- success: function(request) {
- var blb;
- // Make the raw data into a blob.
- // BlobBuilder fallback adapted from
- // http://stackoverflow.com/questions/15293694/blob-constructor-browser-compatibility
- try {
- blb = new window.Blob([request.response]);
- } catch (e) {
- var BlobBuilder = (
- window.BlobBuilder ||
- window.WebKitBlobBuilder ||
- window.MozBlobBuilder ||
- window.MSBlobBuilder
- );
- if (e.name === 'TypeError' && BlobBuilder) {
- var bb = new BlobBuilder();
- bb.append(request.response);
- blb = bb.getBlob();
- }
- }
- // If the blob is empty for some reason consider the image load a failure.
- if (blb.size === 0) {
- self.errorMsg = "Empty image response.";
- self.finish(false);
- }
- // Create a URL for the blob data and make it the source of the image object.
- // This will still trigger Image.onload to indicate a successful tile load.
- var url = (window.URL || window.webkitURL).createObjectURL(blb);
- self.image.src = url;
- },
- error: function(request) {
- self.errorMsg = "Image load aborted - XHR error";
- self.finish(false);
- }
- });
- // Provide a function to properly abort the request.
- this.abort = function() {
- self.request.abort();
- // Call the existing abort function if available
- if (typeof selfAbort === "function") {
- selfAbort();
- }
- };
- } else {
- if (this.crossOriginPolicy !== false) {
- this.image.crossOrigin = this.crossOriginPolicy;
- }
- this.image.src = this.src;
- }
- },
- finish: function(successful) {
- this.image.onload = this.image.onerror = this.image.onabort = null;
- if (!successful) {
- this.image = null;
- }
- if (this.jobId) {
- window.clearTimeout(this.jobId);
- }
- this.callback(this);
- }
- };
- /**
- * @class ImageLoader
- * @memberof OpenSeadragon
- * @classdesc Handles downloading of a set of images using asynchronous queue pattern.
- * You generally won't have to interact with the ImageLoader directly.
- * @param {Object} options - Options for this ImageLoader.
- * @param {Number} [options.jobLimit] - The number of concurrent image requests. See imageLoaderLimit in {@link OpenSeadragon.Options} for details.
- * @param {Number} [options.timeout] - The max number of milliseconds that an image job may take to complete.
- */
- $.ImageLoader = function(options) {
- $.extend(true, this, {
- jobLimit: $.DEFAULT_SETTINGS.imageLoaderLimit,
- timeout: $.DEFAULT_SETTINGS.timeout,
- jobQueue: [],
- jobsInProgress: 0
- }, options);
- };
- /** @lends OpenSeadragon.ImageLoader.prototype */
- $.ImageLoader.prototype = {
- /**
- * Add an unloaded image to the loader queue.
- * @method
- * @param {Object} options - Options for this job.
- * @param {String} [options.src] - URL of image to download.
- * @param {String} [options.loadWithAjax] - Whether to load this image with AJAX.
- * @param {String} [options.ajaxHeaders] - Headers to add to the image request if using AJAX.
- * @param {String|Boolean} [options.crossOriginPolicy] - CORS policy to use for downloads
- * @param {Boolean} [options.ajaxWithCredentials] - Whether to set withCredentials on AJAX
- * requests.
- * @param {Function} [options.callback] - Called once image has been downloaded.
- * @param {Function} [options.abort] - Called when this image job is aborted.
- */
- addJob: function(options) {
- var _this = this,
- complete = function(job) {
- completeJob(_this, job, options.callback);
- },
- jobOptions = {
- src: options.src,
- loadWithAjax: options.loadWithAjax,
- ajaxHeaders: options.loadWithAjax ? options.ajaxHeaders : null,
- crossOriginPolicy: options.crossOriginPolicy,
- ajaxWithCredentials: options.ajaxWithCredentials,
- callback: complete,
- abort: options.abort,
- timeout: this.timeout
- },
- newJob = new ImageJob(jobOptions);
- if ( !this.jobLimit || this.jobsInProgress < this.jobLimit ) {
- newJob.start();
- this.jobsInProgress++;
- }
- else {
- this.jobQueue.push( newJob );
- }
- },
- /**
- * Clear any unstarted image loading jobs from the queue.
- * @method
- */
- clear: function() {
- for( var i = 0; i < this.jobQueue.length; i++ ) {
- var job = this.jobQueue[i];
- if ( typeof job.abort === "function" ) {
- job.abort();
- }
- }
- this.jobQueue = [];
- }
- };
- /**
- * Cleans up ImageJob once completed.
- * @method
- * @private
- * @param loader - ImageLoader used to start job.
- * @param job - The ImageJob that has completed.
- * @param callback - Called once cleanup is finished.
- */
- function completeJob(loader, job, callback) {
- var nextJob;
- loader.jobsInProgress--;
- if ((!loader.jobLimit || loader.jobsInProgress < loader.jobLimit) && loader.jobQueue.length > 0) {
- nextJob = loader.jobQueue.shift();
- nextJob.start();
- loader.jobsInProgress++;
- }
- callback(job.image, job.errorMsg, job.request);
- }
- }(OpenSeadragon));
- /*
- * OpenSeadragon - Tile
- *
- * Copyright (C) 2009 CodePlex Foundation
- * Copyright (C) 2010-2013 OpenSeadragon contributors
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are
- * met:
- *
- * - Redistributions of source code must retain the above copyright notice,
- * this list of conditions and the following disclaimer.
- *
- * - Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * - Neither the name of CodePlex Foundation nor the names of its
- * contributors may be used to endorse or promote products derived from
- * this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
- * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
- * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
- * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
- (function( $ ){
- /**
- * @class Tile
- * @memberof OpenSeadragon
- * @param {Number} level The zoom level this tile belongs to.
- * @param {Number} x The vector component 'x'.
- * @param {Number} y The vector component 'y'.
- * @param {OpenSeadragon.Rect} bounds Where this tile fits, in normalized
- * coordinates.
- * @param {Boolean} exists Is this tile a part of a sparse image? ( Also has
- * this tile failed to load? )
- * @param {String} url The URL of this tile's image.
- * @param {CanvasRenderingContext2D} context2D The context2D of this tile if it
- * is provided directly by the tile source.
- * @param {Boolean} loadWithAjax Whether this tile image should be loaded with an AJAX request .
- * @param {Object} ajaxHeaders The headers to send with this tile's AJAX request (if applicable).
- * @param {OpenSeadragon.Rect} sourceBounds The portion of the tile to use as the source of the
- * drawing operation, in pixels. Note that this only works when drawing with canvas; when drawing
- * with HTML the entire tile is always used.
- */
- $.Tile = function(level, x, y, bounds, exists, url, context2D, loadWithAjax, ajaxHeaders, sourceBounds) {
- /**
- * The zoom level this tile belongs to.
- * @member {Number} level
- * @memberof OpenSeadragon.Tile#
- */
- this.level = level;
- /**
- * The vector component 'x'.
- * @member {Number} x
- * @memberof OpenSeadragon.Tile#
- */
- this.x = x;
- /**
- * The vector component 'y'.
- * @member {Number} y
- * @memberof OpenSeadragon.Tile#
- */
- this.y = y;
- /**
- * Where this tile fits, in normalized coordinates
- * @member {OpenSeadragon.Rect} bounds
- * @memberof OpenSeadragon.Tile#
- */
- this.bounds = bounds;
- /**
- * The portion of the tile to use as the source of the drawing operation, in pixels. Note that
- * this only works when drawing with canvas; when drawing with HTML the entire tile is always used.
- * @member {OpenSeadragon.Rect} sourceBounds
- * @memberof OpenSeadragon.Tile#
- */
- this.sourceBounds = sourceBounds;
- /**
- * Is this tile a part of a sparse image? Also has this tile failed to load?
- * @member {Boolean} exists
- * @memberof OpenSeadragon.Tile#
- */
- this.exists = exists;
- /**
- * The URL of this tile's image.
- * @member {String} url
- * @memberof OpenSeadragon.Tile#
- */
- this.url = url;
- /**
- * The context2D of this tile if it is provided directly by the tile source.
- * @member {CanvasRenderingContext2D} context2D
- * @memberOf OpenSeadragon.Tile#
- */
- this.context2D = context2D;
- /**
- * Whether to load this tile's image with an AJAX request.
- * @member {Boolean} loadWithAjax
- * @memberof OpenSeadragon.Tile#
- */
- this.loadWithAjax = loadWithAjax;
- /**
- * The headers to be used in requesting this tile's image.
- * Only used if loadWithAjax is set to true.
- * @member {Object} ajaxHeaders
- * @memberof OpenSeadragon.Tile#
- */
- this.ajaxHeaders = ajaxHeaders;
- /**
- * The unique cache key for this tile.
- * @member {String} cacheKey
- * @memberof OpenSeadragon.Tile#
- */
- if (this.ajaxHeaders) {
- this.cacheKey = this.url + "+" + JSON.stringify(this.ajaxHeaders);
- } else {
- this.cacheKey = this.url;
- }
- /**
- * Is this tile loaded?
- * @member {Boolean} loaded
- * @memberof OpenSeadragon.Tile#
- */
- this.loaded = false;
- /**
- * Is this tile loading?
- * @member {Boolean} loading
- * @memberof OpenSeadragon.Tile#
- */
- this.loading = false;
- /**
- * The HTML div element for this tile
- * @member {Element} element
- * @memberof OpenSeadragon.Tile#
- */
- this.element = null;
- /**
- * The HTML img element for this tile.
- * @member {Element} imgElement
- * @memberof OpenSeadragon.Tile#
- */
- this.imgElement = null;
- /**
- * The Image object for this tile.
- * @member {Object} image
- * @memberof OpenSeadragon.Tile#
- */
- this.image = null;
- /**
- * The alias of this.element.style.
- * @member {String} style
- * @memberof OpenSeadragon.Tile#
- */
- this.style = null;
- /**
- * This tile's position on screen, in pixels.
- * @member {OpenSeadragon.Point} position
- * @memberof OpenSeadragon.Tile#
- */
- this.position = null;
- /**
- * This tile's size on screen, in pixels.
- * @member {OpenSeadragon.Point} size
- * @memberof OpenSeadragon.Tile#
- */
- this.size = null;
- /**
- * The start time of this tile's blending.
- * @member {Number} blendStart
- * @memberof OpenSeadragon.Tile#
- */
- this.blendStart = null;
- /**
- * The current opacity this tile should be.
- * @member {Number} opacity
- * @memberof OpenSeadragon.Tile#
- */
- this.opacity = null;
- /**
- * The squared distance of this tile to the viewport center.
- * Use for comparing tiles.
- * @private
- * @member {Number} squaredDistance
- * @memberof OpenSeadragon.Tile#
- */
- this.squaredDistance = null;
- /**
- * The visibility score of this tile.
- * @member {Number} visibility
- * @memberof OpenSeadragon.Tile#
- */
- this.visibility = null;
- /**
- * Whether this tile is currently being drawn.
- * @member {Boolean} beingDrawn
- * @memberof OpenSeadragon.Tile#
- */
- this.beingDrawn = false;
- /**
- * Timestamp the tile was last touched.
- * @member {Number} lastTouchTime
- * @memberof OpenSeadragon.Tile#
- */
- this.lastTouchTime = 0;
- /**
- * Whether this tile is in the right-most column for its level.
- * @member {Boolean} isRightMost
- * @memberof OpenSeadragon.Tile#
- */
- this.isRightMost = false;
- /**
- * Whether this tile is in the bottom-most row for its level.
- * @member {Boolean} isBottomMost
- * @memberof OpenSeadragon.Tile#
- */
- this.isBottomMost = false;
- };
- /** @lends OpenSeadragon.Tile.prototype */
- $.Tile.prototype = {
- /**
- * Provides a string representation of this tiles level and (x,y)
- * components.
- * @function
- * @returns {String}
- */
- toString: function() {
- return this.level + "/" + this.x + "_" + this.y;
- },
- // private
- _hasTransparencyChannel: function() {
- return !!this.context2D || this.url.match('.png');
- },
- /**
- * Renders the tile in an html container.
- * @function
- * @param {Element} container
- */
- drawHTML: function( container ) {
- if (!this.cacheImageRecord) {
- $.console.warn(
- '[Tile.drawHTML] attempting to draw tile %s when it\'s not cached',
- this.toString());
- return;
- }
- if ( !this.loaded ) {
- $.console.warn(
- "Attempting to draw tile %s when it's not yet loaded.",
- this.toString()
- );
- return;
- }
- //EXPERIMENTAL - trying to figure out how to scale the container
- // content during animation of the container size.
- if ( !this.element ) {
- this.element = $.makeNeutralElement( "div" );
- this.imgElement = this.cacheImageRecord.getImage().cloneNode();
- this.imgElement.style.msInterpolationMode = "nearest-neighbor";
- this.imgElement.style.width = "100%";
- this.imgElement.style.height = "100%";
- this.style = this.element.style;
- this.style.position = "absolute";
- }
- if ( this.element.parentNode != container ) {
- container.appendChild( this.element );
- }
- if ( this.imgElement.parentNode != this.element ) {
- this.element.appendChild( this.imgElement );
- }
- this.style.top = this.position.y + "px";
- this.style.left = this.position.x + "px";
- this.style.height = this.size.y + "px";
- this.style.width = this.size.x + "px";
- $.setElementOpacity( this.element, this.opacity );
- },
- /**
- * Renders the tile in a canvas-based context.
- * @function
- * @param {Canvas} context
- * @param {Function} drawingHandler - Method for firing the drawing event.
- * drawingHandler({context, tile, rendered})
- * where <code>rendered</code> is the context with the pre-drawn image.
- * @param {Number} [scale=1] - Apply a scale to position and size
- * @param {OpenSeadragon.Point} [translate] - A translation vector
- */
- drawCanvas: function( context, drawingHandler, scale, translate ) {
- var position = this.position.times($.pixelDensityRatio),
- size = this.size.times($.pixelDensityRatio),
- rendered;
- if (!this.context2D && !this.cacheImageRecord) {
- $.console.warn(
- '[Tile.drawCanvas] attempting to draw tile %s when it\'s not cached',
- this.toString());
- return;
- }
- rendered = this.context2D || this.cacheImageRecord.getRenderedContext();
- if ( !this.loaded || !rendered ){
- $.console.warn(
- "Attempting to draw tile %s when it's not yet loaded.",
- this.toString()
- );
- return;
- }
- context.save();
- context.globalAlpha = this.opacity;
- if (typeof scale === 'number' && scale !== 1) {
- // draw tile at a different scale
- position = position.times(scale);
- size = size.times(scale);
- }
- if (translate instanceof $.Point) {
- // shift tile position slightly
- position = position.plus(translate);
- }
- //if we are supposed to be rendering fully opaque rectangle,
- //ie its done fading or fading is turned off, and if we are drawing
- //an image with an alpha channel, then the only way
- //to avoid seeing the tile underneath is to clear the rectangle
- if (context.globalAlpha === 1 && this._hasTransparencyChannel()) {
- //clearing only the inside of the rectangle occupied
- //by the png prevents edge flikering
- context.clearRect(
- position.x,
- position.y,
- size.x,
- size.y
- );
- }
- // This gives the application a chance to make image manipulation
- // changes as we are rendering the image
- drawingHandler({context: context, tile: this, rendered: rendered});
- var sourceWidth, sourceHeight;
- if (this.sourceBounds) {
- sourceWidth = Math.min(this.sourceBounds.width, rendered.canvas.width);
- sourceHeight = Math.min(this.sourceBounds.height, rendered.canvas.height);
- } else {
- sourceWidth = rendered.canvas.width;
- sourceHeight = rendered.canvas.height;
- }
- context.drawImage(
- rendered.canvas,
- 0,
- 0,
- sourceWidth,
- sourceHeight,
- position.x,
- position.y,
- size.x,
- size.y
- );
- context.restore();
- },
- /**
- * Get the ratio between current and original size.
- * @function
- * @return {Float}
- */
- getScaleForEdgeSmoothing: function() {
- var context;
- if (this.cacheImageRecord) {
- context = this.cacheImageRecord.getRenderedContext();
- } else if (this.context2D) {
- context = this.context2D;
- } else {
- $.console.warn(
- '[Tile.drawCanvas] attempting to get tile scale %s when tile\'s not cached',
- this.toString());
- return 1;
- }
- return context.canvas.width / (this.size.x * $.pixelDensityRatio);
- },
- /**
- * Get a translation vector that when applied to the tile position produces integer coordinates.
- * Needed to avoid swimming and twitching.
- * @function
- * @param {Number} [scale=1] - Scale to be applied to position.
- * @return {OpenSeadragon.Point}
- */
- getTranslationForEdgeSmoothing: function(scale, canvasSize, sketchCanvasSize) {
- // The translation vector must have positive values, otherwise the image goes a bit off
- // the sketch canvas to the top and left and we must use negative coordinates to repaint it
- // to the main canvas. In that case, some browsers throw:
- // INDEX_SIZE_ERR: DOM Exception 1: Index or size was negative, or greater than the allowed value.
- var x = Math.max(1, Math.ceil((sketchCanvasSize.x - canvasSize.x) / 2));
- var y = Math.max(1, Math.ceil((sketchCanvasSize.y - canvasSize.y) / 2));
- return new $.Point(x, y).minus(
- this.position
- .times($.pixelDensityRatio)
- .times(scale || 1)
- .apply(function(x) {
- return x % 1;
- })
- );
- },
- /**
- * Removes tile from its container.
- * @function
- */
- unload: function() {
- if ( this.imgElement && this.imgElement.parentNode ) {
- this.imgElement.parentNode.removeChild( this.imgElement );
- }
- if ( this.element && this.element.parentNode ) {
- this.element.parentNode.removeChild( this.element );
- }
- this.element = null;
- this.imgElement = null;
- this.loaded = false;
- this.loading = false;
- }
- };
- }( OpenSeadragon ));
- /*
- * OpenSeadragon - Overlay
- *
- * Copyright (C) 2009 CodePlex Foundation
- * Copyright (C) 2010-2013 OpenSeadragon contributors
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are
- * met:
- *
- * - Redistributions of source code must retain the above copyright notice,
- * this list of conditions and the following disclaimer.
- *
- * - Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * - Neither the name of CodePlex Foundation nor the names of its
- * contributors may be used to endorse or promote products derived from
- * this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
- * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
- * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
- * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
- (function($) {
- /**
- * An enumeration of positions that an overlay may be assigned relative to
- * the viewport.
- * It is identical to OpenSeadragon.Placement but is kept for backward
- * compatibility.
- * @member OverlayPlacement
- * @memberof OpenSeadragon
- * @see OpenSeadragon.Placement
- * @static
- * @readonly
- * @type {Object}
- * @property {Number} CENTER
- * @property {Number} TOP_LEFT
- * @property {Number} TOP
- * @property {Number} TOP_RIGHT
- * @property {Number} RIGHT
- * @property {Number} BOTTOM_RIGHT
- * @property {Number} BOTTOM
- * @property {Number} BOTTOM_LEFT
- * @property {Number} LEFT
- */
- $.OverlayPlacement = $.Placement;
- /**
- * An enumeration of possible ways to handle overlays rotation
- * @member OverlayRotationMode
- * @memberOf OpenSeadragon
- * @static
- * @readonly
- * @property {Number} NO_ROTATION The overlay ignore the viewport rotation.
- * @property {Number} EXACT The overlay use CSS 3 transforms to rotate with
- * the viewport. If the overlay contains text, it will get rotated as well.
- * @property {Number} BOUNDING_BOX The overlay adjusts for rotation by
- * taking the size of the bounding box of the rotated bounds.
- * Only valid for overlays with Rect location and scalable in both directions.
- */
- $.OverlayRotationMode = $.freezeObject({
- NO_ROTATION: 1,
- EXACT: 2,
- BOUNDING_BOX: 3
- });
- /**
- * @class Overlay
- * @classdesc Provides a way to float an HTML element on top of the viewer element.
- *
- * @memberof OpenSeadragon
- * @param {Object} options
- * @param {Element} options.element
- * @param {OpenSeadragon.Point|OpenSeadragon.Rect} options.location - The
- * location of the overlay on the image. If a {@link OpenSeadragon.Point}
- * is specified, the overlay will be located at this location with respect
- * to the placement option. If a {@link OpenSeadragon.Rect} is specified,
- * the overlay will be placed at this location with the corresponding width
- * and height and placement TOP_LEFT.
- * @param {OpenSeadragon.Placement} [options.placement=OpenSeadragon.Placement.TOP_LEFT]
- * Defines what part of the overlay should be at the specified options.location
- * @param {OpenSeadragon.Overlay.OnDrawCallback} [options.onDraw]
- * @param {Boolean} [options.checkResize=true] Set to false to avoid to
- * check the size of the overlay every time it is drawn in the directions
- * which are not scaled. It will improve performances but will cause a
- * misalignment if the overlay size changes.
- * @param {Number} [options.width] The width of the overlay in viewport
- * coordinates. If specified, the width of the overlay will be adjusted when
- * the zoom changes.
- * @param {Number} [options.height] The height of the overlay in viewport
- * coordinates. If specified, the height of the overlay will be adjusted when
- * the zoom changes.
- * @param {Boolean} [options.rotationMode=OpenSeadragon.OverlayRotationMode.EXACT]
- * How to handle the rotation of the viewport.
- */
- $.Overlay = function(element, location, placement) {
- /**
- * onDraw callback signature used by {@link OpenSeadragon.Overlay}.
- *
- * @callback OnDrawCallback
- * @memberof OpenSeadragon.Overlay
- * @param {OpenSeadragon.Point} position
- * @param {OpenSeadragon.Point} size
- * @param {Element} element
- */
- var options;
- if ($.isPlainObject(element)) {
- options = element;
- } else {
- options = {
- element: element,
- location: location,
- placement: placement
- };
- }
- this.element = options.element;
- this.style = options.element.style;
- this._init(options);
- };
- /** @lends OpenSeadragon.Overlay.prototype */
- $.Overlay.prototype = {
- // private
- _init: function(options) {
- this.location = options.location;
- this.placement = options.placement === undefined ?
- $.Placement.TOP_LEFT : options.placement;
- this.onDraw = options.onDraw;
- this.checkResize = options.checkResize === undefined ?
- true : options.checkResize;
- // When this.width is not null, the overlay get scaled horizontally
- this.width = options.width === undefined ? null : options.width;
- // When this.height is not null, the overlay get scaled vertically
- this.height = options.height === undefined ? null : options.height;
- this.rotationMode = options.rotationMode || $.OverlayRotationMode.EXACT;
- // Having a rect as location is a syntactic sugar
- if (this.location instanceof $.Rect) {
- this.width = this.location.width;
- this.height = this.location.height;
- this.location = this.location.getTopLeft();
- this.placement = $.Placement.TOP_LEFT;
- }
- // Deprecated properties kept for backward compatibility.
- this.scales = this.width !== null && this.height !== null;
- this.bounds = new $.Rect(
- this.location.x, this.location.y, this.width, this.height);
- this.position = this.location;
- },
- /**
- * Internal function to adjust the position of an overlay
- * depending on it size and placement.
- * @function
- * @param {OpenSeadragon.Point} position
- * @param {OpenSeadragon.Point} size
- */
- adjust: function(position, size) {
- var properties = $.Placement.properties[this.placement];
- if (!properties) {
- return;
- }
- if (properties.isHorizontallyCentered) {
- position.x -= size.x / 2;
- } else if (properties.isRight) {
- position.x -= size.x;
- }
- if (properties.isVerticallyCentered) {
- position.y -= size.y / 2;
- } else if (properties.isBottom) {
- position.y -= size.y;
- }
- },
- /**
- * @function
- */
- destroy: function() {
- var element = this.element;
- var style = this.style;
- if (element.parentNode) {
- element.parentNode.removeChild(element);
- //this should allow us to preserve overlays when required between
- //pages
- if (element.prevElementParent) {
- style.display = 'none';
- //element.prevElementParent.insertBefore(
- // element,
- // element.prevNextSibling
- //);
- document.body.appendChild(element);
- }
- }
- // clear the onDraw callback
- this.onDraw = null;
- style.top = "";
- style.left = "";
- style.position = "";
- if (this.width !== null) {
- style.width = "";
- }
- if (this.height !== null) {
- style.height = "";
- }
- var transformOriginProp = $.getCssPropertyWithVendorPrefix(
- 'transformOrigin');
- var transformProp = $.getCssPropertyWithVendorPrefix(
- 'transform');
- if (transformOriginProp && transformProp) {
- style[transformOriginProp] = "";
- style[transformProp] = "";
- }
- },
- /**
- * @function
- * @param {Element} container
- */
- drawHTML: function(container, viewport) {
- var element = this.element;
- if (element.parentNode !== container) {
- //save the source parent for later if we need it
- element.prevElementParent = element.parentNode;
- element.prevNextSibling = element.nextSibling;
- container.appendChild(element);
- // have to set position before calculating size, fix #1116
- this.style.position = "absolute";
- // this.size is used by overlays which don't get scaled in at
- // least one direction when this.checkResize is set to false.
- this.size = $.getElementSize(element);
- }
- var positionAndSize = this._getOverlayPositionAndSize(viewport);
- var position = positionAndSize.position;
- var size = this.size = positionAndSize.size;
- var rotate = positionAndSize.rotate;
- // call the onDraw callback if it exists to allow one to overwrite
- // the drawing/positioning/sizing of the overlay
- if (this.onDraw) {
- this.onDraw(position, size, this.element);
- } else {
- var style = this.style;
- style.left = position.x + "px";
- style.top = position.y + "px";
- if (this.width !== null) {
- style.width = size.x + "px";
- }
- if (this.height !== null) {
- style.height = size.y + "px";
- }
- var transformOriginProp = $.getCssPropertyWithVendorPrefix(
- 'transformOrigin');
- var transformProp = $.getCssPropertyWithVendorPrefix(
- 'transform');
- if (transformOriginProp && transformProp) {
- if (rotate) {
- style[transformOriginProp] = this._getTransformOrigin();
- style[transformProp] = "rotate(" + rotate + "deg)";
- } else {
- style[transformOriginProp] = "";
- style[transformProp] = "";
- }
- }
- if (style.display !== 'none') {
- style.display = 'block';
- }
- }
- },
- // private
- _getOverlayPositionAndSize: function(viewport) {
- var position = viewport.pixelFromPoint(this.location, true);
- var size = this._getSizeInPixels(viewport);
- this.adjust(position, size);
- var rotate = 0;
- if (viewport.degrees &&
- this.rotationMode !== $.OverlayRotationMode.NO_ROTATION) {
- // BOUNDING_BOX is only valid if both directions get scaled.
- // Get replaced by EXACT otherwise.
- if (this.rotationMode === $.OverlayRotationMode.BOUNDING_BOX &&
- this.width !== null && this.height !== null) {
- var rect = new $.Rect(position.x, position.y, size.x, size.y);
- var boundingBox = this._getBoundingBox(rect, viewport.degrees);
- position = boundingBox.getTopLeft();
- size = boundingBox.getSize();
- } else {
- rotate = viewport.degrees;
- }
- }
- return {
- position: position,
- size: size,
- rotate: rotate
- };
- },
- // private
- _getSizeInPixels: function(viewport) {
- var width = this.size.x;
- var height = this.size.y;
- if (this.width !== null || this.height !== null) {
- var scaledSize = viewport.deltaPixelsFromPointsNoRotate(
- new $.Point(this.width || 0, this.height || 0), true);
- if (this.width !== null) {
- width = scaledSize.x;
- }
- if (this.height !== null) {
- height = scaledSize.y;
- }
- }
- if (this.checkResize &&
- (this.width === null || this.height === null)) {
- var eltSize = this.size = $.getElementSize(this.element);
- if (this.width === null) {
- width = eltSize.x;
- }
- if (this.height === null) {
- height = eltSize.y;
- }
- }
- return new $.Point(width, height);
- },
- // private
- _getBoundingBox: function(rect, degrees) {
- var refPoint = this._getPlacementPoint(rect);
- return rect.rotate(degrees, refPoint).getBoundingBox();
- },
- // private
- _getPlacementPoint: function(rect) {
- var result = new $.Point(rect.x, rect.y);
- var properties = $.Placement.properties[this.placement];
- if (properties) {
- if (properties.isHorizontallyCentered) {
- result.x += rect.width / 2;
- } else if (properties.isRight) {
- result.x += rect.width;
- }
- if (properties.isVerticallyCentered) {
- result.y += rect.height / 2;
- } else if (properties.isBottom) {
- result.y += rect.height;
- }
- }
- return result;
- },
- // private
- _getTransformOrigin: function() {
- var result = "";
- var properties = $.Placement.properties[this.placement];
- if (!properties) {
- return result;
- }
- if (properties.isLeft) {
- result = "left";
- } else if (properties.isRight) {
- result = "right";
- }
- if (properties.isTop) {
- result += " top";
- } else if (properties.isBottom) {
- result += " bottom";
- }
- return result;
- },
- /**
- * Changes the overlay settings.
- * @function
- * @param {OpenSeadragon.Point|OpenSeadragon.Rect|Object} location
- * If an object is specified, the options are the same than the constructor
- * except for the element which can not be changed.
- * @param {OpenSeadragon.Placement} placement
- */
- update: function(location, placement) {
- var options = $.isPlainObject(location) ? location : {
- location: location,
- placement: placement
- };
- this._init({
- location: options.location || this.location,
- placement: options.placement !== undefined ?
- options.placement : this.placement,
- onDraw: options.onDraw || this.onDraw,
- checkResize: options.checkResize || this.checkResize,
- width: options.width !== undefined ? options.width : this.width,
- height: options.height !== undefined ? options.height : this.height,
- rotationMode: options.rotationMode || this.rotationMode
- });
- },
- /**
- * Returns the current bounds of the overlay in viewport coordinates
- * @function
- * @param {OpenSeadragon.Viewport} viewport the viewport
- * @returns {OpenSeadragon.Rect} overlay bounds
- */
- getBounds: function(viewport) {
- $.console.assert(viewport,
- 'A viewport must now be passed to Overlay.getBounds.');
- var width = this.width;
- var height = this.height;
- if (width === null || height === null) {
- var size = viewport.deltaPointsFromPixelsNoRotate(this.size, true);
- if (width === null) {
- width = size.x;
- }
- if (height === null) {
- height = size.y;
- }
- }
- var location = this.location.clone();
- this.adjust(location, new $.Point(width, height));
- return this._adjustBoundsForRotation(
- viewport, new $.Rect(location.x, location.y, width, height));
- },
- // private
- _adjustBoundsForRotation: function(viewport, bounds) {
- if (!viewport ||
- viewport.degrees === 0 ||
- this.rotationMode === $.OverlayRotationMode.EXACT) {
- return bounds;
- }
- if (this.rotationMode === $.OverlayRotationMode.BOUNDING_BOX) {
- // If overlay not fully scalable, BOUNDING_BOX falls back to EXACT
- if (this.width === null || this.height === null) {
- return bounds;
- }
- // It is easier to just compute the position and size and
- // convert to viewport coordinates.
- var positionAndSize = this._getOverlayPositionAndSize(viewport);
- return viewport.viewerElementToViewportRectangle(new $.Rect(
- positionAndSize.position.x,
- positionAndSize.position.y,
- positionAndSize.size.x,
- positionAndSize.size.y));
- }
- // NO_ROTATION case
- return bounds.rotate(-viewport.degrees,
- this._getPlacementPoint(bounds));
- }
- };
- }(OpenSeadragon));
- /*
- * OpenSeadragon - Drawer
- *
- * Copyright (C) 2009 CodePlex Foundation
- * Copyright (C) 2010-2013 OpenSeadragon contributors
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are
- * met:
- *
- * - Redistributions of source code must retain the above copyright notice,
- * this list of conditions and the following disclaimer.
- *
- * - Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * - Neither the name of CodePlex Foundation nor the names of its
- * contributors may be used to endorse or promote products derived from
- * this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
- * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
- * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
- * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
- (function( $ ){
- /**
- * @class Drawer
- * @memberof OpenSeadragon
- * @classdesc Handles rendering of tiles for an {@link OpenSeadragon.Viewer}.
- * @param {Object} options - Options for this Drawer.
- * @param {OpenSeadragon.Viewer} options.viewer - The Viewer that owns this Drawer.
- * @param {OpenSeadragon.Viewport} options.viewport - Reference to Viewer viewport.
- * @param {Element} options.element - Parent element.
- * @param {Number} [options.debugGridColor] - See debugGridColor in {@link OpenSeadragon.Options} for details.
- */
- $.Drawer = function( options ) {
- $.console.assert( options.viewer, "[Drawer] options.viewer is required" );
- //backward compatibility for positional args while preferring more
- //idiomatic javascript options object as the only argument
- var args = arguments;
- if( !$.isPlainObject( options ) ){
- options = {
- source: args[ 0 ], // Reference to Viewer tile source.
- viewport: args[ 1 ], // Reference to Viewer viewport.
- element: args[ 2 ] // Parent element.
- };
- }
- $.console.assert( options.viewport, "[Drawer] options.viewport is required" );
- $.console.assert( options.element, "[Drawer] options.element is required" );
- if ( options.source ) {
- $.console.error( "[Drawer] options.source is no longer accepted; use TiledImage instead" );
- }
- this.viewer = options.viewer;
- this.viewport = options.viewport;
- this.debugGridColor = typeof options.debugGridColor === 'string' ? [options.debugGridColor] : options.debugGridColor || $.DEFAULT_SETTINGS.debugGridColor;
- if (options.opacity) {
- $.console.error( "[Drawer] options.opacity is no longer accepted; set the opacity on the TiledImage instead" );
- }
- this.useCanvas = $.supportsCanvas && ( this.viewer ? this.viewer.useCanvas : true );
- /**
- * The parent element of this Drawer instance, passed in when the Drawer was created.
- * The parent of {@link OpenSeadragon.Drawer#canvas}.
- * @member {Element} container
- * @memberof OpenSeadragon.Drawer#
- */
- this.container = $.getElement( options.element );
- /**
- * A <canvas> element if the browser supports them, otherwise a <div> element.
- * Child element of {@link OpenSeadragon.Drawer#container}.
- * @member {Element} canvas
- * @memberof OpenSeadragon.Drawer#
- */
- this.canvas = $.makeNeutralElement( this.useCanvas ? "canvas" : "div" );
- /**
- * 2d drawing context for {@link OpenSeadragon.Drawer#canvas} if it's a <canvas> element, otherwise null.
- * @member {Object} context
- * @memberof OpenSeadragon.Drawer#
- */
- this.context = this.useCanvas ? this.canvas.getContext( "2d" ) : null;
- /**
- * Sketch canvas used to temporarily draw tiles which cannot be drawn directly
- * to the main canvas due to opacity. Lazily initialized.
- */
- this.sketchCanvas = null;
- this.sketchContext = null;
- /**
- * @member {Element} element
- * @memberof OpenSeadragon.Drawer#
- * @deprecated Alias for {@link OpenSeadragon.Drawer#container}.
- */
- this.element = this.container;
- // We force our container to ltr because our drawing math doesn't work in rtl.
- // This issue only affects our canvas renderer, but we do it always for consistency.
- // Note that this means overlays you want to be rtl need to be explicitly set to rtl.
- this.container.dir = 'ltr';
- // check canvas available width and height, set canvas width and height such that the canvas backing store is set to the proper pixel density
- if (this.useCanvas) {
- var viewportSize = this._calculateCanvasSize();
- this.canvas.width = viewportSize.x;
- this.canvas.height = viewportSize.y;
- }
- this.canvas.style.width = "100%";
- this.canvas.style.height = "100%";
- this.canvas.style.position = "absolute";
- $.setElementOpacity( this.canvas, this.opacity, true );
- // explicit left-align
- this.container.style.textAlign = "left";
- this.container.appendChild( this.canvas );
- // Image smoothing for canvas rendering (only if canvas is used).
- // Canvas default is "true", so this will only be changed if user specified "false".
- this._imageSmoothingEnabled = true;
- };
- /** @lends OpenSeadragon.Drawer.prototype */
- $.Drawer.prototype = {
- // deprecated
- addOverlay: function( element, location, placement, onDraw ) {
- $.console.error("drawer.addOverlay is deprecated. Use viewer.addOverlay instead.");
- this.viewer.addOverlay( element, location, placement, onDraw );
- return this;
- },
- // deprecated
- updateOverlay: function( element, location, placement ) {
- $.console.error("drawer.updateOverlay is deprecated. Use viewer.updateOverlay instead.");
- this.viewer.updateOverlay( element, location, placement );
- return this;
- },
- // deprecated
- removeOverlay: function( element ) {
- $.console.error("drawer.removeOverlay is deprecated. Use viewer.removeOverlay instead.");
- this.viewer.removeOverlay( element );
- return this;
- },
- // deprecated
- clearOverlays: function() {
- $.console.error("drawer.clearOverlays is deprecated. Use viewer.clearOverlays instead.");
- this.viewer.clearOverlays();
- return this;
- },
- /**
- * Set the opacity of the drawer.
- * @param {Number} opacity
- * @return {OpenSeadragon.Drawer} Chainable.
- */
- setOpacity: function( opacity ) {
- $.console.error("drawer.setOpacity is deprecated. Use tiledImage.setOpacity instead.");
- var world = this.viewer.world;
- for (var i = 0; i < world.getItemCount(); i++) {
- world.getItemAt( i ).setOpacity( opacity );
- }
- return this;
- },
- /**
- * Get the opacity of the drawer.
- * @returns {Number}
- */
- getOpacity: function() {
- $.console.error("drawer.getOpacity is deprecated. Use tiledImage.getOpacity instead.");
- var world = this.viewer.world;
- var maxOpacity = 0;
- for (var i = 0; i < world.getItemCount(); i++) {
- var opacity = world.getItemAt( i ).getOpacity();
- if ( opacity > maxOpacity ) {
- maxOpacity = opacity;
- }
- }
- return maxOpacity;
- },
- // deprecated
- needsUpdate: function() {
- $.console.error( "[Drawer.needsUpdate] this function is deprecated. Use World.needsDraw instead." );
- return this.viewer.world.needsDraw();
- },
- // deprecated
- numTilesLoaded: function() {
- $.console.error( "[Drawer.numTilesLoaded] this function is deprecated. Use TileCache.numTilesLoaded instead." );
- return this.viewer.tileCache.numTilesLoaded();
- },
- // deprecated
- reset: function() {
- $.console.error( "[Drawer.reset] this function is deprecated. Use World.resetItems instead." );
- this.viewer.world.resetItems();
- return this;
- },
- // deprecated
- update: function() {
- $.console.error( "[Drawer.update] this function is deprecated. Use Drawer.clear and World.draw instead." );
- this.clear();
- this.viewer.world.draw();
- return this;
- },
- /**
- * @return {Boolean} True if rotation is supported.
- */
- canRotate: function() {
- return this.useCanvas;
- },
- /**
- * Destroy the drawer (unload current loaded tiles)
- */
- destroy: function() {
- //force unloading of current canvas (1x1 will be gc later, trick not necessarily needed)
- this.canvas.width = 1;
- this.canvas.height = 1;
- this.sketchCanvas = null;
- this.sketchContext = null;
- },
- /**
- * Clears the Drawer so it's ready to draw another frame.
- */
- clear: function() {
- this.canvas.innerHTML = "";
- if ( this.useCanvas ) {
- var viewportSize = this._calculateCanvasSize();
- if( this.canvas.width != viewportSize.x ||
- this.canvas.height != viewportSize.y ) {
- this.canvas.width = viewportSize.x;
- this.canvas.height = viewportSize.y;
- this._updateImageSmoothingEnabled(this.context);
- if ( this.sketchCanvas !== null ) {
- var sketchCanvasSize = this._calculateSketchCanvasSize();
- this.sketchCanvas.width = sketchCanvasSize.x;
- this.sketchCanvas.height = sketchCanvasSize.y;
- this._updateImageSmoothingEnabled(this.sketchContext);
- }
- }
- this._clear();
- }
- },
- _clear: function (useSketch, bounds) {
- if (!this.useCanvas) {
- return;
- }
- var context = this._getContext(useSketch);
- if (bounds) {
- context.clearRect(bounds.x, bounds.y, bounds.width, bounds.height);
- } else {
- var canvas = context.canvas;
- context.clearRect(0, 0, canvas.width, canvas.height);
- }
- },
- /**
- * Scale from OpenSeadragon viewer rectangle to drawer rectangle
- * (ignoring rotation)
- * @param {OpenSeadragon.Rect} rectangle - The rectangle in viewport coordinate system.
- * @return {OpenSeadragon.Rect} Rectangle in drawer coordinate system.
- */
- viewportToDrawerRectangle: function(rectangle) {
- var topLeft = this.viewport.pixelFromPointNoRotate(rectangle.getTopLeft(), true);
- var size = this.viewport.deltaPixelsFromPointsNoRotate(rectangle.getSize(), true);
- return new $.Rect(
- topLeft.x * $.pixelDensityRatio,
- topLeft.y * $.pixelDensityRatio,
- size.x * $.pixelDensityRatio,
- size.y * $.pixelDensityRatio
- );
- },
- /**
- * Draws the given tile.
- * @param {OpenSeadragon.Tile} tile - The tile to draw.
- * @param {Function} drawingHandler - Method for firing the drawing event if using canvas.
- * drawingHandler({context, tile, rendered})
- * @param {Boolean} useSketch - Whether to use the sketch canvas or not.
- * where <code>rendered</code> is the context with the pre-drawn image.
- * @param {Float} [scale=1] - Apply a scale to tile position and size. Defaults to 1.
- * @param {OpenSeadragon.Point} [translate] A translation vector to offset tile position
- */
- drawTile: function(tile, drawingHandler, useSketch, scale, translate) {
- $.console.assert(tile, '[Drawer.drawTile] tile is required');
- $.console.assert(drawingHandler, '[Drawer.drawTile] drawingHandler is required');
- if (this.useCanvas) {
- var context = this._getContext(useSketch);
- scale = scale || 1;
- tile.drawCanvas(context, drawingHandler, scale, translate);
- } else {
- tile.drawHTML( this.canvas );
- }
- },
- _getContext: function( useSketch ) {
- var context = this.context;
- if ( useSketch ) {
- if (this.sketchCanvas === null) {
- this.sketchCanvas = document.createElement( "canvas" );
- var sketchCanvasSize = this._calculateSketchCanvasSize();
- this.sketchCanvas.width = sketchCanvasSize.x;
- this.sketchCanvas.height = sketchCanvasSize.y;
- this.sketchContext = this.sketchCanvas.getContext( "2d" );
- // If the viewport is not currently rotated, the sketchCanvas
- // will have the same size as the main canvas. However, if
- // the viewport get rotated later on, we will need to resize it.
- if (this.viewport.getRotation() === 0) {
- var self = this;
- this.viewer.addHandler('rotate', function resizeSketchCanvas() {
- if (self.viewport.getRotation() === 0) {
- return;
- }
- self.viewer.removeHandler('rotate', resizeSketchCanvas);
- var sketchCanvasSize = self._calculateSketchCanvasSize();
- self.sketchCanvas.width = sketchCanvasSize.x;
- self.sketchCanvas.height = sketchCanvasSize.y;
- });
- }
- this._updateImageSmoothingEnabled(this.sketchContext);
- }
- context = this.sketchContext;
- }
- return context;
- },
- // private
- saveContext: function( useSketch ) {
- if (!this.useCanvas) {
- return;
- }
- this._getContext( useSketch ).save();
- },
- // private
- restoreContext: function( useSketch ) {
- if (!this.useCanvas) {
- return;
- }
- this._getContext( useSketch ).restore();
- },
- // private
- setClip: function(rect, useSketch) {
- if (!this.useCanvas) {
- return;
- }
- var context = this._getContext( useSketch );
- context.beginPath();
- context.rect(rect.x, rect.y, rect.width, rect.height);
- context.clip();
- },
- // private
- drawRectangle: function(rect, fillStyle, useSketch) {
- if (!this.useCanvas) {
- return;
- }
- var context = this._getContext( useSketch );
- context.save();
- context.fillStyle = fillStyle;
- context.fillRect(rect.x, rect.y, rect.width, rect.height);
- context.restore();
- },
- /**
- * Blends the sketch canvas in the main canvas.
- * @param {Object} options The options
- * @param {Float} options.opacity The opacity of the blending.
- * @param {Float} [options.scale=1] The scale at which tiles were drawn on
- * the sketch. Default is 1.
- * Use scale to draw at a lower scale and then enlarge onto the main canvas.
- * @param {OpenSeadragon.Point} [options.translate] A translation vector
- * that was used to draw the tiles
- * @param {String} [options.compositeOperation] - How the image is
- * composited onto other images; see compositeOperation in
- * {@link OpenSeadragon.Options} for possible values.
- * @param {OpenSeadragon.Rect} [options.bounds] The part of the sketch
- * canvas to blend in the main canvas. If specified, options.scale and
- * options.translate get ignored.
- */
- blendSketch: function(opacity, scale, translate, compositeOperation) {
- var options = opacity;
- if (!$.isPlainObject(options)) {
- options = {
- opacity: opacity,
- scale: scale,
- translate: translate,
- compositeOperation: compositeOperation
- };
- }
- if (!this.useCanvas || !this.sketchCanvas) {
- return;
- }
- opacity = options.opacity;
- compositeOperation = options.compositeOperation;
- var bounds = options.bounds;
- this.context.save();
- this.context.globalAlpha = opacity;
- if (compositeOperation) {
- this.context.globalCompositeOperation = compositeOperation;
- }
- if (bounds) {
- // Internet Explorer, Microsoft Edge, and Safari have problems
- // when you call context.drawImage with negative x or y
- // or x + width or y + height greater than the canvas width or height respectively.
- if (bounds.x < 0) {
- bounds.width += bounds.x;
- bounds.x = 0;
- }
- if (bounds.x + bounds.width > this.canvas.width) {
- bounds.width = this.canvas.width - bounds.x;
- }
- if (bounds.y < 0) {
- bounds.height += bounds.y;
- bounds.y = 0;
- }
- if (bounds.y + bounds.height > this.canvas.height) {
- bounds.height = this.canvas.height - bounds.y;
- }
- this.context.drawImage(
- this.sketchCanvas,
- bounds.x,
- bounds.y,
- bounds.width,
- bounds.height,
- bounds.x,
- bounds.y,
- bounds.width,
- bounds.height
- );
- } else {
- scale = options.scale || 1;
- translate = options.translate;
- var position = translate instanceof $.Point ?
- translate : new $.Point(0, 0);
- var widthExt = 0;
- var heightExt = 0;
- if (translate) {
- var widthDiff = this.sketchCanvas.width - this.canvas.width;
- var heightDiff = this.sketchCanvas.height - this.canvas.height;
- widthExt = Math.round(widthDiff / 2);
- heightExt = Math.round(heightDiff / 2);
- }
- this.context.drawImage(
- this.sketchCanvas,
- position.x - widthExt * scale,
- position.y - heightExt * scale,
- (this.canvas.width + 2 * widthExt) * scale,
- (this.canvas.height + 2 * heightExt) * scale,
- -widthExt,
- -heightExt,
- this.canvas.width + 2 * widthExt,
- this.canvas.height + 2 * heightExt
- );
- }
- this.context.restore();
- },
- // private
- drawDebugInfo: function(tile, count, i, tiledImage) {
- if ( !this.useCanvas ) {
- return;
- }
- var colorIndex = this.viewer.world.getIndexOfItem(tiledImage) % this.debugGridColor.length;
- var context = this.context;
- context.save();
- context.lineWidth = 2 * $.pixelDensityRatio;
- context.font = 'small-caps bold ' + (13 * $.pixelDensityRatio) + 'px arial';
- context.strokeStyle = this.debugGridColor[colorIndex];
- context.fillStyle = this.debugGridColor[colorIndex];
- if ( this.viewport.degrees !== 0 ) {
- this._offsetForRotation({degrees: this.viewport.degrees});
- }
- if (tiledImage.getRotation(true) % 360 !== 0) {
- this._offsetForRotation({
- degrees: tiledImage.getRotation(true),
- point: tiledImage.viewport.pixelFromPointNoRotate(
- tiledImage._getRotationPoint(true), true)
- });
- }
- if (tiledImage.viewport.degrees === 0 && tiledImage.getRotation(true) % 360 === 0){
- if(tiledImage._drawer.viewer.viewport.getFlip()) {
- tiledImage._drawer._flip();
- }
- }
- context.strokeRect(
- tile.position.x * $.pixelDensityRatio,
- tile.position.y * $.pixelDensityRatio,
- tile.size.x * $.pixelDensityRatio,
- tile.size.y * $.pixelDensityRatio
- );
- var tileCenterX = (tile.position.x + (tile.size.x / 2)) * $.pixelDensityRatio;
- var tileCenterY = (tile.position.y + (tile.size.y / 2)) * $.pixelDensityRatio;
- // Rotate the text the right way around.
- context.translate( tileCenterX, tileCenterY );
- context.rotate( Math.PI / 180 * -this.viewport.degrees );
- context.translate( -tileCenterX, -tileCenterY );
- if( tile.x === 0 && tile.y === 0 ){
- context.fillText(
- "Zoom: " + this.viewport.getZoom(),
- tile.position.x * $.pixelDensityRatio,
- (tile.position.y - 30) * $.pixelDensityRatio
- );
- context.fillText(
- "Pan: " + this.viewport.getBounds().toString(),
- tile.position.x * $.pixelDensityRatio,
- (tile.position.y - 20) * $.pixelDensityRatio
- );
- }
- context.fillText(
- "Level: " + tile.level,
- (tile.position.x + 10) * $.pixelDensityRatio,
- (tile.position.y + 20) * $.pixelDensityRatio
- );
- context.fillText(
- "Column: " + tile.x,
- (tile.position.x + 10) * $.pixelDensityRatio,
- (tile.position.y + 30) * $.pixelDensityRatio
- );
- context.fillText(
- "Row: " + tile.y,
- (tile.position.x + 10) * $.pixelDensityRatio,
- (tile.position.y + 40) * $.pixelDensityRatio
- );
- context.fillText(
- "Order: " + i + " of " + count,
- (tile.position.x + 10) * $.pixelDensityRatio,
- (tile.position.y + 50) * $.pixelDensityRatio
- );
- context.fillText(
- "Size: " + tile.size.toString(),
- (tile.position.x + 10) * $.pixelDensityRatio,
- (tile.position.y + 60) * $.pixelDensityRatio
- );
- context.fillText(
- "Position: " + tile.position.toString(),
- (tile.position.x + 10) * $.pixelDensityRatio,
- (tile.position.y + 70) * $.pixelDensityRatio
- );
- if ( this.viewport.degrees !== 0 ) {
- this._restoreRotationChanges();
- }
- if (tiledImage.getRotation(true) % 360 !== 0) {
- this._restoreRotationChanges();
- }
- if (tiledImage.viewport.degrees === 0 && tiledImage.getRotation(true) % 360 === 0){
- if(tiledImage._drawer.viewer.viewport.getFlip()) {
- tiledImage._drawer._flip();
- }
- }
- context.restore();
- },
- // private
- debugRect: function(rect) {
- if ( this.useCanvas ) {
- var context = this.context;
- context.save();
- context.lineWidth = 2 * $.pixelDensityRatio;
- context.strokeStyle = this.debugGridColor[0];
- context.fillStyle = this.debugGridColor[0];
- context.strokeRect(
- rect.x * $.pixelDensityRatio,
- rect.y * $.pixelDensityRatio,
- rect.width * $.pixelDensityRatio,
- rect.height * $.pixelDensityRatio
- );
- context.restore();
- }
- },
- /**
- * Turns image smoothing on or off for this viewer. Note: Ignored in some (especially older) browsers that do not support this property.
- *
- * @function
- * @param {Boolean} [imageSmoothingEnabled] - Whether or not the image is
- * drawn smoothly on the canvas; see imageSmoothingEnabled in
- * {@link OpenSeadragon.Options} for more explanation.
- */
- setImageSmoothingEnabled: function(imageSmoothingEnabled){
- if ( this.useCanvas ) {
- this._imageSmoothingEnabled = imageSmoothingEnabled;
- this._updateImageSmoothingEnabled(this.context);
- this.viewer.forceRedraw();
- }
- },
- // private
- _updateImageSmoothingEnabled: function(context){
- context.mozImageSmoothingEnabled = this._imageSmoothingEnabled;
- context.webkitImageSmoothingEnabled = this._imageSmoothingEnabled;
- context.msImageSmoothingEnabled = this._imageSmoothingEnabled;
- context.imageSmoothingEnabled = this._imageSmoothingEnabled;
- },
- /**
- * Get the canvas size
- * @param {Boolean} sketch If set to true return the size of the sketch canvas
- * @returns {OpenSeadragon.Point} The size of the canvas
- */
- getCanvasSize: function(sketch) {
- var canvas = this._getContext(sketch).canvas;
- return new $.Point(canvas.width, canvas.height);
- },
- getCanvasCenter: function() {
- return new $.Point(this.canvas.width / 2, this.canvas.height / 2);
- },
- // private
- _offsetForRotation: function(options) {
- var point = options.point ?
- options.point.times($.pixelDensityRatio) :
- this.getCanvasCenter();
- var context = this._getContext(options.useSketch);
- context.save();
- context.translate(point.x, point.y);
- if(this.viewer.viewport.flipped){
- context.rotate(Math.PI / 180 * -options.degrees);
- context.scale(-1, 1);
- } else{
- context.rotate(Math.PI / 180 * options.degrees);
- }
- context.translate(-point.x, -point.y);
- },
- // private
- _flip: function(options) {
- options = options || {};
- var point = options.point ?
- options.point.times($.pixelDensityRatio) :
- this.getCanvasCenter();
- var context = this._getContext(options.useSketch);
- context.translate(point.x, 0);
- context.scale(-1, 1);
- context.translate(-point.x, 0);
- },
- // private
- _restoreRotationChanges: function(useSketch) {
- var context = this._getContext(useSketch);
- context.restore();
- },
- // private
- _calculateCanvasSize: function() {
- var pixelDensityRatio = $.pixelDensityRatio;
- var viewportSize = this.viewport.getContainerSize();
- return {
- // canvas width and height are integers
- x: Math.round(viewportSize.x * pixelDensityRatio),
- y: Math.round(viewportSize.y * pixelDensityRatio)
- };
- },
- // private
- _calculateSketchCanvasSize: function() {
- var canvasSize = this._calculateCanvasSize();
- if (this.viewport.getRotation() === 0) {
- return canvasSize;
- }
- // If the viewport is rotated, we need a larger sketch canvas in order
- // to support edge smoothing.
- var sketchCanvasSize = Math.ceil(Math.sqrt(
- canvasSize.x * canvasSize.x +
- canvasSize.y * canvasSize.y));
- return {
- x: sketchCanvasSize,
- y: sketchCanvasSize
- };
- }
- };
- }( OpenSeadragon ));
- /*
- * OpenSeadragon - Viewport
- *
- * Copyright (C) 2009 CodePlex Foundation
- * Copyright (C) 2010-2013 OpenSeadragon contributors
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are
- * met:
- *
- * - Redistributions of source code must retain the above copyright notice,
- * this list of conditions and the following disclaimer.
- *
- * - Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * - Neither the name of CodePlex Foundation nor the names of its
- * contributors may be used to endorse or promote products derived from
- * this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
- * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
- * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
- * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
- (function( $ ){
- /**
- * @class Viewport
- * @memberof OpenSeadragon
- * @classdesc Handles coordinate-related functionality (zoom, pan, rotation, etc.)
- * for an {@link OpenSeadragon.Viewer}.
- * @param {Object} options - Options for this Viewport.
- * @param {Object} [options.margins] - See viewportMargins in {@link OpenSeadragon.Options}.
- * @param {Number} [options.springStiffness] - See springStiffness in {@link OpenSeadragon.Options}.
- * @param {Number} [options.animationTime] - See animationTime in {@link OpenSeadragon.Options}.
- * @param {Number} [options.minZoomImageRatio] - See minZoomImageRatio in {@link OpenSeadragon.Options}.
- * @param {Number} [options.maxZoomPixelRatio] - See maxZoomPixelRatio in {@link OpenSeadragon.Options}.
- * @param {Number} [options.visibilityRatio] - See visibilityRatio in {@link OpenSeadragon.Options}.
- * @param {Boolean} [options.wrapHorizontal] - See wrapHorizontal in {@link OpenSeadragon.Options}.
- * @param {Boolean} [options.wrapVertical] - See wrapVertical in {@link OpenSeadragon.Options}.
- * @param {Number} [options.defaultZoomLevel] - See defaultZoomLevel in {@link OpenSeadragon.Options}.
- * @param {Number} [options.minZoomLevel] - See minZoomLevel in {@link OpenSeadragon.Options}.
- * @param {Number} [options.maxZoomLevel] - See maxZoomLevel in {@link OpenSeadragon.Options}.
- * @param {Number} [options.degrees] - See degrees in {@link OpenSeadragon.Options}.
- * @param {Boolean} [options.homeFillsViewer] - See homeFillsViewer in {@link OpenSeadragon.Options}.
- */
- $.Viewport = function( options ) {
- //backward compatibility for positional args while preferring more
- //idiomatic javascript options object as the only argument
- var args = arguments;
- if (args.length && args[0] instanceof $.Point) {
- options = {
- containerSize: args[0],
- contentSize: args[1],
- config: args[2]
- };
- }
- //options.config and the general config argument are deprecated
- //in favor of the more direct specification of optional settings
- //being passed directly on the options object
- if ( options.config ){
- $.extend( true, options, options.config );
- delete options.config;
- }
- this._margins = $.extend({
- left: 0,
- top: 0,
- right: 0,
- bottom: 0
- }, options.margins || {});
- delete options.margins;
- $.extend( true, this, {
- //required settings
- containerSize: null,
- contentSize: null,
- //internal state properties
- zoomPoint: null,
- viewer: null,
- //configurable options
- springStiffness: $.DEFAULT_SETTINGS.springStiffness,
- animationTime: $.DEFAULT_SETTINGS.animationTime,
- minZoomImageRatio: $.DEFAULT_SETTINGS.minZoomImageRatio,
- maxZoomPixelRatio: $.DEFAULT_SETTINGS.maxZoomPixelRatio,
- visibilityRatio: $.DEFAULT_SETTINGS.visibilityRatio,
- wrapHorizontal: $.DEFAULT_SETTINGS.wrapHorizontal,
- wrapVertical: $.DEFAULT_SETTINGS.wrapVertical,
- defaultZoomLevel: $.DEFAULT_SETTINGS.defaultZoomLevel,
- minZoomLevel: $.DEFAULT_SETTINGS.minZoomLevel,
- maxZoomLevel: $.DEFAULT_SETTINGS.maxZoomLevel,
- degrees: $.DEFAULT_SETTINGS.degrees,
- flipped: $.DEFAULT_SETTINGS.flipped,
- homeFillsViewer: $.DEFAULT_SETTINGS.homeFillsViewer
- }, options );
- this._updateContainerInnerSize();
- this.centerSpringX = new $.Spring({
- initial: 0,
- springStiffness: this.springStiffness,
- animationTime: this.animationTime
- });
- this.centerSpringY = new $.Spring({
- initial: 0,
- springStiffness: this.springStiffness,
- animationTime: this.animationTime
- });
- this.zoomSpring = new $.Spring({
- exponential: true,
- initial: 1,
- springStiffness: this.springStiffness,
- animationTime: this.animationTime
- });
- this._oldCenterX = this.centerSpringX.current.value;
- this._oldCenterY = this.centerSpringY.current.value;
- this._oldZoom = this.zoomSpring.current.value;
- this._setContentBounds(new $.Rect(0, 0, 1, 1), 1);
- this.goHome(true);
- this.update();
- };
- /** @lends OpenSeadragon.Viewport.prototype */
- $.Viewport.prototype = {
- /**
- * Updates the viewport's home bounds and constraints for the given content size.
- * @function
- * @param {OpenSeadragon.Point} contentSize - size of the content in content units
- * @return {OpenSeadragon.Viewport} Chainable.
- * @fires OpenSeadragon.Viewer.event:reset-size
- */
- resetContentSize: function(contentSize) {
- $.console.assert(contentSize, "[Viewport.resetContentSize] contentSize is required");
- $.console.assert(contentSize instanceof $.Point, "[Viewport.resetContentSize] contentSize must be an OpenSeadragon.Point");
- $.console.assert(contentSize.x > 0, "[Viewport.resetContentSize] contentSize.x must be greater than 0");
- $.console.assert(contentSize.y > 0, "[Viewport.resetContentSize] contentSize.y must be greater than 0");
- this._setContentBounds(new $.Rect(0, 0, 1, contentSize.y / contentSize.x), contentSize.x);
- return this;
- },
- // deprecated
- setHomeBounds: function(bounds, contentFactor) {
- $.console.error("[Viewport.setHomeBounds] this function is deprecated; The content bounds should not be set manually.");
- this._setContentBounds(bounds, contentFactor);
- },
- // Set the viewport's content bounds
- // @param {OpenSeadragon.Rect} bounds - the new bounds in viewport coordinates
- // without rotation
- // @param {Number} contentFactor - how many content units per viewport unit
- // @fires OpenSeadragon.Viewer.event:reset-size
- // @private
- _setContentBounds: function(bounds, contentFactor) {
- $.console.assert(bounds, "[Viewport._setContentBounds] bounds is required");
- $.console.assert(bounds instanceof $.Rect, "[Viewport._setContentBounds] bounds must be an OpenSeadragon.Rect");
- $.console.assert(bounds.width > 0, "[Viewport._setContentBounds] bounds.width must be greater than 0");
- $.console.assert(bounds.height > 0, "[Viewport._setContentBounds] bounds.height must be greater than 0");
- this._contentBoundsNoRotate = bounds.clone();
- this._contentSizeNoRotate = this._contentBoundsNoRotate.getSize().times(
- contentFactor);
- this._contentBounds = bounds.rotate(this.degrees).getBoundingBox();
- this._contentSize = this._contentBounds.getSize().times(contentFactor);
- this._contentAspectRatio = this._contentSize.x / this._contentSize.y;
- if (this.viewer) {
- /**
- * Raised when the viewer's content size or home bounds are reset
- * (see {@link OpenSeadragon.Viewport#resetContentSize}).
- *
- * @event reset-size
- * @memberof OpenSeadragon.Viewer
- * @type {object}
- * @property {OpenSeadragon.Viewer} eventSource - A reference to the Viewer which raised this event.
- * @property {OpenSeadragon.Point} contentSize
- * @property {OpenSeadragon.Rect} contentBounds - Content bounds.
- * @property {OpenSeadragon.Rect} homeBounds - Content bounds.
- * Deprecated use contentBounds instead.
- * @property {Number} contentFactor
- * @property {?Object} userData - Arbitrary subscriber-defined object.
- */
- this.viewer.raiseEvent('reset-size', {
- contentSize: this._contentSizeNoRotate.clone(),
- contentFactor: contentFactor,
- homeBounds: this._contentBoundsNoRotate.clone(),
- contentBounds: this._contentBounds.clone()
- });
- }
- },
- /**
- * Returns the home zoom in "viewport zoom" value.
- * @function
- * @returns {Number} The home zoom in "viewport zoom".
- */
- getHomeZoom: function() {
- if (this.defaultZoomLevel) {
- return this.defaultZoomLevel;
- }
- var aspectFactor = this._contentAspectRatio / this.getAspectRatio();
- var output;
- if (this.homeFillsViewer) { // fill the viewer and clip the image
- output = aspectFactor >= 1 ? aspectFactor : 1;
- } else {
- output = aspectFactor >= 1 ? 1 : aspectFactor;
- }
- return output / this._contentBounds.width;
- },
- /**
- * Returns the home bounds in viewport coordinates.
- * @function
- * @returns {OpenSeadragon.Rect} The home bounds in vewport coordinates.
- */
- getHomeBounds: function() {
- return this.getHomeBoundsNoRotate().rotate(-this.getRotation());
- },
- /**
- * Returns the home bounds in viewport coordinates.
- * This method ignores the viewport rotation. Use
- * {@link OpenSeadragon.Viewport#getHomeBounds} to take it into account.
- * @function
- * @returns {OpenSeadragon.Rect} The home bounds in vewport coordinates.
- */
- getHomeBoundsNoRotate: function() {
- var center = this._contentBounds.getCenter();
- var width = 1.0 / this.getHomeZoom();
- var height = width / this.getAspectRatio();
- return new $.Rect(
- center.x - (width / 2.0),
- center.y - (height / 2.0),
- width,
- height
- );
- },
- /**
- * @function
- * @param {Boolean} immediately
- * @fires OpenSeadragon.Viewer.event:home
- */
- goHome: function(immediately) {
- if (this.viewer) {
- /**
- * Raised when the "home" operation occurs (see {@link OpenSeadragon.Viewport#goHome}).
- *
- * @event home
- * @memberof OpenSeadragon.Viewer
- * @type {object}
- * @property {OpenSeadragon.Viewer} eventSource - A reference to the Viewer which raised this event.
- * @property {Boolean} immediately
- * @property {?Object} userData - Arbitrary subscriber-defined object.
- */
- this.viewer.raiseEvent('home', {
- immediately: immediately
- });
- }
- return this.fitBounds(this.getHomeBounds(), immediately);
- },
- /**
- * @function
- */
- getMinZoom: function() {
- var homeZoom = this.getHomeZoom(),
- zoom = this.minZoomLevel ?
- this.minZoomLevel :
- this.minZoomImageRatio * homeZoom;
- return zoom;
- },
- /**
- * @function
- */
- getMaxZoom: function() {
- var zoom = this.maxZoomLevel;
- if (!zoom) {
- zoom = this._contentSize.x * this.maxZoomPixelRatio / this._containerInnerSize.x;
- zoom /= this._contentBounds.width;
- }
- return Math.max( zoom, this.getHomeZoom() );
- },
- /**
- * @function
- */
- getAspectRatio: function() {
- return this._containerInnerSize.x / this._containerInnerSize.y;
- },
- /**
- * @function
- * @returns {OpenSeadragon.Point} The size of the container, in screen coordinates.
- */
- getContainerSize: function() {
- return new $.Point(
- this.containerSize.x,
- this.containerSize.y
- );
- },
- /**
- * The margins push the "home" region in from the sides by the specified amounts.
- * @function
- * @returns {Object} Properties (Numbers, in screen coordinates): left, top, right, bottom.
- */
- getMargins: function() {
- return $.extend({}, this._margins); // Make a copy so we are not returning our original
- },
- /**
- * The margins push the "home" region in from the sides by the specified amounts.
- * @function
- * @param {Object} margins - Properties (Numbers, in screen coordinates): left, top, right, bottom.
- */
- setMargins: function(margins) {
- $.console.assert($.type(margins) === 'object', '[Viewport.setMargins] margins must be an object');
- this._margins = $.extend({
- left: 0,
- top: 0,
- right: 0,
- bottom: 0
- }, margins);
- this._updateContainerInnerSize();
- if (this.viewer) {
- this.viewer.forceRedraw();
- }
- },
- /**
- * Returns the bounds of the visible area in viewport coordinates.
- * @function
- * @param {Boolean} current - Pass true for the current location; defaults to false (target location).
- * @returns {OpenSeadragon.Rect} The location you are zoomed/panned to, in viewport coordinates.
- */
- getBounds: function(current) {
- return this.getBoundsNoRotate(current).rotate(-this.getRotation());
- },
- /**
- * Returns the bounds of the visible area in viewport coordinates.
- * This method ignores the viewport rotation. Use
- * {@link OpenSeadragon.Viewport#getBounds} to take it into account.
- * @function
- * @param {Boolean} current - Pass true for the current location; defaults to false (target location).
- * @returns {OpenSeadragon.Rect} The location you are zoomed/panned to, in viewport coordinates.
- */
- getBoundsNoRotate: function(current) {
- var center = this.getCenter(current);
- var width = 1.0 / this.getZoom(current);
- var height = width / this.getAspectRatio();
- return new $.Rect(
- center.x - (width / 2.0),
- center.y - (height / 2.0),
- width,
- height
- );
- },
- /**
- * @function
- * @param {Boolean} current - Pass true for the current location; defaults to false (target location).
- * @returns {OpenSeadragon.Rect} The location you are zoomed/panned to,
- * including the space taken by margins, in viewport coordinates.
- */
- getBoundsWithMargins: function(current) {
- return this.getBoundsNoRotateWithMargins(current).rotate(
- -this.getRotation(), this.getCenter(current));
- },
- /**
- * @function
- * @param {Boolean} current - Pass true for the current location; defaults to false (target location).
- * @returns {OpenSeadragon.Rect} The location you are zoomed/panned to,
- * including the space taken by margins, in viewport coordinates.
- */
- getBoundsNoRotateWithMargins: function(current) {
- var bounds = this.getBoundsNoRotate(current);
- var factor = this._containerInnerSize.x * this.getZoom(current);
- bounds.x -= this._margins.left / factor;
- bounds.y -= this._margins.top / factor;
- bounds.width += (this._margins.left + this._margins.right) / factor;
- bounds.height += (this._margins.top + this._margins.bottom) / factor;
- return bounds;
- },
- /**
- * @function
- * @param {Boolean} current - Pass true for the current location; defaults to false (target location).
- */
- getCenter: function( current ) {
- var centerCurrent = new $.Point(
- this.centerSpringX.current.value,
- this.centerSpringY.current.value
- ),
- centerTarget = new $.Point(
- this.centerSpringX.target.value,
- this.centerSpringY.target.value
- ),
- oldZoomPixel,
- zoom,
- width,
- height,
- bounds,
- newZoomPixel,
- deltaZoomPixels,
- deltaZoomPoints;
- if ( current ) {
- return centerCurrent;
- } else if ( !this.zoomPoint ) {
- return centerTarget;
- }
- oldZoomPixel = this.pixelFromPoint(this.zoomPoint, true);
- zoom = this.getZoom();
- width = 1.0 / zoom;
- height = width / this.getAspectRatio();
- bounds = new $.Rect(
- centerCurrent.x - width / 2.0,
- centerCurrent.y - height / 2.0,
- width,
- height
- );
- newZoomPixel = this._pixelFromPoint(this.zoomPoint, bounds);
- deltaZoomPixels = newZoomPixel.minus( oldZoomPixel );
- deltaZoomPoints = deltaZoomPixels.divide( this._containerInnerSize.x * zoom );
- return centerTarget.plus( deltaZoomPoints );
- },
- /**
- * @function
- * @param {Boolean} current - Pass true for the current location; defaults to false (target location).
- */
- getZoom: function( current ) {
- if ( current ) {
- return this.zoomSpring.current.value;
- } else {
- return this.zoomSpring.target.value;
- }
- },
- // private
- _applyZoomConstraints: function(zoom) {
- return Math.max(
- Math.min(zoom, this.getMaxZoom()),
- this.getMinZoom());
- },
- /**
- * @function
- * @private
- * @param {OpenSeadragon.Rect} bounds
- * @return {OpenSeadragon.Rect} constrained bounds.
- */
- _applyBoundaryConstraints: function(bounds) {
- var newBounds = new $.Rect(
- bounds.x,
- bounds.y,
- bounds.width,
- bounds.height);
- if (this.wrapHorizontal) {
- //do nothing
- } else {
- var horizontalThreshold = this.visibilityRatio * newBounds.width;
- var boundsRight = newBounds.x + newBounds.width;
- var contentRight = this._contentBoundsNoRotate.x + this._contentBoundsNoRotate.width;
- var leftDx = this._contentBoundsNoRotate.x - boundsRight + horizontalThreshold;
- var rightDx = contentRight - newBounds.x - horizontalThreshold;
- if (horizontalThreshold > this._contentBoundsNoRotate.width) {
- newBounds.x += (leftDx + rightDx) / 2;
- } else if (rightDx < 0) {
- newBounds.x += rightDx;
- } else if (leftDx > 0) {
- newBounds.x += leftDx;
- }
- }
- if (this.wrapVertical) {
- //do nothing
- } else {
- var verticalThreshold = this.visibilityRatio * newBounds.height;
- var boundsBottom = newBounds.y + newBounds.height;
- var contentBottom = this._contentBoundsNoRotate.y + this._contentBoundsNoRotate.height;
- var topDy = this._contentBoundsNoRotate.y - boundsBottom + verticalThreshold;
- var bottomDy = contentBottom - newBounds.y - verticalThreshold;
- if (verticalThreshold > this._contentBoundsNoRotate.height) {
- newBounds.y += (topDy + bottomDy) / 2;
- } else if (bottomDy < 0) {
- newBounds.y += bottomDy;
- } else if (topDy > 0) {
- newBounds.y += topDy;
- }
- }
- return newBounds;
- },
- /**
- * @function
- * @private
- * @param {Boolean} [immediately=false] - whether the function that triggered this event was
- * called with the "immediately" flag
- */
- _raiseConstraintsEvent: function(immediately) {
- if (this.viewer) {
- /**
- * Raised when the viewport constraints are applied (see {@link OpenSeadragon.Viewport#applyConstraints}).
- *
- * @event constrain
- * @memberof OpenSeadragon.Viewer
- * @type {object}
- * @property {OpenSeadragon.Viewer} eventSource - A reference to the Viewer which raised this event.
- * @property {Boolean} immediately - whether the function that triggered this event was
- * called with the "immediately" flag
- * @property {?Object} userData - Arbitrary subscriber-defined object.
- */
- this.viewer.raiseEvent( 'constrain', {
- immediately: immediately
- });
- }
- },
- /**
- * Enforces the minZoom, maxZoom and visibilityRatio constraints by
- * zooming and panning to the closest acceptable zoom and location.
- * @function
- * @param {Boolean} [immediately=false]
- * @return {OpenSeadragon.Viewport} Chainable.
- * @fires OpenSeadragon.Viewer.event:constrain
- */
- applyConstraints: function(immediately) {
- var actualZoom = this.getZoom();
- var constrainedZoom = this._applyZoomConstraints(actualZoom);
- if (actualZoom !== constrainedZoom) {
- this.zoomTo(constrainedZoom, this.zoomPoint, immediately);
- }
- var bounds = this.getBoundsNoRotate();
- var constrainedBounds = this._applyBoundaryConstraints(bounds);
- this._raiseConstraintsEvent(immediately);
- if (bounds.x !== constrainedBounds.x ||
- bounds.y !== constrainedBounds.y ||
- immediately) {
- this.fitBounds(
- constrainedBounds.rotate(-this.getRotation()),
- immediately);
- }
- return this;
- },
- /**
- * Equivalent to {@link OpenSeadragon.Viewport#applyConstraints}
- * @function
- * @param {Boolean} [immediately=false]
- * @return {OpenSeadragon.Viewport} Chainable.
- * @fires OpenSeadragon.Viewer.event:constrain
- */
- ensureVisible: function(immediately) {
- return this.applyConstraints(immediately);
- },
- /**
- * @function
- * @private
- * @param {OpenSeadragon.Rect} bounds
- * @param {Object} options (immediately=false, constraints=false)
- * @return {OpenSeadragon.Viewport} Chainable.
- */
- _fitBounds: function(bounds, options) {
- options = options || {};
- var immediately = options.immediately || false;
- var constraints = options.constraints || false;
- var aspect = this.getAspectRatio();
- var center = bounds.getCenter();
- // Compute width and height of bounding box.
- var newBounds = new $.Rect(
- bounds.x,
- bounds.y,
- bounds.width,
- bounds.height,
- bounds.degrees + this.getRotation())
- .getBoundingBox();
- if (newBounds.getAspectRatio() >= aspect) {
- newBounds.height = newBounds.width / aspect;
- } else {
- newBounds.width = newBounds.height * aspect;
- }
- // Compute x and y from width, height and center position
- newBounds.x = center.x - newBounds.width / 2;
- newBounds.y = center.y - newBounds.height / 2;
- var newZoom = 1.0 / newBounds.width;
- if (constraints) {
- var newBoundsAspectRatio = newBounds.getAspectRatio();
- var newConstrainedZoom = this._applyZoomConstraints(newZoom);
- if (newZoom !== newConstrainedZoom) {
- newZoom = newConstrainedZoom;
- newBounds.width = 1.0 / newZoom;
- newBounds.x = center.x - newBounds.width / 2;
- newBounds.height = newBounds.width / newBoundsAspectRatio;
- newBounds.y = center.y - newBounds.height / 2;
- }
- newBounds = this._applyBoundaryConstraints(newBounds);
- center = newBounds.getCenter();
- this._raiseConstraintsEvent(immediately);
- }
- if (immediately) {
- this.panTo(center, true);
- return this.zoomTo(newZoom, null, true);
- }
- this.panTo(this.getCenter(true), true);
- this.zoomTo(this.getZoom(true), null, true);
- var oldBounds = this.getBounds();
- var oldZoom = this.getZoom();
- if (oldZoom === 0 || Math.abs(newZoom / oldZoom - 1) < 0.00000001) {
- this.zoomTo(newZoom, true);
- return this.panTo(center, immediately);
- }
- newBounds = newBounds.rotate(-this.getRotation());
- var referencePoint = newBounds.getTopLeft().times(newZoom)
- .minus(oldBounds.getTopLeft().times(oldZoom))
- .divide(newZoom - oldZoom);
- return this.zoomTo(newZoom, referencePoint, immediately);
- },
- /**
- * Makes the viewport zoom and pan so that the specified bounds take
- * as much space as possible in the viewport.
- * Note: this method ignores the constraints (minZoom, maxZoom and
- * visibilityRatio).
- * Use {@link OpenSeadragon.Viewport#fitBoundsWithConstraints} to enforce
- * them.
- * @function
- * @param {OpenSeadragon.Rect} bounds
- * @param {Boolean} [immediately=false]
- * @return {OpenSeadragon.Viewport} Chainable.
- */
- fitBounds: function(bounds, immediately) {
- return this._fitBounds(bounds, {
- immediately: immediately,
- constraints: false
- });
- },
- /**
- * Makes the viewport zoom and pan so that the specified bounds take
- * as much space as possible in the viewport while enforcing the constraints
- * (minZoom, maxZoom and visibilityRatio).
- * Note: because this method enforces the constraints, part of the
- * provided bounds may end up outside of the viewport.
- * Use {@link OpenSeadragon.Viewport#fitBounds} to ignore them.
- * @function
- * @param {OpenSeadragon.Rect} bounds
- * @param {Boolean} [immediately=false]
- * @return {OpenSeadragon.Viewport} Chainable.
- */
- fitBoundsWithConstraints: function(bounds, immediately) {
- return this._fitBounds(bounds, {
- immediately: immediately,
- constraints: true
- });
- },
- /**
- * Zooms so the image just fills the viewer vertically.
- * @param {Boolean} immediately
- * @return {OpenSeadragon.Viewport} Chainable.
- */
- fitVertically: function(immediately) {
- var box = new $.Rect(
- this._contentBounds.x + (this._contentBounds.width / 2),
- this._contentBounds.y,
- 0,
- this._contentBounds.height);
- return this.fitBounds(box, immediately);
- },
- /**
- * Zooms so the image just fills the viewer horizontally.
- * @param {Boolean} immediately
- * @return {OpenSeadragon.Viewport} Chainable.
- */
- fitHorizontally: function(immediately) {
- var box = new $.Rect(
- this._contentBounds.x,
- this._contentBounds.y + (this._contentBounds.height / 2),
- this._contentBounds.width,
- 0);
- return this.fitBounds(box, immediately);
- },
- /**
- * Returns bounds taking constraints into account
- * Added to improve constrained panning
- * @param {Boolean} current - Pass true for the current location; defaults to false (target location).
- * @return {OpenSeadragon.Viewport} Chainable.
- */
- getConstrainedBounds: function(current) {
- var bounds,
- constrainedBounds;
- bounds = this.getBounds(current);
- constrainedBounds = this._applyBoundaryConstraints(bounds);
- return constrainedBounds;
- },
- /**
- * @function
- * @param {OpenSeadragon.Point} delta
- * @param {Boolean} immediately
- * @return {OpenSeadragon.Viewport} Chainable.
- * @fires OpenSeadragon.Viewer.event:pan
- */
- panBy: function( delta, immediately ) {
- var center = new $.Point(
- this.centerSpringX.target.value,
- this.centerSpringY.target.value
- );
- return this.panTo( center.plus( delta ), immediately );
- },
- /**
- * @function
- * @param {OpenSeadragon.Point} center
- * @param {Boolean} immediately
- * @return {OpenSeadragon.Viewport} Chainable.
- * @fires OpenSeadragon.Viewer.event:pan
- */
- panTo: function( center, immediately ) {
- if ( immediately ) {
- this.centerSpringX.resetTo( center.x );
- this.centerSpringY.resetTo( center.y );
- } else {
- this.centerSpringX.springTo( center.x );
- this.centerSpringY.springTo( center.y );
- }
- if( this.viewer ){
- /**
- * Raised when the viewport is panned (see {@link OpenSeadragon.Viewport#panBy} and {@link OpenSeadragon.Viewport#panTo}).
- *
- * @event pan
- * @memberof OpenSeadragon.Viewer
- * @type {object}
- * @property {OpenSeadragon.Viewer} eventSource - A reference to the Viewer which raised this event.
- * @property {OpenSeadragon.Point} center
- * @property {Boolean} immediately
- * @property {?Object} userData - Arbitrary subscriber-defined object.
- */
- this.viewer.raiseEvent( 'pan', {
- center: center,
- immediately: immediately
- });
- }
- return this;
- },
- /**
- * @function
- * @return {OpenSeadragon.Viewport} Chainable.
- * @fires OpenSeadragon.Viewer.event:zoom
- */
- zoomBy: function(factor, refPoint, immediately) {
- return this.zoomTo(
- this.zoomSpring.target.value * factor, refPoint, immediately);
- },
- /**
- * Zooms to the specified zoom level
- * @function
- * @param {Number} zoom The zoom level to zoom to.
- * @param {OpenSeadragon.Point} [refPoint] The point which will stay at
- * the same screen location. Defaults to the viewport center.
- * @param {Boolean} [immediately=false]
- * @return {OpenSeadragon.Viewport} Chainable.
- * @fires OpenSeadragon.Viewer.event:zoom
- */
- zoomTo: function(zoom, refPoint, immediately) {
- var _this = this;
- this.zoomPoint = refPoint instanceof $.Point &&
- !isNaN(refPoint.x) &&
- !isNaN(refPoint.y) ?
- refPoint :
- null;
- if (immediately) {
- this._adjustCenterSpringsForZoomPoint(function() {
- _this.zoomSpring.resetTo(zoom);
- });
- } else {
- this.zoomSpring.springTo(zoom);
- }
- if (this.viewer) {
- /**
- * Raised when the viewport zoom level changes (see {@link OpenSeadragon.Viewport#zoomBy} and {@link OpenSeadragon.Viewport#zoomTo}).
- *
- * @event zoom
- * @memberof OpenSeadragon.Viewer
- * @type {object}
- * @property {OpenSeadragon.Viewer} eventSource - A reference to the Viewer which raised this event.
- * @property {Number} zoom
- * @property {OpenSeadragon.Point} refPoint
- * @property {Boolean} immediately
- * @property {?Object} userData - Arbitrary subscriber-defined object.
- */
- this.viewer.raiseEvent('zoom', {
- zoom: zoom,
- refPoint: refPoint,
- immediately: immediately
- });
- }
- return this;
- },
- /**
- * Rotates this viewport to the angle specified.
- * @function
- * @return {OpenSeadragon.Viewport} Chainable.
- */
- setRotation: function(degrees) {
- if (!this.viewer || !this.viewer.drawer.canRotate()) {
- return this;
- }
- this.degrees = $.positiveModulo(degrees, 360);
- this._setContentBounds(
- this.viewer.world.getHomeBounds(),
- this.viewer.world.getContentFactor());
- this.viewer.forceRedraw();
- /**
- * Raised when rotation has been changed.
- *
- * @event rotate
- * @memberof OpenSeadragon.Viewer
- * @type {object}
- * @property {OpenSeadragon.Viewer} eventSource - A reference to the Viewer which raised the event.
- * @property {Number} degrees - The number of degrees the rotation was set to.
- * @property {?Object} userData - Arbitrary subscriber-defined object.
- */
- this.viewer.raiseEvent('rotate', {"degrees": degrees});
- return this;
- },
- /**
- * Gets the current rotation in degrees.
- * @function
- * @return {Number} The current rotation in degrees.
- */
- getRotation: function() {
- return this.degrees;
- },
- /**
- * @function
- * @return {OpenSeadragon.Viewport} Chainable.
- * @fires OpenSeadragon.Viewer.event:resize
- */
- resize: function( newContainerSize, maintain ) {
- var oldBounds = this.getBoundsNoRotate(),
- newBounds = oldBounds,
- widthDeltaFactor;
- this.containerSize.x = newContainerSize.x;
- this.containerSize.y = newContainerSize.y;
- this._updateContainerInnerSize();
- if ( maintain ) {
- // TODO: widthDeltaFactor will always be 1; probably not what's intended
- widthDeltaFactor = newContainerSize.x / this.containerSize.x;
- newBounds.width = oldBounds.width * widthDeltaFactor;
- newBounds.height = newBounds.width / this.getAspectRatio();
- }
- if( this.viewer ){
- /**
- * Raised when the viewer is resized (see {@link OpenSeadragon.Viewport#resize}).
- *
- * @event resize
- * @memberof OpenSeadragon.Viewer
- * @type {object}
- * @property {OpenSeadragon.Viewer} eventSource - A reference to the Viewer which raised this event.
- * @property {OpenSeadragon.Point} newContainerSize
- * @property {Boolean} maintain
- * @property {?Object} userData - Arbitrary subscriber-defined object.
- */
- this.viewer.raiseEvent( 'resize', {
- newContainerSize: newContainerSize,
- maintain: maintain
- });
- }
- return this.fitBounds( newBounds, true );
- },
- // private
- _updateContainerInnerSize: function() {
- this._containerInnerSize = new $.Point(
- Math.max(1, this.containerSize.x - (this._margins.left + this._margins.right)),
- Math.max(1, this.containerSize.y - (this._margins.top + this._margins.bottom))
- );
- },
- /**
- * Update the zoom and center (X and Y) springs.
- * @function
- * @returns {Boolean} True if any change has been made, false otherwise.
- */
- update: function() {
- var _this = this;
- this._adjustCenterSpringsForZoomPoint(function() {
- _this.zoomSpring.update();
- });
- this.centerSpringX.update();
- this.centerSpringY.update();
- var changed = this.centerSpringX.current.value !== this._oldCenterX ||
- this.centerSpringY.current.value !== this._oldCenterY ||
- this.zoomSpring.current.value !== this._oldZoom;
- this._oldCenterX = this.centerSpringX.current.value;
- this._oldCenterY = this.centerSpringY.current.value;
- this._oldZoom = this.zoomSpring.current.value;
- return changed;
- },
- _adjustCenterSpringsForZoomPoint: function(zoomSpringHandler) {
- if (this.zoomPoint) {
- var oldZoomPixel = this.pixelFromPoint(this.zoomPoint, true);
- zoomSpringHandler();
- var newZoomPixel = this.pixelFromPoint(this.zoomPoint, true);
- var deltaZoomPixels = newZoomPixel.minus(oldZoomPixel);
- var deltaZoomPoints = this.deltaPointsFromPixels(
- deltaZoomPixels, true);
- this.centerSpringX.shiftBy(deltaZoomPoints.x);
- this.centerSpringY.shiftBy(deltaZoomPoints.y);
- if (this.zoomSpring.isAtTargetValue()) {
- this.zoomPoint = null;
- }
- } else {
- zoomSpringHandler();
- }
- },
- /**
- * Convert a delta (translation vector) from viewport coordinates to pixels
- * coordinates. This method does not take rotation into account.
- * Consider using deltaPixelsFromPoints if you need to account for rotation.
- * @param {OpenSeadragon.Point} deltaPoints - The translation vector to convert.
- * @param {Boolean} [current=false] - Pass true for the current location;
- * defaults to false (target location).
- * @returns {OpenSeadragon.Point}
- */
- deltaPixelsFromPointsNoRotate: function(deltaPoints, current) {
- return deltaPoints.times(
- this._containerInnerSize.x * this.getZoom(current)
- );
- },
- /**
- * Convert a delta (translation vector) from viewport coordinates to pixels
- * coordinates.
- * @param {OpenSeadragon.Point} deltaPoints - The translation vector to convert.
- * @param {Boolean} [current=false] - Pass true for the current location;
- * defaults to false (target location).
- * @returns {OpenSeadragon.Point}
- */
- deltaPixelsFromPoints: function(deltaPoints, current) {
- return this.deltaPixelsFromPointsNoRotate(
- deltaPoints.rotate(this.getRotation()),
- current);
- },
- /**
- * Convert a delta (translation vector) from pixels coordinates to viewport
- * coordinates. This method does not take rotation into account.
- * Consider using deltaPointsFromPixels if you need to account for rotation.
- * @param {OpenSeadragon.Point} deltaPixels - The translation vector to convert.
- * @param {Boolean} [current=false] - Pass true for the current location;
- * defaults to false (target location).
- * @returns {OpenSeadragon.Point}
- */
- deltaPointsFromPixelsNoRotate: function(deltaPixels, current) {
- return deltaPixels.divide(
- this._containerInnerSize.x * this.getZoom(current)
- );
- },
- /**
- * Convert a delta (translation vector) from pixels coordinates to viewport
- * coordinates.
- * @param {OpenSeadragon.Point} deltaPixels - The translation vector to convert.
- * @param {Boolean} [current=false] - Pass true for the current location;
- * defaults to false (target location).
- * @returns {OpenSeadragon.Point}
- */
- deltaPointsFromPixels: function(deltaPixels, current) {
- return this.deltaPointsFromPixelsNoRotate(deltaPixels, current)
- .rotate(-this.getRotation());
- },
- /**
- * Convert viewport coordinates to pixels coordinates.
- * This method does not take rotation into account.
- * Consider using pixelFromPoint if you need to account for rotation.
- * @param {OpenSeadragon.Point} point the viewport coordinates
- * @param {Boolean} [current=false] - Pass true for the current location;
- * defaults to false (target location).
- * @returns {OpenSeadragon.Point}
- */
- pixelFromPointNoRotate: function(point, current) {
- return this._pixelFromPointNoRotate(
- point, this.getBoundsNoRotate(current));
- },
- /**
- * Convert viewport coordinates to pixel coordinates.
- * @param {OpenSeadragon.Point} point the viewport coordinates
- * @param {Boolean} [current=false] - Pass true for the current location;
- * defaults to false (target location).
- * @returns {OpenSeadragon.Point}
- */
- pixelFromPoint: function(point, current) {
- return this._pixelFromPoint(point, this.getBoundsNoRotate(current));
- },
- // private
- _pixelFromPointNoRotate: function(point, bounds) {
- return point.minus(
- bounds.getTopLeft()
- ).times(
- this._containerInnerSize.x / bounds.width
- ).plus(
- new $.Point(this._margins.left, this._margins.top)
- );
- },
- // private
- _pixelFromPoint: function(point, bounds) {
- return this._pixelFromPointNoRotate(
- point.rotate(this.getRotation(), this.getCenter(true)),
- bounds);
- },
- /**
- * Convert pixel coordinates to viewport coordinates.
- * This method does not take rotation into account.
- * Consider using pointFromPixel if you need to account for rotation.
- * @param {OpenSeadragon.Point} pixel Pixel coordinates
- * @param {Boolean} [current=false] - Pass true for the current location;
- * defaults to false (target location).
- * @returns {OpenSeadragon.Point}
- */
- pointFromPixelNoRotate: function(pixel, current) {
- var bounds = this.getBoundsNoRotate(current);
- return pixel.minus(
- new $.Point(this._margins.left, this._margins.top)
- ).divide(
- this._containerInnerSize.x / bounds.width
- ).plus(
- bounds.getTopLeft()
- );
- },
- /**
- * Convert pixel coordinates to viewport coordinates.
- * @param {OpenSeadragon.Point} pixel Pixel coordinates
- * @param {Boolean} [current=false] - Pass true for the current location;
- * defaults to false (target location).
- * @returns {OpenSeadragon.Point}
- */
- pointFromPixel: function(pixel, current) {
- return this.pointFromPixelNoRotate(pixel, current).rotate(
- -this.getRotation(),
- this.getCenter(true)
- );
- },
- // private
- _viewportToImageDelta: function( viewerX, viewerY ) {
- var scale = this._contentBoundsNoRotate.width;
- return new $.Point(
- viewerX * this._contentSizeNoRotate.x / scale,
- viewerY * this._contentSizeNoRotate.x / scale);
- },
- /**
- * Translates from OpenSeadragon viewer coordinate system to image coordinate system.
- * This method can be called either by passing X,Y coordinates or an
- * OpenSeadragon.Point
- * Note: not accurate with multi-image; use TiledImage.viewportToImageCoordinates instead.
- * @function
- * @param {(OpenSeadragon.Point|Number)} viewerX either a point or the X
- * coordinate in viewport coordinate system.
- * @param {Number} [viewerY] Y coordinate in viewport coordinate system.
- * @return {OpenSeadragon.Point} a point representing the coordinates in the image.
- */
- viewportToImageCoordinates: function(viewerX, viewerY) {
- if (viewerX instanceof $.Point) {
- //they passed a point instead of individual components
- return this.viewportToImageCoordinates(viewerX.x, viewerX.y);
- }
- if (this.viewer) {
- var count = this.viewer.world.getItemCount();
- if (count > 1) {
- $.console.error('[Viewport.viewportToImageCoordinates] is not accurate ' +
- 'with multi-image; use TiledImage.viewportToImageCoordinates instead.');
- } else if (count === 1) {
- // It is better to use TiledImage.viewportToImageCoordinates
- // because this._contentBoundsNoRotate can not be relied on
- // with clipping.
- var item = this.viewer.world.getItemAt(0);
- return item.viewportToImageCoordinates(viewerX, viewerY, true);
- }
- }
- return this._viewportToImageDelta(
- viewerX - this._contentBoundsNoRotate.x,
- viewerY - this._contentBoundsNoRotate.y);
- },
- // private
- _imageToViewportDelta: function( imageX, imageY ) {
- var scale = this._contentBoundsNoRotate.width;
- return new $.Point(
- imageX / this._contentSizeNoRotate.x * scale,
- imageY / this._contentSizeNoRotate.x * scale);
- },
- /**
- * Translates from image coordinate system to OpenSeadragon viewer coordinate system
- * This method can be called either by passing X,Y coordinates or an
- * OpenSeadragon.Point
- * Note: not accurate with multi-image; use TiledImage.imageToViewportCoordinates instead.
- * @function
- * @param {(OpenSeadragon.Point | Number)} imageX the point or the
- * X coordinate in image coordinate system.
- * @param {Number} [imageY] Y coordinate in image coordinate system.
- * @return {OpenSeadragon.Point} a point representing the coordinates in the viewport.
- */
- imageToViewportCoordinates: function(imageX, imageY) {
- if (imageX instanceof $.Point) {
- //they passed a point instead of individual components
- return this.imageToViewportCoordinates(imageX.x, imageX.y);
- }
- if (this.viewer) {
- var count = this.viewer.world.getItemCount();
- if (count > 1) {
- $.console.error('[Viewport.imageToViewportCoordinates] is not accurate ' +
- 'with multi-image; use TiledImage.imageToViewportCoordinates instead.');
- } else if (count === 1) {
- // It is better to use TiledImage.viewportToImageCoordinates
- // because this._contentBoundsNoRotate can not be relied on
- // with clipping.
- var item = this.viewer.world.getItemAt(0);
- return item.imageToViewportCoordinates(imageX, imageY, true);
- }
- }
- var point = this._imageToViewportDelta(imageX, imageY);
- point.x += this._contentBoundsNoRotate.x;
- point.y += this._contentBoundsNoRotate.y;
- return point;
- },
- /**
- * Translates from a rectangle which describes a portion of the image in
- * pixel coordinates to OpenSeadragon viewport rectangle coordinates.
- * This method can be called either by passing X,Y,width,height or an
- * OpenSeadragon.Rect
- * Note: not accurate with multi-image; use TiledImage.imageToViewportRectangle instead.
- * @function
- * @param {(OpenSeadragon.Rect | Number)} imageX the rectangle or the X
- * coordinate of the top left corner of the rectangle in image coordinate system.
- * @param {Number} [imageY] the Y coordinate of the top left corner of the rectangle
- * in image coordinate system.
- * @param {Number} [pixelWidth] the width in pixel of the rectangle.
- * @param {Number} [pixelHeight] the height in pixel of the rectangle.
- * @returns {OpenSeadragon.Rect} This image's bounds in viewport coordinates
- */
- imageToViewportRectangle: function(imageX, imageY, pixelWidth, pixelHeight) {
- var rect = imageX;
- if (!(rect instanceof $.Rect)) {
- //they passed individual components instead of a rectangle
- rect = new $.Rect(imageX, imageY, pixelWidth, pixelHeight);
- }
- if (this.viewer) {
- var count = this.viewer.world.getItemCount();
- if (count > 1) {
- $.console.error('[Viewport.imageToViewportRectangle] is not accurate ' +
- 'with multi-image; use TiledImage.imageToViewportRectangle instead.');
- } else if (count === 1) {
- // It is better to use TiledImage.imageToViewportRectangle
- // because this._contentBoundsNoRotate can not be relied on
- // with clipping.
- var item = this.viewer.world.getItemAt(0);
- return item.imageToViewportRectangle(
- imageX, imageY, pixelWidth, pixelHeight, true);
- }
- }
- var coordA = this.imageToViewportCoordinates(rect.x, rect.y);
- var coordB = this._imageToViewportDelta(rect.width, rect.height);
- return new $.Rect(
- coordA.x,
- coordA.y,
- coordB.x,
- coordB.y,
- rect.degrees
- );
- },
- /**
- * Translates from a rectangle which describes a portion of
- * the viewport in point coordinates to image rectangle coordinates.
- * This method can be called either by passing X,Y,width,height or an
- * OpenSeadragon.Rect
- * Note: not accurate with multi-image; use TiledImage.viewportToImageRectangle instead.
- * @function
- * @param {(OpenSeadragon.Rect | Number)} viewerX either a rectangle or
- * the X coordinate of the top left corner of the rectangle in viewport
- * coordinate system.
- * @param {Number} [viewerY] the Y coordinate of the top left corner of the rectangle
- * in viewport coordinate system.
- * @param {Number} [pointWidth] the width of the rectangle in viewport coordinate system.
- * @param {Number} [pointHeight] the height of the rectangle in viewport coordinate system.
- */
- viewportToImageRectangle: function(viewerX, viewerY, pointWidth, pointHeight) {
- var rect = viewerX;
- if (!(rect instanceof $.Rect)) {
- //they passed individual components instead of a rectangle
- rect = new $.Rect(viewerX, viewerY, pointWidth, pointHeight);
- }
- if (this.viewer) {
- var count = this.viewer.world.getItemCount();
- if (count > 1) {
- $.console.error('[Viewport.viewportToImageRectangle] is not accurate ' +
- 'with multi-image; use TiledImage.viewportToImageRectangle instead.');
- } else if (count === 1) {
- // It is better to use TiledImage.viewportToImageCoordinates
- // because this._contentBoundsNoRotate can not be relied on
- // with clipping.
- var item = this.viewer.world.getItemAt(0);
- return item.viewportToImageRectangle(
- viewerX, viewerY, pointWidth, pointHeight, true);
- }
- }
- var coordA = this.viewportToImageCoordinates(rect.x, rect.y);
- var coordB = this._viewportToImageDelta(rect.width, rect.height);
- return new $.Rect(
- coordA.x,
- coordA.y,
- coordB.x,
- coordB.y,
- rect.degrees
- );
- },
- /**
- * Convert pixel coordinates relative to the viewer element to image
- * coordinates.
- * Note: not accurate with multi-image.
- * @param {OpenSeadragon.Point} pixel
- * @returns {OpenSeadragon.Point}
- */
- viewerElementToImageCoordinates: function( pixel ) {
- var point = this.pointFromPixel( pixel, true );
- return this.viewportToImageCoordinates( point );
- },
- /**
- * Convert pixel coordinates relative to the image to
- * viewer element coordinates.
- * Note: not accurate with multi-image.
- * @param {OpenSeadragon.Point} pixel
- * @returns {OpenSeadragon.Point}
- */
- imageToViewerElementCoordinates: function( pixel ) {
- var point = this.imageToViewportCoordinates( pixel );
- return this.pixelFromPoint( point, true );
- },
- /**
- * Convert pixel coordinates relative to the window to image coordinates.
- * Note: not accurate with multi-image.
- * @param {OpenSeadragon.Point} pixel
- * @returns {OpenSeadragon.Point}
- */
- windowToImageCoordinates: function(pixel) {
- $.console.assert(this.viewer,
- "[Viewport.windowToImageCoordinates] the viewport must have a viewer.");
- var viewerCoordinates = pixel.minus(
- $.getElementPosition(this.viewer.element));
- return this.viewerElementToImageCoordinates(viewerCoordinates);
- },
- /**
- * Convert image coordinates to pixel coordinates relative to the window.
- * Note: not accurate with multi-image.
- * @param {OpenSeadragon.Point} pixel
- * @returns {OpenSeadragon.Point}
- */
- imageToWindowCoordinates: function(pixel) {
- $.console.assert(this.viewer,
- "[Viewport.imageToWindowCoordinates] the viewport must have a viewer.");
- var viewerCoordinates = this.imageToViewerElementCoordinates(pixel);
- return viewerCoordinates.plus(
- $.getElementPosition(this.viewer.element));
- },
- /**
- * Convert pixel coordinates relative to the viewer element to viewport
- * coordinates.
- * @param {OpenSeadragon.Point} pixel
- * @returns {OpenSeadragon.Point}
- */
- viewerElementToViewportCoordinates: function( pixel ) {
- return this.pointFromPixel( pixel, true );
- },
- /**
- * Convert viewport coordinates to pixel coordinates relative to the
- * viewer element.
- * @param {OpenSeadragon.Point} point
- * @returns {OpenSeadragon.Point}
- */
- viewportToViewerElementCoordinates: function( point ) {
- return this.pixelFromPoint( point, true );
- },
- /**
- * Convert a rectangle in pixel coordinates relative to the viewer element
- * to viewport coordinates.
- * @param {OpenSeadragon.Rect} rectangle the rectangle to convert
- * @returns {OpenSeadragon.Rect} the converted rectangle
- */
- viewerElementToViewportRectangle: function(rectangle) {
- return $.Rect.fromSummits(
- this.pointFromPixel(rectangle.getTopLeft(), true),
- this.pointFromPixel(rectangle.getTopRight(), true),
- this.pointFromPixel(rectangle.getBottomLeft(), true)
- );
- },
- /**
- * Convert a rectangle in viewport coordinates to pixel coordinates relative
- * to the viewer element.
- * @param {OpenSeadragon.Rect} rectangle the rectangle to convert
- * @returns {OpenSeadragon.Rect} the converted rectangle
- */
- viewportToViewerElementRectangle: function(rectangle) {
- return $.Rect.fromSummits(
- this.pixelFromPoint(rectangle.getTopLeft(), true),
- this.pixelFromPoint(rectangle.getTopRight(), true),
- this.pixelFromPoint(rectangle.getBottomLeft(), true)
- );
- },
- /**
- * Convert pixel coordinates relative to the window to viewport coordinates.
- * @param {OpenSeadragon.Point} pixel
- * @returns {OpenSeadragon.Point}
- */
- windowToViewportCoordinates: function(pixel) {
- $.console.assert(this.viewer,
- "[Viewport.windowToViewportCoordinates] the viewport must have a viewer.");
- var viewerCoordinates = pixel.minus(
- $.getElementPosition(this.viewer.element));
- return this.viewerElementToViewportCoordinates(viewerCoordinates);
- },
- /**
- * Convert viewport coordinates to pixel coordinates relative to the window.
- * @param {OpenSeadragon.Point} point
- * @returns {OpenSeadragon.Point}
- */
- viewportToWindowCoordinates: function(point) {
- $.console.assert(this.viewer,
- "[Viewport.viewportToWindowCoordinates] the viewport must have a viewer.");
- var viewerCoordinates = this.viewportToViewerElementCoordinates(point);
- return viewerCoordinates.plus(
- $.getElementPosition(this.viewer.element));
- },
- /**
- * Convert a viewport zoom to an image zoom.
- * Image zoom: ratio of the original image size to displayed image size.
- * 1 means original image size, 0.5 half size...
- * Viewport zoom: ratio of the displayed image's width to viewport's width.
- * 1 means identical width, 2 means image's width is twice the viewport's width...
- * Note: not accurate with multi-image.
- * @function
- * @param {Number} viewportZoom The viewport zoom
- * target zoom.
- * @returns {Number} imageZoom The image zoom
- */
- viewportToImageZoom: function(viewportZoom) {
- if (this.viewer) {
- var count = this.viewer.world.getItemCount();
- if (count > 1) {
- $.console.error('[Viewport.viewportToImageZoom] is not ' +
- 'accurate with multi-image.');
- } else if (count === 1) {
- // It is better to use TiledImage.viewportToImageZoom
- // because this._contentBoundsNoRotate can not be relied on
- // with clipping.
- var item = this.viewer.world.getItemAt(0);
- return item.viewportToImageZoom(viewportZoom);
- }
- }
- var imageWidth = this._contentSizeNoRotate.x;
- var containerWidth = this._containerInnerSize.x;
- var scale = this._contentBoundsNoRotate.width;
- var viewportToImageZoomRatio = (containerWidth / imageWidth) * scale;
- return viewportZoom * viewportToImageZoomRatio;
- },
- /**
- * Convert an image zoom to a viewport zoom.
- * Image zoom: ratio of the original image size to displayed image size.
- * 1 means original image size, 0.5 half size...
- * Viewport zoom: ratio of the displayed image's width to viewport's width.
- * 1 means identical width, 2 means image's width is twice the viewport's width...
- * Note: not accurate with multi-image.
- * @function
- * @param {Number} imageZoom The image zoom
- * target zoom.
- * @returns {Number} viewportZoom The viewport zoom
- */
- imageToViewportZoom: function(imageZoom) {
- if (this.viewer) {
- var count = this.viewer.world.getItemCount();
- if (count > 1) {
- $.console.error('[Viewport.imageToViewportZoom] is not accurate ' +
- 'with multi-image.');
- } else if (count === 1) {
- // It is better to use TiledImage.imageToViewportZoom
- // because this._contentBoundsNoRotate can not be relied on
- // with clipping.
- var item = this.viewer.world.getItemAt(0);
- return item.imageToViewportZoom(imageZoom);
- }
- }
- var imageWidth = this._contentSizeNoRotate.x;
- var containerWidth = this._containerInnerSize.x;
- var scale = this._contentBoundsNoRotate.width;
- var viewportToImageZoomRatio = (imageWidth / containerWidth) / scale;
- return imageZoom * viewportToImageZoomRatio;
- },
- /**
- * Toggles flip state and demands a new drawing on navigator and viewer objects.
- * @function
- * @return {OpenSeadragon.Viewport} Chainable.
- */
- toggleFlip: function() {
- this.setFlip(!this.getFlip());
- return this;
- },
- /**
- * Get flip state stored on viewport.
- * @function
- * @return {Boolean} Flip state.
- */
- getFlip: function() {
- return this.flipped;
- },
- /**
- * Sets flip state according to the state input argument.
- * @function
- * @param {Boolean} state - Flip state to set.
- * @return {OpenSeadragon.Viewport} Chainable.
- */
- setFlip: function( state ) {
- if ( this.flipped === state ) {
- return this;
- }
- this.flipped = state;
- if(this.viewer.navigator){
- this.viewer.navigator.setFlip(this.getFlip());
- }
- this.viewer.forceRedraw();
- /**
- * Raised when flip state has been changed.
- *
- * @event flip
- * @memberof OpenSeadragon.Viewer
- * @type {object}
- * @property {OpenSeadragon.Viewer} eventSource - A reference to the Viewer which raised the event.
- * @property {Number} flipped - The flip state after this change.
- * @property {?Object} userData - Arbitrary subscriber-defined object.
- */
- this.viewer.raiseEvent('flip', {"flipped": state});
- return this;
- }
- };
- }( OpenSeadragon ));
- /*
- * OpenSeadragon - TiledImage
- *
- * Copyright (C) 2009 CodePlex Foundation
- * Copyright (C) 2010-2013 OpenSeadragon contributors
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are
- * met:
- *
- * - Redistributions of source code must retain the above copyright notice,
- * this list of conditions and the following disclaimer.
- *
- * - Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * - Neither the name of CodePlex Foundation nor the names of its
- * contributors may be used to endorse or promote products derived from
- * this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
- * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
- * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
- * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
- (function( $ ){
- /**
- * You shouldn't have to create a TiledImage instance directly; get it asynchronously by
- * using {@link OpenSeadragon.Viewer#open} or {@link OpenSeadragon.Viewer#addTiledImage} instead.
- * @class TiledImage
- * @memberof OpenSeadragon
- * @extends OpenSeadragon.EventSource
- * @classdesc Handles rendering of tiles for an {@link OpenSeadragon.Viewer}.
- * A new instance is created for each TileSource opened.
- * @param {Object} options - Configuration for this TiledImage.
- * @param {OpenSeadragon.TileSource} options.source - The TileSource that defines this TiledImage.
- * @param {OpenSeadragon.Viewer} options.viewer - The Viewer that owns this TiledImage.
- * @param {OpenSeadragon.TileCache} options.tileCache - The TileCache for this TiledImage to use.
- * @param {OpenSeadragon.Drawer} options.drawer - The Drawer for this TiledImage to draw onto.
- * @param {OpenSeadragon.ImageLoader} options.imageLoader - The ImageLoader for this TiledImage to use.
- * @param {Number} [options.x=0] - Left position, in viewport coordinates.
- * @param {Number} [options.y=0] - Top position, in viewport coordinates.
- * @param {Number} [options.width=1] - Width, in viewport coordinates.
- * @param {Number} [options.height] - Height, in viewport coordinates.
- * @param {OpenSeadragon.Rect} [options.fitBounds] The bounds in viewport coordinates
- * to fit the image into. If specified, x, y, width and height get ignored.
- * @param {OpenSeadragon.Placement} [options.fitBoundsPlacement=OpenSeadragon.Placement.CENTER]
- * How to anchor the image in the bounds if options.fitBounds is set.
- * @param {OpenSeadragon.Rect} [options.clip] - An area, in image pixels, to clip to
- * (portions of the image outside of this area will not be visible). Only works on
- * browsers that support the HTML5 canvas.
- * @param {Number} [options.springStiffness] - See {@link OpenSeadragon.Options}.
- * @param {Boolean} [options.animationTime] - See {@link OpenSeadragon.Options}.
- * @param {Number} [options.minZoomImageRatio] - See {@link OpenSeadragon.Options}.
- * @param {Boolean} [options.wrapHorizontal] - See {@link OpenSeadragon.Options}.
- * @param {Boolean} [options.wrapVertical] - See {@link OpenSeadragon.Options}.
- * @param {Boolean} [options.immediateRender] - See {@link OpenSeadragon.Options}.
- * @param {Number} [options.blendTime] - See {@link OpenSeadragon.Options}.
- * @param {Boolean} [options.alwaysBlend] - See {@link OpenSeadragon.Options}.
- * @param {Number} [options.minPixelRatio] - See {@link OpenSeadragon.Options}.
- * @param {Number} [options.smoothTileEdgesMinZoom] - See {@link OpenSeadragon.Options}.
- * @param {Boolean} [options.iOSDevice] - See {@link OpenSeadragon.Options}.
- * @param {Number} [options.opacity=1] - Set to draw at proportional opacity. If zero, images will not draw.
- * @param {Boolean} [options.preload=false] - Set true to load even when the image is hidden by zero opacity.
- * @param {String} [options.compositeOperation] - How the image is composited onto other images; see compositeOperation in {@link OpenSeadragon.Options} for possible values.
- * @param {Boolean} [options.debugMode] - See {@link OpenSeadragon.Options}.
- * @param {String|CanvasGradient|CanvasPattern|Function} [options.placeholderFillStyle] - See {@link OpenSeadragon.Options}.
- * @param {String|Boolean} [options.crossOriginPolicy] - See {@link OpenSeadragon.Options}.
- * @param {Boolean} [options.ajaxWithCredentials] - See {@link OpenSeadragon.Options}.
- * @param {Boolean} [options.loadTilesWithAjax]
- * Whether to load tile data using AJAX requests.
- * Defaults to the setting in {@link OpenSeadragon.Options}.
- * @param {Object} [options.ajaxHeaders={}]
- * A set of headers to include when making tile AJAX requests.
- */
- $.TiledImage = function( options ) {
- var _this = this;
- /**
- * The {@link OpenSeadragon.TileSource} that defines this TiledImage.
- * @member {OpenSeadragon.TileSource} source
- * @memberof OpenSeadragon.TiledImage#
- */
- $.console.assert( options.tileCache, "[TiledImage] options.tileCache is required" );
- $.console.assert( options.drawer, "[TiledImage] options.drawer is required" );
- $.console.assert( options.viewer, "[TiledImage] options.viewer is required" );
- $.console.assert( options.imageLoader, "[TiledImage] options.imageLoader is required" );
- $.console.assert( options.source, "[TiledImage] options.source is required" );
- $.console.assert(!options.clip || options.clip instanceof $.Rect,
- "[TiledImage] options.clip must be an OpenSeadragon.Rect if present");
- $.EventSource.call( this );
- this._tileCache = options.tileCache;
- delete options.tileCache;
- this._drawer = options.drawer;
- delete options.drawer;
- this._imageLoader = options.imageLoader;
- delete options.imageLoader;
- if (options.clip instanceof $.Rect) {
- this._clip = options.clip.clone();
- }
- delete options.clip;
- var x = options.x || 0;
- delete options.x;
- var y = options.y || 0;
- delete options.y;
- // Ratio of zoomable image height to width.
- this.normHeight = options.source.dimensions.y / options.source.dimensions.x;
- this.contentAspectX = options.source.dimensions.x / options.source.dimensions.y;
- var scale = 1;
- if ( options.width ) {
- scale = options.width;
- delete options.width;
- if ( options.height ) {
- $.console.error( "specifying both width and height to a tiledImage is not supported" );
- delete options.height;
- }
- } else if ( options.height ) {
- scale = options.height / this.normHeight;
- delete options.height;
- }
- var fitBounds = options.fitBounds;
- delete options.fitBounds;
- var fitBoundsPlacement = options.fitBoundsPlacement || OpenSeadragon.Placement.CENTER;
- delete options.fitBoundsPlacement;
- var degrees = options.degrees || 0;
- delete options.degrees;
- $.extend( true, this, {
- //internal state properties
- viewer: null,
- tilesMatrix: {}, // A '3d' dictionary [level][x][y] --> Tile.
- coverage: {}, // A '3d' dictionary [level][x][y] --> Boolean; shows what areas have been drawn.
- loadingCoverage: {}, // A '3d' dictionary [level][x][y] --> Boolean; shows what areas are loaded or are being loaded/blended.
- lastDrawn: [], // An unordered list of Tiles drawn last frame.
- lastResetTime: 0, // Last time for which the tiledImage was reset.
- _midDraw: false, // Is the tiledImage currently updating the viewport?
- _needsDraw: true, // Does the tiledImage need to update the viewport again?
- _hasOpaqueTile: false, // Do we have even one fully opaque tile?
- _tilesLoading: 0, // The number of pending tile requests.
- //configurable settings
- springStiffness: $.DEFAULT_SETTINGS.springStiffness,
- animationTime: $.DEFAULT_SETTINGS.animationTime,
- minZoomImageRatio: $.DEFAULT_SETTINGS.minZoomImageRatio,
- wrapHorizontal: $.DEFAULT_SETTINGS.wrapHorizontal,
- wrapVertical: $.DEFAULT_SETTINGS.wrapVertical,
- immediateRender: $.DEFAULT_SETTINGS.immediateRender,
- blendTime: $.DEFAULT_SETTINGS.blendTime,
- alwaysBlend: $.DEFAULT_SETTINGS.alwaysBlend,
- minPixelRatio: $.DEFAULT_SETTINGS.minPixelRatio,
- smoothTileEdgesMinZoom: $.DEFAULT_SETTINGS.smoothTileEdgesMinZoom,
- iOSDevice: $.DEFAULT_SETTINGS.iOSDevice,
- debugMode: $.DEFAULT_SETTINGS.debugMode,
- crossOriginPolicy: $.DEFAULT_SETTINGS.crossOriginPolicy,
- ajaxWithCredentials: $.DEFAULT_SETTINGS.ajaxWithCredentials,
- placeholderFillStyle: $.DEFAULT_SETTINGS.placeholderFillStyle,
- opacity: $.DEFAULT_SETTINGS.opacity,
- preload: $.DEFAULT_SETTINGS.preload,
- compositeOperation: $.DEFAULT_SETTINGS.compositeOperation
- }, options );
- this._preload = this.preload;
- delete this.preload;
- this._fullyLoaded = false;
- this._xSpring = new $.Spring({
- initial: x,
- springStiffness: this.springStiffness,
- animationTime: this.animationTime
- });
- this._ySpring = new $.Spring({
- initial: y,
- springStiffness: this.springStiffness,
- animationTime: this.animationTime
- });
- this._scaleSpring = new $.Spring({
- initial: scale,
- springStiffness: this.springStiffness,
- animationTime: this.animationTime
- });
- this._degreesSpring = new $.Spring({
- initial: degrees,
- springStiffness: this.springStiffness,
- animationTime: this.animationTime
- });
- this._updateForScale();
- if (fitBounds) {
- this.fitBounds(fitBounds, fitBoundsPlacement, true);
- }
- // We need a callback to give image manipulation a chance to happen
- this._drawingHandler = function(args) {
- /**
- * This event is fired just before the tile is drawn giving the application a chance to alter the image.
- *
- * NOTE: This event is only fired when the drawer is using a <canvas>.
- *
- * @event tile-drawing
- * @memberof OpenSeadragon.Viewer
- * @type {object}
- * @property {OpenSeadragon.Viewer} eventSource - A reference to the Viewer which raised the event.
- * @property {OpenSeadragon.Tile} tile - The Tile being drawn.
- * @property {OpenSeadragon.TiledImage} tiledImage - Which TiledImage is being drawn.
- * @property {OpenSeadragon.Tile} context - The HTML canvas context being drawn into.
- * @property {OpenSeadragon.Tile} rendered - The HTML canvas context containing the tile imagery.
- * @property {?Object} userData - Arbitrary subscriber-defined object.
- */
- _this.viewer.raiseEvent('tile-drawing', $.extend({
- tiledImage: _this
- }, args));
- };
- };
- $.extend($.TiledImage.prototype, $.EventSource.prototype, /** @lends OpenSeadragon.TiledImage.prototype */{
- /**
- * @returns {Boolean} Whether the TiledImage needs to be drawn.
- */
- needsDraw: function() {
- return this._needsDraw;
- },
- /**
- * @returns {Boolean} Whether all tiles necessary for this TiledImage to draw at the current view have been loaded.
- */
- getFullyLoaded: function() {
- return this._fullyLoaded;
- },
- // private
- _setFullyLoaded: function(flag) {
- if (flag === this._fullyLoaded) {
- return;
- }
- this._fullyLoaded = flag;
- /**
- * Fired when the TiledImage's "fully loaded" flag (whether all tiles necessary for this TiledImage
- * to draw at the current view have been loaded) changes.
- *
- * @event fully-loaded-change
- * @memberof OpenSeadragon.TiledImage
- * @type {object}
- * @property {Boolean} fullyLoaded - The new "fully loaded" value.
- * @property {OpenSeadragon.TiledImage} eventSource - A reference to the TiledImage which raised the event.
- * @property {?Object} userData - Arbitrary subscriber-defined object.
- */
- this.raiseEvent('fully-loaded-change', {
- fullyLoaded: this._fullyLoaded
- });
- },
- /**
- * Clears all tiles and triggers an update on the next call to
- * {@link OpenSeadragon.TiledImage#update}.
- */
- reset: function() {
- this._tileCache.clearTilesFor(this);
- this.lastResetTime = $.now();
- this._needsDraw = true;
- },
- /**
- * Updates the TiledImage's bounds, animating if needed.
- * @returns {Boolean} Whether the TiledImage animated.
- */
- update: function() {
- var xUpdated = this._xSpring.update();
- var yUpdated = this._ySpring.update();
- var scaleUpdated = this._scaleSpring.update();
- var degreesUpdated = this._degreesSpring.update();
- if (xUpdated || yUpdated || scaleUpdated || degreesUpdated) {
- this._updateForScale();
- this._needsDraw = true;
- return true;
- }
- return false;
- },
- /**
- * Draws the TiledImage to its Drawer.
- */
- draw: function() {
- if (this.opacity !== 0 || this._preload) {
- this._midDraw = true;
- this._updateViewport();
- this._midDraw = false;
- }
- // Images with opacity 0 should not need to be drawn in future. this._needsDraw = false is set in this._updateViewport() for other images.
- else {
- this._needsDraw = false;
- }
- },
- /**
- * Destroy the TiledImage (unload current loaded tiles).
- */
- destroy: function() {
- this.reset();
- },
- /**
- * Get this TiledImage's bounds in viewport coordinates.
- * @param {Boolean} [current=false] - Pass true for the current location;
- * false for target location.
- * @returns {OpenSeadragon.Rect} This TiledImage's bounds in viewport coordinates.
- */
- getBounds: function(current) {
- return this.getBoundsNoRotate(current)
- .rotate(this.getRotation(current), this._getRotationPoint(current));
- },
- /**
- * Get this TiledImage's bounds in viewport coordinates without taking
- * rotation into account.
- * @param {Boolean} [current=false] - Pass true for the current location;
- * false for target location.
- * @returns {OpenSeadragon.Rect} This TiledImage's bounds in viewport coordinates.
- */
- getBoundsNoRotate: function(current) {
- return current ?
- new $.Rect(
- this._xSpring.current.value,
- this._ySpring.current.value,
- this._worldWidthCurrent,
- this._worldHeightCurrent) :
- new $.Rect(
- this._xSpring.target.value,
- this._ySpring.target.value,
- this._worldWidthTarget,
- this._worldHeightTarget);
- },
- // deprecated
- getWorldBounds: function() {
- $.console.error('[TiledImage.getWorldBounds] is deprecated; use TiledImage.getBounds instead');
- return this.getBounds();
- },
- /**
- * Get the bounds of the displayed part of the tiled image.
- * @param {Boolean} [current=false] Pass true for the current location,
- * false for the target location.
- * @returns {$.Rect} The clipped bounds in viewport coordinates.
- */
- getClippedBounds: function(current) {
- var bounds = this.getBoundsNoRotate(current);
- if (this._clip) {
- var worldWidth = current ?
- this._worldWidthCurrent : this._worldWidthTarget;
- var ratio = worldWidth / this.source.dimensions.x;
- var clip = this._clip.times(ratio);
- bounds = new $.Rect(
- bounds.x + clip.x,
- bounds.y + clip.y,
- clip.width,
- clip.height);
- }
- return bounds.rotate(this.getRotation(current), this._getRotationPoint(current));
- },
- /**
- * @returns {OpenSeadragon.Point} This TiledImage's content size, in original pixels.
- */
- getContentSize: function() {
- return new $.Point(this.source.dimensions.x, this.source.dimensions.y);
- },
- // private
- _viewportToImageDelta: function( viewerX, viewerY, current ) {
- var scale = (current ? this._scaleSpring.current.value : this._scaleSpring.target.value);
- return new $.Point(viewerX * (this.source.dimensions.x / scale),
- viewerY * ((this.source.dimensions.y * this.contentAspectX) / scale));
- },
- /**
- * Translates from OpenSeadragon viewer coordinate system to image coordinate system.
- * This method can be called either by passing X,Y coordinates or an {@link OpenSeadragon.Point}.
- * @param {Number|OpenSeadragon.Point} viewerX - The X coordinate or point in viewport coordinate system.
- * @param {Number} [viewerY] - The Y coordinate in viewport coordinate system.
- * @param {Boolean} [current=false] - Pass true to use the current location; false for target location.
- * @return {OpenSeadragon.Point} A point representing the coordinates in the image.
- */
- viewportToImageCoordinates: function(viewerX, viewerY, current) {
- var point;
- if (viewerX instanceof $.Point) {
- //they passed a point instead of individual components
- current = viewerY;
- point = viewerX;
- } else {
- point = new $.Point(viewerX, viewerY);
- }
- point = point.rotate(-this.getRotation(current), this._getRotationPoint(current));
- return current ?
- this._viewportToImageDelta(
- point.x - this._xSpring.current.value,
- point.y - this._ySpring.current.value) :
- this._viewportToImageDelta(
- point.x - this._xSpring.target.value,
- point.y - this._ySpring.target.value);
- },
- // private
- _imageToViewportDelta: function( imageX, imageY, current ) {
- var scale = (current ? this._scaleSpring.current.value : this._scaleSpring.target.value);
- return new $.Point((imageX / this.source.dimensions.x) * scale,
- (imageY / this.source.dimensions.y / this.contentAspectX) * scale);
- },
- /**
- * Translates from image coordinate system to OpenSeadragon viewer coordinate system
- * This method can be called either by passing X,Y coordinates or an {@link OpenSeadragon.Point}.
- * @param {Number|OpenSeadragon.Point} imageX - The X coordinate or point in image coordinate system.
- * @param {Number} [imageY] - The Y coordinate in image coordinate system.
- * @param {Boolean} [current=false] - Pass true to use the current location; false for target location.
- * @return {OpenSeadragon.Point} A point representing the coordinates in the viewport.
- */
- imageToViewportCoordinates: function(imageX, imageY, current) {
- if (imageX instanceof $.Point) {
- //they passed a point instead of individual components
- current = imageY;
- imageY = imageX.y;
- imageX = imageX.x;
- }
- var point = this._imageToViewportDelta(imageX, imageY);
- if (current) {
- point.x += this._xSpring.current.value;
- point.y += this._ySpring.current.value;
- } else {
- point.x += this._xSpring.target.value;
- point.y += this._ySpring.target.value;
- }
- return point.rotate(this.getRotation(current), this._getRotationPoint(current));
- },
- /**
- * Translates from a rectangle which describes a portion of the image in
- * pixel coordinates to OpenSeadragon viewport rectangle coordinates.
- * This method can be called either by passing X,Y,width,height or an {@link OpenSeadragon.Rect}.
- * @param {Number|OpenSeadragon.Rect} imageX - The left coordinate or rectangle in image coordinate system.
- * @param {Number} [imageY] - The top coordinate in image coordinate system.
- * @param {Number} [pixelWidth] - The width in pixel of the rectangle.
- * @param {Number} [pixelHeight] - The height in pixel of the rectangle.
- * @param {Boolean} [current=false] - Pass true to use the current location; false for target location.
- * @return {OpenSeadragon.Rect} A rect representing the coordinates in the viewport.
- */
- imageToViewportRectangle: function(imageX, imageY, pixelWidth, pixelHeight, current) {
- var rect = imageX;
- if (rect instanceof $.Rect) {
- //they passed a rect instead of individual components
- current = imageY;
- } else {
- rect = new $.Rect(imageX, imageY, pixelWidth, pixelHeight);
- }
- var coordA = this.imageToViewportCoordinates(rect.getTopLeft(), current);
- var coordB = this._imageToViewportDelta(rect.width, rect.height, current);
- return new $.Rect(
- coordA.x,
- coordA.y,
- coordB.x,
- coordB.y,
- rect.degrees + this.getRotation(current)
- );
- },
- /**
- * Translates from a rectangle which describes a portion of
- * the viewport in point coordinates to image rectangle coordinates.
- * This method can be called either by passing X,Y,width,height or an {@link OpenSeadragon.Rect}.
- * @param {Number|OpenSeadragon.Rect} viewerX - The left coordinate or rectangle in viewport coordinate system.
- * @param {Number} [viewerY] - The top coordinate in viewport coordinate system.
- * @param {Number} [pointWidth] - The width in viewport coordinate system.
- * @param {Number} [pointHeight] - The height in viewport coordinate system.
- * @param {Boolean} [current=false] - Pass true to use the current location; false for target location.
- * @return {OpenSeadragon.Rect} A rect representing the coordinates in the image.
- */
- viewportToImageRectangle: function( viewerX, viewerY, pointWidth, pointHeight, current ) {
- var rect = viewerX;
- if (viewerX instanceof $.Rect) {
- //they passed a rect instead of individual components
- current = viewerY;
- } else {
- rect = new $.Rect(viewerX, viewerY, pointWidth, pointHeight);
- }
- var coordA = this.viewportToImageCoordinates(rect.getTopLeft(), current);
- var coordB = this._viewportToImageDelta(rect.width, rect.height, current);
- return new $.Rect(
- coordA.x,
- coordA.y,
- coordB.x,
- coordB.y,
- rect.degrees - this.getRotation(current)
- );
- },
- /**
- * Convert pixel coordinates relative to the viewer element to image
- * coordinates.
- * @param {OpenSeadragon.Point} pixel
- * @returns {OpenSeadragon.Point}
- */
- viewerElementToImageCoordinates: function( pixel ) {
- var point = this.viewport.pointFromPixel( pixel, true );
- return this.viewportToImageCoordinates( point );
- },
- /**
- * Convert pixel coordinates relative to the image to
- * viewer element coordinates.
- * @param {OpenSeadragon.Point} pixel
- * @returns {OpenSeadragon.Point}
- */
- imageToViewerElementCoordinates: function( pixel ) {
- var point = this.imageToViewportCoordinates( pixel );
- return this.viewport.pixelFromPoint( point, true );
- },
- /**
- * Convert pixel coordinates relative to the window to image coordinates.
- * @param {OpenSeadragon.Point} pixel
- * @returns {OpenSeadragon.Point}
- */
- windowToImageCoordinates: function( pixel ) {
- var viewerCoordinates = pixel.minus(
- OpenSeadragon.getElementPosition( this.viewer.element ));
- return this.viewerElementToImageCoordinates( viewerCoordinates );
- },
- /**
- * Convert image coordinates to pixel coordinates relative to the window.
- * @param {OpenSeadragon.Point} pixel
- * @returns {OpenSeadragon.Point}
- */
- imageToWindowCoordinates: function( pixel ) {
- var viewerCoordinates = this.imageToViewerElementCoordinates( pixel );
- return viewerCoordinates.plus(
- OpenSeadragon.getElementPosition( this.viewer.element ));
- },
- // private
- // Convert rectangle in viewport coordinates to this tiled image point
- // coordinates (x in [0, 1] and y in [0, aspectRatio])
- _viewportToTiledImageRectangle: function(rect) {
- var scale = this._scaleSpring.current.value;
- rect = rect.rotate(-this.getRotation(true), this._getRotationPoint(true));
- return new $.Rect(
- (rect.x - this._xSpring.current.value) / scale,
- (rect.y - this._ySpring.current.value) / scale,
- rect.width / scale,
- rect.height / scale,
- rect.degrees);
- },
- /**
- * Convert a viewport zoom to an image zoom.
- * Image zoom: ratio of the original image size to displayed image size.
- * 1 means original image size, 0.5 half size...
- * Viewport zoom: ratio of the displayed image's width to viewport's width.
- * 1 means identical width, 2 means image's width is twice the viewport's width...
- * @function
- * @param {Number} viewportZoom The viewport zoom
- * @returns {Number} imageZoom The image zoom
- */
- viewportToImageZoom: function( viewportZoom ) {
- var ratio = this._scaleSpring.current.value *
- this.viewport._containerInnerSize.x / this.source.dimensions.x;
- return ratio * viewportZoom;
- },
- /**
- * Convert an image zoom to a viewport zoom.
- * Image zoom: ratio of the original image size to displayed image size.
- * 1 means original image size, 0.5 half size...
- * Viewport zoom: ratio of the displayed image's width to viewport's width.
- * 1 means identical width, 2 means image's width is twice the viewport's width...
- * Note: not accurate with multi-image.
- * @function
- * @param {Number} imageZoom The image zoom
- * @returns {Number} viewportZoom The viewport zoom
- */
- imageToViewportZoom: function( imageZoom ) {
- var ratio = this._scaleSpring.current.value *
- this.viewport._containerInnerSize.x / this.source.dimensions.x;
- return imageZoom / ratio;
- },
- /**
- * Sets the TiledImage's position in the world.
- * @param {OpenSeadragon.Point} position - The new position, in viewport coordinates.
- * @param {Boolean} [immediately=false] - Whether to animate to the new position or snap immediately.
- * @fires OpenSeadragon.TiledImage.event:bounds-change
- */
- setPosition: function(position, immediately) {
- var sameTarget = (this._xSpring.target.value === position.x &&
- this._ySpring.target.value === position.y);
- if (immediately) {
- if (sameTarget && this._xSpring.current.value === position.x &&
- this._ySpring.current.value === position.y) {
- return;
- }
- this._xSpring.resetTo(position.x);
- this._ySpring.resetTo(position.y);
- this._needsDraw = true;
- } else {
- if (sameTarget) {
- return;
- }
- this._xSpring.springTo(position.x);
- this._ySpring.springTo(position.y);
- this._needsDraw = true;
- }
- if (!sameTarget) {
- this._raiseBoundsChange();
- }
- },
- /**
- * Sets the TiledImage's width in the world, adjusting the height to match based on aspect ratio.
- * @param {Number} width - The new width, in viewport coordinates.
- * @param {Boolean} [immediately=false] - Whether to animate to the new size or snap immediately.
- * @fires OpenSeadragon.TiledImage.event:bounds-change
- */
- setWidth: function(width, immediately) {
- this._setScale(width, immediately);
- },
- /**
- * Sets the TiledImage's height in the world, adjusting the width to match based on aspect ratio.
- * @param {Number} height - The new height, in viewport coordinates.
- * @param {Boolean} [immediately=false] - Whether to animate to the new size or snap immediately.
- * @fires OpenSeadragon.TiledImage.event:bounds-change
- */
- setHeight: function(height, immediately) {
- this._setScale(height / this.normHeight, immediately);
- },
- /**
- * Positions and scales the TiledImage to fit in the specified bounds.
- * Note: this method fires OpenSeadragon.TiledImage.event:bounds-change
- * twice
- * @param {OpenSeadragon.Rect} bounds The bounds to fit the image into.
- * @param {OpenSeadragon.Placement} [anchor=OpenSeadragon.Placement.CENTER]
- * How to anchor the image in the bounds.
- * @param {Boolean} [immediately=false] Whether to animate to the new size
- * or snap immediately.
- * @fires OpenSeadragon.TiledImage.event:bounds-change
- */
- fitBounds: function(bounds, anchor, immediately) {
- anchor = anchor || $.Placement.CENTER;
- var anchorProperties = $.Placement.properties[anchor];
- var aspectRatio = this.contentAspectX;
- var xOffset = 0;
- var yOffset = 0;
- var displayedWidthRatio = 1;
- var displayedHeightRatio = 1;
- if (this._clip) {
- aspectRatio = this._clip.getAspectRatio();
- displayedWidthRatio = this._clip.width / this.source.dimensions.x;
- displayedHeightRatio = this._clip.height / this.source.dimensions.y;
- if (bounds.getAspectRatio() > aspectRatio) {
- xOffset = this._clip.x / this._clip.height * bounds.height;
- yOffset = this._clip.y / this._clip.height * bounds.height;
- } else {
- xOffset = this._clip.x / this._clip.width * bounds.width;
- yOffset = this._clip.y / this._clip.width * bounds.width;
- }
- }
- if (bounds.getAspectRatio() > aspectRatio) {
- // We will have margins on the X axis
- var height = bounds.height / displayedHeightRatio;
- var marginLeft = 0;
- if (anchorProperties.isHorizontallyCentered) {
- marginLeft = (bounds.width - bounds.height * aspectRatio) / 2;
- } else if (anchorProperties.isRight) {
- marginLeft = bounds.width - bounds.height * aspectRatio;
- }
- this.setPosition(
- new $.Point(bounds.x - xOffset + marginLeft, bounds.y - yOffset),
- immediately);
- this.setHeight(height, immediately);
- } else {
- // We will have margins on the Y axis
- var width = bounds.width / displayedWidthRatio;
- var marginTop = 0;
- if (anchorProperties.isVerticallyCentered) {
- marginTop = (bounds.height - bounds.width / aspectRatio) / 2;
- } else if (anchorProperties.isBottom) {
- marginTop = bounds.height - bounds.width / aspectRatio;
- }
- this.setPosition(
- new $.Point(bounds.x - xOffset, bounds.y - yOffset + marginTop),
- immediately);
- this.setWidth(width, immediately);
- }
- },
- /**
- * @returns {OpenSeadragon.Rect|null} The TiledImage's current clip rectangle,
- * in image pixels, or null if none.
- */
- getClip: function() {
- if (this._clip) {
- return this._clip.clone();
- }
- return null;
- },
- /**
- * @param {OpenSeadragon.Rect|null} newClip - An area, in image pixels, to clip to
- * (portions of the image outside of this area will not be visible). Only works on
- * browsers that support the HTML5 canvas.
- * @fires OpenSeadragon.TiledImage.event:clip-change
- */
- setClip: function(newClip) {
- $.console.assert(!newClip || newClip instanceof $.Rect,
- "[TiledImage.setClip] newClip must be an OpenSeadragon.Rect or null");
- if (newClip instanceof $.Rect) {
- this._clip = newClip.clone();
- } else {
- this._clip = null;
- }
- this._needsDraw = true;
- /**
- * Raised when the TiledImage's clip is changed.
- * @event clip-change
- * @memberOf OpenSeadragon.TiledImage
- * @type {object}
- * @property {OpenSeadragon.TiledImage} eventSource - A reference to the
- * TiledImage which raised the event.
- * @property {?Object} userData - Arbitrary subscriber-defined object.
- */
- this.raiseEvent('clip-change');
- },
- /**
- * @returns {Number} The TiledImage's current opacity.
- */
- getOpacity: function() {
- return this.opacity;
- },
- /**
- * @param {Number} opacity Opacity the tiled image should be drawn at.
- * @fires OpenSeadragon.TiledImage.event:opacity-change
- */
- setOpacity: function(opacity) {
- if (opacity === this.opacity) {
- return;
- }
- this.opacity = opacity;
- this._needsDraw = true;
- /**
- * Raised when the TiledImage's opacity is changed.
- * @event opacity-change
- * @memberOf OpenSeadragon.TiledImage
- * @type {object}
- * @property {Number} opacity - The new opacity value.
- * @property {OpenSeadragon.TiledImage} eventSource - A reference to the
- * TiledImage which raised the event.
- * @property {?Object} userData - Arbitrary subscriber-defined object.
- */
- this.raiseEvent('opacity-change', {
- opacity: this.opacity
- });
- },
- /**
- * @returns {Boolean} whether the tiledImage can load its tiles even when it has zero opacity.
- */
- getPreload: function() {
- return this._preload;
- },
- /**
- * Set true to load even when hidden. Set false to block loading when hidden.
- */
- setPreload: function(preload) {
- this._preload = !!preload;
- this._needsDraw = true;
- },
- /**
- * Get the rotation of this tiled image in degrees.
- * @param {Boolean} [current=false] True for current rotation, false for target.
- * @returns {Number} the rotation of this tiled image in degrees.
- */
- getRotation: function(current) {
- return current ?
- this._degreesSpring.current.value :
- this._degreesSpring.target.value;
- },
- /**
- * Set the current rotation of this tiled image in degrees.
- * @param {Number} degrees the rotation in degrees.
- * @param {Boolean} [immediately=false] Whether to animate to the new angle
- * or rotate immediately.
- * @fires OpenSeadragon.TiledImage.event:bounds-change
- */
- setRotation: function(degrees, immediately) {
- if (this._degreesSpring.target.value === degrees &&
- this._degreesSpring.isAtTargetValue()) {
- return;
- }
- if (immediately) {
- this._degreesSpring.resetTo(degrees);
- } else {
- this._degreesSpring.springTo(degrees);
- }
- this._needsDraw = true;
- this._raiseBoundsChange();
- },
- /**
- * Get the point around which this tiled image is rotated
- * @private
- * @param {Boolean} current True for current rotation point, false for target.
- * @returns {OpenSeadragon.Point}
- */
- _getRotationPoint: function(current) {
- return this.getBoundsNoRotate(current).getCenter();
- },
- /**
- * @returns {String} The TiledImage's current compositeOperation.
- */
- getCompositeOperation: function() {
- return this.compositeOperation;
- },
- /**
- * @param {String} compositeOperation the tiled image should be drawn with this globalCompositeOperation.
- * @fires OpenSeadragon.TiledImage.event:composite-operation-change
- */
- setCompositeOperation: function(compositeOperation) {
- if (compositeOperation === this.compositeOperation) {
- return;
- }
- this.compositeOperation = compositeOperation;
- this._needsDraw = true;
- /**
- * Raised when the TiledImage's opacity is changed.
- * @event composite-operation-change
- * @memberOf OpenSeadragon.TiledImage
- * @type {object}
- * @property {String} compositeOperation - The new compositeOperation value.
- * @property {OpenSeadragon.TiledImage} eventSource - A reference to the
- * TiledImage which raised the event.
- * @property {?Object} userData - Arbitrary subscriber-defined object.
- */
- this.raiseEvent('composite-operation-change', {
- compositeOperation: this.compositeOperation
- });
- },
- // private
- _setScale: function(scale, immediately) {
- var sameTarget = (this._scaleSpring.target.value === scale);
- if (immediately) {
- if (sameTarget && this._scaleSpring.current.value === scale) {
- return;
- }
- this._scaleSpring.resetTo(scale);
- this._updateForScale();
- this._needsDraw = true;
- } else {
- if (sameTarget) {
- return;
- }
- this._scaleSpring.springTo(scale);
- this._updateForScale();
- this._needsDraw = true;
- }
- if (!sameTarget) {
- this._raiseBoundsChange();
- }
- },
- // private
- _updateForScale: function() {
- this._worldWidthTarget = this._scaleSpring.target.value;
- this._worldHeightTarget = this.normHeight * this._scaleSpring.target.value;
- this._worldWidthCurrent = this._scaleSpring.current.value;
- this._worldHeightCurrent = this.normHeight * this._scaleSpring.current.value;
- },
- // private
- _raiseBoundsChange: function() {
- /**
- * Raised when the TiledImage's bounds are changed.
- * Note that this event is triggered only when the animation target is changed;
- * not for every frame of animation.
- * @event bounds-change
- * @memberOf OpenSeadragon.TiledImage
- * @type {object}
- * @property {OpenSeadragon.TiledImage} eventSource - A reference to the
- * TiledImage which raised the event.
- * @property {?Object} userData - Arbitrary subscriber-defined object.
- */
- this.raiseEvent('bounds-change');
- },
- // private
- _isBottomItem: function() {
- return this.viewer.world.getItemAt(0) === this;
- },
- // private
- _getLevelsInterval: function() {
- var lowestLevel = Math.max(
- this.source.minLevel,
- Math.floor(Math.log(this.minZoomImageRatio) / Math.log(2))
- );
- var currentZeroRatio = this.viewport.deltaPixelsFromPointsNoRotate(
- this.source.getPixelRatio(0), true).x *
- this._scaleSpring.current.value;
- var highestLevel = Math.min(
- Math.abs(this.source.maxLevel),
- Math.abs(Math.floor(
- Math.log(currentZeroRatio / this.minPixelRatio) / Math.log(2)
- ))
- );
- // Calculations for the interval of levels to draw
- // can return invalid intervals; fix that here if necessary
- highestLevel = Math.max(highestLevel, this.source.minLevel || 0);
- lowestLevel = Math.min(lowestLevel, highestLevel);
- return {
- lowestLevel: lowestLevel,
- highestLevel: highestLevel
- };
- },
- /**
- * @private
- * @inner
- * Pretty much every other line in this needs to be documented so it's clear
- * how each piece of this routine contributes to the drawing process. That's
- * why there are so many TODO's inside this function.
- */
- _updateViewport: function() {
- this._needsDraw = false;
- this._tilesLoading = 0;
- this.loadingCoverage = {};
- // Reset tile's internal drawn state
- while (this.lastDrawn.length > 0) {
- var tile = this.lastDrawn.pop();
- tile.beingDrawn = false;
- }
- var viewport = this.viewport;
- var drawArea = this._viewportToTiledImageRectangle(
- viewport.getBoundsWithMargins(true));
- if (!this.wrapHorizontal && !this.wrapVertical) {
- var tiledImageBounds = this._viewportToTiledImageRectangle(
- this.getClippedBounds(true));
- drawArea = drawArea.intersection(tiledImageBounds);
- if (drawArea === null) {
- return;
- }
- }
- var levelsInterval = this._getLevelsInterval();
- var lowestLevel = levelsInterval.lowestLevel;
- var highestLevel = levelsInterval.highestLevel;
- var bestTile = null;
- var haveDrawn = false;
- var currentTime = $.now();
- // Update any level that will be drawn
- for (var level = highestLevel; level >= lowestLevel; level--) {
- var drawLevel = false;
- //Avoid calculations for draw if we have already drawn this
- var currentRenderPixelRatio = viewport.deltaPixelsFromPointsNoRotate(
- this.source.getPixelRatio(level),
- true
- ).x * this._scaleSpring.current.value;
- if (level === lowestLevel ||
- (!haveDrawn && currentRenderPixelRatio >= this.minPixelRatio)) {
- drawLevel = true;
- haveDrawn = true;
- } else if (!haveDrawn) {
- continue;
- }
- //Perform calculations for draw if we haven't drawn this
- var targetRenderPixelRatio = viewport.deltaPixelsFromPointsNoRotate(
- this.source.getPixelRatio(level),
- false
- ).x * this._scaleSpring.current.value;
- var targetZeroRatio = viewport.deltaPixelsFromPointsNoRotate(
- this.source.getPixelRatio(
- Math.max(
- this.source.getClosestLevel(),
- 0
- )
- ),
- false
- ).x * this._scaleSpring.current.value;
- var optimalRatio = this.immediateRender ? 1 : targetZeroRatio;
- var levelOpacity = Math.min(1, (currentRenderPixelRatio - 0.5) / 0.5);
- var levelVisibility = optimalRatio / Math.abs(
- optimalRatio - targetRenderPixelRatio
- );
- // Update the level and keep track of 'best' tile to load
- bestTile = updateLevel(
- this,
- haveDrawn,
- drawLevel,
- level,
- levelOpacity,
- levelVisibility,
- drawArea,
- currentTime,
- bestTile
- );
- // Stop the loop if lower-res tiles would all be covered by
- // already drawn tiles
- if (providesCoverage(this.coverage, level)) {
- break;
- }
- }
- // Perform the actual drawing
- drawTiles(this, this.lastDrawn);
- // Load the new 'best' tile
- if (bestTile && !bestTile.context2D) {
- loadTile(this, bestTile, currentTime);
- this._needsDraw = true;
- this._setFullyLoaded(false);
- } else {
- this._setFullyLoaded(this._tilesLoading === 0);
- }
- },
- // private
- _getCornerTiles: function(level, topLeftBound, bottomRightBound) {
- var leftX;
- var rightX;
- if (this.wrapHorizontal) {
- leftX = $.positiveModulo(topLeftBound.x, 1);
- rightX = $.positiveModulo(bottomRightBound.x, 1);
- } else {
- leftX = Math.max(0, topLeftBound.x);
- rightX = Math.min(1, bottomRightBound.x);
- }
- var topY;
- var bottomY;
- var aspectRatio = 1 / this.source.aspectRatio;
- if (this.wrapVertical) {
- topY = $.positiveModulo(topLeftBound.y, aspectRatio);
- bottomY = $.positiveModulo(bottomRightBound.y, aspectRatio);
- } else {
- topY = Math.max(0, topLeftBound.y);
- bottomY = Math.min(aspectRatio, bottomRightBound.y);
- }
- var topLeftTile = this.source.getTileAtPoint(level, new $.Point(leftX, topY));
- var bottomRightTile = this.source.getTileAtPoint(level, new $.Point(rightX, bottomY));
- var numTiles = this.source.getNumTiles(level);
- if (this.wrapHorizontal) {
- topLeftTile.x += numTiles.x * Math.floor(topLeftBound.x);
- bottomRightTile.x += numTiles.x * Math.floor(bottomRightBound.x);
- }
- if (this.wrapVertical) {
- topLeftTile.y += numTiles.y * Math.floor(topLeftBound.y / aspectRatio);
- bottomRightTile.y += numTiles.y * Math.floor(bottomRightBound.y / aspectRatio);
- }
- return {
- topLeft: topLeftTile,
- bottomRight: bottomRightTile,
- };
- }
- });
- /**
- * @private
- * @inner
- * Updates all tiles at a given resolution level.
- * @param {OpenSeadragon.TiledImage} tiledImage - Which TiledImage is being drawn.
- * @param {Boolean} haveDrawn
- * @param {Boolean} drawLevel
- * @param {Number} level
- * @param {Number} levelOpacity
- * @param {Number} levelVisibility
- * @param {OpenSeadragon.Point} viewportTL - The index of the most top-left visible tile.
- * @param {OpenSeadragon.Point} viewportBR - The index of the most bottom-right visible tile.
- * @param {Number} currentTime
- * @param {OpenSeadragon.Tile} best - The current "best" tile to draw.
- */
- function updateLevel(tiledImage, haveDrawn, drawLevel, level, levelOpacity,
- levelVisibility, drawArea, currentTime, best) {
- var topLeftBound = drawArea.getBoundingBox().getTopLeft();
- var bottomRightBound = drawArea.getBoundingBox().getBottomRight();
- if (tiledImage.viewer) {
- /**
- * <em>- Needs documentation -</em>
- *
- * @event update-level
- * @memberof OpenSeadragon.Viewer
- * @type {object}
- * @property {OpenSeadragon.Viewer} eventSource - A reference to the Viewer which raised the event.
- * @property {OpenSeadragon.TiledImage} tiledImage - Which TiledImage is being drawn.
- * @property {Object} havedrawn
- * @property {Object} level
- * @property {Object} opacity
- * @property {Object} visibility
- * @property {OpenSeadragon.Rect} drawArea
- * @property {Object} topleft deprecated, use drawArea instead
- * @property {Object} bottomright deprecated, use drawArea instead
- * @property {Object} currenttime
- * @property {Object} best
- * @property {?Object} userData - Arbitrary subscriber-defined object.
- */
- tiledImage.viewer.raiseEvent('update-level', {
- tiledImage: tiledImage,
- havedrawn: haveDrawn,
- level: level,
- opacity: levelOpacity,
- visibility: levelVisibility,
- drawArea: drawArea,
- topleft: topLeftBound,
- bottomright: bottomRightBound,
- currenttime: currentTime,
- best: best
- });
- }
- resetCoverage(tiledImage.coverage, level);
- resetCoverage(tiledImage.loadingCoverage, level);
- //OK, a new drawing so do your calculations
- var cornerTiles = tiledImage._getCornerTiles(level, topLeftBound, bottomRightBound);
- var topLeftTile = cornerTiles.topLeft;
- var bottomRightTile = cornerTiles.bottomRight;
- var numberOfTiles = tiledImage.source.getNumTiles(level);
- var viewportCenter = tiledImage.viewport.pixelFromPoint(
- tiledImage.viewport.getCenter());
- for (var x = topLeftTile.x; x <= bottomRightTile.x; x++) {
- for (var y = topLeftTile.y; y <= bottomRightTile.y; y++) {
- // Optimisation disabled with wrapping because getTileBounds does not
- // work correctly with x and y outside of the number of tiles
- if (!tiledImage.wrapHorizontal && !tiledImage.wrapVertical) {
- var tileBounds = tiledImage.source.getTileBounds(level, x, y);
- if (drawArea.intersection(tileBounds) === null) {
- // This tile is outside of the viewport, no need to draw it
- continue;
- }
- }
- best = updateTile(
- tiledImage,
- drawLevel,
- haveDrawn,
- x, y,
- level,
- levelOpacity,
- levelVisibility,
- viewportCenter,
- numberOfTiles,
- currentTime,
- best
- );
- }
- }
- return best;
- }
- /**
- * @private
- * @inner
- * Update a single tile at a particular resolution level.
- * @param {OpenSeadragon.TiledImage} tiledImage - Which TiledImage is being drawn.
- * @param {Boolean} haveDrawn
- * @param {Boolean} drawLevel
- * @param {Number} x
- * @param {Number} y
- * @param {Number} level
- * @param {Number} levelOpacity
- * @param {Number} levelVisibility
- * @param {OpenSeadragon.Point} viewportCenter
- * @param {Number} numberOfTiles
- * @param {Number} currentTime
- * @param {OpenSeadragon.Tile} best - The current "best" tile to draw.
- */
- function updateTile( tiledImage, haveDrawn, drawLevel, x, y, level, levelOpacity, levelVisibility, viewportCenter, numberOfTiles, currentTime, best){
- var tile = getTile(
- x, y,
- level,
- tiledImage,
- tiledImage.source,
- tiledImage.tilesMatrix,
- currentTime,
- numberOfTiles,
- tiledImage._worldWidthCurrent,
- tiledImage._worldHeightCurrent
- ),
- drawTile = drawLevel;
- if( tiledImage.viewer ){
- /**
- * <em>- Needs documentation -</em>
- *
- * @event update-tile
- * @memberof OpenSeadragon.Viewer
- * @type {object}
- * @property {OpenSeadragon.Viewer} eventSource - A reference to the Viewer which raised the event.
- * @property {OpenSeadragon.TiledImage} tiledImage - Which TiledImage is being drawn.
- * @property {OpenSeadragon.Tile} tile
- * @property {?Object} userData - Arbitrary subscriber-defined object.
- */
- tiledImage.viewer.raiseEvent( 'update-tile', {
- tiledImage: tiledImage,
- tile: tile
- });
- }
- setCoverage( tiledImage.coverage, level, x, y, false );
- var loadingCoverage = tile.loaded || tile.loading || isCovered(tiledImage.loadingCoverage, level, x, y);
- setCoverage(tiledImage.loadingCoverage, level, x, y, loadingCoverage);
- if ( !tile.exists ) {
- return best;
- }
- if ( haveDrawn && !drawTile ) {
- if ( isCovered( tiledImage.coverage, level, x, y ) ) {
- setCoverage( tiledImage.coverage, level, x, y, true );
- } else {
- drawTile = true;
- }
- }
- if ( !drawTile ) {
- return best;
- }
- positionTile(
- tile,
- tiledImage.source.tileOverlap,
- tiledImage.viewport,
- viewportCenter,
- levelVisibility,
- tiledImage
- );
- if (!tile.loaded) {
- if (tile.context2D) {
- setTileLoaded(tiledImage, tile);
- } else {
- var imageRecord = tiledImage._tileCache.getImageRecord(tile.cacheKey);
- if (imageRecord) {
- var image = imageRecord.getImage();
- setTileLoaded(tiledImage, tile, image);
- }
- }
- }
- if ( tile.loaded ) {
- var needsDraw = blendTile(
- tiledImage,
- tile,
- x, y,
- level,
- levelOpacity,
- currentTime
- );
- if ( needsDraw ) {
- tiledImage._needsDraw = true;
- }
- } else if ( tile.loading ) {
- // the tile is already in the download queue
- tiledImage._tilesLoading++;
- } else if (!loadingCoverage) {
- best = compareTiles( best, tile );
- }
- return best;
- }
- /**
- * @private
- * @inner
- * Obtains a tile at the given location.
- * @param {Number} x
- * @param {Number} y
- * @param {Number} level
- * @param {OpenSeadragon.TiledImage} tiledImage
- * @param {OpenSeadragon.TileSource} tileSource
- * @param {Object} tilesMatrix - A '3d' dictionary [level][x][y] --> Tile.
- * @param {Number} time
- * @param {Number} numTiles
- * @param {Number} worldWidth
- * @param {Number} worldHeight
- * @returns {OpenSeadragon.Tile}
- */
- function getTile(
- x, y,
- level,
- tiledImage,
- tileSource,
- tilesMatrix,
- time,
- numTiles,
- worldWidth,
- worldHeight
- ) {
- var xMod,
- yMod,
- bounds,
- sourceBounds,
- exists,
- url,
- ajaxHeaders,
- context2D,
- tile;
- if ( !tilesMatrix[ level ] ) {
- tilesMatrix[ level ] = {};
- }
- if ( !tilesMatrix[ level ][ x ] ) {
- tilesMatrix[ level ][ x ] = {};
- }
- if ( !tilesMatrix[ level ][ x ][ y ] ) {
- xMod = ( numTiles.x + ( x % numTiles.x ) ) % numTiles.x;
- yMod = ( numTiles.y + ( y % numTiles.y ) ) % numTiles.y;
- bounds = tileSource.getTileBounds( level, xMod, yMod );
- sourceBounds = tileSource.getTileBounds( level, xMod, yMod, true );
- exists = tileSource.tileExists( level, xMod, yMod );
- url = tileSource.getTileUrl( level, xMod, yMod );
- // Headers are only applicable if loadTilesWithAjax is set
- if (tiledImage.loadTilesWithAjax) {
- ajaxHeaders = tileSource.getTileAjaxHeaders( level, xMod, yMod );
- // Combine tile AJAX headers with tiled image AJAX headers (if applicable)
- if ($.isPlainObject(tiledImage.ajaxHeaders)) {
- ajaxHeaders = $.extend({}, tiledImage.ajaxHeaders, ajaxHeaders);
- }
- } else {
- ajaxHeaders = null;
- }
- context2D = tileSource.getContext2D ?
- tileSource.getContext2D(level, xMod, yMod) : undefined;
- bounds.x += ( x - xMod ) / numTiles.x;
- bounds.y += (worldHeight / worldWidth) * (( y - yMod ) / numTiles.y);
- tile = new $.Tile(
- level,
- x,
- y,
- bounds,
- exists,
- url,
- context2D,
- tiledImage.loadTilesWithAjax,
- ajaxHeaders,
- sourceBounds
- );
- if (xMod === numTiles.x - 1) {
- tile.isRightMost = true;
- }
- if (yMod === numTiles.y - 1) {
- tile.isBottomMost = true;
- }
- tilesMatrix[ level ][ x ][ y ] = tile;
- }
- tile = tilesMatrix[ level ][ x ][ y ];
- tile.lastTouchTime = time;
- return tile;
- }
- /**
- * @private
- * @inner
- * Dispatch a job to the ImageLoader to load the Image for a Tile.
- * @param {OpenSeadragon.TiledImage} tiledImage
- * @param {OpenSeadragon.Tile} tile
- * @param {Number} time
- */
- function loadTile( tiledImage, tile, time ) {
- tile.loading = true;
- tiledImage._imageLoader.addJob({
- src: tile.url,
- loadWithAjax: tile.loadWithAjax,
- ajaxHeaders: tile.ajaxHeaders,
- crossOriginPolicy: tiledImage.crossOriginPolicy,
- ajaxWithCredentials: tiledImage.ajaxWithCredentials,
- callback: function( image, errorMsg, tileRequest ){
- onTileLoad( tiledImage, tile, time, image, errorMsg, tileRequest );
- },
- abort: function() {
- tile.loading = false;
- }
- });
- }
- /**
- * @private
- * @inner
- * Callback fired when a Tile's Image finished downloading.
- * @param {OpenSeadragon.TiledImage} tiledImage
- * @param {OpenSeadragon.Tile} tile
- * @param {Number} time
- * @param {Image} image
- * @param {String} errorMsg
- * @param {XMLHttpRequest} tileRequest
- */
- function onTileLoad( tiledImage, tile, time, image, errorMsg, tileRequest ) {
- if ( !image ) {
- $.console.log( "Tile %s failed to load: %s - error: %s", tile, tile.url, errorMsg );
- /**
- * Triggered when a tile fails to load.
- *
- * @event tile-load-failed
- * @memberof OpenSeadragon.Viewer
- * @type {object}
- * @property {OpenSeadragon.Tile} tile - The tile that failed to load.
- * @property {OpenSeadragon.TiledImage} tiledImage - The tiled image the tile belongs to.
- * @property {number} time - The time in milliseconds when the tile load began.
- * @property {string} message - The error message.
- * @property {XMLHttpRequest} tileRequest - The XMLHttpRequest used to load the tile if available.
- */
- tiledImage.viewer.raiseEvent("tile-load-failed", {
- tile: tile,
- tiledImage: tiledImage,
- time: time,
- message: errorMsg,
- tileRequest: tileRequest
- });
- tile.loading = false;
- tile.exists = false;
- return;
- }
- if ( time < tiledImage.lastResetTime ) {
- $.console.log( "Ignoring tile %s loaded before reset: %s", tile, tile.url );
- tile.loading = false;
- return;
- }
- var finish = function() {
- var cutoff = tiledImage.source.getClosestLevel();
- setTileLoaded(tiledImage, tile, image, cutoff, tileRequest);
- };
- // Check if we're mid-update; this can happen on IE8 because image load events for
- // cached images happen immediately there
- if ( !tiledImage._midDraw ) {
- finish();
- } else {
- // Wait until after the update, in case caching unloads any tiles
- window.setTimeout( finish, 1);
- }
- }
- /**
- * @private
- * @inner
- * @param {OpenSeadragon.TiledImage} tiledImage
- * @param {OpenSeadragon.Tile} tile
- * @param {Image} image
- * @param {Number} cutoff
- */
- function setTileLoaded(tiledImage, tile, image, cutoff, tileRequest) {
- var increment = 0;
- function getCompletionCallback() {
- increment++;
- return completionCallback;
- }
- function completionCallback() {
- increment--;
- if (increment === 0) {
- tile.loading = false;
- tile.loaded = true;
- if (!tile.context2D) {
- tiledImage._tileCache.cacheTile({
- image: image,
- tile: tile,
- cutoff: cutoff,
- tiledImage: tiledImage
- });
- }
- tiledImage._needsDraw = true;
- }
- }
- /**
- * Triggered when a tile has just been loaded in memory. That means that the
- * image has been downloaded and can be modified before being drawn to the canvas.
- *
- * @event tile-loaded
- * @memberof OpenSeadragon.Viewer
- * @type {object}
- * @property {Image} image - The image of the tile.
- * @property {OpenSeadragon.TiledImage} tiledImage - The tiled image of the loaded tile.
- * @property {OpenSeadragon.Tile} tile - The tile which has been loaded.
- * @property {XMLHttpRequest} tiledImage - The AJAX request that loaded this tile (if applicable).
- * @property {function} getCompletionCallback - A function giving a callback to call
- * when the asynchronous processing of the image is done. The image will be
- * marked as entirely loaded when the callback has been called once for each
- * call to getCompletionCallback.
- */
- tiledImage.viewer.raiseEvent("tile-loaded", {
- tile: tile,
- tiledImage: tiledImage,
- tileRequest: tileRequest,
- image: image,
- getCompletionCallback: getCompletionCallback
- });
- // In case the completion callback is never called, we at least force it once.
- getCompletionCallback()();
- }
- /**
- * @private
- * @inner
- * @param {OpenSeadragon.Tile} tile
- * @param {Boolean} overlap
- * @param {OpenSeadragon.Viewport} viewport
- * @param {OpenSeadragon.Point} viewportCenter
- * @param {Number} levelVisibility
- * @param {OpenSeadragon.TiledImage} tiledImage
- */
- function positionTile( tile, overlap, viewport, viewportCenter, levelVisibility, tiledImage ){
- var boundsTL = tile.bounds.getTopLeft();
- boundsTL.x *= tiledImage._scaleSpring.current.value;
- boundsTL.y *= tiledImage._scaleSpring.current.value;
- boundsTL.x += tiledImage._xSpring.current.value;
- boundsTL.y += tiledImage._ySpring.current.value;
- var boundsSize = tile.bounds.getSize();
- boundsSize.x *= tiledImage._scaleSpring.current.value;
- boundsSize.y *= tiledImage._scaleSpring.current.value;
- var positionC = viewport.pixelFromPointNoRotate(boundsTL, true),
- positionT = viewport.pixelFromPointNoRotate(boundsTL, false),
- sizeC = viewport.deltaPixelsFromPointsNoRotate(boundsSize, true),
- sizeT = viewport.deltaPixelsFromPointsNoRotate(boundsSize, false),
- tileCenter = positionT.plus( sizeT.divide( 2 ) ),
- tileSquaredDistance = viewportCenter.squaredDistanceTo( tileCenter );
- if ( !overlap ) {
- sizeC = sizeC.plus( new $.Point( 1, 1 ) );
- }
- if (tile.isRightMost && tiledImage.wrapHorizontal) {
- sizeC.x += 0.75; // Otherwise Firefox and Safari show seams
- }
- if (tile.isBottomMost && tiledImage.wrapVertical) {
- sizeC.y += 0.75; // Otherwise Firefox and Safari show seams
- }
- tile.position = positionC;
- tile.size = sizeC;
- tile.squaredDistance = tileSquaredDistance;
- tile.visibility = levelVisibility;
- }
- /**
- * @private
- * @inner
- * Updates the opacity of a tile according to the time it has been on screen
- * to perform a fade-in.
- * Updates coverage once a tile is fully opaque.
- * Returns whether the fade-in has completed.
- *
- * @param {OpenSeadragon.TiledImage} tiledImage
- * @param {OpenSeadragon.Tile} tile
- * @param {Number} x
- * @param {Number} y
- * @param {Number} level
- * @param {Number} levelOpacity
- * @param {Number} currentTime
- * @returns {Boolean}
- */
- function blendTile( tiledImage, tile, x, y, level, levelOpacity, currentTime ){
- var blendTimeMillis = 1000 * tiledImage.blendTime,
- deltaTime,
- opacity;
- if ( !tile.blendStart ) {
- tile.blendStart = currentTime;
- }
- deltaTime = currentTime - tile.blendStart;
- opacity = blendTimeMillis ? Math.min( 1, deltaTime / ( blendTimeMillis ) ) : 1;
- if ( tiledImage.alwaysBlend ) {
- opacity *= levelOpacity;
- }
- tile.opacity = opacity;
- tiledImage.lastDrawn.push( tile );
- if ( opacity === 1 ) {
- setCoverage( tiledImage.coverage, level, x, y, true );
- tiledImage._hasOpaqueTile = true;
- } else if ( deltaTime < blendTimeMillis ) {
- return true;
- }
- return false;
- }
- /**
- * @private
- * @inner
- * Returns true if the given tile provides coverage to lower-level tiles of
- * lower resolution representing the same content. If neither x nor y is
- * given, returns true if the entire visible level provides coverage.
- *
- * Note that out-of-bounds tiles provide coverage in this sense, since
- * there's no content that they would need to cover. Tiles at non-existent
- * levels that are within the image bounds, however, do not.
- *
- * @param {Object} coverage - A '3d' dictionary [level][x][y] --> Boolean.
- * @param {Number} level - The resolution level of the tile.
- * @param {Number} x - The X position of the tile.
- * @param {Number} y - The Y position of the tile.
- * @returns {Boolean}
- */
- function providesCoverage( coverage, level, x, y ) {
- var rows,
- cols,
- i, j;
- if ( !coverage[ level ] ) {
- return false;
- }
- if ( x === undefined || y === undefined ) {
- rows = coverage[ level ];
- for ( i in rows ) {
- if ( rows.hasOwnProperty( i ) ) {
- cols = rows[ i ];
- for ( j in cols ) {
- if ( cols.hasOwnProperty( j ) && !cols[ j ] ) {
- return false;
- }
- }
- }
- }
- return true;
- }
- return (
- coverage[ level ][ x] === undefined ||
- coverage[ level ][ x ][ y ] === undefined ||
- coverage[ level ][ x ][ y ] === true
- );
- }
- /**
- * @private
- * @inner
- * Returns true if the given tile is completely covered by higher-level
- * tiles of higher resolution representing the same content. If neither x
- * nor y is given, returns true if the entire visible level is covered.
- *
- * @param {Object} coverage - A '3d' dictionary [level][x][y] --> Boolean.
- * @param {Number} level - The resolution level of the tile.
- * @param {Number} x - The X position of the tile.
- * @param {Number} y - The Y position of the tile.
- * @returns {Boolean}
- */
- function isCovered( coverage, level, x, y ) {
- if ( x === undefined || y === undefined ) {
- return providesCoverage( coverage, level + 1 );
- } else {
- return (
- providesCoverage( coverage, level + 1, 2 * x, 2 * y ) &&
- providesCoverage( coverage, level + 1, 2 * x, 2 * y + 1 ) &&
- providesCoverage( coverage, level + 1, 2 * x + 1, 2 * y ) &&
- providesCoverage( coverage, level + 1, 2 * x + 1, 2 * y + 1 )
- );
- }
- }
- /**
- * @private
- * @inner
- * Sets whether the given tile provides coverage or not.
- *
- * @param {Object} coverage - A '3d' dictionary [level][x][y] --> Boolean.
- * @param {Number} level - The resolution level of the tile.
- * @param {Number} x - The X position of the tile.
- * @param {Number} y - The Y position of the tile.
- * @param {Boolean} covers - Whether the tile provides coverage.
- */
- function setCoverage( coverage, level, x, y, covers ) {
- if ( !coverage[ level ] ) {
- $.console.warn(
- "Setting coverage for a tile before its level's coverage has been reset: %s",
- level
- );
- return;
- }
- if ( !coverage[ level ][ x ] ) {
- coverage[ level ][ x ] = {};
- }
- coverage[ level ][ x ][ y ] = covers;
- }
- /**
- * @private
- * @inner
- * Resets coverage information for the given level. This should be called
- * after every draw routine. Note that at the beginning of the next draw
- * routine, coverage for every visible tile should be explicitly set.
- *
- * @param {Object} coverage - A '3d' dictionary [level][x][y] --> Boolean.
- * @param {Number} level - The resolution level of tiles to completely reset.
- */
- function resetCoverage( coverage, level ) {
- coverage[ level ] = {};
- }
- /**
- * @private
- * @inner
- * Determines whether the 'last best' tile for the area is better than the
- * tile in question.
- *
- * @param {OpenSeadragon.Tile} previousBest
- * @param {OpenSeadragon.Tile} tile
- * @returns {OpenSeadragon.Tile} The new best tile.
- */
- function compareTiles( previousBest, tile ) {
- if ( !previousBest ) {
- return tile;
- }
- if ( tile.visibility > previousBest.visibility ) {
- return tile;
- } else if ( tile.visibility == previousBest.visibility ) {
- if ( tile.squaredDistance < previousBest.squaredDistance ) {
- return tile;
- }
- }
- return previousBest;
- }
- /**
- * @private
- * @inner
- * Draws a TiledImage.
- * @param {OpenSeadragon.TiledImage} tiledImage
- * @param {OpenSeadragon.Tile[]} lastDrawn - An unordered list of Tiles drawn last frame.
- */
- function drawTiles( tiledImage, lastDrawn ) {
- if (tiledImage.opacity === 0 || (lastDrawn.length === 0 && !tiledImage.placeholderFillStyle)) {
- return;
- }
- var tile = lastDrawn[0];
- var useSketch;
- if (tile) {
- useSketch = tiledImage.opacity < 1 ||
- (tiledImage.compositeOperation &&
- tiledImage.compositeOperation !== 'source-over') ||
- (!tiledImage._isBottomItem() && tile._hasTransparencyChannel());
- }
- var sketchScale;
- var sketchTranslate;
- var zoom = tiledImage.viewport.getZoom(true);
- var imageZoom = tiledImage.viewportToImageZoom(zoom);
- if (lastDrawn.length > 1 &&
- imageZoom > tiledImage.smoothTileEdgesMinZoom &&
- !tiledImage.iOSDevice &&
- tiledImage.getRotation(true) % 360 === 0 && // TODO: support tile edge smoothing with tiled image rotation.
- $.supportsCanvas) {
- // When zoomed in a lot (>100%) the tile edges are visible.
- // So we have to composite them at ~100% and scale them up together.
- // Note: Disabled on iOS devices per default as it causes a native crash
- useSketch = true;
- sketchScale = tile.getScaleForEdgeSmoothing();
- sketchTranslate = tile.getTranslationForEdgeSmoothing(sketchScale,
- tiledImage._drawer.getCanvasSize(false),
- tiledImage._drawer.getCanvasSize(true));
- }
- var bounds;
- if (useSketch) {
- if (!sketchScale) {
- // Except when edge smoothing, we only clean the part of the
- // sketch canvas we are going to use for performance reasons.
- bounds = tiledImage.viewport.viewportToViewerElementRectangle(
- tiledImage.getClippedBounds(true))
- .getIntegerBoundingBox()
- .times($.pixelDensityRatio);
- if(tiledImage._drawer.viewer.viewport.getFlip()) {
- if (tiledImage.viewport.degrees !== 0 || tiledImage.getRotation(true) % 360 !== 0){
- bounds.x = tiledImage._drawer.viewer.container.clientWidth - (bounds.x + bounds.width);
- }
- }
- }
- tiledImage._drawer._clear(true, bounds);
- }
- // When scaling, we must rotate only when blending the sketch canvas to
- // avoid interpolation
- if (!sketchScale) {
- if (tiledImage.viewport.degrees !== 0) {
- tiledImage._drawer._offsetForRotation({
- degrees: tiledImage.viewport.degrees,
- useSketch: useSketch
- });
- }
- if (tiledImage.getRotation(true) % 360 !== 0) {
- tiledImage._drawer._offsetForRotation({
- degrees: tiledImage.getRotation(true),
- point: tiledImage.viewport.pixelFromPointNoRotate(
- tiledImage._getRotationPoint(true), true),
- useSketch: useSketch
- });
- }
- if (tiledImage.viewport.degrees === 0 && tiledImage.getRotation(true) % 360 === 0){
- if(tiledImage._drawer.viewer.viewport.getFlip()) {
- tiledImage._drawer._flip();
- }
- }
- }
- var usedClip = false;
- if ( tiledImage._clip ) {
- tiledImage._drawer.saveContext(useSketch);
- var box = tiledImage.imageToViewportRectangle(tiledImage._clip, true);
- box = box.rotate(-tiledImage.getRotation(true), tiledImage._getRotationPoint(true));
- var clipRect = tiledImage._drawer.viewportToDrawerRectangle(box);
- if (sketchScale) {
- clipRect = clipRect.times(sketchScale);
- }
- if (sketchTranslate) {
- clipRect = clipRect.translate(sketchTranslate);
- }
- tiledImage._drawer.setClip(clipRect, useSketch);
- usedClip = true;
- }
- if ( tiledImage.placeholderFillStyle && tiledImage._hasOpaqueTile === false ) {
- var placeholderRect = tiledImage._drawer.viewportToDrawerRectangle(tiledImage.getBounds(true));
- if (sketchScale) {
- placeholderRect = placeholderRect.times(sketchScale);
- }
- if (sketchTranslate) {
- placeholderRect = placeholderRect.translate(sketchTranslate);
- }
- var fillStyle = null;
- if ( typeof tiledImage.placeholderFillStyle === "function" ) {
- fillStyle = tiledImage.placeholderFillStyle(tiledImage, tiledImage._drawer.context);
- }
- else {
- fillStyle = tiledImage.placeholderFillStyle;
- }
- tiledImage._drawer.drawRectangle(placeholderRect, fillStyle, useSketch);
- }
- for (var i = lastDrawn.length - 1; i >= 0; i--) {
- tile = lastDrawn[ i ];
- tiledImage._drawer.drawTile( tile, tiledImage._drawingHandler, useSketch, sketchScale, sketchTranslate );
- tile.beingDrawn = true;
- if( tiledImage.viewer ){
- /**
- * <em>- Needs documentation -</em>
- *
- * @event tile-drawn
- * @memberof OpenSeadragon.Viewer
- * @type {object}
- * @property {OpenSeadragon.Viewer} eventSource - A reference to the Viewer which raised the event.
- * @property {OpenSeadragon.TiledImage} tiledImage - Which TiledImage is being drawn.
- * @property {OpenSeadragon.Tile} tile
- * @property {?Object} userData - Arbitrary subscriber-defined object.
- */
- tiledImage.viewer.raiseEvent( 'tile-drawn', {
- tiledImage: tiledImage,
- tile: tile
- });
- }
- }
- if ( usedClip ) {
- tiledImage._drawer.restoreContext( useSketch );
- }
- if (!sketchScale) {
- if (tiledImage.getRotation(true) % 360 !== 0) {
- tiledImage._drawer._restoreRotationChanges(useSketch);
- }
- if (tiledImage.viewport.degrees !== 0) {
- tiledImage._drawer._restoreRotationChanges(useSketch);
- }
- }
- if (useSketch) {
- if (sketchScale) {
- if (tiledImage.viewport.degrees !== 0) {
- tiledImage._drawer._offsetForRotation({
- degrees: tiledImage.viewport.degrees,
- useSketch: false
- });
- }
- if (tiledImage.getRotation(true) % 360 !== 0) {
- tiledImage._drawer._offsetForRotation({
- degrees: tiledImage.getRotation(true),
- point: tiledImage.viewport.pixelFromPointNoRotate(
- tiledImage._getRotationPoint(true), true),
- useSketch: false
- });
- }
- }
- tiledImage._drawer.blendSketch({
- opacity: tiledImage.opacity,
- scale: sketchScale,
- translate: sketchTranslate,
- compositeOperation: tiledImage.compositeOperation,
- bounds: bounds
- });
- if (sketchScale) {
- if (tiledImage.getRotation(true) % 360 !== 0) {
- tiledImage._drawer._restoreRotationChanges(false);
- }
- if (tiledImage.viewport.degrees !== 0) {
- tiledImage._drawer._restoreRotationChanges(false);
- }
- }
- }
- if (!sketchScale) {
- if (tiledImage.viewport.degrees === 0 && tiledImage.getRotation(true) % 360 === 0){
- if(tiledImage._drawer.viewer.viewport.getFlip()) {
- tiledImage._drawer._flip();
- }
- }
- }
- drawDebugInfo( tiledImage, lastDrawn );
- }
- /**
- * @private
- * @inner
- * Draws special debug information for a TiledImage if in debug mode.
- * @param {OpenSeadragon.TiledImage} tiledImage
- * @param {OpenSeadragon.Tile[]} lastDrawn - An unordered list of Tiles drawn last frame.
- */
- function drawDebugInfo( tiledImage, lastDrawn ) {
- if( tiledImage.debugMode ) {
- for ( var i = lastDrawn.length - 1; i >= 0; i-- ) {
- var tile = lastDrawn[ i ];
- try {
- tiledImage._drawer.drawDebugInfo(
- tile, lastDrawn.length, i, tiledImage);
- } catch(e) {
- $.console.error(e);
- }
- }
- }
- }
- }( OpenSeadragon ));
- /*
- * OpenSeadragon - TileCache
- *
- * Copyright (C) 2009 CodePlex Foundation
- * Copyright (C) 2010-2013 OpenSeadragon contributors
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are
- * met:
- *
- * - Redistributions of source code must retain the above copyright notice,
- * this list of conditions and the following disclaimer.
- *
- * - Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * - Neither the name of CodePlex Foundation nor the names of its
- * contributors may be used to endorse or promote products derived from
- * this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
- * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
- * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
- * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
- (function( $ ){
- // private class
- var TileRecord = function( options ) {
- $.console.assert( options, "[TileCache.cacheTile] options is required" );
- $.console.assert( options.tile, "[TileCache.cacheTile] options.tile is required" );
- $.console.assert( options.tiledImage, "[TileCache.cacheTile] options.tiledImage is required" );
- this.tile = options.tile;
- this.tiledImage = options.tiledImage;
- };
- // private class
- var ImageRecord = function(options) {
- $.console.assert( options, "[ImageRecord] options is required" );
- $.console.assert( options.image, "[ImageRecord] options.image is required" );
- this._image = options.image;
- this._tiles = [];
- };
- ImageRecord.prototype = {
- destroy: function() {
- this._image = null;
- this._renderedContext = null;
- this._tiles = null;
- },
- getImage: function() {
- return this._image;
- },
- getRenderedContext: function() {
- if (!this._renderedContext) {
- var canvas = document.createElement( 'canvas' );
- canvas.width = this._image.width;
- canvas.height = this._image.height;
- this._renderedContext = canvas.getContext('2d');
- this._renderedContext.drawImage( this._image, 0, 0 );
- //since we are caching the prerendered image on a canvas
- //allow the image to not be held in memory
- this._image = null;
- }
- return this._renderedContext;
- },
- setRenderedContext: function(renderedContext) {
- $.console.error("ImageRecord.setRenderedContext is deprecated. " +
- "The rendered context should be created by the ImageRecord " +
- "itself when calling ImageRecord.getRenderedContext.");
- this._renderedContext = renderedContext;
- },
- addTile: function(tile) {
- $.console.assert(tile, '[ImageRecord.addTile] tile is required');
- this._tiles.push(tile);
- },
- removeTile: function(tile) {
- for (var i = 0; i < this._tiles.length; i++) {
- if (this._tiles[i] === tile) {
- this._tiles.splice(i, 1);
- return;
- }
- }
- $.console.warn('[ImageRecord.removeTile] trying to remove unknown tile', tile);
- },
- getTileCount: function() {
- return this._tiles.length;
- }
- };
- /**
- * @class TileCache
- * @memberof OpenSeadragon
- * @classdesc Stores all the tiles displayed in a {@link OpenSeadragon.Viewer}.
- * You generally won't have to interact with the TileCache directly.
- * @param {Object} options - Configuration for this TileCache.
- * @param {Number} [options.maxImageCacheCount] - See maxImageCacheCount in
- * {@link OpenSeadragon.Options} for details.
- */
- $.TileCache = function( options ) {
- options = options || {};
- this._maxImageCacheCount = options.maxImageCacheCount || $.DEFAULT_SETTINGS.maxImageCacheCount;
- this._tilesLoaded = [];
- this._imagesLoaded = [];
- this._imagesLoadedCount = 0;
- };
- /** @lends OpenSeadragon.TileCache.prototype */
- $.TileCache.prototype = {
- /**
- * @returns {Number} The total number of tiles that have been loaded by
- * this TileCache.
- */
- numTilesLoaded: function() {
- return this._tilesLoaded.length;
- },
- /**
- * Caches the specified tile, removing an old tile if necessary to stay under the
- * maxImageCacheCount specified on construction. Note that if multiple tiles reference
- * the same image, there may be more tiles than maxImageCacheCount; the goal is to keep
- * the number of images below that number. Note, as well, that even the number of images
- * may temporarily surpass that number, but should eventually come back down to the max specified.
- * @param {Object} options - Tile info.
- * @param {OpenSeadragon.Tile} options.tile - The tile to cache.
- * @param {String} options.tile.cacheKey - The unique key used to identify this tile in the cache.
- * @param {Image} options.image - The image of the tile to cache.
- * @param {OpenSeadragon.TiledImage} options.tiledImage - The TiledImage that owns that tile.
- * @param {Number} [options.cutoff=0] - If adding this tile goes over the cache max count, this
- * function will release an old tile. The cutoff option specifies a tile level at or below which
- * tiles will not be released.
- */
- cacheTile: function( options ) {
- $.console.assert( options, "[TileCache.cacheTile] options is required" );
- $.console.assert( options.tile, "[TileCache.cacheTile] options.tile is required" );
- $.console.assert( options.tile.cacheKey, "[TileCache.cacheTile] options.tile.cacheKey is required" );
- $.console.assert( options.tiledImage, "[TileCache.cacheTile] options.tiledImage is required" );
- var cutoff = options.cutoff || 0;
- var insertionIndex = this._tilesLoaded.length;
- var imageRecord = this._imagesLoaded[options.tile.cacheKey];
- if (!imageRecord) {
- $.console.assert( options.image, "[TileCache.cacheTile] options.image is required to create an ImageRecord" );
- imageRecord = this._imagesLoaded[options.tile.cacheKey] = new ImageRecord({
- image: options.image
- });
- this._imagesLoadedCount++;
- }
- imageRecord.addTile(options.tile);
- options.tile.cacheImageRecord = imageRecord;
- // Note that just because we're unloading a tile doesn't necessarily mean
- // we're unloading an image. With repeated calls it should sort itself out, though.
- if ( this._imagesLoadedCount > this._maxImageCacheCount ) {
- var worstTile = null;
- var worstTileIndex = -1;
- var worstTileRecord = null;
- var prevTile, worstTime, worstLevel, prevTime, prevLevel, prevTileRecord;
- for ( var i = this._tilesLoaded.length - 1; i >= 0; i-- ) {
- prevTileRecord = this._tilesLoaded[ i ];
- prevTile = prevTileRecord.tile;
- if ( prevTile.level <= cutoff || prevTile.beingDrawn ) {
- continue;
- } else if ( !worstTile ) {
- worstTile = prevTile;
- worstTileIndex = i;
- worstTileRecord = prevTileRecord;
- continue;
- }
- prevTime = prevTile.lastTouchTime;
- worstTime = worstTile.lastTouchTime;
- prevLevel = prevTile.level;
- worstLevel = worstTile.level;
- if ( prevTime < worstTime ||
- ( prevTime == worstTime && prevLevel > worstLevel ) ) {
- worstTile = prevTile;
- worstTileIndex = i;
- worstTileRecord = prevTileRecord;
- }
- }
- if ( worstTile && worstTileIndex >= 0 ) {
- this._unloadTile(worstTileRecord);
- insertionIndex = worstTileIndex;
- }
- }
- this._tilesLoaded[ insertionIndex ] = new TileRecord({
- tile: options.tile,
- tiledImage: options.tiledImage
- });
- },
- /**
- * Clears all tiles associated with the specified tiledImage.
- * @param {OpenSeadragon.TiledImage} tiledImage
- */
- clearTilesFor: function( tiledImage ) {
- $.console.assert(tiledImage, '[TileCache.clearTilesFor] tiledImage is required');
- var tileRecord;
- for ( var i = 0; i < this._tilesLoaded.length; ++i ) {
- tileRecord = this._tilesLoaded[ i ];
- if ( tileRecord.tiledImage === tiledImage ) {
- this._unloadTile(tileRecord);
- this._tilesLoaded.splice( i, 1 );
- i--;
- }
- }
- },
- // private
- getImageRecord: function(cacheKey) {
- $.console.assert(cacheKey, '[TileCache.getImageRecord] cacheKey is required');
- return this._imagesLoaded[cacheKey];
- },
- // private
- _unloadTile: function(tileRecord) {
- $.console.assert(tileRecord, '[TileCache._unloadTile] tileRecord is required');
- var tile = tileRecord.tile;
- var tiledImage = tileRecord.tiledImage;
- tile.unload();
- tile.cacheImageRecord = null;
- var imageRecord = this._imagesLoaded[tile.cacheKey];
- imageRecord.removeTile(tile);
- if (!imageRecord.getTileCount()) {
- imageRecord.destroy();
- delete this._imagesLoaded[tile.cacheKey];
- this._imagesLoadedCount--;
- }
- /**
- * Triggered when a tile has just been unloaded from memory.
- *
- * @event tile-unloaded
- * @memberof OpenSeadragon.Viewer
- * @type {object}
- * @property {OpenSeadragon.TiledImage} tiledImage - The tiled image of the unloaded tile.
- * @property {OpenSeadragon.Tile} tile - The tile which has been unloaded.
- */
- tiledImage.viewer.raiseEvent("tile-unloaded", {
- tile: tile,
- tiledImage: tiledImage
- });
- }
- };
- }( OpenSeadragon ));
- /*
- * OpenSeadragon - World
- *
- * Copyright (C) 2009 CodePlex Foundation
- * Copyright (C) 2010-2013 OpenSeadragon contributors
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are
- * met:
- *
- * - Redistributions of source code must retain the above copyright notice,
- * this list of conditions and the following disclaimer.
- *
- * - Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * - Neither the name of CodePlex Foundation nor the names of its
- * contributors may be used to endorse or promote products derived from
- * this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
- * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
- * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
- * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
- (function( $ ){
- /**
- * @class World
- * @memberof OpenSeadragon
- * @extends OpenSeadragon.EventSource
- * @classdesc Keeps track of all of the tiled images in the scene.
- * @param {Object} options - World options.
- * @param {OpenSeadragon.Viewer} options.viewer - The Viewer that owns this World.
- **/
- $.World = function( options ) {
- var _this = this;
- $.console.assert( options.viewer, "[World] options.viewer is required" );
- $.EventSource.call( this );
- this.viewer = options.viewer;
- this._items = [];
- this._needsDraw = false;
- this._autoRefigureSizes = true;
- this._needsSizesFigured = false;
- this._delegatedFigureSizes = function(event) {
- if (_this._autoRefigureSizes) {
- _this._figureSizes();
- } else {
- _this._needsSizesFigured = true;
- }
- };
- this._figureSizes();
- };
- $.extend( $.World.prototype, $.EventSource.prototype, /** @lends OpenSeadragon.World.prototype */{
- /**
- * Add the specified item.
- * @param {OpenSeadragon.TiledImage} item - The item to add.
- * @param {Number} [options.index] - Index for the item. If not specified, goes at the top.
- * @fires OpenSeadragon.World.event:add-item
- * @fires OpenSeadragon.World.event:metrics-change
- */
- addItem: function( item, options ) {
- $.console.assert(item, "[World.addItem] item is required");
- $.console.assert(item instanceof $.TiledImage, "[World.addItem] only TiledImages supported at this time");
- options = options || {};
- if (options.index !== undefined) {
- var index = Math.max(0, Math.min(this._items.length, options.index));
- this._items.splice(index, 0, item);
- } else {
- this._items.push( item );
- }
- if (this._autoRefigureSizes) {
- this._figureSizes();
- } else {
- this._needsSizesFigured = true;
- }
- this._needsDraw = true;
- item.addHandler('bounds-change', this._delegatedFigureSizes);
- item.addHandler('clip-change', this._delegatedFigureSizes);
- /**
- * Raised when an item is added to the World.
- * @event add-item
- * @memberOf OpenSeadragon.World
- * @type {object}
- * @property {OpenSeadragon.Viewer} eventSource - A reference to the World which raised the event.
- * @property {OpenSeadragon.TiledImage} item - The item that has been added.
- * @property {?Object} userData - Arbitrary subscriber-defined object.
- */
- this.raiseEvent( 'add-item', {
- item: item
- } );
- },
- /**
- * Get the item at the specified index.
- * @param {Number} index - The item's index.
- * @returns {OpenSeadragon.TiledImage} The item at the specified index.
- */
- getItemAt: function( index ) {
- $.console.assert(index !== undefined, "[World.getItemAt] index is required");
- return this._items[ index ];
- },
- /**
- * Get the index of the given item or -1 if not present.
- * @param {OpenSeadragon.TiledImage} item - The item.
- * @returns {Number} The index of the item or -1 if not present.
- */
- getIndexOfItem: function( item ) {
- $.console.assert(item, "[World.getIndexOfItem] item is required");
- return $.indexOf( this._items, item );
- },
- /**
- * @returns {Number} The number of items used.
- */
- getItemCount: function() {
- return this._items.length;
- },
- /**
- * Change the index of a item so that it appears over or under others.
- * @param {OpenSeadragon.TiledImage} item - The item to move.
- * @param {Number} index - The new index.
- * @fires OpenSeadragon.World.event:item-index-change
- */
- setItemIndex: function( item, index ) {
- $.console.assert(item, "[World.setItemIndex] item is required");
- $.console.assert(index !== undefined, "[World.setItemIndex] index is required");
- var oldIndex = this.getIndexOfItem( item );
- if ( index >= this._items.length ) {
- throw new Error( "Index bigger than number of layers." );
- }
- if ( index === oldIndex || oldIndex === -1 ) {
- return;
- }
- this._items.splice( oldIndex, 1 );
- this._items.splice( index, 0, item );
- this._needsDraw = true;
- /**
- * Raised when the order of the indexes has been changed.
- * @event item-index-change
- * @memberOf OpenSeadragon.World
- * @type {object}
- * @property {OpenSeadragon.World} eventSource - A reference to the World which raised the event.
- * @property {OpenSeadragon.TiledImage} item - The item whose index has
- * been changed
- * @property {Number} previousIndex - The previous index of the item
- * @property {Number} newIndex - The new index of the item
- * @property {?Object} userData - Arbitrary subscriber-defined object.
- */
- this.raiseEvent( 'item-index-change', {
- item: item,
- previousIndex: oldIndex,
- newIndex: index
- } );
- },
- /**
- * Remove an item.
- * @param {OpenSeadragon.TiledImage} item - The item to remove.
- * @fires OpenSeadragon.World.event:remove-item
- * @fires OpenSeadragon.World.event:metrics-change
- */
- removeItem: function( item ) {
- $.console.assert(item, "[World.removeItem] item is required");
- var index = $.indexOf(this._items, item );
- if ( index === -1 ) {
- return;
- }
- item.removeHandler('bounds-change', this._delegatedFigureSizes);
- item.removeHandler('clip-change', this._delegatedFigureSizes);
- item.destroy();
- this._items.splice( index, 1 );
- this._figureSizes();
- this._needsDraw = true;
- this._raiseRemoveItem(item);
- },
- /**
- * Remove all items.
- * @fires OpenSeadragon.World.event:remove-item
- * @fires OpenSeadragon.World.event:metrics-change
- */
- removeAll: function() {
- // We need to make sure any pending images are canceled so the world items don't get messed up
- this.viewer._cancelPendingImages();
- var item;
- var i;
- for (i = 0; i < this._items.length; i++) {
- item = this._items[i];
- item.removeHandler('bounds-change', this._delegatedFigureSizes);
- item.removeHandler('clip-change', this._delegatedFigureSizes);
- item.destroy();
- }
- var removedItems = this._items;
- this._items = [];
- this._figureSizes();
- this._needsDraw = true;
- for (i = 0; i < removedItems.length; i++) {
- item = removedItems[i];
- this._raiseRemoveItem(item);
- }
- },
- /**
- * Clears all tiles and triggers updates for all items.
- */
- resetItems: function() {
- for ( var i = 0; i < this._items.length; i++ ) {
- this._items[i].reset();
- }
- },
- /**
- * Updates (i.e. animates bounds of) all items.
- */
- update: function() {
- var animated = false;
- for ( var i = 0; i < this._items.length; i++ ) {
- animated = this._items[i].update() || animated;
- }
- return animated;
- },
- /**
- * Draws all items.
- */
- draw: function() {
- for ( var i = 0; i < this._items.length; i++ ) {
- this._items[i].draw();
- }
- this._needsDraw = false;
- },
- /**
- * @returns {Boolean} true if any items need updating.
- */
- needsDraw: function() {
- for ( var i = 0; i < this._items.length; i++ ) {
- if ( this._items[i].needsDraw() ) {
- return true;
- }
- }
- return this._needsDraw;
- },
- /**
- * @returns {OpenSeadragon.Rect} The smallest rectangle that encloses all items, in viewport coordinates.
- */
- getHomeBounds: function() {
- return this._homeBounds.clone();
- },
- /**
- * To facilitate zoom constraints, we keep track of the pixel density of the
- * densest item in the World (i.e. the item whose content size to viewport size
- * ratio is the highest) and save it as this "content factor".
- * @returns {Number} the number of content units per viewport unit.
- */
- getContentFactor: function() {
- return this._contentFactor;
- },
- /**
- * As a performance optimization, setting this flag to false allows the bounds-change event handler
- * on tiledImages to skip calculations on the world bounds. If a lot of images are going to be positioned in
- * rapid succession, this is a good idea. When finished, setAutoRefigureSizes should be called with true
- * or the system may behave oddly.
- * @param {Boolean} [value] The value to which to set the flag.
- */
- setAutoRefigureSizes: function(value) {
- this._autoRefigureSizes = value;
- if (value & this._needsSizesFigured) {
- this._figureSizes();
- this._needsSizesFigured = false;
- }
- },
- /**
- * Arranges all of the TiledImages with the specified settings.
- * @param {Object} options - Specifies how to arrange.
- * @param {Boolean} [options.immediately=false] - Whether to animate to the new arrangement.
- * @param {String} [options.layout] - See collectionLayout in {@link OpenSeadragon.Options}.
- * @param {Number} [options.rows] - See collectionRows in {@link OpenSeadragon.Options}.
- * @param {Number} [options.columns] - See collectionColumns in {@link OpenSeadragon.Options}.
- * @param {Number} [options.tileSize] - See collectionTileSize in {@link OpenSeadragon.Options}.
- * @param {Number} [options.tileMargin] - See collectionTileMargin in {@link OpenSeadragon.Options}.
- * @fires OpenSeadragon.World.event:metrics-change
- */
- arrange: function(options) {
- options = options || {};
- var immediately = options.immediately || false;
- var layout = options.layout || $.DEFAULT_SETTINGS.collectionLayout;
- var rows = options.rows || $.DEFAULT_SETTINGS.collectionRows;
- var columns = options.columns || $.DEFAULT_SETTINGS.collectionColumns;
- var tileSize = options.tileSize || $.DEFAULT_SETTINGS.collectionTileSize;
- var tileMargin = options.tileMargin || $.DEFAULT_SETTINGS.collectionTileMargin;
- var increment = tileSize + tileMargin;
- var wrap;
- if (!options.rows && columns) {
- wrap = columns;
- } else {
- wrap = Math.ceil(this._items.length / rows);
- }
- var x = 0;
- var y = 0;
- var item, box, width, height, position;
- this.setAutoRefigureSizes(false);
- for (var i = 0; i < this._items.length; i++) {
- if (i && (i % wrap) === 0) {
- if (layout === 'horizontal') {
- y += increment;
- x = 0;
- } else {
- x += increment;
- y = 0;
- }
- }
- item = this._items[i];
- box = item.getBounds();
- if (box.width > box.height) {
- width = tileSize;
- } else {
- width = tileSize * (box.width / box.height);
- }
- height = width * (box.height / box.width);
- position = new $.Point(x + ((tileSize - width) / 2),
- y + ((tileSize - height) / 2));
- item.setPosition(position, immediately);
- item.setWidth(width, immediately);
- if (layout === 'horizontal') {
- x += increment;
- } else {
- y += increment;
- }
- }
- this.setAutoRefigureSizes(true);
- },
- // private
- _figureSizes: function() {
- var oldHomeBounds = this._homeBounds ? this._homeBounds.clone() : null;
- var oldContentSize = this._contentSize ? this._contentSize.clone() : null;
- var oldContentFactor = this._contentFactor || 0;
- if (!this._items.length) {
- this._homeBounds = new $.Rect(0, 0, 1, 1);
- this._contentSize = new $.Point(1, 1);
- this._contentFactor = 1;
- } else {
- var item = this._items[0];
- var bounds = item.getBounds();
- this._contentFactor = item.getContentSize().x / bounds.width;
- var clippedBounds = item.getClippedBounds().getBoundingBox();
- var left = clippedBounds.x;
- var top = clippedBounds.y;
- var right = clippedBounds.x + clippedBounds.width;
- var bottom = clippedBounds.y + clippedBounds.height;
- for (var i = 1; i < this._items.length; i++) {
- item = this._items[i];
- bounds = item.getBounds();
- this._contentFactor = Math.max(this._contentFactor,
- item.getContentSize().x / bounds.width);
- clippedBounds = item.getClippedBounds().getBoundingBox();
- left = Math.min(left, clippedBounds.x);
- top = Math.min(top, clippedBounds.y);
- right = Math.max(right, clippedBounds.x + clippedBounds.width);
- bottom = Math.max(bottom, clippedBounds.y + clippedBounds.height);
- }
- this._homeBounds = new $.Rect(left, top, right - left, bottom - top);
- this._contentSize = new $.Point(
- this._homeBounds.width * this._contentFactor,
- this._homeBounds.height * this._contentFactor);
- }
- if (this._contentFactor !== oldContentFactor ||
- !this._homeBounds.equals(oldHomeBounds) ||
- !this._contentSize.equals(oldContentSize)) {
- /**
- * Raised when the home bounds or content factor change.
- * @event metrics-change
- * @memberOf OpenSeadragon.World
- * @type {object}
- * @property {OpenSeadragon.World} eventSource - A reference to the World which raised the event.
- * @property {?Object} userData - Arbitrary subscriber-defined object.
- */
- this.raiseEvent('metrics-change', {});
- }
- },
- // private
- _raiseRemoveItem: function(item) {
- /**
- * Raised when an item is removed.
- * @event remove-item
- * @memberOf OpenSeadragon.World
- * @type {object}
- * @property {OpenSeadragon.World} eventSource - A reference to the World which raised the event.
- * @property {OpenSeadragon.TiledImage} item - The item's underlying item.
- * @property {?Object} userData - Arbitrary subscriber-defined object.
- */
- this.raiseEvent( 'remove-item', { item: item } );
- }
- });
- }( OpenSeadragon ));
- //# sourceMappingURL=openseadragon.js.map
- ;/*!
- * Bootstrap v4.4.1 (https://getbootstrap.com/)
- * Copyright 2011-2019 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors)
- * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
- */
- (function (global, factory) {
- typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('jquery'), require('popper.js')) :
- typeof define === 'function' && define.amd ? define(['exports', 'jquery', 'popper.js'], factory) :
- (global = global || self, factory(global.bootstrap = {}, global.jQuery, global.Popper));
- }(this, (function (exports, $, Popper) { 'use strict';
- $ = $ && $.hasOwnProperty('default') ? $['default'] : $;
- Popper = Popper && Popper.hasOwnProperty('default') ? Popper['default'] : Popper;
- function _defineProperties(target, props) {
- for (var i = 0; i < props.length; i++) {
- var descriptor = props[i];
- descriptor.enumerable = descriptor.enumerable || false;
- descriptor.configurable = true;
- if ("value" in descriptor) descriptor.writable = true;
- Object.defineProperty(target, descriptor.key, descriptor);
- }
- }
- function _createClass(Constructor, protoProps, staticProps) {
- if (protoProps) _defineProperties(Constructor.prototype, protoProps);
- if (staticProps) _defineProperties(Constructor, staticProps);
- return Constructor;
- }
- function _defineProperty(obj, key, value) {
- if (key in obj) {
- Object.defineProperty(obj, key, {
- value: value,
- enumerable: true,
- configurable: true,
- writable: true
- });
- } else {
- obj[key] = value;
- }
- return obj;
- }
- function ownKeys(object, enumerableOnly) {
- var keys = Object.keys(object);
- if (Object.getOwnPropertySymbols) {
- var symbols = Object.getOwnPropertySymbols(object);
- if (enumerableOnly) symbols = symbols.filter(function (sym) {
- return Object.getOwnPropertyDescriptor(object, sym).enumerable;
- });
- keys.push.apply(keys, symbols);
- }
- return keys;
- }
- function _objectSpread2(target) {
- for (var i = 1; i < arguments.length; i++) {
- var source = arguments[i] != null ? arguments[i] : {};
- if (i % 2) {
- ownKeys(Object(source), true).forEach(function (key) {
- _defineProperty(target, key, source[key]);
- });
- } else if (Object.getOwnPropertyDescriptors) {
- Object.defineProperties(target, Object.getOwnPropertyDescriptors(source));
- } else {
- ownKeys(Object(source)).forEach(function (key) {
- Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key));
- });
- }
- }
- return target;
- }
- function _inheritsLoose(subClass, superClass) {
- subClass.prototype = Object.create(superClass.prototype);
- subClass.prototype.constructor = subClass;
- subClass.__proto__ = superClass;
- }
- /**
- * --------------------------------------------------------------------------
- * Bootstrap (v4.4.1): util.js
- * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
- * --------------------------------------------------------------------------
- */
- /**
- * ------------------------------------------------------------------------
- * Private TransitionEnd Helpers
- * ------------------------------------------------------------------------
- */
- var TRANSITION_END = 'transitionend';
- var MAX_UID = 1000000;
- var MILLISECONDS_MULTIPLIER = 1000; // Shoutout AngusCroll (https://goo.gl/pxwQGp)
- function toType(obj) {
- return {}.toString.call(obj).match(/\s([a-z]+)/i)[1].toLowerCase();
- }
- function getSpecialTransitionEndEvent() {
- return {
- bindType: TRANSITION_END,
- delegateType: TRANSITION_END,
- handle: function handle(event) {
- if ($(event.target).is(this)) {
- return event.handleObj.handler.apply(this, arguments); // eslint-disable-line prefer-rest-params
- }
- return undefined; // eslint-disable-line no-undefined
- }
- };
- }
- function transitionEndEmulator(duration) {
- var _this = this;
- var called = false;
- $(this).one(Util.TRANSITION_END, function () {
- called = true;
- });
- setTimeout(function () {
- if (!called) {
- Util.triggerTransitionEnd(_this);
- }
- }, duration);
- return this;
- }
- function setTransitionEndSupport() {
- $.fn.emulateTransitionEnd = transitionEndEmulator;
- $.event.special[Util.TRANSITION_END] = getSpecialTransitionEndEvent();
- }
- /**
- * --------------------------------------------------------------------------
- * Public Util Api
- * --------------------------------------------------------------------------
- */
- var Util = {
- TRANSITION_END: 'bsTransitionEnd',
- getUID: function getUID(prefix) {
- do {
- // eslint-disable-next-line no-bitwise
- prefix += ~~(Math.random() * MAX_UID); // "~~" acts like a faster Math.floor() here
- } while (document.getElementById(prefix));
- return prefix;
- },
- getSelectorFromElement: function getSelectorFromElement(element) {
- var selector = element.getAttribute('data-target');
- if (!selector || selector === '#') {
- var hrefAttr = element.getAttribute('href');
- selector = hrefAttr && hrefAttr !== '#' ? hrefAttr.trim() : '';
- }
- try {
- return document.querySelector(selector) ? selector : null;
- } catch (err) {
- return null;
- }
- },
- getTransitionDurationFromElement: function getTransitionDurationFromElement(element) {
- if (!element) {
- return 0;
- } // Get transition-duration of the element
- var transitionDuration = $(element).css('transition-duration');
- var transitionDelay = $(element).css('transition-delay');
- var floatTransitionDuration = parseFloat(transitionDuration);
- var floatTransitionDelay = parseFloat(transitionDelay); // Return 0 if element or transition duration is not found
- if (!floatTransitionDuration && !floatTransitionDelay) {
- return 0;
- } // If multiple durations are defined, take the first
- transitionDuration = transitionDuration.split(',')[0];
- transitionDelay = transitionDelay.split(',')[0];
- return (parseFloat(transitionDuration) + parseFloat(transitionDelay)) * MILLISECONDS_MULTIPLIER;
- },
- reflow: function reflow(element) {
- return element.offsetHeight;
- },
- triggerTransitionEnd: function triggerTransitionEnd(element) {
- $(element).trigger(TRANSITION_END);
- },
- // TODO: Remove in v5
- supportsTransitionEnd: function supportsTransitionEnd() {
- return Boolean(TRANSITION_END);
- },
- isElement: function isElement(obj) {
- return (obj[0] || obj).nodeType;
- },
- typeCheckConfig: function typeCheckConfig(componentName, config, configTypes) {
- for (var property in configTypes) {
- if (Object.prototype.hasOwnProperty.call(configTypes, property)) {
- var expectedTypes = configTypes[property];
- var value = config[property];
- var valueType = value && Util.isElement(value) ? 'element' : toType(value);
- if (!new RegExp(expectedTypes).test(valueType)) {
- throw new Error(componentName.toUpperCase() + ": " + ("Option \"" + property + "\" provided type \"" + valueType + "\" ") + ("but expected type \"" + expectedTypes + "\"."));
- }
- }
- }
- },
- findShadowRoot: function findShadowRoot(element) {
- if (!document.documentElement.attachShadow) {
- return null;
- } // Can find the shadow root otherwise it'll return the document
- if (typeof element.getRootNode === 'function') {
- var root = element.getRootNode();
- return root instanceof ShadowRoot ? root : null;
- }
- if (element instanceof ShadowRoot) {
- return element;
- } // when we don't find a shadow root
- if (!element.parentNode) {
- return null;
- }
- return Util.findShadowRoot(element.parentNode);
- },
- jQueryDetection: function jQueryDetection() {
- if (typeof $ === 'undefined') {
- throw new TypeError('Bootstrap\'s JavaScript requires jQuery. jQuery must be included before Bootstrap\'s JavaScript.');
- }
- var version = $.fn.jquery.split(' ')[0].split('.');
- var minMajor = 1;
- var ltMajor = 2;
- var minMinor = 9;
- var minPatch = 1;
- var maxMajor = 4;
- if (version[0] < ltMajor && version[1] < minMinor || version[0] === minMajor && version[1] === minMinor && version[2] < minPatch || version[0] >= maxMajor) {
- throw new Error('Bootstrap\'s JavaScript requires at least jQuery v1.9.1 but less than v4.0.0');
- }
- }
- };
- Util.jQueryDetection();
- setTransitionEndSupport();
- /**
- * ------------------------------------------------------------------------
- * Constants
- * ------------------------------------------------------------------------
- */
- var NAME = 'alert';
- var VERSION = '4.4.1';
- var DATA_KEY = 'bs.alert';
- var EVENT_KEY = "." + DATA_KEY;
- var DATA_API_KEY = '.data-api';
- var JQUERY_NO_CONFLICT = $.fn[NAME];
- var Selector = {
- DISMISS: '[data-dismiss="alert"]'
- };
- var Event = {
- CLOSE: "close" + EVENT_KEY,
- CLOSED: "closed" + EVENT_KEY,
- CLICK_DATA_API: "click" + EVENT_KEY + DATA_API_KEY
- };
- var ClassName = {
- ALERT: 'alert',
- FADE: 'fade',
- SHOW: 'show'
- };
- /**
- * ------------------------------------------------------------------------
- * Class Definition
- * ------------------------------------------------------------------------
- */
- var Alert =
- /*#__PURE__*/
- function () {
- function Alert(element) {
- this._element = element;
- } // Getters
- var _proto = Alert.prototype;
- // Public
- _proto.close = function close(element) {
- var rootElement = this._element;
- if (element) {
- rootElement = this._getRootElement(element);
- }
- var customEvent = this._triggerCloseEvent(rootElement);
- if (customEvent.isDefaultPrevented()) {
- return;
- }
- this._removeElement(rootElement);
- };
- _proto.dispose = function dispose() {
- $.removeData(this._element, DATA_KEY);
- this._element = null;
- } // Private
- ;
- _proto._getRootElement = function _getRootElement(element) {
- var selector = Util.getSelectorFromElement(element);
- var parent = false;
- if (selector) {
- parent = document.querySelector(selector);
- }
- if (!parent) {
- parent = $(element).closest("." + ClassName.ALERT)[0];
- }
- return parent;
- };
- _proto._triggerCloseEvent = function _triggerCloseEvent(element) {
- var closeEvent = $.Event(Event.CLOSE);
- $(element).trigger(closeEvent);
- return closeEvent;
- };
- _proto._removeElement = function _removeElement(element) {
- var _this = this;
- $(element).removeClass(ClassName.SHOW);
- if (!$(element).hasClass(ClassName.FADE)) {
- this._destroyElement(element);
- return;
- }
- var transitionDuration = Util.getTransitionDurationFromElement(element);
- $(element).one(Util.TRANSITION_END, function (event) {
- return _this._destroyElement(element, event);
- }).emulateTransitionEnd(transitionDuration);
- };
- _proto._destroyElement = function _destroyElement(element) {
- $(element).detach().trigger(Event.CLOSED).remove();
- } // Static
- ;
- Alert._jQueryInterface = function _jQueryInterface(config) {
- return this.each(function () {
- var $element = $(this);
- var data = $element.data(DATA_KEY);
- if (!data) {
- data = new Alert(this);
- $element.data(DATA_KEY, data);
- }
- if (config === 'close') {
- data[config](this);
- }
- });
- };
- Alert._handleDismiss = function _handleDismiss(alertInstance) {
- return function (event) {
- if (event) {
- event.preventDefault();
- }
- alertInstance.close(this);
- };
- };
- _createClass(Alert, null, [{
- key: "VERSION",
- get: function get() {
- return VERSION;
- }
- }]);
- return Alert;
- }();
- /**
- * ------------------------------------------------------------------------
- * Data Api implementation
- * ------------------------------------------------------------------------
- */
- $(document).on(Event.CLICK_DATA_API, Selector.DISMISS, Alert._handleDismiss(new Alert()));
- /**
- * ------------------------------------------------------------------------
- * jQuery
- * ------------------------------------------------------------------------
- */
- $.fn[NAME] = Alert._jQueryInterface;
- $.fn[NAME].Constructor = Alert;
- $.fn[NAME].noConflict = function () {
- $.fn[NAME] = JQUERY_NO_CONFLICT;
- return Alert._jQueryInterface;
- };
- /**
- * ------------------------------------------------------------------------
- * Constants
- * ------------------------------------------------------------------------
- */
- var NAME$1 = 'button';
- var VERSION$1 = '4.4.1';
- var DATA_KEY$1 = 'bs.button';
- var EVENT_KEY$1 = "." + DATA_KEY$1;
- var DATA_API_KEY$1 = '.data-api';
- var JQUERY_NO_CONFLICT$1 = $.fn[NAME$1];
- var ClassName$1 = {
- ACTIVE: 'active',
- BUTTON: 'btn',
- FOCUS: 'focus'
- };
- var Selector$1 = {
- DATA_TOGGLE_CARROT: '[data-toggle^="button"]',
- DATA_TOGGLES: '[data-toggle="buttons"]',
- DATA_TOGGLE: '[data-toggle="button"]',
- DATA_TOGGLES_BUTTONS: '[data-toggle="buttons"] .btn',
- INPUT: 'input:not([type="hidden"])',
- ACTIVE: '.active',
- BUTTON: '.btn'
- };
- var Event$1 = {
- CLICK_DATA_API: "click" + EVENT_KEY$1 + DATA_API_KEY$1,
- FOCUS_BLUR_DATA_API: "focus" + EVENT_KEY$1 + DATA_API_KEY$1 + " " + ("blur" + EVENT_KEY$1 + DATA_API_KEY$1),
- LOAD_DATA_API: "load" + EVENT_KEY$1 + DATA_API_KEY$1
- };
- /**
- * ------------------------------------------------------------------------
- * Class Definition
- * ------------------------------------------------------------------------
- */
- var Button =
- /*#__PURE__*/
- function () {
- function Button(element) {
- this._element = element;
- } // Getters
- var _proto = Button.prototype;
- // Public
- _proto.toggle = function toggle() {
- var triggerChangeEvent = true;
- var addAriaPressed = true;
- var rootElement = $(this._element).closest(Selector$1.DATA_TOGGLES)[0];
- if (rootElement) {
- var input = this._element.querySelector(Selector$1.INPUT);
- if (input) {
- if (input.type === 'radio') {
- if (input.checked && this._element.classList.contains(ClassName$1.ACTIVE)) {
- triggerChangeEvent = false;
- } else {
- var activeElement = rootElement.querySelector(Selector$1.ACTIVE);
- if (activeElement) {
- $(activeElement).removeClass(ClassName$1.ACTIVE);
- }
- }
- } else if (input.type === 'checkbox') {
- if (this._element.tagName === 'LABEL' && input.checked === this._element.classList.contains(ClassName$1.ACTIVE)) {
- triggerChangeEvent = false;
- }
- } else {
- // if it's not a radio button or checkbox don't add a pointless/invalid checked property to the input
- triggerChangeEvent = false;
- }
- if (triggerChangeEvent) {
- input.checked = !this._element.classList.contains(ClassName$1.ACTIVE);
- $(input).trigger('change');
- }
- input.focus();
- addAriaPressed = false;
- }
- }
- if (!(this._element.hasAttribute('disabled') || this._element.classList.contains('disabled'))) {
- if (addAriaPressed) {
- this._element.setAttribute('aria-pressed', !this._element.classList.contains(ClassName$1.ACTIVE));
- }
- if (triggerChangeEvent) {
- $(this._element).toggleClass(ClassName$1.ACTIVE);
- }
- }
- };
- _proto.dispose = function dispose() {
- $.removeData(this._element, DATA_KEY$1);
- this._element = null;
- } // Static
- ;
- Button._jQueryInterface = function _jQueryInterface(config) {
- return this.each(function () {
- var data = $(this).data(DATA_KEY$1);
- if (!data) {
- data = new Button(this);
- $(this).data(DATA_KEY$1, data);
- }
- if (config === 'toggle') {
- data[config]();
- }
- });
- };
- _createClass(Button, null, [{
- key: "VERSION",
- get: function get() {
- return VERSION$1;
- }
- }]);
- return Button;
- }();
- /**
- * ------------------------------------------------------------------------
- * Data Api implementation
- * ------------------------------------------------------------------------
- */
- $(document).on(Event$1.CLICK_DATA_API, Selector$1.DATA_TOGGLE_CARROT, function (event) {
- var button = event.target;
- if (!$(button).hasClass(ClassName$1.BUTTON)) {
- button = $(button).closest(Selector$1.BUTTON)[0];
- }
- if (!button || button.hasAttribute('disabled') || button.classList.contains('disabled')) {
- event.preventDefault(); // work around Firefox bug #1540995
- } else {
- var inputBtn = button.querySelector(Selector$1.INPUT);
- if (inputBtn && (inputBtn.hasAttribute('disabled') || inputBtn.classList.contains('disabled'))) {
- event.preventDefault(); // work around Firefox bug #1540995
- return;
- }
- Button._jQueryInterface.call($(button), 'toggle');
- }
- }).on(Event$1.FOCUS_BLUR_DATA_API, Selector$1.DATA_TOGGLE_CARROT, function (event) {
- var button = $(event.target).closest(Selector$1.BUTTON)[0];
- $(button).toggleClass(ClassName$1.FOCUS, /^focus(in)?$/.test(event.type));
- });
- $(window).on(Event$1.LOAD_DATA_API, function () {
- // ensure correct active class is set to match the controls' actual values/states
- // find all checkboxes/readio buttons inside data-toggle groups
- var buttons = [].slice.call(document.querySelectorAll(Selector$1.DATA_TOGGLES_BUTTONS));
- for (var i = 0, len = buttons.length; i < len; i++) {
- var button = buttons[i];
- var input = button.querySelector(Selector$1.INPUT);
- if (input.checked || input.hasAttribute('checked')) {
- button.classList.add(ClassName$1.ACTIVE);
- } else {
- button.classList.remove(ClassName$1.ACTIVE);
- }
- } // find all button toggles
- buttons = [].slice.call(document.querySelectorAll(Selector$1.DATA_TOGGLE));
- for (var _i = 0, _len = buttons.length; _i < _len; _i++) {
- var _button = buttons[_i];
- if (_button.getAttribute('aria-pressed') === 'true') {
- _button.classList.add(ClassName$1.ACTIVE);
- } else {
- _button.classList.remove(ClassName$1.ACTIVE);
- }
- }
- });
- /**
- * ------------------------------------------------------------------------
- * jQuery
- * ------------------------------------------------------------------------
- */
- $.fn[NAME$1] = Button._jQueryInterface;
- $.fn[NAME$1].Constructor = Button;
- $.fn[NAME$1].noConflict = function () {
- $.fn[NAME$1] = JQUERY_NO_CONFLICT$1;
- return Button._jQueryInterface;
- };
- /**
- * ------------------------------------------------------------------------
- * Constants
- * ------------------------------------------------------------------------
- */
- var NAME$2 = 'carousel';
- var VERSION$2 = '4.4.1';
- var DATA_KEY$2 = 'bs.carousel';
- var EVENT_KEY$2 = "." + DATA_KEY$2;
- var DATA_API_KEY$2 = '.data-api';
- var JQUERY_NO_CONFLICT$2 = $.fn[NAME$2];
- var ARROW_LEFT_KEYCODE = 37; // KeyboardEvent.which value for left arrow key
- var ARROW_RIGHT_KEYCODE = 39; // KeyboardEvent.which value for right arrow key
- var TOUCHEVENT_COMPAT_WAIT = 500; // Time for mouse compat events to fire after touch
- var SWIPE_THRESHOLD = 40;
- var Default = {
- interval: 5000,
- keyboard: true,
- slide: false,
- pause: 'hover',
- wrap: true,
- touch: true
- };
- var DefaultType = {
- interval: '(number|boolean)',
- keyboard: 'boolean',
- slide: '(boolean|string)',
- pause: '(string|boolean)',
- wrap: 'boolean',
- touch: 'boolean'
- };
- var Direction = {
- NEXT: 'next',
- PREV: 'prev',
- LEFT: 'left',
- RIGHT: 'right'
- };
- var Event$2 = {
- SLIDE: "slide" + EVENT_KEY$2,
- SLID: "slid" + EVENT_KEY$2,
- KEYDOWN: "keydown" + EVENT_KEY$2,
- MOUSEENTER: "mouseenter" + EVENT_KEY$2,
- MOUSELEAVE: "mouseleave" + EVENT_KEY$2,
- TOUCHSTART: "touchstart" + EVENT_KEY$2,
- TOUCHMOVE: "touchmove" + EVENT_KEY$2,
- TOUCHEND: "touchend" + EVENT_KEY$2,
- POINTERDOWN: "pointerdown" + EVENT_KEY$2,
- POINTERUP: "pointerup" + EVENT_KEY$2,
- DRAG_START: "dragstart" + EVENT_KEY$2,
- LOAD_DATA_API: "load" + EVENT_KEY$2 + DATA_API_KEY$2,
- CLICK_DATA_API: "click" + EVENT_KEY$2 + DATA_API_KEY$2
- };
- var ClassName$2 = {
- CAROUSEL: 'carousel',
- ACTIVE: 'active',
- SLIDE: 'slide',
- RIGHT: 'carousel-item-right',
- LEFT: 'carousel-item-left',
- NEXT: 'carousel-item-next',
- PREV: 'carousel-item-prev',
- ITEM: 'carousel-item',
- POINTER_EVENT: 'pointer-event'
- };
- var Selector$2 = {
- ACTIVE: '.active',
- ACTIVE_ITEM: '.active.carousel-item',
- ITEM: '.carousel-item',
- ITEM_IMG: '.carousel-item img',
- NEXT_PREV: '.carousel-item-next, .carousel-item-prev',
- INDICATORS: '.carousel-indicators',
- DATA_SLIDE: '[data-slide], [data-slide-to]',
- DATA_RIDE: '[data-ride="carousel"]'
- };
- var PointerType = {
- TOUCH: 'touch',
- PEN: 'pen'
- };
- /**
- * ------------------------------------------------------------------------
- * Class Definition
- * ------------------------------------------------------------------------
- */
- var Carousel =
- /*#__PURE__*/
- function () {
- function Carousel(element, config) {
- this._items = null;
- this._interval = null;
- this._activeElement = null;
- this._isPaused = false;
- this._isSliding = false;
- this.touchTimeout = null;
- this.touchStartX = 0;
- this.touchDeltaX = 0;
- this._config = this._getConfig(config);
- this._element = element;
- this._indicatorsElement = this._element.querySelector(Selector$2.INDICATORS);
- this._touchSupported = 'ontouchstart' in document.documentElement || navigator.maxTouchPoints > 0;
- this._pointerEvent = Boolean(window.PointerEvent || window.MSPointerEvent);
- this._addEventListeners();
- } // Getters
- var _proto = Carousel.prototype;
- // Public
- _proto.next = function next() {
- if (!this._isSliding) {
- this._slide(Direction.NEXT);
- }
- };
- _proto.nextWhenVisible = function nextWhenVisible() {
- // Don't call next when the page isn't visible
- // or the carousel or its parent isn't visible
- if (!document.hidden && $(this._element).is(':visible') && $(this._element).css('visibility') !== 'hidden') {
- this.next();
- }
- };
- _proto.prev = function prev() {
- if (!this._isSliding) {
- this._slide(Direction.PREV);
- }
- };
- _proto.pause = function pause(event) {
- if (!event) {
- this._isPaused = true;
- }
- if (this._element.querySelector(Selector$2.NEXT_PREV)) {
- Util.triggerTransitionEnd(this._element);
- this.cycle(true);
- }
- clearInterval(this._interval);
- this._interval = null;
- };
- _proto.cycle = function cycle(event) {
- if (!event) {
- this._isPaused = false;
- }
- if (this._interval) {
- clearInterval(this._interval);
- this._interval = null;
- }
- if (this._config.interval && !this._isPaused) {
- this._interval = setInterval((document.visibilityState ? this.nextWhenVisible : this.next).bind(this), this._config.interval);
- }
- };
- _proto.to = function to(index) {
- var _this = this;
- this._activeElement = this._element.querySelector(Selector$2.ACTIVE_ITEM);
- var activeIndex = this._getItemIndex(this._activeElement);
- if (index > this._items.length - 1 || index < 0) {
- return;
- }
- if (this._isSliding) {
- $(this._element).one(Event$2.SLID, function () {
- return _this.to(index);
- });
- return;
- }
- if (activeIndex === index) {
- this.pause();
- this.cycle();
- return;
- }
- var direction = index > activeIndex ? Direction.NEXT : Direction.PREV;
- this._slide(direction, this._items[index]);
- };
- _proto.dispose = function dispose() {
- $(this._element).off(EVENT_KEY$2);
- $.removeData(this._element, DATA_KEY$2);
- this._items = null;
- this._config = null;
- this._element = null;
- this._interval = null;
- this._isPaused = null;
- this._isSliding = null;
- this._activeElement = null;
- this._indicatorsElement = null;
- } // Private
- ;
- _proto._getConfig = function _getConfig(config) {
- config = _objectSpread2({}, Default, {}, config);
- Util.typeCheckConfig(NAME$2, config, DefaultType);
- return config;
- };
- _proto._handleSwipe = function _handleSwipe() {
- var absDeltax = Math.abs(this.touchDeltaX);
- if (absDeltax <= SWIPE_THRESHOLD) {
- return;
- }
- var direction = absDeltax / this.touchDeltaX;
- this.touchDeltaX = 0; // swipe left
- if (direction > 0) {
- this.prev();
- } // swipe right
- if (direction < 0) {
- this.next();
- }
- };
- _proto._addEventListeners = function _addEventListeners() {
- var _this2 = this;
- if (this._config.keyboard) {
- $(this._element).on(Event$2.KEYDOWN, function (event) {
- return _this2._keydown(event);
- });
- }
- if (this._config.pause === 'hover') {
- $(this._element).on(Event$2.MOUSEENTER, function (event) {
- return _this2.pause(event);
- }).on(Event$2.MOUSELEAVE, function (event) {
- return _this2.cycle(event);
- });
- }
- if (this._config.touch) {
- this._addTouchEventListeners();
- }
- };
- _proto._addTouchEventListeners = function _addTouchEventListeners() {
- var _this3 = this;
- if (!this._touchSupported) {
- return;
- }
- var start = function start(event) {
- if (_this3._pointerEvent && PointerType[event.originalEvent.pointerType.toUpperCase()]) {
- _this3.touchStartX = event.originalEvent.clientX;
- } else if (!_this3._pointerEvent) {
- _this3.touchStartX = event.originalEvent.touches[0].clientX;
- }
- };
- var move = function move(event) {
- // ensure swiping with one touch and not pinching
- if (event.originalEvent.touches && event.originalEvent.touches.length > 1) {
- _this3.touchDeltaX = 0;
- } else {
- _this3.touchDeltaX = event.originalEvent.touches[0].clientX - _this3.touchStartX;
- }
- };
- var end = function end(event) {
- if (_this3._pointerEvent && PointerType[event.originalEvent.pointerType.toUpperCase()]) {
- _this3.touchDeltaX = event.originalEvent.clientX - _this3.touchStartX;
- }
- _this3._handleSwipe();
- if (_this3._config.pause === 'hover') {
- // If it's a touch-enabled device, mouseenter/leave are fired as
- // part of the mouse compatibility events on first tap - the carousel
- // would stop cycling until user tapped out of it;
- // here, we listen for touchend, explicitly pause the carousel
- // (as if it's the second time we tap on it, mouseenter compat event
- // is NOT fired) and after a timeout (to allow for mouse compatibility
- // events to fire) we explicitly restart cycling
- _this3.pause();
- if (_this3.touchTimeout) {
- clearTimeout(_this3.touchTimeout);
- }
- _this3.touchTimeout = setTimeout(function (event) {
- return _this3.cycle(event);
- }, TOUCHEVENT_COMPAT_WAIT + _this3._config.interval);
- }
- };
- $(this._element.querySelectorAll(Selector$2.ITEM_IMG)).on(Event$2.DRAG_START, function (e) {
- return e.preventDefault();
- });
- if (this._pointerEvent) {
- $(this._element).on(Event$2.POINTERDOWN, function (event) {
- return start(event);
- });
- $(this._element).on(Event$2.POINTERUP, function (event) {
- return end(event);
- });
- this._element.classList.add(ClassName$2.POINTER_EVENT);
- } else {
- $(this._element).on(Event$2.TOUCHSTART, function (event) {
- return start(event);
- });
- $(this._element).on(Event$2.TOUCHMOVE, function (event) {
- return move(event);
- });
- $(this._element).on(Event$2.TOUCHEND, function (event) {
- return end(event);
- });
- }
- };
- _proto._keydown = function _keydown(event) {
- if (/input|textarea/i.test(event.target.tagName)) {
- return;
- }
- switch (event.which) {
- case ARROW_LEFT_KEYCODE:
- event.preventDefault();
- this.prev();
- break;
- case ARROW_RIGHT_KEYCODE:
- event.preventDefault();
- this.next();
- break;
- }
- };
- _proto._getItemIndex = function _getItemIndex(element) {
- this._items = element && element.parentNode ? [].slice.call(element.parentNode.querySelectorAll(Selector$2.ITEM)) : [];
- return this._items.indexOf(element);
- };
- _proto._getItemByDirection = function _getItemByDirection(direction, activeElement) {
- var isNextDirection = direction === Direction.NEXT;
- var isPrevDirection = direction === Direction.PREV;
- var activeIndex = this._getItemIndex(activeElement);
- var lastItemIndex = this._items.length - 1;
- var isGoingToWrap = isPrevDirection && activeIndex === 0 || isNextDirection && activeIndex === lastItemIndex;
- if (isGoingToWrap && !this._config.wrap) {
- return activeElement;
- }
- var delta = direction === Direction.PREV ? -1 : 1;
- var itemIndex = (activeIndex + delta) % this._items.length;
- return itemIndex === -1 ? this._items[this._items.length - 1] : this._items[itemIndex];
- };
- _proto._triggerSlideEvent = function _triggerSlideEvent(relatedTarget, eventDirectionName) {
- var targetIndex = this._getItemIndex(relatedTarget);
- var fromIndex = this._getItemIndex(this._element.querySelector(Selector$2.ACTIVE_ITEM));
- var slideEvent = $.Event(Event$2.SLIDE, {
- relatedTarget: relatedTarget,
- direction: eventDirectionName,
- from: fromIndex,
- to: targetIndex
- });
- $(this._element).trigger(slideEvent);
- return slideEvent;
- };
- _proto._setActiveIndicatorElement = function _setActiveIndicatorElement(element) {
- if (this._indicatorsElement) {
- var indicators = [].slice.call(this._indicatorsElement.querySelectorAll(Selector$2.ACTIVE));
- $(indicators).removeClass(ClassName$2.ACTIVE);
- var nextIndicator = this._indicatorsElement.children[this._getItemIndex(element)];
- if (nextIndicator) {
- $(nextIndicator).addClass(ClassName$2.ACTIVE);
- }
- }
- };
- _proto._slide = function _slide(direction, element) {
- var _this4 = this;
- var activeElement = this._element.querySelector(Selector$2.ACTIVE_ITEM);
- var activeElementIndex = this._getItemIndex(activeElement);
- var nextElement = element || activeElement && this._getItemByDirection(direction, activeElement);
- var nextElementIndex = this._getItemIndex(nextElement);
- var isCycling = Boolean(this._interval);
- var directionalClassName;
- var orderClassName;
- var eventDirectionName;
- if (direction === Direction.NEXT) {
- directionalClassName = ClassName$2.LEFT;
- orderClassName = ClassName$2.NEXT;
- eventDirectionName = Direction.LEFT;
- } else {
- directionalClassName = ClassName$2.RIGHT;
- orderClassName = ClassName$2.PREV;
- eventDirectionName = Direction.RIGHT;
- }
- if (nextElement && $(nextElement).hasClass(ClassName$2.ACTIVE)) {
- this._isSliding = false;
- return;
- }
- var slideEvent = this._triggerSlideEvent(nextElement, eventDirectionName);
- if (slideEvent.isDefaultPrevented()) {
- return;
- }
- if (!activeElement || !nextElement) {
- // Some weirdness is happening, so we bail
- return;
- }
- this._isSliding = true;
- if (isCycling) {
- this.pause();
- }
- this._setActiveIndicatorElement(nextElement);
- var slidEvent = $.Event(Event$2.SLID, {
- relatedTarget: nextElement,
- direction: eventDirectionName,
- from: activeElementIndex,
- to: nextElementIndex
- });
- if ($(this._element).hasClass(ClassName$2.SLIDE)) {
- $(nextElement).addClass(orderClassName);
- Util.reflow(nextElement);
- $(activeElement).addClass(directionalClassName);
- $(nextElement).addClass(directionalClassName);
- var nextElementInterval = parseInt(nextElement.getAttribute('data-interval'), 10);
- if (nextElementInterval) {
- this._config.defaultInterval = this._config.defaultInterval || this._config.interval;
- this._config.interval = nextElementInterval;
- } else {
- this._config.interval = this._config.defaultInterval || this._config.interval;
- }
- var transitionDuration = Util.getTransitionDurationFromElement(activeElement);
- $(activeElement).one(Util.TRANSITION_END, function () {
- $(nextElement).removeClass(directionalClassName + " " + orderClassName).addClass(ClassName$2.ACTIVE);
- $(activeElement).removeClass(ClassName$2.ACTIVE + " " + orderClassName + " " + directionalClassName);
- _this4._isSliding = false;
- setTimeout(function () {
- return $(_this4._element).trigger(slidEvent);
- }, 0);
- }).emulateTransitionEnd(transitionDuration);
- } else {
- $(activeElement).removeClass(ClassName$2.ACTIVE);
- $(nextElement).addClass(ClassName$2.ACTIVE);
- this._isSliding = false;
- $(this._element).trigger(slidEvent);
- }
- if (isCycling) {
- this.cycle();
- }
- } // Static
- ;
- Carousel._jQueryInterface = function _jQueryInterface(config) {
- return this.each(function () {
- var data = $(this).data(DATA_KEY$2);
- var _config = _objectSpread2({}, Default, {}, $(this).data());
- if (typeof config === 'object') {
- _config = _objectSpread2({}, _config, {}, config);
- }
- var action = typeof config === 'string' ? config : _config.slide;
- if (!data) {
- data = new Carousel(this, _config);
- $(this).data(DATA_KEY$2, data);
- }
- if (typeof config === 'number') {
- data.to(config);
- } else if (typeof action === 'string') {
- if (typeof data[action] === 'undefined') {
- throw new TypeError("No method named \"" + action + "\"");
- }
- data[action]();
- } else if (_config.interval && _config.ride) {
- data.pause();
- data.cycle();
- }
- });
- };
- Carousel._dataApiClickHandler = function _dataApiClickHandler(event) {
- var selector = Util.getSelectorFromElement(this);
- if (!selector) {
- return;
- }
- var target = $(selector)[0];
- if (!target || !$(target).hasClass(ClassName$2.CAROUSEL)) {
- return;
- }
- var config = _objectSpread2({}, $(target).data(), {}, $(this).data());
- var slideIndex = this.getAttribute('data-slide-to');
- if (slideIndex) {
- config.interval = false;
- }
- Carousel._jQueryInterface.call($(target), config);
- if (slideIndex) {
- $(target).data(DATA_KEY$2).to(slideIndex);
- }
- event.preventDefault();
- };
- _createClass(Carousel, null, [{
- key: "VERSION",
- get: function get() {
- return VERSION$2;
- }
- }, {
- key: "Default",
- get: function get() {
- return Default;
- }
- }]);
- return Carousel;
- }();
- /**
- * ------------------------------------------------------------------------
- * Data Api implementation
- * ------------------------------------------------------------------------
- */
- $(document).on(Event$2.CLICK_DATA_API, Selector$2.DATA_SLIDE, Carousel._dataApiClickHandler);
- $(window).on(Event$2.LOAD_DATA_API, function () {
- var carousels = [].slice.call(document.querySelectorAll(Selector$2.DATA_RIDE));
- for (var i = 0, len = carousels.length; i < len; i++) {
- var $carousel = $(carousels[i]);
- Carousel._jQueryInterface.call($carousel, $carousel.data());
- }
- });
- /**
- * ------------------------------------------------------------------------
- * jQuery
- * ------------------------------------------------------------------------
- */
- $.fn[NAME$2] = Carousel._jQueryInterface;
- $.fn[NAME$2].Constructor = Carousel;
- $.fn[NAME$2].noConflict = function () {
- $.fn[NAME$2] = JQUERY_NO_CONFLICT$2;
- return Carousel._jQueryInterface;
- };
- /**
- * ------------------------------------------------------------------------
- * Constants
- * ------------------------------------------------------------------------
- */
- var NAME$3 = 'collapse';
- var VERSION$3 = '4.4.1';
- var DATA_KEY$3 = 'bs.collapse';
- var EVENT_KEY$3 = "." + DATA_KEY$3;
- var DATA_API_KEY$3 = '.data-api';
- var JQUERY_NO_CONFLICT$3 = $.fn[NAME$3];
- var Default$1 = {
- toggle: true,
- parent: ''
- };
- var DefaultType$1 = {
- toggle: 'boolean',
- parent: '(string|element)'
- };
- var Event$3 = {
- SHOW: "show" + EVENT_KEY$3,
- SHOWN: "shown" + EVENT_KEY$3,
- HIDE: "hide" + EVENT_KEY$3,
- HIDDEN: "hidden" + EVENT_KEY$3,
- CLICK_DATA_API: "click" + EVENT_KEY$3 + DATA_API_KEY$3
- };
- var ClassName$3 = {
- SHOW: 'show',
- COLLAPSE: 'collapse',
- COLLAPSING: 'collapsing',
- COLLAPSED: 'collapsed'
- };
- var Dimension = {
- WIDTH: 'width',
- HEIGHT: 'height'
- };
- var Selector$3 = {
- ACTIVES: '.show, .collapsing',
- DATA_TOGGLE: '[data-toggle="collapse"]'
- };
- /**
- * ------------------------------------------------------------------------
- * Class Definition
- * ------------------------------------------------------------------------
- */
- var Collapse =
- /*#__PURE__*/
- function () {
- function Collapse(element, config) {
- this._isTransitioning = false;
- this._element = element;
- this._config = this._getConfig(config);
- this._triggerArray = [].slice.call(document.querySelectorAll("[data-toggle=\"collapse\"][href=\"#" + element.id + "\"]," + ("[data-toggle=\"collapse\"][data-target=\"#" + element.id + "\"]")));
- var toggleList = [].slice.call(document.querySelectorAll(Selector$3.DATA_TOGGLE));
- for (var i = 0, len = toggleList.length; i < len; i++) {
- var elem = toggleList[i];
- var selector = Util.getSelectorFromElement(elem);
- var filterElement = [].slice.call(document.querySelectorAll(selector)).filter(function (foundElem) {
- return foundElem === element;
- });
- if (selector !== null && filterElement.length > 0) {
- this._selector = selector;
- this._triggerArray.push(elem);
- }
- }
- this._parent = this._config.parent ? this._getParent() : null;
- if (!this._config.parent) {
- this._addAriaAndCollapsedClass(this._element, this._triggerArray);
- }
- if (this._config.toggle) {
- this.toggle();
- }
- } // Getters
- var _proto = Collapse.prototype;
- // Public
- _proto.toggle = function toggle() {
- if ($(this._element).hasClass(ClassName$3.SHOW)) {
- this.hide();
- } else {
- this.show();
- }
- };
- _proto.show = function show() {
- var _this = this;
- if (this._isTransitioning || $(this._element).hasClass(ClassName$3.SHOW)) {
- return;
- }
- var actives;
- var activesData;
- if (this._parent) {
- actives = [].slice.call(this._parent.querySelectorAll(Selector$3.ACTIVES)).filter(function (elem) {
- if (typeof _this._config.parent === 'string') {
- return elem.getAttribute('data-parent') === _this._config.parent;
- }
- return elem.classList.contains(ClassName$3.COLLAPSE);
- });
- if (actives.length === 0) {
- actives = null;
- }
- }
- if (actives) {
- activesData = $(actives).not(this._selector).data(DATA_KEY$3);
- if (activesData && activesData._isTransitioning) {
- return;
- }
- }
- var startEvent = $.Event(Event$3.SHOW);
- $(this._element).trigger(startEvent);
- if (startEvent.isDefaultPrevented()) {
- return;
- }
- if (actives) {
- Collapse._jQueryInterface.call($(actives).not(this._selector), 'hide');
- if (!activesData) {
- $(actives).data(DATA_KEY$3, null);
- }
- }
- var dimension = this._getDimension();
- $(this._element).removeClass(ClassName$3.COLLAPSE).addClass(ClassName$3.COLLAPSING);
- this._element.style[dimension] = 0;
- if (this._triggerArray.length) {
- $(this._triggerArray).removeClass(ClassName$3.COLLAPSED).attr('aria-expanded', true);
- }
- this.setTransitioning(true);
- var complete = function complete() {
- $(_this._element).removeClass(ClassName$3.COLLAPSING).addClass(ClassName$3.COLLAPSE).addClass(ClassName$3.SHOW);
- _this._element.style[dimension] = '';
- _this.setTransitioning(false);
- $(_this._element).trigger(Event$3.SHOWN);
- };
- var capitalizedDimension = dimension[0].toUpperCase() + dimension.slice(1);
- var scrollSize = "scroll" + capitalizedDimension;
- var transitionDuration = Util.getTransitionDurationFromElement(this._element);
- $(this._element).one(Util.TRANSITION_END, complete).emulateTransitionEnd(transitionDuration);
- this._element.style[dimension] = this._element[scrollSize] + "px";
- };
- _proto.hide = function hide() {
- var _this2 = this;
- if (this._isTransitioning || !$(this._element).hasClass(ClassName$3.SHOW)) {
- return;
- }
- var startEvent = $.Event(Event$3.HIDE);
- $(this._element).trigger(startEvent);
- if (startEvent.isDefaultPrevented()) {
- return;
- }
- var dimension = this._getDimension();
- this._element.style[dimension] = this._element.getBoundingClientRect()[dimension] + "px";
- Util.reflow(this._element);
- $(this._element).addClass(ClassName$3.COLLAPSING).removeClass(ClassName$3.COLLAPSE).removeClass(ClassName$3.SHOW);
- var triggerArrayLength = this._triggerArray.length;
- if (triggerArrayLength > 0) {
- for (var i = 0; i < triggerArrayLength; i++) {
- var trigger = this._triggerArray[i];
- var selector = Util.getSelectorFromElement(trigger);
- if (selector !== null) {
- var $elem = $([].slice.call(document.querySelectorAll(selector)));
- if (!$elem.hasClass(ClassName$3.SHOW)) {
- $(trigger).addClass(ClassName$3.COLLAPSED).attr('aria-expanded', false);
- }
- }
- }
- }
- this.setTransitioning(true);
- var complete = function complete() {
- _this2.setTransitioning(false);
- $(_this2._element).removeClass(ClassName$3.COLLAPSING).addClass(ClassName$3.COLLAPSE).trigger(Event$3.HIDDEN);
- };
- this._element.style[dimension] = '';
- var transitionDuration = Util.getTransitionDurationFromElement(this._element);
- $(this._element).one(Util.TRANSITION_END, complete).emulateTransitionEnd(transitionDuration);
- };
- _proto.setTransitioning = function setTransitioning(isTransitioning) {
- this._isTransitioning = isTransitioning;
- };
- _proto.dispose = function dispose() {
- $.removeData(this._element, DATA_KEY$3);
- this._config = null;
- this._parent = null;
- this._element = null;
- this._triggerArray = null;
- this._isTransitioning = null;
- } // Private
- ;
- _proto._getConfig = function _getConfig(config) {
- config = _objectSpread2({}, Default$1, {}, config);
- config.toggle = Boolean(config.toggle); // Coerce string values
- Util.typeCheckConfig(NAME$3, config, DefaultType$1);
- return config;
- };
- _proto._getDimension = function _getDimension() {
- var hasWidth = $(this._element).hasClass(Dimension.WIDTH);
- return hasWidth ? Dimension.WIDTH : Dimension.HEIGHT;
- };
- _proto._getParent = function _getParent() {
- var _this3 = this;
- var parent;
- if (Util.isElement(this._config.parent)) {
- parent = this._config.parent; // It's a jQuery object
- if (typeof this._config.parent.jquery !== 'undefined') {
- parent = this._config.parent[0];
- }
- } else {
- parent = document.querySelector(this._config.parent);
- }
- var selector = "[data-toggle=\"collapse\"][data-parent=\"" + this._config.parent + "\"]";
- var children = [].slice.call(parent.querySelectorAll(selector));
- $(children).each(function (i, element) {
- _this3._addAriaAndCollapsedClass(Collapse._getTargetFromElement(element), [element]);
- });
- return parent;
- };
- _proto._addAriaAndCollapsedClass = function _addAriaAndCollapsedClass(element, triggerArray) {
- var isOpen = $(element).hasClass(ClassName$3.SHOW);
- if (triggerArray.length) {
- $(triggerArray).toggleClass(ClassName$3.COLLAPSED, !isOpen).attr('aria-expanded', isOpen);
- }
- } // Static
- ;
- Collapse._getTargetFromElement = function _getTargetFromElement(element) {
- var selector = Util.getSelectorFromElement(element);
- return selector ? document.querySelector(selector) : null;
- };
- Collapse._jQueryInterface = function _jQueryInterface(config) {
- return this.each(function () {
- var $this = $(this);
- var data = $this.data(DATA_KEY$3);
- var _config = _objectSpread2({}, Default$1, {}, $this.data(), {}, typeof config === 'object' && config ? config : {});
- if (!data && _config.toggle && /show|hide/.test(config)) {
- _config.toggle = false;
- }
- if (!data) {
- data = new Collapse(this, _config);
- $this.data(DATA_KEY$3, data);
- }
- if (typeof config === 'string') {
- if (typeof data[config] === 'undefined') {
- throw new TypeError("No method named \"" + config + "\"");
- }
- data[config]();
- }
- });
- };
- _createClass(Collapse, null, [{
- key: "VERSION",
- get: function get() {
- return VERSION$3;
- }
- }, {
- key: "Default",
- get: function get() {
- return Default$1;
- }
- }]);
- return Collapse;
- }();
- /**
- * ------------------------------------------------------------------------
- * Data Api implementation
- * ------------------------------------------------------------------------
- */
- $(document).on(Event$3.CLICK_DATA_API, Selector$3.DATA_TOGGLE, function (event) {
- // preventDefault only for <a> elements (which change the URL) not inside the collapsible element
- if (event.currentTarget.tagName === 'A') {
- event.preventDefault();
- }
- var $trigger = $(this);
- var selector = Util.getSelectorFromElement(this);
- var selectors = [].slice.call(document.querySelectorAll(selector));
- $(selectors).each(function () {
- var $target = $(this);
- var data = $target.data(DATA_KEY$3);
- var config = data ? 'toggle' : $trigger.data();
- Collapse._jQueryInterface.call($target, config);
- });
- });
- /**
- * ------------------------------------------------------------------------
- * jQuery
- * ------------------------------------------------------------------------
- */
- $.fn[NAME$3] = Collapse._jQueryInterface;
- $.fn[NAME$3].Constructor = Collapse;
- $.fn[NAME$3].noConflict = function () {
- $.fn[NAME$3] = JQUERY_NO_CONFLICT$3;
- return Collapse._jQueryInterface;
- };
- /**
- * ------------------------------------------------------------------------
- * Constants
- * ------------------------------------------------------------------------
- */
- var NAME$4 = 'dropdown';
- var VERSION$4 = '4.4.1';
- var DATA_KEY$4 = 'bs.dropdown';
- var EVENT_KEY$4 = "." + DATA_KEY$4;
- var DATA_API_KEY$4 = '.data-api';
- var JQUERY_NO_CONFLICT$4 = $.fn[NAME$4];
- var ESCAPE_KEYCODE = 27; // KeyboardEvent.which value for Escape (Esc) key
- var SPACE_KEYCODE = 32; // KeyboardEvent.which value for space key
- var TAB_KEYCODE = 9; // KeyboardEvent.which value for tab key
- var ARROW_UP_KEYCODE = 38; // KeyboardEvent.which value for up arrow key
- var ARROW_DOWN_KEYCODE = 40; // KeyboardEvent.which value for down arrow key
- var RIGHT_MOUSE_BUTTON_WHICH = 3; // MouseEvent.which value for the right button (assuming a right-handed mouse)
- var REGEXP_KEYDOWN = new RegExp(ARROW_UP_KEYCODE + "|" + ARROW_DOWN_KEYCODE + "|" + ESCAPE_KEYCODE);
- var Event$4 = {
- HIDE: "hide" + EVENT_KEY$4,
- HIDDEN: "hidden" + EVENT_KEY$4,
- SHOW: "show" + EVENT_KEY$4,
- SHOWN: "shown" + EVENT_KEY$4,
- CLICK: "click" + EVENT_KEY$4,
- CLICK_DATA_API: "click" + EVENT_KEY$4 + DATA_API_KEY$4,
- KEYDOWN_DATA_API: "keydown" + EVENT_KEY$4 + DATA_API_KEY$4,
- KEYUP_DATA_API: "keyup" + EVENT_KEY$4 + DATA_API_KEY$4
- };
- var ClassName$4 = {
- DISABLED: 'disabled',
- SHOW: 'show',
- DROPUP: 'dropup',
- DROPRIGHT: 'dropright',
- DROPLEFT: 'dropleft',
- MENURIGHT: 'dropdown-menu-right',
- MENULEFT: 'dropdown-menu-left',
- POSITION_STATIC: 'position-static'
- };
- var Selector$4 = {
- DATA_TOGGLE: '[data-toggle="dropdown"]',
- FORM_CHILD: '.dropdown form',
- MENU: '.dropdown-menu',
- NAVBAR_NAV: '.navbar-nav',
- VISIBLE_ITEMS: '.dropdown-menu .dropdown-item:not(.disabled):not(:disabled)'
- };
- var AttachmentMap = {
- TOP: 'top-start',
- TOPEND: 'top-end',
- BOTTOM: 'bottom-start',
- BOTTOMEND: 'bottom-end',
- RIGHT: 'right-start',
- RIGHTEND: 'right-end',
- LEFT: 'left-start',
- LEFTEND: 'left-end'
- };
- var Default$2 = {
- offset: 0,
- flip: true,
- boundary: 'scrollParent',
- reference: 'toggle',
- display: 'dynamic',
- popperConfig: null
- };
- var DefaultType$2 = {
- offset: '(number|string|function)',
- flip: 'boolean',
- boundary: '(string|element)',
- reference: '(string|element)',
- display: 'string',
- popperConfig: '(null|object)'
- };
- /**
- * ------------------------------------------------------------------------
- * Class Definition
- * ------------------------------------------------------------------------
- */
- var Dropdown =
- /*#__PURE__*/
- function () {
- function Dropdown(element, config) {
- this._element = element;
- this._popper = null;
- this._config = this._getConfig(config);
- this._menu = this._getMenuElement();
- this._inNavbar = this._detectNavbar();
- this._addEventListeners();
- } // Getters
- var _proto = Dropdown.prototype;
- // Public
- _proto.toggle = function toggle() {
- if (this._element.disabled || $(this._element).hasClass(ClassName$4.DISABLED)) {
- return;
- }
- var isActive = $(this._menu).hasClass(ClassName$4.SHOW);
- Dropdown._clearMenus();
- if (isActive) {
- return;
- }
- this.show(true);
- };
- _proto.show = function show(usePopper) {
- if (usePopper === void 0) {
- usePopper = false;
- }
- if (this._element.disabled || $(this._element).hasClass(ClassName$4.DISABLED) || $(this._menu).hasClass(ClassName$4.SHOW)) {
- return;
- }
- var relatedTarget = {
- relatedTarget: this._element
- };
- var showEvent = $.Event(Event$4.SHOW, relatedTarget);
- var parent = Dropdown._getParentFromElement(this._element);
- $(parent).trigger(showEvent);
- if (showEvent.isDefaultPrevented()) {
- return;
- } // Disable totally Popper.js for Dropdown in Navbar
- if (!this._inNavbar && usePopper) {
- /**
- * Check for Popper dependency
- * Popper - https://popper.js.org
- */
- if (typeof Popper === 'undefined') {
- throw new TypeError('Bootstrap\'s dropdowns require Popper.js (https://popper.js.org/)');
- }
- var referenceElement = this._element;
- if (this._config.reference === 'parent') {
- referenceElement = parent;
- } else if (Util.isElement(this._config.reference)) {
- referenceElement = this._config.reference; // Check if it's jQuery element
- if (typeof this._config.reference.jquery !== 'undefined') {
- referenceElement = this._config.reference[0];
- }
- } // If boundary is not `scrollParent`, then set position to `static`
- // to allow the menu to "escape" the scroll parent's boundaries
- // https://github.com/twbs/bootstrap/issues/24251
- if (this._config.boundary !== 'scrollParent') {
- $(parent).addClass(ClassName$4.POSITION_STATIC);
- }
- this._popper = new Popper(referenceElement, this._menu, this._getPopperConfig());
- } // If this is a touch-enabled device we add extra
- // empty mouseover listeners to the body's immediate children;
- // only needed because of broken event delegation on iOS
- // https://www.quirksmode.org/blog/archives/2014/02/mouse_event_bub.html
- if ('ontouchstart' in document.documentElement && $(parent).closest(Selector$4.NAVBAR_NAV).length === 0) {
- $(document.body).children().on('mouseover', null, $.noop);
- }
- this._element.focus();
- this._element.setAttribute('aria-expanded', true);
- $(this._menu).toggleClass(ClassName$4.SHOW);
- $(parent).toggleClass(ClassName$4.SHOW).trigger($.Event(Event$4.SHOWN, relatedTarget));
- };
- _proto.hide = function hide() {
- if (this._element.disabled || $(this._element).hasClass(ClassName$4.DISABLED) || !$(this._menu).hasClass(ClassName$4.SHOW)) {
- return;
- }
- var relatedTarget = {
- relatedTarget: this._element
- };
- var hideEvent = $.Event(Event$4.HIDE, relatedTarget);
- var parent = Dropdown._getParentFromElement(this._element);
- $(parent).trigger(hideEvent);
- if (hideEvent.isDefaultPrevented()) {
- return;
- }
- if (this._popper) {
- this._popper.destroy();
- }
- $(this._menu).toggleClass(ClassName$4.SHOW);
- $(parent).toggleClass(ClassName$4.SHOW).trigger($.Event(Event$4.HIDDEN, relatedTarget));
- };
- _proto.dispose = function dispose() {
- $.removeData(this._element, DATA_KEY$4);
- $(this._element).off(EVENT_KEY$4);
- this._element = null;
- this._menu = null;
- if (this._popper !== null) {
- this._popper.destroy();
- this._popper = null;
- }
- };
- _proto.update = function update() {
- this._inNavbar = this._detectNavbar();
- if (this._popper !== null) {
- this._popper.scheduleUpdate();
- }
- } // Private
- ;
- _proto._addEventListeners = function _addEventListeners() {
- var _this = this;
- $(this._element).on(Event$4.CLICK, function (event) {
- event.preventDefault();
- event.stopPropagation();
- _this.toggle();
- });
- };
- _proto._getConfig = function _getConfig(config) {
- config = _objectSpread2({}, this.constructor.Default, {}, $(this._element).data(), {}, config);
- Util.typeCheckConfig(NAME$4, config, this.constructor.DefaultType);
- return config;
- };
- _proto._getMenuElement = function _getMenuElement() {
- if (!this._menu) {
- var parent = Dropdown._getParentFromElement(this._element);
- if (parent) {
- this._menu = parent.querySelector(Selector$4.MENU);
- }
- }
- return this._menu;
- };
- _proto._getPlacement = function _getPlacement() {
- var $parentDropdown = $(this._element.parentNode);
- var placement = AttachmentMap.BOTTOM; // Handle dropup
- if ($parentDropdown.hasClass(ClassName$4.DROPUP)) {
- placement = AttachmentMap.TOP;
- if ($(this._menu).hasClass(ClassName$4.MENURIGHT)) {
- placement = AttachmentMap.TOPEND;
- }
- } else if ($parentDropdown.hasClass(ClassName$4.DROPRIGHT)) {
- placement = AttachmentMap.RIGHT;
- } else if ($parentDropdown.hasClass(ClassName$4.DROPLEFT)) {
- placement = AttachmentMap.LEFT;
- } else if ($(this._menu).hasClass(ClassName$4.MENURIGHT)) {
- placement = AttachmentMap.BOTTOMEND;
- }
- return placement;
- };
- _proto._detectNavbar = function _detectNavbar() {
- return $(this._element).closest('.navbar').length > 0;
- };
- _proto._getOffset = function _getOffset() {
- var _this2 = this;
- var offset = {};
- if (typeof this._config.offset === 'function') {
- offset.fn = function (data) {
- data.offsets = _objectSpread2({}, data.offsets, {}, _this2._config.offset(data.offsets, _this2._element) || {});
- return data;
- };
- } else {
- offset.offset = this._config.offset;
- }
- return offset;
- };
- _proto._getPopperConfig = function _getPopperConfig() {
- var popperConfig = {
- placement: this._getPlacement(),
- modifiers: {
- offset: this._getOffset(),
- flip: {
- enabled: this._config.flip
- },
- preventOverflow: {
- boundariesElement: this._config.boundary
- }
- }
- }; // Disable Popper.js if we have a static display
- if (this._config.display === 'static') {
- popperConfig.modifiers.applyStyle = {
- enabled: false
- };
- }
- return _objectSpread2({}, popperConfig, {}, this._config.popperConfig);
- } // Static
- ;
- Dropdown._jQueryInterface = function _jQueryInterface(config) {
- return this.each(function () {
- var data = $(this).data(DATA_KEY$4);
- var _config = typeof config === 'object' ? config : null;
- if (!data) {
- data = new Dropdown(this, _config);
- $(this).data(DATA_KEY$4, data);
- }
- if (typeof config === 'string') {
- if (typeof data[config] === 'undefined') {
- throw new TypeError("No method named \"" + config + "\"");
- }
- data[config]();
- }
- });
- };
- Dropdown._clearMenus = function _clearMenus(event) {
- if (event && (event.which === RIGHT_MOUSE_BUTTON_WHICH || event.type === 'keyup' && event.which !== TAB_KEYCODE)) {
- return;
- }
- var toggles = [].slice.call(document.querySelectorAll(Selector$4.DATA_TOGGLE));
- for (var i = 0, len = toggles.length; i < len; i++) {
- var parent = Dropdown._getParentFromElement(toggles[i]);
- var context = $(toggles[i]).data(DATA_KEY$4);
- var relatedTarget = {
- relatedTarget: toggles[i]
- };
- if (event && event.type === 'click') {
- relatedTarget.clickEvent = event;
- }
- if (!context) {
- continue;
- }
- var dropdownMenu = context._menu;
- if (!$(parent).hasClass(ClassName$4.SHOW)) {
- continue;
- }
- if (event && (event.type === 'click' && /input|textarea/i.test(event.target.tagName) || event.type === 'keyup' && event.which === TAB_KEYCODE) && $.contains(parent, event.target)) {
- continue;
- }
- var hideEvent = $.Event(Event$4.HIDE, relatedTarget);
- $(parent).trigger(hideEvent);
- if (hideEvent.isDefaultPrevented()) {
- continue;
- } // If this is a touch-enabled device we remove the extra
- // empty mouseover listeners we added for iOS support
- if ('ontouchstart' in document.documentElement) {
- $(document.body).children().off('mouseover', null, $.noop);
- }
- toggles[i].setAttribute('aria-expanded', 'false');
- if (context._popper) {
- context._popper.destroy();
- }
- $(dropdownMenu).removeClass(ClassName$4.SHOW);
- $(parent).removeClass(ClassName$4.SHOW).trigger($.Event(Event$4.HIDDEN, relatedTarget));
- }
- };
- Dropdown._getParentFromElement = function _getParentFromElement(element) {
- var parent;
- var selector = Util.getSelectorFromElement(element);
- if (selector) {
- parent = document.querySelector(selector);
- }
- return parent || element.parentNode;
- } // eslint-disable-next-line complexity
- ;
- Dropdown._dataApiKeydownHandler = function _dataApiKeydownHandler(event) {
- // If not input/textarea:
- // - And not a key in REGEXP_KEYDOWN => not a dropdown command
- // If input/textarea:
- // - If space key => not a dropdown command
- // - If key is other than escape
- // - If key is not up or down => not a dropdown command
- // - If trigger inside the menu => not a dropdown command
- if (/input|textarea/i.test(event.target.tagName) ? event.which === SPACE_KEYCODE || event.which !== ESCAPE_KEYCODE && (event.which !== ARROW_DOWN_KEYCODE && event.which !== ARROW_UP_KEYCODE || $(event.target).closest(Selector$4.MENU).length) : !REGEXP_KEYDOWN.test(event.which)) {
- return;
- }
- event.preventDefault();
- event.stopPropagation();
- if (this.disabled || $(this).hasClass(ClassName$4.DISABLED)) {
- return;
- }
- var parent = Dropdown._getParentFromElement(this);
- var isActive = $(parent).hasClass(ClassName$4.SHOW);
- if (!isActive && event.which === ESCAPE_KEYCODE) {
- return;
- }
- if (!isActive || isActive && (event.which === ESCAPE_KEYCODE || event.which === SPACE_KEYCODE)) {
- if (event.which === ESCAPE_KEYCODE) {
- var toggle = parent.querySelector(Selector$4.DATA_TOGGLE);
- $(toggle).trigger('focus');
- }
- $(this).trigger('click');
- return;
- }
- var items = [].slice.call(parent.querySelectorAll(Selector$4.VISIBLE_ITEMS)).filter(function (item) {
- return $(item).is(':visible');
- });
- if (items.length === 0) {
- return;
- }
- var index = items.indexOf(event.target);
- if (event.which === ARROW_UP_KEYCODE && index > 0) {
- // Up
- index--;
- }
- if (event.which === ARROW_DOWN_KEYCODE && index < items.length - 1) {
- // Down
- index++;
- }
- if (index < 0) {
- index = 0;
- }
- items[index].focus();
- };
- _createClass(Dropdown, null, [{
- key: "VERSION",
- get: function get() {
- return VERSION$4;
- }
- }, {
- key: "Default",
- get: function get() {
- return Default$2;
- }
- }, {
- key: "DefaultType",
- get: function get() {
- return DefaultType$2;
- }
- }]);
- return Dropdown;
- }();
- /**
- * ------------------------------------------------------------------------
- * Data Api implementation
- * ------------------------------------------------------------------------
- */
- $(document).on(Event$4.KEYDOWN_DATA_API, Selector$4.DATA_TOGGLE, Dropdown._dataApiKeydownHandler).on(Event$4.KEYDOWN_DATA_API, Selector$4.MENU, Dropdown._dataApiKeydownHandler).on(Event$4.CLICK_DATA_API + " " + Event$4.KEYUP_DATA_API, Dropdown._clearMenus).on(Event$4.CLICK_DATA_API, Selector$4.DATA_TOGGLE, function (event) {
- event.preventDefault();
- event.stopPropagation();
- Dropdown._jQueryInterface.call($(this), 'toggle');
- }).on(Event$4.CLICK_DATA_API, Selector$4.FORM_CHILD, function (e) {
- e.stopPropagation();
- });
- /**
- * ------------------------------------------------------------------------
- * jQuery
- * ------------------------------------------------------------------------
- */
- $.fn[NAME$4] = Dropdown._jQueryInterface;
- $.fn[NAME$4].Constructor = Dropdown;
- $.fn[NAME$4].noConflict = function () {
- $.fn[NAME$4] = JQUERY_NO_CONFLICT$4;
- return Dropdown._jQueryInterface;
- };
- /**
- * ------------------------------------------------------------------------
- * Constants
- * ------------------------------------------------------------------------
- */
- var NAME$5 = 'modal';
- var VERSION$5 = '4.4.1';
- var DATA_KEY$5 = 'bs.modal';
- var EVENT_KEY$5 = "." + DATA_KEY$5;
- var DATA_API_KEY$5 = '.data-api';
- var JQUERY_NO_CONFLICT$5 = $.fn[NAME$5];
- var ESCAPE_KEYCODE$1 = 27; // KeyboardEvent.which value for Escape (Esc) key
- var Default$3 = {
- backdrop: true,
- keyboard: true,
- focus: true,
- show: true
- };
- var DefaultType$3 = {
- backdrop: '(boolean|string)',
- keyboard: 'boolean',
- focus: 'boolean',
- show: 'boolean'
- };
- var Event$5 = {
- HIDE: "hide" + EVENT_KEY$5,
- HIDE_PREVENTED: "hidePrevented" + EVENT_KEY$5,
- HIDDEN: "hidden" + EVENT_KEY$5,
- SHOW: "show" + EVENT_KEY$5,
- SHOWN: "shown" + EVENT_KEY$5,
- FOCUSIN: "focusin" + EVENT_KEY$5,
- RESIZE: "resize" + EVENT_KEY$5,
- CLICK_DISMISS: "click.dismiss" + EVENT_KEY$5,
- KEYDOWN_DISMISS: "keydown.dismiss" + EVENT_KEY$5,
- MOUSEUP_DISMISS: "mouseup.dismiss" + EVENT_KEY$5,
- MOUSEDOWN_DISMISS: "mousedown.dismiss" + EVENT_KEY$5,
- CLICK_DATA_API: "click" + EVENT_KEY$5 + DATA_API_KEY$5
- };
- var ClassName$5 = {
- SCROLLABLE: 'modal-dialog-scrollable',
- SCROLLBAR_MEASURER: 'modal-scrollbar-measure',
- BACKDROP: 'modal-backdrop',
- OPEN: 'modal-open',
- FADE: 'fade',
- SHOW: 'show',
- STATIC: 'modal-static'
- };
- var Selector$5 = {
- DIALOG: '.modal-dialog',
- MODAL_BODY: '.modal-body',
- DATA_TOGGLE: '[data-toggle="modal"]',
- DATA_DISMISS: '[data-dismiss="modal"]',
- FIXED_CONTENT: '.fixed-top, .fixed-bottom, .is-fixed, .sticky-top',
- STICKY_CONTENT: '.sticky-top'
- };
- /**
- * ------------------------------------------------------------------------
- * Class Definition
- * ------------------------------------------------------------------------
- */
- var Modal =
- /*#__PURE__*/
- function () {
- function Modal(element, config) {
- this._config = this._getConfig(config);
- this._element = element;
- this._dialog = element.querySelector(Selector$5.DIALOG);
- this._backdrop = null;
- this._isShown = false;
- this._isBodyOverflowing = false;
- this._ignoreBackdropClick = false;
- this._isTransitioning = false;
- this._scrollbarWidth = 0;
- } // Getters
- var _proto = Modal.prototype;
- // Public
- _proto.toggle = function toggle(relatedTarget) {
- return this._isShown ? this.hide() : this.show(relatedTarget);
- };
- _proto.show = function show(relatedTarget) {
- var _this = this;
- if (this._isShown || this._isTransitioning) {
- return;
- }
- if ($(this._element).hasClass(ClassName$5.FADE)) {
- this._isTransitioning = true;
- }
- var showEvent = $.Event(Event$5.SHOW, {
- relatedTarget: relatedTarget
- });
- $(this._element).trigger(showEvent);
- if (this._isShown || showEvent.isDefaultPrevented()) {
- return;
- }
- this._isShown = true;
- this._checkScrollbar();
- this._setScrollbar();
- this._adjustDialog();
- this._setEscapeEvent();
- this._setResizeEvent();
- $(this._element).on(Event$5.CLICK_DISMISS, Selector$5.DATA_DISMISS, function (event) {
- return _this.hide(event);
- });
- $(this._dialog).on(Event$5.MOUSEDOWN_DISMISS, function () {
- $(_this._element).one(Event$5.MOUSEUP_DISMISS, function (event) {
- if ($(event.target).is(_this._element)) {
- _this._ignoreBackdropClick = true;
- }
- });
- });
- this._showBackdrop(function () {
- return _this._showElement(relatedTarget);
- });
- };
- _proto.hide = function hide(event) {
- var _this2 = this;
- if (event) {
- event.preventDefault();
- }
- if (!this._isShown || this._isTransitioning) {
- return;
- }
- var hideEvent = $.Event(Event$5.HIDE);
- $(this._element).trigger(hideEvent);
- if (!this._isShown || hideEvent.isDefaultPrevented()) {
- return;
- }
- this._isShown = false;
- var transition = $(this._element).hasClass(ClassName$5.FADE);
- if (transition) {
- this._isTransitioning = true;
- }
- this._setEscapeEvent();
- this._setResizeEvent();
- $(document).off(Event$5.FOCUSIN);
- $(this._element).removeClass(ClassName$5.SHOW);
- $(this._element).off(Event$5.CLICK_DISMISS);
- $(this._dialog).off(Event$5.MOUSEDOWN_DISMISS);
- if (transition) {
- var transitionDuration = Util.getTransitionDurationFromElement(this._element);
- $(this._element).one(Util.TRANSITION_END, function (event) {
- return _this2._hideModal(event);
- }).emulateTransitionEnd(transitionDuration);
- } else {
- this._hideModal();
- }
- };
- _proto.dispose = function dispose() {
- [window, this._element, this._dialog].forEach(function (htmlElement) {
- return $(htmlElement).off(EVENT_KEY$5);
- });
- /**
- * `document` has 2 events `Event.FOCUSIN` and `Event.CLICK_DATA_API`
- * Do not move `document` in `htmlElements` array
- * It will remove `Event.CLICK_DATA_API` event that should remain
- */
- $(document).off(Event$5.FOCUSIN);
- $.removeData(this._element, DATA_KEY$5);
- this._config = null;
- this._element = null;
- this._dialog = null;
- this._backdrop = null;
- this._isShown = null;
- this._isBodyOverflowing = null;
- this._ignoreBackdropClick = null;
- this._isTransitioning = null;
- this._scrollbarWidth = null;
- };
- _proto.handleUpdate = function handleUpdate() {
- this._adjustDialog();
- } // Private
- ;
- _proto._getConfig = function _getConfig(config) {
- config = _objectSpread2({}, Default$3, {}, config);
- Util.typeCheckConfig(NAME$5, config, DefaultType$3);
- return config;
- };
- _proto._triggerBackdropTransition = function _triggerBackdropTransition() {
- var _this3 = this;
- if (this._config.backdrop === 'static') {
- var hideEventPrevented = $.Event(Event$5.HIDE_PREVENTED);
- $(this._element).trigger(hideEventPrevented);
- if (hideEventPrevented.defaultPrevented) {
- return;
- }
- this._element.classList.add(ClassName$5.STATIC);
- var modalTransitionDuration = Util.getTransitionDurationFromElement(this._element);
- $(this._element).one(Util.TRANSITION_END, function () {
- _this3._element.classList.remove(ClassName$5.STATIC);
- }).emulateTransitionEnd(modalTransitionDuration);
- this._element.focus();
- } else {
- this.hide();
- }
- };
- _proto._showElement = function _showElement(relatedTarget) {
- var _this4 = this;
- var transition = $(this._element).hasClass(ClassName$5.FADE);
- var modalBody = this._dialog ? this._dialog.querySelector(Selector$5.MODAL_BODY) : null;
- if (!this._element.parentNode || this._element.parentNode.nodeType !== Node.ELEMENT_NODE) {
- // Don't move modal's DOM position
- document.body.appendChild(this._element);
- }
- this._element.style.display = 'block';
- this._element.removeAttribute('aria-hidden');
- this._element.setAttribute('aria-modal', true);
- if ($(this._dialog).hasClass(ClassName$5.SCROLLABLE) && modalBody) {
- modalBody.scrollTop = 0;
- } else {
- this._element.scrollTop = 0;
- }
- if (transition) {
- Util.reflow(this._element);
- }
- $(this._element).addClass(ClassName$5.SHOW);
- if (this._config.focus) {
- this._enforceFocus();
- }
- var shownEvent = $.Event(Event$5.SHOWN, {
- relatedTarget: relatedTarget
- });
- var transitionComplete = function transitionComplete() {
- if (_this4._config.focus) {
- _this4._element.focus();
- }
- _this4._isTransitioning = false;
- $(_this4._element).trigger(shownEvent);
- };
- if (transition) {
- var transitionDuration = Util.getTransitionDurationFromElement(this._dialog);
- $(this._dialog).one(Util.TRANSITION_END, transitionComplete).emulateTransitionEnd(transitionDuration);
- } else {
- transitionComplete();
- }
- };
- _proto._enforceFocus = function _enforceFocus() {
- var _this5 = this;
- $(document).off(Event$5.FOCUSIN) // Guard against infinite focus loop
- .on(Event$5.FOCUSIN, function (event) {
- if (document !== event.target && _this5._element !== event.target && $(_this5._element).has(event.target).length === 0) {
- _this5._element.focus();
- }
- });
- };
- _proto._setEscapeEvent = function _setEscapeEvent() {
- var _this6 = this;
- if (this._isShown && this._config.keyboard) {
- $(this._element).on(Event$5.KEYDOWN_DISMISS, function (event) {
- if (event.which === ESCAPE_KEYCODE$1) {
- _this6._triggerBackdropTransition();
- }
- });
- } else if (!this._isShown) {
- $(this._element).off(Event$5.KEYDOWN_DISMISS);
- }
- };
- _proto._setResizeEvent = function _setResizeEvent() {
- var _this7 = this;
- if (this._isShown) {
- $(window).on(Event$5.RESIZE, function (event) {
- return _this7.handleUpdate(event);
- });
- } else {
- $(window).off(Event$5.RESIZE);
- }
- };
- _proto._hideModal = function _hideModal() {
- var _this8 = this;
- this._element.style.display = 'none';
- this._element.setAttribute('aria-hidden', true);
- this._element.removeAttribute('aria-modal');
- this._isTransitioning = false;
- this._showBackdrop(function () {
- $(document.body).removeClass(ClassName$5.OPEN);
- _this8._resetAdjustments();
- _this8._resetScrollbar();
- $(_this8._element).trigger(Event$5.HIDDEN);
- });
- };
- _proto._removeBackdrop = function _removeBackdrop() {
- if (this._backdrop) {
- $(this._backdrop).remove();
- this._backdrop = null;
- }
- };
- _proto._showBackdrop = function _showBackdrop(callback) {
- var _this9 = this;
- var animate = $(this._element).hasClass(ClassName$5.FADE) ? ClassName$5.FADE : '';
- if (this._isShown && this._config.backdrop) {
- this._backdrop = document.createElement('div');
- this._backdrop.className = ClassName$5.BACKDROP;
- if (animate) {
- this._backdrop.classList.add(animate);
- }
- $(this._backdrop).appendTo(document.body);
- $(this._element).on(Event$5.CLICK_DISMISS, function (event) {
- if (_this9._ignoreBackdropClick) {
- _this9._ignoreBackdropClick = false;
- return;
- }
- if (event.target !== event.currentTarget) {
- return;
- }
- _this9._triggerBackdropTransition();
- });
- if (animate) {
- Util.reflow(this._backdrop);
- }
- $(this._backdrop).addClass(ClassName$5.SHOW);
- if (!callback) {
- return;
- }
- if (!animate) {
- callback();
- return;
- }
- var backdropTransitionDuration = Util.getTransitionDurationFromElement(this._backdrop);
- $(this._backdrop).one(Util.TRANSITION_END, callback).emulateTransitionEnd(backdropTransitionDuration);
- } else if (!this._isShown && this._backdrop) {
- $(this._backdrop).removeClass(ClassName$5.SHOW);
- var callbackRemove = function callbackRemove() {
- _this9._removeBackdrop();
- if (callback) {
- callback();
- }
- };
- if ($(this._element).hasClass(ClassName$5.FADE)) {
- var _backdropTransitionDuration = Util.getTransitionDurationFromElement(this._backdrop);
- $(this._backdrop).one(Util.TRANSITION_END, callbackRemove).emulateTransitionEnd(_backdropTransitionDuration);
- } else {
- callbackRemove();
- }
- } else if (callback) {
- callback();
- }
- } // ----------------------------------------------------------------------
- // the following methods are used to handle overflowing modals
- // todo (fat): these should probably be refactored out of modal.js
- // ----------------------------------------------------------------------
- ;
- _proto._adjustDialog = function _adjustDialog() {
- var isModalOverflowing = this._element.scrollHeight > document.documentElement.clientHeight;
- if (!this._isBodyOverflowing && isModalOverflowing) {
- this._element.style.paddingLeft = this._scrollbarWidth + "px";
- }
- if (this._isBodyOverflowing && !isModalOverflowing) {
- this._element.style.paddingRight = this._scrollbarWidth + "px";
- }
- };
- _proto._resetAdjustments = function _resetAdjustments() {
- this._element.style.paddingLeft = '';
- this._element.style.paddingRight = '';
- };
- _proto._checkScrollbar = function _checkScrollbar() {
- var rect = document.body.getBoundingClientRect();
- this._isBodyOverflowing = rect.left + rect.right < window.innerWidth;
- this._scrollbarWidth = this._getScrollbarWidth();
- };
- _proto._setScrollbar = function _setScrollbar() {
- var _this10 = this;
- if (this._isBodyOverflowing) {
- // Note: DOMNode.style.paddingRight returns the actual value or '' if not set
- // while $(DOMNode).css('padding-right') returns the calculated value or 0 if not set
- var fixedContent = [].slice.call(document.querySelectorAll(Selector$5.FIXED_CONTENT));
- var stickyContent = [].slice.call(document.querySelectorAll(Selector$5.STICKY_CONTENT)); // Adjust fixed content padding
- $(fixedContent).each(function (index, element) {
- var actualPadding = element.style.paddingRight;
- var calculatedPadding = $(element).css('padding-right');
- $(element).data('padding-right', actualPadding).css('padding-right', parseFloat(calculatedPadding) + _this10._scrollbarWidth + "px");
- }); // Adjust sticky content margin
- $(stickyContent).each(function (index, element) {
- var actualMargin = element.style.marginRight;
- var calculatedMargin = $(element).css('margin-right');
- $(element).data('margin-right', actualMargin).css('margin-right', parseFloat(calculatedMargin) - _this10._scrollbarWidth + "px");
- }); // Adjust body padding
- var actualPadding = document.body.style.paddingRight;
- var calculatedPadding = $(document.body).css('padding-right');
- $(document.body).data('padding-right', actualPadding).css('padding-right', parseFloat(calculatedPadding) + this._scrollbarWidth + "px");
- }
- $(document.body).addClass(ClassName$5.OPEN);
- };
- _proto._resetScrollbar = function _resetScrollbar() {
- // Restore fixed content padding
- var fixedContent = [].slice.call(document.querySelectorAll(Selector$5.FIXED_CONTENT));
- $(fixedContent).each(function (index, element) {
- var padding = $(element).data('padding-right');
- $(element).removeData('padding-right');
- element.style.paddingRight = padding ? padding : '';
- }); // Restore sticky content
- var elements = [].slice.call(document.querySelectorAll("" + Selector$5.STICKY_CONTENT));
- $(elements).each(function (index, element) {
- var margin = $(element).data('margin-right');
- if (typeof margin !== 'undefined') {
- $(element).css('margin-right', margin).removeData('margin-right');
- }
- }); // Restore body padding
- var padding = $(document.body).data('padding-right');
- $(document.body).removeData('padding-right');
- document.body.style.paddingRight = padding ? padding : '';
- };
- _proto._getScrollbarWidth = function _getScrollbarWidth() {
- // thx d.walsh
- var scrollDiv = document.createElement('div');
- scrollDiv.className = ClassName$5.SCROLLBAR_MEASURER;
- document.body.appendChild(scrollDiv);
- var scrollbarWidth = scrollDiv.getBoundingClientRect().width - scrollDiv.clientWidth;
- document.body.removeChild(scrollDiv);
- return scrollbarWidth;
- } // Static
- ;
- Modal._jQueryInterface = function _jQueryInterface(config, relatedTarget) {
- return this.each(function () {
- var data = $(this).data(DATA_KEY$5);
- var _config = _objectSpread2({}, Default$3, {}, $(this).data(), {}, typeof config === 'object' && config ? config : {});
- if (!data) {
- data = new Modal(this, _config);
- $(this).data(DATA_KEY$5, data);
- }
- if (typeof config === 'string') {
- if (typeof data[config] === 'undefined') {
- throw new TypeError("No method named \"" + config + "\"");
- }
- data[config](relatedTarget);
- } else if (_config.show) {
- data.show(relatedTarget);
- }
- });
- };
- _createClass(Modal, null, [{
- key: "VERSION",
- get: function get() {
- return VERSION$5;
- }
- }, {
- key: "Default",
- get: function get() {
- return Default$3;
- }
- }]);
- return Modal;
- }();
- /**
- * ------------------------------------------------------------------------
- * Data Api implementation
- * ------------------------------------------------------------------------
- */
- $(document).on(Event$5.CLICK_DATA_API, Selector$5.DATA_TOGGLE, function (event) {
- var _this11 = this;
- var target;
- var selector = Util.getSelectorFromElement(this);
- if (selector) {
- target = document.querySelector(selector);
- }
- var config = $(target).data(DATA_KEY$5) ? 'toggle' : _objectSpread2({}, $(target).data(), {}, $(this).data());
- if (this.tagName === 'A' || this.tagName === 'AREA') {
- event.preventDefault();
- }
- var $target = $(target).one(Event$5.SHOW, function (showEvent) {
- if (showEvent.isDefaultPrevented()) {
- // Only register focus restorer if modal will actually get shown
- return;
- }
- $target.one(Event$5.HIDDEN, function () {
- if ($(_this11).is(':visible')) {
- _this11.focus();
- }
- });
- });
- Modal._jQueryInterface.call($(target), config, this);
- });
- /**
- * ------------------------------------------------------------------------
- * jQuery
- * ------------------------------------------------------------------------
- */
- $.fn[NAME$5] = Modal._jQueryInterface;
- $.fn[NAME$5].Constructor = Modal;
- $.fn[NAME$5].noConflict = function () {
- $.fn[NAME$5] = JQUERY_NO_CONFLICT$5;
- return Modal._jQueryInterface;
- };
- /**
- * --------------------------------------------------------------------------
- * Bootstrap (v4.4.1): tools/sanitizer.js
- * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
- * --------------------------------------------------------------------------
- */
- var uriAttrs = ['background', 'cite', 'href', 'itemtype', 'longdesc', 'poster', 'src', 'xlink:href'];
- var ARIA_ATTRIBUTE_PATTERN = /^aria-[\w-]*$/i;
- var DefaultWhitelist = {
- // Global attributes allowed on any supplied element below.
- '*': ['class', 'dir', 'id', 'lang', 'role', ARIA_ATTRIBUTE_PATTERN],
- a: ['target', 'href', 'title', 'rel'],
- area: [],
- b: [],
- br: [],
- col: [],
- code: [],
- div: [],
- em: [],
- hr: [],
- h1: [],
- h2: [],
- h3: [],
- h4: [],
- h5: [],
- h6: [],
- i: [],
- img: ['src', 'alt', 'title', 'width', 'height'],
- li: [],
- ol: [],
- p: [],
- pre: [],
- s: [],
- small: [],
- span: [],
- sub: [],
- sup: [],
- strong: [],
- u: [],
- ul: []
- };
- /**
- * A pattern that recognizes a commonly useful subset of URLs that are safe.
- *
- * Shoutout to Angular 7 https://github.com/angular/angular/blob/7.2.4/packages/core/src/sanitization/url_sanitizer.ts
- */
- var SAFE_URL_PATTERN = /^(?:(?:https?|mailto|ftp|tel|file):|[^&:/?#]*(?:[/?#]|$))/gi;
- /**
- * A pattern that matches safe data URLs. Only matches image, video and audio types.
- *
- * Shoutout to Angular 7 https://github.com/angular/angular/blob/7.2.4/packages/core/src/sanitization/url_sanitizer.ts
- */
- var DATA_URL_PATTERN = /^data:(?:image\/(?:bmp|gif|jpeg|jpg|png|tiff|webp)|video\/(?:mpeg|mp4|ogg|webm)|audio\/(?:mp3|oga|ogg|opus));base64,[a-z0-9+/]+=*$/i;
- function allowedAttribute(attr, allowedAttributeList) {
- var attrName = attr.nodeName.toLowerCase();
- if (allowedAttributeList.indexOf(attrName) !== -1) {
- if (uriAttrs.indexOf(attrName) !== -1) {
- return Boolean(attr.nodeValue.match(SAFE_URL_PATTERN) || attr.nodeValue.match(DATA_URL_PATTERN));
- }
- return true;
- }
- var regExp = allowedAttributeList.filter(function (attrRegex) {
- return attrRegex instanceof RegExp;
- }); // Check if a regular expression validates the attribute.
- for (var i = 0, l = regExp.length; i < l; i++) {
- if (attrName.match(regExp[i])) {
- return true;
- }
- }
- return false;
- }
- function sanitizeHtml(unsafeHtml, whiteList, sanitizeFn) {
- if (unsafeHtml.length === 0) {
- return unsafeHtml;
- }
- if (sanitizeFn && typeof sanitizeFn === 'function') {
- return sanitizeFn(unsafeHtml);
- }
- var domParser = new window.DOMParser();
- var createdDocument = domParser.parseFromString(unsafeHtml, 'text/html');
- var whitelistKeys = Object.keys(whiteList);
- var elements = [].slice.call(createdDocument.body.querySelectorAll('*'));
- var _loop = function _loop(i, len) {
- var el = elements[i];
- var elName = el.nodeName.toLowerCase();
- if (whitelistKeys.indexOf(el.nodeName.toLowerCase()) === -1) {
- el.parentNode.removeChild(el);
- return "continue";
- }
- var attributeList = [].slice.call(el.attributes);
- var whitelistedAttributes = [].concat(whiteList['*'] || [], whiteList[elName] || []);
- attributeList.forEach(function (attr) {
- if (!allowedAttribute(attr, whitelistedAttributes)) {
- el.removeAttribute(attr.nodeName);
- }
- });
- };
- for (var i = 0, len = elements.length; i < len; i++) {
- var _ret = _loop(i);
- if (_ret === "continue") continue;
- }
- return createdDocument.body.innerHTML;
- }
- /**
- * ------------------------------------------------------------------------
- * Constants
- * ------------------------------------------------------------------------
- */
- var NAME$6 = 'tooltip';
- var VERSION$6 = '4.4.1';
- var DATA_KEY$6 = 'bs.tooltip';
- var EVENT_KEY$6 = "." + DATA_KEY$6;
- var JQUERY_NO_CONFLICT$6 = $.fn[NAME$6];
- var CLASS_PREFIX = 'bs-tooltip';
- var BSCLS_PREFIX_REGEX = new RegExp("(^|\\s)" + CLASS_PREFIX + "\\S+", 'g');
- var DISALLOWED_ATTRIBUTES = ['sanitize', 'whiteList', 'sanitizeFn'];
- var DefaultType$4 = {
- animation: 'boolean',
- template: 'string',
- title: '(string|element|function)',
- trigger: 'string',
- delay: '(number|object)',
- html: 'boolean',
- selector: '(string|boolean)',
- placement: '(string|function)',
- offset: '(number|string|function)',
- container: '(string|element|boolean)',
- fallbackPlacement: '(string|array)',
- boundary: '(string|element)',
- sanitize: 'boolean',
- sanitizeFn: '(null|function)',
- whiteList: 'object',
- popperConfig: '(null|object)'
- };
- var AttachmentMap$1 = {
- AUTO: 'auto',
- TOP: 'top',
- RIGHT: 'right',
- BOTTOM: 'bottom',
- LEFT: 'left'
- };
- var Default$4 = {
- animation: true,
- template: '<div class="tooltip" role="tooltip">' + '<div class="arrow"></div>' + '<div class="tooltip-inner"></div></div>',
- trigger: 'hover focus',
- title: '',
- delay: 0,
- html: false,
- selector: false,
- placement: 'top',
- offset: 0,
- container: false,
- fallbackPlacement: 'flip',
- boundary: 'scrollParent',
- sanitize: true,
- sanitizeFn: null,
- whiteList: DefaultWhitelist,
- popperConfig: null
- };
- var HoverState = {
- SHOW: 'show',
- OUT: 'out'
- };
- var Event$6 = {
- HIDE: "hide" + EVENT_KEY$6,
- HIDDEN: "hidden" + EVENT_KEY$6,
- SHOW: "show" + EVENT_KEY$6,
- SHOWN: "shown" + EVENT_KEY$6,
- INSERTED: "inserted" + EVENT_KEY$6,
- CLICK: "click" + EVENT_KEY$6,
- FOCUSIN: "focusin" + EVENT_KEY$6,
- FOCUSOUT: "focusout" + EVENT_KEY$6,
- MOUSEENTER: "mouseenter" + EVENT_KEY$6,
- MOUSELEAVE: "mouseleave" + EVENT_KEY$6
- };
- var ClassName$6 = {
- FADE: 'fade',
- SHOW: 'show'
- };
- var Selector$6 = {
- TOOLTIP: '.tooltip',
- TOOLTIP_INNER: '.tooltip-inner',
- ARROW: '.arrow'
- };
- var Trigger = {
- HOVER: 'hover',
- FOCUS: 'focus',
- CLICK: 'click',
- MANUAL: 'manual'
- };
- /**
- * ------------------------------------------------------------------------
- * Class Definition
- * ------------------------------------------------------------------------
- */
- var Tooltip =
- /*#__PURE__*/
- function () {
- function Tooltip(element, config) {
- if (typeof Popper === 'undefined') {
- throw new TypeError('Bootstrap\'s tooltips require Popper.js (https://popper.js.org/)');
- } // private
- this._isEnabled = true;
- this._timeout = 0;
- this._hoverState = '';
- this._activeTrigger = {};
- this._popper = null; // Protected
- this.element = element;
- this.config = this._getConfig(config);
- this.tip = null;
- this._setListeners();
- } // Getters
- var _proto = Tooltip.prototype;
- // Public
- _proto.enable = function enable() {
- this._isEnabled = true;
- };
- _proto.disable = function disable() {
- this._isEnabled = false;
- };
- _proto.toggleEnabled = function toggleEnabled() {
- this._isEnabled = !this._isEnabled;
- };
- _proto.toggle = function toggle(event) {
- if (!this._isEnabled) {
- return;
- }
- if (event) {
- var dataKey = this.constructor.DATA_KEY;
- var context = $(event.currentTarget).data(dataKey);
- if (!context) {
- context = new this.constructor(event.currentTarget, this._getDelegateConfig());
- $(event.currentTarget).data(dataKey, context);
- }
- context._activeTrigger.click = !context._activeTrigger.click;
- if (context._isWithActiveTrigger()) {
- context._enter(null, context);
- } else {
- context._leave(null, context);
- }
- } else {
- if ($(this.getTipElement()).hasClass(ClassName$6.SHOW)) {
- this._leave(null, this);
- return;
- }
- this._enter(null, this);
- }
- };
- _proto.dispose = function dispose() {
- clearTimeout(this._timeout);
- $.removeData(this.element, this.constructor.DATA_KEY);
- $(this.element).off(this.constructor.EVENT_KEY);
- $(this.element).closest('.modal').off('hide.bs.modal', this._hideModalHandler);
- if (this.tip) {
- $(this.tip).remove();
- }
- this._isEnabled = null;
- this._timeout = null;
- this._hoverState = null;
- this._activeTrigger = null;
- if (this._popper) {
- this._popper.destroy();
- }
- this._popper = null;
- this.element = null;
- this.config = null;
- this.tip = null;
- };
- _proto.show = function show() {
- var _this = this;
- if ($(this.element).css('display') === 'none') {
- throw new Error('Please use show on visible elements');
- }
- var showEvent = $.Event(this.constructor.Event.SHOW);
- if (this.isWithContent() && this._isEnabled) {
- $(this.element).trigger(showEvent);
- var shadowRoot = Util.findShadowRoot(this.element);
- var isInTheDom = $.contains(shadowRoot !== null ? shadowRoot : this.element.ownerDocument.documentElement, this.element);
- if (showEvent.isDefaultPrevented() || !isInTheDom) {
- return;
- }
- var tip = this.getTipElement();
- var tipId = Util.getUID(this.constructor.NAME);
- tip.setAttribute('id', tipId);
- this.element.setAttribute('aria-describedby', tipId);
- this.setContent();
- if (this.config.animation) {
- $(tip).addClass(ClassName$6.FADE);
- }
- var placement = typeof this.config.placement === 'function' ? this.config.placement.call(this, tip, this.element) : this.config.placement;
- var attachment = this._getAttachment(placement);
- this.addAttachmentClass(attachment);
- var container = this._getContainer();
- $(tip).data(this.constructor.DATA_KEY, this);
- if (!$.contains(this.element.ownerDocument.documentElement, this.tip)) {
- $(tip).appendTo(container);
- }
- $(this.element).trigger(this.constructor.Event.INSERTED);
- this._popper = new Popper(this.element, tip, this._getPopperConfig(attachment));
- $(tip).addClass(ClassName$6.SHOW); // If this is a touch-enabled device we add extra
- // empty mouseover listeners to the body's immediate children;
- // only needed because of broken event delegation on iOS
- // https://www.quirksmode.org/blog/archives/2014/02/mouse_event_bub.html
- if ('ontouchstart' in document.documentElement) {
- $(document.body).children().on('mouseover', null, $.noop);
- }
- var complete = function complete() {
- if (_this.config.animation) {
- _this._fixTransition();
- }
- var prevHoverState = _this._hoverState;
- _this._hoverState = null;
- $(_this.element).trigger(_this.constructor.Event.SHOWN);
- if (prevHoverState === HoverState.OUT) {
- _this._leave(null, _this);
- }
- };
- if ($(this.tip).hasClass(ClassName$6.FADE)) {
- var transitionDuration = Util.getTransitionDurationFromElement(this.tip);
- $(this.tip).one(Util.TRANSITION_END, complete).emulateTransitionEnd(transitionDuration);
- } else {
- complete();
- }
- }
- };
- _proto.hide = function hide(callback) {
- var _this2 = this;
- var tip = this.getTipElement();
- var hideEvent = $.Event(this.constructor.Event.HIDE);
- var complete = function complete() {
- if (_this2._hoverState !== HoverState.SHOW && tip.parentNode) {
- tip.parentNode.removeChild(tip);
- }
- _this2._cleanTipClass();
- _this2.element.removeAttribute('aria-describedby');
- $(_this2.element).trigger(_this2.constructor.Event.HIDDEN);
- if (_this2._popper !== null) {
- _this2._popper.destroy();
- }
- if (callback) {
- callback();
- }
- };
- $(this.element).trigger(hideEvent);
- if (hideEvent.isDefaultPrevented()) {
- return;
- }
- $(tip).removeClass(ClassName$6.SHOW); // If this is a touch-enabled device we remove the extra
- // empty mouseover listeners we added for iOS support
- if ('ontouchstart' in document.documentElement) {
- $(document.body).children().off('mouseover', null, $.noop);
- }
- this._activeTrigger[Trigger.CLICK] = false;
- this._activeTrigger[Trigger.FOCUS] = false;
- this._activeTrigger[Trigger.HOVER] = false;
- if ($(this.tip).hasClass(ClassName$6.FADE)) {
- var transitionDuration = Util.getTransitionDurationFromElement(tip);
- $(tip).one(Util.TRANSITION_END, complete).emulateTransitionEnd(transitionDuration);
- } else {
- complete();
- }
- this._hoverState = '';
- };
- _proto.update = function update() {
- if (this._popper !== null) {
- this._popper.scheduleUpdate();
- }
- } // Protected
- ;
- _proto.isWithContent = function isWithContent() {
- return Boolean(this.getTitle());
- };
- _proto.addAttachmentClass = function addAttachmentClass(attachment) {
- $(this.getTipElement()).addClass(CLASS_PREFIX + "-" + attachment);
- };
- _proto.getTipElement = function getTipElement() {
- this.tip = this.tip || $(this.config.template)[0];
- return this.tip;
- };
- _proto.setContent = function setContent() {
- var tip = this.getTipElement();
- this.setElementContent($(tip.querySelectorAll(Selector$6.TOOLTIP_INNER)), this.getTitle());
- $(tip).removeClass(ClassName$6.FADE + " " + ClassName$6.SHOW);
- };
- _proto.setElementContent = function setElementContent($element, content) {
- if (typeof content === 'object' && (content.nodeType || content.jquery)) {
- // Content is a DOM node or a jQuery
- if (this.config.html) {
- if (!$(content).parent().is($element)) {
- $element.empty().append(content);
- }
- } else {
- $element.text($(content).text());
- }
- return;
- }
- if (this.config.html) {
- if (this.config.sanitize) {
- content = sanitizeHtml(content, this.config.whiteList, this.config.sanitizeFn);
- }
- $element.html(content);
- } else {
- $element.text(content);
- }
- };
- _proto.getTitle = function getTitle() {
- var title = this.element.getAttribute('data-original-title');
- if (!title) {
- title = typeof this.config.title === 'function' ? this.config.title.call(this.element) : this.config.title;
- }
- return title;
- } // Private
- ;
- _proto._getPopperConfig = function _getPopperConfig(attachment) {
- var _this3 = this;
- var defaultBsConfig = {
- placement: attachment,
- modifiers: {
- offset: this._getOffset(),
- flip: {
- behavior: this.config.fallbackPlacement
- },
- arrow: {
- element: Selector$6.ARROW
- },
- preventOverflow: {
- boundariesElement: this.config.boundary
- }
- },
- onCreate: function onCreate(data) {
- if (data.originalPlacement !== data.placement) {
- _this3._handlePopperPlacementChange(data);
- }
- },
- onUpdate: function onUpdate(data) {
- return _this3._handlePopperPlacementChange(data);
- }
- };
- return _objectSpread2({}, defaultBsConfig, {}, this.config.popperConfig);
- };
- _proto._getOffset = function _getOffset() {
- var _this4 = this;
- var offset = {};
- if (typeof this.config.offset === 'function') {
- offset.fn = function (data) {
- data.offsets = _objectSpread2({}, data.offsets, {}, _this4.config.offset(data.offsets, _this4.element) || {});
- return data;
- };
- } else {
- offset.offset = this.config.offset;
- }
- return offset;
- };
- _proto._getContainer = function _getContainer() {
- if (this.config.container === false) {
- return document.body;
- }
- if (Util.isElement(this.config.container)) {
- return $(this.config.container);
- }
- return $(document).find(this.config.container);
- };
- _proto._getAttachment = function _getAttachment(placement) {
- return AttachmentMap$1[placement.toUpperCase()];
- };
- _proto._setListeners = function _setListeners() {
- var _this5 = this;
- var triggers = this.config.trigger.split(' ');
- triggers.forEach(function (trigger) {
- if (trigger === 'click') {
- $(_this5.element).on(_this5.constructor.Event.CLICK, _this5.config.selector, function (event) {
- return _this5.toggle(event);
- });
- } else if (trigger !== Trigger.MANUAL) {
- var eventIn = trigger === Trigger.HOVER ? _this5.constructor.Event.MOUSEENTER : _this5.constructor.Event.FOCUSIN;
- var eventOut = trigger === Trigger.HOVER ? _this5.constructor.Event.MOUSELEAVE : _this5.constructor.Event.FOCUSOUT;
- $(_this5.element).on(eventIn, _this5.config.selector, function (event) {
- return _this5._enter(event);
- }).on(eventOut, _this5.config.selector, function (event) {
- return _this5._leave(event);
- });
- }
- });
- this._hideModalHandler = function () {
- if (_this5.element) {
- _this5.hide();
- }
- };
- $(this.element).closest('.modal').on('hide.bs.modal', this._hideModalHandler);
- if (this.config.selector) {
- this.config = _objectSpread2({}, this.config, {
- trigger: 'manual',
- selector: ''
- });
- } else {
- this._fixTitle();
- }
- };
- _proto._fixTitle = function _fixTitle() {
- var titleType = typeof this.element.getAttribute('data-original-title');
- if (this.element.getAttribute('title') || titleType !== 'string') {
- this.element.setAttribute('data-original-title', this.element.getAttribute('title') || '');
- this.element.setAttribute('title', '');
- }
- };
- _proto._enter = function _enter(event, context) {
- var dataKey = this.constructor.DATA_KEY;
- context = context || $(event.currentTarget).data(dataKey);
- if (!context) {
- context = new this.constructor(event.currentTarget, this._getDelegateConfig());
- $(event.currentTarget).data(dataKey, context);
- }
- if (event) {
- context._activeTrigger[event.type === 'focusin' ? Trigger.FOCUS : Trigger.HOVER] = true;
- }
- if ($(context.getTipElement()).hasClass(ClassName$6.SHOW) || context._hoverState === HoverState.SHOW) {
- context._hoverState = HoverState.SHOW;
- return;
- }
- clearTimeout(context._timeout);
- context._hoverState = HoverState.SHOW;
- if (!context.config.delay || !context.config.delay.show) {
- context.show();
- return;
- }
- context._timeout = setTimeout(function () {
- if (context._hoverState === HoverState.SHOW) {
- context.show();
- }
- }, context.config.delay.show);
- };
- _proto._leave = function _leave(event, context) {
- var dataKey = this.constructor.DATA_KEY;
- context = context || $(event.currentTarget).data(dataKey);
- if (!context) {
- context = new this.constructor(event.currentTarget, this._getDelegateConfig());
- $(event.currentTarget).data(dataKey, context);
- }
- if (event) {
- context._activeTrigger[event.type === 'focusout' ? Trigger.FOCUS : Trigger.HOVER] = false;
- }
- if (context._isWithActiveTrigger()) {
- return;
- }
- clearTimeout(context._timeout);
- context._hoverState = HoverState.OUT;
- if (!context.config.delay || !context.config.delay.hide) {
- context.hide();
- return;
- }
- context._timeout = setTimeout(function () {
- if (context._hoverState === HoverState.OUT) {
- context.hide();
- }
- }, context.config.delay.hide);
- };
- _proto._isWithActiveTrigger = function _isWithActiveTrigger() {
- for (var trigger in this._activeTrigger) {
- if (this._activeTrigger[trigger]) {
- return true;
- }
- }
- return false;
- };
- _proto._getConfig = function _getConfig(config) {
- var dataAttributes = $(this.element).data();
- Object.keys(dataAttributes).forEach(function (dataAttr) {
- if (DISALLOWED_ATTRIBUTES.indexOf(dataAttr) !== -1) {
- delete dataAttributes[dataAttr];
- }
- });
- config = _objectSpread2({}, this.constructor.Default, {}, dataAttributes, {}, typeof config === 'object' && config ? config : {});
- if (typeof config.delay === 'number') {
- config.delay = {
- show: config.delay,
- hide: config.delay
- };
- }
- if (typeof config.title === 'number') {
- config.title = config.title.toString();
- }
- if (typeof config.content === 'number') {
- config.content = config.content.toString();
- }
- Util.typeCheckConfig(NAME$6, config, this.constructor.DefaultType);
- if (config.sanitize) {
- config.template = sanitizeHtml(config.template, config.whiteList, config.sanitizeFn);
- }
- return config;
- };
- _proto._getDelegateConfig = function _getDelegateConfig() {
- var config = {};
- if (this.config) {
- for (var key in this.config) {
- if (this.constructor.Default[key] !== this.config[key]) {
- config[key] = this.config[key];
- }
- }
- }
- return config;
- };
- _proto._cleanTipClass = function _cleanTipClass() {
- var $tip = $(this.getTipElement());
- var tabClass = $tip.attr('class').match(BSCLS_PREFIX_REGEX);
- if (tabClass !== null && tabClass.length) {
- $tip.removeClass(tabClass.join(''));
- }
- };
- _proto._handlePopperPlacementChange = function _handlePopperPlacementChange(popperData) {
- var popperInstance = popperData.instance;
- this.tip = popperInstance.popper;
- this._cleanTipClass();
- this.addAttachmentClass(this._getAttachment(popperData.placement));
- };
- _proto._fixTransition = function _fixTransition() {
- var tip = this.getTipElement();
- var initConfigAnimation = this.config.animation;
- if (tip.getAttribute('x-placement') !== null) {
- return;
- }
- $(tip).removeClass(ClassName$6.FADE);
- this.config.animation = false;
- this.hide();
- this.show();
- this.config.animation = initConfigAnimation;
- } // Static
- ;
- Tooltip._jQueryInterface = function _jQueryInterface(config) {
- return this.each(function () {
- var data = $(this).data(DATA_KEY$6);
- var _config = typeof config === 'object' && config;
- if (!data && /dispose|hide/.test(config)) {
- return;
- }
- if (!data) {
- data = new Tooltip(this, _config);
- $(this).data(DATA_KEY$6, data);
- }
- if (typeof config === 'string') {
- if (typeof data[config] === 'undefined') {
- throw new TypeError("No method named \"" + config + "\"");
- }
- data[config]();
- }
- });
- };
- _createClass(Tooltip, null, [{
- key: "VERSION",
- get: function get() {
- return VERSION$6;
- }
- }, {
- key: "Default",
- get: function get() {
- return Default$4;
- }
- }, {
- key: "NAME",
- get: function get() {
- return NAME$6;
- }
- }, {
- key: "DATA_KEY",
- get: function get() {
- return DATA_KEY$6;
- }
- }, {
- key: "Event",
- get: function get() {
- return Event$6;
- }
- }, {
- key: "EVENT_KEY",
- get: function get() {
- return EVENT_KEY$6;
- }
- }, {
- key: "DefaultType",
- get: function get() {
- return DefaultType$4;
- }
- }]);
- return Tooltip;
- }();
- /**
- * ------------------------------------------------------------------------
- * jQuery
- * ------------------------------------------------------------------------
- */
- $.fn[NAME$6] = Tooltip._jQueryInterface;
- $.fn[NAME$6].Constructor = Tooltip;
- $.fn[NAME$6].noConflict = function () {
- $.fn[NAME$6] = JQUERY_NO_CONFLICT$6;
- return Tooltip._jQueryInterface;
- };
- /**
- * ------------------------------------------------------------------------
- * Constants
- * ------------------------------------------------------------------------
- */
- var NAME$7 = 'popover';
- var VERSION$7 = '4.4.1';
- var DATA_KEY$7 = 'bs.popover';
- var EVENT_KEY$7 = "." + DATA_KEY$7;
- var JQUERY_NO_CONFLICT$7 = $.fn[NAME$7];
- var CLASS_PREFIX$1 = 'bs-popover';
- var BSCLS_PREFIX_REGEX$1 = new RegExp("(^|\\s)" + CLASS_PREFIX$1 + "\\S+", 'g');
- var Default$5 = _objectSpread2({}, Tooltip.Default, {
- placement: 'right',
- trigger: 'click',
- content: '',
- template: '<div class="popover" role="tooltip">' + '<div class="arrow"></div>' + '<h3 class="popover-header"></h3>' + '<div class="popover-body"></div></div>'
- });
- var DefaultType$5 = _objectSpread2({}, Tooltip.DefaultType, {
- content: '(string|element|function)'
- });
- var ClassName$7 = {
- FADE: 'fade',
- SHOW: 'show'
- };
- var Selector$7 = {
- TITLE: '.popover-header',
- CONTENT: '.popover-body'
- };
- var Event$7 = {
- HIDE: "hide" + EVENT_KEY$7,
- HIDDEN: "hidden" + EVENT_KEY$7,
- SHOW: "show" + EVENT_KEY$7,
- SHOWN: "shown" + EVENT_KEY$7,
- INSERTED: "inserted" + EVENT_KEY$7,
- CLICK: "click" + EVENT_KEY$7,
- FOCUSIN: "focusin" + EVENT_KEY$7,
- FOCUSOUT: "focusout" + EVENT_KEY$7,
- MOUSEENTER: "mouseenter" + EVENT_KEY$7,
- MOUSELEAVE: "mouseleave" + EVENT_KEY$7
- };
- /**
- * ------------------------------------------------------------------------
- * Class Definition
- * ------------------------------------------------------------------------
- */
- var Popover =
- /*#__PURE__*/
- function (_Tooltip) {
- _inheritsLoose(Popover, _Tooltip);
- function Popover() {
- return _Tooltip.apply(this, arguments) || this;
- }
- var _proto = Popover.prototype;
- // Overrides
- _proto.isWithContent = function isWithContent() {
- return this.getTitle() || this._getContent();
- };
- _proto.addAttachmentClass = function addAttachmentClass(attachment) {
- $(this.getTipElement()).addClass(CLASS_PREFIX$1 + "-" + attachment);
- };
- _proto.getTipElement = function getTipElement() {
- this.tip = this.tip || $(this.config.template)[0];
- return this.tip;
- };
- _proto.setContent = function setContent() {
- var $tip = $(this.getTipElement()); // We use append for html objects to maintain js events
- this.setElementContent($tip.find(Selector$7.TITLE), this.getTitle());
- var content = this._getContent();
- if (typeof content === 'function') {
- content = content.call(this.element);
- }
- this.setElementContent($tip.find(Selector$7.CONTENT), content);
- $tip.removeClass(ClassName$7.FADE + " " + ClassName$7.SHOW);
- } // Private
- ;
- _proto._getContent = function _getContent() {
- return this.element.getAttribute('data-content') || this.config.content;
- };
- _proto._cleanTipClass = function _cleanTipClass() {
- var $tip = $(this.getTipElement());
- var tabClass = $tip.attr('class').match(BSCLS_PREFIX_REGEX$1);
- if (tabClass !== null && tabClass.length > 0) {
- $tip.removeClass(tabClass.join(''));
- }
- } // Static
- ;
- Popover._jQueryInterface = function _jQueryInterface(config) {
- return this.each(function () {
- var data = $(this).data(DATA_KEY$7);
- var _config = typeof config === 'object' ? config : null;
- if (!data && /dispose|hide/.test(config)) {
- return;
- }
- if (!data) {
- data = new Popover(this, _config);
- $(this).data(DATA_KEY$7, data);
- }
- if (typeof config === 'string') {
- if (typeof data[config] === 'undefined') {
- throw new TypeError("No method named \"" + config + "\"");
- }
- data[config]();
- }
- });
- };
- _createClass(Popover, null, [{
- key: "VERSION",
- // Getters
- get: function get() {
- return VERSION$7;
- }
- }, {
- key: "Default",
- get: function get() {
- return Default$5;
- }
- }, {
- key: "NAME",
- get: function get() {
- return NAME$7;
- }
- }, {
- key: "DATA_KEY",
- get: function get() {
- return DATA_KEY$7;
- }
- }, {
- key: "Event",
- get: function get() {
- return Event$7;
- }
- }, {
- key: "EVENT_KEY",
- get: function get() {
- return EVENT_KEY$7;
- }
- }, {
- key: "DefaultType",
- get: function get() {
- return DefaultType$5;
- }
- }]);
- return Popover;
- }(Tooltip);
- /**
- * ------------------------------------------------------------------------
- * jQuery
- * ------------------------------------------------------------------------
- */
- $.fn[NAME$7] = Popover._jQueryInterface;
- $.fn[NAME$7].Constructor = Popover;
- $.fn[NAME$7].noConflict = function () {
- $.fn[NAME$7] = JQUERY_NO_CONFLICT$7;
- return Popover._jQueryInterface;
- };
- /**
- * ------------------------------------------------------------------------
- * Constants
- * ------------------------------------------------------------------------
- */
- var NAME$8 = 'scrollspy';
- var VERSION$8 = '4.4.1';
- var DATA_KEY$8 = 'bs.scrollspy';
- var EVENT_KEY$8 = "." + DATA_KEY$8;
- var DATA_API_KEY$6 = '.data-api';
- var JQUERY_NO_CONFLICT$8 = $.fn[NAME$8];
- var Default$6 = {
- offset: 10,
- method: 'auto',
- target: ''
- };
- var DefaultType$6 = {
- offset: 'number',
- method: 'string',
- target: '(string|element)'
- };
- var Event$8 = {
- ACTIVATE: "activate" + EVENT_KEY$8,
- SCROLL: "scroll" + EVENT_KEY$8,
- LOAD_DATA_API: "load" + EVENT_KEY$8 + DATA_API_KEY$6
- };
- var ClassName$8 = {
- DROPDOWN_ITEM: 'dropdown-item',
- DROPDOWN_MENU: 'dropdown-menu',
- ACTIVE: 'active'
- };
- var Selector$8 = {
- DATA_SPY: '[data-spy="scroll"]',
- ACTIVE: '.active',
- NAV_LIST_GROUP: '.nav, .list-group',
- NAV_LINKS: '.nav-link',
- NAV_ITEMS: '.nav-item',
- LIST_ITEMS: '.list-group-item',
- DROPDOWN: '.dropdown',
- DROPDOWN_ITEMS: '.dropdown-item',
- DROPDOWN_TOGGLE: '.dropdown-toggle'
- };
- var OffsetMethod = {
- OFFSET: 'offset',
- POSITION: 'position'
- };
- /**
- * ------------------------------------------------------------------------
- * Class Definition
- * ------------------------------------------------------------------------
- */
- var ScrollSpy =
- /*#__PURE__*/
- function () {
- function ScrollSpy(element, config) {
- var _this = this;
- this._element = element;
- this._scrollElement = element.tagName === 'BODY' ? window : element;
- this._config = this._getConfig(config);
- this._selector = this._config.target + " " + Selector$8.NAV_LINKS + "," + (this._config.target + " " + Selector$8.LIST_ITEMS + ",") + (this._config.target + " " + Selector$8.DROPDOWN_ITEMS);
- this._offsets = [];
- this._targets = [];
- this._activeTarget = null;
- this._scrollHeight = 0;
- $(this._scrollElement).on(Event$8.SCROLL, function (event) {
- return _this._process(event);
- });
- this.refresh();
- this._process();
- } // Getters
- var _proto = ScrollSpy.prototype;
- // Public
- _proto.refresh = function refresh() {
- var _this2 = this;
- var autoMethod = this._scrollElement === this._scrollElement.window ? OffsetMethod.OFFSET : OffsetMethod.POSITION;
- var offsetMethod = this._config.method === 'auto' ? autoMethod : this._config.method;
- var offsetBase = offsetMethod === OffsetMethod.POSITION ? this._getScrollTop() : 0;
- this._offsets = [];
- this._targets = [];
- this._scrollHeight = this._getScrollHeight();
- var targets = [].slice.call(document.querySelectorAll(this._selector));
- targets.map(function (element) {
- var target;
- var targetSelector = Util.getSelectorFromElement(element);
- if (targetSelector) {
- target = document.querySelector(targetSelector);
- }
- if (target) {
- var targetBCR = target.getBoundingClientRect();
- if (targetBCR.width || targetBCR.height) {
- // TODO (fat): remove sketch reliance on jQuery position/offset
- return [$(target)[offsetMethod]().top + offsetBase, targetSelector];
- }
- }
- return null;
- }).filter(function (item) {
- return item;
- }).sort(function (a, b) {
- return a[0] - b[0];
- }).forEach(function (item) {
- _this2._offsets.push(item[0]);
- _this2._targets.push(item[1]);
- });
- };
- _proto.dispose = function dispose() {
- $.removeData(this._element, DATA_KEY$8);
- $(this._scrollElement).off(EVENT_KEY$8);
- this._element = null;
- this._scrollElement = null;
- this._config = null;
- this._selector = null;
- this._offsets = null;
- this._targets = null;
- this._activeTarget = null;
- this._scrollHeight = null;
- } // Private
- ;
- _proto._getConfig = function _getConfig(config) {
- config = _objectSpread2({}, Default$6, {}, typeof config === 'object' && config ? config : {});
- if (typeof config.target !== 'string') {
- var id = $(config.target).attr('id');
- if (!id) {
- id = Util.getUID(NAME$8);
- $(config.target).attr('id', id);
- }
- config.target = "#" + id;
- }
- Util.typeCheckConfig(NAME$8, config, DefaultType$6);
- return config;
- };
- _proto._getScrollTop = function _getScrollTop() {
- return this._scrollElement === window ? this._scrollElement.pageYOffset : this._scrollElement.scrollTop;
- };
- _proto._getScrollHeight = function _getScrollHeight() {
- return this._scrollElement.scrollHeight || Math.max(document.body.scrollHeight, document.documentElement.scrollHeight);
- };
- _proto._getOffsetHeight = function _getOffsetHeight() {
- return this._scrollElement === window ? window.innerHeight : this._scrollElement.getBoundingClientRect().height;
- };
- _proto._process = function _process() {
- var scrollTop = this._getScrollTop() + this._config.offset;
- var scrollHeight = this._getScrollHeight();
- var maxScroll = this._config.offset + scrollHeight - this._getOffsetHeight();
- if (this._scrollHeight !== scrollHeight) {
- this.refresh();
- }
- if (scrollTop >= maxScroll) {
- var target = this._targets[this._targets.length - 1];
- if (this._activeTarget !== target) {
- this._activate(target);
- }
- return;
- }
- if (this._activeTarget && scrollTop < this._offsets[0] && this._offsets[0] > 0) {
- this._activeTarget = null;
- this._clear();
- return;
- }
- var offsetLength = this._offsets.length;
- for (var i = offsetLength; i--;) {
- var isActiveTarget = this._activeTarget !== this._targets[i] && scrollTop >= this._offsets[i] && (typeof this._offsets[i + 1] === 'undefined' || scrollTop < this._offsets[i + 1]);
- if (isActiveTarget) {
- this._activate(this._targets[i]);
- }
- }
- };
- _proto._activate = function _activate(target) {
- this._activeTarget = target;
- this._clear();
- var queries = this._selector.split(',').map(function (selector) {
- return selector + "[data-target=\"" + target + "\"]," + selector + "[href=\"" + target + "\"]";
- });
- var $link = $([].slice.call(document.querySelectorAll(queries.join(','))));
- if ($link.hasClass(ClassName$8.DROPDOWN_ITEM)) {
- $link.closest(Selector$8.DROPDOWN).find(Selector$8.DROPDOWN_TOGGLE).addClass(ClassName$8.ACTIVE);
- $link.addClass(ClassName$8.ACTIVE);
- } else {
- // Set triggered link as active
- $link.addClass(ClassName$8.ACTIVE); // Set triggered links parents as active
- // With both <ul> and <nav> markup a parent is the previous sibling of any nav ancestor
- $link.parents(Selector$8.NAV_LIST_GROUP).prev(Selector$8.NAV_LINKS + ", " + Selector$8.LIST_ITEMS).addClass(ClassName$8.ACTIVE); // Handle special case when .nav-link is inside .nav-item
- $link.parents(Selector$8.NAV_LIST_GROUP).prev(Selector$8.NAV_ITEMS).children(Selector$8.NAV_LINKS).addClass(ClassName$8.ACTIVE);
- }
- $(this._scrollElement).trigger(Event$8.ACTIVATE, {
- relatedTarget: target
- });
- };
- _proto._clear = function _clear() {
- [].slice.call(document.querySelectorAll(this._selector)).filter(function (node) {
- return node.classList.contains(ClassName$8.ACTIVE);
- }).forEach(function (node) {
- return node.classList.remove(ClassName$8.ACTIVE);
- });
- } // Static
- ;
- ScrollSpy._jQueryInterface = function _jQueryInterface(config) {
- return this.each(function () {
- var data = $(this).data(DATA_KEY$8);
- var _config = typeof config === 'object' && config;
- if (!data) {
- data = new ScrollSpy(this, _config);
- $(this).data(DATA_KEY$8, data);
- }
- if (typeof config === 'string') {
- if (typeof data[config] === 'undefined') {
- throw new TypeError("No method named \"" + config + "\"");
- }
- data[config]();
- }
- });
- };
- _createClass(ScrollSpy, null, [{
- key: "VERSION",
- get: function get() {
- return VERSION$8;
- }
- }, {
- key: "Default",
- get: function get() {
- return Default$6;
- }
- }]);
- return ScrollSpy;
- }();
- /**
- * ------------------------------------------------------------------------
- * Data Api implementation
- * ------------------------------------------------------------------------
- */
- $(window).on(Event$8.LOAD_DATA_API, function () {
- var scrollSpys = [].slice.call(document.querySelectorAll(Selector$8.DATA_SPY));
- var scrollSpysLength = scrollSpys.length;
- for (var i = scrollSpysLength; i--;) {
- var $spy = $(scrollSpys[i]);
- ScrollSpy._jQueryInterface.call($spy, $spy.data());
- }
- });
- /**
- * ------------------------------------------------------------------------
- * jQuery
- * ------------------------------------------------------------------------
- */
- $.fn[NAME$8] = ScrollSpy._jQueryInterface;
- $.fn[NAME$8].Constructor = ScrollSpy;
- $.fn[NAME$8].noConflict = function () {
- $.fn[NAME$8] = JQUERY_NO_CONFLICT$8;
- return ScrollSpy._jQueryInterface;
- };
- /**
- * ------------------------------------------------------------------------
- * Constants
- * ------------------------------------------------------------------------
- */
- var NAME$9 = 'tab';
- var VERSION$9 = '4.4.1';
- var DATA_KEY$9 = 'bs.tab';
- var EVENT_KEY$9 = "." + DATA_KEY$9;
- var DATA_API_KEY$7 = '.data-api';
- var JQUERY_NO_CONFLICT$9 = $.fn[NAME$9];
- var Event$9 = {
- HIDE: "hide" + EVENT_KEY$9,
- HIDDEN: "hidden" + EVENT_KEY$9,
- SHOW: "show" + EVENT_KEY$9,
- SHOWN: "shown" + EVENT_KEY$9,
- CLICK_DATA_API: "click" + EVENT_KEY$9 + DATA_API_KEY$7
- };
- var ClassName$9 = {
- DROPDOWN_MENU: 'dropdown-menu',
- ACTIVE: 'active',
- DISABLED: 'disabled',
- FADE: 'fade',
- SHOW: 'show'
- };
- var Selector$9 = {
- DROPDOWN: '.dropdown',
- NAV_LIST_GROUP: '.nav, .list-group',
- ACTIVE: '.active',
- ACTIVE_UL: '> li > .active',
- DATA_TOGGLE: '[data-toggle="tab"], [data-toggle="pill"], [data-toggle="list"]',
- DROPDOWN_TOGGLE: '.dropdown-toggle',
- DROPDOWN_ACTIVE_CHILD: '> .dropdown-menu .active'
- };
- /**
- * ------------------------------------------------------------------------
- * Class Definition
- * ------------------------------------------------------------------------
- */
- var Tab =
- /*#__PURE__*/
- function () {
- function Tab(element) {
- this._element = element;
- } // Getters
- var _proto = Tab.prototype;
- // Public
- _proto.show = function show() {
- var _this = this;
- if (this._element.parentNode && this._element.parentNode.nodeType === Node.ELEMENT_NODE && $(this._element).hasClass(ClassName$9.ACTIVE) || $(this._element).hasClass(ClassName$9.DISABLED)) {
- return;
- }
- var target;
- var previous;
- var listElement = $(this._element).closest(Selector$9.NAV_LIST_GROUP)[0];
- var selector = Util.getSelectorFromElement(this._element);
- if (listElement) {
- var itemSelector = listElement.nodeName === 'UL' || listElement.nodeName === 'OL' ? Selector$9.ACTIVE_UL : Selector$9.ACTIVE;
- previous = $.makeArray($(listElement).find(itemSelector));
- previous = previous[previous.length - 1];
- }
- var hideEvent = $.Event(Event$9.HIDE, {
- relatedTarget: this._element
- });
- var showEvent = $.Event(Event$9.SHOW, {
- relatedTarget: previous
- });
- if (previous) {
- $(previous).trigger(hideEvent);
- }
- $(this._element).trigger(showEvent);
- if (showEvent.isDefaultPrevented() || hideEvent.isDefaultPrevented()) {
- return;
- }
- if (selector) {
- target = document.querySelector(selector);
- }
- this._activate(this._element, listElement);
- var complete = function complete() {
- var hiddenEvent = $.Event(Event$9.HIDDEN, {
- relatedTarget: _this._element
- });
- var shownEvent = $.Event(Event$9.SHOWN, {
- relatedTarget: previous
- });
- $(previous).trigger(hiddenEvent);
- $(_this._element).trigger(shownEvent);
- };
- if (target) {
- this._activate(target, target.parentNode, complete);
- } else {
- complete();
- }
- };
- _proto.dispose = function dispose() {
- $.removeData(this._element, DATA_KEY$9);
- this._element = null;
- } // Private
- ;
- _proto._activate = function _activate(element, container, callback) {
- var _this2 = this;
- var activeElements = container && (container.nodeName === 'UL' || container.nodeName === 'OL') ? $(container).find(Selector$9.ACTIVE_UL) : $(container).children(Selector$9.ACTIVE);
- var active = activeElements[0];
- var isTransitioning = callback && active && $(active).hasClass(ClassName$9.FADE);
- var complete = function complete() {
- return _this2._transitionComplete(element, active, callback);
- };
- if (active && isTransitioning) {
- var transitionDuration = Util.getTransitionDurationFromElement(active);
- $(active).removeClass(ClassName$9.SHOW).one(Util.TRANSITION_END, complete).emulateTransitionEnd(transitionDuration);
- } else {
- complete();
- }
- };
- _proto._transitionComplete = function _transitionComplete(element, active, callback) {
- if (active) {
- $(active).removeClass(ClassName$9.ACTIVE);
- var dropdownChild = $(active.parentNode).find(Selector$9.DROPDOWN_ACTIVE_CHILD)[0];
- if (dropdownChild) {
- $(dropdownChild).removeClass(ClassName$9.ACTIVE);
- }
- if (active.getAttribute('role') === 'tab') {
- active.setAttribute('aria-selected', false);
- }
- }
- $(element).addClass(ClassName$9.ACTIVE);
- if (element.getAttribute('role') === 'tab') {
- element.setAttribute('aria-selected', true);
- }
- Util.reflow(element);
- if (element.classList.contains(ClassName$9.FADE)) {
- element.classList.add(ClassName$9.SHOW);
- }
- if (element.parentNode && $(element.parentNode).hasClass(ClassName$9.DROPDOWN_MENU)) {
- var dropdownElement = $(element).closest(Selector$9.DROPDOWN)[0];
- if (dropdownElement) {
- var dropdownToggleList = [].slice.call(dropdownElement.querySelectorAll(Selector$9.DROPDOWN_TOGGLE));
- $(dropdownToggleList).addClass(ClassName$9.ACTIVE);
- }
- element.setAttribute('aria-expanded', true);
- }
- if (callback) {
- callback();
- }
- } // Static
- ;
- Tab._jQueryInterface = function _jQueryInterface(config) {
- return this.each(function () {
- var $this = $(this);
- var data = $this.data(DATA_KEY$9);
- if (!data) {
- data = new Tab(this);
- $this.data(DATA_KEY$9, data);
- }
- if (typeof config === 'string') {
- if (typeof data[config] === 'undefined') {
- throw new TypeError("No method named \"" + config + "\"");
- }
- data[config]();
- }
- });
- };
- _createClass(Tab, null, [{
- key: "VERSION",
- get: function get() {
- return VERSION$9;
- }
- }]);
- return Tab;
- }();
- /**
- * ------------------------------------------------------------------------
- * Data Api implementation
- * ------------------------------------------------------------------------
- */
- $(document).on(Event$9.CLICK_DATA_API, Selector$9.DATA_TOGGLE, function (event) {
- event.preventDefault();
- Tab._jQueryInterface.call($(this), 'show');
- });
- /**
- * ------------------------------------------------------------------------
- * jQuery
- * ------------------------------------------------------------------------
- */
- $.fn[NAME$9] = Tab._jQueryInterface;
- $.fn[NAME$9].Constructor = Tab;
- $.fn[NAME$9].noConflict = function () {
- $.fn[NAME$9] = JQUERY_NO_CONFLICT$9;
- return Tab._jQueryInterface;
- };
- /**
- * ------------------------------------------------------------------------
- * Constants
- * ------------------------------------------------------------------------
- */
- var NAME$a = 'toast';
- var VERSION$a = '4.4.1';
- var DATA_KEY$a = 'bs.toast';
- var EVENT_KEY$a = "." + DATA_KEY$a;
- var JQUERY_NO_CONFLICT$a = $.fn[NAME$a];
- var Event$a = {
- CLICK_DISMISS: "click.dismiss" + EVENT_KEY$a,
- HIDE: "hide" + EVENT_KEY$a,
- HIDDEN: "hidden" + EVENT_KEY$a,
- SHOW: "show" + EVENT_KEY$a,
- SHOWN: "shown" + EVENT_KEY$a
- };
- var ClassName$a = {
- FADE: 'fade',
- HIDE: 'hide',
- SHOW: 'show',
- SHOWING: 'showing'
- };
- var DefaultType$7 = {
- animation: 'boolean',
- autohide: 'boolean',
- delay: 'number'
- };
- var Default$7 = {
- animation: true,
- autohide: true,
- delay: 500
- };
- var Selector$a = {
- DATA_DISMISS: '[data-dismiss="toast"]'
- };
- /**
- * ------------------------------------------------------------------------
- * Class Definition
- * ------------------------------------------------------------------------
- */
- var Toast =
- /*#__PURE__*/
- function () {
- function Toast(element, config) {
- this._element = element;
- this._config = this._getConfig(config);
- this._timeout = null;
- this._setListeners();
- } // Getters
- var _proto = Toast.prototype;
- // Public
- _proto.show = function show() {
- var _this = this;
- var showEvent = $.Event(Event$a.SHOW);
- $(this._element).trigger(showEvent);
- if (showEvent.isDefaultPrevented()) {
- return;
- }
- if (this._config.animation) {
- this._element.classList.add(ClassName$a.FADE);
- }
- var complete = function complete() {
- _this._element.classList.remove(ClassName$a.SHOWING);
- _this._element.classList.add(ClassName$a.SHOW);
- $(_this._element).trigger(Event$a.SHOWN);
- if (_this._config.autohide) {
- _this._timeout = setTimeout(function () {
- _this.hide();
- }, _this._config.delay);
- }
- };
- this._element.classList.remove(ClassName$a.HIDE);
- Util.reflow(this._element);
- this._element.classList.add(ClassName$a.SHOWING);
- if (this._config.animation) {
- var transitionDuration = Util.getTransitionDurationFromElement(this._element);
- $(this._element).one(Util.TRANSITION_END, complete).emulateTransitionEnd(transitionDuration);
- } else {
- complete();
- }
- };
- _proto.hide = function hide() {
- if (!this._element.classList.contains(ClassName$a.SHOW)) {
- return;
- }
- var hideEvent = $.Event(Event$a.HIDE);
- $(this._element).trigger(hideEvent);
- if (hideEvent.isDefaultPrevented()) {
- return;
- }
- this._close();
- };
- _proto.dispose = function dispose() {
- clearTimeout(this._timeout);
- this._timeout = null;
- if (this._element.classList.contains(ClassName$a.SHOW)) {
- this._element.classList.remove(ClassName$a.SHOW);
- }
- $(this._element).off(Event$a.CLICK_DISMISS);
- $.removeData(this._element, DATA_KEY$a);
- this._element = null;
- this._config = null;
- } // Private
- ;
- _proto._getConfig = function _getConfig(config) {
- config = _objectSpread2({}, Default$7, {}, $(this._element).data(), {}, typeof config === 'object' && config ? config : {});
- Util.typeCheckConfig(NAME$a, config, this.constructor.DefaultType);
- return config;
- };
- _proto._setListeners = function _setListeners() {
- var _this2 = this;
- $(this._element).on(Event$a.CLICK_DISMISS, Selector$a.DATA_DISMISS, function () {
- return _this2.hide();
- });
- };
- _proto._close = function _close() {
- var _this3 = this;
- var complete = function complete() {
- _this3._element.classList.add(ClassName$a.HIDE);
- $(_this3._element).trigger(Event$a.HIDDEN);
- };
- this._element.classList.remove(ClassName$a.SHOW);
- if (this._config.animation) {
- var transitionDuration = Util.getTransitionDurationFromElement(this._element);
- $(this._element).one(Util.TRANSITION_END, complete).emulateTransitionEnd(transitionDuration);
- } else {
- complete();
- }
- } // Static
- ;
- Toast._jQueryInterface = function _jQueryInterface(config) {
- return this.each(function () {
- var $element = $(this);
- var data = $element.data(DATA_KEY$a);
- var _config = typeof config === 'object' && config;
- if (!data) {
- data = new Toast(this, _config);
- $element.data(DATA_KEY$a, data);
- }
- if (typeof config === 'string') {
- if (typeof data[config] === 'undefined') {
- throw new TypeError("No method named \"" + config + "\"");
- }
- data[config](this);
- }
- });
- };
- _createClass(Toast, null, [{
- key: "VERSION",
- get: function get() {
- return VERSION$a;
- }
- }, {
- key: "DefaultType",
- get: function get() {
- return DefaultType$7;
- }
- }, {
- key: "Default",
- get: function get() {
- return Default$7;
- }
- }]);
- return Toast;
- }();
- /**
- * ------------------------------------------------------------------------
- * jQuery
- * ------------------------------------------------------------------------
- */
- $.fn[NAME$a] = Toast._jQueryInterface;
- $.fn[NAME$a].Constructor = Toast;
- $.fn[NAME$a].noConflict = function () {
- $.fn[NAME$a] = JQUERY_NO_CONFLICT$a;
- return Toast._jQueryInterface;
- };
- exports.Alert = Alert;
- exports.Button = Button;
- exports.Carousel = Carousel;
- exports.Collapse = Collapse;
- exports.Dropdown = Dropdown;
- exports.Modal = Modal;
- exports.Popover = Popover;
- exports.Scrollspy = ScrollSpy;
- exports.Tab = Tab;
- exports.Toast = Toast;
- exports.Tooltip = Tooltip;
- exports.Util = Util;
- Object.defineProperty(exports, '__esModule', { value: true });
- })));
- //# sourceMappingURL=bootstrap.js.map
- ;
- /* **********************************************
- Begin prism-core.js
- ********************************************** */
- var _self = (typeof window !== 'undefined')
- ? window // if in browser
- : (
- (typeof WorkerGlobalScope !== 'undefined' && self instanceof WorkerGlobalScope)
- ? self // if in worker
- : {} // if in node js
- );
- /**
- * Prism: Lightweight, robust, elegant syntax highlighting
- * MIT license http://www.opensource.org/licenses/mit-license.php/
- * @author Lea Verou http://lea.verou.me
- */
- var Prism = (function (_self){
- // Private helper vars
- var lang = /\blang(?:uage)?-([\w-]+)\b/i;
- var uniqueId = 0;
- var _ = {
- manual: _self.Prism && _self.Prism.manual,
- disableWorkerMessageHandler: _self.Prism && _self.Prism.disableWorkerMessageHandler,
- util: {
- encode: function (tokens) {
- if (tokens instanceof Token) {
- return new Token(tokens.type, _.util.encode(tokens.content), tokens.alias);
- } else if (Array.isArray(tokens)) {
- return tokens.map(_.util.encode);
- } else {
- return tokens.replace(/&/g, '&').replace(/</g, '<').replace(/\u00a0/g, ' ');
- }
- },
- type: function (o) {
- return Object.prototype.toString.call(o).slice(8, -1);
- },
- objId: function (obj) {
- if (!obj['__id']) {
- Object.defineProperty(obj, '__id', { value: ++uniqueId });
- }
- return obj['__id'];
- },
- // Deep clone a language definition (e.g. to extend it)
- clone: function deepClone(o, visited) {
- var clone, id, type = _.util.type(o);
- visited = visited || {};
- switch (type) {
- case 'Object':
- id = _.util.objId(o);
- if (visited[id]) {
- return visited[id];
- }
- clone = {};
- visited[id] = clone;
- for (var key in o) {
- if (o.hasOwnProperty(key)) {
- clone[key] = deepClone(o[key], visited);
- }
- }
- return clone;
- case 'Array':
- id = _.util.objId(o);
- if (visited[id]) {
- return visited[id];
- }
- clone = [];
- visited[id] = clone;
- o.forEach(function (v, i) {
- clone[i] = deepClone(v, visited);
- });
- return clone;
- default:
- return o;
- }
- },
- /**
- * Returns the Prism language of the given element set by a `language-xxxx` or `lang-xxxx` class.
- *
- * If no language is set for the element or the element is `null` or `undefined`, `none` will be returned.
- *
- * @param {Element} element
- * @returns {string}
- */
- getLanguage: function (element) {
- while (element && !lang.test(element.className)) {
- element = element.parentElement;
- }
- if (element) {
- return (element.className.match(lang) || [, 'none'])[1].toLowerCase();
- }
- return 'none';
- },
- /**
- * Returns the script element that is currently executing.
- *
- * This does __not__ work for line script element.
- *
- * @returns {HTMLScriptElement | null}
- */
- currentScript: function () {
- if (typeof document === 'undefined') {
- return null;
- }
- if ('currentScript' in document) {
- return document.currentScript;
- }
- // IE11 workaround
- // we'll get the src of the current script by parsing IE11's error stack trace
- // this will not work for inline scripts
- try {
- throw new Error();
- } catch (err) {
- // Get file src url from stack. Specifically works with the format of stack traces in IE.
- // A stack will look like this:
- //
- // Error
- // at _.util.currentScript (http://localhost/components/prism-core.js:119:5)
- // at Global code (http://localhost/components/prism-core.js:606:1)
- var src = (/at [^(\r\n]*\((.*):.+:.+\)$/i.exec(err.stack) || [])[1];
- if (src) {
- var scripts = document.getElementsByTagName('script');
- for (var i in scripts) {
- if (scripts[i].src == src) {
- return scripts[i];
- }
- }
- }
- return null;
- }
- }
- },
- languages: {
- extend: function (id, redef) {
- var lang = _.util.clone(_.languages[id]);
- for (var key in redef) {
- lang[key] = redef[key];
- }
- return lang;
- },
- /**
- * Insert a token before another token in a language literal
- * As this needs to recreate the object (we cannot actually insert before keys in object literals),
- * we cannot just provide an object, we need an object and a key.
- * @param inside The key (or language id) of the parent
- * @param before The key to insert before.
- * @param insert Object with the key/value pairs to insert
- * @param root The object that contains `inside`. If equal to Prism.languages, it can be omitted.
- */
- insertBefore: function (inside, before, insert, root) {
- root = root || _.languages;
- var grammar = root[inside];
- var ret = {};
- for (var token in grammar) {
- if (grammar.hasOwnProperty(token)) {
- if (token == before) {
- for (var newToken in insert) {
- if (insert.hasOwnProperty(newToken)) {
- ret[newToken] = insert[newToken];
- }
- }
- }
- // Do not insert token which also occur in insert. See #1525
- if (!insert.hasOwnProperty(token)) {
- ret[token] = grammar[token];
- }
- }
- }
- var old = root[inside];
- root[inside] = ret;
- // Update references in other language definitions
- _.languages.DFS(_.languages, function(key, value) {
- if (value === old && key != inside) {
- this[key] = ret;
- }
- });
- return ret;
- },
- // Traverse a language definition with Depth First Search
- DFS: function DFS(o, callback, type, visited) {
- visited = visited || {};
- var objId = _.util.objId;
- for (var i in o) {
- if (o.hasOwnProperty(i)) {
- callback.call(o, i, o[i], type || i);
- var property = o[i],
- propertyType = _.util.type(property);
- if (propertyType === 'Object' && !visited[objId(property)]) {
- visited[objId(property)] = true;
- DFS(property, callback, null, visited);
- }
- else if (propertyType === 'Array' && !visited[objId(property)]) {
- visited[objId(property)] = true;
- DFS(property, callback, i, visited);
- }
- }
- }
- }
- },
- plugins: {},
- highlightAll: function(async, callback) {
- _.highlightAllUnder(document, async, callback);
- },
- highlightAllUnder: function(container, async, callback) {
- var env = {
- callback: callback,
- container: container,
- selector: 'code[class*="language-"], [class*="language-"] code, code[class*="lang-"], [class*="lang-"] code'
- };
- _.hooks.run('before-highlightall', env);
- env.elements = Array.prototype.slice.apply(env.container.querySelectorAll(env.selector));
- _.hooks.run('before-all-elements-highlight', env);
- for (var i = 0, element; element = env.elements[i++];) {
- _.highlightElement(element, async === true, env.callback);
- }
- },
- highlightElement: function(element, async, callback) {
- // Find language
- var language = _.util.getLanguage(element);
- var grammar = _.languages[language];
- // Set language on the element, if not present
- element.className = element.className.replace(lang, '').replace(/\s+/g, ' ') + ' language-' + language;
- // Set language on the parent, for styling
- var parent = element.parentNode;
- if (parent && parent.nodeName.toLowerCase() === 'pre') {
- parent.className = parent.className.replace(lang, '').replace(/\s+/g, ' ') + ' language-' + language;
- }
- var code = element.textContent;
- var env = {
- element: element,
- language: language,
- grammar: grammar,
- code: code
- };
- function insertHighlightedCode(highlightedCode) {
- env.highlightedCode = highlightedCode;
- _.hooks.run('before-insert', env);
- env.element.innerHTML = env.highlightedCode;
- _.hooks.run('after-highlight', env);
- _.hooks.run('complete', env);
- callback && callback.call(env.element);
- }
- _.hooks.run('before-sanity-check', env);
- if (!env.code) {
- _.hooks.run('complete', env);
- callback && callback.call(env.element);
- return;
- }
- _.hooks.run('before-highlight', env);
- if (!env.grammar) {
- insertHighlightedCode(_.util.encode(env.code));
- return;
- }
- if (async && _self.Worker) {
- var worker = new Worker(_.filename);
- worker.onmessage = function(evt) {
- insertHighlightedCode(evt.data);
- };
- worker.postMessage(JSON.stringify({
- language: env.language,
- code: env.code,
- immediateClose: true
- }));
- }
- else {
- insertHighlightedCode(_.highlight(env.code, env.grammar, env.language));
- }
- },
- highlight: function (text, grammar, language) {
- var env = {
- code: text,
- grammar: grammar,
- language: language
- };
- _.hooks.run('before-tokenize', env);
- env.tokens = _.tokenize(env.code, env.grammar);
- _.hooks.run('after-tokenize', env);
- return Token.stringify(_.util.encode(env.tokens), env.language);
- },
- matchGrammar: function (text, strarr, grammar, index, startPos, oneshot, target) {
- for (var token in grammar) {
- if (!grammar.hasOwnProperty(token) || !grammar[token]) {
- continue;
- }
- var patterns = grammar[token];
- patterns = Array.isArray(patterns) ? patterns : [patterns];
- for (var j = 0; j < patterns.length; ++j) {
- if (target && target == token + ',' + j) {
- return;
- }
- var pattern = patterns[j],
- inside = pattern.inside,
- lookbehind = !!pattern.lookbehind,
- greedy = !!pattern.greedy,
- lookbehindLength = 0,
- alias = pattern.alias;
- if (greedy && !pattern.pattern.global) {
- // Without the global flag, lastIndex won't work
- var flags = pattern.pattern.toString().match(/[imsuy]*$/)[0];
- pattern.pattern = RegExp(pattern.pattern.source, flags + 'g');
- }
- pattern = pattern.pattern || pattern;
- // Don’t cache length as it changes during the loop
- for (var i = index, pos = startPos; i < strarr.length; pos += strarr[i].length, ++i) {
- var str = strarr[i];
- if (strarr.length > text.length) {
- // Something went terribly wrong, ABORT, ABORT!
- return;
- }
- if (str instanceof Token) {
- continue;
- }
- if (greedy && i != strarr.length - 1) {
- pattern.lastIndex = pos;
- var match = pattern.exec(text);
- if (!match) {
- break;
- }
- var from = match.index + (lookbehind && match[1] ? match[1].length : 0),
- to = match.index + match[0].length,
- k = i,
- p = pos;
- for (var len = strarr.length; k < len && (p < to || (!strarr[k].type && !strarr[k - 1].greedy)); ++k) {
- p += strarr[k].length;
- // Move the index i to the element in strarr that is closest to from
- if (from >= p) {
- ++i;
- pos = p;
- }
- }
- // If strarr[i] is a Token, then the match starts inside another Token, which is invalid
- if (strarr[i] instanceof Token) {
- continue;
- }
- // Number of tokens to delete and replace with the new match
- delNum = k - i;
- str = text.slice(pos, p);
- match.index -= pos;
- } else {
- pattern.lastIndex = 0;
- var match = pattern.exec(str),
- delNum = 1;
- }
- if (!match) {
- if (oneshot) {
- break;
- }
- continue;
- }
- if(lookbehind) {
- lookbehindLength = match[1] ? match[1].length : 0;
- }
- var from = match.index + lookbehindLength,
- match = match[0].slice(lookbehindLength),
- to = from + match.length,
- before = str.slice(0, from),
- after = str.slice(to);
- var args = [i, delNum];
- if (before) {
- ++i;
- pos += before.length;
- args.push(before);
- }
- var wrapped = new Token(token, inside? _.tokenize(match, inside) : match, alias, match, greedy);
- args.push(wrapped);
- if (after) {
- args.push(after);
- }
- Array.prototype.splice.apply(strarr, args);
- if (delNum != 1)
- _.matchGrammar(text, strarr, grammar, i, pos, true, token + ',' + j);
- if (oneshot)
- break;
- }
- }
- }
- },
- tokenize: function(text, grammar) {
- var strarr = [text];
- var rest = grammar.rest;
- if (rest) {
- for (var token in rest) {
- grammar[token] = rest[token];
- }
- delete grammar.rest;
- }
- _.matchGrammar(text, strarr, grammar, 0, 0, false);
- return strarr;
- },
- hooks: {
- all: {},
- add: function (name, callback) {
- var hooks = _.hooks.all;
- hooks[name] = hooks[name] || [];
- hooks[name].push(callback);
- },
- run: function (name, env) {
- var callbacks = _.hooks.all[name];
- if (!callbacks || !callbacks.length) {
- return;
- }
- for (var i=0, callback; callback = callbacks[i++];) {
- callback(env);
- }
- }
- },
- Token: Token
- };
- _self.Prism = _;
- function Token(type, content, alias, matchedStr, greedy) {
- this.type = type;
- this.content = content;
- this.alias = alias;
- // Copy of the full string this token was created from
- this.length = (matchedStr || '').length|0;
- this.greedy = !!greedy;
- }
- Token.stringify = function(o, language) {
- if (typeof o == 'string') {
- return o;
- }
- if (Array.isArray(o)) {
- return o.map(function(element) {
- return Token.stringify(element, language);
- }).join('');
- }
- var env = {
- type: o.type,
- content: Token.stringify(o.content, language),
- tag: 'span',
- classes: ['token', o.type],
- attributes: {},
- language: language
- };
- if (o.alias) {
- var aliases = Array.isArray(o.alias) ? o.alias : [o.alias];
- Array.prototype.push.apply(env.classes, aliases);
- }
- _.hooks.run('wrap', env);
- var attributes = Object.keys(env.attributes).map(function(name) {
- return name + '="' + (env.attributes[name] || '').replace(/"/g, '"') + '"';
- }).join(' ');
- return '<' + env.tag + ' class="' + env.classes.join(' ') + '"' + (attributes ? ' ' + attributes : '') + '>' + env.content + '</' + env.tag + '>';
- };
- if (!_self.document) {
- if (!_self.addEventListener) {
- // in Node.js
- return _;
- }
- if (!_.disableWorkerMessageHandler) {
- // In worker
- _self.addEventListener('message', function (evt) {
- var message = JSON.parse(evt.data),
- lang = message.language,
- code = message.code,
- immediateClose = message.immediateClose;
- _self.postMessage(_.highlight(code, _.languages[lang], lang));
- if (immediateClose) {
- _self.close();
- }
- }, false);
- }
- return _;
- }
- //Get current script and highlight
- var script = _.util.currentScript();
- if (script) {
- _.filename = script.src;
- if (script.hasAttribute('data-manual')) {
- _.manual = true;
- }
- }
- if (!_.manual) {
- function highlightAutomaticallyCallback() {
- if (!_.manual) {
- _.highlightAll();
- }
- }
- // If the document state is "loading", then we'll use DOMContentLoaded.
- // If the document state is "interactive" and the prism.js script is deferred, then we'll also use the
- // DOMContentLoaded event because there might be some plugins or languages which have also been deferred and they
- // might take longer one animation frame to execute which can create a race condition where only some plugins have
- // been loaded when Prism.highlightAll() is executed, depending on how fast resources are loaded.
- // See https://github.com/PrismJS/prism/issues/2102
- var readyState = document.readyState;
- if (readyState === 'loading' || readyState === 'interactive' && script && script.defer) {
- document.addEventListener('DOMContentLoaded', highlightAutomaticallyCallback);
- } else {
- if (window.requestAnimationFrame) {
- window.requestAnimationFrame(highlightAutomaticallyCallback);
- } else {
- window.setTimeout(highlightAutomaticallyCallback, 16);
- }
- }
- }
- return _;
- })(_self);
- if (typeof module !== 'undefined' && module.exports) {
- module.exports = Prism;
- }
- // hack for components to work correctly in node.js
- if (typeof global !== 'undefined') {
- global.Prism = Prism;
- }
- /* **********************************************
- Begin prism-markup.js
- ********************************************** */
- Prism.languages.markup = {
- 'comment': /<!--[\s\S]*?-->/,
- 'prolog': /<\?[\s\S]+?\?>/,
- 'doctype': {
- pattern: /<!DOCTYPE(?:[^>"'[\]]|"[^"]*"|'[^']*')+(?:\[(?:(?!<!--)[^"'\]]|"[^"]*"|'[^']*'|<!--[\s\S]*?-->)*\]\s*)?>/i,
- greedy: true
- },
- 'cdata': /<!\[CDATA\[[\s\S]*?]]>/i,
- 'tag': {
- pattern: /<\/?(?!\d)[^\s>\/=$<%]+(?:\s(?:\s*[^\s>\/=]+(?:\s*=\s*(?:"[^"]*"|'[^']*'|[^\s'">=]+(?=[\s>]))|(?=[\s/>])))+)?\s*\/?>/i,
- greedy: true,
- inside: {
- 'tag': {
- pattern: /^<\/?[^\s>\/]+/i,
- inside: {
- 'punctuation': /^<\/?/,
- 'namespace': /^[^\s>\/:]+:/
- }
- },
- 'attr-value': {
- pattern: /=\s*(?:"[^"]*"|'[^']*'|[^\s'">=]+)/i,
- inside: {
- 'punctuation': [
- /^=/,
- {
- pattern: /^(\s*)["']|["']$/,
- lookbehind: true
- }
- ]
- }
- },
- 'punctuation': /\/?>/,
- 'attr-name': {
- pattern: /[^\s>\/]+/,
- inside: {
- 'namespace': /^[^\s>\/:]+:/
- }
- }
- }
- },
- 'entity': /&#?[\da-z]{1,8};/i
- };
- Prism.languages.markup['tag'].inside['attr-value'].inside['entity'] =
- Prism.languages.markup['entity'];
- // Plugin to make entity title show the real entity, idea by Roman Komarov
- Prism.hooks.add('wrap', function(env) {
- if (env.type === 'entity') {
- env.attributes['title'] = env.content.replace(/&/, '&');
- }
- });
- Object.defineProperty(Prism.languages.markup.tag, 'addInlined', {
- /**
- * Adds an inlined language to markup.
- *
- * An example of an inlined language is CSS with `<style>` tags.
- *
- * @param {string} tagName The name of the tag that contains the inlined language. This name will be treated as
- * case insensitive.
- * @param {string} lang The language key.
- * @example
- * addInlined('style', 'css');
- */
- value: function addInlined(tagName, lang) {
- var includedCdataInside = {};
- includedCdataInside['language-' + lang] = {
- pattern: /(^<!\[CDATA\[)[\s\S]+?(?=\]\]>$)/i,
- lookbehind: true,
- inside: Prism.languages[lang]
- };
- includedCdataInside['cdata'] = /^<!\[CDATA\[|\]\]>$/i;
- var inside = {
- 'included-cdata': {
- pattern: /<!\[CDATA\[[\s\S]*?\]\]>/i,
- inside: includedCdataInside
- }
- };
- inside['language-' + lang] = {
- pattern: /[\s\S]+/,
- inside: Prism.languages[lang]
- };
- var def = {};
- def[tagName] = {
- pattern: RegExp(/(<__[\s\S]*?>)(?:<!\[CDATA\[[\s\S]*?\]\]>\s*|[\s\S])*?(?=<\/__>)/.source.replace(/__/g, tagName), 'i'),
- lookbehind: true,
- greedy: true,
- inside: inside
- };
- Prism.languages.insertBefore('markup', 'cdata', def);
- }
- });
- Prism.languages.xml = Prism.languages.extend('markup', {});
- Prism.languages.html = Prism.languages.markup;
- Prism.languages.mathml = Prism.languages.markup;
- Prism.languages.svg = Prism.languages.markup;
- /* **********************************************
- Begin prism-css.js
- ********************************************** */
- (function (Prism) {
- var string = /("|')(?:\\(?:\r\n|[\s\S])|(?!\1)[^\\\r\n])*\1/;
- Prism.languages.css = {
- 'comment': /\/\*[\s\S]*?\*\//,
- 'atrule': {
- pattern: /@[\w-]+[\s\S]*?(?:;|(?=\s*\{))/,
- inside: {
- 'rule': /@[\w-]+/
- // See rest below
- }
- },
- 'url': {
- pattern: RegExp('url\\((?:' + string.source + '|[^\n\r()]*)\\)', 'i'),
- inside: {
- 'function': /^url/i,
- 'punctuation': /^\(|\)$/
- }
- },
- 'selector': RegExp('[^{}\\s](?:[^{};"\']|' + string.source + ')*?(?=\\s*\\{)'),
- 'string': {
- pattern: string,
- greedy: true
- },
- 'property': /[-_a-z\xA0-\uFFFF][-\w\xA0-\uFFFF]*(?=\s*:)/i,
- 'important': /!important\b/i,
- 'function': /[-a-z0-9]+(?=\()/i,
- 'punctuation': /[(){};:,]/
- };
- Prism.languages.css['atrule'].inside.rest = Prism.languages.css;
- var markup = Prism.languages.markup;
- if (markup) {
- markup.tag.addInlined('style', 'css');
- Prism.languages.insertBefore('inside', 'attr-value', {
- 'style-attr': {
- pattern: /\s*style=("|')(?:\\[\s\S]|(?!\1)[^\\])*\1/i,
- inside: {
- 'attr-name': {
- pattern: /^\s*style/i,
- inside: markup.tag.inside
- },
- 'punctuation': /^\s*=\s*['"]|['"]\s*$/,
- 'attr-value': {
- pattern: /.+/i,
- inside: Prism.languages.css
- }
- },
- alias: 'language-css'
- }
- }, markup.tag);
- }
- }(Prism));
- /* **********************************************
- Begin prism-clike.js
- ********************************************** */
- Prism.languages.clike = {
- 'comment': [
- {
- pattern: /(^|[^\\])\/\*[\s\S]*?(?:\*\/|$)/,
- lookbehind: true
- },
- {
- pattern: /(^|[^\\:])\/\/.*/,
- lookbehind: true,
- greedy: true
- }
- ],
- 'string': {
- pattern: /(["'])(?:\\(?:\r\n|[\s\S])|(?!\1)[^\\\r\n])*\1/,
- greedy: true
- },
- 'class-name': {
- pattern: /(\b(?:class|interface|extends|implements|trait|instanceof|new)\s+|\bcatch\s+\()[\w.\\]+/i,
- lookbehind: true,
- inside: {
- 'punctuation': /[.\\]/
- }
- },
- 'keyword': /\b(?:if|else|while|do|for|return|in|instanceof|function|new|try|throw|catch|finally|null|break|continue)\b/,
- 'boolean': /\b(?:true|false)\b/,
- 'function': /\w+(?=\()/,
- 'number': /\b0x[\da-f]+\b|(?:\b\d+\.?\d*|\B\.\d+)(?:e[+-]?\d+)?/i,
- 'operator': /[<>]=?|[!=]=?=?|--?|\+\+?|&&?|\|\|?|[?*/~^%]/,
- 'punctuation': /[{}[\];(),.:]/
- };
- /* **********************************************
- Begin prism-javascript.js
- ********************************************** */
- Prism.languages.javascript = Prism.languages.extend('clike', {
- 'class-name': [
- Prism.languages.clike['class-name'],
- {
- pattern: /(^|[^$\w\xA0-\uFFFF])[_$A-Z\xA0-\uFFFF][$\w\xA0-\uFFFF]*(?=\.(?:prototype|constructor))/,
- lookbehind: true
- }
- ],
- 'keyword': [
- {
- pattern: /((?:^|})\s*)(?:catch|finally)\b/,
- lookbehind: true
- },
- {
- pattern: /(^|[^.]|\.\.\.\s*)\b(?:as|async(?=\s*(?:function\b|\(|[$\w\xA0-\uFFFF]|$))|await|break|case|class|const|continue|debugger|default|delete|do|else|enum|export|extends|for|from|function|get|if|implements|import|in|instanceof|interface|let|new|null|of|package|private|protected|public|return|set|static|super|switch|this|throw|try|typeof|undefined|var|void|while|with|yield)\b/,
- lookbehind: true
- },
- ],
- 'number': /\b(?:(?:0[xX](?:[\dA-Fa-f](?:_[\dA-Fa-f])?)+|0[bB](?:[01](?:_[01])?)+|0[oO](?:[0-7](?:_[0-7])?)+)n?|(?:\d(?:_\d)?)+n|NaN|Infinity)\b|(?:\b(?:\d(?:_\d)?)+\.?(?:\d(?:_\d)?)*|\B\.(?:\d(?:_\d)?)+)(?:[Ee][+-]?(?:\d(?:_\d)?)+)?/,
- // Allow for all non-ASCII characters (See http://stackoverflow.com/a/2008444)
- 'function': /#?[_$a-zA-Z\xA0-\uFFFF][$\w\xA0-\uFFFF]*(?=\s*(?:\.\s*(?:apply|bind|call)\s*)?\()/,
- 'operator': /--|\+\+|\*\*=?|=>|&&|\|\||[!=]==|<<=?|>>>?=?|[-+*/%&|^!=<>]=?|\.{3}|\?[.?]?|[~:]/
- });
- Prism.languages.javascript['class-name'][0].pattern = /(\b(?:class|interface|extends|implements|instanceof|new)\s+)[\w.\\]+/;
- Prism.languages.insertBefore('javascript', 'keyword', {
- 'regex': {
- pattern: /((?:^|[^$\w\xA0-\uFFFF."'\])\s])\s*)\/(?:\[(?:[^\]\\\r\n]|\\.)*]|\\.|[^/\\\[\r\n])+\/[gimyus]{0,6}(?=(?:\s|\/\*[\s\S]*?\*\/)*(?:$|[\r\n,.;:})\]]|\/\/))/,
- lookbehind: true,
- greedy: true
- },
- // This must be declared before keyword because we use "function" inside the look-forward
- 'function-variable': {
- pattern: /#?[_$a-zA-Z\xA0-\uFFFF][$\w\xA0-\uFFFF]*(?=\s*[=:]\s*(?:async\s*)?(?:\bfunction\b|(?:\((?:[^()]|\([^()]*\))*\)|[_$a-zA-Z\xA0-\uFFFF][$\w\xA0-\uFFFF]*)\s*=>))/,
- alias: 'function'
- },
- 'parameter': [
- {
- pattern: /(function(?:\s+[_$A-Za-z\xA0-\uFFFF][$\w\xA0-\uFFFF]*)?\s*\(\s*)(?!\s)(?:[^()]|\([^()]*\))+?(?=\s*\))/,
- lookbehind: true,
- inside: Prism.languages.javascript
- },
- {
- pattern: /[_$a-z\xA0-\uFFFF][$\w\xA0-\uFFFF]*(?=\s*=>)/i,
- inside: Prism.languages.javascript
- },
- {
- pattern: /(\(\s*)(?!\s)(?:[^()]|\([^()]*\))+?(?=\s*\)\s*=>)/,
- lookbehind: true,
- inside: Prism.languages.javascript
- },
- {
- pattern: /((?:\b|\s|^)(?!(?:as|async|await|break|case|catch|class|const|continue|debugger|default|delete|do|else|enum|export|extends|finally|for|from|function|get|if|implements|import|in|instanceof|interface|let|new|null|of|package|private|protected|public|return|set|static|super|switch|this|throw|try|typeof|undefined|var|void|while|with|yield)(?![$\w\xA0-\uFFFF]))(?:[_$A-Za-z\xA0-\uFFFF][$\w\xA0-\uFFFF]*\s*)\(\s*)(?!\s)(?:[^()]|\([^()]*\))+?(?=\s*\)\s*\{)/,
- lookbehind: true,
- inside: Prism.languages.javascript
- }
- ],
- 'constant': /\b[A-Z](?:[A-Z_]|\dx?)*\b/
- });
- Prism.languages.insertBefore('javascript', 'string', {
- 'template-string': {
- pattern: /`(?:\\[\s\S]|\${(?:[^{}]|{(?:[^{}]|{[^}]*})*})+}|(?!\${)[^\\`])*`/,
- greedy: true,
- inside: {
- 'template-punctuation': {
- pattern: /^`|`$/,
- alias: 'string'
- },
- 'interpolation': {
- pattern: /((?:^|[^\\])(?:\\{2})*)\${(?:[^{}]|{(?:[^{}]|{[^}]*})*})+}/,
- lookbehind: true,
- inside: {
- 'interpolation-punctuation': {
- pattern: /^\${|}$/,
- alias: 'punctuation'
- },
- rest: Prism.languages.javascript
- }
- },
- 'string': /[\s\S]+/
- }
- }
- });
- if (Prism.languages.markup) {
- Prism.languages.markup.tag.addInlined('script', 'javascript');
- }
- Prism.languages.js = Prism.languages.javascript;
- /* **********************************************
- Begin prism-file-highlight.js
- ********************************************** */
- (function () {
- if (typeof self === 'undefined' || !self.Prism || !self.document || !document.querySelector) {
- return;
- }
- /**
- * @param {Element} [container=document]
- */
- self.Prism.fileHighlight = function(container) {
- container = container || document;
- var Extensions = {
- 'js': 'javascript',
- 'py': 'python',
- 'rb': 'ruby',
- 'ps1': 'powershell',
- 'psm1': 'powershell',
- 'sh': 'bash',
- 'bat': 'batch',
- 'h': 'c',
- 'tex': 'latex'
- };
- Array.prototype.slice.call(container.querySelectorAll('pre[data-src]')).forEach(function (pre) {
- // ignore if already loaded
- if (pre.hasAttribute('data-src-loaded')) {
- return;
- }
- // load current
- var src = pre.getAttribute('data-src');
- var language, parent = pre;
- var lang = /\blang(?:uage)?-([\w-]+)\b/i;
- while (parent && !lang.test(parent.className)) {
- parent = parent.parentNode;
- }
- if (parent) {
- language = (pre.className.match(lang) || [, ''])[1];
- }
- if (!language) {
- var extension = (src.match(/\.(\w+)$/) || [, ''])[1];
- language = Extensions[extension] || extension;
- }
- var code = document.createElement('code');
- code.className = 'language-' + language;
- pre.textContent = '';
- code.textContent = 'Loading…';
- pre.appendChild(code);
- var xhr = new XMLHttpRequest();
- xhr.open('GET', src, true);
- xhr.onreadystatechange = function () {
- if (xhr.readyState == 4) {
- if (xhr.status < 400 && xhr.responseText) {
- code.textContent = xhr.responseText;
- Prism.highlightElement(code);
- // mark as loaded
- pre.setAttribute('data-src-loaded', '');
- }
- else if (xhr.status >= 400) {
- code.textContent = '✖ Error ' + xhr.status + ' while fetching file: ' + xhr.statusText;
- }
- else {
- code.textContent = '✖ Error: File does not exist or is empty';
- }
- }
- };
- xhr.send(null);
- });
- };
- document.addEventListener('DOMContentLoaded', function () {
- // execute inside handler, for dropping Event as argument
- self.Prism.fileHighlight();
- });
- })();
- ;
|